Phase E.0 Task 5b. Calibrates FillModel coefficients from historical MBP-10
+ trade tape. Streams snapshots concurrently with time-sorted trades; per
snapshot, determines binary fill outcome ("would a posted L1 limit have
been hit within next --window-seconds?"), accumulates (FillFeatures, y),
calls fit_poisson (cloglog Bernoulli, see d08ab461d). Writes 6 fitted
coefficient sets to JSON.
L1-only limitation: DbnParser::parse_mbp10_streaming ignores
Mbp10Msg.levels[1..10] (only stores levels[0] via update_level(0, ...)),
so this binary fits L1 distributions only and replicates them across
L2/L3 with β_0 -= ln(L+1) attenuation. The parser bug is documented
inline; fixing it is out of Phase E.0 scope.
Scale-bug workaround: parser stores mbp10.price (1e9 fixed-point) directly
into BidAskPair.bid_px/ask_px, but BidAskPair::price_to_f64 divides by 1e12
(different convention). Net: helper returns prices 1000× too small. Binary
uses raw_price_to_f32 (i64 * 1e-9) directly — confirmed in smoke run
(bid_l1=0 with helper, bid_l1=4500-range with workaround).
Smoke run (2K snapshot cap):
- 5.2M trades loaded, front-month filtered
- 19.7M MBP-10 events in file → 2K accumulated via interval=10
- bid_l1 empirical = 0.7%, ask_l1 empirical = 19.4% (uptrend bias in
early-2024 file region; data, not bug)
- bid β_spread = -4.05, ask β_spread = -0.39 (signs sane: wider
spread reduces fill probability)
- Full run pending (sequential mode per user)
Run:
cargo run -p ml --release --example phase_e_fit_fill_model -- \
--mbp10-dir /home/jgrusewski/Work/foxhunt/test_data/futures-baseline-mbp10/ES.FUT \
--trades-dir /home/jgrusewski/Work/foxhunt/test_data/futures-baseline-trades/ES.FUT \
--window-seconds 60 \
--snapshot-interval 50 \
--out-path phase_e_fill_coeffs.json