Files
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

388 lines
20 KiB
Python

"""XSEC v3 — fattori cross-sectional "low-risk cousins" su ~51 alt Hyperliquid (1d, STAT-MODE).
TESI (filone C, terza ondata). I primi due filoni cross-sectional hanno coperto: momentum (XS01,
sleeve attivo), reversal/idio-reversal, TOTAL-low-vol (LOWVOL) e betting-against-beta (BAB).
Restano TRE anomalie "cugine del low-risk", documentate in equity ma MAI provate qui, che POTREBBERO
diversificare il portafoglio essendo strutturalmente diverse dal momentum e dal total-vol:
1. MAX (lottery-demand, Bali-Cakici-Whitelaw 2011). Gli asset col MASSIMO rendimento giornaliero
piu' alto nelle ultime B sedute attraggono domanda "da lotteria" e poi sottoperformano.
SHORT high-max / LONG low-max -> score = -max(daily_ret over B).
Diverso dal momentum (e' la coda destra recente, non il trend) e dal total-vol (un singolo
estremo, non la dispersione).
2. IVOL (idiosyncratic vol, Ang-Hodrick-Xing-Zhang 2006). SHORT alta vol del RESIDUO
(dopo aver tolto beta*mercato su finestra B) / LONG bassa. score = -ivol_residuo.
DIVERSO da LOWVOL (gia' provato in v2) che usa la vol TOTALE: qui si toglie prima il fattore
di mercato, isolando il rischio idiosincratico (ortogonale a BAB, che e' il beta sistematico).
3. AMIHUD (illiquidity, Amihud 2002). Ranking su |ret|/dollar_volume medio su B (dollar_volume =
volume_coin * close, perche' il volume HL e' in coin -> va dollarizzato per confrontare asset).
Tesi standard: premio di illiquidita' -> LONG illiquido / SHORT liquido. In crypto il segno e'
incerto (flight-to-quality verso i major liquidi), quindi si provano ENTRAMBI i segni e si tiene
quello con tesi economica + edge: AMIHUD_ILLIQ (score=+amihud) vs AMIHUD_LIQ (score=-amihud).
GATE OBBLIGATORI (CLAUDE.md + parita' con xsec_v2_nonmom):
- Griglia B in {20,30,60} x H in {5,10} x k in {5,8}, su ENTRAMBI gli universi (51-all, 19-major).
- CAUSALE: score a close[i], peso tenuto in i+1 (engine shifta W[i-1]*dret[i]); vol=0 gata.
Verifica prefix-consistency (xv.causality_prefix_check) sul best: ok=True, max_tail_diff~0.
- NETTO fee 0.10% RT su ogni gamba a ogni ribilancio (engine) + turnover/anno riportato.
- DEFLATED Sharpe (Bailey-Lopez de Prado) sul best, con TUTTI gli Sharpe FULL testati come trial
(multiple-testing): serve DSR>0.95 per un claim forte.
- corr vs XS01 e vs TP01 (vogliamo |corrXS|<0.6 per diversificare).
- Uplift del portafoglio 4->5 sleeve a 10% e 15% (active_sleeves, non modificati).
- Per-anno (breadth) + HOLD-OUT (2025-01-01+).
- ANTI-selection-on-holdout: il best e' scelto per HOLD massimo; si riporta ANCHE il best scelto
per Sharpe IN-SAMPLE (<2025) e si verifica che il deflated-Sharpe (che usa il FULL, in-sample
incluso) regga comunque.
CAVEAT immutabili: storia ~2.5 anni (deflated-Sharpe + multiple-testing), book a molte gambe NON
eseguibile a $600 -> STAT-MODE / forward-monitor, MAI deploy. Nessuno sleeve registrato.
uv run python scripts/research/xsec_v3_lowrisk.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))
sys.path.insert(0, str(PROJECT_ROOT / "scripts" / "research"))
import numpy as np
import pandas as pd
import xsec_v2_nonmom as xv # HARNESS collaudato (engine, metriche, statistica, portafoglio)
HOLDOUT = xv.HOLDOUT
metrics = xv.metrics
# ===========================================================================
# SCORE BUILDERS — "low-risk cousins". Tutti CAUSALI (dati <= i). Convenzione
# engine: long ALTO score / short BASSO score (vol=0 gata automaticamente).
# ===========================================================================
def make_max(PX, B):
"""MAX / lottery-demand: massimo rendimento giornaliero nelle ultime B sedute.
score = -max -> long low-max / short high-max (la 'lotteria' sottoperforma)."""
px, n, A, DR, m = xv._precompute(PX)
ROLLMAX = DR.rolling(B, min_periods=int(0.6 * B)).max().values
def score_at(i):
mx = ROLLMAX[i]
valid = np.isfinite(mx) & np.isfinite(px[i])
return -mx, valid
return score_at, B + 1
def make_ivol(PX, B):
"""IVOL: vol del RESIDUO dopo beta*mercato su finestra B (OLS in-window, esatto:
var_resid = var(y) - beta^2*var(m) con beta = cov/var, >= 0 per costruzione).
score = -ivol -> long bassa idio-vol / short alta (anomalia Ang et al.)."""
px, n, A, DR, m = xv._precompute(PX)
beta, varm = xv._rolling_beta(DR, m, B) # beta (n,A), varm (n,)
mp = int(0.6 * B)
ExDR = DR.rolling(B, min_periods=mp).mean()
ExDR2 = (DR * DR).rolling(B, min_periods=mp).mean()
varDR = (ExDR2 - ExDR ** 2).values # var population (coerente con _rolling_beta)
resid_var = varDR - (beta ** 2) * varm[:, None]
ivol = np.sqrt(np.clip(resid_var, 0.0, None))
def score_at(i):
iv = ivol[i]
valid = np.isfinite(iv) & np.isfinite(beta[i]) & np.isfinite(px[i])
return -iv, valid
return score_at, B + 1
def make_amihud(PX, VOL, B, sign):
"""AMIHUD illiquidity: media su B di |ret| / dollar_volume (volume_coin*close).
sign=+1 -> LONG illiquido (premio di illiquidita'); sign=-1 -> LONG liquido."""
px, n, A, DR, m = xv._precompute(PX)
dvol = (VOL * PX).replace(0, np.nan) # dollarizza il volume in coin
illiq = (DR.abs() / dvol).replace([np.inf, -np.inf], np.nan)
AMI = illiq.rolling(B, min_periods=int(0.6 * B)).mean().values
def score_at(i):
a = AMI[i]
valid = np.isfinite(a) & np.isfinite(px[i])
return sign * a, valid
return score_at, B + 1
def amihud_builder(VOL_full, sign):
"""Builder (PX,cfg) per AMIHUD che richiede il volume: chiude su VOL_full e lo RIALLINEA a
PX.index. Cosi' la causality_prefix_check (che tronca PX a PXc=PX[:cut] e chiama builder(PXc))
riceve automaticamente VOLc = VOL_full.reindex(PXc.index) -> nessun look-ahead dal volume."""
def builder(PX, p):
VOL = VOL_full.reindex(PX.index)
return make_amihud(PX, VOL, p["B"], sign)
return builder
# Griglia condivisa (parita' con i gate): B x H x k.
BHK = [dict(B=B, H=H, k=k) for B in (20, 30, 60) for H in (5, 10) for k in (5, 8)]
def build_mechanisms(VOL):
"""Catalogo per-universo: AMIHUD va legato al VOL dell'universo corrente."""
return {
"MAX": (lambda PX, p: make_max(PX, p["B"]), BHK),
"IVOL": (lambda PX, p: make_ivol(PX, p["B"]), BHK),
"AMIHUD_ILLIQ": (amihud_builder(VOL, +1), BHK), # long illiquido / short liquido
"AMIHUD_LIQ": (amihud_builder(VOL, -1), BHK), # long liquido / short illiquido
}
# Tesi economica per il verdetto AMIHUD (quale segno ha senso se mostra edge).
AMIHUD_THESIS = {
"AMIHUD_ILLIQ": "premio di illiquidita' (long illiquido / short liquido)",
"AMIHUD_LIQ": "flight-to-quality verso i major liquidi (long liquido / short illiquido)",
}
# ===========================================================================
# Helper
# ===========================================================================
def insample_sharpe(daily):
pre = daily[daily.index < HOLDOUT]
return metrics(pre)["sharpe"] if len(pre) > 30 else float("nan")
def per_year(daily):
_, yrs = xv.yr_breadth(daily)
years = [int(y) for y, _ in daily.groupby(daily.index.year)]
return [(y, round(v, 3)) for y, v in zip(years, yrs)]
def uplift_for(cand_daily, base, bf, bh, fractions=(0.10, 0.15)):
"""Uplift portafoglio 4->5 sleeve riusando le CACHE di `base` (Sleeve cached). Ritorna
{fr: (cf, ch, wgt)} e il best combinato (dFULL+dHOLD)."""
cand_fn = lambda: cand_daily
out, best = {}, None
for fr in fractions:
wraw = fr / (1.0 - fr) # cand_frac ~ fr (sum_base=1)
cand = xv.Sleeve("XSV3_cand", wraw, cand_fn)
pf1 = xv.StrategyPortfolio(base + [cand])
cf = metrics(pf1.combined_daily())
ch = metrics(pf1.combined_daily(lo=HOLDOUT))
wgt = pf1.weights().get("XSV3_cand", 0.0)
out[fr] = (cf, ch, wgt)
d = (cf["sharpe"] - bf["sharpe"]) + (ch["sharpe"] - bh["sharpe"])
best = d if best is None else max(best, d)
return out, best
INSAMPLE_EDGE = 0.5 # gate del progetto (scorer indurito): edge standalone PRE-holdout >=0.5
def robust_candidate(rows):
"""Candidato GIUDICATO: NON il best-by-HOLD nudo (che premia il holdout-fitting: una config
negativa in-sample con HOLD alto e' overfit alla finestra OOS, lezione dello scorer indurito),
ma il best fra le config con EDGE IN-SAMPLE (>=0.5) E HOLD>0, ordinate per Sharpe BILANCIATO
(insample+hold)/2. Se nessuna ha in-sample edge -> None (il meccanismo non ha edge reale,
qualunque HOLD alto e' artefatto di selezione)."""
elig = [r for r in rows if np.isfinite(r["insample"]) and r["insample"] >= INSAMPLE_EDGE and r["hold"] > 0]
if not elig:
return None
return max(elig, key=lambda r: 0.5 * (r["insample"] + r["hold"]))
def verdict(cand, dsr, caus_ok, uplift_best, has_isedge):
full, hold, corrXS = cand["full"], cand["hold"], cand["corrXS"]
diversifies = abs(corrXS) < 0.6
helps = (uplift_best is not None) and uplift_best > 0.10
if not has_isedge:
return "SCARTATO", "nessuna config con edge in-sample>=0.5 + HOLD>0 (qualunque HOLD alto e' selezione-su-holdout)"
strong = (dsr > 0.95) and (hold > 0.30) and (full > 0.70) and caus_ok and diversifies
if strong and helps:
return "SLEEVE-CANDIDATE", "edge robusto (DSR>0.95, in-sample+OOS, causale, diversifica, alza il portafoglio)"
if (full > 0.5 and hold > 0.0 and diversifies and caus_ok) and (helps or dsr > 0.50):
return "LEAD-forward-monitor", "edge in-sample E OOS coerente + diversifica, ma DSR<0.95 (96 trial, storia ~2.5y)"
if full > 0.3 and hold > 0.0:
return "DEBOLE", "segno giusto ma Sharpe/robustezza insufficienti"
return "SCARTATO", "no edge (full/hold non positivi o non diversifica)"
# ===========================================================================
# MAIN
# ===========================================================================
def main():
print("=" * 104)
print(" XSEC v3 — LOW-RISK COUSINS cross-sectional su Hyperliquid (MAX / IVOL / AMIHUD) — STAT-MODE")
print("=" * 104)
tp_daily = xv.tp01_sleeve().daily()
xs_daily = xv.xsec_sleeve().daily()
print(" riferimenti corr: TP01 (trend, deployable) e XS01 (momentum cross-sec, sleeve attivo).")
universes = {"51-all": None, "19-major": xv.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]:>2} asset, {PX.shape[0]} giorni "
f"[{PX.index[0].date()} -> {PX.index[-1].date()}]")
# ---- griglia completa: raccoglie tutte le righe + tutti gli Sharpe FULL (trial DSR) ----
MECHS = ("MAX", "IVOL", "AMIHUD_ILLIQ", "AMIHUD_LIQ")
rows_by_mech = {mn: [] for mn in MECHS}
all_sr = []
builders = {} # (uname, mech) -> builder (per causality)
for uname, (PX, VOL) in mats.items():
mechs = build_mechanisms(VOL)
print("\n" + "#" * 104)
print(f"# UNIVERSO {uname}")
print("#" * 104)
for mn in MECHS:
builder, cfgs = mechs[mn]
builders[(uname, mn)] = builder
rows = xv.run_grid(PX, VOL, mn, builder, cfgs, xs_daily, tp_daily, uname)
for r in rows:
r["uni"] = uname
r["mech"] = mn
r["insample"] = insample_sharpe(r["daily"])
rows_by_mech[mn].extend(rows)
all_sr.extend([r["full"] for r in rows])
if not rows:
print(f"\n [{mn}] nessuna config valida")
continue
pos_full = sum(r["full"] > 0 for r in rows)
pos_hold = sum(r["hold"] > 0 for r in rows)
print(f"\n [{mn}] {len(rows)} config | plateau FULL>0: {pos_full}/{len(rows)}"
f" | HOLD>0: {pos_hold}/{len(rows)}")
print(f" {'cfg':<18}{'FULL':>7}{'inS':>7}{'HOLD':>7}{'DD%':>6}{'ret%':>7}"
f"{'anni+':>7}{'corrXS':>8}{'corrTP':>8}{'turn/y':>8}")
for r in sorted(rows, key=lambda r: -r["hold"])[:3]:
print(f" {xv.tag(r['cfg']):<18}{r['full']:>7.2f}{r['insample']:>7.2f}{r['hold']:>7.2f}"
f"{r['dd']*100:>6.0f}{r['ret']*100:>+7.0f}{r['pct']*100:>6.0f}%"
f"{r['corrXS']:>+8.2f}{r['corrTP']:>+8.2f}{r['turn']:>8.0f}")
print(f"\n TRIAL TOTALI testati (per deflated-Sharpe): {len([s for s in all_sr if np.isfinite(s)])}")
# ---- base portafoglio una sola volta (Sleeve cached, riusati per ogni candidato) ----
base = xv.active_sleeves()
pf0 = xv.StrategyPortfolio(base); pf0.backtest()
bf = metrics(pf0.combined_daily()); bh = metrics(pf0.combined_daily(lo=HOLDOUT))
# ---- analisi per meccanismo ----
# CANDIDATO GIUDICATO = robust_candidate (edge in-sample>=0.5 E HOLD>0, best bilanciato): evita la
# trappola del best-by-HOLD nudo (che premia config negative in-sample = overfit alla finestra OOS).
# Si riportano comunque, per trasparenza/anti-cherry, anche il naive best-HOLD e il best-inSAMPLE.
summary = []
chosen_daily = {} # mech -> serie del candidato giudicato (per corr-matrix)
for mn in MECHS:
rows = rows_by_mech[mn]
print("\n" + "=" * 104)
print(f" MECCANISMO {mn}")
print("=" * 104)
if not rows:
print(" nessuna config valida -> SCARTATO")
summary.append((mn, "SCARTATO", "nessuna config valida", None))
continue
naive_hold = max(rows, key=lambda r: r["hold"])
best_is = max(rows, key=lambda r: (r["insample"] if np.isfinite(r["insample"]) else -9))
cand = robust_candidate(rows)
has_isedge = cand is not None
print(f" naive best-HOLD : [{naive_hold['uni']}] {xv.tag(naive_hold['cfg']):<16} "
f"FULL {naive_hold['full']:+.2f} inS {naive_hold['insample']:+.2f} HOLD {naive_hold['hold']:+.2f}"
f" {'(in-sample NEGATIVO -> holdout-fit)' if naive_hold['insample'] < INSAMPLE_EDGE else ''}")
print(f" best-inSAMPLE : [{best_is['uni']}] {xv.tag(best_is['cfg']):<16} "
f"FULL {best_is['full']:+.2f} inS {best_is['insample']:+.2f} HOLD {best_is['hold']:+.2f}"
f" (anti-selection-on-holdout)")
if not has_isedge:
print(f" CANDIDATO GIUDICATO: NESSUNA config con in-sample>=0.5 E HOLD>0 "
f"-> il meccanismo NON ha edge reale (ogni HOLD alto e' selezione-su-holdout).")
verd, why = verdict(naive_hold, float("nan"), True, None, False)
print(f"\n >>> VERDETTO {mn}: {verd}{why}." +
(f" tesi: {AMIHUD_THESIS[mn]}" if mn in AMIHUD_THESIS else ""))
summary.append((mn, verd, why, dict(uni=naive_hold["uni"], cfg=xv.tag(naive_hold["cfg"]),
full=naive_hold["full"], hold=naive_hold["hold"], dd=naive_hold["dd"],
dsr=float("nan"), corrXS=naive_hold["corrXS"], up=None, isedge=False)))
continue
daily = cand["daily"]
chosen_daily[mn] = daily
f, h, pct = xv.evalcfg(daily)
dsr, sr0 = xv.deflated_sharpe(f["sharpe"], all_sr, daily)
caus = xv.causality_prefix_check(*mats[cand["uni"]], builders[(cand["uni"], mn)], cand["cfg"])
ups, up_best = uplift_for(daily, base, bf, bh)
print(f" CANDIDATO GIUDICATO (in-sample>=0.5 & HOLD>0, best bilanciato):")
print(f" [{cand['uni']}] {xv.tag(cand['cfg']):<16} FULL {cand['full']:+.2f} "
f"inSAMPLE {cand['insample']:+.2f} HOLD {cand['hold']:+.2f} (in-sample EDGE = OK)")
print(f" standalone: DD {f['maxdd']*100:.0f}% ret {f['ret']*100:+.0f}% "
f"anni+ {pct*100:.0f}% turnover/y {cand['turn']:.0f}")
print(f" corr vs XS01 {cand['corrXS']:+.2f} | corr vs TP01 {cand['corrTP']:+.2f}")
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_sr if np.isfinite(s)])} trial): {dsr:.3f}"
f" | soglia Sharpe-max-null annualizz. {sr0:.2f} (serve DSR>0.95)")
print(f" per-anno: {per_year(daily)}")
print(f" UPLIFT portafoglio (base FULL {bf['sharpe']:.2f} / HOLD {bh['sharpe']:.2f}):")
for fr, (cf, ch, wgt) in ups.items():
print(f" +cand @{wgt*100:>4.1f}% FULL {cf['sharpe']:.2f} ({cf['sharpe']-bf['sharpe']:+.2f})"
f" DD {cf['maxdd']*100:.0f}% | HOLD {ch['sharpe']:.2f} ({ch['sharpe']-bh['sharpe']:+.2f})")
verd, why = verdict(cand, dsr, caus["ok"], up_best, has_isedge)
extra = f" tesi: {AMIHUD_THESIS[mn]}" if mn in AMIHUD_THESIS else ""
print(f"\n >>> VERDETTO {mn}: {verd}{why}.{extra}")
summary.append((mn, verd, why, dict(uni=cand["uni"], cfg=xv.tag(cand["cfg"]), full=cand["full"],
hold=cand["hold"], dd=f["maxdd"], dsr=dsr, corrXS=cand["corrXS"],
up=up_best, isedge=True)))
# ---- redundancy check: i 3 'low-risk cousins' sono UNA scommessa o TRE? ----
if len(chosen_daily) >= 2:
print("\n" + "=" * 104)
print(" RIDONDANZA — correlazione tra i candidati giudicati (sono lo stesso bet 'evita-speculativo'?)")
print("=" * 104)
names = list(chosen_daily.keys())
print(" " + "".join(f"{n[:8]:>10}" for n in names))
for a in names:
line = f" {a[:8]:<10}"
for b in names:
line += f"{xv._corr(chosen_daily[a], chosen_daily[b]):>10.2f}"
print(line)
print(" NB: corr alta tra i candidati = sono la stessa anomalia low-risk in tre vesti, non tre edge.")
# ---- AMIHUD: scegli il segno con tesi economica + edge ----
print("\n" + "=" * 104)
print(" AMIHUD — scelta del segno (tesi economica + edge)")
print("=" * 104)
a_ill = next((s for s in summary if s[0] == "AMIHUD_ILLIQ"), None)
a_liq = next((s for s in summary if s[0] == "AMIHUD_LIQ"), None)
for s in (a_ill, a_liq):
if s and s[3]:
print(f" {s[0]:<14} FULL {s[3]['full']:+.2f} HOLD {s[3]['hold']:+.2f} "
f"DSR {s[3]['dsr']:.2f} corrXS {s[3]['corrXS']:+.2f} -> {s[1]} ({AMIHUD_THESIS[s[0]]})")
cand_signs = [s for s in (a_ill, a_liq) if s and s[3] and s[3]["full"] > 0 and s[3]["hold"] > 0]
if cand_signs:
win = max(cand_signs, key=lambda s: 0.5 * (s[3]["full"] + s[3]["hold"]))
print(f" -> segno con edge+tesi: {win[0]} ({AMIHUD_THESIS[win[0]]})")
else:
print(" -> NESSUN segno mostra edge positivo (full>0 e hold>0): AMIHUD SCARTATO in entrambi i versi.")
# ---- sintesi finale ----
print("\n" + "=" * 104)
print(" SINTESI FINALE — c'e' un sopravvissuto reale?")
print("=" * 104)
for mn, verd, why, info in summary:
if info:
print(f" {mn:<14} {verd:<22} FULL {info['full']:+.2f} HOLD {info['hold']:+.2f} "
f"DSR {info['dsr']:.2f} corrXS {info['corrXS']:+.2f} upliftBest {info['up'] if info['up'] is not None else float('nan'):+.2f}")
else:
print(f" {mn:<14} {verd}")
survivors = [s for s in summary if s[1] == "SLEEVE-CANDIDATE"]
leads = [s for s in summary if s[1] == "LEAD-forward-monitor"]
if survivors:
print(f"\n SOPRAVVISSUTO: {', '.join(s[0] for s in survivors)} (sleeve-candidate, comunque STAT-MODE).")
elif leads:
print(f"\n Nessuno sleeve-candidate. LEAD da forward-monitor: {', '.join(s[0] for s in leads)}.")
else:
print("\n NESSUN sopravvissuto: tutti DEBOLE/SCARTATO. Risultato valido (la maggior parte muore).")
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' solo lavoro statistico (vincoli del filone C).")
if __name__ == "__main__":
main()