diff --git a/CLAUDE.md b/CLAUDE.md index cd5b486..399ae68 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,7 +17,7 @@ chi la riapre deve battere il motivo, non ripetere la misura. | `docs/memory/40-produzione-e-deploy.md` | esecutore, tripwire, monitor, libro di bordo, PRIIPs/UCITS | tocchi cio' che gira con soldi veri | | `docs/memory/50-dati-e-feed.md` | difetti del dato trovati e riparati, catena opzioni | tocchi i feed | | `docs/memory/60-metodo-e-gate.md` | i gate codificati in `altlib.py` | valuti un candidato | -| `docs/research/RESULTS-0822.md` | registro per filone §1-69 | vuoi il dettaglio di un filone | +| `docs/research/RESULTS-0822.md` | registro per filone §1-70 | vuoi il dettaglio di un filone | | `docs/diary/` (123 voci) | la sessione originale, per data | vuoi il contesto completo | --- @@ -91,6 +91,7 @@ sono all'ancora fortunata; le stime oneste sono la **mediana della banda d'ancor | VRP01 | f = 1,00 | **f = 0,73** misurato sulle quote reali (IC95 [0,698; 0,780]) | | muro capitale-rendita | $272k (lordo) | **$313k** al netto di fisco **e** funding — banda **[$187k – $1,14M]** | | P(≥50 €/g), canale funded, 36 mesi | 42% | **2,6%** [1,5 – 4,7], P(zero) 40,3% | +| XSR01 | Sharpe 1,82 (e' una **terza** lente, divisore fisso 50) | **1,79** alla scoperta / **1,56-1,63** a oggi — lente dei gate, sole barre chiuse. Citare sempre la coppia (lente, ultima barra chiusa) | | soffitto direzionale BTC/ETH | ~1,3 | **~1,15** col funding dentro | 📌 **Il libro a k=1 rende MENO dell'S&P 500** (15,19% contro 17,40%, stessa finestra): il vantaggio @@ -161,12 +162,19 @@ sull'hold-out; cambiarne le soglie guardando l'esito, pure. | gate | data | criterio | stato | |---|---|---|---| | **STATARB** | 2026-09-27 | soglie 24/07 (non toccate) + diagnostica statica sempre-short | 🚨 **si ribalta col difetto `advance()`**: riparata → RITIRO, attuale → candidato | -| **XSR01** | 2026-10-23 | Sharpe ≥1,0 **E** haircut a $5.000 ≤40% (se sfonda → ritiro a prescindere), poi `weights_tilt_null` e capitale ≥$5k | 🚨 legge un monitor **mal tarato** (vedi §5.3) | +| **XSR01** | 2026-10-23 | Sharpe ≥1,0 **E** haircut a $5.000 ≤40% (se sfonda → ritiro a prescindere), poi `weights_tilt_null` e capitale ≥$5k | 🚨 legge un monitor **mal tarato** (§5.3), e P(capitale ≥$5k quel giorno) = **0,0%** (mediana $2.635) → **il criterio di capitale non e' raggiungibile**. Sotto la lente RENDITA il sleeve e' **eguagliato da un conto remunerato** (📌 sotto) | | **DVOLSPREAD** | kill 2026-10-24 se Sh<−0,50; decisione 2027-01-24 | (a) Sharpe>0 (b) marginale ADDS+robust_oos+insample_edge (c) **DSR ≥0,95** (d) `weights_tilt_null`; **veto** se barre attive <80% → si estende, non si decide | il veto **non scatta** su una serie che registra 2 min/giorno (§5.1) | | **GATE PROP-01** | chiuso 22-23/08 | (a) listino 13/13 ✅ (b) ancora SKH01 23/23 ✅ (c) economica+sopravvivenza 2/2 ✅ | **chiuso 3/3** — ma il numero operativo e' sceso 42% → 7,8% → 4,4% → 2,6% | | **GATE PREVDAY-01** | scritto 23/08 | 10 condizioni | **8/10**: la cella che GIRA non e' quella che la selezione onesta sceglie, e quella viva **fallisce il DSR** (0,905) | | **`edge_watch`** (kill del book live) | continuo | **(A)** Sharpe rolling 36m < −0,5 → si riapre `weights_tilt_null`+DSR, il book **non si spegne da solo**; **(B)** in un anno con DD buy&hold >10%, il DD di TP01 deve restare <75% di quello | stato 27/07: Sharpe 36m +1,51, protezione **8/8** | +📌 **XSR01 sotto la lente RENDITA (25/08): il meccanismo vale ~0.** Mescolarne i rendimenti da' lo +stesso muro ($476-484k contro $478,7k), e un **conto remunerato** allo stesso tasso (3,72%, vol 0, corr 0, +nessun secondo venue) da' **$477,6k** — identico dentro la risoluzione MC, e **meglio** al 4,0%. Cio' che +si compra e' un **drift scorrelato**, non la scorrelazione (a drift zero il muro SALE). E l'haircut lo +mangia: **non pareggia un conto al 4% nemmeno a haircut ZERO**; pareggia il 2% solo sotto il 46%. +Per un 25% sopra il C\* vero servono **$60.000** di conto. `r0825_xsr_rendita.py`. + 📌 **Un gate superato non e' un numero confermato:** differenze e livelli ereditano la fortuna d'ancora in modo diverso (nella differenza si cancella in parte, nel livello per niente). diff --git a/docs/diary/2026-08-25-versamento-3k.md b/docs/diary/2026-08-25-versamento-3k.md new file mode 100644 index 0000000..0b89183 --- /dev/null +++ b/docs/diary/2026-08-25-versamento-3k.md @@ -0,0 +1,38 @@ +# 2026-08-25 — Il versamento da $3.000: cosa compra, e cosa NON compra + +**Domanda dell'operatore:** *"rianalizza e vediamo come procedere. potrei versare a breve altri 3k"*. +**Script:** `scripts/research/r0825_versamento_3k.py` (lente L3, stessi semi e path del piano: +tutto appaiato, M22). Assunzioni dichiarate: 3k = $3.000 USDC, lump oggi, start $2.065 (watermark +18:47:13Z). Controllo M23: da $2.065 il 10a riproduce $114.929 contro i $114.934 pubblicati da +$2.067 — la macchina riproduce il numero vecchio. + +## Cosa compra (€500/mese, appaiato sugli stessi path) + +| orizzonte | senza lump | con lump | Δ | €/g con lump | P(≥50€/g) | +|---|---|---|---|---|---| +| 5a | $44.921 | $49.730 | +10,7% | 7,94 | 0% | +| 10a | $114.929 | $122.491 | +6,6% | 19,56 | 0% | +| 15a | $228.770 | $241.490 | +5,6% | 38,56 | 12,3% → 17,3% | +| 20a | $408.105 | $427.738 | +4,8% | 68,30 | 79,4% → 83,9% | + +Il lump = **5,5 mesi di versamenti anticipati in un colpo** (N3: e' la prima leva, esercitata). +Pietre miliari con €500/mese: $15k (C\* Hyperliquid) 1,7a → **1,3a**; $20k (si riapre la scelta +del venue) 2,4a → **1,9a**. + +## L'effetto collaterale che conta: il gate XSR01 del 23/10 si RIBALTA + +P(capitale ≥$5k al 23/10): **0,0% → 100,0%** (mediana $5.664, p10 $5.417). Senza lump il gate +moriva da solo sul criterio di capitale e i suoi tre difetti erano accademici. Col lump il +criterio formale **passa**, e la decisione poggerebbe per intero su: +(a) il monitor rotto che registra ~41 min/giorno (§5.1); (b) l'haircut col pavimento sbagliato +di 5-7× (§5.3); (c) un C\* vero $15-20k contro i $1.266 che il 25% di $5.065 mette sul venue. +**Il lump rende il gate LEGGIBILE senza rendere XSR01 comprabile** — il caso peggiore per un +criterio scritto male. La scelta fra riscrivere adesso (dichiarandolo) e lasciare-e-citare va +presa **prima** che il lump atterri, non il 23/10. + +## Meccanica live a $5.065 — niente da toccare + +cap per-asset $1.032 → $2.532 (equity×0,5, si adegua da solo); min_order $5 dal 0,48% al 0,20% +del cap (C2); fallback min($3.000, wm×0,5) = $2.532; disaster-SL invariato (e il nome continua a +ingannare). ⚠️ **Slip-audit da rifare** dopo i primi fill: misurato su $597-2.067, a $5k la taglia +relativa raddoppia — lo script con normalizzazione per-fill c'e' gia'. diff --git a/docs/diary/2026-08-25-xsr01-sotto-la-lente-rendita.md b/docs/diary/2026-08-25-xsr01-sotto-la-lente-rendita.md new file mode 100644 index 0000000..7ad7421 --- /dev/null +++ b/docs/diary/2026-08-25-xsr01-sotto-la-lente-rendita.md @@ -0,0 +1,114 @@ +# 2026-08-25 — XSR01 sotto la lente della RENDITA: e' un conto deposito con un secondo venue + +**Domanda dell'operatore:** *"rendita perpetua ma non ho ancora capito se serve XSR01"*. +**Script:** `scripts/research/r0825_xsr_rendita.py` (N11: ogni numero qui sotto lo riproduce). + +## Perche' era una misura nuova + +XSR01 era stato giudicato tre volte — ammissione (25/07), gate di deploy pre-registrato (23/10), +accumulo (oggi) — e **mai sotto la lente della rendita**. Sono criteri diversi: l'accumulo premia +il **drift**, la rendita premia la **perpetua** (il prelievo piu' alto che sopravvive a 20 anni con +P≥90%). Dichiarato l'obiettivo (N6), il criterio cambia, e nessuno aveva rifatto il conto. + +## Cosa mi aspettavo prima di misurare (M12) + +(a) a iso-nozionale il mix perde drift e **alza** il muro; (b) a iso-rischio (M6) il verdetto puo' +**ribaltarsi**; (c) il vincolo che decide non sara' ne' (a) ne' (b) ma il **capitale**. +**Tutte e tre vere** — ma (c) e' arrivata con una gamba che non avevo previsto: il null. + +## Le serie, e la finestra in cui esistono entrambe + +| serie | finestra | drift/a | vol/a | Sharpe | maxDD | SE drift | +|---|---|---|---|---|---|---| +| libro L3 — **tutta** la storia | 2019-03-14 → 2026-08-22 | 15,19% | 11,29% | 1,35 | −12,4% | 5,19% | +| libro L3 — finestra comune | 2024-01-01 → 2026-08-22 | 10,96% | 10,14% | 1,08 | −8,6% | 7,97% | +| XSR01 — finestra comune | idem (965 g, 2,64 anni) | **3,72%** | 2,34% | 1,59 | −2,5% | 1,46% | + +corr libro↔XSR01 **+0,049**. Finestra comune = **35%** della storia del libro → **il muro calcolato +qui NON e' il $313k pubblicato**: si confronta solo con se stesso (base $602.660). +📌 Sharpe di XSR01 a oggi, **lente dei gate, sole barre chiuse: 1,56** — il "1,82" pubblicato e' una +**terza** lente (XSR-REPRO, 22/08). Si cita sempre la coppia (lente, ultima barra chiusa). + +## (2) iso-nozionale — XSR01 ALZA il muro + +w 5→50%: muro da $604k a $668k (**+0,3% → +10,8%**). Il mix e' piu' liscio ma piu' povero, e per +una rendita il drift comanda. **A iso-nozionale XSR01 peggiora l'obiettivo dichiarato.** + +## (3) iso-rischio (M6) — si ribalta, e sale il sospetto + +Ri-scalando ogni mix a `vol(libro)`: w 25% → k **1,32x**, muro **$478.729 (−20,6%)**. +⚠️ **L'argmax e' al BORDO** (w=50%, k=1,93x): il muro scende **monotonamente** in w, quindi la +griglia non indica un ottimo, dice *"il piu' possibile"* — e cio' che si compra in quella direzione +e' il **k**, non XSR01 (M8). Sopra 1,40x il libro **non puo' eseguire** (decisione 23/08, e in +`config/live.json` non esiste una chiave di scala). Peso portato avanti: **25%, k 1,32x**. + +## (3b) IL NULL — ed e' il risultato della sessione + +A iso-vol vale `drift = Sharpe × vol_libro`: la colonna "drift" della sezione (3) **e' la colonna +"Sharpe" ri-etichettata**. Tutto il guadagno di muro e' guadagno di **Sharpe da diversificazione**. +Quattro sostituti, stesso peso 25%, stessa ri-scalatura: + +| sostituto | muro | Δ vs base | +|---|---|---| +| **XSR01 (vero)** | **$478.729** | **−20,6%** | +| N1 mescolato — stessi rendimenti, ordine casuale (3 semi) | $475.933 / $483.846 / $484.421 | −21,0 / −19,7 / −19,6% | +| N2 rumore gaussiano, media e vol di XSR01 (3 semi) | $457.751 / $499.263 / $531.158 | −24,0 / −17,2 / −11,9% | +| N3 rumore, **drift ZERO** (3 semi) | $558.080 / $617.270 / $662.436 | −7,4 / **+2,4** / **+9,9%** | +| **N4 conto remunerato 3,72%, vol 0** | **$477.607** | **−20,8%** | +| N4 conto remunerato 4,0%, vol 0 | $469.353 | −22,1% | + +📌 **Il MECCANISMO di XSR01 vale 0,8% di muro** — dentro la risoluzione Monte Carlo (spread fra semi +0,151% di perpetua ≈ $17k di muro, M23). **Mescolare i suoi rendimenti non cambia niente.** +📌 **N3 (drift zero) non paga**: mediana $617k **sopra** la base. La sola riduzione di varianza non +compra rendita. Quindi **cio' che si compra e' il DRIFT scorrelato**, non la scorrelazione. +🚨 **N4 e' la riga che risponde alla domanda.** Un **conto remunerato** allo stesso tasso (3,72%), +**vol zero, corr zero, nessun secondo venue**, da' $477.607 contro i $478.729 di XSR01: **identici +dentro la risoluzione**. Al 4,0% e' **meglio**. Ed e' un confronto **in svantaggio per il conto**: +la lente L3 gli applica il **33%** di `c-sexies`, mentre un BOT paga 12,5% e un deposito 26%. + +## (4)-(5) gate e risoluzione + +`weights_tilt_null` (XSR01 25% vs 0%): Δ_is +0,061 · Δ_hold +0,162 · pctl 25,8 < 90,0 → **PASS**. +⚠️ hold-out del gate = 2025-01-01 → la gamba in-sample e' **un solo anno**: gira, ma e' un indizio. +Differenza appaiata di drift **+1,16%/anno, SE 0,48% → t +2,40**. Drift XSR01 **+3,72%, SE 1,46%, +t +2,55**. Risolto, ma di poco, e su 2,64 anni. + +## (6) il vincolo che non e' nelle tabelle + +XSR01 gira su **Hyperliquid**: il suo peso e' capitale che **esce** da Deribit, non che si aggiunge. +A $2.067 il 25% sono **$517**, contro un **C\* misurato di $15.000-20.000** (il gate 23/10 e' tarato +su ~$3.000, §5.3). Per un 25% sopra C\* servono **$60.000 sul conto totale**. +E l'haircut mangia **proprio la cosa che il null dice di star comprando**: + +| haircut | netto/anno | vs conto 4,0% | vs conto 2,0% | +|---|---|---|---| +| 0% | 3,72% | SOTTO | sopra | +| 20% | 2,97% | SOTTO | sopra | +| 40% (soglia del gate) | 2,23% | SOTTO | sopra | +| 60% | 1,49% | SOTTO | **SOTTO** | + +**Pareggio con un conto al 2%: haircut 46%. Con un conto al 4%: non pareggia nemmeno a haircut ZERO.** +E l'haircut pubblicato ($14,41 di ticket) e' **~2,4x ottimista e senza script che lo riproduca**. + +## VERDETTO + +**4/6 condizioni** — ma il conteggio non e' la lettura. La lettura e': + +> Sotto la lente **rendita perpetua**, XSR01 fa una cosa reale — porta un **drift scorrelato a bassa +> vol** — e la fa in modo **non proprietario**: mescolarlo non cambia il risultato, e un conto +> remunerato allo stesso tasso lo **eguaglia a vol zero e senza un secondo venue**. +> Il vincolo binding non e' il rendimento: e' il **capitale** ($60.000 per un 25% sopra C\*), e sotto +> quella soglia la domanda *"serve XSR01?"* **non ha una risposta comprabile**. + +**Cosa NON dice.** Non dice che XSR01 e' morto: lo Sharpe standalone 1,56 e il DSR 0,983 reggono, e +sotto la lente **accumulo** il conto va rifatto (li' il drift comanda in modo diverso). Dice che +**per la rendita non e' lo strumento**, e che il gate del 23/10 sta per decidere su una gamba che a +questo capitale non e' comprabile. + +## Cosa e' cambiato in me mentre misuravo + +Ho copiato in questo script **lo stesso difetto che avevo riparato stamattina** in +`r0825_capitale_fermo.py`: la maschera dei giorni flat presa dalla serie **de-luckata**, dove gli +zeri esatti non esistono piu' perche' `deluck` sottrae una costante. Stampava `0.0%` e non si sarebbe +notato senza la riga che conta i giorni. Riparata con l'`assert` che manca(va) a monte: la maschera +si prende dalla **serie grezza**. *Riparare un difetto in un file non lo ripara nella testa.* diff --git a/docs/memory/10-sleeve-e-candidati.md b/docs/memory/10-sleeve-e-candidati.md index 0108a7f..fc17e28 100644 --- a/docs/memory/10-sleeve-e-candidati.md +++ b/docs/memory/10-sleeve-e-candidati.md @@ -328,6 +328,38 @@ Ogni numero va letto con la sua banda d'ancora e la sua lente. (ampiezza effettiva 4.5 → c'e' un fattore comune da togliere); e i suoi null vanno confrontati **a fee zero**, perche' permutare un segnale ne fa esplodere il turnover e il null perderebbe per costo invece che per assenza di informazione (p-value trionfale e falso). + 🚨 **SOTTO LA LENTE *RENDITA PERPETUA* IL MECCANISMO VALE ~0 (2026-08-25, `r0825_xsr_rendita.py`).** + Prima valutazione di XSR01 sul criterio della **perpetua** invece che su Sharpe o accumulo — N6: + dichiarato l'obiettivo, cambia il criterio, e nessuno aveva rifatto il conto. + Finestra comune libro↔XSR01 **2024-01-01 → 2026-08-22 (965 g, 2,64 anni = 35% della storia del + libro)** → i muri qui sotto **non sono** il $313k pubblicato, si confrontano solo fra loro + (base libro-solo: perpetua 3,30%, muro **$602.660**). XSR01 su questa finestra: drift **3,72%/a** + (SE 1,46%, t +2,55), vol 2,34%, Sharpe **1,56**, maxDD −2,5%, corr col libro **+0,049**. + · **iso-nozionale il muro SALE** (+0,3% a w=5% → +10,8% a w=50%): per una rendita il drift comanda. + · **iso-rischio (M6)** si ribalta: w=25%, k **1,32x** → muro **$478.729 (−20,6%)**. Ma **l'argmax e' + al BORDO** (w=50%, k=1,93x, muro scende monotonamente in w) → la griglia dice *"il piu' possibile"*, + e cio' che si compra in quella direzione e' il **k**, non XSR01 (M8). Sopra 1,40x il libro non + puo' eseguire (decisione 23/08 + nessuna chiave di scala in config). + · **IL NULL, che e' il risultato.** A iso-vol `drift = Sharpe × vol_libro`, quindi tutto il + guadagno di muro e' guadagno di **Sharpe da diversificazione**. Quattro sostituti a w=25%: + **XSR01 mescolato** (stessi rendimenti, ordine casuale, 3 semi) → $475.933 / $483.846 / $484.421 + contro i **$478.729** del vero → **il meccanismo vale 0,8% di muro, dentro la risoluzione MC** + (spread fra semi 0,151% di perpetua ≈ $17k). **Rumore a media e vol di XSR01** → mediana $499.263. + **Rumore a drift ZERO** → mediana **$617.270, SOPRA la base**: la sola riduzione di varianza + **non compra rendita**. Si compra il **drift scorrelato**, non la scorrelazione. + · 🚨 **Un CONTO REMUNERATO allo stesso tasso lo eguaglia**: 3,72% a vol 0, corr 0, nessun secondo + venue → **$477.607**; al 4,0% → **$469.353, meglio**. Ed e' un confronto **in svantaggio per il + conto**: la lente L3 gli applica il 33% di `c-sexies` (un BOT paga 12,5%, un deposito 26%). + · **L'haircut mangia esattamente cio' che il null dice di star comprando**: 3,72% lordi → 2,97% a + haircut 20%, **2,23% alla soglia 40% del gate**, 1,49% al 60%. **Non pareggia un conto al 4,0% + nemmeno a haircut ZERO**; pareggia il 2,0% solo sotto il **46%**. E l'haircut pubblicato ($14,41 + di ticket) e' ~2,4x ottimista e senza script che lo riproduca (XSR-REPRO). + · `weights_tilt_null` (25% vs 0%) **PASS** (Δ_is +0,061 · Δ_hold +0,162 · pctl 25,8 < 90,0) — ma + l'hold-out del gate e' 2025-01-01, quindi la gamba in-sample e' **un solo anno**: indizio, non prova. + · **Il vincolo binding non e' il rendimento, e' il CAPITALE**: a $2.067 un 25% sono $517 contro un + **C\* misurato $15.000-20.000**; per un 25% sopra C\* servono **$60.000 sul conto totale**. + 📌 **Cosa NON dice:** non che XSR01 sia morto — Sharpe standalone 1,56 e DSR 0,983 reggono, e sotto + la lente **accumulo** il conto va rifatto. Dice che **per la RENDITA non e' lo strumento**. - **Soffitto strutturale BTC/ETH-direzionale ~1.3** superato SOLO espandendo a un meccanismo diverso: cross-sectional su universo Hyperliquid certificato (XS01) → portafoglio Sharpe ~1.55. diff --git a/docs/research/RESULTS-0822.md b/docs/research/RESULTS-0822.md index 371080e..d621e69 100644 --- a/docs/research/RESULTS-0822.md +++ b/docs/research/RESULTS-0822.md @@ -16,6 +16,7 @@ null de-levering superato + eseguibilita' al capitale dichiarato. | 6 | OI-PIN | **SCARTATO** | il max-pain batte uno strike casuale ma **non batte mai (0/24) la media a 7 giorni dello spot**, un livello che non usa NESSUN dato di opzioni: non e' pinning, e' reversione verso il centro recente con l'OI come stimatore rumoroso di quel centro | | 8 | VOL-SIZE | **LEAD RIDIMENSIONATO** — gate 22/12 **gia' fallito oggi** | dare a SKH01 una size per-trade regge a **23/23 ancore**, 8/8 anni, null di permutazione e trasferimento su V1 — ma vale **+0,07 di Sharpe di libro, un terzo della fortuna d'ancora del libro stesso (+0,196)**. Il vol-target di **libro** e' invece falsificato: compra peso SKH gia' respinto e peggiora l'eseguibilita' | | — | **XSR-REPRO** (integrita') | 🚨 **DIFETTO DI PRODUZIONE** | il numero 1.82 e' SPIEGATO e non era sbagliato (era su una **terza** lente, e su una barra non ancora chiusa) — ma cercandone la causa e' emerso che **`paper_xsr` registra ~41 minuti di mercato al giorno**, non un giorno. Tre gate pre-registrati leggono serie costruite cosi' | +| 70 | **XSR-RENDITA** | **SCARTATO sotto la lente RENDITA** (non come sleeve) | il muro scende del 20,6% a iso-rischio, ma **mescolare i rendimenti di XSR01 da' lo stesso muro** (il meccanismo vale 0,8%, dentro la risoluzione MC) e un **conto remunerato allo stesso tasso lo eguaglia a vol 0 e senza secondo venue**. A drift zero il muro **sale**: si compra un drift scorrelato, non la scorrelazione. E l'haircut non pareggia un conto al 4% **nemmeno a haircut ZERO** | | 5 | SKEW | **SCARTATO** (Q1, Q2) + **LEAD** (Q3, gate 2027-02-22) | il prezzo muove lo skew (t 3,1-9,4 su 8/8 test), **non il contrario** (max |t| in avanti 2,35 contro 2,08 atteso dal rumore). Ma Q3 e' grosso: **il f=0,73 di VRP01 e' per il 42% STRUTTURA A TERMINE e solo per il 25% skew** | | — | **HL-EXEC** (audit di fatto) | **3 falsificazioni misurate** | il pavimento vero e' **$10 (non $5)** e il taker **4,50 bps (non 5,0)** — ma il *"XS01 serve ~$20k"* e' **refutato del tutto** (nessuna soglia da min-order), il *"XSR01 ~$5.000"* e' **conservativo di 1,7x** (vero ~$3.000), e lo **slippage "rischio #1" di XSR01 e' refutato** con margine **21x** | | — | **SLIP-AUDIT** (audit di fatto) | **SCARTATO** = nessun costo nascosto a questa taglia | i backtest a 10 bps RT restano **conservativi di ~1,5 bps/lato**; nessuna evidenza di impatto sopravvive al null (p=0,274; estremi avversi **1/18 contro 4,7 attesi**). **Ma la misura ha una data di scadenza** | @@ -3730,3 +3731,66 @@ pubblicato, e nessuno dei tre produce reddito. **Con questa sono 69 filoni e 0 candidati promossi.** I vincoli binding restano due: **il capitale che entra** e **il conto che non sparisce**. + +--- + +## 70 — XSR-RENDITA (r0825_xsr_rendita.py) — XSR01 sul criterio della PERPETUA + +**Domanda dell'operatore:** *"rendita perpetua ma non ho ancora capito se serve XSR01"*. XSR01 era +stato giudicato su **ammissione**, **gate di deploy** e **accumulo**, mai sulla **rendita** — e sono +criteri diversi (l'accumulo premia il drift, la rendita la **perpetua**). N6: dichiarato l'obiettivo, +si rifa' il conto. + +**Finestra.** Un MIX esiste solo dove esistono ENTRAMBE le serie → **2024-01-01 → 2026-08-22** +(965 g, 2,64 anni, **35%** della storia del libro). ⚠️ **I muri di questo filone NON sono il $313k +pubblicato**: base libro-solo su questa finestra = perpetua 3,30%, muro **$602.660**. +XSR01, lente dei gate su sole barre chiuse: drift **3,72%/a** (SE 1,46% → t +2,55), vol 2,34%, +Sharpe **1,56**, maxDD −2,5%, corr col libro **+0,049**. + +**(a) iso-nozionale: il muro SALE** (+0,3% a w=5% → **+10,8%** a w=50%). Il mix e' piu' liscio e piu' +povero, e per una rendita il drift comanda. +**(b) iso-rischio (M6): si ribalta** — w=25%, k **1,32x** → muro **$478.729 (−20,6%)**. +⚠️ **argmax al BORDO** (w=50%, k=1,93x; il muro scende **monotonamente** in w): la griglia non indica +un ottimo, dice *"il piu' possibile"*, e in quella direzione si compra il **k**, non XSR01 (M8). +Sopra 1,40x il libro **non puo' eseguire** (decisione 23/08; nessuna chiave di scala in config). + +🚨 **(c) IL NULL — ed e' il risultato.** A iso-vol `drift = Sharpe × vol_libro`: la colonna "drift" +della tabella iso-rischio **e' la colonna "Sharpe" ri-etichettata**, quindi tutto il guadagno di muro +e' guadagno di **Sharpe da diversificazione**. Quattro sostituti a w=25%, stessa ri-scalatura: + +| sostituto | muro | Δ vs base | +|---|---|---| +| **XSR01 (vero)** | **$478.729** | **−20,6%** | +| XSR01 **mescolato** (3 semi) | $475.933 · $483.846 · $484.421 | −21,0 · −19,7 · −19,6% | +| rumore a media e vol di XSR01 (3 semi) | $457.751 · $499.263 · $531.158 | −24,0 · −17,2 · −11,9% | +| rumore a **drift ZERO** (3 semi) | $558.080 · $617.270 · $662.436 | −7,4 · **+2,4** · **+9,9%** | +| **conto remunerato 3,72%, vol 0** | **$477.607** | **−20,8%** | +| conto remunerato 4,0%, vol 0 | $469.353 | −22,1% | + +📌 **Il meccanismo di XSR01 vale 0,8% di muro** — dentro la risoluzione Monte Carlo (spread fra semi +**0,151%** di perpetua ≈ $17k, M23). **Mescolarne i rendimenti non cambia niente.** +📌 **A drift zero il muro SALE**: la sola riduzione di varianza **non compra rendita**. Cio' che si +compra e' un **drift scorrelato**, non la scorrelazione. +🚨 **Un conto remunerato allo stesso tasso lo eguaglia**, a vol 0, corr 0 e **senza un secondo venue** +— e il confronto e' **in svantaggio per il conto**: la lente L3 gli applica il 33% di `c-sexies`, +mentre un BOT paga 12,5% e un deposito 26%. + +**(d) L'haircut mangia esattamente cio' che si sta comprando.** 3,72% lordi → 2,97% a haircut 20%, +**2,23% alla soglia 40% del gate**, 1,49% al 60%. **Non pareggia un conto al 4,0% nemmeno a haircut +ZERO**; pareggia il 2,0% solo sotto il **46%**. L'haircut pubblicato ($14,41/ticket) e' ~2,4x +ottimista e **senza script che lo riproduca** (XSR-REPRO). + +**(e) `weights_tilt_null` (25% vs 0%): PASS** (Δ_is +0,061 · Δ_hold +0,162 · pctl 25,8 < soglia 90,0) +— ma l'hold-out del gate e' 2025-01-01 e la gamba in-sample e' **un solo anno**: indizio, non prova. + +**(f) Il vincolo binding non e' il rendimento, e' il CAPITALE.** XSR01 gira su Hyperliquid: il suo +peso e' capitale che **esce** da Deribit. A $2.067 un 25% sono **$517** contro un **C\* misurato +$15.000-20.000**; per un 25% sopra C\* servono **$60.000 sul conto totale**. + +**VERDETTO 4/6 — ma la lettura non e' il conteggio:** sotto la lente rendita XSR01 fa una cosa reale +(porta un drift scorrelato a bassa vol) **in modo non proprietario**. **Non dice che e' morto** +(Sharpe 1,56 e DSR 0,983 reggono, e sotto la lente accumulo il conto va rifatto): dice che **per la +rendita non e' lo strumento**, e che il gate del 23/10 sta per decidere su una gamba che a questo +capitale **non e' comprabile**. + +**Con questa sono 70 filoni e 0 candidati promossi.** diff --git a/scripts/research/r0825_versamento_3k.py b/scripts/research/r0825_versamento_3k.py new file mode 100644 index 0000000..39c7cd0 --- /dev/null +++ b/scripts/research/r0825_versamento_3k.py @@ -0,0 +1,159 @@ +"""r0825_versamento_3k.py — cosa compra un versamento di $3.000 ADESSO, dal conto vero. + +DOMANDA (operatore, 2026-08-25 sera): *"rianalizza e vediamo come procedere. potrei versare a +breve altri 3k"*. Il conto passerebbe da ~$2.065 a ~$5.065 — esattamente a cavallo della soglia +$5k su cui sta scritto il criterio di capitale del gate XSR01 (23/10), e a un terzo del muro +$15-20k che e' insieme il C* vero di Hyperliquid E la soglia della decisione vincolante +"100% Deribit fino a $20k" (26/07). + +ASSUNZIONI DICHIARATE: + - "3k" = 3.000 USDC = $3.000 (i versamenti precedenti dell'operatore sono in USDC); + - il lump atterra OGGI. "A breve" puo' voler dire fra settimane: ritardarlo di un mese sposta + i numeri di poco e sempre nello stesso verso (un po' meno beneficio). Non lo modello. + - equity di partenza: 2.065,00 — letta dal watermark alle 18:47:13Z di oggi (l'istante + dell'evento, non l'istante della scrittura). + +LENTE: L3 CONGIUNTA (funding esatto + fisco d'accumulo + ancora de-luckata), la stessa di +`r0825_piano_10a_500` — stessi semi, stessi path: ogni confronto qui e' APPAIATO (M22). +⚠️ Stessi difetti ereditati e dichiarati la': 121 versamenti in 10 anni, `versato` scalare. +⚠️ M23: N_PATHS 3000, blocchi 20g. La banda del muro resta [$187k – $1,14M]: i numeri assoluti +qui sotto stanno sulla lente `hourly`+L3 e vanno letti con quella etichetta (P7). + + uv run python scripts/research/r0825_versamento_3k.py +""" +from __future__ import annotations + +import sys +from pathlib import Path + +import numpy as np + +ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(ROOT)) +sys.path.insert(0, str(ROOT / "scripts" / "research")) + +import r0725_capcurve as CC # noqa: E402 +import r0807_piano_netto as PN # noqa: E402 +import r0822d_piano_vero as PV # noqa: E402 +import r0823_deposit_timing as DT # noqa: E402 + +START_OGGI = 2065.00 # watermark 2026-08-25T18:47:13Z +LUMP_USD = 3000.0 # il versamento ipotizzato, in USD +START_LUMP = START_OGGI + LUMP_USD +DEP = 500.0 # EUR/mese, il piano dichiarato +ORIZZONTI = (5, 10, 15, 20) +N_PATHS = 3000 +SEED = PV.SEED_TRAJ +BLOCK = PV.BLOCK +GATE_XSR = 59 # giorni da oggi al 2026-10-23 (25/08 -> 23/10) +MILESTONE = (5_000.0, 15_000.0, 20_000.0) # criterio gate · C* Hyperliquid · muro "100% Deribit" + + +def sez(t: str) -> None: + print(f"\n{'='*100}\n {t}\n{'='*100}") + + +def eur_g(cap: np.ndarray, perp: float) -> np.ndarray: + return cap * perp / 365.0 / CC.EURUSD + + +def main() -> None: + print("=" * 100) + print(f" r0825 — IL VERSAMENTO DA $3.000: ${START_OGGI:,.0f} -> ${START_LUMP:,.0f}") + print("=" * 100) + + B = PV.costruisci_serie() + r = PV.deluck(B["fund"].values.astype(float)) + lente = PV.Lente("L3 congiunta", r, lordizza=False) + lente.perp, lente.muro = PV.muro_di(lente) + print(f"\n lente L3: drift {lente.drift:.2%} · vol {lente.vol:.2%} · perpetua {lente.perp:.2%}" + f" · muro ${lente.muro:,.0f}") + + # ---------------------------------------------------- (1) cosa compra il lump, appaiato + sez(f"(1) COSA COMPRA IL LUMP — €{DEP:.0f}/mese, stessi path, da ${START_OGGI:,.0f} vs " + f"${START_LUMP:,.0f}") + print(f" {'oriz.':>6} {'senza lump':>13} {'con lump':>13} {'guadagno':>10} " + f"{'€/g senza':>11} {'€/g con':>9} {'P(50) senza':>12} {'P(50) con':>10}") + print(" " + "-" * 92) + d10 = {} + for anni in ORIZZONTI: + rng = np.random.default_rng(SEED) + ii = DT.boot_idx(len(r), N_PATHS, anni * 365, BLOCK, rng) + paths = r[ii] + a = PN.accumula(paths, DEP, lente.muro, lente.aliq, lente.patr, start=START_OGGI) + b = PN.accumula(paths, DEP, lente.muro, lente.aliq, lente.patr, start=START_LUMP) + ea, eb = eur_g(a["cap"], lente.perp), eur_g(b["cap"], lente.perp) + pa, pb = float((ea >= 50).mean()), float((eb >= 50).mean()) + if anni == 10: + d10 = dict(a=a, b=b, ea=ea, eb=eb, pa=pa, pb=pb) + print(f" {anni:>4}a ${np.median(a['cap']):>11,.0f} ${np.median(b['cap']):>11,.0f} " + f"{np.median(b['cap'])/np.median(a['cap'])-1:>9.1%} {np.median(ea):>10.2f} " + f"{np.median(eb):>8.2f} {pa:>11.1%} {pb:>9.1%}") + lump_in_mesi = LUMP_USD / CC.EURUSD / DEP + print(f"\n il lump equivale a {lump_in_mesi:.1f} mesi di versamenti anticipati in un colpo.") + print(f" N3: la leva 'versare' resta la prima — questo lump E' quella leva, esercitata.") + + # ---------------------------------------------------- (2) pietre miliari + sez("(2) QUANDO ARRIVANO LE PIETRE MILIARI (€500/mese, primo passaggio, 10 anni di path)") + rng = np.random.default_rng(SEED) + ii = DT.boot_idx(len(r), N_PATHS, 10 * 365, BLOCK, rng) + paths10 = r[ii] + print(f" {'soglia':>9} {'cosa sblocca':<44} {'senza lump':>11} {'con lump':>9}") + print(" " + "-" * 82) + naming = {5_000: "criterio di capitale del gate XSR01", + 15_000: "C* vero di Hyperliquid (pavimento misurato)", + 20_000: "decisione '100% Deribit fino a $20k' si riapre"} + for ms in MILESTONE: + riga = [] + for st in (START_OGGI, START_LUMP): + out = PN.accumula(paths10, DEP, ms, lente.aliq, lente.patr, start=st) + if st >= ms: + riga.append("gia' li'") + continue + d = PV.leggi(out["colpito"], anni_sim=10) + riga.append("oltre 10a" if not np.isfinite(d["med"]) else f"{d['med']:.1f}a") + print(f" ${ms:>7,.0f} {naming[int(ms)]:<44} {riga[0]:>11} {riga[1]:>9}") + + # ---------------------------------------------------- (3) il gate del 23/10 + sez(f"(3) GATE XSR01 (2026-10-23, fra {GATE_XSR} giorni) — P(capitale >= $5.000 quel giorno)") + rng = np.random.default_rng(SEED) + ii = DT.boot_idx(len(r), N_PATHS, GATE_XSR, BLOCK, rng) + pg = r[ii] + for st, nome in ((START_OGGI, "senza lump"), (START_LUMP, "con lump")): + out = PN.accumula(pg, DEP, 5_000.0, lente.aliq, lente.patr, start=st) + p = float((out["cap"] >= 5_000.0).mean()) + print(f" {nome:<11} start ${st:>7,.0f} -> P(>= $5k al 23/10) = {p:>6.1%} " + f"(mediana ${np.median(out['cap']):,.0f}, p10 ${np.percentile(out['cap'],10):,.0f})") + print(f"\n ⚠️ Il criterio FORMALE diventa raggiungibile col lump — ma il C* VERO del venue e'") + print(f" $15.000-20.000 (§5.3), il 25% a ${START_LUMP:,.0f} sono ${START_LUMP*0.25:,.0f} su") + print(f" Hyperliquid, e la decisione vincolante del 26/07 dice 100% Deribit fino a $20k.") + print(f" Il lump rende il gate LEGGIBILE, non rende XSR01 comprabile.") + + # ---------------------------------------------------- (4) meccanica live a $5.065 + sez(f"(4) COSA CAMBIA NELLA MECCANICA LIVE, DA SOLO, A ${START_LUMP:,.0f}") + print(f" cap per-asset (equity x 0,5, nessun tetto assoluto sul percorso normale):") + print(f" ${START_OGGI*0.5:,.0f} -> ${START_LUMP*0.5:,.0f} per asset — nessuna config da toccare,") + print(f" il profilo di rischio RELATIVO non cambia (leva lorda max resta 1,0x; realizzata") + print(f" max 0,52x: il vincolo e' il segnale, non il cap)") + print(f" min_order $5: passa dal {5/(START_OGGI*0.5):.2%} al {5/(START_LUMP*0.5):.2%} del cap") + print(f" per-asset (C2: un parametro in valuta assoluta pesa meno al crescere del conto)") + print(f" fallback a equity illeggibile: min($3.000, watermark x 0,5) = " + f"min(3000, {START_LUMP*0.5:,.0f}) -> ${min(3000.0, START_LUMP*0.5):,.0f}") + print(f" disaster-SL -30%: invariato — resta rotolante e simmetrico (peggior DD misurato") + print(f" dall'ingresso -60,6%/-61,5%: il nome inganna anche a $5k)") + print(f" ⚠️ slip-audit: misurato su fill a $597-2.067; a ${START_LUMP:,.0f} la taglia relativa") + print(f" raddoppia -> la misura VA RIFATTA dopo i primi fill alla taglia nuova (C2/C5),") + print(f" lo script c'e' gia' (`r0822_slip_audit.py`, normalizzazione per-fill)") + + # ---------------------------------------------------- verdetto + sez("VERDETTO (runtime)") + med_a, med_b = np.median(d10["a"]["cap"]), np.median(d10["b"]["cap"]) + print(f" a 10 anni con €500/mese: ${med_a:,.0f} -> ${med_b:,.0f} ({med_b/med_a-1:+.1%}), " + f"€/g {np.median(d10['ea']):.2f} -> {np.median(d10['eb']):.2f}") + print(f" P(>=50 €/g a 10a): {d10['pa']:.1%} -> {d10['pb']:.1%}") + print(f" il lump anticipa il piano di ~{lump_in_mesi:.0f} mesi e rende leggibile il gate del") + print(f" 23/10; NON rende comprabile XSR01 (C* $15-20k) e NON riapre la scelta del venue ($20k).") + + +if __name__ == "__main__": + main() diff --git a/scripts/research/r0825_xsr_rendita.py b/scripts/research/r0825_xsr_rendita.py new file mode 100644 index 0000000..46aaef3 --- /dev/null +++ b/scripts/research/r0825_xsr_rendita.py @@ -0,0 +1,372 @@ +"""r0825_xsr_rendita.py — XSR01 serve alla RENDITA PERPETUA? (non all'accumulo, non allo Sharpe) + +DOMANDA (operatore, 2026-08-25): *"rendita perpetua ma non ho ancora capito se serve XSR01"*. + +PERCHE' E' UNA MISURA NUOVA E NON UNA CITAZIONE. XSR01 e' stato giudicato tre volte — ammissione +(25/07), gate di deploy (23/10, pre-registrato), accumulo (oggi) — e **mai sotto la lente della +rendita**. Sono criteri diversi: l'accumulo premia il DRIFT, la rendita premia la **PERPETUA**, +cioe' il prelievo piu' alto che sopravvive a 20 anni con P>=90% (definizione 25/07, invariata). +La perpetua e' una funzione di drift **e** di coda: un diversificatore che non aggiunge drift puo' +comunque alzarla, ed e' esattamente il caso che nessuno ha misurato. N6: l'obiettivo si dichiara +prima di ottimizzarlo — qui e' dichiarato, quindi si misura quello. + +COSA MI ASPETTO PRIMA DI MISURARE (M12: una previsione dichiarata va misurata, non assunta): + (a) a ISO-NOZIONALE il mix perde drift (XSR01 rende 4,2%/a contro i 15,2% del libro) e quindi + **alza il muro** anche se abbassa la vol -> XSR01 sembrera' inutile; + (b) a ISO-RISCHIO (M6, la lente obbligatoria per un diversificatore a basso CAGR) il mix puo' + essere ri-scalato fino alla vol del libro e allora il verdetto puo' **ribaltarsi**; + (c) il vincolo che decide non sara' ne' (a) ne' (b) ma il **capitale**. +Se (c) e' vero, le prime due sezioni sono aritmetica su una cosa che non si puo' comprare. + +⚠️ TRE DIFETTI EREDITATI, DICHIARATI PRIMA DEI NUMERI (nessuno riparato qui): + 1. **La finestra comune e' corta.** Il libro ha ~7,4 anni, XSR01 ~2,6 (Hyperliquid parte 2024). + Regola del 07/08, gia' violata una volta: **un MIX esiste solo dove esistono ENTRAMBE le + serie**. Quindi il muro del mix NON e' confrontabile col $313k pubblicato (7,4 anni): il + confronto valido e' **libro-sulla-finestra-comune vs mix-sulla-finestra-comune**, appaiato. + 2. **Il numero di titolo di XSR01 (Sharpe 1.82) non e' la sua serie.** XSR-REPRO (22/08): 1.82 + viene da una TERZA lente (divisore fisso 50); la lente che ha girato i gate da' **1.79** alla + scoperta e **1.63** a oggi. Qui si usa la **lente dei gate su sole barre chiuse**, ed e' l'unica + citabile — sempre con la coppia (lente, ultima barra chiusa). + 3. **Il forward monitor di XSR01 e' rotto** (`advance()` registra ~41 min di mercato al giorno, + vol registrata 0,46% contro 2,74% ricalcolata): i 31 giorni di `data/paper_xsr/returns.jsonl` + **non sono usabili** e non entrano in questo script. Qui si misura il BACKTEST, non il vivo. + + uv run python scripts/research/r0825_xsr_rendita.py +""" +from __future__ import annotations + +import sys +from pathlib import Path + +import numpy as np +import pandas as pd + +ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(ROOT)) +sys.path.insert(0, str(ROOT / "scripts" / "research")) + +import r0725_capcurve as CC # noqa: E402 +import r0807_piano_netto as PN # noqa: E402 +import r0822_xsr_repro as XR # noqa: E402 +import r0822d_piano_vero as PV # noqa: E402 +from src.portfolio.portfolio import weights_tilt_null # noqa: E402 + +ANN = np.sqrt(365.0) +N_WALL = 1500 # M23: la risoluzione del Monte Carlo si dichiara — misurata in sezione 6 +SEED_WALL = PV.SEED_WALL +BLOCK = PV.BLOCK +PESI = (0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.40, 0.50) + + +def sez(t: str) -> None: + print(f"\n{'='*100}\n {t}\n{'='*100}") + + +def _sh(r: np.ndarray) -> float: + r = np.asarray(r, float) + return float(r.mean() / r.std() * ANN) if r.std() > 0 else 0.0 + + +def _dd(r: np.ndarray) -> float: + eq = np.cumprod(1.0 + np.asarray(r, float)) + return float((eq / np.maximum.accumulate(eq) - 1.0).min()) + + +def perpetua(r: np.ndarray, aliq: float, patr: float, n_paths: int = N_WALL, + seed: int = SEED_WALL) -> float: + """Il prelievo annuo (frazione del capitale) piu' alto con P(cap a 20a >= cap iniziale) >= 90%. + + Bisezione IDENTICA a `PV.muro_di` — non re-implementata a mano: stessa `PN.sopravvivenza`, + stessi 13 passi, stesso criterio `p10_end >= cap0`. Cambia solo che qui la serie e' un + argomento invece che un attributo di `Lente`, perche' vanno confrontati molti mix.""" + lo, hi = 0.0, 0.40 + for _ in range(13): + mid = (lo + hi) / 2 + s = PN.sopravvivenza(r, 1e6, 1e6 * mid, 20, aliq, patr, n_paths=n_paths, seed=seed) + lo, hi = (mid, hi) if s["p10_end"] >= 1e6 else (lo, mid) + return lo + + +def muro_da(perp: float, prelievo: float) -> float: + return prelievo / perp if perp > 0.002 else float("inf") + + +def se_drift(r: np.ndarray, n: int = 2000, seed: int = 20260825) -> float: + """SE annualizzata del drift, block-bootstrap a blocchi di 20 giorni (la stessa dipendenza + seriale che usa il muro). Serve a dire quanto e' RISOLTA la differenza fra due mix.""" + rng = np.random.default_rng(seed) + B = CC._boot_paths(np.asarray(r, float), n, len(r), BLOCK, rng) + return float(B.mean(axis=1).std() * 365.0) + + +def main() -> None: + print("=" * 100) + print(" r0825 — XSR01 SERVE ALLA RENDITA PERPETUA?") + print("=" * 100) + + # ---------------------------------------------------------------- (0) le due serie + sez("(0) LE DUE SERIE E LA FINESTRA IN CUI ESISTONO ENTRAMBE") + + B = PV.costruisci_serie() + libro_full = pd.Series(PV.deluck(B["fund"].values.astype(float)), index=B.index) + lente_full = PV.Lente("L3 congiunta", libro_full.values, lordizza=False) + prelievo = lente_full.prelievo + aliq, patr = lente_full.aliq, lente_full.patr + + # XSR01: lente dei GATE (basket_from_positions, demean) su sole barre CHIUSE. + P, S = XR.panel(partial_last=False) + xsr_full = XR.lens_L1(P, S).dropna() + ultima = xsr_full.index.max() + # l'ultima barra su disco e' quella del giorno IN CORSO (difetto §5.1): si scarta. + xsr_full = xsr_full[xsr_full.index < pd.Timestamp.utcnow().normalize()] + chiusa = xsr_full.index.max() + print(f" libro L3 (75/25, funding dentro, ancora de-luckata): " + f"{libro_full.index.min().date()} -> {libro_full.index.max().date()} " + f"({len(libro_full)} giorni)") + print(f" XSR01 lente-dei-gate, sole barre chiuse: " + f"{xsr_full.index.min().date()} -> {chiusa.date()} ({len(xsr_full)} giorni)") + print(f" (ultima barra su disco {ultima.date()} = giorno IN CORSO, scartata — difetto §5.1)") + print(f" Sharpe XSR01 a oggi, questa lente: {_sh(xsr_full.values):.2f} " + f"[il '1.82' pubblicato e' una TERZA lente — XSR-REPRO 22/08]") + + ix = libro_full.index.normalize().intersection(xsr_full.index.normalize()) + lb = pd.Series(libro_full.values, index=libro_full.index.normalize()).reindex(ix) + xs = pd.Series(xsr_full.values, index=xsr_full.index.normalize()).reindex(ix) + lb, xs = lb.dropna(), xs.dropna() + ix = lb.index.intersection(xs.index) + lb, xs = lb.reindex(ix).values, xs.reindex(ix).values + print(f"\n FINESTRA COMUNE: {ix.min().date()} -> {ix.max().date()} ({len(ix)} giorni, " + f"{len(ix)/365:.2f} anni)") + print(f" copre il {len(ix)/len(libro_full):.0%} della storia del libro -> il muro calcolato " + f"qui NON e' il $313k pubblicato (7,4 anni): si confronta solo con se stesso.") + + # ---------------------------------------------------------------- (1) le due gambe + sez("(1) LE DUE GAMBE SULLA FINESTRA COMUNE — e quanto e' diversa da tutta la storia") + print(f" {'serie':<34} {'drift/a':>9} {'vol/a':>8} {'Sharpe':>8} {'maxDD':>9} {'SE drift':>10}") + print(" " + "-" * 84) + for nome, r in (("libro L3 — TUTTA la storia", libro_full.values), + ("libro L3 — finestra comune", lb), + ("XSR01 — finestra comune", xs)): + print(f" {nome:<34} {r.mean()*365:>8.2%} {r.std()*ANN:>7.2%} {_sh(r):>8.2f} " + f"{_dd(r):>8.1%} {se_drift(r):>9.2%}") + corr = float(np.corrcoef(lb, xs)[0, 1]) + print(f"\n correlazione libro <-> XSR01 sulla finestra comune: {corr:+.3f}") + # ⚠️ la maschera dei FLAT si prende dalla serie GREZZA, non da quella de-luckata: `deluck` + # sottrae una costante e gli zeri esatti spariscono (difetto trovato e riparato il 25/08). + grezza = pd.Series(B["fund"].values.astype(float), index=B.index.normalize()).reindex(ix) + flat = (grezza.abs() < 1e-12).values + assert flat.sum() > 0, "maschera flat vuota: si sta guardando la serie sbagliata" + print(f" giorni in cui il libro e' FLAT: {flat.mean():.1%} ({flat.sum()} giorni) " + f"— li' XSR01 rende {xs[flat].mean()*365:+.2%}/anno annualizzato contro " + f"{xs[~flat].mean()*365:+.2%} negli altri") + + # ---------------------------------------------------------------- (2) iso-nozionale + sez("(2) MIX A ISO-NOZIONALE — la lente INGENUA (w di XSR01 tolto al libro)") + print(f" {'w XSR01':>8} {'drift/a':>9} {'vol/a':>8} {'Sharpe':>8} {'maxDD':>9} " + f"{'perpetua':>10} {'MURO':>13} {'d muro':>10}") + print(" " + "-" * 88) + base_perp = perpetua(lb, aliq, patr) + base_muro = muro_da(base_perp, prelievo) + iso_n = {} + for w in PESI: + m = (1 - w) * lb + w * xs + p = perpetua(m, aliq, patr) + mu = muro_da(p, prelievo) + iso_n[w] = dict(perp=p, muro=mu, drift=m.mean()*365, vol=m.std()*ANN, sh=_sh(m), dd=_dd(m)) + dmu = "n/d" if not np.isfinite(mu) or not np.isfinite(base_muro) else f"{mu/base_muro-1:+.1%}" + print(f" {w:>7.0%} {m.mean()*365:>8.2%} {m.std()*ANN:>7.2%} {_sh(m):>8.2f} {_dd(m):>8.1%} " + f"{p:>9.2%} ${mu:>12,.0f} {dmu:>10}") + + # ---------------------------------------------------------------- (3) iso-rischio + sez("(3) MIX A ISO-RISCHIO (M6) — la lente OBBLIGATORIA per un diversificatore a basso CAGR") + print(" Ogni mix e' ri-scalato di k = vol(libro)/vol(mix) cosi' che la vol sia IDENTICA a") + print(" quella del libro da solo. E' l'unico confronto che non regala al mix il merito di") + print(" essere semplicemente piu' piccolo (M6, e null del de-levering M5).") + vol_b = lb.std() + print(f"\n {'w XSR01':>8} {'k richiesto':>12} {'drift/a':>9} {'vol/a':>8} {'Sharpe':>8} " + f"{'maxDD':>9} {'perpetua':>10} {'MURO':>13} {'d muro':>10}") + print(" " + "-" * 96) + iso_r = {} + for w in PESI: + m = (1 - w) * lb + w * xs + k = vol_b / m.std() + mk = m * k + p = perpetua(mk, aliq, patr) + mu = muro_da(p, prelievo) + iso_r[w] = dict(k=k, perp=p, muro=mu, drift=mk.mean()*365, sh=_sh(mk), dd=_dd(mk)) + dmu = "n/d" if not np.isfinite(mu) or not np.isfinite(base_muro) else f"{mu/base_muro-1:+.1%}" + print(f" {w:>7.0%} {k:>11.3f}x {mk.mean()*365:>8.2%} {mk.std()*ANN:>7.2%} {_sh(mk):>8.2f} " + f"{_dd(mk):>8.1%} {p:>9.2%} ${mu:>12,.0f} {dmu:>10}") + print("\n ⚠️ Il k di questa colonna e' ARITMETICA, non una configurazione: in `config/live.json`") + print(" NON esiste una chiave di scala (§3), e il gradino di leva NON e' autorizzato oggi") + print(" (1,50x BOCCIATO, k max difendibile 1,40). Un k > 1,40 in questa tabella e' un") + print(" numero che il libro **non puo' eseguire**.") + + # M8: l'argmax di questa griglia sta al BORDO (il muro scende monotonamente in w) -> non e' + # una decisione, e' il bordo. Il peso che si porta avanti e' il migliore DENTRO il k + # autorizzato (<= 1,40, decisione operatore 23/08); il bordo resta come nota. + bordo = max((w for w in PESI if w > 0), key=lambda w: -iso_r[w]["muro"]) + ammessi = [w for w in PESI if w > 0 and iso_r[w]["k"] <= 1.40] + best_w = max(ammessi, key=lambda w: -iso_r[w]["muro"]) if ammessi else min( + w for w in PESI if w > 0) + print(f"\n ⚠️ M8 — l'argmax di questa griglia e' al BORDO (w={bordo:.0%}, k={iso_r[bordo]['k']:.2f}x):") + print(f" il muro scende MONOTONAMENTE in w, quindi la griglia non indica un ottimo, dice") + print(f" 'il piu' possibile'. Cio' che si sta comprando in quella direzione e' il k, non XSR01.") + print(f" => peso portato avanti = il migliore col k AUTORIZZATO (<=1,40x): XSR01 {best_w:.0%} " + f"(k={iso_r[best_w]['k']:.2f}x)") + + # ------------------------------------------------------- (3b) da dove viene il guadagno? + sez("(3b) IL NULL — il guadagno a iso-rischio e' di XSR01, o di 'una qualunque cosa scorrelata'?") + print(" A iso-vol vale `drift = Sharpe x vol_libro`: la colonna 'drift' della sezione (3) E' la") + print(" colonna 'Sharpe' ri-etichettata. Quindi tutto il guadagno di muro e' un guadagno di") + print(" SHARPE da diversificazione — e va chiesto se e' XSR01 o la sua sola statistica.") + print(" Tre sostituti, stessa w, stessa ri-scalatura a iso-vol del libro:") + print(" N1 XSR01 MESCOLATO — stessi rendimenti in ordine casuale: stesso drift, stessa vol,") + print(" nessuna relazione col libro giorno per giorno (null M14).") + print(" N2 RUMORE a drift di XSR01 — gaussiano, stessa media e stessa vol, zero struttura.") + print(" N3 RUMORE a drift ZERO — stessa vol, media 0: isola quanta parte e' pura varianza.") + rng = np.random.default_rng(20260825) + print(f"\n {'sostituto':<26} {'drift/a':>9} {'Sharpe':>8} {'perpetua':>10} {'MURO':>13} {'d muro':>10}") + print(" " + "-" * 80) + w0 = best_w + def _riga(nome, sub): + m = (1 - w0) * lb + w0 * sub + m = m * (vol_b / m.std()) + pp = perpetua(m, aliq, patr) + mm = muro_da(pp, prelievo) + print(f" {nome:<26} {m.mean()*365:>8.2%} {_sh(m):>8.2f} {pp:>9.2%} ${mm:>12,.0f} " + f"{mm/base_muro-1:>9.1%}") + return mm + m_vero = iso_r[w0]["muro"] + print(f" {'XSR01 (vero)':<26} {iso_r[w0]['drift']:>8.2%} {iso_r[w0]['sh']:>8.2f} " + f"{iso_r[w0]['perp']:>9.2%} ${m_vero:>12,.0f} {m_vero/base_muro-1:>9.1%}") + mescolati = [_riga(f"N1 mescolato (seme {i})", rng.permutation(xs)) for i in range(3)] + n2 = [_riga(f"N2 rumore, drift XSR01 ({i})", rng.normal(xs.mean(), xs.std(), len(xs))) + for i in range(3)] + n3 = [_riga(f"N3 rumore, drift ZERO ({i})", rng.normal(0.0, xs.std(), len(xs))) + for i in range(3)] + # N4: la gamba che nessuno ha messo sul tavolo — un rendimento SENZA rischio e senza venue. + # Se il merito di XSR01 e' 'un drift scorrelato a bassa vol', allora un conto remunerato + # (T-bill / USDC lending) lo fornisce con vol ~0, corr 0 e nessun pavimento d'esecuzione. + n4 = {y: _riga(f"N4 conto remunerato {y:.1%}", np.full(len(xs), y / 365.0)) + for y in (0.020, 0.0372, 0.040)} + print(f"\n LETTURA (calcolata, non asserita):") + print(f" XSR01 vero ${m_vero:>10,.0f}") + print(f" N1 mescolato, mediana ${np.median(mescolati):>10,.0f} " + f"-> il MECCANISMO vale {abs(m_vero-np.median(mescolati))/base_muro:.1%} di muro") + print(f" N2 rumore, mediana ${np.median(n2):>10,.0f} " + f"-> (media, vol) da sole bastano: {'SI' if np.median(n2) <= m_vero else 'NO'}") + print(f" N3 drift zero, mediana${np.median(n3):>10,.0f} " + f"-> la sola riduzione di varianza paga: " + f"{'SI' if np.median(n3) < base_muro else 'NO, il muro SALE'}") + print(f" N4 conto remunerato 4,0% ${n4[0.040]:>8,.0f} " + f"-> {'MEGLIO' if n4[0.040] <= m_vero else 'peggio'} di XSR01, " + f"a vol ZERO e senza secondo venue") + print(f"\n drift di XSR01 sulla finestra comune: {xs.mean()*365:+.2%}/anno · SE {se_drift(xs):.2%} " + f"-> t = {xs.mean()*365/se_drift(xs):+.2f}") + + # ---------------------------------------------------------------- (4) gate dei pesi + sez("(4) `weights_tilt_null` — il gate obbligatorio per OGNI proposta di cambio pesi (§8.5)") + print(f" proposta testata: XSR01 {best_w:.0%} (il miglior peso a k autorizzato, sezione 3).") + print(f" ⚠️ hold-out del gate = 2025-01-01: su questa finestra la gamba in-sample e' UN SOLO") + print(f" anno (2024). Il gate gira, ma con questa risoluzione va letto come indizio.") + cols = {"LIBRO": pd.Series(lb, index=ix), "XSR01": pd.Series(xs, index=ix)} + try: + g = weights_tilt_null(cols, + {"LIBRO": 1.0, "XSR01": 0.0}, # corrente: XSR01 fuori dal book + {"LIBRO": 1.0 - best_w, "XSR01": best_w}, + floor=0.0, n=500, k_seen=len(PESI)) + print(f" delta_insample {g['delta_insample']:+.3f} · delta_hold {g['delta_hold']:+.3f} " + f"· pctl_hold {g['pctl_hold']:.1f} · soglia best-of-k {g['bestofk_pctl']:.1f}") + print(f" tilt casuali che battono CURRENT sull'hold-out: {g['frac_random_beat_hold']:.0%}") + print(f" => gate_pass = {g['gate_pass']}") + except Exception as e: + g = None + print(f" NON ESEGUIBILE su questa finestra: {type(e).__name__}: {e}") + + # ---------------------------------------------------------------- (5) risoluzione + sez("(5) LA RISOLUZIONE — la differenza misurata e' piu' grande del rumore che la misura?") + se_b = se_drift(lb) + m_best = (1 - best_w) * lb + best_w * xs + m_best = m_best * (vol_b / m_best.std()) + se_m = se_drift(m_best) + d_drift = m_best.mean() * 365 - lb.mean() * 365 + se_pair = se_drift(m_best - lb) + print(f" drift libro {lb.mean()*365:>8.2%} (SE {se_b:.2%})") + print(f" drift mix {best_w:.0%} {m_best.mean()*365:>8.2%} (SE {se_m:.2%}) iso-rischio") + print(f" DIFFERENZA appaiata (M7: si misura la differenza, non si differenziano le mediane):") + print(f" {d_drift:+.2%}/anno · SE della differenza {se_pair:.2%} -> " + f"t = {d_drift/se_pair if se_pair > 0 else float('nan'):+.2f}") + p1 = perpetua(m_best, aliq, patr, seed=SEED_WALL) + p2 = perpetua(m_best, aliq, patr, seed=SEED_WALL + 1) + p3 = perpetua(m_best, aliq, patr, seed=SEED_WALL + 2) + print(f"\n risoluzione Monte Carlo del muro (M23), stesso mix a tre semi diversi:") + print(f" perpetua {p1:.3%} / {p2:.3%} / {p3:.3%} -> spread {max(p1,p2,p3)-min(p1,p2,p3):.3%}") + print(f" in muro: ${muro_da(p1, prelievo):,.0f} / ${muro_da(p2, prelievo):,.0f} / " + f"${muro_da(p3, prelievo):,.0f}") + + # ---------------------------------------------------------------- (6) il vincolo vero + sez("(6) IL VINCOLO CHE NON E' NELLE TABELLE — si puo' COMPRARE questo mix?") + eq = 2066.88 + print(f" capitale reale oggi: ${eq:,.0f} (Deribit). XSR01 gira su **Hyperliquid**: e' un") + print(f" SECONDO CONTO, quindi il suo peso e' capitale che ESCE da Deribit, non che si aggiunge.") + print(f"\n {'w XSR01':>8} {'$ su Hyperliquid':>18} {'ticket mediano/gamba':>22} {'eseguibile?':>14}") + print(" " + "-" * 68) + TICKET_MED = 3.33 # XSR-REPRO 22/08, misurato: mediano $3,33 · medio $5,98 · 63% sotto $5 + C_STAR = 15_000.0 # pavimento vero del venue misurato (non i ~$3.000 su cui e' tarato il gate) + for w in (0.10, 0.20, 0.30, 0.50): + cap_x = eq * w + tk = TICKET_MED * cap_x / 5_000.0 # il ticket misurato era a taglia $5.000 + print(f" {w:>7.0%} ${cap_x:>17,.0f} ${tk:>21,.2f} " + f"{'NO' if cap_x < C_STAR else 'forse':>14}") + print(f"\n E IL COSTO DI ESECUZIONE MANGIA PROPRIO LA COSA CHE SI STA COMPRANDO.") + print(f" Il gate 23/10 chiede 'haircut a $5.000 <= 40%'. Il drift lordo di XSR01 su questa") + print(f" finestra e' {xs.mean()*365:.2%}/anno: e' quello, e solo quello, cio' che il null dice") + print(f" di star comprando. Al netto dell'haircut:") + for h in (0.0, 0.20, 0.40, 0.60): + netto = xs.mean() * 365 * (1 - h) + print(f" haircut {h:>4.0%} -> {netto:>6.2%}/anno netti " + f"{'sopra' if netto > 0.040 else 'SOTTO'} un conto al 4,0% · " + f"{'sopra' if netto > 0.020 else 'SOTTO'} un conto al 2,0%") + h_be = 1 - 0.020 / (xs.mean() * 365) + print(f" pareggio con un conto al 2,0%: haircut {h_be:.0%}. Con un conto al 4,0%: " + f"NON pareggia nemmeno a haircut ZERO.") + print(f" ⚠️ e l'haircut pubblicato ($14,41 di ticket) e' ~2,4x ottimista e senza script" + f" che lo riproduca (XSR-REPRO 22/08).") + print(f"\n C* misurato del venue: ${C_STAR:,.0f}-20.000 (il gate 23/10 e' tarato su ~$3.000, §5.3).") + print(f" Capitale che serve per dare a XSR01 il {best_w:.0%} restando sopra C*: " + f"${C_STAR/best_w:,.0f} sul conto totale.") + + # ---------------------------------------------------------------- verdetto + sez("VERDETTO — calcolato a runtime, non scritto a mano (N11)") + mu_n = iso_n[best_w]["muro"] + mu_r = iso_r[best_w]["muro"] + print(f" base (libro solo, finestra comune): perpetua {base_perp:.2%} · muro ${base_muro:,.0f}") + print(f" a ISO-NOZIONALE, XSR01 {best_w:.0%}: muro ${mu_n:,.0f} ({mu_n/base_muro-1:+.1%})") + print(f" a ISO-RISCHIO, XSR01 {best_w:.0%}: muro ${mu_r:,.0f} ({mu_r/base_muro-1:+.1%}) " + f"[k = {iso_r[best_w]['k']:.2f}x]") + aiuta_n = mu_n < base_muro + aiuta_r = mu_r < base_muro + risolto = abs(d_drift) > 2 * se_pair + eseguibile = eq * best_w >= C_STAR + k_ok = iso_r[best_w]["k"] <= 1.40 + print(f"\n (a) abbassa il muro a iso-nozionale? {'SI' if aiuta_n else 'NO'}") + print(f" (b) abbassa il muro a iso-rischio? {'SI' if aiuta_r else 'NO'}") + print(f" (c) la differenza e' RISOLTA (|d| > 2 SE)? {'SI' if risolto else 'NO'}") + print(f" (d) il k richiesto e' autorizzato (<= 1,40)? {'SI' if k_ok else 'NO'}") + print(f" (e) e' ESEGUIBILE al capitale di oggi? {'SI' if eseguibile else 'NO'}") + if g is not None: + print(f" (f) `weights_tilt_null` passa? {'SI' if g['gate_pass'] else 'NO'}") + cond = [aiuta_r, risolto, k_ok, eseguibile] + ([g["gate_pass"]] if g is not None else []) + print(f" (g) batte un CONTO REMUNERATO al 4,0% a pari peso? " + f"{'SI' if m_vero < n4[0.040] else 'NO'} " + f"(XSR01 ${m_vero:,.0f} vs conto ${n4[0.040]:,.0f})") + cond.append(m_vero < n4[0.040]) + print(f"\n => XSR01 sotto la lente RENDITA: {sum(cond)}/{len(cond)} condizioni.") + if not eseguibile: + print(f" => Il vincolo BINDING non e' il rendimento: e' il CAPITALE. Sotto ${C_STAR/best_w:,.0f}") + print(f" totali la domanda 'serve XSR01?' non ha una risposta comprabile.") + + +if __name__ == "__main__": + main()