Files
PythagorasGoal/docs/diary/2026-07-30-vrp-quote-reali.md
T
Adriano Dal Pastro 8c18e82f1a research(vrp): il f del credito netto e' 0.73 sulle quote reali, non 1.0
Prima integrazione della catena opzioni Deribit mainnet accumulata da
cerbero-bite (/opt/docker/cerbero-bite, dal 2026-06-09: entrambe le ali,
1g-3mesi, oraria, con book_depth). E' l'unica fonte di prezzi opzioni VERI
del progetto, e non e' ricostruibile a posteriori: Deribit non serve book
storici, un'ora non raccolta e' persa.

VRP01 prezza entrambe le gambe con BS su DVOL ATM (VRP_CFG f=1.0 sul credito
NETTO). Misurato agli STESSI strike su 8/8 scadenze settimanali con entrambe
le gambe quotate (delta -0.270/-0.099 contro target -0.280/-0.100):

  f gamba corta   1.02   <- replica la calibrazione del 20/06
  f gamba lunga   2.30   <- l'ala che si COMPRA
  f credito NETTO 0.73   IC95% [0.698, 0.780], 0/15 osservazioni >= 1.0

Meccanismo, non rumore: IV(corta)-DVOL +0.8pp ma IV(lunga)-DVOL +7.5pp -> il
modello prezza a vol ATM anche l'ala comprata. Il difetto non e' nel premio
incassato ma nella protezione comprata, cioe' proprio il "defined-risk" per
cui v2 fu promosso.

Conseguenza standalone (solo f): 1.00 -> FULL 1.08 / HOLD +0.58; 0.80 -> 0.51
/ -0.02; 0.73 -> 0.31 / -0.23. Book 5-sleeve: FULL -0.069, HOLD -0.103, DD
invariato = dentro la banda d'ancora, ma ~meta' del contributo LOO di VRP01
era il prezzo che il modello si faceva da solo.

VRP_CFG["f"] NON cambiato: 15 osservazioni, 7 settimane, e 0/8 passano il
gate IV-rank>0.30 -> il f e' misurato nel regime in cui il sleeve sta FLAT.
Caveat quantificato, non nuovo parametro. Il criterio del 19/06 (rivalutare
quando cerbero-bite cattura un crash) e' intatto.

Book, pesi, cron, config INVARIATI.

Regole nuove congelate nei test:
- il f di una struttura multi-gamba non e' il f di una sua gamba (misurare la
  sola gamba venduta da' la risposta sbagliata con segno rassicurante);
- un guasto IN CORSO si misura al GIORNO PEGGIORE, non in media (la prima
  stesura della certificazione diluiva un guasto di 2 giorni da 51.7% a 13.5%);
- una riga presente non e' un dato presente.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 19:51:52 +00:00

8.9 KiB
Raw Blame History

2026-07-30 — VRP01 contro le quote REALI: il f del credito netto è 0.73, non 1.0

Esito: nessun cambio a book, pesi, cron o config. Un'ipotesi di VRP01 è falsificata con misura, e il dataset che l'ha falsificata entra nel progetto (estrattore + certificazione + harness + test). Il muro dichiarato per il deploy — il f di stress — resta in piedi, intatto.

Da dove viene il dato

/opt/docker/cerbero-bite accumula dal 2026-06-09 la catena opzioni Deribit mainnet: entrambe le ali, scadenze 1g→3 mesi, ora per ora, con la profondità top-3 del book. È la fonte che il progetto cita dal 20/06 (options_vrp_calibrate.py) e che finora era stata usata una volta sola, via export manuale in /tmp.

Il motivo per cui quel dato si memorizza invece di richiederlo quando serve: una catena opzioni non è ricostruibile a posteriori — Deribit non serve book storici. Un'ora non raccolta è persa per sempre. Stessa asimmetria dello storico spot certificato, con l'aggravante che qui non esiste un secondo venue da cui recuperarla.

La domanda

src/portfolio/sleeves.py:136VRP_CFG gira a f=1.0, applicato al credito NETTO:

net_prem = (_bs_put(S0, Ks, T, sig) - _bs_put(S0, Kl, T, sig)) * cfg["f"]

cioè assume che il mercato paghi esattamente ciò che Black-Scholes su DVOL-ATM dice, per entrambe le gambe. Il caveat di ammissione ("premio MODELLATO su DVOL ATM, skew non esplicito") era dichiarato dal 19/06 e mai quantificato, per mancanza di quote vere.

D1 — copertura: la struttura c'è

Finestra settimanale 4-10 DTE: 8 scadenze su 8 hanno entrambe le gambe quotate, per entrambi gli asset. Delta realizzati 0.270 (target 0.280) e 0.099 (target 0.100): la struttura misurata è quella del sleeve, non una sua approssimazione.

D2 — il numero: f = 0.73

Confronto agli stessi strike (così il rapporto isola l'errore di prezzo, non la scelta dello strike), fill conservativo (vendi al bid, compri all'ask):

grandezza mediana p25 p75
f gamba corta (venduta) 1.02 0.88 1.12
f gamba lunga (comprata) 2.30 1.68 2.87
f credito NETTO ← quello che entra nel book 0.73 0.71 0.82
f credito netto, a mid 0.80 0.79 0.89

IC95% bootstrap sulla mediana [0.698, 0.780]; 0 osservazioni su 15 arrivano a 1.0.

⚠️ La calibrazione del 20/06 non è contraddetta, è replicata: misurava la sola gamba corta e trovava f ≈ 1.0; qui la stessa quantità, con implementazione indipendente, dà 1.02. La differenza è che VRP01 è uno spread, e il problema sta nell'altra gamba.

Il meccanismo (senza il quale il numero sarebbe un artefatto)

IV(corta) DVOL = +0.8 pp, IV(lunga) DVOL = +7.5 pp. Il modello prezza entrambe le gambe a vol ATM; l'ala più OTM — quella che si compra — sta 8 punti sopra per skew, quindi costa ~2.3× il modello, e il credito netto si comprime del 27%.

Colpisce esattamente il punto su cui VRP01-v2 era stato promosso: "(a) defined-risk taglia la coda (worst-week 16.6%→−7.4%)". Nel modello quell'assicurazione è comprata a vol ATM, cioè quasi gratis. Nel mercato costa più del doppio. Il difetto non è nel premio incassato, è nella protezione comprata.

Conseguenza, misurata

VRP01 standalone sostituendo solo f (replica sana a f=1.0: 1.08 / 0.58 / 11.8% contro i 1.10 / 0.60 / 12% pubblicati):

f Sh FULL DD FULL CAGR Sh HOLD-OUT
1.00 (assunto) 1.08 11.8% 8.2% +0.58
0.80 (misurato a mid) 0.51 14.4% 3.6% 0.02
0.73 (misurato, fill conservativo) 0.31 15.3% 2.0% 0.23

Book a 5 sleeve (VRP01 al 12%), ancore canoniche: FULL 2.215 → 2.1460.069), HOLD 2.347 → 2.2440.103), DD invariato.

Lettura onesta: il book non crolla — l'effetto è dentro la banda d'ancora (canonico 2.22 contro mediana de-luckata 1.95). Ma il LOO del 26/07 attribuiva a VRP01 +0.122 di FULL: circa metà di quel contributo era il prezzo che il modello si faceva da solo. L'audit d'ancora aveva corretto la fortuna di calendario; l'ottimismo di prezzo non l'aveva corretto nessuno.

D3 — ciò che solo questo dato permette

Le stesse due gambe sono riquotate ~164 volte per trade (orarie) fino a scadenza. Da qui:

  • il 50%-profit-take scatterebbe in 13/15 settimane → la regola di gestione diventa testabile;
  • peggior mark infra-settimana (N=13 trade con path): mediana 6.9% del capitale, minimo 81.7%, su un trade che è finito in utile.

⚠️ Il sleeve marca solo a scadenza: quel minimo non esiste nei suoi numeri. È la stessa classe di errore della lente wick accoppiata del 25/07 — un rischio valutato sul minimo non si misura sulle chiusure. Il DD 12% di VRP01 è un DD di chiusure settimanali.

D4 — ciò che il dato NON dice, ed è la ragione per cui si raccoglie

Nella finestra raccolta VRP01 non avrebbe aperto una sola posizione. IV-rank mediano 0.12 (ETH) e 0.08 (BTC) contro il gate >0.30: 0 settimane su 8 per entrambi. Il massimo di DVOL raccolto sta al 24° percentile (ETH) e 28° (BTC) della storia.

Quindi il f = 0.73 è misurato nel regime in cui il sleeve sta flat. Lo skew è strutturale e in stress si irripidisce, quindi la direzione è robusta — ma la taglia in stress resta non misurata, e il criterio dichiarato il 19/06 ("rivalutare quando cerbero-bite cattura un crash") non è stato toccato da questo lavoro.

Il difetto trovato nel dato, e la certificazione che ora c'è

Il feed ha un guasto in corso dal 29/07 05:00 UTC: il collettore persiste la riga anche quando il ticker fallisce (rate-limit per-IP), con bid/ask/iv/delta NULL. Il conteggio righe non cambia — 13.000/giorno prima e dopo — quindi ogni controllo di copertura basato sulle righe dice "tutto bene". Tasso di quote vuote per settimana: 0.4 · 0.7 · 2.2 · 1.5 · 0.5 · 0.4 · 0.3 → 22%.

scripts/analysis/fetch_cb_chain.py certifica quattro difetti (quote vuote, book incrociato, premio non monotono nello strike, depth==0 ambiguo by design) ed è il posto dove è stata imparata la lezione seguente.

⚠️ La prima stesura della certificazione confrontava "tutto il campione" con "ultimi 7 giorni" — e diluiva un guasto di 2 giorni al 13.5%, cioè commetteva a 7 giorni lo stesso errore che dichiarava di voler evitare a 3 mesi. Con la statistica giusta (giorno peggiore della finestra) lo stesso dato dice 51.7% e lo status passa da DEGRADATO a QUOTE-VUOTE. REGOLA: un guasto IN CORSO non si misura con una media su una finestra — si misura al suo giorno peggiore. Qualunque finestra è abbastanza lunga da nascondere un guasto abbastanza giovane. Congelata in test_giorno_peggiore_non_si_lascia_diluire_dalla_media.

Cosa NON è stato fatto, e perché

VRP_CFG["f"] non è stato cambiato. La misura sta su 15 osservazioni, 7 settimane, un solo regime — per giunta il regime in cui il sleeve è flat. Sostituire 1.0 con 0.73 significherebbe propagare in tutto il book un numero misurato dove il book non opera. La mossa corretta è il caveat quantificato: i numeri 1.10 / 0.60 / 12% si citano d'ora in poi con "a f=1.0; f misurato sulle quote reali 0.73 [0.70, 0.78], a cui l'hold-out standalone è 0.23".

Che VRP01 stia al 12% e non sia deployato è, retroattivamente, la decisione giusta per il motivo giusto: non "short-vol da modello per prudenza", ma "il modello sovrastima il credito del 27% e lo fa sulla gamba che doveva proteggere".

Regole nuove

  1. Il f di una struttura multi-gamba non è il f di una sua gamba. Misurare la sola gamba venduta dà la risposta sbagliata con segno rassicurante (1.02 invece di 0.73). Ogni futura calibrazione su quote reali di una struttura a N gambe si fa sul netto. Congelata in test_il_f_della_gamba_corta_non_e_il_f_dello_spread (3 ali, identità algebrica f_net = (1 f_long·k)/(1 k) — una soglia sola sarebbe stata una fixture fortunata).
  2. Un guasto in corso si misura al giorno peggiore, non in media (sopra).
  3. Una riga presente non è un dato presente. Il difetto delle quote vuote è invisibile a qualunque controllo che conti righe: va contato ciò che è quotato, non ciò che è scritto. È la terza occorrenza della stessa famiglia dopo la contabilità a 3 stati di paper_dvolspread (26/07) e il silenzio letto come zero di fresh_5m (26/07).

File

  • scripts/analysis/fetch_cb_chain.py — estrazione + certificazione → data/raw/cb_chain.parquet
  • scripts/research/cblib.py — harness (loader, pick_legs, f_factors, close_cost_path)
  • scripts/research/r0730_vrp_real_quotes.py — lo studio (D1-D4, meccanismo, sweep f, --book)
  • tests/test_cb_chain_vrp.py — 14 casi (rilevatori + controlli positivi + guardia di convenzione)