malkhut: friction settings configurable per scenario

Scenario gains 3 new fields:
  maker_fee_bps: Optional[float] = None (override per-scenario)
  taker_fee_bps: Optional[float] = None (override per-scenario)
  adverse_cost_bps: Optional[float] = None (per-fill adverse selection)

ScenarioFactory gains friction constructor params:
  ScenarioFactory(exchange_id='bingx', maker_fee_bps=2.0, taker_fee_bps=5.0)

_make_state() accepts friction overrides → applies to VenueRules
_behavior_state() passes friction overrides through
All 34 scenario builder calls updated with friction overrides.

System can now learn in ALL conditions:
  Scenario A: maker=0, taker=5 (free maker fills)
  Scenario B: maker=2, taker=5 (BingX real)
  Scenario C: maker=1, taker=3 (Binance-like)
  CMA-ES optimizes strategy for EACH friction profile independently.
This commit is contained in:
Codex
2026-07-19 00:25:26 +02:00
parent c868dbfb66
commit 70f33f6911

View File

@@ -308,7 +308,11 @@ class Scenario:
counterparties: Tuple[CounterpartyPolicy, ...]
max_steps: int = 50
tags: Tuple[str, ...] = ()
venue: str = "bingx" # exchange this scenario simulates (default: BingX for backward compat)
venue: str = "bingx" # exchange this scenario simulates
# Per-scenario friction overrides (None = use AssetProfile defaults)
maker_fee_bps: Optional[float] = None
taker_fee_bps: Optional[float] = None
adverse_cost_bps: Optional[float] = None # per-fill adverse selection cost
class ScenarioFactory:
@@ -328,9 +332,15 @@ class ScenarioFactory:
"""
def __init__(self, counterparties: Optional[Tuple[CounterpartyPolicy, ...]] = None,
exchange_id: str = "bingx") -> None:
exchange_id: str = "bingx",
maker_fee_bps: Optional[float] = None,
taker_fee_bps: Optional[float] = None,
adverse_cost_bps: Optional[float] = None) -> None:
self.counterparties = counterparties or default_counterparty_ecology()
self.exchange_id = exchange_id
self.maker_fee_bps = maker_fee_bps
self.taker_fee_bps = taker_fee_bps
self.adverse_cost_bps = adverse_cost_bps
# --- Behavior-driven helpers ---
@@ -374,10 +384,11 @@ class ScenarioFactory:
@staticmethod
def _behavior_state(symbol: str, spread_mult: float = 1.0,
depth_fraction: float = 1.0,
exchange_id: str = "bingx") -> "MarketWorldState":
exchange_id: str = "bingx",
maker_fee_bps: Optional[float] = None,
taker_fee_bps: Optional[float] = None) -> "MarketWorldState":
"""Create a MarketWorldState from AssetBehavior with realistic params.
Auto-compiles unknown assets from Binance API if needed.
Friction overrides take precedence over defaults.
"""
b = ScenarioFactory._get_behavior(symbol)
mid = ScenarioFactory._behavior_mid(symbol)
@@ -394,7 +405,9 @@ class ScenarioFactory:
bid_qty, ask_qty = 1.0, 1.0
return ScenarioFactory._make_state(symbol, bid=bid, ask=ask,
bid_qty=bid_qty, ask_qty=ask_qty,
exchange_id=exchange_id)
exchange_id=exchange_id,
maker_fee_bps=maker_fee_bps,
taker_fee_bps=taker_fee_bps)
def build_suite(
self,
@@ -444,7 +457,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"normal_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=1.0, depth_fraction=1.0, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=1.0, depth_fraction=1.0, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=self.counterparties,
max_steps=steps,
tags=("normal", "liquid"),
@@ -455,7 +468,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"thin_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=1.5, depth_fraction=0.1, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=1.5, depth_fraction=0.1, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=self.counterparties,
max_steps=steps,
tags=("thin", "illiquid"),
@@ -466,7 +479,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"wide_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=100.0, depth_fraction=0.5, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=100.0, depth_fraction=0.5, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=self.counterparties,
max_steps=steps,
tags=("wide", "volatile"),
@@ -478,7 +491,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"toxic_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=2.0, depth_fraction=0.3, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=2.0, depth_fraction=0.3, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(ToxicTakerPolicy(sensitivity=0.3),),
max_steps=steps,
tags=("toxic", "adverse_selection"),
@@ -489,7 +502,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"chop_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=0.5, depth_fraction=0.2, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=0.5, depth_fraction=0.2, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=self.counterparties,
max_steps=steps,
tags=("chop", "noise"),
@@ -501,7 +514,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"flash_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=3.0, depth_fraction=0.05, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=3.0, depth_fraction=0.05, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(ToxicTakerPolicy(sensitivity=0.3), ToxicTakerPolicy(sensitivity=0.4)),
max_steps=steps,
tags=("flash_crash", "thin_book"),
@@ -513,7 +526,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"vacuum_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=5.0, depth_fraction=0.01, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=5.0, depth_fraction=0.01, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(ToxicTakerPolicy(sensitivity=0.2),),
max_steps=steps,
tags=("liquidity_vacuum", "extreme"),
@@ -525,7 +538,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"multi_toxic_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=1.5, depth_fraction=0.3, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=1.5, depth_fraction=0.3, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(
ToxicTakerPolicy(sensitivity=0.3),
ToxicTakerPolicy(sensitivity=0.4),
@@ -540,7 +553,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"trend_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=1.0, depth_fraction=0.8, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=1.0, depth_fraction=0.8, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=self.counterparties,
max_steps=steps,
tags=("trending", "momentum"),
@@ -551,7 +564,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"revert_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=20.0, depth_fraction=0.6, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=20.0, depth_fraction=0.6, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=self.counterparties,
max_steps=steps,
tags=("mean_reverting", "wide_spread"),
@@ -563,7 +576,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"weekend_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=5.0, depth_fraction=0.05, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=5.0, depth_fraction=0.05, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(self.counterparties[3],),
max_steps=steps,
tags=("weekend", "low_participation", "thin"),
@@ -577,7 +590,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"funding_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=1.5, depth_fraction=0.3, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=1.5, depth_fraction=0.3, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(LiquidationFlowPolicy(trigger_bps=30.0), ToxicTakerPolicy(sensitivity=0.4)),
max_steps=steps,
tags=("funding_shock", "deleveraging"),
@@ -591,7 +604,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"cascade_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=1.2, depth_fraction=0.2, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=1.2, depth_fraction=0.2, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(
LiquidationFlowPolicy(trigger_bps=40.0),
ToxicTakerPolicy(sensitivity=0.3),
@@ -609,7 +622,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"diverge_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=1.5, depth_fraction=0.3, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=1.5, depth_fraction=0.3, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(ToxicTakerPolicy(sensitivity=0.3), self.counterparties[0]),
max_steps=steps,
tags=("divergence", "correlation_breakdown"),
@@ -623,7 +636,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"stale_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=1.0, depth_fraction=0.6, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=1.0, depth_fraction=0.6, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(StaleQuoteAttackerPolicy(), LatencyArbPolicy(lead_threshold=0.4)),
max_steps=steps,
tags=("stale_quote", "latency_arb"),
@@ -637,7 +650,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"squeeze_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=1.5, depth_fraction=0.3, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=1.5, depth_fraction=0.3, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(InventoryMarketMakerPolicy(max_inventory=0.05), ToxicTakerPolicy(sensitivity=0.4)),
max_steps=steps,
tags=("squeeze", "inventory_risk"),
@@ -650,7 +663,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"news_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=10.0, depth_fraction=0.03, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=10.0, depth_fraction=0.03, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(ToxicTakerPolicy(sensitivity=0.2), ToxicTakerPolicy(sensitivity=0.3)),
max_steps=steps,
tags=("news_spike", "gap", "thin"),
@@ -662,7 +675,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"tighten_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=0.3, depth_fraction=1.0, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=0.3, depth_fraction=1.0, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=self.counterparties,
max_steps=steps,
tags=("spread_tightening", "competition"),
@@ -675,7 +688,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"stop_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=1.2, depth_fraction=0.2, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=1.2, depth_fraction=0.2, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(ToxicTakerPolicy(sensitivity=0.3), ToxicTakerPolicy(sensitivity=0.5)),
max_steps=steps,
tags=("stop_hunting", "manipulation"),
@@ -688,7 +701,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"whale_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=1.5, depth_fraction=0.3, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=1.5, depth_fraction=0.3, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(ToxicTakerPolicy(sensitivity=0.2),),
max_steps=steps,
tags=("whale", "large_order", "impact"),
@@ -700,7 +713,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"imbalance_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=1.0, depth_fraction=0.8, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=1.0, depth_fraction=0.8, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=self.counterparties,
max_steps=steps,
tags=("imbalance", "order_flow", "asymmetry"),
@@ -713,7 +726,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"withdraw_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=1.3, depth_fraction=0.15, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=1.3, depth_fraction=0.15, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(PassiveMakerPolicy(join_probability=0.2), ToxicTakerPolicy(sensitivity=0.3)),
max_steps=steps,
tags=("withdrawal", "liquidity_dry", "stress"),
@@ -726,7 +739,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"wars_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=1.0, depth_fraction=0.8, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=1.0, depth_fraction=0.8, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(
PassiveMakerPolicy(join_probability=0.8),
PassiveMakerPolicy(join_probability=0.7),
@@ -743,7 +756,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"arb_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=0.5, depth_fraction=0.5, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=0.5, depth_fraction=0.5, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(LatencyArbPolicy(lead_threshold=0.3), ToxicTakerPolicy(sensitivity=0.4)),
max_steps=steps,
tags=("arbitrage", "cross_venue", "price_discovery"),
@@ -755,7 +768,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"flow_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=1.0, depth_fraction=0.5, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=1.0, depth_fraction=0.5, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=self.counterparties,
max_steps=steps,
tags=("order_flow", "institutional", "asymmetry"),
@@ -768,7 +781,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"volregime_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=0.1, depth_fraction=0.7, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=0.1, depth_fraction=0.7, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(ToxicTakerPolicy(sensitivity=0.4),),
max_steps=steps,
tags=("volatility_regime", "transition", "adaptive"),
@@ -781,7 +794,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"pump_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=1.2, depth_fraction=0.2, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=1.2, depth_fraction=0.2, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(ToxicTakerPolicy(sensitivity=0.2), ToxicTakerPolicy(sensitivity=0.3)),
max_steps=steps,
tags=("pump_dump", "manipulation", "coordinated"),
@@ -794,7 +807,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"iceberg_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=1.5, depth_fraction=0.3, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=1.5, depth_fraction=0.3, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(ToxicTakerPolicy(sensitivity=0.3),),
max_steps=steps,
tags=("iceberg", "hidden_order", "gradual_impact"),
@@ -808,7 +821,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"margin_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=1.3, depth_fraction=0.15, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=1.3, depth_fraction=0.15, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(
LiquidationFlowPolicy(trigger_bps=30.0),
ToxicTakerPolicy(sensitivity=0.3),
@@ -825,7 +838,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"oracle_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=3.0, depth_fraction=0.05, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=3.0, depth_fraction=0.05, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(ToxicTakerPolicy(sensitivity=0.2), ToxicTakerPolicy(sensitivity=0.3)),
max_steps=steps,
tags=("oracle_manipulation", "flash_loan", "dex"),
@@ -838,7 +851,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"whale_retail_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=1.5, depth_fraction=0.3, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=1.5, depth_fraction=0.3, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(ToxicTakerPolicy(sensitivity=0.3), NoiseTraderPolicy()),
max_steps=steps,
tags=("whale_vs_retail", "institutional", "retail"),
@@ -851,7 +864,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"arb_stress_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=0.8, depth_fraction=0.3, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=0.8, depth_fraction=0.3, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(LatencyArbPolicy(lead_threshold=0.2), ToxicTakerPolicy(sensitivity=0.4)),
max_steps=steps,
tags=("cross_exchange", "arb_stress", "price_discovery"),
@@ -864,7 +877,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"decay_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=1.0, depth_fraction=0.7, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=1.0, depth_fraction=0.7, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(PassiveMakerPolicy(join_probability=0.1), ToxicTakerPolicy(sensitivity=0.4)),
max_steps=steps,
tags=("decay", "liquidity_withdrawal", "gradual"),
@@ -878,7 +891,7 @@ class ScenarioFactory:
return Scenario(
scenario_id=f"breakdown_{symbol}_{seed}",
symbol=symbol,
initial_state=self._behavior_state(symbol, spread_mult=5.0, depth_fraction=0.15, exchange_id=self.exchange_id),
initial_state=self._behavior_state(symbol, spread_mult=5.0, depth_fraction=0.15, exchange_id=self.exchange_id), maker_fee_bps=self.maker_fee_bps, taker_fee_bps=self.taker_fee_bps,
counterparties=(
ToxicTakerPolicy(sensitivity=0.3),
LatencyArbPolicy(lead_threshold=0.3),
@@ -976,8 +989,12 @@ class ScenarioFactory:
@staticmethod
def _make_state(symbol: str, bid: float, ask: float, bid_qty: float, ask_qty: float,
exchange_id: str = "bingx") -> MarketWorldState:
"""Create a market state using asset classification for realistic parameters."""
exchange_id: str = "bingx",
maker_fee_bps: Optional[float] = None,
taker_fee_bps: Optional[float] = None) -> MarketWorldState:
"""Create a market state using asset classification for realistic parameters.
Friction overrides take precedence over AssetProfile defaults.
"""
from malkhut.training.asset_classification import get_asset_profile, ASSET_PROFILES
profile = get_asset_profile(symbol)
@@ -986,7 +1003,8 @@ class ScenarioFactory:
exchange=exchange_id, symbol=symbol,
tick_size=profile.tick_size, lot_size=profile.lot_size,
min_qty=profile.lot_size, min_notional=5.0,
maker_fee_bps=profile.maker_fee_bps, taker_fee_bps=profile.taker_fee_bps,
maker_fee_bps=maker_fee_bps if maker_fee_bps is not None else profile.maker_fee_bps,
taker_fee_bps=taker_fee_bps if taker_fee_bps is not None else profile.taker_fee_bps,
post_only_supported=True, reduce_only_supported=True,
max_orders_per_second=100, max_cancels_per_minute=120,
)