Files
PythagorasGoal/tests/test_weights_tilt_null.py
T
Adriano Dal Pastro 491411ac77 research(wave-0701): 6 filoni multi-agente — 0 nuovi sleeve, pesi confermati, gate weights_tilt_null
Ondata onesta su angoli non coperti: funding-TS (chiude il filone funding su 3
lati), breadth alt (non-ridondante ma DSR 0.43, rivisitabile con storia),
XS-residmom (REDUNDANT), pesi+guardia-DD (EW-STR refutato dallo scettico come
selezione-sull'hold-out di 2° ordine, firma best-of-15), VRP-refine (filone
esaurito), stagionalità-XS (morta allo step statistico).

Lezione codificata: weights_tilt_null + combine_outer in src/portfolio
(ogni cambio-pesi vs null di tilt casuali cap-respecting + delta in-sample>=0);
5 test nuovi, suite 165/165.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-01 23:21:59 +00:00

83 lines
3.4 KiB
Python

"""Test del gate weights_tilt_null (lezione 2026-07-01: EW-STR refutato come best-of-k).
Dati SINTETICI deterministici: 3 sleeve a date d'inizio diverse, di cui uno ("C") con
Sharpe gonfiato SOLO nell'hold-out — il tilt verso C deve risultare sospetto (percentile
alto fra i tilt casuali, delta_insample ~<=0), mentre un tilt nullo deve essere innocuo.
"""
import sys
from pathlib import Path
import numpy as np
import pandas as pd
import pytest
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from src.portfolio.portfolio import HOLDOUT, StrategyPortfolio, Sleeve, combine_outer, weights_tilt_null
def _mk_daily(start: str, n: int, mu: float, sigma: float, seed: int,
mu_holdout: float | None = None) -> pd.Series:
rng = np.random.default_rng(seed)
idx = pd.date_range(start, periods=n, freq="1D", tz="UTC")
r = rng.normal(mu, sigma, n)
if mu_holdout is not None:
m = idx >= HOLDOUT
r[m] = rng.normal(mu_holdout, sigma, int(m.sum()))
return pd.Series(r, index=idx)
@pytest.fixture(scope="module")
def cols() -> dict:
n = 2600 # ~2019-07 -> 2026-08: copre pre e post hold-out
return {
"A": _mk_daily("2019-07-01", n, 8e-4, 0.010, seed=1),
"B": _mk_daily("2021-01-01", n - 550, 6e-4, 0.012, seed=2),
# C: rumore pre-holdout, forte SOLO nell'hold-out (imita lo sleeve selezionato sull'OOS)
"C": _mk_daily("2019-07-01", n, 0.0, 0.011, seed=3, mu_holdout=18e-4),
}
def test_combine_outer_equivale_a_combined_daily(cols):
sleeves = [Sleeve(nm, w, daily_fn=(lambda s=cols[nm]: s))
for nm, w in [("A", 0.5), ("B", 0.3), ("C", 0.2)]]
port = StrategyPortfolio(sleeves)
a = port.combined_daily()
b = combine_outer(cols, {"A": 0.5, "B": 0.3, "C": 0.2})
assert np.allclose(a.values, b.values) and a.index.equals(b.index)
def test_tilt_identico_e_neutro(cols):
w = {"A": 0.5, "B": 0.3, "C": 0.2}
rep = weights_tilt_null(cols, w, w, n=100, seed=7)
assert rep["delta_hold"] == 0.0 and rep["delta_full"] == 0.0 and rep["delta_insample"] == 0.0
assert rep["n_samples"] == 100
assert 0.0 <= rep["frac_random_beat_hold"] <= 1.0
def test_vincoli_floor_caps_rispettati(cols):
rep = weights_tilt_null(cols, {"A": 0.5, "B": 0.3, "C": 0.2}, {"A": 0.4, "B": 0.3, "C": 0.3},
caps={"B": 0.35}, floor=0.05, n=150, seed=11)
S = rep["samples"]
assert (S >= 0.05 - 1e-12).all() and (S[:, 1] <= 0.35 + 1e-12).all()
assert np.allclose(S.sum(axis=1), 1.0)
def test_tilt_verso_sleeve_holdout_only_e_sospetto(cols):
"""Tilt verso C (edge solo hold-out): delta_hold>0 ma insample<=~0 -> gate_pass False."""
w_cur = {"A": 0.5, "B": 0.3, "C": 0.2}
w_pro = {"A": 0.30, "B": 0.25, "C": 0.45}
rep = weights_tilt_null(cols, w_cur, w_pro, n=300, seed=13, k_seen=15)
assert rep["delta_hold"] > 0 # sull'hold-out "vince" (per costruzione)
assert rep["delta_insample"] <= 0.05 # ma pre-holdout non c'e' edge
assert rep["bestofk_pctl"] == pytest.approx(100 * 15 / 16)
assert not rep["gate_pass"]
def test_determinismo(cols):
w_cur = {"A": 0.5, "B": 0.3, "C": 0.2}
w_pro = {"A": 0.4, "B": 0.3, "C": 0.3}
r1 = weights_tilt_null(cols, w_cur, w_pro, n=80, seed=42)
r2 = weights_tilt_null(cols, w_cur, w_pro, n=80, seed=42)
assert r1["pctl_hold"] == r2["pctl_hold"] and np.allclose(r1["samples"], r2["samples"])