fix: weekend-aware cycle — IVGate, pandas MultiIndex, ticker aliases, day/session in AI prompt
- auto_cycle.py: detect weekend/market session, build cycle_meta with day_of_week/is_weekend/market_note; IVGate skips iv_rank>=99 on weekends to avoid artificial weekend option premium cascade; inject portfolio context (open trades + price moves + concentration) before AI scoring; pass portfolio_context_block + run_id to both AI scorer and suggester - ai_analyzer.py: _build_temporal_news_block injects market session banner (WEEKEND warning, pre/after-market note, or open session label) so AI knows markets are closed and defers execution to Monday - iv_engine.py: add WHEAT/EUR/USD ticker aliases; skip saving IV snapshots on weekends to protect history; resolve aliases before slash-format conversion in _resolve_ticker - technical_indicators.py: fix pandas MultiIndex from yfinance>=0.2 (droplevel+squeeze); use period proportional to lookback instead of fixed period=1d - database.py: asset_class ticker-based fallback (_asset_class_from_ticker); one-time backfill migration for all NULL asset_class rows; ai_call_logs table + save/get helpers; normalize_ticker public function Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -48,9 +48,21 @@ IV_WATCHLIST = [
|
||||
]
|
||||
|
||||
|
||||
# Common AI-hallucinated ticker names → canonical Yahoo Finance tickers
|
||||
_TICKER_ALIASES: Dict[str, str] = {
|
||||
"WHEAT": "ZW=F", "CORN_FUTURES": "ZC=F", "SOYBEANS": "ZS=F", "SOYBEAN": "ZS=F",
|
||||
"CRUDE": "CL=F", "OIL": "CL=F", "WTI": "CL=F", "BRENT": "BZ=F",
|
||||
"GOLD": "GC=F", "SILVER": "SI=F", "COPPER": "HG=F",
|
||||
"SP500": "^GSPC", "S&P500": "^GSPC", "NASDAQ": "QQQ",
|
||||
}
|
||||
|
||||
|
||||
def _resolve_ticker(ticker: str) -> str:
|
||||
"""Return the optionable proxy ticker for a given symbol."""
|
||||
t = ticker.upper().strip()
|
||||
# Normalize alias names (WHEAT → ZW=F, etc.)
|
||||
if t in _TICKER_ALIASES:
|
||||
t = _TICKER_ALIASES[t]
|
||||
# Normalize slash-format forex (EUR/USD → EURUSD=X) before proxy lookup
|
||||
if '/' in t:
|
||||
parts = t.split('/')
|
||||
@@ -412,8 +424,8 @@ def get_full_iv_snapshot(ticker: str) -> Dict[str, Any]:
|
||||
|
||||
rank_data: Dict[str, Any] = {}
|
||||
if iv_current:
|
||||
if live_iv:
|
||||
# Only persist to history when we have a fresh live IV
|
||||
if live_iv and date.today().weekday() < 5:
|
||||
# Only persist weekday IV — weekend premium inflates IV and corrupts history
|
||||
save_iv_snapshot(proxy, today, iv_current, term.get("iv_30d"), term.get("iv_60d"), term.get("iv_90d"))
|
||||
rank_data = get_iv_rank_percentile(proxy, iv_current)
|
||||
|
||||
@@ -498,7 +510,9 @@ def get_iv_context_for_prompt(tickers: List[str]) -> str:
|
||||
continue
|
||||
from services.database import get_iv_rank_percentile, save_iv_snapshot
|
||||
today = date.today().isoformat()
|
||||
save_iv_snapshot(proxy, today, iv, None, None, None)
|
||||
# Don't save weekend IV — market premium inflates it, would corrupt history
|
||||
if date.today().weekday() < 5:
|
||||
save_iv_snapshot(proxy, today, iv, None, None, None)
|
||||
rank = get_iv_rank_percentile(proxy, iv)
|
||||
|
||||
iv_rank = rank.get("iv_rank")
|
||||
|
||||
Reference in New Issue
Block a user