# src/web/api/v2/schemas/calibration_strategies.py from typing import Any, Dict, List, Literal, Optional, Union from pydantic import BaseModel, Field from .calibration_risk import StopConfigSchema, RiskConfigSchema, GlobalRiskRulesSchema ParameterValue = Union[int, float, bool, str] class WalkForwardConfigSchema(BaseModel): train_days: int = Field(..., gt=0) test_days: int = Field(..., gt=0) step_days: Optional[int] = Field(None, gt=0) min_trades_test: int = Field(10, ge=0) class StrategySelectionSchema(BaseModel): strategy_id: str parameters: Dict[str, ParameterValue] class CalibrationStrategiesInspectRequest(BaseModel): symbol: str timeframe: str # snapshot from Step 2 (closed) stop: StopConfigSchema risk: RiskConfigSchema global_rules: GlobalRiskRulesSchema account_equity: float = Field(..., gt=0) strategies: List[StrategySelectionSchema] wf: WalkForwardConfigSchema commission: float = Field(0.001, ge=0) slippage: float = Field(0.0005, ge=0) class WindowRowSchema(BaseModel): window: int train_start: str train_end: str test_start: str test_end: str return_pct: float sharpe: float max_dd_pct: float trades: int params: Dict[str, Any] class StrategyRunResultSchema(BaseModel): strategy_id: str status: Literal["ok", "warning", "fail"] message: str n_windows: int oos_final_equity: float oos_total_return_pct: float oos_max_dd_worst_pct: float degradation_sharpe: Optional[float] = None windows: List[WindowRowSchema] class CalibrationStrategiesInspectResponse(BaseModel): valid: bool status: Literal["ok", "warning", "fail"] checks: Dict[str, Any] message: str results: List[StrategyRunResultSchema] class CalibrationStrategiesValidateResponse(CalibrationStrategiesInspectResponse): series: Dict[str, Any]