malkhut(T1): scaffold — frozen state model, actions, features, engine
T1 scaffold: 42 frozen dataclasses (state.py), action model + PlannedPolicy + RiskDecision (actions.py), 17-feature extraction (features.py), FulfilmentEngine hot-path orchestrator (engine.py).
This commit is contained in:
333
MALKHUT/malkhut/state.py
Normal file
333
MALKHUT/malkhut/state.py
Normal file
@@ -0,0 +1,333 @@
|
||||
"""
|
||||
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
|
||||
Reference in New Issue
Block a user