fix: backtest starvation — schedule the labeler nightly, make inserts idempotent

Dashboard backtest sources sessions from setup_events v1, which only the
labeler writes — and the labeler was never in the nightly scheduler, so
setups (and therefore backtests) froze at the last manual run (07-17).
Root blocker was _insert crashing on duplicate setup_ids on any re-run;
now ON CONFLICT DO NOTHING, so incremental nightly builds are safe.
Scheduler gains 'labeler build' + 'build-tod1' after eod_replay in both
compose files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
momentum devandClaude Fable 5 committed 2026-07-22 06:51:56 -05:00
1 parent 5f33799a9a
commit ed176b3712
3 files changed
+22 -2

No files matched your search

+1 -1
View File
@@ -569,7 +569,7 @@ services:
command: command:
- /bin/sh - /bin/sh
- -c - -c
- "echo \"[scheduler] up; fires 20:00 ET Mon-Fri\"\nwhile true; do\n if [ \"$$(date +%H:%M)\" = \"20:00\" ] && [ \"$$(date +%u)\" -le 5 ]; then\n python -m research.premarket_eval || echo \"[scheduler] eval FAILED\"\n python -m research.ingestion.market_context || echo \"[scheduler] market_context FAILED\"\n python -m research.eod_reconcile || echo \"[scheduler] reconcile FAILED\"\n python eod_replay.py || echo \"[scheduler] eod_replay FAILED\"\n python nightly_tune.py || echo \"[scheduler] nightly_tune FAILED\"\n python -m research.regime build-features || echo \"[scheduler] regime build FAILED\"\n python -m research.regime score --model threshold_v1 || echo \"[scheduler] regime threshold FAILED\"\n python -m research.regime score --model meta_v1 || echo \"[scheduler] regime meta FAILED\"\n python -m research.experiments run || echo \"[scheduler] experiments run FAILED\"\n python -m research.experiments adjudicate || echo \"[scheduler] experiments adjudicate FAILED\"\n sleep 120\n fi\n sleep 20\ndone\n" - "echo \"[scheduler] up; fires 20:00 ET Mon-Fri\"\nwhile true; do\n if [ \"$$(date +%H:%M)\" = \"20:00\" ] && [ \"$$(date +%u)\" -le 5 ]; then\n python -m research.premarket_eval || echo \"[scheduler] eval FAILED\"\n python -m research.ingestion.market_context || echo \"[scheduler] market_context FAILED\"\n python -m research.eod_reconcile || echo \"[scheduler] reconcile FAILED\"\n python eod_replay.py || echo \"[scheduler] eod_replay FAILED\"\n python -m research.labeler build || echo \"[scheduler] labeler build FAILED\"\n python -m research.labeler build-tod1 || echo \"[scheduler] labeler build-tod1 FAILED\"\n python nightly_tune.py || echo \"[scheduler] nightly_tune FAILED\"\n python -m research.regime build-features || echo \"[scheduler] regime build FAILED\"\n python -m research.regime score --model threshold_v1 || echo \"[scheduler] regime threshold FAILED\"\n python -m research.regime score --model meta_v1 || echo \"[scheduler] regime meta FAILED\"\n python -m research.experiments run || echo \"[scheduler] experiments run FAILED\"\n python -m research.experiments adjudicate || echo \"[scheduler] experiments adjudicate FAILED\"\n sleep 120\n fi\n sleep 20\ndone\n"
environment: environment:
ALPACA_API_KEY: <FILL-ME-ALPACA-KEY> ALPACA_API_KEY: <FILL-ME-ALPACA-KEY>
ALPACA_BACKFILL_FEED: sip ALPACA_BACKFILL_FEED: sip
+4 -1
View File
@@ -443,9 +443,12 @@ def compute_setup(symbol, session_date, df, prior_close, avg_vol, cats: dict,
def _insert(con, table, row): def _insert(con, table, row):
# ON CONFLICT DO NOTHING: builds are re-runnable (the nightly scheduler calls
# them incrementally); already-built setup_ids are skipped, not crashed on.
cols = list(row.keys()) cols = list(row.keys())
con.execute(f"INSERT INTO {table} ({', '.join(cols)}) VALUES " con.execute(f"INSERT INTO {table} ({', '.join(cols)}) VALUES "
f"({', '.join(['?'] * len(cols))})", [row[c] for c in cols]) f"({', '.join(['?'] * len(cols))}) ON CONFLICT DO NOTHING",
[row[c] for c in cols])
def build(con, limit=None, rebuild=False): def build(con, limit=None, rebuild=False):
+17
View File
@@ -237,3 +237,20 @@ def test_tod1_silent_without_volume_explosion():
df = _tod1_bars(base_vol=300) # 300/min vs med 100k -> tod << 50 df = _tod1_bars(base_vol=300) # 300/min vs med 100k -> tod << 50
assert compute_tod1_setups("QUIET", D, df, prior_close=2.00, assert compute_tod1_setups("QUIET", D, df, prior_close=2.00,
med_vol=100_000, cats={}) == [] med_vol=100_000, cats={}) == []
def test_insert_is_idempotent_on_setup_id():
"""Nightly incremental builds re-scan history — a duplicate setup_id must be
skipped (ON CONFLICT DO NOTHING), not crash the whole build."""
from storage.event_store import EventStore
from research.labeler import _insert
s = EventStore(":memory:")
con = s.con
row = dict(setup_id="X:2026-06-01:v9", symbol="X", setup_time=datetime(2026, 6, 1, 9, 40),
session_date=date(2026, 6, 1), setup_name="t", setup_version="v9",
entry_reference_price=1.0, invalidation_price=0.9)
_insert(con, "setup_events", row)
_insert(con, "setup_events", row) # duplicate: must not raise
n = con.execute("SELECT count(*) FROM setup_events WHERE setup_version='v9'").fetchone()[0]
assert n == 1
s.close()