feat: Phase 1 — IV Rank, Term Structure, Skew, Options Flow (Sprint 1.1/1.2/1.3)
Backend: - iv_engine.py: ATM IV, term structure (30/60/90/180j), put/call skew, options flow (P/C OI ratio, unusual strikes, gamma bias), proxy map for futures→ETFs - database.py: iv_history table + save_iv_snapshot, get_iv_rank_percentile, get_iv_history - routers/options_vol.py: /api/options-vol/ endpoints (snapshot, batch, watchlist, history) - auto_cycle.py: inject IV context string into scoring prompt (step 3.5) - ai_analyzer.py: score_patterns_with_context accepts iv_context param - main.py: register options_vol router Frontend: - pages/OptionsLab.tsx: full IV dashboard (watchlist by IVR, term structure, skew, flow, sparkline) - pages/JournalDeBord.tsx: IvRankCell component + IV Rank column per trade - hooks/useApi.ts: useIvSnapshot, useIvWatchlist, useIvBatch, useIvHistory, useIvForTrade Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -231,6 +231,25 @@ def run_cycle_once(trigger: str = "auto") -> Dict[str, Any]:
|
||||
|
||||
summary["patterns_added"] = added_count
|
||||
|
||||
# ── Step 3.5: Collect IV context ─────────────────────────────────────
|
||||
iv_context = ""
|
||||
try:
|
||||
from services.iv_engine import get_iv_context_for_prompt, IV_WATCHLIST
|
||||
# Collect underlyings from current trade journal + default watchlist
|
||||
from services.database import get_mtm_trades_with_traces
|
||||
_mtm = get_mtm_trades_with_traces(days=90)
|
||||
_trade_tickers = list({
|
||||
(t.get("underlying") or "").upper()
|
||||
for t in _mtm.get("all_trades", [])
|
||||
if t.get("underlying")
|
||||
})
|
||||
_iv_tickers = (_trade_tickers + IV_WATCHLIST[:6])[:10]
|
||||
iv_context = get_iv_context_for_prompt(_iv_tickers)
|
||||
if iv_context:
|
||||
logger.info(f"[Cycle {run_id[:16]}] IV context collected for {len(_iv_tickers)} tickers")
|
||||
except Exception as _e:
|
||||
logger.warning(f"[Cycle] IV context collection failed (non-blocking): {_e}")
|
||||
|
||||
# ── Step 4: Score ALL patterns ────────────────────────────────────────
|
||||
# Verify all patterns have IDs before scoring (guard against stale data)
|
||||
patterns_with_id = [p for p in existing if p.get("id")]
|
||||
@@ -251,6 +270,7 @@ def run_cycle_once(trigger: str = "auto") -> Dict[str, Any]:
|
||||
category_filter=None,
|
||||
macro_regime=macro_regime,
|
||||
portfolio_lessons=portfolio_lessons,
|
||||
iv_context=iv_context,
|
||||
)
|
||||
scored_with_id = [s for s in scored if s.get("pattern_id")]
|
||||
scored_without_id = [s for s in scored if not s.get("pattern_id")]
|
||||
|
||||
Reference in New Issue
Block a user