334 lines
8.2 KiB
Python
334 lines
8.2 KiB
Python
|
|
"""
|
||
|
|
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):
|
||
|
|
LIMIT = "LIMIT"
|
||
|
|
MARKET = "MARKET"
|
||
|
|
POST_ONLY = "POST_ONLY"
|
||
|
|
IOC = "IOC"
|
||
|
|
FOK = "FOK"
|
||
|
|
REDUCE_ONLY_LIMIT = "REDUCE_ONLY_LIMIT"
|
||
|
|
REDUCE_ONLY_MARKET = "REDUCE_ONLY_MARKET"
|
||
|
|
|
||
|
|
|
||
|
|
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
|