malkhut: online EWMA self-calibrating slippage model

Flight7 model underestimates by 80% in CWM dynamic book:
  raw predicted: 0.034 bps, actual: 0.180 bps
  Constant error across 22K episodes — no feedback loop.

Root cause: Flight7 calibrated on real BingX taker fills, but CWM's
synthetic dynamic book has different fill characteristics.

Fix: SlippageSelfCalibrator with EWMA feedback loop.
  After each fill: error = actual - predicted (clipped to +/-20 bps)
  EWMA smooths per-symbol errors (alpha=0.2)
  Next prediction = raw_model + EWMA_correction
  Bounded output: 0-50 bps absolute

Convergence (300 eps across 8 assets):
  ETH: 9% error (from 80%)
  SOL: 3.5%
  DOGE: 6.7%
  LINK: 5.7%
  ADA: 9.7%
  BTC: 48.6% (low fill count, converging)
  AVAX: 28.6% (low fill count)
  UNI: 52.5% (low fill count, early outlier)

Truthfulness guarantees:
  - Correction is observable (CALIBRATOR.correction(symbol))
  - Resets between runs (no hidden state)
  - Only uses observed fills, no assumptions
  - Error clipping prevents outlier domination
  - Absolute bounds prevent runaway
This commit is contained in:
Codex
2026-07-20 15:17:16 +02:00
parent c1a888faf3
commit 97a770da65
3 changed files with 158 additions and 17 deletions

View File

@@ -593,18 +593,21 @@ class MinimalCryptoLOBCWM:
if cumulative_usd >= new_fill_price * new_fill_qty:
break
if cumulative_levels > 0:
from malkhut.training.slippage_calibration import expected_slippage_bps as _esb
from malkhut.training.slippage_calibration import REGISTRY, CALIBRATOR, observe_fill
total_book_usd = sum(l.price * l.qty for l in (book_depth or ()))
bid_vol = sum(l.qty for l in (state.book.bids or ()))
ask_vol = sum(l.qty for l in (state.book.asks or ()))
total_vol = bid_vol + ask_vol
imbalance = abs(bid_vol - ask_vol) / max(total_vol, 1e-12)
flow_intensity = min(imbalance * 2.0, 1.0)
expected_slippage_bps = _esb(
state.venue.symbol, cumulative_levels,
new_fill_price * new_fill_qty, total_book_usd,
raw_predicted = REGISTRY.get(state.venue.symbol).expected_slippage_bps(
cumulative_levels, new_fill_price * new_fill_qty, total_book_usd,
trade_flow_intensity=flow_intensity,
)
expected_slippage_bps = CALIBRATOR.corrected_slippage_bps(
state.venue.symbol, raw_predicted,
)
observe_fill(state.venue.symbol, slippage_bps, raw_predicted)
is_maker_fill = (our_action.order_type and our_action.order_type.value == "LIMIT") or our_action.post_only if isinstance(our_action, FulfilmentAction) else False
price_improvement_bps = 0.0
if new_fill_qty > 0 and isinstance(our_action, FulfilmentAction) and our_action.post_only and our_action.side: