feat: wavelets
This commit is contained in:
@@ -3623,32 +3623,42 @@ def get_latest_wavelet_signals() -> List[Dict]:
|
||||
|
||||
|
||||
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 []
|
||||
"""One row per Watchlist instrument, always — its own most recently scanned band state
|
||||
(a fired signal if that scan had one, otherwise the fastest band's slope/direction), or
|
||||
a data=None placeholder if it's never been scanned successfully at all. Looked up
|
||||
independently per ticker (its own MAX(computed_at)) rather than pinned to one shared
|
||||
run_id, so an instrument that failed the latest scan (e.g. a transient Saxo hiccup)
|
||||
still shows its last known state instead of silently disappearing from the list, and
|
||||
self-heals the next time it scans successfully. Feeds the Dashboard's Wavelets Signal
|
||||
card, which should mirror the Watchlist exactly — every instrument represented, not
|
||||
just the ones with fresh data this run."""
|
||||
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,),
|
||||
"SELECT w.* FROM wavelet_watchlist_signals w "
|
||||
"JOIN (SELECT ticker, MAX(computed_at) AS max_computed_at FROM wavelet_watchlist_signals "
|
||||
" WHERE band_label != 'ridge' GROUP BY ticker) w2 "
|
||||
" ON w.ticker = w2.ticker AND w.computed_at = w2.max_computed_at "
|
||||
"WHERE w.band_label != 'ridge' "
|
||||
"ORDER BY w.ticker, (w.signal_kind IS NULL), w.period_low_days ASC"
|
||||
).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:
|
||||
# No fired signal 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 by_ticker.values():
|
||||
if d.get("direction") is None and d.get("slope") is not None:
|
||||
d["direction"] = "up" if d["slope"] > 0 else "down"
|
||||
|
||||
out: List[Dict] = []
|
||||
for item in get_instruments_watchlist():
|
||||
ticker = item["ticker"].upper()
|
||||
out.append(by_ticker.get(ticker) or {
|
||||
"ticker": ticker, "band_label": None, "signal_kind": None, "direction": None,
|
||||
"computed_at": None, "slope": None, "value": None, "energy": None,
|
||||
})
|
||||
return out
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user