Two carried-over limitations from Phase E.0 / E.1 fixed and verified.
1. MBP-10 parser bug fix (`parse_mbp10_streaming` + `parse_mbp10_file`)
The DBN crate's `Mbp10Msg` carries the FULL post-update top-10 book
in `levels: [BidAskPair; 10]` per message — not just the single
update event's price/size. Previously the parser only called
`update_level(0, ...)` with the update event's fields, leaving
`current_snapshot.levels[1..10]` at default-empty. Downstream:
- OFI calculator reading L2-L5 got zeros → produced wrong OFI
features (the canonical Phase 1c/1d 81-dim feature stack has
multi-level OFI as features 0..5; with the bug these were
constant zero).
- microprice (`snapshot.levels[1]`) got zeros.
- FillModel L2/L3 fit observations got zeros, so L2/L3
coefficients were undefined (we worked around by replicating
L1 with attenuated intercept).
Fix: after `update_level(0, ...)`, copy fields from
`mbp10.levels[lvl]` into `current_snapshot.levels[lvl]` for `lvl
in 1..max_lvl`. Field-by-field copy preserves the existing scale
convention (raw 1e9 fixed-point i64). Applied to both streaming
and async file-parse code paths.
Comment "For simplicity, store all updates in level 0 / A full
implementation would maintain proper level ordering" removed.
2. fit_poisson L2 regularization
New `fit_poisson_l2(features, observed, max_iters, lr, l2_lambda)`
API (the old `fit_poisson` delegates with l2_lambda=0). L2 penalty
applies to slope coefficients β[1..5] but NOT to intercept β[0]
(penalizing the intercept biases toward p≈0.5 for all-zero-feature
samples, breaking the recovery test). Per-iteration update:
β[0] -= lr · grad[0] / n (intercept)
β[k] -= lr · (grad[k] / n + λ · β[k]) (slope, k ∈ 1..5)
Canonical motivation: on real 5.2M-trade ES.FUT data the
unregularized fitter converged to β_spread ≈ -40 (Task 5c commit
12151ccf6), producing near-zero limit fill probability at typical
spreads despite empirical fill rate ~70%. With l2_lambda=0.01 the
slope shrinks modestly while intercept tracks the empirical rate.
Default in the calibration binary bumped to 0.01.
New unit test `fit_poisson_l2_shrinks_slope_on_pathological_outlier`
constructs 990 typical samples + 10 wide-spread outliers and
verifies `|β_spread|` with L2 < `|β_spread|` without L2. Passes.
3. Cascade re-run verifies the fix is verdict-robust:
New fit (with L2 + parser fix, 500K snapshots):
BID L1: β_0=-0.24 β_spread=-1.87 β_imbal=-0.10 β_ofi=-0.006 β_logτ=-0.30
ASK L1: β_0=+0.21 β_spread=-36.41 β_imbal=+0.19 β_ofi=+0.81 β_logτ=+0.22
(β_spread on ask still large but β_0 sane; cloglog model
fundamentally mis-fits the binary tight-spread / wide-spread regime.)
New baseline (with new fill model):
mean = -5191.53 (vs old -5185.13)
std = 4963.62 (vs old 4952.85)
Negligible drift, env dynamics essentially unchanged.
H=6000 smoke re-run (same alpha cache, new fill model + parser):
Q_SPREAD_EMA = 29.59 (was 35.44)
ACTION_ENTROPY_EMA = 2.00 (was 2.00)
RETURN_VS_RANDOM_EMA = +1.001σ (was +1.003σ)
EARLY_Q_MOVEMENT_EMA = 0.130 (was 0.130)
Overall: PASS (was PASS)
Verdict is ROBUST to the fixes — the fxcache-based smoke is
insulated from the MBP-10 parser bug (uses synthesized bid/ask
from mid), and the FillModel quality improvement is minor enough
that the policy's behaviour is essentially unchanged. The fixes
matter MORE for production training paths that read MBP-10
directly (those see the full L2-L10 book now).
Files touched:
crates/data/src/providers/databento/dbn_parser.rs (parser fix in
both parse_mbp10_streaming and parse_mbp10_file)
crates/ml/src/env/fill_model.rs (new fit_poisson_l2 + test)
crates/ml/examples/alpha_fit_fill_model.rs (--l2-lambda flag)
crates/ml/examples/alpha_dqn_h600_smoke.rs (updated hardcoded
baseline values to match the new random baseline run)
config/ml/alpha_fill_coeffs.json (re-fitted with both fixes)
config/ml/alpha_random_baseline.json (re-run with new fill model)
config/ml/alpha_dqn_h6000_smoke.json (verified PASS)
All 8 fill_model tests pass. Build clean across data, ml-alpha, ml.
ml
10-model ML ensemble for the Foxhunt HFT system, built on Candle v0.9.1.
Models
- DQN (Rainbow) — deep Q-network with prioritized replay, dueling heads, noisy nets
- PPO — proximal policy optimization with GAE, LSTM policies, clip-higher
- TFT — temporal fusion transformer for multi-horizon forecasting
- Mamba2 — state space model for sequence prediction
- Liquid Networks — biologically inspired networks for non-stationary data
- TLOB — transformer-based limit order book analysis
- KAN — Kolmogorov-Arnold networks
- xLSTM — extended LSTM architecture
- TGGN — temporal graph neural network
- Diffusion — diffusion-based generative model
Key Modules
ensemble— model ensemble coordination and confidence aggregationhyperopt— PSO-based hyperparameter optimization with per-model adapterstrainers— unified training loops (DQN, PPO, supervised)inference—InferenceAdaptertrait for predictioncheckpoint— model checkpointing and restorationevaluation— walk-forward evaluation pipeline
Usage
use ml::dqn::DQN;
use ml::ppo::PpoTrainer;