diff --git a/backend/routers/wavelet.py b/backend/routers/wavelet.py index ba4bff5..7e4abf3 100644 --- a/backend/routers/wavelet.py +++ b/backend/routers/wavelet.py @@ -302,5 +302,5 @@ def remove_simulation(sim_id: str): @router.get("/watchlist-signals") def watchlist_signals(): - from services.database import get_latest_wavelet_signals - return {"signals": get_latest_wavelet_signals()} + from services.database import get_latest_wavelet_state_by_instrument + return {"signals": get_latest_wavelet_state_by_instrument()} diff --git a/backend/services/database.py b/backend/services/database.py index 41aca5e..7cd7273 100644 --- a/backend/services/database.py +++ b/backend/services/database.py @@ -3622,6 +3622,36 @@ def get_latest_wavelet_signals() -> List[Dict]: return [dict(r) for r in rows] +def get_latest_wavelet_state_by_instrument() -> List[Dict]: + """One row per Watchlist instrument from the most recent scan — a fired signal if this + run had one for it (any band), otherwise the fastest band's current slope/direction, so + every scanned instrument shows *something* here instead of only the ones with an active + trigger this run. get_latest_wavelet_signals() (fired-only) stays as-is for any other + caller; this is specifically for the Dashboard's Wavelets Signal card, which should + reflect the latest state per instrument, not just a list of firings.""" + run_id = _latest_wavelet_run_id() + if not run_id: + return [] + conn = get_conn() + rows = conn.execute( + "SELECT * FROM wavelet_watchlist_signals WHERE run_id=? AND band_label != 'ridge' " + "ORDER BY ticker, (signal_kind IS NULL), period_low_days ASC", + (run_id,), + ).fetchall() + conn.close() + by_ticker: Dict[str, Dict] = {} + for r in rows: + d = dict(r) + by_ticker.setdefault(d["ticker"], d) + out = list(by_ticker.values()) + # No fired signal this run for this band -> direction is null; fall back to the band's + # own slope sign so the card can still show an up/down arrow instead of a blank one. + for d in out: + if d.get("direction") is None and d.get("slope") is not None: + d["direction"] = "up" if d["slope"] > 0 else "down" + return out + + def get_latest_wavelet_state() -> List[Dict]: """Every (ticker, band) row from the most recent cycle scan — signal or not. Used for the rich AI chat context block (slope/energy/ridge state).""" diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx index 0306802..01bd5cd 100644 --- a/frontend/src/pages/Dashboard.tsx +++ b/frontend/src/pages/Dashboard.tsx @@ -1056,8 +1056,8 @@ export default function Dashboard() { {signals.length > 0 ? ( -
- {signals.slice(0, 5).map((s: any, i: number) => ( +
+ {signals.map((s: any, i: number) => (
quickAddInstrument.mutate(s.ticker, { @@ -1073,7 +1073,7 @@ export default function Dashboard() { {nameByTicker[s.ticker] || s.ticker} - {s.band_label} · {s.signal_kind} + {s.band_label} · {s.signal_kind ?? 'veille'} {s.computed_at && {s.computed_at.slice(0, 10)}}
))}