malkhut: Flight9 learnings — markout=quality, queue×flow, depth-for-size

Fable's Flight9/BLUE generalizable features incorporated:

1. Slippage model gains trade_flow_intensity parameter:
   - Estimated from book imbalance (proxy for trade arrivals)
   - More flow → better fills (lower slippage)
   - Fable: 'fill = queue position × trade-flow intensity'

2. Markout = quality concept documented:
   - Score fills by post-fill markout, not just fill/no-fill
   - Maker fills are adversely selected

3. Depth-for-size documented:
   - Spread lies; key on depth-within-K-bps vs order notional

4. Measured fees:
   - BingX maker=2.00bp, taker=5.016bp (over 1,455 fills)
   - BingX commission = NEGATIVE (debit)

5. OB study updated with Flight9 learnings
This commit is contained in:
Codex
2026-07-17 16:18:43 +02:00
parent 8857daedfa
commit bb229833d3
4 changed files with 84 additions and 4 deletions

View File

@@ -583,9 +583,15 @@ class MinimalCryptoLOBCWM:
if cumulative_levels > 0:
from malkhut.training.slippage_calibration import expected_slippage_bps as _esb
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,
trade_flow_intensity=flow_intensity,
)
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

View File

@@ -503,12 +503,19 @@ class HftBacktestCWM:
if cumulative_usd >= new_fill_price * new_fill_qty:
break
if cumulative_levels > 0:
# Calibrated slippage: per-asset model from VST data
# Calibrated slippage: per-asset model with flow intensity (Fable Flight9)
from malkhut.training.slippage_calibration import expected_slippage_bps as _esb
total_book_usd = sum(l.price * l.qty for l in (book_depth or ()))
# Estimate flow intensity from book imbalance (proxy for trade arrivals)
bid_vol = sum(l.qty for l in (prev_book.bids or ()))
ask_vol = sum(l.qty for l in (prev_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) # high imbalance = more flow
expected_slippage_bps = _esb(
prev_state.venue.symbol, cumulative_levels,
new_fill_price * new_fill_qty, total_book_usd,
trade_flow_intensity=flow_intensity,
)
# Price improvement: how much better than best bid/ask?

View File

@@ -64,8 +64,18 @@ class SlippageCalibration:
order_usd: float = 0.0,
book_depth_usd: float = 1.0,
is_mainnet: bool = False,
trade_flow_intensity: float = 0.0,
) -> float:
"""Predict slippage. Switches model based on book depth."""
"""Predict slippage. Switches model based on book depth.
Generalizable features (Fable, Flight9/BLUE):
1. Fill = queue position × trade-flow intensity (not just book snapshot)
2. Depth-for-size > spread (spread lies — unfillable behind $977)
3. Markout = quality (post-fill adverse selection)
"""
# Flow intensity boost: more trade arrivals → higher fill probability
flow_boost = 1.0 + trade_flow_intensity * 0.1
if book_depth_usd < self.thin_book_threshold_usd:
# THIN BOOK: intercept-dominant (alts, meme coins)
# The fill walks the entire book in 1-2 levels.
@@ -77,6 +87,9 @@ class SlippageCalibration:
depth_ratio = order_usd / max(book_depth_usd, 1.0)
base = self.alpha * levels_consumed + self.beta * depth_ratio
# Adjust for flow intensity: more flow = better fills (lower slippage)
base /= max(flow_boost, 0.5)
if is_mainnet:
base *= self.testnet_to_mainnet
return base
@@ -123,10 +136,11 @@ class SlippageRegistry:
order_usd: float = 0.0,
book_depth_usd: float = 1.0,
is_mainnet: bool = False,
trade_flow_intensity: float = 0.0,
) -> float:
"""Predict slippage using the appropriate model."""
model = self.get(symbol)
return model.expected_slippage_bps(levels_consumed, order_usd, book_depth_usd, is_mainnet)
return model.expected_slippage_bps(levels_consumed, order_usd, book_depth_usd, is_mainnet, trade_flow_intensity)
# ==============================================================================
@@ -219,6 +233,7 @@ def expected_slippage_bps(
order_usd: float = 0.0,
book_depth_usd: float = 1.0,
is_mainnet: bool = False,
trade_flow_intensity: float = 0.0,
) -> float:
"""Predict slippage using Flight7-calibrated model."""
return REGISTRY.expected_slippage_bps(symbol, levels_consumed, order_usd, book_depth_usd, is_mainnet)
return REGISTRY.expected_slippage_bps(symbol, levels_consumed, order_usd, book_depth_usd, is_mainnet, trade_flow_intensity)