feat: wavelets

This commit is contained in:
OpenSquared
2026-07-24 20:22:21 +02:00
parent 32f2d27782
commit 67d27eaeb6
5 changed files with 99 additions and 31 deletions

View File

@@ -18,6 +18,45 @@ def _base_ticker(t: str) -> str:
"""Normalize Yahoo Finance tickers for comparison: EURUSD=X → EURUSD, BZ=F → BZ, ^GSPC → GSPC."""
return re.sub(r'(=X|=F|=RR|-USD|\^)$', '', t.strip().upper())
# Reverse of the common "yfinance/futures ticker" an Instrument Analysis id (curated or
# quick-added) uses vs. the Cockpit Watchlist's own friendly ticker for the same underlying
# — needed because _base_ticker() only strips a SUFFIX (EURUSD=X -> EURUSD works since the
# base "EURUSD" already equals the Watchlist ticker), it can't turn "GC=F" into "GOLD" or
# "^GSPC" into "SP500" (nothing in common to strip; ^ is a prefix, not a suffix, so
# _base_ticker doesn't even touch it despite what its own docstring claims). Mirrors
# frontend/src/pages/Dashboard.tsx's UNDERLYING_ALIASES table (kept in sync manually — a
# small, stable list, not worth sharing across a Python/TS boundary).
_WAVELET_UNDERLYING_ALIASES = {
"^GSPC": "SP500", "^NDX": "NASDAQ", "^DJI": "DOW", "^RUT": "RUSSELL2000",
"GC=F": "GOLD", "SI=F": "SILVER", "HG=F": "COPPER", "PL=F": "PLATINUM",
"CL=F": "CRUDE", "BZ=F": "BRENT", "NG=F": "NATGAS",
"ZW=F": "WHEAT", "ZC=F": "CORN", "ZS=F": "SOYBEANS",
}
def resolve_watchlist_ticker(instrument_id: str) -> str:
"""Best-effort map from an Instrument Analysis id to the Cockpit Watchlist ticker it
represents — for looking up data keyed by the Watchlist ticker (wavelet cache in
particular). Tries an exact match, then the suffix-stripped form (EURUSD=X -> EURUSD),
then the alias table above (GC=F -> GOLD) — each checked against the tickers actually
in the Watchlist right now, not just "is this a known alias", so a curated catalog id
that happens to share a root with an alias but isn't Watchlist-linked doesn't falsely
resolve. Falls back to the suffix-stripped form if nothing matches."""
uid = instrument_id.strip().upper()
base = _base_ticker(uid)
from services.database import get_instruments_watchlist
watchlist_tickers = {r["ticker"].upper() for r in get_instruments_watchlist()}
if uid in watchlist_tickers:
return uid
if base in watchlist_tickers:
return base
alias = _WAVELET_UNDERLYING_ALIASES.get(uid) or _WAVELET_UNDERLYING_ALIASES.get(base)
if alias and alias in watchlist_tickers:
return alias
return base
logger = logging.getLogger(__name__)
# ── Config loading ─────────────────────────────────────────────────────────────