feat: Pattern Lab — historical backtest engine for pattern discovery
- Remove all built-in patterns (no proof of legitimacy); seed_builtin_patterns is now a no-op
- DB: add backtest_lab_runs table + backtest_hits/runs_count columns on patterns
- services/pattern_lab.py: build_historical_context (yfinance + RSI/MA200),
run_ai_backtest (GPT-4o as historical analyst), evaluate_outcomes (actual moves at T+horizon)
- routers/pattern_lab.py: POST /run, POST /evaluate/{id}, GET /runs, DELETE /runs/{id},
POST /save-pattern (promotes hit pattern to library with reliability counters)
- PatternLab.tsx: 34 preset events 2015-2025 (macro/geo/credit/fx/commodities/volatility/tech),
3-panel layout — preset selector + wizard + run history, market data table,
AI pattern cards with hit/miss outcome display, Save to Library button
- useApi.ts: usePatternLabRuns, useRunPatternLab, useEvaluatePatternLab, useSaveLabPattern, useDeleteLabRun
- Sidebar + App.tsx: /pattern-lab route + FlaskConical nav link
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -83,12 +83,39 @@ def init_db():
|
||||
"ALTER TABLE custom_patterns ADD COLUMN signal_direction TEXT",
|
||||
# Pattern Explorer — taxonomy tree path
|
||||
"ALTER TABLE custom_patterns ADD COLUMN taxonomy_path TEXT DEFAULT '[]'",
|
||||
# Pattern Lab — backtest reliability tracking
|
||||
"ALTER TABLE custom_patterns ADD COLUMN backtest_hits INTEGER DEFAULT 0",
|
||||
"ALTER TABLE custom_patterns ADD COLUMN backtest_runs_count INTEGER DEFAULT 0",
|
||||
# Remove all built-in patterns (no proof of legitimacy)
|
||||
"DELETE FROM custom_patterns WHERE source = 'builtin'",
|
||||
]:
|
||||
try:
|
||||
c.execute(_sql)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Pattern Lab — historical backtest runs
|
||||
c.execute("""CREATE TABLE IF NOT EXISTS backtest_lab_runs (
|
||||
id TEXT PRIMARY KEY,
|
||||
preset_id TEXT,
|
||||
theme TEXT NOT NULL,
|
||||
analysis_date TEXT NOT NULL,
|
||||
horizon_days INTEGER NOT NULL,
|
||||
assets TEXT NOT NULL,
|
||||
theme_hint TEXT,
|
||||
context_snapshot TEXT,
|
||||
ai_result TEXT,
|
||||
outcome TEXT,
|
||||
status TEXT DEFAULT 'pending',
|
||||
error_msg TEXT,
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
evaluated_at TEXT
|
||||
)""")
|
||||
try:
|
||||
c.execute("CREATE INDEX IF NOT EXISTS idx_blr_date ON backtest_lab_runs(analysis_date DESC)")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Phase 4.2 — Régime clusters (K-Means sur gauges macro)
|
||||
c.execute("""CREATE TABLE IF NOT EXISTS regime_clusters (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
@@ -885,37 +912,8 @@ def update_position_notes(pos_id: str, notes: str):
|
||||
# ── Custom Patterns ────────────────────────────────────────────────────────────
|
||||
|
||||
def seed_builtin_patterns(builtin_patterns: List[Dict[str, Any]]):
|
||||
"""Seed built-in patterns into DB (idempotent). Always updates taxonomy_path."""
|
||||
conn = get_conn()
|
||||
existing = {r[0] for r in conn.execute("SELECT id FROM custom_patterns").fetchall()}
|
||||
for p in builtin_patterns:
|
||||
taxonomy_json = json.dumps(p.get("taxonomy_path", []))
|
||||
if p["id"] not in existing:
|
||||
conn.execute("""INSERT INTO custom_patterns (
|
||||
id, name, description, triggers, keywords, historical_instances,
|
||||
suggested_trades, asset_class, expected_move_pct, probability,
|
||||
horizon_days, source, is_active, taxonomy_path, updated_at
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,'builtin',1,?,datetime('now'))""", (
|
||||
p["id"],
|
||||
p.get("name", ""),
|
||||
p.get("description", ""),
|
||||
json.dumps(p.get("triggers", [])),
|
||||
json.dumps(p.get("keywords", [])),
|
||||
json.dumps(p.get("historical_instances", [])),
|
||||
json.dumps(p.get("suggested_trades", [])),
|
||||
p.get("asset_class", "indices"),
|
||||
p.get("expected_move_pct", 0),
|
||||
p.get("probability", 0.5),
|
||||
p.get("horizon_days", 30),
|
||||
taxonomy_json,
|
||||
))
|
||||
else:
|
||||
conn.execute(
|
||||
"UPDATE custom_patterns SET taxonomy_path=? WHERE id=? AND source='builtin'",
|
||||
(taxonomy_json, p["id"])
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
"""No-op: built-in patterns removed in favour of Pattern Lab backtested patterns."""
|
||||
pass
|
||||
|
||||
|
||||
def save_custom_pattern(pattern: Dict[str, Any]) -> str:
|
||||
|
||||
Reference in New Issue
Block a user