d597fc64e8
S5.1 RIPARATO E RIGENERATO. Filtro condiviso src/live/paper_guard.py (barra open-labeled chiusa = ts + cadenza <= adesso) importato da tutti e 6 i monitor; serie rigenerate dallo stesso start_ts con scripts/live/paper_regen.py (evidenza in *.pre_regen_20260826.*): statarb +1,95 -> -1,61 (il ribaltamento del gate 27/09 previsto dall'audit), dvolspread -14,73 -> -4,41, xsr -4,98 -> -2,72, prevday invariato. Nessuna data di gate si sposta. Guardia cablata in monitor_health: stato PREMATURO (ultima barra che chiude dopo l'mtime, grazia 5 min, open_labeled=False per collect_chain) — sul dato vivo segnala i 5 rotti e tace sui 2 sani; dopo la rigenerazione 7/7 OK. paper_portfolio non rigenerato (GTAA su ADJUSTED_LAST: replay != serie registrata, P12), tolta la coda non chiusa. D6 pagata di nuovo nel fix: asi8 in pandas 3 e' in us, non ns — blindata con test su tre risoluzioni. S5.12 ESTESO: conftest devia anche trades.db (wrapper su connect: il default e' catturato alla definizione) e docs/journal/; book_executions.jsonl sorvegliato con impronta inizio/fine suite. S5.5 FATTO: test_leva_massima cancellato con nota (misurava frac*n_asset: con una chiave di scala avrebbe continuato a passare smettendo di controllare). S5.9 INDAGATO E RIPARATO (r0826_skh_band_drift): il dato regge (taglio 02/07 riproduce l'audit 1,6376, in-sample identico su ogni taglio); la deriva era la finestra hold-out — e la sola settimana 15-22/08 vale +0,35 di Sharpe hold-out. Il test ora taglia il feed al 02/07 e verifica la riproduzione stretta. Suite: 751 passati, 0 falliti. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B9UyJLHzR7EJzxR3iQ3RN1
943 lines
80 KiB
Markdown
943 lines
80 KiB
Markdown
# 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 E RIGENERATO (2026-08-26): `advance()` e i 6 forward-monitor (§5.1).**
|
||
Il fix e' un filtro condiviso (`src/live/paper_guard.py`): barra open-labeled chiusa =
|
||
`ts + cadenza ≤ adesso`, importato da tutti e sei i monitor — `paper_combo` compreso, che era
|
||
sano *per caso* (aspettava la chiusura di una borsa, non per progetto) ed e' ora sano *per
|
||
costruzione*. Rigenerazione con `scripts/live/paper_regen.py`: stesso `start_ts` pre-registrato,
|
||
config congelata, replay del codice di produzione riparato (il metodo validato 2 volte
|
||
dall'audit 22/08); evidenza del difetto archiviata in `*.pre_regen_20260826.*` dentro il
|
||
perimetro di backup. Esiti (Sharpe rotto → vero): statarb **+1,95 → −1,61** (il ribaltamento
|
||
del gate 27/09 previsto dall'audit) · dvolspread −14,73 → −4,41 · xsr −4,98 → −2,72 · prevday
|
||
+0,39 → +0,40. `paper_portfolio` non rigenerato (GTAA legge ADJUSTED_LAST ri-aggiustato: il
|
||
replay non sarebbe la serie registrata, P12): tolta solo la coda non chiusa, storia pre-fix
|
||
dichiarata corrotta. La guardia raccomandata dall'audit e' **cablata in `monitor_health`**:
|
||
stato **PREMATURO** (l'ultima barra chiude dopo l'mtime del file, grazia 5 min), derivato
|
||
dalle spec esistenti (P1), con `open_labeled=False` per `collect_chain` che timbra giri e non
|
||
barre (P14). Controllo positivo sul dato vivo: prima della rigenerazione segnala **esattamente
|
||
i 5 rotti** e tace sui 2 sani; dopo, 7/7 OK. ⚠️ Lezione nel fix: la prima stesura di
|
||
`indice_chiuso` usava `asi8` assumendo `ns` — pandas 3 usa `us`, e la barra del giorno in corso
|
||
risultava "chiusa" **senza eccezioni** (D6, pagata di nuovo scrivendo il codice che doveva
|
||
impedirla). Blindato con un test su tre risoluzioni (`ns/us/s`).
|
||
|
||
🚨 **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).
|