Files
PythagorasGoal/scripts/research/xsec_v3_momstruct.py
T
Adriano Dal Pastro cff5fa2bf5 research(sweep): 5 thread paralleli — 0 nuovi sleeve, STATARB-RESID LEAD ortogonale+eseguibile
Ricerca onesta su aree inesplorate (harness altlib+xsec_v2_nonmom, tutti i gate incl.
study_family_honest anti-selection-on-holdout). Branch main, nessun impatto live, test 143/143.

1 XSEC low-risk cousins (MAX/idio-vol/Amihud) -> 1 LEAD (IVOL), STAT-MODE, DSR 0.37<0.95
2 XSEC momentum-structure vs XS01            -> tutto REDUNDANT (sostituire XS01 distrugge hold)
3 Meta-allocazione dinamica (4 sleeve)       -> pesi fissi vincono (gia quasi risk-parity)
4 Segnali ortogonali ETH/BTC (2 gambe)       -> STATARB-RESID + DVOLSPREAD LEAD
5 1-gamba a segnale (MACD/RSI/Supertrend/...) -> 0/12 earns_slot (trend=TP01, MR morta, hedge)

LEAD principale STATARB-RESID (mean-rev residuo ETH-b*BTC, OLS rolling, 2 gambe): primo stream
INSIEME ortogonale (corr->book 0.027, beta-mkt 0.013) ED eseguibile a $600 (haircut ~0, NON
STAT-MODE) -> cadono i 2 muri di XS01/opzioni. Resta solo il muro dell'edge (Sharpe 0.84,
DSR 0.929 same-sign <0.95). Causalita+fee verificate dal coordinatore. Forward-monitor, non sleeve.

Soffitto direzionale ~1.3 riconfermato. Diario 2026-06-29-strategy-search-5threads.md, CLAUDE.md agg.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 20:50:33 +00:00

418 lines
19 KiB
Python

"""XSEC v3 — varianti STRUTTURALI di momentum cross-sectional su Hyperliquid (STAT-MODE).
TESI (filone XS). XS01 (sleeve attivo) e' momentum cross-sectional sui 19 major: blend di lookback
[30,90] (z-score cross-sectional mediato) + gate di dispersione, vol-target 20%. Lezione del
progetto (diari 2026-06-19/20): "i margini su XS sono nella STRUTTURA DEL SEGNALE, non nel numero
di asset". Quindi NON allarghiamo l'universo: testiamo 4 COSTRUZIONI di momentum STRUTTURALMENTE
diverse e chiediamo se MIGLIORANO o DIVERSIFICANO XS01 (o se sono solo XS01 travestito).
Varianti (tutte L/S dollar-neutral, top-k/bottom-k, CAUSALI; long alto score / short basso score):
RAMOM - RISK-ADJUSTED momentum: score = ritorno cumulato su L / vol realizzata su L
(momentum "Sharpe-like", non grezzo). Penalizza i trend rumorosi.
ACCEL - momentum ACCELERATION: score = mom(L_breve) - mom(L_lungo), la curvatura/2a differenza
del trend relativo (chi sta accelerando vs chi sta decelerando).
FIP - FROG-IN-THE-PAN / information discreteness: score = sign(mom) * ID, dove
ID = |%giorni-su - %giorni-giu| su L. Privilegia i trend LISCI (path consistente).
VOLSC - VOLATILITY-MANAGED momentum (Moreira-Muir): selezione = momentum, ma la LEVA del book
e' scalata dall'inverso della vol di MERCATO cross-section recente (rischia di piu' a
mercato calmo, meno in tempesta) invece del vol-target sulla vol della STRATEGIA.
GIUDIZIO = MARGINALE vs XS01, non assoluto. Una variant con corr~0.9 a XS01 e Sharpe simile NON
aggiunge nulla (e' XS01 travestito). Per ognuna calcolo: (a) corr vs XS01 e TP01; (b) uplift del
PORTAFOGLIO 4->5 sleeve a 10%/15%; (c) SOSTITUZIONE di XS01 con la variant a parita' di peso. Vince
solo se DIVERSIFICA (corr<0.7) E migliora l'hold-out aggiunta, OPPURE DOMINA XS01 a parita' di slot.
GATE (CLAUDE.md, metodologia obbligatoria):
1. griglia L in {30,60,90} (Ls/Ll per ACCEL), H in {5,10}, k in {5,8}, ENTRAMBI universi (51/19).
2. CAUSALE: score a close[i], peso tenuto in i+1 (engine shifta); vol=0 gata; prefix-check ok.
3. NETTO fee 0.10% RT su ogni gamba/ribilancio + turnover; sweep fee monotona (test).
4. DEFLATED Sharpe sul best con TUTTI gli Sharpe FULL come trial (multiple-testing; serve >0.95).
5. per-anno + HOLD-OUT 2025-01-01. ANTI selection-on-holdout: riporto best per IN-SAMPLE(<2025)
E best per HOLD, e verifico col deflated-Sharpe.
6. CAVEAT IMMUTABILE: book a molte gambe NON eseguibile a $600 -> STAT-MODE, MAI deploy.
uv run python scripts/research/xsec_v3_momstruct.py
"""
from __future__ import annotations
import sys
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(PROJECT_ROOT / "scripts" / "research"))
sys.path.insert(0, str(PROJECT_ROOT))
import numpy as np
import pandas as pd
import xsec_v2_nonmom as xv # harness collaudato (load_matrix, xs_engine, evalcfg, ...)
from src.portfolio.sleeves import XS_UNIVERSE
DPY = xv.DPY
TV = xv.TV
FEE = xv.FEE
HOLDOUT = xv.HOLDOUT
# ===========================================================================
# SCORE BUILDERS — closure score_at(i)->(score[A], valid[A]) + warmup. CAUSALI (dati <= i).
# Modellati su make_mom/make_resid di xsec_v2_nonmom.py.
# ===========================================================================
def make_ramom(PX, L):
"""Risk-adjusted momentum: score = (px[i]/px[i-L]-1) / std(ritorni giornalieri su L)."""
px, n, A, DR, _ = xv._precompute(PX)
RVL = DR.rolling(L, min_periods=int(0.8 * L)).std().values
def score_at(i):
if i - L < 0:
return np.full(A, np.nan), np.zeros(A, bool)
r = px[i] / px[i - L] - 1.0
rv = RVL[i]
with np.errstate(invalid="ignore", divide="ignore"):
score = r / rv
valid = np.isfinite(score) & np.isfinite(px[i]) & np.isfinite(px[i - L]) & (rv > 0)
return score, valid
return score_at, L + 1
def make_accel(PX, Ls, Ll):
"""Acceleration: score = mom(Ls) - mom(Ll) (Ls<Ll), entrambi a close[i]."""
px, n, A, *_ = xv._precompute(PX)
def score_at(i):
if i - Ll < 0:
return np.full(A, np.nan), np.zeros(A, bool)
r_s = px[i] / px[i - Ls] - 1.0
r_l = px[i] / px[i - Ll] - 1.0
score = r_s - r_l
valid = np.isfinite(px[i]) & np.isfinite(px[i - Ls]) & np.isfinite(px[i - Ll])
return score, valid
return score_at, Ll + 1
def make_fip(PX, L):
"""Frog-in-the-pan / information discreteness: score = sign(mom_L) * |%up - %down| su L.
%up/%down calcolati sui SOLI giorni osservati (NaN pre-listing esclusi). Trend lisci -> |.| alto."""
px, n, A, DR, _ = xv._precompute(PX)
up = (DR > 0).astype(float).where(DR.notna())
dn = (DR < 0).astype(float).where(DR.notna())
mp = int(0.8 * L)
UPc = up.rolling(L, min_periods=mp).sum().values
DNc = dn.rolling(L, min_periods=mp).sum().values
CNT = DR.rolling(L, min_periods=mp).count().values
def score_at(i):
if i - L < 0:
return np.full(A, np.nan), np.zeros(A, bool)
r = px[i] / px[i - L] - 1.0
c = CNT[i]
with np.errstate(invalid="ignore", divide="ignore"):
pu = UPc[i] / c
pdn = DNc[i] / c
idd = np.abs(pu - pdn)
score = np.sign(r) * idd
valid = (np.isfinite(px[i]) & np.isfinite(px[i - L]) & np.isfinite(idd) & (c >= mp))
return score, valid
return score_at, L + 1
# ===========================================================================
# ENGINE volatility-managed (VOLSC): selezione momentum top-k/bottom-k IDENTICA a xs_engine, ma il
# vol-target NON e' sulla vol della STRATEGIA bensi' sull'inverso della vol di MERCATO cross-section
# (equal-weight) recente (Moreira-Muir). Distinzione strutturale unica da XS01. CAUSALE (shift(1)).
# ===========================================================================
def xs_engine_mktvol(PX, VOL, score_at, H, k, B_mkt=20, target_vol=TV, fee=FEE, min_assets=10,
warmup=0, cap=3.0):
px = PX.values
vol = VOL.values
n, A = px.shape
dret = np.full((n, A), np.nan)
dret[1:] = px[1:] / px[:-1] - 1.0
W = np.zeros((n, A))
w = np.zeros(A)
for i in range(n):
if i >= warmup and i % H == 0:
score, valid = score_at(i)
valid = valid & np.isfinite(score) & (vol[i] > 0)
idxv = np.where(valid)[0]
if len(idxv) >= min_assets:
kk = min(k, len(idxv) // 2)
order = idxv[np.argsort(score[idxv])]
lo, hi = order[:kk], order[-kk:]
w = np.zeros(A)
w[hi] = 0.5 / kk
w[lo] = -0.5 / kk
else:
w = np.zeros(A)
W[i] = w
gross = np.zeros(n)
gross[1:] = np.nansum(W[:-1] * np.nan_to_num(dret[1:]), axis=1)
turn = np.zeros(n)
turn[0] = np.abs(W[0]).sum()
turn[1:] = np.abs(np.diff(W, axis=0)).sum(axis=1)
net = gross - turn * (fee / 2.0)
s = pd.Series(net, index=PX.index)
# vol-target sulla vol di MERCATO (equal-weight), causale (shift 1): leva alta a mercato calmo
mkt = PX.pct_change().mean(axis=1)
sig_mkt = mkt.rolling(B_mkt, min_periods=int(0.6 * B_mkt)).std().shift(1) * np.sqrt(DPY)
scale = np.clip(np.nan_to_num(target_vol / sig_mkt.replace(0, np.nan).values, nan=0.0), 0, cap)
turn_py = float(turn.sum() / (n / DPY)) if n else 0.0
return pd.Series(s.values * scale, index=PX.index), turn_py
def caus_check_mktvol(PX, VOL, builder, cfg, B_mkt=20, frac=0.85, tail=60, tol=1e-9):
"""Prefix-check di causalita' per il pipeline VOLSC (engine custom): ricostruisce su un prefisso
e confronta la coda con la run completa. Look-ahead -> divergenza."""
sa, warm = builder(PX, cfg)
full, _ = xs_engine_mktvol(PX, VOL, sa, cfg["H"], cfg["k"], B_mkt=B_mkt, warmup=warm)
cut = int(len(PX) * frac)
PXc, VOLc = PX.iloc[:cut], VOL.iloc[:cut]
sa2, warm2 = builder(PXc, cfg)
pre, _ = xs_engine_mktvol(PXc, VOLc, sa2, cfg["H"], cfg["k"], B_mkt=B_mkt, warmup=warm2)
lo = max(0, cut - tail)
a = full.values[lo:cut]
b = pre.values[lo:cut]
worst = float(np.max(np.abs(a - b))) if len(a) else float("nan")
return dict(ok=bool(worst <= tol), max_tail_diff=worst, cut=cut, tail=len(a))
# ===========================================================================
# REGISTRY varianti: builder(PX,p)->(score_at,warm), griglia config, engine.
# ===========================================================================
def variants():
Lg = (30, 60, 90)
Hk = [dict(H=H, k=k) for H in (5, 10) for k in (5, 8)]
accel_pairs = [(30, 60), (30, 90), (60, 90)]
return {
"RAMOM": dict(
builder=lambda PX, p: make_ramom(PX, p["L"]),
cfgs=[dict(L=L, **hk) for L in Lg for hk in Hk],
engine="std", B_mkt=None),
"ACCEL": dict(
builder=lambda PX, p: make_accel(PX, p["Ls"], p["Ll"]),
cfgs=[dict(Ls=ls, Ll=ll, **hk) for (ls, ll) in accel_pairs for hk in Hk],
engine="std", B_mkt=None),
"FIP": dict(
builder=lambda PX, p: make_fip(PX, p["L"]),
cfgs=[dict(L=L, **hk) for L in Lg for hk in Hk],
engine="std", B_mkt=None),
"VOLSC": dict(
builder=lambda PX, p: xv.make_mom(PX, p["L"], +1),
cfgs=[dict(L=L, **hk) for L in Lg for hk in Hk],
engine="mktvol", B_mkt=20),
}
def run_variant_cfg(PX, VOL, v, p):
sa, warm = v["builder"](PX, p)
if v["engine"] == "mktvol":
s, turn = xs_engine_mktvol(PX, VOL, sa, p["H"], p["k"], B_mkt=v["B_mkt"], warmup=warm)
else:
s, turn = xv.xs_engine(PX, VOL, sa, p["H"], p["k"], warmup=warm)
return xv.to_daily(s), turn
def tag(p):
return " ".join(f"{kk}{vv}" for kk, vv in p.items())
def run_grid(PX, VOL, v, xs_daily, tp_daily, uname):
rows = []
for p in v["cfgs"]:
daily, turn = run_variant_cfg(PX, VOL, v, p)
if daily.std() == 0 or len(daily) < 60:
continue
f, h, pct = xv.evalcfg(daily)
ins = xv.metrics(daily[daily.index < HOLDOUT])["sharpe"]
rows.append(dict(cfg=p, uni=uname, daily=daily, full=f["sharpe"], hold=h["sharpe"],
ins=ins, dd=f["maxdd"], ret=f["ret"], pct=pct,
corrXS=xv._corr(daily, xs_daily), corrTP=xv._corr(daily, tp_daily),
turn=turn))
return rows
# ===========================================================================
# PORTAFOGLIO — base cablata una sola volta (cache sleeve riusate per uplift+sostituzione).
# ===========================================================================
_BASE = None
_BASE_M = None
def _base():
global _BASE, _BASE_M
if _BASE is None:
_BASE = xv.active_sleeves()
pf = xv.StrategyPortfolio(_BASE)
pf.backtest() # warma le cache degli sleeve
_BASE_M = (xv.metrics(pf.combined_daily()), xv.metrics(pf.combined_daily(lo=HOLDOUT)))
return _BASE, _BASE_M
def add_uplift(daily, fr):
base, _ = _base()
wraw = fr / (1.0 - fr)
cand = xv.Sleeve("XSV3_cand", wraw, lambda d=daily: d)
pf = xv.StrategyPortfolio(base + [cand])
return (xv.metrics(pf.combined_daily()), xv.metrics(pf.combined_daily(lo=HOLDOUT)),
pf.weights().get("XSV3_cand", 0.0))
def substitute_xs01(daily):
base, _ = _base()
sub = [xv.Sleeve("XSV3_sub", s.weight, lambda d=daily: d) if s.name == "XS01_xsec_hl" else s
for s in base]
pf = xv.StrategyPortfolio(sub)
return xv.metrics(pf.combined_daily()), xv.metrics(pf.combined_daily(lo=HOLDOUT))
# ===========================================================================
# REPORT
# ===========================================================================
def per_year(daily):
out = []
for y, g in daily.groupby(daily.index.year):
out.append((int(y), round(float((1 + g).prod() - 1), 3)))
return out
def variant_verdict(pick, up_best, sub_full_d, sub_hold_d, caus_ok):
cx = abs(pick["corrXS"])
if not caus_ok:
return "SCARTATO", "non causale (prefix-check fallito)"
if pick["full"] <= 0.3 or pick["hold"] <= 0:
return "SCARTATO", f"standalone debole (FULL {pick['full']:+.2f}, HOLD {pick['hold']:+.2f})"
dominates = (sub_full_d > 0.02 and sub_hold_d > 0.05)
diversifies = (cx < 0.7) and (up_best[1] > 0.05) # up_best=(Δfull,Δhold)
if dominates:
return "MIGLIORA-XS01", f"sostituendo XS01 il book sale FULL {sub_full_d:+.2f} / HOLD {sub_hold_d:+.2f}"
if diversifies:
return "DIVERSIFICA", f"corrXS {pick['corrXS']:+.2f}<0.7 e uplift HOLD aggiunta {up_best[1]:+.2f}"
if cx >= 0.7:
return "REDUNDANT", f"corrXS {pick['corrXS']:+.2f} alta -> momentum XS01 travestito"
return "REDUNDANT", f"scorrelata (corrXS {pick['corrXS']:+.2f}) ma non additiva (uplift HOLD {up_best[1]:+.2f}, sub HOLD {sub_hold_d:+.2f})"
def main():
print("=" * 104)
print(" XSEC v3 — VARIANTI STRUTTURALI di momentum cross-sectional (RAMOM/ACCEL/FIP/VOLSC) — STAT-MODE")
print("=" * 104)
tp_daily = xv.tp01_sleeve().daily()
xs_daily = xv.xsec_sleeve().daily()
print(" riferimenti: XS01 (momentum blend+gate, sleeve attivo) e TP01 (trend BTC/ETH).")
xs_f = xv.metrics(xs_daily)
xs_h = xv.metrics(xs_daily[xs_daily.index >= HOLDOUT])
print(f" XS01 standalone: FULL Sh {xs_f['sharpe']:.2f} DD {xs_f['maxdd']*100:.0f}% | "
f"HOLD Sh {xs_h['sharpe']:.2f}")
universes = {"51-all": None, "19-major": XS_UNIVERSE}
mats = {}
for uname, u in universes.items():
PX, VOL = xv.load_matrix(u)
mats[uname] = (PX, VOL)
print(f" universo {uname:<9}: {PX.shape[1]} asset, {PX.shape[0]} giorni "
f"[{PX.index[0].date()} -> {PX.index[-1].date()}]")
vdefs = variants()
all_full = []
per_var_rows = {}
for vname, v in vdefs.items():
rows_all = []
for uname, (PX, VOL) in mats.items():
rows = run_grid(PX, VOL, v, xs_daily, tp_daily, uname)
rows_all += rows
all_full += [r["full"] for r in rows]
per_var_rows[vname] = rows_all
base, (bf, bh) = _base()
print(f"\n BASE portafoglio (4 sleeve attivi): FULL Sh {bf['sharpe']:.2f} DD {bf['maxdd']*100:.0f}%"
f" | HOLD Sh {bh['sharpe']:.2f} DD {bh['maxdd']*100:.0f}%")
summary = []
for vname, v in vdefs.items():
rows = per_var_rows[vname]
if not rows:
print(f"\n [{vname}] nessuna config valida.")
continue
n = len(rows)
pos_full = sum(r["full"] > 0 for r in rows)
pos_hold = sum(r["hold"] > 0 for r in rows)
pick_ins = max(rows, key=lambda r: (r["ins"], r["full"])) # selezione ONESTA (in-sample)
pick_hold = max(rows, key=lambda r: r["hold"]) # ceiling ottimistico
print("\n" + "#" * 104)
print(f"# {vname} | {n} config x2 universi | plateau FULL>0 {pos_full}/{n} | HOLD>0 {pos_hold}/{n}")
print("#" * 104)
print(f" {'pick':<12}{'cfg':<24}{'uni':<10}{'FULL':>6}{'INS':>6}{'HOLD':>6}{'DD%':>6}"
f"{'ret%':>7}{'an+':>6}{'crXS':>7}{'crTP':>7}{'t/y':>7}")
for lbl, r in (("by-INS<2025", pick_ins), ("by-HOLD", pick_hold)):
print(f" {lbl:<12}{tag(r['cfg']):<24}{r['uni']:<10}{r['full']:>6.2f}{r['ins']:>6.2f}"
f"{r['hold']:>6.2f}{r['dd']*100:>6.0f}{r['ret']*100:>+7.0f}{r['pct']*100:>5.0f}%"
f"{r['corrXS']:>+7.2f}{r['corrTP']:>+7.2f}{r['turn']:>7.0f}")
# top-3 per IN-SAMPLE per leggere il plateau
print(" --- top-3 by IN-SAMPLE Sharpe (plateau) ---")
for r in sorted(rows, key=lambda r: -r["ins"])[:3]:
print(f" {tag(r['cfg']):<24}{r['uni']:<10}FULL {r['full']:+.2f} INS {r['ins']:+.2f}"
f" HOLD {r['hold']:+.2f} corrXS {r['corrXS']:+.2f}")
# ---- gate sul pick_ins (selezione onesta) ----
pick = pick_ins
v_uni = pick["uni"]
PX, VOL = mats[v_uni]
if v["engine"] == "mktvol":
caus = caus_check_mktvol(PX, VOL, v["builder"], pick["cfg"], B_mkt=v["B_mkt"])
else:
caus = xv.causality_prefix_check(PX, VOL, v["builder"], pick["cfg"])
dsr, sr0 = xv.deflated_sharpe(pick["full"], all_full, pick["daily"])
print(f" CAUSALITA' (prefix-check) ok={caus['ok']} max_tail_diff={caus['max_tail_diff']:.2e}")
print(f" DEFLATED Sharpe (N={len([s for s in all_full if np.isfinite(s)])} trial GLOBALI): "
f"{dsr:.3f} | soglia Sharpe-max-null {sr0:.2f} (serve >0.95)")
print(f" per-anno (pick-INS): {per_year(pick['daily'])}")
# ---- portafoglio: uplift 4->5 e SOSTITUZIONE di XS01 (a parita' di peso) ----
print(" UPLIFT (aggiunta come 5o sleeve):")
up_best = (-9.0, -9.0)
for fr in (0.10, 0.15):
cf, ch, wgt = add_uplift(pick["daily"], fr)
df_, dh_ = cf["sharpe"] - bf["sharpe"], ch["sharpe"] - bh["sharpe"]
print(f" @{wgt*100:>4.1f}% FULL {cf['sharpe']:.2f} ({df_:+.2f}) DD {cf['maxdd']*100:.0f}%"
f" | HOLD {ch['sharpe']:.2f} ({dh_:+.2f})")
if (df_ + dh_) > (up_best[0] + up_best[1]):
up_best = (df_, dh_)
sf, sh = substitute_xs01(pick["daily"])
sub_full_d, sub_hold_d = sf["sharpe"] - bf["sharpe"], sh["sharpe"] - bh["sharpe"]
print(f" SOSTITUZIONE XS01->{vname} (peso {base[1].weight:.4f}): "
f"FULL {sf['sharpe']:.2f} ({sub_full_d:+.2f}) DD {sf['maxdd']*100:.0f}%"
f" | HOLD {sh['sharpe']:.2f} ({sub_hold_d:+.2f})")
verdict, why = variant_verdict(pick, up_best, sub_full_d, sub_hold_d, caus["ok"])
print(f" >>> VERDETTO {vname}: {verdict}{why}")
summary.append(dict(name=vname, pick=pick, dsr=dsr, caus=caus["ok"], up=up_best,
sub=(sub_full_d, sub_hold_d), verdict=verdict))
# ---- SINTESI ----
print("\n" + "=" * 104)
print(" SINTESI — giudizio MARGINALE vs XS01 (sleeve attivo)")
print("=" * 104)
print(f" {'variant':<8}{'FULL':>6}{'HOLD':>6}{'DD%':>6}{'corrXS':>8}{'corrTP':>8}{'DSR':>7}"
f"{'+upHOLD':>9}{'subHOLD':>9} verdetto")
for s in summary:
p = s["pick"]
print(f" {s['name']:<8}{p['full']:>6.2f}{p['hold']:>6.2f}{p['dd']*100:>6.0f}"
f"{p['corrXS']:>+8.2f}{p['corrTP']:>+8.2f}{s['dsr']:>7.3f}{s['up'][1]:>+9.2f}"
f"{s['sub'][1]:>+9.2f} {s['verdict']}")
winners = [s for s in summary if s["verdict"] in ("MIGLIORA-XS01", "DIVERSIFICA")]
print("\n CONCLUSIONE:")
if not winners:
print(" NESSUNA variante batte o diversifica davvero XS01. Tutte sono momentum-family ad")
print(" alta corr con XS01 e/o non additive al portafoglio -> REDUNDANT/SCARTATO. La")
print(" struttura del segnale (risk-adj/accel/smoothness/vol-timing) NON apre uno slot nuovo.")
else:
for s in winners:
print(f" {s['name']}: {s['verdict']} (forward-monitor). corrXS {s['pick']['corrXS']:+.2f}, "
f"+upHOLD {s['up'][1]:+.2f}, subHOLD {s['sub'][1]:+.2f}, DSR {s['dsr']:.3f}.")
print("\n CAVEAT (immutabili): storia ~2.5 anni (deflated-Sharpe + multiple-testing); book a molte")
print(" gambe NON eseguibile a $600 -> STAT-MODE / forward-monitor, MAI deploy. Nessuno sleeve")
print(" registrato: e' lavoro statistico (vincoli del filone XS).")
if __name__ == "__main__":
main()