annotate schwab_trader/dashboard_server.py @ 247:70f2a3dafc1c

[build] Bundle offline Tectonic runtime Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Mon, 03 Aug 2026 20:20:06 -0700
parents eb8b4230fdb9
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
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