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:
@@ -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)/(A−1) è 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.
|
||||
@@ -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)/(A−1) 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()
|
||||
Reference in New Issue
Block a user