annotate schwab_trader/dashboard.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 json
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
4 import os
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
5 import re
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
6 import sqlite3
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
7 import time
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
8 from dataclasses import dataclass
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
9 from pathlib import Path
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
10 from typing import Any
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
11
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
12 from schwab_trader.schwab_client import DEFAULT_TOKEN_FILE, SchwabConfig, load_tokens
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
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
15 DEFAULT_DASHBOARD_DB = "~/.local/share/zenbu/schwab_trader/dashboard.db"
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
16
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
17 DEFAULT_SETTINGS: dict[str, Any] = {
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
18 "profit_target_pct": 3.0,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
19 "stop_loss_pct": 2.0,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
20 "max_trade_dollars": 500.0,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
21 "max_account_pct": 2.0,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
22 "max_open_positions": 3,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
23 "min_confidence": 0.60,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
24 "min_evidence_count": 3,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
25 "min_source_count": 2,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
26 "cooldown_minutes": 60,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
27 "market_hours_only": True,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
28 "max_daily_loss_dollars": 250.0,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
29 "day_trade_limit": 3,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
30 "live_trading_enabled": False,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
31 "require_manual_confirmation": True,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
32 "llm_enabled": False,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
33 "allowlist": [],
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
34 "blocklist": [],
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
35 }
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
36
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
37 POSITIVE_WORDS = {
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
38 "beat",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
39 "beats",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
40 "bull",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
41 "bullish",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
42 "buy",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
43 "calls",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
44 "growth",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
45 "hype",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
46 "moon",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
47 "mooning",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
48 "profit",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
49 "rally",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
50 "strong",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
51 "surge",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
52 "up",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
53 "winner",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
54 }
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
55
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
56 NEGATIVE_WORDS = {
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
57 "bear",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
58 "bearish",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
59 "crash",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
60 "dump",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
61 "fall",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
62 "falling",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
63 "fraud",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
64 "lawsuit",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
65 "loss",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
66 "miss",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
67 "puts",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
68 "risk",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
69 "sell",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
70 "short",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
71 "weak",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
72 }
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 SYMBOL_RE = re.compile(r"(?<![A-Z0-9])\$?([A-Z]{1,5})(?![A-Z0-9])")
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
75 COMMON_WORDS = {
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
76 "A",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
77 "AI",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
78 "AM",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
79 "API",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
80 "CEO",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
81 "CFO",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
82 "DD",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
83 "ETF",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
84 "GDP",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
85 "IPO",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
86 "IRS",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
87 "LLM",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
88 "PDT",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
89 "SEC",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
90 "USA",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
91 "USD",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
92 }
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
93
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 @dataclass(frozen=True)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
96 class EvidenceInput:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
97 source: str
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
98 text: str
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
99 symbol: str | None = None
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
100 url: str | None = None
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
101 engagement: float = 0.0
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
102 raw: dict[str, Any] | None = None
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
103
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
104
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
105 class DashboardStore:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
106 def __init__(self, db_path: Path | str | None = None) -> None:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
107 if db_path is None:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
108 db_path = os.environ.get("SCHWAB_DASHBOARD_DB", DEFAULT_DASHBOARD_DB)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
109 self.db_path = Path(db_path).expanduser()
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
110 self.db_path.parent.mkdir(parents=True, exist_ok=True)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
111 self._init_db()
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
112
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
113 def get_status(self) -> dict[str, Any]:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
114 token_file = Path(os.environ.get("SCHWAB_TOKEN_FILE", DEFAULT_TOKEN_FILE)).expanduser()
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
115 token_status: dict[str, Any] = {
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
116 "path": str(token_file),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
117 "exists": token_file.exists(),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
118 "access_token_present": False,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
119 "refresh_token_present": False,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
120 "saved_at": None,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
121 "age_seconds": None,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
122 }
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
123 if token_file.exists():
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
124 try:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
125 tokens = load_tokens(token_file)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
126 saved_at = tokens.get("saved_at")
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
127 token_status.update(
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
128 {
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
129 "access_token_present": bool(tokens.get("access_token")),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
130 "refresh_token_present": bool(tokens.get("refresh_token")),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
131 "saved_at": saved_at,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
132 "age_seconds": int(time.time()) - int(saved_at) if saved_at else None,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
133 }
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 except (OSError, ValueError, TypeError) as error:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
136 token_status["error"] = str(error)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
137
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
138 env_status = {
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
139 "SCHWAB_APP_KEY": bool(os.environ.get("SCHWAB_APP_KEY")),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
140 "SCHWAB_APP_SECRET": bool(os.environ.get("SCHWAB_APP_SECRET")),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
141 "SCHWAB_REDIRECT_URI": bool(os.environ.get("SCHWAB_REDIRECT_URI")),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
142 }
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
143
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
144 return {
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
145 "service": "schwab-dashboard",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
146 "database": str(self.db_path),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
147 "env": env_status,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
148 "tokens": token_status,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
149 "live_trading_enabled": False,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
150 "live_trading_note": "Dashboard has no live-trade endpoint; use CLI dry-run/manual confirmation flow.",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
151 "counts": self.get_counts(),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
152 }
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
153
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
154 def get_counts(self) -> dict[str, int]:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
155 with self._connect() as conn:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
156 return {
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
157 "evidence": self._count(conn, "evidence"),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
158 "signals": self._count(conn, "signals"),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
159 "paper_trades": self._count(conn, "paper_trades"),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
160 "audit_events": self._count(conn, "audit"),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
161 }
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
162
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
163 def get_settings(self) -> dict[str, Any]:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
164 settings = dict(DEFAULT_SETTINGS)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
165 with self._connect() as conn:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
166 for row in conn.execute("SELECT key, value_json FROM settings"):
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
167 settings[row["key"]] = json.loads(row["value_json"])
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
168 return settings
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
169
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
170 def update_settings(self, updates: dict[str, Any]) -> dict[str, Any]:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
171 allowed = set(DEFAULT_SETTINGS)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
172 unknown = sorted(set(updates) - allowed)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
173 if unknown:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
174 raise ValueError("Unknown settings: " + ", ".join(unknown))
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
175
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
176 current = self.get_settings()
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
177 current.update(updates)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
178 self._validate_settings(current)
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 with self._connect() as conn:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
181 for key, value in current.items():
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
182 conn.execute(
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
183 """
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
184 INSERT INTO settings(key, value_json)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
185 VALUES (?, ?)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
186 ON CONFLICT(key) DO UPDATE SET value_json = excluded.value_json
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
187 """,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
188 (key, json.dumps(value, sort_keys=True)),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
189 )
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
190 conn.commit()
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
191 self.add_audit("settings.updated", "Dashboard settings updated", updates)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
192 return current
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
193
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
194 def add_evidence(self, item: EvidenceInput) -> dict[str, Any]:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
195 source = item.source.strip().lower()
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
196 text = item.text.strip()
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
197 if not source:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
198 raise ValueError("source is required")
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
199 if not text:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
200 raise ValueError("text is required")
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
201
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
202 symbol = normalize_symbol(item.symbol) if item.symbol else extract_symbol(text)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
203 if not symbol:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
204 raise ValueError("symbol is required or must be detectable as a ticker in text")
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
205
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
206 sentiment_score = score_sentiment(text)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
207 created_at = int(time.time())
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
208
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
209 with self._connect() as conn:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
210 cursor = conn.execute(
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
211 """
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
212 INSERT INTO evidence(
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
213 source, symbol, url, text, engagement, sentiment_score, raw_json, created_at
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
214 )
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
215 VALUES (?, ?, ?, ?, ?, ?, ?, ?)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
216 """,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
217 (
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
218 source,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
219 symbol,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
220 item.url,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
221 text,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
222 float(item.engagement),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
223 sentiment_score,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
224 json.dumps(item.raw or {}, sort_keys=True),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
225 created_at,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
226 ),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
227 )
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
228 evidence_id = int(cursor.lastrowid)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
229 conn.commit()
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
230
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
231 signal = self.recompute_signal(symbol)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
232 self.add_audit(
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
233 "evidence.added",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
234 f"Added {source} evidence for {symbol}",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
235 {"evidence_id": evidence_id, "symbol": symbol, "signal": signal},
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
236 )
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
237 return {"id": evidence_id, "symbol": symbol, "sentiment_score": sentiment_score, "signal": signal}
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 def list_evidence(self, limit: int = 100) -> list[dict[str, Any]]:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
240 with self._connect() as conn:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
241 rows = conn.execute(
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
242 """
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
243 SELECT id, source, symbol, url, text, engagement, sentiment_score, created_at
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
244 FROM evidence
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
245 ORDER BY id DESC
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
246 LIMIT ?
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 (limit,),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
249 ).fetchall()
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
250 return [dict(row) for row in rows]
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
251
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
252 def recompute_signal(self, symbol: str) -> dict[str, Any]:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
253 symbol = normalize_symbol(symbol)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
254 settings = self.get_settings()
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
255 with self._connect() as conn:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
256 rows = conn.execute(
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
257 """
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
258 SELECT source, sentiment_score, engagement, created_at
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
259 FROM evidence
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
260 WHERE symbol = ?
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
261 ORDER BY id DESC
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
262 LIMIT 100
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
263 """,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
264 (symbol,),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
265 ).fetchall()
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
266
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
267 evidence_count = len(rows)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
268 source_count = len({row["source"] for row in rows})
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
269 weighted_total = 0.0
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
270 weight_sum = 0.0
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
271 for row in rows:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
272 engagement_weight = min(5.0, 1.0 + max(0.0, float(row["engagement"])) / 100.0)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
273 weighted_total += float(row["sentiment_score"]) * engagement_weight
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
274 weight_sum += engagement_weight
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
275 sentiment_score = weighted_total / weight_sum if weight_sum else 0.0
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
276 confidence = compute_confidence(sentiment_score, evidence_count, source_count)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
277 action = decide_signal_action(symbol, sentiment_score, confidence, evidence_count, source_count, settings)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
278 summary = summarize_signal(symbol, sentiment_score, confidence, evidence_count, source_count, action)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
279 updated_at = int(time.time())
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
280
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
281 conn.execute(
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 INSERT INTO signals(
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
284 symbol, sentiment_score, confidence, evidence_count, source_count,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
285 action, summary, updated_at
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 VALUES (?, ?, ?, ?, ?, ?, ?, ?)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
288 ON CONFLICT(symbol) DO UPDATE SET
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
289 sentiment_score = excluded.sentiment_score,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
290 confidence = excluded.confidence,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
291 evidence_count = excluded.evidence_count,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
292 source_count = excluded.source_count,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
293 action = excluded.action,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
294 summary = excluded.summary,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
295 updated_at = excluded.updated_at
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
296 """,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
297 (
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
298 symbol,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
299 sentiment_score,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
300 confidence,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
301 evidence_count,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
302 source_count,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
303 action,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
304 summary,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
305 updated_at,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
306 ),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
307 )
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
308 conn.commit()
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 return {
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
311 "symbol": symbol,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
312 "sentiment_score": round(sentiment_score, 4),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
313 "confidence": round(confidence, 4),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
314 "evidence_count": evidence_count,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
315 "source_count": source_count,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
316 "action": action,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
317 "summary": summary,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
318 "updated_at": updated_at,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
319 }
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
320
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
321 def list_signals(self) -> list[dict[str, Any]]:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
322 with self._connect() as conn:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
323 rows = conn.execute(
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
324 """
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
325 SELECT symbol, sentiment_score, confidence, evidence_count, source_count,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
326 action, summary, updated_at
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
327 FROM signals
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
328 ORDER BY confidence DESC, updated_at DESC
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
329 """
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
330 ).fetchall()
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
331 return [dict(row) for row in rows]
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
332
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
333 def add_paper_trade(self, payload: dict[str, Any]) -> dict[str, Any]:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
334 symbol = normalize_symbol(str(payload.get("symbol", "")))
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
335 action = str(payload.get("action", "")).upper()
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
336 quantity = float(payload.get("quantity", 0))
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
337 price = float(payload.get("price", 0))
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
338 reason = str(payload.get("reason", "manual paper trade")).strip()
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
339
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
340 if action not in {"BUY", "SELL"}:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
341 raise ValueError("action must be BUY or SELL")
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
342 if not symbol:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
343 raise ValueError("symbol is required")
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
344 if quantity <= 0:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
345 raise ValueError("quantity must be greater than zero")
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
346 if price <= 0:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
347 raise ValueError("price must be greater than zero")
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
348
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
349 settings = self.get_settings()
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
350 notional = quantity * price
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
351 status = "accepted"
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
352 if notional > float(settings["max_trade_dollars"]):
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
353 status = "rejected_max_trade_dollars"
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
354 elif symbol in {normalize_symbol(s) for s in settings["blocklist"]}:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
355 status = "rejected_blocklist"
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
356
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
357 created_at = int(time.time())
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
358 with self._connect() as conn:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
359 cursor = conn.execute(
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
360 """
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
361 INSERT INTO paper_trades(symbol, action, quantity, price, notional, status, reason, created_at)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
362 VALUES (?, ?, ?, ?, ?, ?, ?, ?)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
363 """,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
364 (symbol, action, quantity, price, notional, status, reason, created_at),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
365 )
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
366 trade_id = int(cursor.lastrowid)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
367 conn.commit()
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
368
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
369 result = {
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
370 "id": trade_id,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
371 "symbol": symbol,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
372 "action": action,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
373 "quantity": quantity,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
374 "price": price,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
375 "notional": notional,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
376 "status": status,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
377 "reason": reason,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
378 "created_at": created_at,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
379 }
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
380 self.add_audit("paper_trade.created", f"Paper trade {status}: {action} {quantity} {symbol}", result)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
381 return result
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
382
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
383 def list_paper_trades(self, limit: int = 100) -> list[dict[str, Any]]:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
384 with self._connect() as conn:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
385 rows = conn.execute(
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
386 """
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
387 SELECT id, symbol, action, quantity, price, notional, status, reason, created_at
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
388 FROM paper_trades
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
389 ORDER BY id DESC
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
390 LIMIT ?
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
391 """,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
392 (limit,),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
393 ).fetchall()
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
394 return [dict(row) for row in rows]
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
395
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
396 def add_audit(self, event_type: str, message: str, payload: dict[str, Any] | None = None) -> None:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
397 with self._connect() as conn:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
398 conn.execute(
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
399 """
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
400 INSERT INTO audit(event_type, message, payload_json, created_at)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
401 VALUES (?, ?, ?, ?)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
402 """,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
403 (event_type, message, json.dumps(payload or {}, sort_keys=True), int(time.time())),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
404 )
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
405 conn.commit()
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
406
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
407 def list_audit(self, limit: int = 100) -> list[dict[str, Any]]:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
408 with self._connect() as conn:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
409 rows = conn.execute(
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
410 """
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
411 SELECT id, event_type, message, payload_json, created_at
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
412 FROM audit
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
413 ORDER BY id DESC
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
414 LIMIT ?
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
415 """,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
416 (limit,),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
417 ).fetchall()
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
418 events = []
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
419 for row in rows:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
420 event = dict(row)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
421 event["payload"] = json.loads(event.pop("payload_json"))
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
422 events.append(event)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
423 return events
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
424
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
425 def _init_db(self) -> None:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
426 with self._connect() as conn:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
427 conn.executescript(
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
428 """
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
429 CREATE TABLE IF NOT EXISTS settings (
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
430 key TEXT PRIMARY KEY,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
431 value_json TEXT NOT NULL
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
432 );
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
433
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
434 CREATE TABLE IF NOT EXISTS evidence (
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
435 id INTEGER PRIMARY KEY AUTOINCREMENT,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
436 source TEXT NOT NULL,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
437 symbol TEXT NOT NULL,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
438 url TEXT,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
439 text TEXT NOT NULL,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
440 engagement REAL NOT NULL DEFAULT 0,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
441 sentiment_score REAL NOT NULL,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
442 raw_json TEXT NOT NULL DEFAULT '{}',
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
443 created_at INTEGER NOT NULL
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
444 );
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
445
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
446 CREATE INDEX IF NOT EXISTS idx_evidence_symbol_created
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
447 ON evidence(symbol, created_at);
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
448
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
449 CREATE TABLE IF NOT EXISTS signals (
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
450 symbol TEXT PRIMARY KEY,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
451 sentiment_score REAL NOT NULL,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
452 confidence REAL NOT NULL,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
453 evidence_count INTEGER NOT NULL,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
454 source_count INTEGER NOT NULL,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
455 action TEXT NOT NULL,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
456 summary TEXT NOT NULL,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
457 updated_at INTEGER NOT NULL
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
458 );
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
459
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
460 CREATE TABLE IF NOT EXISTS paper_trades (
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
461 id INTEGER PRIMARY KEY AUTOINCREMENT,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
462 symbol TEXT NOT NULL,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
463 action TEXT NOT NULL,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
464 quantity REAL NOT NULL,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
465 price REAL NOT NULL,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
466 notional REAL NOT NULL,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
467 status TEXT NOT NULL,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
468 reason TEXT NOT NULL,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
469 created_at INTEGER NOT NULL
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
470 );
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
471
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
472 CREATE TABLE IF NOT EXISTS audit (
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
473 id INTEGER PRIMARY KEY AUTOINCREMENT,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
474 event_type TEXT NOT NULL,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
475 message TEXT NOT NULL,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
476 payload_json TEXT NOT NULL,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
477 created_at INTEGER NOT NULL
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
478 );
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
479 """
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
480 )
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
481 for key, value in DEFAULT_SETTINGS.items():
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
482 conn.execute(
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
483 "INSERT OR IGNORE INTO settings(key, value_json) VALUES (?, ?)",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
484 (key, json.dumps(value, sort_keys=True)),
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
485 )
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
486 conn.commit()
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
487
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
488 def _connect(self) -> sqlite3.Connection:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
489 conn = sqlite3.connect(self.db_path)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
490 conn.row_factory = sqlite3.Row
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
491 return conn
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
492
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
493 @staticmethod
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
494 def _count(conn: sqlite3.Connection, table: str) -> int:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
495 return int(conn.execute(f"SELECT COUNT(*) FROM {table}").fetchone()[0])
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
496
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
497 @staticmethod
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
498 def _validate_settings(settings: dict[str, Any]) -> None:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
499 positive_numbers = [
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
500 "profit_target_pct",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
501 "stop_loss_pct",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
502 "max_trade_dollars",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
503 "max_account_pct",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
504 "max_daily_loss_dollars",
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
505 ]
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
506 for key in positive_numbers:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
507 if float(settings[key]) <= 0:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
508 raise ValueError(f"{key} must be greater than zero")
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
509 if not 0 <= float(settings["min_confidence"]) <= 1:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
510 raise ValueError("min_confidence must be between 0 and 1")
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
511 if int(settings["min_evidence_count"]) < 1:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
512 raise ValueError("min_evidence_count must be at least 1")
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
513 if int(settings["min_source_count"]) < 1:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
514 raise ValueError("min_source_count must be at least 1")
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
515 if bool(settings["live_trading_enabled"]):
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
516 raise ValueError("live_trading_enabled cannot be enabled from the dashboard")
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
517
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
518
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
519 def normalize_symbol(value: str) -> str:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
520 symbol = value.strip().upper().lstrip("$")
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
521 if not re.fullmatch(r"[A-Z]{1,5}", symbol):
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
522 raise ValueError("symbol must be 1-5 letters")
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
523 return symbol
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
524
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
525
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
526 def extract_symbol(text: str) -> str | None:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
527 for match in SYMBOL_RE.finditer(text.upper()):
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
528 symbol = match.group(1)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
529 if symbol not in COMMON_WORDS:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
530 return symbol
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
531 return None
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
532
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
533
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
534 def score_sentiment(text: str) -> float:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
535 words = re.findall(r"[a-zA-Z']+", text.lower())
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
536 positive = sum(1 for word in words if word in POSITIVE_WORDS)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
537 negative = sum(1 for word in words if word in NEGATIVE_WORDS)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
538 total = positive + negative
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
539 if total == 0:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
540 return 0.0
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
541 return max(-1.0, min(1.0, (positive - negative) / total))
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
542
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
543
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
544 def compute_confidence(sentiment_score: float, evidence_count: int, source_count: int) -> float:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
545 evidence_component = min(0.35, evidence_count * 0.07)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
546 source_component = min(0.25, source_count * 0.10)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
547 sentiment_component = min(0.20, abs(sentiment_score) * 0.20)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
548 return min(0.95, 0.20 + evidence_component + source_component + sentiment_component)
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
549
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
550
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
551 def decide_signal_action(
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
552 symbol: str,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
553 sentiment_score: float,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
554 confidence: float,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
555 evidence_count: int,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
556 source_count: int,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
557 settings: dict[str, Any],
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
558 ) -> str:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
559 blocklist = {normalize_symbol(item) for item in settings.get("blocklist", [])}
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
560 allowlist = {normalize_symbol(item) for item in settings.get("allowlist", [])}
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
561 if symbol in blocklist:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
562 return "NO_TRADE_BLOCKED"
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
563 if allowlist and symbol not in allowlist:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
564 return "NO_TRADE_NOT_ALLOWLISTED"
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
565 if evidence_count < int(settings["min_evidence_count"]):
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
566 return "NO_TRADE_NEEDS_EVIDENCE"
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
567 if source_count < int(settings["min_source_count"]):
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
568 return "NO_TRADE_NEEDS_SOURCE_DIVERSITY"
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
569 if confidence < float(settings["min_confidence"]):
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
570 return "NO_TRADE_LOW_CONFIDENCE"
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
571 if sentiment_score >= 0.25:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
572 return "CONSIDER_BUY"
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
573 if sentiment_score <= -0.25:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
574 return "CONSIDER_SELL"
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
575 return "WATCH"
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
576
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
577
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
578 def summarize_signal(
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
579 symbol: str,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
580 sentiment_score: float,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
581 confidence: float,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
582 evidence_count: int,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
583 source_count: int,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
584 action: str,
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
585 ) -> str:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
586 direction = "positive" if sentiment_score > 0 else "negative" if sentiment_score < 0 else "neutral"
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
587 return (
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
588 f"{symbol} has {direction} social sentiment from {evidence_count} evidence item(s) "
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
589 f"across {source_count} source(s). Confidence is {confidence:.0%}. Action: {action}."
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
590 )
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
591
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
592
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
593 def get_config_if_available() -> SchwabConfig | None:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
594 try:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
595 return SchwabConfig.from_env()
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
596 except Exception:
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
597 return None
eb8b4230fdb9 [schwab-trader] Add guarded trading experiment
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
598