# 2026-09-09b — Debito §5.18 riparato: `cblib` causale (spot +1h, DVOL +1 giorno) e §11 rimisurato **Richiesta dell'operatore:** *"ripara il debito §5.18 su cblib e rimisura §11"*. Revisione col modello `fable` (agente fresco), come da regola. **Esito in una riga:** riparato in `cblib` — spot e DVOL escono **gia' causali** — e §11 rimisurato allo stesso taglio: **0,714 [0,690-0,779] → 0,712 [0,664-0,732]** (n=19). Il numero non si muove perche' le due correzioni hanno **segno opposto**; **nessun verdetto cambia**; riparando la prima serie ne e' uscita una **seconda con lo stesso difetto** (il DVOL giornaliero, fino a 24 ore avanti); il DVOL orario resta un passo aperto **con costo misurato**. Libro, config, cron: invariati. Nessun ordine. ## 0. Il difetto, verificato prima di ripararlo (D6: si controlla col lag, non si assume) | serie | come e' etichettata | prova | look-ahead di `asof(ts)` | |---|---|---|---| | feed 1h certificato | all'**apertura**: la barra T contiene la chiusura di T+1h | chiusura 1h a T == chiusura 5m a **T+55** nel **100%** delle barre (BTC ed ETH, 1-5/06); a T−5 nell'1,7% / 0% | **+1 ora** | | `dvol_{asset}.parquet` (giornaliero, `fetch_dvol`, `resolution=86400`) | al giorno D contiene la **chiusura delle 23:00 di D** | API pubblica a risoluzione 1h, 1-4/06: `close` 1D del giorno D == `close` 1h delle 23:00 di D, e `open` 1D di D+1 == `close` di D | **fino a +24 ore** | La seconda riga non era nel debito: e' uscita controllando la convenzione dell'altra serie di contesto che `cblib` legge con lo stesso `asof`. Il sleeve (`_vrp_weekly_asset`) **non ha il difetto**: lavora a cadenza giornaliera e accosta la chiusura del giorno D al DVOL del giorno D, che a quella cadenza e' coerente. Il difetto e' solo di chi entra **a un'ora** con una serie giornaliera. ## 1. La riparazione `cblib.causale(s, cadenza)` sposta le **etichette** (mai i valori) dall'apertura all'istante in cui la chiusura e' nota. `spot_series` → `causale(·, "1h")`, `dvol_series` → `causale(·, "1D")`. Conseguenze: - `asof(ts)` = ultima chiusura **conosciuta** a ts (prima della prima chiusura nota: NaN, non un valore). - il regolamento `ST = S.asof(exp)` alle 08:00 e' ora la chiusura della barra 07:00, cioe' **il prezzo delle 08:00** — prima era la chiusura della barra 08:00, cioe' il prezzo delle **09:00**, un'ora dopo la scadenza. Deribit regola su una TWAP dei 30 minuti prima delle 08:00: l'approssimazione e' ora dalla parte giusta dell'ora. - `r0909_crash_catturato` **non ri-sposta** (lo `spot_causale` locale della mattina e' rimosso: applicato due volte darebbe la chiusura di **due** ore prima — lo stesso errore col segno opposto). Test di non-regressione sul sorgente. - chi usa `cblib`: `r0822_vrp_real_quotes` (§11), `r0822_skew` (§5), `r0909` (§75), **`vrp_f_watch`** (cron giornaliero, `data/vrp_f_watch/state.json`), e — trovati dal revisore, non da me — **`r0730_vrp_real_quotes`** (il f 0,73 [0,706-0,805] di CLAUDE.md §2: rigirato stampa **0,71 [0,680-0,756]**, n=27, 0/27 ≥ 1, f gamba lunga 1,66 → 1,49; verdetto invariato, numero dichiarato in §2) e **`r0901_btc_collar`** (join del DVOL **per giorno**: con la serie causale avrebbe accostato l'IV di D al DVOL di D−1 — riallineato con `causale(·, "-1D")`, bit-exact a prima, §71 non si muove). `options_vrp_calibrate.py` ha una **copia propria** di `spot_series` (numeri del 20/06): **non toccato**, dichiarato. - la sezione di REGIME di `r0822` (date di calendario: «ultimo giorno aperto», IV-rank per anno) e la tabella di contesto di `r0909` usano `causale(V, "-1D")`: li' serve il DVOL **del** giorno, non l'etichetta causale — altrimenti ogni data stampata slitta di un giorno (trovato dal revisore). - `r0822_vrp_real_quotes` ha ora il flag `--al ` (taglio delle scadenze) per riprodurre una misura a una data: senza, "riprodurre il 22/08" era impossibile perche' la catena cresce ogni ora. ## 2. Prima di pubblicare il numero nuovo, la macchina ha riprodotto quello vecchio (M23) Worktree di HEAD (`git worktree add`, `data/raw` in symlink), stesso script con `--al 2026-08-22`: **pooled 0,714 (IC95 [0,690, 0,779], n=19)** — al millesimo, IC compreso. Poi lo stesso comando sul codice riparato. ## 3. Scomposizione: due correzioni di segno opposto (M26) e un controllo orario | taglio | spot | DVOL | pooled f | IC95 | n | |---|---|---|---|---|---| | 22/08 | grezzo | grezzo (com'era) | **0,714** | [0,690, 0,779] | 19 | | 22/08 | causale | grezzo | 0,721 | [0,685, 0,758] | 19 | | 22/08 | grezzo | causale (giornaliero) | 0,693 | [0,670, 0,748] | 19 | | 22/08 | causale | causale (giornaliero) = **`cblib` oggi** | **0,712** | [0,664, 0,732] | 19 | | 22/08 | causale | causale **orario** (controllo, API pubblica) | 0,717 | [0,667, 0,732] | 19 | | 09/09 | grezzo | grezzo | 0,720 | [0,717, 0,952] | 23 | | 09/09 | causale | causale (giornaliero) | **0,712** | [0,677, 0,741] | 23 | | 09/09 | causale | causale orario | 0,726 | [0,680, 0,738] | 23 | Il DVOL di fine giornata **alzava** f: nel rally la vol scende dentro il giorno, la chiusura di D sta **sotto** quella di D−1 (+1,05 pt di vol mediana su 19/19 trade, misura del revisore) → denominatore piu' basso → f piu' alto; causale: −0,021. Lo spot di un'ora dopo, invece, **non e' un meccanismo ma rumore**: lo scarto per trade ha segno misto (mediana −0,02% BTC / −0,11% ETH, banda −0,6…+0,9%) e sposta f fino a **±0,15 per trade** (lo spread δ−0,28/−0,10 ha delta netto ~0,18: 0,5% di spot vale 15-20% del credito modellato); il +0,007 netto e' la somma di 19 segni. ⚠️ Corollario: lo spot causale resta **stantio fino a 25 minuti** (snapshot al :25, chiusura nota al :00), cioe' ±0,05-0,10 di f per osservazione; il feed **5m certificato** lo porterebbe a ≤5 minuti. Non fatto: dichiarato in §6. Il DVOL giornaliero causale e' **stantio fino a 23 ore** (agli ingressi delle 08:00: 8 ore): scarto mediano dall'orario **0,2 punti di vol**, max 1,1 (22/08) / 3,0 (oggi). Su §11 vale 0,005 di f. ## 4. Cosa cambia in §11 (taglio 22/08, stesso campione) | grandezza | prima | dopo | perche' | |---|---|---|---| | f pooled | 0,714 [0,690-0,779] | **0,712 [0,664-0,732]** | §3 | | f BTC / ETH | 0,706 / 0,715 | 0,720 / 0,692 | | | Sharpe canonico BTC (titolo, non citabile) | 32,58 | 29,61 | regolamento alle 08:00 | | **mediana onesta** BTC (9 ancore) | 1,45 | **2,12** | banda [−0,19, 29,61] (era [−0,35, 32,58]) | | Sharpe canonico ETH → mediana onesta | 3,90 → 2,41 | 7,64 → 3,35 | una settimana ETH cambia taglia: 26/06 −27,38 → −12,84 $ (BTC: 31/07 19,88 → 31,69, 14/08 12,94 → 8,68) | | media BTC fra le ancore | −1,35% → +11,22% | −0,66% → +11,44% | **cambia ancora segno**; sd 11,1× → 9,2× | | forfait fee vs listino | 1,44× / 1,80× | 1,51× / 1,87× | credito modellato a DVOL causale | | peggior mark: mediana / minimo / minimo fra le vincenti | −6,4% / −91,0% / −56,9% | −6,4% / −91,5% / −55,2% | | | P&L somma 19 settimane | $2.733 | $2.822 (+3,3%) | tutto dal regolamento | | sottostante nel campione | +25,1% / +44,2% | +21,1% / +40,1% | il vecchio regolamento del 21/08 era il prezzo delle **09:00**, che sta **+3,9%** sopra quello delle 08:00 (76.311 → 79.316): un numero a due estremi pesa un'ora intera. (La prima stesura diceva «−1,8%»: avevo letto l'ora dopo, 09:00 → 10:00. Corretto dal revisore) | **Verdetto di §11: SCARTATO, per le stesse quattro ragioni** (il campione non contiene la strategia; il titolo non sopravvive all'ora d'ingresso; la famiglia con storia non e' quella eseguibile; lo spread e' il costo dominante). Nessuna delle quattro dipende da un'ora di spot. Il testo del verdetto nello script riporta i numeri nuovi con i vecchi accanto. ## 5. Effetti collaterali, misurati (tutto cio' che legge `cblib`) **§75, crollo catturato** (`r0909`, rigirato): ETH pre-crollo f_net **0,74 [0,65-0,82] → 0,76 [0,71-0,86]**; peggior MTM 1,08 [1,07-1,31] → 1,08 [1,06-1,27]; put δ−0,10 1,92× → 1,98×; al picco 0,94 → 0,99; BTC durante **0,79 → 0,89 [0,74-1,05]**, ETH durante 0,71 → 0,78. Con il DVOL **orario** BTC durante fa **0,83**: nel crollo il DVOL sale di 5-6 punti al giorno, quindi il giornaliero di fine giornata (com'era) sottostima f e quello del giorno prima (causale) lo sovrastima — **−0,04/+0,06 di f e' il prezzo della stantiezza in un crollo**, ed e' l'argomento per il feed orario. DVOL "pre" 37,3 → **36,4** (BTC): il 37,3 era la chiusura del **primo giorno di crollo** (01/06, −3,5%), non del giorno prima — IV-rank pre 0,05 → 0,03. Picco e percentili **invariati** (49,1 / 69,2; 29° / 46°) dopo aver corretto la tabella di contesto (§7). Verdetto invariato. **§5, skew** (`r0822_skew`, rigirato, 66 s): la scomposizione sul panel si muove di **≤0,005** per fattore (term 0,880 · skew 0,917 · spread 0,910 · fit 0,995 · f_tot 0,729); Q1/Q2 (lag prezzo/skew) **non si toccano** (serie proprie ancorate a `ts_max`). Agli ingressi il controllo `f_markfit` passa da 1,045 a **0,991** e le osservazioni che lo superano da 22/28 a **25/28**: l'ora di spot in piu' stava **nel controllo**, non nell'effetto — riparare ha reso il controllo piu' pulito, non il risultato diverso. **`vrp_f_watch`** (sorvegliante in `cron_daily`, criteri pre-registrati il 30/07 e **non toccati**): | | prima (state.json del 09/09 mattina) | dopo | |---|---|---| | coppie | 28 | 28 | | f canonico | 0,732 [0,702-0,784] | **0,706 [0,678-0,745]** | | f candidato | 0,803 [0,741-0,842] | 0,805 [0,771-0,840] | | differenza | +0,054 [−0,027, +0,105], 18/28 > 0 | **+0,097 [+0,042, +0,140]**, 21/28 > 0 | | ampiezza IC95 (b ≤ 0,12) | 0,131 — no | **0,098 — OK** | | coppie (a ≥ 40) | no | no | | `ready` | False | False | Lo `state.json` e' stato **riscritto** dal mio lancio (21:14Z, senza `--quiet`; nessun Telegram: notifica solo a `ready`); il «prima» completo e' la colonna qui sopra (n_positive 18, `esclude_zero` False), e il cron lo rigenera domani. La differenza ora **esclude lo zero**. Non decide niente: (a) non e' soddisfatto e il candidato resta bocciato sul deflated-Sharpe, che non dipende da f. Si registra perche' fra un mese qualcuno leggera' "esclude lo zero" e deve sapere da quando e perche'. ## 6. Cosa resta aperto, col suo costo | voce | costo di non farlo | costo di farlo | |---|---|---| | **DVOL orario** nel feed (`fetch_dvol --res 3600`, riga in `cron_daily`, `cblib` che lo legge) | su §11 0,005 di f; **su un crollo ±0,05**; ogni misura oraria su opzioni a DVOL giornaliero eredita lo scarto | un fetch pubblico gratuito (3.163 barre da maggio in 2 chiamate), una riga di cron, e una decisione: e' un feed nuovo dentro il perimetro (D1, certificazione, backup) | | **spot causale stantio ≤25 min** (snapshot :25, chiusura :00): ±0,05-0,10 di f per osservazione | `cblib.spot_series` sul feed **5m** certificato (`causale(·, "5m")`): cambia di nuovo ogni numero di `cblib`, quindi con rimisura dichiarata | | `options_vrp_calibrate.spot_series` (copia, 20/06) | i numeri del 20/06 (f 0,73) restano con l'ora in piu'; §11 li ha gia' sostituiti | riscriverlo su `cblib` | | la tabella pubblicata di §5 (x0,869 / x0,904 / x0,920) | ≤0,005 per fattore: sotto la risoluzione del campione (14+14 scadenze) | nulla: nota aggiunta | ## 7. Errori dell'autore in sessione - **`asi8` in microsecondi** scrivendo il test della causalita' (pandas 3): i timestamp sintetici finivano nel 1970 e il test falliva per la fixture, non per il codice. E' la trappola del debito 1, ripagata scrivendo un test *sul* debito 18. Corretto con `(idx − epoch) // Timedelta(ms)`, la convenzione di `load_tf`. - **La tabella di contesto di `r0909` si e' spostata di un giorno** dopo la riparazione: mostrava il DVOL del giorno D−1 accanto al prezzo del giorno D, e il picco del crollo cambiava (69,2 → 68,3, 46° → 43°) non per una misura ma per un'etichetta. Trovato **col diff degli output**, non da un test: una tabella per umani non ha test. Ora il contesto usa la chiusura *del* giorno (`causale(V, "-1D")`, dichiarato nel commento) e `dvol_pre` / `ivr_pre` restano causali. - Il verdetto §7 di `r0822_vrp_real_quotes` era **prosa con numeri cablati**: sarebbe rimasto a 0,714 per sempre (N11 al contrario). Aggiornato con i vecchi accanto; il difetto di forma resta (i numeri del verdetto andrebbero calcolati, non scritti). ## 8. Revisione (`fable`, agente fresco) — 16 segnalazioni, verdetto «correggi e committa» Ha **riverificato in proprio** la convenzione delle due serie (1h vs 5m su 120 barre: 100%; DVOL contro l'API oraria) e riprodotto ogni numero dei documenti lanciando gli script. Applicate: **#2** «−1,8% in un'ora» era falso (avevo letto 09:00 → 10:00; il fatto e' +3,9% 08:00 → 09:00) · **#3** due delle tre settimane «ETH» erano BTC · **#4** la parentesi sul DVOL era invertita (la chiusura di D e' piu' BASSA, non piu' alta) · **#5** lo spot non e' un meccanismo ma rumore, e resta stantio ≤25 min · **#6/#7** due lettori di `cblib` non dichiarati: `r0730` (0,73 → 0,71, ora in CLAUDE.md §2) e `r0901` (riallineato) · **#8** le date di calendario di `r0822` §1 slittavano di un giorno · **#9** il test anti-doppio-shift non catturava `causale(spot_series, "1h")`: ora conta i `causale(` · **#10** ancora 0,74 → 0,76 nel test d'integrazione · **#11** teste di §75 e memoria 20 §5 con i numeri vecchi come correnti (M28) · **#12** «±0,05» → «−0,04/+0,06» · **#13** `state.json` riscritto: dichiarato · **#15** lo skew agli ingressi cambia piu' del panel (correlazioni che cambiano segno): dichiarato in §5. Non applicate: nessuna. Confermati senza rilievo (#1, #14, #16): verso e taglia degli shift, nessun doppio spostamento, `ST` alle 08:00 coerente con la TWAP 07:30-08:00, tutti i numeri di §3-§5. ## 9. Test `tests/test_cb_chain_vrp.py`: +3 (causale; `spot_series` causale sul feed 1h con controllo positivo sulla serie grezza; `dvol_series` causale sul giornaliero). `tests/test_r0909_crash_catturato.py`: il test dello shift locale diventa "lo script usa le serie di `cblib` senza ri-spostarle" (sorgente + controllo positivo su `cblib.causale`). Suite intera: **1011 passati, 0 falliti** (erano 1008), rilanciata dopo le correzioni della revisione: 1011/1011.