""" MALKHUT canonical data model. All state objects are frozen+slots for: - deterministic tree search (immutable snapshots) - GraalVM compatibility (no mutable default hell) - lock-free shared memory (readers never see partial writes) """ from __future__ import annotations from dataclasses import dataclass, field from enum import Enum from typing import Any, Dict, Mapping, Optional, Sequence, Tuple import math # ============================================================================== # Enums # ============================================================================== class Side(str, Enum): BUY = "BUY" SELL = "SELL" class OrderType(str, Enum): """Standardized order types — FIX/CCXT-aligned, multi-exchange. Five-layer taxonomy (values are uppercase for backward compatibility): Layer 1: Base types (FIX Tag 40) — MARKET, LIMIT Layer 2: Time-in-force (FIX Tag 59) — GTC, IOC, FOK, GTD Layer 3: Conditional (FIX Tag 3/4+MIT) — STOP_MARKET, STOP_LIMIT, etc. Layer 4: Instructions (FIX Tag 18) — POST_ONLY, REDUCE_ONLY, etc. Layer 5: Compound (exchange-specific) — OCO, TP_SL """ # Layer 1: Base types (FIX Tag 40) MARKET = "MARKET" LIMIT = "LIMIT" # Layer 2: Time-in-force (FIX Tag 59) GTC = "GTC" IOC = "IOC" FOK = "FOK" GTD = "GTD" # Layer 3: Conditional (FIX Tag 3/4 + MIT) STOP_MARKET = "STOP_MARKET" STOP_LIMIT = "STOP_LIMIT" TRIGGER_MARKET = "TRIGGER_MARKET" TRIGGER_LIMIT = "TRIGGER_LIMIT" TRAILING_STOP = "TRAILING_STOP" # Layer 4: Instructions (FIX Tag 18) POST_ONLY = "POST_ONLY" REDUCE_ONLY = "REDUCE_ONLY" HIDDEN = "HIDDEN" ICEBERG = "ICEBERG" # Layer 5: Compound (exchange-specific) OCO = "OCO" TP_SL = "TP_SL" class ActionKind(str, Enum): NOOP = "NOOP" PLACE = "PLACE" CANCEL = "CANCEL" CANCEL_REPLACE = "CANCEL_REPLACE" CROSS_SPREAD = "CROSS_SPREAD" REDUCE = "REDUCE" FULL_EXIT = "FULL_EXIT" MOVE_STOP = "MOVE_STOP" MOVE_TAKE_PROFIT = "MOVE_TAKE_PROFIT" THROTTLE = "THROTTLE" class IntentKind(str, Enum): ENTER_LONG = "ENTER_LONG" ENTER_SHORT = "ENTER_SHORT" ADD_LONG = "ADD_LONG" ADD_SHORT = "ADD_SHORT" REDUCE_LONG = "REDUCE_LONG" REDUCE_SHORT = "REDUCE_SHORT" EXIT_LONG = "EXIT_LONG" EXIT_SHORT = "EXIT_SHORT" MAINTAIN = "MAINTAIN" class AgentRole(str, Enum): OUR_FULFILMENT = "OUR_FULFILMENT" PASSIVE_MAKER = "PASSIVE_MAKER" TOXIC_TAKER = "TOXIC_TAKER" LATENCY_ARB = "LATENCY_ARB" MOMENTUM_TAKER = "MOMENTUM_TAKER" MEAN_REVERSION_TAKER = "MEAN_REVERSION_TAKER" INVENTORY_MM = "INVENTORY_MM" LIQUIDATION_FLOW = "LIQUIDATION_FLOW" NOISE_TRADER = "NOISE_TRADER" STALE_QUOTE_ATTACKER = "STALE_QUOTE_ATTACKER" class Mode(str, Enum): REPLAY_NO_IMPACT = "REPLAY_NO_IMPACT" ENDOGENOUS_AGENT_SIM = "ENDOGENOUS_AGENT_SIM" PAPER = "PAPER" SHADOW_LIVE = "SHADOW_LIVE" LIVE = "LIVE" # ============================================================================== # Core constants # ============================================================================== HOT_PATH_BUDGET_MS: int = 100 DEFAULT_PLANNER_BUDGET_MS: int = 25 DEFAULT_TREE_DEPTH: int = 3 DEFAULT_MAX_SIMS: int = 256 DEFAULT_UCB_C: float = 1.41421356237 DEFAULT_MIN_ROOT_POLICY_ENTROPY: float = 0.25 DEFAULT_SELF_PLAY_POOL_MAX: int = 12 DEFAULT_POLICY_PROMOTION_MIN_EDGE_BPS: float = 0.75 DEFAULT_POLICY_PROMOTION_MIN_PVALUE: float = 0.05 MAX_ACCOUNT_LEVERAGE: float = 2.0 MAX_EXCHANGE_LEVERAGE: float = 5.0 MAX_SINGLE_ORDER_NOTIONAL_FRACTION: float = 0.05 MAX_SYMBOL_NOTIONAL_FRACTION: float = 0.20 MAX_CANCELS_PER_SYMBOL_PER_MINUTE: int = 90 TAIL_QUANTILE: float = 0.05 # ============================================================================== # Frozen data model # ============================================================================== @dataclass(frozen=True, slots=True) class VenueRules: exchange: str symbol: str tick_size: float lot_size: float min_qty: float min_notional: float maker_fee_bps: float taker_fee_bps: float post_only_supported: bool reduce_only_supported: bool max_orders_per_second: int max_cancels_per_minute: int @dataclass(frozen=True, slots=True) class PriceLevel: price: float qty: float @dataclass(frozen=True, slots=True) class OrderBookState: ts_ns: int symbol: str bids: Tuple[PriceLevel, ...] asks: Tuple[PriceLevel, ...] last_trade_price: Optional[float] = None last_trade_qty: Optional[float] = None last_trade_side: Optional[Side] = None @property def best_bid(self) -> float: return self.bids[0].price @property def best_ask(self) -> float: return self.asks[0].price @property def mid(self) -> float: return 0.5 * (self.best_bid + self.best_ask) @property def spread(self) -> float: return self.best_ask - self.best_bid @property def spread_bps(self) -> float: return 10_000.0 * self.spread / max(self.mid, 1e-12) @dataclass(frozen=True, slots=True) class PositionState: symbol: str qty: float avg_entry: float unrealized_pnl: float realized_pnl: float liquidation_price: Optional[float] leverage: float side: Optional[Side] @dataclass(frozen=True, slots=True) class AccountState: ts_ns: int equity: float wallet_balance: float available_balance: float margin_used: float total_notional: float positions: Mapping[str, PositionState] = field(default_factory=dict) @dataclass(frozen=True, slots=True) class OpenOrderState: client_order_id: str venue_order_id: Optional[str] symbol: str side: Side order_type: OrderType price: Optional[float] qty: float remaining_qty: float queue_ahead_estimate: Optional[float] created_ts_ns: int last_update_ts_ns: int reduce_only: bool = False post_only: bool = False @dataclass(frozen=True, slots=True) class TradePathState: """In-trade path encoding for path-aware SL/TP.""" symbol: str side: Side entry_ts_ns: int now_ts_ns: int bars_held: int seconds_held: float pnl_bps: float mae_bps: float mfe_bps: float distance_from_mfe_bps: float distance_from_entry_bps: float time_to_mfe_s: float time_in_loss_s: float time_in_profit_s: float time_since_last_profit_s: float time_since_deep_mae_s: float loss_to_profit_transitions: int deep_loss_recoveries: int failed_recovery_count: int recovery_velocity_bps_per_s: float adverse_velocity_bps_per_s: float dolphin_regime_score: float jericho_signal_strength: float volatility_bps: float orderflow_toxicity: float queue_churn_score: float book_imbalance: float cross_venue_lead_score: float @dataclass(frozen=True, slots=True) class ExecutionIntent: intent_id: str ts_ns: int symbol: str kind: IntentKind target_qty: float max_notional: float urgency: float alpha_horizon_s: float alpha_bps: float max_slippage_bps: float prefer_maker: bool reduce_only: bool ttl_s: float reason: str @dataclass(frozen=True, slots=True) class MarketWorldState: """Complete CWM root state. Immutable for safe tree search.""" ts_ns: int mode: Mode venue: VenueRules book: OrderBookState account: AccountState open_orders: Tuple[OpenOrderState, ...] = () trade_path: Optional[TradePathState] = None intent: Optional[ExecutionIntent] = None funding_bps: Optional[float] = None volatility_state: Optional[float] = None market_regime: Optional[str] = None feed_latency_ms: float = 0.0 order_latency_ms: float = 0.0 rng_seed: int = 0 @dataclass(frozen=True, slots=True) class FulfilmentPolicyParams: """ Frozen parameter set loaded by the live planner. CMA-ES tunes this object offline. """ version: str # Planner ucb_c: float max_sims: int max_depth: int rollout_depth: int root_temperature: float min_root_entropy: float # Quote menu quote_offsets_ticks: Tuple[int, ...] quote_size_fractions: Tuple[float, ...] passive_ttl_ms: int aggressive_ttl_ms: int # Maker/taker thresholds maker_edge_min_bps: float cross_spread_edge_min_bps: float adverse_toxicity_cancel_threshold: float queue_churn_cancel_threshold: float # SL/TP/path risk mae_tail_cut_bps: float mfe_giveback_cut_fraction: float max_time_in_loss_s: float failed_recovery_cut_count: int recovery_velocity_min_bps_per_s: float # Inventory/account max_symbol_notional_fraction: float max_single_order_notional_fraction: float reduce_when_global_up_fraction: float session_profit_lock_fraction: float # Reward weights w_expected_pnl: float w_fill_probability: float w_adverse_selection: float w_queue_priority: float w_inventory_risk: float w_tail_loss: float w_fee_quality: float w_time_decay: float w_policy_entropy: float # Scenario robustness robust_tail_weight: float toxic_counterparty_weight: float low_liquidity_weight: float latency_stress_weight: float