2751a7efd0
cron_daily (00:30 UTC) faceva DUE chiamate al giornale: chiudeva IERI (giusto)
e apriva OGGI. La pagina di oggi nasceva con ~30 minuti 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 chiusa -> passava ogni controllo di
completezza e freschezza, e la parzialita' stava solo in §Salute, quinta
sezione su otto. Stessa famiglia di "una barra presente non e' una giornata
presente", in veste nuova: la pagina e' presente, il giorno no.
Taglia misurata sul caso reale di oggi: la pagina congelata alle 00:37 diceva
"giorno: $+0.54 (1 letture)" e la regola [pnl] chiosava "senza operare". Alle
08:54 il libro aveva fatto 3 fill, 2 round-trip e +$25.86 ($642.56 -> $667.88).
Avrebbe raccontato una giornata ferma per tutta una giornata operativa.
Riparato in due pezzi indipendenti:
1. la pagina dichiara la parzialita' nel TITOLO e nella prima riga —
"PARZIALE (giorno in corso)", copertura esplicita (N giri su 24), il fatto
che non si aggiorna da sola, e quali numeri sono di quella frazione;
2. tolta la seconda chiamata dal cron. In docs/journal/ restano solo giorni
chiusi; lo stato corrente si prende da journal.py a mano (pagina marcata)
o da trades.db, che cron_book sincronizza ogni ora.
Scelta dichiarata: NON si rigenera ogni ora. Costerebbe una riga in cron_book
ma riscriverebbe un file tracciato da git 24 volte al giorno per un consumatore
che non esiste (l'analista legge il giorno chiuso). Se un domani servisse, la
strada e' RIGENERARLA, non congelarla: e' scritto nel commento del cron.
Prove (test_journal 28 -> 31): una accende la marcatura, una la tiene spenta su
un giorno chiuso (una marcatura sempre accesa non si legge), una e' guardia sul
SORGENTE di cron_daily.sh — ogni chiamata a journal.py deve avere --giorno
esplicito, perche' senza argomenti scrive OGGI.
Regolare docs/journal/2026-08-25.md rigenerato: ora si dichiara PARZIALE (9/24).
NON riparato, e registrato come debito aperto in CLAUDE.md §5:
test_wave_0726::test_t1_canonical_riproduce_il_backtest_ufficiale fallisce, e
falliva gia' prima di questa modifica (verificato con git stash sull'albero
pulito). Sharpe hold-out SKH01 canonical 1,9223 contro la banda cablata
1.3 < h < 1.9: il codice non e' cambiato, sono cambiati i dati (data/raw e'
gitignored, il cron lo ricostruisce ogni notte). La banda NON e' stata
allargata — lezione 07/08. Suite: 710 passati, 1 fallito.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
803 lines
70 KiB
Markdown
803 lines
70 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'**.
|
||
|
||
⚠️ **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.**
|