fix(pre-alpha): preserve injected backtest services

This commit is contained in:
Bu5hm4nn
2026-03-26 12:18:39 +01:00
parent 18fd0681ca
commit d7117bb6a3
3 changed files with 62 additions and 5 deletions

View File

@@ -2,6 +2,7 @@ from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from datetime import date from datetime import date
from math import isclose
from app.domain.backtesting_math import materialize_backtest_portfolio_state from app.domain.backtesting_math import materialize_backtest_portfolio_state
from app.models.backtest import ( from app.models.backtest import (
@@ -10,7 +11,7 @@ from app.models.backtest import (
ProviderRef, ProviderRef,
TemplateRef, TemplateRef,
) )
from app.services.backtesting.historical_provider import DailyClosePoint from app.services.backtesting.historical_provider import DailyClosePoint, SyntheticHistoricalProvider
from app.services.backtesting.service import BacktestService from app.services.backtesting.service import BacktestService
from app.services.strategy_templates import StrategyTemplateService from app.services.strategy_templates import StrategyTemplateService
@@ -64,12 +65,19 @@ class BacktestPageService:
template_service: StrategyTemplateService | None = None, template_service: StrategyTemplateService | None = None,
) -> None: ) -> None:
self.template_service = template_service or StrategyTemplateService() self.template_service = template_service or StrategyTemplateService()
self.backtest_service = backtest_service or BacktestService( base_service = backtest_service or BacktestService(
template_service=self.template_service, template_service=self.template_service,
provider=None, provider=None,
) )
provider = self.backtest_service.provider fixture_provider = SyntheticHistoricalProvider(
provider.source = DeterministicBacktestFixtureSource() source=DeterministicBacktestFixtureSource(),
implied_volatility=base_service.provider.implied_volatility,
risk_free_rate=base_service.provider.risk_free_rate,
)
self.backtest_service = BacktestService(
provider=fixture_provider,
template_service=self.template_service,
)
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,7 +130,12 @@ class BacktestPageService:
self.template_service.get_template(template_slug) self.template_service.get_template(template_slug)
derived_entry_spot = self.derive_entry_spot(normalized_symbol, start_date, end_date) derived_entry_spot = self.derive_entry_spot(normalized_symbol, start_date, end_date)
if entry_spot is not None and entry_spot != derived_entry_spot: if entry_spot is not None and not isclose(
entry_spot,
derived_entry_spot,
rel_tol=BacktestService.ENTRY_SPOT_REL_TOLERANCE,
abs_tol=BacktestService.ENTRY_SPOT_ABS_TOLERANCE,
):
raise ValueError( raise ValueError(
f"Supplied entry spot ${entry_spot:,.2f} does not match derived historical entry spot ${derived_entry_spot:,.2f}" f"Supplied entry spot ${entry_spot:,.2f} does not match derived historical entry spot ${derived_entry_spot:,.2f}"
) )

View File

@@ -0,0 +1,10 @@
from __future__ import annotations
from datetime import date
from app.services.backtesting.historical_provider import DailyClosePoint
class StaticBacktestSource:
def load_daily_closes(self, symbol: str, start_date: date, end_date: date) -> list[DailyClosePoint]:
return [DailyClosePoint(date=date(2024, 1, 3), close=123.0)]

View File

@@ -4,7 +4,10 @@ from datetime import date
import pytest import pytest
from app.services.backtesting.historical_provider import SyntheticHistoricalProvider
from app.services.backtesting.service import BacktestService
from app.services.backtesting.ui_service import BacktestPageService from app.services.backtesting.ui_service import BacktestPageService
from tests.helpers_backtest_sources import StaticBacktestSource
def test_backtest_page_service_uses_fixture_window_for_deterministic_run() -> None: def test_backtest_page_service_uses_fixture_window_for_deterministic_run() -> None:
@@ -186,6 +189,23 @@ def test_backtest_preview_validation_requires_supported_fixture_window_even_with
) )
def test_backtest_preview_validation_accepts_close_supplied_entry_spot() -> None:
service = BacktestPageService()
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=100.005,
)
assert entry_spot == 100.0
def test_backtest_preview_validation_rejects_mismatched_supplied_entry_spot() -> None: def test_backtest_preview_validation_rejects_mismatched_supplied_entry_spot() -> None:
service = BacktestPageService() service = BacktestPageService()
@@ -200,3 +220,17 @@ def test_backtest_preview_validation_rejects_mismatched_supplied_entry_spot() ->
margin_call_ltv=0.75, margin_call_ltv=0.75,
entry_spot=99.0, entry_spot=99.0,
) )
def test_backtest_page_service_does_not_mutate_injected_backtest_service() -> None:
provider = SyntheticHistoricalProvider(
source=StaticBacktestSource(),
implied_volatility=0.2,
risk_free_rate=0.01,
)
injected_service = BacktestService(provider=provider)
BacktestPageService(backtest_service=injected_service)
history = injected_service.provider.load_history("GLD", date(2024, 1, 3), date(2024, 1, 3))
assert history[0].close == 123.0