annotate schwab_trader/dashboard.py @ 268:f7b1188d5fb1

support repo rules Copilot bundle path Match both +_repo_rules2+ and +http_archive+ Copilot CLI repository layouts during deployment and production startup. Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Fri, 07 Aug 2026 13:02:41 -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