from __future__ import annotations from datetime import date from decimal import Decimal import pytest from app.services.event_comparison_ui import EventComparisonFixtureHistoricalPriceSource, EventComparisonPageService def test_event_comparison_page_service_accepts_string_and_decimal_boundary_values() -> None: service = EventComparisonPageService() preview = service.preview_scenario( preset_slug="gld-jan-2024-selloff", template_slugs=("protective-put-atm-12m",), underlying_units="1000.0", loan_amount=Decimal("68000.0"), margin_call_ltv="0.75", ) 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=Decimal("68000.0"), margin_call_ltv="0.75", ) assert preview.initial_portfolio.underlying_units == 1000.0 assert preview.initial_portfolio.loan_amount == 68000.0 assert preview.initial_portfolio.margin_call_ltv == 0.75 assert report.scenario.initial_portfolio.underlying_units == 1000.0 assert report.scenario.initial_portfolio.loan_amount == 68000.0 assert report.scenario.initial_portfolio.margin_call_ltv == 0.75 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_rejects_empty_template_selection() -> None: service = EventComparisonPageService() with pytest.raises(ValueError, match="Select at least one strategy template"): 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, ) with pytest.raises(ValueError, match="Select at least one strategy template"): service.preview_scenario( preset_slug="gld-jan-2024-selloff", template_slugs=(), underlying_units=1000.0, loan_amount=68000.0, margin_call_ltv=0.75, ) 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_keeps_fixture_window_while_using_caller_portfolio_inputs() -> None: service = EventComparisonPageService() report = service.run_read_only_comparison( preset_slug="gld-jan-2024-selloff", template_slugs=("protective-put-atm-12m",), underlying_units=9680.0, loan_amount=222000.0, margin_call_ltv=0.80, ) assert report.scenario.start_date.isoformat() == "2024-01-02" assert report.scenario.end_date.isoformat() == "2024-01-08" assert report.scenario.initial_portfolio.entry_spot == 100.0 assert report.scenario.initial_portfolio.underlying_units == 9680.0 assert report.scenario.initial_portfolio.loan_amount == 222000.0 assert report.scenario.initial_portfolio.margin_call_ltv == 0.80 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_page_service_rejects_undercollateralized_historical_start() -> None: service = EventComparisonPageService() with pytest.raises(ValueError, match="Historical scenario starts undercollateralized"): service.preview_scenario( preset_slug="gld-jan-2024-selloff", template_slugs=("protective-put-atm-12m",), underlying_units=1000.0, loan_amount=145000.0, margin_call_ltv=0.75, ) with pytest.raises(ValueError, match="Historical scenario starts undercollateralized"): service.run_read_only_comparison( preset_slug="gld-jan-2024-selloff", template_slugs=("protective-put-atm-12m",), underlying_units=1000.0, loan_amount=145000.0, margin_call_ltv=0.75, ) 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) def test_event_comparison_page_service_builds_drilldown_for_selected_ranking() -> 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, ) drilldown = service.drilldown_model(report, template_slug="protective-put-95pct-12m") assert drilldown.rank == 2 assert drilldown.template_name == "Protective Put 95%" assert drilldown.margin_call_days_hedged == report.rankings[1].margin_call_days_hedged assert drilldown.hedge_cost == report.rankings[1].hedge_cost assert drilldown.final_equity == report.rankings[1].final_equity assert ( drilldown.total_option_payoff_realized == report.rankings[1].result.summary_metrics.total_option_payoff_realized ) assert drilldown.worst_ltv_date == "2024-01-08" assert drilldown.rows[0].date == "2024-01-02" assert drilldown.rows[-1].date == "2024-01-08" def test_event_comparison_page_service_defaults_drilldown_to_top_ranked_strategy() -> 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, ) drilldown = service.drilldown_model(report) assert drilldown.rank == 1 assert drilldown.template_slug == report.rankings[0].template_slug assert drilldown.template_name == report.rankings[0].template_name def test_event_comparison_page_service_rejects_unknown_drilldown_template_slug() -> None: service = EventComparisonPageService() report = service.run_read_only_comparison( 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, ) with pytest.raises(ValueError, match="Unknown ranked template"): service.drilldown_model(report, template_slug="missing-template")