Enterprise clean-rebuild (no foxhunt code). Hexagonal/ports-and-adapters: pure domain (gauntlet math, strategies, backtest, models) | ports (DataProvider, repositories) | adapters (Yahoo data, SQLAlchemy operational [Postgres/SQLite], DuckDB analytical) | application (ResearchService, DI) | CLI composition root. Gauntlet-first: Deflated Sharpe (Bailey-LdP) built + falsification-tested (kills best-of-N-on-noise, keeps real premium). Full vertical slice runs end-to-end on real data: data -> strategy(trend) -> backtest(net of costs) -> IS/OOS gauntlet -> persistence. 4/4 tests green. Postgres+DuckDB split, pydantic contracts, typed, DRY via one-contract-per-port. ADR + README. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
42 lines
1.7 KiB
Python
42 lines
1.7 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."""
|
|
real = RNG.normal(0.0005, 0.01, 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)
|