Commit bdbc991
l2-regime: rolling-RV regime filter, OOS-verified 2× IC uplift (#236)
* l2-regime: recursive + cyclic + walk-forward analysis scripts
Three diagnostic scripts built on existing primitives (slice_features,
run_killtest, cross_sectional_ricci_signal) — no new abstractions
(AE principles 1, 20).
* scripts/l2_killtest_recursive.py — depth-first bisection (up to
depth 3) + cyclic K=8 disjoint blocks. Reveals regime structure
hidden by full-window averaging. On collected substrate: 3/8 blocks
PROCEED with IC up to +0.339; 3/8 KILL with IC as low as -0.109.
Signal is intermittent, not uniform.
* scripts/l2_regime_analysis.py — per-block regime features
(realized vol, cross-asset correlation, dispersion, signed trend,
κ_min moments). Spearman rank-correlates block IC against each
feature. On K=8: corr_mean strongest direction (ρ=+0.429, p=0.29
n=8). Underpowered for statistical claim; motivates finer-grained
analysis.
* scripts/l2_walk_forward.py — 40-minute rolling window with 5-minute
step across substrate. ~56 windows gives the statistical power that
8 disjoint blocks lack. Reports IC trajectory, Spearman ρ at rolling
resolution, quartile bins on the most-correlated feature to find a
discriminator threshold.
Output artifacts:
* results/REGIME_ANALYSIS.json (8-block table + ρ matrix)
* results/L2_WALK_FORWARD.json (56-row trajectory, quartile bins)
Non-goals: new dataclasses, new production modules. Pure diagnostics.
If walk-forward identifies a regime discriminator, next commit adds
the regime filter to killtest.py as an optional parameter.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* l2-regime: rolling-RV regime filter, OOS-verified 2× IC uplift
Walk-forward analysis (scripts/l2_walk_forward.py, 56 rolling 40-min
windows) identified rolling realized volatility as the dominant regime
discriminator:
Spearman ρ(IC_signal, rv_mean) = +0.352 p = 0.008 ***
Spearman ρ(IC_signal, corr_mean) = +0.317 p = 0.017 *
Spearman ρ(IC_signal, trend_*) = not significant
Quartile analysis on rv_mean:
Q1_low IC median +0.027 (signal ≈ noise)
Q4_high IC median +0.137 (signal works)
IN-SAMPLE CONDITIONAL (scripts/l2_regime_conditional.py):
unconditional IC = +0.122
rv_w600_q75 IC = +0.256 (frac_on = 24.2 %)
rv_w300_q50 IC = +0.177 (frac_on = 49.2 %)
TRUE OOS (scripts/l2_regime_oos.py, threshold trained on first half,
applied to second half, no information leakage):
TEST unconditional IC = +0.116 frac_on = 100.0 %
TEST q50 thr from train IC = +0.202 frac_on = 43.9 %
TEST q75 thr from train IC = +0.236 frac_on = 36.3 %
=> 2.03× IC uplift OOS, threshold generalizes
Components
- research/microstructure/regime.py — 4 functions, no new dataclasses:
* rolling_corr_regime(features, window_rows)
* rolling_rv_regime(features, window_rows) (primary, OOS-verified)
* regime_mask_from_score(score, threshold)
* regime_mask_from_quantile(score, quantile)
- research/microstructure/killtest.py — single optional parameter:
* run_killtest(..., regime_mask: NDArray[bool] | None = None)
* Backwards-compatible: None → identical behavior to before
* When supplied, mask is applied at scoring time (ricci + target →
NaN outside mask), Ricci signal itself still computed on full
contiguous series (its rolling corr needs consecutive rows)
- tests/test_l2_regime.py — 7 new tests:
* shape + warmup on rolling_corr_regime
* high-ρ vs low-ρ synthetic discrimination
* argument validation (window too small, single symbol)
* mask NaN handling
* killtest rejects wrong mask shape
* trivial all-True mask matches unconditional (regression)
All 26 tests green. ruff + black + mypy --strict clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* l2-regime: walk-forward calibration + cross-session OOS scripts
Two more diagnostic scripts for the regime filter, completing the
generalization ladder:
* scripts/l2_regime_walkforward_calibration.py
Rolling 60-min calibration + 30-min evaluation. Slides across
substrate; at each step derives q50/q75 thresholds from the
calibration window, applies them to the next evaluation window.
Result on collected substrate: uplift POSITIVE in only 1 of 7
steps (q50) / 1 of 5 steps (q75). HONEST LIMIT: the 50/50 split
uplift (IC +0.12 → +0.24 OOS) does NOT survive production-style
short-window rolling recalibration. Threshold needs longer
calibration horizons to stabilize.
* scripts/l2_regime_cross_session.py
Cross-session OOS scaffold: takes --train-dir and --test-dir,
derives quantile thresholds from the train session, applies to
the test session, writes results/L2_REGIME_CROSS_SESSION.json.
Runnable against the second 8h session currently being collected
into data/binance_l2_perp_v2. Strongest form of OOS we can do
without multi-day walk-forward.
These land as diagnostics only. The regime MODULE (regime.py) and
its integration (run_killtest regime_mask param) are the shippable
artifacts. Scripts document the calibration surface honestly —
including where the filter breaks under stricter recalibration.
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 9d3611c commit bdbc991
10 files changed
Lines changed: 1382 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
355 | 355 | | |
356 | 356 | | |
357 | 357 | | |
| 358 | + | |
358 | 359 | | |
359 | 360 | | |
360 | | - | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
361 | 369 | | |
362 | 370 | | |
363 | 371 | | |
364 | 372 | | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
365 | 383 | | |
366 | 384 | | |
367 | 385 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
0 commit comments