from backtesting import engine, statistics def test_moving_block_bootstrap_is_deterministic_and_handles_constant_series(): first = statistics.moving_block_bootstrap_ci( [12.5] * 120, block_size=15, n_resamples=200, seed=7, ) second = statistics.moving_block_bootstrap_ci( [12.5] * 120, block_size=15, n_resamples=200, seed=7, ) assert first == second assert first == {"estimate": 12.5, "ci_low": 12.5, "ci_high": 12.5, "n": 120} def test_summarize_returns_reports_observations_and_block_bootstrap_interval(): summary = statistics.summarize_returns( [10.0, -5.0, 20.0, -10.0], block_size=2, n_resamples=200, seed=3, ) assert summary["n"] == 4 assert summary["mean"] == 3.75 assert summary["median"] == 2.5 assert summary["win_rate"] == 50.0 assert summary["mean_ci_low"] <= summary["mean"] <= summary["mean_ci_high"] def test_backtest_brackets_publish_bootstrap_confidence_intervals(): stats = {} engine._add_return_statistics(stats, "90d", [10.0, -5.0, 20.0, -10.0]) assert stats["avg_90d"] == 3.75 assert stats["median_90d"] == 2.5 assert stats["win_rate_90d"] == 50.0 assert stats["avg_90d_ci_low"] <= stats["avg_90d"] <= stats["avg_90d_ci_high"] def test_long_horizon_returns_use_a_matching_dependence_block(monkeypatch): observed = {} def fake_summary(values, *, block_size, n_resamples): observed.update(block_size=block_size, n_resamples=n_resamples) return { "mean": 1.0, "median": 1.0, "win_rate": 100.0, "mean_ci_low": 0.5, "mean_ci_high": 1.5, "n": len(values), } monkeypatch.setattr(engine, "summarize_returns", fake_summary) engine._add_return_statistics({}, "365d", [1.0] * 500) assert observed == {"block_size": 365, "n_resamples": 400}