feat(replay): R0 snapshot recorder — archivo por ciclo en signals #15

Merged
chemavx merged 1 commits from feat/replay-r0-recorder into main 2026-07-02 12:16:13 +00:00
Owner

Contexto

Primera PR del motor de replay (línea 2.1 del plan de fase). Hallazgo que la motiva: las tablas signals y markets existen en el schema desde Fase 2/5 pero nunca tuvieron escritor (0 filas en prod). Sin archivo de decisiones no hay replay — y cada día sin recorder es muestra perdida.

Cambios

  • signals: 1 fila por mercado evaluado y ciclo, con INPUTS (news_sentiment, feat_*_lo, volume_24h, days_to_resolution) además de los outputs ya definidos (probs, edges, gates). skip_reason granular: unsupported/no_signals/prior_extreme/family/edge_net/confidence/reentry_guard. news_budget_skipped distingue "GNews no consultado" (budget 5/ciclo) de "sin noticias" — sin esto el replay de politics leería la inanición de budget como ausencia de señal.
  • ext_snapshots (nueva): 1 fila/ciclo con el snapshot de ExternalSignals; las filas de signals se unen por cycle_ts.
  • markets: upsert de metadatos por ciclo (el replay reconstruye Market desde aquí).
  • Retención: prune diario de filas > SIGNALS_RETENTION_DAYS (90 por defecto). Volumen estimado ~50 MB/mes.
  • SIGNAL_RECORDER_ENABLED (default true) gatea las escrituras; todas van en try/except — el recorder no puede romper el trading.
  • acted_on=true solo cuando la señal acabó en trade ejecutado (evaluate puede emitir señal que el risk sizing rechace).

Cumplimiento del freeze

Cambios en bayesian.py puramente aditivos (acumulación de registros en cada punto de salida de evaluate()). Cero cambios en pesos, umbrales, gates, exposure, sizing o flags de Manifold.

Validación

  • 85/85 tests (10 nuevos en tests/test_signal_recorder.py).
  • Migración validada contra el postgres de prod en transacción con ROLLBACK (idempotente, IF NOT EXISTS).

🤖 Generated with Claude Code

## Contexto Primera PR del motor de replay (línea 2.1 del plan de fase). Hallazgo que la motiva: las tablas `signals` y `markets` existen en el schema desde Fase 2/5 pero **nunca tuvieron escritor** (0 filas en prod). Sin archivo de decisiones no hay replay — y cada día sin recorder es muestra perdida. ## Cambios - **`signals`**: 1 fila por mercado evaluado y ciclo, con INPUTS (news_sentiment, feat_*_lo, volume_24h, days_to_resolution) además de los outputs ya definidos (probs, edges, gates). `skip_reason` granular: `unsupported/no_signals/prior_extreme/family/edge_net/confidence/reentry_guard`. `news_budget_skipped` distingue "GNews no consultado" (budget 5/ciclo) de "sin noticias" — sin esto el replay de politics leería la inanición de budget como ausencia de señal. - **`ext_snapshots`** (nueva): 1 fila/ciclo con el snapshot de ExternalSignals; las filas de signals se unen por `cycle_ts`. - **`markets`**: upsert de metadatos por ciclo (el replay reconstruye `Market` desde aquí). - **Retención**: prune diario de filas > `SIGNALS_RETENTION_DAYS` (90 por defecto). Volumen estimado ~50 MB/mes. - **`SIGNAL_RECORDER_ENABLED`** (default true) gatea las escrituras; todas van en try/except — el recorder no puede romper el trading. - `acted_on=true` solo cuando la señal acabó en trade ejecutado (evaluate puede emitir señal que el risk sizing rechace). ## Cumplimiento del freeze Cambios en `bayesian.py` puramente aditivos (acumulación de registros en cada punto de salida de `evaluate()`). Cero cambios en pesos, umbrales, gates, exposure, sizing o flags de Manifold. ## Validación - 85/85 tests (10 nuevos en `tests/test_signal_recorder.py`). - Migración validada contra el postgres de prod en transacción con ROLLBACK (idempotente, `IF NOT EXISTS`). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
chemavx added 1 commit 2026-07-02 08:52:31 +00:00
The signals and markets tables existed since Phase 2/5 but never had a
writer; the replay engine (phase plan line 2.1) needs a per-(market, cycle)
archive of what the strategy saw and decided. This wires them up:

- signals: one row per evaluated market per cycle, now carrying INPUTS
  (news_sentiment, feat_*_lo, volume_24h, days_to_resolution) plus the
  existing outputs (probs, edges, gates, skip_reason). skip_reason is
  granular: unsupported/no_signals/prior_extreme/family/edge_net/
  confidence/reentry_guard. news_budget_skipped distinguishes "GNews not
  asked" (5-query budget) from "no news".
- ext_snapshots: one row per cycle with the ExternalSignals snapshot;
  signals rows join on cycle_ts.
- markets: metadata upserted each cycle (replay rebuilds Market from it).
- Retention: prune > SIGNALS_RETENTION_DAYS (default 90) once a day.
- SIGNAL_RECORDER_ENABLED (default true) gates all DB writes; every write
  is try/except — the recorder can never break trading.

Strategy changes are purely additive (record accumulation at each exit
path of evaluate()); no weights, thresholds, gates or sizing touched,
per the freeze in the current phase plan.

Tests: 10 new deterministic tests (85 total passing). Schema migration
dry-run validated against prod postgres inside a rolled-back transaction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
chemavx merged commit eb4f67414a into main 2026-07-02 12:16:13 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: chemavx/polymarket-bot#15