from __future__ import annotations from dataclasses import dataclass, field from datetime import date @dataclass(frozen=True) class BacktestPortfolioState: currency: str underlying_units: float entry_spot: float loan_amount: float margin_call_ltv: float cash_balance: float = 0.0 financing_rate: float = 0.0 def __post_init__(self) -> None: if self.currency.upper() != "USD": raise ValueError("USD is the only supported currency in the MVP") if self.underlying_units <= 0: raise ValueError("underlying_units must be positive") if self.entry_spot <= 0: raise ValueError("entry_spot must be positive") if self.loan_amount < 0: raise ValueError("loan_amount must be non-negative") if not 0 < self.margin_call_ltv < 1: raise ValueError("margin_call_ltv must be between 0 and 1") if self.loan_amount >= self.underlying_units * self.entry_spot: raise ValueError("loan_amount must be less than initial collateral value") @property def start_value(self) -> float: return self.underlying_units * self.entry_spot @dataclass(frozen=True) class TemplateRef: slug: str version: int def __post_init__(self) -> None: if not self.slug: raise ValueError("template slug is required") if self.version <= 0: raise ValueError("template version must be positive") @dataclass(frozen=True) class ProviderRef: provider_id: str pricing_mode: str def __post_init__(self) -> None: if not self.provider_id: raise ValueError("provider_id is required") if not self.pricing_mode: raise ValueError("pricing_mode is required") @dataclass(frozen=True) class BacktestScenario: scenario_id: str display_name: str symbol: str start_date: date end_date: date initial_portfolio: BacktestPortfolioState template_refs: tuple[TemplateRef, ...] provider_ref: ProviderRef def __post_init__(self) -> None: if not self.scenario_id: raise ValueError("scenario_id is required") if not self.display_name: raise ValueError("display_name is required") if not self.symbol: raise ValueError("symbol is required") if self.start_date > self.end_date: raise ValueError("start_date must be on or before end_date") if not self.template_refs: raise ValueError("at least one template ref is required") @dataclass(frozen=True) class BacktestDailyPoint: date: date spot_close: float underlying_value: float option_market_value: float premium_cashflow: float realized_option_cashflow: float net_portfolio_value: float loan_amount: float ltv_unhedged: float ltv_hedged: float margin_call_unhedged: bool margin_call_hedged: bool active_position_ids: tuple[str, ...] = field(default_factory=tuple) @dataclass(frozen=True) class BacktestSummaryMetrics: start_value: float end_value_unhedged: float end_value_hedged_net: float total_hedge_cost: float total_option_payoff_realized: float max_ltv_unhedged: float max_ltv_hedged: float margin_threshold_breached_unhedged: bool margin_threshold_breached_hedged: bool @dataclass(frozen=True) class TemplateBacktestResult: template_slug: str template_id: str template_version: int template_name: str summary_metrics: BacktestSummaryMetrics daily_path: tuple[BacktestDailyPoint, ...] @dataclass(frozen=True) class BacktestRunResult: scenario_id: str template_results: tuple[TemplateBacktestResult, ...]