Files
fxhnt/tests/unit/test_gauntlet.py
jgrusewski 39fa034f49 fix(test): strengthen gauntlet keep-premium signal to seed-robust (200/200)
Reviewer noted drift 0.001/vol 0.008 (Sharpe ~2) still failed the OOS-decay
check on ~5% of seeds (seed 11 happened to pass). Bump to 0.0015/0.007
(Sharpe ~3.4), verified 200/200 seeds pass — robust, not seed-lucky.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 20:17:44 +02:00

49 lines
2.2 KiB
Python

"""Falsification test for the gauntlet — the proof it's safe to scale a search on top of it.
It MUST reject a best-of-N artifact mined on noise, and KEEP a genuine N=1 premium."""
from __future__ import annotations
import numpy as np
from fxhnt.domain.gauntlet import annualized_sharpe, deflated_sharpe, evaluate
RNG = np.random.default_rng(7)
T = 2520
N_TRIALS = 1000
def test_rejects_best_of_n_on_noise() -> None:
"""Mine the best of N random strategies on pure noise; the gauntlet must reject it."""
trials = RNG.normal(0.0, 0.01, (N_TRIALS, T))
srs = trials.mean(1) / trials.std(1)
best = int(np.argmax(srs))
best_r = trials[best]
sr_var = float(srs.var())
assert annualized_sharpe(best_r) > 0.8 # looks great in-sample by luck
split = int(0.6 * T)
v = evaluate(best_r[:split], best_r[split:], n_trials=N_TRIALS, sr_variance=sr_var, has_economic_rationale=False)
assert not v.passed, "gauntlet accepted an overfit best-of-N artifact"
assert v.dsr < 0.95
def test_keeps_real_premium() -> None:
"""A genuine positive-drift series, tested as one hypothesis, must pass.
Uses a LOCAL seeded RNG (order-independent of the module RNG) and a genuinely STRONG premium
(drift 0.0015 / vol 0.007 → annualised Sharpe ~3.4 — passes on 200/200 seeds, i.e. seed-robust, not
seed-lucky). The prior marginal signal (0.0005 / 0.01, Sharpe ~0.8) had an in-sample mean that could
come out ~flat by chance — the gauntlet then CORRECTLY rejected it (no in-sample evidence), so the
test's own premise, not the gauntlet, was wrong. A real edge this test claims to keep must have
unambiguous in-sample drift."""
real = np.random.default_rng(11).normal(0.0015, 0.007, T)
split = int(0.6 * T)
v = evaluate(real[:split], real[split:], n_trials=1, sr_variance=0.0, has_economic_rationale=True)
assert v.passed, "gauntlet rejected a genuine OOS-confirmed premium"
assert v.dsr >= 0.95
def test_dsr_falls_as_trials_rise() -> None:
"""The same returns get a strictly lower deflated Sharpe as the search size grows."""
r = RNG.normal(0.0004, 0.01, T)
sr_var = 0.04
assert deflated_sharpe(r, 1, sr_var) >= deflated_sharpe(r, 100, sr_var) >= deflated_sharpe(r, 10000, sr_var)