From 12151ccf6aaa1fa4d3e213917fbefc300beadfe6 Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Fri, 15 May 2026 13:26:21 +0200 Subject: [PATCH] feat(alpha): fitted FillModel coefficients from 500K ES.FUT snapshots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase E.0 Task 5c. Ran phase_e_fit_fill_model on the ES.FUT 2024-Q1 MBP-10 + trade tape (5.2M trades, 3.9M MBP-10 events, 500K snapshots accumulated at snapshot_interval=50 over a ~24-minute window). Total runtime ~80s. Empirical fill rates within 60s window: - bid_l1: 4.97% (matches L1 maker-side activity in trending market) - ask_l1: 71.34% (high — most 60s windows see an aggressive buy) Fitted L1 cloglog coefficients (all 5 features): BID L1: β_0=-0.213 β_spread=-2.064 β_imbal=-0.099 β_ofi=-0.006 β_logτ=-0.286 ASK L1: β_0=+0.016 β_spread=-40.336 β_imbal=+0.041 β_ofi=+0.652 β_logτ=-0.055 Sanity (sign checks all pass): - β_spread < 0 both sides (wider spread → fewer fills) ✓ - bid β_imbal < 0 (more bid stack → harder to get hit by sell) ✓ - ask β_ofi > 0 (buying pressure correlates with ask fills) ✓ - β_logτ < 0 both sides (quieter markets → slower execution) ✓ L1-only limitation: as documented in the binary header, the parser only populates levels[0]; L2/L3 in the JSON are L1 with β_0 -= ln(L+1) attenuation. Default --out-path bumped to config/ml/phase_e_fill_coeffs.json so future re-runs land in the same committed location. --- config/ml/phase_e_fill_coeffs.json | 60 ++++++++++++++++++++ crates/ml/examples/phase_e_fit_fill_model.rs | 2 +- 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 config/ml/phase_e_fill_coeffs.json diff --git a/config/ml/phase_e_fill_coeffs.json b/config/ml/phase_e_fill_coeffs.json new file mode 100644 index 000000000..aea5fdf63 --- /dev/null +++ b/config/ml/phase_e_fill_coeffs.json @@ -0,0 +1,60 @@ +{ + "ask_coeffs": [ + [ + 0.015584787353873253, + -40.335906982421875, + 0.041306495666503906, + 0.6524077653884888, + -0.055438391864299774 + ], + [ + -0.677562415599823, + -40.335906982421875, + 0.041306495666503906, + 0.6524077653884888, + -0.055438391864299774 + ], + [ + -1.0830274820327759, + -40.335906982421875, + 0.041306495666503906, + 0.6524077653884888, + -0.055438391864299774 + ] + ], + "bid_coeffs": [ + [ + -0.21279112994670868, + -2.063631057739258, + -0.09875621646642685, + -0.006330371834337711, + -0.28642183542251587 + ], + [ + -0.9059383273124695, + -2.063631057739258, + -0.09875621646642685, + -0.006330371834337711, + -0.28642183542251587 + ], + [ + -1.3114033937454224, + -2.063631057739258, + -0.09875621646642685, + -0.006330371834337711, + -0.28642183542251587 + ] + ], + "empirical_ask_l1_rate": 0.713424, + "empirical_bid_l1_rate": 0.049668, + "fit_iters": 1000, + "fit_lr": 0.009999999776482582, + "l1_only_limitation": true, + "l1_only_limitation_reason": "parse_mbp10_streaming ignores Mbp10Msg.levels[1..10]; L2/L3 coefficients are L1 with attenuated β_0 (-ln(L+1))", + "n_degenerate_skipped": 138941, + "n_snapshots": 500000, + "n_trades": 5198778, + "phase": "E.0 Task 5", + "snapshot_interval": 50, + "window_seconds": 60.0 +} \ No newline at end of file diff --git a/crates/ml/examples/phase_e_fit_fill_model.rs b/crates/ml/examples/phase_e_fit_fill_model.rs index 063028729..96f492d2d 100644 --- a/crates/ml/examples/phase_e_fit_fill_model.rs +++ b/crates/ml/examples/phase_e_fit_fill_model.rs @@ -74,7 +74,7 @@ struct Cli { #[arg(long, default_value_t = 60.0)] window_seconds: f32, /// Output JSON path. - #[arg(long, default_value = "phase_e_fill_coeffs.json")] + #[arg(long, default_value = "config/ml/phase_e_fill_coeffs.json")] out_path: PathBuf, /// MBP-10 events between snapshot emissions (denser = larger sample). #[arg(long, default_value_t = 10)]