feat(BT-001A): add backtest scenario runner page
This commit is contained in:
114
tests/test_backtest_ui.py
Normal file
114
tests/test_backtest_ui.py
Normal file
@@ -0,0 +1,114 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date
|
||||
|
||||
import pytest
|
||||
|
||||
from app.services.backtesting.ui_service import BacktestPageService
|
||||
|
||||
|
||||
def test_backtest_page_service_uses_fixture_window_for_deterministic_run() -> None:
|
||||
service = BacktestPageService()
|
||||
|
||||
entry_spot = service.derive_entry_spot("GLD", date(2024, 1, 2), date(2024, 1, 8))
|
||||
result = service.run_read_only_scenario(
|
||||
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,
|
||||
)
|
||||
|
||||
assert entry_spot == 100.0
|
||||
assert result.scenario.initial_portfolio.entry_spot == 100.0
|
||||
assert result.run_result.template_results[0].summary_metrics.margin_threshold_breached_unhedged is True
|
||||
assert result.run_result.template_results[0].summary_metrics.margin_threshold_breached_hedged is False
|
||||
assert [point.date.isoformat() for point in result.run_result.template_results[0].daily_path] == [
|
||||
"2024-01-02",
|
||||
"2024-01-03",
|
||||
"2024-01-04",
|
||||
"2024-01-05",
|
||||
"2024-01-08",
|
||||
]
|
||||
|
||||
|
||||
def test_backtest_non_default_template_slug_runs_successfully() -> None:
|
||||
service = BacktestPageService()
|
||||
|
||||
options = service.template_options("GLD")
|
||||
non_default_slug = str(options[1]["slug"])
|
||||
|
||||
result = service.run_read_only_scenario(
|
||||
symbol="GLD",
|
||||
start_date=date(2024, 1, 2),
|
||||
end_date=date(2024, 1, 8),
|
||||
template_slug=non_default_slug,
|
||||
underlying_units=1000.0,
|
||||
loan_amount=68000.0,
|
||||
margin_call_ltv=0.75,
|
||||
)
|
||||
|
||||
assert result.scenario.template_refs[0].slug == non_default_slug
|
||||
assert result.run_result.template_results[0].template_slug == non_default_slug
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("kwargs", "message"),
|
||||
[
|
||||
(
|
||||
{
|
||||
"symbol": "",
|
||||
"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,
|
||||
},
|
||||
"Symbol is required",
|
||||
),
|
||||
(
|
||||
{
|
||||
"symbol": "GLD",
|
||||
"start_date": date(2024, 1, 8),
|
||||
"end_date": date(2024, 1, 2),
|
||||
"template_slug": "protective-put-atm-12m",
|
||||
"underlying_units": 1000.0,
|
||||
"loan_amount": 68000.0,
|
||||
"margin_call_ltv": 0.75,
|
||||
},
|
||||
"Start date must be on or before end date",
|
||||
),
|
||||
(
|
||||
{
|
||||
"symbol": "GLD",
|
||||
"start_date": date(2024, 1, 2),
|
||||
"end_date": date(2024, 1, 8),
|
||||
"template_slug": "protective-put-atm-12m",
|
||||
"underlying_units": 0.0,
|
||||
"loan_amount": 68000.0,
|
||||
"margin_call_ltv": 0.75,
|
||||
},
|
||||
"Underlying units must be positive",
|
||||
),
|
||||
(
|
||||
{
|
||||
"symbol": "TLT",
|
||||
"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,
|
||||
},
|
||||
"BT-001A backtests are currently limited to GLD on this page",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_backtest_page_service_validation_errors_are_user_facing(kwargs: dict[str, object], message: str) -> None:
|
||||
service = BacktestPageService()
|
||||
|
||||
with pytest.raises(ValueError, match=message):
|
||||
service.run_read_only_scenario(**kwargs)
|
||||
Reference in New Issue
Block a user