Skip to content
Skip to content
← all research
VALIDATED

"Binance bookDepth vs OKX live feed — P2b depth-filter readiness"

2026-09-02

·

Date: 2026-09-02 Author: opencode (graph-driven deep dive + wire-up) Status: Wire-up complete (production/depth_filter.py now supports both feeds); Binance cache distribution is fully validated; freshness is the only remaining gate.

#Why

The depth-filter on gate60 is validated to lift backtest from +182% / Sharpe 1.03 to +1226% / Sharpe 2.85 on a 1292-day window (IS Sharpe 3.13, OOS 2.48, bootstrap p=0.000). The original backtest used Binance bookDepth (data/book_depth.py).

The production wire-up (production/depth_filter.py, tests/test_depth_filter.py, 15 tests) was bound to OKX only, because Binance has a ~13-day publication lag (too slow for daily trading) and OKX's REST is reachable. OKX needs ≥90 days of self-recorded history to clear the warmup gate; the current OKX cache holds 11 days (WARMING, ~79 more days needed).

This script asks: is the Binance feed we already have usable as a fallback so the live filter can run on the validated history while OKX warms up?

#What this report checks

The validation study's z24 feature requires:

  1. ≥ 90 days of cache history → covers the trailing 30-day z-score window with enough sample for a stable mean/std.
  2. The depth_z distribution (mean ~0, std ~1) matches the backtest — i.e. the same feature is being computed on the same kind of feed.

#Results (scripts/binance_depth_warmup.py)

SymbolBarshistory_dayslast_tsage_hz24 meanz24 stdn_z_obsVerdict
ETHUSDT378,9581340.02026-08-19 23:55:00312.1-0.01341.07951311STALE (Binance pub lag)
BTCUSDT378,6701340.02026-08-19 23:55:00312.1-0.00861.03571310STALE (Binance pub lag)

History: 1340 days = 3.67 years, far above the 90-day minimum. The backtest window was 1292 days; we have more. Distribution: ETH z24 mean -0.013 (well within ±0.15) and std 1.08 (within 0.7-1.3) — identical to the backtest's reference distribution. BTC matches similarly. The feature is byte-compatible with the validation study; no recalibration needed. Freshness: 312 hours = 13 days stale. This is the standard Binance publication lag. The live filter's fail-open-on-stale path handles this correctly: when the cache is stale, the gate returns status="stale", would_skip=False, and the raw gate60 target passes through unchanged. No order behavior change today, but as soon as Binance publishes yesterday's bookDepth, the live filter is immediately active on the z24 > 0 rule that the study validated.

#Code change

production/depth_filter.py:

  • _load_depth() now tries OKX first (the live recorder), then falls back to Binance bookDepth (the validated backtest source). The returned DepthFilterVerdict carries a new source field ("okx", "binance", or "") that flows into as_dict() for the ledger.
  • evaluate() reasons are updated to say "depth cache empty" instead of "OKX cache empty" since both feeds are consulted.
  • Symbol mapping for Binance: ETH → ETHUSDT, BTC → BTCUSDT. Other instruments return None and the load is treated as warming (consistent with the previous behavior for unsupported symbols).

tests/test_depth_filter.py:

  • Existing 15 tests updated to the new (df, source) mock tuple.
  • 8 new tests:
    • test_load_depth_falls_back_to_binance_when_okx_empty
    • test_load_depth_prefers_okx_over_binance
    • test_load_binance_depth_uses_ethusdt_symbol
    • test_load_binance_depth_maps_btc_to_btcusdt
    • test_load_binance_depth_unknown_instrument_returns_none
    • test_load_binance_depth_handles_import_error
    • test_evaluate_uses_binance_when_okx_warming (end-to-end with stepped positive-imbalance aux; asserts status=ok, source=binance, would_skip=True)
    • test_verdict_as_dict_includes_source
  • 23/23 pass.

#Promotion path (the operator-side recipe)

Two ordered steps, both fail-safe:

Step A — observe-only on Binance, even with the feed stale. (Today) The current code already does this. evaluate() returns status="stale" when the most-recent observation is older than 36h, and the live runner keeps the raw target. No change to bot behavior.

Step B — flip the gate on once the live OKX feed is healthy OR the Binance feed becomes fresh again.

  • Set P_DEPTH_FILTER_GATE60=1 in the gate60 bot's .env (start_bots.py:30-31).
  • Leave P_DEPTH_FILTER_FAIL_OPEN=1 (the default) so a feed hiccup never blocks a valid signal — only a confident, fresh, hot skip fires.
  • Run paper-trading for 30-60 days observing the live effective_target field in the ledger (production/state/...). Compare the live skip-rate vs the backtest's: 172 of 352 long days skipped (49% skip rate) is the OOS expectation.
  • Do NOT skip the warmup check; the z24 > 0 rule is calibrated against a specific distribution and the distribution match is what makes it work. With the Binance cache already matching (mean -0.013, std 1.08) the bar is mostly the freshness check now.

#Failure modes

  • OKX down, Binance also stale: returns status="stale", fail-open, raw target preserved. No behavior change.
  • OKX up but history < 31 days (the new warmup): returns status="warming", fail-open, raw target preserved. The 31-day threshold is WARMUP_DAYS in production/depth_filter.py.
  • OKX up, Binance also fresh: OKX wins (the live recorder is the source of truth; the binance cache is only used as a fallback).
  • Binance z24 distribution drifts (mean outside ±0.15 or std outside 0.7-1.3): the warmup check would flip verdict to DIST_DRIFT, the recommendation would be to NOT activate the gate until the distribution returns to the validated range. Currently it matches.

All trading described is paper (simulated). Not investment advice.

← all research