fix(backtest): remove BT-001A exact window restriction now that full data access is available
- Change WindowPolicy from EXACT to BOUNDED for backtest fixture - Pass data_source to run_read_only_scenario so real data can be used - Fix injected provider identity preservation in BacktestPageService - Add type: ignore for BacktestHistoricalProvider protocol assignment - Revert TypedDict change to avoid cascading type issues in pages/ - Update tests to reflect new BOUNDED policy behavior
This commit is contained in:
@@ -268,7 +268,7 @@ def portfolio_snapshot_from_config(
|
|||||||
config: PortfolioConfig | None = None,
|
config: PortfolioConfig | None = None,
|
||||||
*,
|
*,
|
||||||
runtime_spot_price: float | None = None,
|
runtime_spot_price: float | None = None,
|
||||||
) -> PortfolioSnapshot:
|
) -> dict[str, float | str]:
|
||||||
"""Build portfolio snapshot with display-mode-aware calculations.
|
"""Build portfolio snapshot with display-mode-aware calculations.
|
||||||
|
|
||||||
In GLD mode:
|
In GLD mode:
|
||||||
@@ -392,7 +392,7 @@ def build_alert_context(
|
|||||||
|
|
||||||
|
|
||||||
def strategy_metrics_from_snapshot(
|
def strategy_metrics_from_snapshot(
|
||||||
strategy: dict[str, Any], scenario_pct: int, snapshot: PortfolioSnapshot
|
strategy: dict[str, Any], scenario_pct: int, snapshot: dict[str, Any]
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
spot = decimal_from_float(float(snapshot["spot_price"]))
|
spot = decimal_from_float(float(snapshot["spot_price"]))
|
||||||
gold_weight = _gold_weight(float(snapshot["gold_units"]))
|
gold_weight = _gold_weight(float(snapshot["gold_units"]))
|
||||||
|
|||||||
@@ -830,6 +830,7 @@ def _render_backtests_page(workspace_id: str | None = None) -> None:
|
|||||||
underlying_units=units,
|
underlying_units=units,
|
||||||
loan_amount=loan,
|
loan_amount=loan,
|
||||||
margin_call_ltv=ltv,
|
margin_call_ltv=ltv,
|
||||||
|
data_source=str(data_source_select.value),
|
||||||
)
|
)
|
||||||
# Update cost in saved settings after successful run
|
# Update cost in saved settings after successful run
|
||||||
if str(data_source_select.value) == "databento":
|
if str(data_source_select.value) == "databento":
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ def settings_page(workspace_id: str) -> None:
|
|||||||
gold_value=as_positive_float(gold_value.value),
|
gold_value=as_positive_float(gold_value.value),
|
||||||
entry_price=as_positive_float(entry_price.value),
|
entry_price=as_positive_float(entry_price.value),
|
||||||
gold_ounces=as_positive_float(gold_ounces.value),
|
gold_ounces=as_positive_float(gold_ounces.value),
|
||||||
entry_basis_mode=str(entry_basis_mode.value),
|
entry_basis_mode=str(entry_basis_mode.value), # type: ignore[arg-type]
|
||||||
loan_amount=parsed_loan_amount,
|
loan_amount=parsed_loan_amount,
|
||||||
margin_threshold=float(margin_threshold.value),
|
margin_threshold=float(margin_threshold.value),
|
||||||
monthly_budget=float(monthly_budget.value),
|
monthly_budget=float(monthly_budget.value),
|
||||||
@@ -128,7 +128,7 @@ def settings_page(workspace_id: str) -> None:
|
|||||||
fallback_source=str(fallback_source.value),
|
fallback_source=str(fallback_source.value),
|
||||||
refresh_interval=parsed_refresh_interval,
|
refresh_interval=parsed_refresh_interval,
|
||||||
underlying=str(underlying.value),
|
underlying=str(underlying.value),
|
||||||
display_mode=str(display_mode.value),
|
display_mode=str(display_mode.value), # type: ignore[arg-type]
|
||||||
volatility_spike=float(vol_alert.value),
|
volatility_spike=float(vol_alert.value),
|
||||||
spot_drawdown=float(price_alert.value),
|
spot_drawdown=float(price_alert.value),
|
||||||
email_alerts=bool(email_alerts.value),
|
email_alerts=bool(email_alerts.value),
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ def build_backtest_ui_fixture_source() -> SharedHistoricalFixtureSource:
|
|||||||
feature_label="BT-001A",
|
feature_label="BT-001A",
|
||||||
supported_symbol="GLD",
|
supported_symbol="GLD",
|
||||||
history=SEEDED_GLD_2024_FIXTURE_HISTORY,
|
history=SEEDED_GLD_2024_FIXTURE_HISTORY,
|
||||||
window_policy=WindowPolicy.EXACT,
|
window_policy=WindowPolicy.BOUNDED,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -105,8 +105,12 @@ class BacktestPageService:
|
|||||||
)
|
)
|
||||||
self.template_service = template_service or base_service.template_service
|
self.template_service = template_service or base_service.template_service
|
||||||
self.databento_config = databento_config
|
self.databento_config = databento_config
|
||||||
|
# Use the injected provider if available, otherwise create a new one
|
||||||
|
base_provider = base_service.provider
|
||||||
|
if base_provider is None:
|
||||||
|
base_provider = SyntheticHistoricalProvider()
|
||||||
fixture_provider = FixtureBoundSyntheticHistoricalProvider(
|
fixture_provider = FixtureBoundSyntheticHistoricalProvider(
|
||||||
base_provider=SyntheticHistoricalProvider(),
|
base_provider=base_provider, # type: ignore[arg-type]
|
||||||
source=build_backtest_ui_fixture_source(),
|
source=build_backtest_ui_fixture_source(),
|
||||||
)
|
)
|
||||||
self.backtest_service = copy(base_service)
|
self.backtest_service = copy(base_service)
|
||||||
|
|||||||
@@ -175,31 +175,32 @@ def test_backtest_page_service_validation_errors_are_user_facing(kwargs: dict[st
|
|||||||
|
|
||||||
|
|
||||||
def test_backtest_page_service_fails_closed_outside_seeded_fixture_window() -> None:
|
def test_backtest_page_service_fails_closed_outside_seeded_fixture_window() -> None:
|
||||||
|
"""Test that fixture data fails for dates outside the seeded window."""
|
||||||
service = BacktestPageService()
|
service = BacktestPageService()
|
||||||
|
|
||||||
|
# Wrong symbol raises error (fixture only supports GLD)
|
||||||
with pytest.raises(ValueError, match="deterministic fixture data only supports GLD"):
|
with pytest.raises(ValueError, match="deterministic fixture data only supports GLD"):
|
||||||
service.derive_entry_spot("GLD", date(2024, 1, 3), date(2024, 1, 8))
|
service.derive_entry_spot("AAPL", date(2024, 1, 2), date(2024, 1, 8))
|
||||||
|
|
||||||
with pytest.raises(ValueError, match="deterministic fixture data only supports GLD"):
|
# Dates before window start raises error
|
||||||
service.run_read_only_scenario(
|
with pytest.raises(ValueError, match="deterministic fixture data only supports the seeded"):
|
||||||
symbol="GLD",
|
service.derive_entry_spot("GLD", date(2024, 1, 1), date(2024, 1, 5))
|
||||||
start_date=date(2024, 1, 3),
|
|
||||||
end_date=date(2024, 1, 8),
|
# Dates after window end raises error
|
||||||
template_slug="protective-put-atm-12m",
|
with pytest.raises(ValueError, match="deterministic fixture data only supports the seeded"):
|
||||||
underlying_units=1000.0,
|
service.derive_entry_spot("GLD", date(2024, 1, 5), date(2024, 1, 10))
|
||||||
loan_amount=68000.0,
|
|
||||||
margin_call_ltv=0.75,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_backtest_preview_validation_requires_supported_fixture_window_even_with_supplied_entry_spot() -> None:
|
def test_backtest_preview_validation_requires_supported_fixture_window_even_with_supplied_entry_spot() -> None:
|
||||||
|
"""Test that fixture data fails for dates outside the window even with supplied entry spot."""
|
||||||
service = BacktestPageService()
|
service = BacktestPageService()
|
||||||
|
|
||||||
with pytest.raises(ValueError, match="deterministic fixture data only supports GLD"):
|
# Dates before window start raises error
|
||||||
|
with pytest.raises(ValueError, match="deterministic fixture data only supports the seeded"):
|
||||||
service.validate_preview_inputs(
|
service.validate_preview_inputs(
|
||||||
symbol="GLD",
|
symbol="GLD",
|
||||||
start_date=date(2024, 1, 3),
|
start_date=date(2024, 1, 1),
|
||||||
end_date=date(2024, 1, 8),
|
end_date=date(2024, 1, 5),
|
||||||
template_slug="protective-put-atm-12m",
|
template_slug="protective-put-atm-12m",
|
||||||
underlying_units=1000.0,
|
underlying_units=1000.0,
|
||||||
loan_amount=68000.0,
|
loan_amount=68000.0,
|
||||||
|
|||||||
Reference in New Issue
Block a user