malkhut: conditional slippage (Fable)
This commit is contained in:
@@ -568,8 +568,20 @@ class MinimalCryptoLOBCWM:
|
|||||||
# ── Fill Quality computation ────────────────────────────────────────
|
# ── Fill Quality computation ────────────────────────────────────────
|
||||||
mid = state.book.mid if state.book.bids and state.book.asks else 0.0
|
mid = state.book.mid if state.book.bids and state.book.asks else 0.0
|
||||||
slippage_bps = 0.0
|
slippage_bps = 0.0
|
||||||
|
expected_slippage_bps = 0.0
|
||||||
if new_fill_qty > 0 and mid > 0 and new_fill_price > 0:
|
if new_fill_qty > 0 and mid > 0 and new_fill_price > 0:
|
||||||
slippage_bps = abs(new_fill_price - mid) / mid * 10_000
|
slippage_bps = abs(new_fill_price - mid) / mid * 10_000
|
||||||
|
# Conditional: expected slippage from book depth
|
||||||
|
book_depth = state.book.asks if isinstance(our_action, FulfilmentAction) and our_action.side == Side.BUY else state.book.bids if isinstance(our_action, FulfilmentAction) else ()
|
||||||
|
cumulative_usd = 0.0
|
||||||
|
cumulative_levels = 0
|
||||||
|
for level in (book_depth or ()):
|
||||||
|
cumulative_usd += level.price * level.qty
|
||||||
|
cumulative_levels += 1
|
||||||
|
if cumulative_usd >= new_fill_price * new_fill_qty:
|
||||||
|
break
|
||||||
|
if cumulative_levels > 0:
|
||||||
|
expected_slippage_bps = cumulative_levels * 0.1
|
||||||
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
|
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
|
price_improvement_bps = 0.0
|
||||||
if new_fill_qty > 0 and isinstance(our_action, FulfilmentAction) and our_action.post_only and our_action.side:
|
if new_fill_qty > 0 and isinstance(our_action, FulfilmentAction) and our_action.post_only and our_action.side:
|
||||||
@@ -594,7 +606,8 @@ class MinimalCryptoLOBCWM:
|
|||||||
fill_value = 0.0
|
fill_value = 0.0
|
||||||
if new_fill_qty > 0:
|
if new_fill_qty > 0:
|
||||||
quality = price_improvement_bps if is_maker_fill else max(0.0, spread_bps - slippage_bps)
|
quality = price_improvement_bps if is_maker_fill else max(0.0, spread_bps - slippage_bps)
|
||||||
fill_value = quality - abs(post_fill_adverse) * 0.5
|
slippage_surprise = slippage_bps - expected_slippage_bps
|
||||||
|
fill_value = quality - abs(post_fill_adverse) * 0.5 - max(0.0, slippage_surprise) * 0.3
|
||||||
|
|
||||||
fq = FillQuality(
|
fq = FillQuality(
|
||||||
filled=new_fill_qty > 0,
|
filled=new_fill_qty > 0,
|
||||||
@@ -602,6 +615,7 @@ class MinimalCryptoLOBCWM:
|
|||||||
fill_price=new_fill_price,
|
fill_price=new_fill_price,
|
||||||
requested_qty=our_action.qty_fraction * state.account.available_balance / max(mid, 1e-12) if isinstance(our_action, FulfilmentAction) and our_action.qty_fraction > 0 and mid > 0 else 0.0,
|
requested_qty=our_action.qty_fraction * state.account.available_balance / max(mid, 1e-12) if isinstance(our_action, FulfilmentAction) and our_action.qty_fraction > 0 and mid > 0 else 0.0,
|
||||||
slippage_bps=slippage_bps,
|
slippage_bps=slippage_bps,
|
||||||
|
expected_slippage_bps=expected_slippage_bps,
|
||||||
price_improvement_bps=price_improvement_bps,
|
price_improvement_bps=price_improvement_bps,
|
||||||
levels_consumed=0,
|
levels_consumed=0,
|
||||||
is_maker_fill=is_maker_fill,
|
is_maker_fill=is_maker_fill,
|
||||||
|
|||||||
@@ -483,11 +483,28 @@ class HftBacktestCWM:
|
|||||||
mid = prev_book.mid if prev_book.bids and prev_book.asks else 0.0
|
mid = prev_book.mid if prev_book.bids and prev_book.asks else 0.0
|
||||||
spread_bps = prev_book.spread_bps if prev_book.bids and prev_book.asks else 0.0
|
spread_bps = prev_book.spread_bps if prev_book.bids and prev_book.asks else 0.0
|
||||||
|
|
||||||
# Slippage: how far from mid did we fill?
|
# ── CONDITIONAL SLIPPAGE (not constant) ──────────────────────────
|
||||||
|
# Slippage depends on: order size, book depth, levels consumed
|
||||||
slippage_bps = 0.0
|
slippage_bps = 0.0
|
||||||
|
expected_slippage_bps = 0.0
|
||||||
if filled and mid > 0 and new_fill_price > 0:
|
if filled and mid > 0 and new_fill_price > 0:
|
||||||
|
# Actual slippage: how far from mid did we fill?
|
||||||
slippage_bps = abs(new_fill_price - mid) / mid * 10_000
|
slippage_bps = abs(new_fill_price - mid) / mid * 10_000
|
||||||
|
|
||||||
|
# Expected slippage from book depth model (power-law)
|
||||||
|
# Walk levels until we accumulate fill_qty
|
||||||
|
book_depth = prev_book.asks if action.side == Side.BUY else prev_book.bids
|
||||||
|
cumulative_usd = 0.0
|
||||||
|
cumulative_levels = 0
|
||||||
|
for level in (book_depth or ()):
|
||||||
|
level_usd = level.price * level.qty
|
||||||
|
cumulative_usd += level_usd
|
||||||
|
cumulative_levels += 1
|
||||||
|
if cumulative_usd >= new_fill_price * new_fill_qty:
|
||||||
|
break
|
||||||
|
if cumulative_levels > 0:
|
||||||
|
expected_slippage_bps = cumulative_levels * 0.1 # ~0.1 bps per level consumed
|
||||||
|
|
||||||
# Price improvement: how much better than best bid/ask?
|
# Price improvement: how much better than best bid/ask?
|
||||||
price_improvement_bps = 0.0
|
price_improvement_bps = 0.0
|
||||||
if filled and action.post_only and action.side:
|
if filled and action.post_only and action.side:
|
||||||
@@ -525,11 +542,13 @@ class HftBacktestCWM:
|
|||||||
else:
|
else:
|
||||||
rolling_fill_rate = 0.0
|
rolling_fill_rate = 0.0
|
||||||
|
|
||||||
# Composite fill value score
|
# Composite fill value score — conditioned on expected slippage
|
||||||
fill_value = 0.0
|
fill_value = 0.0
|
||||||
if filled:
|
if filled:
|
||||||
quality = price_improvement_bps if is_maker else max(0.0, spread_bps - slippage_bps)
|
quality = price_improvement_bps if is_maker else max(0.0, spread_bps - slippage_bps)
|
||||||
fill_value = quality - abs(post_fill_adverse) * 0.5
|
# Adjust by slippage surprise: actual vs expected
|
||||||
|
slippage_surprise = slippage_bps - expected_slippage_bps # positive = worse than expected
|
||||||
|
fill_value = quality - abs(post_fill_adverse) * 0.5 - max(0.0, slippage_surprise) * 0.3
|
||||||
|
|
||||||
return FillQuality(
|
return FillQuality(
|
||||||
filled=filled,
|
filled=filled,
|
||||||
@@ -537,6 +556,7 @@ class HftBacktestCWM:
|
|||||||
fill_price=new_fill_price,
|
fill_price=new_fill_price,
|
||||||
requested_qty=action.qty_fraction * prev_state.account.available_balance / max(mid, 1e-12) if action.qty_fraction > 0 and mid > 0 else 0.0,
|
requested_qty=action.qty_fraction * prev_state.account.available_balance / max(mid, 1e-12) if action.qty_fraction > 0 and mid > 0 else 0.0,
|
||||||
slippage_bps=slippage_bps,
|
slippage_bps=slippage_bps,
|
||||||
|
expected_slippage_bps=expected_slippage_bps,
|
||||||
price_improvement_bps=price_improvement_bps,
|
price_improvement_bps=price_improvement_bps,
|
||||||
levels_consumed=levels_consumed,
|
levels_consumed=levels_consumed,
|
||||||
is_maker_fill=is_maker,
|
is_maker_fill=is_maker,
|
||||||
|
|||||||
@@ -283,6 +283,9 @@ class FillQuality:
|
|||||||
# How close to mid did we fill? (for aggressive: positive = slipped)
|
# How close to mid did we fill? (for aggressive: positive = slipped)
|
||||||
slippage_bps: float = 0.0
|
slippage_bps: float = 0.0
|
||||||
|
|
||||||
|
# Expected slippage from book depth model (conditional on actual book state)
|
||||||
|
expected_slippage_bps: float = 0.0
|
||||||
|
|
||||||
# For passive fills: how much better than best bid/ask? (positive = improvement)
|
# For passive fills: how much better than best bid/ask? (positive = improvement)
|
||||||
price_improvement_bps: float = 0.0
|
price_improvement_bps: float = 0.0
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user