62d3b23cc6
Lo scorer fisso-HOLDOUT + jackknife-mese era ingannabile: 17/18 book relative-value "ADDS"
su una sola finestra 2025 (ETH-bleed dove TP01 è debole). Tre gate nuovi in
altlib.marginal_vs_tp01:
1. persistenza multi-cut (uplift a più date di taglio, non solo 2025) -> robust_oos
2. has_insample_edge: Sharpe standalone PRE-holdout >= 0.5 (la basket faceva 0.29).
null_pctl_* (vs asset-rumore corr-zero) restano come CONTESTO (diversification math).
3. is_hedge: low-corr che paga solo quando TP01 è debole = hedge, non alpha.
Verdetti nuovi HEDGE/NOISE; earns_slot = ADDS + robust_oos + has_insample_edge + not hedge.
Effetto: sull'onda ortho 17/18 "ADDS" -> 1 (dvol_spread, unico con edge in-sample reale 0.57);
gli altri 16 -> NOISE/HEDGE. Un sleeve sintetico Sharpe~1.3 scorrelato resta ADDS (non rigetta
i diversificatori veri). +5 test (noise/hedge/single-regime/high-Sharpe-uncorr/in-sample-edge);
suite 37 passed. CLAUDE.md aggiornato.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
123 lines
4.8 KiB
Python
123 lines
4.8 KiB
Python
"""Locks the marginal-vs-TP01 scorer (altlib) — the harness fix from the 2026-06-20 sweep.
|
|
|
|
The point: absolute Sharpe is not enough to earn a portfolio slot. A candidate must
|
|
IMPROVE the TP01 baseline out-of-sample. These tests pin the invariants:
|
|
* the TP01 baseline reproduces the canonical ~1.30 full Sharpe,
|
|
* TP01-vs-itself is REDUNDANT (zero marginal),
|
|
* a flat (do-nothing) sleeve never earns a slot.
|
|
"""
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import numpy as np
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT / "scripts" / "research" / "alt"))
|
|
|
|
import altlib as al # noqa: E402
|
|
from src.strategies.trend_portfolio import CANONICAL, TrendPortfolio # noqa: E402
|
|
|
|
|
|
def test_tp01_baseline_reproduces_canonical():
|
|
r = al.tp01_baseline_daily().values
|
|
sharpe = float(np.mean(r) / np.std(r) * np.sqrt(365.25))
|
|
assert 1.1 < sharpe < 1.5, f"TP01 baseline Sharpe {sharpe:.2f} off canonical ~1.30"
|
|
|
|
|
|
def test_tp01_vs_itself_is_redundant():
|
|
cand = al.candidate_daily(lambda df: TrendPortfolio(**CANONICAL).target_series(df))
|
|
m = al.marginal_vs_tp01(cand)
|
|
assert m["corr_full"] > 0.95
|
|
assert abs(m["blends"]["w25"]["uplift_full"]) < 0.05
|
|
assert m["marginal_verdict"] == "REDUNDANT"
|
|
assert al.study_marginal("tp01-self", lambda df: TrendPortfolio(**CANONICAL).target_series(df))["earns_slot"] is False
|
|
|
|
|
|
def test_flat_sleeve_earns_no_slot():
|
|
m = al.marginal_vs_tp01(al.candidate_daily(lambda df: np.zeros(len(df))))
|
|
assert m["marginal_verdict"] != "ADDS"
|
|
|
|
|
|
def test_call_target_passes_asset_when_accepted():
|
|
seen = {}
|
|
|
|
def two_arg(df, asset):
|
|
seen[asset] = True
|
|
return np.zeros(len(df))
|
|
|
|
al.candidate_daily(two_arg)
|
|
assert seen == {"BTC": True, "ETH": True}
|
|
|
|
|
|
# --- hardening gates (2026-06-21 ortho wave) -------------------------------------------
|
|
# A low-corr asset adds ~+0.03 Sharpe by pure diversification MATH; a sleeve that only
|
|
# pays when TP01 is weak is a HEDGE; an edge living in one late window is NOT robust.
|
|
# These pin that the scorer rejects all three, but still PASSES a real high-Sharpe
|
|
# uncorrelated sleeve (the diversification of a genuine return stream IS value).
|
|
import pandas as pd # noqa: E402
|
|
|
|
|
|
def _tp01_index():
|
|
return al.tp01_baseline_daily().index
|
|
|
|
|
|
def test_noise_sleeve_is_not_adds():
|
|
"""Near-zero-Sharpe, zero-corr asset = diversification math, not signal -> not ADDS."""
|
|
idx = _tp01_index()
|
|
rng = np.random.default_rng(1)
|
|
c = pd.Series(rng.normal(0.0001, 0.02, len(idx)), index=idx)
|
|
m = al.marginal_vs_tp01(c)
|
|
assert m["marginal_verdict"] != "ADDS"
|
|
assert m["beats_noise_null"] is False
|
|
|
|
|
|
def test_high_sharpe_uncorrelated_is_not_rejected_as_noise():
|
|
"""A genuine Sharpe~1.3 uncorrelated sleeve (XS01-like) must NOT be called NOISE."""
|
|
idx = _tp01_index()
|
|
rng = np.random.default_rng(2)
|
|
c = pd.Series(rng.normal(0.0011, 0.013, len(idx)), index=idx)
|
|
m = al.marginal_vs_tp01(c)
|
|
assert m["beats_noise_null"] is True
|
|
assert m["marginal_verdict"] == "ADDS"
|
|
|
|
|
|
def test_hedge_only_when_tp01_weak_is_flagged():
|
|
"""A sleeve that pays only when TP01 trails down is a HEDGE, not alpha -> no slot."""
|
|
B = al.tp01_baseline_daily()
|
|
trail = B.rolling(60, min_periods=20).sum().shift(1)
|
|
base = np.where(trail.values <= 0, 0.004, -0.001)
|
|
rng = np.random.default_rng(4)
|
|
c = pd.Series(base + rng.normal(0, 0.004, len(B)), index=B.index)
|
|
m = al.marginal_vs_tp01(c)
|
|
assert m["is_hedge"] is True
|
|
assert m["marginal_verdict"] == "HEDGE"
|
|
|
|
|
|
def test_flat_in_sample_late_edge_earns_no_slot():
|
|
"""An edge FLAT in-sample that only appears in the recent window has NO standalone
|
|
in-sample merit -> NOISE (not ADDS), no slot. This is the ortho 2025-window pattern."""
|
|
B = al.tp01_baseline_daily()
|
|
rng = np.random.default_rng(3)
|
|
c = pd.Series(0.0, index=B.index) # exactly flat in-sample
|
|
hold = B.index >= al.HOLDOUT
|
|
c[hold] = 0.004 + rng.normal(0, 0.002, int(hold.sum())) # edge only in the hold-out
|
|
m = al.marginal_vs_tp01(c)
|
|
assert m["has_insample_edge"] is False
|
|
assert m["marginal_verdict"] == "NOISE"
|
|
assert al.marginal_vs_tp01(c)["marginal_verdict"] != "ADDS"
|
|
|
|
|
|
def test_pre_holdout_underperformer_earns_no_slot():
|
|
"""A sleeve with no real in-sample standalone edge (it bleeds before the hold-out and
|
|
only wins in the recent window) never earns a slot — the 2025-only ortho pattern."""
|
|
B = al.tp01_baseline_daily()
|
|
early = B.index < al.HOLDOUT
|
|
rng = np.random.default_rng(5)
|
|
c = pd.Series(0.0, index=B.index)
|
|
c[early] = -0.0008 + rng.normal(0, 0.012, int(early.sum())) # weak/negative in-sample
|
|
c[~early] = 0.010 + rng.normal(0, 0.012, int((~early).sum())) # strong only post-hold-out
|
|
m = al.marginal_vs_tp01(c)
|
|
assert m["cand_insample_sharpe"] < 0.5
|
|
assert m["has_insample_edge"] is False
|
|
assert m["marginal_verdict"] != "ADDS"
|