Mercurial
annotate schwab_trader/dashboard_server.py @ 258:60a876c4587a
[ui] Add semantic primitive ownership
Build a layered Zenbu token and sizing system, make authored controls use native-underneath primitives, migrate mrjunejune without imposing visual surfaces, and document/enforce HTML ownership in the catalog and wiki.
Co-authored-by: Copilot <[email protected]>
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Wed, 05 Aug 2026 05:25:40 -0700 |
| parents | eb8b4230fdb9 |
| children |
| rev | line source |
|---|---|
|
220
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1 from __future__ import annotations |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3 import argparse |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
4 import json |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
5 from http import HTTPStatus |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
6 from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
7 from typing import Any |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
8 from urllib.parse import parse_qs, urlparse |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
9 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
10 from schwab_trader.dashboard import DashboardStore, EvidenceInput, get_config_if_available |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
11 from schwab_trader.schwab_client import SchwabError, get_account, get_account_numbers, get_accounts, load_tokens |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
12 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
13 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
14 INDEX_HTML = """<!doctype html> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
15 <html lang="en"> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
16 <head> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
17 <meta charset="utf-8"> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
18 <meta name="viewport" content="width=device-width, initial-scale=1"> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
19 <title>Schwab Sentiment Dashboard</title> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
20 <style> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
21 :root { color-scheme: dark; font-family: Inter, system-ui, sans-serif; background: #0b1020; color: #eef2ff; } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
22 body { margin: 0; } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
23 header { padding: 24px; background: linear-gradient(135deg, #172554, #0f172a); border-bottom: 1px solid #334155; } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
24 h1 { margin: 0 0 8px; font-size: 28px; } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
25 main { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); padding: 16px; } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
26 section { background: #111827; border: 1px solid #334155; border-radius: 14px; padding: 16px; box-shadow: 0 10px 25px #0004; } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
27 h2 { margin-top: 0; font-size: 18px; } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
28 label { display: block; margin: 10px 0 4px; color: #cbd5e1; } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
29 input, textarea, select, button { width: 100%; box-sizing: border-box; border-radius: 8px; border: 1px solid #475569; background: #020617; color: #f8fafc; padding: 10px; } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
30 button { margin-top: 12px; background: #2563eb; border: 0; font-weight: 700; cursor: pointer; } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
31 button.secondary { background: #334155; } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
32 pre { overflow: auto; white-space: pre-wrap; word-break: break-word; background: #020617; padding: 12px; border-radius: 8px; } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
33 table { width: 100%; border-collapse: collapse; font-size: 13px; } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
34 th, td { border-bottom: 1px solid #334155; padding: 8px; text-align: left; vertical-align: top; } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
35 .ok { color: #86efac; } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
36 .warn { color: #fbbf24; } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
37 .bad { color: #fca5a5; } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
38 .span2 { grid-column: 1 / -1; } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
39 </style> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
40 </head> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
41 <body> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
42 <header> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
43 <h1>Schwab Sentiment Dashboard</h1> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
44 <div>This is local-first and read/paper-trade focused. There is no live-trade endpoint in this dashboard.</div> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
45 </header> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
46 <main> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
47 <section> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
48 <h2>Status</h2> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
49 <button onclick="loadAll()">Refresh</button> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
50 <pre id="status">Loading...</pre> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
51 </section> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
52 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
53 <section> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
54 <h2>Settings</h2> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
55 <label>Profit target %</label><input id="profit_target_pct" type="number" step="0.1"> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
56 <label>Stop loss %</label><input id="stop_loss_pct" type="number" step="0.1"> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
57 <label>Max dollars per paper trade</label><input id="max_trade_dollars" type="number" step="1"> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
58 <label>Minimum confidence</label><input id="min_confidence" type="number" step="0.01" min="0" max="1"> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
59 <button onclick="saveSettings()">Save Settings</button> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
60 <pre id="settingsResult"></pre> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
61 </section> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
62 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
63 <section> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
64 <h2>Add Social Evidence</h2> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
65 <label>Source</label><input id="source" placeholder="reddit"> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
66 <label>Symbol</label><input id="symbol" placeholder="AAPL"> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
67 <label>URL</label><input id="url" placeholder="https://..."> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
68 <label>Engagement</label><input id="engagement" type="number" value="0"> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
69 <label>Text</label><textarea id="text" rows="5" placeholder="$AAPL looks bullish..."></textarea> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
70 <button onclick="addEvidence()">Add Evidence</button> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
71 <pre id="evidenceResult"></pre> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
72 </section> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
73 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
74 <section> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
75 <h2>Paper Trade</h2> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
76 <label>Action</label><select id="paperAction"><option>BUY</option><option>SELL</option></select> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
77 <label>Symbol</label><input id="paperSymbol" placeholder="AAPL"> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
78 <label>Quantity</label><input id="paperQuantity" type="number" step="0.01" value="1"> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
79 <label>Price</label><input id="paperPrice" type="number" step="0.01"> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
80 <label>Reason</label><input id="paperReason" placeholder="manual paper trade"> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
81 <button onclick="addPaperTrade()">Create Paper Trade</button> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
82 <pre id="paperResult"></pre> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
83 </section> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
84 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
85 <section class="span2"> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
86 <h2>Signals</h2> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
87 <div id="signals"></div> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
88 </section> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
89 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
90 <section> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
91 <h2>Recent Evidence</h2> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
92 <div id="evidence"></div> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
93 </section> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
94 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
95 <section> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
96 <h2>Paper Trades</h2> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
97 <div id="paperTrades"></div> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
98 </section> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
99 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
100 <section class="span2"> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
101 <h2>Audit Log</h2> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
102 <div id="audit"></div> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
103 </section> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
104 </main> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
105 <script> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
106 async function api(path, options = {}) { |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
107 const response = await fetch(path, { |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
108 headers: {'Content-Type': 'application/json'}, |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
109 ...options |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
110 }); |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
111 const body = await response.json(); |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
112 if (!response.ok) throw new Error(body.error || response.statusText); |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
113 return body; |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
114 } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
115 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
116 function json(id, value) { |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
117 document.getElementById(id).textContent = JSON.stringify(value, null, 2); |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
118 } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
119 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
120 function table(rows, cols) { |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
121 if (!rows.length) return '<p class="warn">No data yet.</p>'; |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
122 return '<table><thead><tr>' + cols.map(c => `<th>${c}</th>`).join('') + |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
123 '</tr></thead><tbody>' + rows.map(row => '<tr>' + cols.map(c => `<td>${row[c] ?? ''}</td>`).join('') + '</tr>').join('') + '</tbody></table>'; |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
124 } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
125 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
126 async function loadAll() { |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
127 const [status, settings, signals, evidence, paperTrades, audit] = await Promise.all([ |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
128 api('/api/status'), api('/api/settings'), api('/api/signals'), |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
129 api('/api/evidence'), api('/api/paper-trades'), api('/api/audit') |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
130 ]); |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
131 json('status', status); |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
132 for (const key of ['profit_target_pct', 'stop_loss_pct', 'max_trade_dollars', 'min_confidence']) { |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
133 document.getElementById(key).value = settings[key]; |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
134 } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
135 document.getElementById('signals').innerHTML = table(signals, ['symbol', 'action', 'sentiment_score', 'confidence', 'evidence_count', 'source_count', 'summary']); |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
136 document.getElementById('evidence').innerHTML = table(evidence, ['id', 'source', 'symbol', 'sentiment_score', 'engagement', 'text']); |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
137 document.getElementById('paperTrades').innerHTML = table(paperTrades, ['id', 'symbol', 'action', 'quantity', 'price', 'notional', 'status', 'reason']); |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
138 document.getElementById('audit').innerHTML = table(audit, ['id', 'event_type', 'message', 'created_at']); |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
139 } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
140 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
141 async function saveSettings() { |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
142 try { |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
143 const body = {}; |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
144 for (const key of ['profit_target_pct', 'stop_loss_pct', 'max_trade_dollars', 'min_confidence']) { |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
145 body[key] = Number(document.getElementById(key).value); |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
146 } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
147 json('settingsResult', await api('/api/settings', {method: 'POST', body: JSON.stringify(body)})); |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
148 await loadAll(); |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
149 } catch (error) { json('settingsResult', {error: error.message}); } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
150 } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
151 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
152 async function addEvidence() { |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
153 try { |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
154 const body = { |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
155 source: document.getElementById('source').value, |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
156 symbol: document.getElementById('symbol').value, |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
157 url: document.getElementById('url').value, |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
158 engagement: Number(document.getElementById('engagement').value), |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
159 text: document.getElementById('text').value |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
160 }; |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
161 json('evidenceResult', await api('/api/evidence', {method: 'POST', body: JSON.stringify(body)})); |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
162 await loadAll(); |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
163 } catch (error) { json('evidenceResult', {error: error.message}); } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
164 } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
165 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
166 async function addPaperTrade() { |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
167 try { |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
168 const body = { |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
169 action: document.getElementById('paperAction').value, |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
170 symbol: document.getElementById('paperSymbol').value, |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
171 quantity: Number(document.getElementById('paperQuantity').value), |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
172 price: Number(document.getElementById('paperPrice').value), |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
173 reason: document.getElementById('paperReason').value |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
174 }; |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
175 json('paperResult', await api('/api/paper-trades', {method: 'POST', body: JSON.stringify(body)})); |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
176 await loadAll(); |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
177 } catch (error) { json('paperResult', {error: error.message}); } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
178 } |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
179 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
180 loadAll(); |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
181 </script> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
182 </body> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
183 </html> |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
184 """ |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
185 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
186 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
187 def create_handler(store: DashboardStore) -> type[BaseHTTPRequestHandler]: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
188 class DashboardHandler(BaseHTTPRequestHandler): |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
189 server_version = "SchwabDashboard/0.1" |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
190 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
191 def do_GET(self) -> None: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
192 try: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
193 parsed = urlparse(self.path) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
194 query = parse_qs(parsed.query) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
195 if parsed.path == "/": |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
196 self._send_html(INDEX_HTML) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
197 elif parsed.path == "/api/status": |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
198 self._send_json(store.get_status()) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
199 elif parsed.path == "/api/settings": |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
200 self._send_json(store.get_settings()) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
201 elif parsed.path == "/api/evidence": |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
202 self._send_json(store.list_evidence()) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
203 elif parsed.path == "/api/signals": |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
204 self._send_json(store.list_signals()) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
205 elif parsed.path == "/api/paper-trades": |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
206 self._send_json(store.list_paper_trades()) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
207 elif parsed.path == "/api/audit": |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
208 self._send_json(store.list_audit()) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
209 elif parsed.path == "/api/schwab/account-numbers": |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
210 self._send_json(_load_schwab_account_numbers()) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
211 elif parsed.path == "/api/schwab/accounts": |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
212 fields = "positions" if query.get("positions") == ["1"] else None |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
213 self._send_json(_load_schwab_accounts(fields)) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
214 elif parsed.path == "/api/schwab/account": |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
215 account_hash = query.get("account_hash", [""])[0] |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
216 fields = "positions" if query.get("positions") == ["1"] else None |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
217 self._send_json(_load_schwab_account(account_hash, fields)) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
218 else: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
219 self._send_error(HTTPStatus.NOT_FOUND, "Not found") |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
220 except (ValueError, SchwabError) as error: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
221 self._send_error(HTTPStatus.BAD_REQUEST, str(error)) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
222 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
223 def do_POST(self) -> None: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
224 try: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
225 parsed = urlparse(self.path) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
226 payload = self._read_json() |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
227 if parsed.path == "/api/settings": |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
228 self._send_json(store.update_settings(payload)) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
229 elif parsed.path == "/api/evidence": |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
230 result = store.add_evidence( |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
231 EvidenceInput( |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
232 source=str(payload.get("source", "")), |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
233 symbol=str(payload["symbol"]) if payload.get("symbol") else None, |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
234 url=str(payload["url"]) if payload.get("url") else None, |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
235 text=str(payload.get("text", "")), |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
236 engagement=float(payload.get("engagement", 0)), |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
237 raw=payload.get("raw") if isinstance(payload.get("raw"), dict) else None, |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
238 ) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
239 ) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
240 self._send_json(result, HTTPStatus.CREATED) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
241 elif parsed.path == "/api/paper-trades": |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
242 self._send_json(store.add_paper_trade(payload), HTTPStatus.CREATED) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
243 else: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
244 self._send_error(HTTPStatus.NOT_FOUND, "Not found") |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
245 except (ValueError, KeyError, SchwabError) as error: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
246 self._send_error(HTTPStatus.BAD_REQUEST, str(error)) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
247 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
248 def log_message(self, format: str, *args: Any) -> None: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
249 print(f"[dashboard] {self.address_string()} - {format % args}") |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
250 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
251 def _read_json(self) -> dict[str, Any]: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
252 length = int(self.headers.get("Content-Length", "0")) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
253 if length <= 0: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
254 return {} |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
255 raw = self.rfile.read(length).decode("utf-8") |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
256 value = json.loads(raw) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
257 if not isinstance(value, dict): |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
258 raise ValueError("JSON body must be an object") |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
259 return value |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
260 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
261 def _send_html(self, body: str) -> None: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
262 data = body.encode("utf-8") |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
263 self.send_response(HTTPStatus.OK) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
264 self.send_header("Content-Type", "text/html; charset=utf-8") |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
265 self.send_header("Content-Length", str(len(data))) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
266 self.end_headers() |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
267 self.wfile.write(data) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
268 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
269 def _send_json(self, body: Any, status: HTTPStatus = HTTPStatus.OK) -> None: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
270 data = json.dumps(body, indent=2, sort_keys=True).encode("utf-8") |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
271 self.send_response(status) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
272 self.send_header("Content-Type", "application/json; charset=utf-8") |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
273 self.send_header("Content-Length", str(len(data))) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
274 self.end_headers() |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
275 self.wfile.write(data) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
276 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
277 def _send_error(self, status: HTTPStatus, message: str) -> None: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
278 self._send_json({"error": message}, status) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
279 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
280 return DashboardHandler |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
281 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
282 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
283 def _load_schwab_account_numbers() -> Any: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
284 config, access_token = _load_schwab_access_token() |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
285 return get_account_numbers(access_token).body |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
286 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
287 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
288 def _load_schwab_accounts(fields: str | None) -> Any: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
289 config, access_token = _load_schwab_access_token() |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
290 return get_accounts(access_token, fields).body |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
291 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
292 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
293 def _load_schwab_account(account_hash: str, fields: str | None) -> Any: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
294 if not account_hash: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
295 raise ValueError("account_hash is required") |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
296 config, access_token = _load_schwab_access_token() |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
297 return get_account(access_token, account_hash, fields).body |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
298 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
299 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
300 def _load_schwab_access_token() -> tuple[Any, str]: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
301 config = get_config_if_available() |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
302 if config is None: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
303 raise SchwabError("Schwab environment is not configured") |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
304 tokens = load_tokens(config.token_file) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
305 access_token = tokens.get("access_token") |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
306 if not access_token: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
307 raise SchwabError(f"No access_token in {config.token_file}") |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
308 return config, access_token |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
309 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
310 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
311 def run(host: str, port: int, db_path: str | None = None) -> None: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
312 store = DashboardStore(db_path) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
313 server = ThreadingHTTPServer((host, port), create_handler(store)) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
314 print(f"Schwab dashboard listening on http://{host}:{port}") |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
315 print("Live trading is disabled in this dashboard.") |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
316 server.serve_forever() |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
317 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
318 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
319 def main() -> int: |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
320 parser = argparse.ArgumentParser(description="Run the local Schwab sentiment dashboard") |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
321 parser.add_argument("--host", default="127.0.0.1") |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
322 parser.add_argument("--port", default=8765, type=int) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
323 parser.add_argument("--db", help="SQLite dashboard DB path") |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
324 args = parser.parse_args() |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
325 run(args.host, args.port, args.db) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
326 return 0 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
327 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
328 |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
329 if __name__ == "__main__": |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
330 raise SystemExit(main()) |
|
eb8b4230fdb9
[schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
331 |