feat: 4 remaining institutional reports — Earnings, VX curve, Central Bank RSS, Sentiment
New fetchers (no API keys required): - earnings_fetcher.py: yfinance EPS calendar + surprise tracking for 23 geo-relevant tickers - vx_fetcher.py: VIX term structure (^VIX/^VXV/^VXMT) + CBOE delayed futures, regime detection - central_bank_fetcher.py: Fed + ECB RSS feeds, keyword-based hawkish/dovish classification - sentiment_fetcher.py: CNN Fear & Greed (primary) + NAAIM + AAII (optional fallbacks) Wiring: - institutional_scheduler.py: all 4 now scheduled daily (≥08:00 UTC), deduplicated per day - institutional.py /refresh: all 6 types handled with _run() helper - ai_analyzer.py build_institutional_block(): limit 6→12, generic header text - InstitutionalReports.tsx: 6-type color map, individual refresh buttons, expanded filters Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -151,40 +151,49 @@ def get_report(report_id: int):
|
||||
|
||||
@router.post("/refresh")
|
||||
def refresh_reports(report_type: Optional[str] = Query(None)):
|
||||
"""Trigger a live fetch of COT and/or EIA reports."""
|
||||
"""Trigger a live fetch of any/all institutional report types."""
|
||||
results: Dict[str, str] = {}
|
||||
|
||||
if not report_type or report_type == "cot":
|
||||
def _run(label: str, fetch_fn, *args):
|
||||
try:
|
||||
from services.cot_fetcher import fetch_cot_report
|
||||
report = fetch_cot_report()
|
||||
report = fetch_fn(*args)
|
||||
if report:
|
||||
save_institutional_report(report)
|
||||
results["cot"] = "ok"
|
||||
logger.info(f"[Institutional] COT report saved: {report['report_date']}")
|
||||
results[label] = "ok"
|
||||
logger.info(f"[Institutional] {label} saved: {report['report_date']}")
|
||||
else:
|
||||
results["cot"] = "no_data"
|
||||
results[label] = "no_data"
|
||||
except Exception as e:
|
||||
logger.warning(f"[Institutional] COT refresh failed: {e}")
|
||||
results["cot"] = f"error: {str(e)[:100]}"
|
||||
logger.warning(f"[Institutional] {label} refresh failed: {e}")
|
||||
results[label] = f"error: {str(e)[:100]}"
|
||||
|
||||
if not report_type or report_type == "cot":
|
||||
from services.cot_fetcher import fetch_cot_report
|
||||
_run("cot", fetch_cot_report)
|
||||
|
||||
if not report_type or report_type == "eia":
|
||||
try:
|
||||
eia_key = get_config("eia_api_key") or ""
|
||||
if eia_key:
|
||||
from services.eia_fetcher import fetch_eia_report
|
||||
report = fetch_eia_report(eia_key)
|
||||
if report:
|
||||
save_institutional_report(report)
|
||||
results["eia"] = "ok"
|
||||
logger.info(f"[Institutional] EIA report saved: {report['report_date']}")
|
||||
else:
|
||||
results["eia"] = "no_data"
|
||||
else:
|
||||
results["eia"] = "no_api_key"
|
||||
except Exception as e:
|
||||
logger.warning(f"[Institutional] EIA refresh failed: {e}")
|
||||
results["eia"] = f"error: {str(e)[:100]}"
|
||||
eia_key = get_config("eia_api_key") or ""
|
||||
if eia_key:
|
||||
from services.eia_fetcher import fetch_eia_report
|
||||
_run("eia", fetch_eia_report, eia_key)
|
||||
else:
|
||||
results["eia"] = "no_api_key"
|
||||
|
||||
if not report_type or report_type == "earnings":
|
||||
from services.earnings_fetcher import fetch_earnings_report
|
||||
_run("earnings", fetch_earnings_report)
|
||||
|
||||
if not report_type or report_type == "vx_curve":
|
||||
from services.vx_fetcher import fetch_vx_report
|
||||
_run("vx_curve", fetch_vx_report)
|
||||
|
||||
if not report_type or report_type == "central_bank":
|
||||
from services.central_bank_fetcher import fetch_central_bank_reports
|
||||
_run("central_bank", fetch_central_bank_reports)
|
||||
|
||||
if not report_type or report_type == "sentiment":
|
||||
from services.sentiment_fetcher import fetch_sentiment_report
|
||||
_run("sentiment", fetch_sentiment_report)
|
||||
|
||||
return {"status": "done", "results": results}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user