commands: m2d-log + backtest + batch + stats slash commands (124 tests pass)

This commit is contained in:
Marius
2026-05-13 12:48:26 +03:00
parent 26d084dc4b
commit 34af5b631e
7 changed files with 1111 additions and 730 deletions

83
tests/test_stats_ci.py Normal file
View 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