annotate schwab_trader/dashboard_server.py @ 279:b3b547563ec7

Add Google connector service and agent wiki Implement the C/Seobeo Google Drive and Gmail connector with encrypted OAuth storage, Zenbu authentication, browser testing, AI tool discovery, chunked HTTP decoding, and Bazel coverage. Consolidate repository guidance into progressive wiki documentation and enforce arena-first allocation for new first-party C code. Co-authored-by: Copilot <[email protected]> Copilot-Session: 84c338fd-0939-4bb3-b7f3-1062eb213e5d
author MrJuneJune <me@mrjunejune.com>
date Mon, 17 Aug 2026 22:22:36 -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