from __future__ import annotations from typing import Any, Dict # Simple risk rules for MVP MAX_POSITION_FRACTION = 0.6 # max 60% of equity in a single position def _equity(sim_state: Dict[str, Any], price: float) -> float: cash = float(sim_state.get("cash", 0.0)) pos = sim_state.get("position") qty = float(pos["quantity"]) if pos else 0.0 return cash + qty * price def validate_order(sim_state: Dict[str, Any], action: str, quantity: float, price: float) -> None: action = str(action).upper() if quantity <= 0 or price <= 0: raise ValueError("Quantity and price must be positive") if action == "BUY": # Anti-stacking: only one symbol supported in MVP, allow averaging up to cap pos = sim_state.get("position") current_qty = float(pos["quantity"]) if pos else 0.0 new_qty = current_qty + float(quantity) resulting_position_value = new_qty * float(price) eq_now = _equity(sim_state, price) if eq_now <= 0: raise ValueError("Equity must be positive") if resulting_position_value > MAX_POSITION_FRACTION * eq_now: raise ValueError("Position exceeds max allowed exposure fraction") elif action == "SELL": pos = sim_state.get("position") if not pos or float(quantity) > float(pos.get("quantity", 0.0)): raise ValueError("Insufficient position to sell") else: raise ValueError("Unsupported action")