from __future__ import annotations from libs.intraday.domain import DayResult, IntradayConfig, IntradayTrade, ORBStrategyParams from libs.intraday.metrics import IntradayMetricsAccumulator, compute_metrics, format_summary def test_no_trade_run_preserves_period_and_zero_return_metrics() -> None: config = IntradayConfig( strategy_mode="orb", orb_strategy=ORBStrategyParams(initial_capital=10_000.0), ) day_results = [ DayResult(date="2026-03-13", daily_pnl=0.0, daily_return_pct=0.0), DayResult(date="2026-03-16", daily_pnl=0.0, daily_return_pct=0.0), ] metrics = compute_metrics(day_results, config, run_id="test1234") summary = format_summary(metrics, config) assert metrics.run_id == "test1234" assert metrics.start_date == "2026-03-13" assert metrics.end_date == "2026-03-16" assert metrics.trading_days == 2 assert metrics.days_with_trades == 0 assert metrics.total_trades == 0 assert metrics.total_return_pct == 0.0 assert metrics.annualized_return_pct == 0.0 assert metrics.avg_daily_return_pct == 0.0 assert metrics.max_drawdown_pct == 0.0 assert metrics.final_equity == 10_000.0 assert "2026-03-13" in summary assert "2026-03-16" in summary assert "2 days (0 with trades)" in summary def test_metrics_accumulator_matches_batch_metrics() -> None: config = IntradayConfig( strategy_mode="orb", orb_strategy=ORBStrategyParams(initial_capital=10_000.0), ) trade = IntradayTrade( date="2026-03-13", ticker="AAA", entry_price=100.0, exit_price=101.0, entry_time="2026-03-13T09:35:00-05:00", exit_time="2026-03-13T15:55:00-05:00", shares=10, pnl=10.0, pnl_pct=0.01, exit_reason="close", ) day_results = [ DayResult( date="2026-03-13", trades=[trade], daily_pnl=10.0, daily_return_pct=0.001, capital_deployed=1_000.0, ), DayResult( date="2026-03-16", daily_pnl=-5.0, daily_return_pct=-0.0005, capital_deployed=0.0, ), ] batch = compute_metrics(day_results, config, run_id="batch") accumulator = IntradayMetricsAccumulator(config, run_id="stream") accumulator.extend(day_results) stream = accumulator.finalize() assert stream.start_date == batch.start_date assert stream.end_date == batch.end_date assert stream.total_trades == batch.total_trades assert stream.days_with_trades == batch.days_with_trades assert stream.final_equity == batch.final_equity assert stream.total_return_pct == batch.total_return_pct def test_metrics_accumulator_snapshot_roundtrip() -> None: config = IntradayConfig( strategy_mode="orb", orb_strategy=ORBStrategyParams(initial_capital=10_000.0), ) day_results = [ DayResult(date="2026-03-13", daily_pnl=12.0, daily_return_pct=0.0012), DayResult(date="2026-03-16", daily_pnl=-4.0, daily_return_pct=-0.0004), ] accumulator = IntradayMetricsAccumulator(config, run_id="snap") accumulator.extend(day_results) restored = IntradayMetricsAccumulator.from_snapshot( config, accumulator.snapshot(), run_id="restored", ) assert restored.run_id == "restored" assert restored.n_days == accumulator.n_days assert restored.days_with_trades == accumulator.days_with_trades assert restored.daily_returns == accumulator.daily_returns assert restored.equity == accumulator.equity assert restored.max_drawdown == accumulator.max_drawdown assert restored.finalize().final_equity == accumulator.finalize().final_equity def test_metrics_compute_loss_containment_fields() -> None: config = IntradayConfig( strategy_mode="orb", orb_strategy=ORBStrategyParams(initial_capital=10_000.0), ) base_trade = IntradayTrade( date="2026-03-13", ticker="AAA", entry_price=100.0, exit_price=101.0, entry_time="2026-03-13T09:35:00-05:00", exit_time="2026-03-13T15:55:00-05:00", shares=10, pnl=10.0, pnl_pct=0.01, exit_reason="close", ) day_results = [ DayResult(date="2026-03-13", trades=[base_trade], daily_pnl=100.0, daily_return_pct=0.01), DayResult(date="2026-03-16", trades=[base_trade.model_copy(update={"date": "2026-03-16", "pnl": -50.0, "pnl_pct": -0.005})], daily_pnl=-50.0, daily_return_pct=-0.005), DayResult(date="2026-03-17", trades=[base_trade.model_copy(update={"date": "2026-03-17", "pnl": -200.0, "pnl_pct": -0.02})], daily_pnl=-200.0, daily_return_pct=-0.02), DayResult(date="2026-03-18", trades=[base_trade.model_copy(update={"date": "2026-03-18", "pnl": 0.0, "pnl_pct": 0.0})], daily_pnl=0.0, daily_return_pct=0.0), ] metrics = compute_metrics(day_results, config, run_id="lossctl") assert metrics.loss_day_rate == 0.5 assert metrics.avg_loss_day_pct == -0.0125 assert metrics.tail_loss_20_pct == -0.02 assert metrics.worst_day_return_pct == -0.02 assert metrics.loss_containment_score is not None assert 0.0 < metrics.loss_containment_score < 100.0