feat: institutional reports — CFTC COT + EIA petroleum weekly

- New institutional_reports table (DB) with importance, signals per asset class, key points, absorption tracking
- cot_fetcher.py: CFTC Socrata API (6dca-aqww), 7 instruments (Gold/Silver/Copper/WTI/NatGas/SP500/EURUSD), net positioning + 52-week z-score
- eia_fetcher.py: EIA API v2, 4 series (crude/Cushing/gasoline/distillates), WoW surprise detection
- institutional.py router: GET /reports, GET /reports/{id}, POST /refresh, GET /stats
- institutional_scheduler.py: weekly auto-fetch (COT Saturdays, EIA Wednesday afternoons)
- ai_analyzer.py: build_institutional_block() + institutional_block param injected into AI scoring prompt
- auto_cycle.py: inject institutional block into suggestion + scoring, absorption tracking via keyword overlap after each cycle commentary
- InstitutionalReports.tsx: full page with filter bar (type/category/importance/period), cards with key point bullets, EXTREME alerts highlighted, signal badges, absorption badge, trading implications, expandable detail

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OpenSquared
2026-06-22 13:45:07 +02:00
parent 4423ad91db
commit 3edbd6b0b7
12 changed files with 1228 additions and 2 deletions

View File

@@ -549,6 +549,33 @@ def init_db():
except Exception:
pass
c.execute("""CREATE TABLE IF NOT EXISTS institutional_reports (
id INTEGER PRIMARY KEY AUTOINCREMENT,
report_type TEXT NOT NULL,
report_date TEXT NOT NULL,
fetch_date TEXT NOT NULL DEFAULT (datetime('now')),
title TEXT NOT NULL,
source TEXT NOT NULL DEFAULT '',
importance INTEGER NOT NULL DEFAULT 2,
category TEXT NOT NULL DEFAULT 'multi',
raw_data_json TEXT DEFAULT '{}',
key_points_json TEXT DEFAULT '[]',
trading_implications TEXT DEFAULT '',
signal_energy TEXT DEFAULT 'neutral',
signal_metals TEXT DEFAULT 'neutral',
signal_indices TEXT DEFAULT 'neutral',
signal_forex TEXT DEFAULT 'neutral',
ai_summary TEXT DEFAULT '',
injected_in_cycle_id TEXT DEFAULT NULL,
absorbed_score REAL DEFAULT NULL,
UNIQUE(report_type, report_date)
)""")
try:
c.execute("CREATE INDEX IF NOT EXISTS idx_inst_type_date ON institutional_reports(report_type, report_date DESC)")
c.execute("CREATE INDEX IF NOT EXISTS idx_inst_importance ON institutional_reports(importance DESC, report_date DESC)")
except Exception:
pass
try:
c.execute("CREATE INDEX IF NOT EXISTS idx_kb_category ON knowledge_base(category, status)")
c.execute("CREATE INDEX IF NOT EXISTS idx_rs_version ON reasoning_state(version DESC)")