# Produzione, sorveglianze e deploy > Estratto **verbatim** da `CLAUDE.md` il 2026-08-25 durante la compattazione. > Cio' che gira con soldi veri: esecuzione, tripwire, monitor, libro di bordo, e i vincoli di deploy (PRIIPs/UCITS/broker). > Il testo non e' stato riscritto: e' la memoria originale, spostata. --- - **Ondata 2026-07-26 (3 filoni: esecuzione SKH01 / DVOLSPREAD / XSR01 fuori dal crypto) — 0 sleeve nuovi, 1 raccomandazione operativa aperta, 1 lead promosso, 1 falsificazione.** Script `r0726_{skh_onbook,dvolspread_gate,xsr_equity}.py`, test `tests/test_wave_0726.py` (11 casi), diario `2026-07-26-wave-esecuzione-dvolspread-xsr-equity.md`. **Book/pesi/cron INVARIATI.** (1) ⚠️ **T1 — IL DEGRADO D'ESECUZIONE DI SKH01 ERA ANCH'ESSO FORTUNA D'ANCORA.** Testato l'unico meccanismo che non e' un cron (ordini **resting on-book**: TP = limit al livello per costruzione + fee maker; SL = stop-market). A **offset 0** l'on-book recupera il **94%** del degrado — ma l'audit 02/07 aveva de-luckato i numeri headline di SKH01 e **NON il degrado**, misurato solo a off0. Sulla banda appaiata dei 23 offset il degrado massimo recuperabile e' **+0.054 Sh di book**, non −0.35 di sleeve. ⚠️ Errore di metodo mio, corretto in sessione: **mediana(A)−mediana(B) fra offset e' sbagliato** (confronta offset diversi) → serve la **mediana delle DIFFERENZE appaiate**; con quella il verdetto si RIBALTA. Esito: **solo TP a limite = +0.054 FULL / +0.061 HOLD, positivo in 19/23 (FULL) e 21/23 (HOLD) offset**; **lo SL on-book PEGGIORA** (contributo mediano −0.010, positivo in **11/23 = moneta**) perche' cristallizza la perdita al livello mentre l'exit software ritardata incassa il rimbalzo — e sopra **0.50% di slippage** l'on-book e' peggio del live. Meccanismo misurato: sulle uscite TP il fill orario **batte** il livello nel 42% dei casi (vantaggio medio −0.035%) → il limit TP converte una lotteria in certezza piu' che aggiungere ritorno; solo l'**1% degli SL gappa** davvero a 5m. **RACCOMANDAZIONE NON ESEGUITA (decisione dell'operatore): TP come limit resting reduce-only, SL strategico NON on-book.** E' un cambio di esecuzione (non passa `weights_tilt_null`) ma piccolo, da pesare contro ordini orfani/doppio fill. Il disaster-SL −30% on-book resta com'e'. ❌ **T1 NON IMPLEMENTATO — e il perche' e' una CORREZIONE DI MODELLO** (2026-07-26, diario `2026-07-26-t1-esecuzione-skh-live.md`). Leggendo il codice di produzione: TP01 e SKH01 tradano lo **stesso strumento** con **una sola posizione netta** Deribit → un ordine on-book al livello di SKH chiuderebbe anche quota TP01. Misurati i 2 ostacoli: (A) segno compatibile nel **97%** dei trade che escono in TP (long 100%, short 94%) = risolvibile; (B) divergenza modello/live che sembrava bloccante (ritardo mediano 115 min, **70%** dei TP con ≥1 cron dentro la finestra). ⚠️ **MA (B) NON ESISTE: `resample_5m` NON scarta il bin 230m in corso** (verificato: 21 barre 5m su 46 nell'ultimo bin) **e `_skyhook_positions` ci itera dentro** → il live rileva gia' SL/TP **intra-barra**, entro ~1h dal tocco. I docstring che dicevano "usa solo barre chiuse" / "latenza fino alla chiusura della barra 230m" erano **FALSI** e hanno guidato 3 analisi (02/07, 24/07, T1) — **corretti sul posto**. Conseguenza: la lente `hourly` **sottostima il path live di +0.081 Sharpe FULL di book (23/23 offset, banda appaiata)**, il live vero sta **sopra il canonical** sul FULL, e **il fix richiesto sarebbe un DECLASSAMENTO** (+0.054 vs +0.081) in cambio di ordini parziali sul netto in un percorso con soldi veri. → non fatto, per misura, non per difficolta'. ✅ **CABLATO INVECE il problema vero trovato per strada:** `fresh_5m` **fallisce in SILENZIO** (fallback al feed certificato, rigenerato 1x/giorno) → la latenza d'uscita di SKH01 passa da ~1h a **~1 giorno** senza che nulla lo segnali (conto online, posizione leggibile, e il gate di staleness guarda il feed di TP01: **nessun controllo esistente scatta**). Stesso schema del feed-freeze del 14/07, su un altro feed. Aggiunti: `livefeed.feed_age_minutes` (pura, eta' dalla CHIUSURA della barra, `None`=non misurata, clamp sugli skew), `book_report.skh_feed_age_min` (max fra gli asset), allerta Telegram in `book_execute` sopra `skh_feed_max_age_min`=**30 min**. **Scelta dichiarata: ALLERTA, NON blocca** — bloccare fermerebbe anche TP01 (nettato sullo stesso strumento) per un guasto di rete, e forzare SKH flat chiuderebbe posizioni buone su un glitch. Test `tests/test_skh_feed_freshness.py` (11 casi). Strategia/pesi/cadenza INVARIATI. ⚠️ **SEGUITO 2026-07-29 — l'allerta ha funzionato, la sua CAUSA era una riga CABLATA.** Diario `2026-07-29-feed-skh-causa.md`; test 11 → **16**. **Book/pesi/config/strategia INVARIATI.** Il 29/07 (dopo il riavvio VPS delle 04:11) il feed e' ricaduto sul certificato in **6 giri orari su 8** fra le 05:00 e le 12:00: eta' **265→685 min** (+60 a ogni giro = firma esatta del fallback) → latenza d'uscita SKH01 da ~1h a **~11h**; book flat, nessuna posizione esposta. L'allerta ha segnalato 6/6 — poi ha stampato un perche' che **non aveva misurato**: la nota *"fetch pubblico KO"* era cablata, identica in ogni caso, **compreso quello in cui la coda fresca E' attaccata e il vecchio e' il certificato stesso** (= feed-freeze 14/07 in altra veste). E la causa vera non era recuperabile a posteriori **per costruzione**: `_fetch_recent_5m` ingoia l'eccezione di pagina con un `break` e a prima pagina fallita ritorna un frame vuoto, indistinguibile da "il venue non ha barre"; alle 12:14, a mano, `fresh_5m` rispondeva in 1.7s senza errori. ✅ **Cablato:** `livefeed.last_fetch_error()` (+`_note_error`, registra E logga nel punto in cui l'errore viene ingoiato) → `book_report.skh_feed_errors` (per asset) → allerta con la causa; e stesso buco chiuso sul ramo gemello **"conto offline"**, che aveva gia' la ragione in `mark_src` e non la stampava mai. Tre guasti ora distinti: eccezione / risposta vuota / **certificato vecchio con coda attaccata**. Blindati anche il caso a **meta' paginazione** (coda parziale attaccata: la paginazione va in avanti → mancano le barre PIU' RECENTI) e la non-sopravvivenza della causa a una chiamata riuscita. ⚠️ **La causa del 29/07 resta IGNOTA, e va citata cosi'.** Solo circostanziale: giri falliti lunghi quanto i riusciti (**16-46s → errore immediato, non timeout**); finestra aperta col riavvio ma non chiusa da esso (11:00 ok, 12:00 no); stesso giorno il percorso del **conto** (rete diversa via `cerbero-mcp`, stesso venue a valle) dava `ReadTimeout/404/502`; i due percorsi falliscono **in alternanza**, non insieme. Una prima stesura scriveva nei commenti "*rate limit Deribit per-IP saturato da un altro progetto sulla stessa VPS*" **come fatto**: rimossa — sarebbe stato lo stesso difetto che stavo correggendo, scritto meglio. **Cron spostato `0 * * * *` → `7 * * * *`** (ipotesi contesa al minuto tondo): ripiego da **UNA** osservazione, costo zero, **dichiarato tale in testa a `cron_book.sh`** perche' la riga di crontab vive fuori dal repo. ✅ **SEGUITO 2026-07-30 — il MECCANISMO ipotizzato e poi rimosso e' ora MISURATO; il "perche' adesso" NO.** Analizzando `/opt/docker/cerbero-bite` (stessa VPS, stesso IP pubblico): il suo collettore full-chain gira **al minuto :00** e produce **12.186 risposte 429** in 26 ore, di cui **11.700 (96%) nel minuto :00** — ~770 chiamate ticker + ~770 orderbook in ~26s da un IP solo, senza backoff → **si auto-satura** il rate limit Deribit per-IP (ticker: 5.738 respinte su 20.029). Co-timing **esatto** con l'incidente: le sue quote passano da ~0.4% a ~50% vuote alle **05:00 del 29/07** e non sono ancora rientrate. ⚠️ **Ma la stessa disciplina vale due volte: il carico di bite e' INVARIATO da settimane** (6.100-6.400 righe BTC/giorno prima e dopo, immagine ferma al 09/06) e i log MCP non precedono il riavvio → le 429 spiegano **come** si perdono le chiamate, **non perche' proprio quel giorno**. La causa del cambiamento resta ignota. Nessuna azione: `cron_book` e' gia' a `:07`, fuori dalla finestra di ~26s. Diario `2026-07-30-vrp-quote-reali.md`. **REGOLE:** (a) **una nota di diagnosi cablata e' peggio di nessuna nota** — nessuna manda a guardare i dati, una sbagliata manda sulla pista sbagliata e sembra una misura; (b) se un errore si ingoia per non bloccare, **si registra nel punto in cui lo si ingoia** (non c'e' un secondo momento buono: quando la diagnosi serve, il guasto e' rientrato); (c) un'allerta risponde a **due** domande — *cosa* (decide) e *perche'* (ripara): misurare solo la prima costa un'intera occorrenza del guasto; (d) distinguere guasti diversi **anche quando l'azione e' la stessa** (tre cause → tre riparazioni); (e) una mitigazione da un'osservazione sola si applica pure, ma **si scrive che lo e'**. ✅ **FOLLOW-UP CHIUSO 2026-07-26 — la misura dedicata sugli INGRESSI e' fatta: NON e' un difetto, il verso e' LASCIARLO.** `scripts/research/r0726_skh_partial_entry.py`, test `tests/test_skh_partial_entry.py` (10), diario `2026-07-26-skh-partial-entry.md`. Confronto di due path identici in tutto (livelli, uscite intra-barra, cap, fee) tranne **quando si valuta l'ingresso**: LIVE = a ogni confine orario dentro il bin 230m (cio' che il cron fa oggi), BACKTEST = solo a chiusura di bin. **ΔSharpe su 3 offset x 2 asset: −0.01/+0.04/+0.44/+0.32/+0.59/ +0.98 → 6/6 non negativi, mediana +0.38.** All'offset 0 (l'ancora fortunata del backtest, 93-98° pctl) l'effetto e' ~0 sul FULL ma l'hold-out BTC fa **1.12 live vs 0.71 backtest**. I falsi ingressi (segnale che evapora) sono **~5/anno/asset**, e cannibalizzano il cap `max_per_day` solo 2-3 volte in 7 anni (0.6-0.9% degli ingressi veri). **Meccanismo:** SKH01 e' un Donchian breakout — aspettare fino a 230 min la chiusura del bin fa *pagare il movimento gia' avvenuto* (ingresso **0.25-0.26% peggiore** in media); e siccome i livelli sono percentuali sull'ingresso (long sl4%/tp10%, short sl2%/tp8%), lo stesso 0.26% vale il **6-13% della distanza dallo SL** contro il 2.6-3.3% di quella dal TP → vantaggio **asimmetrico a favore della sopravvivenza del trade** (win rate +8pp ETH/+2pp BTC). **Due attacchi superati:** (a) il divario NON e' concentrato — togliendo i 5 giorni migliori si ALLARGA (ETH@460 35.6x vs 3.0x); e' il *backtest* il path concentrato (~39% del log-equity in 5 giorni contro 17-22% del live); (b) **nessun look-ahead intra-bin** — troncando i 5m alle sole barre gia' chiuse la risposta e' identica in **289/289** osservazioni intra-bin e il prezzo d'ingresso e' sempre l'ultimo close 5m disponibile (test permanente `test_nessun_lookahead_intra_bin`; serviva perche' il self-check valida solo a **chiusura** di bin → un leak solo-intra-bin gli sarebbe invisibile per costruzione). ⚠️ **La lettura che conta e' la seconda: live e backtest girano due strategie DIVERSE e la differenza non e' neutra.** Ogni numero di SKH01 nel progetto (Sharpe standalone, peso 25%, audit d'ancora 02/07, conferma peso/cadenza 24/07) e' calcolato sul path a chiusura di bin, che non e' quello che gira. Sommato alla misura del 26/07 sulle uscite (+0.081 Sharpe FULL di book sottostimato), **il path live di SKH01 e' stato modellato in modo sistematicamente pessimistico su ENTRAMBI i lati.** Cio' che NON si conclude: che l'ingresso intra-bin sia un miglioramento *validato* — e' stato misurato sugli stessi 7 anni su cui SKH01 e' stato selezionato, non e' passato per `study_family_honest` ne' per un deflated-Sharpe, e la taglia varia molto fra ancore. Ma non serve promuoverlo: **e' gia' cio' che il live fa**; l'azione e' smettere di trattare il numero del backtest come l'aspettativa del live, NON "riparare" il live verso un backtest peggiore. **Book, pesi, cron, config INVARIATI.** ⚠️ **Regole nuove (due errori catturati in sessione, entrambi del tipo che passa i test pigri):** (i) **un self-check su eventi rari si campiona sugli EVENTI, non sulla popolazione** — la prima stesura stampava "BTC 80/80 OK" mentre la ricostruzione era rotta da un off-by-one di confine (`obs//MS_LTF` a chiusura cade nel bin successivo, vuoto → segnale 0): con gli ingressi al ~2% dei bin confrontava **zeri con zeri**, potenza zero, e le 2 sole divergenze ETH erano gli unici 2 bin con segnale vero. (ii) **un conteggio di eventi su segnale GREZZO non e' un conteggio di trade** — la prima scansione dava 1361 falsi ingressi e un costo inventato di −3%/anno di sleeve perche' ignorava cap+non-overlap del live (**sovrastima ~20x**); la spia era 305 *giorni* con un falso ingresso a fronte di 663 "eventi", impossibile con cap 1/giorno. (2) ✅ **T2 — DVOLSPREAD ESCE DAL LIMBO** (era fermo dal 21/06, unico sopravvissuto del marginal scorer indurito, mai ripreso: non nel book, non in monitor, non rifiutato). Passato ai due gate che nel giugno NON esistevano. ⚠️ La griglia dichiarata dall'agente ("72 celle") ne contiene **729** (6 assi x 3) → valutate tutte, scelta conservativa (piu' trial = DSR piu' basso). Plateau REALE e larghissimo: **729/729 celle con hold-out positivo**, FULL [0.60,0.71]. Selection-on-holdout **confermata ma mite**: la cella pubblicata e' **83ª/729 sull'hold-out ma 471ª/729 in-sample**. Scegliendo onestamente in-sample: **FULL 0.68 / HOLD 0.69** (non il **0.93** pubblicato — **citare 0.69**) e **DSR 0.953 PASS**, mentre la cella pubblicata **FALLISCE (0.947)**. Marginale ADDS + robust_oos + multicut + non-hedge + insample_edge + beats_noise; corr +0.11, alpha +7.4%/a, dSharpe book **+0.08 FULL / +0.17 HOLD** a w=15%. **PROMOSSO a forward-monitor con i parametri ONESTI** (zwin=180 k=2.0 lw=0.6 zw=1.1 tgt=0.17 svw=60), **NON nel book**: campione **ATTIVO 1949/2691 g** (prima del 2021-03 non c'e' DVOL, book flat) con **hold-out attivo 1.6 anni**, margine DSR sul filo, e `weights_tilt_null` mai affrontato. ✅ **MONITOR CABLATO** (stessa sessione): `scripts/live/paper_dvolspread.py` in `cron_daily.sh` dopo `fetch_dvol.py`, stato `data/paper_dvolspread/` (gitignored), test `tests/test_paper_dvolspread.py` (10 casi). Inception **2026-07-25**, apertura +0.184 = **$111/gamba** (cap $300), 2 libri MODELED $2000 / REAL $600. ⚠️ **Strumentazione specifica:** il book va **flat quando manca il DVOL** → un feed rotto produrrebbe zeri che, contati come evidenza, direbbero "nessuna perdita" invece di "nessuna misura". Contabilita' a **3 stati** (ATTIVE / flat-da-segnale / **flat-senza-dato**) e finestra misurata in **barre attive**, non giorni di calendario; guardia sulla config che **esce 1** se `FROZEN` diverge dallo stato salvato. **GATE PRE-REGISTRATO:** kill **2026-10-24** se Sharpe forward < −0.50; decisione **2027-01-24** solo se TUTTE — (a) Sharpe>0 [debole di proposito: con ~180 barre SE(Sharpe)≈1.4, una soglia alta sarebbe finta precisione] (b) marginale ancora ADDS+robust_oos+insample_edge (c) **deflated-Sharpe ricalcolato ≥0.95** [e' qui il peso: se lo 0.953 gia' sul filo NON migliora con piu' dati, l'edge non c'e'] (d) `weights_tilt_null`; **veto d'integrita'** se barre attive <80% → si ESTENDE, non si decide su dati mancanti. (3) ❌ **T3 — XSR01 NON GENERALIZZA fuori dal crypto** (meccanismo CONGELATO W=45/sgn=+1 su **9 settoriali SPDR 1998+ / 28 ETF 30 anni**, residuo vs SPY, demean giornaliero, split IWM/EFA riparati, **annualizzazione √252**). Diverso dal test del 25/07: quello era a **coppie**, questo e' la versione **DEMEANATA** (quella vera di XSR01). Lordo **+0.24 (SECT9, p=0.193)** e **−0.14 (ALL28, p=0.747)** vs null di permutazione **a fee zero**; netto −1.6/−1.8 ovunque; per decennio stesso profilo nei 2 universi (neg. 1998-2005, debolmente pos. poi) = piu' cambio di regime che edge. **Il risultato che conta non e' lo Sharpe ma l'AMPIEZZA:** il demeaning porta l'ampiezza effettiva **4.5→37.4 (8x) sul crypto** ma solo **5.3→6.2 / 8.4→11.3 (1.2-1.35x) sulle azioni**. Spiegazione strutturale (e migliore descrizione di XSR01 di quella della sua scoperta): il residuo OLS rimuove gia' il beta al fattore comune; sul crypto alle gambe **RESTA** un enorme fattore comune (ampiezza 4.5 su 50 gambe) ed e' quello che il demean toglie — sulle azioni il residuo-vs-SPY e' **gia'** quasi indipendente, quindi non c'e' niente da togliere. **Per il gate del 23/10:** conferma e CHIUDE la scappatoia lasciata aperta dal test a coppie; XSR01 e' **crypto-specifico**. NON prova che sia falso. **Soglie del gate NON toccate**: resta appoggiato interamente su finestra forward + haircut di eseguibilita' a $5.000, come pre-registrato. **LEZIONI:** (a) **se si de-lucka una strategia va de-luckato anche il suo DEGRADO** — ogni Δ fra due varianti misurato su griglia ancorata eredita la fortuna dell'ancora; (b) su offset appaiati la statistica e' la **mediana delle differenze**, non la differenza delle mediane; (c) **un lead "in forward-monitor" senza monitor e senza scadenza e' un lead perso** (DVOLSPREAD: 35 giorni di limbo) → applicare ai lead la stessa disciplina dei candidati (config congelata + gate pre-registrato + cron); (d) il claim di multiple-testing di un agente va **ricontato**, non creduto (72 dichiarate, 729 reali); (e) quando un meccanismo non generalizza, **chiedersi PERCHE' vale piu' del fatto che non generalizzi**. - ✅ **VENUE WATCH — tripwire di fallimento exchange, CABLATO LIVE (2026-07-26).** Risposta alla domanda *"trova un sistema di protezione da fallimento exchange"* **sotto il vincolo** della decisione appena presa (100% Deribit fino a $20k): se non si puo' ridurre l'ESPOSIZIONE, l'unica leva e' il **TEMPO**. Script `r0726_venue_tripwire.py` (segnale) + `r0726_venue_response.py` (costo della risposta); produzione `src/live/venue_watch.py` + `scripts/live/venue_watch.py` in `cron_book.sh`; test `tests/test_venue_watch.py` (**37**); diari `2026-07-26-venue-tripwire.md`, `2026-08-19-venue-watch-disciplina-allarmi.md`, `2026-08-21-venue-taratura-tre-referenze.md`. **Book, pesi, config INVARIATI.** (1) **Il segnale:** un venue che gata i prelievi **rompe l'arbitraggio** → il suo prezzo si stacca dal consenso e ci RESTA. Il segnale e' **|scarto|, non il segno** (Mt.Gox andava a *premio*, un venue in fuga a *sconto*: dicono la stessa cosa). Consenso = **mediana** di venue **USD indipendenti** (Coinbase, Bitstamp, **e Kraken dal 19/08** — vedi sotto); mai USDT (depeg 2022 → falsi allarmi giganti). Deribit sta a **3 bps** dal consenso in mediana su 8 anni — fondo di rumore bassissimo, ed e' cio' che rende possibile una soglia con margine. ⚠️ Le "65.043 ore BTC" citate qui fino al 21/08 erano le ore del consenso **con bitfinex dentro**, che la produzione non ha mai avuto: sull'insieme reale sono **69.633** (ETH 64.541 invariato). (2) **Taratura CONGELATA = 100 bps persistenti 4h a segno costante.** Criterio **dichiarato prima**, perche' i due ovvi sbagliano in versi opposti (provati entrambi): *minimi bps* → 25bps/24h consuma 24 delle ~72h che diede FTX; *minime ore* → 500bps/2h **manca FTX** (margine 0.6x). Regola adottata: (a) zero falsi allarmi su entrambi gli asset in 8 anni; (b) margine ≥3x sul caso storico **piu' debole** (FTX ~300bps) → soglia ≤100bps; (c) a quei vincoli, minima latenza. Margine finale **3x FTX / 5x QuadrigaCX / 10-20x Mt.Gox**. ⚠️ **CORREZIONE 2026-08-21 — la gamba (a) NON e' soddisfatta dalla configurazione che gira, e la frase "zero falsi allarmi inclusi crash COVID 2020-03" era FALSA: il falso allarme E' il COVID.** Vedi il blocco **(8)** in fondo. Il punto (100 bps, 4h) **resta**, per economia e per la gamba (b). (3) ✅ **CONTROLLO POSITIVO superato** (obbligatorio: un rilevatore tarato per non segnalare e' indistinguibile da uno rotto). Puntato su **Bitfinex 2018-19** (problemi bancari/Tether): **22 episodi**, il piu' lungo **2.324 ore consecutive** a +447bps di picco, altri a 1.151h/+663bps e 496h/+1136bps. **22 scatti dove il problema c'era, 1 solo su Deribit in 8 anni** (era dichiarato 0 — vedi **(8)**). E la DURATA risponde alla domanda vera: un venue gated resta dislocato per **settimane** → 4h di latenza costano una frazione trascurabile del preavviso. (4) **Economia della risposta:** costo ATTESO di un falso allarme (flat 3 giorni, misurato sul book reale a ogni data d'inizio) = **0.248%** di equity (coda p5 −2.065%); guadagno di un vero positivo = **100%**. Break-even: `p_annua > (falsi allarmi/anno) × 0.00248` → a 1 ogni 8 anni serve **p > 0.031%**, a 12/anno servirebbe p > 2.97%. **Il valore sta nella SPECIFICITA', non nella sensibilita'.** ⚠️ Errore mio corretto: il break-even si calcola sulla **media**, non sul p5 (con la coda esce 8.3x piu' severo e la conclusione si ribalta). (5) **Tre stati, e il terzo NON e' il primo:** `OK` / `ALERT` / **`BLIND`** (referenze irraggiungibili o in disaccordo fra loro → dopo 12h e' un allarme suo). *"Non vedo" non e' "va tutto bene"* — stessa lezione della contabilita' a 3 stati di `paper_dvolspread`. **ALLERTA, NON BLOCCA:** l'azione a un vero positivo e' *prelevare* (manuale — una chiave API con permesso di prelievo sarebbe essa stessa un rischio), e bloccare non protegge il saldo, che e' a rischio anche stando flat. **Runbook pre-deciso** nel docstring del modulo (escludere guasto referenze → `public/status` → **prelievo di prova**, unica evidenza diretta → flat + prelievo totale). (6) ⚠️ **Cio' che NON copre, e non e' un argomento per riaprire il 26/07:** un fallimento **senza finestra** (furto di chiavi, sequestro, exit-scam notturno) non lo prende nessun tripwire; quella parte di `p` resta scoperta e la sola difesa e' lo split. E il preavviso di 200-2.300 ore viene da **un** caso osservato: e' un'ancora, non una distribuzione. **REGOLE:** (a) un rilevatore tarato per non segnalare va validato su un **controllo positivo**; (b) una soglia si sceglie con un criterio **dichiarato prima** (i criteri ovvi sbagliano in versi opposti); (c) la persistenza richiesta **e' latenza** e va confrontata con la durata del fenomeno da rilevare; (d) un break-even si calcola sulla **media**, non sulla coda; (e) ⚠️ **una diagnostica STAMPATA non e' un controllo** — la prima corsa tronco' il campione da 8 anni a **29 giorni** per un inner-join con Kraken (che serve solo ~700 candele) e la copertura era gia' a video: ora c'e' una guardia che ferma lo script (2ª occorrenza in un giorno dopo GTAA01 — **l'outer-join con referenze di lunghezza diversa e' una trappola ricorrente**); (f) un controllo positivo finito dentro il ramo `else` **non gira mai** (trovato in sessione: era esattamente il difetto che doveva prevenire). ✅ **(7) DISCIPLINA DEGLI ALLARMI + TERZA REFERENZA + SPECIFICHE CONTRATTO (2026-08-19)** — *(bullet scritto il 21/08: la sessione era in un diario e in un commit e NON in memoria operativa, 2ª occorrenza dopo `edge_watch`)*. Nato da 4 🚨 identici il 18/08 per una **manutenzione Deribit annunciata** (14/08 per il 18/08 09:00 UTC, downtime dichiarato 15-30 min) che ha **sforato fino alle 14:07 UTC**. **Il book si era comportato bene** (astensione alle 09:07 e 10:07, *"conto non leggibile → non eseguo a cieco"*): il difetto era negli allarmi. (a) Il blocco piattaforma era un `if` secco **senza memoria**, accanto a un rilevatore che allerta una volta per streak → ora passa da **`lock_step()`** pura: manutenzione entro `MAINT_GRACE_HOURS`=2 → ⚠️ **una volta**; che sfora → 🚨 una volta («ha SFORATO»); blocco **senza** manutenzione dichiarata → 🚨 subito; rientro annunciato una volta; **`public/status` illeggibile → NIENTE** (dichiarare «e' rientrato» perche' non si e' riusciti a guardare sarebbe la bugia peggiore). *Un allarme massimo speso per un evento atteso e' un allarme che non verra' letto il giorno che e' vero.* (b) Il messaggio stampava `locked=true` **cablato** mentre il parser accetta anche `partial` → dichiarava un valore che non aveva letto, e il runbook manda a controllare **proprio quel campo**; ora stampa e **salva** il grezzo. (c) ⚠️ **Il difetto che poteva costare:** il 18/08 Deribit ha cambiato **tick e size dei perpetual lineari USDC** (BTC tick 0.5→**0.1**, ETH 0.05→**0.01**, ETH min/step 0.001→**0.0001**) e la tabella `_CONTRACT` di `deribit.py`, **cablata a mano**, non se n'e' accorta. Nessun ordine rifiutato **e non per merito nostro**: erano *riduzioni*, e un valore piu' grosso resta conforme (costo effettivo: granularita', incremento minimo ETH $1.92 invece di $0.19). **Il giorno che Deribit ALZA un minimo la stessa cecita' fa rifiutare gli ordini.** Cablato **`check_specs()`** nel venue_watch orario, **fuori dal percorso ordini**: la tabella dichiarata resta l'AUTORITA' per costruire un ordine, il venue e' il **controllore** (prendere i valori dall'API dentro l'esecuzione renderebbe l'ordine dipendente da come ha risposto una GET = non ricostruibile). Verdetti: `granularita'` (dichiarato piu' grosso → conforme, ⚠️) vs `rifiuto` (dichiarato piu' fine → 🚨). Uno strumento **non letto** finisce in `non_letti`, non conta come «combacia». (d) Aggiunta **Kraken** come terza referenza: **Coinbase ha comprato Deribit** e una referenza che e' la casa madre non misura piu' se Deribit scolla dal mondo. ⚠️ **(8) LA TARATURA RI-MISURATA (2026-08-21) — «zero falsi allarmi in 8 anni» non ha mai descritto la produzione, e il falso allarme e' il COVID.** Script `r0821_venue_refs.py`, diario `2026-08-21-venue-taratura-tre-referenze.md`. **Soglie, book, config INVARIATI.** **(i)** Il numero pubblicato veniva dal consenso **Coinbase+Bitstamp+Bitfinex** dello script di ricerca; il sorvegliante live girava su **Coinbase+Bitstamp**. Due liste di referenze in due posti diversi, e **nessun test poteva accorgersene**. Sull'insieme reale: **1 falso allarme in 8 anni**, il **2020-03-13 07:00-10:00 UTC**, 4 ore a **−418 bps** di picco su BTC — cioe' proprio l'evento che questo bullet elencava come esempio di cio' su cui NON scattava. La soglia minima a zero falsi allarmi a 4h e' **150 bps** (BTC), non 100. **(ii) Kraken non lo ripara:** su 8 anni porta **un mese** (tetto ~700 candele dell'endpoint pubblico, **ri-verificato oggi**: 704 barre su 70.286 richieste = 1,00%) → LIVE-post ≡ LIVE-pre sulla storia lunga, numeri **identici**. **(iii) Perche' spariva a 3 referenze, ed e' il punto trasferibile:** con bitfinex dentro **1 delle 4 ore diventa BLIND** → lo streak si azzera e l'episodio non esiste, **ma la dislocazione e' ancora li'** (mediana −343 bps). **Lo zero non veniva da un consenso piu' accurato, veniva da un'ora buttata** (ore utilizzabili 93,4% contro 100,0%). **(iv) La direzione dichiarata il 19/08 e' confermata, la sua TAGLIA dipende dal venue** (confronto appaiato, stesse ore): con **bitfinex** le ore non-BLIND passano 99,95% → 86,04% = **−13,91 pp**; con **kraken** **+0,00 pp** (|scarto| mediano appaiato −0,04 bps). Non e' una proprieta' del numero tre: e' **quanto la terza referenza e' d'accordo con le altre**. **(v) Controllo positivo intatto:** Bitfinex 2018-19 → 22 episodi, il piu' lungo 2.324h, picco **1.136 bps = 11,4x** la soglia. **(vi) DECISIONE: (100 bps, 4h) NON si tocca, ma la giustificazione cambia.** 1 falso allarme in 8 anni = 0,125/anno × 0,248% = **0,031%/anno** di equity attesa contro il **100%** che un vero positivo evita, e ogni `p` plausibile (0,5-5%) e' **16-160x sopra** il break-even; alzare a 150 bps porterebbe il margine su FTX da **3,0x a 2,0x**, sotto la gamba (b) dichiarata prima di guardare i dati. **Il numero da citare e' «1 in 8 anni»** — che e' esattamente l'esempio gia' usato al punto (4): **la memoria si contraddiceva da sola e la meta' giusta era quella dell'economia.** **REGOLE NUOVE:** (g) **un numero di taratura si etichetta con la CONFIGURAZIONE, non solo con la finestra** — «zero falsi allarmi in 8 anni» era vero e inutile perche' descriveva un consenso che la produzione non ha mai avuto; (h) **uno "zero" si legge accanto alla quota di campione utilizzabile**: meno falsi allarmi perche' si vede meglio e meno perche' si vede di meno hanno lo stesso valore stampato e valore opposto; (i) **quando due affermazioni della stessa memoria si contraddicono, la contraddizione e' informazione** — una delle due e' stata scritta guardando i dati; (j) **una nota che dichiara un debito puo' sottodimensionarlo**: il 19/08 diceva «il numero non e' ri-misurato», e il numero era sbagliato **prima** della modifica che lo aveva fatto dichiarare. **RESTA APERTO:** il Rulebook Deribit del 12/08 (ADL, perdita socializzata, *emergency powers*, conti dormienti) non lo sorveglia nessuno; `MAINT_GRACE_HOURS`=2 **presume** gli annunci invece di leggerli (due `locked=true` in 4 giorni: 18/08 ~4h, 21/08 ~1h); la terza referenza non e' validabile sulla storia. - 🚨 **IL TRASPORTO DEGLI ALLARMI E' UN PUNTO SINGOLO DI GUASTO — misurato 2026-08-23, NON riparato (produzione, decisione dell'operatore).** Registro `docs/research/RESULTS-0822.md` (sezione NOTIFIER). Il progetto ha tarato il **rilevatore** (`venue_watch`: 1 falso allarme in 8 anni, controllo positivo su 22 episodi Bitfinex, margine 3x su FTX) e **mai il trasporto**. `src/live/notifier.send()` fa **UN tentativo** `urlopen(..., timeout=10)`, `except Exception: return False`, **nessun retry**; `notify()` ritorna `bool` e **`scripts/live/venue_watch.py:80` non lo guarda**; `logs/cron_book.log` ha **0 occorrenze** di un qualsiasi esito d'invio → *quando serve sapere se l'allarme e' arrivato, l'informazione non e' stata scritta* (la regola del 29/07 — *"se un errore si ingoia per non bloccare, si registra nel punto in cui lo si ingoia"* — non e' mai stata applicata al notifier). 🚨 **E lo stato e' marcato PRIMA dell'invio:** in `venue_watch` la riga `new.alerted = True` sta dentro la logica pura e viene persistita comunque; alle ore successive `already = st.alerted and st.sign == sign` fa tornare `"WATCH"` invece di `"ALERT"` → **`notify` non viene piu' chiamata per quell'episodio**. **Un 🚨 perso e' perso per l'episodio intero**, e gli episodi storici durano **200-2.324 ore a segno costante**: e' esattamente il caso in cui la ri-notifica non arriva mai. **Taglia misurata: 6,9% di invii falliti (2/29, IC95 Wilson [1,9%, 22,0%])** sul digest giornaliero. ⚠️ **Fonte dichiarata:** e' il percorso del **digest** usato come **proxy** di quello d'allarme, che **non ha dati propri — ed e' questo il punto**; stessa funzione, stesso endpoint, stesso timeout. ⚠️ La nota stampata (`"config Telegram assente o rete KO"`) **conflazione due cause e la prima e' falsa**: le chiavi sono presenti in `.env` (verificato) → manda a controllare il posto sbagliato, **stessa forma del difetto codificato il 29/07**, su un percorso diverso. **Perche' conta piu' del suo numero:** l'economia del 26/07 (falso allarme 0,248% di equity contro il **100%** che un vero positivo evita, break-even `p > 0,031%`) assume che l'allarme **arrivi**, e questa e' l'unica mitigazione rimasta dopo la decisione *100% Deribit fino a $20k* contro un rischio prezzato a **P(perso tutto) 10/18/34/64%**. **Riparazione in tre pezzi indipendenti, NON eseguita:** (a) retry con backoff in `send()`; (b) **registrare l'esito** nel punto in cui l'eccezione viene ingoiata; (c) marcare `alerted=True` **solo a invio riuscito**. ⚠️ **(c) cambia il comportamento** — rende l'allarme ripetitivo finche' non passa: verso giusto per un 🚨, sbagliato per un ⚠️ → **decisione dell'operatore**, non un fix ovvio. **REGOLA: un rilevatore si valida sul segnale E sul TRASPORTO** — tarare la soglia e non misurare mai se il messaggio arriva lascia un punto singolo di guasto a valle di tutto il lavoro di taratura, e non produce numeri, quindi non si fa notare. - ❌ **GTAA01 NON E' DEPLOYABILE — blocco PRIIPs CONFERMATO sul conto reale (2026-07-26).** Nato da una domanda dell'operatore ("GTAA01 puo' essere in revolut?"), verificato lo stesso giorno tentando l'ordine. Il broker rifiuta: *"Trading limitato — Questo prodotto non dispone di un KID in inglese o in una lingua approvata per il vostro Paese. I clienti retail possono negoziare prodotti retail preconfezionati solo se e' disponibile un KID appropriato."* SPY/QQQ/IWM/TLT/GLD/HYG sono ETF **domiciliati USA**: gli emittenti non pubblicano il KID e i broker UE ne vietano l'**acquisto** al retail. ⚠️ **Le quotazioni restano visibili** — vedere i prezzi non e' poter comprare, ed e' esattamente cio' che rendeva l'assunzione invisibile. **COSA CADE:** lo sleeve **cosi' com'e' non e' deployabile**, e con esso il piano di attivarlo a ~$13k. Restano **valide come ricerca e nulle come deploy**: la validazione a 30 anni (22/06), il fix dei costi IB (25/07), `GTAA_MIN_CAPITAL`, e il risultato del LOO (26/07) che lo indicava come **l'unico sleeve positivo nel 100% delle estrazioni su tutte e tre le metriche**. **COSA NON CADE:** tutte le traiettorie, i muri e le tabelle di rendita pubblicate usano `book_series(with_gtaa=0)` = **solo TP01+SKH01 su Deribit** → nessun numero del piano va rifatto. E il book live non lo include. **REGOLA: la negoziabilita' sul conto REALE va verificata quando lo sleeve entra in RICERCA, non quando entra nel book.** Qui 5 settimane di misure poggiavano su un'assunzione mai controllata, e il controllo e' costato **un ordine di prova**. E' l'analogo azionario di cio' che il progetto fa gia' rigorosamente sul crypto (min-order, haircut small-cap, eseguibilita' a $600): la stessa disciplina non era stata applicata all'equity. - ✅ **LA VIA D'USCITA UCITS E' APERTA E COSTA ~ZERO — misurata 2026-07-26 (domanda dell'operatore: "usiamo revolut o degiro"). E la premessa su cui era stata scartata era MIA e FALSA.** Script `fetch_ib_ucits.py` + `r0726_gtaa_ucits.py`; modulo nuovo `src/data/eq_crosscheck.py`; test `tests/test_eq_crosscheck.py` (14) + `tests/test_gtaa_ucits.py` (10); diario `2026-07-26-gtaa-ucits.md`. **Book/pesi/cron/config INVARIATI.** (0) ⚠️ **CORREZIONE:** la nota diceva "storia UCITS piu' corta → si perde la validazione a 30 anni". FALSO per un motivo strutturale: **il PRIIPs vieta di COMPRARE, non di GUARDARE**. I prezzi dei 6 ETF USA restano leggibili (l'operatore li aveva sul terminale *mentre* l'ordine veniva rifiutato) → il **segnale** gira sui 30 anni per sempre, cambia solo il **veicolo** su cui si incassa. La storia corta serve solo a misurare la deviazione del veicolo, un drift lento. **Regola: prima di dichiarare che un vincolo esterno distrugge un risultato, chiedersi cosa vincola esattamente** — "non posso comprare" ≠ "non posso vedere". (1) **Il cambio di veicolo NON costa.** Tre lenti a un grado di liberta' per volta su 6.3 anni comuni: L0 (segnale USA + rend. USA, pubblicato) **Sh 0.81 / CAGR 3.96%**; L2 (segnale UCITS + rend. UCITS, deploy) **Sh 0.84 / CAGR 4.08%**. Drag del veicolo **+0.10%/anno** EW, coerente coi TER (controllo piu' netto: GLD 0.40% vs IGLN 0.12% → atteso +0.28%, misurato +0.36%); e la **ritenuta USA 15% sui dividendi (~35bps) e' A FAVORE dell'UCITS** e non e' nel drag misurato (`ADJUSTED_LAST` USA e' al lordo). *(Ipotesi fiscale dichiarata, fonte secondaria.)* ⚠️ **Lo stimatore ovvio da' la risposta sbagliata:** la media delle differenze giornaliere dava **−0.47%/anno** su CSPX contro **−0.06%** vero. Le due serie seguono lo stesso indice ma sono campionate a orari diversi (Londra chiude 4h30 prima) → la differenza giornaliera e' dominata da uno sfasamento che si inverte il giorno dopo: **SE ~7%/anno, 15× la quantita' stimata**, piu' il drag di varianza. **REGOLA: la deviazione fra due veicoli sullo stesso indice si misura sul RAPPORTO CUMULATO** (e' una domanda sul drift), mai sulla media delle differenze. (2) ✅ **IL VINCOLO NON E' IL BROKER, E' IL PREZZO DI UNA AZIONE — e quello e' una SCELTA.** CSPX costa $802 e VUAA $144 sullo **stesso** S&P 500. Due insiemi: **STORIA** (CSPX/EQQQ/XRSU/ IDTL/IGLN/IHYU, finestra lunga, per misurare) e **DEPLOY** (VUAA/XNAS/R2US/IDTL/IGLN/IHYU, prezzo unitario basso, per eseguire). A **$3.000** con esecuzione a azioni INTERE: STORIA tiene **4/6 gambe** ed e' a mercato il **33%** del tempo; DEPLOY tiene **6/6** al **65%**, ΔSharpe −0.04. **Il frazionamento del broker NON serve.** Scartati benche' con piu' storia: RTWO (Russell 2000 *quality*), CSUSS (*ESG*), IDP6 (S&P 600) — equivalenza dell'INDICE prima della storia. ⚠️ Letto sulla colonna **gambe vive + vol**, non su Sharpe: il vincolo intero *alza* lo Sharpe a capitale piccolo perche' arrotonda in giu' e paga meno commissioni = **null de-levering, 4ª occorrenza** (VRP-DD, TP01×DVOL, MAT01). (3) **Il costo per ordine.** Col canonico lo sleeve fa **77 ordini/anno a $3k, 102 a $10k, 149 a $50k**; soglia oltre cui non vale la pena (Sharpe<0.45) = **$0.90/ordine a $3k, $2.23 a $10k, $7.62 a $50k**. Listino **proporzionale** ≈ indifferente al capitale, listino **fisso** = tassa regressiva (regola 25/07 riconfermata su altro venue). ⚠️ **Revolut/Degiro NON sbloccano gli ETF USA**: il PRIIPs vale per ogni intermediario UE, IB e' anzi fra i piu' permissivi. - ✅ **ADDENDUM 2026-07-27 — il numero di ordini e' un PARAMETRO, e con la banda giusta il costo smette di essere binding su qualunque broker UE.** Nato dalla correzione dell'operatore *"io ho gia' Revolut e Degiro e li uso da anni, IB sono solo iscritto"*: la raccomandazione del 26/07 ("restare su IB") poggiava su **"il conto esiste gia'"**, che era falso. Il gateway IB serve solo per i **dati** del segnale (basta il paper), quindi il broker di esecuzione e' libero. Script `r0727_gtaa_broker.py`, test `tests/test_gtaa_broker.py` (9). **Book/pesi/cron/config INVARIATI.** (a) I 77-149 ordini/anno sono la conseguenza di `REBAL_EVERY=5`/`REBAL_BAND_USD=50`, scelti il 25/07 **per il listino IB su azioni USA e a una taglia sola**. A cadenza settimanale allargare la banda **non costa**: Sharpe a **costo zero** 1.27 ($50) → **1.27** ($400) mentre gli ordini vanno 84 → 23. Rallentare la CADENZA invece costa (1.27 → 1.12 mensile). Meccanismo: `_exposure` e' la media di 4 indicatori binari, si muove a scatti di 0.25 ≈ $417 su una gamba da $1.667 → **la banda filtra la deriva del vol-target, non il segnale di trend**. ✅ Controllo fuori finestra: sui **veicoli USA su 10 anni** lo Sharpe a costo zero passa 0.98 → **0.96** mentre gli ordini calano del **74%** (133 → 35) — non e' un artefatto della finestra corta UCITS (3.2a). (b) ⚠️ **MA la banda in dollari ASSOLUTI e' la parametrizzazione sbagliata.** A $3.000 una banda da $400 e' l'**80% della gamba**: **3 ordini/anno**, a mercato il **45%** del tempo invece del 66%. Lo Sharpe resta 0.71 — accettabile — **su una strategia che ha smesso di seguire il proprio target**. E' il **null de-levering in veste nuova**: non travestito da "meno drawdown" ma da "meno costi". **REGOLA: quando un parametro di esecuzione e' espresso in valuta assoluta il suo effetto dipende dal capitale, e il controllo non e' lo Sharpe ma la QUOTA DI TEMPO A MERCATO.** (c) **Configurazione proposta: banda = 25% della gamba** → **21 ordini/anno a OGNI capitale** (invariante), esposizione 66% ovunque; soglia per ordine **$5.65 a $3k / $18.90 a $10k / $47 a $25k** contro i **$2.08 a $3k** del canonico. E' la differenza fra "serve un broker economico" e "va bene qualunque broker europeo". ⚠️ **Proposta, non cambio di produzione**: tarata su questa finestra, non passata per `study_family_honest` ne' per un deflated-Sharpe — e GTAA01 non e' deployabile prima dei $20k, quindi c'e' tempo per validarla. (d) **Cosa cercare sul proprio conto — per ISIN, non per ticker** (da contract details IB; tutti domiciliati IE, quotati a **Londra in USD**): **VUAA** `IE00BFMXXD54` ($143.80) · **XNAS** `IE00BMFKG444` ($65.74) · **R2US** `IE00BJ38QD84` ($86.35) · **IDTL** `IE00BSKRJZ44` ($3.09) · **IGLN** `IE00B4ND3602` ($79.06) · **IHYU** `IE00B4PY7Y77` ($94.12). Nessuna azione oggi (decisione venue: 100% Deribit fino a $20k). ⚠️ **CORREZIONE 27/07 A QUESTO PUNTO.** Avevo scritto "una linea in EUR aggiunge la conversione per ordine → prendere quella in USD". E' vero **solo per un conto in USD**. Per un conto in EURO (retail italiano su Revolut/Degiro) vale l'**OPPOSTO**: la linea USD costringe a convertire a ogni ordine, quella EUR no. L'esposizione economica e' identica (il fondo detiene attivi USD e **nessuna delle due linee e' coperta**): la valuta di quotazione non copre nulla, decide solo se serve una conversione. **REGOLA GIUSTA: prendere la linea nella valuta del proprio saldo.** Misurato (turnover lordo **$13.655/anno** su $10k, 21 ordini): 15bps −0.04 Sh/$20 a., 25bps **−0.07 Sh/$34 a.**, 50bps −0.14/$68 → a 25bps equivale a **~$1.6 in piu' per ordine**, piccolo rispetto alla soglia di $18.90. **REGOLA: un costo di conversione non e' una proprieta' dello strumento ma della coppia strumento-CONTO.** (f) **Equivalenze e ripieghi, verificati su contract details IB (27/07).** **ZPRR (Xetra EUR) == R2US (Londra USD): stessa ISIN `IE00BJ38QD84`, stesso fondo, stesso NAV** → se un broker non quota la linea di Londra, quella di Xetra e' identica (e su conto in euro, migliore). **REGOLA: si cerca per ISIN, non per ticker** — lo stesso fondo ha ticker diversi su borse diverse. ⚠️ **SPY4 `IE00B4YBJ215` NON e' small cap ne' S&P 500**: e' SPDR **S&P 400 MID cap** (l'S&P 500 e' SPY5 `IE00B6YX5C33`). Misurato come ripiego della gamba small: su 6.5 anni Sharpe **0.86 vs 0.86**, CAGR 4.26% vs 4.20%, **corr fra i due sleeve 0.991**, peggiore in 3/7 anni = moneta (sulla finestra corta di 3.2a sembrava −0.15: rumore) → **ripiego accettabile**, per ragione meccanica (small e mid USA correlano ~0.95 giornaliero, un TSMOM si accende negli stessi giorni). Non e' "validato": 6.5 anni non distinguono due gambe cosi' simili, ed e' esattamente perche' la scelta non conta. Se c'e' una linea Russell 2000, si usa quella. (g) ✅ **DEGIRO HA TUTTE E SEI LE GAMBE — verificato sul conto reale (2026-07-27, screenshot watchlist "Pythagoras").** Era **Revolut** a non avere R2US. Su Degiro: VUAA e XNAS su **Tradegate in EUR**, R2US/IDTL/IGLN/IHYU su **LSE in USD**. ✅ Verifica indipendente che le linee EUR siano gli stessi fondi (dai dati, non dallo screenshot): il rapporto prezzo-IB/prezzo-Degiro dev'essere **un solo cambio** → VUAA 1.1341, XNAS 1.1315, **scarto 0.23%**; le altre 4 stanno a 0.993-0.998 (gia' USD). Due fondi diversi non darebbero lo stesso cambio. ⚠️ **ERRORE MIO, dello stesso tipo che avevo appena codificato:** per cercare le linee in euro delle altre 4 gambe ho interrogato IB **per TICKER** sulle borse tedesche → "nessuna linea EUR" su 4/4, **falso**. Cercando **per ISIN** ognuna ce l'ha: **ZPRR** (=R2US), **IS04** (=IDTL), **EGLN** (=IGLN, Londra EUR), **IS0R** (=IHYU). **Un "assente" da una ricerca per ticker su una borsa dove quel ticker non esiste NON significa "non esiste".** **Turnover per gamba a $10k (21 ordini/anno):** **IDTL 9 ordini / $6.257 = 46%**, VUAA $2.289 (17%), IGLN $1.695 (12%), XNAS $1.614 (12%), IHYU $958 (7%), R2US $843 (6%) → **il turnover e' concentrato**, il 71% e' su gambe in USD (**$9.752/anno**, conversione **$24/a a 25bps**, $49 a 50bps = 6-12% del CAGR). **Spostare la sola IDTL su IS04 copre il 64% del turnover convertibile.** ⚠️ Non e' un risparmio netto (spread Xetra piu' larghi delle linee primarie di Londra + tariffa di connettivita' per borsa) e su $10k parliamo di decine di dollari l'anno in entrambe le direzioni: **non e' una decisione importante, ed e' piu' utile dirlo che costruire una precisione finta.** ⚠️ Il prompt **W-8BEN** di Degiro ("aliquota ridotta della ritenuta USA") **non riguarda queste sei**: sono fondi IRLANDESI, non titoli USA — la ritenuta 15% la subisce il fondo al proprio interno e nessun modulo dell'investitore la cambia. **Stato: PREPARAZIONE, non azione** (saldo Degiro €328,92; GTAA01 richiede ≥$3k e la decisione venue tiene tutto su Deribit fino a $20k). **LEZIONE: una raccomandazione poggiata su un fatto non verificato sul conto reale vale quanto quel fatto** — due volte in due giorni un'assunzione sul conto ha cambiato la conclusione (prima la negoziabilita' PRIIPs, poi quale conto e' davvero operativo). (e) **"degiro ha api?" — NO, e costa −0.02 di Sharpe.** DEGIRO non espone una API di trading ufficiale al retail; Revolut nemmeno (la sua e' per pagamenti); IB si'. I wrapper NON ufficiali degli endpoint interni girano su credenziali + seed 2FA e **si rompono in silenzio** a ogni cambio di front-end — e il progetto ha gia' pagato quel prezzo (`fresh_5m`, 26/07): **un esecutore automatico che puo' rompersi senza dirlo e' peggio dell'esecuzione manuale.** Misurato invece il costo di NON automatizzare: carico operativo **15 settimane/anno con ≥1 ordine** (29% dei controlli, 1.4 ordini per volta); costo del ritardo su 10 anni **1g −0.02 (peggiora in 6/11 anni = moneta), 3g −0.13, 10g −0.27**. ⚠️ Sulla finestra UCITS di 3.2 anni la curva NON e' monotona (5g −0.26, 10g −0.13) → il campione corto non risolve differenze di questa taglia, si legge la finestra lunga. **Il costo non e' il ritardo tipico ma la CODA: il rischio e' la dimenticanza, e si copre con un ALLARME non con una API** (`gtaa_rebalance_plan` esiste gia' in produzione, nato per un esecutore; l'allerta Telegram c'e' gia' sul book live). ⚠️ Lato opposto, vero: il resto del book e' automatico, una gamba manuale aggiunge un modo di fallire che oggi non c'e' — argomento reale a favore di IB, che pero' richiede un secondo venue e quindi cade sotto la decisione venue ($20k). **REGOLA: una capacita' mancante si valuta sul costo di non averla, non sulla sua assenza.** - 📅 **NUOVO SCHEMA FEE DERIBIT dal 2026-08-01 — misurata la CURVA, nessuna azione oggi (2026-07-26).** Script `r0726_fee_sensitivity.py`, test `tests/test_fee_sensitivity.py` (6), diario `2026-07-26-fee-deribit.md`. **Book/pesi/cron/config INVARIATI.** L'annuncio (taker piu' bassi, **maker rebate piu' bassi**, soglie VIP abbassate, nuovo VIP7, **liquidation fee 1%** su tutti i prodotti, spot a zero fino al collegamento con Coinbase) **non contiene numeri** e ⚠️ **la tabella nell'articolo Insights e' un'IMMAGINE, non letta da fonte primaria** → i valori indicativi (base ~5bps taker / 2bps maker, VIP7 2/0) vengono da un riassunto **secondario** e vanno etichettati come tali. Percio' e' misurata la **curva**: | bps/lato | %RT | TP01 Sh | SKH01 Sh | BOOK Sh | BOOK CAGR | |---|---|---|---|---|---| | 0 | 0.00% | 1.322 | 1.567 | 1.849 | 21.69% | | 3 | 0.06% | 1.303 | 1.495 | 1.799 | 20.99% | | **5** | **0.10%** | **1.290** | **1.446** | **1.766** | **20.53%** ← oggi | | 10 | 0.20% | 1.258 | 1.324 | 1.682 | 19.39% | | 15 | 0.30% | 1.226 | 1.200 | 1.597 | 18.26% | **Sensibilita' marginale del book: −0.017 Sharpe/bps, −0.23% CAGR/bps** → anche un RADDOPPIO del taker costa 0.08 di Sharpe, **meno della banda d'ancora dello stesso book** (2.222 → 1.946). ⚠️ **SKH01 e' ~4× piu' sensibile di TP01** (−0.69% vs −0.09% CAGR/bps: round-trip discreti vs posizione continua vol-targeted) → **se il taker salisse, il primo parametro da rivedere e' il peso 75/25**, non altro. **REGOLA DECISA IN ANTICIPO (per non decidere col numero davanti): taker ≤5bps/lato → non si tocca nulla; >10bps/lato → rivedere il peso di SKH01.** **Punti irrilevanti e perche':** *maker* — il book manda ordini **market**; tocca solo la raccomandazione **T1 non implementata** (TP resting per incassare il maker), che resta chiusa per il netting su strumento unico e il cui beneficio era quasi tutto "convertire una lotteria in certezza", non la fee. *Liquidation fee 1%* — `live.json` da' nozionale lordo max **1.00x l'equity** (`frac 0.5` × 2 asset) con disaster-SL −30%: servirebbe un movimento avverso ~100%; ⚠️ la conclusione poggia sul CAP, quindi e' cablata una **guardia di decisione** (`test_leva_massima_da_config_resta_sotto_o_uguale_a_1x`): **alzare il cap rompe il test**. *VIP/VIP7* — a $600 il volume 30g e' trascurabile, tier base a qualunque soglia. ✅ **Cio' che il cambio non puo' rompere:** tutti i backtest sono a 0.10% RT → se la fee scende diventano **conservativi**. **AZIONE 1 agosto:** leggere il tier reale in Account Settings e applicare la regola sopra. **REGOLE:** (a) a un annuncio senza numeri si risponde misurando la **curva**, non aspettando il numero; (b) dichiarare la **qualita' della fonte** (immagine non letta = dato secondario); (c) una conclusione che poggia su un parametro di config va legata a un **test su quel parametro**, o sopravvive al cambio che la rende falsa. - ✅ **EDGE WATCH — criteri di kill del book LIVE, cablati (2026-07-26).** *(Bullet aggiunto il 27/07: era in cron dal 26/07 e NON stava in CLAUDE.md — il criterio di morte di cio' che gira con soldi veri non era nella memoria operativa.)* `scripts/live/edge_watch.py` (in `cron_daily.sh`), taratura `r0726_edge_death.py`, test `tests/test_edge_watch.py` (11). Chiudeva l'asimmetria: gate di kill pre-registrati per i CANDIDATI, nessuno per il book. **(A) RITORNO: Sharpe rolling 36m < −0.5** — falso kill 1.8% in 10 anni, riconosce l'edge morto nel 91% dei casi in ~3.8 anni. ⚠️ **La lentezza non e' un difetto della regola, e' statistica:** a 12 mesi la stessa regola ucciderebbe un edge VIVO nell'80% dei casi; a 12m il 56% dei casi "morto" e' indistinguibile da uno vivo. **(B) PROTEZIONE: in un anno con DD buy&hold > 10%, il DD di TP01 deve restare sotto il 75% di quello** — storico 8/8 anni di sinistro superati (protezione 1.8x-34.4x). Serve un criterio SEPARATO perche' il LOO misura il contributo hold-out di TP01 negativo nel 99.1% delle ancore: **"non ha guadagnato" non e' evidenza di morte per uno sleeve difensivo** — la sua morte e' non proteggere nel sinistro, e negli anni senza sinistro il criterio NON si valuta. Se (A) scatta il book **non si spegne da solo**: si riapre `weights_tilt_null` + deflated-Sharpe sui dati nuovi. Se (B) fallisce **2 anni di sinistro consecutivi**, il peso di TP01 va rimesso in discussione. Stato 27/07: **Sharpe 36m +1.51, protezione 8/8.** Diario `2026-07-26-edge-death.md`. - ✅ **FEE WATCH + MONITOR HEALTH — due sorveglianze cablate (2026-07-27).** Nessuna tocca l'esecuzione; entrambe in `cron_daily.sh`. (1) **`scripts/live/fee_watch.py`** (test 13): il nuovo schema fee Deribit entra il **1° agosto** e l'annuncio non ha numeri → invece di un promemoria, un sorvegliante. Legge il tier **BASE** da `public/get_instrument` (nessuna chiave; a $600 ogni soglia VIP e' fuori portata), oggi **taker 5.00 / maker 0.00 / liquidazione 75-90 bps**, applica la regola congelata (≤5bps nulla · >10bps rivedere il peso SKH01) e allerta su **qualsiasi** cambiamento dei tre. `test_baseline_e_quella_dei_backtest` lega la soglia al default `fee_rt=0.001` di `backtest_signals`: se divergono, il test lo dice. ⚠️ **CORREZIONE 2026-08-21 — sorvegliava lo STRUMENTO SBAGLIATO (test 13 → 17).** `INSTRUMENTS` era la tupla cablata `("BTC-PERPETUAL","ETH-PERPETUAL")` = i perpetual **INVERSE** (regolati in BTC/ETH), mentre il book esegue sui **LINEARI USDC** (`BTC_USDC-PERPETUAL`). Due conseguenze: (a) il tier sorvegliato era di un prodotto **mai tradato** — oggi identico per caso (3.50/1.50 su entrambe le linee) ma **la prova che si muovono in modo indipendente e' nel progetto**: il cambio del 18/08 tocco' i SOLI lineari (inverse ancora tick 0.5/min 10.0, lineari 0.1/0.0001) → un aumento sulla sola linea lineare sarebbe stato invisibile e la regola ≤5/>10 bps applicata al numero sbagliato; (b) **il cross-check sui trade reali non poteva misurare nulla per costruzione** — chiedeva `trade_history` di uno strumento con **0 fill** e stampava «NON MISURATO» anche nei giorni con 4 esecuzioni (verificato: inverse 0 trade, `_USDC` 3+1). Ora `INSTRUMENTS` si **DERIVA da `src.live.book.INSTRUMENT`**: la divergenza non e' un rischio da ricordare, e' impossibile. ⚠️ **E non era un rename:** le due famiglie hanno unita' DIVERSE — inverse `amount`=nozionale USD e `fee` in valuta base; lineare `amount`=quantita' BASE e `fee` gia' in USDC. Ripuntare senza correggerle avrebbe dato, su un fill vero (0.001 BTC @ 74.305,80, fee 0,02600703), **~2,6e8 bps invece di 3,50** — e **senza sollevare nulla**. Cablate `convenzione()` (lineare/inverse/**ignota**: una famiglia non nota non si indovina) e `fee_bps_di_un_fill()` pure; il cross-check ora gira e da' **3,50 bps effettivi = il tier esatto**, e il report stampa lo scarto effettivo−tier con ⚠️ sopra 1 bps. **REGOLA: un sorvegliante deve DERIVARE il proprio bersaglio dal codice sorvegliato, mai ridichiararlo** — due liste in due file divergono in silenzio, e il giorno che divergono il sorvegliante continua a dire OK. **3ª occorrenza in un giorno della stessa forma** (test di `book_live` senza potenza a libro flat, taratura di `venue_watch` misurata con bitfinex mentre il live girava senza): *un controllo puntato su una configurazione diversa da quella che gira passa sempre, e non sta controllando niente.* (2) **`src/live/monitor_health.py` + `scripts/live/monitor_health.py`** (test 16): **tre gate pre-registrati** (STATARB 27/09, XSR01 23/10, DVOLSPREAD 24/10) si decidono su serie forward di cui **una sola** aveva una guardia d'integrita'. Un monitor fermo produce silenzio, e **il silenzio in una serie di ritorni si legge come zero** (stesso schema di `fresh_5m` e del feed-freeze 14/07). Misura **due guasti**, perche' uno solo non basta: **coda** (ultima barra vecchia) e **buchi interni** (copertura fra prima e ultima barra) — ⚠️ *una serie bucata E fresca passa qualunque controllo di freschezza, ed e' il guasto che falsifica un gate senza farsi notare*. Cadenze dichiarate per monitor (prevday e' **orario**, combo segue il calendario di **borsa**: sbagliarle = un falso allarme a settimana); soglia di copertura **0.80** riusata dal veto DVOLSPREAD perche' i gate restino confrontabili. Stati **OK/FERMO/BUCATO/ASSENTE/NUOVO** — "troppo giovane per un giudizio" non e' "sano". Controlli positivi obbligatori nei test. Stato 27/07: 6/6 giudicati, tutti OK (combo 96% per una festivita' che `np.busday_count` non conosce = limite dichiarato, conservativo). - ✅ **BANDA GTAA01 AL 25% — VALIDATA, produzione NON toccata (2026-07-27).** `r0727_gtaa_band_gate.py`, test `tests/test_gtaa_band_gate.py` (13). Chiude il debito dichiarato il 27/07 ("tarata su questa finestra, non passata per `study_family_honest` ne' per un deflated-Sharpe"). Griglia **30 celle** (5 cadenze × 6 bande) su **29.9 anni** del path di produzione, annualizzazione **√252**. **(A) Selezione in-sample** (cella scelta sui soli dati pre-2015, letta sul 2015+): proposta **4/30 in-sample, 5/30 hold-out** → il rango NON migliora sull'hold-out, quindi **non e' selection-on-holdout**. La cella scelta al buio (cadenza **giornaliera**, banda 25%) vale +0.04 di Sharpe ma costa **250 controlli manuali/anno** su un conto senza API → non e' una configurazione, e' un'ipotesi. **(B) Deflated Sharpe 0.999 PASS** (nullo 0.14). **(C)** ⚠️ **il modo di fallire NON e' il de-levering**: allargando la banda la **vol non scende** (0.99-1.10 del riferimento), si rompe il **TRACKING** (corr 0.951 a 25% → 0.907 a 40% → 0.859 a 60%) e lo Sharpe smette di migliorare insieme alla correlazione → nessuna zona premia il congelamento. **Ma il 25% e' AL BORDO** (0.951 contro soglia 0.95), non al centro di un plateau: citarlo cosi'. **Invarianza**: **25 ordini/anno a $3k/$10k/$50k** (banda 25%, Sharpe 0.66/0.70/0.71) contro **65/92/134** (banda $50 fissa, 0.52/0.62/0.66) — ⚠️ 25 e non i **21** citati il 27/07: stimatore diverso (griglia dei controlli su 30 anni USA vs cambi di posizione sulla finestra UCITS di 3.2a); l'*invarianza*, che e' la proprieta' sotto esame, regge in entrambi. **Impatto sul book: Sharpe FULL 2.221 → 2.221, maxDD 6.08% → 5.94% = zero** (il book modella GTAA01 a $10k, dove la banda fissa gia' funziona) → **il valore e' tutto al capitale piccolo** (0.52 → 0.66 a $3k), cioe' al deploy. **`REBAL_BAND_USD` NON cambiato**: non sposta un numero pubblicato e lo sleeve non e' deployabile prima dei $20k → si applica **al deploy**, con questo gate come giustificazione. **REGOLA: un parametro d'ESECUZIONE scelto guardando il risultato e' selezione come ogni altra** e passa per gli stessi gate, anche quando "non tocca l'allocazione". ⚠️ **CORREZIONE 2026-08-07 — il criterio (A) qui sopra e' RITIRATO: non misurava cio' che dichiarava, e il "4/30 in-sample, 5/30 hold-out" non e' un'evidenza.** Il test lo aveva segnalato smettendo di passare (9ª/30 in-sample, 8ª/30 hold-out) **senza che il codice fosse cambiato** — `data/raw/` e' gitignored e il cron riscrive i parquet equity ogni notte con `ADJUSTED_LAST` di IB, che e' retroattivo. Misurato in `r0807_gtaa_gate_resolution.py`: **(a)** i due ranghi distavano **0.00116 di Sharpe** su uno spread di griglia di **0.3124** (0.4%); **(b)** e soprattutto **il criterio `rank_in <= rank_oos` lo passano 14/30 celle (47%) PER COSTRUZIONE** — la somma dei ranghi e' la stessa nelle due finestre → *e' una moneta, e il 27/07 la moneta era uscita bene*. Contorno: spostamento tipico fra le due finestre **8 ranghi** (max 25), **Spearman IS/OOS +0.05**. ✅ **CRITERIO SOSTITUITO, e la proposta ne esce piu' forte di prima.** La proposta e' una **BANDA** (la cadenza settimanale e' gia' `REBAL_EVERY=5` in produzione), quindi la domanda decidibile e' *quale banda si sceglie a cadenza di produzione guardando solo il pre-2015*: esce **25% = la proposta**, con margine **+0.0151** di Sharpe sulla seconda (**13×** il margine del vecchio criterio); chi avesse scelto **sull'hold-out** avrebbe preso **40%** (controllo positivo: se coincidessero il gate non avrebbe potenza). Su **tutta** la griglia la cella al buio e' cadenza 1 / banda **25%** — stessa banda. **La proposta e' il CONTRARIO di una selezione-sull'hold-out.** Regge identico sull'universo a **5 gambe** (blind 25%, hold-out 40%, margine +0.0164). ⚠️ Il criterio dice da dove VIENE la scelta, **non** che sia la migliore sull'hold-out (con Spearman ~0 nessuna cella lo sarebbe): provenienza ≠ previsione. ⚠️ **TROVATO PER STRADA, ed e' il difetto vero: TLT ha 13.5 ANNI DI STORIA IN MENO.** Parte dal **2016-02-03** invece che dalla quotazione (2002-07-22) → **GTAA01 gira su CINQUE gambe prima del 2016**, e quella assente e' la gamba obbligazionaria, cioe' quella che diversifica. Non e' di oggi (cosi' fin dal primo giro nel `cron_daily.log`, 24/06 = **prima** della validazione del 27/07) e **non e' un fetch da rifare**: una richiesta retro esplicita a IB su questo conto ritorna **0 barre**. Conseguenza metodologica: **l'in-sample e l'hold-out di GTAA01 non sono la stessa strategia**, e ogni confronto fra le due finestre su questo sleeve va letto cosi'. ✅ **Guardia cablata** (`fetch_ib_equities.certify`, test `tests/test_eq_history_guard.py`, 11): **`TRONCATO`** = storia persa rispetto al disco → **il file NON viene sovrascritto** (e neppure fuso: `ADJUSTED_LAST` e' ri-aggiustato all'indietro, incollare due vintage crea un salto sul giunto); **`STORIA-CORTA`** = parte >1 anno dopo la quotazione e non al tetto della richiesta (`PRIMA_QUOTAZIONE`, 6 simboli, **fonte secondaria dichiarata**). Controlli positivi obbligatori: giro normale, serie al tetto 30Y, ETF giovane, simbolo fuori tabella. **REGOLE:** (a) **un criterio si misura sulla sua RISOLUZIONE prima che sul suo esito** — se decide su una frazione di percento dello spread e' rumore anche quando passa; (b) **un gate si valida contando quante volte lo passa un candidato a caso** (qui 47%: il conto si poteva fare il 27/07 senza dati nuovi); (c) **una certificazione che guarda solo DENTRO la serie non vede cio' che la serie ha PERSO** — una serie troncata e' integra, senza gap, senza spike, senza duplicati, e passa tutto (3ª occorrenza dopo split 2:1 e contaminazione EUR/USD); (d) distinguere «giovane» / «al tetto della richiesta» / «troncato», o la guardia segnala sempre e viene ignorata; (e) **un test che fallisce senza che il codice sia cambiato sta segnalando che i dati non sono versionati** — si guarda sotto prima di toccarlo. Diari `2026-08-07-crescita-fisco-etf-scelta.md` §7 (scoperta) e `2026-08-07-gate-gtaa-e-storia-troncata.md` (risoluzione). - 📓 **LIBRO DI BORDO — i trade allineati col tempo, il giornale e l'analista (2026-08-23).** `src/live/{tradesdb,journal,analista}.py`, CLI in `scripts/live/`, voci in `docs/journal/`, test `tests/test_{tradesdb,journal,analista}.py` (77). **Strategia, pesi, config INVARIATI.** 🚨 **IL DIFETTO CHE L'HA FATTO NASCERE: i trade erano salvati e NON allineati col tempo.** `book_execute.py` scriveva `ts_utc = pd.Timestamp(r['last_data'])`, cioe' la data della **barra di segnale**: 19 righe su 19 a `00:00:00`, e **un trade registrato SEI GIORNI prima di essere eseguito** (ETH 0.04 @ 1.869,74: fill vero 2026-07-14T14:00, scritto 08/07). L'ora vera esisteva **solo** in `logs/cron_book.log` — **gitignored, fuori dal backup, ruotabile**: la cronologia reale del libro live viveva in un file che una rotazione avrebbe cancellato senza che nessuno se ne accorgesse. Riparato alla sorgente (`ts_utc` = ora vera, `bar_ts` = barra) **con un test di regressione sul sorgente**: se qualcuno rimette `last_data`, il test lo dice. 📌 **IL DB** (`data/live/trades.db`, dentro il perimetro del backup): `fills` col contesto del segnale a quel giro (tp_frac, skh_sign, entry, target, posizione, equity), `roundtrips` **derivati e ricalcolati da zero**, `equity` oraria, `journal`. Sync **orario** in `cron_book`. **Le tre fonti si INCROCIANO e non si sovrascrivono:** log (ora vera) x jsonl (i fill) x venue (autorevole ma **TRONCA** — 1 trade su BTC, 0 su ETH). `reconcile()` riporta le divergenze e **non ripara niente da solo**: fra due fonti che non concordano, una riparazione silenziosa e' un'invenzione. E' cosi' che il difetto dell'ora e' stato *isolato* invece che dedotto. 📌 **QUATTRO LIVELLI IN PAGINA, SEPARATI PER PROVENIENZA** — e la separazione E' il prodotto: **numeri** (feed + DB) · **Lettura** (12 regole dichiarate, ognuna con l'id stampato accanto alla riga che ha prodotto, ognuna con **due** test: uno che la accende e uno che la tiene spenta) · **Analisi** (prosa di un modello, firmata e datata) · **Nota** (l'operatore, mai riscritta da un ricalcolo). *Un giornale che mescola misura e racconto, a sei mesi, non permette piu' di sapere quale delle due si stava leggendo.* 🚨 **L'ANALISTA SBAGLIA, E IL TRIPWIRE NON PUO' PRENDERLO.** `numeri_non_supportati()` verifica che ogni cifra dell'analisi compaia in cio' che il modello ha ricevuto (oltre 3 numeri liberi: **rifiutata**), ma **due errori su due giri con sonnet-5 non contenevano cifre nuove** — una frase su un merge mai raccontato, e un round-trip corretto dichiarato inesistente con una spiegazione inventata per il proprio dubbio. **Il modello e' stato portato a `claude-opus-5`; il campione e' di due osservazioni, quindi e' un tentativo di abbassare un tasso, non una garanzia.** **REGOLA: una guardia sui NUMERI non copre il RAGIONAMENTO — l'analisi si legge come opinione di un lettore fallibile, mai come parte del registro.** ⚠️ **CICLO DI RETROAZIONE, trovato al primo giro reale:** il modello leggeva la **propria** analisi del giorno prima e la commentava (aveva prodotto un paragrafo sull'avviso del tripwire che si era preso). `senza_analisi()` toglie la sua sezione dalla pagina prima di dargliela; la `Nota` resta, perche' il contesto dell'operatore serve. **Nessun controllo automatico puo' distinguere il meta-commento da prosa valida: si vede solo LEGGENDO l'uscita.** ✅ **TELEGRAM, e due delle tre riparazioni al notifier finalmente fatte.** L'analisi parte ogni giorno con una testata di numeri; se manca, **il messaggio parte lo stesso dicendo perche'** (il silenzio si legge come «non e' successo niente»). Il trasporto era il punto singolo di guasto gia' misurato (**6,9% di invii persi, 2/29**) = ~25 messaggi l'anno su una cadenza giornaliera: (a) `send(text, tentativi=1)` — retry **opzionale**, default invariato perche' alzarlo per tutti cambierebbe la latenza degli allarmi di `venue_watch` e `book_execute` su un percorso con soldi veri; (b) `ultimo_errore()` registra il motivo **nel punto in cui veniva ingoiato** e non sopravvive a un invio riuscito. **(c) `alerted=True` solo a invio riuscito resta la decisione dell'operatore**: cambia il comportamento degli allarmi. Esito nel DB in tre stati: `inviata` / `non configurato` / `FALLITA — motivo`. ⚠️ **CINQUE difetti trovati dai test o leggendo l'uscita, nessuno a occhio:** (1) il tetto di leva era **RIDICHIARATO** (`0.5` cablato) invece che letto da `config/live.json` — **quinta occorrenza** dello schema che il progetto paga da luglio; (2) l'IV-rank era il percentile **a un anno** etichettato col nome del gate di VRP01, che usa quello **espandente**: davano il verdetto **opposto** (0,52 «sopra» contro 0,18 «sotto», e il valore giusto combacia con «0/8 settimane passano il gate» misurato il 30/07); (3) il renderer cadeva in `KeyError` se mancava il blocco mercato; (4) «24 giri attesi» su un giorno **in corso** = allarme a ogni esecuzione; (5) libro e P&L leggevano **due istanti diversi** e la stessa pagina mostrava due equity. 📌 **REGOLE:** (a) **un dato operativo non ricostruibile non puo' vivere in un file gitignored** — si materializza dove il backup arriva; (b) **una guardia piu' stretta del contratto produce allarmi che si impara a ignorare** (il tripwire validava sulla sola pagina mentre il prompt include anche lo storico, e bocciava un'equity vera); (c) **una regola che si accende su $2 di cumulato insegna a saltare la sezione**: serve una soglia di rilevanza; (d) chi scrive in un registro va **firmato**, o il registro perde il suo valore probatorio. --- ## Addendum 2026-08-25 — la voce del GIORNO IN CORSO si presentava come una giornata *(scritto 2026-08-25T08:59:12Z, ora letta da `date -u`)* `cron_daily` gira alle **00:30 UTC** e faceva **due** chiamate al giornale: chiudeva IERI (giorno completo, corretto) **e apriva OGGI**. La pagina di oggi nasceva quindi con **~30 minuti di giornata dentro** e **nessuno la aggiornava fino alla notte dopo**. 🚨 **Il difetto non era il dato mancante, era che la pagina non lo diceva dove si legge.** Aveva **tutte le sezioni** di una pagina chiusa — Mercato, Libro, P&L, Salute, Lettura, Nota — quindi passava ogni controllo di completezza e di freschezza; la parzialita' era dichiarata **solo in §Salute**, quinta sezione su otto (`giri di book_execute: 1/1 *(giorno in corso)*`), mentre titolo, P&L e le regole della Lettura parlavano al passato di una frazione di giornata. E' la stessa famiglia gia' codificata due volte — *"una riga presente non e' un dato presente"*, *"una barra presente non e' una giornata presente"* — in una veste nuova: **la pagina e' presente, il giorno no**. 📌 **La taglia, misurata sul caso reale del giorno stesso** (ed e' il motivo per cui non era un difetto cosmetico): la pagina congelata alle 00:37 diceva **"giorno: $+0.54 di equity (1 letture)"** e la regola `[pnl]` chiosava **"senza operare: e' mark-to-market sulle posizioni gia' aperte"**. Alle 08:54 dello stesso giorno il libro aveva fatto **3 fill, 2 round-trip chiusi e +$25.86** di equity ($642.56 → $667.88). **La pagina avrebbe raccontato una giornata ferma per tutta una giornata operativa**, e chi l'avesse letta — o un modello che se la fosse ritrovata nel prompt — non aveva modo di accorgersene senza contare i giri. ✅ **Riparato in due pezzi indipendenti:** 1. **La pagina dichiara la propria parzialita' nel TITOLO e nella prima riga** (`src/live/journal.py::rendi_markdown`): `⚠️ PARZIALE (giorno in corso)` + copertura esplicita (*"copre N giri su 24"*), il fatto che **non si aggiorna da sola**, e quali numeri sono di quella frazione (giorno) e quali restano corretti (cumulati). 2. **Il cron non la congela piu'**: tolta la seconda chiamata da `scripts/cron_daily.sh`. In `docs/journal/` restano **solo giorni chiusi**; chi vuole lo stato corrente lancia `journal.py` a mano (e riceve una pagina marcata PARZIALE) o legge `trades.db`, che `cron_book` sincronizza **ogni ora**. ⚖️ **Scelta dichiarata: NON si rigenera la voce di oggi ogni ora.** Sarebbe l'altra soluzione difendibile e costa una riga in `cron_book`, ma riscriverebbe un file **tracciato da git 24 volte al giorno** per un consumatore che oggi non esiste (l'analista legge il giorno **chiuso**). Se un domani servisse la voce sempre fresca, **la strada e' rigenerarla, non congelarla**: e' scritto nel commento del cron perche' chi ci torna non debba ri-derivarlo. **Prove (3, in `tests/test_journal.py`, 28 → 31):** una **accende** la marcatura su un giorno in corso e verifica titolo + copertura + *"non si aggiorna da sola"*; una la **tiene spenta** su un giorno chiuso (*una marcatura sempre accesa non si legge*); la terza e' una **guardia sul SORGENTE** di `cron_daily.sh` — ogni chiamata a `journal.py` deve avere `--giorno` esplicito, perche' `journal.py` **senza argomenti scrive OGGI**. La guardia **non vieta** di rigenerare spesso: vieta di scrivere la pagina **una volta e lasciarla li'**. 🚨 **RIPARATO (2026-08-26): il P&L di giornale contava i VERSAMENTI come profitto.** Il «giorno» era un delta di equity fra letture: la voce del 25/08 dichiarava **+$1.414,57 di P&L** quando $1.399,39 erano il deposito USDC dell'operatore, e il «cumulato dall'arming» avrebbe mentito per sempre (e al prossimo versamento da $3.000 avrebbe stampato «+$3.000 di giorno»). Riparazione in `src/live/journal.py::movimenti_capitale` con tre proprieta': (1) la **soglia e' importata** da `book.EQUITY_JUMP_ALERT`, non ridichiarata (P1 — il rilevatore di movimenti del progetto e' quello); (2) un salto e' `movimento` solo se supera di >2x il massimo che il **mercato misurato** (feed certificato, tetto di leva da config) avrebbe potuto produrre nell'intervallo fra le due letture; (3) altrimenti e' **`ambiguo`: dichiarato e NON scorporato** (P12 — un crash vero a tutta leva non deve trasformarsi in "prelievo"). Scansione dell'intera storia reale: **1 evento, esattamente il deposito** (+209,6% contro un massimo di mercato di ±0,22%), zero falsi positivi. Anche la regola `concentrazione` ora lavora sul cumulato di **trading** (il 25/08 leggeva "il 100% del P&L viene dagli ultimi 7 giorni" su un P&L che era il bonifico). Due scelte di design da non perdere: le regole sui movimenti stanno **sopra** l'early-return "nessun giro di book" (un versamento in un giorno senza giri esiste lo stesso: viene dal DB equity, non dal log del libro), e il **limite e' dichiarato** (D5): un movimento sotto soglia — es. $150 su un conto da $2.000 — non si distingue dal mercato e resta nel P&L, come nel rilevatore live. 7 prove nuove in `test_journal.py` (31 → 37), incluso il controllo M15 al contrario: un crash −14% a tutta leva resta AMBIGUO. Voce del 25/08 **rigenerata**: giorno +$1.414,57 di equity → trading **+$15,18**; cumulato +$1.458,53 → trading **+$59,14**. ⚠️ **Trovato girando la suite completa, NON riparato — e' un'altra cosa:** `tests/test_wave_0726.py::test_t1_canonical_riproduce_il_backtest_ufficiale` **fallisce**, e falliva gia' prima di questa modifica (verificato con `git stash`: stesso fallimento sull'albero pulito). Lo Sharpe hold-out di SKH01 `canonical` vale **1,9223** contro la banda cablata `1.3 < h < 1.9`. **Il codice non e' cambiato: sono cambiati i DATI** — `data/raw/` e' gitignored e il cron lo ricostruisce ogni notte, quindi la finestra hold-out si allunga e il numero deriva (verso l'**alto**, cioe' non e' un peggioramento). **La banda NON e' stata allargata**: e' la lezione del 2026-08-07 (*"un test che fallisce senza che il codice sia cambiato sta segnalando che i dati non sono versionati — si guarda sotto prima di toccarlo"*), e allargare una tolleranza per far passare un test e' esattamente la manovra che quella lezione vieta. **Resta aperto.** --- ## Addendum 2026-08-25 — La manutenzione del martedì: chi è rotto, e chi resta scoperto Racconto completo in `docs/diary/2026-08-25-manutenzione-deribit-e-resilienza-venue.md`. Qui restano i fatti citabili e i motivi, che valgono più degli esperimenti che li hanno prodotti. ### I numeri, contati sui log (1.499 giri, 2026-06-23 → 2026-08-25) | esito | n | % | |---|---|---| | manutenzione Deribit | 3 | 0,20% | | traceback duro | 5 | 0,33% | | giri senza esito utile | 26 | 1,7% | **Lo slot di release Deribit è il MARTEDÌ alle 09:00 UTC**, 15-30 min annunciati. Quattro episodi, tutti fra le 09:00 e le 09:07: `2026-07-21` (502 visto dal gateway), `11/08`, `18/08` (giù anche alle 10:07 → **l'annuncio non è la durata**), `25/08` (~20 min). Il cron al `:07` ci cadeva **per costruzione**. 🚨 **Il punto singolo di guasto non è il venue, è NOSTRO.** Dei 5 traceback, **4 sono `cerbero-mcp.tielogic.xyz`**: un 404 su `get_positions`, un 502, due `ReadTimeout`. Sostituire Deribit non toccherebbe il guasto che ci ha davvero morso. E il gateway **non è aggirabile**: le credenziali Deribit vivono solo lì dentro (§5.11). ### I motivi — cioè la parte che chi riapre il tema deve battere - **Una nota di diagnosi cablata è peggio di nessuna nota (P4).** `conto non leggibile (offline)` copriva due guasti con azioni opposte. E il 18/08 il codice **11051 era già dentro il processo**, nello stesso minuto, raccolto da `livefeed`: non mancava il dato, mancava il **trasporto** del dato a chi decideva la gravità. - **Declassare un allarme atteso (P9) è giusto, ma la finestra non è una prova.** `venue_probe` declassa **solo su evidenza** della sonda (mai sull'orologio) e **solo dentro la durata annunciata**: oltre, RIALZA. Senza questi due paletti si costruisce il silenzio esattamente nell'ora in cui è più probabile che serva. - **`naked` ≠ `place-failed` (P5).** Il primo è "*ho tolto la protezione e non sono riuscito a rimetterla*", il secondo "*non sono riuscito a metterla*". Solo uno lascia una posizione aperta senza stop on-book, e non è mai un evento atteso: 🚨 sempre. - **Un guasto su un asset non deve togliere la rete all'altro.** Il 21/07 il ciclo è morto su BTC e nel log **ETH non compare**: né ribilanciato, né verificato nella sua protezione. L'isolamento per asset è la riparazione; il costo di non averlo era un'ora di posizione non controllata. - **Riparato il silenzio, NON toccata la sequenza.** `ensure_disaster_sl` continua a cancellare prima e piazzare dopo. *Piazza-poi-cancella* sembra più sicuro (due STOP `reduce_only` dovrebbero essere innocui) ma "dovrebbe" non basta per cambiare il ciclo di vita dei bracket con soldi veri senza misurarlo. **Chi lo riapre deve portare la misura, non l'intuizione.** - **Il vincolo del minuto `:07` non era "il :07": era "fuori dai ~26s del minuto tondo"** (il collettore catena si auto-satura il rate-limit per-IP — 12.186 risposte 429 in 26 ore, 96% nel minuto `:00`, misura del 30/07). Letto così, il vincolo lascia libero qualunque minuto ≠ `:00`, e il `:47` soddisfa anche il secondo (fuori dallo slot di release). **Una regola operativa va riletta nella sua ragione, non nel suo valore** — altrimenti si difende un numero invece di un motivo. ### Previsione dichiarata, da misurare (M12) Sulle 4 finestre osservate 3 sono rientrate entro l'ora → il `:47` ne avrebbe scavalcate **3 su 4**. **Se al prossimo martedì il `:47` becca comunque la manutenzione, la previsione è sbagliata** e lo slot non è quello descritto in `venue_probe.RELEASE_*`: rileggerlo prima di spostare ancora. ### Cosa resta scoperto 1. **La sonda dice di chi è il guasto, non lo aggira.** Col gateway giù il libro continua ad astenersi. Il fallback vero richiede **chiavi API Deribit create dall'operatore** — decisione, non refactor, con una superficie di rischio propria (§5.11). 2. **La sonda legge se il venue RISPONDE, non cosa il venue ANNUNCIA.** Il Rulebook (ADL, perdita socializzata, *emergency powers*) resta non sorvegliato (§5.8). ### Coda 2026-08-25 — Un test scriveva nel watermark VIVO (e ha mandato un allarme falso) Lanciando la suite completa, `data/live/equity_seen.json` è passato da **$667,68 a $5.000**; il giro del book alle 09:47 ha confrontato l'equity vera contro quel valore e ha spedito su Telegram un **`💰 USCITA DI FONDI: $5.000 → $667,68 (−86,6%)`** completamente falso. Colpevole: `test_book_live.py::test_la_formula_del_report_ha_potenza_anche_a_libro_flat` — prende solo `monkeypatch` (niente `tmp_path`), finge `real_equity=5000.0` e chiama `book.book_report()`, che **come effetto collaterale** scrive il watermark. Riprodotto isolando il singolo test. 🚨 **Non era cosmetico.** `cap_fallback = min(cap_config, watermark × frac)`: con $5.000 dentro, il cap sarebbe passato da **$334 a $2.500/asset** — fino a **$5.000 di nozionale lordo su un conto da $668, ~7,5x di leva**. Il ramo che ci arriva (`eq_fallback`) **allerta e NON blocca**, per scelta dichiarata. **Lanciare i test poteva armare esattamente il pericolo che il watermark esiste per impedire.** Danno reale quel giorno: nessuno — alle 09:47 l'equity era leggibile, quindi il cap si è calcolato sul vero. *È andata bene per la direzione del caso, non per una protezione.* **I motivi da non ri-derivare:** - **Il perimetro di un test non è quello che il test dice di toccare: è quello che tocca il codice che chiama.** Quel test non menziona il watermark, non lo importa, non lo asserisce — lo scrive passando per una funzione di produzione tre livelli sotto. - **La riparazione per-test non regge, ed è dimostrato tre volte.** L'helper `_write_cfg` nasce per questo il **26/07**; il test colpevole è del **21/08** e non lo usa; oggi la stessa scommessa ha perso di nuovo. Quindi fixture **autouse** in `tests/conftest.py`: vale anche per il test che qualcuno scriverà domani senza aver letto niente. Il monkeypatch esplicito di chi vuole davvero pilotare il watermark gira dopo e continua a vincere. - **Una protezione mai vista fallire non è una protezione** → guardia in `test_cap_watermark.py` che si accende se la fixture viene rimossa. - ⚠️ **Resta il principio più largo:** oggi è deviato **solo** il watermark. `data/live/trades.db` e `data/live/book_executions.jsonl` sono esposti allo stesso errore (§5.12).