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>
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
"""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"])
|
||||
Reference in New Issue
Block a user