feat(BT-003A): add event comparison page

This commit is contained in:
Bu5hm4nn
2026-03-24 19:20:35 +01:00
parent 68cb2aa51a
commit ff4e565ee6
8 changed files with 672 additions and 43 deletions

View File

@@ -0,0 +1,130 @@
from __future__ import annotations
from datetime import date
import pytest
from app.services.event_comparison_ui import EventComparisonFixtureHistoricalPriceSource, EventComparisonPageService
def test_event_comparison_page_service_runs_seeded_gld_preset_deterministically() -> None:
service = EventComparisonPageService()
report = service.run_read_only_comparison(
preset_slug="gld-jan-2024-selloff",
template_slugs=("protective-put-atm-12m", "protective-put-95pct-12m"),
underlying_units=1000.0,
loan_amount=68000.0,
margin_call_ltv=0.75,
)
assert report.event_preset.slug == "gld-jan-2024-selloff"
assert report.scenario.start_date.isoformat() == "2024-01-02"
assert report.scenario.end_date.isoformat() == "2024-01-08"
assert [item.template_slug for item in report.rankings] == [
"protective-put-atm-12m",
"protective-put-95pct-12m",
]
assert report.rankings[0].rank == 1
assert (
report.rankings[0].result.daily_path[-1].net_portfolio_value
> report.rankings[-1].result.daily_path[-1].net_portfolio_value
)
def test_event_comparison_page_service_uses_preset_default_templates_when_none_selected() -> None:
service = EventComparisonPageService()
report = service.run_read_only_comparison(
preset_slug="gld-jan-2024-selloff",
template_slugs=(),
underlying_units=1000.0,
loan_amount=68000.0,
margin_call_ltv=0.75,
)
assert [item.template_slug for item in report.rankings] == [
"protective-put-atm-12m",
"ladder-50-50-atm-95pct-12m",
"protective-put-95pct-12m",
"protective-put-90pct-12m",
]
def test_event_comparison_page_service_exposes_seeded_preset_options() -> None:
service = EventComparisonPageService()
options = service.preset_options("GLD")
assert options[0]["slug"] == "gld-jan-2024-selloff"
assert options[0]["label"] == "GLD January 2024 Selloff"
assert tuple(options[0]["default_template_slugs"]) == (
"protective-put-atm-12m",
"protective-put-95pct-12m",
"protective-put-90pct-12m",
"ladder-50-50-atm-95pct-12m",
)
def test_event_comparison_page_service_resets_template_selection_to_preset_defaults() -> None:
service = EventComparisonPageService()
assert service.default_template_selection("gld-jan-2024-selloff") == (
"protective-put-atm-12m",
"protective-put-95pct-12m",
"protective-put-90pct-12m",
"ladder-50-50-atm-95pct-12m",
)
assert service.default_template_selection("gld-jan-2024-drawdown") == (
"protective-put-atm-12m",
"ladder-50-50-atm-95pct-12m",
"ladder-33-33-33-atm-95pct-90pct-12m",
)
def test_event_comparison_page_service_preview_uses_same_materialization_path() -> None:
service = EventComparisonPageService()
scenario = service.preview_scenario(
preset_slug="gld-jan-2024-selloff",
template_slugs=("protective-put-atm-12m",),
underlying_units=1000.0,
loan_amount=68000.0,
margin_call_ltv=0.75,
)
assert scenario.start_date.isoformat() == "2024-01-02"
assert scenario.end_date.isoformat() == "2024-01-08"
assert scenario.initial_portfolio.entry_spot == 100.0
assert [ref.slug for ref in scenario.template_refs] == ["protective-put-atm-12m"]
def test_event_comparison_fixture_fails_closed_for_unsupported_range() -> None:
source = EventComparisonFixtureHistoricalPriceSource()
with pytest.raises(ValueError, match="seeded 2024-01-02 through 2024-01-08"):
source.load_daily_closes("GLD", date(2024, 1, 1), date(2024, 1, 8))
def test_event_comparison_page_service_builds_chart_model_with_unhedged_reference() -> None:
service = EventComparisonPageService()
report = service.run_read_only_comparison(
preset_slug="gld-jan-2024-selloff",
template_slugs=("protective-put-atm-12m", "protective-put-95pct-12m"),
underlying_units=1000.0,
loan_amount=68000.0,
margin_call_ltv=0.75,
)
chart_model = service.chart_model(report)
assert chart_model.dates == (
"2024-01-02",
"2024-01-03",
"2024-01-04",
"2024-01-05",
"2024-01-08",
)
assert chart_model.series[0].name == "Unhedged collateral baseline"
assert chart_model.series[1].name == "Protective Put ATM"
assert len(chart_model.series[0].values) == len(chart_model.dates)