spread_pct P0: real decision-time bid/ask spread as an observe-only metric #3

Merged
fez5587 merged 1 commits from claude/float-recreation-trading-gcn42l into master 2026-06-29 17:48:39 -04:00
fez5587 commented 2026-06-29 17:48:32 -04:00 (Migrated from github.com)

GO_LIVE flags spread_pct as a not-yet-real field. The reason it was null is
structural: OHLCV bars carry no NBBO, so minute_bars.spread_pct can never be
populated from the bars endpoint. The execution-relevant figure is the spread at
the moment a setup is evaluated — a wide book quietly eats R on the thin sub-$20
names this system trades.

This makes that metric real, shadow-first (measure before gating), mirroring how
the catalyst score was introduced:

  • strategy/evaluation/liquidity.py: pure compute_spread_pct(bid, ask) →
    (ask-bid)/mid, or None for missing/crossed/non-positive quotes (None never
    collapses to a real 0.0). Unit-tested.
  • AlpacaPaperClient.get_latest_quotes: batched latest-NBBO fetch, mirrors
    get_latest_trades.
  • Watcher: optional injected spread_provider (same shape as
    catalyst_score_provider); default None == prior behavior byte-for-byte.
    Computed spread is logged onto the criteria_evaluated and signal_ready events.
    Observe-only — it does NOT gate ready/blocked.
  • run_live_paper: TTL-cached spread provider off the client; degrades to None
    on any fault, SPREAD_PCT_ENABLED=0 to silence.

The dead scanner.spread_pct_max knob is intentionally left for a later
promotion, once the logged data shows spread separates outcomes.

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_019psgRHdtTXz6TCnnqPiqGw

GO_LIVE flags `spread_pct` as a not-yet-real field. The reason it was null is structural: OHLCV bars carry no NBBO, so `minute_bars.spread_pct` can never be populated from the bars endpoint. The execution-relevant figure is the spread at the moment a setup is evaluated — a wide book quietly eats R on the thin sub-$20 names this system trades. This makes that metric real, shadow-first (measure before gating), mirroring how the catalyst score was introduced: - `strategy/evaluation/liquidity.py`: pure `compute_spread_pct(bid, ask)` → (ask-bid)/mid, or None for missing/crossed/non-positive quotes (None never collapses to a real 0.0). Unit-tested. - `AlpacaPaperClient.get_latest_quotes`: batched latest-NBBO fetch, mirrors `get_latest_trades`. - `Watcher`: optional injected `spread_provider` (same shape as `catalyst_score_provider`); default None == prior behavior byte-for-byte. Computed spread is logged onto the criteria_evaluated and signal_ready events. Observe-only — it does NOT gate ready/blocked. - `run_live_paper`: TTL-cached spread provider off the client; degrades to None on any fault, SPREAD_PCT_ENABLED=0 to silence. The dead `scanner.spread_pct_max` knob is intentionally left for a later promotion, once the logged data shows spread separates outcomes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019psgRHdtTXz6TCnnqPiqGw
Sign in to join this conversation.