From 1ec928cf092bbd8adabed5b18857f2d631a54b92 Mon Sep 17 00:00:00 2001 From: Adriano Dal Pastro Date: Sat, 22 Aug 2026 20:52:40 +0000 Subject: [PATCH] =?UTF-8?q?research(wave-0822):=20XS01-OOS=20=E2=80=94=20l?= =?UTF-8?q?'edge=20esiste=20FUORI=20dalla=20finestra=20di=20scoperta=20ed?= =?UTF-8?q?=20e'=20piu'=20grande=20li';=20il=20gate=20del=20prop-alloc=20s?= =?UTF-8?q?i=20sostituisce,=20non=20si=20aspetta?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/research/RESULTS-0822.md | 85 ++++++++++++++++++++++++++- scripts/research/r0822b_surface_rv.py | 41 ++++++++++--- 2 files changed, 116 insertions(+), 10 deletions(-) diff --git a/docs/research/RESULTS-0822.md b/docs/research/RESULTS-0822.md index 2590956..a402bd9 100644 --- a/docs/research/RESULTS-0822.md +++ b/docs/research/RESULTS-0822.md @@ -692,7 +692,7 @@ stessi hanno nominato senza poterli eseguire**. Stesso contratto di consegna, st | # | filone | domanda che decide | verdetto | |---|---|---|---| -| 21 | XS01-OOS | il vantaggio del PROP-ALLOC poggia sul drift di XS01 misurato sulla sua finestra di scoperta: regge fuori? | _in corso_ | +| 21 | XS01-OOS | il vantaggio del PROP-ALLOC poggia sul drift di XS01 misurato sulla sua finestra di scoperta: regge fuori? | **LEAD** — regge, ed e' PIU' GRANDE fuori | | 22 | MAKER | l'esecuzione passiva e' una fonte di ritorno, al netto del costo di non essere eseguiti? | **SCARTATO** — il segno dipende da `<` contro `<=` | | 23 | BOCPD | un rilevatore di cambio di regime vero batte il miglior lookback COSTANTE? | **SCARTATO** — filone chiuso definitivamente | | 24 | CRITICO | cosa NON e' stato misurato, e quale singola misura mancante vale di piu' | **3 correzioni all'ondata**, 1 regola mia ritirata | @@ -889,3 +889,86 @@ tre); il muro di XS01 con **tre** numeri; la leva riceve guida **opposta** da GR PROP-ALLOC; il peso di SKH01 e' **respinto da un agente e raddoppiato da un altro**. Piu' 6 modalita' non esplorate e 6 punti di autocritica — incluso che i **20 MB del dataset futures 2018-2026 (l'unico con la coda 2022) vivono solo in uno scratchpad di sessione**. + +--- + +## 21 — XS01-OOS (XS01 fuori dalla sua finestra di scoperta) — **il risultato di testa della serata** + +`scripts/research/r0822b_xs01_oos.py` (78-126 s, cache nello scratchpad, seconda corsa **offline**, +numeri identici su due corse). **140 trial dichiarati** (contati al rialzo: le 6 varianti di config +sono contate come trial anche se sono la *rivalidazione di una selezione passata*), piu' 1.200 +estrazioni di null che **non** sono trial. + +**L'idea:** il gate del PROP-ALLOC diceva *"non aprire un funded prima che XS01 abbia una finestra +fuori dal 2024-2026"* = un'attesa di anni. **Quella finestra esiste gia'** — gli stessi 19 ticker su +**Binance spot USDT 1d, 2021-01 → 2023-12**, che contiene **LUNA e FTX**. Meccanismo **congelato** +(blend [30,90], H=10, k=5, gate p30, vol-target 20%), nessuna griglia cercata. + +⚠️ **L'obiezione ovvia — "Binance non e' la verita'" — e' stata misurata, non aggirata:** quella +regola del progetto riguarda l'**ancoraggio di prezzo**, non un ranking cross-sezionale, e lo scarto +USDT e' un **fattore comune** che sparisce nello z-score. Verificato: corr dei ritorni giornalieri per +asset **≥0,9992** (min NEAR), |differenza| mediana **5,1 bps**, e soprattutto lo **stesso sleeve** +calcolato sui due venue da' **corr 0,9991, dSharpe −0,025**. Replica dello sleeve ufficiale +`max|diff| = 0,0`. Dato certificato: **0 gap, flat 0,00-0,71%, volumi zero 0,00%**, nessun run +iniziale a volume 0 (il rivelatore del backfill sintetico, lezione AXS). + +📌 **IL RISULTATO, e va nel verso opposto a quello temuto:** + +| | Sharpe (**mediana di fase**) | banda 10 fasi | fasi >0 | maxDD | CAGR | +|---|---|---|---|---|---| +| **fuori campione 2021-2023 (U13)** | **+1,12** | [+0,74, +1,54] | **10/10** | **22,0%** | +26,1% | +| finestra di scoperta 2024+ (U13) | +0,37 | — | — | 20,5% | +8,9% | +| **fuori campione (U19-EXP)** | **+1,17** | [+0,91, +1,78] | **10/10** | 20,9% | +30,0% | +| XS01 **ufficiale** (HL, 2024+) | +1,15 | [+0,46, +1,59] | — | **10,8%** | +29,0% | + +**Differenza appaiata per fase (fuori campione − scoperta): +0,668, positiva in 10/10 fasi.** +**Il fuori campione e' la finestra MIGLIORE, non peggiore.** Per anno: 2021 **+2,14** · 2022 +0,22 · +2023 +0,84 · 2024 −0,48 · 2025 +0,68 · 2026 +2,26. + +**Sul LIBRO, solo fuori campione** (TP01+SKH01 75/25 + XS01, n=1076, dati che PROP-ALLOC non aveva mai +visto): w=0 → Sh **+1,17** / DD 9,4% · w=0,25 → **+1,65 / 6,0%** · w=0,33 → +1,67 / 6,6% · +**w=0,50 (l'argmax di PROP-ALLOC) → +1,56 / 8,9%**. corr(XS01, libro) **−0,019**. + +**Gate:** `causality` leak-free (randomizzando i prezzi dopo la decisione `max|diff| = 0`); +`marginal_vs_tp01` **ADDS** su entrambi gli universi (corr +0,031 / −0,01, `is_hedge` **False**); +banda di fase `gate_pass=True` **4/4**, e la fortuna d'ancora e' **piccola** (+0,05) contro il **+0,18** +dello sleeve ufficiale; null di permutazione **a fee zero** p=0,013 (cross-sez.) e 0,017 (pesi +casuali); sopravvive a **30 bps/lato** (+1,01). **`deflated_sharpe` FAIL 0,342** — e l'agente dichiara +che il DSR penalizza una **mediana-di-fase** come se fosse un **max-di-k**, *"ma un gate fallito resta +fallito"*. Null del de-levering **non pertinente e dichiarato**. Haircut a $600: **non e' il vincolo** +(ticket mediano $76, **0,0%** sotto il min-order). + +🚨 **Null "universo dove NON dovrebbe funzionare"** (11 settoriali SPDR, 1998+, meccanismo congelato): +**−0,45, 0/10 fasi positive** → **terza conferma indipendente che il cross-sectional e' +crypto-specifico** (dopo XSR01-equity 26/07 e STATARB-EQ 25/07). + +🚨 **Il risultato che il progetto non si aspettava: i DUE AFFINAMENTI del 2026-06-19 NON si +replicano.** Fuori campione il **blend [30,90] e' PEGGIO del solo L=30** (+1,12 vs +1,28) e il **gate +di dispersione p30 vale ZERO** (+1,12 con e senza) — mentre *dentro* la finestra di scoperta il gate +vale eccome (+0,37 contro −0,03). **Cio' che sopravvive e' il meccanismo cross-sectional NUDO.** +⚠️ Non e' un argomento per cambiarli — cambiarli guardando *questo* risultato sarebbe la stessa +selezione spostata di finestra — **e' un argomento per non attribuire loro il valore che la memoria +del progetto gli attribuisce.** + +⚠️ **Concentrazione misurata prima di crederci** (lezione SOL, stesso giorno): togliendo il **2021** il +fuori campione scende **+1,17 → +0,57**; **leave-one-asset-out**: togliendo **LTC** la mediana di fase +crolla **+1,12 → +0,20** (tutte e 13 restano >0) → **ampiezza effettiva bassa**, stessa diagnosi di +STATARB-MULTI. + +**VERDETTO: LEAD.** Non promuove niente; **riscrive il gate del PROP-ALLOC.** + +📌 **`IL GATE DEL PROP-ALLOC SI PUO' CHIUDERE OGGI: PARZIALMENTE` — e non si chiude, si SOSTITUISCE.** +L'attesa di anni **non serve**. Ma tre cose che il gate non chiedeva sono ora misurate: +- **il rendimento sopravvive, il RISCHIO no**: maxDD standalone **10,8% → 20,9-22,0%** — e su un conto + funded il vincolo binding e' la **regola di DD**, quindi la correzione va nel verso sbagliato; +- **il peso 0,50 dell'argmax-J non sopravvive**: fuori campione a w=0,50 il maxDD di libro risale a + **8,9%** contro **6,0%** a w=0,25, cioe' restituisce quasi tutta la protezione che e' l'unica ragione + per metterlo su un conto a barriera → l'ottimo fuori campione e' **w ≈ 0,25-0,33**; +- **32% dell'universo non e' testabile** (ARB/OP/SUI/APT/SEI/TIA non quotati nel 2021-23) e su di esso + vive gran parte del numero di scoperta (**19 gambe 1,31 vs 13 gambe 0,54**, stessa finestra e stesso + venue) → *cio' che e' stato validato e' una versione piu' DEBOLE di quella che sta nel libro.* + +⚠️ **Il punto fragile che l'agente dichiara e non ha testato:** la validazione di venue gira **solo sul +2024+**, cioe' proprio il periodo **senza depeg USDT**, mentre il fuori campione contiene il **depeg di +maggio 2022**. Se i close Binance/USDT 2021-23 divergessero da un venue **USD** in modo *non comune fra +gli asset*, il ranking cambierebbe e il risultato con esso. diff --git a/scripts/research/r0822b_surface_rv.py b/scripts/research/r0822b_surface_rv.py index d8cfe23..45379f5 100644 --- a/scripts/research/r0822b_surface_rv.py +++ b/scripts/research/r0822b_surface_rv.py @@ -370,7 +370,7 @@ def test_calendario(q: pd.DataFrame, dte_min: float, ogni: int = 4) -> dict: amp_pt = np.where(ok, iv_need - nxt_iv, np.nan) room = amp_pt[ok] # <0 = margine, >0 = violazione - res = {"room_med_pt": float(np.nanmedian(room)), "room_p5_pt": float(np.nanpercentile(room, 5)), + res = {"room_med_pt": float(np.nanmedian(room)), "room_p5_pt": float(np.nanpercentile(room, 95)), "i_n": int(ok.sum()), "i_viol": int(viol_i.sum()), "i_quota": float(viol_i.sum() / max(1, ok.sum())), "i_amp_med_pt": float(np.nanmedian(amp_pt[viol_i])) if viol_i.any() else np.nan, @@ -473,7 +473,8 @@ def persistenza(bf: pd.DataFrame, viol_col: str) -> dict: return {"n": int(m.sum()), "resta": float(nxt[m].mean()) if m.any() else np.nan} -def controfattuale_usdc(bf: pd.DataFrame, asset: str, rel_usdc: float) -> dict: +def controfattuale_usdc(bf: pd.DataFrame, asset: str, rel_usdc: float, + solo_gia_strette: bool = False) -> dict: """SE la famiglia USDC quotasse la STESSA superficie col SUO spread, sarebbe eseguibile? Controfattuale DICHIARATO, non una misura: il collettore non raccoglie le USDC (difetto di @@ -484,6 +485,14 @@ def controfattuale_usdc(bf: pd.DataFrame, asset: str, rel_usdc: float) -> dict: """ x = bf[(bf["asset"] == asset)].dropna(subset=["mid", "mid_p", "mid_n"]).copy() x = x[np.isclose(x["lam"], 0.5, atol=0.02)] + if solo_gia_strette: + # il controfattuale ingenuo prende il MID di un mercato larghissimo e finge di poterlo + # quotare al 2,6%: e' proprio dove il mid e' meno informativo. Qui si tiene solo la parte + # di superficie GIA' quotata stretta quanto la famiglia USDC, dove il mid vale qualcosa. + rs = np.maximum.reduce([((x["ask"] - x["bid"]) / x["mid"]).to_numpy(), + ((x["ask_p"] - x["bid_p"]) / x["mid_p"]).to_numpy(), + ((x["ask_n"] - x["bid_n"]) / x["mid_n"]).to_numpy()]) + x = x[rs <= rel_usdc] lot = MIN_LOT_USDC[asset] out = {} for nome, rel in (("inverse osservato", None), (f"{asset}_USDC controfattuale", rel_usdc)): @@ -499,8 +508,10 @@ def controfattuale_usdc(bf: pd.DataFrame, asset: str, rel_usdc: float) -> dict: f = (fee_coin(b.to_numpy(), 2 * l) + fee_coin(a_p.to_numpy(), l) + fee_coin(a_n.to_numpy(), l)) netto = (cred - f) * x["spot_feed"].to_numpy() out[nome] = {"n": int(len(x)), "pos": int(np.nansum(netto > 0)), - "max": float(np.nanmax(netto)), "p999": float(np.nanpercentile(netto, 99.9)), - "lotto_usd": float(np.nanmedian(x["spot_feed"]) * l)} + "pos1": int(np.nansum(netto > 1.0)), "somma": float(np.nansum(netto[netto > 0])), + "max": float(np.nanmax(netto)) if len(x) else np.nan, + "p999": float(np.nanpercentile(netto, 99.9)) if len(x) else np.nan, + "lotto_usd": float(np.nanmedian(x["spot_feed"]) * l) if len(x) else np.nan} return out @@ -694,7 +705,7 @@ def main() -> None: print(f" valore mediano della violazione (i) al lotto minimo ${c['i_val_usd_med']:.2f} " f"contro un costo di 2 gambe (spread + fee) di ${c['i_costo_usd_med']:.2f}") print(f" margine di monotonia sull'intera superficie: la iv lunga sta {-c['room_med_pt']:.2f} pt-vol " - f"SOPRA il minimo che la non-arbitraggio richiede (p5 {-c['room_p5_pt']:.2f}) " + f"SOPRA il minimo che la non-arbitraggio richiede (5% piu' stretto: {-c['room_p5_pt']:.2f}) " f"-> la superficie non e' nemmeno VICINA al bordo") print("\n" + "=" * 100) @@ -717,10 +728,22 @@ def main() -> None: for a, rel in (("BTC", 0.082), ("ETH", 0.026)): if a not in set(bf["asset"]): continue - cf = controfattuale_usdc(bf, a, rel) - for nome, r in cf.items(): - print(f" {a} {nome:<28}: lotto ${r['lotto_usd']:,.0f} · farfalle a credito netto>0 " - f"{r['pos']:,}/{r['n']:,} · migliore ${r['max']:.2f} · p99.9 ${r['p999']:.2f}") + for strette, et in ((False, "tutta la superficie"), (True, "solo quote gia' strette")): + cf = controfattuale_usdc(bf, a, rel, solo_gia_strette=strette) + for nome, r in cf.items(): + if strette and "inverse" in nome: + continue + print(f" {a} {nome:<26} [{et:<24}]: lotto ${r['lotto_usd']:,.0f} · " + f"credito netto>0 {r['pos']:,}/{r['n']:,} · >$1 {r['pos1']:,} · " + f"migliore ${r['max']:.2f} · somma ${r['somma']:,.0f}") + # dove vive l'incoerenza: e' larga dove il mercato e' largo? + x = bf[(bf["asset"] == a)].dropna(subset=["iv_resid", "relspread"]) + x = x[np.isfinite(x["relspread"])] + dec = pd.qcut(x["relspread"], 5, labels=False, duplicates="drop") + t = x.assign(d=dec).groupby("d").agg(spread=("relspread", "median"), + resid=("iv_resid", lambda v: v.abs().median())) + print(f" |residuo| per quintile di spread relativo {a}: " + + " · ".join(f"{r.spread:.1%}->{r.resid:.3f}pt" for r in t.itertuples())) print("\n QUANTO MANCA per essere eseguibile (near-miss, triple equispaziate):") for a in sorted(bf["asset"].unique()):