refactor(pre-alpha): align preview and runtime fixture validation
This commit is contained in:
@@ -127,7 +127,9 @@ class EventComparisonService:
|
|||||||
if not selected_template_slugs:
|
if not selected_template_slugs:
|
||||||
raise ValueError("Event comparison requires at least one template slug")
|
raise ValueError("Event comparison requires at least one template slug")
|
||||||
|
|
||||||
resolved_history = history or self._load_preset_history(preset)
|
resolved_history = self._load_preset_history(preset) if history is None else history
|
||||||
|
if not resolved_history:
|
||||||
|
raise ValueError("Event comparison history must not be empty")
|
||||||
scenario_portfolio = materialize_backtest_portfolio_state(
|
scenario_portfolio = materialize_backtest_portfolio_state(
|
||||||
symbol=preset.symbol,
|
symbol=preset.symbol,
|
||||||
underlying_units=initial_portfolio.underlying_units,
|
underlying_units=initial_portfolio.underlying_units,
|
||||||
|
|||||||
@@ -68,9 +68,8 @@ class BacktestPageService:
|
|||||||
template_service=self.template_service,
|
template_service=self.template_service,
|
||||||
provider=None,
|
provider=None,
|
||||||
)
|
)
|
||||||
if backtest_service is None:
|
provider = self.backtest_service.provider
|
||||||
provider = self.backtest_service.provider
|
provider.source = DeterministicBacktestFixtureSource()
|
||||||
provider.source = DeterministicBacktestFixtureSource()
|
|
||||||
|
|
||||||
def template_options(self, symbol: str = "GLD") -> list[dict[str, str | int]]:
|
def template_options(self, symbol: str = "GLD") -> list[dict[str, str | int]]:
|
||||||
return [
|
return [
|
||||||
@@ -122,11 +121,13 @@ class BacktestPageService:
|
|||||||
raise ValueError("Template selection is required")
|
raise ValueError("Template selection is required")
|
||||||
|
|
||||||
self.template_service.get_template(template_slug)
|
self.template_service.get_template(template_slug)
|
||||||
resolved_entry_spot = (
|
derived_entry_spot = self.derive_entry_spot(normalized_symbol, start_date, end_date)
|
||||||
entry_spot if entry_spot is not None else self.derive_entry_spot(normalized_symbol, start_date, end_date)
|
if entry_spot is not None and entry_spot != derived_entry_spot:
|
||||||
)
|
raise ValueError(
|
||||||
_validate_initial_collateral(underlying_units, resolved_entry_spot, loan_amount)
|
f"Supplied entry spot ${entry_spot:,.2f} does not match derived historical entry spot ${derived_entry_spot:,.2f}"
|
||||||
return resolved_entry_spot
|
)
|
||||||
|
_validate_initial_collateral(underlying_units, derived_entry_spot, loan_amount)
|
||||||
|
return derived_entry_spot
|
||||||
|
|
||||||
def run_read_only_scenario(
|
def run_read_only_scenario(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -170,18 +170,33 @@ def test_backtest_page_service_fails_closed_outside_seeded_fixture_window() -> N
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_backtest_preview_validation_reuses_supplied_entry_spot() -> None:
|
def test_backtest_preview_validation_requires_supported_fixture_window_even_with_supplied_entry_spot() -> None:
|
||||||
service = BacktestPageService()
|
service = BacktestPageService()
|
||||||
|
|
||||||
entry_spot = service.validate_preview_inputs(
|
with pytest.raises(ValueError, match="deterministic fixture data only supports GLD"):
|
||||||
symbol="GLD",
|
service.validate_preview_inputs(
|
||||||
start_date=date(2024, 1, 2),
|
symbol="GLD",
|
||||||
end_date=date(2024, 1, 8),
|
start_date=date(2024, 1, 3),
|
||||||
template_slug="protective-put-atm-12m",
|
end_date=date(2024, 1, 8),
|
||||||
underlying_units=1000.0,
|
template_slug="protective-put-atm-12m",
|
||||||
loan_amount=68000.0,
|
underlying_units=1000.0,
|
||||||
margin_call_ltv=0.75,
|
loan_amount=68000.0,
|
||||||
entry_spot=100.0,
|
margin_call_ltv=0.75,
|
||||||
)
|
entry_spot=100.0,
|
||||||
|
)
|
||||||
|
|
||||||
assert entry_spot == 100.0
|
|
||||||
|
def test_backtest_preview_validation_rejects_mismatched_supplied_entry_spot() -> None:
|
||||||
|
service = BacktestPageService()
|
||||||
|
|
||||||
|
with pytest.raises(ValueError, match="does not match derived historical entry spot"):
|
||||||
|
service.validate_preview_inputs(
|
||||||
|
symbol="GLD",
|
||||||
|
start_date=date(2024, 1, 2),
|
||||||
|
end_date=date(2024, 1, 8),
|
||||||
|
template_slug="protective-put-atm-12m",
|
||||||
|
underlying_units=1000.0,
|
||||||
|
loan_amount=68000.0,
|
||||||
|
margin_call_ltv=0.75,
|
||||||
|
entry_spot=99.0,
|
||||||
|
)
|
||||||
|
|||||||
@@ -315,3 +315,43 @@ def test_event_comparison_uses_preset_defaults_only_when_template_slugs_are_omit
|
|||||||
"protective-put-90pct-12m",
|
"protective-put-90pct-12m",
|
||||||
"ladder-50-50-atm-95pct-12m",
|
"ladder-50-50-atm-95pct-12m",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
with pytest.raises(ValueError, match="at least one template slug"):
|
||||||
|
service.materialize_scenario(
|
||||||
|
service.event_preset_service.get_preset("gld-jan-2024-selloff"),
|
||||||
|
initial_portfolio=BacktestPortfolioState(
|
||||||
|
currency="USD",
|
||||||
|
underlying_units=1000.0,
|
||||||
|
entry_spot=100.0,
|
||||||
|
loan_amount=68_000.0,
|
||||||
|
margin_call_ltv=0.75,
|
||||||
|
),
|
||||||
|
template_slugs=(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_event_comparison_materialize_scenario_rejects_explicit_empty_history() -> None:
|
||||||
|
provider = SyntheticHistoricalProvider(
|
||||||
|
source=FakeHistorySource(FIXTURE_HISTORY),
|
||||||
|
implied_volatility=0.35,
|
||||||
|
risk_free_rate=0.0,
|
||||||
|
)
|
||||||
|
service = EventComparisonService(
|
||||||
|
provider=provider,
|
||||||
|
template_service=StrategyTemplateService(),
|
||||||
|
event_preset_service=EventPresetService(repository=FileEventPresetRepository()),
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(ValueError, match="history must not be empty"):
|
||||||
|
service.materialize_scenario(
|
||||||
|
service.event_preset_service.get_preset("gld-jan-2024-selloff"),
|
||||||
|
initial_portfolio=BacktestPortfolioState(
|
||||||
|
currency="USD",
|
||||||
|
underlying_units=1000.0,
|
||||||
|
entry_spot=100.0,
|
||||||
|
loan_amount=68_000.0,
|
||||||
|
margin_call_ltv=0.75,
|
||||||
|
),
|
||||||
|
template_slugs=("protective-put-atm-12m",),
|
||||||
|
history=[],
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user