research(hunt-web): sintesi caccia strategie via web — 10 famiglie nuove 0 slot; lead-lag JEDC = reversal travestito (rank-corr -1.000), MAX DSR 0.41

3 agenti web (~50 fonti) + 3 ondate empiriche in una notte. Convergenza indipendente
ricerca<->empirica: il factor zoo crypto si riduce a liquidity/momentum (gia' nel book).
XS su HL: lead-lag all-others e' matematicamente l'inverso del momentum proprio (check
di degenerazione da riusare); MAX segno instabile L7<->L30. HLP probe: crash-long reale
(corr book -0.19, +10.4% nel crash ott-25) ma decay 79->12%/anno + coda JELLY fuori
serie -> ALLOCATION-WATCH. Pista nuova prossima ondata: Polymarket<->Deribit (gap 11pp,
arXiv 2606.19517, spec-mismatch da riconciliare prima di credere). Book INVARIATO.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Adriano Dal Pastro
2026-07-24 22:24:57 +00:00
parent b65f067f69
commit 4c1961d77b
2 changed files with 390 additions and 0 deletions
+107
View File
@@ -0,0 +1,107 @@
# 2026-07-24 — Caccia strategie via web (3 agenti + 3 ondate empiriche): 0 slot, 2 lead operativi, 1 pista infrastrutturale
**Goal**: "trova altre strategie, sfruttiamo anche internet". Seconda metà della serata
(la prima è l'ondata on-chain, diario `2026-07-24-onchain-sentiment-wave.md`). Tre agenti
di ricerca web in parallelo (accademia 2022-26, on-chain/fonti dati, pratica non
convenzionale) + meccanizzazione immediata di tutto ciò che era testabile stanotte.
## Convergenza ricerca ↔ empirica (il quadro)
La letteratura di replicazione 2024-26 dice: dei 36 fattori crypto pubblicati ne
sopravvivono **2-3** (tutti liquidity/momentum — Mercik/Zaremba IRFA 2026); le anomalie
sono micro-cap e bull-conditional (Fieberg IRFA 2024); attention/volume falliscono OOS;
i flussi opzioni predicono la VOL, non la direzione (Alexander JFM 2022). Le prime 5
raccomandazioni degli agenti (MVRV, stablecoin, exchange-flow, F&G, hash ribbons)
**coincidono con le famiglie uccise stanotte dall'ondata on-chain** — la convergenza
indipendente ricerca/empirica è essa stessa il finding: il soffitto ~1.3 direzionale
BTC/ETH non si aggira con "informazione nuova" che è funzione del prezzo.
## Ondata PREMI CROSS-VENUE (empirica, stanotte) — 0/2, con lezione
`scripts/research/r0724_premium_wave.py` — unica famiglia flow con dati 100%
auto-calcolati da candele raw (zero rischio-vintage): Coinbase premium (2015→) e
kimchi premium Upbit/ECB (2017→), allineamento 00:00 UTC verificato (le daily Upbit
ancorano a 09:00 KST = mezzanotte UTC).
- **CBPREM**: medio 1bp (il feed certificato è un indice che include Coinbase →
premio smorzato, strutturale); miglior cella = HEDGE, hold 0.46 → MORTO.
- **KIMCHI d30-follow**: il candidato più forte della serata — **EARNS_SLOT=True al
marginal scorer** (ADDS, robust_oos, uplift positivo OGNI anno 2020-26, non-hedge,
hold standalone 0.74) ma DSR 0.891<0.95. Lo scettico (`r0724_kimchi_skeptic.py`) lo
uccide in due mosse: **niente plateau** (d15 0.44 / d30 1.07 / d45 0.51 / d60 0.24 =
spike isolato) e **lag +1g lo azzera** (hold 0.74→0.14, uplift blend negativo).
→ SCARTATO, parameter-luck. **Lezione: un EARNS_SLOT con DSR<0.95 va SEMPRE allo
scettico plateau+lag prima di qualsiasi entusiasmo** — il DSR aveva ragione.
## Probe HLP vault (dati reali API HL, 89 periodi ~14g, 2023-05→oggi)
L'unico stream **crash-long** mai visto dal progetto: corr col book **0.19**, nei
periodi in cui il book perde HLP fa in media **+2.5%**; crash ott-2025 **+10.4%**,
liquidazione feb-2026 +7.0%. Sharpe grezzo 1.98 (su barre 14g — il daily vero è più
brutto), maxDD 5.8%. MA: **decay netto** (+60%/+79% 2023/24 → +19% 2025 → ~+12%
run-rate 2026, TVL diluisce) e la coda di protocollo (JELLY mar-2025, $12M
quasi-socializzati; contract/bridge risk) NON è nella serie → lo Sharpe è
event-luck + short-tail-risk travestito da income. **Verdetto: ALLOCATION-WATCH**
(non è una strategia: è un deposito USDC con lockup 4g). A $600 la materialità è
~€10-30/anno → decisione operatore, non urgente. Dati: `data/external/hlp_vault.json`.
## LEAD infrastrutturale nuovo: Polymarket ↔ Deribit (probabilità implicite)
Il best-find dell'agente pratica: le binarie BTC/ETH su Polymarket vs le probabilità
implicite della chain opzioni Deribit — **gap medio 11pp vs Deribit** (arXiv 2606.19517),
half-life ~4h (= retail-compatible), delta-hedge col perp che già tradiamo. Unico
candidato 2026 che è insieme: meccanismo NUOVO (non prezzo), eseguibile a $600 (PM min
$1, no fee), e costruibile su infra ESISTENTE (motore BS/DVOL di VRP01 + cerbero-bite).
Scettici seri dichiarati: campione accademico piccolo e clusterizzato; parte del gap è
probabile **spec-mismatch** (fonte/orario di risoluzione PM vs settle Deribit) da
riconciliare contratto-per-contratto PRIMA di credere a un bp; book PM sottili vicino a
risoluzione; l'arb veloce è dei bot (73% sub-100ms) — l'angolo onesto è la convergenza
lenta a limit-order. **→ Prossima ondata di ricerca: studio di riconciliazione** (dati:
PM CLOB API gratuita + DB storico pubblicato arXiv 2606.04217). Nessun codice stasera.
## Altre azioni a costo zero raccomandate dagli agenti (per l'operatore)
1. **Puntare cerbero-bite anche sulle scadenze DAILY Deribit** da subito: 365 expiry/anno
= una serie di premi REALI si accumula 7× più veloce delle weekly → in 6 mesi decide
il dossier 0DTE/calendar (che resta bloccato dalla regola no-short-vol-da-modello).
2. Dataset Reservoir S3 (tape completo HL: fill, liquidazioni, ADL, L2) = primo dataset
non-di-prezzo del progetto, requester-pays (serve account AWS) → abilita studio
liquidation-exhaustion + listing-effects. Parcheggiato: richiede setup infra.
3. ETF flows: storia 2.6 anni → NON testabile onestamente fino al ~2028 (parcheggiato
con data di rivisita).
## Test cross-sectional su HL (lead-lag JEDC 2024 + salience/MAX) — DEAD 2/2
`scripts/research/r0724_xs_leadlag_max.py` (agente, 19 major HL, 2024→2026-07, 936g,
0 backfill verificato a runtime, convenzioni XS01: fee 0.10% RT/gamba, vol-target 20%,
holdout 2025).
- **LEAD-LAG (Guo et al. JEDC 2024)**: finding strutturale — con predictor equal-weight
"all-others", S_i = (Σr r_i)/(A1) è affine DECRESCENTE nel ritorno proprio
(rank-corr empirica **1.000**): il "lead-lag accademico" sull'universo È lo
short-term reversal, già morto (3ª conferma FOLLOW>FADE). Le 8 celle hanno IS Sharpe
TUTTE negative (best 0.11); le celle large5 "buone" in hold-out (1.18) non sono
selezionabili in-sample (trappola selection-on-holdout) E sono degenerate (14/19
asset a segnale identico). DSR 0.237. **DEAD.**
- **MAX/lottery (Cai&Zhao JBF 2024)**: cella IS L30/H10 FULL 0.59 / HOLD 0.47,
DSR **0.412**, il segno si INVERTE tra L7 e L30 (plateau assente), 34 ribilanci IS
(small-n), fee-zero non salva (edge lordo piccolo, non morte-per-fee). Sotto lo
standard STATARB (0.84/DSR 0.929) → **DEAD**, non lead. Direzione coerente con la
letteratura ma indistinguibile dal rumore su ~2.5 anni.
Nota metodologica in omaggio: la degenerazione "predictor affine nel ritorno proprio"
è un check di 3 righe (rank-corr segnale↔momentum proprio) che smaschera i lead-lag
cross-section travestiti — da riusare su ogni futura proposta XS.
## Bilancio del goal "trova altre strategie"
Su tutta la serata: **10 famiglie nuove meccanizzate e giudicate coi gate completi + 1
probe** (6 on-chain/sentiment + 2 premi cross-venue + 2 cross-sectional HL + probe HLP),
3 report di ricerca web con ~50 fonti primarie, **0 slot nuovi** — coerente con la
storia del progetto (il book attuale resta il massimo onesto raggiunto) — ma:
1 pista genuinamente nuova con meccanismo non-di-prezzo (Polymarket↔Deribit, prossima
ondata), 1 allocation-watch crash-long (HLP), 2 azioni a costo zero (daily options
capture, rivisita ETF 2028), e 2 regole metodologiche nuove a diario (vintage-risk
delle fonti esterne; scettico plateau+lag obbligatorio sugli EARNS_SLOT con DSR<0.95).
Book/pesi INVARIATI.
+283
View File
@@ -0,0 +1,283 @@
"""r0724_xs — LEAD-LAG cross-crypto (Guo/Sang/Tu/Wang JEDC 2024) + SALIENCE/MAX (Cai&Zhao
JBF 2024) sui 19 major Hyperliquid certificati (1d, 2024-2026).
TESI (2026-07-24). Due famiglie cross-sectional dalla letteratura accademica, mai meccanizzate
qui, sullo stesso universo/harness di XS01:
FAMIGLIA A — LEAD-LAG "cross-cryptocurrency return predictability" (diffusione lenta
dell'informazione: i ritardi degli ALTRI coin predicono il focale). Segnale
S_i(t) = media equal-weight dei ritorni a k giorni degli ALTRI asset (mai il proprio);
variante predictor-set = solo large-cap (BTC/ETH/SOL/BNB/XRP, leave-one-out se il focale
e' large). Book L/S top-5 / bottom-5 per S_i, hold H, come XS01.
Griglia: k in {3,7} x predictor in {all-others, large5} x H in {3,7} = 8 celle.
⚠️ IDENTITA' MATEMATICA (dichiarata PRIMA di guardare i numeri): con predictor
all-others equal-weight, S_i = (SommaTotale r_i)/(A1) e' AFFINE DECRESCENTE nel
ritorno proprio r_i → il rank cross-section di S e' ESATTAMENTE il rank inverso del
momentum proprio a k giorni: la cella "all-others" E' la short-term reversal (gia'
negativa nella griglia rev di xsec_research). La verifico empiricamente (rank-corr 1).
⚠️ DEGENERAZIONE large5: i 14 asset non-large hanno segnale IDENTICO (media dei 5
large) → la composizione del book fra i pari e' decisa dal tie-break dell'argsort
(stabile = ordine colonne), economicamente arbitraria. Dichiarata, non nascosta.
FAMIGLIA B — SALIENCE/MAX (lottery demand: i coin col massimo ritorno giornaliero
recente vengono sovracomprati → overpriced). MAX_i(t) = max ritorno 1d negli ultimi L
giorni; book LONG bottom-quintile MAX (k=4) / SHORT top-quintile, hold H.
Griglia: L in {7,30} x H in {7,10} = 4 celle.
GATE (CLAUDE.md, metodologia obbligatoria):
1. CAUSALE: segnale da close<=t, peso tenuto da t+1 (l'engine shifta W[i-1]*dret[i]).
2. NETTO fee 0.10% RT per gamba (FEE=0.001, addebitata FEE/2 per lato sul turnover,
identica a xsec_research/sleeves) + probe fee-zero sulla cella scelta (morte-per-fee
vs assenza di edge lordo).
3. Selezione cella IN-SAMPLE-ONLY (pre-2025-01-01), poi hold-out bloccato.
4. DEFLATED Sharpe (Bailey & Lopez de Prado, altlib) su TUTTE le celle della famiglia.
5. RIDONDANZA: corr giornaliera vs XS01 (sleeves._xsec_returns) e TP01
(altlib.tp01_baseline_daily) + uplift del blend 0.75*XS01+0.25*candidato (full e
hold-out). corr>0.5 a XS01 o uplift negativo -> REDUNDANT.
6. BANDA DI FASE del ciclo H sulla cella scelta (lezione anchor-luck 2026-07-02: i
numeri di strategie a ribilanciamento ancorato si citano con la banda).
CAVEAT IMMUTABILI: storia HL nativa ~2.5 anni (join 19 major = 2024-01-01+, 0 barre
sintetiche/vol=0 — verificato a runtime); IS = solo il 2024 (~330 giorni utili, ~48
ribilanci a H=7 → small-n); book L/S a 8-10 gambe NON eseguibile a $600 → STAT-MODE
come XS01 (eventuale esito positivo = forward-monitor, non deploy).
uv run python scripts/research/r0724_xs_leadlag_max.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" / "alt"))
sys.path.insert(0, str(PROJECT_ROOT))
import numpy as np
import pandas as pd
from altlib import deflated_sharpe, tp01_baseline_daily # noqa: E402
from src.portfolio.portfolio import HOLDOUT, metrics, to_daily # noqa: E402
from src.portfolio.sleeves import XS_UNIVERSE, _xsec_returns # noqa: E402
RAW = PROJECT_ROOT / "data" / "raw"
FEE = 0.001 # 0.10% RT per gamba (come xsec_research: FEE/2 per lato sul turnover)
TV = 0.20 # vol-target come XS01
WARMUP = 30 # warmup comune a tutte le celle (max lookback della griglia) -> stessi giorni
DPY = 365.25
LARGE5 = ["BTC", "ETH", "SOL", "BNB", "XRP"]
def load_universe():
"""Chiusure (e guardia volume) dei 19 major XS01. Inner-join = 2024+ nativo (0 backfill)."""
cols, vols = {}, {}
for s in XS_UNIVERSE:
d = pd.read_parquet(RAW / f"hl_{s.lower()}_1d.parquet")
idx = pd.to_datetime(d["timestamp"], unit="ms", utc=True)
cols[s] = pd.Series(d["close"].values.astype(float), index=idx)
vols[s] = pd.Series(d["volume"].values.astype(float), index=idx)
C = pd.concat(cols, axis=1, join="inner").sort_index().dropna()
V = pd.concat(vols, axis=1, join="inner").sort_index().reindex(C.index)
nz = int((V.values == 0).sum())
assert nz == 0, f"barre vol=0 nella finestra joined ({nz}): possibile backfill sintetico"
return C
def kret_matrix(px: np.ndarray, k: int) -> np.ndarray:
"""Ritorno a k giorni fino a close[t] (causale). NaN nelle prime k righe."""
R = np.full_like(px, np.nan, dtype=float)
R[k:] = px[k:] / px[:-k] - 1.0
return R
def sig_leadlag(px: np.ndarray, cols: list[str], k: int, pred: str) -> np.ndarray:
"""S_i(t) = media dei ritorni k-giorni degli ALTRI asset (mai il proprio).
pred='all' : tutti gli altri 18 (⚠️ affine decrescente nel proprio ritorno).
pred='large5': solo BTC/ETH/SOL/BNB/XRP (leave-one-out se il focale e' large)."""
R = kret_matrix(px, k)
n, A = R.shape
S = np.full((n, A), np.nan)
if pred == "all":
tot = R.sum(axis=1)
for i in range(A):
S[:, i] = (tot - R[:, i]) / (A - 1)
else:
li = [cols.index(s) for s in LARGE5]
totL = R[:, li].sum(axis=1)
for i in range(A):
if i in li:
S[:, i] = (totL - R[:, i]) / (len(li) - 1)
else:
S[:, i] = totL / len(li)
return S
def sig_neg_max(px: np.ndarray, L: int) -> np.ndarray:
"""score = MAX_i(t) (max ritorno 1d negli ultimi L giorni): rank alto = low-MAX = LONG."""
n, A = px.shape
dret = np.vstack([np.full((1, A), np.nan), px[1:] / px[:-1] - 1.0])
mx = pd.DataFrame(dret).rolling(L, min_periods=L).max().values
return -mx
def xs_book(C: pd.DataFrame, S: np.ndarray, H: int, k: int,
phase: int = 0, fee: float = FEE) -> tuple[pd.Series, dict]:
"""Book L/S market-neutral da matrice di segnale (rank a close[i], tenuto da i+1),
fee sul turnover, vol-target 20% causale — convenzioni identiche a XS01."""
px = C.values
n, A = px.shape
dret = np.vstack([np.zeros((1, A)), px[1:] / px[:-1] - 1.0])
W = np.zeros((n, A))
w = np.zeros(A)
nreb = nreb_is = 0
is_mask = C.index < HOLDOUT
for i in range(n):
if i >= WARMUP and (i - WARMUP - phase) % H == 0 and np.isfinite(S[i]).all():
order = np.argsort(S[i], kind="stable")
w = np.zeros(A)
w[order[-k:]] = 0.5 / k
w[order[:k]] = -0.5 / k
nreb += 1
nreb_is += int(is_mask[i])
W[i] = w
gross = np.zeros(n)
gross[1:] = np.sum(W[:-1] * 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=C.index)
rv = s.rolling(30, min_periods=15).std().shift(1) * np.sqrt(DPY)
scale = np.clip(np.nan_to_num(TV / rv.replace(0, np.nan).values, nan=0.0), 0, 3.0)
out = pd.Series(s.values * scale, index=C.index)
diag = dict(time_in_market=float((W != 0).any(axis=1).mean()),
turnover_yr=float(turn.sum() / (n / DPY)),
n_reb=nreb, n_reb_is=nreb_is)
return out, diag
def cell_row(d: pd.Series) -> dict:
f = metrics(d)
i = metrics(d[d.index < HOLDOUT])
h = metrics(d[d.index >= HOLDOUT])
return dict(full=f, ins=i, hold=h)
def redundancy(cand: pd.Series) -> dict:
"""Corr vs XS01/TP01 + uplift del blend 0.75*XS01+0.25*cand (finestra comune)."""
xs = to_daily(_xsec_returns())
tp = tp01_baseline_daily()
Jx = pd.concat({"xs": xs, "c": cand}, axis=1, join="inner").dropna()
Jt = pd.concat({"tp": tp, "c": cand}, axis=1, join="inner").dropna()
corr_xs = float(Jx["xs"].corr(Jx["c"])) if len(Jx) > 5 else float("nan")
corr_tp = float(Jt["tp"].corr(Jt["c"])) if len(Jt) > 5 else float("nan")
blend = 0.75 * Jx["xs"] + 0.25 * Jx["c"]
up_f = metrics(blend)["sharpe"] - metrics(Jx["xs"])["sharpe"]
bh = blend[blend.index >= HOLDOUT]
xh = Jx["xs"][Jx["xs"].index >= HOLDOUT]
up_h = metrics(bh)["sharpe"] - metrics(xh)["sharpe"]
return dict(corr_xs=corr_xs, corr_tp=corr_tp, uplift_full=up_f, uplift_hold=up_h,
xs_full=metrics(Jx["xs"])["sharpe"], xs_hold=metrics(xh)["sharpe"])
def phase_band(C, S, H, k) -> list[float]:
"""Sharpe FULL a ogni fase del ciclo H (lezione anchor-luck: cita la banda, non la fase 0)."""
return [metrics(xs_book(C, S, H, k, phase=p)[0])["sharpe"] for p in range(H)]
def run_family(name: str, C: pd.DataFrame, cells: list[tuple[str, np.ndarray, int, int]]):
"""cells = [(tag, S, H, k)]. Selezione IS-only, DSR su tutte le celle, ridondanza + fasi."""
print("\n" + "=" * 100)
print(f" FAMIGLIA {name}")
print("=" * 100)
print(f" {'cella':<30}{'IS Sh':>8}{'FULL Sh':>9}{'HOLD Sh':>9}{'DD%':>7}{'TiM':>6}{'to/yr':>7}{'reb':>5}")
rows = []
for tag, S, H, k in cells:
d, diag = xs_book(C, S, H, k)
m = cell_row(d)
rows.append((tag, S, H, k, d, diag, m))
print(f" {tag:<30}{m['ins']['sharpe']:>8.2f}{m['full']['sharpe']:>9.2f}"
f"{m['hold']['sharpe']:>9.2f}{m['full']['maxdd'] * 100:>7.1f}"
f"{diag['time_in_market']:>6.2f}{diag['turnover_yr']:>7.1f}{diag['n_reb']:>5}")
# selezione IN-SAMPLE-ONLY
best = max(rows, key=lambda r: r[6]["ins"]["sharpe"])
tag, S, H, k, d, diag, m = best
all_full = [r[6]["full"]["sharpe"] for r in rows]
dsr, sr0 = deflated_sharpe(m["full"]["sharpe"], all_full, d)
red = redundancy(d)
d0, _ = xs_book(C, S, H, k, fee=0.0)
m0 = cell_row(d0)
band = phase_band(C, S, H, k)
print(f"\n CELLA IS-BEST: {tag} (scelta sul solo IS Sharpe {m['ins']['sharpe']:.2f})")
print(f" FULL Sh {m['full']['sharpe']:.2f} DD {m['full']['maxdd'] * 100:.1f}% | "
f"HOLD Sh {m['hold']['sharpe']:.2f} DD {m['hold']['maxdd'] * 100:.1f}%")
print(f" deflated-Sharpe (N={len(rows)} celle): DSR={dsr:.3f} (null-max ~{sr0:.2f}) "
f"{'PASS' if dsr >= 0.95 else 'FAIL'}")
print(f" fee-zero probe: IS {m0['ins']['sharpe']:.2f} / FULL {m0['full']['sharpe']:.2f} / "
f"HOLD {m0['hold']['sharpe']:.2f} (edge lordo vs morte-per-fee)")
print(f" corr vs XS01 {red['corr_xs']:+.2f} | corr vs TP01 {red['corr_tp']:+.2f}")
print(f" blend 0.75*XS01+0.25*cand: uplift FULL {red['uplift_full']:+.2f} "
f"(XS01 solo {red['xs_full']:.2f}) | HOLD {red['uplift_hold']:+.2f} (XS01 solo {red['xs_hold']:.2f})")
print(f" banda di fase H={H} (Sh FULL): min {min(band):.2f} / med {np.median(band):.2f} / "
f"max {max(band):.2f} [fase0 = {band[0]:.2f}]")
print(f" ribilanci: {diag['n_reb']} totali, {diag['n_reb_is']} in IS"
+ (" ⚠️ SMALL-N (<50 in IS)" if diag['n_reb_is'] < 50 else ""))
return dict(tag=tag, m=m, dsr=dsr, red=red, band=band, diag=diag, fee0=m0)
def main():
C = load_universe()
cols = list(C.columns)
px = C.values
print("=" * 100)
print(f" r0724 LEAD-LAG + MAX — {len(cols)} major HL, {len(C)} giorni "
f"[{C.index[0].date()} -> {C.index[-1].date()}], fee {FEE * 100:.2f}% RT/gamba, "
f"holdout {HOLDOUT.date()}, IS = {int((C.index < HOLDOUT).sum())} giorni")
print("=" * 100)
# verifica empirica dell'identita' (Famiglia A, all-others): rank-corr(S, r_proprio) = -1
Sa = sig_leadlag(px, cols, 7, "all")
Ra = kret_matrix(px, 7)
i = len(px) // 2
rc = pd.Series(Sa[i]).corr(pd.Series(Ra[i]), method="spearman")
print(f"\n [check identita' A/all-others] rank-corr(S_i, ritorno proprio 7g) al giorno "
f"{C.index[i].date()}: {rc:+.3f} (atteso -1: la cella 'all' E' reversal del proprio momentum)")
# FAMIGLIA A — lead-lag: k x predictor x H = 8 celle (k lati book = 5 come XS01)
cells_a = []
for kk in (3, 7):
for pred in ("all", "large5"):
S = sig_leadlag(px, cols, kk, pred)
for H in (3, 7):
cells_a.append((f"LL k{kk} {pred:<7} H{H}", S, H, 5))
res_a = run_family("A — LEAD-LAG cross-crypto (8 celle)", C, cells_a)
# FAMIGLIA B — MAX/salience: L x H = 4 celle (quintile: 4 gambe/lato)
cells_b = []
for L in (7, 30):
S = sig_neg_max(px, L)
for H in (7, 10):
cells_b.append((f"MAX L{L} H{H}", S, H, 4))
res_b = run_family("B — SALIENCE/MAX lottery-demand (4 celle)", C, cells_b)
print("\n" + "=" * 100)
print(" VERDETTO (bar: DSR>=0.95, corr XS01<0.5, uplift blend>0, hold-out>0, small-n onesto)")
print("=" * 100)
for nm, r in (("A LEAD-LAG", res_a), ("B MAX", res_b)):
flags = []
if r["dsr"] < 0.95:
flags.append(f"DSR {r['dsr']:.2f}<0.95")
if abs(r["red"]["corr_xs"]) > 0.5:
flags.append(f"corrXS01 {r['red']['corr_xs']:+.2f}")
if r["red"]["uplift_full"] < 0 or r["red"]["uplift_hold"] < 0:
flags.append("uplift blend negativo")
if r["m"]["hold"]["sharpe"] <= 0:
flags.append("hold-out <=0")
if r["diag"]["n_reb_is"] < 50:
flags.append("small-n IS")
print(f" {nm}: cella {r['tag']}" + ("; ".join(flags) if flags else "tutti i gate passati"))
print("\n NB: STAT-MODE (8-10 gambe, non eseguibile a $600); storia ~2.5 anni; IS = solo 2024.")
if __name__ == "__main__":
main()