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:
@@ -393,6 +393,7 @@ def score_patterns_with_context(
|
||||
fred_block: str = "",
|
||||
price_discovery_block: str = "",
|
||||
portfolio_context_block: str = "",
|
||||
institutional_block: str = "",
|
||||
run_id: str = "",
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Score all patterns with rich context (news, prices, IV, risk clusters) using GPT-4o."""
|
||||
@@ -616,6 +617,7 @@ Scoring instructions:
|
||||
_fred_sc_section = f"\n{fred_block}\n" if fred_block else ""
|
||||
_pd_sc_section = f"\n{price_discovery_block}\n" if price_discovery_block else ""
|
||||
_portfolio_sc_section = f"\n{portfolio_context_block}\n" if portfolio_context_block else ""
|
||||
_inst_sc_section = f"\n{institutional_block}\n" if institutional_block else ""
|
||||
|
||||
user = f"""GLOBAL CONTEXT:
|
||||
- Geopolitical risk score: {geo_score.get('score', 50)}/100 ({geo_score.get('level', 'medium')})
|
||||
@@ -625,6 +627,7 @@ Scoring instructions:
|
||||
{_fred_sc_section}
|
||||
{_pd_sc_section}
|
||||
{_tech_sc_section}
|
||||
{_inst_sc_section}
|
||||
{_portfolio_sc_section}
|
||||
SCORING TEMPLATE:
|
||||
{scoring_template}
|
||||
@@ -1194,6 +1197,51 @@ def _build_temporal_news_block(partitioned: Dict[str, List], cycle_meta: Dict) -
|
||||
return block
|
||||
|
||||
|
||||
def build_institutional_block(days: int = 7) -> str:
|
||||
"""Build a concise institutional reports block for injection into AI prompts."""
|
||||
try:
|
||||
from services.database import get_conn
|
||||
conn = get_conn()
|
||||
try:
|
||||
from datetime import datetime as _dt, timedelta as _td
|
||||
cutoff = (_dt.utcnow() - _td(days=days)).strftime("%Y-%m-%d")
|
||||
rows = conn.execute(
|
||||
"SELECT report_type, report_date, key_points_json, trading_implications, "
|
||||
"signal_energy, signal_metals, signal_indices, signal_forex, importance "
|
||||
"FROM institutional_reports WHERE report_date >= ? ORDER BY report_date DESC LIMIT 6",
|
||||
(cutoff,),
|
||||
).fetchall()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
if not rows:
|
||||
return ""
|
||||
|
||||
import json as _json
|
||||
lines = ["## INSTITUTIONAL REPORTS (CFTC COT + EIA — last 7 days)"]
|
||||
for r in rows:
|
||||
rtype = r["report_type"].upper()
|
||||
rdate = r["report_date"]
|
||||
importance_str = "★★★" if r["importance"] == 3 else "★★" if r["importance"] == 2 else "★"
|
||||
signals = (
|
||||
f"Energy={r['signal_energy']} | Metals={r['signal_metals']} | "
|
||||
f"Indices={r['signal_indices']} | Forex={r['signal_forex']}"
|
||||
)
|
||||
lines.append(f"\n### {rtype} {rdate} {importance_str} — {signals}")
|
||||
try:
|
||||
kps = _json.loads(r["key_points_json"] or "[]")
|
||||
for kp in kps[:4]:
|
||||
lines.append(f" • {kp}")
|
||||
except Exception:
|
||||
pass
|
||||
if r["trading_implications"]:
|
||||
lines.append(f" → Implications: {r['trading_implications'][:200]}")
|
||||
|
||||
return "\n".join(lines)
|
||||
except Exception as _e:
|
||||
return ""
|
||||
|
||||
|
||||
def suggest_patterns_from_market_context(
|
||||
news: List[Dict],
|
||||
quotes_by_class: Dict[str, List[Dict]],
|
||||
|
||||
Reference in New Issue
Block a user