Commit fef666e
l2-killtest: fail-fast Ricci edge validator on real Binance-perp L2 substrate (#234)
* l2-killtest: fail-fast Ricci edge validator on real Binance-perp L2 substrate
Minimal-scope kill test: ingest → 4 features → one target → 3 baselines
→ null tests → orthogonality residual → binary VERDICT. No Kafka,
no UI, no 7-day production run — first gate at 6-10h.
Components
- scripts/collect_binance_perp_l2.py: async WebSocket collector for
fstream.binance.com depth5@100ms across 10 USDT-M perps, hourly
parquet shards with zstd + canonical L2 schema, reconnect w/ backoff,
SIGTERM/SIGINT drain, ~6h default duration.
- research/microstructure/l2_schema.py: canonical pyarrow schema
(ts_event, ts_ingest, symbol, update_id, {bid,ask}_{px,sz}_[1..5]).
- research/microstructure/killtest.py: single-file gate
* OFI (Cont-Kukanov-Stoikov 2014) + queue imbalance at L1
* cross-sectional OFI correlation graph + Forman-Ricci κ_min
(reuses core.physics.forman_ricci.FormanRicciCurvature)
* pooled Spearman IC vs 1/2/3/4/5-min forward mid log-return
* baselines: plain return, realized vol (60s), plain OFI
* nulls: shuffle + circular-shift permutation (500 trials each)
* orthogonality: OLS residualization against baselines
* GateVerdict dataclass → JSON (seed=42, NaN-safe round-trip)
- scripts/run_l2_killtest.py: thin CLI that loads shards, builds
feature frame, runs gate, writes L2_KILLTEST_VERDICT.json.
Gate thresholds
IC_signal ≥ 0.03 (Spearman, pooled)
IC_signal > max(IC_baselines)
residual_IC > 0 & permutation p < 0.05
stable lead: IC > 0 at all horizons {60,120,180,240,300}s
null-test p < 0.05 on shuffle + circular
Any failure → KILL. All pass → PROCEED (escalate to 3-7d run).
Tests (13, all green)
- Collector parser: depth message → row (accept/reject), hourly shard
splitting, append-on-existing-shard idempotence.
- Killtest math: QI bounds ∈ [-1,1], OFI = 0 on constant book,
κ_min finite on non-degenerate input.
- Verdict: KILL on pure-noise substrate, deterministic under fixed
seed (JSON round-trip), JSON contract fields present, smoke pass
on injected-edge substrate.
Quality gates: ruff format+check clean, black --check clean,
mypy --strict --follow-imports=silent clean on all 7 new files.
Non-goals (explicit): L3, execution, Kafka, UI, Askar-tap integration.
These are out of scope until the fail-fast gate emits PROCEED.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* l2-killtest: swap delisted MATICUSDT→POLUSDT, add cycle orchestrator
* research/microstructure/l2_schema.py: MATICUSDT was migrated off
Binance USDT-M perps (replaced by POLUSDT mid-2024); smoke run
confirmed 0 rows for MATIC stream. Replace in DEFAULT_SYMBOLS so
the cross-sectional graph has all 10 nodes populated.
* scripts/run_l2_killtest_cycle.sh: one-shot orchestrator that runs
the collector for DURATION_SEC (default 21600 = 6h), then fires
the kill-test CLI, tailing both logs and recording a phase status
file under logs/killtest_cycle.status.
Smoke: 10s run of the updated default set → 714 rows across 10
shards including POLUSDT, all other 9 confirmed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent b79f657 commit fef666e
8 files changed
Lines changed: 1124 additions & 0 deletions
File tree
- research/microstructure
- scripts
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
0 commit comments