commands: m2d-log + backtest + batch + stats slash commands (124 tests pass)
This commit is contained in:
83
tests/test_stats_ci.py
Normal file
83
tests/test_stats_ci.py
Normal file
@@ -0,0 +1,83 @@
|
||||
"""Pure-math tests for stats CI primitives (no I/O)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
|
||||
from scripts.stats import bootstrap_expectancy_ci, wilson_ci # noqa: E402
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Wilson CI
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestWilsonCI:
|
||||
def test_wilson_n_zero(self) -> None:
|
||||
assert wilson_ci(0, 0) == (0.0, 0.0)
|
||||
|
||||
def test_wilson_perfect_winrate(self) -> None:
|
||||
lo, hi = wilson_ci(10, 10)
|
||||
assert lo > 0.65
|
||||
assert hi == pytest.approx(1.0, abs=1e-12)
|
||||
|
||||
def test_wilson_reference_15_55(self) -> None:
|
||||
"""wins=8, n=15 (WR≈53%) → CI approximately [29%, 76%] ±2%."""
|
||||
lo, hi = wilson_ci(8, 15)
|
||||
assert lo == pytest.approx(0.29, abs=0.02)
|
||||
assert hi == pytest.approx(0.76, abs=0.02)
|
||||
|
||||
def test_wilson_all_losses(self) -> None:
|
||||
lo, hi = wilson_ci(0, 10)
|
||||
assert lo == pytest.approx(0.0, abs=1e-12)
|
||||
assert hi < 0.35
|
||||
|
||||
def test_wilson_wins_out_of_range(self) -> None:
|
||||
with pytest.raises(ValueError):
|
||||
wilson_ci(11, 10)
|
||||
with pytest.raises(ValueError):
|
||||
wilson_ci(-1, 10)
|
||||
|
||||
def test_wilson_clamps_at_50pct_n40(self) -> None:
|
||||
"""Reference at WR=50%, N=40: CI ≈ [35.2%, 64.8%]."""
|
||||
lo, hi = wilson_ci(20, 40)
|
||||
assert lo == pytest.approx(0.352, abs=0.005)
|
||||
assert hi == pytest.approx(0.648, abs=0.005)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Bootstrap CI
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestBootstrap:
|
||||
def test_bootstrap_deterministic(self) -> None:
|
||||
values = [1.0, -0.5, 0.5, -1.0]
|
||||
a = bootstrap_expectancy_ci(values, n_resamples=1000, seed=42)
|
||||
b = bootstrap_expectancy_ci(values, n_resamples=1000, seed=42)
|
||||
assert a == b
|
||||
|
||||
def test_bootstrap_different_seed_different_result(self) -> None:
|
||||
values = [1.0, -0.5, 0.5, -1.0, 0.2, -0.3, 0.5]
|
||||
a = bootstrap_expectancy_ci(values, n_resamples=1000, seed=1)
|
||||
b = bootstrap_expectancy_ci(values, n_resamples=1000, seed=2)
|
||||
assert a != b
|
||||
|
||||
def test_bootstrap_empty(self) -> None:
|
||||
assert bootstrap_expectancy_ci([], n_resamples=100, seed=0) == (0.0, 0.0)
|
||||
|
||||
def test_bootstrap_single_value(self) -> None:
|
||||
lo, hi = bootstrap_expectancy_ci([0.5], n_resamples=100, seed=0)
|
||||
assert lo == pytest.approx(0.5, abs=1e-9)
|
||||
assert hi == pytest.approx(0.5, abs=1e-9)
|
||||
|
||||
def test_bootstrap_brackets_the_mean(self) -> None:
|
||||
values = [0.5, -1.0, 0.5, 0.5, -1.0, 0.2, -0.3, 0.5, -1.0, 0.5] * 5
|
||||
mean = sum(values) / len(values)
|
||||
lo, hi = bootstrap_expectancy_ci(values, n_resamples=1000, seed=7)
|
||||
assert lo <= mean <= hi
|
||||
Reference in New Issue
Block a user