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

158 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:136``VRP_CFG` gira a **`f=1.0`**, applicato al credito **NETTO**:
```python
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.146**0.069),
HOLD 2.347 → **2.244**0.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)