66 Commits

Author SHA1 Message Date
Adriano Dal Pastro 5b72a4b188 §3: quota USDE 0,64 derivata dal cuscino e' decisione vincolante dell'operatore (10/09)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kqvff47UBGeYfj1QeN4zE
2026-09-10 14:19:31 +00:00
Adriano Dal Pastro a2b03aa2b6 USDE allineato a quota 0,64 su ordine dell'operatore (238 USDE venduti, slack +$266 = OK)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kqvff47UBGeYfj1QeN4zE
2026-09-10 14:17:43 +00:00
Adriano Dal Pastro 37d1565a1e cuscino_watch: riacquisto automatico di USDE con isteresi (ECCEDENTE), bande in config, escape HTML nel sink di notify
Decisione dell'operatore 10/09 («riportarla a quota in autonomia»). Verificato da revisione fable
(10 segnalazioni, 9 applicate; loop numerico su usde_convert.piano: nessuna sequenza vendita→acquisto).

- quarto stato ECCEDENTE: slack > cuscino_riacquisto_frac (0,40) x cuscino => acquisto di USDE fino
  allo stesso bersaglio della vendita, q* = 1 - 0,30 x (1 + cuscino_margine_frac 0,20) = 0,64.
  Isteresi [0 ; 0,40] x cuscino con bersaglio unico 0,20: per oscillare servono +-8,6% di equity
  USDC (~$380); un bonifico alza lo slack di 0,7 x importo e sopra ~9% dell'equity ricompra da solo.
- le tre frazioni (preavviso/margine/riacquisto) in config/live.json sezione usde, lette da
  usde.bande_cuscino() che verifica l'ordine; quota_target 0,70 TOLTO (slack zero per costruzione).
- notifier.notify: html.escape su titolo e valori — la prima allerta vera (13:53Z) era andata persa
  per un '<' nudo; allerta_errore registrato nel record (P3).
- da ECCEDENTE un fallimento si ritenta ogni 24h (non 4 Telegram/giorno per sempre); bande fuori
  ordine => BLIND con motivo, non un traceback ingoiato dal cron.
- CLAUDE.md §1/§4/§5.17, config _nota_usde/_nota_cuscino, diario. 0,64 e' una DERIVAZIONE
  dell'autore, non la quota decisa il 30/08 (0,70): dichiarato, da confermare.
- test: 1070 (+8 netti).

Fixes #7

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kqvff47UBGeYfj1QeN4zE
2026-09-10 14:14:29 +00:00
Adriano Dal Pastro f82f685528 cinque punti della revisione 09/09: usde_watch a 1000 trade + finestra 24h, cuscino_watch (equity USDC, riconverte da solo), PREVDAY-01 kill/veto cablati, SCALA-01 al 2027-02-28, versamento 04/09 dichiarato
Decisi dall'operatore il 2026-09-10, verificati da revisione fable (15 segnalazioni, 12 applicate).

- usde_watch: TRADE_LIMIT 1000 (count max Deribit) e trade_copertura(): lista troncata o ultima
  lettura oltre la finestra di 24h del gateway => somma NON leggibile, reward non attribuito (P12).
  Riga del 07/09 corretta nel log con campo `correzione` (400 USDE erano acquisti, non reward).
- cuscino_watch.py (cron :53, monitor_health): equity USDC contro cuscino derivato da
  usde.cuscino_richiesto_usd (formula spostata in src/live/usde.py, usde_convert la importa);
  OK/PREAVVISO/SCOPERTO/BLIND; sotto zero lancia usde_convert --quota quota_ripristino(0.20)=0.64
  --esegui con guardie (execution_enabled, depeg_warn, 1 tentativo/6h). Primo giro: PREAVVISO, +$26.
- usde_convert: il tetto del venue vale solo in ACQUISTO (bloccava la vendita).
- paper_prevday: GATE PREVDAY-01 cablato (2027-06-21, kill Sharpe giornaliero < -0,50 su >=180 g
  attivi, veto >=80% barre ricostruibili + divergenze non crescenti con soglia materiale).
  Oggi: +0,95 su 81 g, 1942/1943 ricostruibili, kill NON MATURO.
- CLAUDE.md: arming 20/06 (TP01) / 23/06 (BOOK); piano EUR 5.000 chiuso col versamento 04/09
  ($2.414,68 dal balance, dichiarato); SCALA-01 non prima del 2027-02-28 (A2 dal 01/09);
  PREVDAY-01 con data, kill, veto; §5.17 riparato con i limiti dichiarati (ratchet, slack zero a 0,70).
- test: 1062 (+31): test_cuscino_watch (16), test_paper_prevday_gate (8), test_usde_watch (+5).

Fixes #2
Fixes #3
Fixes #4
Fixes #5
Fixes #6

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kqvff47UBGeYfj1QeN4zE
2026-09-10 13:24:28 +00:00
Adriano Dal Pastro 79afe41ec2 giornale 2026-09-09 (cron_daily 00:30Z)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bjj5vPEBoAJrB23P6RjKzs
2026-09-10 04:32:48 +00:00
Adriano Dal Pastro 8f2e6c5c2c revisione settimanale in sola lettura: revisore fable, rapporto firmato + Telegram, lunedi' 06:15 UTC
- src/live/revisione.py: materiale (CLAUDE.md, config, crontab, git, monitor_health, stati dei
  sorveglianti, 7 g di giornale e diari, ~195k caratteri), prompt con dieci regole (sola lettura, fonte
  per ogni segnalazione, solo numeri del materiale, §3 non si ripropone, idee nuove solo citando la
  memoria, gate con data e criterio), guardia sui numeri, rapporto firmato (P13), Telegram = Sintesi
  con taglio dichiarato; modello muto o risposta senza titoli -> rapporto «NON eseguita» + 🚨 (P5).
- scripts/live/revisione.py (guardia cli.valida, --secco/--no-telegram/--quiet/--giorno/--modello),
  scripts/cron_review.sh, crontab `15 6 * * 1`.
- primo giro reale: il prompt su argv moriva con `Argument list too long` senza rapporto ne' allerta ->
  prompt su stdin, ogni eccezione diventa errore dichiarato; `analista.pulisci` toglieva i titoli.
  Secondo giro: docs/revisioni/2026-09-09.md, stato sospetta (cifre in formato italiano), nessun URGENTE.
- scartati con la memoria: autoregolazione dei parametri, generazione automatica di strategie, macro
  da internet (diario 2026-09-09c, memoria 40).
- test: tests/test_revisione.py (18) + 2 in test_cli_flag; suite 1031/1031.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bjj5vPEBoAJrB23P6RjKzs
2026-09-09 22:07:50 +00:00
Adriano Dal Pastro 0a2f780d3c cblib causale: spot +1h e DVOL +1 giorno (debito §5.18); §11 rimisurato 0,714 → 0,712, verdetti invariati
- cblib.causale(s, cadenza): spot_series e dvol_series escono causali (asof = ultima chiusura NOTA);
  il DVOL giornaliero era una seconda serie con lo stesso difetto (fino a 24h avanti), verificato contro
  l'API pubblica a 1h. Regolamento ST alle 08:00 invece delle 09:00.
- §11 riprodotto al millesimo su worktree HEAD (0,714 [0,690-0,779], n=19, flag --al) e rimisurato:
  0,712 [0,664-0,732]; solo spot 0,721, solo DVOL 0,693, DVOL orario di controllo 0,717. Mediana onesta
  BTC 1,45 → 2,12, fee 1,51x/1,87x. r0730: 0,73 → 0,71. §75: ETH pre 0,76, BTC durante 0,89 (orario 0,83).
  skew panel ≤0,005. vrp_f_watch: f canonico 0,732 → 0,706, differenza +0,097 [+0,042, +0,140].
- r0909 senza doppio shift, contesto sul DVOL del giorno; r0822 regime su date di calendario; r0901
  join riallineato (bit-exact). Aperti con costo: DVOL orario (−0,04/+0,06 nel crollo), spot 5m (≤25 min).
- test: +3 causalita' in test_cb_chain_vrp, anti-doppio-shift in test_r0909; suite 1011/1011.
- revisione fable: 16 segnalazioni, tutte applicate (fra cui un «−1,8% in un'ora» che era +3,9%).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bjj5vPEBoAJrB23P6RjKzs
2026-09-09 21:41:23 +00:00
Adriano Dal Pastro bb40e87d13 crolli: il libro perde nel giorno e chiude > 0 in 8/8 finestre dal 2022 (SKH01 short); crollo catturato a vol bassa, XRP diluisce come SOL
Quattro misure (r0909_*), nessun cambio a libro/pesi/config:
- libro nei crolli: −0,48%/g nei 160 giorni ≤ −5%, positivo per finestra dal 2022
  per la gamba short di SKH01 (108% dei guadagni); beta 0,0769 riprodotto (§46);
  il peso di SKH01 resta chiuso dal gate (in-sample).
- crollo catturato 1-5/06/2026 a quote vere: f_net 0,74 = rally; put δ−0,10
  1,92× il modello; a vol bassa e fuori dal gate di VRP01 → §3 non si riapre.
- XRP terza gamba (harness r0822_sol_leg): hold-out −0,169 in 0/24, un anno buono.
- universo Deribit: liquidi solo BTC/ETH/XRP/SOL; XS01 13/19; BTCDVOL future
  non negoziabile; PAXG non misurato.
Revisione fable: due conclusioni smontate (MTM 2,6× era un artefatto di quote;
«8/12 guadagna» era l'ordine delle classi). Debito §5.18: cblib.spot_series
+ asof guarda un'ora avanti (feed 1h etichettato all'apertura).
Test Opus: 92 nuovi, suite 1008/1008; due difetti del verdetto XRP corretti.
Docs: diario, RESULTS §74-77, CLAUDE.md, memoria 20/50, README; journal 07-08/09.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017zJjHUS7mf4pnGE6pq9RTt
2026-09-09 16:33:54 +00:00
Adriano Dal Pastro 9f55652d99 usde al 68,7%: il tetto del venue del 30-31/08 non c'era piu'; il versamento di prova del 25/08 dichiarato, TWR +7,83%
Su autorizzazione dell'operatore («usa piu' USDE», «cerca % massima raggiungibile»):
- usde_convert al 31,8% (774 USDE), poi sonda a saldo crescente r0906_usde_tetto_sonda.py
  (passi 100->20->4->1, doppio rifiuto prima di scendere, conferma a saldo neutro): da 31,8%
  a 68,7% in 39 ordini @ 1,0005, ZERO rifiuti. Fermata dal cuscino di regolamento (70%,
  derivato da config), non dal venue. «Il 70% non e' raggiungibile ne' ora ne' mai» (31/08)
  e' falsificato: il tetto e' sparito, non scalato. Non spiegato, puo' tornare.
- config: venue_cap_frac -> null (misura in venue_cap_misurato), quota_max_frac 0,50 -> 0,85
  (il valore scelto dall'operatore il 30/08 per questo scenario).
- debito §5.17: nessuno sorveglia il cuscino USDC (slack $58; una perdita del libro lo consuma
  da sola).

Versamento di prova di EUR 25 il 25/08 08:00Z (dichiarato dall'operatore): +3,3% su $647, sotto
la soglia del 10% del rilevatore, contato per 12 giorni come trading.
- data/live/movimenti_dichiarati.jsonl (append-only, nel backup) letto da
  journal.movimenti_dichiarati; movimenti_capitale lo fonde coi rilevati (fonte dichiarato,
  importo dell'operatore, dopo = prima + importo; dichiarato+rilevato se coincide; fuori
  letture o riga rotta -> avvisi). Report e nota di giornale lo dicono.
- trading da arming +87,35 -> +62,45; TWR +11,98% -> +7,83%. CLAUDE.md §2 aggiornato.
- tests: +6 in test_journal (conftest isola il file). Suite 916 verdi.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KSnordG9FT4q8M4MVm85GQ
2026-09-07 05:49:15 +00:00
Adriano Dal Pastro 95424f0755 docs: il README era fermo a 15 giorni PRIMA del reset — riscritto contro lo stato vero
Ultimo commit del README: 2026-06-04. Reset v2.0.0: 2026-06-19. Per 75 giorni la prima pagina
del repo ha pubblicato la libreria pre-reset (FADE/HONEST/PAIRS/TSMOM/SHAPE, PORT01-06) con
Sharpe 7,84/10,06 e CAGR ~79% come risultati correnti — cioe' esattamente i numeri che §2
elenca sotto "non citare", dalla libreria che il reset aveva dichiarato artefatto. Meta' dei
file citati non esiste piu' (strategies.yml, portfolios.yml, scripts/waste, scripts/portfolios,
src/live/multi_runner.py), e l'esecuzione descritta era su TESTNET, che e' la causa del reset.

- README riscritto (423 -> 152 righe): cosa gira adesso (TP01+SKH01 75/25, ~$2.050, cadenza
  oraria :47), i numeri nella lente di §2 (TWR +10,6%, non la crescita del conto), la riga che
  ordina il piano, il metodo e i suoi sei requisiti, il dato, la struttura VERIFICATA file per
  file, i comandi, i gate con le loro date, l'obiettivo con la sua onesta'.
- CLAUDE.md §0 e memoria 40: registrato il difetto e la lezione — un reset invalida anche i
  documenti che nessuno rilegge; l'inventario di cosa cita numeri morti va fatto il giorno del
  reset, non 75 giorni dopo per caso.
- Diario 02/09c: sezione col confronto riga per riga; coda dichiarata (l'inventario completo
  degli altri documenti pre-reset non e' stato fatto).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XDJsH3iDSaBns3ccpPBwiu
2026-09-02 15:53:42 +00:00
Adriano Dal Pastro de83909db9 un flag sconosciuto non e' l'azione di default: guardia su 18 script, indice USDE orario, pulizia
MISURATO oggi durante la revisione: `trades_db.py --help` non stampava l'uso, cadeva in sync()
e riscriveva meta.ultimo_sync. Nessuno dei 18 script di scripts/live/ usava argparse: un flag
sbagliato era il ramo else. Su journal.py avrebbe scritto pagina e riga di DB, su analista.py
avrebbe speso una chiamata al modello e mandato un Telegram.

- src/live/cli.valida: prima istruzione di ogni __main__, prima di connect()/sync/rete.
  --help -> 0 con l'uso; flag ignoto, valore mancante o posizionale -> 2 con l'elenco dei
  previsti (P4). NIENTE argparse: cambierebbe messaggi, codici d'uscita e --help di script
  che il cron gia' chiama.
- 18 script cablati (i 3 che scrivono + 14 + cc01), flag invariati.
- tests/test_cli_flag.py (30): elenco DERIVATO dalla cartella (P1), valida come prima
  istruzione, uso che documenta i flag, e i flag che il CRON usa davvero restano accettati
  (P15/P16); end-to-end su --help e flag ignoto con trades.db non toccato (M15).
  Verificato a mano: monitor_health --quiet, trades_db --sync --quiet, book_execute dry-run.

Debito §5.15, primo passo: balance_watch (orario) registra `usde_usdc` a ogni campione — None
con la ragione se illeggibile, mai 1,0. Il cablaggio nel bound quando la serie ha storia.

Pulizia dalla revisione: tests/helpers.carica_script al posto della 15a copia del loader
importlib (5 file del libro live); il fill di prova via upsert_fills invece di un INSERT che
lasciava verified NULL; asserzioni non ancorate al padding; movimenti_capitale accetta le
righe gia' lette (una SELECT invece di due ai due lati di una scrittura del cron).

Test 910 verdi (+52). Diario 2026-09-02c.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XDJsH3iDSaBns3ccpPBwiu
2026-09-02 15:49:40 +00:00
Adriano Dal Pastro 1279ea605a revisione 02/09, seconda tornata: il classificatore dei movimenti era cieco ~23 ore al giorno
Il feed 1h certificato si ferma alle 00:00: per le letture successive `asof` dava la stessa
barra a t0 e t1, mercato "fermo" = 0, e qualunque calo >=10% del giorno sarebbe stato un
"movimento" scorporato come prelievo (giornale dal 25/08, report da oggi). Con feed assente
tutto era "ambiguo" e il report stampava e1/e0-1 sotto l'etichetta TWR.

- journal.movimenti_capitale: stato "mercato non misurabile" (feed fermo/assente/barra
  mancante) => ambiguo con la ragione; leva_tetto = min(frac x n x scala, LEVA_LORDA_MAX) (P1).
- journal.rendimento_twr: salto non classificabile => twr E trading None con motivo.
- journal.pnl_giorno CHIAMA rendimento_twr (prima rifaceva cum - certi: "entrambi la chiamano"
  era falso); la pagina stampa il TWR; analista qualifica il cumulato "di cui versati".
- trades_db --report: la classe di ogni salto con la sua misura (mercato max a leva piena).
- book_execute docstring: banda dello stop -33,5%/-26,5% (era invertita), "non costa ordini"
  -> micro-ordini di ri-taglia (22/48, C2), latenza <=1h solo con feed fresca, conteggi di
  r0823 al posto di "raddoppia" (2 contro 1 e' rotolante vs pavimento sulla riga 4h; la
  peggiore misurata e' 24h).
- test_book_cadenza: LTF_MIN importato da skyhook; tutte le righe attive della crontab (una
  sola); tre stati dello skip (col progetto ma senza cron_book => ROSSO); slot di release
  testato con venue_probe (il :07 come controllo positivo); parser regolare, passo > 0.
- r0823_sl_anchor: guardia sul :47 (era sul vecchio :07), prosa al passato; cron_chain.sh idem.
- CLAUDE.md: §5.7 frase invertita corretta, §5.14 limite vero (non l'artefatto della fixture),
  §2 ora della LETTURA (13:47Z), nuovo debito §5.15 (USDE fuori dal bound di mercato).
- diari: tempi "scritto" corretti coi commit; sezione "Seconda tornata".

Test +11 (858 verdi).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XDJsH3iDSaBns3ccpPBwiu
2026-09-02 15:33:13 +00:00
Adriano Dal Pastro 835e0c8666 revisione 02/09: il rotolante si CONTROLLA ogni ora (non "si ri-ancora"), TWR con limiti dichiarati e tre stati
Quattro segnalazioni della revisione sui commit di oggi, tutte verificate e riparate.

1. book_execute.py (+ CLAUDE.md §5.7, memoria 40): «il disaster-SL rotolante si ri-ancora ogni
   ora» era falso — ensure_disaster_sl lascia il bracket finche' lo stop e' entro il 5% e la
   taglia entro il 10%; il giro orario CONTROLLA, ri-ancora oltre la tolleranza (mark
   +5,263%/-4,762%). Lo stop siede fra -26,3% e -33,3% dal mark corrente.
2. journal.rendimento_twr: limite dichiarato (D5) — l'intervallo che contiene un movimento
   certo esce intero dal rendimento, il suo P&L di mercato va in `certi`; errore massimo meta'
   del movimento per costruzione (25/08: ~$0,5). Non si stima (P12). Segmenti a lunghezza zero
   non prodotti; nessun tempo a mercato -> 0,0 dichiarato numero.
3. base di equity zero: `twr` E `trading` None con motivo (il salto 0->X e' invisibile al
   classificatore, `trading` valeva l'intero conto); il report stampa n/d.
4. test_book_cadenza: la guardia estrae ogni prescrizione «ogni/every N unita'» e pretende 60
   minuti, con controllo positivo (M15); limite P13 dichiarato.

Test +5 (847 verdi). Diari 02/09 e 02/09b con la sezione "Revisione".

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RziUCB336YPUUyDJ29x4Ke
2026-09-02 15:17:05 +00:00
Adriano Dal Pastro 861cc7fc27 debito 7: la cadenza del libro e' ORARIA — docstring riscritto, tre fonti tenute d'accordo da un test
Il docstring di book_execute.py prescriveva «ogni ~230 minuti» mentre il cron gira ogni ora
(47 * * * *). Era il docstring a sbagliare: sulla riga 4h BTC raddoppia gli scatti del
disaster-SL rotolante (r0823_sl_anchor.py), e chi avesse "corretto" il cron verso il
docstring avrebbe spostato il libro sulla riga peggiore.

- scripts/live/book_execute.py: CADENZA: ORARIA, la riga di crontab, la ragione (giro
  idempotente, latenza SKH01 <=1h, ri-ancoraggio orario) e il divieto esplicito con la data.
- tests/test_book_cadenza.py (10): deriva e confronta docstring, intestazione di
  cron_book.sh e crontab installata (crontab -l; SALTATO se illeggibile, non verde);
  parser a 5 campi solo per cadenze regolari; 60 < 230 < 240; minuto != :00.
- docs: CLAUDE.md §5.7 chiuso, §13 conteggio 842; memoria 40; diario 02/09b.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RziUCB336YPUUyDJ29x4Ke
2026-09-02 14:38:53 +00:00
Adriano Dal Pastro 2f701b8469 debito 14: il report stampa il TWR (+10,61%), non il bonifico (+243%)
`trades_db.py --report` calcolava e1/e0-1 sulla serie grezza di equity: il 96,3% del
numero era il versamento di $1.399,39 del 25/08. La riparazione (journal.movimenti_capitale)
esisteva e non aveva attraversato il confine fra i due lettori della stessa serie (P1).

- src/live/journal.py: `rendimento_twr` — funzione unica, spezza la serie sui movimenti
  CERTI e moltiplica i segmenti; gli ambigui restano dentro, dichiarati (P12); tre stati.
- scripts/live/trades_db.py: report() la chiama; stampa TWR con segmenti datati, movimenti
  elencati, trading al netto, delta $ etichettato "movimenti INCLUSI"; il % grezzo sparisce.
- test: +5 in test_journal.py (incl. riproduzione del +10,80% del diario 01/09, M23),
  +2 in test_trades_report.py sul testo stampato con connect() deviato in tmp. 832 verdi.
- docs: CLAUDE.md §5.14 chiuso, §2 e §13 aggiornati; memoria 40; diario 02/09.

Limite ereditato e dichiarato (D5): +10% di trading fra due letture consecutive tocca la
soglia del rilevatore e a mercato fermo verrebbe classificato movimento.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RziUCB336YPUUyDJ29x4Ke
2026-09-02 14:22:50 +00:00
Adriano Dal Pastro bc43218a12 docs: soldi fermi §73 — l'operatore sceglie di versare (piano 27/07: €5k dentro, ~€1k fuori)
Conclusione dell'operatore (02/09): "mettere gli XEON su Deribit col
sistema attivo". Si', ed e' cio' che la memoria aveva scritto il 27/07.
Tre precisazioni, tutte gia' misurate e ora registrate:
- €5.000, non €6.043: la protezione venue satura a qualunque quota > 0,
  l'ultimo migliaio dentro compra ~€100/anno e butta via l'assicurazione;
- il "~€600" e' un'attesa a banda larga, non un tasso (libro a mercato il
  22% dei giorni, TWR +10,8% in 70g, hold-out TP01 ~+0,05, fisco −30%/10a);
- fondo d'emergenza da dichiarare.
Nessuna azione di config (cap dinamico, rilevatore collaudato il 25/08).
In attesa di importo e data; al deposito una riga di giornale, scritta
prima e non ricostruita dopo. N9: le opzioni sicure (+€52-62/anno) sono
state viste e messe da parte con motivo.

- CLAUDE.md §1: riga "soldi fermi" + indice §1-73
- RESULTS §73 + riga d'indice
- memoria 30-piano-capitale-fisco: voce
- diario 2026-09-01d: §7 la conclusione dell'operatore
- docs/journal/2026-09-01.md (voce del cron, non era committata)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 06:06:15 +00:00
Adriano Dal Pastro d95fabf060 soldi fermi: i modi esistono e valgono €5/mese — BOT/conto deposito +€52-62/anno, sUSDe distrugge lo split
On-venue chiuso con l'elenco intero: 4/50 valute remunerate = le 4 gia'
chiuse il 31/08. L'USDC su Deribit non e' fermo (base di sizing + cuscino).
I €6k in XEON sono lo split-cassa. Tassi dal web con fonte (BOT 2,768%
asta 08/2026, DFR 2,25%, conti deposito 3,25-3,50%, sUSDe ~9%). In euro
netti su €6k: XEON 81, BOT 133, conto deposito 143, sUSDe 350 (ma stesso
emittente dell'USDE: split distrutto), libro ~600 (trading, split
distrutto). Miglior guadagno sicuro: +€62/anno. €100/mese di bonifico =
+4,07%/anno di drift. Non sono pareri fiscali. Nessun ordine.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 21:21:14 +00:00
Adriano Dal Pastro 1c0549080c PAVIMENTO-LEVA (§72): il pavimento non licenzia taglia, 0/16 — e corregge §71
Domanda dell'operatore: "possiamo usare quanto conosciamo del pavimento per
studiare una strategia". L'inverso di COLLAR01: non "quanto DD mi risparmia
il pavimento a taglia fissa" (perdeva contro il de-levering) ma "quanta
TAGLIA mi autorizza a DD fisso" — l'unica cosa che il de-levering non puo'
comprare (cambia la FORMA, non la proporzione), e la grandezza su cui vive
il tetto di leva del progetto (n·frac·scala·sl <= 0,50 => 1,67x).

RISULTATO: 0/16. La sola put a premio reale PEGGIORA il maxDD in 16/16 celle
(FORTE 51,83% -> 55,4-75,8%; LARGO 71,94% -> 74,8-81,7%), quindi k_f = 1,000
ovunque: niente da licenziare. Monotono nella protezione: piu' la put e'
vicina, peggio va — il bleed del premio E' il drawdown.

🚨 CORREGGE §71 (stesso giorno). Stamattina A1 diceva "il pavimento funziona
davvero". E' il COLLAR a ridurre il DD, non il pavimento: a premio zero la
put lo riduce (51,83 -> 36,76, il controllo), a premio reale lo peggiora =>
tutto il beneficio e' mangiato dal premio, e oltre. Nel collar la riduzione
viene dal TETTO: il suo premio compensa il bleed, e cappare l'upside abbassa
il picco da cui il DD si misura — un picco piu' basso, non protezione.
Premio di pareggio: 36-79% del reale; il DVOL sta 1,32x sopra la RV, quindi
un prezzo equo (~76%) sfiora il pareggio nelle celle migliori e lo manca
nelle altre. Il motivo di §46 (beta) non si applica — a beta 1,0 la put
paga davvero — ma il verdetto di §46, "il maxDD SALE", si riproduce per un
motivo diverso. Corretti il diario di §71 (titolo e A1), RESULTS, memoria.

IL MECCANISMO (B2), trasferibile: i drawdown di BTC sono GRIND. maxDD FORTE
2021-07-20 -> 2022-05-06 = 290 giorni; LARGO fino al 2023-10-16 = 818. Una
put a 7-14g copre UNA finestra; il DD che conta dura 20-60 finestre; la put
scade OTM ogni settimana (para nel 0-9% dei cicli) mentre il premio sanguina.
Non protegge nemmeno la finestra peggiore: 14g FORTE -22,9% nudo -> -23,6%
col pavimento. Su BTC il pavimento compra protezione contro la cosa sbagliata.

LA LICENZA DEL DISASTER-SL E' DI CARTA (B3), scritto prima che sia comodo:
con la put a 5d/14g a 21,9% dallo spot, sostituire sl con quella distanza
darebbe 2,28x. Ma l'invariante limita UN episodio, la put limita UNA
finestra, e il massimo su finestre consecutive non e' limitato da nulla:
1 finestra -23,6% (coperta), 8 finestre -33,6% (non coperte) => a 2,28x un
grind di 112 giorni costerebbe il 77% dell'equity. disaster_sl_pct NON si
sostituisce con la distanza di un pavimento. Aggiunto a CLAUDE.md §3.

GATED SULL'IV (B4) — la copertura dinamica che §46 non aveva provato: put
ON solo sotto il 25°/50° pctl di DVOL/RV. Migliora molto (FORTE +3,8% ->
+8,4%) ma resta sotto la base (+10,04%) e il DD resta >=. L'incollatura
ignora lo spread delle transizioni A FAVORE del gated: perde a maggior ragione.

Quattro attese a priori (B1-B4) scritte prima, tutte confermate.

NON MISURATO, dichiarato: nessun DSR (cade al primo gate); nessuna lente
reale; nessun pavimento a scadenza lunga (30-90g, che coprirebbe piu'
finestre di grind) perche' l'operatore ha vincolato a <=15 giorni — e'
l'unica variante che B2 lascia aperta, e sta fuori dal vincolo.

Libro, pesi, cron, config INVARIATI. Nessun ordine. Test 25/25 sui filoni.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 20:45:02 +00:00
Adriano Dal Pastro ec8478308f GATE SCALA-01: la chiave di scala esiste, ed e' INERTE (SPEC §8 punti 1-4)
Chiude il debito che CLAUDE.md dichiarava da settimane: la regola "ogni cambio
di scala passa dal cap di config, non da target_vol" NON era implementabile
perche' una chiave di scala non esisteva — qualunque cambio sarebbe finito su
WEIGHT/W_TP01/W_SKH, cioe' codice su un percorso con soldi veri e per giunta
nel posto sbagliato (W_TP01/W_SKH sono il RAPPORTO 75/25, non la taglia).

Specifica gia' scritta in docs/research/SPEC-scale-key.md (618 righe, 9
condizioni di gate, prototipo). Non ho progettato: ho eseguito i punti 1-4.

🚨 config/live.json NON E' STATO TOCCATO. La chiave e' assente, vale 1,00, e
T7 dimostra bit-exact che il libro e' quello di ieri (max|diff| = 0.0).
Verificato anche a runtime: book_execute in dry-run da' gli stessi target del
cron delle 15:47 (BTC $+355, ETH $+214).

LE QUATTRO DECISIONI CHE NON SONO DI COMODO
- La scala si applica DOPO il clamp. Prima, il cap se la mangerebbe proprio
  nei giorni di massima convinzione (a tp=1/sg=+1 il grezzo vale esattamente
  cap => k_eff tornerebbe a 1,00 a ogni k): sarebbe un cambio di FORMA
  travestito da cambio di taglia, e la curva g(k) con cui il gradino viene
  autorizzato non descriverebbe quel libro. Prezzo dichiarato: il cap diventa
  il tetto del libro UNITARIO, e la guardia sulla leva lorda va ricostruita.
- Il tetto e' sul PRODOTTO e sta nel CODICE. Sulla sola chiave lascerebbe
  aperta la porta accanto (frac 0,625 x scala 1,25 = 1,562x); in config
  sarebbe un lucchetto con la chiave attaccata. LEVA_LORDA_MAX 1,25 in
  src/live/book.py => il gradino a 1,50 richiede codice, quindi review.
- Fuori scaletta o fuori tetto = STOP, non clamp. book_execute si ferma, non
  invia, allerta (ScalaNonAutorizzata). E SCALA_LADDER (1,00 · 1,25) rende
  INESPRIMIBILE "solo un po'": 1,05 non e' prudente, e' fuori scaletta.
- La scala vive solo sul percorso fidato (equity illeggibile => 1,00), cosi'
  "il fallback non e' piu' permissivo" e' vero per costruzione. Ma la
  VALIDAZIONE avviene sempre: una config rotta non si nasconde dietro un giro
  in cui l'equity non era leggibile.

LA GUARDIA CHE MORDE PER PRIMA non e' il peggior giorno (k <= 3,49x) ma il
COSTO di un disaster-SL (k <= 1,67x, 2,1x piu' stringente): l'invariante
n_asset x frac x scala x disaster_sl_pct <= 0,50 scatta anche se qualcuno
allarga lo stop invece di alzare la scala.

TEST T1-T11 (tests/test_book_scale.py, 18 verdi). Il piu' importante e' T1b:
a k=1 l'implementazione simmetrica e quella asimmetrica danno lo STESSO
numero, quindi un test di simmetria scritto sul caso di default ha potenza
ZERO. T1b verifica che le due coincidano a k=1 (il rischio e' reale) e che
fuori da k=1 l'asserzione le SEPARI, con un'implementazione asimmetrica
scritta nel test apposta perche' fallisca.

SORVEGLIANTE scale_watch (cron_daily, 3 domande / 3 azioni / 3 stati, una
allerta per streak, marcatore scritto solo dopo invio riuscito — debito #2).
Riporta la frequenza del ramo di fallback, che sopra il 2% in 90 giorni
invaliderebbe la regola: misurata 0/1.676, coi 19 giri "paper capital"
(pre-finanziamento, dove il libro non invia) contati e dichiarati a parte.
Non puo' impedire la modifica: la rende visibile entro 24h e attribuibile.

CHIUDE il debito #5 di §5: T2/T3 sostituiscono il vecchio
test_leva_massima_da_config (che misurava frac x n_asset mentre la grandezza
vera e' frac x n_asset x scala), e T11 verifica che sia rimasto cancellato.

NON FATTO, deliberato: la chiave in config (punto 2 lo vieta), GATE SCALA-01
(A2 richiede >=30 giorni a 1,00 col sorvegliante attivo — "l'unico modo di
scoprire che il sorvegliante e' rotto mentre la leva e' ancora 1,00"),
r0726_fee_sensitivity rifatto (A7: serve solo al gradino; a 1,25x una
liquidazione costerebbe 1,25% non 1,00%, e ereditarlo sarebbe l'errore).

Nessun ordine. Suite: 825 passati.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 17:44:02 +00:00
Adriano Dal Pastro 05816c49f9 COLLAR01 (§71): il pavimento funziona, il tetto lo paga troppo — e cio' che vince e' VRP01
Chiesto dall'operatore in quattro battute: hold BTC long o short coperto in
opzioni, scadenza <=15gg, "ridurre la vincita ma bloccare la perdita" (=>
collar, non put protettiva), entrata gated da indicatori ("forte bull"), e
uscita dalle opzioni fra il 50% e il 75% del tempo.

PERCHE' SI POTEVA RIAPRIRE DOPO §46. §46 (tail-hedge) fu refutato sul beta
+0,076 del libro — "non si assicura un libro che nei crash e' gia' quasi
piatto". Qui il sottostante e' un hold di BTC, beta 1,0: quel motivo non si
applica. E §46 dichiarava non provata proprio la copertura gated su regime.

L'entrata non aggiunge un solo parametro: tsmom_blend media tre np.sign() su
(30,90,180) => valori in {-1,-1/3,+1/3,+1}, quindi "forte bull" = |blend|==1
(52,5% dei giorni) contro il confronto dichiarato |blend|>=1/3 (97,1%).

RISULTATI (lente lunga 2021-03 -> 2026-09, 5,44 anni; griglia 48 celle
dichiarata prima + 36 di estensione dichiarata):
- A1 CONFERMATA: il pavimento FUNZIONA, maxDD scende in 36/48 (§46: saliva
  in 162/162). La meta' della domanda ha risposta positiva.
- A3 CONFERMATA: il de-levering lo fa meglio in 45/48. Δdrift/ΔmaxDD 7,90
  (gate forte) / 1,98 (largo): 2-8 punti di drift per punto di DD.
- C9 in forma pura: il tetto taglia il 46,2% dei cicli VINCENTI, il pavimento
  para il 6,7% dei PERDENTI — 7x piu' spesso sui vincenti: troncatura.
- M1: collar Sharpe 0,508 vs TP01 0,852; TP01+10% => +0,000 di Sharpe e
  +2,32pp di maxDD.

IL FATTO CHE VALE PIU' DEL VERDETTO. Le 3 celle vincenti stavano tutte sul
BORDO; estesa la famiglia vince 35/36 nell'ANGOLO (dput 0,02 / dcall 0,50,
Sharpe 1,471) — e il limite di quell'angolo e' una COVERED CALL: la pendenza
porta fuori dalla domanda posta e dentro lo short-vol. E quel 1,471 e' il
prezzatore che si paga da solo: DVOL/RV-forward 1,320 a 7g (sopra nel 76,9%
dei giorni) => riprezzato alla vol vera l'angolo cade a 0,511, che e' VRP01
(0,47). Non una scoperta: VRP01 per una strada piu' lunga. §3 lo blocca.

USCITA ANTICIPATA: implementata (exit_frac) e COSTA. Cella onesta gate forte:
drift +9,48% (scadenza) -> +3,84% (50%), esito da VINCE a perde sotto 0,75.
Il meccanismo previsto c'e' (VRP residuo +3,38 -> +1,27pp) ma lo spread lo
travolge. Corregge l'applicazione di §46: "un roll anticipato non paga f"
vale per una copertura solo LONG; in un collar la gamba venduta va
RICOMPRATA, quindi si paga f sulla parte che a scadenza si regolava gratis.
L'asimmetria si INVERTE quando la struttura ha una gamba corta.

CONTROLLI DELL'APPARATO 3/3 (M15): pranzo gratis riconosciuto (maxDD
51,83%->36,76%, drift +10,04%->+35,69%), premio x10 rifiutato, zero-cost
finito. Cinque difetti miei catturati dai controlli, non a occhio: bisezione
zero-cost invertita (dava Sharpe -3,9), dcall=NaN nella cassa, C9 non
consapevole della direzione (S1>S0 non e' "vincente" per uno short), e due di
contabilita' che avrebbero ADULATO il collar (base che rollava lo spot
pagando ~3,6%/a di fee inesistenti; roll che chiudeva lo spot senza motivo).

Corregge anche un muro di §46: il tick da 5 USDC e' della famiglia USDC; la
catena che raccogliamo e' 100% inverse, quindi li' non si applica.

Regole nuove in CLAUDE.md: M29 (un edge da opzioni prezzate a modello si
riprezza alla vol REALIZZATA prima di crederci), M8 esteso (un argmax sul
BORDO e' una pendenza, non una cella), C4 esteso (il segno dell'asimmetria di
f dipende dal verso della gamba).

Libro, pesi, cron, config INVARIATI. Nessun ordine. Suite: 807 passati.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 17:30:18 +00:00
Adriano Dal Pastro 5bcf212923 stato trades: il "+243%" del report e' per il 96,3% un bonifico — TWR +10,80%
`trades_db.py --report` stampa `$598,06 -> $2.051,84 (+243,08%)` accanto a
`netto +40,77`. La serie di 1.657 letture orarie contiene UN SOLO salto: il
versamento di $1.399,39 del 25/08 11:47Z. Confermato dal classificatore
ufficiale del progetto (`journal.movimenti_capitale`: certi 1399.39,
ambigui 0.0, classe `movimento`).

Al netto: TWR +10,80% spezzato sul versamento (+11,61% fino al 25/08,
-0,73% dopo), equity +$54,39 in 69 giorni. Il giornale, che lo scorporo lo
fa gia', concorda: $+62,00 al 31/08, -7,61 di marcatura fino a oggi.

Il difetto non e' nuovo — e' la stessa riparazione che NON ha attraversato
il confine. `journal.py:121` porta il caso d'origine nel docstring (la voce
del 25/08 che dichiarava «+$1.414,57» di giornata); `trades_db.py:83`
calcola `100*(e1/e0-1)` sulla serie grezza e non chiama
`movimenti_capitale()`, che e' a un import di distanza. Variante di P1: non
un sorvegliante che ridichiara il bersaglio, ma una riparazione che non si
e' propagata al secondo lettore della stessa serie. Danno sui soldi nessuno
(sola lettura); danno di citazione si', ed e' l'unico numero fuorviante che
il progetto produce su richiesta di un comando pubblicato in §13.

Codice NON toccato: sta su uno script che legge il libro vivo. Debito #14.

Stato del libro al 01/09 16:47Z, per il resto invariato: 45 fill (ultimo
31/08 15:47), 29 round-trip (21 in utile, netto +40,77), posizioni BTC
0,0045 @ $79.208,11 e ETH 0,0872 @ $2.473,04, non realizzato -$11,72, leva
lorda 0,27x. Nessun fill da 25 ore = banda morta del min_order_usd $5, non
un blocco (il cron logga «gia' al target» a ogni giro). monitor_health 8/8
OK, riconcilio 44/45 con 0 prezzi divergenti (l'unica coppia scoperta e' il
difetto noto dei sei giorni fra log e jsonl sullo stesso fill).

- CLAUDE.md §2: riga nella tabella dei numeri da non citare
- CLAUDE.md §5: debito #14
- docs/diary/2026-09-01-stato-trades.md
- docs/journal/2026-08-31.md (voce del cron, non era committata)

Suite: 800 passati, 0 falliti.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 16:52:48 +00:00
Adriano Dal Pastro 8d391ed29a margini: la pagina si ricostruisce dal gateway — e l'"IM %" non e' il margine delle posizioni
L'operatore ha incollato la riga CROSS col modello ATTIVO invece che proiettato:
Available $2.010,81 · IM 2,15% · MM 0,37%. Il gateway, letto allo stesso minuto, dice
available_funds = 2010,82299383. Scarto $0,01.

(a) LO SCREENSHOT NON SERVE PIU'. La riga CROSS della pagina E' `available_funds`, che
leggiamo da soli a ogni giro. Ieri quella schermata era l'UNICA fonte per l'haircut e per il
modello attivo, e per averla e' servito un passaggio dall'operatore e da Wasabi.
r0831_margini_conto.live() la ricostruisce.

(b) TERZA CONFERMA DEL 5%, E LA PRIMA ESATTA:
    1.400,71 + 0,95x654,176 + 0,20 dust - 11,553 IM = $2.010,82   contro pagina $2.010,81
Scarto $0,007. Il 10% della vecchia config non e' improbabile: invertendo la stessa riga da'
IM = -$21,16, aritmeticamente impossibile. Tre strade indipendenti, stesso numero.

(c) MIA LETTURA SBAGLIATA, CORRETTA. L'"IM %" della pagina e'
(margin_balance - available)/margin_balance, non il margine delle posizioni:
    haircut USDE  $32,71 = 1,592%   +   IM vera  $11,55 = 0,562%   =  2,153%
Il 74% di quella percentuale e' HAIRCUT. E' salita da 2,13% a 2,15% perche' abbiamo comprato
collaterale a rendimento — letta come rischio direbbe che ieri sera abbiamo alzato la leva
comprando USDE, l'opposto di quello che e' successo. La MM invece e' pulita ($7,60) e non puo'
contenere l'haircut, che da solo la renderebbe negativa: le due colonne hanno basi DIVERSE e la
pagina non lo dice. Regola: una percentuale letta da una schermata va INVERTITA nella sua
definizione prima di essere confrontata (P7 su superficie nuova).

(d) LA BOCCIATURA DI X:PM ESCE RAFFORZATA. La KB da' USDe al 5% sotto entrambi i modelli:
l'haircut si cancella e l'inversa da' il margine di POSIZIONE sulle stesse due posizioni,
stesso istante — X:SM $11,64 contro X:PM $88,23, 7,6x, e 9,3x la MM. Due misure pulite,
stesso verso. E la riga X:SM era una PROIEZIONE di un modello inattivo che oggi si riproduce
al centesimo: anche la riga X:PM era affidabile, quindi decidere senza provare era corretto.
Ora si sa perche', non solo che.

Suite: 800 passati.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BmkQty4a99pVmvwGfxAMQ9
2026-08-31 08:39:45 +00:00
Adriano Dal Pastro 62025e7840 docs: X:SM ri-sondato e X:PM scartato — il modello di margine entra nel tetto ma non lo spiega
Le due misure di ieri sera vivevano solo in chat. Ora sono nei documenti, con i numeri
e con cio' che le riapre.

X:SM vs S:SM, stessa notte, stesso conto:
  S:SM  tetto [643,18 - 644,18)  equity $2.055,56  ->  31,29% - 31,34%
  X:SM  tetto [654,18 - 655,18)  equity $2.054,90  ->  31,84% - 31,88%
+0,55pp = +11 USDE ~ $11. L'ipotesi "il tetto dipende dal modello di margine" e' VERA e
INUTILE: per arrivare al 70% mancano ~38 punti, un fattore 2,2x. Un'ipotesi confermata al
terzo decimale e falsa all'ordine di grandezza si archivia come falsa — il tetto resta
misurato e non spiegato.

La lezione di metodo vale piu' del risultato: il primo probe fu un BUY 20, RIFIUTATO, e da
solo avrebbe chiuso la questione con "tetto invariato". Falso: il tetto si era mosso di 11,
cioe' MENO della taglia del probe. Un probe unico di taglia sbagliata produce un falso
negativo che si legge come risultato — la taglia si sceglie sulla risoluzione dell'effetto
che si cerca (M13 in veste nuova).

X:PM valutato e SCARTATO senza provarlo: la pagina margini lo dice gia'. Available Balance
$1.935,14 contro $2.011,73 (-$76,59), MM 3,43% contro 0,37% (~9x). Il PM e' basato su
scenari e premia il rischio che si compensa; due long direzionali nudi sono il suo caso
peggiore. La MM e' la riga che decide: non morde a 0,28x ma sposta il punto di liquidazione.
NON "PM e' peggio" ma "PM e' peggio PER QUESTO portafoglio" — cosa lo riapre: il deploy di
VRP01 o di un altro sleeve di opzioni, che fa compensare il rischio e cambia il segno del
confronto.

Registrato anche il prezzo del cross, che non e' zero: sotto segregato l'USDE era ring-fenced
dalle perdite del libro, sotto cross risponde l'intero conto. Non morde oggi (perdita massima
plausibile ~$617, dentro il solo USDC), ma il verso e' cambiato e tornare a S:SM lo ri-recinta.

Suite: 800 passati.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BmkQty4a99pVmvwGfxAMQ9
2026-08-31 08:30:51 +00:00
Adriano Dal Pastro 4705d71ceb tetto ri-sondato sotto X:SM: il modello entra ma non spiega — +11 USDE
L'operatore e' passato a Cross: Standard Margin. Verificato dal gateway:
available_funds USDC da ~$1.400 a $2.011,09 contro $2.010,90 attesi con haircut
5% (scarto $0,19) — seconda conferma indipendente del 5%, da una lettura
completamente diversa dallo screenshot.

RI-SONDATO il tetto a saldo neutro, stesso metodo del 31/08:
  SEGREGATO S:SM  [643,18 - 644,18)  equity $2.055,56  = 31,29-31,34%
  CROSS     X:SM  [654,18 - 655,18)  equity $2.054,90  = 31,84-31,88%
Il modello ENTRA nel tetto ma NON lo spiega: +0,55pp = +11 USDE (~$11), non le
centinaia che servirebbero per il 70% (manca ancora un fattore ~2,2x).
L'ipotesi "il tetto dipende dal modello segregato" e' quantitativamente demolita
come spiegazione, pur essendo tecnicamente non nulla.

Nota di metodo: il primo probe (BUY 20) era stato rifiutato e avrebbe fatto
concludere "tetto invariato". Era solo troppo grosso: il test a saldo neutro con
passi piccoli ha mostrato che il tetto si era mosso di 11. Un probe unico di
taglia sbagliata produce un falso negativo che si legge come risultato.

config: venue_cap_frac 0.312 -> 0.318 (bordo basso del bracket CROSS, che e' il
modello attivo; se si torna a S:SM va rimesso a 0.312).

E il costo del passaggio, che va detto: sotto SEGREGATO l'USDE era RING-FENCED
dalle perdite del libro (solo il silo USDC rispondeva); sotto CROSS risponde
l'intero conto. Non morde oggi (perdita massima plausibile ~$617 col disaster-SL,
dentro il solo USDC) ma il rischio strutturale ha cambiato verso. Il cross ha
comprato $611 di margine utilizzabile — inutile a 0,28x, serve sopra ~1,4x che
non e' autorizzata — piu' $11 di capienza.

Suite: 800 passati.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 08:12:31 +00:00
Adriano Dal Pastro fd7595e819 margini: haircut 5% (la config aveva torto) — e il conto NON e' cross-collateral
Lo screenshot della pagina margini era su Wasabi (rclone remote wasabi:, bucket
adp-work, cartella _scambio), non sulla VPS: per questo il percorso non esisteva.
Riconciliazione riproducibile in scripts/research/r0831_margini_conto.py (N11).

(a) HAIRCUT = 5%. La pagina non lo espone come numero: si ricava per differenza,
perche' il CROSS conta l'USDE scontato e il SEGREGATO non lo conta affatto.
  S:SM (attivo)  USDC Available 1.400,86  vs  equity 1.412,28 - IM 11,55 = 1.400,73
  X:SM           CROSS Available 2.011,73
  contributo USDE al cross = $610,81 su $643,05  ->  5,0137%
    5% (KB)     atteso $610,89  scarto $ 0,09  TORNA
   10% (nostra) atteso $578,74  scarto $32,06  NON torna
config/live.json: haircut 0.10 -> 0.05. Il 26/08 il 10% fu registrato come
"verificato sul venue" senza traccia di COME, e non lo era.

(b) 🚨 IL CONTO NON E' CROSS-COLLATERAL: modello attivo "Segregated: Standard
Margin" (S:SM). Nella tabella del modello attivo l'USDE NON COMPARE: non fa
margine per i perp USDC-settled del book, e l'haircut oggi non si applica. Il che
spiega a posteriori perche' la misura di stamattina trovava $0,0000 accantonati:
non stavo guardando nel secchio sbagliato, cercavo un parametro che sul nostro
conto non e' in vigore.

NON MORDE: al massimo lordo del libro (1,0x = ~$2.056 di nozionale) l'IM sarebbe
~$41 contro $1.400 di USDC disponibile, 34x di copertura. Nessuna decisione
operativa cambia oggi.

Ma la premessa in CLAUDE.md era falsa, e il modo in cui lo era e' istruttivo:
"l'equity del book e' il TOTALE cross-collateral" metteva due cose sotto un nome
solo. Come RICCHEZZA sommare USDC+USDE e' giusto ed era il punto della riparazione
del 26/08 (evito' il falso "USCITA DI FONDI -24%"); come CAPACITA' DI MARGINE e'
sbagliato, perche' nel modello attivo l'USDE vale zero. Finche' il margine non
morde le due coincidono nell'uso, e infatti non era mai emerso.

Corretta anche la riga di usde_watch che stampava "margine utilizzabile ~$2.013
(haircut 10%)": era falsa due volte insieme. Ora stampa il solo silo USDC e,
accanto, cosa darebbe il cross.

La quota USDE non e' "collaterale diversificato": e' cassa a rendimento FUORI dal
sistema di margine. Passare a X:SM aggiungerebbe $610,87 di margine utilizzabile
ma porta la meccanica cross (collateral fee 0,05%/giorno sul saldo negativo,
ribilanciamento automatico): decisione dell'operatore, oggi non serve.

Ipotesi nuova e non verificata: il tetto del ~31,2% potrebbe dipendere proprio dal
modello segregato. Si saprebbe passando a X:SM e ri-sondando.

Suite: 800 passati.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 08:01:21 +00:00
Adriano Dal Pastro c8be83ef6e haircut: la divergenza non si chiude dal gateway — misurato perche', non supposto
Richiesta: leggere la pagina margini del conto per chiudere il 5% (KB) vs 10%
(nostra config). NON E' RAGGIUNGIBILE da qui: account_summary per la valuta USD
da' "Invalid currency", il parametro `extended` viene ignorato, e il gateway
filtra a 8 campi senza initial_margin/maintenance_margin (debito #11).

E non e' che si sia guardato nel secchio sbagliato: la contabilita' TORNA ESATTA
senza alcun termine di haircut.
  USDC equity 1412.281074  available 1400.728093  riservato 11.5530
  USDE equity  643.175691  available  643.175691  riservato  0.0000
  posizioni lorde $577.65 -> IM al 2% = $11.5530 -> RESIDUO -$0.0000
Un haircut al 5% chiederebbe $32,16 accantonati, al 10% $64,32: non ci sono.
=> L'haircut non e' osservabile in ALCUN campo esposto. O e' applicato solo nella
vista cross/USD che il gateway filtra, o non e' applicato al nostro conto: da qui
le due cose non si distinguono, e non le si sceglie tirando a indovinare.

La divergenza resta APERTA, ma con la ragione MISURATA invece che supposta. La
chiudono 30 secondi sulla web UI o delle chiavi API Deribit (la decisione gia'
dichiarata nel debito #11, non un refactor). Nel frattempo non morde nulla di
osservabile: l'IM e' il 2% del nozionale e l'USDE resta interamente disponibile,
quindi 5% o 10% non cambia una cifra operativa.

L'operatore ha dichiarato che il conto e' STANDARD MARGIN. Aggancia due cose:
  - la colonna da leggere e' Haircut (X:SM), che per USDe dice 5% come la PM:
    ora si sa con certezza quale numero ufficiale contraddice il nostro;
  - spiega perche' l'IM misurata e' esattamente 1/50: il nuovo modello di margine
    del 05/08 si applica ai "standard margin accounts", tier 1 C1=50. Un fatto
    dichiarato dall'operatore e una misura fatta senza conoscerlo si confermano
    a vicenda — ed e' la PRIMA affermazione del venue che il conto conferma questa
    settimana, dopo l'APR USDC e "all users can buy BUIDL".

Lo screenshot non e' arrivato: il percorso sta sul desktop dell'operatore, non
sulla VPS.

Suite: 800 passati.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 07:53:26 +00:00
Adriano Dal Pastro 30286ea033 venue_news: sorvegliare cosa il venue ANNUNCIA — e due correzioni dalla KB
Nasce dall'annuncio sulla fine della Proof of Reserves. La notizia in se' vale
poco: si perde un segnale ad alta frequenza e DEBOLE (una PoR auto-pubblicata
prova che gli attivi esistono in un istante, non che coprano le passivita') e si
guadagna un audit annuale indipendente sotto VARA piu' il 90% degli attivi presso
Coinbase come custode. E non l'abbiamo mai sorvegliata: zero occorrenze nel
codice. Rinunciato di proposito a catturare l'ultimo dato — uno snapshot singolo
auto-riportato non regge lo standard di prova e non permetterebbe di stimare `p`.

CONSEGUENZA VERA: la decisione "100% Deribit fino a $20k" ha fra i suoi riapritori
"`p` che diventa stimabile invece che assunto". Dal 1/9 il segnale pubblico passa
da quotidiano ad ANNUALE: da una serie si stima, da un punto all'anno no. Quel
riapritore diventa praticamente irraggiungibile => la decisione e' ora gated SOLO
dal capitale.

TROVATO CERCANDO: esiste un feed RSS degli exchange-update, e conteneva quattro
annunci materiali che nessuno leggeva. Il caso che decide: le specifiche dei perp
USDC sono cambiate il 18/08, ANNUNCIATE IL 14/08. check_specs() e' nato da quella
svista e gira ogni ora, ma rileva la deriva DOPO; il feed l'avrebbe detta quattro
giorni PRIMA. La prima volta e' andata bene solo perche' i cambi erano RIDUZIONI.

Costruito scripts/live/venue_news.py (in cron_daily accanto a fee_watch):
  - NON interpreta: dice "e' uscito questo, guardalo". Nessun automatismo su un
    testo di marketing (P13). Classifica solo l'urgenza.
  - parole-chiave DERIVATE da deribit._CONTRACT e config/live.json, non
    ridichiarate (P1): chi aggiunge un asset allarga la sorveglianza da solo, ed
    e' il test che lo blinda.
  - primo giro semina senza allertare (P9); feed illeggibile -> exit 2, mai
    silenzio implicito (P5).
Debito #8 si RESTRINGE, non si chiude: il Rulebook non ha un feed.

Confermato due volte lo zero USDC: l'espansione del 31/07 non contiene l'Italia
ne' alcun paese UE, mentre San Marino e Citta' del Vaticano SONO idonei. Non
asserisco una causa (ci sono anche Canada e Giappone).

DUE CORREZIONI dalla KB "Cross collateral specifications":
(a) Il saldo negativo costa una collateral fee dello 0,05% AL GIORNO = 18,25%/anno,
    al secondo — 4,3x la resa USDE. Il 30/08 avevo scritto "lo finanzia a
    interesse" SENZA il numero: giusto e vuoto. Col numero, il criterio del
    cuscino di regolamento diventa aritmetica (-14 punti). E il ribilanciamento
    automatico non salva: scatta a $1M assoluti o al 100% della cross equity,
    irraggiungibili a $2k. Non veniamo ribilanciati: sanguiniamo la fee.
(b) L'haircut USDe ufficiale e' 5%, noi abbiamo 0.10 registrato come "verificato
    sul venue" il 26/08 senza traccia di come. NON riparato (P12/M28): si tiene
    0.10 perche' e' il lato conservativo, non e' sul percorso soldi e non morde
    fino al 90% di quota. Divergenza dichiarata in config.
Di lato: BUIDL haircut 2% (sarebbe stato il miglior collaterale del listino, se
si potesse comprare), stETH 7,5% (terza ragione indipendente per lasciarlo stare).
Nessuna fonte documenta un tetto sulle QUANTITA': il ~31,2% resta misurato e non
spiegato — e ora si sa che non e' una svista di lettura.

Suite: 800 passati (5 nuovi su venue_news).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 07:47:15 +00:00
Adriano Dal Pastro 41f83b5dd8 BUIDL-01 CHIUSO: non entrabile — e la frase pubblicata da Deribit e' falsa per noi
Esito del test pre-registrato dieci minuti prima (7b8e356). Si chiude con un esito
che NON era fra i due previsti: i criteri contemplavano ">=1 reward -> IDONEO" e
"zero reward -> NON IDONEO", ma non si riesce a comprare BUIDL affatto, quindi la
domanda sull'eligibilita' ai reward e' priva di oggetto. Un gate puo' fallire sulla
sua PRECONDIZIONE invece che sul suo criterio, e va scritto cosi'.

Undici ordini rifiutati con not_enough_funds_in_currency, fino a 1 unita' a limite
1,0100 contro ask 1,0004 con 10.730 di profondita', tenendo ZERO BUIDL e avendo
$1.412 disponibili per comprarne $1. Esclusi uno per uno tutti i sospetti che
sull'USDE avevano portato fuori strada: prezzo (incrocia di 96 bps), liquidita',
taglia (il minimo dello strumento), fondi, tetto sul livello (teniamo zero), wallet
(account_summary risponde). Costo del test: $0, nessun fill.

🚨 "All Deribit users are permitted to buy and sell BUIDL tokens in the spot markets
on Deribit, with no extra requirements" — insights.deribit.com. NON per noi.
Motivo plausibile e non verificato: BUIDL e' un titolo (fondo BlackRock via
Securitize), distribuzione ristretta a monte dell'exchange. Il fatto misurato e' il
rifiuto, non il suo motivo.

REGOLA (due affermazioni pubblicate smentite dal conto in due giorni — APR USDC
3,40% e "all users can buy BUIDL"): una capacita' PUBBLICATA dal venue non e' una
capacita' del CONTO. Si verifica sul conto, prima che entri in un piano, e costa un
ordine da $1. E' N10 un passo piu' in la': non basta il sito, non basta il venue,
serve il conto.

SOTTOPRODOTTO che vale piu' del test: not_enough_funds_in_currency e' il messaggio
GENERICO di Deribit per "non puoi acquisire altra di questa valuta" — permesso
(BUIDL, zero in mano) o tetto (USDE, ~31,2% dell'equity), mai i fondi. Chi lo
incontra salti subito alla domanda giusta invece di inseguire taglia, cadenza e
prezzo come e' successo il 30/08.

=> L'USDE resta l'unico collaterale a rendimento che il conto puo' usare, ed e' al
suo tetto; i $1.412 di USDC continuano a rendere zero. Non per mancanza di
alternative: le tre alternative sono chiuse per giurisdizione (USDC), per permesso
(BUIDL) e perche' non e' un dollaro (stETH).

balance_watch esteso a BUIDL: se l'accesso si aprisse, un balance non nullo lo
direbbe da solo. Suite: 795 passati.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 07:35:32 +00:00
Adriano Dal Pastro 7b8e35633d BUIDL-01: criteri pre-registrati PRIMA dell'esecuzione e dell'esito
Test di eligibilita' sul collaterale BlackRock (Treasury USA tokenizzati), taglia
500 BUIDL come il test USDE del 26/08. Nasce dalla domanda su stETH: stETH rende
2,22% (meta' dell'USDE) ed e' ETH, non un dollaro — coprirlo e' CC01, gia'
parcheggiato a scala 20k+. I custodi sono istituzionali e comunque nessuna via
custodiale restituisce i reward USDC (esclusione italiana per giurisdizione).

BUIDL invece e' comprabile, liquido, in cross-collateral, e soprattutto e' un
emittente DIVERSO: oggi il conto ha $1.412 di USDC che rendono zero e il rischio
emittente concentrato al 100% su Ethena.

Criteri dichiarati adesso, esito da leggere il 2026-09-04:
  ELIGIBILITA': >=1 reward entro le 23:00 UTC del 03/09 -> IDONEO; zero -> NON
  IDONEO, si riconverte e la pista si chiude. Tre finestre feriali piene.
  TETTO: sondato a saldo neutro, registrato col suo bracket E l'equity del
  momento (sull'USDE il tetto e' una frazione, non un livello).
  HAIRCUT: dichiarato NON misurabile a questa taglia, e non vincolante.

Committato PRIMA dell'esecuzione: l'ordine dei fatti sta in git.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 07:30:40 +00:00
Adriano Dal Pastro f151316bdf usde: il tetto e' una FRAZIONE dell'equity (~31,2%), non un livello — e ora e' in config
Fonte: articolo ufficiale Deribit "Yield/reward bearing coins" (WebFetch lo prende
403, si legge dall'API Help Center in JSON). Tre correzioni alle conclusioni di
ieri.

1) L'ITALIA e' nella lista delle giurisdizioni escluse dai reward USDC. Lo zero
misurato e' confermato dalla fonte, ma la mia ipotesi MiCA era SBAGLIATA: la
lista contiene Canada e Giappone, non e' il perimetro MiCA. E' policy di
giurisdizione Deribit. M27: una fonte normativa si verifica, non si deduce.

2) La finestra di pagamento USDC e' di DUE SETTIMANE ("within the first two
weeks of the following month"), non tre giorni. Avevo verificato su 8/14 e 3/14
giorni. Rifatto sulle finestre vere: LUGLIO conclusivo (atteso $1,72 contro
un'escursione TOTALE dell'equity di $0,48 in 1-16/08, zero scalini compatibili),
GIUGNO no (il libro opera da meta' mese, rumore della taglia del segnale). La
conclusione non cambia, ma l'evidenza e' UNA finestra piu' la lista ufficiale,
non due: il "172x" del 30/08 era sovra-affermato.

3) IL TETTO NON E' UN LIVELLO, E' UNA FRAZIONE. L'articolo documenta un Cap
ETHENA che diluisce il TASSO a livello di exchange e nessun limite sulle
quantita' detenibili: il muro non aveva base documentale e andava ri-sondato.
Fatto il 31/08 (giorno UTC nuovo -> non e' un limite giornaliero): ieri si
tornava a 644,18, oggi no, con l'equity scesa di $8.
    30/08  tetto [644,18 · 645,18)  equity $2.063,79  = 31,21-31,26%
    31/08  tetto [643,18 · 644,18)  equity $2.055,56  = 31,29-31,34%
0,05pp di scarto, dentro il rumore dell'equity (+-$2-8/ora). Candidato pulito
5/16 = 31,25%, ma a questa risoluzione non si distingue da una regola sul
collaterale scontato dell'haircut (~29%): si cita la banda (M25).
=> Il tetto SCALA col conto: la quota resta ~31%, il valore in dollari cresce
col capitale, il 70% non e' raggiungibile ne' ora ne' mai. E, essendo pinnati
al tetto, il rischio emittente resta una frazione COSTANTE del conto.

Cablato (rispondeva a "dove e' scritto il valore del tetto": in tre note di
testo e in nessun posto che il codice leggesse, tanto che usde_convert --quota
0.70 dichiarava "piano valido" per un ordine che il venue rifiuta):
  - config/live.json usde.venue_cap_frac 0.312 + venue_cap_misurato
  - src/live/usde.py lo porta nei default (unica autorita', P1)
  - usde_convert.piano() rifiuta il bersaglio sopra il tetto PRIMA di sparare
    e stampa il massimo raggiungibile. Verificato su entrambi i rami.

Anche: l'USDe ha un fee Deribit del 5% mai nominato prima. Il nostro misurato
(~4,5%/anno) e' gia' netto: e' l'unico numero da citare.

Stato: USDE 643,175691 (31,29%, al tetto), USDC $1.412,40, totale $2.055,57.
Suite: 795 passati.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 07:27:23 +00:00
Adriano Dal Pastro c250c04371 usde: l'USDC NON frutta sul nostro conto — misurato, e §6 di stasera era sbagliata
Domanda dell'operatore: "verifica se l'USDC frutta davvero sul nostro conto".

Sembrava dover aspettare: il gateway non espone il Transaction Log
(get_transaction_log, get_settlement_history, get_deposits, get_transfers,
get_interest_history: tutti 404 — debito #11) e l'unica serie storica e'
trades.db.equity, oraria ma arrotondata a 2 decimali e sporcata dal P&L non
realizzato (+-$2-8/ora a posizioni aperte), dove $0,13/giorno sparisce.

La fonte ufficiale Deribit ha spostato il problema dal rumore al calendario: i
reward USDC si pagano UNA VOLTA AL MESE ("paid out as a single monthly payment
early in the following month"), accrual alle 00:00 UTC sulla minima equity —
non ogni giorno come l'USDE. Ecco perche' una sorveglianza giornaliera non
poteva vederli. E un accredito mensile da qualche dollaro si vede benissimo
anche a 2 decimali, purche' il libro sia FLAT: allora equity == balance == USDC
e ogni scalino e' un accredito.

MISURA, due confini di mese entrambi a libro flat:
  29/06 -> 08/07  equity 598,06 costante, 237/237 ore ferme, 0 scalini  (attesi $0,45)
  31/07 -> 03/08  equity 596,92 costante per 4 giorni pieni             (attesi $1,72)
Il secondo e' decisivo: $1,72 contro una risoluzione di $0,01, 172x. Zero
MISURATO, non zero sotto soglia.

=> La premessa originale del gate USDE-01 e' RESTAURATA: il guadagno di tenere
USDE e' il tasso pieno ~4,1%, non lo spread 0,71 punti che avevo scritto poche
ore fa. Sui $644: ~$26/anno, non ~$4,6.

Cosa avevo sbagliato: ho letto `apr: 3.4` in public/get_currencies e l'ho
trattato come una proprieta' del NOSTRO CONTO. Era un listino del VENUE. La
"conferma indiretta" che invocavo provava che il listino e' reale per l'USDE e
non diceva nulla sull'idoneita' dell'USDC. Un tasso pubblicato non e' un tasso
incassato: si verifica sul conto — ed e' costato una query su una serie che
avevamo gia'.

Esclusione plausibile ma NON verificata: MiCA (USDC e' e-money token, USDe no);
Deribit dice solo "eligibility is based on their location". Il fatto misurato
e' lo zero, non il suo motivo.

Nuovo: scripts/live/balance_watch.py + scripts/cron_balance.sh (orario al
minuto :42, libero fra :25/:35/:47, sola lettura). Registra il BALANCE a 8
decimali per valuta — la serie che mancava — col conteggio dei fill dall'ultimo
campione e il nozionale lordo, cosi' una finestra sporca si riconosce invece di
essere mediata dentro. Etichetta PULITA le finestre a 0 fill e libro flat, dove
il delta USDC E' l'interesse, e ne stampa l'APR implicita.

Suite: 795 passati. L'ERROR di teardown e' il falso positivo dichiarato dalla
guardia stessa: il cron :47 ha scritto un fill reale (ETH BUY $+9, 23:47:19)
mentre la suite girava.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 23:48:52 +00:00
Adriano Dal Pastro a45794d2cb usde: il venue mette un TETTO al 31,21% — e l'USDC paga gia' 3,40%
Richiesta dell'operatore: "porta in usde tutto il capitale che non viene
usato". Il margine consuma $11 su $2.063 e l'USDE e' cross-collateral: preso
alla lettera vale una quota ~99%, cioe' il 100% che il gate esclude. Portata
all'operatore con le cifre, ha scelto 70%.

Il 70% non e' un argmax (M8): e' il massimo compatibile col CUSCINO DI
REGOLAMENTO, il vincolo che r0830_usde_quota non aveva guardato. P&L e funding
dei perp USDC-lineari si regolano in USDC, non nel collaterale, quindi la
quota non la limita l'haircut ma il saldo USDC che deve reggere il disaster-SL
sulla massima esposizione: 2 x 0,5 x 0,30 = 30% dell'equity, che lascia il 70%.

ESEGUITO +144 USDE (500,1757 -> 644,175691), quota 24,24% -> 31,21%, fee 0.
Poi il muro.

(a) TETTO DEL VENUE, misurato e non documentato da nessuna parte. Provato a
saldo neutro: BUY 20 rifiutato, SELL 20 OK, BUY 20 OK, BUY 5 rifiutato, con
$1.408 disponibili. E' un tetto sul LIVELLO. Il messaggio del venue
(not_enough_funds_in_currency) e' fuorviante e ha fatto inseguire tre ipotesi
sbagliate: taglia (falso, ma min_trade_amount=1 e' vero), rate-limit (falso),
prezzo (vero in parte: il book REST pubblico e' in ritardo sul matching engine
e prezzavo l'ordine sull'INDICE invece che sul BOOK — l'indice marca il
collaterale, il book prezza lo scambio). La cronaca resta nel diario: chi
rilegge non deve rifare il giro.

(b) L'USDC PAGA 3,40%. public/get_currencies: USDE 4,1071%, USDC 3,4000%, e i
nostri reward USDE misurati (~4,5%/anno) confermano che quelle APR sono reali.
Il guadagno non e' il tasso, e' lo SPREAD: 0,71 punti, ~$4,6/anno sui $644 che
teniamo, non i $21 che il gate implicava. Il gate ha misurato il reward
dell'USDE e non ha mai chiesto cosa facesse l'USDC fermo: manca il
controfattuale, M1 in un'altra veste. NON dimostrato che sia accreditato sul
nostro conto — da verificare su un giorno senza trade.

Nuovo attrezzo scripts/live/usde_convert.py: dry-run di default, banda prezzo,
tetto hard di quota, cuscino di regolamento derivato da config. NON passa da
execution.ALLOWED, che resta ai soli due perp.

config: quota_target 0.70 registrato; quota_max_frac alzato a 0.85 e RIMESSO a
0.50 nella stessa sessione — la soglia larga presupponeva un 70% che non
esiste, e lasciarla avrebbe disarmato la guardia per uno scenario che non si e'
verificato.

Suite: 795 passati, 0 falliti.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 20:18:31 +00:00
Adriano Dal Pastro 3cf884fa39 CLAUDE.md: la riga USDE-01 cita il tasso con la sua banda — 3,26% [1,13-4,51], n=4
Citava "3,80% su UNA finestra, 1,90% su DUE": due letture di n=1 e n=2, senza banda,
cioe' esattamente cio' che la §2 vieta. A quattro finestre il numero e' 3,26% annuo
con banda bootstrap [1,13% - 4,51%], e la larghezza (3,4 punti su un livello di 3,2%)
E' il risultato: a n=4 il tasso non e' misurato.

Aggiunto il risultato che governa la decisione di domani: l'EV e' lineare in q, quindi
non puo' scegliere una quota interna. E i tre numeri che la incorniciano: il margine
non e' il vincolo (l'haircut non morde fino al 90% di quota, nemmeno a leva 1,50x con
USDE marcato alla soglia critica 0,95 — 29x di cuscino), la resa vale $16/anno cioe'
meno di un mese di versamento, e aspettare quattro settimane costa $1,33 per 8x le
finestre.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E1gJmWPA4rEhmc6CQQuNZw
2026-08-30 17:41:14 +00:00
Adriano Dal Pastro 035bd086a2 usde: l'EV non puo' scegliere la quota — e aspettare 4 settimane costa $1,33
La decisione di quota e' dovuta lunedi' 31/08. Il risultato strutturale, dichiarato
prima di guardare i numeri: l'EV e' LINEARE in q, quindi il suo argmax e' sempre un
angolo (0% o 100%) e non puo' produrre una quota interna. Ogni quota intermedia nasce
da un criterio sulla CODA, che va dichiarato e non ottimizzato (M8). Lo script quindi
non sceglie: mette il prezzo accanto a ogni criterio candidato.

Misure:
- rendimento 3,26% annuo su n=4 finestre (3 pagate), banda bootstrap 95%
  [1,13% - 4,51%] su 200.000 ricampionamenti. Larga 3,4 punti su un livello di 3,2%:
  a n=4 il tasso non e' misurato, e' abbozzato. Il punto stimato si DERIVA da
  usde_watch.rendimento() invece di essere ridichiarato (P1), e la divergenza fra i
  due denominatori (0,06 punti) e' stampata, non appianata (P12).
- l'hazard annuo di pareggio E' l'APR: la quota e' EV-positiva se e solo se si crede
  che Ethena stia sotto il 3,3%/anno di evento catastrofico. Numero ASSUNTO, non
  stimato, come il p di Deribit del 26/07 — il cui framework P=1-(1-p)^20 lo script
  riproduce prima di usarlo (10/18/33/64% contro 10/18/34/64% registrati).
- il haircut 10% NON morde a nessuna quota testata, fino al 90%: il libro gira al
  2,00% di margine ($11,37 su $568,64), tre ordini di grandezza sotto il collaterale.
  Il vincolo non e' il margine (D5: buco quantificato e innocuo).
- il churn da depeg critico vale ~$52 di nozionale a quota 50%: secondo ordine.

Il numero che decide (N10 — una data si giustifica col COSTO della misura): la
conversione e' fee 0 e ~3 bps di spread, quindi la decisione e' reversibile a
~6 bps. Alzare a 50% fra quattro settimane invece che domani costa $1,33 di resa non
incassata e porta il campione da 4 a 32 finestre, stringendo la banda da ~3,4 punti
a ~1,2.

Due letture del rischio che non si annullano (M28): l'USDE fa perdere soldi solo nel
mondo in cui Ethena salta E Deribit sopravvive, quindi il rischio aggiunto e' di
secondo ordine — ma e' un terzo strato sullo stesso conto, e N4 dice che un rischio
di venue si compra con un secondo CONTO. La prima dice che costa poco, la seconda
che non e' li' che si compra sicurezza.

Verdetto a runtime: risoluzione insufficiente per alzare · nessun vincolo operativo ·
scala (la resa di oggi vale $16/anno, meno di UN mese di versamento: la leva binding
resta il bonifico) · asimmetria (gia' a 24,2% l'evento emittente vale 3,1x il maxDD
dell'intero libro) · reversibile e quasi gratis da rimandare.

La scelta resta dell'operatore: lo script mette i prezzi, non ne sceglie uno (N4).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E1gJmWPA4rEhmc6CQQuNZw
2026-08-30 17:38:35 +00:00
Adriano Dal Pastro fc24ee5f52 journal: voce COMPLETA del 29/08 sostituisce la parziale — 24/24 giri
Il cron notturno (00:37Z del 30/08) ha riscritto la pagina come annunciato
dalla versione parziale. I numeri di GIORNO passano dalla frazione di 7 giri
al giorno intero: equity $2,056.87 (era $2,051.29), P&L giorno +$4.51 (era
-$1.07), trading cumulato +$59.42, DD dal picco -0.81%.

Implicita ancora sotto la realizzata su entrambe le gambe (BTC 37.4 vs 44.0,
ETH 51.1 vs 68.9), DVOL al 19° e 18° percentile dell'anno: VRP01 resta fermo
per costruzione, IV-rank 0.07 e 0.12 sotto la soglia 0.30 del gate.

Aggiunta la sezione Analisi (agente), assente nella parziale. Salute pulita:
24/24 giri di book_execute, feed SKH a 0 min.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E1gJmWPA4rEhmc6CQQuNZw
2026-08-30 13:11:48 +00:00
Adriano Dal Pastro bb33df472f journal: voce PARZIALE del 29/08 — implicita al 24° e 17° percentile, VRP01 resta fermo
Scritta a mano alle 06:49Z, quindi marcata PARZIALE: copre 7 giri su 24 e non si
aggiorna da sola. Il cron la riscrive completa dopo le 00:30 di domani.

Equity $2.051,29, nozionale $555, leva 0.27x. Giornata -$1,07 su 7 letture, 1 fill
e 1 round-trip (+$0,02 netto). Cumulato dall'arming +$1.453,23, di cui $1.399,39
versati -> trading +$53,84. Entrambe le gambe a target, SKH01 flat su tutte e due:
il rischio resta interamente TP01.

Due cose che la Lettura tira fuori:
- l'implicita e' crollata in un giorno — DVOL BTC dal 47° al 24° percentile di un
  anno, ETH dal 45° al 17° — MENTRE la realizzata 30g saliva (44,1% e 68,9%). E'
  lo spread in cui vivrebbe VRP01, ma il suo gate guarda l'IV-rank espandente
  (0,09 e 0,12 contro una soglia di 0,30) e tiene lo sleeve fermo: dice di no
  esattamente dove l'occhio direbbe di si', ed e' per questo che esiste.
- prima comparsa della riga [drawdown]: -1,08% dal picco di $2.073,59, con la sua
  avvertenza attaccata (letture ORARIE, non minimo intra-giorno: e' un pavimento).

Campo `nota` lasciato vuoto: e' dell'operatore.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-29 06:51:22 +00:00
Adriano Dal Pastro 993d17556a usde: gate CHIUSO IDONEO, quota rinviata a lunedi' — l'APR si legge col suo n
Il 2026-08-28 12:35Z `usde_watch` ha rilevato il primo reward (+0.052055 USDE su
500), e la regola pre-registrata il 26/08 PRIMA dell'esito ha chiuso il gate
USDE-01 su IDONEO. Si apre la decisione di QUOTA, che e' dell'operatore.

DECISIONE DELL'OPERATORE (29/08): la quota si decide lunedi' 31/08, su tre-quattro
finestre invece che su una. Il motivo e' un numero: sul solo pagamento il tasso
implicito e' 3,80% annuo, su DUE finestre — il 27/08 aveva pagato ZERO — e' 1,90%.
Fra i due c'e' tutta la decisione, e il campione e' UN pagamento.

`rendimento()`: APR sulle finestre OSSERVATE, col denominatore sul TEMPO VERO e
non sul numero di finestre pagate — cosi' una finestra che non paga ABBASSA la
stima invece di sparire (P5: quel silenzio e' uno zero). Le letture con trade nel
mezzo si escludono, non si riparano in silenzio (P12). L'APR si stampa sempre col
suo `n`, e sotto 4 finestre la riga dice «un pagamento non e' un tasso». Sulle
letture vere: 1,97% annuo su 1,9 giorni, 1/2 finestre pagate.

`quota_da_decidere()`: vera SOLO se IDONEO E il rinvio e' scaduto. Da lunedi' il
watch manda il 📌 OGNI giorno — quota attuale, tetto di allerta, APR osservato —
finche' non si decide (N9: una decisione rinviata senza promemoria e' rinviata per
sempre). Stesso schema della soglia $15k di GTAA01, con la data al posto del
capitale. Si smette togliendo `DECISIONE_QUOTA_DAL`.

6 test nuovi, incluso il controllo positivo — «la finestra sola darebbe il doppio»
— che e' esattamente la ragione per cui l'APR non si cita senza il suo n.

CONFERMATO il fix IB di ieri: il giro delle 00:30 ha 0 righe
`orders request timed out` (erano 2 per giro, 63 su 63) e tutti e sei gli ETF
scaricati. E una conferma involontaria del debito §5.13: SPY e' passato da
1996-09-04 a 1996-09-05 — la finestra rotolante ha perso un altro giorno in una
notte, come misurato.

Diario: docs/diary/2026-08-29-usde-idoneo-e-ib.md
Suite: 795 passati, 0 falliti.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-29 06:45:47 +00:00
Adriano Dal Pastro 2beb11b764 fetch IB: client in readonly — via due righe d'errore da 63 notti su 63
`fetch_ib_equities.py` si connetteva senza `readonly`, e ogni notte il log del
cron si prendeva

    open orders request timed out
    completed orders request timed out

126 righe in 63 giri. Due errori innocui ripetuti per sempre sono il modo in cui
un errore VERO smette di farsi notare (P14): su `cron_daily.log`, 126 dei 146
match di "error" erano questi.

CAUSA, letta nel sorgente di ib_async e non indovinata: in `IB.connectAsync` le
richieste "open orders" e "completed orders" esistono SOLO se il client non e'
readonly (`if not readonly: reqs[...]`), e sul gateway paper non rispondono.
Questo client scarica storico e non manda ordini mai: `readonly=True` e' insieme
la cura del rumore e la dichiarazione corretta di cosa fa. Tolta la CAUSA, non
filtrato il messaggio — filtrarlo avrebbe nascosto anche il giorno in cui quel
timeout significasse qualcosa.

VERIFICATO con un A/B sul solo flag, contro il gateway vero:
  · readonly=False -> le due righe compaiono, e si apre sul gateway il dialogo
    modale "API client needs write access action confirmation" (visto nei log del
    container, resta su ~75s);
  · readonly=True  -> nessuna delle due righe, nessun dialogo.

⚠️ CIO' CHE NON E' STATO VERIFICATO, e va detto: in nessuna delle quattro prove
fra le 13:05 e le 15:35 UTC il gateway ha servito storico — 0 barre con ENTRAMBI
i flag, quindi la causa non e' questa modifica, ma non ho potuto confermare
end-to-end che il fetch continui a riportare barre. La conferma e' il log del
cron di stanotte: se SPY/QQQ/IWM/TLT/GLD/HYG tornano con le loro barre e senza
le due righe di timeout, e' a posto; se tornano tutti a 0, si revoca il flag.
Il fallimento e' comunque innocuo: con 0 barre lo script NON sovrascrive i
parquet (verificato: eq_spy/eq_qqq intatti dopo i tentativi falliti).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 13:31:22 +00:00
Adriano Dal Pastro 32e1649dcb test: la suite non parla piu' al canale di allarme vero
Difetto trovato addosso: `uv run pytest` mandava messaggi Telegram VERI sul
canale dell'operatore — cinque per giro, col testo delle fixture («Analisi di
ieri.», «rete KO», «Testo dell'agente.») sotto l'intestazione di una voce di
giornale del 21/08. Colpevoli i cinque test di `analista.CLI.scrivi()`, che non
passavano `telegram=False` e percorrevano `invia()` fino a `notifier.send()`.
`_cfg()` legge il token da `.env.mainnet`/`.env`: il fatto che non fosse
nell'ambiente non proteggeva niente.

NON E' COSMETICO. E' P9 al contrario — allarmi finti spesi su un canale che
deve restare credibile il giorno che l'allarme e' vero. Chi riceve cinque
messaggi identici a ogni giro di test impara a non aprirli, ed e' l'unico
canale da cui passano disaster-SL, uscita di fondi e venue giu'.

RIPARAZIONE STRUTTURALE, stessa lezione del watermark (§5.12): si blocca la
RETE in una fixture autouse, non si chiede a ogni autore di ricordarsi un
parametro. Un test che vuole davvero esercitare `send()` continua a funzionare
— patcha `urlopen` nel proprio corpo e vince su questo.

In piu' una guardia di sessione: bloccare non basta, un tentativo va TROVATO e
reso esplicito, o resta li' pronto a tornare vero il giorno che qualcuno cambia
il boundary. Validata su controllo positivo (M15): un test che chiama
`notifier.send()` fa fallire la sessione.

⚠️ E un difetto fatto e corretto nello stesso giro: la prima versione registrava
l'URL, che contiene il BOT TOKEN in chiaro — e quella lista finisce nel
messaggio di un assert, cioe' nell'output della suite e nei log. Ora si registra
solo "telegram sendMessage": serve sapere CHE si e' tentato, non VERSO DOVE.
Il token e' stato esposto una volta nell'output di quella verifica: va ruotato.

Suite: 789 passati, 0 falliti.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 13:04:19 +00:00
Adriano Dal Pastro 418f523be7 gtaa: la decisione tenere/bloccare va a $15k — gate (A) ritirato, soglia sorvegliata
Decisione dell'operatore: GTAA01 NON si blocca oggi, si decide quando il book
arriva a $15k. Il motivo registrato non e' «non contribuisce» — la misura dice
il contrario (+0,095/+0,124 di Sharpe a iso-rischio secondo la fase, positivo in
tutte e cinque, hold-out +0,247, 6 anni su 8, correlazione col resto +0,087).
Il motivo e' che sotto $15k di book lo sleeve NON E' ACCENDIBILE:
GTAA_MIN_CAPITAL e' $3.000 allocati, che al peso 20% fa $15.000 contro i $2.068
attuali. Finora era manutenzione senza beneficio incassabile.

Registrate anche le due ragioni contro il blocco, che restano valide: N7 (uno
sleeve difensivo si giudica sul sinistro, e questa finestra non lo contiene) e
N4 (togliendolo il portafoglio di ricerca diventa 100% cripto su un venue solo).
E il costo che sarebbe andato pagato: GTAA01 e' uno dei quattro nomi di
W_DEPLOY, da cui escono i $313k del muro.

PERCHE' LA DECISIONE NON SI DIMENTICHI (N9). Una decisione parcheggiata su un
numero che nessuno sorveglia e' parcheggiata per sempre. `journal.SOGLIE_CAPITALE`
legge l'equity ogni giorno — il giornale lo fa comunque — e il giorno che supera
la soglia la voce dice quale decisione si sblocca e perche'. Seconda riga: $20k,
dove si riapre «100% Deribit fino a $20k». Una riga si TOGLIE quando la decisione
e' presa. 5 test, incluso «equity non leggibile non e' una soglia superata» (P5).

GATE (A) RITIRATO come pass/fail, congelando il MOTIVO e non l'esito — come il
07/08 col confronto fra ranghi, e per la stessa ragione: il criterio decide su un
margine piu' piccolo del rumore che lo scuote. In piu' una guardia sulla CAUSA
(`test_la_fase_di_ribilanciamento_e_ancora_ancorata_alla_POSIZIONE`) che si rompe
il giorno che qualcuno ancorasse la fase al calendario: quel giorno il gate
potrebbe tornare decidibile, ed e' un fatto da guardare, non da ignorare.
Il test sul margine assoluto si e' auto-ritirato: diceva «se scendesse sotto un
centesimo questo criterio smetterebbe di essere una misura», ed e' sceso a 0,0074.

CLAUDE.md: §3 nuova riga · §5.2 RISOLTO (trasporto allarmi) · §5.4 riscritto (la
domanda fiscale (a) ha una risposta alla fonte: derivati in c-quater al 26%, non
c-sexies al 33% — Circolare AdE 30/E del 27/10/2023, citata verbatim) · §5.13
nuovo debito, la fase che ruota — quantificata e dichiarata INNOCUA oggi (D5):
0,029 di Sharpe a livello di portafoglio, dentro la banda [1,81-2,12].

Diario: docs/diary/2026-08-28-gtaa-fase-e-allarmi.md
Suite: 789 passati, 0 falliti.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 12:54:08 +00:00
Adriano Dal Pastro 21fe289383 research(gtaa): cosa cambia togliendo GTAA01 — +0.11 di Sharpe a iso-rischio, sul filo
Misura chiesta prima di decidere del gate (A): se togliere lo sleeve non
cambiasse niente, la domanda sul gate sarebbe accademica. Portafoglio di
RICERCA a 5 sleeve (33/15/12/20/20), NON il book live (dove GTAA01 non c'e').

Due lenti, ed e' la seconda che decide (M6: un diversificatore a basso CAGR si
giudica a iso-rischio, mai a iso-nozionale; M5: "meno drawdown" si testa a
iso-rischio o si sta comprando de-levering):
  · iso-nozionale: CON Sh 2.28 CAGR +19.2% DD 6.1% vol 7.8%
                   SENZA Sh 2.16 CAGR +23.8% DD 7.8% vol 10.1%
    -> togliendolo si guadagna DRIFT e si compra VOLATILITA'.
  · iso-rischio (×0.77): SENZA Sh 2.16 CAGR +18.0% DD 6.0%
    -> Δ Sharpe +0.121, Δ maxDD +0.02pp.
Hold-out 2025+: Δ +0.247. Per anno: GTAA01 aiuta in 6/8. Correlazione col resto
del portafoglio +0.087 — e' davvero altro. Standalone Sh 1.06, CAGR +5.8%.

⚠️ E LA BANDA DI FASE, che e' la ragione per cui questo numero va citato con la
sua incertezza: il Δ sulle 5 fasi va da +0.095 a +0.124 e supera la soglia
dichiarata (0.12) in 2 fasi su 5. Il SEGNO e' stabile — GTAA01 aiuta in ogni
fase — ma il verdetto BINARIO dipende dalla notte in cui lo si legge. Sanity:
la fase 0 riproduce lo sleeve di produzione bit-exact (max|Δ| = 0.00e+00).

Lettura: il contributo e' reale e sempre positivo, e vale ~+0.11 di Sharpe, cioe'
ESATTAMENTE quanto lo spread fra le tre baseline che il progetto gia' si porta
dietro (0.12, §2). Non e' "GTAA01 e' inutile": e' "sta al limite di cio' che
questi dati risolvono".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 12:34:44 +00:00
Adriano Dal Pastro 801bd13f10 allarmi: il marcatore "gia' detto" si scrive DOPO l'invio, non prima
Debito §5.2 chiuso su decisione dell'operatore. `run_once` salvava lo stato coi
marcatori `alerted` gia' a True e l'invio lo faceva il chiamante DOPO: col 6,9%
di invii falliti misurato (2 su 29), un 🚨 perso restava perso per l'EPISODIO
INTERO — l'ora dopo lo stato diceva "gia' detto" e usciva WATCH/MUTO. Gli
episodi storici durano 200-2.324 ore, quindi il buco non era teorico.

- `run_once(state_path, sender=None)`: il sender e' INIETTATO, non importato —
  e' cio' che tiene la funzione testabile senza rete d'uscita, che era la
  ragione del disegno precedente. Senza sender il comportamento resta quello di
  prima e il report lo DICE (`invio`), invece di lasciar credere che qualcosa
  sia partito.
- Su invio fallito si disfano SOLO i marcatori "gia' detto", non le misure:
  · asset in ALERT -> alerted=False, l'ora dopo ri-allerta;
  · lock MAINT/ALERT -> alerted_soft/hard=False ma le ORE restano a correre,
    cosi' una manutenzione che sfora la grazia sale ad ALERT anche col trasporto
    giu' (disfare anche le ore congelerebbe l'escalation proprio mentre non si
    riesce a parlare);
  · lock RIENTRATO -> si ripristina l'intero LockState, perche' il rientro si
    annuncia una volta sola e senza le ore non ci sarebbe piu' niente da dire.
- `notify(..., tentativi=)`: il retry esisteva in `send` e non arrivava qui.
  venue_watch ora manda con 3 tentativi.
- L'esito dell'invio finisce nel log del cron invece di sparire.

5 test nuovi. Il primo e' quello che conta — dopo un invio fallito, l'ora dopo
ri-allerta — col suo controllo positivo (un invio riuscito consuma l'allarme
UNA volta sola), senza il quale "ri-allerta sempre" passerebbe.

Suite: 782 passati, 2 falliti (i due del gate GTAA, non toccati qui).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 12:34:44 +00:00
Adriano Dal Pastro c21a0d4058 research(gtaa): criterio (C) — la mediana delle 5 fasi regge come strumento, e sceglie il 60%
Analisi richiesta prima di decidere che fare del gate (A). Il criterio: a cadenza
di produzione, la banda si sceglie sulla MEDIANA delle 5 fasi di ribilanciamento
invece che sulla fase capitata (M7: su ancore appaiate la statistica e' la
mediana). Toglie la componente di FASE; resta quella di DATO.

⚠️ DICHIARATO IN TESTA ALLO SCRIPT: (C) e' stato scelto DOPO aver visto la
tabella delle fasi di r0828_gtaa_band_phase. Sceglierne uno nuovo guardando
l'esito del vecchio e' selezione. Quindi (C) misura lo STRUMENTO — risoluzione
e potenza — e NON valida la proposta del 27/07. Pavimento tenuto a 0.01, quello
che il test si diede il 07/08: il metro non si ritocca sul risultato.

ESITO, coi tre criteri calcolati a runtime:
  (i)   scelta stabile su 10 notti : SI — ['60%'] in tutte e dieci
  (ii)  margine sempre >= 0.01     : SI — minimo 0.0197 (fase singola: 0.0026,
        e sotto il pavimento in 4 notti su 10)
  (iii) potenza (in-sample ≠ hold-out) : SI — al buio 60%, sull'hold-out 40%
Lo strumento funziona: l'escursione dello Sharpe in-sample su 10 notti cala del
74-99% per ogni banda (60%: da 0.1120 a 0.0006).

MA LA CELLA CHE SCEGLIE NON E' LA PROPOSTA. Al buio esce il **60%**; il 25%
vince **0 notti su 10**. Da tenere accanto: sull'hold-out il 60% e' penultimo
(0.7430 contro 0.9315 del 40%), coerente con lo Spearman IS/OOS ~0 misurato il
07/08 — la scelta in-sample non predice, e non va letta come "la banda giusta".

CONTORNO che vale piu' del verdetto: le 5 serie di fase correlano **0.988**,
quindi N_eff = 1.01. La mediana di 5 fasi e' UNA osservazione: toglie
l'artefatto, non compra precisione. Va citata come robustezza alla fase, mai
come campione (§2: «positivo in N/N ancore NON e' N osservazioni»).

Nessuna decisione presa qui: il gate (A) non e' toccato e i due test restano
rossi in attesa della scelta dell'operatore.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 08:51:43 +00:00
Adriano Dal Pastro 31f82e1730 research(gtaa): il gate (A) della banda e' una moneta — la finestra "congelata" rotola ogni notte
Il 28/08 falliscono `test_la_banda_proposta_e_quella_scelta_al_buio` e
`test_il_margine_del_blind_non_e_un_arrotondamento`, a codice fermo dal 07/08
(nessun commit su src/portfolio/gtaa.py ne' su r0727_gtaa_band_gate.py). Al buio
esce il 60% invece del 25%, con margine 0.0074 — sotto il pavimento di 0.01 che
il test si era dato. Questo script chiede, per la terza volta sullo stesso
parametro, se il criterio misura cio' che dichiara. Replica bit-exact della
produzione verificata a fase 0 / taglio 0 (max|Δ| = 0.00e+00 su 7.544 barre).

(0) LA FINESTRA IN-SAMPLE NON E' FERMA. IB serve una finestra ROTOLANTE di 30
    anni. SPY e' l'unica gamba al muro (30.0 anni), e nel cron log la sua data
    d'inizio avanza di un giorno di borsa ogni notte: 1996-07-02 il 24/06,
    1996-09-04 oggi. Il pre-2015, che tutto il progetto tratta come finestra
    congelata, perde il giorno piu' vecchio ogni notte. QQQ e IWM arriveranno
    allo stesso muro fra 2,5 e 3,7 anni.

(1) L'AMPLIFICATORE E' LA FASE. `_gated_returns` ribilancia su `i % every == 0`,
    indice di POSIZIONE nell'array: se la prima barra scivola, si ri-fasa ogni
    decisione di trent'anni. Il progetto sapeva che la fase conta
    (`r0726_loo_deluck.gtaa01_at(ph)`, 5 ancore) ma la assumeva fissa alla
    canonica 0. Non lo e': la fase canonica cambia da sola ogni notte.

MISURE. Sulla finestra CHIUSA pre-2015, che non puo' acquisire dati nuovi, lo
Sharpe si e' mosso fino a 0.0760 fra il 07/08 e oggi. Su 10 notti consecutive
la banda scelta al buio e' 25% / 40% / 60% — la proposta vince il 40% delle
notti — e il margine sta sotto il pavimento in 4 notti su 10 (minimo 0.0026).
A dato FISSO, muovendo la sola fase, il verdetto cambia lo stesso (25% su 3
fasi, 60% su 2): l'imputato e' la fase, non le barre perse.

VERDETTO calcolato a runtime coi criteri dichiarati in testa (M13): il gate (A)
NON ha risoluzione. Il verdetto non e' una proprieta' della banda, e' una
proprieta' della notte in cui lo si legge. La banda al 25% non e' ne' confermata
ne' smentita: non e' decidibile cosi'.

Lo script misura e basta. Ritirare il criterio, come si fece il 07/08 col
confronto fra ranghi, e' una decisione dell'operatore e non e' presa qui.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 08:44:25 +00:00
Adriano Dal Pastro cb40ab3e20 journal: recuperata l'analisi del 27/08, saltata dal guasto di autenticazione
Rigenerata con `analista.py --giorno 2026-08-27 --no-telegram`. Le sezioni
misurate escono IDENTICHE (ricostruzione deterministica da trades.db e dal
feed): cambiano solo l'ora di scrittura in testata e la sezione `Analisi`,
che ora c'e'. Controllo sui numeri: ok.

`--no-telegram` di proposito: la notifica delle 00:37:02Z era partita e diceva
«analisi non disponibile (errore)». Quel record e' la prova che l'allarme del
guasto e' uscito, e riscriverlo con un invio di oggi la cancellerebbe.

L'analisi legge lo storico com'e' OGGI, non com'era stanotte: la testata porta
l'ora vera di scrittura (08:29:00Z), quindi la pagina non si spaccia per una
scritta a ridosso del giorno chiuso.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 08:32:41 +00:00
Adriano Dal Pastro 05a698a83e analista: il motivo di un guasto si prende da stdout E da stderr, o si perde
Il 2026-08-28 alle 00:37:00Z la CLI `claude` e' uscita 1 scrivendo
«Failed to authenticate: OAuth session expired and could not be refreshed»
su STDOUT, con stderr VUOTO. `interroga()` componeva il motivo dal solo
stderr, quindi nel DB e' finito `analisi_stato='errore'`,
`analisi_motivi='uscita 1: '` — e su Telegram e' partito
«analisi non disponibile (errore): uscita 1:».

La meccanica ha retto (nessuna analisi di ieri spacciata per quella di oggi,
esito registrato, notifica partita): a mancare era solo il PERCHE'. P4 —
un'allerta risponde a due domande, e con la prima sola la causa si ricostruisce
aprendo a mano il transcript della sessione headless. P3 — quando la diagnosi
serve, il guasto e' gia' rientrato: il motivo si cattura li' o mai.

- `motivo_uscita(returncode, stdout, stderr)`: unisce i due canali (stderr per
  primo), tronca a 300 caratteri.
- Silenzio totale su entrambi i canali -> lo DICE, invece di lasciare i due
  punti a vuoto: «la CLI non ha detto perche'» e «il perche' l'abbiamo perso
  noi» sono guasti diversi e finora si scrivevano uguali.
- 4 test nuovi, fra cui la regressione col messaggio testuale del 28/08.

NON risolve la causa a monte: perche' quella refresh sia fallita non sta in
nessun log locale. Il refresh token era valido (rinnovo automatico riuscito
alle 08:03:35Z dello stesso giorno, scadenza 2026-09-25) e la stessa chiamata
rifatta a mano esce 0 -> guasto transitorio, unico su 24 sessioni locali.
Da qui in avanti, se ricapita, il motivo si legge dalla notifica.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 08:32:41 +00:00
Adriano Dal Pastro a6b5756cc0 journal: voci automatiche del 26-27/08 — rischio tutto su TP01, SKH01 flat
Due voci scritte dal cron delle 00:37Z (27/08 e 28/08). Numeri come registrati,
nessuna riscrittura a mano.

- 26/08: equity $2.065,45, giorno $+8,86, 4 fill / 2 round-trip, 24/24 giri.
  L'analisi dell'agente segnala il denominatore cambiato il 25/08 (conversione
  USDE): stessa esposizione ($582 lordi), leva 0,28x non confrontabile coi
  giorni precedenti.
- 27/08: equity $2.070,23, giorno $+4,78, 1 fill / 1 round-trip, 24/24 giri.
  Manca la sezione "Analisi (agente)" — il passo dell'analista nel cron e'
  a `|| true`, quindi un suo fallimento non lascia traccia nella voce.

Cumulato dall'arming $+1.472,17 di equity, di cui $+1.399,39 versati:
trading **$+72,78** su 65 giorni e 23 round-trip — taglia a cui il P&L non
distingue l'edge dalla fortuna, e con l'82% del cumulato negli ultimi 7 giorni
(la settimana in cui BTC ha fatto +9,94% e il libro era lungo su entrambe le
gambe).

SKH01 flat su BTC e ETH in entrambe le voci: l'esposizione e' interamente TP01.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 08:19:09 +00:00
Adriano Dal Pastro e5052a690f usde: da patch d'emergenza a struttura — config, modulo unico, sorveglianza
- config/live.json sezione `usde` (unica autorita', P1): indice, haircut 10%,
  tetto allerta quota 50%, soglie depeg 0.99/0.95 coi criteri dichiarati (P6)
- src/live/usde.py: config + catena di prezzo (indice pubblico -> ticker ->
  1.0 dichiarato) + valutazione PURA; shadow._collaterale_usde ora deriva da qui
- scripts/live/usde_watch.py + cron_usde.sh (12:35 UTC, dopo la finestra reward):
  reward per delta netto trade (P12: senza inventare attribuzioni), depeg
  (crit ripetuto, resto a transizione, P9), quota anche per deriva passiva (N4);
  applica il verdetto di eligibilita' pre-registrato (>=1 reward entro 29/08)
- serie data/live/usde_watch.jsonl sotto monitor_health (max 30h, P5: un watch
  fermo non deve leggersi come "va tutto bene"); baseline 14:17Z registrata
- GATE USDE-01 in CLAUDE.md §4; test 775 (+17 in tests/test_usde_watch.py)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B9UyJLHzR7EJzxR3iQ3RN1
2026-08-26 15:11:44 +00:00
Adriano Dal Pastro 631e854b29 live: l'equity del book e' il TOTALE cross-collateral — USDE valutato all'indice pubblico
Trovato eseguendo il test di eligibilita' USDE ($500 convertiti alle 13:04Z,
fill 1.0003 fee 0): shadow._equity leggeva solo il conto USDC -> il giro
successivo avrebbe visto -24,3%, mandato un falso "USCITA DI FONDI" e venduto
~$140 di posizioni. Riparato prima del giro delle 13:47: _collaterale_usde()
valuta l'USDE all'indice pubblico usde_usdc (mediana multi-exchange, lezione
Binance 10/10/2025), depeg passa nel sizing, clamp a 1.0 sopra la pari,
fallback 1.0 dichiarato (mai 0: il fallback si sceglie sul danno, P5).
6 test nuovi in tests/test_shadow_usde.py.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B9UyJLHzR7EJzxR3iQ3RN1
2026-08-26 13:07:29 +00:00
Adriano Dal Pastro 562e95338b ricerca: USDE come collaterale a rendimento — analisi da candidato + test di eligibilita' pre-registrato
Verificato sul venue: apr 4.0 (netto fee), spot USDE_USDC spread ~3bps fee zero,
haircut 10% (non morde al nostro profilo di leva), indice usde_usd multi-exchange
con mediana+clamp (la differenza strutturale dal caso Binance 10/10/2025).
Rischi dichiarati R1-R4, p non stimabile -> la leva di controllo e' la QUOTA (N4).
Aritmetica: a $5k/quota 50% ~$101/anno; -100% = 25 anni di resa, non si recupera.
Pre-registrato il test di eligibilita' (~$500, 3 giorni, regola dichiarata prima).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B9UyJLHzR7EJzxR3iQ3RN1
2026-08-26 11:44:58 +00:00
Adriano Dal Pastro c4c82a8fbd diario: USDC rewards — verdetto: Italia esclusa per MiCA, pista chiusa + episodio scam in chat
Coinbase (chi paga i reward del programma Deribit) ha cessato i reward USDC
nell'EEA dal 2024-12-01 per il divieto MiCA di remunerare gli EMT; l'espansione
Deribit del 2026-08-01 aggiunge 80 paesi, nessuno EEA. Nessun ticket necessario.
Registrato l'episodio dei finti agenti in chat ("Yes, Italy is supported" =
falso) con la regola permanente sul canale di supporto.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B9UyJLHzR7EJzxR3iQ3RN1
2026-08-26 11:37:30 +00:00
Adriano Dal Pastro e7a5fee2fd diario: USDC Rewards Deribit — prodotto verificato sul venue (apr 3.4), il conto NON li riceve
Verifica empirica sulla serie equity oraria: nelle finestre di pagamento
(1-14 lug, 1-14 ago) il libro era flat e nessun accredito da ~$0,65-2,00
compare (0-1 salti >=$0,40, tutti spiegati dai fill). Resta il ticket al
supporto sull'idoneita' dell'Italia. Vale ~$170/anno a $5k.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B9UyJLHzR7EJzxR3iQ3RN1
2026-08-26 11:15:47 +00:00
Adriano Dal Pastro 3034bfdc58 gate XSR01: riscrittura DICHIARATA (opzione A dell'operatore) + haircut con script
Decisione operatore 26/08, a 58 giorni dall'esito, in direzione che stringe:
(1) capitale deploy $5k -> $20k — riconcilia il gate (25/07) con la decisione
vincolante "100% Deribit fino a $20k" (26/07), posteriore, che governa (M28);
deploy XSR01 e scelta del venue ora coincidono in un punto solo.
(2) gamba haircut: numero decisivo da r0826_xsr_haircut.py (N11) a pavimento
$10 (il vero, HL-EXEC), citato con la frazione di ordini eseguiti (P7).
Misurato: FULL 968 barre floor $10 -> haircut -1,1% con 22% di eseguiti (la
guardia da sola era vacua: un libro fermo ha haircut piccolo); ticket mediano
$3,34/gamba, 84% sotto $10 — sostituisce il "$14,41" senza script.
(3) contesto 1.82 -> 1.79 (lente dei gate). Soglie numeriche INVARIATE.

Chiude il debito S5.3. Diario 2026-08-26-xsr01-gate-riscritto.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B9UyJLHzR7EJzxR3iQ3RN1
2026-08-26 11:04:57 +00:00
Adriano Dal Pastro d597fc64e8 monitor: advance() consuma solo barre CHIUSE — serie rigenerate, guardia PREMATURO, 4 debiti chiusi
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
2026-08-26 10:12:23 +00:00
Adriano Dal Pastro a51844875b journal: i versamenti non sono piu' P&L — movimenti di capitale scorporati dal trading
Il P&L di giornale era un delta di equity: la voce del 25/08 dichiarava +$1.414,57
di "giorno" quando $1.399,39 erano il deposito USDC, e il cumulato dall'arming
avrebbe mentito per sempre. Nuova `movimenti_capitale()`:
- soglia IMPORTATA da book.EQUITY_JUMP_ALERT, non ridichiarata (P1);
- "movimento" solo se il salto supera di >2x il massimo che il mercato MISURATO
  (feed certificato, tetto di leva da config) poteva produrre fra le due letture;
- altrimenti AMBIGUO: dichiarato con attenzione e NON scorporato (P12 -- un crash
  vero a tutta leva non deve diventare "prelievo"); idem a feed illeggibile (P5).
Scansione dell'intera storia: 1 evento, esattamente il deposito (+209,6% contro
mercato max 0,22%), zero falsi positivi. `concentrazione` ora lavora sul cumulato
di TRADING; le regole sui movimenti stanno sopra l'early-return "nessun giro".
Limite dichiarato (D5): un movimento sotto il 10% dell'equity resta nel P&L.
7 prove nuove (31 -> 37), incluso il controllo M15 al contrario. Voce del 25/08
rigenerata: giorno -> trading +$15,18; cumulato -> trading +$59,14.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B9UyJLHzR7EJzxR3iQ3RN1
2026-08-26 07:35:15 +00:00
Adriano Dal Pastro f10d847816 ricerca: XSR01 sotto la lente RENDITA (filone 70) + cosa compra un versamento da $3k
XSR-RENDITA (r0825_xsr_rendita.py): prima valutazione di XSR01 sul criterio della
perpetua. A iso-nozionale alza il muro; a iso-rischio lo abbassa del 20,6% MA il null
mostra che il meccanismo vale 0,8% (mescolare i rendimenti non cambia nulla) e un
conto remunerato allo stesso tasso lo eguaglia a vol zero senza secondo venue.
A drift zero il muro SALE: si compra un drift scorrelato, non la scorrelazione.
L'haircut non pareggia un conto al 4% nemmeno a zero. Vincolo binding: capitale
($60k per un 25% sopra C*). Corretta in CLAUDE.md la riga Sharpe 1,82 (terza lente;
la lente dei gate da' 1,79 alla scoperta / 1,56-1,63 a oggi).

VERSAMENTO-3K (r0825_versamento_3k.py): $2.065 -> $5.065 appaiato sugli stessi path
del piano = 5,5 mesi di versamenti anticipati; 10a $114.929 -> $122.491 (+6,6%);
$15k in 1,3a e $20k in 1,9a. P(cap >= $5k al gate XSR01 del 23/10): 0% -> 100% --
il lump rende il gate leggibile senza rendere XSR01 comprabile: la decisione sulle
soglie (S5.3) va presa PRIMA che il lump atterri.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B9UyJLHzR7EJzxR3iQ3RN1
2026-08-26 06:56:40 +00:00
Adriano Dal Pastro 4f91b25b72 journal: voce automatica completa del 25/08 (riscritta dal cron delle 00:37Z)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B9UyJLHzR7EJzxR3iQ3RN1
2026-08-26 06:56:40 +00:00
Adriano Dal Pastro ee5c6ed539 ricerca: il piano a 10 anni dal conto vero, l'ETF-quando-flat SCARTATO, slippage rifatto
Giornata partita da "stato trades" e finita sul disegno del sistema. Versamento di
1.400 USDC atterrato alle 11:03:35Z (equity $667,88 -> $2.066,88); il giro delle 11:47
ha ribilanciato correttamente e ha ripiazzato i disaster-SL alla taglia nuova -- prima
volta che il ramo riparato stamattina gira sul serio.

(1) PIANO A 10 ANNI DAL CONTO VERO -- r0825_piano_10a_500.py (nuovo)
Le tabelle pubblicate partono da $600/$635: rifatte su $2.067, lente L3 CONGIUNTA.
Replica superata: chiede EUR 1.718/m dove la tabella da $635 chiedeva EUR 1.733.
EUR 500/mese per 10 anni -> mediana $114.934, rendita 18,35 EUR/g, P(>=50 EUR/g) = 0,0%
su 3.000 traiettorie. Il bersaglio con EUR 500/m arriva al 17o anno.

  L'EQUIVALENZA CHE ORDINA IL PIANO: EUR 100/mese in piu' == +4,07%/anno di drift,
  cioe' +27% su TUTTO il drift del libro. A 10 anni i bonifici fanno il 59%.
  Tutta la leva autorizzabile vale quanto EUR 100-150/mese: k=1,25 (+15,6%) vale MENO
  di EUR 100/mese in piu' (+19,1%), e si porta dietro il peggior giorno al 21,48%.

  E il libro a k=1 rende MENO dell'S&P (15,19% vs 17,40%): il vantaggio sta nello
  Sharpe (1,35 vs 0,89) e senza leva NON si converte in rendimento. Senza leva il libro
  non si giustifica come veicolo di ACCUMULO -- si giustifica come veicolo di RENDITA,
  dove serve 2,5x meno capitale ($254k contro $646k) perche' la perpetua vive sul DD.

(2) "ETF QUANDO IL LIBRO E' FLAT" -- SCARTATO, r0825_capitale_fermo.py (nuovo)
Il libro e' flat il 28,0% dei giorni (2,7% nel 2021, 77,0% nel 2022, 75,6% nel 2026);
live, esposto 14 giorni su 64, leva lorda mediana 0,00x e max 0,52x su un tetto di 1,0x
-- il cap non ha mai morso, il vincolo e' il segnale.
A ISO-RISCHIO il dinamico PERDE: Sharpe 1,01 contro 1,40 del 50/50 e 1,35 del libro. E
il null a maschera casuale (400 estrazioni, stessa quota, blocchi 20g) lo mette al 30o
percentile: fa PEGGIO di commutare a caso.
A iso-nozionale sembrava vincere (drift 17,09%, il piu' alto) perche' aveva la vol piu'
alta: e' la trappola di M6, il de-levering e' il PRIMO test.

  E IL MECCANISMO CHE SEMBRAVA OVVIO NON ESISTE. Aggregato: SPY 4,91% nei giorni flat
  contro 21,48% negli altri (-16,57%), e la storia si scrive da sola. FALSA: scomposta
  per anno il segno ALTERNA (3 su, 4 giu') e il 2022 -- l'anno che doveva reggerla --
  ha il segno OPPOSTO. Artefatto di composizione: i giorni flat stanno negli ANNI brutti
  per l'azionario, non nei GIORNI brutti. M9 ha fatto il suo lavoro su di me.

(3) SLIPPAGE RIFATTO ALLA TAGLIA VERA -- r0822_slip_audit.py riparato
26 fill (erano 18), $6-$319. I due piu' grandi mai eseguiti sono di oggi e hanno preso
1,01% e 0,54% della loro barra 5m, con il print a meta' del range (q=0,50). Il caso
peggiore resta un fill da $74 del 18/07 al 21,9%: un sabato, nastro inesistente.
L'attrito non e' funzione della TAGLIA ma di taglia/volume-della-barra -- l'estrapolazione
lineare che prevedeva ~71% a questo capitale e' refutata dalla misura diretta.
NB non e' "assente", e' "non ancora testato": manca un fill grande in una barra sottile,
e il weekend e' dove TP01 fa il 38% del proprio gross.

  DIFETTO P1 RIPARATO: l'equity era CABLATA a 636.0 con un commento che diceva di
  leggerla dal watermark. Il giorno del versamento avrebbe stampato $636 sbagliando di
  3,3x proprio la sezione che esiste per dire QUANDO la misura scade. Riparata leggendo
  il watermark -- e poi riparata di nuovo, perche' i fill del campione sono stati
  eseguiti a conti diversi ($597-$2.067) e una base sola e' sbagliata comunque la si
  scelga. Ora la normalizzazione e' PER FILL, e a $600 riproduce il 22,0% originale.

DUE ERRORI MIEI, CATTURATI PRIMA DI PUBBLICARLI
- maschera VUOTA vestita da risultato: la prima stesura di r0825_capitale_fermo prendeva
  i giorni flat dalla serie DE-LUCKATA, ma `deluck` sottrae una costante e lo zero esatto
  sparisce -> maschera vuota, dinamico identico al book, e lo script ha stampato tabelle
  piene e un p-value. Lo ha rivelato solo la riga "0 = 0.0%".
  REGOLA: stampare la CARDINALITA' di una maschera prima di usarla.
- il meccanismo falso della (2), demolito dalla scomposizione per anno.

Suite: 730 passati, 1 fallito -- quello gia' noto di 5.9 (deriva dati, non codice).
Watermark del libro live sopravvissuto intatto alla suite (fixture autouse di stamattina).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B9UyJLHzR7EJzxR3iQ3RN1
2026-08-25 18:13:15 +00:00
Adriano Dal Pastro 14e1567125 docs: il cap per-asset non ha tetto assoluto — CLAUDE.md dichiarava la formula del fallback
CLAUDE.md 1 descriveva il guardrail live come `min($3.000, equity_osservata x 0.5)`.
Il codice dice altro (src/live/book._cap):

    if trusted:                      # equity reale leggibile
        return float(equity) * float(frac)          # <-- nessun min() con `fixed`
    return min(fixed, wm * float(frac)) ...         # <-- il $3.000 vive SOLO qui

Cioe' `max_notional_per_asset_usd` NON morde mai sul percorso normale: e' il tetto del
ramo di FALLBACK (equity illeggibile), e la riga lo spacciava per quello vivo. Stessa
classe del difetto 5.7 — una descrizione puntata su una configurazione diversa da quella
che gira.

Conseguenza da sapere, emersa da "cosa succede se arrivo a 3.000 USDC": nessun livello
di capitale cambia il profilo di rischio RELATIVO. Il book scala indefinitamente a leva
lorda massima 1,0x (0,40x al segnale corrente), e $3.000 non e' una soglia — il numero
compare in tre posti del progetto e nessuno scatta li':
  - max_notional_per_asset_usd: cap sul nozionale, non soglia di equity, non morde;
  - "la soglia $3k" di 3: decisione CHIUSA il 26/07, si riapre a $20k;
  - C* ~$3.000 del monitor XSR01: difetto noto (5.3), il pavimento vero e' $15-20k.

CODICE NON TOCCATO: il comportamento e' intenzionale e documentato nel docstring di
_cap (la "frontiera" del 03/07 esiste apposta perche' un deposito non resti strozzato).
Era sbagliata la descrizione, non la scelta. Verificato anche che il tetto sul PRODOTTO
del GATE SCALA-01 (n_asset x frac x scala x disaster_sl_pct = 0,30 <= 0,50) non dipende
dall'equity, quindi regge identico a ogni capitale.

Contesto: versamento di 1.400 USDC atterrato alle 11:03:35Z, equity $667,88 -> $2.066,96.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B9UyJLHzR7EJzxR3iQ3RN1
2026-08-25 11:07:11 +00:00
Adriano Dal Pastro 426735448e live: diagnosi del guasto venue, disaster-SL scoperto, isolamento per asset, cron :47
Nato da "stato trades" durante una manutenzione Deribit (system_maintenance 11051,
08:57-09:20 UTC). Contati i log invece che ricordarli: 1.499 giri dal 23/06, 3 con
manutenzione, 5 traceback duri — e 4 dei 5 sono il NOSTRO gateway, non il venue.
Le release Deribit escono il martedi' alle 09:00 UTC: il cron al :07 ci cadeva dentro
per costruzione (21/07, 11/08, 18/08, 25/08).

DIAGNOSI (src/live/venue_probe.py, nuovo)
Sonda l'API PUBBLICA Deribit senza gateway e senza credenziali, e classifica il guasto:
VENUE_MANUTENZIONE / VENUE_GIU / GATEWAY / IGNOTO. Prima ogni causa stampava la stessa
riga ("conto non leggibile (offline)") con nota di diagnosi CABLATA — P4 violata, e il
18/08 il codice 11051 era gia' dentro il processo senza arrivare a chi decideva la
gravita'. Parte solo dopo un guasto: sul percorso sano costa zero. P1 rispettata: la
firma 11051 si importa da venue_watch.is_maintenance, non si ridichiara.

P9: la manutenzione dentro lo slot declassa il titolo a info, ma solo su EVIDENZA della
sonda (mai sull'orologio) e solo dentro la durata annunciata — oltre, RIALZA. Un gateway
rotto di martedi' mattina resta al massimo.

DISASTER-SL SCOPERTO (src/live/execution.py)
ensure_disaster_sl cancella i bracket incoerenti PRIMA di ripiazzarne uno: fra le due
chiamate la posizione e' senza stop. Se il ripiazzamento sollevava, l'eccezione risaliva
a main() e il guasto peggiore aveva la faccia di un errore qualunque. Ora: due tentativi,
poi stato `naked`, distinto da `place-failed` (P5). Non e' "non sono riuscito a
proteggere": e' "ho tolto la protezione e non sono riuscito a rimetterla" -> allarme
massimo, mai declassato. La SEQUENZA non e' stata invertita: piazza-poi-cancella sembra
piu' sicuro ma "sembra" non basta con soldi veri senza misurarlo.

ISOLAMENTO PER ASSET (scripts/live/book_execute.py)
Il 21/07 un 502 dentro ensure_disaster_sl su BTC ha ucciso il giro intero: nel log ETH
non compare — ne' ribilanciato ne' verificato nella protezione. Ora un asset che esplode
non ferma il ciclo, e il giro degradato esce con codice 2.

CRON :07 -> :47
Il vincolo vero non era ":07" ma "fuori dai ~26s del minuto tondo" (rate-limit per-IP
auto-saturato dal collettore catena, misura del 30/07). Il :47 lo soddisfa e in piu' sta
fuori dallo slot di release. PREVISIONE DICHIARATA (M12): 3 delle 4 finestre osservate
sono rientrate entro l'ora -> il :47 ne avrebbe scavalcate 3 su 4; se martedi' prossimo
becca comunque la manutenzione, la previsione e' sbagliata.

WATERMARK AVVELENATO DAI TEST (tests/conftest.py, nuovo)
Trovato addosso: lanciando la suite, data/live/equity_seen.json passava a $5.000 e il giro
successivo mandava un allarme Telegram FALSO ("USCITA DI FONDI -86,6%"). Non cosmetico:
cap_fallback = min(cap_config, watermark x frac) sarebbe passato da $334 a $2.500/asset,
~7,5x di leva su un conto da $668, sul ramo eq_fallback che allerta e NON blocca — cioe'
i test potevano armare il pericolo che il watermark esiste per impedire. Riparato con una
fixture AUTOUSE, non per-test: chiedere a ogni autore di ricordarsene ha gia' perso il
26/07 e il 21/08. Resta esposto lo stesso errore su trades.db e book_executions.jsonl.

BLOCCATO, NON RINVIATO
Il fallback diretto ai privati Deribit richiede chiavi API create dall'operatore: in
locale esiste solo CERBERO_TOKEN. Il gateway resta un punto singolo di guasto non
aggirabile (CLAUDE.md 5.11). La sonda dice di chi e' il guasto, non lo aggira.

Test: 20 nuovi, nessuno tocca la rete; controllo positivo fatto (5 su 7 falliscono contro
il codice vecchio). Suite 730 passati, 1 fallito — quello gia' noto di 5.9 (deriva dati).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B9UyJLHzR7EJzxR3iQ3RN1
2026-08-25 10:27:36 +00:00
Adriano Dal Pastro 2751a7efd0 journal: la voce del giorno IN CORSO non si presenta piu' come una giornata
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>
2026-08-25 08:59:42 +00:00
Adriano Dal Pastro 11a2370027 journal: voci automatiche del 23-25/08
Giri notturni del cron. Il 23/08 e' stato rigenerato alle 00:36 del 24 col
giorno COMPLETO (24 giri invece di 20, equity $637.58, cumulato $+39.52):
la voce precedente era stata scritta alle 19:44 a giornata in corso.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 08:50:02 +00:00
Adriano Dal Pastro b237ad2e8b docs: compattazione di CLAUDE.md — 3458 -> 424 righe, memoria in docs/memory/
CLAUDE.md era arrivato a 310 KB (~80k token caricati a OGNI sessione) e la
sua funzione si era sdoppiata: era insieme il manuale operativo e l'archivio
di 69 filoni di ricerca. Le due cose hanno lettori diversi.

I 65 bullet-blocco sono stati spostati VERBATIM in docs/memory/ (nulla
riscritto). Verifica meccanica riga per riga prima del commit: 3272 righe
non vuote, 0 mancanti, 0 aggiunte, zero buchi e zero sovrapposizioni nella
copertura delle regioni estratte.

  10-sleeve-e-candidati.md    30 KB  TP01 XS01 VRP01 SKH01 GTAA01 XSR01
  20-ondate-e-scartati.md    126 KB  69 filoni, ogni scartato col suo perche'
  30-piano-capitale-fisco.md  59 KB  muri, versamenti, venue risk, fisco, prop
  40-produzione-e-deploy.md   66 KB  esecutore, tripwire, monitor, PRIIPs/UCITS
  50-dati-e-feed.md           14 KB  difetti del dato, catena opzioni
  60-metodo-e-gate.md          4 KB  i gate di altlib.py

In CLAUDE.md resta solo cio' che serve a non sbagliare una decisione: stato,
book live vs book di ricerca, i numeri da citare e quelli da NON citare,
7 decisioni vincolanti dell'operatore con "cosa le riapre", 6 gate
pre-registrati con la data, 9 debiti aperti non riparati, le regole di
prim'ordine (D/M/C/P/N, distillate dalle 113 righe che contenevano REGOLA),
IL DATO, metodologia, stack/struttura/comandi.

Tre fatti che erano sepolti in 3400 righe e ora stanno in testa: le TRE
baseline diverse che girano sotto il nome "libro 75/25" (spread piu' grande
di quasi tutti gli effetti misurati), il funding non modellato in nessun
backtest (-2,16%/anno), e che «N/N ancore» vale ~2 osservazioni.

Verificato prima del commit: 36/36 percorsi citati esistono su disco,
708 test collezionati, code fence bilanciati. Nessun file di codice toccato.

Convenzione aggiunta (§14) perche' il file non torni a crescere: quando un
risultato CAMBIA UNA DECISIONE si aggiorna CLAUDE.md; quando aggiunge
racconto, va in docs/memory/.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 08:50:02 +00:00
Adriano Dal Pastro 214599e0a8 docs: libro di bordo in CLAUDE.md + diario del 23/08
CLAUDE.md: bullet del libro di bordo (il difetto dell'ora dei fill, il DB a
tre fonti incrociate, i quattro livelli della pagina, l'analista e i suoi
limiti, le due riparazioni al notifier), piu' i file nuovi nella Struttura e
i quattro comandi nella sezione Comandi.

Diario `2026-08-23-libro-di-bordo.md`: la storia per esteso, i numeri del
libro live dall'arming (+$37,50 in 64 giorni, ma l'80% delle giornate a
equity invariata e tutto il P&L in sei giorni), i cinque difetti trovati dai
test e il ciclo di retroazione dell'agente.

Le due cose che un lettore futuro deve trovare scritte: che l'analisi in
prosa NON e' parte del registro (una guardia sui numeri non copre il
ragionamento — due errori su due giri con sonnet-5, nessuno con una cifra
nuova), e che il modello e' stato cambiato su un campione di due
osservazioni, quindi e' un tentativo di abbassare un tasso e non una
garanzia.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-23 19:50:56 +00:00
Adriano Dal Pastro 12dc04c969 analista: modello di default a opus-5
Sonnet-5 ha prodotto due errori di ragionamento in due giri: una frase su un
merge di cui nessuno gli aveva parlato, e un round-trip corretto sulla pagina
dichiarato inesistente, con una spiegazione inventata per il proprio dubbio.
Nessuno dei due contiene una cifra nuova, quindi numeri_non_supportati() non
li vede: e' il buco dichiarato quando la guardia e' stata scritta.

Primo giro con opus-5 sulla stessa giornata: nessuna affermazione inventata,
e le affermazioni numeriche verificate a mano contro il DB tornano tutte
(fill 1 contro 2/5/4/3 dei giorni precedenti, equity ferma a $597.12 dal 13
al 18/08, target BTC 189 -> 115). Resta un'imprecisione di nome: chiama
"target" un valore che nella fonte era la POSIZIONE. Il numero e' della
fonte, la classe di errore e' cambiata.

E ha prodotto una riformulazione che le regole non possono dare: la
concentrazione del P&L in 7 giorni ha una lettura alternativa altrettanto
compatibile — quei sette giorni sono anche gli UNICI in cui il libro e'
stato a mercato, quindi la finestra non separa l'edge dal beta a un rialzo
del 22-29%.

⚠️ Il campione e' di due osservazioni contro una: e' un tentativo di
abbassare un tasso, non una garanzia, ed e' scritto cosi' nel sorgente.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-23 19:47:11 +00:00
Adriano Dal Pastro 782c0ce4bc analista: manda l'analisi giornaliera su Telegram, con l'esito registrato
La notifica porta una testata coi numeri che si leggono senza aprire nulla
(equity, delta del giorno, cumulato, posizioni, leva, fill e round-trip) e
sotto l'analisi del modello.

Il trasporto Telegram e' pero' il punto singolo di guasto gia' misurato in
questo progetto: un tentativo, nessun retry, esito mai registrato, 6,9% di
invii persi (2/29). Su un messaggio al giorno sono ~25 messaggi persi
all'anno, quindi qui sono state fatte due delle tre riparazioni dichiarate
il 2026-08-23 e mai eseguite:

(a) `send(text, tentativi=1)` — retry OPZIONALE con backoff. Il default resta
    1, cosi' il comportamento e' invariato per tutti i chiamanti esistenti:
    alzarlo per tutti cambierebbe la latenza degli allarmi di venue_watch e
    book_execute su un percorso con soldi veri, e non e' una modifica da fare
    di straforo dentro un'altra funzionalita'. L'analista chiede 3.
(b) `ultimo_errore()` — il motivo si registra nel punto in cui l'eccezione
    veniva ingoiata, e NON sopravvive a un invio riuscito. Regola gia'
    codificata il 29/07 su un altro percorso e mai applicata al notifier.

La terza (marcare `alerted=True` solo a invio riuscito in venue_watch) cambia
il comportamento degli allarmi e resta una decisione dell'operatore.

L'esito finisce nel DB in tre stati: inviata / non configurato / FALLITA col
motivo. Un invio perso che non lascia traccia, il giorno dopo, non si
distingue da "non e' successo niente".

E se l'analisi manca, il messaggio parte lo stesso dicendo PERCHE': senza
quel ramo un guasto del modello si leggerebbe come una giornata senza nulla
da dire. Taglio a 4096 caratteri dichiarato, mai silenzioso; HTML del modello
neutralizzato.

708 test passano. Strategia, pesi, config INVARIATI.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-23 19:40:56 +00:00
165 changed files with 24380 additions and 3994 deletions
+684 -3348
View File
File diff suppressed because it is too large Load Diff
+117 -387
View File
@@ -1,423 +1,153 @@
# PythagorasGoal
Sistema di riconoscimento pattern frattali e predizione per il trading di criptovalute (BTC, ETH), ispirato al framework teorico di Serleto & Malanga (*Pythagoras Trading Prediction*).
Ricerca e esecuzione di strategie algoritmiche su BTC/ETH, con **un libro che gira con soldi veri**
su Deribit mainnet dal 20 giugno 2026.
## Obiettivo
> 🚨 **v2.0.0 — RESET del 2026-06-19. Tutto ciò che questo README diceva prima è archiviato in
> `Old/` e non è fidato.** L'intera libreria di strategie "validata out-of-sample" (le famiglie
> FADE/HONEST/PAIRS/TSMOM/SHAPE, i portafogli PORT01-06, gli Sharpe fra 6 e 10) era un **artefatto
> di uno storico contaminato**: print fantasma di un feed *testnet* più storico Binance/USDT.
> Ri-testate sul feed reale ricostruito da Deribit mainnet, **perdono ogni anno**. Documento di
> fondazione: `docs/diary/2026-06-19-deribit-history.md`.
>
> Questo README descrive il progetto **dopo** il reset. È stato riscritto il 2026-09-02, dopo
> essere rimasto fermo al 4 giugno — quindici giorni prima del reset — mentre pubblicava i numeri
> che il progetto aveva già dichiarato falsi.
Partendo da un capitale iniziale di €1.000, raggiungere un profitto medio di €50 al giorno entro 68 mesi, tramite un portafoglio di strategie algoritmiche poco correlate fra loro — mean-reversion, trend/rotazione e spread market-neutral — validate out-of-sample e fee-aware.
**L'autorità sui numeri e sulle decisioni è `CLAUDE.md`**, e il racconto completo sta in
`docs/memory/`. Questo file è l'ingresso, non la fonte.
## Risultati
---
> ⚠️ **Revisione 2026-05-28.** La famiglia squeeze-breakout (SQ/MT/ML/AD/CM/PD, con
> accuracy storiche dichiarate 76-82%) è stata **scartata**: quei numeri erano un
> **artefatto di look-ahead**. I backtest decidevano la direzione dalla candela di
> breakout `close[i]` ma entravano a `close[i-1]` — impossibile dal vivo. Sotto
> ingresso onesto (`close[i]`) e fee reali, l'edge sparisce e tutte perdono, anche
> a fee zero. Dettagli e prove: `scripts/analysis/oos_validation.py`.
## Cos'è vivo, adesso
Dopo una validazione **out-of-sample, fee-aware** di molte famiglie di strategie,
emergono cinque famiglie con edge netto reale, tutte radicate nella stessa lezione
(in cripto la **mean-reversion** funziona, la continuazione no) o nella diversificazione:
| | |
|---|---|
| libro live | **TP01 + SKH01 a 75/25**, nettati in software su una sola posizione per asset (50/50 BTC/ETH) |
| venue | Deribit mainnet, perpetual lineari USDC. Esecuzione **armata** dal 2026-06-20 |
| capitale | ~$2.050 (USDC + USDE), non i €2.000 nominali di nessun paper trader |
| cadenza | **oraria**, minuto `:47` (`scripts/cron_book.sh`) |
| leva | lorda ~0,28x su un tetto di 1,00x. Il cap non ha mai morso: **il vincolo è il segnale** |
| protezione on-book | un solo disaster-SL rotolante al 30% sulla posizione netta |
| Famiglia | Meccanismo | Strategie | Profilo (netto OOS) |
|----------|-----------|-----------|---------------------|
| **FADE** | mean-reversion intraday 1h (long/short, BTC/ETH) | MR01 Bollinger, MR02 Donchian, MR07 Return-reversal | Acc 52-55%, DD 18-34% |
| **HONEST** | long-only multi-regime multi-crypto | DIP01 dip-buy, TR01 EMA-trend, ROT02 dual-momentum | CAGR 31-56%, DD 15-27% |
| **PAIRS** | spread reversion *market-neutral* (2 gambe) | PR01 ETH/BTC, LTC/ETH, ADA/ETH, BTC/LTC, ETH/SOL | Sharpe 2.0-4.4, corr col mercato ~0.05 |
| **TSMOM** | time-series momentum multi-orizzonte | TSM01 (3/6/12m + risk-off) | diversificatore, DD 15-22% |
| **SHAPE** | ML walk-forward su feature di *forma* del prezzo | SH01 (LogisticRegression, orizzonte 12 barre) | diversificatore, corr +0.08 col resto |
**Non** sono nel libro live: XS01, VRP01, GTAA01, XSR01. Vivono nel portafoglio di **ricerca**
(paper, 5 sleeve), che è una serie diversa da quella che gira — quasi tutti i numeri di portafoglio
del progetto sono su quella, non su questa.
Tutti i numeri sono **netti** dopo fee realistiche (Deribit 0.10% RT single-leg, 0.20%
RT/coppia sui pairs), leva 3x, su finestra held-out. Le strategie sono robuste su griglia
parametri, sweep fee 0.00-0.20% RT e — per i pairs — validate con **walk-forward** e
config universale (niente cherry-picking).
## I numeri, con la loro lente
### Portafoglio combinato (la vera leva anti-drawdown)
Ogni numero va scritto con la lente e la banda che gli appartengono. La tabella completa
("non citare" ↔ "citare") è in `CLAUDE.md` §2; qui i tre che contano.
Le famiglie sono **quasi scorrelate fra loro** (~0.05). Combinandole in un unico
portafoglio equipesato il drawdown crolla sotto quello di ogni singola sleeve:
| grandezza | valore onesto |
|---|---|
| rendimento del libro live | **TWR +10,6%** dall'armamento, spezzato sul versamento del 25/08: +11,61% prima, 0,9% dopo |
| crescita di trading | **+$50** in 71 giorni, su 30 round-trip chiusi e $0,87 di fee totali |
| Sharpe del portafoglio di ricerca | **1,95** [1,81 2,12] full, **1,54** [1,11 1,91] hold-out |
| Portafoglio | CAGR | Max DD | Sharpe |
|-------------|------|--------|--------|
| FADE (6 sleeve) | ~46% | 8% | 3.9 |
| HONEST (3 sleeve) | ~46% | 13% | 2.2 |
| **MASTER** (FADE + HONEST, 9) | ~47% | **5%** | 4.2 |
| **MASTER + PAIRS + TSM01** (15) | ~67% | ~5% | ~6 |
| **PORT06 live** (17 sleeve, cap pairs 33%, leva 2×, config EXIT-16) | ~79% | **2.6%** | 7.8 (FULL) / 10.1 (OOS) |
⚠️ Il rendimento dell'equity **non** è la performance: il 96% della crescita del conto è un
bonifico. Chi legge una percentuale su questo progetto deve sapere se è un TWR o un rapporto fra
due saldi — è stato un difetto reale, riparato il 2026-09-02.
> 🔎 **Numeri sobri (anti-overfit).** L'OOS singolo cade nel regime favorevole 2024-25:
> i valori di Sharpe/DD sopra sono ottimistici di circa il 50%. Da pianificare per le
> decisioni: **Sharpe atteso ~5**, **worst-drawdown su 90 giorni ~6%**, profilo che regge
> a leva 2x con slippage raddoppiato. Configurazione raccomandata: equal-weight, leva 2x,
> con un cap sull'allocazione ai pairs (~30-35%, poiché concentrano ~57% del rischio).
> Tutto resta da confermare nel paper trading live.
## La riga che ordina tutto il resto
## Come funziona
> La ricerca ha smesso di essere il vincolo il 2026-07-26, e **sei ondate successive lo hanno
> confermato invece che ribaltarlo**. I vincoli sono **il capitale che entra** e **il conto che non
> sparisce**.
### MR01 — Bollinger Fade (mean-reversion)
Misurato: il miglior candidato nuovo vale **+0,046 €/giorno**; versare €500/mese invece di €250
porta la probabilità di arrivare al traguardo in 20 anni **dal 14% all'85%**. E €100/mese in più
equivalgono a **+4,07%/anno di drift**, cioè più di tutta la leva autorizzabile.
La strategia attiva sfrutta il fatto, emerso dai dati, che su BTC/ETH a 1h gli estremi
di prezzo **rientrano verso la media** più di quanto proseguano:
## Metodo — cosa deve superare una strategia nuova
1. **Bollinger Bands** (window `n`, `k` deviazioni standard) sul close.
2. **Entry** — quando il close esce *sotto* la banda inferiore → **long** (o *sopra* la superiore → **short**). Ingresso a `close[i]`, eseguibile dal vivo.
3. **Take-profit** alla media mobile (il rientro atteso).
4. **Stop-loss** a `sl_atr × ATR` oltre l'estremo; **time-limit** a `max_bars`.
Sei requisiti, nessuno negoziabile (`CLAUDE.md` §8, gate in `scripts/research/alt/altlib.py`):
Nessun look-ahead: direzione e livelli sono calcolati con dati fino a `close[i]`.
1. **ingresso eseguibile** — direzione e prezzo da dati fino a `close[i]`, mai l'estremo di una candela;
2. **backtest netto** dopo fee Deribit realistiche, più la leva;
3. **out-of-sample** held-out, robustezza su griglia, sweep fee;
4. **liquidità e plausibilità** — un edge su un book fermo o su wick fantasma non è un edge;
5. i **gate**: `marginal_vs_tp01` (Sharpe marginale, non assoluto), `study_family_honest` con
deflated-Sharpe ≥ 0,95, `day_boundary_robust`, `anchor_luck_band`, `weights_tilt_null`;
6. codice, test e diario.
### Le altre famiglie
Il progetto ha **73 regole di prim'ordine** (`CLAUDE.md` §6) — sul dato, sul metodo, sui costi,
sulla produzione e sul piano — ognuna pagata almeno una volta. La più
ricorrente, cinque occorrenze: *un sorvegliante deve derivare il proprio bersaglio dal codice
sorvegliato, mai ridichiararlo — un controllo puntato su una configurazione diversa da quella che
gira passa sempre, e non sta controllando niente.*
- **FADE** (oltre MR01): MR02 fada la rottura del canale Donchian verso il centro;
MR07 fada il movimento di barra estremo misurato in deviazioni standard dei
rendimenti. Stessa logica di reversione, indicatori indipendenti.
- **HONEST** (long-only, multi-crypto): DIP01 compra i dip estremi e rivende al
recupero; TR01 segue il trend con incrocio di EMA su un paniere; ROT02 ruota ogni
giorno sui tre asset col momentum più forte, andando in cash quando BTC è sotto la
sua media (risk-off). Coprono i regimi di trend e rotazione, complementari alle fade.
- **PAIRS** (market-neutral): scommette sul rientro verso la media del log-ratio fra
due cripto (z-score). Long su una, short sull'altra: l'esposizione netta al mercato è
quasi nulla (correlazione ~0.02), il che la rende un diversificatore eccellente.
- **TSMOM**: tiene gli asset con momentum positivo persistente su più orizzonti
(3/6/12 mesi), con overlay risk-off. Rende meno ma è poco correlato, utile in ensemble.
## Il dato
### Perché lo squeeze breakout è stato abbandonato
- **La verità è Deribit mainnet**, perché è dove si esegue. Binance è un audit indipendente, mai
un'ancora per "ripulire": è USDT, ~10 bps fuori, fino al 3% sotto depeg.
- Universo certificato: **solo BTC/ETH**, ogni timeframe. Gli alt sono esclusi.
- Lo storico si aggiorna **solo** con `rebuild_history.py` e si certifica **sempre** con
`certify_feed.py`. Il vecchio downloader è la causa del reset.
- La catena opzioni si **raccoglie** ogni ora (`collect_chain.py`, minuto `:25`): un'ora non
raccolta è persa per sempre.
L'ipotesi originale era opposta — *continuazione* dopo la compressione di volatilità
(Bollinger dentro Keltner → breakout direzionale). Su dati storici sembrava dare
76-82% di accuracy, ma era un **artefatto di look-ahead**: il backtest entrava a
`close[i-1]` con direzione decisa da `close[i]`. Replicando l'esecuzione reale
(ingresso a `close[i]`) l'edge collassa al ~47% (lancio di moneta) e i costi fanno
il resto. Il test sui breakout intra-barra a 5m conferma che il movimento *rientra*
subito (mean-reversion), giustificando MR01. Tutta la famiglia squeeze è in `scripts/waste/`.
### Lezione metodologica
Ogni nuova strategia deve passare: (1) **ingresso eseguibile** senza look-ahead,
(2) backtest **netto** dopo fee realistiche (0.10% RT Deribit), (3) validazione
**out-of-sample** + robustezza su griglia parametri + sweep fee. Strumenti in
`scripts/analysis/` (`strategy_research.py`, `oos_validation.py`, `intrabar_test.py`).
## Struttura progetto
## Struttura
```
PythagorasGoal/
├── src/
├── data/ # Download e gestione dati (Cerbero MCP + Binance)
├── fractal/ # Indicatori frattali: Hurst, Higuchi FD, self-similarity
│ ├── backtest/ # Motore di backtesting con fee e metriche
│ ├── strategies/ # Classe base Strategy ABC + indicatori condivisi
│ │ ├── base.py # Strategy, Signal, BacktestResult, YearlyStats
│ │ └── indicators.py # keltner_ratio, detect_squeezes, ema, atr, rv, corr
│ ├── live/ # Paper trading live su Deribit testnet
│ │ ├── multi_runner.py # Orchestratore multi-strategia (strategie + pairs)
│ │ ├── strategy_worker.py # Worker single-leg con stato persistente
│ │ ├── pairs_worker.py # Worker a 2 gambe per i pairs (market-neutral)
│ │ ├── strategy_loader.py # Import dinamico classi Strategy
│ │ ├── cerbero_client.py # Client HTTP per Cerbero MCP
│ │ ├── signal_engine.py # Squeeze + ML real-time (legacy) + validazione OOS
│ │ └── telegram_notifier.py
│ └── portfolio/ # Portafogli di prima classe (capitale condiviso, backtest + live)
│ ├── base.py # SleeveSpec, Portfolio (.backtest), load_active_portfolio
│ ├── weighting.py # Schemi di ponderazione: equal, cap, inverse_vol, cluster_rp, manual
│ ├── sleeves.py # Builder unificato equity-per-sleeve (fonte unica, parità report)
│ ├── ledger.py # PortfolioLedger: PnL/DD aggregati, persistenza e resume
│ └── runner.py # PortfolioRunner live (Cerbero v2, sizing, ribilancio giornaliero)
├── scripts/
│ ├── strategies/ # Strategie con edge validato OOS (FADE, HONEST, PAIRS, TSMOM + portafogli)
│ ├── portfolios/ # Definizioni PORT01-06 e report run() dei portafogli di prima classe
│ ├── waste/ # Strategie scartate (squeeze SQ/MT/ML/AD/CM/PD, MR03, ROT01, W01-W28)
│ └── analysis/ # Ricerca/validazione OOS fee-aware, gestione rischio, report
├── strategies.yml # Config multi-strategy paper trader
├── data/
│ ├── raw/ # Parquet OHLCV (gitignored, ~70 MB)
│ └── regime/ # DVOL + funding (Deribit mainnet) + cache feature regime (gitignored)
├── VERSION # versione semver (cotta nell'immagine, mostrata nei msg Telegram)
├── docs/
│ ├── diary/ # Diario di ricerca giornaliero
│ └── specs/ # Specifiche di design
├── Dockerfile
├── docker-compose.yml
└── pyproject.toml
src/
data/downloader.py load_data(asset, tf) sui parquet certificati
strategies/ trend_portfolio.py (TP01) · skyhook.py (SKH01) · base · indicators
portfolio/ portfolio.py (N sleeve + weights_tilt_null) · sleeves.py · gtaa.py
backtest/harness.py backtest onesto, senza look-ahead
live/ book.py (esecutore netto) · deribit · livefeed · usde
venue_watch · venue_probe · venue_news · monitor_health · scale_watch
tradesdb · journal · analista · notifier · cli
scripts/
live/ book_execute · trades_db · journal · analista · balance_watch
paper_* (forward-monitor) · usde_watch · usde_convert · fee_watch
research/ r<data>_*.py — un file per esperimento, harness in alt/altlib.py
analysis/ rebuild_history · certify_feed · audit_feed · multi_source_check
cron_{book,daily,chain,balance,usde,opt_snapshot,vol_term}.sh
docs/
memory/ LA MEMORIA — 6 file, indicizzati in testa a CLAUDE.md
research/ RESULTS-0822 (§1-77, un registro per filone) · BRIEF-0822 · SPEC-scale-key
diary/ una voce per esperimento (144)
journal/ libro di bordo, una voce al giorno, 4 livelli per provenienza
tests/ 1008, tutti verdi
Old/ archivio pre-reset — consultabile, non fidato
```
## Strategie attive
Le strategie single-asset estendono `src.strategies.base.Strategy`
(`generate_signals() → backtest()`); i pairs hanno un worker dedicato a 2 gambe.
| Codice | Script | Famiglia | Descrizione |
|--------|--------|----------|-------------|
| **MR01** | `MR01_bollinger_fade.py` | FADE | Fada la banda di Bollinger, TP alla media, SL ad ATR |
| **MR02** | `MR02_donchian_fade.py` | FADE | Fada la rottura del canale Donchian, TP al centro |
| **MR07** | `MR07_return_reversal.py` | FADE | Fada il movimento di barra estremo (z dei rendimenti) |
| **DIP01** | `DIP01_dip_reversion.py` | HONEST | Dip-buy long-only su z-score estremo |
| **TR01** | `TR01_ema_trend.py` | HONEST | EMA 20/100 trend-following su paniere cripto (4h) |
| **ROT02** | `ROT02_dual_momentum.py` | HONEST | Rotazione cross-sectional top-3 + risk-off (1d) |
| **PR01** | `PR01_pairs_reversion.py` | PAIRS | Spread reversion market-neutral su 5 coppie |
| **TSM01** | `tsmom_research.py` | TSMOM | Time-series momentum multi-orizzonte + risk-off |
| **SH01** | `SH01_shape_ml.py` | SHAPE | LogisticRegression walk-forward su 17 feature di forma, orizzonte 12 barre (diversificatore) |
Le fade applicano tre protezioni live: un **filtro trend** (`trend_max`/`ema_long`,
salta i segnali col prezzo troppo esteso rispetto alla EMA200), un **loss-guard Hurst**
(`hurst_max=0.55`, salta i segnali in regime persistente/trending dove si concentrano gli stop-loss
— dimezza il drawdown del portafoglio, calcolato dalle sole close) e l'**EXIT-16 close-confirm SL**
(`sl_confirm_atr=0.5`, 2026-06-04: lo stop scatta solo se la barra *chiude* oltre `sl ∓ 0.5·ATR14`
gli stop intrabar da wick erano falsi negativi, l'overshoot che buca lo stop è proprio il movimento
che la fade fada; a livello PORT06 porta l'OOS Sharpe da 8.82 a 10.06). Più un filtro `min_tp_frac`
che scarta i micro-scalp col take-profit entro il costo delle fee. Le tre protezioni sono
complementari: Hurst toglie il regime tossico, il trend-filter gli ingressi sovra-estesi, il
close-confirm i falsi stop. Portafogli pronti: `PORT01`
(honest), `PORT02` (fade), `PORT03` (master fade+honest), **`PORT06`** (master esteso, default live).
**Scartate** (in `scripts/waste/`): la famiglia squeeze (SQ01-04, ML01, MT01, PD01,
CM01, AD01 — artefatto di look-ahead), MR03 Keltner (debole/ridondante con MR01) e
ROT01 (dominata da ROT02).
### Comandi utili
## Comandi
```bash
# Backtest di una strategia
uv run python scripts/strategies/MR01_bollinger_fade.py
uv run python scripts/strategies/PR01_pairs_reversion.py
# Ricerca e validazione fee-aware out-of-sample
uv run python scripts/analysis/strategy_research.py # screening famiglie + deep-dive fade
uv run python scripts/analysis/strategy_research_v2.py # MR02 / MR03 / MR07
uv run python scripts/analysis/oos_validation.py # perche' la famiglia squeeze e' scartata
uv run python scripts/analysis/pairs_research.py # ricerca + verifica no-look-ahead dei pairs
# Gestione rischio, combinazione, report
uv run python scripts/analysis/risk_management.py # filtro trend + portafoglio fade
uv run python scripts/analysis/combine_portfolio.py # combinare fade + honest
uv run python scripts/analysis/combine_v2.py # master esteso con pairs + TSM01
uv run python scripts/analysis/report_families.py # report per anno di tutte le famiglie
# Validazione dei worker live (replay == backtest)
uv run python scripts/analysis/validate_worker_mr01.py # worker single-leg su MR01
uv run python scripts/analysis/validate_worker_pairs.py # worker a 2 gambe sui pairs
uv run python scripts/analysis/live_smoke_pairs.py # smoke test feed live reale dei pairs
uv sync # dipendenze
uv run python scripts/analysis/rebuild_history.py --asset BTC ETH # storico da Deribit mainnet
uv run python scripts/analysis/certify_feed.py # certifica i feed
uv run python scripts/live/trades_db.py --report # stato del libro + TWR
uv run python scripts/live/journal.py # voce del giorno
uv run python scripts/live/analista.py --secco # analisi del giorno, senza salvare
uv run python scripts/portfolio/run_portfolio.py # portafoglio di ricerca
uv run pytest # 1008 test
```
## Paper Trading Live
Ogni script di `scripts/live/` accetta `--help` e **rifiuta un flag che non conosce** (esce 2): fino
al 2026-09-02 un flag sbagliato eseguiva l'azione di default, e su tre script quell'azione scriveva.
Il multi-strategy runner esegue N strategie in parallelo su dati live da Cerbero MCP,
ognuna con €1000 USDC virtuali indipendenti. Gestisce due tipi di worker:
## Gate aperti
- **Single-leg** (`strategy_worker.py`): per le strategie direzionali. Se un `Signal`
porta `tp`/`sl`/`max_bars` in `metadata` (come le fade), chiude su take-profit /
stop-loss / time-limit; altrimenti usa il fallback `hold_bars`/stop -2%.
- **Due gambe** (`pairs_worker.py`): per i pairs market-neutral. Apre long su una gamba
e short sull'altra, esce sul rientro dello z-score o per time-limit, conta le fee su
entrambe le gambe. Validato: il replay storico coincide *esattamente* col backtest.
Un gate si decide **alla data**, coi criteri scritti **prima**. Elenco completo in `CLAUDE.md` §4.
### Avvio
| gate | data | dove punta oggi |
|---|---|---|
| STATARB | 2026-09-27 | ritiro (Sharpe 1,61 sulla serie vera) |
| SCALA-01 — leva 1,00 → 1,25 | non prima del 2026-10-01 | chiave costruita e **inerte**; 9 condizioni, 2 fatte |
| XSR01 | 2026-10-23 | sotto la lente rendita il meccanismo vale ~0 |
| DVOLSPREAD | kill 2026-10-24 | serie 4,41 |
| GTAA01 tenere/bloccare | al book a $15k | contributo reale +0,10/+0,12 di Sharpe, non incassabile sotto la soglia |
```bash
# Locale
uv run python -m src.live.multi_runner
## Obiettivo, e cosa lo rende difficile
# Docker
docker compose up -d
```
Il target dichiarato è **€50/giorno partendo da €1.000**. Non è raggiungibile a questo capitale:
servono ~$313k (banda [$187k $1,14M]) o **€1.733/mese per dieci anni** per averlo al 90% di
probabilità. La leva non è la scorciatoia — quella difendibile vale quanto €100-150/mese di
versamento. La via è target-vol, capitale e tempo.
### Configurazione
Le strategie attive sono definite in `strategies.yml`:
```yaml
defaults:
capital: 1000
position_size: 0.15
leverage: 3
strategies: # strategie single-leg
- name: MR01_bollinger_fade
asset: BTC
tf: 1h
enabled: true
params: { bb_window: 50, k: 2.5, sl_atr: 2.0, max_bars: 24, trend_max: 3.0, ema_long: 200 }
pairs: # strategie a 2 gambe (market-neutral)
- name: PR01_pairs_reversion
a: ETH
b: BTC
tf: 1h
enabled: true
params: { n: 50, z_in: 2.0, z_exit: 0.75, max_bars: 72, jump_max: 0.08 }
```
Per aggiungere una strategia: nuova riga in `strategies.yml` (sezione `strategies` o
`pairs`), poi `docker compose restart`. Lo storico delle strategie esistenti rimane intatto.
### Persistenza
Ogni strategia ha la sua directory in `data/paper_trades/`:
```
data/paper_trades/
MR01_bollinger_fade__BTC__1h/
trades.jsonl # Storico trade append-only
status.json # Stato corrente (resume al restart, include tp/sl/max_bars)
```
Notifiche Telegram per ogni trade (richiede `TELEGRAM_BOT_TOKEN` e `TELEGRAM_CHAT_ID` in `.env`).
## Paper Trading a Portafoglio
Accanto al multi-strategy runner originale — in cui ogni strategia gestisce autonomamente il proprio conto virtuale da €1.000 — il progetto dispone ora di un **paper trader a portafoglio** (`src/portfolio/`) che tratta l'insieme delle strategie come un unico organismo con un capitale condiviso.
### Come funziona
La definizione di un portafoglio (`SleeveSpec` + schema di peso) ha due facce sulla stessa sorgente dati:
- **Backtest** (`.backtest()`): ricostruisce le equity-curve di ogni sleeve tramite il builder unificato in `sleeves.py`, le pondera secondo lo schema scelto e calcola le metriche aggregate (CAGR, Sharpe, max DD). La parità con i report prodotti da `report_families.py` è garantita dalla fonte unica.
- **Live** (`PortfolioRunner`): ogni ora il runner scarica le candele aggiornate via Cerbero v2, calcola i pesi correnti, avvia i worker appropriati per ogni sleeve attiva e registra il PnL aggregato nel ledger (`data/portfolios/{code}/`). Il ledger persiste tra i riavvii.
### Schemi di ponderazione
Il modulo `weighting.py` mette a disposizione cinque schemi: `equal` (default), `cap` (tetto per famiglia — p.es. `pairs: 0.33` per limitare la concentrazione), `inverse_vol` (pesi inversamente proporzionali alla volatilità storica), `cluster_rp` (equal tra cluster naturali poi inverse-vol all'interno del cluster) e `manual` (pesi liberi). Lo schema si specifica in `portfolios.yml` insieme al codice portafoglio e alla leva.
### Portafoglio di default: PORT06
La configurazione raccomandata è **PORT06** (`scripts/portfolios/PORT06_master_shape.py`): portafoglio master esteso che include tutte e sei le famiglie (FADE, HONEST, PAIRS, TSMOM, SHAPE), con schema `cap` che limita i pairs al 33% del capitale per moderare la loro concentrazione di rischio. Backtest canonico (dati al 2026-05-28): Sharpe 6.47 (FULL) / 8.82 (OOS), drawdown massimo 4.10% (FULL) / 1.30% (OOS), leva 2×; **con la config live attuale (EXIT-16 close-confirm): Sharpe 7.84 / 10.06, DD 2.60% / 1.15%**.
### Scope live
Il runner esegue **tutti e 17 gli sleeve** di PORT06: **fade** (MR01, MR02, MR07 × BTC/ETH),
**honest** (DIP01, TR01-basket 4h, ROT02-rotation 1d), **pairs** (PR01, cinque coppie),
**TSMOM** (TSM01 1d) e **shape** (SH01 × BTC/ETH). Worker dedicati: `StrategyWorker` (single-leg, fade/
dip/**shape**), `PairsWorker` (2 gambe), `BasketTrendWorker`, `RotationWorker`, `TsmomWorker`. Il runner
fetcha 1h da Cerbero v2 e resampla a 4h/1d; il pool di capitale, il ribilancio giornaliero e il ledger
sono validati == backtest.
> **SH01 (2026-06-01):** gira come `StrategyWorker` normale (il walk-forward è interno a
> `generate_signals`). Il vecchio `MLWorkerWrapper` usava il `SignalEngine` **squeeze scartato** —
> rimosso. **Loss-guard Hurst (2026-06-02):** le fade saltano i segnali in regime persistente
> (rolling-Hurst ≥ 0.55), dove si concentrano gli stop-loss — dimezza il drawdown del portafoglio
> (FULL 4.1%→2.4%; stop-loss fade 67% in numero, perdite totali 68%). Calcolato dalle sole close,
> attivo live (`hurst_max` nei params). Il report orario su Telegram **monitora lo stop-rate fade
> prima/dopo l'attivazione** e dà il verdetto automatico quando il campione è sufficiente.
### Esecuzione reale (shadow, Deribit testnet)
Sette sleeve single-leg — le **6 fade** (MR01/MR02/MR07 × BTC/ETH) e **DIP01** (dal 2026-06-04) —
eseguono ordini **reali su Deribit testnet** accanto al fill simulato (*shadow*: il sim resta la
verità che guida le decisioni; il reale misura la fattibilità). Punti chiave:
- **Strumenti lineari USDC** (`BTC_USDC`/`ETH_USDC-PERPETUAL`): payoff lineare = matematica del
backtest; fee e PnL in USDC. Quantizzazione `Decimal` di amount (step) e prezzi (tick).
- **Take-profit reale = limit reduce-only AL livello** (v1.0.7): piazzato all'apertura, copre la
sola quota del worker (gli strumenti sono condivisi fra worker e nettati per conto); alla
chiusura il worker cancella il resting, riconcilia i fill dal trade history per `order_id` e
chiude a market solo il residuo. Fix della divergenza misurata: il market-on-poll usciva
+235 bps oltre il livello TP. Fill da resting = fee maker (~0%).
- **Stop-loss close-confirm** (v1.1.0): uscita al close che sfonda il livello → market
reduce-only al poll (nessun ordine stop sul book, per scelta: i trigger Deribit generano un
nuovo order_id allo scatto, non verificabile, e i wick non devono stoppare).
- **Verifica sul trade** (order_id in `get_trade_history`), fee reali dai `trades[]`, ledger
reale parallelo persistito (`real_capital`), eventi `REAL_OPEN`/`REAL_TP_RESTING`/`REAL_CLOSE`
nel log + alert Telegram (`REAL_EXEC_LIVE`, `REAL_OPEN_FAIL`).
- Config in `portfolios.yml``overrides.execution {enabled, sleeves, instruments}`.
**Pairs/rotation/TSMOM/shape restano simulati**: i pairs richiedono un executor a 2 gambe
(leg-risk), i multi-asset un rebalance-to-target; roadmap nel diario.
### Versione & deploy
Ogni deploy ha una **versione** (file `VERSION`, semver) che compare nei messaggi Telegram (notifiche
trade + report orario), così correli ogni messaggio al codice che l'ha generato. Il sorgente è **cotto
nell'immagine** → per aggiornare il live serve un **rebuild**, non un semplice restart:
```bash
./scripts/deploy.sh # bump patch (1.0.0 → 1.0.1) + commit + rebuild + ricrea container
./scripts/deploy.sh minor # 1.0.x → 1.1.0
```
Il volume `data/` persiste tra i deploy → i worker fanno RESUME dello stato (capitale, posizioni aperte).
### Avvio del paper trader a portafoglio
```bash
# Backtest del portafoglio di default (PORT06)
uv run python scripts/portfolios/PORT06_master_shape.py
# Paper trading live a portafoglio
uv run python -m src.portfolio.runner
# Report orario su Telegram (stato + stop-rate fade prima/dopo loss-guard) — via cron
uv run python scripts/portfolios/hourly_report.py
# Smoke test del data layer Cerbero v2
uv run python scripts/analysis/smoke_portfolio.py
```
## Setup
```bash
# Clona e installa
git clone <repo-url> && cd PythagorasGoal
uv sync
# Scarica dati storici (~70 MB)
uv run python -m src.data.downloader
# Backtest strategia attiva
uv run python scripts/strategies/MR01_bollinger_fade.py
# Paper trading live
uv run python -m src.live.multi_runner
```
### Requisiti
- Python ≥ 3.11
- [uv](https://docs.astral.sh/uv/) come package manager
- Accesso a Cerbero MCP (`cerbero-mcp.tielogic.xyz`) per dati Deribit live
- Docker (opzionale, per deploy su VPS)
## Dati
| Asset | Timeframe | Copertura |
|-------|-----------|-----------|
| BTC, ETH | 5m / 15m / 1h | 2018-01 → oggi |
| SOL, LTC, ADA, XRP, BNB, DOGE | 15m / 1h | 2019-2022 → oggi (variabile per asset) |
Fonte primaria: perpetual Deribit via Cerbero MCP. Fallback: Binance spot via ccxt.
Formato: Apache Parquet (in `data/raw/`, gitignored).
> **Nota sul naming Deribit (per il feed live).** I major sono perpetui *inverse*
> (`BTC-PERPETUAL`, `ETH-PERPETUAL`); gli altcoin sono perpetui *lineari USDC*
> (`SOL_USDC-PERPETUAL`, `LTC_USDC-PERPETUAL`, …) con storia dal 2022. Attenzione:
> `LTC-PERPETUAL`/`ADA-PERPETUAL` non esistono e `SOL-PERPETUAL` restituisce dati
> errati — per gli altcoin usare sempre la forma `_USDC-PERPETUAL`.
### Discovery & validazione strumenti
`src/data/instruments.py` scopre e **valida** gli strumenti disponibili sugli
exchange implementati — **Deribit** e **Hyperliquid** (esclusi Alpaca/stocks e
**Bybit**, feed testnet inaffidabile). Ogni perpetuo viene testato sui dati
storici realmente raccoglibili: esistenza, congruenza OHLC, contratto non-morto,
liquidità e **congruenza prezzo cross-exchange** (mediana per base-coin, tolleranza
5%) — così feed farlocchi e contratti sbagliati (es. `SOL-PERPETUAL`=9.6) vengono
scartati. Il risultato è `data/instruments_registry.json` (strumenti validi +
timeframe + data d'inizio).
**Solo gli strumenti validati possono essere scaricati**: il downloader ha un gate
(`_download_cerbero_range`) che rifiuta quelli non nel registry. Rigenera con:
```bash
uv run python -m src.data.instruments
```
Simboli Deribit: BTC/ETH = `<COIN>-PERPETUAL` (inverse); altcoin =
`<COIN>_USDC-PERPETUAL` (lineari USDC). Registry attuale (testnet): Deribit 18/106
validi (major liquidi, BTC dal 2018), Hyperliquid 66/74.
## Riferimenti
- Serleto, L. & Malanga, C. — *Pythagoras Trading Prediction* (2024)
- Serleto, L. & Malanga, C. — *Libro dei Frattali* (2024)
## Licenza
Uso privato. Non destinato alla distribuzione.
**Onestà prima di tutto**: nessun numero va creduto finché non è netto fee, out-of-sample, robusto
su griglia, e su dati certificati, liquidi ed eseguibili. Il resto di questo repo esiste per rendere
quella frase verificabile invece che dichiarata.
+16 -2
View File
@@ -1,6 +1,6 @@
{
"_nota": "Config esecuzione LIVE del BOOK DERIBIT (TP01+SKH01 nettati in software). execution_enabled=true + --execute -> ordini REALI. ARMATO 2026-06-23: esecutore scripts/live/book_execute.py via cron ORARIO scripts/cron_book.sh (SKH01 e' a 230m). disaster-SL on-book -30% sulla posizione netta. Tutto flat all'arming -> nessun ordine finche' un segnale non arma.",
"_nota_cap": "Cap notional per-asset DINAMICO (frontiera 2026-07-03): con max_notional_per_asset_frac=0.5 il cap = equity/2, cosi' cresce col capitale e un deposito non resta strozzato. AGGIORNATO 2026-07-26: max_notional_per_asset_usd alzato 300 -> 3000 in previsione del versamento (EUR 5.000 + 500/mese -> equity ~$6.050, equity/2 ~$3.025). \u26a0\ufe0f Alzarlo NON e' pericoloso perche' dal 2026-07-26 il cap di FALLBACK (equity reale non leggibile) e' min(questo valore, ultima_equity_reale_osservata * frac) \u2014 vedi src/live/book._cap e il watermark data/live/equity_seen.json. Senza quel legame, un cap da $3.000 su un conto da $597 avrebbe permesso $2.000 di nozionale lordo = 3.35x di leva nel momento peggiore. Questo rende inutile l'azione manuale 'al deposito alzare il cap' (pre-registrata 2026-07-02).",
"_nota_cap": "Cap notional per-asset DINAMICO (frontiera 2026-07-03): con max_notional_per_asset_frac=0.5 il cap = equity/2, cosi' cresce col capitale e un deposito non resta strozzato. AGGIORNATO 2026-07-26: max_notional_per_asset_usd alzato 300 -> 3000 in previsione del versamento (EUR 5.000 + 500/mese -> equity ~$6.050, equity/2 ~$3.025). ⚠️ Alzarlo NON e' pericoloso perche' dal 2026-07-26 il cap di FALLBACK (equity reale non leggibile) e' min(questo valore, ultima_equity_reale_osservata * frac) vedi src/live/book._cap e il watermark data/live/equity_seen.json. Senza quel legame, un cap da $3.000 su un conto da $597 avrebbe permesso $2.000 di nozionale lordo = 3.35x di leva nel momento peggiore. Questo rende inutile l'azione manuale 'al deposito alzare il cap' (pre-registrata 2026-07-02).",
"execution_enabled": true,
"max_notional_per_asset_usd": 3000,
"max_notional_per_asset_frac": 0.5,
@@ -9,5 +9,19 @@
"_nota_stale": "Staleness-gate (2026-07-25): se l'ultima barra del feed certificato e' piu' vecchia di max_data_age_days, book_execute NON invia ordini e allerta su Telegram. Il 2026-07-14 il book compro' ETH con il feed fermo da 6 giorni (conto online e posizione leggibile -> gli altri due gate non scattavano). Follow-up raccomandato nel diario 2026-07-15-feed-freeze, ora cablato.",
"max_data_age_days": 2,
"_nota_skh_feed": "Freschezza del feed 5m usato per il segnale SKH01 (2026-07-26). fresh_5m ricade sul feed certificato IN SILENZIO se il fetch pubblico Deribit fallisce, e il certificato si rigenera 1x/giorno: senza controllo la latenza d'uscita di SKH01 passa da ~1h a ~1 giorno senza segnalazione. Sopra soglia book_execute ALLERTA e NON blocca (bloccare fermerebbe anche TP01, nettato sullo stesso strumento, per un guasto di rete). Diario 2026-07-26-t1-esecuzione-skh-live.md.",
"skh_feed_max_age_min": 30
"skh_feed_max_age_min": 30,
"_nota_usde": "Collaterale USDE a rendimento (test di eligibilita' 2026-08-26: 500 USDE, verdetto entro il 29/08 — diario 2026-08-26-usde-analisi). L'autorita' che LEGGE questa sezione e' src/live/usde.py; la usano shadow._collaterale_usde (equity oraria del book) e scripts/live/usde_watch.py (sorveglianza giornaliera 12:35 UTC). Criteri delle soglie, dichiarati (P6): depeg_warn 0.99 = fuori dalla banda operativa dello spot (~3 bps) e oltre il clamp +-0.5% per fonte dell'indice usde_usdc — a quel prezzo non e' rumore di book; depeg_crit 0.95 = meta' del buffer di haircut (10%) consumata. quota_target 0.70 (CHIAVE TOLTA il 2026-09-10, issue #7: il bersaglio operativo e' DERIVATO dalle bande del cuscino, vedi usde._nota_cuscino — quanto segue e' la storia della decisione) = la quota DECISA dall'operatore il 2026-08-30 (la decisione di quota che il gate USDE-01 apriva; anticipata di un giorno sul rinvio al 31/08, su richiesta esplicita dell'operatore \"porta in usde tutto il capitale che non viene usato\"). Il 70% NON e' un argmax (M8): e' il massimo compatibile col CUSCINO DI REGOLAMENTO, cioe' il vincolo che r0830_usde_quota non aveva guardato — il P&L e il funding dei perp USDC-lineari si regolano in USDC, non nel collaterale, quindi a quota alta il saldo USDC va negativo alla prima perdita del libro e Deribit lo finanzia a interesse. Il cuscino richiesto e' il disaster-SL sulla massima esposizione lorda: n_asset x frac x disaster_sl_pct = 2 x 0.5 x 0.30 = 30% dell'equity, che lascia esattamente il 70%. quota_max_frac 0.50 = tetto di ALLERTA sulla quota USDE/equity totale (N4: la quota e' l'unica leva contro il rischio emittente, -100% = 25 anni di resa, non recuperabile). Alzato a 0.85 il 2026-08-30 in previsione della quota al 70% e RIMESSO A 0.50 lo stesso giorno perche' il 70% pareva irraggiungibile; RIMESSO A 0.85 il 2026-09-06, quando la quota e' arrivata al 68,7% su autorizzazione dell'operatore e la sonda non ha trovato tetto. 🚨 venue_cap_frac: il 06/09 il tetto NON C'ERA (null, vedi venue_cap_misurato) — quanto segue e' la storia del 30-31/08, tenuta perche' il tetto puo' tornare: venue_cap_frac 0.318 = TETTO DEL VENUE sull'USDE, misurato e NON documentato da Deribit (ne' 'Cross collateral specifications' ne' 'Yield-generating collateral' prevedono un limite sulle quantita' detenibili; il Cap ETHENA che esiste diluisce il TASSO a livello di exchange, non limita gli acquisti). Ogni acquisto oltre il tetto e' rifiutato con `not_enough_funds_in_currency` pur avendo $1.400 disponibili: messaggio FUORVIANTE. E' una FRAZIONE dell'equity, non un livello: provato il 31/08 lasciando scendere l'equity di $8, il tetto e' sceso con lei. ⚠️ DIPENDE DAL MODELLO DI MARGINE, ma pochissimo — misurato a saldo neutro sotto entrambi: SEGREGATO S:SM [643,18-644,18) con equity $2.055,56 = 31,29-31,34%; CROSS X:SM [654,18-655,18) con equity $2.054,90 = 31,84-31,88%. Il passaggio a cross ha comprato +0,55pp = **+11 USDE (~$11)**: il modello entra nel tetto, ma non lo spiega. ⇒ la quota resta ~31-32% sotto qualunque configurazione e il 70% di quota_target NON e' raggiungibile (manca un fattore ~2,2x). Il valore 0.318 e' il bordo BASSO del bracket CROSS, che e' il modello attivo: fa fallire il piano PRIMA dell'ordine. Se si torna a S:SM va rimesso a 0.312. haircut 0.05 — ✅ DIVERGENZA CHIUSA il 2026-08-31 (era 0.10, SBAGLIATO). La pagina margini del conto non espone l'haircut come numero: si RICAVA per differenza fra le due righe, perche' il modello CROSS conta l'USDE scontato e il SEGREGATO non lo conta affatto. Contributo dell'USDE al cross $610,81 su $643,05 di valore -> 5,0137%: l'ipotesi 5% torna a $0,09, la 10% sbaglia di $32,06. Riproducibile: `scripts/research/r0831_margini_conto.py` (N11). Il 26/08 il 10% fu registrato come 'verificato sul venue' senza lasciare traccia di come, e non lo era. 🚨 SCOPERTO NELLA STESSA LETTURA, e vale piu' dell'haircut: il modello di margine ATTIVO e' **Segregated: Standard Margin (S:SM)**, NON cross-collateral. Nella tabella del modello attivo l'USDE **non compare**: non fa margine per i perp USDC-settled del book. Percio' l'haircut oggi non si applica affatto — ed e' esattamente il motivo per cui la misura del 31/08 trovava $0,0000 accantonati. Passare a X:SM aggiungerebbe $610,87 di margine utilizzabile: e' una decisione dell'operatore, non un refactor, e porta con se' la meccanica cross (collateral fee 0,05%/giorno sul saldo negativo, ribilanciamento automatico).",
"usde": {
"index_name": "usde_usdc",
"haircut": 0.05,
"_nota_cuscino": "Dal 2026-09-10 (issue #7) la quota USDE NON e' un numero scelto: e' DERIVATA dal cuscino USDC di regolamento (n_asset x frac x disaster_sl_pct = 30% dell'equity) e da queste tre frazioni del cuscino, lette da src/live/usde.py per scripts/live/cuscino_watch.py (cron :53). cuscino_margine_frac 0.20 = bersaglio unico di ogni conversione: quota = 1 - 0.30 x 1.20 = 0.64 (il vecchio quota_target 0.70 lasciava slack ZERO: ogni ora in perdita avrebbe venduto). cuscino_preavviso_frac 0.10 = sotto, allerta. cuscino_riacquisto_frac 0.40 = sopra, si RICOMPRA USDE fino al bersaglio (un bonifico alza lo slack di 0,7 x importo e fa scattare il riacquisto da solo). Isteresi [0 ; 0.40] x cuscino con bersaglio a 0.20: oscillare richiede +-6% di equity di slack, cioe' +-8,6% di equity USDC (slack = 0,7 USDC - 0,3 USDE). Soglie DICHIARATE, non ottimizzate (M8): un giro costa ~6 bps sull'importo mosso. Ordine obbligato: 0 < preavviso < margine < riacquisto (usde.bande_cuscino lo verifica).",
"cuscino_preavviso_frac": 0.1,
"cuscino_margine_frac": 0.2,
"cuscino_riacquisto_frac": 0.4,
"venue_cap_frac": null,
"venue_cap_misurato": "2026-09-06: NESSUN tetto fino al 68,7% (fermata dal cuscino di regolamento al 70%, non dal venue): +2.434 USDE in 39 ordini a saldo crescente, zero rifiuti (r0906_usde_tetto_sonda.py). Il 30-31/08 il tetto c'era: [643-644) e [654-655) USDE su ~$2.055 = 31,3-31,9%. Non spiegato: e' SPARITO, non e' scalato. null = nessun tetto noto; usde_convert si affida al chunk+backoff sui rifiuti",
"quota_max_frac": 0.85,
"depeg_warn": 0.99,
"depeg_crit": 0.95
}
}
+96
View File
@@ -0,0 +1,96 @@
# 2026-08-23 — Libro di bordo: i trade allineati col tempo, il giornale, l'analista
**Libro, pesi, cron di strategia, config: INVARIATI. Nessun ordine.** Tutto ciò che segue è
sorveglianza e registrazione.
## La domanda che l'ha fatto nascere
*«I trade vengono salvati?»* — sì, in `data/live/book_executions.jsonl`. Ma non allineati col
tempo: `book_execute.py:213` scriveva
```python
ts_utc = str(pd.Timestamp(r['last_data'])) # la data della BARRA DI SEGNALE
```
19 righe su 19 a `00:00:00`. E non era cosmetico: **un trade risultava registrato sei giorni prima
di essere eseguito** — ETH 0.04 @ 1.869,74, fill vero il **2026-07-14 alle 14:00**, scritto
**08/07**. La riconciliazione lo isola da sola: 18 fill concordano fra log e jsonl, 1 no, ed è
quello.
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.
## Cosa c'è adesso
**`data/live/trades.db`** (sqlite, dentro il perimetro del backup rotativo). `fills` con il
contesto del segnale a quel giro, `roundtrips` **derivati** e ricalcolati da zero, `equity`
oraria (1.437 letture), `journal`. Sync orario in `cron_book`, subito dopo l'esecuzione.
Tre fonti che **si incrociano e non si sovrascrivono**: il cron log (ora vera + contesto), il
jsonl (i fill), il venue (autorevole su `order_id` ma **tronca**: 1 trade su BTC, 0 su ETH).
`reconcile()` riporta le divergenze e non ripara niente da solo.
**`docs/journal/YYYY-MM-DD.md`**, 62 voci ricostruite dall'arming. Quattro livelli separati per
provenienza, e la separazione è il prodotto:
| livello | chi scrive | può sbagliare? |
|---|---|---|
| numeri | feed certificato + DB | è una misura |
| Lettura | 12 regole dichiarate, ognuna con id | deterministica |
| Analisi | un modello (`claude-opus-5`) | **sì**, ed è firmata |
| Nota | l'operatore | — |
L'analisi parte ogni giorno su Telegram con una testata di numeri; se manca, il messaggio parte
lo stesso **dicendo perché**.
## Cosa ha trovato il lavoro, oltre al difetto iniziale
**P&L del libro dall'arming: +$37,50 su $598,06 (+6,27%) in 64 giorni**, 12 round-trip di cui 11
in utile, fee totali $0,41. Riconciliazione indipendente: la ricostruzione FIFO dà +$38,15 contro
i +$37,50 dell'equity del venue — scarto $0,65 (1,7%), coerente col funding pagato sui long.
Ma il numero che conta è un altro: **tutto il P&L è di sei giorni**. Fino al 17/08 il conto era a
$597,12, cioè **$0,94 in otto settimane**; l'equity è rimasta invariata nell'**80% delle
giornate**. Non è un difetto — è cosa sono TP01 e SKH01 — ma significa che +6,27% in due mesi non
è un tasso di rendimento.
E l'analista ha aggiunto la lettura che le regole non producono: quei sette giorni sono anche gli
**unici in cui il libro è stato a mercato**, e coincidono con +22,94% su BTC e +29,49% su ETH →
la finestra **non separa l'edge dal beta** a un rialzo forte, né il trend dal breakout.
## 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 la statistica sbagliata col nome giusto**: percentile a un anno etichettato
come il gate di VRP01, che usa quello **espandente**. Verdetto **opposto** (0,52 «sopra»
contro 0,18 «sotto»); il valore giusto combacia con «0/8 settimane passano il gate» (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**: la stessa pagina mostrava due equity.
## Il ciclo di retroazione, e il limite che resta
Al primo giro reale il modello ha letto la **propria analisi precedente** — con l'avviso del
tripwire — e l'ha commentata. `senza_analisi()` toglie la sua sezione prima di dargli la pagina.
**Nessun controllo automatico può distinguere il meta-commento da prosa valida: si vede solo
leggendo l'uscita.**
E il limite dichiarato quando la guardia è stata scritta si è materializzato subito: **due errori
su due giri con sonnet-5, nessuno dei due con una cifra nuova** — una frase su un merge mai
raccontato, e un round-trip corretto dichiarato inesistente. `numeri_non_supportati()` prende
l'invenzione, non il ragionamento. Modello portato a **opus-5** (primo giro pulito, affermazioni
verificate a mano contro il DB), ma **due osservazioni contro una non provano niente**: è un
tentativo di abbassare un tasso.
## Regole
- Un **dato operativo non ricostruibile** non può vivere in un file gitignored: si materializza
dove il backup arriva.
- Una **guardia più 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).
- Una regola che si accende su **$2 di cumulato** insegna a saltare la sezione: serve una soglia
di rilevanza.
- **Chi scrive in un registro va firmato**, o il registro perde il suo valore probatorio.
- Una **guardia sui numeri non copre il ragionamento**.
@@ -0,0 +1,271 @@
# 2026-08-25 — La manutenzione del martedì, e i due difetti che ha scoperchiato
*Scritto da Claude (agente), su richiesta dell'operatore. Firmato come vuole P13: l'analisi in
prosa è opinione di un lettore fallibile, i numeri qui sotto vengono dai log e sono riproducibili.*
## Come è cominciata
Domanda dell'operatore: «stato trades». Report normale del libro di bordo — 24 fill, equity
$598,06 → $667,88 (+11,67%), 16 round-trip di cui 15 in utile, netto +39,78. Poi il `--reconcile`
ha stampato la riga che ha aperto la giornata:
```
venue: NON LETTO (HTTPError) — non e' 'zero trade', e' 'non misurato'
```
Non era il gateway. Era **Deribit in manutenzione**: `system_maintenance`, codice 11051, HTTP 503
sia sul pubblico che sul privato. Iniziata fra le **08:57:40Z** (ultimo 200 OK nei log di
`cerbero-mcp`) e le **09:01:46Z** (primo 503). Rientrata verso le 09:20 — ~20 minuti, coerenti con
i 15-30 annunciati da Deribit per le sue release.
## Cosa dicono i log, contati invece che ricordati
1.499 giri di `cron_book` fra il 2026-06-23 e il 2026-08-25:
| esito | n | % |
|---|---|---|
| manutenzione Deribit | 3 | 0,20% |
| traceback duro | 5 | 0,33% |
| giri senza esito utile | 26 | 1,7% |
E gli episodi di venue non sono sparsi:
```
2026-07-21T09:00 Tue 502 su get_positions (Deribit giù, vista dal gateway)
2026-08-11T09:07 Tue system_maintenance 11051
2026-08-18T09:07 Tue system_maintenance 11051 (giù anche alle 10:07)
2026-08-25T09:07 Tue system_maintenance 11051
```
**Quattro martedì su dieci, tutti fra le 09:00 e le 09:07 UTC.** Il supporto Deribit conferma la
meccanica: le release escono il martedì alle 09:00 UTC. Il minuto `:07` del cron cadeva dentro
quella finestra — e ci cadeva per costruzione, non per sfortuna.
**Il punto singolo di guasto non è Deribit: siamo noi.** Dei 5 traceback, **4 sono il nostro
gateway** `cerbero-mcp.tielogic.xyz` (un 404 su `get_positions`, un 502, due `ReadTimeout`).
## I due difetti veri
### 1. Una riga sola per due guasti che vogliono azioni opposte
Fino a oggi qualunque guasto sul percorso Deribit stampava `conto non leggibile (offline)`, con la
nota di diagnosi **cablata**. Ma "Deribit in manutenzione" (aspetta, rientra da sola) e "il nostro
gateway è rotto" (ripara) non sono la stessa notizia. È **P4** violata, con l'aggravante di **P4
seconda metà**: *una nota di diagnosi cablata è peggio di nessuna nota*, perché si legge come una
misura.
Peggio ancora: il 18/08 **il codice 11051 era già dentro il processo**, nello stesso minuto,
raccolto da `livefeed`. Semplicemente non arrivava a chi decideva la gravità dell'allarme.
### 2. La finestra scoperta del disaster-SL — e l'asset che sparisce
`ensure_disaster_sl` ricostruisce un bracket incoerente **cancellando prima e ripiazzando dopo**.
Fra le due chiamate la posizione è senza alcuno stop on-book. Finché il ripiazzamento sollevava,
quell'eccezione risaliva fino a `main()`: il guasto peggiore (**posizione scoperta**) aveva la
stessa faccia di un errore qualunque.
E c'è il corollario che è successo davvero. Il **2026-07-21 alle 09:00 UTC** il 502 è arrivato
dentro `ensure_disaster_sl` su BTC. Nel log di quel giro **ETH non compare**: non è stato
ribilanciato e — quel che conta — **la sua protezione non è stata verificata**. Un guasto su un
asset toglieva la rete di sicurezza all'altro.
## Cosa è stato fatto
**`src/live/venue_probe.py` (nuovo).** Interroga l'API **pubblica** Deribit in diretta — niente
gateway, niente credenziali — e classifica: `VENUE_MANUTENZIONE` / `VENUE_GIU` / `GATEWAY` /
`IGNOTO`. La sonda parte **solo dopo un guasto**: sul percorso sano costa zero. Rispetta **P1**:
non ridichiara la firma 11051, la importa da `venue_watch.is_maintenance`.
Su **P9** (*un allarme massimo speso per un evento atteso è un allarme che non verrà letto il
giorno che è vero*): la manutenzione dentro lo slot declassa il titolo a ️. Ma con due paletti,
perché il rischio qui è costruire il silenzio proprio nell'ora in cui serve:
- declassa **solo su evidenza** della sonda, mai sull'orologio da solo → un gateway rotto di
martedì mattina resta 🛑;
- declassa **solo dentro la durata annunciata** (30 min) → il 18/08 alle 10:07 la manutenzione
aveva sforato, e torna una notizia. Stessa logica di `MAINT_GRACE_HOURS`, altra domanda.
**Isolamento per asset** in `book_execute`: un asset che esplode non ferma il ciclo, e il giro
esce con codice 2 per essere contabile.
**Stato `naked`** in `ensure_disaster_sl`: due tentativi di ripiazzamento, e se falliscono
entrambi lo stato è distinto da `place-failed` (**P5**: guasti diversi si distinguono anche quando
l'azione è la stessa). Non è "non sono riuscito a proteggere": è "**ho tolto la protezione e non
sono riuscito a rimetterla**" → 🚨 sempre, **mai** declassato da P9.
Non è stata invertita la sequenza in *piazza-poi-cancella*: due STOP `reduce_only` contemporanei
sono *probabilmente* innocui, ma "probabilmente" non basta per cambiare il ciclo di vita dei
bracket su un percorso con soldi veri senza misurarlo. **Riparato il silenzio, non toccata la
sequenza.**
**Cron `:07` → `:47`.** I due vincoli sono entrambi misurati e compatibili: 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) **e** fuori dallo slot di release. Il commento in
`cron_book.sh` è stato riscritto: lasciarlo dire `:07` sarebbe stato il difetto §5.7 in versione
nuova.
**Previsione dichiarata (M12).** Sulle 4 finestre osservate, 3 sono rientrate entro l'ora. Il `:47`
ne avrebbe scavalcate **3 su 4**. Se martedì prossimo il `:47` becca comunque la manutenzione, la
previsione è sbagliata e lo slot non è quello che credo.
## Cosa NON è stato fatto, e perché
**Il fallback diretto ai privati Deribit è bloccato, non rinviato.** Le credenziali Deribit
esistono **solo dentro il gateway**: in locale c'è `CERBERO_TOKEN` e basta. Leggere conto e
posizioni scavalcando `cerbero-mcp` richiede **chiavi API create dall'operatore** sul conto
Deribit. È una decisione con una superficie di rischio propria (una chiave in più che può
trapelare), non un refactor.
È stato fatto il pezzo che non le richiede — la sonda pubblica — e resta a debito in §5.11 il
resto. Nota onesta: la sonda pubblica **dice di chi è il guasto, non lo aggira**. Con il gateway
giù il libro continua ad astenersi; sa solo dire perché.
## Test
**19 nuovi** (12 in `test_venue_probe.py`, 7 in `test_book_resilienza_venue.py`), nessuno tocca la
rete. Suite: **730 passati, 1 fallito** — il fallito è quello già noto di §5.9 (SKH01 `canonical`
1,9223 contro banda cablata `<1,9`, deriva dei dati, non del codice).
Controllo positivo fatto, perché un test mai visto fallire non dimostra niente: contro il codice
vecchio **5 dei 7** test di resilienza falliscono. Con una riserva da dichiarare — il test di
isolamento, sul codice vecchio, fallisce perché il modulo di diagnosi non esiste, non perché
dimostri l'isolamento rotto. La prova di *quel* difetto è il log del 21/07, dove ETH non compare.
---
## Coda: la suite di test ha mandato un allarme falso sul telefono dell'operatore
Il primo giro al nuovo minuto `:47` è andato bene — entrambi gli asset elaborati, entrambi i
disaster-SL verificati `ok`, exit 0 — ma nel log c'era una riga che non poteva essere vera:
```
💰 USCITA DI FONDI: $5,000.00 -> $667.68 (-86.6%) · cap/asset ora $333.84
```
Il conto non ha mai visto $5.000. Ha visto $598-668 da sempre.
**L'ho causato io**, lanciando `uv run pytest` per verificare le riparazioni di oggi.
`test_book_live.py::test_la_formula_del_report_ha_potenza_anche_a_libro_flat` prende solo
`monkeypatch` (niente `tmp_path`), sostituisce `shadow_report` con uno che dichiara
`real_equity=5000.0`, e chiama `book.book_report()` — che come **effetto collaterale** scrive
`data/live/equity_seen.json`. Riprodotto isolando il singolo test: watermark $667,68 → **$5.000**.
L'helper `_write_cfg` esisteva già proprio per questo — il difetto gemello è del **2026-07-26**
ma quel test, aggiunto il **21/08**, non lo usa. È la terza volta che la stessa scommessa perde.
### Non era cosmetico
Il watermark alimenta il cap di fallback:
```
cap_fallback = min(cap_fisso_di_config, watermark × frac)
```
Con $5.000 dentro: `min($3.000, $2.500) = $2.500` per asset invece di `$334`. Su un conto da $668
sono fino a **$5.000 di nozionale lordo, ~7,5x di leva**. E il ramo che ci arriva è raggiungibile:
`eq_fallback` in `book_execute` **allerta e NON blocca**, per scelta dichiarata.
Cioè: **lanciare la suite di test poteva armare esattamente il pericolo che il watermark esiste
per impedire** (nota del 26/07 in `book.py` sul cap fisso e la leva 3,35x).
Danno reale oggi: **nessuno**. Alle 09:47 l'equity era leggibile, quindi il cap è stato calcolato
sull'equity vera e il watermark è stato riscritto col valore giusto. La finestra scoperta è stata
~09:25 → 09:47, e in quella finestra non c'è stato nessun giro con equity illeggibile. È andata
bene per la direzione del caso, non perché ci fosse una protezione.
### Riparazione, e perché è strutturale
`tests/conftest.py` con una fixture **autouse** che devia `EQUITY_WATERMARK` in `tmp_path` per
**ogni** test. Chiedere a ogni autore di ricordarsi il monkeypatch è la scommessa che ha già perso
due volte: la protezione deve valere anche per il test che qualcuno scriverà domani senza aver
letto niente. Un test che vuole davvero pilotare il watermark continua a funzionare — il suo
monkeypatch esplicito gira dopo e vince.
Più una guardia in `test_cap_watermark.py` che si accende se qualcuno rimuove la fixture:
controllo positivo, perché una protezione mai vista fallire non è una protezione.
**Resta aperto il principio più largo** (§5.12): un test non dovrebbe poter scrivere in
`data/live/` *affatto*. Oggi è deviato solo il watermark — `trades.db` e `book_executions.jsonl`
sono ancora esposti allo stesso errore.
### La lezione
Un **effetto collaterale su file** trasforma un test puro in un attore sul sistema vivo. Qui il
test non menzionava il watermark, non lo importava, non lo asseriva: lo scriveva passando per una
funzione di produzione tre livelli più in basso. **Il perimetro di un test non è quello che il test
dice di toccare: è quello che tocca il codice che chiama.**
---
## Seconda parte della giornata: il versamento, e le tre misure che ne sono uscite
Alle **11:03:35Z** sono atterrati **1.400 USDC**: equity **$667,88 → $2.066,88**. Il giro delle
11:47 ha ribilanciato correttamente (cap/asset $1.033, BTC +$238, ETH +$319, disaster-SL `placed`
su entrambi alla taglia nuova) — la prima volta che il ramo riparato al mattino gira sul serio.
Poi l'operatore ha chiesto tre cose in fila, e ognuna ha rotto qualcosa.
### (1) «€500/mese per 10 anni»
Rifatto sul conto vero invece che citare la tabella da $635. **Non centra**: capitale mediano
$114.934, rendita 18,35 €/g, **P(≥50 €/g) = 0,0%** su 3.000 traiettorie. Il bersaglio con €500/m
arriva al 17° anno; per averlo a 10 servono €1.718/mese.
Il numero che ordina tutto: **€100/mese in più valgono +4,07%/anno di drift**, cioè +27% su tutto
il drift del libro. Dettaglio in `30-piano-capitale-fisco.md`.
### (2) «A cosa serve XSR01, allora?» → e poi: «l'abbiamo fatto per la leva»
Due osservazioni dell'operatore, entrambe centrate, che hanno spostato la conversazione dal
candidato al **disegno**.
XSR01 ha già fallito `weights_tilt_null` (peso ottimo ~0). Ma la risposta vera è che **anche un
uplift di strategia riuscito vale meno di un bonifico modesto**, e questo si applica a qualunque
sleeve, non solo a XSR01.
Poi la misura che ha ribaltato la discussione sulla leva: **il libro non è poco levato, è FERMO**.
Esposizione lorda realizzata in 64 giorni live: mediana **0,00x**, media 0,05x, **max 0,52x** su un
tetto strutturale di 1,0x, esposto in **14 giorni su 64**. Il cap non ha mai morso: il vincolo è il
segnale. *Il basso drawdown di TP01 non viene dall'essere piccolo nel mercato — viene dallo stare
fuori dal mercato.*
⚠️ Correzione che mi sono fatto da solo: quel 78% è il **regime attuale**, non la media. Sull'intera
storia i giorni flat sono il **28,0%** — 2,7% nel 2021, 77,0% nel 2022, 75,6% nel 2026.
### (3) «Allora usiamo l'ETF quando il libro è flat» → SCARTATO
Test in `r0825_capitale_fermo.py`. **Perde** a iso-rischio (Sharpe 1,01 contro 1,40 del 50/50 e
1,35 del libro), e il null a maschera casuale lo mette al **30° percentile**: fa peggio di
commutare a caso.
E qui la parte che vale più del risultato. Stavo per scrivere un meccanismo pulito — *«il libro va
flat quando anche l'azionario soffre»* — sostenuto da un aggregato netto (SPY 4,91% nei giorni
flat contro 21,48% negli altri, 16,57%). **La scomposizione per anno lo ha demolito**: il segno
alterna, 3 anni su e 4 giù, e il **2022** — l'anno che doveva reggerlo — ha il segno **opposto**.
Artefatto di composizione: i giorni flat stanno negli **anni** brutti per l'azionario, non nei
**giorni** brutti. M9 ha fatto esattamente il suo lavoro, su di me.
### I due difetti di misurazione trovati addosso
1. **Maschera vuota vestita da risultato.** La prima versione prendeva i giorni flat dalla serie
**de-luckata**; ma `deluck` sottrae una costante, quindi lo zero esatto sparisce. Maschera
vuota → dinamico identico al book → e lo script ha stampato tabelle piene e un p-value. Lo ha
rivelato solo la riga che stampava `0 = 0.0%`.
**Regola: stampare la CARDINALITÀ di una maschera prima di usarla.**
2. **`r0822_slip_audit.py` aveva l'equity CABLATA a `636.0`** con un commento che diceva di leggerla
dal watermark — P1 in piena regola. Il giorno del versamento avrebbe continuato a stampare $636,
sbagliando di 3,3x proprio la sezione che esiste per dire *quando la misura scade*. Riparato
leggendo il watermark — e poi riparato **di nuovo**, perché la prima riparazione divideva tutti i
fill per l'equity di oggi mentre i fill del campione sono stati eseguiti a conti diversi
($597$2.067). Ora la normalizzazione è **per fill**, e a $600 riproduce il 22,0% originale.
### La misura di slippage, rifatta alla taglia vera
26 fill (erano 18), taglia $6$319. I due più grandi mai eseguiti sono di oggi e hanno preso
**1,01%** e **0,54%** della loro barra 5m, con il print **a metà del range** (q=0,50). Il caso
peggiore resta un fill da **$74 del 18/07 al 21,9%** — un sabato, nastro inesistente.
📌 **L'attrito non è funzione della taglia: è funzione di `taglia / volume della barra`.** Un fill
4,3× più grande in un'ora liquida ha fatto 20× meno partecipazione di uno piccolo in un'ora morta.
⚠️ Ma non è «assente», è **«non ancora testato»**: nel campione non c'è ancora un fill grande in una
barra sottile, e il weekend è dove TP01 fa il 38% del proprio gross.
+38
View File
@@ -0,0 +1,38 @@
# 2026-08-25 — Il versamento da $3.000: cosa compra, e cosa NON compra
**Domanda dell'operatore:** *"rianalizza e vediamo come procedere. potrei versare a breve altri 3k"*.
**Script:** `scripts/research/r0825_versamento_3k.py` (lente L3, stessi semi e path del piano:
tutto appaiato, M22). Assunzioni dichiarate: 3k = $3.000 USDC, lump oggi, start $2.065 (watermark
18:47:13Z). Controllo M23: da $2.065 il 10a riproduce $114.929 contro i $114.934 pubblicati da
$2.067 — la macchina riproduce il numero vecchio.
## Cosa compra (€500/mese, appaiato sugli stessi path)
| orizzonte | senza lump | con lump | Δ | €/g con lump | P(≥50€/g) |
|---|---|---|---|---|---|
| 5a | $44.921 | $49.730 | +10,7% | 7,94 | 0% |
| 10a | $114.929 | $122.491 | +6,6% | 19,56 | 0% |
| 15a | $228.770 | $241.490 | +5,6% | 38,56 | 12,3% → 17,3% |
| 20a | $408.105 | $427.738 | +4,8% | 68,30 | 79,4% → 83,9% |
Il lump = **5,5 mesi di versamenti anticipati in un colpo** (N3: e' la prima leva, esercitata).
Pietre miliari con €500/mese: $15k (C\* Hyperliquid) 1,7a → **1,3a**; $20k (si riapre la scelta
del venue) 2,4a → **1,9a**.
## L'effetto collaterale che conta: il gate XSR01 del 23/10 si RIBALTA
P(capitale ≥$5k al 23/10): **0,0% → 100,0%** (mediana $5.664, p10 $5.417). Senza lump il gate
moriva da solo sul criterio di capitale e i suoi tre difetti erano accademici. Col lump il
criterio formale **passa**, e la decisione poggerebbe per intero su:
(a) il monitor rotto che registra ~41 min/giorno (§5.1); (b) l'haircut col pavimento sbagliato
di 5-7× (§5.3); (c) un C\* vero $15-20k contro i $1.266 che il 25% di $5.065 mette sul venue.
**Il lump rende il gate LEGGIBILE senza rendere XSR01 comprabile** — il caso peggiore per un
criterio scritto male. La scelta fra riscrivere adesso (dichiarandolo) e lasciare-e-citare va
presa **prima** che il lump atterri, non il 23/10.
## Meccanica live a $5.065 — niente da toccare
cap per-asset $1.032 → $2.532 (equity×0,5, si adegua da solo); min_order $5 dal 0,48% al 0,20%
del cap (C2); fallback min($3.000, wm×0,5) = $2.532; disaster-SL invariato (e il nome continua a
ingannare). ⚠️ **Slip-audit da rifare** dopo i primi fill: misurato su $597-2.067, a $5k la taglia
relativa raddoppia — lo script con normalizzazione per-fill c'e' gia'.
@@ -0,0 +1,114 @@
# 2026-08-25 — XSR01 sotto la lente della RENDITA: e' un conto deposito con un secondo venue
**Domanda dell'operatore:** *"rendita perpetua ma non ho ancora capito se serve XSR01"*.
**Script:** `scripts/research/r0825_xsr_rendita.py` (N11: ogni numero qui sotto lo riproduce).
## Perche' era una misura nuova
XSR01 era stato giudicato tre volte — ammissione (25/07), gate di deploy pre-registrato (23/10),
accumulo (oggi) — e **mai sotto la lente della rendita**. Sono criteri diversi: l'accumulo premia
il **drift**, la rendita premia la **perpetua** (il prelievo piu' alto che sopravvive a 20 anni con
P≥90%). Dichiarato l'obiettivo (N6), il criterio cambia, e nessuno aveva rifatto il conto.
## Cosa mi aspettavo prima di misurare (M12)
(a) a iso-nozionale il mix perde drift e **alza** il muro; (b) a iso-rischio (M6) il verdetto puo'
**ribaltarsi**; (c) il vincolo che decide non sara' ne' (a) ne' (b) ma il **capitale**.
**Tutte e tre vere** — ma (c) e' arrivata con una gamba che non avevo previsto: il null.
## Le serie, e la finestra in cui esistono entrambe
| serie | finestra | drift/a | vol/a | Sharpe | maxDD | SE drift |
|---|---|---|---|---|---|---|
| libro L3 — **tutta** la storia | 2019-03-14 → 2026-08-22 | 15,19% | 11,29% | 1,35 | 12,4% | 5,19% |
| libro L3 — finestra comune | 2024-01-01 → 2026-08-22 | 10,96% | 10,14% | 1,08 | 8,6% | 7,97% |
| XSR01 — finestra comune | idem (965 g, 2,64 anni) | **3,72%** | 2,34% | 1,59 | 2,5% | 1,46% |
corr libro↔XSR01 **+0,049**. Finestra comune = **35%** della storia del libro → **il muro calcolato
qui NON e' il $313k pubblicato**: si confronta solo con se stesso (base $602.660).
📌 Sharpe di XSR01 a oggi, **lente dei gate, sole barre chiuse: 1,56** — il "1,82" pubblicato e' una
**terza** lente (XSR-REPRO, 22/08). Si cita sempre la coppia (lente, ultima barra chiusa).
## (2) iso-nozionale — XSR01 ALZA il muro
w 5→50%: muro da $604k a $668k (**+0,3% → +10,8%**). Il mix e' piu' liscio ma piu' povero, e per
una rendita il drift comanda. **A iso-nozionale XSR01 peggiora l'obiettivo dichiarato.**
## (3) iso-rischio (M6) — si ribalta, e sale il sospetto
Ri-scalando ogni mix a `vol(libro)`: w 25% → k **1,32x**, muro **$478.729 (20,6%)**.
⚠️ **L'argmax e' al BORDO** (w=50%, k=1,93x): il muro scende **monotonamente** in w, quindi la
griglia non indica un ottimo, dice *"il piu' possibile"* — e cio' che si compra in quella direzione
e' il **k**, non XSR01 (M8). Sopra 1,40x il libro **non puo' eseguire** (decisione 23/08, e in
`config/live.json` non esiste una chiave di scala). Peso portato avanti: **25%, k 1,32x**.
## (3b) IL NULL — ed e' il risultato della sessione
A iso-vol vale `drift = Sharpe × vol_libro`: la colonna "drift" della sezione (3) **e' la colonna
"Sharpe" ri-etichettata**. Tutto il guadagno di muro e' guadagno di **Sharpe da diversificazione**.
Quattro sostituti, stesso peso 25%, stessa ri-scalatura:
| sostituto | muro | Δ vs base |
|---|---|---|
| **XSR01 (vero)** | **$478.729** | **20,6%** |
| N1 mescolato — stessi rendimenti, ordine casuale (3 semi) | $475.933 / $483.846 / $484.421 | 21,0 / 19,7 / 19,6% |
| N2 rumore gaussiano, media e vol di XSR01 (3 semi) | $457.751 / $499.263 / $531.158 | 24,0 / 17,2 / 11,9% |
| N3 rumore, **drift ZERO** (3 semi) | $558.080 / $617.270 / $662.436 | 7,4 / **+2,4** / **+9,9%** |
| **N4 conto remunerato 3,72%, vol 0** | **$477.607** | **20,8%** |
| N4 conto remunerato 4,0%, vol 0 | $469.353 | 22,1% |
📌 **Il MECCANISMO di XSR01 vale 0,8% di muro** — dentro la risoluzione Monte Carlo (spread fra semi
0,151% di perpetua ≈ $17k di muro, M23). **Mescolare i suoi rendimenti non cambia niente.**
📌 **N3 (drift zero) non paga**: mediana $617k **sopra** la base. La sola riduzione di varianza non
compra rendita. Quindi **cio' che si compra e' il DRIFT scorrelato**, non la scorrelazione.
🚨 **N4 e' la riga che risponde alla domanda.** Un **conto remunerato** allo stesso tasso (3,72%),
**vol zero, corr zero, nessun secondo venue**, da' $477.607 contro i $478.729 di XSR01: **identici
dentro la risoluzione**. Al 4,0% e' **meglio**. Ed e' un confronto **in svantaggio per il conto**:
la lente L3 gli applica il **33%** di `c-sexies`, mentre un BOT paga 12,5% e un deposito 26%.
## (4)-(5) gate e risoluzione
`weights_tilt_null` (XSR01 25% vs 0%): Δ_is +0,061 · Δ_hold +0,162 · pctl 25,8 < 90,0 → **PASS**.
⚠️ hold-out del gate = 2025-01-01 → la gamba in-sample e' **un solo anno**: gira, ma e' un indizio.
Differenza appaiata di drift **+1,16%/anno, SE 0,48% → t +2,40**. Drift XSR01 **+3,72%, SE 1,46%,
t +2,55**. Risolto, ma di poco, e su 2,64 anni.
## (6) il vincolo che non e' nelle tabelle
XSR01 gira su **Hyperliquid**: il suo peso e' capitale che **esce** da Deribit, non che si aggiunge.
A $2.067 il 25% sono **$517**, contro un **C\* misurato di $15.000-20.000** (il gate 23/10 e' tarato
su ~$3.000, §5.3). Per un 25% sopra C\* servono **$60.000 sul conto totale**.
E l'haircut mangia **proprio la cosa che il null dice di star comprando**:
| haircut | netto/anno | vs conto 4,0% | vs conto 2,0% |
|---|---|---|---|
| 0% | 3,72% | SOTTO | sopra |
| 20% | 2,97% | SOTTO | sopra |
| 40% (soglia del gate) | 2,23% | SOTTO | sopra |
| 60% | 1,49% | SOTTO | **SOTTO** |
**Pareggio con un conto al 2%: haircut 46%. Con un conto al 4%: non pareggia nemmeno a haircut ZERO.**
E l'haircut pubblicato ($14,41 di ticket) e' **~2,4x ottimista e senza script che lo riproduca**.
## VERDETTO
**4/6 condizioni** — ma il conteggio non e' la lettura. La lettura e':
> Sotto la lente **rendita perpetua**, XSR01 fa una cosa reale — porta un **drift scorrelato a bassa
> vol** — e la fa in modo **non proprietario**: mescolarlo non cambia il risultato, e un conto
> remunerato allo stesso tasso lo **eguaglia a vol zero e senza un secondo venue**.
> Il vincolo binding non e' il rendimento: e' il **capitale** ($60.000 per un 25% sopra C\*), e sotto
> quella soglia la domanda *"serve XSR01?"* **non ha una risposta comprabile**.
**Cosa NON dice.** Non dice che XSR01 e' morto: lo Sharpe standalone 1,56 e il DSR 0,983 reggono, e
sotto la lente **accumulo** il conto va rifatto (li' il drift comanda in modo diverso). Dice che
**per la rendita non e' lo strumento**, e che il gate del 23/10 sta per decidere su una gamba che a
questo capitale non e' comprabile.
## Cosa e' cambiato in me mentre misuravo
Ho copiato in questo script **lo stesso difetto che avevo riparato stamattina** in
`r0825_capitale_fermo.py`: la maschera dei giorni flat presa dalla serie **de-luckata**, dove gli
zeri esatti non esistono piu' perche' `deluck` sottrae una costante. Stampava `0.0%` e non si sarebbe
notato senza la riga che conta i giorni. Riparata con l'`assert` che manca(va) a monte: la maschera
si prende dalla **serie grezza**. *Riparare un difetto in un file non lo ripara nella testa.*
@@ -0,0 +1,95 @@
# 2026-08-26 — `advance()` riparato e rigenerato, quattro debiti chiusi in un giorno
Quattro debiti di CLAUDE.md §5 chiusi: **§5.1** (advance + rigenerazione + guardia),
**§5.12** (isolamento test esteso), **§5.5** (test di leva cancellato con nota),
**§5.9** (test rosso indagato e ancorato). Suite: **751 passati, 0 falliti** — tutta verde
per la prima volta dal ~24/08.
## §5.1 — advance() consuma solo barre CHIUSE, e le serie sono RIGENERATE
**Il fix** (`src/live/paper_guard.py` + 6 monitor): la riga
`new = [i for i in range(len(ts)) if ts[i] > last_ts]` diventa `PG.nuove_chiuse(ts, last_ts,
cadenza)` — una barra open-labeled e' chiusa quando `ts + cadenza <= adesso`. Filtro condiviso,
importato da tutti e sei i monitor (anche `paper_combo`, che era sano *per caso*: ora e' sano
*per costruzione*).
⚠️ **D6 pagata DI NUOVO scrivendo il fix:** la prima stesura di `indice_chiuso` usava `asi8`
assumendo nanosecondi — pandas 3 usa `us` di default, e l'intero nella scala sbagliata dava
"chiusa" la barra del giorno in corso **senza eccezioni**. Beccata dal test di fumo, riparata
con la sottrazione dall'epoca (la forma di `resample_tf`), e blindata da un test che prova
`ns/us/s`. *La regola che stavo cablando e' quella che stavo violando.*
**La guardia** (era "raccomandata e non cablata"): `monitor_health` ha ora lo stato
**PREMATURO** — ultima barra che chiude DOPO l'mtime del file, grazia 5 min. Derivata dalle
spec esistenti (P1), con `open_labeled=False` per `collect_chain` che timbra i giri, non barre
(P14: senza il flag avrebbe allertato sempre). Controllo positivo sul dato vivo: **ha segnalato
esattamente i 5 rotti e taciuto sui 2 sani** prima della rigenerazione; dopo, 7/7 OK.
**La rigenerazione** (`scripts/live/paper_regen.py`): stesso `start_ts` pre-registrato, config
congelata, replay del codice di produzione riparato — il metodo validato due volte dall'audit
del 22/08 (0 barre chiuse cambiate su 1.427/1.488 + 6/6 al bit). Vecchi file archiviati come
`*.pre_regen_20260826.*` (evidenza del difetto, nel perimetro di backup). **Nessuna data di
gate si sposta.**
| monitor | barre | Sharpe rotto → vero |
|---|---|---|
| `paper_statarb` (gate **27/09**) | 58 → 57 | **+1,95 → 1,61** — il ribaltamento previsto dall'audit |
| `paper_dvolspread` (kill 24/10) | 32 → 31 | 14,73 → 4,41 |
| `paper_xsr` (gate 23/10) | 32 → 31 | 4,98 → 2,72 |
| `paper_prevday` | 1584 → 1584 | +0,39 → +0,40 |
`paper_portfolio` **non** rigenerato (gamba GTAA su ADJUSTED_LAST ri-aggiustato: il replay non
sarebbe la serie registrata — P12); tolta la sola coda non chiusa, cosi' la guardia non resta
accesa per sempre su un difetto gia' riparato (P14). Storia pre-fix dichiarata corrotta.
## §5.12 — un test non puo' piu' scrivere nel libro di bordo vivo
`tests/conftest.py`: oltre al watermark, ora deviati **`trades.db`** (wrapper su `connect`
il default e' catturato alla definizione, patchare la costante non serviva a niente) e
**`docs/journal/`** (pagine tracciate da git). Per `book_executions.jsonl`, che i test caricano
ad-hoc con importlib e conftest non puo' raggiungere prima: **impronta a inizio suite, verifica
a fine suite**, col falso positivo possibile (fill reale del cron :47 durante la suite)
dichiarato nel messaggio.
## §5.5 — il test di leva e' CANCELLATO, non aggiustato
`test_leva_massima_da_config_resta_sotto_o_uguale_a_1x` misurava `frac x n_asset`: con una
chiave di scala la grandezza vera diventerebbe `frac x n_asset x scala` e il test avrebbe
continuato a passare smettendo di controllare. Al suo posto una nota che rimanda al tetto sul
PRODOTTO di GATE SCALA-01, da cablare nel codice che leggera' la chiave, quando esistera'.
## §5.9 — il test rosso non segnalava un guasto: segnalava una banda scritta male
`r0826_skh_band_drift.py` — feed tagliato a date crescenti, stesso codice di produzione:
| taglio | Sharpe hold-out |
|---|---|
| 02/07 (audit) | **1,6376** — riproduce l'audit |
| 25/07 (banda cablata) | 1,5751 |
| 01→15/08 | 1,566 → 1,547 (scivola piano) |
| **22/08** | **1,8966** |
| oggi | 1,9306 — riproduce il rosso |
**Il dato regge** (in-sample 1,424 **identico su ogni taglio**; il taglio all'epoca riproduce
l'epoca). Il codice non c'entra. 📌 **La scoperta vera: la SOLA settimana 15-22/08 (rally ETH
con SKH01 long) vale +0,35 di Sharpe hold-out** — il numero e' cosi' fragile, e chi cita
"hold-out 1,9" oggi citerebbe 1,55 di una settimana fa. Coerente con §2: l'hold-out canonico
di SKH01 era gia' il numero da non citare.
**La riparazione:** il test ora **taglia il feed al 02/07** e verifica la riproduzione stretta
(±0,02) di full/in-sample/hold-out/maxDD. Si rompe se cambiano codice o dato sulla finestra
congelata — e per nessun altro motivo. La deriva del vivo non e' affare suo: per quella ci
sono i monitor (e da oggi la guardia PREMATURO).
Nel farlo, un altro inciampo istruttivo: la prima esecuzione del drift stampava **sette righe
identiche** — `run_asset` memoizza in un dict di modulo (`_CACHE`), non in `lru_cache`, e lo
svuotamento non lo toccava. Anche il test riparato ora pulisce quel dict, prima E dopo, per
non avvelenare gli altri test.
## Conseguenze sui gate (nessuna data si muove)
- **STATARB 27/09**: il monitor ora misura la serie vera (**1,61** a oggi). Coi criteri
pre-registrati invariati, a oggi punta al RITIRO — si legge quel giorno, non prima.
- **XSR01 23/10 / DVOLSPREAD 24/10**: metrica primaria finalmente su serie reali.
Restano i difetti dichiarati (§5.3 pavimento del gate XSR01; veto DVOLSPREAD ora sorvegliato
dalla guardia).
+58
View File
@@ -0,0 +1,58 @@
# 2026-08-26 — USDC Rewards su Deribit: il prodotto esiste, il conto NON li riceve
**Domanda dell'operatore:** *"con 5k non si possono far partire altre strategie/investimenti?"*
→ unica pista compatibile con "100% Deribit fino a $20k": rendimento sull'USDC fermo (il libro
e' flat il 75% dei giorni del 2026, e la misura del 25/08 dice che un 3,7-4% su quel capitale
vale quanto tutto XSR01).
## Il prodotto (verificato SUL VENUE prima che sulle fonti, N10)
- API pubblica `public/get_currencies`, 2026-08-26: **USDC `apr: 3.4`** · USDE `apr: 4.0` ·
USYC nel pool cross-collateral · haircut cross-collateral USDC **0%** → il saldo matura
MENTRE fa da margine, nessun lock.
- Fonti (Deribit Insights): programma "USDC Rewards" dal 15/07/2025 — Deribit custodisce
presso Coinbase, Coinbase paga, Deribit gira agli **utenti di giurisdizioni autorizzate**
(lista non pubblicata). Maturazione sul **minimo di equity USDC delle 24h** (00:00 UTC),
pagamento mensile in unica soluzione nei primi ~14 giorni del mese successivo. Tasso
variabile (4% a lug 2025, 3,4% oggi).
## La verifica empirica: il conto NON li riceve
L'operatore era da cellulare → verifica indiretta sulla NOSTRA serie equity oraria
(`data/live/trades.db`), che su libro flat e' piatta salvo accrediti:
| finestra | letture | equity | salti orari ≥$0,40 | reward atteso |
|---|---|---|---|---|
| 01-14 lug (paga giugno) | 326 | $598,06 → $598,49 | 1 (= il fill ETH del 14/07) | ~$0,65 — **assente** |
| 01-14 ago (paga luglio) | 334 | $596,92 → $597,12 | **0** | **~$1,70-2,00 — assente** |
Un accredito da $2 su una serie che si muove di $0,20 in due settimane sarebbe stato
inequivocabile. **Due mesi indipendenti, stesso esito: nessun reward.**
## VERDETTO (stesso giorno, poche ore dopo): l'Italia e' ESCLUSA PER LEGGE — pista chiusa
La causa non era il conto ma **MiCA**: il regolamento UE vieta la remunerazione dei token di
moneta elettronica, e **Coinbase — che e' chi paga i reward del programma Deribit — ha cessato
i reward USDC in tutta l'EEA dal 2024-12-01** proprio per questo. Conferme indipendenti:
(a) l'espansione Deribit del 2026-08-01 aggiunge **80 paesi, nessuno EEA**; (b) il transaction
log del conto non ha MAI avuto una voce reward (misurato stamattina su due mesi). **Nessun
ticket necessario: la risposta e' normativa, non di supporto.** Cade anche la domanda fiscale.
⚠️ **Episodio di sicurezza, registrato perche' non si ripeta.** L'operatore ha chiesto dei
reward in una chat dove due sedicenti agenti ("Matt Andreas [MOD]", "Tom Mavencourt") hanno
risposto a copione — stessa domanda fuori tema due volte, scuse per un "guasto" mai segnalato,
l'avviso "beware of impersonators" usato per comprarsi fiducia — e l'affermazione
**"Yes, Italy is supported for USDC rewards": FALSA** (un agente Deribit vero sa che l'EEA e'
esclusa). Pattern coerente con lo script dei wallet-drainer: trattenere in chat, poi chiedere
"verifica" (link/2FA/chiavi API/schermo). Regola permanente: il supporto passa SOLO
dall'app ufficiale o da support.deribit.com; nessun codice, chiave o link, mai; una
risoluzione vera non richiede azioni dell'utente.
## Cosa resta della pista "rendimento sul capitale fermo"
- **USDC rewards: morta** per residenti EEA finche' MiCA resta cosi'.
- **USDE (Ethena, apr 4.0 dall'API)** come collaterale: non e' un EMT, MiCA non lo vieta allo
stesso modo — ma ha rischio proprio (depeg, basis-trade, Ethena GmbH chiusa dopo BaFin).
Se si apre, va da candidato: misura + gate + verifica C6 sul NOSTRO conto.
- **USYC**: probabile riservato a istituzionali — da verificare prima di parlarne.
- Altrimenti vale N3: a questo capitale la leva vera restano i versamenti.
+105
View File
@@ -0,0 +1,105 @@
# 2026-08-26 — USDE come collaterale a rendimento: analisi da candidato
**Domanda dell'operatore:** *"analizziamo usde"* — dopo la chiusura della pista USDC rewards
(Italia esclusa da MiCA). **Script:** `scripts/research/r0826_usde_scenari.py` (N11).
## Verificato SUL VENUE (API pubblica, oggi — N10)
- `apr: 4.0` per USDE (era "fino a 9%" al lancio 03/2025: tasso = APR settimanale Ethena 5%
di fee Deribit; comprime coi funding) · reward **giornalieri** ~12:00 UTC sul minimo di
equity USDE, voce nel Transaction Log → **falsificazione in 24-72h**
- spot `USDE_USDC` attivo: spread osservato **~3 bps**, fee spot **zero**
- **haircut cross-collateral 10%** (USDC 0%): il 90% fa margine — al nostro profilo di leva
(max realizzata 0,52x su tetto 1,0x) **non morde**
- **indice `usde_usd` multi-exchange con mediana e clamp ±0,5%** — NON il book interno:
è la differenza strutturale dal caso Binance del 10/10/2025 ($0,65 dall'oracle sul proprio
book da $8M mentre USDe quotava ~$0,99 altrove, riscatti regolari, peg tornato in 8h)
- eligibilità **per residenza, lista non pubblica** → si verifica sul CONTO, non sui documenti
(la lezione USDC di stamattina)
## Rischi dichiarati (p non stimabile → si controlla con la QUOTA, N4)
R1 **emittente**: basis trade (staking + short perp); fallimento = fino a 100% della quota —
si SOMMA al rischio venue accettato il 26/07, non lo sostituisce. R2 **marcatura in crash**
(P10: l'accoppiamento — il mark scende proprio quando il libro è lungo). R3 **tasso**: 9→4%
in 17 mesi; funding negativi prolungati → ~0. R4 **regolatorio EU** (Ethena GmbH liquidata
dopo BaFin; USDe non è EMT MiCA — per questo PUÒ pagare dove USDC non può).
📌 Nota a favore da misurare se si procede: il rendimento USDe sale coi **funding positivi**,
cioè quando il nostro libro long li PAGA (2,16%/anno): sconto parziale correlato sulla tassa
di funding.
## L'aritmetica (dallo script)
| equity | quota 50% rende | 100% costa | note |
|---|---|---|---|
| $2.065 | $41/anno | $1.032 | conversione ripagata in ~3 giorni |
| $5.065 | $101/anno | $2.532 | scenario post-versamento |
| $20.000 | $400/anno | $10.000 | dove la posta diventa seria |
Mark 1% = 3 mesi di resa · 3,5% = 11 mesi · **100% = 25 anni: non si recupera mai**
→ la leva di controllo è la **quota**, non il tasso.
## PROPOSTA (pre-registrata qui, esito da scrivere sotto)
**Test di eligibilità sul conto**: convertire **~$500 USDC→USDE** (costo <$0,20 totale),
tenere **3 giorni**, guardare il Transaction Log alle ~12:00 UTC (reward atteso ~$0,05/g).
Regola dichiarata PRIMA di vedere l'esito: **≥1 voce reward in 3 giorni → idoneo**, e si apre
la decisione di quota (con un gate: quota massima, chi la decide, cosa la riapre);
**0 voci → non idoneo**, si riconverte e la pista si chiude come l'USDC.
L'esecuzione è dell'operatore (app: Spot → USDE/USDC) o autorizzata esplicitamente via gateway.
Il libro non c'entra: nessun codice del percorso soldi viene toccato dal test.
## ESECUZIONE DEL TEST (stesso giorno, 13:04 UTC — autorizzata dall'operatore: "fai test")
Ordine via gateway, limit marcabile con banda di sicurezza sul prezzo (0,995-1,005):
**500 USDE @ 1,0003, filled, fee 0** (`USDE_USDC-8977542793`). Costo totale ~$0,15.
Prima finestra utile di reward: ~**28/08 12:00 UTC** (il calcolo usa il minimo di equity
USDE della finestra); verifica finale il **29/08**: ≥1 voce reward nel Transaction Log →
idoneo; 0 voci → si riconverte e la pista si chiude.
🚨 **DIFETTO TROVATO E RIPARATO DURANTE IL TEST — il test valeva gia' per questo.**
`shadow._equity` leggeva SOLO il conto USDC: dopo la conversione il book avrebbe visto
$1.556 (24,3%) al giro delle 13:47 → falso 💰 "USCITA DI FONDI" **e vendita indesiderata
di ~$140 di posizioni** (i target scalano con l'equity). Riparato in 35 minuti, prima del
giro: `_collaterale_usde()` valuta l'USDE all'**indice pubblico** `usde_usdc` (mediana
multi-exchange — la lezione Binance 10/10), con tre proprieta' blindate da 6 test nuovi
(`tests/test_shadow_usde.py`): un **depeg PASSA nel sizing** (mai nascosto), sopra la pari
si **clampa a 1,0**, indice illeggibile → **1,0 dichiarato, mai 0** (contare 0 ricreerebbe
il falso 24%: il fallback si sceglie sul danno, P5). Verificato live: equity vista dal
book $2.055,88 ("mainnet USDC + USDE 500 @ 0.9999").
*Un test da $500 progettato per misurare l'eligibilita' ha scoperto che il book era cieco
al cross-collateral: se la prima conversione fosse stata fatta a quota vera, l'errore
sarebbe costato caro. I test piccoli esistono per questo.*
*Esito eligibilita': (da scrivere entro il 29/08)*
## STRUTTURA (stesso giorno, ~14:20 UTC — "struttura il progetto per gestire subito usde")
La patch d'emergenza delle 13:07 e' diventata struttura di prima classe:
- **`config/live.json` sezione `usde`** — l'unica autorita' sui parametri (P1): `index_name`,
`haircut` 0.10, `quota_max_frac` 0.50 (tetto di ALLERTA — la quota effettiva la decide
l'operatore), `depeg_warn` 0.99 / `depeg_crit` 0.95 coi **criteri dichiarati** in `_nota_usde`
(P6: warn = fuori dalla banda spot ~3 bps e oltre il clamp ±0,5% per fonte dell'indice;
crit = meta' del buffer di haircut consumata).
- **`src/live/usde.py`** — modulo unico per config, catena di prezzo (indice pubblico → ticker
spot → 1,0 dichiarato) e valutazione `valuta()` PURA. `shadow._collaterale_usde` ora DERIVA
da qui (niente ridichiarazioni); i 6 test di shadow passano invariati.
- **`scripts/live/usde_watch.py`** + `cron_usde.sh` alle **12:35 UTC** (dopo la finestra reward
~12:00, fuori da :00/:25/:47) — sola lettura: (1) **reward per DELTA di equity al netto dei
trade spot** (il gateway non espone il Transaction Log; un delta con trade nel mezzo si
dichiara, trade illeggibili → delta NON attribuito, P12); (2) **depeg**: 🚨 solo sotto crit
e ripetuto, ⚠️ warn/quota/BLIND solo alla transizione (P9); (3) **quota sul totale**, che
allerta anche per deriva PASSIVA (il libro perde → la quota sale da sola, N4). Applica il
**verdetto pre-registrato** (≥1 reward entro il 29/08 → IDONEO; zero → riconvertire) invece
di lasciarlo a un promemoria. Limite dichiarato: cadenza giornaliera, un depeg intraday puo'
sfuggire all'ALLERTA — ma non al SIZING, che legge l'indice a ogni giro orario del book.
- **Serie `data/live/usde_watch.jsonl`** — dentro il perimetro di backup, `ts` in ms epoch →
sorvegliata da **`monitor_health`** (max_age 30h, `open_labeled=False`): un watch fermo
spegnerebbe gli allarmi depeg e il silenzio si leggerebbe come "va tutto bene" (P5).
- **Baseline registrata alle 14:17:52Z**: 500 USDE @ 1,0000 (indice) = $500,00 · quota 24,3%
(tetto 50%) · margine utilizzabile ~$2.008 · verdetto IN_ATTESA. E' la lettura che rende
misurabile il delta del 28/08.
- **Test: 775 passati** (+17 nuovi in `tests/test_usde_watch.py`: valutazione condivisa,
rilevatore reward, verdetto — incluso che NON anticipa la scadenza del 29/08 —, condizioni
di allerta, `px=None` che non finge un depeg).
@@ -0,0 +1,44 @@
# 2026-08-26 — Gate XSR01 (23/10): riscrittura DICHIARATA, decisa dall'operatore
**Decisione dell'operatore** (oggi, discussione in sessione): opzione **A** fra le tre proposte —
riscrivere ora il gate, dichiarandolo, invece di lasciarlo e citare i difetti (B) o ritirare
XSR01 in anticipo (C, che avrebbe violato la pre-registrazione).
Il docstring del gate prescrive: *"ogni modifica va motivata nel diario come violazione"*.
Questa e' quella motivazione. **Scritta a 58 giorni dalla decisione, prima di vederne l'esito,
e in direzione che STRINGE i criteri** — le tre proprieta' che rendono una riscrittura
difendibile invece che selection-on-forward.
## Le tre modifiche
1. **Capitale per il deploy: $5.000 → $20.000.** Non e' tuning: il gate fu scritto il 25/07,
e il 26/07 l'operatore ha preso la decisione vincolante *"100% Deribit fino a $20k"*
posteriore, quindi governa (M28: quando due impegni si contraddicono, la contraddizione e'
informazione). Il criterio a $5k autorizzava capitale su Hyperliquid che un'altra decisione
gia' vietava. Ora il deploy di XSR01 e la riapertura della scelta del venue coincidono a
$20k: **una decisione sola, un punto solo**.
2. **Gamba haircut: il numero decisivo viene da uno script committato**
`r0826_xsr_haircut.py` (N11: il "$14,41" dell'ammissione non aveva script ed era ~2,4x
ottimista), **a pavimento $10** (il pavimento vero misurato da HL-EXEC; il libro REAL-$5000
del monitor gira a $5 ed e' ottimista per costruzione), **citato accanto alla frazione di
ordini eseguiti** (P7). Misurato oggi, ed e' la conferma empirica del difetto dello
strumento: sul FULL a pavimento $10 l'haircut e' **1,1%** con **il 22% di ordini
eseguiti** — la guardia non puo' quasi scattare per costruzione, perche' un libro che non
si muove ha haircut piccolo. **La soglia resta il 40% pre-registrato: nessuna soglia nuova
viene scritta oggi coi dati in mano.**
3. **Contesto corretto:** il riferimento `IS_SHARPE_NET = 1.82` era una terza lente
(XSR-REPRO): ora cita **1,79**, lente dei gate alla scoperta. Il criterio non cambia.
## I numeri di oggi (58 giorni alla decisione — NON decidono niente)
- forward rigenerato (serie vera dal 26/08): **31 barre, Sharpe 2,72**, maxDD 0,8%
- ticket per gamba a $5.000: mediano **$3,34**, medio $5,99, 63% sotto $5, **84% sotto $10**
(46.200 ordini replay) — riproduce XSR-REPRO e sostituisce il $14,41
- haircut forward a pavimento $10: 10,7% (dentro la guardia), **ordini eseguiti 38%**
- FULL 968 barre: MODELED Sharpe 2,12; floor $10 → haircut 1,1%, eseguiti **22%**
## Cosa riaprirebbe XSR01 (registrato qui perche' non vada riscoperto)
Capitale ≥$60k (per un 25% sopra C\*) · un venue con C\* più basso di $15-20k · una misura che
mostri nel meccanismo qualcosa oltre la coppia (media, vol) — il null del 25/08
(`r0825_xsr_rendita.py`) ha detto che oggi non c'e'.
@@ -0,0 +1,158 @@
# 2026-08-28 — La fase che ruota, gli allarmi che si perdevano, e una risposta fiscale
*Scritto il 2026-08-28. Numeri riprodotti dagli script citati; le letture in prosa sono firmate
come tali (P13).*
## In una riga
Un test rosso su GTAA01 non parlava di GTAA01: parlava del fatto che **la finestra "congelata"
pre-2015 rotola da sola ogni notte**. Nel frattempo si e' chiuso il debito del trasporto degli
allarmi, e la prima delle due domande al commercialista ha trovato una risposta alla fonte.
---
## 1. Il gate (A) della banda GTAA01 e' una moneta — la finestra rotola
`r0828_gtaa_band_phase.py`. Replica bit-exact della produzione verificata (max|Δ| = 0.00e+00 su
7.544 barre). Codice fermo dal 07/08: nessun commit su `src/portfolio/gtaa.py` ne' su
`r0727_gtaa_band_gate.py`. Si e' mosso il DATO.
**(0) La finestra in-sample non e' ferma.** IB serve una finestra **rotolante di 30 anni**. SPY e'
l'unica gamba al muro (30,0 anni) e nel `logs/cron_daily.log` la sua data d'inizio avanza di un
giorno di borsa ogni notte: `1996-07-02` il 24/06 → `1996-09-04` oggi. QQQ e IWM arriveranno allo
stesso muro fra **2,5 e 3,7 anni**.
**(1) L'amplificatore e' la fase.** `_gated_returns` ribilancia su `i % every == 0`, dove `i` e' la
**posizione nell'array**. Se la prima barra scivola, si ri-fasa ogni decisione di ribilanciamento
di trent'anni di storia.
| misura | valore |
|---|---|
| spostamento max dello Sharpe in-sample dal 07/08 (finestra CHIUSA, codice fermo) | **0,076** |
| banda scelta al buio in 10 notti consecutive | 25% · 40% · 60% |
| notti col margine sotto il pavimento dichiarato (0,01) | **4/10** (minimo 0,0026) |
| a dato FISSO, muovendo solo la fase | 25% su 3 fasi, 60% su 2 |
Il criterio decideva su ~0,007-0,015 di Sharpe mentre il rumore che lo scuote vale fino a 0,076.
E' lo stesso modo di fallire del criterio a ranghi ritirato il 07/08 (che decideva su 0,00116), in
un costume nuovo.
## 2. Criterio (C): la mediana delle 5 fasi
`r0828_gtaa_band_median_phase.py`. ⚠️ **Scelto DOPO aver visto la tabella delle fasi**: misura lo
strumento, non valida la banda. Pavimento tenuto a 0,01, invariato.
- (i) scelta stabile su 10 notti: **SI**`60%` in tutte e dieci
- (ii) margine sempre ≥ 0,01: **SI** — minimo 0,0197
- (iii) potenza (al buio ≠ hold-out): **SI** — 60% contro 40%
Escursione dello Sharpe in-sample su 10 notti: **74/99%** per ogni banda. Lo strumento regge.
**Ma la cella che sceglie non e' la proposta:** al buio esce il 60%, il 25% vince **0 notti su 10**.
Da tenere accanto: sull'hold-out il 60% e' **penultimo** (0,7430 contro 0,9315 del 40%), coerente
con lo Spearman IS/OOS ~0 misurato il 07/08 — la scelta in-sample **non predice**.
**Contorno che vale piu' del verdetto:** le 5 serie di fase correlano **0,988****N_eff 1,01**.
La mediana di 5 fasi e' UNA osservazione: toglie l'artefatto, non compra precisione.
### Le due domande che erano state confuse
Il gate (A) puo' chiedere due cose diverse, e il 07/08 coincidevano **per fortuna di fase**:
| forma | difetto | quanto e' grave |
|---|---|---|
| «la banda e' la scelta che si fa al buio?» | **non ha risoluzione** | cambia verdetto ogni notte |
| «la banda non e' stata selezionata sull'hold-out?» | **non ha potenza** | 1 cella su 30 e' l'argmax dell'hold-out → **29/30 lo passano per costruzione** |
📌 *Lettura (agente, fallibile).* Il 25% non viene dalla griglia affatto: viene dall'argomento
**strutturale** del 27/07 — una banda in dollari assoluti degenera al variare del capitale, una
frazione no. Sotto la mediana delle fasi il 25% e' **2° in-sample e 3° sull'hold-out**: decente
ovunque, primo da nessuna parte, che e' la firma di un parametro NON selezionato sui dati. A un
parametro scelto per ragioni strutturali non si applica un gate di *selezione* ma uno di
*robustezza* — «fa danno da qualche parte?». Risposta: no.
**Esito:** gate (A) **ritirato** come pass/fail, congelando il MOTIVO (come il 07/08), piu' una
guardia sulla CAUSA che si rompe il giorno che la fase venisse ancorata al calendario.
## 3. Cosa cambia togliendo GTAA01 — e la decisione dell'operatore
`r0828_senza_gtaa.py`. Portafoglio di **ricerca** a 5 sleeve; NON il book live.
| | Sharpe | CAGR | maxDD | vol |
|---|---|---|---|---|
| CON GTAA01 | **2,28** | +19,2% | 6,1% | 7,8% |
| SENZA, iso-nozionale | 2,16 | **+23,8%** | 7,8% | 10,1% |
| SENZA, **iso-rischio** (×0,77) | 2,16 | +18,0% | 6,0% | 7,8% |
A iso-nozionale togliendolo si guadagna **drift** e si compra **volatilita'**. A iso-rischio — la
sola lente onesta per un diversificatore a basso CAGR (M6) — **Δ Sharpe +0,121**, maxDD invariato.
Hold-out 2025+: **Δ +0,247**. Aiuta in **6 anni su 8**. Correlazione col resto **+0,087**: e'
davvero altro. Standalone Sharpe 1,06, CAGR +5,8%.
⚠️ **Con la sua fascia di fase:** il Δ sulle 5 fasi va da **+0,095 a +0,124** e supera la soglia
dichiarata (0,12) in **2 fasi su 5**. Il **segno e' stabile**, il verdetto **binario** no.
### La decisione: rinviata a $15k di book
Discussa con l'operatore. L'argomento che ha deciso non e' «GTAA01 non contribuisce» — la misura
dice il contrario — ma che **a questo capitale lo sleeve non e' accendibile**: `GTAA_MIN_CAPITAL`
e' **$3.000 allocati**, che al peso 20% significa **$15.000 di book** contro i **$2.068** attuali.
Sotto quella soglia e' manutenzione senza beneficio incassabile.
Due ragioni registrate **contro** il blocco immediato, entrambe regole del progetto:
- **N7** — uno sleeve difensivo si giudica **sul sinistro, non sul premio**, e la finestra
2019-2026 non contiene il sinistro per cui esiste un GTAA a sei gambe;
- **N4** — il rischio di venue si compra con un **conto**, non con uno sleeve: togliere GTAA01
renderebbe il portafoglio di ricerca **100% cripto su un venue solo**.
E un costo che sarebbe andato pagato, non saltato: GTAA01 e' uno dei **quattro** nomi di
`r0726_wall_fixedpoint.W_DEPLOY`, da cui escono i **$313k** e tutta la pianificazione da €500/mese.
**Perche' la decisione non si dimentichi** (N9), la soglia e' sorvegliata dal giornale:
`journal.SOGLIE_CAPITALE` legge l'equity ogni giorno e il giorno che supera $15k la voce lo dice,
con la domanda da rispondere accanto. Stessa tabella, seconda riga: **$20k**, dove si riapre
«100% Deribit fino a $20k».
## 4. Allarmi: il marcatore «gia' detto» si scrive DOPO l'invio
Debito §5.2 chiuso su decisione dell'operatore. `venue_watch.run_once` salvava lo stato coi
marcatori `alerted` gia' a True e l'invio lo faceva il chiamante **dopo**: col 6,9% di invii
falliti misurato (2/29), un 🚨 perso restava perso per l'**episodio intero** (200-2.324 ore).
Ora il sender e' **iniettato** (la funzione resta testabile senza rete d'uscita, che era la ragione
del disegno precedente) e su fallimento si disfano i **soli marcatori**, non le misure:
asset in ALERT → ri-allerta l'ora dopo; lock MAINT/ALERT → flag azzerati **ma le ore continuano a
correre**, cosi' una manutenzione che sfora la grazia sale ad ALERT anche col trasporto giu';
lock RIENTRATO → stato ripristinato, perche' il rientro si annuncia una volta sola.
`notify()` accetta `tentativi` (venue_watch: 3) e l'esito finisce nel log del cron.
⚠️ Resta scoperto **ogni altro chiamante di `notify()`**: la disciplina e' oggi solo in venue_watch.
## 5. L'analista del 27/08, e la riga di diagnosi che mancava
La voce del 27/08 era senza analisi. Causa, dal transcript della sessione headless:
`Failed to authenticate: OAuth session expired and could not be refreshed`, exit 1 alle
`00:37:00.98Z`. La meccanica aveva retto (nessuna analisi vecchia spacciata per nuova, esito nel DB,
notifica partita) ma il motivo registrato era `uscita 1: ` — vuoto: `interroga()` leggeva il solo
**stderr**, e la CLI aveva scritto su **stdout**. Riparato (`motivo_uscita`), analisi recuperata.
Guasto **transitorio**: il refresh token era valido, unico caso su 24 sessioni.
## 6. Fisco: la domanda (a) ha una risposta alla fonte
Circolare AdE **30/E del 27/10/2023**, letta direttamente dal PDF ufficiale. **I derivati con
sottostante cripto stanno in `c-quater` → 26%**, non in `c-sexies` → 33%, e la circolare dice
esplicitamente che la riforma 2023 non sposta nulla. Il **collaterale** sconta il **2‰** al 31/12
(intermediario non residente → imposta sul valore delle cripto-attivita', ~$4/anno a questo
capitale). E la conversione **USDC→USDE non e' fattispecie realizzativa**. Dettaglio e citazioni
verbatim in CLAUDE.md §5.4. La (b), i payout di una prop firm, **resta aperta**.
## Cosa cambia
- `CLAUDE.md`: §3 nuova riga (GTAA01 rinviato a $15k) · §5.2 risolto · §5.4 riscritto · §5.13 nuovo
debito (la fase che ruota, quantificata e innocua oggi).
- `src/live/journal.py`: `SOGLIE_CAPITALE` + regola `soglia_capitale`.
- `src/live/venue_watch.py`, `src/live/notifier.py`, `scripts/live/venue_watch.py`: commit dopo l'invio.
- `src/live/analista.py`: `motivo_uscita` da stdout E stderr.
- `tests/test_gtaa_band_gate.py`: gate (A) ritirato, motivo e causa congelati.
- Script nuovi: `r0828_gtaa_band_phase.py`, `r0828_gtaa_band_median_phase.py`, `r0828_senza_gtaa.py`.
**Suite: 789 passati, 0 falliti.**
+67
View File
@@ -0,0 +1,67 @@
# 2026-08-29 — USDE idoneo, la quota rinviata a lunedì, e il fix IB confermato
*Scritto il 2026-08-29. Numeri letti dalle serie citate.*
## 1. GATE USDE-01: CHIUSO **IDONEO**
Il 2026-08-28 alle 12:35Z `usde_watch` ha rilevato il primo reward:
```
equity USDE : 500.0521 @ 1.0001 (indice) -> $500.05
delta : +0.052055 USDE dall'ultima lettura
verdetto : IDONEO — reward rilevato il 2026-08-28T12:35:01Z
```
La regola era pre-registrata il 26/08, **prima** dell'esito: ≥1 reward entro il 29/08 → IDONEO,
e si apre la decisione di QUOTA, che è dell'operatore. Il gate ha fatto quello che doveva.
## 2. Il tasso, e perché la quota è stata rinviata
| lettura | tasso implicito |
|---|---|
| il solo pagamento (0,052055 su 500) | **3,80% annuo** |
| su **due** finestre — il 27/08 ha pagato **zero** | **1,90% annuo** |
| annunciato da Deribit (ricerca 26/08) | 9% |
**Decisione dell'operatore (29/08): la quota si decide lunedì 31/08**, su tre-quattro finestre
invece che su una. Il motivo è nella tabella: fra 3,80% e 1,90% c'è tutta la decisione, e il
campione è **un pagamento**. Il costo dell'attesa è zero — i 500 USDE rendono comunque.
📌 Perché il numero non venga letto male: `usde_watch.rendimento()` calcola l'APR sulle finestre
**osservate**, col denominatore sul **tempo vero** e non sul numero di finestre pagate — così una
finestra che non paga **abbassa** la stima invece di sparire (P5: quel silenzio è uno zero). Le
letture con trade nel mezzo si escludono (P12). L'APR si stampa sempre **col suo `n`**, e sotto
4 finestre la riga dice *«un pagamento non è un tasso»*. Sulle letture vere di oggi:
**1,97% annuo su 1,9 giorni, 1/2 finestre pagate**.
📌 Perché la decisione non si dimentichi (N9): `quota_da_decidere()` è vera **solo** se il conto è
IDONEO **e** il rinvio è scaduto. Da lunedì il watch manda il 📌 **ogni giorno** — con quota
attuale, tetto di allerta e APR osservato accanto — finché non si decide. Si smette togliendo
`DECISIONE_QUOTA_DAL`. È lo stesso schema della soglia $15k di GTAA01, con la data al posto
del capitale.
⚠️ Nota di contorno che vale una lezione: il 📌 dell'IDONEO era partito su Telegram il 28/08 alle
12:35Z, **in mezzo ai messaggi-spazzatura dei test** delle 14:47 e 14:52 (difetto riparato lo
stesso giorno). L'unico messaggio che contava è arrivato nel giorno in cui il canale era sporco.
È P9 in forma concreta.
## 3. Il fix IB è confermato
Il giro del cron delle 00:30 di stanotte: **zero** righe `open orders request timed out` (erano
2 per giro, 63 giri su 63) e tutti e sei gli ETF scaricati — `SPY n=7541`, `QQQ n=6912`,
`IWM n=6602`, `TLT n=2658`, `GLD n=5476`, `HYG n=4877`, aggiornati al 28/08. Il `readonly=True`
resta.
📌 E una conferma involontaria del debito §5.13: **SPY è passato da `1996-09-04` a `1996-09-05`**.
La finestra rotolante ha perso un altro giorno di borsa in una notte, esattamente come misurato
il 28/08 — il difetto si legge a occhio nudo nel log del cron.
## Cosa cambia
- `scripts/live/usde_watch.py`: `rendimento()` (APR sulle finestre osservate, col suo n) ·
`quota_da_decidere()` · `DECISIONE_QUOTA_DAL = "2026-08-31"` · il 📌 giornaliero da lunedì.
- `tests/test_usde_watch.py`: 6 test nuovi, incluso il controllo positivo (*la finestra sola
darebbe il doppio*) che è la ragione per cui l'APR si stampa col suo `n`.
- `CLAUDE.md`: §1 e §4 aggiornati — gate CHIUSO IDONEO, quota rinviata a lunedì.
**Suite: 795 passati, 0 falliti.**
@@ -0,0 +1,318 @@
# 2026-08-30 — "porta in USDE tutto il capitale che non viene usato": il venue dice 31%
**Richiesta dell'operatore**, verbatim: *"porta in usde tutto il capitale che non viene usato"*.
Arriva il 30/08, cioè **un giorno prima** della data a cui l'operatore stesso aveva rinviato la
decisione di quota (gate USDE-01, rinvio al 31/08). L'anticipo è esplicito e vale come decisione.
## 1. "Il capitale che non viene usato" non è una quantità piccola: è quasi tutto
Stato letto dal gateway alle 19:50Z: USDC **$1.563,09** (di cui **$11,36** impegnati a margine),
USDE 500,1757 → **$500,18**, totale **$2.063,26**, quota **24,2%**. Posizioni BTC $354,72 + ETH
$213,36 = **$568,08** lordi (0,275x).
Il margine consuma **$11 su $2.063**, e l'USDE è cross-collateral: convertirlo non lo toglie
dall'uso. Anche a piena esposizione del libro (lordo 1,0x) il margine sarebbe ~$41. Presa alla
lettera la richiesta è una quota **~99%** — il 100% che il gate esclude per nome ("mai 100%: R1
emittente non recuperabile"). Le due letture plausibili ("non a margine" e "non esposto dal libro")
convergono entrambe su ~97-99%: non è un'ambiguità che si risolve scegliendo, è una che si porta
all'operatore.
## 2. Il vincolo che `r0830_usde_quota` non aveva guardato: il cuscino di REGOLAMENTO
L'analisi del 30/08 aveva misurato il **margine** e concluso — correttamente — che l'haircut non
morde a nessuna quota fino al 90%, nemmeno a leva 1,50x con USDE a 0,95. Vero, e irrilevante:
il P&L e il funding dei perp **USDC-lineari si regolano in USDC**, non nel collaterale. Lo si vede
nel conto: `equity balance = $7,24` di floating, tutto nel secchio USDC.
A quota ~99% resterebbero **$41 di USDC** contro un libro che, col disaster-SL a 30% sulla massima
esposizione, può perderne **~$619**: il saldo va negativo e Deribit lo finanzia a interesse, che si
mangia la resa che si stava comprando. **Il vincolo non è il margine, ma non è nemmeno assente.**
> 📌 **Corretto e QUANTIFICATO il 31/08** (KB *Cross collateral specifications*): «*a collateral fee
> will be charged […] **default = 0.05% per day***», al secondo, sulla valuta negativa — cioè
> **18,25%/anno, 4,3× la resa USDE** che si starebbe comprando. Il ribilanciamento automatico non
> salva: scatta a **$1M** assoluti o al **100% della cross equity**, soglie irraggiungibili a $2k.
> Qui il 30/08 avevo scritto «lo finanzia a interesse» **senza il numero**: il numero rende il
> criterio del cuscino più giustificato, non meno.
Criterio dichiarato: cuscino USDC ≥ disaster-SL sulla massima esposizione lorda del libro =
`n_asset × frac × disaster_sl_pct` = 2 × 0,5 × 0,30 = **30% dell'equity** — tutto derivato da
`config/live.json` e `src/live/book.py`, mai ridichiarato (P1). Che lascia esattamente **70%**.
Non è un argmax (M8): la quota massima e il criterio coincidono per costruzione, e infatti il 70%
cade **esattamente sul limite, con zero slack**.
Tabella portata all'operatore (APR allora creduta 3,26% [1,13-4,51], n=4 — vedi §5, è sbagliata):
| quota | converti | USDC che resta | resa/anno | evento emittente vs maxDD libro |
|---|---|---|---|---|
| 24,2% (allora) | — | $1.563 | $16 | 3,1× |
| 50% | $531 | $1.032 | $34 | 6,3× |
| **70%** | **$944** | **$619** | **$47** | **8,8×** |
| ~99% | $1.522 | $41 | $66 | 12,3× |
**Scelta dell'operatore: 70%**, il massimo compatibile col cuscino.
## 3. Esecuzione: da 500 a 644 USDE, poi il muro
Nuovo attrezzo **`scripts/live/usde_convert.py`** (dry-run di default). Non passa da
`DeribitTrader`: `execution.ALLOWED` ammette solo i due perp del libro ed è il guardrail
anti-fat-finger del percorso soldi — **non si allarga** per farci entrare uno spot che col libro
non c'entra. Lo script porta le proprie guardie: banda prezzo [0,995 1,005], tetto HARD di quota
0,95, il cuscino di regolamento, dry-run.
Convertiti **+144 USDE** (500,1757 → **644,175691**), quota **24,24% → 31,21%**, tutti i fill a
1,0002-1,0003, **fee 0**. Poi ogni acquisto ha smesso di passare.
## 4. `not_enough_funds_in_currency` è un messaggio FUORVIANTE — la cronaca delle ipotesi sbagliate
Si tiene apposta, perché chi rilegge non deve rifare questo giro. Tre ipotesi, tutte plausibili,
tutte sbagliate:
1. **"È la taglia."** 943,98 rifiutato per `Invalid params` → vero: `min_trade_amount: 1` e
`contract_size: 1`, il passo è **intero** (confermato da `public/get_instrument`; il gateway
non espone quel tool, la risposta è arrivata dall'API pubblica). Quantizzato a intero: 933
rifiutato per **fondi**, con **$1.541 disponibili**. Falso indizio.
2. **"È il rate-limit."** 100 passava e 300 no; poi anche **1** rifiutato otto volte di fila.
Aggiunto backoff sul tempo fino a 90s: rifiutato lo stesso. Falso.
3. **"È il prezzo."** Diagnosi a tre ordini da 1 USDE: SELL @1,0000 OK, BUY **@1,0003 OK**,
BUY @1,0002 e BUY market rifiutati. Vero **in parte**: il book REST pubblico è in **ritardo**
sul matching engine, e un limite a `ask+1 tick` a volte non incrocia davvero. Difetto mio, e
istruttivo: prezzavo l'ordine sull'**indice** invece che sul **book**. Sono due prezzi con due
mestieri — l'indice **marca** il collaterale (`usde.valuta`, mediana multi-exchange: la lezione
Binance 10/10), il book **prezza** lo scambio. Corretto con 5 tick di margine e tetto duro
1,0010: il fill avviene al prezzo del libro, quindi il margine non si paga.
Ma corretto il prezzo, **200 e 800 continuavano a fallire con 4.552 di profondità all'ask**.
## 5. 🚨 Il fatto vero: un TETTO DEL VENUE sull'USDE, a 644,175691 (31,21%)
Esperimento **a saldo neutro**, quattro ordini di fila (20:14Z):
```
BUY 20 @1.0007 -> not_enough_funds_in_currency USDE 644.1757
SELL 20 @0.9996 -> OK USDE 624.1757
BUY 20 @1.0007 -> OK USDE 644.1757
BUY 5 @1.0007 -> not_enough_funds_in_currency USDE 644.1757
```
È un **tetto sul LIVELLO**: non taglia, non prezzo, non liquidità, non cadenza, non fondi
($1.408 disponibili). Lo stesso ordine che viene rifiutato passa subito dopo una vendita di pari
taglia. **La quota del 70% non è raggiungibile**, e nemmeno il 50%.
La formula del tetto **non è leggibile da qui**: `public/get_currencies` non espone un cap
(`in_cross_collateral_pool: true`, nient'altro), e il gateway non espone né `get_order_book`
`available_withdrawal_funds` — è il debito #11, il gateway è l'unico pezzo della catena che
possediamo e si ha solo quel che espone. Registrato come fatto misurato, non spiegato (D5: un buco
quantificato è un risultato, uno taciuto è un debito).
## 6. 🚨 E la resa non è quella che credevamo: **l'USDC paga 3,40%**
`public/get_currencies`, letta oggi:
| valuta | APR pubblicata |
|---|---|
| USDE | **4,1071%** |
| USDC | **3,4000%** |
I nostri reward USDE misurati (+0,052055 · +0,061815 · +0,061821 al giorno su 500 → **~4,5%/anno**)
**confermano** che la APR pubblicata di USDE è reale. Il che rende credibile anche l'altra riga.
Se l'USDC frutta già 3,40%, il guadagno del passaggio a USDE **non è il tasso: è lo spread**,
**0,71 punti**. Sui $644 che teniamo vale **~$4,6/anno**, non $21; sui $144 convertiti oggi,
**~$1,0/anno**. Il gate USDE-01 ha misurato con cura il reward dell'USDE e **non ha mai chiesto
cosa facesse l'USDC fermo**: è il controfattuale mancante — M1 in un'altra veste, si giudica il
**marginale**, non il livello. E cambia il verso della decisione: si prende un rischio emittente
**non recuperabile** (a 31,2% vale 3,9× il maxDD dell'intero libro) per **0,71 punti**.
⚠️ **Quello che NON è dimostrato:** che la APR USDC sia effettivamente accreditata *sul nostro
conto*. Sull'USDE l'abbiamo vista arrivare per delta; sull'USDC il P&L di trading copre $0,13/giorno
di interesse e il gateway non espone il Transaction Log. **Va verificato su un giorno senza trade
prima di trattarlo come misura** — qui è una APR pubblicata dal venue più una conferma indiretta,
non un reward osservato.
## 7. Cosa resta a terra
- **Conto**: USDC $1.419,61 · USDE 644,175691 ($644,18) · totale **$2.063,79** · quota **31,21%**.
Nessun ordine spot appeso (restano i due `tp01-disaster` reduce_only). Posizioni invariate.
Equity vista dal book **$2.063,86** ("mainnet USDC + USDE 644 @ 1.0000"): sizing corretto,
nessun falso "uscita di fondi".
- **`config/live.json`**: aggiunto `quota_target: 0.70` (la decisione dell'operatore, oggi bloccata
dal venue). `quota_max_frac` alzato a 0,85 e **rimesso a 0,50** nella stessa sessione: la soglia
larga presupponeva un 70% che non esiste, e lasciarla avrebbe **disarmato la guardia per uno
scenario che non si è verificato. Con la quota reale al 31%, il 50% morde di nuovo.**
- **Non fatto**: portare la quota al 70%. Il venue non lo consente. Non è una rinuncia
discrezionale, è un rifiuto misurato e riproducibile.
---
# APPENDICE (stessa sera, 23:40-23:55Z) — **l'USDC NON frutta sul nostro conto: §6 era sbagliata**
Richiesta dell'operatore: *"verifica se l'USDC frutta davvero sul nostro conto"*. Verificato.
**La risposta ribalta la §6 di questo stesso diario**, che va letta con questa appendice accanto.
## Perché la domanda sembrava dover aspettare, e invece no
Il gateway non espone il Transaction Log (provati `get_transaction_log`, `get_settlement_history`,
`get_deposits`, `get_transfers`, `get_interest_history`: **tutti 404** — debito #11). L'unica serie
storica del conto è `trades.db.equity`: **oraria ma arrotondata a 2 decimali**, e contiene il P&L
non realizzato, che a posizioni aperte oscilla di ±$2-8/ora. $0,13/giorno di interesse ci sparisce.
Poi la fonte ufficiale Deribit ha spostato il problema dal *rumore* al *calendario*:
> «Every day at 00:00 UTC, Deribit calculates the **minimum equity** of USDC that a user has been
> holding over the previous 24 hours. […] After the month is over, the rewards from each day are
> summed together and **paid out as a single monthly payment early in the following month**.»
> — `insights.deribit.com/education/usdc-rewards-now-paid-on-deribit/`
**I reward USDC si pagano UNA VOLTA AL MESE**, non ogni giorno come quelli USDE. Ecco perché non li
avevamo mai visti: guardavamo a cadenza giornaliera, dove l'USDE si vede e l'USDC per costruzione no.
E un accredito mensile da qualche dollaro **si vede benissimo anche a 2 decimali** — purché il libro
sia FLAT, perché allora `equity == balance == USDC` e ogni scalino è un accredito.
## La misura: due confini di mese, entrambi a libro flat
| finestra | punti orari | equity min | equity max | ore senza alcun movimento | scalini >1 cent | reward atteso se idoneo |
|---|---|---|---|---|---|---|
| **29/06 → 08/07** | 238 | **598,06** | **598,06** | **237 / 237** | **0** | $0,45 (8 gg dal 23/06) |
| **31/07 → 03/08** | 96 | **596,92** | **596,92** | **tutte** | **0** | **$1,72** (luglio intero) |
Il secondo è il caso decisivo: **$1,72 attesi contro una risoluzione di $0,01 — 172×** — e l'equity
non si muove di un centesimo per quattro giorni pieni, coprendo tutta la finestra "early in the
following month". Non è un'assenza sotto la soglia di rilevabilità: è uno zero misurato.
**Il conto NON riceve i reward USDC.** N=2 confini indipendenti, entrambi puliti per costruzione.
## Perché, e perché è coerente col fatto che l'USDE invece paga
Deribit: *«A user's eligibility to receive USDC is based on their location»*. L'ipotesi che spiega
entrambe le osservazioni è **MiCA**: USDC è un *e-money token* regolamentato, e a un residente UE
non se ne può corrispondere rendimento; **USDe non è un EMT**, e infatti i suoi reward arrivano
(li abbiamo visti per delta: +0,052055 · +0,061815 · +0,061821). ⚠️ *Questa è la spiegazione
plausibile, non una fonte normativa verificata* (P13/M27: una norma citata si verifica come un
numero). **Il fatto misurato è lo zero, non il suo motivo.**
## Cosa cambia (e cosa la §6 aveva sbagliato)
- **La premessa originale del gate USDE-01 è RESTAURATA.** Il guadagno di tenere USDE **non è lo
spread 0,71 punti: è il tasso pieno ~4,1%**, perché l'alternativa sul nostro conto rende **0**.
Sui $644 che teniamo: **~$26/anno** (e ~$29 al ritmo misurato di ~4,5%), non i ~$4,6 di §6.
- **Cosa avevo sbagliato, e la lezione.** Ho letto `apr: 3.4` in `public/get_currencies` e l'ho
trattato come una proprietà **del nostro conto**. È una proprietà **del venue**: un listino, non
un accredito. La conferma indiretta che invocavo ("i reward USDE misurati coincidono con la loro
APR pubblicata") provava che il listino è reale **per l'USDE**, e non diceva nulla sull'idoneità
dell'USDC. 📌 **Un tasso pubblicato non è un tasso incassato: si verifica sul CONTO, e la verifica
costava una query sulla serie che avevamo già.** È N10 in una veste nuova — la verifica a €0 va
fatta *prima*, e si fa **sul venue, non sul sito**; qui perfino il venue non bastava, serviva il
conto.
- Resta vero e non toccato: il **tetto del venue al 31,21%**, il **cuscino di regolamento** (30%
dell'equity, che lascia il 70%), e che a 31,2% l'evento emittente vale ~3,9× il maxDD del libro.
La decisione "tenere o no i $644" cambia però di segno rispetto a come l'avevo chiusa: si compra
**$26/anno**, non $4,6.
## Strumento lasciato in piedi
**`scripts/live/balance_watch.py`** + **`scripts/cron_balance.sh`** (orario al minuto **:42**, libero
fra :25/:35/:47; sola lettura). Registra il **balance a 8 decimali** per valuta — la serie che
mancava — con il conteggio dei fill dall'ultimo campione e il nozionale lordo, così una finestra
sporca si riconosce invece di essere mediata dentro. Etichetta PULITA le finestre a 0 fill e libro
flat, dove il Δ USDC **è** l'interesse, e ne stampa l'APR implicita accanto a quella attesa.
Serve a sorvegliare che lo zero resti zero (o che cambi, se l'idoneità cambia): l'inizio di
settembre è il prossimo confine di mese, ed è già strumentato.
---
# APPENDICE 2 (2026-08-31) — la fonte ufficiale: **due correzioni alle mie conclusioni**
L'operatore ha passato l'articolo `support.deribit.com/.../Yield-reward-bearing-coins`. WebFetch lo
prende 403 (Cloudflare); si legge dall'**API Help Center** in JSON:
`support.deribit.com/api/v2/help_center/en-us/articles/31424939199261.json` → HTTP 200.
*Aggiornato dal venue il 2026-08-20.* Contiene tre cose che il progetto non sapeva.
## 1. L'ITALIA è nella lista degli esclusi dai reward USDC — e non è MiCA
> «The following jurisdictions are **not eligible** to receive USDC rewards: Austria, Belarus,
> Belgium, Bulgaria, Canada, […] **Italy**, Japan, […]»
Lo zero misurato è **confermato dalla fonte**. Ma l'ipotesi che avevo scritto — MiCA, USDC è un
e-money token e USDe no — **è sbagliata**: la lista contiene Canada e Giappone, non è il perimetro
MiCA. È policy di giurisdizione di Deribit. *M27: una fonte normativa citata si verifica come un
numero — e io l'avevo dedotta invece di leggerla.*
## 2. La finestra di pagamento è di DUE SETTIMANE, non di tre giorni — il «172×» era sovra-affermato
> «The monthly USDC rewards payment is then made **within the first two weeks** of the following
> month.»
Avevo verificato su 29/06→08/07 e 31/07→03/08: **8 giorni su 14 e 3 su 14**. Rifatto sulle finestre
vere:
| accredito | finestra vera | letture | escursione TOTALE equity | scalini ≥70% dell'atteso | esito |
|---|---|---|---|---|---|
| giugno ($0,45) | 01→16/07 | 374 | **$2,74** | 7 | ❌ **NON conclusivo** |
| luglio ($1,72) | 01→16/08 | 382 | **$0,48** | **0** | ✅ **conclusivo** |
Luglio regge da solo: il credito atteso è **3,6× l'intera escursione dell'equity** su due settimane.
Giugno no — il libro ha iniziato a operare a metà mese e il rumore ha la taglia del segnale.
⇒ La conclusione **non cambia** (una finestra conclusiva + la lista ufficiale), ma l'evidenza è
**una** finestra, non due. *Avevo citato la sotto-finestra piatta e chiamato «zero misurato» ciò che
la finestra completa non copriva: scegliere la sotto-finestra che fa vedere lo zero è la stessa
mossa che il progetto vieta a un backtest.*
## 3. Il tetto NON è un livello: è una FRAZIONE dell'equity, e scala col conto
L'articolo documenta un **Cap ETHENA** che diluisce il **tasso** a livello di exchange —
`User APR = min(min_exchange_USDe_balance, Ethena Cap) / min_exchange_USDe_balance × Ethena APR`
e **nessun limite sulle quantità detenibili da un conto**. Quindi il muro non aveva base
documentale, e andava ri-sondato. Fatto (31/08, giorno UTC nuovo → non è un limite giornaliero):
```
BUY 5 -> not_enough_funds_in_currency USDE 644.175691 (muro ancora li')
SELL 10 -> OK USDE 634.175691
BUY 10 -> not_enough_funds_in_currency USDE 634.175691 <-- ieri 644,18 passava!
BUY 5 -> OK USDE 639.175691
BUY 3 -> OK USDE 642.175691
BUY 1 -> OK USDE 643.175691
BUY 1 -> not_enough_funds_in_currency USDE 643.175691
```
**Ieri si tornava a 644,18 e oggi no**, con l'equity scesa di $8. Il tetto si è mosso con lei:
| | tetto | equity | frazione |
|---|---|---|---|
| 30/08 | [644,18 · 645,18) | $2.063,79 | 31,21% 31,26% |
| 31/08 | [643,18 · 644,18) | $2.055,56 | 31,29% 31,34% |
I bracket distano **0,05pp**, dentro il rumore dell'equity (±$2-8/ora). Candidato pulito **5/16 =
31,25%**, ma con questa risoluzione non si distingue da una regola sul collaterale scontato
dell'haircut (~29%): **non si sceglie quella che conviene, si cita la banda** (M25).
**Il tetto SCALA col conto.** La quota resta ~31% per sempre, il valore in dollari cresce col
capitale, e il 70% non è raggiungibile né ora né mai. Conseguenza che ieri non avevo visto: essendo
pinnati al tetto, **il rischio emittente resta una frazione COSTANTE del conto** — non si diluisce
crescendo, a meno di vendere apposta.
## 4. E l'USDe ha un fee del 5% che non avevamo mai nominato
> «Deribit Fee: A percentage deducted from the reward before distribution (**currently 5%**)»
> · reward su **minima equity 00:00→24:00 UTC**, distribuiti **~12:00 UTC**, «visible inside
> Transaction Log» (che il gateway non espone: da lì il metodo per delta).
Il nostro **misurato** (~4,5%/anno) è già netto del fee: è quello che arriva sul conto, ed è
l'unico numero da citare. La `apr` di `get_currencies` (4,1071%) è un listino, non un incasso —
la stessa confusione che mi era costata la §6.
## 5. Cablato, così il tetto smette di vivere in prosa
Alla domanda dell'operatore «dove è scritto il valore del tetto» la risposta era: **in tre note di
testo e in nessun posto che il codice legga**, tanto che `usde_convert --quota 0.70` dichiarava
«piano valido» per un ordine che il venue avrebbe rifiutato. Ora:
- `config/live.json``usde.venue_cap_frac` **0.312** (bordo BASSO dei bracket: fa fallire il
piano *prima* dell'ordine invece che dopo il rifiuto) + `venue_cap_misurato` con la data;
- `src/live/usde.py` lo porta nei default — unica autorità, come tutto il resto della sezione (P1);
- `usde_convert.piano()` rifiuta il bersaglio sopra il tetto e stampa il massimo raggiungibile.
Verificato: `--quota 0.70` → *«TETTO DEL VENUE: bersaglio $1.438,90 sopra $641,34 (31,2%
dell'equity, misurato 2026-08-31)»*, nessun ordine inviato.
**Stato finale**: USDE **643,175691** (quota **31,29%**, cioè al tetto), USDC $1.412,40,
totale $2.055,57.
+128
View File
@@ -0,0 +1,128 @@
# 2026-08-30 — La quota USDE: l'EV non puo' sceglierla, e aspettare costa $1,33
*Scritto 2026-08-30T17:32:57Z. Script: `scripts/research/r0830_usde_quota.py` (verdetto a runtime, N11).
Stato conto congelato nello script alla lettura del 2026-08-30T17:05Z.*
## La domanda
Il gate USDE-01 e' **chiuso IDONEO** dal 28/08. Resta la decisione di **quota**, rinviata
dall'operatore a **lunedi' 31/08**: quanto del conto tenere in USDE.
## Il risultato strutturale — dichiarato prima di guardare i numeri
Il valore atteso e' **lineare in q**. Un massimo lineare sta sempre in un **angolo** (0% o
100%). ⇒ **l'EV non puo' scegliere una quota interna**: puo' solo dirne il segno. Ogni quota
intermedia nasce da un criterio sulla **coda**, che va **dichiarato**, non ottimizzato (M8).
Per questo lo script non sceglie: mette il **prezzo accanto a ogni criterio**.
## (A) Il rendimento, col suo n
| | |
|---|---|
| finestre misurabili | **4** (pagate 3) |
| per finestra | 0,00% · 3,80% · 4,51% · 4,51% |
| APR di `usde_watch` | **3,26%** annuo (tempo vero, 3,93 giorni) — *il numero che gira* |
| APR per-finestra | 3,21% (n finestre, 4,00 giorni) |
| banda bootstrap 95% | **[1,13% 4,51%]** (200.000 ricampionamenti) |
📌 La banda e' larga **3,4 punti** su un livello di 3,2%: a n=4 il tasso **non e' misurato, e'
abbozzato**. La divergenza fra i due denominatori (0,06 punti) e' **dichiarata, non appianata**
(P12), e il punto stimato si **deriva** da `usde_watch.rendimento()` invece di essere
ridichiarato (P1).
## (B) L'hazard di pareggio — l'unico criterio che non richiede un criterio
Perdita totale **non recuperabile**, resa 3,26%/anno ⇒ **hazard annuo di pareggio = APR**.
- **3,26%/anno** (banda 1,13% 4,51%)
- recupero di una perdita totale con la sola resa: **30,6 anni**, *indipendente dalla quota*
- ⇒ la quota e' EV-positiva **se e solo se** si crede che Ethena abbia meno del **3,3% annuo**
di probabilita' di evento catastrofico. Quel numero e' **assunto, non stimato** — esattamente
come il `p` di Deribit della decisione 26/07.
## (C) Il rischio marginale, dato il 100%-Deribit gia' accettato
Framework **ereditato** dalla decisione 26/07, non re-inventato: `P = 1-(1-p)^20`. Lo script lo
**riproduce** (M23: far riprodurre alla macchina il numero vecchio prima di pubblicarne uno nuovo):
10/18/33/64% contro i 10/18/34/64% registrati — **OK** a 1,5 punti.
Due letture che **non si annullano**, e la contraddizione e' informazione (M28):
- ✅ L'USDE fa perdere soldi **solo** nel mondo in cui Ethena salta **e Deribit sopravvive**. Nel
mondo in cui salta Deribit, USDC e USDE si perdono insieme e la quota e' irrilevante. Il rischio
**aggiunto** e' quindi di **secondo ordine** rispetto a quello gia' accettato.
- ⚠️ Ma e' un **terzo strato sullo stesso conto**, e **N4** dice che un rischio di venue si compra
con un **secondo CONTO**, non aumentando l'esposizione al primo.
La prima dice che la quota costa poco; la seconda che **non e' li' che si compra sicurezza**.
## (D) I vincoli operativi: nessuno morde
Il libro gira al **2,00% di margine** ($11,37 su $568,64 di nozionale lordo). Al tetto di leva
1,00x servirebbero ~$41 su un collaterale di ~$2.000.
| quota | margine utilizzabile | serve al tetto | morde? |
|---|---|---|---|
| 24,2% | $2.014 | $41 | no |
| 50,0% | $1.961 | $41 | no |
| 90,0% | $1.878 | $41 | no |
**l'haircut 10% non morde a nessuna quota testata**, tre ordini di grandezza di margine. D5:
buco quantificato e innocuo. **Il vincolo non e' il margine.**
Churn da depeg: a soglia critica 0,95 l'equity cala di 5%·q ⇒ a quota 50% sono **2,50%**, cioe'
~$52 di nozionale da girare. Il libro **de-risca mentre l'USDE e' a sconto e ri-carica al ritorno
del peg** — compra alto e vende basso — ma per importi di **secondo ordine**. Non e' il vincolo.
## (E) Il menu dei criteri, ciascuno col suo prezzo
| criterio | quota | USDE | resa $/a | drift libro | == €/mese | mesi di bonifico a rischio |
|---|---|---|---|---|---|---|
| **C0** status quo | 24,2% | $500 | $16 | 0,79% | 19 | **2,0** |
| **C1** ≤ maxDD del libro (7,94%) | 7,9% | $164 | $5 | 0,26% | 6 | 0,7 |
| **C2** ≤ 6 mesi di versamento | 72,7% | $1.500 | $49 | 2,37% | 58 | 6,0 |
| **C3** ≤ 12 mesi di versamento | 100% | $2.064 | $67 | 3,26% | 80 | 8,3 |
| **C4** tetto di ALLERTA di config | 50,0% | $1.032 | $34 | 1,63% | 40 | 4,1 |
⚠️ **Due monete, non si sommano** (N8): *resa $/a* e' **cassa** vera; *== €/mese* e' l'equivalente
in **versamento** via l'equivalenza registrata (100 €/mese == +4,07%/anno di drift), che e' una
capitalizzazione a 10 anni, **non un flusso**. Semplificazione dichiarata: EUR e USD 1:1 (a ~1,08
sposta i mesi di ~8%, non muove nessuna soglia).
## (F) Cosa costa aspettare — il numero decisivo
La conversione e' su spot `USDE_USDC`: **fee 0, spread ~3 bps** ⇒ andata+ritorno **~6 bps**. La
decisione e' **reversibile a costo quasi nullo** — non e' una porta a senso unico e va decisa come
tale.
| attendere | finestre | andata/ritorno | resa persa | costo totale |
|---|---|---|---|---|
| 0 sett | 4 | $0,32 | $0,00 | $0,32 |
| 2 sett | 18 | $0,32 | $0,67 | $0,98 |
| **4 sett** | **32** | $0,32 | $1,33 | **$1,65** |
| 8 sett | 60 | $0,32 | $2,66 | $2,98 |
📌 **Portare la quota a 50% fra quattro settimane invece che domani costa $1,33 di resa non
incassata e compra 8× le finestre** (n da 4 a 32), stringendo la banda di ~2,8× — da ~3,4 punti a
~1,2. **$1,33 per sapere se il tasso e' 1,1% o 4,5%.** E' l'applicazione diretta di N10: una data
si giustifica col **costo della misura**, non con la sua difficolta' percepita.
## Verdetto (calcolato a runtime)
- **RISOLUZIONE INSUFFICIENTE PER ALZARE** — n=4, banda larga 3,4 punti. Alzare ora e' comprare un
tasso che non e' ancora misurato.
- **NESSUN VINCOLO OPERATIVO** — l'haircut non morde a nessuna quota; il vincolo non e' il margine.
- **SCALA** — la resa alla quota di oggi vale **$16/anno**, meno di **un mese** di versamento. La
leva binding resta il **bonifico** (N1/N3), non la quota.
- **ASIMMETRIA** — gia' a 24,2% l'evento emittente vale **3,1× il maxDD dell'intero libro** (7,94%),
che e' il rischio che il progetto ha passato due anni a limare.
- **REVERSIBILE E QUASI GRATIS DA RIMANDARE** — $1,33 + 6 bps per 8× il campione.
## Cosa NON dice questa analisi
- Non stima `p` per Ethena. Nessuna fonte qui lo misura: e' **assunto**, e lo script lo tratta come
parametro, non come risultato.
- Non sceglie la quota. **La scelta e' dell'operatore** (N4).
- Il tasso osservato viene da un metodo **indiretto** (delta di equity al netto dei trade): il
gateway non espone il Transaction Log. Vale col suo limite gia' dichiarato in `usde_watch`.
@@ -0,0 +1,181 @@
# 2026-08-31 — GATE BUIDL-01: test di eligibilità sul collaterale BlackRock
⚠️ **Questo documento è scritto PRIMA di eseguire e PRIMA di conoscere l'esito, e committato
separatamente dall'esecuzione**: l'ordine dei fatti è verificabile in git, non sulla mia parola.
È la stessa disciplina che ha reso valido il test USDE del 26/08 (M13, N11).
## Da dove nasce
L'operatore chiedeva se convenisse aggiungere **stETH** e i custodi Fireblocks/Sygnum/Komainu.
Risposta: no a entrambi — stETH rende **2,2254%** (metà dell'USDE) e non è un dollaro ma **ETH**,
quindi è esposizione direzionale che `book.py` non netta e che nessun gate autorizza; coprirla con
uno short perp è **CC01**, già misurato il 26/06 e parcheggiato come *"LEAD a scala ~$20k+"*.
I custodi sono istituzionali, fuori portata a $2k e coperti dalla decisione *"100% Deribit fino a
$20k"* — e comunque **nessuna via custodiale restituisce i reward USDC**, perché l'esclusione
italiana è per giurisdizione.
Ma la ricerca ha trovato la voce che conta. Le valute a rendimento su Deribit sono quattro:
| valuta | APR pubblicata | emittente / natura | note |
|---|---|---|---|
| USDE | 4,2143% | Ethena, sintetico delta-hedged | **al tetto** (~31,2% dell'equity) |
| USDC | 3,4000% | Circle / custodia Coinbase | **Italia esclusa → 0 sul nostro conto** |
| **BUIDL** | **3,2018%** | **BlackRock, Treasury USA tokenizzati** | `BUIDL_USDC` liquido |
| STETH | 2,2254% | Lido | è ETH, non un dollaro |
**Il punto non è il tasso, è l'emittente.** Oggi il conto ha **$1.412,71 di USDC che rendono
esattamente zero** e un rischio emittente concentrato al 100% su Ethena. BUIDL è un R1
**completamente diverso** (Treasury USA): non aggiunge un grammo di esposizione Ethena e va
esattamente nella direzione che N4 indica — *la quota è l'unica leva contro il rischio emittente*.
## Cosa si compra e cosa resta
- USDC $1.412,71 · USDE 643,175691 ($643,18) · **totale $2.055,88**
- cuscino di regolamento richiesto (`n_asset × frac × disaster_sl_pct` = 30% dell'equity): **$616,76**
- **USDC libero sopra il cuscino: $795,94**
**Taglia del test: 500 BUIDL (~$500)** — la stessa del test USDE, per confrontabilità. Lascia USDC
a ~$912, cioè **$295 di slack sopra il cuscino**: il vincolo di regolamento resta rispettato con
margine. Costo: spread 4 bps ≈ **$0,20**, reversibile.
## Meccanica dichiarata (dall'articolo ufficiale, non dedotta)
> «BUIDL rewards are paid based on the **minimum equity** of BUIDL held in an account over the
> preceding day. The minimum equity held over the preceding day must be **at least 1 BUIDL**.
> The minimum equity calculation period is the **24 hours up to 20:00 UTC** each day. […] BUIDL
> rewards are **only paid on weekdays** […] between **20:00 UTC and 23:00 UTC**. […] Deribit
> charges an administrative fee of **5%**.»
Comprando **lunedì 31/08 mattina**, la finestra 20:00 dom → 20:00 lun ha minimo **0** (prima
dell'acquisto non tenevamo BUIDL): **nessun reward atteso lunedì sera**. La prima finestra piena è
20:00 lun → 20:00 mar, pagata **martedì 01/09 fra le 20:00 e le 23:00 UTC**.
Reward atteso su 500 BUIDL: `500 × 3,2018%/365` = **0,04386/giorno** lordo, ~**0,0417 netto** del
fee 5%. BUIDL ha **6 decimali**: il segnale è ~40× la soglia di rumore che usiamo per l'USDE (1e-3).
⚠️ *La APR pubblicata è un listino, non un incasso* — lezione pagata il 30/08 sull'USDC. Il numero
da citare sarà quello **misurato**, con il suo `n`.
## 🔒 CRITERI PRE-REGISTRATI — dichiarati adesso, prima dell'esito
**GATE BUIDL-01, si legge il 2026-09-04.**
1. **ELIGIBILITÀ** *(la domanda primaria: la giurisdizione italiana esclude l'USDC, e per BUIDL
l'articolo non pubblica alcuna lista — quindi è ignoto, non noto)*
**≥1 reward BUIDL rilevato entro le 23:00 UTC del 2026-09-03 → IDONEO**; si apre la decisione
di quota, che è dell'**operatore**. **Zero reward → NON IDONEO**: si riconverte in USDC e la
pista si chiude, come si sarebbe chiusa quella USDE.
Finestra: tre giorni feriali pieni (mar 01, mer 02, gio 03), robusta a una finestra mancata.
Rilevazione **per delta** sull'equity BUIDL al netto dei trade — il gateway non espone il
Transaction Log (debito #11), stesso metodo dichiarato dell'USDE, coi suoi limiti.
Soglia di rilevazione **1e-3 BUIDL** (il reward atteso è 40×).
2. **TETTO DEL VENUE** — si sonda subito, col metodo **a saldo neutro** già validato sull'USDE
(BUY rifiutato → SELL → BUY riaccettato → BUY rifiutato). Si registra il bracket **con l'equity
del momento**, perché sull'USDE il tetto si è rivelato una **frazione** e non un livello: senza
l'equity accanto il numero non è interpretabile (P7).
3. **HAIRCUT**: **dichiarato NON misurabile a questa taglia** e non vincolante — sull'USDE è stato
misurato che non morde fino al 90% di quota, e qui il margine impegnato è ~$11 su $2.056.
Non lo si stima al buio: si scrive che non lo sappiamo (D5).
**Cosa NON decide questo test**: la quota. Decide solo se la pista esiste. La quota è
dell'operatore, e sull'USDE si è vista arrivare comunque dal venue.
## Sorveglianza
`balance_watch.py` (orario, minuto :42) viene esteso a **BUIDL**: registra il balance a 6-8
decimali accanto a USDC e USDE, col conteggio dei fill dall'ultimo campione. È già dentro il
perimetro di backup. La finestra dei reward BUIDL (20:00-23:00 UTC) è coperta da 4 campioni orari.
*Esito: da scrivere dopo il 2026-09-03.*
---
## ESITO (stesso giorno, 07:35-07:45Z) — **GATE BUIDL-01 CHIUSO: NON ENTRABILE**
Il gate si chiude in dieci minuti, e con un esito che **non era fra i due previsti**. I criteri
pre-registrati contemplavano *"≥1 reward → IDONEO"* e *"zero reward → NON IDONEO"*. La realtà è una
terza cosa: **non si riesce a comprare BUIDL affatto**, quindi la domanda sull'eligibilità ai reward
è priva di oggetto. *Un gate può fallire sulla sua precondizione invece che sul suo criterio, e va
scritto così invece di essere forzato in una delle caselle previste.*
### La misura
Undici ordini rifiutati, tutti con `not_enough_funds_in_currency`:
```
BUY 500 · 250 · 125 · 62 · 31 · 15 · 7 · 3 @1.0009 -> rifiutati (scala per dimezzamento)
BUY 1 @1.0006 (ask 1.0004 x 10.730) -> rifiutato
BUY 1 @1.0024 (ask 1.0004 x 10.730) -> rifiutato
BUY 1 @1.0100 (ask 1.0004 x 10.730) -> rifiutato
```
Con **BUIDL 0,000000 in mano** e **USDC $1.412,32 disponibili**, per comprare **$1**. Esclusi uno
per uno tutti i sospetti che sull'USDE avevano portato fuori strada:
| sospetto | escluso perché |
|---|---|
| prezzo non marcabile | limite **1,0100** contro ask **1,0004** — incrocia di 96 bps |
| liquidità | **10.730** unità al miglior ask, 23.416 tre livelli sotto |
| taglia | rifiutata **1 unità**, il minimo dello strumento |
| fondi | **$1.412** disponibili per un ordine da **$1** |
| tetto sul livello | **teniamo zero**: non c'è nulla da superare |
| wallet inesistente | `account_summary("BUIDL")` risponde, equity 0.0 |
**Il conto non è abilitato ad acquisire BUIDL.** Costo del test: **$0** (nessun fill).
### 🚨 E la frase pubblicata è FALSA per il nostro conto
> «**All Deribit users are permitted to buy and sell BUIDL tokens in the spot markets on Deribit,
> with no extra requirements.**» — `insights.deribit.com/education/buidl-launches-on-deribit/`
Non per noi. **Il fatto misurato è il rifiuto, non il suo motivo** (stessa disciplina dello zero
USDC). Le spiegazioni candidate sono due, e da fuori **non sono distinguibili**:
- **(a) giurisdizione** — BUIDL è un **titolo** (fondo BlackRock emesso via Securitize), la cui
distribuzione è ristretta a monte dell'exchange. L'articolo di marketing non lo dice.
- **(b) limiti di spot per TIER DI CONTO** — la documentazione BUIDL, sezione *Limits and
Requirements*: «*Default non-margin spot order limits apply across assets. Check your account
tiers […] for specific maximum open order size configurations.*» Un tier che per questo asset
vale **zero** produrrebbe esattamente ciò che vediamo.
⚠️ Ma i nostri dati **restringono**: lo **spot USDE funziona** — ne abbiamo comprati 644 lo stesso
giorno, sullo stesso conto, con lo stesso codice. Quindi **non è un blocco generale sullo spot né
un limite di tier trasversale: è SPECIFICO DELL'ASSET.** Il che è compatibile con entrambe le
ipotesi (una giurisdizione per-asset, o un tier per-asset a zero) e non ne elegge nessuna.
*Restringere le ipotesi con l'evidenza che si ha vale più che sceglierne una con l'evidenza che
non si ha.*
Irrilevante per noi ma registrato: «*Deribit only supports the ERC-20 version of BUIDL on the
Ethereum blockchain*» — riguarda i trasferimenti on-chain, e la nostra uscita sarebbe comunque
una vendita sullo spot, non un prelievo.
📌 **Due affermazioni pubblicate di Deribit smentite dal conto in due giorni**: la APR USDC al
3,40% (30/08) e ora «all users can buy BUIDL». Non è sfortuna, è una **regola**:
> **Una capacità pubblicata dal venue non è una capacità del conto. Si verifica sul CONTO, prima
> che entri in un piano — e costa un ordine da $1.**
È N10 portata un passo più in là: la verifica a €0 va fatta prima, si fa **sul venue e non sul
sito** — e adesso si sa che nemmeno il venue basta: serve il **conto**.
### Il sottoprodotto che vale più del test
`not_enough_funds_in_currency` è il **messaggio generico di Deribit per «non puoi acquisire altra
di questa valuta»**, qualunque sia il motivo — permesso, giurisdizione, o tetto. Lo si è visto ora
su BUIDL (permesso assente, zero in mano) e il 30-31/08 su USDE (tetto a ~31,2% dell'equity). Il
messaggio parla di **fondi** e i fondi non c'entrano mai. Chi lo incontra in futuro deve saltare
direttamente alla domanda giusta — *questa valuta la posso acquisire, e fino a quanto?* — invece di
inseguire taglia, cadenza e prezzo come è successo il 30/08.
### Cosa resta
- **Pista BUIDL chiusa.** Non riapribile da noi: dipende da un permesso del venue, non da una
nostra scelta. La riapre solo un cambio lato Deribit.
- **`balance_watch` tiene BUIDL nella lista** (costo: una lettura oraria). Se l'accesso si aprisse,
un balance diverso da zero lo direbbe da solo — la stessa logica per cui l'USDC resta sorvegliato
malgrado lo zero misurato.
- **L'USDE resta l'unico collaterale a rendimento che il conto può usare**, al suo tetto di ~31,2%,
e i **$1.412 di USDC continuano a rendere zero**. Non per mancanza di alternative: perché le tre
alternative sono chiuse una per giurisdizione (USDC), una per permesso (BUIDL) e una perché non è
un dollaro (stETH).
@@ -0,0 +1,426 @@
# 2026-08-31 — Fine della Proof of Reserves, e il guardiano che mancava
L'operatore ha passato l'annuncio *Deribit to Discontinue Daily Proof of Reserves Publication*.
La notizia in sé vale poco per noi. Quello che è emerso cercandola vale molto di più.
## 1. La notizia: frequenza giù, sostanza su, per noi nulla cambia
Dal **1 settembre 2026** la pagina Proof of Reserves sparisce e gli aggiornamenti quotidiani
cessano. Al loro posto, sotto il regime **VARA** di Dubai: audit **annuale** indipendente delle
riserve, audit annuale del bilancio, obbligo di **copertura 100%** e **segregazione** degli attivi
dei clienti. E: «*approximately 90% of client assets have been migrated to Coinbase, which acts as
a custodian for Deribit*».
**Non è un peggioramento netto, ed è importante non raccontarlo come tale.** Si perde un segnale
**ad alta frequenza e debole** (una PoR auto-pubblicata prova che gli attivi esistono in un istante,
non che coprano le passività, ed è aggirabile attorno allo snapshot) e si guadagna un segnale **a
bassa frequenza e più forte** (audit indipendente obbligatorio invece di pubblicazione volontaria),
più un miglioramento **sostanziale** di custodia: 90% degli attivi presso un custode terzo regolato
invece che nei wallet dell'exchange.
**Per noi non cambia nulla di operativo**, e per una ragione che va detta: *la PoR non l'abbiamo
mai sorvegliata*. Zero occorrenze in tutto il codice. `venue_probe` legge se il venue **risponde**,
non cosa **dichiara**. Si perde un segnale che non stavamo usando.
Ho provato a catturare l'ultimo dato prima che la pagina sparisse — è la mossa giusta per un dato
non ricostruibile con una scadenza. **Rinunciato di proposito**: la pagina è una SPA e
`get_proof_of_reserves` non esiste come metodo API («Method not found»), ma soprattutto uno
snapshot singolo e auto-riportato non regge lo standard di prova di questo progetto e **non
permetterebbe comunque di stimare `p`**. Un dato che non cambierebbe una decisione non vale il
lavoro per prenderlo.
## 2. 📌 La conseguenza vera: il riapritore della decisione più grande si è ristretto
La decisione vincolante **«100% Deribit fino a $20k»** (26/07) ha come riapritori dichiarati:
*«**$20k**, o un cambio di piano, o `p` che diventa **stimabile** invece che assunto»*.
Dal 1 settembre il segnale pubblico di solvibilità passa da **quotidiano ad annuale**. Il terzo
riapritore non è formalmente chiuso — un audit indipendente è semmai evidenza più forte — ma
diventa **praticamente irraggiungibile**: da una serie giornaliera si può costruire una stima, da
un punto all'anno no. ⇒ **In pratica quella decisione è ora gated SOLO dal capitale**: si riapre a
$20k, o non si riapre. Non cambia la decisione di oggi (fu presa con `p` esplicitamente **assunto**,
non osservato), ma chiude l'unica uscita non-capitale che aveva.
## 3. 🚨 Quello che ho trovato cercando: quattro annunci materiali che nessuno leggeva
Il debito #8 dice, testuale: *«Resta scoperto il Rulebook vero e proprio: la sonda legge se il
venue RISPONDE, non cosa il venue ANNUNCIA»*. Esiste un feed RSS pubblico
(`insights.deribit.com/exchange-updates/feed/`) con 10 voci. Alla prima lettura:
| data | annuncio | perché ci tocca |
|---|---|---|
| 27/08 | Discontinue Daily Proof of Reserves | §1 e §2 di questo diario |
| **14/08** | **Contract Specifications change for Linear USDC Perpetuals** | **i nostri due strumenti** |
| 05/08 | New SM Margin Model On Deribit | leva per taglia, tiered |
| **31/07** | **USDC Rewards Available In More Countries** | tocca una conclusione viva |
| 29/06 | New Fee Schedule On Deribit | `fee_watch` esiste per questo |
**Il caso che decide la questione è il 14/08.** Le specifiche dei perpetual USDC sono cambiate il
**18/08**; l'annuncio è del **14/08**. Il codice lo racconta già con onestà: la tabella «*non se
n'era accorta […] è andata bene per la direzione del cambiamento, non perché ce ne fossimo
accorti*» — i cambi erano **riduzioni** (tick BTC 0,5→0,1, ETH 0,05→0,01, min/step ETH
0,001→0,0001) e un valore più grosso resta conforme. `check_specs()` è **nato da quella svista** e
oggi gira ogni ora. Ma rileva la deriva **dopo**; il feed l'avrebbe detta **quattro giorni prima**.
*Il giorno che Deribit ALZA un minimo, «dopo» significa ordini rifiutati.*
Il 31/07 chiude un cerchio aperto ieri: l'espansione delle giurisdizioni idonee ai reward USDC
**non contiene l'Italia**, né alcun paese UE — mentre **San Marino e Città del Vaticano** (i due
microstati europei fuori dall'UE) **sono idonei**. La lista che avevo letto (aggiornata 20/08) è
posteriore all'espansione: **la conclusione dello zero USDC è confermata due volte**. Il pattern
UE-fuori/microstati-dentro suggerisce un driver regolatorio, ma la lista contiene anche Canada e
Giappone: **non asserisco una causa**, il fatto è l'esclusione.
## 4. Costruito: `venue_news.py`
Sorveglianza giornaliera del feed, dentro `cron_daily.sh` accanto a `fee_watch` (suo parente
stretto). Scelte che contano:
- **NON interpreta.** Dice *«è uscito questo, guardalo»*. Nessun automatismo su un testo di
marketing: P13 — una guardia sui NUMERI non copre il RAGIONAMENTO, e la prosa si legge come
opinione di un lettore fallibile. L'unica cosa che classifica è l'**urgenza**.
- **Le parole-chiave sono DERIVATE, non ridichiarate** (P1, il difetto più ricorrente del
progetto): gli strumenti vengono da `deribit._CONTRACT`, la valuta di collaterale da
`config/live.json`. *Chi aggiunge un asset al book allarga la sorveglianza senza toccare questo
file* — ed è esattamente il test che lo blinda.
- **Primo giro semina senza allertare** (P9: l'allarme massimo non si spende per un arretrato di
10 voci, o non verrà letto il giorno che è vero).
- **Feed illeggibile → codice 2 e nessun silenzio implicito** (P5: «non vedo» non è «niente di
nuovo»).
5 test sulle funzioni pure. La rete no: `scarica()` ritorna `None` su qualunque errore, ed è quello
il contratto.
**Cosa NON copre**, e va detto: il **Rulebook** vero e proprio (ADL, perdita socializzata,
*emergency powers*, conti dormienti) non ha un feed. Il debito #8 si restringe, non si chiude.
---
## 5. La Knowledge Base risponde a due domande aperte — e corregge due cose mie
L'operatore ha passato anche *yield-generating-collateral-usde-buidl-and-more*, che **non** documenta
tetti né haircut ma rimanda alla Knowledge Base. Interrogata via API Zendesk
(`support.deribit.com/api/v2/help_center/articles/search.json`), l'articolo giusto è
**«Cross collateral specifications»** (aggiornato 2026-02-20).
### (a) 🚨 Il costo del saldo negativo: **0,05% al GIORNO**
> «*While the equity of a currency in an account remains negative, a **collateral fee** will be
> charged to that account. This fee is charged daily in the same currency as the negative balance
> (**default = 0.05% per day**). The fee is charged based on the amount of time the negative equity
> is held, down to a granularity of seconds.*»
**18,25% annuo**, cioè **4,3× la resa USDE** che si starebbe comprando tenendo meno USDC. Il 30/08
avevo scritto «Deribit lo finanzia a interesse» **senza il numero** — l'affermazione era giusta e
vuota. Con il numero il criterio del **cuscino di regolamento** smette di essere prudenza e diventa
aritmetica: scendere sotto il cuscino per tenere più USDE è **14 punti**.
E il ribilanciamento automatico **non salva**: scatta solo oltre **$1.000.000** assoluti o il
**100% della cross equity** (default tabulati), soglie che a $2k non si toccano mai. Non veniamo
ribilanciati: **sanguiniamo la fee**. *La cosa che sembrava un paracadute è, alla nostra taglia,
esattamente l'assenza di un paracadute.*
### (b) 🚨 Haircut USDe: la fonte dice **5%**, noi abbiamo registrato **10%**
| valuta | haircut X:PM | X:SM |
|---|---|---|
| BTC · ETH · **USDC** | — | — |
| USDT · USYC · **BUIDL** | 2% | 2% |
| PAXG | 2,5% | 5% |
| **USDe** | **5%** | **5%** |
| stETH | 7,5% | 7,5% |
| SOL | — | 15% |
`config/live.json` dice `haircut: 0.10`, e il diario del 26/08 lo dà per «verificato sul venue»
senza lasciare traccia di **come**. L'articolo è **anteriore** a quella data, quindi non si sa
quale delle due sia stale.
**Non l'ho riparato** (P12: fra due fonti che non concordano, una riparazione silenziosa è
un'invenzione; M28: la contraddizione è informazione). Si tiene **0,10** per tre ragioni dichiarate:
è il lato **conservativo** (sottostima il margine utilizzabile), **non è sul percorso soldi** (non
entra nel sizing — lo usa solo il report di `usde_watch`), ed è già misurato che **non morde a
nessuna quota fino al 90%**. Si chiude leggendo la pagina margini del conto, che è dove Deribit
stesso dice di guardare.
### (c) Un dato di lato che vale per il futuro
**BUIDL ha haircut 2%** — sarebbe stato il *miglior* collaterale a rendimento del listino (contro
il 5% dell'USDe), se solo lo si potesse comprare. E **stETH 7,5%**, il peggiore: terza ragione
indipendente per lasciarlo stare, dopo il tasso dimezzato e l'esposizione ETH.
📌 **Nessuna delle due fonti documenta un tetto sulle quantità detenibili.** Il ~31,2% sull'USDE
resta **misurato e non spiegato** — e ora si sa che non è una svista di lettura: non è scritto da
nessuna parte.
### (d) Tentata la chiusura della divergenza sull'haircut: **non è raggiungibile da qui**
Deribit dice: «*Haircut rates can be seen on the margin page in your account*». Quella pagina è web
UI, e le credenziali vivono solo dentro il gateway (debito #11). Provato comunque:
- `account_summary` per la valuta **USD** (la "riga USD" che il doc menziona) → `Invalid currency`;
- parametro **`extended`** (che su Deribit apre i dettagli di margine) → **ignorato**;
- il gateway filtra a **8 campi**: niente `initial_margin`, niente `maintenance_margin`.
E non è che si sia guardato nel secchio sbagliato — **la contabilità torna esatta senza haircut**:
```
USDC equity 1412.281074 available 1400.728093 riservato 11.5530
USDE equity 643.175691 available 643.175691 riservato 0.0000
posizioni lorde $577.65
riservato in USDC ........... $11.5530
IM di posizione al 2% ....... $11.5530
RESIDUO ..................... $-0.0000
```
Un haircut al **5%** chiederebbe **$32,16** accantonati, al **10%** ne chiederebbe **$64,32**: non
ci sono, e il secchio USDE riserva `0.000000`. ⇒ **L'haircut non è osservabile in alcun campo
esposto.** O è applicato solo nella vista cross/USD che il gateway filtra via, o non è applicato al
nostro conto: da qui **le due cose non si distinguono**, e non le si sceglie tirando a indovinare.
**La divergenza resta APERTA**, ma ora con la ragione misurata invece che supposta. La chiudono due
cose, entrambe dell'operatore: **30 secondi sulla pagina margini** della web UI, oppure delle
**chiavi API Deribit** — che è la decisione già dichiarata nel debito #11, non un refactor.
Nel frattempo **non morde nulla di osservabile**: l'IM è il 2% del nozionale e il valore pieno
dell'USDE resta disponibile, quindi 5% o 10% non cambia una singola cifra operativa.
📌 **Sottoprodotto non cercato**: $11,5530 / $577,65 = **2,0000% = esattamente 1/50**. È il
`C1 = 50` (start leverage, tier 1) del **nuovo modello di margine SM** annunciato il 05/08 — quello
che `venue_news` ha appena tirato fuori dal feed. **Confermato sul nostro conto senza averlo
cercato**, ed è la prima volta che un annuncio del venue viene verificato contro il conto invece
che creduto.
### (e) L'operatore dichiara: il conto è **Standard Margin** — due conferme incrociate
Lo screenshot della pagina margini non è arrivato fin qui (sta sul desktop dell'operatore, non
sulla VPS: il percorso non esiste su questa macchina). Ma il fatto dichiarato — **«è attivo
Standard margin»** — aggancia due cose che erano sospese:
1. **Quale colonna leggere.** La tabella KB ha `Haircut (X:PM)` e `Haircut (X:SM)`. Il conto è
**X:SM**, quindi vale la seconda — che per USDe dice **5%**, come la PM. La divergenza col
nostro `0.10` resta quindi intatta, ma ora si sa con certezza *quale* numero ufficiale la
contraddice, invece di doverne scegliere uno fra due colonne.
2. **Perché l'IM misurata era esattamente 1/50.** Il nuovo modello di margine del 05/08 si applica,
testuale, ai *«standard margin accounts»*. Il conto è standard margin, e noi abbiamo misurato
IM = **2,0000%** del nozionale = **1/50** = il `C1 = 50` di tier 1. **Le due cose si confermano
a vicenda**: un fatto dichiarato dall'operatore e una misura fatta sul conto senza conoscerlo.
*Vale la pena notarlo perché è raro: in tutta questa settimana ogni affermazione pubblicata dal
venue si è rivelata falsa per il nostro conto (APR USDC, «all users can buy BUIDL»). Questa è la
prima che il conto conferma.*
**Cosa manca ancora**, e resta l'unica cosa che chiude la divergenza: il numero di haircut per USDe
**mostrato sulla pagina Standard Margin del conto**. Se dice 5%, la nostra config è stale e il 26/08
registrò male; se dice 10%, la KB è stale e la config ha ragione. Finché non c'è, si tiene 0.10 —
lato conservativo, fuori dal percorso soldi, e **senza un solo effetto operativo misurabile**.
---
## 6. La pagina margini arriva davvero — e la seconda cosa che dice vale più della prima
Lo screenshot era su **Wasabi** (`rclone` remote `wasabi:`, bucket `adp-work`, cartella `_scambio`),
non sulla VPS: per questo il percorso non esisteva. Scaricato e letto.
Riconciliazione riproducibile in **`scripts/research/r0831_margini_conto.py`** (N11).
### (a) ✅ Haircut = **5%**. La nostra config aveva torto.
La pagina **non espone l'haircut come numero**: si ricava per differenza, perché il modello CROSS
conta l'USDE scontato e il SEGREGATO non lo conta affatto.
```
S:SM (attivo) USDC Available 1,400.86 · USDC equity 1,412.28 IM 11.55 = 1,400.73 (scarto $0.13)
X:SM CROSS Available 2,011.73
contributo USDE al cross = 2,011.73 (1,412.28 + 0.20 11.55) = $610.81 su $643.05 di valore
→ haircut implicito 5,0137%
ipotesi 5% (KB) → atteso $610.89 scarto $ 0.09 ✅
ipotesi 10% (nostra) → atteso $578.74 scarto $32.06 ❌
```
`config/live.json` passa da `0.10` a **`0.05`**. Il 26/08 il 10% fu registrato come «verificato sul
venue» **senza lasciare traccia di come**, e non lo era. *Una nota di provenienza che dice «verificato»
senza dire con quale lettura non è provenienza: è la stessa parola usata come garanzia.*
### (b) 🚨 Il conto **non è cross-collateral**. È `Segregated: Standard Margin`.
Lo dice la schermata in cima — *Current status: **Segregated: Standard Margin*** — e la tabella del
modello attivo elenca **BTC, ETH, USDC. L'USDE non c'è.**
**L'USDE non fa margine per i perp USDC-settled del book**, e l'haircut **oggi non si applica
affatto**. Il che spiega, a posteriori e in modo pulito, perché la misura di stamattina trovava
**$0,0000** accantonati: non stavo guardando nel secchio sbagliato, *stavo cercando un parametro
che sul nostro conto non è in vigore*.
**NON MORDE**, e va detto subito per non allarmare: al massimo lordo del libro (1,0x ≈ $2.056 di
nozionale) l'IM sarebbe ~$41 contro **$1.400 di USDC disponibile** — 34× di copertura. Nessuna
decisione operativa cambia oggi.
**Ma la premessa scritta in CLAUDE.md era falsa**, e il modo in cui era falsa è istruttivo. Diceva:
*«l'equity del book è il TOTALE cross-collateral»*. Sono due cose diverse messe sotto un nome solo:
- come **ricchezza**, sommare USDC+USDE è **giusto** — ed era il punto della riparazione del 26/08,
che evitò il falso «USCITA DI FONDI 24%» e una vendita indesiderata;
- come **capacità di margine**, è **sbagliato**: nel modello attivo l'USDE vale zero.
Finché il margine non morde le due coincidono nell'uso, e infatti non è mai emerso. *Un errore che
non ha conseguenze finché una terza cosa resta vera è un debito, non un'assoluzione.* Corretto in
CLAUDE.md, e corretta anche la riga di `usde_watch` che stampava «margine utilizzabile ~$2.013
(haircut 10%)»: era falsa due volte insieme.
### (c) Come cambia il modo di pensare alla quota USDE
Non è «collaterale diversificato»: è **cassa messa da parte a rendimento, fuori dal sistema di
margine**. Il che rende il tetto del venue al ~31,2% meno una limitazione e più un caso fortunato —
tiene automaticamente dentro al 31% la parte di conto che non lavora come margine.
E apre una **decisione dell'operatore**, non mia: passare a **X:SM** aggiungerebbe **$610,87** di
margine utilizzabile, ma porta con sé la meccanica cross — *collateral fee 0,05%/giorno* sul saldo
negativo e ribilanciamento automatico. Oggi non serve (34× di copertura); servirebbe solo a leve
che non sono autorizzate.
📌 **Ipotesi nuova sul tetto del ~31,2%**, non verificata: potrebbe dipendere proprio dal modello
segregato. Si saprebbe passando a X:SM e ri-sondando — un esperimento che ora ha un senso, dove
prima non si sapeva nemmeno cosa variare.
---
## 7. Passaggio a X:SM e ri-sondaggio del tetto — l'ipotesi regge tecnicamente e cade in pratica
L'operatore è passato a **Cross: Standard Margin** e ha chiesto di ri-sondare, per verificare
l'ipotesi lasciata aperta in §6(c): *il tetto del ~31,2% dipende dal modello segregato?*
### Il passaggio è verificabile dal gateway, senza fidarsi di una dichiarazione
`available_funds` USDC è passato da **~$1.400** a **$2.011,09**, contro **$2.010,90** attesi
sommando l'USDE scontato al 5% — **scarto $0,19**. È anche la **seconda conferma indipendente
dell'haircut al 5%**, da una lettura completamente diversa dallo screenshot: due strade, stesso
numero.
### Il risultato
| modello | tetto misurato | equity | frazione |
|---|---|---|---|
| Segregato **S:SM** (31/08) | [643,18 644,18) | $2.055,56 | 31,29% 31,34% |
| Cross **X:SM** (31/08, dopo) | [654,18 655,18) | $2.054,90 | **31,84% 31,88%** |
**+0,55pp = +11 USDE ≈ $11.** ⇒ **L'ipotesi è vera e inutile**: il modello di margine *entra* nel
tetto, ma non lo spiega. Per arrivare al 70% mancano ancora ~38 punti, un fattore **2,2×**.
*Un'ipotesi confermata al terzo decimale e falsa all'ordine di grandezza va archiviata come falsa:
il tetto resta misurato e non spiegato.*
### 🚨 La lezione di metodo, che vale più del risultato
Il **primo probe fu un BUY 20, rifiutato**. Da solo avrebbe chiuso la questione con *"tetto
invariato, l'ipotesi è refutata"* — e sarebbe stato **falso**. Il tetto si era mosso di 11, cioè
meno della taglia del probe. È stato il test a **saldo neutro con passi piccoli** (BUY 1 → SELL 10
→ BUY 10 → BUY 1, tutti accettati) a rivelarlo.
📌 **Un probe unico di taglia sbagliata produce un falso negativo che si legge come risultato.**
La taglia del sondaggio va scelta **sulla risoluzione dell'effetto che si cerca**, non su ciò che
è comodo — ed è M13 in una veste nuova: *un criterio si misura sulla sua risoluzione prima che sul
suo esito*.
### Cosa ha comprato il passaggio, e cosa è costato
- **Comprato**: **$611** di margine utilizzabile — inutile a 0,28x di leva, serve solo sopra ~1,4x
che non è autorizzata — più **$11** di capienza sul tetto.
- **Costato**: sotto segregato l'USDE era **ring-fenced** dalle perdite del libro (solo il silo
USDC rispondeva delle posizioni); sotto cross **risponde l'intero conto**. Non morde oggi (perdita
massima plausibile ~$617 col disaster-SL, dentro il solo USDC), ma **il rischio strutturale ha
cambiato verso**. Se un giorno il cross non servisse più, tornare a S:SM ri-recinta l'USDE.
`config`: `venue_cap_frac` 0.312 → **0.318** (bordo basso del bracket CROSS, che è il modello
attivo). Se si torna a S:SM va rimesso a 0.312.
## 8. **X:PM valutato e SCARTATO** — la risposta era già nello screenshot
L'operatore ha chiesto di provare **Cross: Portfolio Margin**. Non serve provarlo: il confronto è
nella schermata di §6.
| modello | Available Balance | IM | MM |
|---|---|---|---|
| **X:SM** (attivo) | **$2.011,73** | 2,13% | **0,37%** |
| **X:PM** | **$1.935,14** | 5,85% | **3,43%** |
Sulla colonna il cui significato è inequivocabile — stessa riga, stesse unità — **X:PM dà $76,59
di collaterale utilizzabile in MENO**, e chiede più margine. ⚠️ *La frase «su entrambe le misure»
è stata corretta in §9: l'«IM %» stampata dalla pagina non è il margine delle posizioni.
La conclusione non cambia — migliora.*
**Perché**, ed è strutturale e non contingente: il Portfolio Margin è **basato su scenari di
rischio** e premia i portafogli in cui il rischio si **compensa**, tipicamente i book di opzioni.
Il nostro è **due long direzionali nudi senza nulla che compensi** — il caso peggiore per PM, che
addebita lo scenario di stress invece di un'aliquota piatta.
**La riga che decide è la MM: 3,43% contro 0,37%, ~9×.** Il maintenance margin è ciò che innesca la
liquidazione: non morde a 0,28x, ma sposta il punto di liquidazione molto più vicino su un libro
con soldi veri. E il beneficio atteso sul tetto è noto per analogia: il salto S:SM→X:SM ne ha
comprato **$11**.
**SCARTATO.** ⚠️ *Non "PM è peggio", ma "PM è peggio PER QUESTO portafoglio"*:
**cosa lo riapre** — il giorno che VRP01 o un altro sleeve di opzioni entra in deploy, il rischio
inizia a compensarsi e quel confronto **cambia di segno**. Va rifatto allora, non prima.
---
## 9. X:SM attivo: la pagina si ricostruisce dal gateway — e una mia lettura era sbagliata
L'operatore ha incollato la riga CROSS col modello **attivo** invece che proiettato:
`Available $2.010,81 · IM 2,15% · MM 0,37%`. Il gateway, letto **allo stesso minuto** (08:34Z), dice
`available_funds` = **2010,82299383**. **Scarto $0,01.**
### (a) Lo screenshot non serve più
**La riga CROSS della pagina È `available_funds`**, che leggiamo da soli a ogni giro. Ieri quella
schermata era l'**unica** fonte per l'haircut e per il modello attivo, e per averla è servito un
passaggio dall'operatore e da Wasabi. Oggi non serve a niente: `r0831_margini_conto.live()` la
ricostruisce.
### (b) Terza conferma del 5%, e la prima ESATTA
1.400,71 + 0,95 × 654,176 + 0,20 dust 11,553 IM = $2.010,82 pagina $2.010,81
**Scarto $0,007.** E il 10% della vecchia config non è *improbabile*: invertendo la stessa riga dà
**IM = $21,16**, cioè **aritmeticamente impossibile**. Le tre strade — differenza fra le righe
della pagina (31/08 07:50), salto di `available_funds` al passaggio S:SM→X:SM ($0,19), e ora questa
ricostruzione al centesimo — sono indipendenti e danno lo stesso numero.
### (c) 🚨 L'«IM %» della pagina NON è il margine delle posizioni — mia lettura sbagliata
`(margin_balance available)/margin_balance = 2,1532%`, ed è **esattamente** ciò che la pagina
stampa come «IM 2,15%». Ma si scompone così:
| voce | USD | % di margin_balance |
|---|---|---|
| haircut sull'USDE (5% × 654,18) | $32,71 | **1,592%** |
| IM vera delle due posizioni | $11,55 | 0,562% |
| **totale riservato** | **$44,26** | **2,153%** |
**il 74% di quella percentuale è haircut, non rischio.** È salita da 2,13% a 2,15% **perché
abbiamo comprato collaterale a rendimento** — cioè per un motivo che con l'esposizione del libro
non c'entra nulla. *Letta come misura di rischio direbbe che ieri sera abbiamo alzato la leva
comprando USDE: l'opposto di quello che è successo.*
La **MM invece è pulita**: 0,37% = $7,60, e non può contenere l'haircut ($32,71 da solo la
renderebbe negativa). **Le due colonne hanno basi diverse**, e la pagina non lo dice.
📌 Regola: *una percentuale letta da una schermata va **invertita nella sua definizione** prima di
essere confrontata.* Due colonne affiancate, con la stessa unità e lo stesso aspetto, possono avere
denominatori — e qui numeratori — diversi. È P7 su una superficie nuova: un numero si etichetta
con la sua configurazione, e una pagina del venue non è obbligata a farlo per noi.
### (d) La decisione su X:PM esce RAFFORZATA
La KB dà **USDe al 5% sotto entrambi i modelli** (§6b): nel confronto l'haircut **si cancella**, e
l'inversa restituisce il margine di posizione puro sulle stesse due posizioni, stesso istante:
| modello | available | **IM di posizione** |
|---|---|---|
| X:SM | $2.011,73 | **$11,64** |
| X:PM | $1.935,14 | **$88,23** |
**PM chiede 7,6× il margine iniziale e 9,3× la MM.** Due misure ora pulite, stesso verso: la
bocciatura di §8 **regge, meglio fondata di quando l'ho scritta**.
E c'è una conferma che ieri mancava: la riga X:SM era una **proiezione** di un modello **inattivo**,
e oggi che è attivo la stessa aritmetica la riproduce al centesimo. ⇒ **anche la riga X:PM, che
resta una proiezione, è affidabile**: la decisione presa senza provare il modello era presa su un
numero buono. *Non provare X:PM è stato corretto, e ora si sa perché e non solo che.*
+238
View File
@@ -0,0 +1,238 @@
# 2026-09-01 — COLLAR01: il collar riduce il DD, il tetto lo paga troppo, e ciò che vince è VRP01 travestito
> ⚠️ **Titolo corretto lo stesso giorno.** Diceva *«il pavimento funziona»*: la riduzione di DD è del
> **collar**, non del pavimento — la put da sola lo peggiora in 16/16 celle (§72, sezione A1 sotto).
*Scritto il 2026-09-01. Ogni numero è riprodotto da `scripts/research/r0901_btc_collar.py`.
**Libro, pesi, cron, config INVARIATI. Nessun ordine.***
## 0. La domanda, come è arrivata
> *«crea una strategia di hold BTC in long o short con copertura con options (max 15gg)»*
> *«opzione deve essere al max di 15gg»*
> *«voglio ridurre la vincita, ma bloccare la perdita»*
> *«ovviamente l'entrata deve essere gestita da una strategia confortata da indicatori (es. è in forte bull)»*
> *«dalle opzioni dobbiamo uscire prima del termine (tra 50% e 75% del tempo)»*
Le tre precisazioni cambiano l'oggetto, e vanno lette insieme: **non è una put protettiva, è un
COLLAR** (pavimento comprato, tetto venduto per finanziarlo), su un **hold gated da indicatori**,
con **scadenza ≤15 giorni**.
## 1. Perché questo filone si poteva riaprire
§46 (TAIL-HEDGE, 2026-08-23) è **REFUTATO** e la memoria dice di battere il motivo, non di
ripetere la misura. Il motivo di §46, verbatim:
> *il maxDD **SALE in 162/162 celle**, a ogni lente di f, perché il **beta del libro al sottostante
> è +0,076**: non si assicura un libro che nei crash è già quasi piatto.*
**Quel motivo non si applica qui.** §46 assicurava il *libro* (TP01+SKH01, piatto il 28% dei
giorni); qui il sottostante è un **hold di BTC, beta 1,0 per costruzione**. E §46 dichiara di non
aver provato proprio questo: *«Nessuna copertura dinamica (gated su regime) è stata provata: la
domanda era la statica»*.
Riusati e non rimisurati: metriche e `k_for_same_dd` da `r0823_tail_hedge`, il listino fee opzioni
Deribit, la catena via `cblib.load_chain()`.
## 2. L'entrata, presa dal progetto invece che inventata
`trend_portfolio.tsmom_blend` media tre `np.sign()` sugli orizzonti (30, 90, 180), quindi assume
**solo** i valori {1, 1/3, +1/3, +1} (il bucket 2/3 non esiste — errore già corretto in §57).
Questo dà a **"forte bull" una definizione che non aggiunge nemmeno un parametro nuovo**:
- **gate FORTE** = `|blend| == 1`, tutti e tre gli orizzonti concordi → a mercato **52,5%** dei giorni
- **gate LARGO** = `|blend| ≥ 1/3`, confronto dichiarato → a mercato **97,1%** dei giorni
Il segno dà la direzione, quindi **long e short** sono entrambi coperti come chiesto. Si entra il
giorno **dopo** il segnale (eseguibile, §8.1).
## 3. La calibrazione: quello che il modello non poteva assumere
161.964 quote a due lati su 95 giorni (2026-05-07 → 2026-09-01), scadenze 1-15 giorni.
**Lo skew, ed è il fatto strutturale del filone:**
| \|δ\| | IV put / ATM | IV call / ATM | la put costa |
|---|---|---|---|
| 0,10 | 1,249 | 0,959 | **+30%** della call |
| 0,20 | 1,132 | 0,950 | **+19%** |
| 0,30 | 1,072 | 0,960 | **+12%** |
**Un collar delta-simmetrico è un debito netto**: compro l'ala cara e vendo quella a buon mercato.
Non è un dettaglio di prezzo, è la ragione per cui "gratis" non esiste a delta simmetrici.
Spread (mezza forchetta / mid): put 1,9-8,3%, call 2,1-10,0% secondo il delta. Struttura a termine
IV/DVOL30 ≈ 0,92-0,94 a 2-14 giorni.
📌 **A5 confermata, e corregge un muro di §46:** il tick da **5 USDC** che lì era «il secondo muro,
strutturale» vale per la famiglia **USDC**. La catena che raccogliamo è **100% inverse**
(`BTC-31JUL26-45000-P`), col tick in BTC. Il muro di §46 **non si applica a questo filone**.
## 4. Il risultato, in ordine di come uccide
Lente lunga 2021-03-24 → 2026-09-01 (1.988 giorni, 5,44 anni). BTC buy&hold nudo: Sharpe 0,413 ·
maxDD 76,73% · drift +7,82%/a. Griglia dichiarata prima: 3 δput × 3 δcall × 2 tenor × 2 gate = **48
celle**, più 12 varianti zero-cost dichiarate a parte.
### ✅ A1 CONFERMATA — il COLLAR riduce il maxDD (36/48) — ⚠️ ma NON è il pavimento a farlo
**maxDD scende in 36/48 celle.** Con beta 1,0 la put para davvero: è il risultato che §46 non
poteva ottenere.
🚨 **CORREZIONE (stesso giorno, `r0901c_pavimento_leva.py`, §72).** Questa riga diceva *«il
pavimento funziona davvero»* e attribuiva la riduzione al pavimento. **È il COLLAR a ridurre il DD,
non il pavimento**: la put **da sola**, a premio reale, **peggiora il maxDD in 16/16 celle**
(FORTE 51,83% → 55,4-75,8%, LARGO 71,94% → 74,8-81,7%). Nel collar la riduzione viene dal **tetto**
— il suo premio compensa il bleed della put, e cappare l'upside **abbassa il picco** da cui il DD si
misura. Il pavimento pareggerebbe il DD nudo solo se le put costassero il **36-79% del reale**.
Quindi: *il motivo di §46 (beta) non si applica, ma il suo verdetto — il maxDD SALE — si riproduce
a beta 1,0 per un motivo diverso:* **i drawdown di BTC sono grind di 290-818 giorni, e una put a
≤15 giorni copre una finestra.** Dettaglio in `2026-09-01c-pavimento-leva.md`.
### ❌ A3 CONFERMATA — ma il de-levering lo fa meglio in 45/48 celle
| gate | base gated senza opzioni | |
|---|---|---|
| FORTE | Sharpe 0,442 · maxDD **51,83%** · drift **+10,04%**/a | ← il null da battere |
| LARGO | Sharpe 0,578 · maxDD **71,94%** · drift **+17,37%**/a | |
Il null è **lo stesso hold gated, senza opzioni, scalato a iso-maxDD** — generato dallo stesso
motore, non ridichiarato (P1). Il collar lo batte in **3 celle su 48**.
### ❌ A2 CONFERMATA — ogni punto di DD risparmiato costa 2-8 punti di drift
Δdrift/ΔmaxDD mediano: **7,90** col gate FORTE, **1,98** col LARGO. Il tetto costa più di quanto
il pavimento renda, e non di poco.
### 🚨 C9 — non è protezione, è troncatura
Cella migliore, 196 cicli: **il tetto taglia nel 46,2% dei cicli vincenti, il pavimento para nel
6,7% dei cicli perdenti.** Scatta **7 volte più spesso sui vincenti che sui perdenti** — la
definizione letterale di C9. *La regola dell'operatore («ridurre la vincita, bloccare la perdita»)
è implementata fedelmente: il problema è che su BTC quel baratto è pagato male.*
### ❌ M1 — dentro il libro non aggiunge nulla
Collar Sharpe 0,508 contro TP01 **0,852**, corr +0,486. TP01 + 10% di collar: Sharpe **+0,000** e
maxDD **+2,32pp**. A 25%: **0,065** di Sharpe e **+6,57pp** di maxDD. *Peggiora proprio la cosa
che dovrebbe proteggere.*
## 5. 🚨 Il fatto che vale più del verdetto
**Tutte e 3 le celle vincenti stanno sul BORDO** della griglia (δput al minimo, δcall al massimo).
M8: un argmax sul bordo non è una decisione. Ho esteso la famiglia (M4) con 36 trial dichiarati
verso l'angolo — e lì **vince in 35/36 celle**, con il massimo *nell'angolo*:
> gate FORTE, 7 giorni, **δput 0,02 · δcall 0,50** → Sharpe **1,471** · maxDD **18,12%** · drift **+29,99%**/a
Il limite di quell'angolo è *nessun pavimento, tetto ATM*: **una covered call**. Cioè **la pendenza
porta fuori da ciò che l'operatore ha chiesto e dentro lo short-vol.**
E quel Sharpe 1,471 non è una scoperta — **è il mio prezzatore che si paga da solo**:
| | DVOL / RV-forward | DVOL sta sopra |
|---|---|---|
| a 7 giorni | **1,320** | **76,9%** dei giorni |
| a 14 giorni | **1,253** | **74,8%** dei giorni |
Riprezzando le opzioni alla **volatilità effettivamente realizzata** (diagnostica con look-ahead
dichiarato — è il valore equo ex-post, non una strategia):
| cella | a DVOL | a vol realizzata | il VRP valeva |
|---|---|---|---|
| FORTE 7g δ0,02/0,50 | Sh **1,471** · +29,99% | Sh **0,511** · +8,45% | **+21,54 pp (72%)** |
| LARGO 7g δ0,02/0,50 | Sh **1,141** · +32,17% | Sh **0,114** · **1,10%** | **+33,26 pp (tutto)** |
| FORTE 7g δ0,10/0,30 | VINCE | **perde** | +6,75 pp |
| LARGO 14g δ0,10/0,30 | perde | perde | +8,02 pp |
**Il 72-100% dell'edge dell'angolo è il premio di varianza**, non la struttura. E ciò che
sopravvive alla riprezzatura — **Sharpe 0,511** — è, entro il rumore, **il numero che il progetto
ha già**: VRP01 a f=0,73 vale **Sharpe 0,47**. *Non ho trovato una strategia nuova: ho ri-scoperto
VRP01 per una strada più lunga.* E §3 lo blocca comunque: **«niente short-vol da modello in
deploy»** — questo è esattamente short-vol da modello.
## 5-bis. L'uscita anticipata: chiesta, implementata, e costa
L'operatore ha chiesto di **uscire dalle opzioni fra il 50% e il 75% del tempo**. Implementato come
`exit_frac`, su tenor 14 giorni (⇒ uscita a 7 / 9 / 10 giorni). **La mia ipotesi a priori era che
migliorasse**: uscire presto recupera valore temporale sulla put e rinuncia al theta più veloce
sulla call, cioè proprio alla parte che stampava il premio di varianza. **Metà giusta, metà no.**
| cella `δ0,10/0,30`, gate FORTE | uscita | Sharpe | drift | null | esito | VRP residuo |
|---|---|---|---|---|---|---|
| a scadenza | 1,000 | 0,488 | **+9,48%** | +8,07% | VINCE | +3,38pp |
| 75% | 0,750 | 0,457 | +8,59% | +7,72% | VINCE | +4,93pp |
| 62,5% | 0,625 | 0,366 | +6,11% | +7,98% | **perde** | +2,51pp |
| 50% | 0,500 | 0,278 | **+3,84%** | +8,04% | **perde** | **+1,27pp** |
**Il meccanismo previsto c'è** — il VRP residuo scende da +3,38 a +1,27pp, quindi uscire presto
*davvero* rinuncia allo short-vol — **ma lo spread lo travolge**: 5,6 punti di drift sul gate
FORTE, 10,2 sul LARGO (12,06% → 1,88%), e su `δ0,20/0,20` da 4,37% a 8,58%.
🚨 **E qui §46 va corretto nella sua applicazione.** §46 misurò: *«f si paga solo sulla parte di
valore che converge a intrinseco, quindi un roll anticipato non lo paga»*. **Vero per una copertura
SOLO LONG.** In un collar c'è una **gamba venduta da ricomprare**: uscire prima paga f *esattamente*
sulla parte che a scadenza si sarebbe regolata gratis, e si fa ~2× più spesso per unità di tempo.
**L'asimmetria di §46 si inverte quando la struttura ha una gamba corta.** È il risultato
trasferibile di questa richiesta.
**Nella banda chiesta (50-75%) il verdetto si spacca:** a **0,75** la cella onesta passa ancora il
null (di poco); a **0,625 e sotto** non lo passa più. Nessun `exit_frac` ribalta il verdetto del
filone — lo peggiora.
## 6. I controlli dell'apparato (M15) — 3/3
§46 insegna che *«un controllo positivo rotto dichiara guasto l'apparato»*. Prima di credere a un
verdetto negativo ho verificato che l'apparato sappia riconoscere un successo:
| controllo | atteso | misurato | |
|---|---|---|---|
| pavimento a **premio zero** (pranzo gratis) | deve VINCERE | maxDD 51,83%→**36,76%**, drift +10,04%→**+35,69%** | ✅ |
| premio **×10** | deve PERDERE | drift **33,96%**/a | ✅ |
| zero-cost costruibile e finito | non NaN | drift 3,50%/a | ✅ |
## 7. Tre difetti miei, catturati dai controlli e non a occhio
1. **Bisezione dello zero-cost invertita** (`hi = md` dove serve `lo = md`): il premio cresce col
delta, quindi se incasso troppo poco il tetto va *avvicinato*. Dava Sharpe 3,0/3,9 e drift
53%/a — spazzatura che al primo giro avevo quasi pubblicato.
2. **`dcall=NaN`** nella variante zero-cost propagava NaN nella cassa allo smontaggio anticipato.
3. **C9 non consapevole della direzione:** usavo `S1 > S0` come proxy di "ciclo vincente", ma per
un ciclo **short** un prezzo che sale è una **perdita**. Era la causa del risultato impossibile
*«il pavimento para nello 0,0% dei cicli perdenti»* con una put a 10 delta.
E due difetti di contabilità trovati prima di misurare, che avrebbero **adulato il collar**:
la base senza opzioni rollava lo spot ogni `tenor` giorni pagando ~3,6%/anno di fee inesistenti;
e al roll delle opzioni chiudevo e riaprivo anche lo spot, che non ha motivo di muoversi.
## 8. Cosa NON ho misurato, dichiarato
- **La lente reale (quote vere, 2026-05→09) non è stata girata come backtest.** 123 giorni = ~8
cicli non sovrapposti: sotto-potenziata per costruzione, e su una finestra in cui BTC è salito da
~64,7k a ~77,5k — **avversa a un collar per costruzione**. La catena è servita a **calibrare**
(skew, termine, spread), che è l'uso in cui 161.964 quote hanno potenza.
- **Nessun DSR, nessun `study_family_honest`.** Non servono: il filone cade al **primo** gate (M5),
e M2 si spende su ciò che il primo gate lascia in piedi.
- **A6 e A7 non verificate** (lente reale non girata; il verso short è dentro i gate ma non
separato). Restano previsioni non misurate, non risultati.
- **Il funding dei perp non è nel motore**, come in ogni backtest del progetto: 2,16%/a di drift.
Colpisce base e collar quasi allo stesso modo (stessa esposizione spot), quindi **non cambia il
segno del confronto**, ma abbassa entrambi.
## 9. Verdetto
> **`IL PAVIMENTO FUNZIONA — E' IL TETTO CHE NON SI PUO' PAGARE. E CIO' CHE VINCE SUL BORDO NON E'
> LA PROTEZIONE: E' IL PREMIO DI VARIANZA, CIOE' VRP01`** — **REFUTATO come chiesto.**
Con una postilla che è il vero risultato trasferibile: **la domanda «bloccare la perdita» ha
risposta positiva sul maxDD (36/48) e negativa sul prezzo (45/48).** Su BTC il pavimento si compra
meglio **tenendo meno BTC** che comprando una put e vendendo una call — e il de-levering, a
differenza del collar, non tocca il rendimento nella coda destra dove BTC vive.
## 10. Cosa lo riaprirebbe
- Un **f di stress misurato su un crash catturato** — la stessa condizione che §3 pone allo
short-vol. Con quello, l'angolo covered-call diventa discutibile invece che escluso.
- Un sottostante **senza coda destra grassa**: il tetto costa perché BTC vive lì. Su un asset a
distribuzione più simmetrica il baratto cambia di segno, e il conto va rifatto.
- **Non** lo riapre un tenor diverso, un delta diverso o una griglia più fine: la pendenza è
monotona verso l'angolo short-vol, e l'angolo è già misurato.
+92
View File
@@ -0,0 +1,92 @@
# 2026-09-01 — Stato trades, e la riparazione che non si è propagata
*Scritto il 2026-09-01, misure lette fra le 09:35Z e le 16:47Z. Numeri dalle serie citate.*
## 1. Lo stato, in breve
| | valore | fonte |
|---|---|---|
| fill registrati | **45**, dal 2026-07-14T14:00 al **2026-08-31T15:47** | `trades.db` |
| round-trip chiusi | **29** (21 in utile) — lordo +41,43 · fee allocate 0,66 · **netto +40,77** | `trades.db` |
| fee totali pagate | **$0,8617** | `trades.db` |
| equity (lettura 16:47Z) | **$2.051,84** · picco $2.073,59 | serie `equity`, 1.657 letture |
| posizione BTC | 0,0045 @ medio $79.208,11, dal 25/08 | libro di bordo |
| posizione ETH | 0,0872 @ medio $2.473,04, dal 25/08 | libro di bordo |
| non realizzato (mark 16:38Z) | **$11,72** — BTC 7,91 (2,22%) · ETH 3,80 (1,76%) | gateway |
| gross nozionale | $560,36 → **leva 0,27x** su un tetto di 1,0x | gateway |
**Nessun fill da 25 ore, e non è un blocco.** Il log del cron lo dice a ogni giro fino alle 15:47:
`=> Nessuna azione: conto gia' al target netto del book`. TP01 fermo a +0,461 (BTC) e +0,278 (ETH),
SKH01 flat su entrambi, scarto posizione-target sotto il `min_order_usd` di $5 ($356 contro $351,
$214 contro $213). Feed SKH fresco (0 min), disaster-SL armati a $55.428,0 e $1.708,9.
`monitor_health`: **8/8 OK**, coperture 98-129%. Riconcilio a 3 fonti: **44/45 concordano, 0 prezzi
divergenti**; l'unica coppia scoperta è il difetto già noto — stesso fill (ETH buy 0,04 @ 1869,74)
timbrato `2026-07-14T14:00` nel log del cron e `2026-07-08` nel jsonl, i sei giorni di scarto della
lezione già registrata. Non è un fill perso.
## 2. Il difetto: `--report` stampa un rendimento che non è un rendimento
`scripts/live/trades_db.py --report` stampa questa riga:
```
equity : $598.06 -> $2,051.84 (+1453.78, +243.08%) | picco $2,073.59 | 1657 letture
```
Accanto a una riga di P&L che dice `netto +40,77`. **Il +243% non è rendimento: è quasi tutto un
versamento.** La serie di 1.657 letture orarie contiene **un solo salto**, il 2026-08-25 alle 11:47Z:
$667,49 → $2.066,88, **+$1.399,39**.
Non è una mia classificazione a occhio: è quella del progetto. `journal.movimenti_capitale()`, che
importa la soglia viva da `book.EQUITY_JUMP_ALERT` (0,10) e confronta il salto col massimo che il
**mercato misurato** avrebbe potuto produrre in quell'ora a tetto di leva, restituisce
`certi 1399.39 · ambigui 0.0`, un solo evento, classe `movimento`.
Al netto:
| lettura | valore |
|---|---|
| periodo 1 — 23/06 22:00Z → 25/08 10:47Z ($598,06 → $667,49) | **+11,61%** |
| periodo 2 — 25/08 11:47Z → 01/09 16:47Z ($2.066,88 → $2.051,84) | **0,73%** |
| **TWR spezzato sul versamento** | **+10,80%** |
| crescita di equity al netto del versamento, 69 giorni | **+$54,39** |
**Il numero regge al controllo incrociato.** Il giornale del 31/08, che lo scorporo lo fa già,
scrive: *«cumulato dall'arming: $+1.461,39 di equity — di cui $+1.399,39 versati/prelevati ->
trading $+62,00»*. Stessa base ($598,06), stesso versamento; la differenza fra i suoi +62,00 e i
miei +54,39 è **7,61 di marcatura** fra l'ultima lettura del 31/08 e quella di oggi. Le due misure
concordano.
### Perché è un difetto e non un dettaglio di stampa
La riparazione **esiste già, e in questo stesso repo**. `src/live/journal.py:121` la porta scritta
nel docstring, col caso d'origine:
> *«Il P&L di giornale e' un delta di equity, quindi un versamento ci finisce dentro come se fosse
> profitto: la voce del 2026-08-25 dichiarava «giorno: +$1.414,57» quando $1.399 erano un deposito
> USDC, e il «cumulato dall'arming» avrebbe mentito per sempre.»*
Quel difetto fu trovato e riparato nel giornale. **`trades_db.py:83` non ha mai ricevuto la
riparazione**: calcola `100*(e1/e0-1)` sulla serie grezza e non chiama `movimenti_capitale()`, che
è a un import di distanza e restituisce già `certi` e `ambigui` pronti da scorporare.
È una variante di **P1** — non un sorvegliante che ridichiara il proprio bersaglio, ma **una
riparazione che non ha attraversato il confine fra due lettori della stessa serie**. Il primo
strumento dice la verità; il secondo, interrogato con lo stesso comando pubblicato in CLAUDE.md §13
(`trades_db.py --report`), stampa un numero che si legge come una performance ed è per il **96,3%**
un bonifico.
**Danno oggi: nessuno sui soldi**`--report` è in sola lettura e non decide niente. Il danno è di
citazione: è esattamente la classe di numeri che §2 raccoglie sotto *«non citare»*, ed è l'unico che
il progetto produce **su richiesta esplicita di un comando documentato**.
## 3. Cosa NON ho fatto
**Non ho toccato il codice.** La riparazione è piccola — chiamare `movimenti_capitale()` e stampare
la riga scorporata accanto a quella grezza, come fa il giornale — ma sta su uno script che legge il
libro di bordo vivo, e la richiesta era di aggiornare i documenti. Registrata come debito aperto.
Non ho scomposto la differenza fra il P&L dei round-trip (+$40,77) più il non realizzato ($11,72)
= **+$29,05** e i +$54,39 di equity al netto del versamento. Lo scarto di ~$25 sta in voci che il
conteggio round-trip non copre — funding dei perp (non modellato in nessun backtest, §2), reward
USDE, marcatura dell'USDE all'indice. A questa taglia non vale il costo della misura: si dichiara.
+166
View File
@@ -0,0 +1,166 @@
# 2026-09-01 — La chiave di scala esiste, ed è inerte
*Scritto il 2026-09-01. Implementazione dei punti 1-4 di `docs/research/SPEC-scale-key.md` §8.*
**Nessun ordine. `config/live.json` NON è stato toccato: il libro che gira è bit-exact quello di ieri.**
## 0. Da dove nasce
Il progetto aveva una regola — *«ogni cambio di scala passa dal cap di config, non da `target_vol`»*
e CLAUDE.md la accompagnava da settimane con questa nota:
> ⚠️ **NON È IMPLEMENTABILE COME SCRITTA:** in `config/live.json` non esiste una chiave di scala.
Era un debito preciso: la regola prescriveva un percorso che non esisteva, quindi qualunque cambio
di scala sarebbe finito su `WEIGHT`/`W_TP01`/`W_SKH` in `src/live/book.py` — cioè codice su un
percorso con soldi veri, e per giunta nel posto sbagliato (`W_TP01`/`W_SKH` sono il **rapporto**
75/25, non la taglia: moltiplicarli romperebbe la parità coi pesi del backtest).
La specifica era già scritta, 618 righe, con nove condizioni di gate e il prototipo che ne dimostra
la meccanica. **Non ho progettato niente: ho eseguito.**
## 1. Il rischio che l'implementazione doveva rendere impossibile
La §0 della specifica lo dice meglio di come lo direi io:
> *Una chiave di scala è esattamente il tipo di parametro che si alza «solo un po'» dopo un mese
> buono. È un numero, sta in un file di config, non richiede di capire niente per cambiarlo, e il
> suo effetto è immediato e piacevole.*
E il motivo per cui il progetto non aveva già una guardia non è dimenticanza, è **aritmetica**:
**lo Sharpe è invariante alla scala.** `deflated_sharpe` e `marginal_vs_tp01` leggono un numero che
a k=1,00 e a k=2,00 è identico. Non falliscono: **non vedono**.
## 2. Cosa ho costruito
| pezzo | dove | cosa fa |
|---|---|---|
| `book_scale_k` | `config/live.json`**assente** | la chiave. Assente = 1,00 = libro di sempre |
| `_scala()` | `src/live/book.py` | legge, **valida sempre**, applica solo sul percorso fidato |
| `book_net_target(..., scala=)` | `src/live/book.py` | applica la scala **DOPO il clamp** |
| `LEVA_LORDA_MAX` 1,25 · `SCALA_LADDER` (1,00 · 1,25) · `DISASTER_SL_BUDGET` 0,50 | `src/live/book.py` | **costanti di CODICE** |
| `ScalaNonAutorizzata` | `src/live/book.py` | eccezione, **non** un clamp |
| riga «scala libro» + stop-e-allerta | `scripts/live/book_execute.py` | la scala è visibile a ogni giro |
| `scale_watch` | `src/live/` + `scripts/live/` + `cron_daily.sh` | 3 domande, 3 azioni, 3 stati |
| `scale_history.jsonl` | `data/live/` | giornale append-only (già nel backup) |
| T1-T11 | `tests/test_book_scale.py` | **18 test, tutti verdi** |
### 2.1 Le quattro decisioni che non sono di comodo
**(a) La scala si applica DOPO il clamp.** Applicandola prima, il cap se la mangerebbe *proprio nei
giorni di massima convinzione*: a `tp=1, sg=+1` il grezzo vale esattamente `cap`, quindi `k_eff`
tornerebbe a 1,00 a ogni k. Il risultato non sarebbe «il libro a leva k» ma un libro che alza i
giorni piccoli e lascia fermi i grandi — un cambio di **forma** travestito da cambio di taglia. E
allora la curva `g(k)` con cui il gradino viene autorizzato **non descriverebbe quel libro**.
*Autorizzare con una curva e implementarne un'altra è il modo più silenzioso di sbagliare.*
Il prezzo, dichiarato: il cap smette di essere il tetto assoluto del nozionale e diventa il tetto
del libro **unitario**. La guardia sulla leva lorda è ricostruita esplicitamente altrove.
**(b) Il tetto è sul PRODOTTO, e sta nel CODICE.** Un tetto sulla sola chiave lascia aperta la porta
accanto: `frac` 0,625 × scala 1,25 = **1,562x**, che passerebbe. E un tetto in config sarebbe
modificabile dalla stessa mano, nello stesso file, nello stesso momento — *un lucchetto con la chiave
attaccata*. Con `LEVA_LORDA_MAX` in `src/live/book.py`, il gradino 1,25 → 1,50 richiede una modifica
di codice, quindi una review. **Il tetto è basso apposta: è il meccanismo del cricchetto.**
**(c) Fuori scaletta o fuori tetto = STOP, non clamp.** `book_execute` si ferma, non invia, allerta.
Tagliare silenziosamente farebbe girare una config che *dichiara* un numero e un libro che ne
*esegue* un altro. E `SCALA_LADDER` esiste perché **una scala a gradini rende inesprimibile «solo un
po'»**: 1,05 non è un valore prudente, è un valore fuori scaletta, e viene rifiutato.
**(d) La scala vive solo sul percorso fidato.** Equity reale illeggibile ⇒ scala 1,00. *Una scala è
una decisione di rischio presa conoscendo il conto; quando non si sa quanto vale il conto, non si
prende.* Così «il fallback non è più permissivo» è vero **per costruzione**, non per aritmetica.
⚠️ Ma la **validazione** avviene comunque, anche sul percorso degradato: una config rotta è un
errore di configurazione, non una condizione di mercato, e non deve nascondersi dietro un giro in
cui l'equity non era leggibile.
### 2.2 La guardia che morde per prima, e non è quella che sembra
Il disaster-SL è un movimento di **prezzo** (30% sul mark), ma il suo impatto in equity è quel
movimento **moltiplicato per il lordo** — quindi cresce con k anche se `disaster_sl_pct` non cambia:
- dal peggior giorno possibile: `k ≤ 3,49x`
- **dal costo di un disaster-SL: `k ≤ 1,67x`** ← **2,1× più stringente**
Da qui l'invariante `n_asset · frac · scala · disaster_sl_pct ≤ 0,50`, che scatta anche se qualcuno
allarga lo stop invece di alzare la scala.
## 3. Il test che conta più degli altri
**T1b**, e la ragione per cui la specifica gli dedica un paragrafo: **a k=1 l'implementazione
simmetrica e quella asimmetrica danno lo stesso identico numero.** Un test di simmetria scritto sul
caso di default passa sempre e non controlla niente — potenza **zero**, la stessa firma dei test di
`book_live` scoperti senza potenza a libro flat.
T1b quindi verifica due cose: *(a)* che a k=1 le due implementazioni siano davvero indistinguibili
(cioè che il rischio sia reale), e *(b)* che fuori da k=1 l'asserzione di T1 le **separi**. Nel test
c'è scritta un'implementazione asimmetrica apposta perché fallisca.
Gli altri: T2/T3 i due tetti letti da config coi bersagli **importati** dalla produzione; T4 il
fallback mai più permessivo in ogni stato degradato; T5 il cap che morde ancora; T6 lo stop non
clampato (più T6b sul prodotto e T6c sul disaster-SL); **T7 l'inerzia bit-exact**; T8 il report che
la dichiara; T9 i bersagli derivati e non ridichiarati; T10 il **controllo positivo del
sorvegliante** (con una config non autorizzata deve parlare, con config e giornale concordi deve
tacere) più T10b sulla disciplina degli allarmi e T10c sui tre stati; T11 che il vecchio test sia
stato **sostituito, non rilassato**.
## 4. Il sorvegliante, e cosa non può fare
`scale_watch` gira in `cron_daily` e risponde a tre domande che hanno **tre azioni diverse**:
| domanda | azione se 🚨 |
|---|---|
| la config dichiara una scala che il **giornale** non ha mai autorizzato? | ricostruire come ci si è arrivati, scendere a k0 |
| il criterio che autorizzò la scala **corrente** passa ancora oggi? | scendere di un gradino |
| il **prodotto** `frac · scala · n_asset` ha superato il tetto? | bloccare l'esecuzione |
Tre stati (`OK` / `ALLARME` / **`NON MISURABILE`**), una allerta per streak, e il marcatore «già
detto» scritto **solo dopo un invio riuscito** (il debito #2 del 28/08: un 🚨 perso prima era perso
per l'episodio intero).
Riporta anche la **frequenza del ramo di fallback**, che se superasse il 2% in 90 giorni
invaliderebbe la regola (d): il libro girerebbe a una leva **mista** e `g(k)` smetterebbe di
descriverlo. Misurata oggi: **0 giri su 1.676**. ⚠️ I 19 giri senza equity reale sono tutti
«paper capital», cioè **pre-finanziamento**, dove il libro non invia affatto — il sorvegliante li
conta e li dichiara separatamente invece di gonfiare la quota.
🚨 **Ciò che il sorvegliante NON può fare: impedire la modifica.** Chi ha accesso al file può
scriverci dentro. Rende la modifica visibile entro 24 ore e attribuibile. È la stessa onestà di
`venue_watch`: non protegge il saldo, **compra tempo**.
## 5. Verifica che il libro non è cambiato
```
sizing base : $2,052.22 | cap/asset $1026 | min $5 | disaster-SL -30%
scala libro : 1.00x (tetto 1.25x) | leva lorda max 1.000x dell'equity [chiave assente o 1,00 = libro invariato]
BTC TP +0.461 · SKH +0(flat) -> net $+355 | pos $+357 -> HOLD (a target)
ETH TP +0.278 · SKH +0(flat) -> net $+214 | pos $+212 -> HOLD (a target)
```
Gli stessi target del cron delle 15:47. **Suite: 825 passati** (807 + 18 nuovi).
## 6. Cosa NON ho fatto, ed è deliberato
**Non ho scritto la chiave in config.** Il punto 2 della checklist lo prescrive esplicitamente, e T7
dimostra bit-exact che senza la chiave il libro è quello di prima.
**Non ho eseguito GATE SCALA-01**, e non era possibile: A2 richiede che k0 sia in produzione con il
criterio passato ogni giorno, e il punto 5 impone **≥30 giorni a 1,00 col sorvegliante attivo prima**
di eseguire il gate. Cito la specifica perché la frase è il motivo:
> *Il punto 5 non è burocrazia: è l'unico modo di scoprire che il sorvegliante è rotto mentre la
> leva è ancora 1,00.*
**Non ho rifatto `r0726_fee_sensitivity`** (A7). Serve solo *se* e *quando* il gradino viene chiesto:
la sua conclusione «liquidation fee 1% irrilevante» era condizionata a lordo ≤1x, e a 1,25x una
liquidazione costerebbe **1,25%** dell'equity. Non è un problema, è un numero da ricalcolare — e
ereditarlo sarebbe l'errore.
📌 **A6 (anti-recency) oggi non morde**: `k_ammesso ≈ 3,5x` contro un gradino di 1,25x, e il vincolo
che morde è il disaster-SL. È scritto adesso **perché adesso non è comodo per nessuno** — che è
l'unico momento in cui una regola del genere si può scrivere onestamente.
## 7. Stato del gate
`GATE SCALA-01` è ora in CLAUDE.md §4 con data **non prima del 2026-10-01**. A8 e A9 sono fatti;
A2 matura col tempo; A1/A4/A5 sono aritmetica già cablata; A3/A6/A7 si eseguono il giorno del gate,
sui dati di quel giorno, non su questi.
+137
View File
@@ -0,0 +1,137 @@
# 2026-09-01 — PAVIMENTO-LEVA: il pavimento non licenzia taglia, e corregge quanto scritto stamattina
*Scritto il 2026-09-01. Ogni numero è riprodotto da `scripts/research/r0901c_pavimento_leva.py`,
che riusa il motore di `r0901_btc_collar.py`. **Libro, pesi, cron, config INVARIATI. Nessun ordine.***
## 0. La domanda
> *«possiamo usare quanto conosciamo del pavimento per studiare una strategia»*
Cosa sapevamo, da COLLAR01 (§71, poche ore prima): il collar riduce il maxDD in 36/48 celle; a
premio **zero** il pavimento vale moltissimo (maxDD 51,83% → 36,76%, drift +10,04% → +35,69%); ciò
che uccise il collar fu il **tetto**, non il pavimento (C9); la put è l'ala cara.
**L'inversione.** COLLAR01 chiese *«quanto DD mi risparmia il pavimento a taglia fissa?»* e perse
contro il de-levering. La domanda speculare è *«quanta TAGLIA mi autorizza il pavimento a DD
fisso?»*. Non è la stessa, per due ragioni misurabili:
1. de-levare riduce taglia e rischio **in proporzione**; un pavimento cambia la **forma** — se taglia
la coda sinistra più di quanto costi, scalare *in su* la struttura protetta può battere la base
nuda alla stessa DD. È l'unica cosa che il de-levering **non può** comprare;
2. il tetto di leva del progetto non è fissato dallo Sharpe ma dal **costo di un episodio** di
disaster-SL (`n · frac · scala · sl ≤ 0,50` ⇒ k ≤ 1,67x). Il disaster-SL è **rotolante** e non
limita la perdita (peggior DD dall'ingresso misurato 60,6%). Una put la limita per costruzione —
*dentro la sua finestra*.
Quattro attese scritte prima di misurare (B1-B4). **Tutte e quattro confermate.**
## 1. Il risultato: 0 celle su 16
Griglia dichiarata: 4 δput × 2 tenor × 2 gate. Struttura: hold gated + **sola put**, premio reale
(DVOL × skew × termine dalla catena — realistico per chi *compra*: M29 corregge chi vende a
modello, non chi compra).
| gate | base nuda | pavimento a premio reale (16 celle) |
|---|---|---|
| FORTE | maxDD **51,83%** · drift +10,04% | maxDD **55,4% → 75,8%** · drift +3,8% → 17,0% |
| LARGO | maxDD **71,94%** · drift +17,37% | maxDD **74,8% → 81,7%** · drift +9,9% → 13,9% |
**Il pavimento peggiora il maxDD in 16 celle su 16.** Quindi `k_f = 1,000` ovunque: non c'è taglia
da licenziare, perché non c'è DD da spendere. Il null M5 classico concorda (0/16). Monotono nella
protezione: più la put è vicina, peggio va — il bleed del premio *è* il drawdown.
## 2. 🚨 La correzione a COLLAR01
Stamattina ho scritto, in A1: *«il pavimento funziona davvero (e §46 è battuto sul suo motivo)»*.
**La prima metà è sbagliata nell'attribuzione.** Il DD lo riduce **il collar**, non il pavimento:
- la put da sola, a premio reale, **peggiora** il DD (questo filone);
- a premio zero lo riduce (il controllo del pranzo gratis) — quindi **tutto il beneficio del
pavimento è mangiato dal suo premio, e oltre**;
- nel collar la riduzione viene dal **tetto**: il suo premio compensa il bleed della put, e cappare
l'upside **abbassa il picco** da cui il DD si misura — che non è protezione, è un picco più basso.
**Il premio di pareggio** lo quantifica: il pavimento inizia a ridurre il DD nudo solo se le put
costano il **36-79% del reale** (secondo la cella). Il DVOL sta 1,32× sopra la vol realizzata, quindi
un prezzo *equo* sarebbe ~76% del reale: **anche a prezzo equo il pareggio si sfiora nelle celle
migliori e si manca nelle altre**. E il mercato non vende a prezzo equo.
La seconda metà della frase regge, ma va detta con precisione: **il motivo di §46 non si applica**
(a beta 1,0 la put paga davvero: para nel 2-20% dei cicli), **ma il verdetto di §46 — *il maxDD
SALE* — si riproduce a beta 1,0 per un motivo diverso.** Il motivo è la sezione seguente.
Corretti stesso giorno: il diario di COLLAR01 (titolo e A1), RESULTS §71, la memoria. M28:
*quando due affermazioni della stessa memoria si contraddicono, la contraddizione è informazione*
qui erano due mie affermazioni a poche ore di distanza, e la seconda ha corretto la prima.
## 3. B2 — Il meccanismo: la put copre una finestra, BTC scende in grind
| gate | maxDD della base | da → a | durata |
|---|---|---|---|
| FORTE | 51,83% | 2021-07-20 → 2022-05-06 | **290 giorni** |
| LARGO | 71,94% | 2021-07-20 → 2023-10-16 | **818 giorni** |
Una put a 7-14 giorni copre **una** finestra. Il drawdown che conta è lungo **20-60 finestre**. In
un grind la put scade OTM settimana dopo settimana — il pavimento para nel **0-9%** dei cicli col
gate FORTE — mentre il premio sanguina. E non protegge nemmeno la finestra peggiore: peggior 14
giorni FORTE **22,9% nudo → 23,6% col pavimento**, perché la put a 5 delta sta a ~22% dallo spot,
cioè esattamente al bordo di ciò che è successo, e il premio la rende netta negativa.
**Su BTC il pavimento compra protezione contro la cosa sbagliata**: i crolli veloci dentro una
finestra, mentre le perdite che definiscono il maxDD sono grind di mesi. Questo è ciò che §46 non
poteva vedere (a beta 0,076 il libro non aveva né crolli né grind da proteggere) e che a beta 1,0
diventa il fatto dominante.
## 4. B3 — La licenza del disaster-SL è di carta
L'invariante `n · frac · scala · sl ≤ 0,50` con `sl = 0,30`**1,67x**. Con la put a 5δ/14g il
pavimento sta a **21,9%** dallo spot: sostituire `sl` con quella distanza darebbe **2,28x**
*1,4× in più*. È la proposta che qualcuno farà, e va uccisa ora:
| finestre consecutive | giorni | peggior perdita col pavimento | la put… |
|---|---|---|---|
| 1 | 14 | 23,6% | copre |
| 2 | 28 | 25,9% | **non copre** |
| 4 | 56 | 28,8% | **non copre** |
| 8 | 112 | **33,6%** | **non copre** |
L'invariante limita **un episodio**; la put limita **una finestra**; il massimo su finestre
consecutive **non è limitato da nulla**. A 2,28x un grind di 112 giorni costerebbe il **77%
dell'equity**. ⇒ **`disaster_sl_pct` non si sostituisce con la distanza di un pavimento**, e va
scritto prima che sia comodo per qualcuno.
## 5. B4 — Il pavimento gated sull'IV: migliora, non basta
§46 dichiarava *«nessuna copertura dinamica gated su regime è stata provata»*. Provata: put ON solo
quando DVOL/RV trailing sta sotto il 25° o 50° percentile dell'anno (il premio di varianza è
sottile).
| gate | soglia | ON (giorni a mercato) | drift | vs base |
|---|---|---|---|---|
| FORTE | 25° pctl | 27% | **+8,39%** | +10,04% → **perde** |
| FORTE | 50° pctl | 47% | +6,77% | perde |
| LARGO | 25° pctl | 25% | **+15,31%** | +17,37% → **perde** |
| LARGO | 50° pctl | 50% | +13,93% | perde |
Migliora molto rispetto al pavimento sempre acceso (+3,8% → +8,4% col gate FORTE) e il DD resta
≥ della base (k_f = 1,000). ⚠️ L'incollatura ignora lo spread delle transizioni ON/OFF, **a favore**
del gated: se perde così, perde a maggior ragione.
## 6. Cosa NON ho fatto, dichiarato
- Nessun DSR, nessun `study_family_honest`: il filone cade al primo gate, 0/16.
- Nessuna lente reale (123 giorni, ~8 cicli): stessa ragione di COLLAR01.
- Non ho provato il pavimento **a scadenza lunga** (30-90 giorni), che coprirebbe più di una
finestra di grind — perché l'operatore ha vincolato la scadenza a **≤15 giorni**. È l'unica
variante che B2 lascia aperta, e sta fuori dal vincolo dichiarato. Va detto: se il vincolo cadesse,
sarebbe la prima cosa da misurare, e la memoria di §46 ha già l'apparato (tenor 30).
## 7. Verdetto
> **`IL PAVIMENTO NON LICENZIA TAGLIA A PREMIO REALE: 0/16. Ciò che il de-levering non può comprare,
> la put lo vende a un prezzo che nessuna cella ripaga — e su BTC compra protezione contro i crolli
> mentre le perdite sono grind.`** — REFUTATO.
**Cosa lo riapre:** una scadenza che copra il grind (fuori dal vincolo ≤15g), o un mercato che venda
la put al 36-79% del prezzo attuale — cioè un premio di varianza *negativo*, che su BTC non si è
mai misurato. Non lo riaprono delta, tenor ≤15 o gate sull'IV: sono misurati.
+91
View File
@@ -0,0 +1,91 @@
# 2026-09-01 — Soldi fermi: i modi esistono, valgono €5/mese, e uno di loro distruggerebbe lo split
*Scritto il 2026-09-01. Numeri da `scripts/research/r0901d_soldi_fermi.py`; tassi dal web con fonte e
data (cutoff del modello 05/2026: non li sapevo). **Non sono pareri fiscali.** Nessun ordine.*
## 0. La domanda
> *«troviamo altri modi per fare guadagnare con soldi che sarebbero fermi. non per forza trading»*
## 1. Dove sta il capitale fermo — e cosa NON lo è
**Deribit (lettura 20:42Z):** USDC $1.406 · USDE 654 · libro a 0,27x. ⚠️ **L'USDC non è fermo**: è
la base di sizing del libro (cap = equity × 0,5) e il cuscino di regolamento del disaster-SL. Spostarlo
fuori rimpicciolisce le posizioni, non libera capitale.
**Fuori:** ~€6.043 in XEON «a ~0% reale netto» (memoria 27/07). È lo **split-cassa** — la protezione
dal rischio venue, P(perso tutto) 18% → 3,5% a p=1% — e la memoria dichiara di non sapere quanto sia
fondo d'emergenza. **Assunzione dichiarata: €6.000 investibili a 12+ mesi.**
## 2. On-venue: chiuso, e stavolta con l'elenco intero
`public/get_currencies` oggi: **4 valute su 50** con APR > 0 — USDE 4,32 · USDC 3,40 · BUIDL 3,20 ·
STETH 2,22. Sono esattamente le quattro chiuse il 31/08 (al tetto · Italia esclusa · non
acquistabile · è ETH). Non era un campione: era la lista.
## 3. I modi, in euro netti l'anno su €6.000
| opzione | lordo | fisco | **netto/anno** | vs XEON | rischio | split |
|---|---|---|---|---|---|---|
| XEON (status quo) | 2,10% | 26% | **€81** | — | ~0 | intatto |
| **BOT 12 mesi** | 2,77% | **12,5%** | **€133** | **+€52** | ~0 (Stato) | intatto |
| **Conto deposito vinc. 12m** | 3,50% | 26% | **€143** | **+€62** | ~0 (FITD) | intatto |
| sUSDe diretto (Ethena) | ~9% | 33% | €350 | +€269 | **emittente + smart contract** | **distrutto** |
| versare sul libro | ~10% atteso | — | ~€600 | +€519 | maxDD 11%, venue | **distrutto** |
Fonti: BOT 2,768% asta agosto 2026 (Banca d'Italia 14/08); BCE DFR 2,25% (23/07) ⇒ €STR ~2,2%;
conti deposito 3,25-3,50% (Aidexa, Banca Progetto, Cherry, settembre 2026); sUSDe «high single
digits» Q2 2026 (9,4% 7g / 11,8% 90g ad aprile). Bollo 0,2% su tutto.
## 4. La lettura
- **Fra le opzioni che lasciano intatto lo split, la migliore vale +€62/anno = €5/mese.** BOT e conto
deposito distano €10/anno: il 12,5% del BOT compensa quasi tutto il lordo in meno. Dentro il
rumore di *una* asta.
- **sUSDe diretto rende ~2× Deribit** (che trattiene metà dell'APR Ethena) — ma metterebbe i €6k
sullo **stesso emittente** dei 654 USDE già in conto. Lo split-cassa esiste per non essere
correlato al venue e al crypto: questa opzione lo trasforma in concentrazione. Già a 24,2% di quota
l'evento emittente vale 3,1× il maxDD dell'intero libro (memoria 30/08).
- **Versare sul libro** è l'alternativa che la memoria indica come dominante (€10k = 2,45× in 13
anni) — ma è trading, e distrugge lo split. Non risponde alla domanda: la cambia.
## 5. La taglia
Il miglior guadagno *sicuro* sullo status quo è **€62/anno**. **€100/mese di bonifico valgono
+4,07%/anno di drift.** Il modo esiste, è reale, ed è due ordini di grandezza sotto la leva che il
progetto ha misurato sei volte come binding.
## 6. Cosa resta all'operatore
1. **Quanto dei €6.043 è fondo d'emergenza.** Con quello, la tabella si applica al resto.
2. Fra BOT e conto deposito: **preferenza di liquidità** (MOT vs vincolo 12 mesi) e di controparte
(Stato vs FITD), non di rendimento — la differenza è €10/anno.
3. On-venue non c'è niente da decidere. La chiave di scala a 1,25x (GATE SCALA-01, ottobre)
libererebbe ~$400 → ~$10/anno: lo dico perché è l'unica via strutturale, non perché valga.
## 7. La conclusione dell'operatore (02/09, 06:00Z) — e le tre precisazioni
> *«quindi la miglior cosa è mettere gli XEON su deribit con il nostro sistema attivo»*
**Sì, ed è la conclusione che la memoria aveva già scritto il 27/07** (`r0727_lumpsum_split.py`:
piano «€5.000 dei €6.043 in XEON + €500/mese»; il 25/08 sono entrati $1.399, non €5.000). La
lettura della tabella è quella giusta: versare batte tenere fermo, ed è la leva binding. Ma il
*come* ha tre vincoli, tutti già misurati:
1. **Non tutti: €5.000, non €6.043.** La protezione dal rischio venue **satura a qualunque quota
> 0** (P(perso tutto) 18% → 3,5% tenendo fuori il 10%, e resta 3,5% al 25% e al 40%). L'ultimo
migliaio dentro compra ~€100/anno attesi e butta via l'intera assicurazione. **~€1.000 restano
fuori: è lo split, e costa zero.**
2. **Il «~€600» non è un tasso.** È un'attesa a banda larga su una strategia a mercato il 22% dei
giorni (live: 14/64, leva mediana 0,00x). Numeri onesti: TWR **+10,8% in 70 giorni**, Sharpe
hold-out TP01 **~+0,05**, fisco d'accumulo **30% a 10 anni**. Un anno piatto è possibile; il
conto deposito non lo ha.
3. **Il fondo d'emergenza** — non dichiarato dall'operatore, non noto alla memoria. Se una parte
dei €6.043 lo è, resta fuori.
**Operativamente non serve nulla:** il sistema è live dal 20/06, il cap è dinamico (`equity × 0,5`),
il rilevatore di salti ha già gestito il 25/08 senza falsi allarmi. Il deposito viene assorbito al
giro successivo delle :47. Resta da scrivere **una riga di giornale con data e importo** — la fonte
del cumulato dall'arming va scritta prima, non ricostruita dopo (debito #14). **In attesa di importo
e data dall'operatore.** N9: le opzioni sicure (+€52-62/anno) sono state viste e messe da parte
*con motivo*, non ignorate.
@@ -0,0 +1,76 @@
# 2026-09-02 — Debito 14: il report stampa il TWR, non il bonifico
*Scritto il 2026-09-02 fra le 14:16Z e le 14:22Z (commit `2f701b8`, 14:22:50Z; la prima stesura diceva «fino alle 14:30Z» senza averlo letto — corretto in revisione). Numeri dalla lettura di equity delle 13:47:01Z (1.678 letture) e dal report rilanciato dopo la riparazione.*
## Cosa c'era
`trades_db.py --report` calcolava `100*(e1/e0-1)` sulla serie grezza di equity e lo stampava come
performance: **+243%**. La serie contiene un solo salto, il versamento di $1.399,39 del 25/08
11:47Z: il 96,3% di quel numero era un bonifico. Il classificatore che lo scorpora esisteva già
(`journal.movimenti_capitale`, dal 25/08) e non era mai arrivato al secondo lettore della stessa
serie. Trovato e registrato il 01/09 (`2026-09-01-stato-trades.md`), non riparato perché lo script
legge il libro vivo.
## Cosa ho fatto
| pezzo | dove | cosa |
|---|---|---|
| funzione unica | `src/live/journal.py``rendimento_twr(con, fino_ts)` | chiama `movimenti_capitale`, spezza la serie all'equity PRIMA e DOPO ogni movimento **certo**, moltiplica i segmenti. Gli **ambigui non spezzano** (P12): restano nel rendimento, dichiarati. Tre stati: numero, o `None` con `motivo` |
| il report | `scripts/live/trades_db.py``report()` | chiama `rendimento_twr` (P1: non rifà il conto). Stampa TWR con i segmenti datati, i movimenti elencati con la classe, `trading da arming`, e il delta $ grezzo etichettato «movimenti di capitale INCLUSI». Il `%` grezzo non compare più |
| test | `tests/test_journal.py` (+5) · `tests/test_trades_report.py` (+2) | versamento spezzato e grezzo no · senza movimenti = rendimento semplice · ambiguo non spezza · serie vuota → `None` con motivo · **riproduzione del +10,80% del diario 01/09** (M23) · il testo stampato contiene TWR e non il grezzo · `connect()` nudo deviato in tmp (conftest), verificato nel test |
## Il report, prima e dopo
```
prima: equity : $598.06 -> $2,051.84 (+1453.78, +243.08%)
dopo: equity : $598.06 -> $2,048.40 (+1,450.34 di equity, movimenti di capitale INCLUSI) | picco $2,073.59 | 1678 letture
movimenti capitale : +1,399.39 certi | +0.00 ambiguo/i (restano nel P&L, dichiarati)
2026-08-25T11:47 +1,399.39 [movimento]
trading da arming : +50.95 (equity al netto dei movimenti certi)
TWR : +10.61% = +11.61% [2026-06-23 -> 2026-08-25] x -0.89% [2026-08-25 -> 2026-09-02]
```
Il giornale del giorno concorda: «cumulato dall'arming $+1.450,34 — di cui $+1.399,39 versati ->
trading **$+50,95**». Stessa base, stesso versamento, stessa funzione.
## Una cosa imparata riparando
Quattro test sono nati rossi con scenari a **+10% di trading** fra due letture: la soglia del
rilevatore (`book.EQUITY_JUMP_ALERT`) è esattamente 0,10, e a mercato piatto un +10% supera «2× il
massimo che il mercato poteva fare» (che è zero) — viene classificato **movimento**. Non è un difetto
della riparazione: è il limite già dichiarato del rilevatore live (D5, nel docstring di
`movimenti_capitale`). Nella serie vera non morde — fra due letture orarie consecutive il trading non
fa +10% — ma un test che lo tocca lo rende visibile. I test ora usano +5%, e il limite è scritto
nel test, nel CLAUDE.md e qui.
## Cosa NON ho fatto
- Il giornale (`pnl_giorno`, pagina markdown) **non stampa il TWR**: stampa già lo scorporo in
dollari e la sua pagina ha una forma testata. Aggiungerlo è una riga, ma non era il debito.
- Non ho scomposto lo scarto (~$25) fra round-trip + non realizzato e il trading al netto: era già
fuori scope il 01/09 e lo resta.
## Revisione del codice, prima tornata (15:15Z, commit `835e0c8` 15:17:05Z) — tre cose trovate, tutte riparate
| trovato | vero? | riparazione |
|---|---|---|
| l'intervallo che contiene un movimento certo esce intero dal rendimento: il suo P&L di mercato finisce in `certi` e fuori da `trading`, e nessun documento lo diceva | sì, per costruzione. Errore massimo = metà del movimento riconosciuto (margine del classificatore). Il 25/08: ~$263 lordi a ±0,2% → ~$0,5 | dichiarato nel docstring, nel report, in CLAUDE.md §5.14 e in memoria 40. Non si stima (P12) |
| a base di equity zero `trading` era un numero mentre `twr` era None, e sbagliato: il salto 0→X è invisibile al classificatore, `trading` valeva l'intero conto sotto l'etichetta "al netto dei versamenti" | sì. Non raggiungibile dal cron (non scrive mai 0), ma raggiungibile da un backfill | tre stati veri: `twr` e `trading` entrambi None con motivo; il report stampa n/d. Test in `test_journal.py` e `test_trades_report.py` |
| movimento nel primo intervallo o due consecutivi → segmento `+0.00% [x -> x]` | sì, cosmetico | i segmenti a lunghezza zero non si producono; nessun tempo a mercato → TWR 0,0 dichiarato numero. Test |
Il numero pubblicato non cambia: +10,61% alle 14:02Z, +10,56% alle 14:47Z per la marcatura.
## Seconda tornata (15:30Z) — la piu' importante di tutte
| trovato | vero? | riparazione |
|---|---|---|
| il classificatore dei movimenti era **cieco ~23 ore al giorno**: il feed 1h si ferma alle 00:00, `asof` dava la stessa barra alle due letture, mercato «fermo» = 0, qualunque calo ≥10% del giorno diventava «movimento». Riprodotto dalla revisione su copia del DB: 12% appeso alle 14:47 → `certi` 1.399 → 1.153, TWR invariato invece di 2,71% | si'. Verificato: ultima barra 00:00, adesso 15:26 | nuovo stato **«mercato non misurabile»** (feed fermo, assente, o barra mancante) ⇒ `ambiguo` con la ragione; `twr` e `trading` None con motivo |
| con `data/raw` assente (host ripristinato prima del rebuild) tutto era «ambiguo», `certi` 0, e il report stampava **+243% sotto l'etichetta TWR** | si' | stesso stato: n/d, mai il grezzo |
| «`rendimento_twr` e' la funzione che entrambi chiamano» era **falso**: `pnl_giorno` rifaceva `cum certi`, e la testata Telegram mandava il cumulato grezzo | si' | `pnl_giorno` chiama `rendimento_twr`; la pagina stampa il TWR accanto al cumulato; la testata dice «di cui versati → trading» |
| `leva_tetto` del classificatore ridichiarava `frac × n` ignorando `book_scale_k` e `LEVA_LORDA_MAX` (P1) | si' | `min(frac × n × scala, LEVA_LORDA_MAX)`; fallback = tetto di codice |
| il «limite ereditato: +10% di trading a mercato fermo» scritto la mattina era un **artefatto della fixture piatta** (a mercato misurato il trading non supera 2·max_mkt) | si' | cancellato ovunque; il limite vero e' quello sopra |
| il bound di mercato guarda solo BTC/ETH, ma il 31% dell'equity e' USDE: un depeg pieno sarebbe un «prelievo» | si' | **non riparato**: l'indice USDE e' registrato una volta al giorno. Debito **§5.15** |
| l'ora del report scritta in §2 (14:02Z) era l'ora di esecuzione, non quella della lettura (13:47:01Z); i tempi «scritto» dei diari erano posteriori ai commit | si' | corretti coi tempi veri |
Il numero pubblicato: TWR +10,61% alla lettura delle 13:47Z; +10,56% alle 14:47Z.
@@ -0,0 +1,67 @@
# 2026-09-02 — Debito 7: il docstring diceva 230 minuti, il cron gira ogni ora
*Scritto il 2026-09-02 fra le 14:36Z e le 14:38Z (commit `861cc7f`, 14:38:53Z; la prima stesura diceva «alle 14:45Z» senza averlo letto — corretto in revisione). Cadenza misurata dal log (`r0823_sl_anchor.py`: 1.443 giri in
1.442 ore) e dalla crontab installata, letta dal test.*
## Il difetto
| fonte | cosa diceva | dal |
|---|---|---|
| `scripts/live/book_execute.py`, docstring | «va lanciato ogni ~230 minuti» | 20/06 |
| `scripts/cron_book.sh`, intestazione | cadenza ORARIA, `47 * * * *` | 25/08 (prima `:07`, sempre orario) |
| crontab della VPS | `47 * * * *` | 25/08 |
Era il **docstring** a sbagliare. E non era una svista innocua: sulla riga 4h (la più vicina ai 230
minuti) BTC **raddoppia** gli scatti del disaster-SL rotolante (2 contro 1; a 24h ETH arriva a 5, con
2 in 30 giorni). Il rischio era un lettore diligente che "allineasse" il cron al docstring.
## La riparazione
1. **Docstring riscritto**: `CADENZA: ORARIA`, la riga di crontab citata, la ragione (giro
idempotente → girare più fitto della griglia non costa ordini; latenza ≤1h sugli ingressi/uscite
software di SKH01; ri-ancoraggio orario del rotolante) e il divieto esplicito, con la data.
2. **`tests/test_book_cadenza.py`, 10 test.** P1: non ridichiara «60 minuti», **deriva** le tre
fonti e le confronta fra loro:
- il docstring non contiene più «ogni ~230» e dichiara ORARIA;
- la riga di crontab citata nel docstring è quella dichiarata in `cron_book.sh`;
- la riga dichiarata è oraria (parser a 5 campi, solo casi regolari, rifiuta il resto),
sotto i 230 della griglia e lontana dai 240 della riga peggiore, e fuori dal minuto `:00`;
- **la crontab installata** (`crontab -l`) coincide con la dichiarata. Dove la crontab non è
leggibile il test è **saltato**, non verde (P5). Sulla VPS oggi gira e passa.
## Numeri
| | |
|---|---|
| test nuovi | 10, tutti verdi; suite dei moduli che toccano `book_execute`: 231 verdi |
| cadenza dichiarata / installata | 60 min / 60 min |
| griglia SKH01 | 230 min (la riga da non usare) |
| riga peggiore misurata | 240 min (BTC 2 scatti contro 1) |
## Cosa NON ho fatto
- Non ho toccato il cron né `cron_book.sh`: la configurazione che gira era quella giusta.
- Non ho aggiunto un sorvegliante a runtime sulla cadenza (contare i giri per ora nel log): il
giornale già stampa «giri mancanti» ogni giorno, e il test copre la deriva di configurazione.
## Revisione del codice, prima tornata (15:15Z, commit `835e0c8`) — due cose trovate, tutte riparate
| trovato | vero? | riparazione |
|---|---|---|
| il docstring nuovo diceva «il disaster-SL rotolante si ri-ancora ogni ora»: falso. `ensure_disaster_sl` lascia il bracket com'è finché lo stop voluto è entro il 5% da quello piazzato e la taglia entro il 10%; il giro orario **controlla**, ri-ancora oltre la tolleranza (mark +5,263% / 4,762%). I «BTC 0 / ETH 1» di `r0823` sono misurati **con** questa isteresi | sì, verificato in `execution.py:233-235`. Propagato anche a CLAUDE.md §5.7 e memoria 40 | riscritto in tutti e tre i posti: «controllato ogni ora, ri-ancorato solo oltre la tolleranza; lo stop siede fra 26,3% e 33,3% dal mark corrente». Frase sbagliata su un meccanismo di sicurezza vivo: era la segnalazione più importante |
| la guardia del test cercava due letterali («ogni ~230»): «ogni quattro ore» sarebbe passato | sì | la guardia estrae **ogni** prescrizione «ogni/every N unità» e pretende 60 minuti; controllo positivo (M15) su cinque frasi. Limite dichiarato (P13): una prosa che prescrive senza «ogni» passa |
## Seconda tornata (15:30Z)
| trovato | vero? | riparazione |
|---|---|---|
| la frase nuova di CLAUDE.md §5.7 era **invertita** («chi lo avesse corretto nel verso del cron»): correggere il docstring verso il cron e' proprio la riparazione; il pericolo e' correggere il CRON verso il docstring | si' | riscritta nel verso giusto |
| la banda dello stop era sbagliata: con isteresi 5% sullo stop (0,70·mark) lo stop siede a 0,6650,735 × mark, cioe' **33,5% / 26,5%**, non 26,3/33,3 | si' | corretta in docstring, CLAUDE.md, memoria |
| «BTC raddoppia gli scatti» leggeva male `r0823`: il «2 contro 1» e' rotolante contro pavimento **dentro la riga 4h**; a 1h il rotolante ne fa 0, e la riga peggiore misurata e' **24h** (ETH 5) | si' (la revisione ha eseguito `r0823.simula`) | riscritto ovunque coi conteggi; nel test `RIGA_4H_MIN`, non «peggiore»; `LTF_MIN` importato da `skyhook` (P1) |
| «girare piu' fitto non costa ordini» era falso: 22 dei 48 ordini live sono ri-taglie |Δ|≤$10 a segnale invariato; il deadband e' in valuta assoluta (C2) | si' (contati) | riscritto: «costa solo micro-ordini di ri-taglia», con la regola C2 |
| «latenza ≤1h» senza condizione: vale solo con la feed 5m fresca; `fresh_5m` ripiega in silenzio sul giornaliero e `skh_feed_max_age_min` allerta e basta | si' | condizione scritta |
| `_cron_installato` leggeva la **prima** riga: una seconda riga attiva sotto (`17 * * * *`, dentro lo slot di release) era invisibile | si' (simulato) | si contano **tutte** le righe attive, e devono essere una |
| lo skip collassava tre stati: crontab illeggibile, leggibile senza il progetto, leggibile col progetto ma senza `cron_book.sh` attivo (= libro non schedulato) | si' | tre stati: SALTATO, SALTATO, **ROSSO** |
| solo il vincolo 1 del `:47` era testato (minuto ≠ 00); il vincolo 2 (fuori dallo slot di release del martedi') no | si' | test con `venue_probe.in_release_window`, il :07 come controllo positivo |
| `_cron_dichiarato` accettava solo `N * * * *` e falliva come «assente» su `*/30`; `cadenza_minuti` accettava `*/45`, `0 */5`, `*/0` | si' | regex a 5 campi qualunque, regolarita' e passo > 0 imposti, parametrizzati |
| `r0823_sl_anchor.py` si rifiutava di girare nella finestra del **vecchio** `:07` e stampava a runtime che il docstring «prescrive ~230 min»; `cron_chain.sh` giustificava il :25 «fuori dal :07» | si' | guardia sul :47, prosa al passato, commento aggiornato |
+103
View File
@@ -0,0 +1,103 @@
# 2026-09-02 — Un flag sconosciuto non è l'azione di default
*Scritto fra le 15:45Z e le 16:05Z. Il difetto è stato misurato, non ipotizzato: la revisione del
codice di questa sessione ha eseguito `trades_db.py --help` come sonda di tempi e lo ha visto
sincronizzare il DB.*
## 1. Il difetto
Nessuno dei 18 script di `scripts/live/` usava argparse. Leggevano gli argomenti così:
```python
if "--reconcile" in args: ...
elif "--report" in args: ...
else: sync() # <- qui finiva QUALUNQUE cosa non riconosciuta
```
Un flag sbagliato non era un errore: era il ramo finale. Costo osservato e costi possibili:
| script | `--help` faceva | gravità |
|---|---|---|
| `trades_db.py` | `sync()`: riscriveva `meta.ultimo_sync` | osservato oggi, danno nullo |
| `journal.py` | scriveva la pagina di oggi e una riga nel DB | non osservato, ma scrive |
| `analista.py` | spendeva una chiamata al modello e mandava un Telegram | non osservato, ma spende |
## 2. La riparazione, e perché non è argparse
`src/live/cli.valida(nome, uso, flag=..., con_valore=...)`, chiamata come **prima istruzione** di
ogni blocco `__main__` — prima di `connect()`, prima del sync, prima della rete.
- `--help` / `-h` → stampa l'uso, esce **0**;
- flag non dichiarato, o valore mancante, o posizionale → stderr con **cosa** e **cosa si poteva**
(P4), esce **2** (errore d'uso, distinto dall'1 con cui gli script segnalano un esito negativo);
- tutto il resto invariato.
**Argparse no.** Cambierebbe messaggi d'errore, codici d'uscita e il comportamento di `--help` su
18 script che il cron già invoca: romperebbe ciò che gira per riparare ciò che non gira mai. Qui
serviva l'opposto — lasciare intatti i flag esistenti e rifiutare solo l'ignoto.
## 3. Il test guarda entrambe le metà del contratto
`tests/test_cli_flag.py`, 30 test:
- **l'elenco degli script si deriva dalla cartella** (P1): uno script nuovo che legge `sys.argv` a
mano e non chiama `valida` fa fallire il test, non passa inosservato perché nessuno ha aggiornato
una lista;
- `valida` dev'essere la **prima istruzione** di `__main__` (uscire dopo l'effetto non è uscire);
- l'uso deve **nominare** i flag che lo script accetta;
- **i flag che il cron usa davvero devono restare accettati** (P15/P16, letti dai `cron_*.sh`): una
guardia che ferma il libro di bordo la notte stessa sarebbe peggio del difetto;
- end-to-end sui tre script che scrivono: `--help` esce 0 e **non tocca `trades.db`**; un flag
ignoto esce 2 e non lo tocca (M15: il caso che ha causato il difetto, non un caso vicino).
Verificato a mano anche il contrario: `monitor_health.py --quiet`, `trades_db.py --sync --quiet`
e `book_execute.py` in dry-run escono 0 come prima.
## 4. Il resto della sessione
**Indice USDE orario** (debito §5.15, primo passo). Il classificatore dei movimenti di capitale
confronta due letture di equity a un'ora di distanza e il suo bound guarda solo BTC/ETH, mentre il
31% dell'equity è USDE: un depeg sarebbe scorporato come un prelievo. `usde_watch` registra
l'indice una volta al giorno — troppo rado. Ora `balance_watch` (orario, sola lettura) lo registra
a ogni campione, `None` con la ragione se non leggibile, mai 1,0. Il cablaggio nel bound viene
quando la serie ha storia: prima serve il dato, poi la regola.
**Pulizia lasciata dalla revisione.** `tests/helpers.carica_script` sostituisce la dodicesima copia
del caricatore `importlib` (15 in giro, con nomi di modulo diversi per lo stesso file e differenze
silenziose su `sys.modules`); il fill di prova si scrive con `upsert_fills` invece che con un
`INSERT` a mano che lasciava `verified` NULL, una forma che la produzione non produce; le
asserzioni sul testo del report non sono più ancorate al padding; `movimenti_capitale` accetta le
righe già lette, così il report non fa due SELECT sulla stessa tabella a un'ora del cron — due
letture ai due lati di una scrittura descriverebbero due istanti diversi.
## 5. Il README era fermo a quindici giorni prima del reset
Chiesto un aggiornamento della documentazione, ho aperto il README e ho trovato un documento del
**2026-06-04**. Il reset v2.0.0 è del **19 giugno**. Per 75 giorni la prima pagina del repository ha
descritto la libreria pre-reset come se fosse il presente:
| cosa diceva | stato vero |
|---|---|
| famiglie FADE / HONEST / PAIRS / TSMOM / SHAPE, strategie MR01…SH01 | archiviate in `Old/`, artefatto di feed contaminato |
| PORT06: Sharpe **7,84 / 10,06**, DD 2,60%, CAGR ~79% | numeri che `CLAUDE.md` §2 elenca sotto «non citare» |
| paper trader su `strategies.yml`, portafogli in `portfolios.yml` | **file inesistenti** |
| `scripts/waste/`, `scripts/portfolios/`, `src/live/multi_runner.py` | **inesistenti** |
| esecuzione shadow su Deribit **testnet** | il testnet è *la causa del reset*; oggi si esegue su mainnet con soldi veri |
Riscritto contro lo stato vero: cosa gira adesso, i numeri nella lente di §2 (TWR +10,6%, non la
crescita del conto), il metodo e i sei requisiti, la struttura verificata file per file, i gate con
le loro date, l'obiettivo con la sua onestà. Ogni percorso citato è stato controllato: esiste.
Da 423 righe a 152.
**La lezione che ho registrato non è «aggiornare il README».** È che un reset invalida anche i
documenti che nessuno rilegge, e l'inventario di cosa cita numeri morti va fatto il giorno del
reset — non 75 giorni dopo, per caso, mentre si fa altro.
## 6. Cosa NON ho fatto
- I loader `importlib` dei test sulla **ricerca** (moduli in `scripts/research/`) restano come
sono: sono a livello di modulo, con radici diverse, e toccarli non ripara niente.
- Il bound USDE nel classificatore: manca la storia, non il codice.
- Non ho cercato altri documenti fermi al pre-reset fuori da `README.md` e `docs/`: l'inventario
completo (ogni file che cita un numero morto) resta da fare, ed è la vera coda di questa scoperta.
+91
View File
@@ -0,0 +1,91 @@
# 2026-09-06 — USDE: dal 14,6% al 68,7%. Il tetto del venue del 30-31/08 non c'era piu'; il versamento di prova del 25/08 dichiarato
**Ore:** 20:56-21:16Z. **Autorizzazioni dell'operatore, in sessione:** *«usa piu' USDE se ne hai bisogno»*,
poi *«cerca % massima raggiungibile in USDE»*, sulla decisione gia' presa il 30/08 (quota bersaglio 70%,
`usde.quota_target`). E, a meta' sessione: *«i 25 euro erano un versamento di prova»*.
## 1. Il versamento del 04/09 aveva dimezzato la quota
Il versamento di **$2.410,14 del 04/09 14:47Z** (riconosciuto dal classificatore: mercato max 0,56% a leva
piena contro un salto del +116,8%) ha portato l'equity a **~$4.495** e la quota USDE da 31,3% a **14,6%**.
Nessun automatismo la riportava su.
## 2. Primo passo: al tetto di config (31,8%) — e la lettura SBAGLIATA che ne ho dato
`usde_convert.py --quota 0.318 --esegui`: 774 USDE in 8 ordini @ 1,0005 medio, zero rifiuti, quota 31,79%.
Ho scritto in CLAUDE.md «il tetto ha tenuto la stessa frazione a equity 2,2× maggiore: terza conferma».
**Falso**: nessun rifiuto fino al 31,8% dimostra che il tetto e' **≥** 31,8%, non che e' **=** 31,8%. Il
dato non conteneva la conferma; l'ho letta perche' me l'aspettavo. Corretto venti minuti dopo, quando
l'operatore ha chiesto la % massima e la sonda ha mostrato che la conferma non c'era.
## 3. La sonda: nessun tetto fino al 68,7%
`scripts/research/r0906_usde_tetto_sonda.py` — a saldo crescente, passi piccoli, stesso metodo dei diari
30-31/08 (un probe unico di taglia sbagliata produce un falso negativo): sale finche' il venue non rifiuta,
ripete il rifiuto dopo aver riletto il book (il messaggio `not_enough_funds_in_currency` e' lo stesso di un
limite che non incrocia), scende di passo a ogni doppio rifiuto, e conferma il muro a saldo neutro
(SELL 1 → BUY 1 ok → BUY 1 rifiutato). Guardie: prezzo dal book (ask+5 tick, tetto 1,0010), quota mai oltre
il 70% e **USDC mai sotto il cuscino di regolamento** derivato da config (`usde_convert.cuscino_richiesto_usd`).
| corsa | passi | da → a (USDE) | quota | rifiuti |
|---|---|---|---|---|
| 21:02-21:09 | 30 × 2 | 1.428,7 → 1.488,7 | 31,8% → 33,1% | **0** (fermata dal mio limite +60) |
| 21:11-21:15 | 16 × 100 | 1.488,7 → **3.088,7** | 33,1% → **68,7%** | **0** (fermata dal cuscino: +100 avrebbe superato il 70%) |
**Totale del giorno: 2.434 USDE in 39 ordini @ 1,0005, ~$1,2 di spread.** Conto finale: USDC **$1.407,40** +
USDE **3.088,72** = $4.496; cuscino richiesto $1.349 → slack **+$58**. Registro: `data/live/usde_convert.jsonl`
(due record `sonda_tetto`, con ogni ordine).
## 4. Cosa dice il risultato — e cosa non dice
1. **Il tetto del 30-31/08 era vero allora ed e' sparito ora.** Tre bracket riproducibili a 1 USDE (31,21-31,26% ·
31,29-31,34% · 31,84-31,88%) contro 2.434 USDE accettati oggi senza un rifiuto. **Non e' scalato, e'
sparito**: la frase del 31/08 «il 70% non e' raggiungibile ne' ora ne' mai» e' falsificata.
2. **Non spiegato** (D5). Ipotesi non verificabili da qui (il gateway non espone il transaction log ne' i
parametri del cap): un'allocazione per-conto del Cap ETHENA che varia col saldo aggregato dell'exchange;
un vincolo sui fondi depositati di recente (ma l'USDC del 04/09 e' stato convertibile a 2 giorni); un
cambio di policy. **Il tetto puo' tornare**: quando tornera', bloccherebbe gli ACQUISTI, mai le vendite
(tutte le SELL sono sempre passate). `venue_cap_frac`**null** con la misura in `venue_cap_misurato`;
`usde_convert` si affida al chunk+backoff sui rifiuti, che e' cio' che ha sempre fatto.
3. **Il limite che morde e' NOSTRO: il cuscino di regolamento.** P&L e funding dei perp si regolano in USDC;
a 68,7% restano $58 sopra il cuscino. ⚠️ **Nessuno sorveglia il cuscino**: `usde_watch` allerta sulla
quota (`quota_max_frac`, riportata a **0,85**, il valore che l'operatore aveva scelto il 30/08 «in previsione
della quota al 70%»), non sull'USDC. Una perdita del libro fa salire la quota da sola: a $58 il cuscino e'
scoperto, e Deribit finanzia un saldo USDC negativo allo 0,05%/giorno (18%/anno). Debito nuovo, §5.17.
4. **Rischio emittente**: a 24,2% l'evento Ethena valeva 3,1× il maxDD del libro (30/08); a 68,7% vale **~8,8×**.
E' la decisione del 30/08 presa con l'informazione completa (r0830_usde_quota), oggi eseguita. Resa attesa
~4,1% × $3.089 ≈ **$127/anno** (era $26): ancora meno di un mese di versamento.
## 5. Il versamento di prova del 25/08: dichiarato, scorporato, e il TWR scende di 4 punti
Trovato stamattina per differenza (equity 07:07→08:07 **+$21,11** con le posizioni a **$3,3/4,3**) e
confermato dall'operatore: **€25 di prova** prima del bonifico da $1.399,39 delle 11:47. Il rilevatore non
poteva vederlo (+3,3% contro soglia 10%) e abbassare la soglia farebbe di ogni ora di mercato un candidato:
**l'unica fonte che sa l'importo e' l'operatore**. Costruito il canale:
- `data/live/movimenti_dichiarati.jsonl` (append-only, nel perimetro di backup — P11), letto da
`journal.movimenti_dichiarati()`; `movimenti_capitale` lo fonde coi rilevati: fonte `dichiarato`, importo
dell'operatore (non il salto), `dopo = prima + importo` cosi' il mercato dell'ora **resta** nel rendimento;
se coincide con un salto rilevato l'importo dichiarato vince (`dichiarato+rilevato`); fuori dalle letture →
`avvisi`, non applicato; riga rotta → `avvisi`, le altre restano (P3).
- Importo registrato **$24,90 ± 0,50**: stimato per differenza (salto +21,11, mercato 3,3/4,3 dal log del
book, fee 0,03) — il gateway non espone `get_deposits`, quindi il numero USDC esatto non e' leggibile; la
banda sta nel record e nel report (P12).
- `tests/test_journal.py`: +6 (scorporo sotto soglia · dichiarato+rilevato · fuori letture · riga rotta ·
senza file niente cambia · la nota di giornale lo dice); `conftest` isola il file nei test.
| | prima (20:47Z) | dopo (21:14Z) |
|---|---|---|
| movimenti certi | $3.809,53 | **$3.834,43** |
| trading da arming | +$87,35 | **+$62,45** |
| TWR | +11,98% | **+7,83%** = +8,14% × 0,62% × 0,12% × +0,46% |
**Un terzo del «trading» era un bonifico da 25 euro.** Regola nuova (CLAUDE.md §2): ogni versamento, anche
di prova, si dichiara nel file il giorno stesso.
## 6. File toccati
`config/live.json` (usde: venue_cap_frac null, quota_max_frac 0.85, nota) · `src/live/journal.py` ·
`scripts/live/trades_db.py` · `tests/conftest.py` · `tests/test_journal.py` (+6) ·
`scripts/research/r0906_usde_tetto_sonda.py` (nuovo) · `data/live/movimenti_dichiarati.jsonl` (nuovo) ·
`CLAUDE.md` §1 §2 §4 §5.
@@ -0,0 +1,219 @@
# 2026-09-09 — Guadagnare nei crolli: revisione dei sistemi, il crollo catturato, piu' monete su Deribit
**Richiesta dell'operatore:** *"fai analisi e revisione dei sistemi adottati e trova soluzione per
guadagnare anche nei crolli (usa le opzioni se serve), anche + monete ma sempre in deribit"*.
Analisi e revisione col modello `fable` (revisore fresco, agente separato), test con Opus.
**Esito in una riga:** nel **giorno** del crollo il libro live **perde** (0,48%/g, positivo nel 18%
dei giorni ≤ 5%); per **finestra** e' immune-o-positivo e **dal 2022 chiude in utile 8/8 peggiori
finestre**, tutte per la gamba SHORT di SKH01 marcata all'uscita; la sola leva che aumenterebbe quel
guadagno e' il peso di SKH01, che il gate del progetto boccia su una condizione in-sample; le
opzioni, misurate per la prima volta su un **crollo catturato a quote vere**, danno un rapporto di
credito identico al rally (f_net 0,74 vs 0,714) — **non** il `f` di stress che la regola del 19/06
aspettava (crollo a vol bassa, gate di VRP01 chiuso, famiglia inverse); XRP, l'unica "altra
moneta" liquida e non misurata di Deribit, **diluisce** esattamente come SOL. **Libro, pesi,
config, cron INVARIATI. Nessun ordine.**
## 0. Prima di misurare: cosa era gia' morto (memoria letta, non riaperta)
| meccanismo "guadagnare nei crolli" | esito | perche' e' morto (chi riapre deve battere questo) |
|---|---|---|
| gamba short del trend a 1d (§42) | SCARTATO | 1,91%/anno di drift in 0/24 ancore; il 2022 lo tiene a galla a meta' |
| put deep-OTM statica (§46) | REFUTATO 0/162 | beta del libro +0,076: non si assicura un libro gia' piatto |
| collar ≤15g (§71) · pavimento come licenza (§72) | REFUTATI | il DD lo riduce il TETTO (troncatura, C9); la put sola peggiora il DD 16/16 |
| gamma scalping long-vol | SCARTATO | paga il VRP ogni anno, ogni frequenza |
| DVOL direzionale · gate macro · skew de-risk | HEDGE / RIDONDANTI | TP01 e' gia' flat nei crolli (Δ 0,00) |
| SOL terza gamba (22/08) | ESCLUSO dall'operatore | hold-out 0,166 in 0/24; un anno solo (2023) |
| XS01 su Deribit (§50) | MORTO per ampiezza | 11 gambe con ≥1 anno: 1,265 → 0,116 |
Condizione di riapertura scritta tre volte (§3 short-vol, §71, §72): **un `f` di stress reale su
un crash catturato**. E' la cosa che oggi si poteva misurare e non era stata misurata.
## 1. Il libro nei crolli — `r0909_libro_nei_crolli.py`
Lente: sleeve di ricerca, ancora canonica, fee 0,10% RT, funding fuori; pesi importati da
`src/live/book` (P1). ⚠️ **SKH01 e' marcato all'USCITA del trade** (`harness.backtest_signals`):
la serie giornaliera e' zero nell'87,7% dei giorni, e uno short aperto nella finestra e chiuso
fuori e' accreditato fuori — per questo la tabella per GIORNO mostra SKH01 ≈ 0 e quella per
FINESTRA il guadagno. Indice 50/50 BTC/ETH; giorno di crollo ≤ 5%; episodio = 12 peggiori
finestre di 20 giorni non sovrapposte + 9 episodi nominati prima. Classi ESCLUSIVE nell'ordine
dichiarato: immune (|libro| < |idx|/10, col segno) → POSITIVO → PERDE.
| misura | valore |
|---|---|
| beta del libro all'indice | **+0,0769 sulla finestra di §46** (2021-03-24+; memoria +0,076: riprodotto al millesimo) · +0,086 su tutta la storia · +0,002 nei giorni ≤ 3% |
| 160 giorni ≤ 5% (indice 7,76%/g) | libro **0,48%/g**: TP01 0,63, SKH01 +0,15; **libro > 0 nel 18%** dei giorni; somme per anno: 2024 11,9%, 2025 7,3%, 2022 +8,2%, 2026 +4,7% |
| 24 giorni ≤ 10% (indice 14,4%/g) | libro 0,51%/g: TP01 0,93, SKH01 **+0,42** |
| 12 peggiori finestre 20g | **4 POSITIVO / 6 immune (4 col segno +) / 2 PERDE**; libro > 0 in 8/12; **dal 2022 in 8/8** (mediana +3,3% contro indice 29%); le 2 perdite sono 2019 e maggio 2021 (TP01 ancora long: il trend non si era girato) |
| 9 episodi nominati | libro > 0 in LUNA (+4,3%), giugno 2022 (+3,1%), FTX (+0,7%), agosto 2024 (+0,4%), 5/02/2026 (+4,4%), 1-5/06/2026 (+3,3%); COVID 0,04%, feb 2025 1,5%, maggio 2021 3,4% |
| da dove viene il guadagno | la gamba **SHORT di SKH01 = 108%** dei guadagni negli episodi con libro > 0 (scomposizione ESATTA: long + short == intera su 2735/2735 giorni, verificato con assert; TP01 contribuisce in negativo); per anno al peso 0,25: 2022 **+10,7%**, 2026 **+7,7%** |
| peggior giorno del libro (lente canonica) | 2025-10-10 3,38%, tutto TP01 con esposizione **0,505**, indice 9,7%: **e' un giorno di crollo con TP01 mezzo long** (lo squeeze di §33 sta sulla lente hourly e qui non si riproduce: dichiarato nel docstring) |
| riga informativa 37,5/62,5 (§26) | batte il 75/25 in 12/12 episodi, **ma** Sharpe 1,81 vs 1,82 e maxDD 12,1% vs 9,4%: paga i crolli con gli squeeze |
🚨 **Errore catturato in revisione (fable):** la prima stesura provava "GUADAGNA" prima di
"immune" e stampava **8/12 GUADAGNA**; con le classi nell'ordine dichiarato sono **4 POSITIVO +
6 immune**. *L'ordine delle regole era il verdetto.* Ora il verdetto e' tre affermazioni separate
(giorno / finestra / provenienza) e il conteggio nudo libro > 0 e' stampato accanto.
📌 **Il meccanismo "guadagnare nei crolli" esiste ed e' in produzione: e' SKH01 short** — ma
guadagna **dopo** il giorno del crollo (marcato all'uscita), non dentro. Nel senso stretto della
domanda dell'operatore il libro **perde nel giorno** e recupera nella finestra. Le due perdite
storiche per finestra sono di TP01 nei crolli che arrivano *dentro* un trend rialzista (2019,
2021): e' il costo del long-flat (§42 ha misurato che la short del trend lo aggrava).
**La leva che aumenterebbe il guadagno e' il peso di SKH01, e il gate la boccia:**
`r0726_reeval_live_weight.py` rilanciato: argmax 0,35, plateau [0,25-0,50] (il live e' dentro),
`weights_tilt_null` 0,25→0,35: `delta_insample 0,0026`, `pctl_hold 31,2`, **`gate_pass False`**.
⚠️ Precisazione della revisione: lo script legge la cache `data/_cache/skh_sigtab/*` **ferma al
2026-07-25** — sono le stesse cifre del diario 26/07, non un ri-esame coi segnali di oggi. La
condizione che fallisce e' pero' **in-sample (pre-2025)**, che 46 giorni di dati nuovi non possono
cambiare: la decisione 75/25 resta vincolante e la sua condizione di riapertura resta non
soddisfatta, ma la data da citare e' il 26/07.
## 2. Il crollo catturato — `r0909_crash_catturato.py`
**Fatto nuovo:** `bite_archive.parquet` copre con bid E ask il crollo del **1-5 giugno 2026**
(BTC 25% / ETH 30% dal 31/05 al minimo; ETH 11,1% il 05/06) su una scadenza (19/06, 14-21
DTE). Il campione del 22/08 (§11) partiva **dopo**, dal fondo. Limiti dichiarati prima: n = 1
crollo, 1 scadenza, tenor 14-21 g (VRP01 e' a 7), spot dal feed 1h certificato, regolamento allo
spot dell'ora di scadenza, **famiglia INVERSE** (premi in ETH: VRP01 opererebbe sulla USDC-lineare,
che il conto margina e che NON e' nell'archivio). Ancora d'ingresso = ogni snapshot 30/05-04/06;
mediana e banda.
⚠️ **Non e' un crollo a vol alta:** DVOL pre 37 (BTC) / 49 (ETH) all'IV-rank 0,05 / 0,10; il
**picco** del crollo (49 / 69) sta al **29° / 46° percentile storico** — sotto la mediana. Il gate
IV-rank ≥ 0,30 di VRP01 e' **chiuso in tutte le ore** e il modello del sleeve segna 0,0 su tutte le
settimane di giugno: VRP01 non sarebbe stato a mercato.
| ETH, ingressi PRE-crollo (286 snapshot = **4 strutture** di strike, δ 0,27/0,09, larghezza 200) | reale | modello (BS su DVOL) | f |
|---|---|---|---|
| credito del put credit spread | | | **0,74** [0,65-0,82] — 22/08 in rally: 0,714 |
| P&L a scadenza per unita' | **$174** | $168 | 1,04 — **tautologico nell'83%** dei trade: entrambe le gambe ITM ⇒ payoff = larghezza per reale e modello, resta solo f_net |
| perdita sul rischio (fee incluse) | 101% [65-102] | | = perdita **MASSIMA** dello spread (+ fee); 65% per la 1900/1600 |
| peggior MTM, chiusura **tagliata alla larghezza** | **$172** | $161 | **1,08** [1,07-1,31]; a quell'ora IV corta 68 = DVOL 68 |
| put δ−0,10 comprata all'ask: costo | | | **1,92×** il modello (§46 a δ−0,10 su BTC_USDC: 1,27 — punto NUOVO e piu' alto, ETH inverse) |
| put δ−0,10 a scadenza (ITM di $4: K 1700, ST 1694) | $6 | ≈ $0 | 0,96 sul solo sottoinsieme con payoff modellato ≥ $5 |
| put δ−0,10 al picco del percorso (look-ahead, limite sup.) | +$163 | +$173 | 0,94 |
| debit spread 0,28/0,10 a scadenza | +$169 | +$163 | 1,04 (stessa tautologia, specchio) |
| ingressi DURANTE il crollo (02-04/06, 283 snapshot) | mediana +$5, **perde nel 46%**, p10 $133 / p90 +$26 | | bimodale: la mediana nasconde le perdite |
BTC: **non misurabile pre-crollo** — la griglia di strike dell'archivio il 30/05-01/06 non
contiene la δ−0,10 (78 snapshot fuori geometria, 0 in geometria prima del 02/06). Durante:
247 snapshot, f_net 0,79, perde nel 37% (p10 $1.718 / p90 +$818 su larghezza 5000).
🚨 **Tre correzioni della revisione, applicate:**
1. *"Il percorso costa 2,6× il modello (f_mtm 2,58), la IV della gamba corta e' esplosa a 108 per lo
skew"* — **artefatto di quote.** Il costo di chiusura `(ask corta bid lunga)` superava la
**larghezza** dello spread (una quota 0,208/0,376 ETH su un intrinseco di $296: 158% di spread
relativo, a T→0) e `min()` pescava li'; uno spread a rischio definito non costa mai piu' della
larghezza per chiudere. Tagliato: **f_mtm 1,08**. E la IV 108 e' del **18/06 alle 17:00** (deep
ITM, T→0), non del crollo: all'ora del peggior MTM la IV e' 68 = DVOL.
2. *"f a scadenza 1,04: il modello prezza bene la perdita"***tautologia**: con entrambe le gambe
ITM il payoff e' la larghezza per entrambi; f_exp e' una funzione di f_net. E la "banda su 286
snapshot" sono 4 strutture (N_eff = 1 crollo × 4).
3. `S.asof(ts)` sul feed 1h **guardava un'ora avanti**: la barra e' etichettata all'apertura (la
20:00 chiude col 5m delle 20:55). Corretto con `spot_causale` (indice +1h). ⚠️ **`cblib.spot_series`
ha lo stesso difetto** → il f 0,714 di §11 e ogni numero di `cblib` con `asof` sullo spot; qui
innocuo (ambo le gambe ITM per ogni ST < 1700) e nel MTM giocava contro l'autore. **Debito nuovo,
registrato in CLAUDE.md §5.** Non toccato: cambierebbe numeri pubblicati.
📌 **Cosa dice davvero il crollo catturato (n = 1):** il **rapporto di credito reale/modello non
cambia col regime** (0,74 nel crollo contro 0,714 nel rally); la put comprata paga nel crollo
cio' che il modello dice (0,94-1,04) ma **costa 1,9× il modello all'ingresso** — il bleed e' dove
il modello sbaglia, il payoff no (e' il motivo di §46/§72 visto dal lato del sinistro); il gate di
VRP01 lo ha tenuto fuori. **La condizione di riapertura di §3 e' soddisfatta nella forma** (un
archivio bite con bid/ask su un crollo) **e non nella sostanza**: crollo a vol bassa, sotto il gate,
tenor 14-21, famiglia inverse, e l'unico f non tautologico (MTM) vale 1,08 su 4 strutture. **Non si
riapre.** Primo punto di una distribuzione che `collect_chain` riempira' da solo; il secondo punto
utile e' un crollo **con IV-rank > 0,30**.
## 3. Piu' monete, sempre in Deribit — `r0909_deribit_universo.py` · `r0909_xrp_terza_gamba.py`
**Il venue letto oggi (15:05Z):** 32 perpetual USDC-lineari aperti (+6 non aperti, fra cui **APT
inactive**: e' il mancante rispetto al 23/08). Volume 24h ≥ $10M solo per **XRP ($38M, il piu'
scambiato del venue, spread 4 bps, listato 2022-03-16), ETH, BTC, SOL ($13M)**; tutto il resto fra
$0,01M e $7M (TAO, HYPE, ADA, AVAX, NEAR…) con spread 2-20 bps. XS01: **13/19** gambe — mancano
ARB, OP, APT, INJ, TIA, SEI → §50 resta: morto per ampiezza. Opzioni USDC-lineari su 7 sottostanti
(SOL 676, HYPE 442, XRP 442, TRX, AVAX) con la liquidita' misurata il 22/08 (SOL: 9% delle put a
due lati). Trovati dalla revisione, registrati perche' il controllo costa €0 e nessuno li riapra:
**`BTCDVOL_USDC-30SEP26`** (l'unico strumento long-vol diretto del venue: volume 0, OI 132, bid/ask
39,1/43,9 = spread 12% → non negoziabile, e in contango sanguina come la put) e **PAXG perpetual**
(oro, dal 2024-12: $0,18M/g, OI $8,3M — non misurato, non morto, stessi muri di SOL/XRP: 9 mesi di
storia contro i 3 anni della decisione 22/08).
**L'unica "altra moneta" liquida su Deribit mai misurata come gamba direzionale e' XRP.** Misurata
col harness di `r0822_sol_leg` (TP01 e SKH01 **congelati**, 24 ancore, differenze appaiate,
de-levering, per anno), ipotesi a priori dichiarata: diluisce.
Certificazione (Coinbase dal rilisting 2023-07, Bitstamp prima — Coinbase aveva delistato XRP per
la causa SEC): >1% dal riferimento 0,06% (2022) · **0,57% (2023)** · 0,01-0,02% (2024+); mediana
5-6 bps. ⇒ **L-PULITA dal 2024** (stessa soglia e stesso anno sporco di SOL). ⚠️ Barre 5m flat
**27-61%** (BTC/ETH 2021+: 0,1-2%; SOL 21%): il 5m su cui gira SKH01 e' 20-100× piu' flat.
| | L-FULL (2022-03+) | L-PULITA (2024+) |
|---|---|---|
| dSharpe FULL (mediana, 24 ancore) | **0,206** [0,26, 0,16], >0 in 0% | 0,066, >0 in 12% |
| dSharpe hold-out | **0,169** [0,32, 0,15], **>0 in 0%** | 0,169, 0% |
| dCAGR | 2,27 pp, 0% | 1,32 pp, 4% |
| null del de-levering | Sh 3g 1,23 vs 2g×0,974 **1,44** → de-levering | 1,54 vs 1,53 → pari |
| per anno (dSh) | 2022 0,55 · 2023 0,66 · **2024 +0,54** · 2025 0,44 · 2026 0,25 → **1/5** | |
| corr(gamba XRP, book) | +0,35 / +0,37 (SOL 0,40-0,56) | |
**XRP DILUISCE** — lo stesso 0,17 di hold-out di SOL, in 0/24 ancore, con **un anno buono** (2024).
TP01 XRP da solo: Sharpe 0,01, hold-out 0,49; SKH01 XRP maxDD 40,1% (il criterio di selezione di
SKH01-V2-DD, maxDD<30%, fallisce anche qui, come SOL). Eseguibilita' non e' il vincolo (min 1 XRP =
$1,42). Non misurato: **SKH01 solo-short su una terza moneta** (la domanda riguarda i crolli, dove
paga lo short) — ma il filtro di direzione e' una famiglia nuova (M20) e il 5m di XRP e' peggio di
SOL: il muro e' lo stesso. **Nessuna proposta.** Il dato vive in `data/raw/alt_xrp_*` (namespace di
ricerca, non rinfrescato dal cron, `load_data("XRP")` fallisce; `tests/test_sol_leg.py` lo presidia
via `DERIBIT_INSTR`). La cache del riferimento in `data/_cache` e' gitignored e fuori backup: la
certificazione si riproduce **con rete** (N11 vale con rete).
## 4. Cosa cambia una decisione — niente; cosa si registra
| voce | prima | oggi |
|---|---|---|
| il libro nei crolli | "immune, beta 0,076" | perde nel GIORNO (0,48%/g, 18% positivi), immune-o-positivo per finestra, **> 0 in 8/8 dal 2022** per SKH01 short marcato all'uscita; beta 0,0769 riprodotto |
| f di stress delle opzioni | non misurato ("serve un crash catturato") | crollo catturato **a vol bassa, sotto il gate, inverse**: f_net 0,74 = rally; f_mtm 1,08; ali 1,92×. **Condizione non soddisfatta nella sostanza** |
| peso SKH01 | gate fallisce (26/07) | invariato: la condizione che fallisce e' in-sample; cache segnali ferma al 25/07 |
| XRP | mai misurato | diluisce come SOL (0,169 hold-out, 0/24) |
| universo Deribit | 14/19 (23/08) | 13/19 (APT inactive); liquidi solo BTC/ETH/XRP/SOL; BTCDVOL future non negoziabile; PAXG non misurato |
| debito nuovo | — | `cblib.spot_series` + `asof` guarda un'ora avanti (feed 1h etichettato all'apertura) |
**Cosa riaprirebbe qualcosa:** (a) un secondo crollo catturato **a IV-rank > 0,30** (VRP01 a
mercato), sulla famiglia USDC-lineare, per il f di stress; (b) `weights_tilt_null` che passa — si
rigenera `skh_sigtab` e si rilancia `r0726_reeval_live_weight`, non si ridiscute; (c) per le
"altre monete", un meccanismo che non sia TP01/SKH01 congelati, o 3 anni di dato 5m liquido.
## 5. Errori catturati in sessione (prima della revisione)
- Percorso ora per ora in un ciclo `groupby` per ancora: >10 minuti; riscritto su una tabella
ora×strumento (`Quote`), ~3 minuti.
- BTC "in geometria" senza guardia: la griglia rada faceva scegliere a `pick_legs` una δ−0,02 al
posto della δ−0,10 (K72000/55000). Guardia `TOL_D` (±0,08/±0,06), scritta dopo aver visto BTC ma
che per ETH scarta 2/288 e non cambia nessun verdetto (verificato dal revisore).
- Coinbase restituiva un batch vuoto per XRP 2022 e il ciclo si fermava: certificazione vuota.
Riscritto con avanzamento su batch vuoto e riferimento Bitstamp dove Coinbase manca.
## 6. Revisione (`fable`, agente fresco) — 21 segnalazioni, tutte verificate
Applicate: #1 artefatto del MTM (taglio alla larghezza; IV 108 e' a T→0) · #2 f_exp tautologico
(quota stampata, N_eff = strutture) · #3 "101%" = perdita massima + fee · #4 `asof` un'ora avanti
(`spot_causale`; debito su `cblib`) · #5 famiglia inverse dichiarata · #8 "durante" bimodale (p10/p90,
quota che perde) · #9 ordine delle classi (immune → POSITIVO → PERDE, tre affermazioni nel verdetto)
· #10 peggior giorno = crollo con TP01 0,505, docstring (c) corretto · #11 beta sulla finestra di §46
(0,0769, tolleranza 0,01) · #12 scomposizione esatta con assert · #13 lente "marcata all'uscita"
dichiarata · #14 confronto bit-exact con `_skyhook_returns` · #15 cache `skh_sigtab` ferma al 25/07
· #16 1,92 e' un punto nuovo, non "gia' noto" · #17 il crollo e' sotto la mediana del DVOL: verdetto
riscritto · #18-19 BTCDVOL future e PAXG registrati · #21 APT inactive, cache con rete.
Non applicate: nessuna. Il revisore ha trovato **due conclusioni che non reggevano** (il 2,6× e
l'"8/12 guadagna") e le ha smontate con numeri riprodotti: la regola *"chi rivede non e' chi ha
scritto"* ha pagato in una sessione.
**Test (Opus, agente separato): `tests/test_r0909_*.py`, 92 test, suite intera 1008/1008 verdi**
(erano 916). Coprono: ordine delle classi e riproduzione verbatim dei verdetti, `spot_causale` col
controllo positivo sul difetto che ripara, taglio alla larghezza, geometria `in_geom` derivata da
`VRP_CFG`, regola L-PULITA (anno pulito isolato ignorato), `_fetch_1h` su batch vuoti, P1 per
identita' (`W_TP01`, `XS_UNIVERSE`), zero rete e zero scritture in `data/`. **Due difetti trovati da
Opus nel verdetto di XRP, corretti:** un campione vuoto stampava "DILUISCE" (ora "NON MISURABILE",
P5) e il criterio per anno era cablato al 2024 invece che derivato dall'anno della lente (P1).
@@ -0,0 +1,182 @@
# 2026-09-09b — Debito §5.18 riparato: `cblib` causale (spot +1h, DVOL +1 giorno) e §11 rimisurato
**Richiesta dell'operatore:** *"ripara il debito §5.18 su cblib e rimisura §11"*. Revisione col modello
`fable` (agente fresco), come da regola.
**Esito in una riga:** riparato in `cblib` — spot e DVOL escono **gia' causali** — e §11 rimisurato allo
stesso taglio: **0,714 [0,690-0,779] → 0,712 [0,664-0,732]** (n=19). Il numero non si muove perche' le due
correzioni hanno **segno opposto**; **nessun verdetto cambia**; riparando la prima serie ne e' uscita una
**seconda con lo stesso difetto** (il DVOL giornaliero, fino a 24 ore avanti); il DVOL orario resta un
passo aperto **con costo misurato**. Libro, config, cron: invariati. Nessun ordine.
## 0. Il difetto, verificato prima di ripararlo (D6: si controlla col lag, non si assume)
| serie | come e' etichettata | prova | look-ahead di `asof(ts)` |
|---|---|---|---|
| feed 1h certificato | all'**apertura**: la barra T contiene la chiusura di T+1h | chiusura 1h a T == chiusura 5m a **T+55** nel **100%** delle barre (BTC ed ETH, 1-5/06); a T5 nell'1,7% / 0% | **+1 ora** |
| `dvol_{asset}.parquet` (giornaliero, `fetch_dvol`, `resolution=86400`) | al giorno D contiene la **chiusura delle 23:00 di D** | API pubblica a risoluzione 1h, 1-4/06: `close` 1D del giorno D == `close` 1h delle 23:00 di D, e `open` 1D di D+1 == `close` di D | **fino a +24 ore** |
La seconda riga non era nel debito: e' uscita controllando la convenzione dell'altra serie di
contesto che `cblib` legge con lo stesso `asof`. Il sleeve (`_vrp_weekly_asset`) **non ha il difetto**:
lavora a cadenza giornaliera e accosta la chiusura del giorno D al DVOL del giorno D, che a quella
cadenza e' coerente. Il difetto e' solo di chi entra **a un'ora** con una serie giornaliera.
## 1. La riparazione
`cblib.causale(s, cadenza)` sposta le **etichette** (mai i valori) dall'apertura all'istante in cui la
chiusura e' nota. `spot_series``causale(·, "1h")`, `dvol_series``causale(·, "1D")`. Conseguenze:
- `asof(ts)` = ultima chiusura **conosciuta** a ts (prima della prima chiusura nota: NaN, non un valore).
- il regolamento `ST = S.asof(exp)` alle 08:00 e' ora la chiusura della barra 07:00, cioe' **il prezzo
delle 08:00** — prima era la chiusura della barra 08:00, cioe' il prezzo delle **09:00**, un'ora dopo la
scadenza. Deribit regola su una TWAP dei 30 minuti prima delle 08:00: l'approssimazione e' ora dalla
parte giusta dell'ora.
- `r0909_crash_catturato` **non ri-sposta** (lo `spot_causale` locale della mattina e' rimosso: applicato
due volte darebbe la chiusura di **due** ore prima — lo stesso errore col segno opposto). Test di
non-regressione sul sorgente.
- chi usa `cblib`: `r0822_vrp_real_quotes` (§11), `r0822_skew` (§5), `r0909` (§75), **`vrp_f_watch`**
(cron giornaliero, `data/vrp_f_watch/state.json`), e — trovati dal revisore, non da me —
**`r0730_vrp_real_quotes`** (il f 0,73 [0,706-0,805] di CLAUDE.md §2: rigirato stampa **0,71
[0,680-0,756]**, n=27, 0/27 ≥ 1, f gamba lunga 1,66 → 1,49; verdetto invariato, numero dichiarato in §2)
e **`r0901_btc_collar`** (join del DVOL **per giorno**: con la serie causale avrebbe accostato l'IV di D
al DVOL di D1 — riallineato con `causale(·, "-1D")`, bit-exact a prima, §71 non si muove).
`options_vrp_calibrate.py` ha una **copia propria** di `spot_series` (numeri del 20/06): **non toccato**,
dichiarato.
- la sezione di REGIME di `r0822` (date di calendario: «ultimo giorno aperto», IV-rank per anno) e la
tabella di contesto di `r0909` usano `causale(V, "-1D")`: li' serve il DVOL **del** giorno, non
l'etichetta causale — altrimenti ogni data stampata slitta di un giorno (trovato dal revisore).
- `r0822_vrp_real_quotes` ha ora il flag `--al <data>` (taglio delle scadenze) per riprodurre una misura
a una data: senza, "riprodurre il 22/08" era impossibile perche' la catena cresce ogni ora.
## 2. Prima di pubblicare il numero nuovo, la macchina ha riprodotto quello vecchio (M23)
Worktree di HEAD (`git worktree add`, `data/raw` in symlink), stesso script con `--al 2026-08-22`:
**pooled 0,714 (IC95 [0,690, 0,779], n=19)** — al millesimo, IC compreso. Poi lo stesso comando sul
codice riparato.
## 3. Scomposizione: due correzioni di segno opposto (M26) e un controllo orario
| taglio | spot | DVOL | pooled f | IC95 | n |
|---|---|---|---|---|---|
| 22/08 | grezzo | grezzo (com'era) | **0,714** | [0,690, 0,779] | 19 |
| 22/08 | causale | grezzo | 0,721 | [0,685, 0,758] | 19 |
| 22/08 | grezzo | causale (giornaliero) | 0,693 | [0,670, 0,748] | 19 |
| 22/08 | causale | causale (giornaliero) = **`cblib` oggi** | **0,712** | [0,664, 0,732] | 19 |
| 22/08 | causale | causale **orario** (controllo, API pubblica) | 0,717 | [0,667, 0,732] | 19 |
| 09/09 | grezzo | grezzo | 0,720 | [0,717, 0,952] | 23 |
| 09/09 | causale | causale (giornaliero) | **0,712** | [0,677, 0,741] | 23 |
| 09/09 | causale | causale orario | 0,726 | [0,680, 0,738] | 23 |
Il DVOL di fine giornata **alzava** f: nel rally la vol scende dentro il giorno, la chiusura di D sta
**sotto** quella di D1 (+1,05 pt di vol mediana su 19/19 trade, misura del revisore) → denominatore piu'
basso → f piu' alto; causale: 0,021. Lo spot di un'ora dopo, invece, **non e' un meccanismo ma rumore**:
lo scarto per trade ha segno misto (mediana 0,02% BTC / 0,11% ETH, banda 0,6…+0,9%) e sposta f fino a
**±0,15 per trade** (lo spread δ−0,28/0,10 ha delta netto ~0,18: 0,5% di spot vale 15-20% del credito
modellato); il +0,007 netto e' la somma di 19 segni. ⚠️ Corollario: lo spot causale resta **stantio fino
a 25 minuti** (snapshot al :25, chiusura nota al :00), cioe' ±0,05-0,10 di f per osservazione; il feed
**5m certificato** lo porterebbe a ≤5 minuti. Non fatto: dichiarato in §6. Il DVOL giornaliero causale e' **stantio fino a
23 ore** (agli ingressi delle 08:00: 8 ore): scarto mediano dall'orario **0,2 punti di vol**, max 1,1
(22/08) / 3,0 (oggi). Su §11 vale 0,005 di f.
## 4. Cosa cambia in §11 (taglio 22/08, stesso campione)
| grandezza | prima | dopo | perche' |
|---|---|---|---|
| f pooled | 0,714 [0,690-0,779] | **0,712 [0,664-0,732]** | §3 |
| f BTC / ETH | 0,706 / 0,715 | 0,720 / 0,692 | |
| Sharpe canonico BTC (titolo, non citabile) | 32,58 | 29,61 | regolamento alle 08:00 |
| **mediana onesta** BTC (9 ancore) | 1,45 | **2,12** | banda [0,19, 29,61] (era [0,35, 32,58]) |
| Sharpe canonico ETH → mediana onesta | 3,90 → 2,41 | 7,64 → 3,35 | una settimana ETH cambia taglia: 26/06 27,38 → 12,84 $ (BTC: 31/07 19,88 → 31,69, 14/08 12,94 → 8,68) |
| media BTC fra le ancore | 1,35% → +11,22% | 0,66% → +11,44% | **cambia ancora segno**; sd 11,1× → 9,2× |
| forfait fee vs listino | 1,44× / 1,80× | 1,51× / 1,87× | credito modellato a DVOL causale |
| peggior mark: mediana / minimo / minimo fra le vincenti | 6,4% / 91,0% / 56,9% | 6,4% / 91,5% / 55,2% | |
| P&L somma 19 settimane | $2.733 | $2.822 (+3,3%) | tutto dal regolamento |
| sottostante nel campione | +25,1% / +44,2% | +21,1% / +40,1% | il vecchio regolamento del 21/08 era il prezzo delle **09:00**, che sta **+3,9%** sopra quello delle 08:00 (76.311 → 79.316): un numero a due estremi pesa un'ora intera. (La prima stesura diceva «−1,8%»: avevo letto l'ora dopo, 09:00 → 10:00. Corretto dal revisore) |
**Verdetto di §11: SCARTATO, per le stesse quattro ragioni** (il campione non contiene la strategia; il
titolo non sopravvive all'ora d'ingresso; la famiglia con storia non e' quella eseguibile; lo spread e'
il costo dominante). Nessuna delle quattro dipende da un'ora di spot. Il testo del verdetto nello script
riporta i numeri nuovi con i vecchi accanto.
## 5. Effetti collaterali, misurati (tutto cio' che legge `cblib`)
**§75, crollo catturato** (`r0909`, rigirato): ETH pre-crollo f_net **0,74 [0,65-0,82] → 0,76
[0,71-0,86]**; peggior MTM 1,08 [1,07-1,31] → 1,08 [1,06-1,27]; put δ−0,10 1,92× → 1,98×; al picco 0,94 →
0,99; BTC durante **0,79 → 0,89 [0,74-1,05]**, ETH durante 0,71 → 0,78. Con il DVOL **orario** BTC durante fa **0,83**: nel crollo
il DVOL sale di 5-6 punti al giorno, quindi il giornaliero di fine giornata (com'era) sottostima f e
quello del giorno prima (causale) lo sovrastima — **0,04/+0,06 di f e' il prezzo della stantiezza in un
crollo**, ed e' l'argomento per il feed orario. DVOL "pre" 37,3 → **36,4** (BTC): il 37,3 era la chiusura
del **primo giorno di crollo** (01/06, 3,5%), non del giorno prima — IV-rank pre 0,05 → 0,03. Picco e
percentili **invariati** (49,1 / 69,2; 29° / 46°) dopo aver corretto la tabella di contesto (§7).
Verdetto invariato.
**§5, skew** (`r0822_skew`, rigirato, 66 s): la scomposizione sul panel si muove di **≤0,005** per fattore
(term 0,880 · skew 0,917 · spread 0,910 · fit 0,995 · f_tot 0,729); Q1/Q2 (lag prezzo/skew) **non si
toccano** (serie proprie ancorate a `ts_max`). Agli ingressi il controllo `f_markfit` passa da 1,045 a
**0,991** e le osservazioni che lo superano da 22/28 a **25/28**: l'ora di spot in piu' stava **nel
controllo**, non nell'effetto — riparare ha reso il controllo piu' pulito, non il risultato diverso.
**`vrp_f_watch`** (sorvegliante in `cron_daily`, criteri pre-registrati il 30/07 e **non toccati**):
| | prima (state.json del 09/09 mattina) | dopo |
|---|---|---|
| coppie | 28 | 28 |
| f canonico | 0,732 [0,702-0,784] | **0,706 [0,678-0,745]** |
| f candidato | 0,803 [0,741-0,842] | 0,805 [0,771-0,840] |
| differenza | +0,054 [0,027, +0,105], 18/28 > 0 | **+0,097 [+0,042, +0,140]**, 21/28 > 0 |
| ampiezza IC95 (b ≤ 0,12) | 0,131 — no | **0,098 — OK** |
| coppie (a ≥ 40) | no | no |
| `ready` | False | False |
Lo `state.json` e' stato **riscritto** dal mio lancio (21:14Z, senza `--quiet`; nessun Telegram: notifica
solo a `ready`); il «prima» completo e' la colonna qui sopra (n_positive 18, `esclude_zero` False), e il
cron lo rigenera domani. La differenza ora **esclude lo zero**. Non decide niente: (a) non e' soddisfatto e il candidato resta
bocciato sul deflated-Sharpe, che non dipende da f. Si registra perche' fra un mese qualcuno leggera'
"esclude lo zero" e deve sapere da quando e perche'.
## 6. Cosa resta aperto, col suo costo
| voce | costo di non farlo | costo di farlo |
|---|---|---|
| **DVOL orario** nel feed (`fetch_dvol --res 3600`, riga in `cron_daily`, `cblib` che lo legge) | su §11 0,005 di f; **su un crollo ±0,05**; ogni misura oraria su opzioni a DVOL giornaliero eredita lo scarto | un fetch pubblico gratuito (3.163 barre da maggio in 2 chiamate), una riga di cron, e una decisione: e' un feed nuovo dentro il perimetro (D1, certificazione, backup) |
| **spot causale stantio ≤25 min** (snapshot :25, chiusura :00): ±0,05-0,10 di f per osservazione | `cblib.spot_series` sul feed **5m** certificato (`causale(·, "5m")`): cambia di nuovo ogni numero di `cblib`, quindi con rimisura dichiarata |
| `options_vrp_calibrate.spot_series` (copia, 20/06) | i numeri del 20/06 (f 0,73) restano con l'ora in piu'; §11 li ha gia' sostituiti | riscriverlo su `cblib` |
| la tabella pubblicata di §5 (x0,869 / x0,904 / x0,920) | ≤0,005 per fattore: sotto la risoluzione del campione (14+14 scadenze) | nulla: nota aggiunta |
## 7. Errori dell'autore in sessione
- **`asi8` in microsecondi** scrivendo il test della causalita' (pandas 3): i timestamp sintetici finivano
nel 1970 e il test falliva per la fixture, non per il codice. E' la trappola del debito 1, ripagata
scrivendo un test *sul* debito 18. Corretto con `(idx epoch) // Timedelta(ms)`, la convenzione di
`load_tf`.
- **La tabella di contesto di `r0909` si e' spostata di un giorno** dopo la riparazione: mostrava il DVOL
del giorno D1 accanto al prezzo del giorno D, e il picco del crollo cambiava (69,2 → 68,3, 46° → 43°)
non per una misura ma per un'etichetta. Trovato **col diff degli output**, non da un test: una tabella
per umani non ha test. Ora il contesto usa la chiusura *del* giorno (`causale(V, "-1D")`, dichiarato
nel commento) e `dvol_pre` / `ivr_pre` restano causali.
- Il verdetto §7 di `r0822_vrp_real_quotes` era **prosa con numeri cablati**: sarebbe rimasto a 0,714 per
sempre (N11 al contrario). Aggiornato con i vecchi accanto; il difetto di forma resta (i numeri del
verdetto andrebbero calcolati, non scritti).
## 8. Revisione (`fable`, agente fresco) — 16 segnalazioni, verdetto «correggi e committa»
Ha **riverificato in proprio** la convenzione delle due serie (1h vs 5m su 120 barre: 100%; DVOL contro
l'API oraria) e riprodotto ogni numero dei documenti lanciando gli script. Applicate: **#2** «−1,8% in
un'ora» era falso (avevo letto 09:00 → 10:00; il fatto e' +3,9% 08:00 → 09:00) · **#3** due delle tre
settimane «ETH» erano BTC · **#4** la parentesi sul DVOL era invertita (la chiusura di D e' piu' BASSA,
non piu' alta) · **#5** lo spot non e' un meccanismo ma rumore, e resta stantio ≤25 min · **#6/#7** due
lettori di `cblib` non dichiarati: `r0730` (0,73 → 0,71, ora in CLAUDE.md §2) e `r0901` (riallineato) ·
**#8** le date di calendario di `r0822` §1 slittavano di un giorno · **#9** il test anti-doppio-shift non
catturava `causale(spot_series, "1h")`: ora conta i `causale(` · **#10** ancora 0,74 → 0,76 nel test
d'integrazione · **#11** teste di §75 e memoria 20 §5 con i numeri vecchi come correnti (M28) · **#12**
«±0,05» → «−0,04/+0,06» · **#13** `state.json` riscritto: dichiarato · **#15** lo skew agli ingressi cambia
piu' del panel (correlazioni che cambiano segno): dichiarato in §5. Non applicate: nessuna. Confermati
senza rilievo (#1, #14, #16): verso e taglia degli shift, nessun doppio spostamento, `ST` alle 08:00
coerente con la TWAP 07:30-08:00, tutti i numeri di §3-§5.
## 9. Test
`tests/test_cb_chain_vrp.py`: +3 (causale; `spot_series` causale sul feed 1h con controllo positivo sulla
serie grezza; `dvol_series` causale sul giornaliero). `tests/test_r0909_crash_catturato.py`: il test dello
shift locale diventa "lo script usa le serie di `cblib` senza ri-spostarle" (sorgente + controllo positivo
su `cblib.causale`). Suite intera: **1011 passati, 0 falliti** (erano 1008), rilanciata dopo le correzioni della revisione: 1011/1011.
@@ -0,0 +1,98 @@
# 2026-09-09c — Revisione settimanale in sola lettura: cosa si costruisce e cosa no
**Richiesta dell'operatore:** *«schedulare ogni x tempo un processo di auto revisione dei dati e della
configurazione del sistema in modo che migliori con il tempo … autoregolazione e autoaggiornamento dei
sistemi o anche creare nuovi sistemi … valutando dati macroeconomici … e poi informa me tramite
Telegram»*. Dopo la discussione: *«fai»*.
**Esito in una riga:** costruita la **rilettura settimanale in sola lettura** con revisore diverso
dall'autore (`claude-fable-5-1`), rapporto firmato in `docs/revisioni/` e Sintesi su Telegram, lunedi'
06:15 UTC; **scartati** — con la memoria del progetto, non per prudenza generica — autoregolazione dei
parametri, generazione automatica di strategie e macro da internet. Libro, config, pesi: invariati.
## 1. Perche' tre pezzi su quattro sono no
| pezzo | cosa dice la memoria | esito |
|---|---|---|
| auto-revisione periodica | nove sorveglianti, ognuno sulla sua grandezza; nessuno rilegge l'insieme; la revisione col secondo modello ha trovato oggi due conclusioni false e un look-ahead | **si'** |
| autoregolazione dei parametri | TP01/SKH01 congelati (M20), 75/25 confermato 3 volte, `weights_tilt_null` fallisce; `scale_watch` esiste per rendere visibile ogni cambio di config; A8: una riga di config + una di giornale, decisione dell'operatore | **no** |
| generazione e test di strategie nuove | 69 filoni in 6 ondate; ogni candidato e' un trial (M3) e deflaziona tutto; la ricerca non e' il vincolo binding dal 26/07 (+0,046 €/g il miglior lead contro +4,07%/anno per €100/mese) | **no, salvo freno**: prima si apre la memoria e si batte il motivo della morte; i candidati vanno in forward-monitor con gate e data, mai nel book |
| dati macro da internet | gate macro / DVOL direzionale / skew de-risk: HEDGE o ridondanti (TP01 gia' flat nei crolli); un dato dal web non e' certificato (D1-D4; la APR USDC 3,40% pubblicata e mai incassata) | **no** |
Il centro dell'argomento: **il progetto ha gia' misurato che piu' ricerca non sposta il traguardo, i
bonifici si'**. Un ciclo automatico che cerca strategie ottimizza la leva sbagliata e paga in trial bruciati.
## 2. Cosa gira
`scripts/cron_review.sh` (lunedi' **06:15 UTC**: fuori dal :00, fuori dal martedi' 09:00, dopo il
`cron_daily` delle 00:30 cosi' il giornale di domenica e' chiuso) → `scripts/live/revisione.py --quiet`
`src/live/revisione.scrivi`. Riga installata in crontab (`15 6 * * 1`), letta dal test.
**Materiale** (sola lettura, ~195k caratteri, 27 voci): CLAUDE.md intero · `config/live.json` ·
`crontab -l` · `git log` 14 g + `git status` · `monitor_health` · stato dei sorveglianti (scale, vrp_f,
usde, balance, venue_news, movimenti dichiarati, watermark) · 7 pagine di giornale · diari della
settimana. Gli stati stanno **prima** delle voci grandi: alla prima stesura il tetto totale (320k) aveva
tagliato `usde_convert.jsonl` a 2 caratteri — un sorvegliante invisibile per un limite di lunghezza.
**Prompt**: dieci regole vincolanti (sola lettura · ogni segnalazione cita la fonte · solo numeri del
materiale · le decisioni di §3 non si ripropongono se la colonna «cosa la riapre» non e' soddisfatta ·
un'idea nuova solo citando la memoria che ha ucciso la simile · i gate con data e criterio, senza
anticipare · misurato ≠ dedotto · niente previsioni · URGENTE per cio' che serve all'operatore subito ·
cinque titoli fissi, ~1.000 parole). L'ultimo titolo, «Cosa ho letto e cosa mi mancava», e' l'elenco da
cui il materiale della settimana dopo migliora: e' l'unico «miglioramento col tempo» che questo sistema
si concede, e passa da una modifica al codice, cioe' da una revisione.
**Uscita**: `docs/revisioni/<data>.md` firmato (modello, ora, «SOLA LETTURA», «lettore fallibile P13»,
guardia sui numeri come `analista` con soglia 12, tabella del materiale letto). Telegram: la sola
Sintesi, taglio dichiarato sotto i 4.096. Modello muto o risposta senza i titoli → rapporto «NON
eseguita» col motivo e 🚨: il silenzio non e' una revisione (P5).
**Cosa non puo' fare, per costruzione**: scrivere altro che il rapporto (test sull'albero dei file:
`--secco` non scrive niente, un giro scrive UN file), eseguire proposte, cambiare config. Il modello e'
`claude-fable-5-1` per costante, e un test verifica che sia diverso da `analista.MODELLO_DEFAULT`.
## 3. Test — `tests/test_revisione.py` (16)
Finestra del materiale e file assenti dichiarati · ordine stati-prima-delle-voci-grandi · troncamento
dichiarato · regole nel prompt · guardia (vuoto, titoli mancanti, numeri fuori con controllo positivo) ·
rapporto firmato · modello muto → rapporto + 🚨 · Telegram con HTML neutralizzato e taglio dichiarato ·
`--secco` non chiama e non scrive · un giro scrive un solo file · `--no-telegram` · revisore ≠ autore ·
cadenza: il `.sh` dichiara «lunedi' HH:MM UTC», minuto ≠ 0, fuori dallo slot di release, e la crontab
installata ha gli stessi cinque campi (skip se illeggibile). `test_cli_flag` ha preso lo script da solo
(guardia `valida`, USO coi flag, flag del cron accettati).
## 4. Errori in sessione
- `_tronca(testo, n=MAX_CHARS_VOCE)`: il default e' catturato alla definizione, il test che patchava la
costante non vedeva niente — la stessa trappola del debito 12 (`connect`). Letto a runtime.
- Il tetto totale tagliava in coda: i sorveglianti erano gli ultimi e i primi a sparire. Riordinato, e
il test lo presidia.
## 5. Primo giro reale (22:03Z) — e i tre difetti che solo il giro reale poteva trovare
Il primo lancio e' **morto prima di chiamare il modello**: `OSError: Argument list too long` — 195k
caratteri di prompt passati come argomento alla CLI superano il limite del sistema, e `analista.interroga`
non lo catturava: zero rapporto, zero Telegram, **il silenzio che il modulo esiste per vietare**. Nessun
test l'avrebbe trovato (i test iniettano `interroga`): e' un difetto di trasporto, e il trasporto si valida
sul trasporto (P2). Tre correzioni: (1) `revisione.interroga` proprio, prompt su **stdin**, ogni eccezione
restituita come errore; (2) `scrivi` incapsula chiamata e invio: un'eccezione diventa un rapporto «NON
eseguita» + 🚨; (3) `_cmd` (crontab, git) dichiara qualunque eccezione invece di propagarla. Test con
controllo positivo su `OSError(7)`. Terzo difetto, trovato dal test: `analista.pulisci` **toglie le righe
che iniziano con `#`**, cioe' i cinque titoli che la guardia richiede — usarlo avrebbe reso ogni revisione
«rifiutata: titoli mancanti». Sostituito con `strip()`.
Secondo lancio: **rapporto scritto, stato `sospetta`**, 1.435 parole, `docs/revisioni/2026-09-09.md`.
La guardia ha segnato 11 numeri «non nel materiale»: sono cifre in **formato italiano** («4.477,46»,
«1.341») contro il materiale in formato USA («$4,477.46») — la normalizzazione di `analista` conosce una
convenzione sola. E' un limite dichiarato, non un errore del revisore: si aggiusta quando si vede quanto
spesso scatta. Contenuto: **nessun URGENTE**; tre verifiche in cima (una possibile attribuzione a reward di
400 USDE che erano un acquisto in `usde_watch` del 07/09 — `n_trades` fermo a 50 contro 54 ordini nel
registro; il cuscino USDC del debito §5.17 senza definizione balance/equity; il versamento del 04/09 non
dichiarato in `movimenti_dichiarati.jsonl`); una contraddizione in CLAUDE.md §1 sull'haircut (M28); la data
di arming che differisce fra §0, config e TWR; quattro sorveglianti senza timestamp o fuori da
`monitor_health`; e, come richiesto dalla regola 5, **zero idee di strategia**. «Cosa mi mancava»: stati
con ora di `edge/fee/venue/scale_watch`, l'output di `usde_watch.rendimento()`, i paper a oggi,
`cron_daily.sh`, il registro fill della settimana — l'elenco da cui il materiale della prossima settimana
migliora. Le segnalazioni sono dell'operatore e della prossima sessione: **nessuna e' stata applicata qui**
(sarebbe l'agente che esegue le proposte del revisore nello stesso giro, cioe' il ciclo che si e' scartato).
Costo: una chiamata, ~4 minuti.
@@ -0,0 +1,216 @@
# I cinque punti della revisione del 09/09 — decisi e fatti
**Data:** 2026-09-10 (sessione 12:08Z → 13:10Z; i timestamp qui dentro sono letti, non ricordati)
**Origine:** la prima revisione settimanale (`docs/revisioni/2026-09-09.md`, revisore `fable`) e il
giornale del 09/09. Ogni punto e' stato spiegato all'operatore con pro e contro e **deciso da lui**
(§3-style: decisioni con l'informazione completa). Issue sul Gitea di casa #2-#6, chiuse con la
soluzione scritta (regola globale del 10/09).
## Le decisioni, in una tabella
| # | punto | decisione dell'operatore | issue |
|---|---|---|---|
| 1 | `usde_watch` contava 400 USDE come reward (07/09) | ripara ora + issue | #2 bug |
| 2 | cuscino USDC senza sorvegliante (§5.17) | **equity USDC, riconverte da solo** | #3 sviluppo |
| 3 | versamento 04/09 solo rilevato; piano €5.000 «in attesa» | **e' l'INTERO versamento previsto**: dichiarato, piano chiuso | #6 sviluppo |
| 4 | SCALA-01 A2: da quando contano i 180 giorni | **dal 01/09** (sorvegliante attivo) → gate non prima del **2027-02-28** | #5 bug |
| 5 | PREVDAY-01 «senza data» | **tieni 21/06/2027, cabla kill e veto, correggi §4** | #4 bug |
Sul punto 5 la domanda giusta e' stata «ricorda dove dobbiamo arrivare (50 €/g)»: PREVDAY
de-luckato al 15% vale +0,10 di Sharpe sul libro e la somma di *tutti* i lead dell'ondata di agosto
vale +0,036 €/giorno. Verso i 50 €/g il candidato vale centesimi in qualunque esito del gate:
non si spende una sessione di ricerca per anticipare (a)+(b), e non si butta l'unica gamba short
scorrelata oltre a SKH01 per risparmiare un cron. Costo scelto: mezza giornata, una volta.
## Punto 1 — il lettore dei trade spot aveva DUE limiti, non uno
Il primo era noto dalla revisione: `trade_history(limit=50)` con 54 ordini. Il secondo l'ho
trovato sondando il gateway per ripararlo: `get_user_trades_by_instrument` chiamato senza
`historical` restituisce **solo le ultime ~24 ore** — il fill BTC dell'08/09 06:47Z e' invisibile
alle 12:55Z del 10/09, quello del 09/09 21:47Z si vede. Il cron gira ogni 24h alle 12:35: sta
dentro la finestra per pochi secondi. Un giro saltato avrebbe reso invisibili i trade fra 48h e
24h fa, e il delta sarebbe stato attribuito al reward *esattamente come il 07/09*, con un
meccanismo diverso.
Ora `trade_copertura()` dichiara **non leggibile** (somma `None`, e `analizza` non attribuisce:
P12) in entrambi i casi — lista lunga quanto il limite (troncata) o ultima lettura oltre 24h+5min —
e il record porta `trades_motivo` (P4: il perche'). `TRADE_LIMIT = 1000` e' il `count` massimo di
Deribit. Controllo positivo nei test: 54 ordini si sommano tutti.
La riga del 07/09 in `data/live/usde_watch.jsonl` e' **corretta in loco** con campo `correzione`
(originale conservato dentro, copia del file in `usde_watch.jsonl.pre_fix_20260910`): 54 ordini
= 8 + 30 + 16 dal registro `usde_convert.jsonl`, 2.434 USDE, reward 0,080934. Il tasso non
cambia — la riga era gia' esclusa per `con_trade` — e `rendimento()` oggi da' **3,94% su 14
finestre, 12 pagate**.
## Punto 2 — `cuscino_watch`: la grandezza che nessuno guardava
Il cuscino richiesto e' `2 × 0,5 × 0,30 × equity_tot` = 30% dell'equity, **derivato** da
config e `book.WEIGHT`. La formula e' passata da `usde_convert.py` a `src/live/usde.py`, e
`usde_convert` la importa da li': due lettori, una formula (P1 — cinque occorrenze nel
progetto di sorveglianti puntati su un bersaglio ridichiarato).
| stato | criterio | azione |
|---|---|---|
| OK | slack ≥ 10% del cuscino | niente |
| PREAVVISO | 0 ≤ slack < 10% | ⚠️ alla transizione |
| SCOPERTO | slack < 0 | 🚨 + `usde_convert --quota 0,64 --esegui` (cuscino + 20% di margine, alzato dal 10% in revisione) |
| BLIND | conto non leggibile | ⚠️ alla transizione |
Guardie della riconversione: `execution_enabled` del libro (un solo interruttore), niente vendita
sotto `depeg_warn` (li' decide l'operatore, e l'allarme lo dice), un tentativo ogni 6h, le guardie
proprie di `usde_convert` (banda di prezzo, book leggibile, tetto HARD) ereditate lanciando lo
script vero (P15). Il marcatore «gia' detto» e' l'esito di `notify`: un invio fallito non consuma
la transizione (lezione del debito §5.2). Cron `:53` = dopo il giro del book (`:47`), fuori dal
minuto tondo. `monitor_health` lo sorveglia (max 3h).
📌 **Primo giro a secco (13:00Z): gia' PREAVVISO.** USDC equity $1.361,23 contro $1.335,26
richiesti: **slack +$25,97**. Il 09/09 il revisore aveva stimato +$41/+$59; la marcatura del
libro dal picco del 06/09 ne aveva consumati altri 15-30 senza che nessuno lo vedesse. E' il
caso d'uso del sorvegliante, arrivato prima del sorvegliante.
## Punto 3 — il versamento del 04/09, dal balance e non dall'equity
Il salto di equity 13:47→14:47Z diceva +$2.410,14 col mercato dell'ora dentro. Il **balance
USDC** di `balance_watch` (cambia solo per P&L realizzato, fee, funding e movimenti) dice
1.417,73455581 → 3.832,41432481 = **+$2.414,68**, nessun fill nella finestra, funding su $541
di nozionale ~$0,01. Dichiarato con banda $0,05. Il report ora lo mostra come dichiarato e
il TWR passa a **+6,66%** (era +7,22% stamattina, +7,83% il 06/09): la coda 0,63% dal 04/09 e'
marcatura delle due long, trading da arming **+$13,82**.
L'operatore ha detto che era **l'intero versamento previsto**: la voce «in attesa di importo e
data» di §1 e' chiusa. Resta la lezione di §2: per sei giorni il movimento e' stato solo
rilevato, la regola diceva il giorno stesso.
## Punto 4 — SCALA-01: la data era il punto 5 della SPEC scambiato per il gate
«Non prima del 2026-10-01» erano i 30 giorni di sorvegliante del punto 5 (§7 della SPEC); A2
chiede **≥180 giorni** col criterio passato ogni giorno, e il criterio lo misura solo
`scale_watch`, attivo dal 01/09. Le tre letture possibili, con pro e contro, sono nel diario di
sessione; l'operatore ha scelto **dal 01/09 → 2027-02-28**. Contare dall'arming avrebbe dato
un criterio ricostruito a posteriori, che non prova nulla sul sorvegliante; ridurre A2 a 30 giorni
avrebbe allentato un gate senza un costo d'attesa che lo giustifichi (il gradino 1,25x vale meno
di €100/mese di versamento).
Riconciliata anche la data d'arming: **20/06 = TP01 da solo** (commit `4650aa7`), **23/06 = il
BOOK** (commit `db738bc`, prima lettura di equity 23/06 22:00Z). CLAUDE.md §0 diceva solo la
prima, `config/live.json` e il TWR usano la seconda: erano due eventi, non un errore.
## Punto 5 — PREVDAY-01 aveva la data; non l'aveva la tabella
`RESULTS-0822` §54: decisione **2027-06-21**, kill, veto. La tabella §4 diceva «scritto 23/08 ·
10 condizioni», e il revisore ha letto quella. Ora la riga ha data, kill, veto e stato (5/7:
(a) e (b) sono strutturali, nessun forward le cambia).
Cablato in `paper_prevday.py` (prima esisteva solo nel testo, a differenza di `paper_dvolspread`):
| misura | valore al 10/09 |
|---|---|
| Sharpe forward **giornaliero** (lente del kill; l'oraria +1,19 non si cita, §54) | **+0,95** su 81 giorni, 81 attivi |
| kill (< 0,50 su ≥180 g attivi) | **NON MATURO**, leggibile dal ~18/12/2026 |
| barre ricostruibili dal feed di oggi | **1.942/1.943** (99,9%); l'unica divergente e' il 26/08 00:00, il giorno del fix di `advance()` |
| veto | ok |
⚠️ **Errore mio, catturato prima di committare:** la prima stesura del veto leggeva «divergenze
non crescenti» come `tasso_recente > tasso_totale`, e con UNA barra divergente negli ultimi 30
giorni (0,03/g contro 0,01/g) dichiarava VETO. Una guardia piu' stretta del contratto produce
allarmi che si impara a ignorare (P14): ora la crescita richiede **≥5 barre recenti E tasso
doppio**, dichiarato nel codice e provato nel test con i numeri della serie vera.
La ricostruzione al 99,9% contro il 95,6% di §54: la misura del 23/08 era stata fatta *prima*
del fix di `advance()` del 26/08 (le 67 barre divergenti erano «all'ora del cron», cioe' barre
parziali); oggi il feed rivisto e la serie coincidono tranne quella barra.
## Cose trovate per strada
- Due timestamp «di comodo» scritti a mano (13:05Z e 13:07Z) invece dell'ora letta (12:57:43Z e
13:04:09Z): corretti subito. E' esattamente la regola globale «la data si LEGGE» — vale anche per
chi la scrive nel campo `correzione`.
- `issue --json` va **prima** del sottocomando; il percorso nella regola globale era
`/opt/docker/AI-OS` invece di `/opt/AI-OS` (corretto la mattina, nel test dello strumento).
## Verifica
`uv run pytest`: **1060 passati** (erano 1031). Nuovi: `test_cuscino_watch.py` (14),
`test_paper_prevday_gate.py` (8), +5 in `test_usde_watch.py`. Revisione del diff affidata a
`fable` (agente fresco): esito e correzioni applicate in coda a questo diario.
## Revisione `fable` (13:10-13:18Z): 15 segnalazioni, 4 medie — tutte verificate, 12 applicate
| # | segnalazione | verifica | esito |
|---|---|---|---|
| 1 | `puo_riconvertire` contava anche i NON tentativi (`tentata=False`): un giro `--secco` o un prezzo illeggibile bloccava la riconversione vera per 6h | riprodotta | filtro su `tentata` + test |
| 2 | il tetto del venue in `usde_convert.piano` bloccava anche la **vendita**: con `venue_cap_frac` rimesso in config la riconversione automatica sarebbe stata morta | riprodotta (`side=sell`, `ok=False`) | vale solo in acquisto |
| 3 | `MARGINE_RIPRISTINO` 10% == `PREAVVISO_FRAC` 10%: dopo ogni 🚨 un ⚠️ per costruzione (P14) | riprodotta col floor di 1 USDE | margine **20%** → quota **0,64**, test |
| 4 | `--secco` scriveva nel registro vivo (veicolo del punto 1, e maschera un cron fermo) | riprodotta (riga 13:00:15Z) | non scrive; riga tolta |
| 5 | due bersagli di quota: `quota_target` 0,70 in config contro 0,64 del ripristino; il ripristino e' un **ratchet verso il basso** | letto | dichiarato in CLAUDE.md §5.17 e nel docstring: decisione dell'operatore (N9) |
| 7 | buco di ≤5 min dentro la tolleranza della finestra 24h | letto | dichiarato (D5) + fonte Deribit documentata |
| 8 | `giorni_attivi` legge posizioni REAL, il kill e' MODELED | letto | dichiarato nel docstring (81/81 oggi) |
| 10 | il test del cron leggeva un commento nello `.sh`, non la crontab | letto | legge `crontab -l` come `test_book_cadenza` |
| 11 | `prevday_target` patchato senza `monkeypatch` | letto | `monkeypatch` |
| 13 | «QUATTRO movimenti» in §2: sono TRE, in quattro tratti | letto | corretto |
| 14 | «5/7» contro «8/10» di §54: base diversa non dichiarata | letto | dichiarata |
| 6, 9, 12, 15 | verifiche che reggono (`sys.executable`, `cwd`, `piano()` in SCOPERTO, ricostruzione ≡ `advance`) | — | — |
Non applicato: il lock fra cron e conversione a mano (DUBBIO 2) — dichiarato come limite nel
docstring. Il DUBBIO 1 (ratchet) e' la domanda aperta per l'operatore.
## Coda (13:55Z →): il riacquisto automatico — issue #7
Alla domanda aperta (ratchet verso il basso) l'operatore ha risposto «crea una strategia per
riportarla a quota in autonomia». La risposta ingenua — ricomprare fino a `quota_target` 0,70 —
e' sbagliata per costruzione: a 0,70 lo slack e' zero, la prima ora in perdita vende, il
riacquisto ricompra, e cosi' via a 6 bps al giro. Serve **isteresi con un bersaglio unico**:
| slack / cuscino | stato | azione |
|---|---|---|
| < 0 | SCOPERTO | 🚨 vende USDE → 0,20 |
| 0 0,10 | PREAVVISO | ⚠️ |
| 0,10 0,40 | OK | — |
| > 0,40 | **ECCEDENTE** | 📌 compra USDE → 0,20 |
Bersaglio 0,20 ⇒ quota **0,64**. Per oscillare lo slack deve muoversi di ±0,20×cuscino = ±6%
dell'equity, cioe' **±8,6% di equity USDC (~$380)** perche' slack = 0,7·USDC 0,3·USDE (la prima
stesura diceva ±6%/$270: corretta in revisione): a leva 0,26x sono ~33% di mercato. Un bonifico alza lo slack di
0,7×importo: sopra ~9% dell'equity (~$380) il riacquisto scatta da solo — la regola di §1
«dopo un bonifico si rilancia `usde_convert`» non dipende piu' da qualcuno che se ne ricordi.
Le tre frazioni vivono in `config/live.json` (`usde.bande_cuscino` verifica 0 < preavviso <
margine < riacquisto), `quota_target` 0,70 e' stato tolto. Soglie dichiarate, non ottimizzate
(M8): il costo di un giro e' ~$0,2, la frequenza attesa qualche giro al mese nei tratti mossi.
📌 **Trovato al primo giro vero del cron (13:53Z): l'allerta PREAVVISO non e' partita.** Il token
era valido (`getMe` ok); il testo conteneva «(< 10% del cuscino)» e Telegram in `parse_mode=HTML`
rifiuta un `<` nudo. Testi riscritti senza `<`, `allerta_errore` registrato nel record (P3),
test che legge il sorgente delle `notify`. Un sorvegliante nuovo che non riesce a parlare al
primo giro e' il caso di P2: si valida sul segnale E sul trasporto.
Stato al giro a secco delle 14:0xZ: quota 69,3%, slack +$30 = PREAVVISO. Il piano a 0,64 e'
valido (SELL 237 USDE, slack dopo +$267): il sorvegliante lo esegue alla prima ora sotto zero;
allinearlo subito e' una riga a mano.
### Seconda revisione `fable` (14:04Z): 10 segnalazioni, 1 ALTA, 3 MEDIE — applicate 9
| # | segnalazione | esito |
|---|---|---|
| 1 ALTA | il `<` arrivava ancora a Telegram dal campo `motivo` («(< 6h)») dentro l'allerta 🚨: il 🚨 piu' importante perso fino a 4h | escape nel **sink** `notifier.notify` (`html.escape` su titolo e valori; nessuno dei 45 chiamanti passa tag: grep) + motivi senza `<` |
| 2 MEDIA | il test sul sorgente era parziale (codice morto, cieco ai valori interpolati) | sostituito: cattura la POST e verifica `&lt;` |
| 3 MEDIA | ECCEDENTE con guasto persistente = «acquisto FALLITA» ogni 6h per sempre | da ECCEDENTE dopo un fallimento si riprova ogni 24h; da SCOPERTO resta 6h |
| 4 MEDIA | bande fuori ordine ⇒ traceback ingoiato dal cron, nessun record, «fermo» invece di «rotto» | `giudica` ⇒ BLIND con motivo; record e allerta scritti |
| 5-7 | CLAUDE.md §1 diceva ancora «nessuno sorveglia»; §5.17 «tre stati»; `_nota_usde` con `quota_target` | corretti |
| 8 | «±6% di equity USDC» sbagliato di 0,7: slack = 0,7·USDC 0,3·USDE ⇒ ±8,6% (~$380) | corretto in 4 posti |
| 9 | `TOLL` ridichiarato nel test | importato da `usde_convert` |
| 10 | esempi `--quota 0.70` nel docstring di `usde_convert` | 0,64 |
Verifiche numeriche del revisore (loop su `piano()` con passo 1 USDE, TOLL, min order): bonifico
+$2.000 → 11 acquisti → OK, slack +$387; perdita $50 → 3 vendite → OK, +$264; conto tutto USDC →
20 acquisti → OK. **Nessuna sequenza vendita→acquisto→vendita.** Non applicato: `min(q*, cap)` con
il tetto del venue (dubbio 3: un conto al tetto resta ECCEDENTE e ora allerta una volta al giorno).
Dubbio 4, che vale una riga in §5.17: **0,64 e' una derivazione dell'autore, non la quota decisa
dall'operatore il 30/08 (0,70)**; ~$10/anno di resa di differenza; da confermare.
### Allineamento eseguito (14:16:38Z, ordine dell'operatore «allinea subito a 0.64, esegui»)
`usde_convert --quota 0.64 --esegui`: SELL 100 + 100 + 38 USDE a 1,0000 medio (limite 0,9995,
fill al book), quota 69,4% → **64,02%**, conto USDC $1.602,79 + USDE 2.851,95 = $4.454,74.
Slack dopo **+$266** su $1.336 di cuscino: `cuscino_watch --secco` dice **OK** (era PREAVVISO a
+$29). Costo: ~$0,1 di spread, fee zero. Registro in `data/live/usde_convert.jsonl`.
+29 -16
View File
@@ -1,48 +1,61 @@
# Giornale di bordo — 2026-08-23
*Scritto 2026-08-23T13:34:16+00:00. Numeri misurati; nessuna interpretazione automatica.*
*Scritto 2026-08-24T00:36:45+00:00. Numeri misurati; nessuna interpretazione automatica.*
## Mercato
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|---|---|---|---|---|---|---|---|
| **BTC** | $77,254.50 | +0.20% | +22.94% | +20.52% | 43.3% | ↑ ↑ (2/3 su) | 42.4 (52° pctl 1a) |
| **ETH** | $2,427.20 | +0.17% | +29.49% | +30.46% | 69.6% | ↑ ↑ ↑ (3/3 su) | 57.0 (44° pctl 1a) |
| **BTC** | $77,750.50 | +0.84% | +23.72% | +21.29% | 43.3% | ↑ ↑ (3/3 su) | 43.2 (56° pctl 1a) |
| **ETH** | $2,464.00 | +1.69% | +31.45% | +32.44% | 69.6% | ↑ ↑ ↑ (3/3 su) | 60.3 (54° pctl 1a) |
## Libro
Equity **$636.14** · nozionale lordo $258 · leva lorda 0.41x · barra dati 2026-08-23 · 14 giri
Equity **$637.58** · nozionale lordo $260 · leva lorda 0.41x · barra dati 2026-08-23 · 24 giri
| | TP01 | SKH01 | target | posizione | azione |
|---|---|---|---|---|---|
| **BTC** | +0.150 | LONG @ 75,280.0 | $+115 | $+116 | HOLD (a target) |
| **ETH** | +0.271 | LONG @ 2,436.4 | $+144 | $+142 | HOLD (a target) |
| **BTC** | +0.150 | LONG @ 75,280.0 | $+116 | $+116 | HOLD (a target) |
| **ETH** | +0.271 | LONG @ 2,436.4 | $+145 | $+144 | HOLD (a target) |
## P&L
- giorno: **$+1.14** di equity (14 letture)
- giorno: **$+2.58** di equity (24 letture)
- realizzato: +5.55 netto su 2 round-trip chiusi · 1 fill · fee 0.0243
- cumulato dall'arming: **$+38.08**
- cumulato dall'arming: **$+39.52**
## Salute
- giri di `book_execute`: 14/14 *(giorno in corso)*
- giri di `book_execute`: 24/24
- eta' feed SKH all'ultimo giro: 0 min
## Lettura
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
- `[stato]` A mercato su BTC $+116 (TP01 +0.150, SKH01 long) e ETH $+142 (TP01 +0.271, SKH01 long).
- `[disaccordo]` BTC: gli orizzonti del trend **non concordano** (2/3 al rialzo) e l'esposizione TP01 e' parziale (+0.150) — e' il meccanismo, non una scelta di oggi.
- `[stato]` A mercato su BTC $+116 (TP01 +0.150, SKH01 long) e ETH $+144 (TP01 +0.271, SKH01 long).
- `[leva]` Leva lorda **0.41x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.59x.
- `[pnl]` Equity $+1.14 con 1 fill e 2 round-trip chiusi (realizzato $+5.55 netto).
- `[concentrazione]` 📌 **Il 102% di tutto il P&L cumulato ($+38.08) viene dagli ultimi 7 giorni.** Oltre il 100% perche' **il periodo precedente era in perdita**: senza questi 7 giorni il libro sarebbe sotto. Un risultato concentrato in una finestra non e' un tasso di rendimento: e' un evento.
- `[drawdown]` 📌 Equity **-1.37%** sotto il picco ($644.98). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
- `[vol]` BTC: implicita 42.4 contro realizzata 30g 43.3 (sotto); DVOL al 52° pctl di un anno; IV-rank espandente 0.18 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[vol]` ETH: implicita 57.0 contro realizzata 30g 69.6 (sotto); DVOL al 44° pctl di un anno; IV-rank espandente 0.18 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[pnl]` Equity $+2.58 con 1 fill e 2 round-trip chiusi (realizzato $+5.55 netto).
- `[concentrazione]` 📌 **Il 102% di tutto il P&L cumulato ($+39.52) viene dagli ultimi 7 giorni.** Oltre il 100% perche' **il periodo precedente era in perdita**: senza questi 7 giorni il libro sarebbe sotto. Un risultato concentrato in una finestra non e' un tasso di rendimento: e' un evento.
- `[drawdown]` 📌 Equity **-1.15%** sotto il picco ($644.98). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
- `[vol]` BTC: implicita 43.2 contro realizzata 30g 43.3 (sotto); DVOL al 56° pctl di un anno; IV-rank espandente 0.20 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[vol]` ETH: implicita 60.3 contro realizzata 30g 69.6 (sotto); DVOL al 54° pctl di un anno; IV-rank espandente 0.23 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[evidenza]` 📌 Campione a oggi: **61 giorni, 12 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
## Analisi (agente)
*Scritta da `claude-opus-5` il 2026-08-24T00:37:10+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **sospetta**.*
> ⚠️ numeri non presenti nella pagina: 327
Il libro esce dal letargo: sei giorni fermi a $597.12 senza fill fino al 18/08, e da allora quattro giorni di lavoro consecutivi. Oggi però è il primo della serie in cui la meccanica si ferma: 1 fill, 2 round-trip chiusi, entrambe le gambe a HOLD (a target), scarto di $1 su ETH. Il libro non sta più costruendo la posizione, la sta tenendo — e i $+2.58 di equity sono per la quasi totalità marcatura, non esecuzione, dato che il realizzato netto è $+5.55.
Il nozionale, però, è sceso: $260 lordi contro i $327 impliciti nelle posizioni di ieri, e la leva a 0.41x resta lontana dal tetto di 1.00x pur con TSMOM a 3/3 su entrambi gli asset e la settimana a +23.72% e +31.45%. Il vol-target lavora contro il segnale: RV30 di ETH al 69.6% comprime la size proprio dove la direzione è più forte. Non è un malfunzionamento, è la definizione dello sleeve.
Il 9.00 di ieri e il 1.15% dal picco sono la prima flessione della finestra: leggerla come "il rally si sta esaurendo" o come normale respiro di quattro giorni in salita è ugualmente compatibile con questi dati — 61 giorni e 12 round-trip non separano le due letture, né lo fa la regola `[concentrazione]`.
L'implicita sta sotto la realizzata su entrambi (43.2 vs 43.3, 60.3 vs 69.6): VRP01 resta fermo per IV-rank, ma è lo stato in cui vendere vol sarebbe meno remunerativo, non più. Nulla da controllare a mano.
## Nota
*(vuota — campo dell'operatore)*
+57
View File
@@ -0,0 +1,57 @@
# Giornale di bordo — 2026-08-24
*Scritto 2026-08-25T00:37:37+00:00. Numeri misurati; nessuna interpretazione automatica.*
## Mercato
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|---|---|---|---|---|---|---|---|
| **BTC** | $78,981.50 | +1.58% | +22.48% | +22.78% | 43.4% | ↑ ↑ ↑ (3/3 su) | 43.4 (59° pctl 1a) |
| **ETH** | $2,483.15 | +0.78% | +29.85% | +32.54% | 69.6% | ↑ ↑ ↑ (3/3 su) | 58.4 (49° pctl 1a) |
## Libro
Equity **$642.02** · nozionale lordo $334 · leva lorda 0.52x · barra dati 2026-08-24 · 24 giri
| | TP01 | SKH01 | target | posizione | azione |
|---|---|---|---|---|---|
| **BTC** | +0.449 | flat | $+108 | $+189 | SELL $-81 |
| **ETH** | +0.271 | LONG @ 2,436.4 | $+145 | $+145 | HOLD (a target) |
## P&L
- giorno: **$+4.44** di equity (24 letture)
- realizzato: +3.22 netto su 2 round-trip chiusi · 2 fill · fee 0.0520
- cumulato dall'arming: **$+43.96**
## Salute
- giri di `book_execute`: 24/24
- eta' feed SKH all'ultimo giro: 0 min
## Lettura
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
- `[stato]` A mercato su BTC $+189 (TP01 +0.449, SKH01 flat) e ETH $+145 (TP01 +0.271, SKH01 long).
- `[leva]` Leva lorda **0.52x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.48x.
- `[pnl]` Equity $+4.44 con 2 fill e 2 round-trip chiusi (realizzato $+3.22 netto).
- `[concentrazione]` 📌 **Il 102% di tutto il P&L cumulato ($+43.96) viene dagli ultimi 7 giorni.** Oltre il 100% perche' **il periodo precedente era in perdita**: senza questi 7 giorni il libro sarebbe sotto. Un risultato concentrato in una finestra non e' un tasso di rendimento: e' un evento.
- `[drawdown]` 📌 Equity **-0.76%** sotto il picco ($646.95). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
- `[vol]` BTC: implicita 43.4 contro realizzata 30g 43.4 (sotto); DVOL al 59° pctl di un anno; IV-rank espandente 0.20 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[vol]` ETH: implicita 58.4 contro realizzata 30g 69.6 (sotto); DVOL al 49° pctl di un anno; IV-rank espandente 0.21 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[evidenza]` 📌 Campione a oggi: **62 giorni, 14 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
## Analisi (agente)
*Scritta da `claude-opus-5` il 2026-08-25T00:38:01+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
Il libro esce dalla settimana ferma: dal 14 al 18 agosto l'equity era immobile a $597.12 con zero fill, e il P&L cumulato di $+43.96 nasce tutto dopo. Oggi è la prima giornata in cui entrambi gli asset mostrano TSMOM 3/3 su e il libro è a mercato su tutti e due, ma l'esposizione non cresce: il target BTC scende a $+108 contro una posizione di $+189 e la macchina vende $-81. È un dettaglio che merita di essere letto bene — il prezzo BTC sale (+1.58% oggi, +22.48% a 7 giorni) e il libro riduce: è il vol-targeting che risponde alla RV30 al 43.4%, non un cambio di segno del trend. La stessa cosa si può descrivere come "il libro incassa forza" oppure come "il libro perde esposizione proprio nella settimana che gli ha dato il risultato": entrambe le letture sono compatibili con questi numeri, e quale sia giusta non è determinabile su 62 giorni.
Insolito rispetto ai giorni scorsi: il 22 agosto era l'unico delta negativo (-$9.00) con 0 round-trip, mentre oggi $+4.44 arriva con 2 fill e 2 round-trip chiusi — la giornata più tranquilla dell'ultima settimana per movimento, non per esito. Salute nominale: 24/24 giri, feed SKH a 0 minuti.
Nulla qui richiede un intervento. L'unica cosa da tenere d'occhio nei prossimi giorni è se le vendite di ribilanciamento su BTC continuano mentre il prezzo sale: sarebbe il comportamento atteso, ma è anche il modo in cui questo libro restituirebbe un guadagno concentrato.
## Nota
*(vuota — campo dell'operatore)*
+58
View File
@@ -0,0 +1,58 @@
# Giornale di bordo — 2026-08-25
*Scritto 2026-08-26T07:05:51+00:00. Numeri misurati; nessuna interpretazione automatica.*
## Mercato
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|---|---|---|---|---|---|---|---|
| **BTC** | $78,503.50 | -0.61% | +21.35% | +20.11% | 43.5% | ↑ ↑ ↑ (3/3 su) | 43.3 (58° pctl 1a) |
| **ETH** | $2,443.20 | -1.61% | +27.44% | +25.06% | 69.1% | ↑ ↑ ↑ (3/3 su) | 58.1 (48° pctl 1a) |
## Libro
Equity **$2,056.59** · nozionale lordo $557 · leva lorda 0.27x · barra dati 2026-08-25 · 25 giri
| | TP01 | SKH01 | target | posizione | azione |
|---|---|---|---|---|---|
| **BTC** | +0.450 | flat | $+347 | $+345 | HOLD (a target) |
| **ETH** | +0.274 | flat | $+212 | $+212 | HOLD (a target) |
## P&L
- giorno: **$+1,414.57** di equity (24 letture) — di cui **$+1,399.39 movimenti di capitale** -> trading **$+15.18**
- realizzato: +3.00 netto su 6 round-trip chiusi · 7 fill · fee 0.3452
- cumulato dall'arming: **$+1,458.53** di equity — di cui $+1,399.39 versati/prelevati -> trading **$+59.14**
## Salute
- giri di `book_execute`: 25/24
- eta' feed SKH all'ultimo giro: 0 min
## Lettura
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
- `[movimento]` 📌 **Movimento di capitale $+1,399.39** fra le 10:47 e le 11:47 UTC: salto +209.6% contro un massimo spiegabile dal mercato di ±0.2% al tetto di leva. Scorporato dal P&L di trading qui sotto.
- `[stato]` A mercato su BTC $+345 (TP01 +0.450, SKH01 flat) e ETH $+212 (TP01 +0.274, SKH01 flat).
- `[leva]` Leva lorda **0.27x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.73x.
- `[pnl]` P&L di trading $+15.18 (equity $+1414.57 al lordo del movimento di capitale) con 7 fill e 6 round-trip chiusi (realizzato $+3.00 netto).
- `[concentrazione]` 📌 **Il 102% di tutto il P&L cumulato ($+59.14) viene dagli ultimi 7 giorni.** Oltre il 100% perche' **il periodo precedente era in perdita**: senza questi 7 giorni il libro sarebbe sotto. Un risultato concentrato in una finestra non e' un tasso di rendimento: e' un evento.
- `[drawdown]` 📌 Equity **-0.51%** sotto il picco ($2,067.23). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
- `[vol]` BTC: implicita 43.3 contro realizzata 30g 43.5 (sotto); DVOL al 58° pctl di un anno; IV-rank espandente 0.20 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[vol]` ETH: implicita 58.1 contro realizzata 30g 69.1 (sotto); DVOL al 48° pctl di un anno; IV-rank espandente 0.20 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[evidenza]` 📌 Campione a oggi: **63 giorni, 20 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
## Analisi (agente)
*Scritta da `claude-opus-5` il 2026-08-26T00:38:40+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
Il fatto del giorno non e' il mercato, e' il conto. L'equity passa da $642.02 a $2,056.59, cioe' $+1,414.57, mentre il realizzato dei sei round-trip chiusi vale $+3.00 netto su $557 di nozionale lordo: le due grandezze non stanno nella stessa scala, e da questa pagina non e' determinabile da dove venga la differenza — un movimento di questa taglia il libro non lo produce. Vale la pena che un umano lo confermi prima che ieri e oggi vengano letti come una serie.
Se la differenza e' esterna, tutto cio' che il generatore costruisce sull'equity ne eredita il difetto: il cumulato $+1,458.53 e la sua concentrazione «100% negli ultimi 7 giorni» misurano il conto, non la strategia; il drawdown -0.51% e' calcolato contro un picco ($2,067.23) nato poche ore fa; la leva 0.27x scende perche' il denominatore e' cresciuto, non perche' il libro si sia ridotto — le posizioni sono a target, entrambe in HOLD.
Sul mercato, BTC -0.61% e ETH -1.61% nel giorno contro +21.35% e +27.44% a sette giorni: la finestra che porta tutto il P&L e' anche la settimana piu' forte, e a 63 giorni e 20 round-trip «edge» ed «essere stati lunghi» restano indistinguibili. L'esposizione e' interamente TP01: SKH01 e' flat su entrambi gli asset. Venticinque giri su 24: uno in piu', causa non misurabile qui.
## Nota
*(vuota — campo dell'operatore)*
+58
View File
@@ -0,0 +1,58 @@
# Giornale di bordo — 2026-08-26
*Scritto 2026-08-27T00:37:47+00:00. Numeri misurati; nessuna interpretazione automatica.*
## Mercato
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|---|---|---|---|---|---|---|---|
| **BTC** | $79,031.00 | +0.67% | +14.00% | +24.05% | 42.0% | ↑ ↑ ↑ (3/3 su) | 40.4 (40° pctl 1a) |
| **ETH** | $2,507.65 | +2.64% | +11.34% | +32.61% | 67.8% | ↑ ↑ ↑ (3/3 su) | 56.1 (41° pctl 1a) |
## Libro
Equity **$2,065.45** · nozionale lordo $582 · leva lorda 0.28x · barra dati 2026-08-26 · 24 giri
| | TP01 | SKH01 | target | posizione | azione |
|---|---|---|---|---|---|
| **BTC** | +0.464 | flat | $+359 | $+364 | SELL $-5 |
| **ETH** | +0.278 | flat | $+215 | $+218 | HOLD (a target) |
## P&L
- giorno: **$+8.86** di equity (24 letture)
- realizzato: +0.29 netto su 2 round-trip chiusi · 4 fill · fee 0.0138
- cumulato dall'arming: **$+1,467.39** di equity — di cui $+1,399.39 versati/prelevati -> trading **$+68.00**
## Salute
- giri di `book_execute`: 24/24
- eta' feed SKH all'ultimo giro: 0 min
## Lettura
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
- `[stato]` A mercato su BTC $+364 (TP01 +0.464, SKH01 flat) e ETH $+218 (TP01 +0.278, SKH01 flat).
- `[leva]` Leva lorda **0.28x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.72x.
- `[pnl]` Equity $+8.86 con 4 fill e 2 round-trip chiusi (realizzato $+0.29 netto).
- `[concentrazione]` 📌 **Il 101% di tutto il P&L cumulato ($+68.00) viene dagli ultimi 7 giorni.** Oltre il 100% perche' **il periodo precedente era in perdita**: senza questi 7 giorni il libro sarebbe sotto. Un risultato concentrato in una finestra non e' un tasso di rendimento: e' un evento.
- `[vol]` BTC: implicita 40.4 contro realizzata 30g 42.0 (sotto); DVOL al 40° pctl di un anno; IV-rank espandente 0.14 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[vol]` ETH: implicita 56.1 contro realizzata 30g 67.8 (sotto); DVOL al 41° pctl di un anno; IV-rank espandente 0.17 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[evidenza]` 📌 Campione a oggi: **64 giorni, 22 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
## Analisi (agente)
*Scritta da `claude-opus-5` il 2026-08-27T00:38:49+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
Il fatto nuovo non sta nei numeri di oggi: e' il denominatore, cambiato ieri. L'equity passa da 642.02 (24/08) a 2,056.59 (25/08) a $2,065.45, ma il nozionale lordo resta $582 e le posizioni si muovono appena — BTC da $+345 a $+364, ETH da $+212 a $+218. La leva 0.28x su un tetto di 1.00x non e' quindi confrontabile con quella dei giorni precedenti: stessa esposizione, denominatore molto piu' grande. Chi rilegge fra mesi guardi $+1,399.39 versati e $+68.00 di trading prima di guardare l'equity.
La settimana che porta il 101% del cumulato e' la stessa in cui BTC ha fatto +14.00% e ETH +11.34%, TSMOM 3/3 su entrambi e il libro lungo su entrambe le gambe. «L'edge ha lavorato» e «un trend-following era lungo mentre il mercato saliva» spiegano quel dato ugualmente bene, e a 64 giorni e 22 round-trip non si separano. Anche l'$+8.86 di oggi e' quasi tutto rivalutazione: il realizzato e' $+0.29 su 2 round-trip, in un giorno da +0.67% e +2.64%.
Due cose insolite: SKH01 e' flat su entrambi gli asset dopo un mese da +24.05% e +32.61%, quindi il rischio e' tutto TP01; e l'implicita sta sotto la realizzata su entrambi (40.4 contro 42.0, 56.1 contro 67.8), con DVOL al 40° e 41° percentile.
Da controllare a mano: 4 fill in una giornata che chiude con un solo SELL $-5. Cosa sia successo fra i 24 giri non e' misurabile qui.
## Nota
*(vuota — campo dell'operatore)*
+60
View File
@@ -0,0 +1,60 @@
# Giornale di bordo — 2026-08-27
*Scritto 2026-08-28T08:28:09+00:00. Numeri misurati; nessuna interpretazione automatica.*
## Mercato
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|---|---|---|---|---|---|---|---|
| **BTC** | $80,266.00 | +1.56% | +9.94% | +25.66% | 42.1% | ↑ ↑ ↑ (3/3 su) | 41.5 (47° pctl 1a) |
| **ETH** | $2,511.75 | +0.16% | +7.96% | +30.79% | 67.9% | ↑ ↑ ↑ (3/3 su) | 57.1 (45° pctl 1a) |
## Libro
Equity **$2,070.23** · nozionale lordo $573 · leva lorda 0.28x · barra dati 2026-08-27 · 24 giri
| | TP01 | SKH01 | target | posizione | azione |
|---|---|---|---|---|---|
| **BTC** | +0.463 | flat | $+359 | $+361 | HOLD (a target) |
| **ETH** | +0.277 | flat | $+215 | $+212 | HOLD (a target) |
## P&L
- giorno: **$+4.78** di equity (24 letture)
- realizzato: +0.13 netto su 1 round-trip chiusi · 1 fill · fee 0.0019
- cumulato dall'arming: **$+1,472.17** di equity — di cui $+1,399.39 versati/prelevati -> trading **$+72.78**
## Salute
- giri di `book_execute`: 24/24
- eta' feed SKH all'ultimo giro: 0 min
## Lettura
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
- `[stato]` A mercato su BTC $+361 (TP01 +0.463, SKH01 flat) e ETH $+212 (TP01 +0.277, SKH01 flat).
- `[leva]` Leva lorda **0.28x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.72x.
- `[pnl]` Equity $+4.78 con 1 fill e 1 round-trip chiusi (realizzato $+0.13 netto).
- `[concentrazione]` 📌 **Il 82% di tutto il P&L cumulato ($+72.78) viene dagli ultimi 7 giorni.** Un risultato concentrato in una finestra non e' un tasso di rendimento: e' un evento.
- `[vol]` BTC: implicita 41.5 contro realizzata 30g 42.1 (sotto); DVOL al 47° pctl di un anno; IV-rank espandente 0.16 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[vol]` ETH: implicita 57.1 contro realizzata 30g 67.9 (sotto); DVOL al 45° pctl di un anno; IV-rank espandente 0.19 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[evidenza]` 📌 Campione a oggi: **65 giorni, 23 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
## Analisi (agente)
*Scritta da `claude-opus-5` il 2026-08-28T08:29:00+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
Il fatto nuovo della settimana non sta nel P&L ma nella colonna equity: il salto del 25/08 da $642.02 a $2,056.59 è capitale entrato, non performance. Dei $+1,472.17 cumulati dall'arming, $+1,399.39 sono versamenti e solo $+72.78 è trading. Chi rileggerà la curva fra mesi non la legga come rendimento.
Merita un occhio umano il fatto che l'equity sia quasi triplicata ma il nozionale no: BTC $+361 / ETH $+212 oggi, contro $+345/$+212 del 25/08 e $+364/$+218 del 26/08 — praticamente fermi, con leva lorda scesa a 0.28x su un tetto di 1.00x. Se il target segua l'equity o l'intensità del segnale (TP01 +0.463 e +0.277) da questa pagina non è determinabile.
Il rischio è tutto su TP01: SKH01 flat su entrambe le gambe. Giornata di attività minima, 1 fill e 1 round-trip, $+4.78 di equity; quanto di quel numero sia mark-to-market delle gambe non è scomponibile qui.
Lettura alternativa della concentrazione: l'82% del cumulato sta negli ultimi 7 giorni, che sono anche i 7 giorni di BTC +9.94% ed ETH +7.96%. Un long-flat che guadagna in una settimana di trend sta facendo il suo mestiere; a 65 giorni e 23 round-trip non è separabile da un buon tratto di mercato.
Salute pulita: 24/24 giri, feed a 0 min. Su entrambi gli asset l'implicita sta sotto la realizzata, ma il gate di VRP01 blocca su un altro asse.
## Nota
*(vuota — campo dell'operatore)*
+58
View File
@@ -0,0 +1,58 @@
# Giornale di bordo — 2026-08-28
*Scritto 2026-08-29T00:36:42+00:00. Numeri misurati; nessuna interpretazione automatica.*
## Mercato
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|---|---|---|---|---|---|---|---|
| **BTC** | $77,850.00 | -3.01% | -0.64% | +21.77% | 44.1% | ↑ ↑ ↑ (3/3 su) | 38.3 (25° pctl 1a) |
| **ETH** | $2,443.35 | -2.72% | -2.93% | +27.98% | 68.9% | ↑ ↑ ↑ (3/3 su) | 50.9 (16° pctl 1a) |
## Libro
Equity **$2,052.36** · nozionale lordo $571 · leva lorda 0.28x · barra dati 2026-08-28 · 24 giri
| | TP01 | SKH01 | target | posizione | azione |
|---|---|---|---|---|---|
| **BTC** | +0.464 | flat | $+357 | $+357 | HOLD (a target) |
| **ETH** | +0.278 | flat | $+214 | $+214 | HOLD (a target) |
## P&L
- giorno: **$-17.87** di equity (24 letture)
- realizzato: +0.00 netto su 0 round-trip chiusi · 2 fill · fee 0.0054
- cumulato dall'arming: **$+1,454.30** di equity — di cui $+1,399.39 versati/prelevati -> trading **$+54.91**
## Salute
- giri di `book_execute`: 24/24
- eta' feed SKH all'ultimo giro: 0 min
## Lettura
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
- `[stato]` A mercato su BTC $+357 (TP01 +0.464, SKH01 flat) e ETH $+214 (TP01 +0.278, SKH01 flat).
- `[leva]` Leva lorda **0.28x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.72x.
- `[pnl]` Equity $-17.87 con 2 fill e 0 round-trip chiusi (realizzato $+0.00 netto).
- `[drawdown]` 📌 Equity **-1.02%** sotto il picco ($2,073.59). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
- `[vol]` BTC: implicita 38.3 contro realizzata 30g 44.1 (sotto); DVOL al 25° pctl di un anno; IV-rank espandente 0.09 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[vol]` ETH: implicita 50.9 contro realizzata 30g 68.9 (sotto); DVOL al 16° pctl di un anno; IV-rank espandente 0.12 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[evidenza]` 📌 Campione a oggi: **66 giorni, 23 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
## Analisi (agente)
*Scritta da `claude-opus-5` il 2026-08-29T00:37:39+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
Prima giornata in rosso dal versamento: dopo +8.86 e +4.78, l'equity fa $-17.87 con zero round-trip chiusi. Il libro non ha deciso niente — BTC $+357 e ETH $+214 restano a target, dove erano ieri ($+361 e $+212) — quindi la perdita e' marcatura di mercato sul giorno a -3.01% BTC e -2.72% ETH, non esecuzione; e con SKH01 flat su entrambi e' esposizione interamente TP01. I 2 fill con fee 0.0054 sono aggiustamenti: se siano ritocchi al target o altro, non e' distinguibile qui. Salute pulita (24/24 giri, feed a 0 min), quindi non spiega nulla della giornata.
Cio' che e' cambiato davvero e' la scala. Dal 25/08 l'equity e' $2,052.36, e il movimento di un solo giorno ($-17.87) e' dello stesso ordine dei $+54.91 di trading accumulati dall'arming in 66 giorni. Il P&L giornaliero e' ora piu' rumoroso della cosa che dovrebbe misurare, mentre i round-trip restano 23.
Due letture reggono ugualmente sugli stessi numeri: TSMOM 3/3 su entrambi con 30 giorni a +21.77% e +27.98% dice trend intatto e ritracciamento; i 7 giorni (-0.64% e -2.93%) dicono che la spinta si era gia' fermata e oggi se ne vede la parte visibile. Con questi dati non si sceglie.
Merita un'occhiata umana il picco $2,073.59: non compare fra le chiusure giornaliere, e il -1.02% e' misurato contro quella lettura oraria.
## Nota
*(vuota — campo dell'operatore)*
+58
View File
@@ -0,0 +1,58 @@
# Giornale di bordo — 2026-08-29
*Scritto 2026-08-30T00:37:12+00:00. Numeri misurati; nessuna interpretazione automatica.*
## Mercato
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|---|---|---|---|---|---|---|---|
| **BTC** | $78,243.00 | +0.50% | +1.48% | +20.85% | 44.0% | ↑ ↑ ↑ (3/3 su) | 37.4 (19° pctl 1a) |
| **ETH** | $2,458.25 | +0.61% | +1.45% | +28.20% | 68.9% | ↑ ↑ ↑ (3/3 su) | 51.1 (18° pctl 1a) |
## Libro
Equity **$2,056.87** · nozionale lordo $560 · leva lorda 0.27x · barra dati 2026-08-29 · 24 giri
| | TP01 | SKH01 | target | posizione | azione |
|---|---|---|---|---|---|
| **BTC** | +0.444 | flat | $+342 | $+344 | HOLD (a target) |
| **ETH** | +0.273 | flat | $+211 | $+216 | HOLD (a target) |
## P&L
- giorno: **$+4.51** di equity (24 letture)
- realizzato: +0.02 netto su 1 round-trip chiusi · 1 fill · fee 0.0054
- cumulato dall'arming: **$+1,458.81** di equity — di cui $+1,399.39 versati/prelevati -> trading **$+59.42**
## Salute
- giri di `book_execute`: 24/24
- eta' feed SKH all'ultimo giro: 0 min
## Lettura
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
- `[stato]` A mercato su BTC $+344 (TP01 +0.444, SKH01 flat) e ETH $+216 (TP01 +0.273, SKH01 flat).
- `[leva]` Leva lorda **0.27x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.73x.
- `[pnl]` Equity $+4.51 con 1 fill e 1 round-trip chiusi (realizzato $+0.02 netto).
- `[drawdown]` 📌 Equity **-0.81%** sotto il picco ($2,073.59). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
- `[vol]` BTC: implicita 37.4 contro realizzata 30g 44.0 (sotto); DVOL al 19° pctl di un anno; IV-rank espandente 0.07 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[vol]` ETH: implicita 51.1 contro realizzata 30g 68.9 (sotto); DVOL al 18° pctl di un anno; IV-rank espandente 0.12 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[evidenza]` 📌 Campione a oggi: **67 giorni, 24 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
## Analisi (agente)
*Scritta da `claude-opus-5` il 2026-08-30T00:38:12+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
Giornata silenziosa: 1 fill, 1 round-trip, entrambe le gambe a target e posizioni ferme dal 25/08 ($+344 su BTC e $+216 su ETH, contro $+345 e $+212). Cio' che e' cambiato non e' il libro ma il denominatore: nozionale lordo $560 con equity $2,056.87 contro i $642.02 del 24/08, e leva lorda 0.27x su un tetto di 1.00x. Il capitale nuovo non ha mosso l'esposizione.
L'insolito sta nel mercato: TSMOM 3/3 su entrambi gli asset e +20.85% / +28.20% a 30 giorni, con l'implicita al 19° e 18° percentile dell'anno e sotto la realizzata (37.4 contro 44.0; 51.1 contro 68.9). Un rally con implicita compressa; VRP01 resta fermo per costruzione, IV-rank 0.07 e 0.12 sotto 0.30.
Lettura alternativa altrettanto compatibile con gli stessi numeri: il $+4.51 di oggi e il $-17.87 di ieri, a posizioni ferme, sono marcature di prezzo piu' che decisioni — il realizzato vale $+0.02. Su 67 giorni e 24 round-trip, dei $+1,458.81 cumulati il trading e' $+59.42.
Merita un occhio umano il drawdown: -0.81% dal picco $2,073.59 e' misurato su una serie che il 25/08 e' saltata di $+1,414.57, quindi ha pochi giorni di storia alla scala attuale. Da quanto SKH01 sia flat su entrambe le gambe non e' misurabile qui. Salute pulita: 24/24 giri, feed a 0 min.
## Nota
*(vuota — campo dell'operatore)*
+58
View File
@@ -0,0 +1,58 @@
# Giornale di bordo — 2026-08-30
*Scritto 2026-08-31T00:36:38+00:00. Numeri misurati; nessuna interpretazione automatica.*
## Mercato
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|---|---|---|---|---|---|---|---|
| **BTC** | $77,702.00 | -0.69% | -0.06% | +23.66% | 42.3% | ↑ ↑ ↑ (3/3 su) | 37.0 (15° pctl 1a) |
| **ETH** | $2,417.45 | -1.66% | -1.89% | +29.87% | 68.1% | ↑ ↑ ↑ (3/3 su) | 51.8 (20° pctl 1a) |
## Libro
Equity **$2,047.39** · nozionale lordo $553 · leva lorda 0.27x · barra dati 2026-08-30 · 24 giri
| | TP01 | SKH01 | target | posizione | azione |
|---|---|---|---|---|---|
| **BTC** | +0.463 | flat | $+355 | $+348 | BUY $+8 |
| **ETH** | +0.278 | flat | $+214 | $+205 | BUY $+9 |
## P&L
- giorno: **$-9.48** di equity (24 letture)
- realizzato: -0.08 netto su 2 round-trip chiusi · 5 fill · fee 0.0164
- cumulato dall'arming: **$+1,449.33** di equity — di cui $+1,399.39 versati/prelevati -> trading **$+49.94**
## Salute
- giri di `book_execute`: 24/24
- eta' feed SKH all'ultimo giro: 0 min
## Lettura
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
- `[stato]` A mercato su BTC $+348 (TP01 +0.463, SKH01 flat) e ETH $+205 (TP01 +0.278, SKH01 flat).
- `[leva]` Leva lorda **0.27x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.73x.
- `[pnl]` Equity $-9.48 con 5 fill e 2 round-trip chiusi (realizzato $-0.08 netto).
- `[drawdown]` 📌 Equity **-1.26%** sotto il picco ($2,073.59). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
- `[vol]` BTC: implicita 37.0 contro realizzata 30g 42.3 (sotto); DVOL al 15° pctl di un anno; IV-rank espandente 0.06 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[vol]` ETH: implicita 51.8 contro realizzata 30g 68.1 (sotto); DVOL al 20° pctl di un anno; IV-rank espandente 0.13 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[evidenza]` 📌 Campione a oggi: **68 giorni, 26 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
## Analisi (agente)
*Scritta da `claude-opus-5` il 2026-08-31T00:37:31+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
Il fatto nuovo non è la perdita ma la sua composizione: $-9.48 di equity contro $-0.08 di realizzato su 2 round-trip. Quasi tutto è mark-to-market su due long in una giornata con BTC -0.69% ed ETH -1.66%; una lettura altrettanto compatibile è che il libro stia semplicemente respirando col beta dei due asset, senza che il segnale abbia detto nulla. È il terzo segno meno da quando l'elenco comincia il 20/08, dopo il 22/08 ($-9.00) e il 28/08 ($-17.87). Insolito è invece il conto dei fill: cinque, con fee 0.0164, per spostare $+8 su BTC e $+9 su ETH, contro l'uno o due dei giorni scorsi. Vale un'occhiata umana a quale giro li abbia generati.
La posizione ETH scende da $+216 a $+205 mentre BTC sale da $+344 a $+348: se a muoverla sia il segnale TP01 o il prezzo non è separabile da questa pagina.
L'anomalia di mercato sta nella vol: +23.66% a 30 giorni su BTC e +29.87% su ETH, con DVOL al 15° e al 20° percentile dell'anno e implicita sotto la realizzata su entrambi. Il rally è arrivato senza che le opzioni lo prezzassero.
Il trading resta $+49.94 su 68 giorni e 26 round-trip: troppo poco per essere altro che rumore.
## Nota
*(vuota — campo dell'operatore)*
+56
View File
@@ -0,0 +1,56 @@
# Giornale di bordo — 2026-08-31
*Scritto 2026-09-01T00:36:58+00:00. Numeri misurati; nessuna interpretazione automatica.*
## Mercato
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|---|---|---|---|---|---|---|---|
| **BTC** | $78,564.00 | +1.11% | -0.53% | +25.15% | 42.3% | ↑ ↑ ↑ (3/3 su) | 37.6 (21° pctl 1a) |
| **ETH** | $2,467.80 | +2.08% | -0.62% | +33.88% | 67.9% | ↑ ↑ ↑ (3/3 su) | 51.3 (19° pctl 1a) |
## Libro
Equity **$2,059.45** · nozionale lordo $569 · leva lorda 0.28x · barra dati 2026-08-31 · 24 giri
| | TP01 | SKH01 | target | posizione | azione |
|---|---|---|---|---|---|
| **BTC** | +0.461 | flat | $+356 | $+354 | HOLD (a target) |
| **ETH** | +0.278 | flat | $+214 | $+215 | HOLD (a target) |
## P&L
- giorno: **$+12.06** di equity (24 letture)
- realizzato: -0.48 netto su 3 round-trip chiusi · 4 fill · fee 0.0100
- cumulato dall'arming: **$+1,461.39** di equity — di cui $+1,399.39 versati/prelevati -> trading **$+62.00**
## Salute
- giri di `book_execute`: 24/24
- eta' feed SKH all'ultimo giro: 0 min
## Lettura
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
- `[stato]` A mercato su BTC $+354 (TP01 +0.461, SKH01 flat) e ETH $+215 (TP01 +0.278, SKH01 flat).
- `[leva]` Leva lorda **0.28x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.72x.
- `[pnl]` Equity $+12.06 con 4 fill e 3 round-trip chiusi (realizzato $-0.48 netto).
- `[drawdown]` 📌 Equity **-0.68%** sotto il picco ($2,073.59). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
- `[vol]` BTC: implicita 37.6 contro realizzata 30g 42.3 (sotto); DVOL al 21° pctl di un anno; IV-rank espandente 0.07 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[vol]` ETH: implicita 51.3 contro realizzata 30g 67.9 (sotto); DVOL al 19° pctl di un anno; IV-rank espandente 0.12 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[evidenza]` 📌 Campione a oggi: **69 giorni, 29 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
## Analisi (agente)
*Scritta da `claude-opus-5` il 2026-09-01T00:38:01+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
Il $+12.06 di oggi non nasce da una decisione: le posizioni sono rimaste a target (BTC $+354 contro $+356, ETH $+215 contro $+214) e i 3 round-trip chiusi hanno realizzato $-0.48. Il guadagno e' marcatura di due long in una giornata di BTC +1.11% e ETH +2.08%; la lettura alternativa altrettanto compatibile e' che il libro sia stato semplicemente beta lungo, e in pagina non c'e' nulla che separi le due cose.
E' il delta piu' grande dal 25/08, che pero' era il versamento: un solo giorno pesa quindi molto sui $+62.00 di trading cumulato dall'arming, su un campione di 69 giorni e 29 round-trip, tre dei quali chiusi oggi. Rispetto a -17.87 del 28/08 e -9.48 del 30/08 il segno cambia, ma le posizioni sono ferme dal 25/08 fra $+344 e $+364 su BTC e fra $+205 e $+218 su ETH: si muove il prezzo, non il libro.
Insolito, e non confrontabile coi giorni precedenti che la volatilita' non la riportano: implicita sotto realizzata su entrambi (37.6 contro 42.3, 51.3 contro 67.9) con DVOL al 21° e 19° percentile dell'anno. Merita un'occhiata che siano serviti 4 fill per lasciare le posizioni dov'erano, e che il picco $2,073.59 da cui si misura il -0.68% non compaia in nessuna chiusura giornaliera.
## Nota
*(vuota — campo dell'operatore)*
+58
View File
@@ -0,0 +1,58 @@
# Giornale di bordo — 2026-09-01
*Scritto 2026-09-02T00:36:47+00:00. Numeri misurati; nessuna interpretazione automatica.*
## Mercato
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|---|---|---|---|---|---|---|---|
| **BTC** | $77,432.50 | -1.44% | -1.36% | +21.89% | 42.9% | ↑ ↑ ↑ (3/3 su) | 37.9 (23° pctl 1a) |
| **ETH** | $2,418.40 | -2.00% | -1.02% | +28.37% | 68.5% | ↑ ↑ ↑ (3/3 su) | 51.8 (21° pctl 1a) |
## Libro
Equity **$2,050.77** · nozionale lordo $568 · leva lorda 0.28x · barra dati 2026-09-01 · 24 giri
| | TP01 | SKH01 | target | posizione | azione |
|---|---|---|---|---|---|
| **BTC** | +0.461 | flat | $+355 | $+357 | HOLD (a target) |
| **ETH** | +0.278 | flat | $+213 | $+211 | HOLD (a target) |
## P&L
- giorno: **$-8.68** di equity (24 letture)
- realizzato: +0.00 netto su 0 round-trip chiusi · 1 fill · fee 0.0027
- cumulato dall'arming: **$+1,452.71** di equity — di cui $+1,399.39 versati/prelevati -> trading **$+53.32**
## Salute
- giri di `book_execute`: 24/24
- eta' feed SKH all'ultimo giro: 0 min
## Lettura
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
- `[stato]` A mercato su BTC $+357 (TP01 +0.461, SKH01 flat) e ETH $+211 (TP01 +0.278, SKH01 flat).
- `[leva]` Leva lorda **0.28x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.72x.
- `[pnl]` Equity $-8.68 con 1 fill e 0 round-trip chiusi (realizzato $+0.00 netto).
- `[drawdown]` 📌 Equity **-1.10%** sotto il picco ($2,073.59). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
- `[vol]` BTC: implicita 37.9 contro realizzata 30g 42.9 (sotto); DVOL al 23° pctl di un anno; IV-rank espandente 0.08 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[vol]` ETH: implicita 51.8 contro realizzata 30g 68.5 (sotto); DVOL al 21° pctl di un anno; IV-rank espandente 0.13 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[evidenza]` 📌 Campione a oggi: **70 giorni, 29 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
## Analisi (agente)
*Scritta da `claude-opus-5` il 2026-09-02T00:37:44+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
La giornata non contiene decisioni: 1 fill, 0 round-trip chiusi, realizzato $+0.00, entrambe le gambe HOLD a target. Gli $-8.68 sono quindi marcatura di posizioni ferme, non esito di operazioni — ed e' la prima giornata senza alcun round-trip chiuso dal 28/08, dopo il 30/08 e il 31/08 che ne avevano chiusi 2 e 3 con 5 e 4 fill. Anche le posizioni cambiano poco: BTC $+357 ed ETH $+211 restano dentro la banda tenuta dal 26/08 (BTC $+344364, ETH $+205218), con leva 0.28x molto sotto l'1.00x.
Cio' che le regole non vedono: il rosso arriva con BTC 1.44% ed ETH 2.00% mentre i tre TSMOM restano 3/3 su e i 30 giorni segnano +21.89% e +28.37%. Lo stesso dato regge due letture — rumore dentro un mese in tendenza, oppure l'avvio di un rientro — e da qui non sono distinguibili: non misurabile con una giornata.
Insolito il quadro volatilita': implicita sotto la realizzata su entrambi (37.9 contro 42.9; 51.8 contro 68.5) e DVOL al 23°/21° percentile, quindi VRP01 resta fermo per l'IV-rank, non per il livello.
Vale un'occhiata umana il fatto che il trading cumulato, $+53.32 in 70 giorni, sia della stessa taglia delle singole oscillazioni giornaliere recenti (17.87 il 28/08, +12.06 il 31/08).
## Nota
*(vuota — campo dell'operatore)*
+60
View File
@@ -0,0 +1,60 @@
# Giornale di bordo — 2026-09-02
*Scritto 2026-09-03T00:36:49+00:00. Numeri misurati; nessuna interpretazione automatica.*
## Mercato
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|---|---|---|---|---|---|---|---|
| **BTC** | $77,312.50 | -0.15% | -2.17% | +21.78% | 42.9% | ↑ ↑ ↑ (3/3 su) | 37.2 (16° pctl 1a) |
| **ETH** | $2,391.55 | -1.11% | -4.63% | +28.63% | 68.5% | ↑ ↑ ↑ (3/3 su) | 50.9 (17° pctl 1a) |
## Libro
Equity **$2,045.82** · nozionale lordo $560 · leva lorda 0.27x · barra dati 2026-09-02 · 24 giri
| | TP01 | SKH01 | target | posizione | azione |
|---|---|---|---|---|---|
| **BTC** | +0.454 | flat | $+348 | $+347 | HOLD (a target) |
| **ETH** | +0.276 | flat | $+212 | $+213 | HOLD (a target) |
## P&L
- giorno: **$-4.95** di equity (24 letture)
- realizzato: -0.35 netto su 1 round-trip chiusi · 2 fill · fee 0.0045
- cumulato dall'arming: **$+1,447.76** di equity — di cui $+1,399.39 versati/prelevati -> trading **$+48.37** · TWR **+10.47%**
## Salute
- giri di `book_execute`: 24/24
- eta' feed SKH all'ultimo giro: 0 min
## Lettura
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
- `[stato]` A mercato su BTC $+347 (TP01 +0.454, SKH01 flat) e ETH $+213 (TP01 +0.276, SKH01 flat).
- `[leva]` Leva lorda **0.27x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.73x.
- `[pnl]` Equity $-4.95 con 2 fill e 1 round-trip chiusi (realizzato $-0.35 netto).
- `[drawdown]` 📌 Equity **-1.34%** sotto il picco ($2,073.59). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
- `[vol]` BTC: implicita 37.2 contro realizzata 30g 42.9 (sotto); DVOL al 16° pctl di un anno; IV-rank espandente 0.06 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[vol]` ETH: implicita 50.9 contro realizzata 30g 68.5 (sotto); DVOL al 17° pctl di un anno; IV-rank espandente 0.12 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[evidenza]` 📌 Campione a oggi: **71 giorni, 30 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
## Analisi (agente)
*Scritta da `claude-opus-5` il 2026-09-03T00:37:49+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
Il fatto nuovo e' l'equity a $2,045.82: sotto ogni chiusura registrata dopo il versamento del 25/08, dove il minimo era $2,047.39 del 30/08. Non viene dal trading — realizzato $-0.35 su un round-trip, fee 0.0045, due fill — ma dal prezzo su due posizioni tenute a target per tutte le 24 letture.
Il picco da cui si misura il -1.34% ($2,073.59) non compare fra le chiusure giornaliere: e' una lettura oraria, quindi quel drawdown vive su una griglia piu' fine della tabella dei giorni.
La tensione del giorno e' fra le scale: TSMOM 3/3 su su entrambi gli asset, ma 1g e 7g negativi (BTC -2.17%, ETH -4.63% a 7g) contro 30g ancora +21.78% e +28.63%. Il libro resta long perche' la gamba lunga tiene; una lettura altrettanto compatibile e' che i 30g stiano semplicemente restituendo il rialzo accumulato. Quale delle due, non e' misurabile qui.
Insolito il pacchetto volatilita': implicita sotto la realizzata su entrambi (37.2 contro 42.9, 50.9 contro 68.5) con DVOL al 16° e 17° percentile dell'anno, mentre il gate di VRP01 legge l'IV-rank (0.06 e 0.12) e resta fermo — guarda una cosa diversa da quello scarto.
Meriterebbe un occhio umano il perche' il $-4.95 arrivi con salute perfetta, 24/24 giri e feed a 0 min. Il campione resta 71 giorni e 30 round-trip, con $+48.37 di trading sui $+1,447.76 cumulati.
## Nota
*(vuota — campo dell'operatore)*
+58
View File
@@ -0,0 +1,58 @@
# Giornale di bordo — 2026-09-03
*Scritto 2026-09-04T00:37:16+00:00. Numeri misurati; nessuna interpretazione automatica.*
## Mercato
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|---|---|---|---|---|---|---|---|
| **BTC** | $81,286.00 | +5.14% | +1.27% | +26.90% | 45.5% | ↑ ↑ ↑ (3/3 su) | 39.8 (38° pctl 1a) |
| **ETH** | $2,507.90 | +4.87% | -0.15% | +34.20% | 69.8% | ↑ ↑ ↑ (3/3 su) | 53.3 (27° pctl 1a) |
## Libro
Equity **$2,074.42** · nozionale lordo $562 · leva lorda 0.27x · barra dati 2026-09-03 · 24 giri
| | TP01 | SKH01 | target | posizione | azione |
|---|---|---|---|---|---|
| **BTC** | +0.453 | flat | $+352 | $+349 | HOLD (a target) |
| **ETH** | +0.274 | flat | $+213 | $+213 | HOLD (a target) |
## P&L
- giorno: **$+28.60** di equity (24 letture)
- realizzato: -0.14 netto su 4 round-trip chiusi · 4 fill · fee 0.0095
- cumulato dall'arming: **$+1,476.36** di equity — di cui $+1,399.39 versati/prelevati -> trading **$+76.97** · TWR **+12.02%**
## Salute
- giri di `book_execute`: 24/24
- eta' feed SKH all'ultimo giro: 0 min
## Lettura
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
- `[stato]` A mercato su BTC $+349 (TP01 +0.453, SKH01 flat) e ETH $+213 (TP01 +0.274, SKH01 flat).
- `[leva]` Leva lorda **0.27x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.73x.
- `[pnl]` Equity $+28.60 con 4 fill e 4 round-trip chiusi (realizzato $-0.14 netto).
- `[vol]` BTC: implicita 39.8 contro realizzata 30g 45.5 (sotto); DVOL al 38° pctl di un anno; IV-rank espandente 0.14 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[vol]` ETH: implicita 53.3 contro realizzata 30g 69.8 (sotto); DVOL al 27° pctl di un anno; IV-rank espandente 0.14 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[evento]` 📌 BTC: giornata a +5.14%, **2.2 deviazioni** giornaliere (sd implicita dalla RV30 = 2.38%).
- `[evidenza]` 📌 Campione a oggi: **72 giorni, 34 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
## Analisi (agente)
*Scritta da `claude-opus-5` il 2026-09-04T00:38:30+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
Il fatto nuovo non e' il segno, e' la taglia: $+28.60 e un'equity a $2,074.42, la piu' alta delle dieci sedute mostrate, dopo giorni in cui il delta si e' mosso fra 17.87 e +12.06. Arriva da un BTC a +5.14%, 2.2 deviazioni, mentre il libro era gia' a target su entrambe le gambe: con nozionale lordo $562 e leva 0.27x, l'aumento e' rivalutazione di una posizione ferma, non l'esito di una decisione presa oggi. Il realizzato lo conferma — 0.14 netto su 4 round-trip, rumore di fee (0.0095) — e le posizioni restano $+349 e $+213, in linea con le sedute precedenti.
Insolito il quadro vol: entrambe le implicite sotto la realizzata 30g (39.8 contro 45.5, 53.3 contro 69.8), coi DVOL al 38° e 27° percentile dell'anno, in una giornata a due deviazioni. Non misurabile qui se sia salita la realizzata oggi o se l'implicita non abbia seguito.
Lettura alternativa, ugualmente compatibile: BTC fa +5.14% in un giorno ma +1.27% in sette, ed ETH 0.15%; buona parte del guadagno e' recupero dentro la settimana, non nuova direzione, benche' TSMOM sia 3/3 su su entrambi.
Un solo giorno pesa molto su un trading da $+76.97 in 72 giorni e 34 round-trip. Varrebbe un controllo umano sui 4 fill di una giornata chiusa a target: dovrebbero essere ri-taglie.
## Nota
*(vuota — campo dell'operatore)*
+58
View File
@@ -0,0 +1,58 @@
# Giornale di bordo — 2026-09-04
*Scritto 2026-09-05T00:36:37+00:00. Numeri misurati; nessuna interpretazione automatica.*
## Mercato
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|---|---|---|---|---|---|---|---|
| **BTC** | $79,660.50 | -2.00% | +2.33% | +23.30% | 46.6% | ↑ ↑ ↑ (3/3 su) | 38.0 (24° pctl 1a) |
| **ETH** | $2,457.05 | -2.03% | +0.56% | +28.81% | 70.5% | ↑ ↑ ↑ (3/3 su) | 51.1 (18° pctl 1a) |
## Libro
Equity **$4,483.73** · nozionale lordo $1,173 · leva lorda 0.26x · barra dati 2026-09-04 · 24 giri
| | TP01 | SKH01 | target | posizione | azione |
|---|---|---|---|---|---|
| **BTC** | +0.428 | flat | $+719 | $+717 | HOLD (a target) |
| **ETH** | +0.270 | flat | $+455 | $+456 | HOLD (a target) |
## P&L
- giorno: **$+2,409.31** di equity (24 letture) — di cui **$+2,410.14 movimenti di capitale** -> trading **$-0.83**
- realizzato: -0.02 netto su 2 round-trip chiusi · 5 fill · fee 0.2328
- cumulato dall'arming: **$+3,885.67** di equity — di cui $+3,809.53 versati/prelevati -> trading **$+76.14** · TWR **+11.70%**
## Salute
- giri di `book_execute`: 24/24
- eta' feed SKH all'ultimo giro: 0 min
## Lettura
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
- `[movimento]` 📌 **Movimento di capitale $+2,410.14** fra le 13:47 e le 14:47 UTC: salto +116.8% contro un massimo spiegabile dal mercato di ±0.6% al tetto di leva. Scorporato dal P&L di trading qui sotto.
- `[stato]` A mercato su BTC $+717 (TP01 +0.428, SKH01 flat) e ETH $+456 (TP01 +0.270, SKH01 flat).
- `[leva]` Leva lorda **0.26x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.74x.
- `[pnl]` P&L di trading $-0.83 (equity $+2409.31 al lordo del movimento di capitale) con 5 fill e 2 round-trip chiusi (realizzato $-0.02 netto).
- `[vol]` BTC: implicita 38.0 contro realizzata 30g 46.6 (sotto); DVOL al 24° pctl di un anno; IV-rank espandente 0.08 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[vol]` ETH: implicita 51.1 contro realizzata 30g 70.5 (sotto); DVOL al 18° pctl di un anno; IV-rank espandente 0.12 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[evidenza]` 📌 Campione a oggi: **73 giorni, 36 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
## Analisi (agente)
*Scritta da `claude-opus-5` il 2026-09-05T00:37:58+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
Il fatto della giornata non è il mercato ma la cassa: l'equity passa da $2,074.42 a $4,483.73, e il salto di $+2,410.14 fra le 13:47 e le 14:47 UTC è il secondo dopo il delta +1,414.57 del 25/08. Il libro l'ha assorbito subito: le posizioni, ferme per dieci giorni fra $+344 e $+364 su BTC e fra $+205 e $+218 su ETH, oggi valgono $+717 e $+456, entrambe a target, a leva lorda 0.26x su un tetto di 1.00x. Nessuna scelta discrezionale: TP01 +0.428 e +0.270, SKH01 flat.
Il numero da maneggiare con cura è il $-0.83 di trading. BTC e ETH chiudono a -2.00% e -2.03% con $1,173 di nozionale lordo lungo: l'intervallo che contiene il versamento esce intero dal conteggio, quindi quel $-0.83 non copre la giornata piena. Il realizzato, $-0.02 netto su 2 round-trip e 0.2328 di fee, è compatibile sia con una giornata quasi neutra sia con una perdita di mercato finita fuori dal segmento — qui non si separa.
Insolito il quadro vol: implicita sotto la realizzata su entrambi (38.0 contro 46.6; 51.1 contro 70.5), DVOL al 24° e 18° percentile; VRP01 resta fermo per l'IV-rank (0.08 e 0.12), non per questo.
Salute piena, 24/24 giri e feed a 0 min; campione 73 giorni e 36 round-trip. Varrebbe che l'operatore confermi a mano importo e ora del versamento: la Nota è vuota.
## Nota
*(vuota — campo dell'operatore)*
+57
View File
@@ -0,0 +1,57 @@
# Giornale di bordo — 2026-09-05
*Scritto 2026-09-06T00:37:28+00:00. Numeri misurati; nessuna interpretazione automatica.*
## Mercato
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|---|---|---|---|---|---|---|---|
| **BTC** | $79,835.00 | +0.22% | +2.03% | +24.19% | 46.4% | ↑ ↑ ↑ (3/3 su) | 38.7 (29° pctl 1a) |
| **ETH** | $2,481.10 | +0.98% | +0.93% | +30.40% | 70.4% | ↑ ↑ ↑ (3/3 su) | 51.7 (21° pctl 1a) |
## Libro
Equity **$4,490.66** · nozionale lordo $1,161 · leva lorda 0.26x · barra dati 2026-09-05 · 24 giri
| | TP01 | SKH01 | target | posizione | azione |
|---|---|---|---|---|---|
| **BTC** | +0.420 | flat | $+708 | $+711 | HOLD (a target) |
| **ETH** | +0.268 | flat | $+451 | $+450 | HOLD (a target) |
## P&L
- giorno: **$+6.93** di equity (24 letture)
- realizzato: -0.16 netto su 3 round-trip chiusi · 3 fill · fee 0.0068
- cumulato dall'arming: **$+3,892.60** di equity — di cui $+3,809.53 versati/prelevati -> trading **$+83.07** · TWR **+11.87%**
## Salute
- giri di `book_execute`: 24/24
- eta' feed SKH all'ultimo giro: 0 min
## Lettura
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
- `[stato]` A mercato su BTC $+711 (TP01 +0.420, SKH01 flat) e ETH $+450 (TP01 +0.268, SKH01 flat).
- `[leva]` Leva lorda **0.26x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.74x.
- `[pnl]` Equity $+6.93 con 3 fill e 3 round-trip chiusi (realizzato $-0.16 netto).
- `[vol]` BTC: implicita 38.7 contro realizzata 30g 46.4 (sotto); DVOL al 29° pctl di un anno; IV-rank espandente 0.10 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[vol]` ETH: implicita 51.7 contro realizzata 30g 70.4 (sotto); DVOL al 21° pctl di un anno; IV-rank espandente 0.13 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[evidenza]` 📌 Campione a oggi: **74 giorni, 39 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
## Analisi (agente)
*Scritta da `claude-opus-5` il 2026-09-06T00:38:25+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
Il fatto della giornata non è di oggi: il 04/09 l'equity è passata da $2,074.42 a $4,483.73, +$2,409.31 in un giorno, e questa è la prima pagina intera scritta sul libro nuovo. La forma non è cambiata — leva lorda 0.26x, HOLD a target su entrambe le gambe, TP01 +0.420 e +0.268, SKH01 flat su tutte e due — ma le posizioni sì: $+711 e $+450 contro i $+347 e $+213 del 02/09. È cambiata la taglia, non il comportamento; e il $+6.93 di oggi va letto su una base doppia, dove la stessa cifra in dollari pesa meno di quanto pesava a fine agosto.
Il cumulato lo dice più chiaramente: $+3,892.60, di cui $+3,809.53 versati o prelevati e $+83.07 di trading in 74 giorni e 39 round-trip. La cifra citabile resta il TWR, +11.87%.
Insolito, e non spiegabile da questa pagina: implicita sotto realizzata su entrambi (38.7 contro 46.4, 51.7 contro 70.4) con DVOL al 29° e 21° percentile, mentre i 30 giorni segnano +24.19% e +30.40%. Lettura alternativa altrettanto compatibile col +$6.93: il mark-to-market di due long in una giornata salita (+0.22% e +0.98%), non un merito di selezione — con 3 fill e realizzato $-0.16 netto, qui non è distinguibile.
Un controllo umano lo merita il movimento del 04/09: che abbia la sua riga di giornale, non ricostruita dopo.
## Nota
*(vuota — campo dell'operatore)*
+57
View File
@@ -0,0 +1,57 @@
# Giornale di bordo — 2026-09-06
*Scritto 2026-09-07T00:36:43+00:00. Numeri misurati; nessuna interpretazione automatica.*
## Mercato
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|---|---|---|---|---|---|---|---|
| **BTC** | $80,376.00 | +0.68% | +3.44% | +23.88% | 46.4% | ↑ ↑ ↑ (3/3 su) | 39.3 (35° pctl 1a) |
| **ETH** | $2,515.10 | +1.37% | +4.04% | +31.44% | 70.4% | ↑ ↑ ↑ (3/3 su) | 53.2 (27° pctl 1a) |
## Libro
Equity **$4,502.93** · nozionale lordo $1,175 · leva lorda 0.26x · barra dati 2026-09-06 · 24 giri
| | TP01 | SKH01 | target | posizione | azione |
|---|---|---|---|---|---|
| **BTC** | +0.420 | flat | $+710 | $+717 | SELL $-7 |
| **ETH** | +0.268 | flat | $+453 | $+458 | HOLD (a target) |
## P&L
- giorno: **$+12.27** di equity (24 letture)
- realizzato: +0.13 netto su 1 round-trip chiusi · 1 fill · fee 0.0028
- cumulato dall'arming: **$+3,904.87** di equity — di cui $+3,834.43 versati/prelevati -> trading **$+70.44** · TWR **+8.02%**
## Salute
- giri di `book_execute`: 24/24
- eta' feed SKH all'ultimo giro: 0 min
## Lettura
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
- `[stato]` A mercato su BTC $+717 (TP01 +0.420, SKH01 flat) e ETH $+458 (TP01 +0.268, SKH01 flat).
- `[leva]` Leva lorda **0.26x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.74x.
- `[pnl]` Equity $+12.27 con 1 fill e 1 round-trip chiusi (realizzato $+0.13 netto).
- `[vol]` BTC: implicita 39.3 contro realizzata 30g 46.4 (sotto); DVOL al 35° pctl di un anno; IV-rank espandente 0.12 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[vol]` ETH: implicita 53.2 contro realizzata 30g 70.4 (sotto); DVOL al 27° pctl di un anno; IV-rank espandente 0.14 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[evidenza]` 📌 Campione a oggi: **75 giorni, 40 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
## Analisi (agente)
*Scritta da `claude-opus-5` il 2026-09-07T00:37:44+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
Il fatto del giorno non e' il $+12.27: e' che dopo il salto del 04/09 il libro ha ripreso la sua forma abituale a una taglia nuova. Le posizioni sono cresciute con l'equity ($+717 e $+458 contro $+349 e $+213 del 03/09), non col segnale: TP01 sta a 0.420 e 0.268, parziale su entrambe le gambe, e la leva lorda resta 0.26x su un tetto di 1.00x. SKH01 e' flat su BTC e su ETH — il quarto del libro che fa breakout oggi non porta nulla, e da quanti giorni sia cosi' non e' misurabile qui.
L'attivita' e' scesa: 1 fill e 1 round-trip contro i 3-5 dei giorni scorsi, e l'unica azione e' un SELL $-7 su una posizione da $+717. Vale un'occhiata umana, senza allarme: le soglie d'ordine sono in dollari assoluti e l'equity ha cambiato scala.
Cumulato $+3,904.87, di cui $+3,834.43 versati; il trading vale $+70.44 su 75 giorni e 40 round-trip — abbastanza poco che un movimento di capitale scorporato male se lo mangerebbe intero.
Una lettura alternativa dello stesso dato: implicite sotto le realizzate su entrambi (39.3 contro 46.4, 53.2 contro 70.4) e DVOL al 35° e 27° percentile dopo trenta giorni a +23.88% e +31.44%. Le regole ci leggono il gate VRP01 chiuso; e' altrettanto compatibile un rialzo che il mercato delle opzioni non sta pagando.
## Nota
*(vuota — campo dell'operatore)*
+57
View File
@@ -0,0 +1,57 @@
# Giornale di bordo — 2026-09-07
*Scritto 2026-09-08T00:36:58+00:00. Numeri misurati; nessuna interpretazione automatica.*
## Mercato
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|---|---|---|---|---|---|---|---|
| **BTC** | $79,100.00 | -1.59% | +0.68% | +21.85% | 47.1% | ↑ ↑ ↑ (3/3 su) | 38.8 (31° pctl 1a) |
| **ETH** | $2,490.35 | -0.98% | +0.91% | +29.98% | 70.6% | ↑ ↑ ↑ (3/3 su) | 52.6 (25° pctl 1a) |
## Libro
Equity **$4,484.18** · nozionale lordo $1,156 · leva lorda 0.26x · barra dati 2026-09-07 · 24 giri
| | TP01 | SKH01 | target | posizione | azione |
|---|---|---|---|---|---|
| **BTC** | +0.420 | flat | $+707 | $+704 | HOLD (a target) |
| **ETH** | +0.268 | flat | $+451 | $+452 | HOLD (a target) |
## P&L
- giorno: **$-18.75** di equity (24 letture)
- realizzato: +0.10 netto su 1 round-trip chiusi · 3 fill · fee 0.0066
- cumulato dall'arming: **$+3,886.12** di equity — di cui $+3,834.43 versati/prelevati -> trading **$+51.69** · TWR **+7.57%**
## Salute
- giri di `book_execute`: 24/24
- eta' feed SKH all'ultimo giro: 0 min
## Lettura
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
- `[stato]` A mercato su BTC $+704 (TP01 +0.420, SKH01 flat) e ETH $+452 (TP01 +0.268, SKH01 flat).
- `[leva]` Leva lorda **0.26x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.74x.
- `[pnl]` Equity $-18.75 con 3 fill e 1 round-trip chiusi (realizzato $+0.10 netto).
- `[vol]` BTC: implicita 38.8 contro realizzata 30g 47.1 (sotto); DVOL al 31° pctl di un anno; IV-rank espandente 0.11 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[vol]` ETH: implicita 52.6 contro realizzata 30g 70.6 (sotto); DVOL al 25° pctl di un anno; IV-rank espandente 0.13 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[evidenza]` 📌 Campione a oggi: **76 giorni, 41 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
## Analisi (agente)
*Scritta da `claude-opus-5` il 2026-09-08T00:37:50+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
La giornata rompe tre chiusure positive di fila (+28.60, +6.93, +12.27) con $-18.75, la perdita giornaliera più grande della serie qui riportata. È quasi tutta valutazione, non attività: 3 fill, 1 round-trip, realizzato $+0.10 netto, con BTC -1.59% e ETH -0.98% e il libro long su entrambi. Il segno torna; la taglia non è verificabile qui, perché la pagina non scompone il P&L per asset.
Ciò che è cambiato davvero risale al salto del 04/09: le posizioni sono passate dai $+357 / $+214 di fine agosto ai $+704 / $+452 di oggi e ci restano, mentre la leva lorda resta 0.26x. A parità di rischio relativo la stessa percentuale di mercato si scrive con un numero di dollari più grande: le prossime giornate rosse sembreranno peggiori senza esserlo.
Insolito il quadro della volatilità: implicita sotto la realizzata su entrambi (38.8 contro 47.1; 52.6 contro 70.6) con DVOL al 31° e al 25° percentile di un anno. Il gate di VRP01 legge l'IV-rank (0.11 e 0.13) e resta chiuso, ma una lettura altrettanto compatibile è che il mercato tratti il movimento a 30 giorni (+21.85%, +29.98%) come già avvenuto.
TP01 porta segnali parziali (0.420, 0.268) e SKH01 è flat malgrado TSMOM 3/3 su: c'è tendenza, non breakout. Il cumulato $+3,886.12 è quasi tutto versato ($+3,834.43); resta $+51.69 di trading su 76 giorni e 41 round-trip, TWR +7.57% — campione che non separa edge e fortuna. Salute piena, 24/24 giri e feed a 0 min: niente da controllare a mano oggi.
## Nota
*(vuota — campo dell'operatore)*
+58
View File
@@ -0,0 +1,58 @@
# Giornale di bordo — 2026-09-08
*Scritto 2026-09-09T00:36:48+00:00. Numeri misurati; nessuna interpretazione automatica.*
## Mercato
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|---|---|---|---|---|---|---|---|
| **BTC** | $78,470.50 | -0.80% | +1.34% | +20.97% | 47.3% | ↑ ↑ ↑ (3/3 su) | 39.9 (40° pctl 1a) |
| **ETH** | $2,485.45 | -0.20% | +2.77% | +30.16% | 70.6% | ↑ ↑ ↑ (3/3 su) | 53.9 (33° pctl 1a) |
## Libro
Equity **$4,477.46** · nozionale lordo $1,149 · leva lorda 0.26x · barra dati 2026-09-08 · 24 giri
| | TP01 | SKH01 | target | posizione | azione |
|---|---|---|---|---|---|
| **BTC** | +0.415 | flat | $+697 | $+698 | HOLD (a target) |
| **ETH** | +0.268 | flat | $+449 | $+451 | HOLD (a target) |
## P&L
- giorno: **$-6.72** di equity (24 letture)
- realizzato: +0.00 netto su 1 round-trip chiusi · 2 fill · fee 0.0055
- cumulato dall'arming: **$+3,879.40** di equity — di cui $+3,834.43 versati/prelevati -> trading **$+44.97** · TWR **+7.41%**
## Salute
- giri di `book_execute`: 24/24
- eta' feed SKH all'ultimo giro: 0 min
## Lettura
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
- `[stato]` A mercato su BTC $+698 (TP01 +0.415, SKH01 flat) e ETH $+451 (TP01 +0.268, SKH01 flat).
- `[leva]` Leva lorda **0.26x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.74x.
- `[pnl]` Equity $-6.72 con 2 fill e 1 round-trip chiusi (realizzato $+0.00 netto).
- `[drawdown]` 📌 Equity **-0.57%** sotto il picco ($4,502.93). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
- `[vol]` BTC: implicita 39.9 contro realizzata 30g 47.3 (sotto); DVOL al 40° pctl di un anno; IV-rank espandente 0.14 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[vol]` ETH: implicita 53.9 contro realizzata 30g 70.6 (sotto); DVOL al 33° pctl di un anno; IV-rank espandente 0.15 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[evidenza]` 📌 Campione a oggi: **77 giorni, 42 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
## Analisi (agente)
*Scritta da `claude-opus-5` il 2026-09-09T00:37:27+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
Terzo giorno sotto il picco del 06/09 ($4,502.93), e secondo consecutivo in rosso dopo i $-18.75 del 07/09: oggi $-6.72. La differenza rispetto alla settimana precedente al versamento non e' il segno ma la scala — le posizioni sono passate da BTC $+347/ETH $+213 a BTC $+698/ETH $+451, quindi oscillazioni di equity dello stesso ordine in dollari valgono ora meno in percentuale. Con realizzato $+0.00 su 1 round-trip e HOLD a target su entrambe le gambe, la perdita del giorno e' rivalutazione delle posizioni aperte contro BTC -0.80% ed ETH -0.20%, non attivita' di trading.
L'elemento insolito e' altrove: TSMOM 3/3 su su entrambi gli asset, BTC +20.97% e ETH +30.16% a 30 giorni, e nonostante questo TP01 sta a +0.415 e +0.268 con SKH01 flat — leva lorda 0.26x su un tetto di 1.00x. Il libro e' poco investito dentro il trend che dovrebbe essere il suo caso favorevole; se questo sia disciplina di sizing o segnale debole non e' misurabile qui.
Lettura alternativa altrettanto compatibile: a 0.26x di esposizione il P&L giornaliero e' quasi interamente beta di mercato, e i 77 giorni / 42 round-trip non permettono di distinguerlo dall'edge.
Merita un'occhiata umana il round-trip chiuso con realizzato esattamente $+0.00 a fronte di fee 0.0055.
## Nota
*(vuota — campo dell'operatore)*
+58
View File
@@ -0,0 +1,58 @@
# Giornale di bordo — 2026-09-09
*Scritto 2026-09-10T00:38:03+00:00. Numeri misurati; nessuna interpretazione automatica.*
## Mercato
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|---|---|---|---|---|---|---|---|
| **BTC** | $78,283.50 | -0.24% | +1.26% | +22.45% | 46.8% | ↑ ↑ ↑ (3/3 su) | 40.2 (42° pctl 1a) |
| **ETH** | $2,467.95 | -0.70% | +3.19% | +31.89% | 70.1% | ↑ ↑ ↑ (3/3 su) | 53.7 (32° pctl 1a) |
## Libro
Equity **$4,472.10** · nozionale lordo $1,157 · leva lorda 0.26x · barra dati 2026-09-09 · 24 giri
| | TP01 | SKH01 | target | posizione | azione |
|---|---|---|---|---|---|
| **BTC** | +0.418 | flat | $+701 | $+704 | HOLD (a target) |
| **ETH** | +0.270 | flat | $+453 | $+453 | HOLD (a target) |
## P&L
- giorno: **$-5.36** di equity (24 letture)
- realizzato: +0.00 netto su 0 round-trip chiusi · 2 fill · fee 0.0047
- cumulato dall'arming: **$+3,874.04** di equity — di cui $+3,834.43 versati/prelevati -> trading **$+39.61** · TWR **+7.28%**
## Salute
- giri di `book_execute`: 24/24
- eta' feed SKH all'ultimo giro: 0 min
## Lettura
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
- `[stato]` A mercato su BTC $+704 (TP01 +0.418, SKH01 flat) e ETH $+453 (TP01 +0.270, SKH01 flat).
- `[leva]` Leva lorda **0.26x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.74x.
- `[pnl]` Equity $-5.36 con 2 fill e 0 round-trip chiusi (realizzato $+0.00 netto).
- `[drawdown]` 📌 Equity **-0.68%** sotto il picco ($4,502.93). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
- `[vol]` BTC: implicita 40.2 contro realizzata 30g 46.8 (sotto); DVOL al 42° pctl di un anno; IV-rank espandente 0.14 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[vol]` ETH: implicita 53.7 contro realizzata 30g 70.1 (sotto); DVOL al 32° pctl di un anno; IV-rank espandente 0.15 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
- `[evidenza]` 📌 Campione a oggi: **78 giorni, 42 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
## Analisi (agente)
*Scritta da `claude-opus-5` il 2026-09-10T00:39:20+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
Il fatto nuovo non e' il segno del giorno ma la sua composizione: $-5.36 di equity con 2 fill, zero round-trip chiusi e realizzato $+0.00. La perdita e' tutta marcatura su posizioni aperte, e le posizioni sono a target su entrambe le gambe ($+704 contro $+701, $+453 contro $+453): il libro non ha deciso niente, ha ri-tagliato. Nella serie qui sopra l'unico altro giorno senza round-trip chiusi e' il 01/09; e' il terzo giorno di equity in discesa dopo il picco del 06/09 a $4,502.93, con BTC -0.24% e ETH -0.70%.
L'esposizione la porta interamente TP01 (+0.418 su BTC, +0.270 su ETH) mentre SKH01 e' flat su entrambi, e questo con TSMOM 3/3 su e 30 giorni a +22.45% e +31.89%: il trend c'e', il breakout no. Esecuzione pulita, 24/24 giri e feed SKH a 0 min.
L'implicita sotto la realizzata (40.2 contro 46.8; 53.7 contro 70.1) si legge in due modi ugualmente compatibili — vol a sconto, oppure realizzata gonfiata dalla corsa dei 30 giorni. Quale dei due, non e' misurabile qui.
Da guardare senza urgenza: il cumulato $+3,874.04 e' quasi tutto versamento ($+3,834.43), il trading vale $+39.61 su 42 round-trip in 78 giorni. A quella taglia il TWR +7.28% non e' ancora una misura di edge.
## Nota
*(vuota — campo dell'operatore)*
+370
View File
@@ -0,0 +1,370 @@
# Sleeve, book e candidati
> Estratto **verbatim** da `CLAUDE.md` il 2026-08-25 durante la compattazione.
> Le cinque gambe del book, il portafoglio attivo e i candidati in forward-monitor.
Ogni numero va letto con la sua banda d'ancora e la sua lente.
> Il testo non e' stato riscritto: e' la memoria originale, spostata.
---
- **TP01 Trend Portfolio — strategia DIFENSIVA robusta (non alpha)** —
`src/strategies/trend_portfolio.py`. TSMOM multi-orizzonte (1-3-6 mesi) vol-targeted, long-flat,
50/50 BTC+ETH. Config canonica **PORT LF1d** (**>=12h, 1d raccomandato**, vol-target 20%, leva cap 2x):
**FULL Sharpe ~1.30, maxDD ~14%; HOLD-OUT 2025-26 Sharpe ~0.31 / +3.5%** mentre il buy&hold 50/50
faceva 39%/DD60%. Verificata indipendentemente col gauntlet onesto (hold-out + cross-asset +
plateau + deflated-Sharpe 0.999): **regge**. **Valore = taglio del drawdown ~6× vs buy&hold**, NON
generazione di ritorno (CAGR ~16% vs ~48% del buy&hold sul toro).
⚠️ **LOOK-AHEAD (2026-06-19):** un ffill MIXED-TIMEFRAME su barre open-labeled gonfiava il 4h
(~1.60 → reale ~1.1). Il calcolo per-singolo-TF è leak-free, ma **NON scendere sotto le 12h**:
costi+overfitting dominano senza vantaggio (FULL Sh piatto ~1.3 da 12h a 4h; hold-out migliore a 1d).
Deploy/paper a **1d**. Diari `2026-06-19-tp01-verification.md` / `-tp01-lookahead-fix-lf.md`.
Paper/forward: `scripts/live/paper_portfolio.py` (TP01+XS01, 1d). Test: `tests/test_trend_portfolio.py`.
(Il vecchio `paper_trend.py` standalone TP01 è RITIRATO in `Old/scripts/live/` 2026-07-17 — inerte
dal 2026-06-19, non in cron, sostituito da `paper_portfolio`.)
Ri-verifica: `scripts/analysis/{verify_tp01,stress_tp01,tp01_lowfreq}.py`.
⚠️ **ANCHOR TIMING-LUCK (2026-07-02, confermato da scettico):** l'hold-out ~0.31 è calcolato
sull'ancora daily 00:00 UTC, che è la **migliore delle 24 possibili** (mediana ancore 0.04, banda
[0.13,+0.30]; P~0.86 che una qualsiasi ancora mostri uno spike così per puro caso) → l'hold-out
2025-26 NON risolve l'edge di ritorno di TP01; ciò che regge a OGNI ancora è il **taglio del DD**
(7-10% vs ~60% B&H). FULL/plateau/deflated-Sharpe/gate INVARIATI (h=0 al 31° pctl su FULL).
Regola: i futuri numeri hold-out di strategie a ribilanciamento ancorato si citano con la banda
d'ancora. Diario `2026-07-02-timing-crt-wave.md`; script `scripts/research/r0702_tp01_offset.py`
+ `r0702_skeptic_offset.py`.
- **XS01 Cross-Sectional Momentum (Hyperliquid) — DIVERSIFICATORE che migliora il portafoglio** —
`src/portfolio/sleeves.py:_xsec_returns`. Market-neutral su **19 alt liquidi major** Hyperliquid (1d,
dal 2024): ogni 10g long i 5 più forti / short i 5 più deboli, vol-target 20%. **Scorrelato a TP01
(~0.12).** Affinato (2026-06-19): **(a) blend di lookback [30,90]** (z-score cross-sectional mediato,
come il multi-orizzonte di TP01); **(b) gate di dispersione p30** (entra solo se la dispersione
cross-section del momentum supera il percentile espandente causale, altrimenti flat — XS è rumore in
regime compatto). Standalone FULL Sh **1.50** / HOLD 1.71 / DD 11%, plateau robusto (lookback, gate
p15-35). **Caveat:** storia ~2.5 anni; STAT-MODE (book a 19 gambe non eseguibile a 2k, serve ~20k) →
monitor forward. NB il gate concentra XS nei regimi dispersi (2025-26 = hold-out alta-dispersione).
⚠️ **PHASE TIMING-LUCK (2026-07-02):** i numeri headline sono sulla fase 0 del ciclo H=10, che è
al **15° pctl di DD** (10.8% vs ~15.5% fase tipica, 29% peggiore) e 85° di FULL fra le 10 fasi
(HOLD solo 65°, non estremo); P(spike per caso)≈0.91-0.94. Lens onesta = **ensemble di fase:
FULL 1.25 / HOLD 1.31 / DD 10.9%**; a fase mediana FULL 1.08/HOLD 1.10/DD 21%. La decisione di
ammissione @15% regge (0 fasi negative, 8/10 FULL≥1.0), i numeri 1.50/1.71/11% no → citarli con
banda di fase. Ora-del-giorno NON testabile (solo 1d HL). Script `scripts/research/r0702_anchor_xs01.py`;
diario `2026-07-02-anchor-audit-xs01-skh01.md`.
Ricerca `scripts/portfolio/{xsec_research,xsec_blend,xsec_dispgate}.py`. Diari `2026-06-19-hyperliquid-xsec`
/ `-xsec-blend` / `-xsec-dispgate` / `-xsec-universe-expansion` / `-trend-multiasset`.
- **PORTAFOGLIO ATTIVO = TP01 (33%) + XS01 (15%) + VRP01 (12%) + SKH01 (20%) + GTAA01 (20%)**
(`src/portfolio/sleeves.active_sleeves`): TP01+XS01 combinato **FULL Sharpe 1.55, HOLD-OUT 1.55,
DD 4.4%**. Aggiunto **VRP01** (options short-vol, sotto): TP01+VRP01 da solo fa FULL Sh 1.30→1.44 /
HOLD 0.31→0.40 a peso 20%. **Aggiunto SKH01-V2-DD @25% effettivo (2026-06-23, sotto)**: 4-sleeve
**FULL Sharpe 1.68→2.13, HOLD-OUT 1.63→2.30, DD full 14.3%→7.8%** (Skyhook quasi-ortogonale,
corr ~0.09). **Aggiunto GTAA01 @20% effettivo (2026-07-01, i 4 preesistenti scalati ×0.80):**
trend difensivo equity 6-ETF su IB (`src/portfolio/gtaa.py`, ~30 anni storia, validato 2026-06-22
su OOS equity 2015+ INDIPENDENTE dall'hold-out crypto, corr al book ~+0.10) → 5-sleeve
**FULL Sharpe 2.12→2.24, HOLD-OUT 2.21→2.46, DD full 7.8%→6.2%** (costo dichiarato: CAGR full
23.3→18.8%; 2022 unico anno con dSh). Uplift positivo in-sample E su tutte le finestre disgiunte
(vs EW-STR refutato lo stesso giorno). Convenzioni: weekend/festivi equity = 0.0 (capitale IB
fermo, non riciclato); attivazione nel book all'era crypto 2019-03; **il book live Deribit
(`deribit_book_sleeves` TP01+SKH01 75/25) NON lo include** (GTAA in paper_combo dal 2026-06-23).
Test `tests/test_gtaa_sleeve.py`; diario `2026-07-01-strategy-wave-6threads.md` (addendum GTAA).
Report `scripts/portfolio/run_portfolio.py`. Sleeve a date d'inizio diverse → outer-join con pesi
rinormalizzati (TP01/SKH01/GTAA dal 2019*, VRP dal 2021, XS dal 2024; *GTAA troncato all'era book).
⚠️ **ANCHOR-LUCK del book — MISURATO 2026-07-26 (la stima del 02/07 era ottimista su 3/3).**
I numeri canonici sono calcolati con TUTTI e cinque gli sleeve alla loro ancora canonica. Il
02/07 la correzione fu **stimata a occhio** sommando le fortune marginali (HOLD ~1.9-2.1, FULL
~2.0-2.2, "DD ~6% invariato"); il 26/07 è stata **misurata sullo spazio congiunto** (24×10×7×23×5
= 193.200 configurazioni, 2000 estrazioni uniformi indipendenti, `r0726_loo_deluck.py`):
| | canonico | pctl | **stima onesta = mediana** | banda p10-p90 |
|---|---|---|---|---|
| Sharpe FULL | +2.222 | 97.0° | **+1.95** | [1.81, 2.12] |
| Sharpe HOLD-OUT | +2.364 | **99.6°** | **+1.54** | [1.11, 1.91] |
| maxDD FULL | 6.07% | 12.0° | **6.85%** | [5.99, 7.94] |
**Solo 9 estrazioni su 2000 battono l'hold-out canonico.** Il book resta positivo e diversificato
a ogni ancora, ma **i numeri da citare sono 1.95 / 1.54 / 6.85%**, non i canonici.
⚠️ **Lezione: la somma delle fortune marginali NON è la mediana congiunta** — sbagliava di +0.82
di Sharpe hold-out, più di quasi tutti gli uplift per cui in questo progetto si è discusso se
ammettere uno sleeve. Quando serve la banda di un AGGREGATO si campiona lo spazio congiunto; gli
audit uno-sleeve-alla-volta (02/07, 03/07) restano validi per giudicare *quello* sleeve.
Diari `2026-07-02-anchor-audit-xs01-skh01.md` (originale) e `2026-07-26-loo-deluck.md` (misura).
🚨 **«POSITIVO IN N/N ANCORE» NON E' N OSSERVAZIONI — misurato 2026-08-23, e tocca OGNI bullet
di questo file che cita una banda d'ancora.** Correlazione media fra le **24 ancore di TP01
0,631 → N_eff 1,55**; fra le **10 fasi di XS01 0,790 → N_eff 1,23**. **E non si salva sulla
differenza appaiata:** la componente comune **non si cancella** (corr 0,593 → N_eff 1,64-2,24) —
era l'attesa a priori del critico ed e' stata **REFUTATA**. Sulla stessa grandezza, la **banda
d'ancora e' 4,3x piu' stretta dell'IC95 bootstrap, che CONTIENE LO ZERO** (su XS01 il rapporto e'
3,8x: due misure indipendenti, stesso fattore ~4).
⚠️ **Cio' che NON significa: che i verdetti cadano.** Le decisioni prese con l'audit d'ancora
poggiano anche su iso-vol, maxDD, selection-on-holdout ed edge lordo. **Cade la PRECISIONE dei
numeri, non la DIREZIONE delle decisioni.**
**REGOLE:** (a) «N/N ancore» si cita come **robustezza alla SCELTA dell'ancora**, ~2 osservazioni
indipendenti — **non come N prove**; (b) **la banda d'ancora NON e' un intervallo di confidenza**
e non va usata al posto di uno; (c) **non attaccare un p-value a un conteggio di ancore o di
finestre sovrapposte** (caso preso in fallo: un «test dei segni 12/12, p=0,0005» su finestre 30g
sovrapposte *e* selezionate come le peggiori).
- **SKH01-V2-DD "Skyhook" — DIVERSIFICATORE quasi-ortogonale (research)** — `src/strategies/skyhook.SKH01_V2_DD`,
sleeve `src/portfolio/sleeves._skyhook_returns`. Sistema dual-TF (segnale 690m / exec 230m) regime
(BuzVola/BuzVolume tipo-Chande) AND pattern (Donchian breakout), NON trend-follower, L/S. Vincitrice
di 2 onde multi-agente (la 2ª = DD-reduction): exit a **percentuale fissa ASIMMETRICA** (long sl4%/tp10%,
short sl2%/tp8% più stretto) → standalone **maxDD BTC 21% / ETH 27% (<30%)**, minFull +0.99, minHold
+1.26, causale (0/400), fee-surviving 0.40%RT. Marginal vs TP01 **ADDS** (corr 0.09, has_insample_edge,
robust_oos multicut 7/7, is_hedge=False); blend 0.75·TP01+0.25·SKH **hold-out 0.31→1.17**. Verificato
leak-free + 2 scettici. **CAVEAT:** equity daily-step (Sharpe lens), ETH DD margine sottile, book 230m
(costi ribilanciamento da verificare a deploy) → research win, forward-monitor. Diario `2026-06-23-skyhook.md`.
⚠️ **GRID TIMING-LUCK (2026-07-02, più forte di TP01):** i numeri headline sono sull'offset 0 della
griglia 230m/690m, al **93-98° pctl dei 23 offset a priori** — minHold +1.26, blend 1.17 e book
HOLD 2.44 sono il MASSIMO dei 23 (mediane: minHold +0.39, blend 0.72, book 1.96); spike, non
plateau (±30m crolla); P(spike)≈0.70. **Il gate DD<30% (criterio di selezione di V2-DD) fallisce
in 15/23 offset** (mediana ETH 29.2%). Regge de-luckato: uplift blend positivo a TUTTE le 23 fasi
(min +0.18, med +0.42) + corr 0.05-0.11 → ADDS sopravvive ridimensionato. **LIVE (SKH=25% del book
Deribit):** path reale cron orario + exit software → book 50/50 FULL 1.46→1.19 / HOLD 1.64→1.15 /
DD 18→25%; nei crash gap-through-stop reale (sl2% modellato → 11/23% realizzato). Pesi/book
INVARIATI (ogni cambio passa weights_tilt_null). **Follow-up CHIUSO 2026-07-24**
(`scripts/research/r0724_skh_live_weight.py`): sul path live (lente hourly, 23 offset) il peso
ottimale de-luckato È 0.25 (argmax mediana-IS di banda, plateau 0.20-0.30; w=0.30 passa il gate
solo a off0 = ancora fortunata, fallisce a offset mediano) e la cadenza 230m vale ~+0.01/+0.02 Sh
mediano (rumore: il degrado live è il fill-al-livello, ~+0.35 Sh, che nessun cron recupera) →
**book 75/25 e cron orario CONFERMATI**; diario `2026-07-24-skh-live-weight.md`.
Script `scripts/research/r0702_anchor_skh01.py`; diario `2026-07-02-anchor-audit-xs01-skh01.md`.
- **VRP01 Options Short-Vol — DIVERSIFICATORE da FinanceOld/OptionsAgent** — `src/portfolio/sleeves._vrp_combo_returns`.
Put credit spread settimanale (vendi put -0.28, compra put -0.10) gated su IV-rank. Idee portate da
`../FinanceOld/OptionsAgent` (Bear Call Spread + gate d'ingresso). Migliora il lead VRP nudo
(options_vrp_lab): **(a) defined-risk** taglia la coda (worst-week -16.6%→-7.4%, DD 33%→14%);
**(b) gate IV-rank>0.30** = vendi vol solo ricca → ribalta HOLD-OUT da -0.25 a +0.28 (l'alpha è il
filtro di regime). Standalone **FULL Sh 1.10, HOLD 0.60, DD 12%**, positivo/piatto ogni anno (2022
crash incluso). Scorrelato a TP01 (~+0.01-0.07). **CAVEAT:** premio MODELLATO su DVOL ATM (skew non
esplicito), book a 1d, f di stress reale non catturato → LEAD robusto, non deploy pieno. Ricerca
`scripts/research/options_vrp_v2.py` (vs baseline `options_vrp_lab.py`). Test `tests/test_vrp_sleeve.py`.
⚠️ **ANCHOR-AUDIT CHIUSO + ondata "migliora e proteggi" (2026-07-03, 7 filoni + 2 lenti + scettico):
VRP01 NON è migliorabile e la protezione DD si compra SOLO con la size.** (a) **Anchor-luck (ciclo
settimanale, 7 fasi): PRIMO sleeve SENZA firma di luck** — la fase canonica è la PEGGIORE delle 7 su
FULL (1.09 = 7° pctl) e su DD (11.8% = 93° pctl), mediana su HOLD (0.59); spike bootstrap NEGATIVO →
i numeri di ammissione FULL 1.10/HOLD 0.60/DD 12% sono CONSERVATIVI, non gonfiati. Da ora si citano
con banda: ShFULL [1.09,1.83], ShHOLD [0.03,1.11], DD [5.7,11.8%]; edge OOS f-dipendente (f=0.8 →
HOLD~0). **Con questo l'audit anchor è completo su 4/4 sleeve ancorati.** (b) Griglia 288 strutture:
nessuna batte VRP01 (DSR 0.000; metà griglia = 3ª occorrenza "0-perdite/Sharpe implausibile" dopo
CC01/ALB-A → gate `implausible_sharpe` alzato di priorità). (c) 4 overlay DD (exit-spike/SL-MTM/
ala-coda/cooldown): 4/4 REFUTED dal null de-levering — la protezione crash vive già nel gate
d'ingresso IV-rank. (d) Gate nuovi: 4° fallimento su 4 (l'alpha è il binario IV-rank>0.30). (e)
Sizing: 12% deploy ≈ 0.27 Kelly onesto (anti-rovina); ⚠️ NON confondere col 12% di PESO del book
(~0.014 Kelly, fattore 19x). (f) Gate term-structure VIX/VXV su SPX (ΔSh +0.90, DSR 0.992) =
**confound di modello al 100%** (la var del gate coincide con l'errore BS-flat vs term-structure) →
nuova regola: riprezzare term-structure-consistent prima di credere a un gate vol su strutture
BS-flat. Book/pesi INVARIATI. Diario `2026-07-03-vrp-improve-dd.md`; script `scripts/research/r0703_vrpimp_*.py` (7 file).
Diario `2026-06-20-financeold-analysis-vrp-v2.md`.
⚠️ **f MISURATO SULLE QUOTE REALI = 0.73, NON 1.0 (2026-07-30) — i numeri di ammissione vanno
citati col f.** Primo confronto del prezzatore del sleeve con la catena Deribit mainnet vera
(`scripts/research/r0730_vrp_real_quotes.py`, dati cerbero-bite). Su 8/8 scadenze settimanali con
ENTRAMBE le gambe quotate (delta realizzati 0.270/0.099 contro target 0.280/0.100), agli
STESSI strike: **f del credito NETTO 0.73** (IC95% bootstrap [0.698, 0.780], **0/15 osservazioni
≥1.0**; 0.80 a mid). **Meccanismo, non rumore:** IV(corta)DVOL **+0.8pp** ma IV(lunga)DVOL
**+7.5pp** → il modello prezza a vol ATM anche l'ala che si COMPRA, che costa **~2.3× il modello**
→ il credito netto si comprime del 27%. **Il difetto non è nel premio incassato ma nella
protezione comprata**, cioè proprio il "(a) defined-risk" per cui v2 fu promosso.
**Conseguenza standalone (solo f cambiato):** f=1.00 → FULL 1.08 / HOLD **+0.58**; f=0.80 → 0.51 /
**0.02**; f=0.73 → 0.31 / **0.23**. **Book 5-sleeve** (VRP01 12%, ancore canoniche): FULL
2.215→2.146 (Δ−0.069), HOLD 2.347→2.244 (Δ−0.103), DD invariato → **dentro la banda d'ancora**,
ma il LOO del 26/07 dava a VRP01 +0.122 di FULL: **~metà era il prezzo che il modello si faceva da
solo** (l'audit d'ancora corregge la fortuna di calendario, non l'ottimismo di prezzo).
⚠️ **La calibrazione del 20/06 NON è contraddetta, è replicata:** misurava la **sola gamba corta**
(qui 1.02, implementazione indipendente). **REGOLA: il f di una struttura multi-gamba non è il f
di una sua gamba — misurare la sola gamba venduta dà la risposta sbagliata con segno
rassicurante.** Congelata in `tests/test_cb_chain_vrp.py`.
**PROFIT-TAKE al 50% del credito — REFUTED (2026-07-30, 3° filone).**
`scripts/research/r0730_vrp_profit_take.py`, test `tests/test_vrp_profit_take.py` (16), diario
`2026-07-30-vrp-profit-take.md`. **Book/pesi/cron/config INVARIATI.** VRP01 tiene FINO A
SCADENZA (`S1 = px[i+tn]`, nessuna gestione infra-settimana) e il profit-take era l'unico grado
di liberta' non misurato: gli overlay del 03/07 erano tutti sul lato del RISCHIO. Replica del
sleeve **bit-exact** (max|diff| = 0.0) prima di ogni delta. Esito con **fee reali per gamba**:
canonico ShFULL **1.32** → PT25 0.22 / **PT50 0.18** / PT75 0.45; **null del de-levering
REFUTED 6/6** (a PT25 basta k=0.818 per lo stesso DD con Sharpe 1.32 invece di 0.22).
**Meccanismo (confronto appaiato per ingresso):** scatta sull'**86-91% dei VINCENTI** e sul
**19-25% dei perdenti** → tronca i vincenti del **27%** e salva un perdente su cinque; la
**peggior settimana e' IDENTICA** (7.27%) in ogni variante = pura troncatura dell'upside.
**Ipotesi mia refutata in sessione:** "su 7g chi tocca +50% e' chi sarebbe scaduto senza
valore, quindi a scadenze lunghe paga" — testata su 7/10/14/18/21/28g, **ΔSh negativo a tutti e
sei** (a 18g, il tenore di cerbero-bite, il DD migliora 7.1→3.8% ma lo Sharpe crolla = firma del
de-levering). ⚠️ La colonna `Sh hold` di quello sweep **NON e' un risultato** (6 celle sul
campione pieno); il tenore >10g resta un buco reale della griglia 03/07, da chiudere con
`study_family_honest`, non con uno sweep. **Con questo gli overlay refutati su VRP01 sono 5/5.**
⚠️ **CORREZIONE a un numero pubblicato lo stesso giorno:** il sleeve modella le fee come 12.5%
del credito NETTO, il listino vero e' **0.03% del sottostante per gamba** (cap 12.5% del premio
della singola opzione, che quasi mai morde) → il modello **sovrastima le fee di ~2x**. Sleeve
ufficiale 1.08/0.58/DD 11.8% vs fee reali **1.32/0.82/DD 10.5%**. Quindi il numero onesto di
VRP01 con **f=0.73** e' **ShFULL 0.47 / DD 14.5%, non 0.31** (che era calcolato col forfait).
`fee_frac` **NON cambiato**: il forfait e' conservativo e questo progetto tiene i numeri di
ammissione conservativi. **Rientro immediato** dopo l'uscita: Sh 1.19 < 1.32 canonico; il suo
ShHOLD 2.42 non e' selezionabile in-sample → **non e' un lead**.
**BUCO DEL TENORE CHIUSO col gate onesto (2026-07-30, 4° filone) — nessun cambio.**
`scripts/research/r0730_vrp_tenor_gate.py`, test `tests/test_vrp_tenor_gate.py` (12), diario
`2026-07-30-vrp-tenor-gate.md`. La griglia 03/07 si fermava a **10g**; qui la famiglia e' stata
**dichiarata prima di guardare**: 8 tenori (5-35g) x 3 delta corti x 3 lunghi = **72 celle**
(riaprire il tenore riapre la struttura → i trial si contano al RIALZO). ⚠️ `study_family_honest`
e' cablato sui candidati DIREZIONALI (`factory -> target_fn` via `candidate_daily`), quindi sono
stati usati i suoi **tre componenti reali**: selezione in-sample-only → `altlib.deflated_sharpe`
`altlib.marginal_vs_tp01` (importati, non riscritti; c'e' un test d'identita').
**Esito: `earns_slot_honest = False`.** Cella scelta al buio **10g 0.28/0.05** (Sh IS 1.75 /
FULL 1.55 / HOLD 1.01 / DD 9.6%) contro il canonico 7g (1.50/1.32/0.82/10.5%; rango **17/72**
in-sample, 26/72 full) → batte il canonico, ma **DSR 0.948 < 0.95 FAIL** (il canonico stesso fa
0.885 dentro questa famiglia). `marginal_vs_tp01` = **ADDS** per entrambe → il verdetto non e'
"VRP01 e' rotto", e' che il vantaggio non sopravvive al conto dei trial.
📌 **Il buco si chiude meglio sul contenuto che sul gate: la regione MAI esplorata PERDE.**
Miglior cella >10g = 18g 0.28/0.05, **rango 8/72**, e solo **3/10** della top-10 stanno oltre i
10 giorni → lo studio **conferma** il 03/07 invece di ribaltarlo.
**"Il vincitore sta dove il modello sbaglia di piu'" — MIO ARGOMENTO, MISURATO E REFUTATO
LO STESSO GIORNO** (5° filone, sotto): l'ala a δ−0.05 e' molto piu' mispriced in RELATIVO
(f_long **5.85** contro 2.23) ma pesa il **3.2%** del premio corto invece del **18.4%**, quindi
sul credito NETTO l'effetto e' MINORE → f_net **0.852** contro **0.718**: il candidato ha un f
**migliore**, non peggiore. Meccanismo giusto, conclusione rovesciata. **La decisione (nessun
cambio) regge, ma su tre gambe invece di quattro:** gate DSR, regione nuova perdente,
ineseguibilita' sotto ~$2.6k.
⚠️ **Robustezza del verdetto, pubblicata:** DSR **0.983 PASS a N=8** / **0.948 FAIL a N=72** /
0.909 a N=360 → **il verdetto si ribalta col conteggio**. La griglia era dichiarata in anticipo e
il conto fatto al rialzo, ma **fallisce per 0.002**: si cita come tale, non come refutazione
netta. Congelato in `test_il_verdetto_dipende_dal_numero_di_trial_dichiarati`.
**Seguito giusto = una MISURA, non un altro backtest:** quanto vale f sul 10g/0.05 sulle quote
vere (la catena ora la raccogliamo noi ogni ora; oggi 15 osservazioni sul solo canonico).
**REGOLE:** (a) quando si riapre un parametro si riapre la sua **famiglia**, e il deflated-Sharpe
e' l'unico posto dove barare e' indolore e invisibile; (b) dichiarare la griglia **prima** e
pubblicare la **sensibilita' del verdetto al conteggio**; (c) un buco si chiude meglio mostrando
che dentro **non c'e' niente** che bocciando il candidato; (d) se la selezione converge dove un
difetto di modello e' gia' misurato, **il sospetto vale piu' del numero** — e si dice che e' un
sospetto.
**f MISURATO SULLE STRUTTURE, e sorvegliante cablato (2026-07-30, 5° filone).**
`scripts/live/vrp_f_watch.py` (in `cron_daily.sh`), test `tests/test_vrp_f_watch.py` (11),
diario `2026-07-30-vrp-f-strutture.md`. **Book/pesi/config INVARIATI.** Il campione c'era gia':
**8 scadenze utilizzabili per asset su 8**, entrambe le strutture, 16 osservazioni ciascuna.
| struttura | f_short | f_long | quota ala/corta | **f_net** |
|---|---|---|---|---|
| canonico 7g δ−0.10 | 1.013 | 2.233 | 18.4% | **0.718** |
| candidato 10g δ−0.05 | 0.972 | **5.846** | **3.2%** | **0.852** |
Aritmetica che rende ovvio l'errore: `f_net = (f_short k·f_long)/(1 k)` con
`k = premio_lungo/premio_corto` → **un f_long grande fa danno solo moltiplicato per un k
grande**. ⚠️ Artefatto di tick ESCLUSO prima di crederci: l'ask dell'ala sta a **22 tick**
mediani (min 15, **0%** a ≤2 tick).
**La DIFFERENZA non e' stabilita:** appaiata per (asset, scadenza) fa **+0.109 IC95
[0.047, +0.193]**, 11/16 positive → **contiene lo zero**. ✅ Replica indipendente del canonico
(**0.718** per un percorso con finestra DTE e pairing diversi da quello che dava 0.73).
Al proprio f: canonico **0.43**, candidato **1.18**.
**CRITERIO PRE-REGISTRATO (dichiarato prima del campione pieno, congelato in un test):
≥40 coppie E ampiezza IC95 ≤0.12** (oggi 16 e 0.241) → prima gamba verso **fine ottobre 2026**;
il sorvegliante rifa' la misura ogni giorno e **notifica una volta sola** quando basta (lezione
DVOLSPREAD: un lead senza sorvegliante e senza data e' un lead perso).
⚠️ **Calibrazione della soglia verificata prima di fidarsene:** con differenza vera nulla lo
zero e' escluso nel **5.0/4.0/3.0%** dei casi a n=16/40/60 = il 5% atteso. (Il test iniziale su
seed fisso falliva perche' quel seed era uno dei 5% legittimi → sostituito con un test sulla
PROPRIETA', 100 estrazioni.)
**NON promuove nulla:** il candidato resta bocciato sul deflated-Sharpe, che non dipende da f.
**REGOLE:** (a) **un fattore di errore si giudica moltiplicato per il suo peso** — 5.85 al 3%
fa meno danno di 2.23 al 18%, e guardare il fattore senza il peso da' la conclusione opposta a
quella vera; (b) prima di credere a un rapporto estremo su un prezzo piccolo, **contare i tick**;
(c) una soglia sull'ampiezza di un IC richiede di **verificare la copertura** di quell'IC;
(d) *"non abbastanza campione" non e' "nessuna differenza"* — si aspetta con una data.
💰 **A $3.000 la domanda non e' il rendimento** (`min_trade_amount` letti dal venue il 30/07):
~~**BTC min 0.1 = $6.210 di collaterale/lotto → FUORI**; ETH min 1 contratto = $1.832 → **1 lotto**.~~
🚨 **CORRETTO 2026-08-23 (§50): misurato sulla famiglia INVERSE, che un conto USDC non puo'
marginare.** Sulla **USDC-lineare** il lotto ETH e' **$242** e il BTC **$772** (7,5x piu' piccoli)
⇒ 1 lotto ETH copre il peso di book 12% gia' da **~$2.000**, e il BTC da ~$6.440.
**CADE IL LOTTO, NON LA REGOLA:** *niente short-vol da modello in deploy* non ha mai avuto
bisogno di un muro di taglia. **5ª occorrenza dello schema `fee_watch`** — un numero misurato su
una configurazione diversa da quella che gira.
Il sleeve 50/50 **diventa ETH-only**, e **al peso di book (12% = $360) sono 0 lotti** — servirebbe
il **61% del conto** su un solo sleeve. **REGOLE:** (a) un'uscita si giudica su un confronto
**appaiato per INGRESSO**, mai allineando le serie sulla data di USCITA — e' proprio quella che
la variante cambia, e l'inner-join tiene solo i casi in cui non e' successo niente (errore
commesso e corretto in sessione, 3ª occorrenza della trappola dopo venue-watch e GTAA; congelato
in un test); (b) **una regola d'uscita che scatta piu' spesso sui vincenti che sui perdenti non
e' protezione, e' troncatura** — si vede in una riga, la peggior settimana non cambia; (c) prima
di misurare il rendimento a un capitale dato, misurare il **lotto minimo del venue**.
**`VRP_CFG["f"]` NON cambiato** (15 osservazioni, 7 settimane, e per giunta **0/8 settimane
passano il gate IV-rank>0.30**: il f è misurato nel regime in cui il sleeve sta FLAT, DVOL max
raccolto al 24°/28° pctl della storia) → **caveat quantificato, non nuovo parametro**; il criterio
del 19/06 ("rivalutare quando cerbero-bite cattura un crash") è **intatto**.
⚠️ **Il DD 12% è un DD di CHIUSURE settimanali:** con le standing quotes (~164 riquotazioni/trade)
il peggior mark INFRA-settimana è mediana 6.9% / minimo **81.7%** del capitale su un trade finito
in utile, e il 50%-profit-take sarebbe scattato in **13/15** settimane. Stessa classe della lente
wick accoppiata (25/07): *un rischio valutato sul minimo non si misura sulle chiusure*.
Diario `2026-07-30-vrp-quote-reali.md`.
- **Universo Hyperliquid: ESPANDERLO NON aiuta XS01** (provato): 52-asset / top-liquidità dinamico /
trend-multi-asset → tutti peggiori (small-cap/memecoin diluiscono il momentum relativo; il trend
multi-asset è ridondante con TP01, corr 0.74). I margini su XS sono nella STRUTTURA DEL SEGNALE
(blend + gate), non nel numero di asset. I **51** parquet certificati restano per ricerca futura.
⚠️ Il test "52-asset = negativo" era in parte inquinato dal backfill sintetico (AXS 83%, ALGO/SAND
37% di barre vol=0) poi rimosso — vedi correzione estrazione 2026-06-20 sotto; resta comunque vero
che il long-tail diluisce XS01, ma il numero netto post-fix è 51.
-**XSR01 "Cross-Sectional Residual" — CANDIDATO NUOVO in forward-monitor (2026-07-25).**
`scripts/live/paper_xsr.py`; scoperta `r0725_statarb_basket_gate.py`, scettico
`r0725_statarb_demean_skeptic.py`, gate `r0725_xsr_deploy_gate.py`, test `tests/test_paper_xsr.py`.
**Cos'e':** il meccanismo congelato di STATARB-RESID (W=45, sgn=+1, residuo OLS causale su BTC,
z-score, tanh, vol-target 20%) applicato ai **50 alt HL**, con posizioni **DEMEANATE
cross-sezionalmente** ogni giorno. Il demeaning annulla ALGEBRICAMENTE la gamba BTC comune
(`sum(p_ip̄)(r_ir_btc) = sum(p_ip̄)r_i`) → non e' un paniere di coppie ma una **strategia
cross-sectional dollar-neutral**, e l'ampiezza effettiva passa da **4.5 a 37.4**.
**Numeri (2.6 anni):** Sharpe netta **1.82** (lorda 2.70), maxDD **2.6%**, vol 2.3%, ret 4.2%/a.
**Gate superati:** marginale **ADDS** (robust_oos + beats_noise_null + non-hedge +
has_insample_edge); **deflated-Sharpe 0.985 PASS**; **corr ~0 a TUTTI e 5 gli sleeve** (TP01 0.060,
XS01 0.019, VRP01 0.001, SKH01 0.001, GTAA01 0.071); book a w=15% HOLD 2.36→**2.51**, DD invariato.
**Scettico superato:** 3 null **a fee-neutrale** (permutazione cross-sezionale / casuale /
temporale a blocchi) centrati a ~0.01, candidato lordo 2.70 **sopra il massimo di 300 estrazioni**
(p<0.004); **lag 1.82/1.19/0.81/0.51** a +0/1/2/3g = decadimento dolce di segnale lento, NON
firma di look-ahead; non ridondante con XS-momentum semplice (corr 0.17-0.29).
**NON nel book, 3 motivi dichiarati:** (a) storia 2.6a monoregime e **crescente** (Sh 2024 1.03 /
2025 1.98 / 2026 3.11) → l'edge e' recente; (b) **`weights_tilt_null` FALLITO** (gate_pass=False a
10% e 15%, delta_insample 0.004/0.007: effetto della storia corta, ma un gate fallito resta
fallito); (c) **margine di costo sottile** — 1.82 a 0.05%/gamba, **0.93 a 0.10%, NEGATIVA a 0.20%**,
con turnover 38.5% del lordo/g su 50 alt anche illiquidi e **slippage NON modellato** (rischio #1).
**Eseguibilita':** ticket/gamba $1.73 a $600 (**sotto min-order $5 → STAT-MODE oggi**), $5.76 a
$2000, **$14.41 a $5000** → diventa reale a **~$5k**, non ai ~$20k di XS01.
✅ **GATE RISCRITTO IL 2026-08-26 (decisione dell'operatore, opzione A: riscrivere adesso,
dichiarandolo — diario `2026-08-26-xsr01-gate-riscritto.md`).** A 58 giorni dalla decisione,
prima dell'esito, in direzione che stringe: (1) **capitale deploy $5k → $20k** — il gate del
25/07 contraddiceva la decisione vincolante "100% Deribit fino a $20k" del 26/07, posteriore,
che governa (M28); ora deploy XSR01 e scelta del venue coincidono in UN punto; (2) gamba
haircut letta da **`r0826_xsr_haircut.py`** (N11) a **pavimento $10** (il vero, HL-EXEC) con
la **frazione di ordini eseguiti** accanto (P7) — misurato: FULL 968 barre, floor $10 →
haircut **1,1%** con **22% di eseguiti**: la guardia da sola era vacua (un libro fermo ha
haircut piccolo); ticket mediano **$3,34**/gamba, 84% sotto $10 — riproduce XSR-REPRO e
sostituisce il "$14,41" senza script; (3) contesto 1.82 → 1.79 (lente dei gate). **Soglie
numeriche INVARIATE** (Sharpe 1,0/0,3; haircut 40%): nessuna soglia nuova coi dati in mano.
**Gate pre-registrato 2026-10-23** (forward-day 0): deploy solo se Sharpe≥1.0 **E** haircut
≤40% (strumento onesto di cui sopra; se sfonda → **RITIRO a prescindere dallo Sharpe**), poi
weights_tilt_null e **capitale ≥$20k**. Monitor in `cron_daily.sh`, 3 libri (MODELED $2000 / REAL $600 /
REAL $5000). ⚠ Lezione: XSR01 **non e' nato da un'idea nuova ma dalla DIAGNOSI di un fallimento**
(ampiezza effettiva 4.5 → c'e' un fattore comune da togliere); e i suoi null vanno confrontati
**a fee zero**, perche' permutare un segnale ne fa esplodere il turnover e il null perderebbe per
costo invece che per assenza di informazione (p-value trionfale e falso).
🚨 **SOTTO LA LENTE *RENDITA PERPETUA* IL MECCANISMO VALE ~0 (2026-08-25, `r0825_xsr_rendita.py`).**
Prima valutazione di XSR01 sul criterio della **perpetua** invece che su Sharpe o accumulo — N6:
dichiarato l'obiettivo, cambia il criterio, e nessuno aveva rifatto il conto.
Finestra comune libro↔XSR01 **2024-01-01 → 2026-08-22 (965 g, 2,64 anni = 35% della storia del
libro)** → i muri qui sotto **non sono** il $313k pubblicato, si confrontano solo fra loro
(base libro-solo: perpetua 3,30%, muro **$602.660**). XSR01 su questa finestra: drift **3,72%/a**
(SE 1,46%, t +2,55), vol 2,34%, Sharpe **1,56**, maxDD 2,5%, corr col libro **+0,049**.
· **iso-nozionale il muro SALE** (+0,3% a w=5% → +10,8% a w=50%): per una rendita il drift comanda.
· **iso-rischio (M6)** si ribalta: w=25%, k **1,32x** → muro **$478.729 (20,6%)**. Ma **l'argmax e'
al BORDO** (w=50%, k=1,93x, muro scende monotonamente in w) → la griglia dice *"il piu' possibile"*,
e cio' che si compra in quella direzione e' il **k**, non XSR01 (M8). Sopra 1,40x il libro non
puo' eseguire (decisione 23/08 + nessuna chiave di scala in config).
· **IL NULL, che e' il risultato.** A iso-vol `drift = Sharpe × vol_libro`, quindi tutto il
guadagno di muro e' guadagno di **Sharpe da diversificazione**. Quattro sostituti a w=25%:
**XSR01 mescolato** (stessi rendimenti, ordine casuale, 3 semi) → $475.933 / $483.846 / $484.421
contro i **$478.729** del vero → **il meccanismo vale 0,8% di muro, dentro la risoluzione MC**
(spread fra semi 0,151% di perpetua ≈ $17k). **Rumore a media e vol di XSR01** → mediana $499.263.
**Rumore a drift ZERO** → mediana **$617.270, SOPRA la base**: la sola riduzione di varianza
**non compra rendita**. Si compra il **drift scorrelato**, non la scorrelazione.
· 🚨 **Un CONTO REMUNERATO allo stesso tasso lo eguaglia**: 3,72% a vol 0, corr 0, nessun secondo
venue → **$477.607**; al 4,0% → **$469.353, meglio**. Ed e' un confronto **in svantaggio per il
conto**: la lente L3 gli applica il 33% di `c-sexies` (un BOT paga 12,5%, un deposito 26%).
· **L'haircut mangia esattamente cio' che il null dice di star comprando**: 3,72% lordi → 2,97% a
haircut 20%, **2,23% alla soglia 40% del gate**, 1,49% al 60%. **Non pareggia un conto al 4,0%
nemmeno a haircut ZERO**; pareggia il 2,0% solo sotto il **46%**. E l'haircut pubblicato ($14,41
di ticket) e' ~2,4x ottimista e senza script che lo riproduca (XSR-REPRO).
· `weights_tilt_null` (25% vs 0%) **PASS** (Δ_is +0,061 · Δ_hold +0,162 · pctl 25,8 < 90,0) — ma
l'hold-out del gate e' 2025-01-01, quindi la gamba in-sample e' **un solo anno**: indizio, non prova.
· **Il vincolo binding non e' il rendimento, e' il CAPITALE**: a $2.067 un 25% sono $517 contro un
**C\* misurato $15.000-20.000**; per un 25% sopra C\* servono **$60.000 sul conto totale**.
📌 **Cosa NON dice:** non che XSR01 sia morto — Sharpe standalone 1,56 e DSR 0,983 reggono, e sotto
la lente **accumulo** il conto va rifatto. Dice che **per la RENDITA non e' lo strumento**.
- **Soffitto strutturale BTC/ETH-direzionale ~1.3** superato SOLO espandendo a un meccanismo diverso:
cross-sectional su universo Hyperliquid certificato (XS01) → portafoglio Sharpe ~1.55.
File diff suppressed because it is too large Load Diff
+782
View File
@@ -0,0 +1,782 @@
# Piano, capitale, fisco e canale funded
> Estratto **verbatim** da `CLAUDE.md` il 2026-08-25 durante la compattazione.
> Muri, traiettorie, versamenti, rischio di venue, fisco e prop firm.
La tabella che comanda e' quella al netto di TUTTO (fisco + funding).
> Il testo non e' stato riscritto: e' la memoria originale, spostata.
---
- ⚠ **RENDITA / CURVA CAPITALE (2026-07-25, 2° filone del giorno) — il muro del 24/07 era ottimista
2-4x, e 1 DIFETTO DI ESEGUIBILITA' su GTAA01.** Script `r0725_capcurve.py` + `r0725_prop_config.py`,
test `tests/test_capcurve.py` (10 casi); diario `2026-07-25-rendita-capcurve-prop.md`. Book/pesi/cron
**INVARIATI**.
(1) **GTAA01 non e' eseguibile come codificato.** `gtaa.py` modella 2bps proporzionali e lo sleeve e'
documentato "switch mensile/basso turnover": FALSO — il vol-target e' **continuo giornaliero su 6
gambe**, e IB ha un **pavimento FISSO** `min(max($0.35, $0.0035/az), 1% valore)` per ordine =
~$530/anno indipendenti dal capitale. A $600: **CAGR 3.5% / Sh 0.55**; negativo fino a ~$3k;
a $50k 0.59. **La banda lo salva**: plateau robusto **weekly x banda $25-100** → Sh 0.45-0.64 a
ogni capitale (a $600: Sh 0.52 / CAGR 3.0%). ⚠ **Correzione a un errore MIO:** la prima stesura
citava il modello vecchio a "0.77/5.5%" — era un **artefatto di annualizzazione** (metricavo la
serie GTAA grezza a ~252 barre/anno con `metrics()` che annualizza a 365 → Sharpe ×1.20, CAGR
×1.45). Valore vero del modello vecchio: **0.64/3.8%**. **Quindi il difetto NON e' "sovrastimava
di 0.13": alla taglia grande il modello vecchio era GIUSTO (0.64 vs 0.59-0.66 reali). Il difetto
e' che era CIECO AL CAPITALE** — dava 0.64 sia a $50k sia a $600, dove la realta' e' 0.55.
**Lezione: una serie su giorni di borsa (~252/anno) non si passa a `metrics()` (che annualizza a
365) senza `to_daily()` — il Sharpe esce ×1.20 e il CAGR ×1.45.**
**FIX CABLATO** (stessa sessione, `src/portfolio/gtaa.py`): costo IB reale (`ib_commission`),
esecuzione a **banda+cadenza** (settimanale, $50/gamba), `gtaa_returns(capital=...)` capital-aware,
soglia `GTAA_MIN_CAPITAL=$3.000` + `gtaa_is_deployable`, e **`gtaa_rebalance_plan(held, capital)`**
per l'esecutore (salta le gambe sotto banda). `sleeves.py` dichiara il capitale assunto
(`GTAA_DEFAULT_CAPITAL=$10.000` ≈ book $50k al peso 20%). **Impatto book misurato: FULL 2.22→2.22,
HOLD 2.36→2.38, DD 6.2→6.0%** = trascurabile alla taglia assunta; il fix conta per il **DEPLOY**
(a $600-2k: da 3.5%/anno a +3.0%/anno). Pesi INVARIATI. Test `tests/test_gtaa_sleeve.py` (+4). **REGOLA NUOVA: il costo di un venue va modellato nella sua
FORMA (fisso vs proporzionale), non solo nel livello** — due venue a pari "costo medio" danno esiti
OPPOSTI al variare del capitale (il pavimento fisso e' una tassa regressiva). Controprova: su Deribit
(proporzionale, senza pavimento) lo Sharpe realistico di TP01 = modellato da ~$500 in su → conferma
indipendente di "a $600 il min-order $5 e' gia' la banda ottimale".
(2) **La rendita NON e' `capitale x CAGR`.** Il 24/07 calcolava capitale = target/CAGR (€122k a CAGR
15%), ignorando il rischio di sequenza. Bootstrap a blocchi sui ritorni REALI (SKH01 su **path live**):
rendita **perpetua** (= prelievo con P(cap a 20a >= cap iniziale) >= 90%) del book de-luckato ×0.6 =
**5.98% → $497k**; SWR-20a 7.31% → $406k; lente modellata 13.57% → $219k. ~~Il muro vero e' una
BANDA $232k-$497k, stima centrale ~$300-400k.~~
⚠️ **AGGIORNATO 2026-07-26 — il ×0.6 era MISURATO troppo severo del 31-34% → il muro scende
del ~45%.** Il fattore d'ancora onesto sul drift e' **×0.89** per il book live (misurato, non a
occhio) e ogni componente del path live e' non-negativa (bullet in fondo). Ricalcolo con la
STESSA macchineria (`r0726_capwall_refresh.py`): **perpetua 6.00% → 10.91%, muro $494.758 →
$272.061** a leva 1.0 (a leva 1.5: $366k → $200k). Il ×0.89 e' un **limite inferiore** del
fattore, quindi il muro vero sta fra la riga ×0.89 e la riga ×1.00 ($233k). **La conclusione
STRUTTURALE non cambia:** $272k restano **~453× il conto di oggi** → i €600 come CAPITALE
restano refutati, come BIGLIETTO (prop) no. Rendita a $600: €0.12/g → **€0.18/g** (centesimi:
il fattore conta per il MURO e le soglie prop, non per il conto attuale).
Cio' che NON dipende dalla lente: **la rendita perpetua vale ~45-55% del CAGR**, quindi ogni muro
calcolato come `target/CAGR` sbaglia di un fattore ~2.
**TRAIETTORIA da $600, ricalcolata col fattore corretto (26/07).** Il fattore agisce **due
volte**: alza il drift (si accumula prima) E abbassa il bersaglio (serve meno capitale), e i due
effetti pesano quasi uguale. Mediana degli anni per toccare il capitale-rendita:
| dep./mese | ×0.60 muro $495k (25/07) | ×0.89 muro $495k (solo drift) | ×0.89 muro $272k (26/07) |
|---|---|---|---|
| €0 | mai | mai | **mai** |
| €250 | 22.4a (8% entro 20a) | 19.6a (53%) | **16.2a (91%)** |
| €500 | 19.6a (48%) | 15.7a (94%) | **12.4a (100%)** |
| €1000 | 14.8a (95%) | 12.0a (100%) | **9.0a (100%)** |
| €2000 | 10.0a (100%) | 8.5a (100%) | **6.0a (100%)** |
⛔ **QUESTA TABELLA E' AL LORDO del fisco d'accumulo — la versione NETTA e' nel bullet "IL FISCO
DURANTE L'ACCUMULO" (r0807_piano_netto, 07/08) e cambia la riga di testa: €250/mese passa da
P(20a) 92% a 52%.** Le colonne qui restano perche' sono la replica di controllo del fattore
d'ancora, non perche' siano il piano.
✅ La colonna ×0.60 **riproduce esattamente** i numeri del 25/07 (19.6a / 14.8a) = validazione
indipendente della replica. ⚠️ **Cio' che NON cambia col fattore: senza depositi il
capitale-rendita non si raggiunge mai** (0% dei path a 20 anni a OGNI fattore) — l'accumulo
viene dai versamenti, non dal rendimento; e la mediana e' una MEDIANA (meta' dei path arriva
dopo, una quota non arriva affatto).
**QUANTO VERSARE PER UN ORIZZONTE DATO** (bersaglio $272k, ×0.89; "arrivarci in N anni" non e'
un numero: dipende dalla confidenza):
| orizzonte | P=50% | P=75% | P=90% | P=95% | totale versato @P=90% |
|---|---|---|---|---|---|
| **10 anni** | €806/m | €995/m | **€1.178/m** | €1.299/m | **$155.923** |
| 15 anni | €313/m | €408/m | €509/m | €583/m | $101.643 |
| 20 anni | €129/m | €180/m | €237/m | €280/m | $63.406 |
**AL LORDO: i numeri da usare sono quelli NETTI** (€1.323 / €672 / €371 a P=90%, bersaglio
$258k) nel bullet "IL FISCO DURANTE L'ACCUMULO" — il fisco costa **+12% al mese a 10 anni, +32%
a 15, +57% a 20**.
⚠️ **Comprimere l'orizzonte da 20 a 10 anni costa 2.5× al mese E 2.5× in totale**: a 10 anni
versi $155k per arrivare a $272k (il rendimento fa il 43%), a 20 anni ne versi $63k (il
rendimento fa il **77%**). **A orizzonti corti non fai lavorare la strategia, COMPRI il
capitale coi bonifici** — il confronto onesto e' sempre `totale versato` vs bersaglio.
Drill-down €250/m (5000 path): traguardo p10 13.4a / **mediana 16.2a** / p90 19.7a;
P(entro 15a) **31%** → P(entro 20a) **92%** (la curva non da' segnali per un decennio e poi si
muove tutta insieme: chi giudica al 5° anno giudica nel punto peggiore); al traguardo hai
versato ~$54k su $272k → **80% viene dal rendimento**. ⚠️ Bug catturato in sessione: il
contatore `paid` si congelava al traguardo mentre `cap` continuava a ricevere i depositi →
rapporto capitale/versato gonfiato (46× invece di 25× a 30 anni); test di regressione cablato.
(3) **Diversificare NON crea reddito a pari nozionale — libera BUDGET DI RISCHIO.** Per tier:
iso-nozionale 2 sleeve 5.98% → 4 sleeve 6.35% (nulla, il diversificatore a basso CAGR toglie vol e
ritorno insieme); a **ISO-RISCHIO** (vol 15%) 7.51%/$395k → **9.25%/$321k (19% di muro)**.
**REGOLA: un diversificatore a basso CAGR si giudica a ISO-RISCHIO, mai a iso-nozionale** (e' il
null de-levering al contrario). L'ipotesi "piu' capitale → piu' sleeve → CAGR super-lineare" e'
**REFUTATA nella forma forte**: la curva Sharpe del book eseguibile e' PIATTA da $600 a $200k.
(4) ✅ **ASIMMETRIA CAPITALE-PROPRIO vs FUNDED (risultato strategico).** Il 24/07 valuto' il fronte
prop **solo col book a 2 sleeve**; ma su un conto funded il capitale e' $100k → **gli sleeve STAT-MODE
per taglia (XS01, ~$20k) diventano eseguibili**, e le regole prop passano sul **DRAWDOWN, non sul
CAGR**. Finestra comune 2024+, de-luck, crypto-only: P(pass) HYRO 1.0x **44.4%→54.7%**, FTMO 1.5x
**51.9%→70.0%**; **funded HYRO 1.0x P(vivo 1a) 18.9%→57.6% (3x)**, FTMO 1.0x 58.8%→**92.0%**,
E[payout] $8.7k→$9.3k (~€15.7/g atteso). **Si RIBALTA la raccomandazione "funded a 0.75x"**: col book
diversificato 1.0x e' sostenibile. **In una riga: sul capitale proprio diversificare non aumenta il
reddito; su un conto funded si', perche' li' il vincolo binding e' la regola di DD, non il capitale
→ gli sleeve "inutili a $600" sono gli asset di maggior valore sull'unico canale che scala.**
⚠ CAVEAT: MC **close-only** (C-bis 24/07: i wick tagliano 6-37pp) → livelli assoluti = TETTO, il
DELTA e' la misura onesta ed e' conservativo (B ha vol minore); la finestra comune 2024+ e' quella in
cui XS01 e' stato scoperto/affinato → la TAGLIA del guadagno e' ottimista (finestra piena: 56.9%→58.5%),
il MECCANISMO (corr bassa → meno DD → piu' sopravvivenza sotto vincolo di DD) e' robusto.
(5) ✅ **LA VIA: i 600 euro come BIGLIETTO, e il problema della correlazione fra conti**
(`r0725_prop_ladder.py`). I €600 come capitale sono refutati; come **biglietto** no: si compra
un'eval, il funded moltiplica il NOZIONALE senza possedere capitale, i payout comprano altri
biglietti. Il 24/07 si fermava a UN conto (cap $200k/firm → ~€15-30/g); **50 EUR/g richiede piu'
conti**, e li' il problema mai studiato: **N conti sullo STESSO book bustano INSIEME** (corr 1.0)
→ la diversificazione fra conti e' illusoria salvo **sleeve diversi su conti diversi**. E' un
problema di portafoglio sotto **barriera di rovina PER-CONTO**. Sim 36 mesi, €600, max 6 conti,
2 firm, regole vere, payout mensile prelevato subito, fisco 33%, **morte-firm 10%/anno**,
bootstrap CONGIUNTO (corr reali). ~~**Vince il MISTO, non gli estremi**~~ 🚨 **SUPERATO 2026-08-23
(§60): rifatta la stessa griglia (288 celle) vince MISTO-A, `P(>=50/g)` 9,27% contro 6,60% — e la
mediana e' 0,00 EUR/g in TUTTE e sei le politiche. Cade il vincitore, non l'ordine fra gli
estremi.** Numeri del 25/07 (close-only, leva 1.0x):
MISTO mediana €10.70/g, P(≥50/g) 20.7%, P(zero) 33.4% > CONC-DIV 5.70/17.8%/33.4% > SPARSO
0.00/16.8%/**66.0%** > **CONC-2SL (= il book live attuale) 0.00/13.1%/55.6% = la PEGGIORE**.
Logica: ogni conto serve Sharpe per sopravvivere alla PROPRIA barriera (uccide SPARSO), ma i
conti servono decorrelati fra loro (penalizza CONC). **Con lente INTRADAY (wick)** tutto crolla:
P(≥50/g) 1.6-2.5%, P(zero) 78-90%. ⚠ Il mio wick e' un **PAVIMENTO**: gap estratto INDIPENDENTE
dal rendimento del giorno → breach spuri → **follow-up dichiarato, poi CHIUSO lo stesso giorno
(bullet successivo): la stima onesta e' P(≥50/g) ~6%, P(zero) 52-65%.** Il confronto fra POLITICHE
e' robusto (stessa lente per tutte) — **verificato a posteriori sulla lente accoppiata: l'ordine
MISTO ≥ CONC-DIV > CONC-2SL ≈ SPARSO regge a tutte e 3 le lenti** → **se si apre il fronte prop,
NON mandarci il book live attuale**.
(6) **ADDENDUM "e se metto 10K su IB?"** (`r0725_ib10k.py`): allocazione fra venue, non domanda su
GTAA01. Deribit = **motore** (CAGR ~11% de-luck), IB/GTAA01 = **diversificatore** (CAGR ~3.9%).
A $11.5k totali: 0% su IB → **€2.16/g**; 50% → €1.54/g (Sharpe migliore 1.13, DD 15.3→11.2%);
**94% (= i 10k su IB) → €0.93/g = REDDITO PIU' CHE DIMEZZATO**. La via d'uscita (levare per
convertire lo Sharpe in reddito) e' **chiusa dai costi**: su ETF a IB con €10k c'e' Reg-T **2x max**
(portfolio margin da ~$110k) e il margine costa **~5.5%/anno** → contando il finanziamento vince
**tutto-Deribit a 1.33x** (rendita €1.33/g vs €0.92/g a 50/50). **REGOLA: l'argomento iso-rischio
(punto 3) vale solo se la leva e' (a) disponibile e (b) a costo < uplift — su un retail da €10k su
ETF non lo e'.** Confronto che ridimensiona GTAA01: €10k nello sleeve = €0.93/g con maxDD 10.5%,
gli stessi €10k FERMI a ~2% = €0.54/g con maxDD ~0% → **premio ~€0.50/g pagato con un DD del 10%**.
Raccomandazione: i depositi vanno su **Deribit**; al massimo **25% su IB** (costa ~€0.08/g di
rendita, taglia il maxDD 15.3→12.0%, unico split che regge coi costi). ⚠ Il confronto FAVORISCE il
crypto per costruzione (7 anni con 2 bull vs 30 anni di GTAA); tassi e aliquote (26%/33%) sono
assunzioni dichiarate, da confermare col commercialista (non un parere fiscale).
(7) **HYROTRADER nello specifico** (`r0725_hyro.py`): la firm meglio classificata per questo book
(API reale da funded, nessun limite di tempo, **weekend consentito** — e il weekend porta il 38%
del gross di TP01). **Ipotesi REFUTATA: la regola di consistency 40% NON morde** (costo ≈0pp a
ogni leva/lente) perche' il book accumula il 10% in 130-300 giorni → nessun giorno si avvicina al
40% del cumulato; ⚠ prima di dichiararlo ho dovuto **provare che il controllo funziona** (un
"costo 0" puo' essere un bug): su un book con profitto concentrato in 1-2 giorni la regola porta
P(pass) da >90% a <5% (test cablato). **Il vincolo vero e' il maxDD 6% STATICO**: config A (book
live) ha maxDD 9.5% > 6% = strutturalmente incompatibile a leva piena — intraday a 1.0x
sopravvive l'**1.4%**; config B (+XS01) ha maxDD 6.2%, Sharpe 1.13. ~~**Funded consigliato: config B
a 0.50x**~~ → **CORRETTO a 0.75x dalla lente accoppiata (bullet successivo)**: il 0.50x era scelto
perche' il wick indipendente dava a 0.75x P(vivo) 55%; la lente onesta da' **76%**, e 0.75x
**massimizza il payout atteso su 3 anni** ($2.674 vs $1.230 a 0.50x e $1.992 a 1.00x). L'argomento
"la sopravvivenza COMPONE su piu' anni" resta valido: si sposta il punto in cui morde.
EV del biglietto $100k **positivo in entrambe le lenti** (+$6.789
close-only, +$1.166 intraday), ma ~69% di perdere la fee nella lente pessimista. Cap $200k/trader
⇒ ~€15-30/g max → per €50/g servono piu' firm (punto 5). ✅ Discrepanza col 24/07 (P(vivo) A@0.75x
58% vs 10% mio) **RISOLTA: non era la finestra, era la lente** — sulla stessa finestra piena la
lente accoppiata da' **63.2%** vs il 58% del 24/07 (accordo entro 5pp, implementazioni separate).
⚠ Bug catturato in sessione: base di prelievo funded aggiornata giornalmente come HWM mobile →
guadagno al checkpoint ≈ 0 → **payout $230/anno invece di ~$7.000**; regole vere = max-loss STATICO
dal saldo iniziale + base ripristinata dopo il prelievo. Test di regressione cablato.
- ✅ **LENTE WICK ACCOPPIATA (2026-07-25, follow-up del bullet precedente — CHIUSO).** Script
`r0725_prop_coupled.py`, test `tests/test_prop_coupled.py` (13 casi), diario
`2026-07-25-prop-wick-accoppiato.md`. Book/pesi/cron **INVARIATI**. Generalizza il recon MTM di
`r0724_goal50_intraday_mc.py`: (a) sleeve TP01/SKH01 **separati** a risoluzione oraria → il minimo
intraday si compone ESATTO per QUALSIASI vettore di pesi (prima erano cablati 75/25); (b) **XS01
accoppiato** dagli OHLC giornalieri HL (4 checkpoint, ordine condiviso avverso; recon = sleeve
ufficiale a `max|Δ|=0.0`).
⚠️ **IL FINDING: la CALIBRAZIONE del wick era giusta, l'errore era l'INDIPENDENZA.** Le marginali
coincidono quasi (p50 0.17pp **identico**, p90 1.03 vs 0.90, p99 2.70 vs 3.50) — sbagliava
**su quali giorni** cadono i tuffi. E la dipendenza va nel **verso inatteso**: il gap e' ~**3× piu'
profondo nei giorni che finiscono BENE** (1.58pp nel decile migliore vs 0.48pp nel peggiore),
perche' un giorno brutto scende tutto il giorno e **chiude sul minimo** (`m==R` nel **26%** dei
giorni). Il breach si valuta sul minimo → l'estrazione indipendente carica i giorni brutti con la
coda dei giorni buoni e **raddoppia i breach da daily-loss (2.0-2.9×, misurato sui giorni storici)**.
**`close-only` non e' conservativa, e' CIECA**: 0.00% di breach da daily-loss su OGNI configurazione
(la regola non scatta mai sulle chiusure) → usarla come controllo, mai come stima.
**Verifiche (un "nessuna differenza" va provato):** (1) **1h vs 5m** sulla gamba TP01 = identici
(p99 3.06 vs 3.14pp) → **il caveat di risoluzione portato avanti dal 24/07 e' quantificato e
trascurabile**, l'ora cattura gia' il minimo del giorno; (2) riconciliazione col 24/07 (sopra);
(3) **bound severo su XS01** (ogni gamba al proprio peggio insieme): config B resta sopra config A
(P(vivo) 57.6% vs 39.4%) → la conclusione non dipende dalla convenzione.
**Decisioni:** funded **0.75x** (non 0.50x, vedi sopra); scala di conti **P(≥50/g) ~5.6-6.5%** in
3 anni (non 1.6-2.5% ne' 20.7%) con **P(zero) 52% a 0.75x / 65% a 1.00x**, e **P(≥10/g) massima a
0.75x (29.7%)** = stesso ottimo di leva del conto singolo. **Ordine fra politiche INVARIATO a
tutte e 3 le lenti.** Verdetto ristretto: **€600→€50/g ≈ 6% in 3 anni, P(perdere i €600) ≈ 52-65%**
— resta coda destra, ma con probabilita' conoscibile invece di una banda di un ordine di grandezza.
**REGOLA NUOVA: un modello di rischio intraday NON si valida sulla marginale del wick ma sul suo
ACCOPPIAMENTO al rendimento del giorno** — qui un test "i percentili coincidono" sarebbe PASSATO
con la stima sbagliata di 2-3×. Ogni regola valutata sul minimo (daily-loss, trailing DD, stop di
conto) va misurata su **tuple accoppiate**, mai su un wick estratto a parte.
- ⚠️ **RISCHIO DI VENUE — mai prezzato in 2 mesi, e non e' diversificabile dagli sleeve
(2026-07-26).** Script `r0726_venue_risk.py`, test `tests/test_venue_risk.py` (13), diario
`2026-07-26-venue-risk.md`. **Book/pesi/cron INVARIATI.**
(0) **Il buco:** il progetto ha prezzato fee, slippage, min-order, pavimento IB, haircut
small-cap, fortuna d'ancora, degrado d'esecuzione, look-ahead, backfill, split — **mai la
probabilita' che l'exchange sparisca col saldo dentro**. E TP01+SKH01+VRP01 stanno **tutti sullo
stesso conto Deribit**: tre sleeve quasi-ortogonali sui ritorni, **perfettamente correlati sul
fallimento del venue** — cosa che la matrice di correlazione del book non vede per costruzione.
(0-bis) ⚠️ **Limite dei muri del 25-26/07:** `book_series` gira a **`alloc=$600` col book a 2
sleeve** → portarlo fino a $272k assume (a) che a $272k si giri ancora il book da $600 e (b)
**"tutto su Deribit" per 10-20 anni, senza dirlo**. ✅ **(a) MISURATO e REFUTATO il 26/07** —
vedi bullet "MURO COME PUNTO FISSO": il muro **non** scende, $273.900 vs $272.061 (**+1%**).
(1) **La misura** (accumulo da $600, €250/m, 20a, bersaglio $272k, ×0.89, jump di venue a
probabilita' annua `p`; CONC = 100% Deribit vs SPLIT = Deribit 65 / HL 15 / IB 20; **bersaglio
identico → conservativo CONTRO lo split**):
| p annua | P(arrivare) CONC | SPLIT | **P(perso TUTTO) CONC** | **SPLIT** |
|---|---|---|---|---|
| 0.5% | 87% | 90% | **10%** | **0%** |
| 1.0% | 81% | 83% | **18%** | **0%** |
| 2.0% | 69% | 71% | **34%** | **4%** |
| 5.0% | 42% | 45% | **64%** | **27%** |
Capitale mediano CONC a 20a: $544k (p=0) → **$0 (p=5%)**.
(2) **La colonna che conta non e' la prima.** Sulla probabilita' di ARRIVARE la concentrazione
costa 1-3pp; sulla **ROVINA** costa fino a 64pp. Motivo strutturale: **con un conto solo "almeno
un fallimento" COINCIDE con "perso tutto"**. ⚠️ Lo SPLIT viene colpito **2.5× piu' spesso** (26%
vs 10%) ed e' molto piu' sicuro → **"quante volte vieni colpito" NON e' una misura di rischio**.
(3) **Onesta' obbligatorie:** `p` **non e' stimato** (sensibilita', non previsione — la sceglie
l'operatore e va dichiarata); i fallimenti sono assunti **indipendenti**, ottimistico per
Deribit-HL (crisi sistemica) → **la parte solida dello split e' IB**, altra classe di rischio;
**a $600 lo split e' impossibile**, la concentrazione e' forzata.
(4) **RISPOSTA: no, ma la domanda ha una DATA.** Oggi concentrazione forzata; **~$3k = prima
riduzione vera** (GTAA01 su IB, 20-25% fuori dal rischio-exchange); ~$20k (XS01/HL) aggiunge
poco perche' e' ancora crypto. ✅ **Convergenza che rafforza il 25/07:** `r0725_ib10k` disse
"max 25% su IB, **costa** ~€0.08/g" su basi di solo RENDIMENTO; sull'asse della ROVINA quello
stesso 25% e' la mossa principale → **€0.08/g non e' il prezzo di un peggioramento, e' il premio
di un'assicurazione contro il modo piu' probabile di perdere tutto.**
⚖️ **(5) DECISIONE DELL'OPERATORE 2026-07-26: TUTTO SU DERIBIT FINO A $20k.** Presa DOPO aver
visto la tabella della rovina, e con la controparte esplicitata. **Cosa e' stato accettato:**
P(perso TUTTO) resta 10/18/34/64% a p=0.5/1/2/5% invece di 0/0/4/27%; in cambio si evita il
costo (~€0.08/g di rendita, commissione fissa IB, un secondo venue da gestire) di proteggere
**$750** alla soglia dei $3k. **L'argomento a favore, che regge:** sull'asse su cui l'operatore
ottimizza — P(ARRIVARE al capitale-rendita) — lo split vale solo **+1-3pp** (81%→83% a p=1%),
ed e' un fatto misurato, non una concessione. **L'argomento contro, che resta vero:** zero e'
**assorbente** (andare a zero all'anno 10 di un piano da 16 anni significa non arrivarci piu',
perche' si riparte da €0 + versamenti), quindi il valore del non-andare-a-zero NON e'
proporzionale alla frazione salvata. **Cosa NON si ri-discute:** la soglia $3k. **Cosa si
ri-apre a $20k:** lo split, che a quella taglia protegge ~$5k e ha senso anche solo per
eseguibilita' (XS01/HL, GTAA01/IB). ⚠️ **Nota per il futuro-me:** questa e' una decisione presa
con l'informazione completa, non una svista da correggere — se a $3k qualcuno propone lo split
"come da CLAUDE.md", la risposta e' che la data e' $20k. Se cambia il piano (orizzonte, importo
dei versamenti) o `p` diventa stimabile invece che assunto, si riapre PRIMA.
**REGOLE:** (a) un rischio non-diversificabile dagli sleeve va prezzato a parte; (b) P(successo)
puo' nascondere P(rovina) — per una rendita la metrica e' la seconda; (c) una raccomandazione
presa su un asse solo va ricontrollata sugli altri prima di considerarla stabile; (d) quando una
raccomandazione viene respinta con motivo, si registra **cosa e' stato accettato in cambio**
altrimenti la stessa analisi la ripropone fra tre mesi come se fosse nuova.
- 💰 **I VERSAMENTI — le 4 ipotesi che il piano non aveva mai fatto (2026-07-26, ultimo filone).**
Tutte le traiettorie del 25-26/07 assumevano versamento **piatto, ininterrotto, per sempre** =
l'ipotesi meno realistica del piano. Script `r0726_deposits.py`, test `tests/test_deposits.py`
(12), diario `2026-07-26-versamenti.md`. **Book/pesi/cron/config INVARIATI** (non tocca la
produzione). Block bootstrap sui ritorni reali del book live, fattore ×0.89 misurato.
(1) **SMETTERE — il costo non e' proporzionale ai soldi mancanti.** €250/m per K anni poi stop,
orizzonte 20a: 3a ($10.410) → $202.771 / P(muro) 32.7%; 5a ($16.950) → $287.081 / **53.3%**;
10a ($33.572) → $410.220 / 77.9%; 20a ($66.818) → $496.778 / 90.0%. **I primi 5 anni sono il 25%
dei soldi e il 58% del risultato.** → un'interruzione al 12° anno costa poco, una al 3° quasi
tutto: argomento per partire con un importo **sostenibile**, non ambizioso.
(2) **CRESCENTE E' PEGGIO DI PIATTO a pari soldi.** €150/m +5%/anno versa €67.998 → $401.889;
piatto €250 versa €66.818 → **$496.778** = **+24% con gli stessi soldi**, solo perche' entrano
prima. Metrica giusta per confrontare piani di taglia diversa = **`$ finale / $ versato`**
(piatti 7.4x, crescenti 5.1-5.9x). Contro-intuitivo: "i versamenti crescono col reddito" e'
prudente per il bilancio, **non** per il capitale.
(3) **STESSO TOTALE, CALENDARIO DIVERSO = fattore 6.** €60.000 distribuiti: ultimi 10 anni
$178.494 (P(muro) 11%) / piatto 20a $496.778 (90%) / primi 10a $806.285 (98.3%) / primi 5a
**$1.104.587 (99.4%)**. ⚠️ NON significa "versa tutto subito": un piano che non si sostiene non
e' un piano — serve a scegliere fra calendari **sostenibili**.
**Verificato col rischio di venue dentro** (il vantaggio front-load mette piu' capitale
sull'exchange prima = proprio il rischio del giorno): **regge**, 2.22x → 2.09x a p=2%, perche' il
rischio colpisce il **tempo**, non il calendario. ⚠️ **MA a p=5% il capitale mediano e' $0 per
OGNI calendario** (64% di rovina su 20a) → **formulazione piu' netta del rischio di venue trovata
finora: non erode il piano, lo CANCELLA.**
(4) **LA DOMANDA INVERSA — rendita netta €/g mediana** (riformula l'obiettivo: €50/g e' UN punto,
non l'unico risultato):
| €/mese | 5a | 10a | 15a | 20a | P(€50/g a 20a) |
|---|---|---|---|---|---|
| 100 | 2.08 | 6.54 | 16.40 | 38.07 | 31.0% |
| 150 | 3.00 | 9.55 | 23.96 | 55.80 | 58.7% |
| **250** | 4.83 | 15.53 | 39.11 | **91.30** | **90.0%** |
| 400 | 7.59 | 24.52 | 61.85 | 144.23 | 98.9% |
| 600 | 11.26 | 36.52 | 92.17 | 215.07 | 100.0% |
Non-linearita': **da 15 a 20 anni la rendita piu' che raddoppia a ogni livello** (gli ultimi anni
contano piu' in *rendita*, i primi piu' in *versamenti*).
(5) **FREQUENZA = la decisione meno importante.** Mensile fino a ~$2 di costo per trasferimento,
bimestrale sopra, trimestrale oltre $25 — ma le differenze sono **1-3%** del capitale finale.
Verificato che un deposito **non resta strozzato**: col cap dinamico attivo cap = equity/2 =
esattamente il nozionale massimo richiedibile per asset.
📌 **ORDINE DI IMPORTANZA (da citare quando si parla del piano):** versare o no (**da mai a 16
anni**) > quando (**6x**) > quanto presto si smette (5 anni = 58% del risultato) > piatto vs
crescente (24%) > frequenza (1-3%). E sopra tutte, fuori scala: **a p=5% di rischio venue il
risultato mediano e' zero comunque.**
**REGOLE:** (a) un piano di accumulo si giudica sulle sue **deviazioni**, non sul caso nominale —
piatto/ininterrotto/per sempre e' l'unico scenario che non succede; (b) piani di taglia diversa si
confrontano con una metrica **normalizzata** (`$ finale / $ versato`), altrimenti "versa di piu'"
vince sempre; (c) un vantaggio calcolato **ignorando un rischio noto** va ri-misurato con quel
rischio dentro anche quando ci si aspetta che regga (qui reggeva, ma la colonna p=5% ha prodotto
il risultato piu' importante del filone); (d) quando l'obiettivo dichiarato non e' raggiungibile,
la tabella utile e' quella **inversa** — non "quando arrivo a X" ma "cosa compro con quello che
ho".
- ❌ **MURO COME PUNTO FISSO — la mia previsione era SBAGLIATA e il numero pubblicato era giusto
per caso (2026-07-26).** Script `r0726_wall_fixedpoint.py`, test `tests/test_wall_fixedpoint.py`
(11), diario `2026-07-26-wall-fixedpoint.md`. **Book/pesi/cron INVARIATI.**
Il follow-up dichiarato diceva: *"i muri usano il book a 2 sleeve da $600 estrapolato a $272k;
il book diversificato ha Sharpe piu' alto → **il muro vero e' piu' basso**"*. **Misurato: FALSO.**
(1) **Struttura giusta: il muro e' un PUNTO FISSO** — serve capitale C per girare il book che
determina il muro C → si itera `C_{n+1} = muro(book(C_n))`. Converge in **1 iterazione** perche'
il muro cade **sopra** la soglia XS01 ($117k), quindi la composizione non cambia; la struttura
conta solo se il muro atterra vicino a una soglia — ma **va iterato per saperlo**.
(2) **Risultato:** book deployable (TP01 38/SKH01 23/GTAA01 23/XS01 17; **VRP01 escluso** per
regola short-vol, **XSR01 escluso** per gate 23/10; costi capital-aware; ancora ×0.860 misurata
su questo book) → Sharpe **1.94**, vol 8.9%, CAGR 18.3%. **Muro $273.900 vs $272.061 = +1%.**
(3) **Perche':** diversificare alza lo Sharpe (1.64 → 1.94) ma abbassa **drift e vol insieme**;
la rendita perpetua vive sul **drift** → 10.91% → 10.84% = invariata. **Il guadagno di Sharpe va
in meno rischio, non in piu' reddito** — cioe' il fatto gia' misurato il 25/07 §3, che avevo
dimenticato scrivendo il follow-up.
(4) ⚠️ **Stavo violando una regola gia' codificata:** *"un diversificatore a basso CAGR si giudica
a ISO-RISCHIO, mai a iso-nozionale"* (25/07 §3). A iso-rischio (leva **1.28x**): rendita 13.35%,
**muro $222.406 = 18%** — ✅ **replica indipendente** del 19% misurato il 25/07 con macchineria
e book diversi. Vale **solo** se la leva e' disponibile e a costo < uplift (IB: Reg-T 2x,
portfolio margin da ~$110k, margine ~5.5%/a): **$222k e' un TETTO, non una stima.**
(5) ⚠️ **BUG catturato prima di pubblicare:** la prima corsa dava Sharpe 0.95 e muro **$854k**
("diversificare triplica il muro" — spettacolare e falso). Causa: `CC.gtaa_banded` ritorna la
storia GTAA **dal 1996** mentre lo sleeve di produzione tronca a `GTAA_BOOK_ACTIVATION`; con la
rinormalizzazione per-riga di `combine_outer` il **75% del campione** era **GTAA01 da solo al
100%**. Preso **non da un test** ma perche' la somma pesata dei componenti (~18%) non tornava col
drift del combinato (6.8%). **Diagnostica decisiva: la COPERTURA PER COLONNA** (TP01 24.6% /
SKH01 24.6% / GTAA01 100.0% / XS01 8.6%) — un 100% accanto a valori bassi dice tutto.
**REGOLE:** (a) un follow-up dichiarato contiene una **previsione**, che va misurata non assunta;
(b) **rileggere le regole gia' codificate prima di impostare il confronto**; (c) quando un
aggregato non torna con la somma dei suoi pezzi, **fermarsi** — era l'unico segnale del bug
(nessun test, nessuna eccezione, output plausibile); (d) **la copertura per colonna e' la prima
diagnostica di un outer-join**.
- 💰 **IL CAPITALE GIA' FERMO — la leva mai misurata, e la decisione di venue che ne dipende
(2026-07-27).** `r0727_lumpsum_split.py`, test `tests/test_lumpsum_split.py` (14), diario
`2026-07-27-lumpsum-venue-gates.md`. **Book/pesi/config INVARIATI.** Tutte le traiettorie del
25-26/07 hanno `START = 600.0` **cablato**: il progetto ha misurato il *calendario* dei
versamenti (fattore 6) e mai un **versamento iniziale**, mentre su Revolut ci sono ~€10.000 (di
cui €6.043 in XEON a ~0% reale netto) contro i $600 che girano. Macchineria = generalizzazione
di `r0726_venue_risk.simulate`, **validata: con lump 0 riproduce IDENTICI i numeri del 26/07**.
(1) **Cosa compra** (p=0, 20a): **€10.000 fermi oggi e mai piu' nulla → traguardo 17.2a mediani,
P 62%, rendita €61.58/g** (il piano €250/m senza lump: 15.7a, P 95%, ma $66.818 versati contro
$10.900). Con entrambi: **13.3a, P 99.5%**. (2) ⚠️ **L'equivalenza si misura in versamento
mensile equivalente, NON in versamenti risparmiati**: la prima stesura diceva "€10k ≈ €7.414
risparmiati = 0.7x" — numero giusto, **domanda sbagliata** (il valore e' arrivare prima, non
versare meno), e invita alla conclusione opposta. Onesto: **€10.000 oggi = +€154/mese per 13
anni = €24.523, cioe' 2.45×.** (3) **Col rischio di venue dentro** (a €10k il conto e' $11.500 e
lo split diventa possibile — a quota IB **26%**, non il 25% preferito: sotto $3.000 la gamba
equity non esiste): lo split costa **1.9-2.6pp** di P(arrivare) e taglia **P(perso tutto) da
18.4% a 3.5%** a p=1% (da 33.7% a 11.2% a p=2%). Il haircut dichiarato sulla gamba IB (0.8pp
taglia piccola + 0.1pp UCITS) sposta **0.1-0.2pp**: il costo della gamba equity non decide.
⚠️ **P(perso tutto) sotto concentrazione NON dipende dal capitale** (con un conto solo "almeno
un fallimento" coincide con "perso tutto"): il lump non la peggiora, **moltiplica cio' che porta
via**. (4) ✅ **IL RISULTATO OPERATIVO — la protezione non e' bloccata dal PRIIPs.** GTAA01 oggi
non e' deployabile, quindi misurato anche lo **SPLIT-CASSA** (seconda gamba ferma): costa
**0.6-0.8pp** di P(arrivare) in piu' e **la protezione e' IDENTICA** (dipende da quanti conti
falliscono, non da cosa ci sta sopra). **Un rischio non-diversificabile si compra con un secondo
CONTO, non con un secondo sleeve.** (5) **Soglie:** split a quota raccomandata da **$12.000**,
forzando al 35% da **$8.571**. La riapertura della decisione venue e' a $20.000: **un lump da
€10k cade sotto quella soglia ma sopra la fattibilita' tecnica, ed e' un cambiamento del piano
= il caso in cui CLAUDE.md dice di riaprire PRIMA.** Materiale pronto; la decisione resta
dell'operatore. ⚠️ Cosa NON decide: quanto dei €6.043 sia vero fondo d'emergenza (un fondo
d'emergenza non e' capitale disponibile).
**ADDENDUM "€5k messi dove" (stessa sessione).** (a) ⚠️ La configurazione REALE non era quella
tabulata: `config/live.json` fu alzato il 26/07 *"in previsione del versamento (EUR 5.000 +
500/mese)"* → il piano e' **€500/mese**, non €250. **Nessuna azione di config al deposito**: il
cap e' gia' `min($3.000, equity_osservata × 0.5)` = leva lorda ≤1x a $6.050, protetto dal
watermark. (b) **A $6.050 non si sblocca NIENTE** (GTAA01 vorrebbe il 50% del conto e non e'
deployabile; XS01 $20k; XSR01 sotto gate) → il book resta TP01+SKH01 e l'unica domanda e' quanta
parte NON sta sull'exchange. (c) **Split in liquidita', p=1%, 20a:** fuori 0% → P(arrivare)
89.4% / 11.4a / P(perso tutto) 18.4%; **10% → 89.0% / 11.8a / 3.5% [0.0% con cassa in banca] /
salvati $6.818**; **25% → 88.5% / 12.4a / salvati $17.045**; 40% → 87.8% / 13.3a / $27.272.
⚠️ **P(perso tutto) SATURA a qualunque quota > 0** (3.5% al 10, 25 e 40%): la protezione binaria
si compra col FATTO di avere un secondo conto, non con quanto ci si mette → **quando una metrica
binaria satura, la decisione si sposta sulla metrica continua** (qui il salvataggio).
⚠️ **Errore mio corretto in sessione:** la colonna del salvataggio riportava prima il capitale a
20 anni condizionato al fallimento ($77k al 10% = 11× il vero), gonfiato dalla convenzione
ereditata dal 26/07 per cui **i versamenti si dirottano ai superstiti** (e si scartano se non ne
resta nessuno) → attribuiva allo split il valore di *continuare a versare*, che si ottiene
comunque aprendo un altro conto. Misura onesta = **salvataggio ISTANTANEO**, congelata in
`test_il_salvataggio_e_istantaneo_non_a_scadenza`. (d) ✅ **Lo split non si costruisce spostando
soldi su un secondo venue: si ottiene versandone di meno.** Con €6.043 in XEON, deporne €5.000
lascia fuori ~16% del capitale investito = gia' dentro la banda 10-25%, **a costo operativo
zero**. Prezzo del 25%: 0.9pp di P(arrivare) e ~1 anno di ritardo mediano.
- 💰 **IL FISCO DURANTE L'ACCUMULO — mai contato in nessuna traiettoria, vale 30% a 10 anni
(misurato 2026-07-27, registrato qui il 2026-08-07).** ⚠️ I quattro risultati del 27/07 sera
(`r0727_3k_vs_5k.py`, `r0727_orizzonte10.py`, `r0727_tasse.py`) erano **solo nei messaggi di
commit**, ne' in CLAUDE.md ne' in un diario — e uno cambia il numero di testa del piano.
`TAX_RATE` compariva in **un solo punto** del progetto: la lordizzazione del bersaglio in fase di
*prelievo*. L'accumulo componeva al **lordo** per dieci o vent'anni.
**Costo (lump €5.000 + €500/mese): 15.7% a 5 anni · 29.8% a 10 · 42.6% a 15**
(27/07 su lump €10k: 16.8 / 31 / 44% → replica coerente). **L'errore e' COMPOSTO.**
⚠️ **Conseguenza: TUTTE le tabelle a 15-20 anni pubblicate sopra sono al LORDO** del fisco
d'accumulo e vanno lette con questo sconto. Assunzioni dichiarate (33% plusvalenze, minusvalenze
in carry 4 anni, 0.2% annuo sul valore), **non un parere fiscale**; il modello tassa la variazione
ANNUA di valore = limite superiore, stretto perche' il book realizza quasi tutto entro l'anno.
**Vincolo dei 10 anni (operatore, 49 anni): il piano NON lo regge.** €5k+€500/m → P(entro 10a)
**19.7%** al lordo; €10k+€500/m → 33.9% lordo ma **3.8% col fisco**. Servono **€880/mese a P=50%**,
**€1.051 a P=75%** (lump €10k) → si versano oltre $119.000 per arrivare a $272.061: *a orizzonte
corto non fai lavorare la strategia, compri il capitale coi bonifici*.
📌 **Contro-intuitivo: versare di piu' RITARDA il sorpasso** (l'anno in cui il guadagno cumulato
supera tutto il versato): 7º anno a €500/m, **8º a €800/m** — alza l'asticella. I €300 in piu'
comprano il **traguardo**, non il sorpasso: mediana al bersaglio al 12º anno invece del 15º,
P(bersaglio) a 15a da 73.2% a **99.0%**. Lump €3k vs €5k = ~1.8 mesi ogni €1.000 → non e' una
decisione. Script `scripts/research/r0807_growth_yearly.py`.
✅ **TABELLE RIFATTE AL NETTO (2026-08-07) — il muro si sposta poco, i VERSAMENTI molto.**
`scripts/research/r0807_piano_netto.py`, test `tests/test_piano_netto.py` (16), diario
`2026-08-07-piano-al-netto.md`. **Book/pesi/cron/config INVARIATI.**
**(0) Controllo di replica superato:** a fisco spento la nuova macchina riproduce **$272.061 al
dollaro** (implementazione separata) e la colonna LORDA delle traiettorie riproduce **4 righe su
4** della tabella pubblicata (16.3/12.4/9.0/6.0 contro 16.2/12.4/9.0/6.0). ⚠️ Trovato per
strada: `perp_and_wall` gira a **2000 path** e a quella taglia da' **$269.648** → **la terza
cifra del muro e' rumore Monte Carlo (0.9%)**: si cita **$272k**, non $272.061.
**(1) IL MURO era calcolato con una convenzione ASIMMETRICA** — prelievo lordizzato ma capitale
che compone senza mai pagare imposte. Coerente (imposte annue dentro il portafoglio, prelievo
gia' netto): perpetua **10.91% → 7.70%**, muro **$272.061 → $258.338 (5.0%)**; a 26% $236.310.
I due errori vanno in versi OPPOSTI e **si compensano quasi — per caso, non per costruzione**.
**(2) TRAIETTORIE da $600** (25a, 3000 path, seed 725; mediana CONDIZIONATA all'arrivo + P(20a)):
| €/mese | LORDO anni / P(20a) | **NETTO anni / P(20a)** |
|---|---|---|
| 0 | mai / 0% | **mai / 0%** |
| **250** | 16.3a / **92%** | **19.8a / 52%** |
| 500 | 12.4a / 100% | **14.7a / 99%** |
| 800 | 10.0a / 100% | **11.4a / 100%** |
| 1000 | 9.0a / 100% | **10.0a / 100%** |
| 2000 | 6.0a / 100% | **6.4a / 100%** |
📌 **€250/mese — il livello con cui il piano risultava «P 92%, funziona» — al netto e' una
moneta (52%).**
**(3) QUANTO VERSARE, al netto** (bersaglio $258.338; fra parentesi il vecchio numero lordo):
| orizzonte | P=50% | P=75% | **P=90%** | P=95% | tot. versato @P=90% |
|---|---|---|---|---|---|
| **10 anni** | €998/m | €1.162/m | **€1.323/m** *(€1.178)* | €1.424/m | **$175.058** *($155.923)* |
| 15 anni | €470/m | €570/m | **€672/m** *(€509)* | €725/m | $133.983 *($101.643)* |
| 20 anni | €245/m | €306/m | **€371/m** *(€237)* | €417/m | $98.762 *($63.406)* |
**Il fisco costa +12% al mese a 10 anni, +32% a 15, +57% a 20** (cresce con l'orizzonte perche'
l'errore era composto). La lettura del 26/07 si RAFFORZA: a 10 anni versi $175k per arrivare a
$258k (il rendimento fa il **32%**), a 20 anni ne versi $99k (il rendimento fa il **62%**).
**(4) RENDITA €/g mediana, netta** (perpetua 7.70%, imposte gia' dentro — non lordizzare due
volte): €250/m → 4.38 (5a) / 11.82 (10a) / 24.61 (15a) / **46.61 (20a)**, P(€50/g a 20a)
**42.0%** contro i **91.30 €/g e 90.0%** pubblicati; €500/m → 92.11 e 97.6%; €800/m → 146.78.
**COSA NON CAMBIA:** senza versamenti il capitale-rendita non si raggiunge **mai** a nessuna
lente fiscale; l'ordine delle leve (versare > quando > quanto presto si smette > piatto vs
crescente > frequenza) e' invariato; il rischio di venue resta fuori scala (a p=5% il mediano
e' zero comunque).
⚠️ **ERRORE MIO catturato prima di pubblicare:** la prima stesura calcolava la mediana degli
anni sull'INTERO vettore coi non-arrivi a `-1` → €250/mese risultava passare da 15.7 a **15.3**
anni col fisco (*piu' veloce*) mentre P crollava da 91% a 53%, perche' con meta' dei path a 1
la mediana cade sui PRIMI arrivi. **REGOLA: un non-arrivo va codificato +∞, mai 1** — con 1 il
numero migliora tanto piu' quanto peggio va la colonna. E **una mediana condizionata si stampa
sempre accanto alla sua probabilita'**.
**REGOLE:** (a) un modello che tassa una meta' del conto e non l'altra non e' conservativo, e'
**incoerente** — e i due errori possono compensarsi quasi esattamente, il che li rende
invisibili finche' non si rifa' il conto in modo simmetrico; (b) prima di pubblicare un numero
nuovo, **far riprodurre alla macchina quello vecchio**; (c) **un Monte Carlo ha una risoluzione
e va detta** ($272.061 e' esatto quanto $269.648: la differenza e' la taglia del campione).
- 🇮🇹 **QUADRO FISCALE VERIFICATO SULLE FONTI (2026-08-07) — il 33% e' confermato, la citazione
normativa del progetto era SBAGLIATA, e la domanda che vale $22k resta aperta.**
Fonti: Fisco Oggi (rivista dell'Agenzia), Eutekne, Fiscomania, Circolare AdE 30/E del
27/10/2023, guide professionali. **Non e' un parere fiscale.** Corretto in
`r0725_capcurve.py`, `r0725_ib10k.py`, `r0727_tasse.py`, `r0807_asset_compare.py` e nel diario
24/07. **Nessun numero del piano cambia**: l'aliquota assunta era ed e' 33%.
**CONFERMATO** — (a) **33%** sulle plusvalenze cripto realizzate **dal 1/1/2026**, su
`art. 67 c.1 lett. c-sexies` TUIR; (b) **franchigia €2.000 ABOLITA dal 2025**;
(c) **minusvalenze riportabili 4 periodi d'imposta** (`art. 68 c. 9-bis`) ma **solo contro
plusvalenze cripto** — comparto separato dagli strumenti finanziari tradizionali;
(d) **patrimoniale 2‰** sul valore al **31 dicembre** (dovuta sopra €12/anno), quadro RW/W;
(e) regime **dichiarativo** per gli exchange esteri (quadro RT per i redditi, RW per il
monitoraggio).
⚠️ **CORREZIONE DI FONTE:** il progetto citava ovunque «L.199/2025» come origine del 33%.
**Falso.** Il 33% dal 2026 e l'abolizione della franchigia vengono dalla **L. 207/2024
art. 1 c. 23-29** (Bilancio 2025). La **L. 199/2025 art. 1 c. 28** (Bilancio 2026) fa un'altra
cosa: ritaglia il **26% per i soli token di moneta elettronica denominati in EURO** (EMT ex
Reg. UE 2023/1114, riserve interamente in attivi in euro presso soggetti UE autorizzati) e
stabilisce che la conversione euro↔EMT non e' realizzo. **BTC/ETH e le stablecoin in DOLLARI
restano al 33%** → nessuna scappatoia per questo book.
**APERTA, e nessuna fonte la chiude: come si qualificano i DERIVATI Deribit.** La Circolare
30/E (118 pagine) definisce le cripto-attivita' e **non tratta i derivati**. Le fonti
professionali sono nette nel senso opposto al nostro assunto — *«i CFD su crypto NON sono
cripto-attivita': sono derivati su sottostante crypto e vivono nella Sezione II del Quadro RT,
aliquota 26%»* (`lett. c-quater`) — **ma parlano di CFD di broker UE regolati in euro**, non di
contratti *inverse* marginati e regolati **in cripto** su sede extra-UE, che e' esattamente il
caso in cui la qualificazione puo' ribaltarsi. **Domanda da porre al commercialista, in questi
termini:** i future/opzioni BTC-ETH di Deribit (inverse, margine e regolamento in cripto, sede
extra-UE) stanno in `c-sexies` (33%, RT Sez. V) o `c-quater` (26%, RT Sez. II)? E il
collaterale in BTC/ETH sconta comunque il 2‰ al 31/12?
💰 **Vale $258.338 → $236.310 di muro (8.5%) e ~€30/mese di versamento a 20 anni** — piu' di
quasi tutti gli uplift per cui in questo progetto si e' discusso se ammettere uno sleeve, e non
si risolve backtestando.
⚠️ **Conseguenza modellistica trovata qui:** se i derivati sono `c-quater` sono un **comparto
di compensazione SEPARATO** dalle cripto → il buffer di carry UNICO a 4 anni di
`r0807_piano_netto` / `r0727_tasse` e' ottimistico **sulla coda** (non sull'aliquota).
📌 L'**affrancamento al 18%** (rideterminazione del costo ai valori 1/1/2025, L. 207/2024) e'
**scaduto il 30/11/2025**: non e' una strada aperta, e a questo capitale non lo sarebbe stata.
**REGOLA: una fonte normativa citata in un commento di codice si verifica come un numero**
questa era sbagliata da settimane in 5 file, e nessun test poteva accorgersene.
- ⚖️ **BOOK vs ETF (S&P 500 / MSCI World) — le due lenti danno risposte OPPOSTE, e la scelta
robusta e' un MIX 50/50 (2026-08-07).** Script `r0807_asset_compare.py` + `r0807_best_strategy.py`;
diario `2026-08-07-crescita-fisco-etf-scelta.md`. **Book/pesi/cron/config INVARIATI.**
Tre cose rese comparabili: **griglia** (azioni su calendario con 0.0 a borsa chiusa = convenzione
GTAA01; senza, Sharpe ×1.20 — lezione 25/07), **fisco** (book realizza ogni anno al 33%; UCITS ad
accumulazione paga 26% **alla vendita** → le curve ETF sono valori di *liquidazione*: il
differimento e' un vantaggio strutturale dell'ETF ed e' nel modello), **bersaglio** ($272.061 vale
per la rendita perpetua *del book* e per il 33% → ricalcolato per ciascuno).
| a 15a, €5k+€500/m | book | S&P 500 | MSCI World (proxy) |
|---|---|---|---|
| rendita perpetua | 11.67% | 4.16% | 3.09% |
| capitale-rendita | $254.524 | $646.172 | $869.729 |
| lente A (storia piena) | $293.823 → **115%** | $204.663 → 32% | $188.417 → 22% |
| lente B (stessa finestra) | $295.540 → 118% | $343.805 → **110%** | $296.707 → 82% |
📌 **Il risultato non e' chi vince, e' che le due lenti si contraddicono:** sulla storia piena il
book stravince, **sulla stessa finestra l'S&P 500 accumula PIU' del book** — il divario della
lente A e' tutto nei crolli 2000/2008 che la strategia non ha mai vissuto.
📌 **E sulla stessa finestra il rendimento e' quasi identico — 17.4% contro 16.8%.** Tutta la
differenza e' nel RISCHIO (vol 11.0 vs 19.6%, maxDD 10.5 vs 33.7%): **il book non guadagna di
piu', perde di meno** — conferma indipendente di cio' che il progetto scrive di TP01 dal 19/06,
misurata contro un'alternativa vera. La rendita perpetua vive sul drawdown → **2.5× meno capitale**.
⚠️ **Un MIX esiste solo dove esistono ENTRAMBE le serie** (errore commesso e corretto: la lente
"storia piena" calcolava i bersagli del mix sull'intersezione → dichiarava 30 anni e ne usava 7).
Percio' la scelta si giudica su UNA finestra con gli scenari espressi come spostamento del
**drift**, simmetrico sui due lati. **A 12 anni, quota del proprio bersaglio:**
| w book | base | equity 30a | book/2 | entrambi cauti | peggiore | **rimpianto max** |
|---|---|---|---|---|---|---|
| 0% | 68% | 23% | 68% | 23% | 23% | 53% |
| 25% | 80% | 41% | 60% | 27% | 27% | 34% |
| **50%** | **86%** | 58% | 48% | 27% | 27% | **20%** |
| 75% | 84% | 69% | 32% | 23% | 23% | 35% |
| 100% | 75% | **75%** | 16% | 16% | 16% | 52% |
**Risposta: 50/50** — non perche' vinca (vince solo nel base) ma per il **rimpianto minimo**.
⚠️ Il criterio del solo caso peggiore **non** distingue 25% da 50% (27.09 vs 26.99% = pareggio
dentro il rumore): a separarli e' il rimpianto.
📌 **L'asimmetria fra i due stress E' il risultato:** quello sull'equity e' **MISURATO** (30 anni
esistono: 11.3% invece di 16.5%), quello sul book e' **GIUDIZIALE** (meta' del drift, a mano,
perche' 7.4 anni sono tutta la storia che ha e non c'e' nulla con cui stressarlo). Conseguenza
brutale: col drift dimezzato il capitale-rendita del book puro passa da $254k a **$771.646**
(rendita 11.67% → 3.85%).
**Perche' il mix non e' un compromesso:** corr book↔S&P **+0.082** (borsa aperta), **+0.046** nei
ribassi; nel 5% di giornate peggiori dell'indice (media 2.94%) il book fa **0.11%** → il
capitale-rendita del 50/50 e' **$235.769**, piu' basso sia del solo ETF ($311.567) sia del solo
book ($254.524): **due motori scorrelati abbassano l'asticella**.
⚠️ MSCI World e' un **PROXY** 70% SPY + 30% EFA (URTH/ACWI/VT non sono nell'abbonamento dati IB);
fra 50 e 80% di quota USA il drift si muove di 0.7pt e il Sharpe di 0.05. Il rischio di venue NON
e' nel conto (spingerebbe ancora verso il mix) e la decisione del 26/07 tiene tutto su Deribit
fino a $20k → **questa analisi e' materiale per quella soglia, non un'indicazione di agire ora**.
- 🖥️ **SIMULATORE NEL BROWSER — `scripts/web/` (2026-08-07).** Motore di accumulo in JavaScript
(`engine.js`) sui ritorni veri del book esportati da `export_series.py`; pagine assemblate da
`build.py` (dati **iniettati** da JSON, mai trascritti); `test_engine.js` prova che il motore
riproduce `r0807_growth_yearly.py` e `dep_necessario`; `smoke.js` ESEGUE le pagine con un DOM
finto. **REGOLE (quattro errori miei, tutti trovati da un controllo e non a occhio):**
(a) i dati di una pagina si **iniettano da un file**, mai si trascrivono — due anni di una serie
erano stati scritti *a memoria* perche' `tail` aveva troncato l'output;
(b) **un confronto punto-contro-distribuzione non prova una distorsione**: "8 semi JS tutti sopra
il Python, +0.75%" erano 8 estrazioni contro UN punto rumoroso → misurato bene (8 semi per parte)
**0.01%, t = 0.04**, ed entrambi i campionatori entro 1.7 SE dall'atteso ANALITICO;
(c) le chiavi di un dizionario Python si **leggono dal JSON**, non si ricostruiscono
(`"0.0"` vs `String(0)`=`"0"` → pagina pubblicata rotta, e i pesi intermedi funzionavano *per caso*);
(d) **`node --check` valida solo la SINTASSI**: una pagina puo' passarlo e morire alla prima riga.
Misura che ha guidato una scelta: la banda del versamento suggerito resta **±1% da 1.200 a 3.000
percorsi** → non domina il Monte Carlo ma la **granularita' della bisezione** (~€5) → percorsi
tenuti bassi e incertezza **dichiarata**.
- 🚨 **IL PIANO AL NETTO DI TUTTO — la tabella congiunta (2026-08-22, `r0822d_piano_vero.py`).**
**QUESTA SOSTITUISCE OGNI TABELLA DI TRAIETTORIA PUBBLICATA SOPRA.** Le due correzioni misurate al
piano — **fisco d'accumulo** (07/08) e **funding** (22/08) — erano state applicate **separatamente
allo stesso numero lordo**, e la tabella con **entrambe** non esisteva. Registro §36.
**Replica 6/6 prima di pubblicare, due esatte AL DOLLARO** sul vintage 07/08 ($272.061 lordo,
$258.338 netto fisco). ⚠️ Sulla serie di **oggi** la stessa riga da' **$278.033 (+2,2%)** — 15
giorni di dati in piu', `data/raw/` gitignored (**stessa lezione GTAA del 07/08**) → **ogni terza
cifra di un muro e' rumore**, e i confronti fra lenti vanno fatti sullo stesso vintage.
| lente (de-luck ×0,89) | drift | perpetua | muro | **€250/m da $635: P(20a)** |
|---|---|---|---|---|
| L0 LORDO (26/07) | 17,11% | 10,68% | $278k | **90%** *(pubbl. 92%)* |
| L1 +FISCO (07/08) | 17,11% | 7,52% | $264k | **49%** *(pubbl. 52%)* |
| L2 +FUNDING (22/08) | 15,19% | 9,06% | $328k | **63%** |
| 🚨 **L3 CONGIUNTA** | **15,19%** | **6,35%** | **$313k** | **14%** |
| L3b congiunta, funding "strumento vero" 1,39%/a | 15,87% | 6,72% | $296k | **26%** |
🚨 **IL MURO E' UNA MEDIANA, E LA SUA BANDA E' ESPLOSIVA (misurato dal critico, 23/08).**
La SE del drift L3 e' **5,151%/anno** (block bootstrap 20g; replicata su un'altra lente a 5,09):
**+1 SE → $204.517 · p90 → $186.623 · PUNTO $313.143 · p10 → $1.141.172 · 1 SE → $709.753 ·
2 SE → il traguardo NON ESISTE a nessun capitale.** «€500/mese → P(20a) 85%» diventa **~0% a
1 SE**; «€250/mese → 14%» diventa **96% a +1 SE**. 📌 **Meccanismo: il muro e'
`prelievo/perpetua` e la perpetua si annulla molto prima del drift → e' un 1/x su una quantita'
che va a zero, quindi l'errore e' asimmetrico verso l'alto.** 🚨 **E il progetto ha pubblicato la
risoluzione MONTE CARLO del muro (0,7%) accanto a un numero la cui incertezza di PARAMETRO e'
cento volte piu' grande: ha misurato la precisione del simulatore e mai quella del suo input.**
⚠️ La SE del block-bootstrap e' un **limite inferiore** (variabilita' dentro gli stessi 7,4 anni,
non il cambio di regime). **REGOLA: il piano si dimensiona sul VERSAMENTO, che e' certo, non sul
muro.**
🚨 **LE DUE CORREZIONI NON SI COMPENSANO: SI SOMMANO — e nel VERSAMENTO necessario il congiunto e'
+10% PEGGIORE della loro somma.** Il meccanismo "meno drift → meno plusvalenza → meno imposta"
**esiste** (+9,97% di capitale recuperato; il fisco toglie il 50,9% a funding OFF e il 46,0% a
funding ON) **ma la funzione versamento→probabilita' e' CONVESSA e ne ribalta il segno**.
Interazione misurata **in tre monete**: +9,97% sul capitale · **$815 sul muro (0,26%, SOTTO la
risoluzione MC)** · **+26 €/mese sul versamento (+10%, segno stabile su 3 semi)**.
**REGOLA: non contare su una compensazione fra correzioni misurate separatamente — la si misura, e
in piu' di una MONETA, perche' un'interazione piccola cambia segno con la moneta.**
**QUANTO VERSARE, al netto di tutto** (da $635, nessun lump):
| orizzonte | P=50% | P=75% | **P=90%** | totale versato @P=90% | **quota del bersaglio** |
|---|---|---|---|---|---|
| **10 anni** | €1.323 | €1.534 | **€1.733** | **$229.189** | **73%** |
| 15 anni | €656 | €784 | **€920** | $183.129 | 58% |
| 20 anni | €360 | €445 | **€541** | $143.804 | 46% |
Il **solo funding** aggiunge **1,27× / 1,33× / 1,40×** sopra la lente fisco-only.
**RENDITA €/g mediana a 20 anni:** €250/m → **31,85 €/g, P(≥50/g) 8,8%** · €500/m → **63,05, P
77,1%** · €800/m → 100,50, P 99,2%.
📌 **IL LUMP, mai entrato nelle tabelle nette: €10.000 oggi + €250/m porta P(20a) da 14% a 45%**
(€5.000 → 30%). E' la leva piu' grande misurata dopo il versamento mensile stesso.
📌 **LA RISPOSTA ALLA DOMANDA DEL PROGETTO:** *€250/mese da $635 → **23,4 anni** (mediana
incondizionata; 22,1 condizionata all'arrivo), **P(20a) 14%** [banda "strumento vero": 22,2 anni,
26%]. Per **€50/giorno in 10 anni** servono **€1.733/mese** a P=90%, totale versato **$229.189**.*
**A 10 anni si versano $229k per arrivare a ~$313k: il rendimento fa il 27%, i bonifici il 73%**
⚠️ **CORREZIONE 23/08: il 73% e' l'INTERO ORIZZONTE, non il versato fino all'arrivo.** Il
contatore `versato` di `accumula` e' uno **scalare che non si ferma al traguardo** (stessa
famiglia del bug `paid` catturato il 25/07, in un altro punto): il versato **fino all'arrivo** e'
**$190.265 = il 61%**, con P(traguardo) 91%. E **«€X/mese» versa ogni 30 GIORNI** →
**121/182/243** versamenti invece di 120/180/240 (+0,83/+1,11/+1,25%), in ogni tabella dal 25/07.
Entrambi i difetti vanno **a favore** del piano. —
forma piu' netta della lezione gia' scritta: *a orizzonte corto non fai lavorare la strategia,
COMPRI il capitale coi bonifici.*
⚠️ **Cosa NON e' incluso, di proposito:** il **rischio di venue**, che e' **rovina e non costo**
(a p=5% il mediano e' zero a ogni calendario) — resta sul suo asse separato.
**Smentitori dichiarati:** il funding pre-2022 e' **proxy inverse sul 40% del campione** (se il
lineare 2019-21 fosse costato quanto il 2022+, la colonna vera e' **L3b**); il modello fiscale tassa
la **variazione annua di valore** = limite superiore; il bootstrap gira su **7,4 anni con due tori**.
- ⚖️ **CANALE FUNDED — `GATE PROP-01` CHIUSO 3/3 in una notte, e il numero onesto e' 2,6%
(2026-08-22).** Registro §29-32, §37. **Libro, pesi, cron, config INVARIATI.**
Il gate nato ieri con **due gambe datate ad anni** e' stato chiuso **tutto oggi**, e ogni chiusura
ha peggiorato il numero:
| gamba | criterio | esito |
|---|---|---|
| **(a) LISTINO** | ≥10 delle 13 gambe shortabili | ✅ **PASS 13/13** — misurato sul **venue** (Bybit `instruments-info`, 833 strumenti), non sul sito |
| **(b) ANCORA SKH01** | delta appaiato > +0,05 sui 23 offset | ✅ **PASS 23/23**, +0,116, 230/230 celle congiunte — ⚠️ ma «23/23» vale **~2 osservazioni**, vedi sotto |
| **(c) CAPITALE** | *riformulata, vedi sotto* | ✅ **PASS 2/2** |
📌 **La (c) e' stata RIFORMULATA perche' era una constatazione, non un criterio** — e la sua forma e'
**imposta dal costo della misura**: l'EV costa una corsa di script (**zero dollari**), la
**rimborsabilita' del deposito costa $579 e si puo' misurare solo COMPRANDO** → un gate d'attesa su
quella sarebbe **superabile solo dopo averlo violato**. Quindi:
**(c1) ECONOMICA** — EV del biglietto **> 0 con la quota trattata come SPESA PERSA**, alla cella
d'ancora **mediana** (non canonica), col **funding dentro**, lente **accoppiata**. *Oggi **$+1.615***.
**(c2) DI SOPRAVVIVENZA** — prezzo del biglietto **≤ 2 versamenti mensili del piano in corso**
(il piano d'accumulo e' la leva dominante misurata, e **zero e' assorbente**). *Oggi **$579 contro
$1.090***. **(c1) e' scritta perche' la rimborsabilita' NON decida:** il rimborso e' **un regalo, non
un'ipotesi su cui si e' scommesso**.
🚨 **IL NUMERO, e la catena di un giorno solo: P(≥50 €/g) da €600 in 36 mesi = 42% → 7,8% → 4,4% →
2,6% [1,5%, 4,7%], con P(zero) 40,3%.** Nell'ordine: universo fuori campione (42% del J stava in 6
gambe che nel 2021-23 non esistevano) · banda d'ancora di SKH01 (canonica al 91° pctl) · **funding**
(dJ 0,049 su **69/69** celle appaiate).
📌 **EV del biglietto POSITIVO in tutte e tre le convenzioni sul deposito** (spesa persa +$1.615 ·
rimborso al pass +$1.919 · rimborso al primo payout +$1.835). ⚠️ **E il progetto conteneva GIA' due
modelli contraddittori della quota:** `r0725_hyro:200` la rimborsa **al pass**, `pc.simulate` **mai**
— e il numero operativo usciva dal secondo.
🚨 **XS01 PAGA il funding, non lo incassa — meccanismo DIMOSTRATO:** `max|ΣW| = 2,8e-17`
(dollar-neutral esatto) ⇒ **il livello del funding non entra, entra solo la DISPERSIONE
cross-sezionale**, e il momentum compra i perp col funding **piu' caro****+1,45%/anno**
[IC95 +1,18/+1,72; banda onesta +0,69/+1,45 se HL e' ~2,1× il venue]. *L'ipotesi opposta ("market-
neutral ⇒ si compensa") era esplicita nel briefing ed e' falsa.*
📌 **Piu' leva sul funded ALZA E[payout] e ABBASSA P(payout): sono due obiettivi diversi**, e chi
vuole il rimborso non vuole la leva massima.
**IL CONFRONTO CHE DECIDE** (36 mesi, $654, stesso bootstrap appaiato, funding e fisco in entrambe):
| versamento | strada | mediana | media | P(≥50/g) |
|---|---|---|---|---|
| €0 | BIGLIETTO | $132 | **$9.592** | 4,2% |
| €0 | LIBRO | **$787** | $803 | 0,0% |
| €500/m | **BIGLIETTO** | **$39.820** | **$46.298** | **13,7%** |
| €500/m | LIBRO | $22.444 | $22.694 | 0,0% |
**`IL BIGLIETTO CONTRO IL VERSARE: MEGLIO — ma solo perche' il piano di versamento c'e', e la ragione
non e' il rendimento, e' il NUMERO DI VOLTE CHE SI PUO' GIOCARE.`** Senza versamenti la stessa
scommessa **perde la mediana 6× e vince la media 12×**: una lotteria a EV positivo che un conto da
$654 puo' giocare **una volta sola**. *(Il libro da solo fa P(≥50/g) 0,0% a 36 mesi **per struttura**:
non e' un difetto del libro, e' l'orizzonte.)*
⚠️ **Cio' su cui poggia tutto, dichiarato:** il drift di XS01 misurato su **13 gambe su 19**; la
**morte-firm ~10%/anno** e' un'**assunzione** (se fosse ≫ l'EV si azzera **senza che nulla nel
modello lo segnali**); il cap **$200k/trader** limita il canale per struttura.
⚠️ **Un numero netto catturato PRIMA di pubblicarlo:** il "capitale d'incrocio €1.000-2.000" **non e'
un meccanismo** — la ricchezza terminale della strada-biglietto e' **bimodale**, `P(cassa < $249)` si
muove **liscia** (56,3 → 0,0%) mentre la **mediana salta di un ordine di grandezza** appena quella
massa passa il 50%. **Li' la mediana non e' robusta**, ed e' esattamente il tipo di numero netto che
si sarebbe finito per citare come soglia di progetto.
- **Onestà sul target €50/giorno:** NON raggiungibile su 2000 in 1-2 anni (servono ~130k di
capitale o un DD da rovina). La leva non è la scorciatoia; la via è target-vol + capitale +
tempo. La strategia che *guadagna* esiste, ma a ~+€1.5/giorno su 2000.
Script ricerca: `scripts/research/track{A,B,C,D,E}_*.py` + `trackD_timing.py`.
---
## 2026-08-25 — €500/mese per 10 anni dal conto vero, e l'ordine delle leve rimisurato
Versamento di **1.400 USDC** atterrato alle 11:03:35Z: equity **$667,88 → $2.066,88**. Le tabelle
pubblicate partono da $600/$635, quindi sono state rifatte sul conto reale.
Script `scripts/research/r0825_piano_10a_500.py`, lente **L3 CONGIUNTA** (funding esatto + fisco
d'accumulo + ancora ×0,89).
**Controllo di replica superato:** da $2.067 lo script chiede **€1.718/m** a 10 anni dove la
tabella pubblicata da $635 chiedeva €1.733 — lo scarto è esattamente il capitale iniziale più alto.
### €500/mese da $2.067
| orizzonte | capitale mediano | p10 | p90 | €/g | P(≥50 €/g) | versato | fin/vers |
|---|---:|---:|---:|---:|---:|---:|---:|
| 5a | $44.925 | $38.015 | $54.453 | 7,17 | 0,0% | $34.767 | 1,29x |
| **10a** | **$114.934** | $89.461 | $152.072 | **18,35** | **0,0%** | $68.012 | 1,69x |
| 15a | $228.778 | $163.333 | $324.376 | 36,53 | 12,3% | $101.257 | 2,26x |
| 20a | $408.117 | $276.623 | $621.664 | 65,16 | 79,4% | $134.502 | 3,03x |
**A 10 anni €500/mese NON centra €50/giorno: centra ~18 €/g, P(≥50) = 0,0% su 3.000 traiettorie.**
Con €500/m il bersaglio arriva al **17,0° anno** (P(20a) 87%). Per €50/g **in 10 anni** servono
**€1.718/m** (P=90%, totale $228.688) o €1.305/m a P=50%.
Quando arriva, per versamento: €0 **mai** · €250 22,9a (arriva il 73%) · **€500 17,0a** ·
€800 13,5a · €1.000 11,8a.
### 🚨 L'EQUIVALENZA CHE ORDINA TUTTO IL PIANO
> **€100/mese in più ($13.189 in dieci anni) vale quanto +4,07%/anno di drift** — cioè portare il
> libro da 15,19% a 19,26%, un **+27% su tutto il drift**.
| leva | mediana 10a | guadagno |
|---|---:|---:|
| base €500/m | $114.934 | — |
| **€600/m** | **$136.877** | **+19,1%** |
| €800/m | $180.864 | +57,4% |
| uplift drift +1%/anno | $119.850 | +4,3% |
| uplift drift +3%/anno | $130.605 | +13,6% |
| **leva k=1,25 sul libro** | $132.846 | +15,6% |
| **leva k=1,40 (max difendibile)** | $145.146 | +26,3% |
📌 **Tutta la leva autorizzabile vale quanto €100-150/mese** — e k=1,25 vale **meno** di €100/mese
in più, portandosi dietro il peggior giorno strutturale al 21,48%. Il bonifico non porta rischio.
### Il versamento di oggi, e quanto conta un lump
I €1.400 di stamattina spostano la mediana a 10 anni solo del **+3,3%** (da $111.297 a $114.934).
Conferma l'ordine delle leve: **versare > quando > quanto presto si smette > piatto vs crescente >
frequenza**. A 10 anni il rendimento fa il **41%** del risultato, i bonifici il **59%**.
### Book vs ETF, rimisurato a iso-rischio (lente L3)
| | rendimento | vol | Sharpe |
|---|---:|---:|---:|
| book k=1 (oggi) | **15,19%** | 11,29% | **1,35** |
| S&P 500 (stessa finestra) | 17,40% | 19,60% | 0,89 |
| book a iso-rischio (k=1,74) | **26,38%** | 19,60% | 1,35 |
| book a k=1,40 (max difendibile) | 21,27% | 15,80% | 1,35 |
🚨 **A k=1 il libro rende 2,2 punti MENO dell'S&P.** Il vantaggio di Sharpe (1,35 vs 0,89) resta
**rischio risparmiato, non rendimento incassato** — e senza leva non si converte. A iso-rischio il
libro farebbe **+9,0 punti** sopra l'indice; a k=1,40 **+3,9 punti con meno vol**.
📌 **Quindi: senza leva il libro NON si giustifica come veicolo di ACCUMULO. Si giustifica come
veicolo di RENDITA**, dove serve **2,5× meno capitale** ($254k contro $646k) perché la perpetua
vive sul drawdown. La scelta del veicolo dipende da quale dei due è l'obiettivo (N6).
⚠️ E il fisco favorisce l'ETF: UCITS ad accumulazione paga **26% alla vendita**, il book **33%
ogni anno**. Il differimento è un vantaggio strutturale reale.
- 💰 **SOLDI FERMI — «altri modi, non per forza trading» (2026-09-01/02).** `r0901d_soldi_fermi.py`,
diario `2026-09-01d-soldi-fermi.md`. **On-venue chiuso con l'ELENCO INTERO:** `get_currencies`
remunera **4 valute su 50** (USDE 4,32 · USDC 3,40 · BUIDL 3,20 · STETH 2,22) = le quattro gia'
chiuse il 31/08 — non era un campione. ⚠️ **L'USDC su Deribit non e' fermo**: e' la base di sizing
(cap = equity x 0,5) e il cuscino del disaster-SL; spostarlo rimpicciolisce il libro. Il capitale
fermo vero sta **fuori**: ~€6.043 in XEON (27/07), che e' lo **split-cassa**. Tassi dal web
(BOT 2,768% asta 08/2026 · DFR 2,25% · conti deposito 3,25-3,50% · sUSDe ~9% Q2 2026), fisco dalla
memoria (12,5 / 26 / 33%). **In euro netti su €6k:** XEON 81 · BOT **133** · conto deposito
**143** · sUSDe 350 ma **stesso emittente dell'USDE gia' in conto: split distrutto** · libro
~600 atteso ma **trading, split distrutto**. **Il miglior guadagno sicuro vale +€62/anno = €5/mese**,
contro €100/mese di bonifico = +4,07%/anno di drift. ✅ **L'operatore ha scelto di versare** (02/09),
che e' il piano del 27/07: **€5.000 dentro, ~€1.000 fuori** (la protezione satura a qualunque
quota > 0), il «~€600» letto come attesa e non come tasso, fondo d'emergenza da dichiarare. Nessuna
azione di config: il cap e' dinamico. **In attesa di importo e data.**
File diff suppressed because it is too large Load Diff
+181
View File
@@ -0,0 +1,181 @@
# Dati, feed e difetti trovati
> Estratto **verbatim** da `CLAUDE.md` il 2026-08-25 durante la compattazione.
> Difetti del dato scoperti e riparati, e la catena opzioni raccolta in proprio.
> Il testo non e' stato riscritto: e' la memoria originale, spostata.
---
- ⚠ **SPLIT NON AGGIUSTATI nel feed equity — difetto sul LIBRO LIVE, riparato (2026-07-25).**
`data/raw/eq_iwm_1d.parquet` ed `eq_efa_1d.parquet` avevano uno split non aggiustato il
**2005-06-09** (IWM 2:1 = 49.5%; EFA 3:1 = 66.5%); IB `ADJUSTED_LAST` non li aveva aggiustati.
**La certificazione non li vedeva per un punto cieco strutturale:** l'unica guardia era
`maxret > 50% → SPIKE?` e uno split 2:1 fa **esattamente 50%** → IWM passava a 49.5% con status
OK. **IWM e' una delle 6 gambe di GTAA01 in produzione.** Impatto: GTAA6 FULL Sh 0.61→**0.64**,
IS 0.49→**0.54**, OOS 2015+ e maxDD **INVARIATI** (artefatto nel 2005, fuori hold-out) → il
difetto SOTTOSTIMAVA lo sleeve, **nessuna decisione presa va rivista**. Discriminante
split-vs-crollo (riusabile): **non il rapporto** (SLV 2026-01-30 ha rapporto 1.3994, a 4bps da
1.4, ma e' un crollo vero: GLD 10.3% lo stesso giorno) ma il **RANGE INTRADAY** — lo split apre
gia' al nuovo livello con range normale (IWM: open 47.00, range 1.7%), il crollo si muove DENTRO
la barra (SLV: range 33%). Modulo `src/data/eq_splits.py` (`detect_unadjusted_splits` a 3
condizioni congiunte + `repair_splits` componibile), riparazione **in lettura** in
`src/portfolio/gtaa.py::_close` e `scripts/research/eqlib.py::load_eq`, status
**`SPLIT-NON-AGG`** in `fetch_ib_equities.certify()`, test `tests/test_eq_splits.py` (8 casi).
**Regola nuova: ogni soglia di certificazione tarata su un valore tondo va controllata contro il
difetto che genera esattamente quel valore** (una soglia a 50% non puo' sorvegliare gli split 2:1).
- ⚠️ **IL FEED EQUITY NON AVEVA UN CROSS-CHECK — buco trovato e chiuso (2026-07-26).**
`src/data/eq_crosscheck.py`. Nel crypto la certificazione incrocia sempre piu' venue
(`certify_feed.py` vs Coinbase USD); il feed equity aveva **solo controlli locali** (integrita',
gap, spike, split). Il primo veicolo estero ha trovato il buco alla prima estrazione: **CSPX
2012-01-13** con `open/high 112.740` in **USD** e `low/close 88.010` in **EUR** (fattore dai close
adiacenti **1.2797** = EURUSD di quel giorno) → 21.9% e poi +27.8% mentre SPY faceva 0.39%.
**La certificazione esistente non lo vedeva:** unica guardia `maxret > 50% → SPIKE?`, e una
contaminazione EUR/USD vale ~22-28% — **stesso schema dello split 2:1 del 25/07** (che valeva
*esattamente* 50%). **2ª conferma: una soglia tarata su una classe di difetto non sorveglia le
altre.**
**Perche' il GEMELLO e non una regola locale:** il discriminante del 25/07 (range intraday) qui
non funziona — anche un crollo vero ha range enorme (SLV 33%). Cio' che separa i casi e' che **un
evento di mercato lo fa anche il gemello**: nel *rapporto* i movimenti veri si cancellano.
Controprova su dati reali: la scansione sui **prezzi** segnalava IDTL 2020-03 (liquidazione
treasury) e IGLN 2013-04 (crollo oro); sul **rapporto** spariscono.
⚠️ **La soglia non era tarabile sulla deviazione** — rumore legittimo fino al **9.90%**
(2025-04-09: Londra chiude alle 11:30 di New York) contro un difetto del 21.4% → margine 2.2×,
sotto il 3× richiesto. **La risposta non e' accettare il margine ma CAMBIARE STATISTICA:**
`|dev| / movimento del gemello` (un disallineamento d'orario **non puo' superare il movimento del
mercato**) → rumore **8.1**, difetto **42.7**, **margine 5.3×**. Soglia 18.0, equidistante in
scala log. Tre condizioni congiunte: deviazione + non-spiegato + **rientro** entro 5 barre.
**Riparazione = SCARTARE la barra, non ricostruirla** (del prezzo vero non si sa nulla).
⚠️ **Limite dichiarato e chiuso sul DANNO, non sulla rilevabilita':** GBPUSD 1.27 → 21% sempre
rilevato; EURUSD 1.09 → 8.3% **dentro il rumore** e non tappabile senza falsi positivi. Misurato
invece il danno di una contaminazione non vista: **dSharpe mediano 0.003, peggiore 0.056,
|Δ|>0.05 nel 2%**. **REGOLA: quando un buco non si puo' chiudere senza generare falsi positivi,
si misura il DANNO del caso non rilevato** — un buco quantificato e innocuo e' un risultato, uno
taciuto e' un debito. Il limite e' congelato in un test (`test_limite_dichiarato_*`): se qualcuno
abbassa la soglia, il test dice cosa e' cambiato.
- **CATENA OPZIONI REALE — RACCOLTA PROPRIA dal 2026-07-30 (cerbero-bite ASSORBITO e dismesso).**
`/opt/docker/cerbero-bite` (progetto separato) accumulava dal 2026-06-09 la catena Deribit
**mainnet** BTC+ETH; **è stato ELIMINATO il 2026-07-30** (container, volume, immagine e cartella
rimossi, 12 GB liberati; codice conservato su Gitea `Adriano/Cerbero-Bite`, ultimo commit di
dismissione) **e la raccolta è passata dentro PythagorasGoal**:
- **raccolta:** `scripts/live/collect_chain.py`, cron **`25 * * * *`** (`scripts/cron_chain.sh`) →
`data/raw/cb_chain/YYYY-MM-DD.parquet`. Entrambe le ali, scadenze ≤95g, OI≥100, ~570 strumenti
a giro, ~3 min. **Minuto :25 scelto, non arbitrario:** :00 era la raffica di bite, :07 è
`cron_book` (feed 5m di SKH01, la cosa che non deve trovare l'IP occupato).
- **archivio ereditato:** `scripts/analysis/import_cb_archive.py` (una-tantum) →
`cb_chain/bite_archive.parquet` (1.23M righe, 2026-05-01+) e `cb_market_snapshots.parquet`
(17.402 righe, 2026-03-26+: DVOL, RV30, funding perp e cross, **dealer net gamma**, gamma flip,
**rischio liquidazioni**, giorni all'evento macro — dati che il progetto non ha altrove).
Snapshot sqlite integrale in `/opt/docker/backups/manual/cerbero-bite-20260730/` (SHA256).
- **certificazione:** `scripts/analysis/certify_cb_chain.py`; **harness** `scripts/research/cblib.py`
(`load_chain()` unisce archivio + raccolta, dedup su `(ts, strumento)`).
- **battuta di cuore:** ogni giro scrive `data/chain_collect/runs.jsonl`, sorvegliato da
`monitor_health` (cadenza 1h, `max_age_h=3`) — **un collettore fermo non produce niente, e il
niente si legge come "nessun dato quel giorno"**.
- **BACKUP (aggiunto 2026-07-30):** `data/raw/` è gitignored → la catena **non è in git**, e il
backup rotativo della VPS non copriva PythagorasGoal. Aggiunta `do_pythagoras` a
`/opt/docker/scripts/backup.sh` (daily 04:00, retention 7/28/185 g): salva **solo il dato non
ricostruibile** — catena + contesto, `data/paper_*` e `data/chain_collect` (serie forward-only
che alimentano i gate pre-registrati: **non sono ricalcolabili**), `data/options_daily`,
`data/live`, `venue_watch`, `fee_watch`, `config/live.json`. ~28 MB. **Esclusi di proposito**
i ~110 MB ricostruibili (`rebuild_history.py`, `fetch_dvol.py`, `fetch_hyperliquid.py`,
`fetch_ib_equities.py`, cache). La funzione **fallisce rumorosamente** se la catena è assente o
vuota, e verifica il tar prodotto (controllo positivo provato in entrambi i versi).
⚠️ `/opt/docker/scripts` **non è un repo git**: quella modifica vive solo su disco.
- **Dopo un riavvio della VPS la raccolta riprende da sola** (cron di sistema `enabled`+`active`,
nessuna dipendenza da docker o da cerbero-mcp: API pubblica Deribit diretta). Verificato con
`env -i` che il giro funzioni nell'ambiente nudo di cron. Finestra scoperta: fino al `:25`
successivo, **senza recupero** — un'ora persa resta persa (il collettore non fa catch-up).
**TRE DIFETTI DI BITE NON REPLICATI** (misurati il 30/07): (a) **una chiamata per strumento**
`get_order_book?depth=3` dà già quote+greche+IV+OI+book+underlying, bite ne faceva due con
rischio di disallineamento; (b) **pacing** (token bucket 4/s + backoff) invece della raffica —
il carico non è mai stato il problema (~570 chiamate/ora = 0.16/s **distribuite**), bite le
sparava in ~26s (~44/s) auto-saturandosi il rate limit per-IP; misurato sul nostro giro:
**574 chiamate, 0 risposte 429**; (c) **`quote_status` esplicito** in {`ok`, `no_quote`, `error`}
— "book vuoto" (fatto di mercato) e "chiamata fallita" (fatto di infrastruttura) sono cose
diverse, e `book_depth_top3` è **NULL su errore, mai 0**. ⚠️ Le righe ereditate da bite hanno
`quote_status='unknown'`: bite non registrava il perché, e si dichiara l'ignoranza invece di
inventare uno stato.
🚨 **BUCO DI COLONNA nell'archivio ereditato, trovato il 2026-08-22 e mai registrato prima:**
`bite_archive` ha `index_price` e `underlying_price` **100% None (1.232.212/1.232.212)**. Il
sottostante esiste **solo dal 2026-07-30** (raccolta propria, 18,2% delle righe) e
`book_depth_top3` solo nel **67,2%**. **Chiunque calcoli moneyness o riprezzi sull'archivio
pre-30/07 sta usando una colonna che non c'è** — e il file si legge senza errori, quindi il
difetto è silenzioso.
**Ed è CHIUDIBILE senza dato nuovo:** il forward si ricostruisce con la **parità put-call**
dalla catena stessa — verificato contro l'osservato su **6.578 coppie: |errore| mediano 0,023%,
p95 0,147%, corr 1,000000** → rende la superficie utilizzabile su tutti i **75 giorni** invece
che 23. (Lo **smile**, che non richiede il forward, esiste invece su **113 giorni dal 2026-05-01**;
la **superficie completa** solo da **2026-06-09**, perché prima bite raccoglieva una sola scadenza
per giro — misurato da **tre** agenti indipendenti.)
⚠️ La famiglia raccolta è quella **inverse** (`get_instruments?currency=BTC|ETH`): la superficie
**USDC-lineare non è nell'archivio**, e ogni conclusione su di essa nel progetto è oggi un
**controfattuale costruito sui mid inverse**, non una misura. Puntare il collettore anche su
`currency=USDC` costa ~~**+587 chiamate/giro (+90%)**~~ 🚨 **CORRETTO 2026-08-23 (§51): sono
+117 chiamate = +18%.** Il 587 era il conteggio **grezzo** (1.140 strumenti USDC ≤ 95g = +81%)
**senza il filtro OI≥100 che il collettore applica davvero**, e quel filtro taglia il 90% della
famiglia USDC: con gli stessi filtri del collettore vivo sono **113 strumenti** contro **649**
inverse (✅ verificato al venue da due percorsi indipendenti; replica esatta di §8 — su Deribit
USDC l'OI e la negoziabilità sono anti-correlati). Il giro passerebbe da 652 chiamate/163 s a
**769/192 s**, dentro la finestra del :25 e senza toccare `cron_book` al :07. Resta una decisione
sul rate-limit per-IP, che ha già causato un guasto il 29/07 — ma **di un ordine di grandezza
più piccola di come era stata scritta**, ed è un numero **di oggi** (cresce con la liquidità
USDC, va ri-misurato prima di accendere).
🚨 **LA RAGIONE PER RACCOGLIERLA E' CADUTA (2026-08-23, §64): le due superfici sono LA STESSA
SUPERFICIE** a strike appaiati — prezzo equo identico per costruzione, mezzo-spread Δ 0,21/0,38 pp,
`f_venue` Δ +0,005/0,011 ⇒ il `f` di VRP01 misurato sull'inverse **non e' «il numero della famiglia
sbagliata», e' lo stesso numero**. Resta vero che la lineare **non e' nell'archivio**; cade
l'argomento che la sua assenza falsi una misura gia' fatta.
**NON assorbito, e perché:** il motore credit-spread ETH (il progetto ha già la regola "niente
short-vol da modello in deploy"), la GUI, kill switch/dead-man/audit (PythagorasGoal ha
`venue_watch`/`edge_watch`/`monitor_health`/`fee_watch`), `dvol_history` (`fetch_dvol.py` ha
storia **più lunga**: 2020+ contro 2026-05), `decisions`/`positions` (59 righe a capitale $52,
0 posizioni). Test `tests/test_collect_chain.py` (11) + `tests/test_cb_chain_vrp.py` (13).
⚠️ **Prima di cancellare la sorgente** (verifiche nel diario `2026-07-30-assorbimento-cerbero-bite.md`):
snapshot **completo** e non solo integro (4 tabelle su 4 con conteggi identici al volume vivo e ai
parquet); i 10,6 GB di backup interni **non** contenevano dati unici (stessa riga più vecchia in
tutti gli snapshot + conteggi monotoni ⇒ nessuna potatura); zero dipendenze a runtime.
⚠️ **`cerbero-mcp` è un progetto DIVERSO e serve a PythagorasGoal** (Hyperliquid, percorso del
conto) — resta acceso; la rete `traefik` che condividevano è `external:` nel compose di bite,
quindi `down -v` non la tocca. **REGOLA: prima di cancellare una sorgente si verifica che la copia
sia COMPLETA, non che esista** — un hash prova che il file non è corrotto, non che contenga tutto.
**Perché si MEMORIZZA invece di interrogarla:** una catena opzioni non è ricostruibile a
posteriori — Deribit non serve book storici, un'ora non raccolta è persa per sempre, e non c'è un
secondo venue da cui recuperarla.
**Certifica 4 difetti:** quote vuote, book incrociato, premio non monotono nello strike,
`depth==0` (ambiguo **by design**: chiamata fallita e book vuoto danno lo stesso valore → si
riporta, non si ripara).
⚠️ **GUASTO IN CORSO dal 2026-07-29 05:00 UTC — status `QUOTE-VUOTE`.** Il collettore persiste la
riga anche quando il ticker fallisce (rate-limit Deribit **per-IP**: ~650 risposte 429 in ~26s a
ogni giro, **96% al minuto :00**, generate dalla spazzata full-chain che si auto-satura) →
`bid`/`ask`/`iv`/`delta` NULL e **conteggio righe INVARIATO** (13k/giorno prima e dopo). Tasso di
quote vuote per settimana: 0.4·0.7·2.2·1.5·0.5·0.4·0.3 → **22%**; giorno peggiore **51.7% BTC /
30.6% ETH**. ✅ **Risolto dal cambio di collettore** (30/07): la raccolta propria è paced e ha
fatto 574 chiamate con **0 risposte 429**. **REGOLA: una riga presente non è un dato presente**
contare ciò che è QUOTATO, non ciò che è SCRITTO (3ª occorrenza dopo `paper_dvolspread` e
`fresh_5m`). **REGOLA: un guasto IN CORSO si misura al GIORNO PEGGIORE, non in media** — la prima
stesura confrontava "tutto" con "ultimi 7g" e diluiva un guasto di 2 giorni al 13.5%, commettendo
a 7 giorni lo stesso errore che dichiarava di evitare a 3 mesi. Diario `2026-07-30-vrp-quote-reali.md`.
- 🚨 **IL FEED 1h E' ETICHETTATO ALL'APERTURA, e `asof(ts)` guarda un'ora avanti (trovato in revisione
il 2026-09-09).** La barra 1h etichettata 20:00 chiude col 5m delle 20:55 (verificato: close 1h 20:00 =
close 5m 20:55 = 1603,5, mentre il 5m 19:55 fa 1572,95). Quindi `S.asof(ts)` su una serie indicizzata
con quell'etichetta restituisce la chiusura di **ts+1h**: un look-ahead di un'ora senza eccezioni e
senza NaN (D6, quarta occorrenza). Colpisce `cblib.spot_series` — cioe' il f 0,714 di §11 e ogni numero
di `cblib` con lo spot, compreso `ST = S.asof(exp)` che e' il prezzo di un'ora DOPO la scadenza.
**RIPARATO la sera stessa in `cblib`** (`causale(s, cadenza)`: spot +1h, e **anche il DVOL
giornaliero, +1 giorno** — la riga del giorno D e' la chiusura delle 23:00 di D, verificato contro la
risoluzione 1h dell'API pubblica: seconda serie con lo stesso difetto, quinta occorrenza di D6). §11
riprodotto al millesimo su worktree HEAD e rimisurato: 0,714 → **0,712** (le correzioni hanno segno
opposto; DVOL orario di controllo 0,717). Il DVOL causale da feed giornaliero e' **stantio fino a 23 ore**
(0,2 pt mediana, 3 pt max; nel crollo ±0,05 di f): il feed DVOL orario e' il passo aperto. Diario
`2026-09-09b-debito-18-cblib-causale.md`.
- **XRP** (`XRP/USDC:USDC`, dal 2022-03-16) ricostruito il 2026-09-09 con `rebuild_history --asset XRP` e
spostato in **`data/raw/alt_xrp_*.parquet`** (namespace di ricerca come SOL: fuori dal feed attivo, non
rinfrescato dal cron, `load_data("XRP")` fallisce, presidiato da `test_sol_leg` via `DERIBIT_INSTR`).
Certificazione per anno vs riferimento USD: **Coinbase non ha XRP fra 2021-01 e 2023-07** (delisting per
la causa SEC) → riferimento **Bitstamp** prima, Coinbase dopo; >1% 0,06% (2022) · 0,57% (2023) · ≤0,02%
(2024+); mediana 5-6 bps; **5m flat 27-61%** (BTC/ETH 0,1-2%, SOL 21%): il 5m e' illiquido. Cache del
riferimento in `data/_cache/` (gitignored, fuori backup: si riproduce con rete).
+52
View File
@@ -0,0 +1,52 @@
# Metodo: gate e harness
> Estratto **verbatim** da `CLAUDE.md` il 2026-08-25 durante la compattazione.
> I gate codificati in `altlib.py` che ogni candidato deve attraversare.
> Il testo non e' stato riscritto: e' la memoria originale, spostata.
---
- **MARGINAL SCORER (implementato 2026-06-20)** — la lezione "Sharpe marginale, non assoluto" è
ora codice in `scripts/research/alt/altlib.py`: `study_marginal(name, target_fn)` valuta un
candidato direzionale BTC/ETH **sia** in assoluto **sia** rispetto al baseline `tp01_baseline_daily()`
(corr, uplift del blend OOS, beta+alpha residua) e ritorna `earns_slot = (abs!=FAIL) AND
(marginal==ADDS)`. **Regola: una nuova strategia direzionale si giudica su `earns_slot`, non sullo
Sharpe assoluto** (gli overlay-su-TSMOM ereditano lo Sharpe di trend e prendono PASS fasulli —
es. CMB04 PASS assoluto → NEUTRAL marginale). Demo `marginal_demo.py`, test `tests/test_marginal_scorer.py`.
⚠️ **INDURITO 2026-06-21 (onda ortho):** la versione fisso-HOLDOUT + jackknife-mese era
ingannabile — 17/18 book relative-value "ADDS" su una sola finestra 2025 (ETH-bleed dove TP01 è
debole). Tre gate nuovi in `marginal_vs_tp01`: **(1) persistenza multi-cut** (uplift positivo a più
date di taglio, non solo 2025); **(2) edge in-sample** (`has_insample_edge`: lo Sharpe standalone
PRE-holdout dev'essere ≥0.5 — un low-corr a Sharpe ~0.3 "aggiunge" solo matematica di
diversificazione, riportata via `null_pctl_*` vs un asset-rumore a corr-zero); **(3) hedge vs
alpha** (`is_hedge`: un low-corr che paga SOLO quando TP01 è debole — `corr(Sharpe-TP01, uplift
annuo)` molto negativa — è un hedge, non alpha). Verdetti nuovi: HEDGE, NOISE. Sull'onda ortho lo
scorer indurito collassa 17/18 → **1** (`dvol_spread`, unico con edge in-sample reale; comunque
forward-monitor per multiple-testing/storia DVOL corta). Lezione: un nuovo sleeve si giudica su
edge-in-sample + persistenza multi-cut + non-hedge, non sull'uplift di una finestra fortunata.
- **HARNESS REALISM (codificato 2026-06-21, onda intraday)** — due gate nuovi in `altlib.py`,
test `tests/test_harness_realism.py`:
- **`day_boundary_robust(target_fn, tf)`** — un effetto ora/sessione/giorno il cui uplift
marginale **si inverte** spostando il confine del giorno UTC di poche ore è un **artefatto di
etichettatura calendario** (ha ucciso `open_drive`: +0.23 a 00:00 → 0.33 a +8h → ARTIFACT-RISK).
Un segnale di prezzo è INVARIANT (spread 0); un effetto calendario vero è ROBUST (resta positivo;
es. `prevday_range_breakout`). **Regola: ogni segnale calendar/session/hour passa questo test
prima di crederci.**
- **`eval_weights_smallcap(df, target, capital=600, min_order=5)`** — a ~$600 un ribilanciamento
di nozionale < min_order **non si esegue**; la fee proporzionale che `eval_weights` applica a
migliaia di micro-trade sub-dollaro (tipici di un overlay vol-target) è **finzione**. Salta i
sub-min_order e riporta lo **Sharpe haircut** reale vs modellato. **Vale per OGNI sleeve a questo
capitale, TP01 incluso** — lo Sharpe netto onesto a $600 è quello small-cap, non quello modellato.
- **SELECTION-ON-HOLDOUT gate (codificato 2026-06-29, filone B intraday ERM)** — terzo gate in
`altlib.py`, test `tests/test_harness_realism.py`. Il lead ERM faceva `earns_slot=True` MA lo script
di scoperta sceglieva la cella per **`min_hold` massimo** su 60+ celle = **selezione-sull'hold-out**:
scegliendola in-sample-only ne esce un'altra (trend-beta corr→TP01 0.53, NEUTRAL) e il deflated-Sharpe
crolla (DSR 0.0-0.24 su 122 trial). `study_marginal` da solo non lo vede (giudica UNO stream, non *come*
è scelto). Tre funzioni: **`deflated_sharpe()`** (Bailey & Lopez de Prado, PASS ≥0.95), **`select_cell_insample()`**
(cella scelta col solo Sharpe pre-HOLDOUT), e il gate combinato **`study_family_honest(name, factory, grid, tfs)`**
`earns_slot_honest = earns_slot[cella in-sample] AND deflated-Sharpe≥0.95`. **Regola: una strategia
direzionale grid-searched si giudica con `study_family_honest`, non chiamando `study_marginal` sulla
cella a max hold-out.** Chiude il punto cieco gemello di CC01 ("Sharpe implausibile"). Diario
`2026-06-29-intraday-regime.md` (analisi `scripts/research/intraday_regime_analysis.py`).
+328
View File
@@ -16,6 +16,10 @@ null de-levering superato + eseguibilita' al capitale dichiarato.
| 6 | OI-PIN | **SCARTATO** | il max-pain batte uno strike casuale ma **non batte mai (0/24) la media a 7 giorni dello spot**, un livello che non usa NESSUN dato di opzioni: non e' pinning, e' reversione verso il centro recente con l'OI come stimatore rumoroso di quel centro |
| 8 | VOL-SIZE | **LEAD RIDIMENSIONATO** — gate 22/12 **gia' fallito oggi** | dare a SKH01 una size per-trade regge a **23/23 ancore**, 8/8 anni, null di permutazione e trasferimento su V1 — ma vale **+0,07 di Sharpe di libro, un terzo della fortuna d'ancora del libro stesso (+0,196)**. Il vol-target di **libro** e' invece falsificato: compra peso SKH gia' respinto e peggiora l'eseguibilita' |
| — | **XSR-REPRO** (integrita') | 🚨 **DIFETTO DI PRODUZIONE** | il numero 1.82 e' SPIEGATO e non era sbagliato (era su una **terza** lente, e su una barra non ancora chiusa) — ma cercandone la causa e' emerso che **`paper_xsr` registra ~41 minuti di mercato al giorno**, non un giorno. Tre gate pre-registrati leggono serie costruite cosi' |
| 70 | **XSR-RENDITA** | **SCARTATO sotto la lente RENDITA** (non come sleeve) | il muro scende del 20,6% a iso-rischio, ma **mescolare i rendimenti di XSR01 da' lo stesso muro** (il meccanismo vale 0,8%, dentro la risoluzione MC) e un **conto remunerato allo stesso tasso lo eguaglia a vol 0 e senza secondo venue**. A drift zero il muro **sale**: si compra un drift scorrelato, non la scorrelazione. E l'haircut non pareggia un conto al 4% **nemmeno a haircut ZERO** |
| 71 | **COLLAR01** | hold BTC gated dal trend, coperto da un collar (pavimento comprato, tetto venduto) a scadenza <=15g: "ridurre la vincita, bloccare la perdita" batte il de-levering? | **REFUTATO** — ✅ il pavimento FUNZIONA (maxDD scende in **36/48**, §46 battuto sul suo motivo: qui beta=1,0) ❌ ma costa **2-8 punti di drift per punto di DD** e il de-levering vince **45/48**. 🚨 Le 3 celle vincenti stanno sul **bordo**: estesa la famiglia vince 35/36 **nell'angolo covered-call**, e **il 72-100% di quell'edge e' il PREMIO DI VARIANZA** (DVOL sta **32% sopra** la RV-forward): riprezzato alla vol vera l'angolo cade da Sharpe **1,471 a 0,511** — che e' VRP01 (0,47), non una scoperta |
| 72 | **PAVIMENTO-LEVA** | il pavimento (sola put, ≤15g) come LICENZA DI TAGLIA a iso-DD — l'inverso di §71 | **REFUTATO 0/16**, e **corregge §71**: la put da sola a premio reale **peggiora il maxDD in 16/16** (FORTE 51,8% → 55-76%; LARGO 71,9% → 75-82%) — nel collar il DD lo riduceva il **TETTO**, non il pavimento. Meccanismo (B2): **i drawdown di BTC sono grind di 290-818 giorni**, la put copre una finestra e para nel 0-9% dei cicli. Pareggio del DD solo a **36-79% del premio reale** (prezzo equo ≈76%: non basta). **La licenza del disaster-SL e' di carta** (B3): sostituire `sl` con la distanza del pavimento darebbe 2,28x, ma su 8 finestre la perdita e' 33,6% → **77% dell'equity**. Gated su IV sottile: migliora (+3,8 → +8,4%) ma **perde** ancora |
| 73 | **SOLDI-FERMI** | altri modi per far rendere il capitale fermo, non per forza trading | **on-venue CHIUSO con l'elenco intero** (4/50 valute = le 4 del 31/08); l'USDC sul conto non e' fermo (sizing + cuscino); fuori, su €6k netti/anno: XEON 81 · **BOT 133** · **conto deposito 143** · sUSDe 350 (**stesso emittente dell'USDE: split distrutto**) · libro ~600 (trading). Miglior guadagno sicuro **+€62/anno**. ✅ L'operatore ha scelto di **versare** (piano 27/07: €5k dentro, ~€1k fuori) |
| 5 | SKEW | **SCARTATO** (Q1, Q2) + **LEAD** (Q3, gate 2027-02-22) | il prezzo muove lo skew (t 3,1-9,4 su 8/8 test), **non il contrario** (max |t| in avanti 2,35 contro 2,08 atteso dal rumore). Ma Q3 e' grosso: **il f=0,73 di VRP01 e' per il 42% STRUTTURA A TERMINE e solo per il 25% skew** |
| — | **HL-EXEC** (audit di fatto) | **3 falsificazioni misurate** | il pavimento vero e' **$10 (non $5)** e il taker **4,50 bps (non 5,0)** — ma il *"XS01 serve ~$20k"* e' **refutato del tutto** (nessuna soglia da min-order), il *"XSR01 ~$5.000"* e' **conservativo di 1,7x** (vero ~$3.000), e lo **slippage "rischio #1" di XSR01 e' refutato** con margine **21x** |
| — | **SLIP-AUDIT** (audit di fatto) | **SCARTATO** = nessun costo nascosto a questa taglia | i backtest a 10 bps RT restano **conservativi di ~1,5 bps/lato**; nessuna evidenza di impatto sopravvive al null (p=0,274; estremi avversi **1/18 contro 4,7 attesi**). **Ma la misura ha una data di scadenza** |
@@ -193,6 +197,18 @@ lati contro il **93%** di BTC_USDC, e il suo miglior bid sta a **un tick**.
DVOL alla mediana del 7°/11° percentile storico, sottostante +25%/+44%.
- ✅ **Replica indipendente del f del 30/07 su campione piu' lungo: f = 0,714 pooled** (IC95
[0,690, 0,779], **0/19 osservazioni >= 1,0**). Attraversare lo spread costa **~10% del credito**.
**RIMISURATO il 2026-09-09 sera** (debito §5.18: `cblib` dava lo spot di un'ora DOPO e il DVOL di
fino a 24 ore DOPO l'ingresso): allo stesso taglio (22/08, n=19) **f = 0,712 [0,664, 0,732]** — le due
correzioni hanno segno opposto (solo spot 0,721, solo DVOL 0,693) e un DVOL orario di controllo da'
0,717; a oggi (n=23) 0,712 [0,677, 0,741]. **Il verdetto non cambia.** Cambiano i numeri di
contorno: Sharpe canonico BTC 32,58 → 29,61 con mediana onesta **2,12** (era 1,45) e banda
[0,19, 29,61]; ETH 3,90 → 7,64 (mediana onesta 3,35); media BTC fra le ancore 0,66% → +11,44%
(cambia ancora segno); forfait fee 1,51× (BTC) / 1,87× (ETH); il regolamento e' il prezzo delle
08:00 e non delle 09:00 (P&L +3,3%); sottostante nel campione +21% / +40% (era +25% / +44%: il vecchio
regolamento del 21/08 era il prezzo delle 09:00, **+3,9%** sopra quello delle 08:00 — quanto pesa un'ora su
un numero a due estremi). Lo stesso `cblib` rigira anche `r0730`: il f 0,73 [0,706-0,805] del 30/07 stampa
ora **0,71 [0,680-0,756]** (n=27, 0/27 ≥ 1).
Diario `2026-09-09b-debito-18-cblib-causale.md`.
- ⚠️ **Correzione a un numero pubblicato:** il forfait fee del sleeve sovrastima il listino vero di
**1,44x (BTC) / 1,80x (ETH)**, non di "~2x". Il segno (conservativo) regge, la taglia no.
- ⚠️ **Rischio sul MINIMO, non sulle chiusure:** peggior mark infra-settimana mediana 6,4% del
@@ -370,6 +386,13 @@ nuovo: **va decisa PRIMA del 23/10, non quel giorno.**
(il collettore li accumula da solo), revisione comunque **2027-02-22**.
- ✅ Confound di modello **ESCLUSO e misurato**: `f_markfit` = **0,992** (la mark IV di Deribit
riprezza i propri mid entro l'1%) -> RR/BF sono un fatto di **prezzo**, non l'output del fit.
📌 **Rigirato il 2026-09-09 sera con spot e DVOL causali** (debito §5.18): la scomposizione sul panel si
muove di **≤0,005** per fattore (term 0,880 · skew 0,917 · spread 0,910 · fit 0,995 · f_tot 0,729) e Q1/Q2
non si toccano (serie proprie); agli **ingressi** il controllo `f_markfit` passa da 1,045 a **0,991** e
le osservazioni che lo superano da 22/28 a **25/28** — l'ora di spot in piu' stava nel controllo, non
nell'effetto. Agli ingressi (n=28) si muove di piu': f_term 0,910 → 0,875, f_tot 0,732 → 0,706, e le
correlazioni `corr(f_tot_mid, rr7)` +0,21 → 0,06 e `atm7` +0,02 → +0,34 **cambiano segno/ordine** — sono
22-25 punti, dichiarate (P12), non usate.
- ✅ **Controllo positivo cablato nello script:** con l'ancora **assunta** a :30 invece del `ts_max`
osservato compare un **falso lead** a +15m (0,102 contro 0,031) — *"e' l'errore che avrei
pubblicato senza quel controllo"*.
@@ -630,6 +653,11 @@ serviva a diagnosticare l'altro guasto.*
📌 **`paper_combo` e' sano PER CASO:** aspetta la chiusura di una **borsa**, non per progetto.
Nessuna riga di codice chiede la barra chiusa — **se la sua griglia cambiasse diventerebbe rotto in
silenzio.**
**CHIUSO IL 2026-08-26:** `advance()` riparato in tutti e 6 i monitor
(`src/live/paper_guard.py`), serie rigenerate dallo stesso `start_ts`
(`scripts/live/paper_regen.py`; statarb +1,95 → **1,61**), guardia PREMATURO cablata in
`monitor_health` (5/6 segnalati prima della rigenerazione, 7/7 OK dopo). Dettaglio nel diario
`2026-08-26-advance-riparato-e-debiti.md`.
✅ **Guardia raccomandata (NON implementata), e ha il pregio di essere banale:**
`ts_ultima_barra + cadenza <= mtime del file di serie`, grazia 5 min. **O(1)**, nessuna strategia da
rieseguire, usa solo cio' che il monitor gia' scrive; segnala **5/6** e **tace sul sano**; controllo
@@ -3730,3 +3758,303 @@ pubblicato, e nessuno dei tre produce reddito.
**Con questa sono 69 filoni e 0 candidati promossi.** I vincoli binding restano due: **il capitale che
entra** e **il conto che non sparisce**.
---
## 70 — XSR-RENDITA (r0825_xsr_rendita.py) — XSR01 sul criterio della PERPETUA
**Domanda dell'operatore:** *"rendita perpetua ma non ho ancora capito se serve XSR01"*. XSR01 era
stato giudicato su **ammissione**, **gate di deploy** e **accumulo**, mai sulla **rendita** — e sono
criteri diversi (l'accumulo premia il drift, la rendita la **perpetua**). N6: dichiarato l'obiettivo,
si rifa' il conto.
**Finestra.** Un MIX esiste solo dove esistono ENTRAMBE le serie → **2024-01-01 → 2026-08-22**
(965 g, 2,64 anni, **35%** della storia del libro). ⚠️ **I muri di questo filone NON sono il $313k
pubblicato**: base libro-solo su questa finestra = perpetua 3,30%, muro **$602.660**.
XSR01, lente dei gate su sole barre chiuse: drift **3,72%/a** (SE 1,46% → t +2,55), vol 2,34%,
Sharpe **1,56**, maxDD 2,5%, corr col libro **+0,049**.
**(a) iso-nozionale: il muro SALE** (+0,3% a w=5% → **+10,8%** a w=50%). Il mix e' piu' liscio e piu'
povero, e per una rendita il drift comanda.
**(b) iso-rischio (M6): si ribalta** — w=25%, k **1,32x** → muro **$478.729 (20,6%)**.
⚠️ **argmax al BORDO** (w=50%, k=1,93x; il muro scende **monotonamente** in w): la griglia non indica
un ottimo, dice *"il piu' possibile"*, e in quella direzione si compra il **k**, non XSR01 (M8).
Sopra 1,40x il libro **non puo' eseguire** (decisione 23/08; nessuna chiave di scala in config).
🚨 **(c) IL NULL — ed e' il risultato.** A iso-vol `drift = Sharpe × vol_libro`: la colonna "drift"
della tabella iso-rischio **e' la colonna "Sharpe" ri-etichettata**, quindi tutto il guadagno di muro
e' guadagno di **Sharpe da diversificazione**. Quattro sostituti a w=25%, stessa ri-scalatura:
| sostituto | muro | Δ vs base |
|---|---|---|
| **XSR01 (vero)** | **$478.729** | **20,6%** |
| XSR01 **mescolato** (3 semi) | $475.933 · $483.846 · $484.421 | 21,0 · 19,7 · 19,6% |
| rumore a media e vol di XSR01 (3 semi) | $457.751 · $499.263 · $531.158 | 24,0 · 17,2 · 11,9% |
| rumore a **drift ZERO** (3 semi) | $558.080 · $617.270 · $662.436 | 7,4 · **+2,4** · **+9,9%** |
| **conto remunerato 3,72%, vol 0** | **$477.607** | **20,8%** |
| conto remunerato 4,0%, vol 0 | $469.353 | 22,1% |
📌 **Il meccanismo di XSR01 vale 0,8% di muro** — dentro la risoluzione Monte Carlo (spread fra semi
**0,151%** di perpetua ≈ $17k, M23). **Mescolarne i rendimenti non cambia niente.**
📌 **A drift zero il muro SALE**: la sola riduzione di varianza **non compra rendita**. Cio' che si
compra e' un **drift scorrelato**, non la scorrelazione.
🚨 **Un conto remunerato allo stesso tasso lo eguaglia**, a vol 0, corr 0 e **senza un secondo venue**
— e il confronto e' **in svantaggio per il conto**: la lente L3 gli applica il 33% di `c-sexies`,
mentre un BOT paga 12,5% e un deposito 26%.
**(d) L'haircut mangia esattamente cio' che si sta comprando.** 3,72% lordi → 2,97% a haircut 20%,
**2,23% alla soglia 40% del gate**, 1,49% al 60%. **Non pareggia un conto al 4,0% nemmeno a haircut
ZERO**; pareggia il 2,0% solo sotto il **46%**. L'haircut pubblicato ($14,41/ticket) e' ~2,4x
ottimista e **senza script che lo riproduca** (XSR-REPRO).
**(e) `weights_tilt_null` (25% vs 0%): PASS** (Δ_is +0,061 · Δ_hold +0,162 · pctl 25,8 < soglia 90,0)
— ma l'hold-out del gate e' 2025-01-01 e la gamba in-sample e' **un solo anno**: indizio, non prova.
**(f) Il vincolo binding non e' il rendimento, e' il CAPITALE.** XSR01 gira su Hyperliquid: il suo
peso e' capitale che **esce** da Deribit. A $2.067 un 25% sono **$517** contro un **C\* misurato
$15.000-20.000**; per un 25% sopra C\* servono **$60.000 sul conto totale**.
**VERDETTO 4/6 — ma la lettura non e' il conteggio:** sotto la lente rendita XSR01 fa una cosa reale
(porta un drift scorrelato a bassa vol) **in modo non proprietario**. **Non dice che e' morto**
(Sharpe 1,56 e DSR 0,983 reggono, e sotto la lente accumulo il conto va rifatto): dice che **per la
rendita non e' lo strumento**, e che il gate del 23/10 sta per decidere su una gamba che a questo
capitale **non e' comprabile**.
**Con questa sono 70 filoni e 0 candidati promossi.**
---
## 71 — COLLAR01 (hold BTC gated dal trend, coperto da un collar a <=15 giorni)
`scripts/research/r0901_btc_collar.py`. **Libro, pesi, cron, config INVARIATI. Nessun ordine.**
Domanda dell'operatore, con tre precisazioni in corso d'opera che ne cambiano l'oggetto: *"hold BTC
long o short con copertura in opzioni"* + *"opzione max 15gg"* + *"ridurre la vincita, ma bloccare
la perdita"* (⇒ **collar**, non put protettiva) + *"l'entrata gestita da indicatori (es. forte bull)"*.
🚨 **PERCHE' SI POTEVA RIAPRIRE DOPO §46.** §46 fu refutato perche' **il beta del libro al
sottostante e' +0,076** — *"non si assicura un libro che nei crash e' gia' quasi piatto"*. Qui il
sottostante e' un **hold di BTC nudo, beta 1,0**: il motivo non si applica. E §46 dichiara di non
aver provato la copertura **gated su regime**, che e' esattamente questa.
**L'entrata non aggiunge parametri:** `tsmom_blend` media tre `np.sign()` su (30, 90, 180) ⇒ valori
in {1, 1/3, +1/3, +1}, quindi *"forte bull"* = `|blend|==1` (3/3 orizzonti, a mercato **52,5%**
dei giorni) contro il confronto dichiarato `|blend|>=1/3` (**97,1%**). Il segno da' la direzione:
long **e** short, come chiesto.
**CALIBRAZIONE dalla catena vera** (161.964 quote a due lati, 95 giorni, DTE 1-15). **Lo skew e' il
fatto strutturale:** IV_put/ATM contro IV_call/ATM = **1,249 vs 0,959** a |δ|0,10 · 1,132 vs 0,950 a
0,20 · 1,072 vs 0,960 a 0,30 ⇒ **un collar delta-simmetrico e' un DEBITO netto**, si compra l'ala
cara e si vende quella a buon mercato. 📌 **Corregge un muro di §46:** il **tick da 5 USDC** e' della
famiglia **USDC**; la catena che raccogliamo e' **100% inverse****quel muro non si applica qui**.
| esito | misura |
|---|---|
| ✅ **A1 — il COLLAR riduce il maxDD** | scende in **36/48** celle (§46: saliva in 162/162). 🚨 **Corretto da §72 lo stesso giorno: NON e' il pavimento** — la put da sola, a premio reale, **peggiora il DD in 16/16**; la riduzione viene dal TETTO (compensa il bleed e abbassa il picco) |
| ❌ **A3 — ma il de-levering lo fa meglio** | il collar batte il null a iso-maxDD in **3/48** |
| ❌ **A2 — il prezzo del baratto** | Δdrift/ΔmaxDD mediano **7,90** (gate forte) / **1,98** (largo) |
| 🚨 **C9 — troncatura, non protezione** | il tetto taglia il **46,2%** dei cicli **vincenti**, il pavimento para il **6,7%** dei **perdenti**: 7x piu' spesso sui vincenti |
| ❌ **M1 — dentro il libro non aggiunge** | collar Sharpe 0,508 vs TP01 **0,852**; TP01+10% ⇒ Sharpe **+0,000** e maxDD **+2,32pp** |
🚨 **IL FATTO CHE VALE PIU' DEL VERDETTO — e che si vede solo estendendo la griglia (M4/M8).** Tutte
e 3 le celle vincenti stanno sul **BORDO** (δput min, δcall max). Estesa la famiglia con 36 trial
dichiarati verso l'angolo: **vince 35/36**, massimo **nell'angolo** (FORTE 7g, δput 0,02 / δcall
0,50) = Sharpe **1,471**, maxDD 18,12%, drift **+29,99%/a**. Il limite di quell'angolo e' *nessun
pavimento, tetto ATM* = **una covered call**: **la pendenza porta FUORI da cio' che l'operatore ha
chiesto e DENTRO lo short-vol.**
🚨 **E quel Sharpe e' il prezzatore che si paga da solo.** **DVOL / RV-forward mediana 1,320 a 7g**
(DVOL sta sopra nel **76,9%** dei giorni), 1,253 a 14g. Riprezzando alla vol **realizzata**
(look-ahead dichiarato, valore equo ex-post): l'angolo cade **1,471 -> 0,511** di Sharpe (VRP
**+21,54pp = 72%** del drift) col gate FORTE, e **1,141 -> 0,114** col LARGO (VRP **+33,26pp = tutto**;
drift **1,10%**). Le celle con un pavimento VERO (δ0,10/0,30) passano da VINCE a **perde**.
📌 **Cio' che sopravvive — Sharpe 0,511 — e' il numero che il progetto GIA' HA:** VRP01 a f=0,73 vale
ShFULL **0,47**. *Non e' una strategia nuova: e' VRP01 ri-scoperto per una strada piu' lunga*, e §3
lo blocca comunque (**"niente short-vol da modello in deploy"**).
🚨 **USCITA ANTICIPATA (chiesta dall'operatore: 50-75% del tempo) — IMPLEMENTATA, E COSTA.**
`exit_frac` su tenor 14g (uscita a 7/9/10 giorni). Cella onesta δ0,10/0,30 gate FORTE: drift
**+9,48% (scadenza) -> +8,59% (0,75) -> +6,11% (0,625) -> +3,84% (0,50)**, e l'esito passa da VINCE a
**perde sotto 0,75**; gate LARGO **12,06% -> 1,88%**. ⚠️ **Il meccanismo previsto c'e'** — il VRP
residuo scende da +3,38 a **+1,27pp**, quindi uscire presto rinuncia davvero allo short-vol — **ma lo
spread lo travolge**. 📌 **E CORREGGE L'APPLICAZIONE DI §46:** *"f si paga solo sulla parte di valore
che converge a intrinseco, quindi un roll anticipato non lo paga"* vale per una copertura **SOLO
LONG**; in un collar c'e' una **gamba VENDUTA da RICOMPRARE**, e uscire prima paga f *esattamente*
sulla parte che a scadenza si sarebbe regolata gratis, ~2x piu' spesso per unita' di tempo.
**L'asimmetria di §46 si INVERTE quando la struttura ha una gamba corta.**
**CONTROLLI DELL'APPARATO 3/3** (M15, la lezione di §46): pavimento a **premio zero** riconosciuto
come vittoria (maxDD 51,83%→**36,76%**, drift +10,04%→**+35,69%**); premio **x10** rifiutato (drift
**33,96%/a**); zero-cost costruibile e finito.
⚠️ **CINQUE DIFETTI MIEI, tutti catturati dai controlli e non a occhio.** (i) **bisezione dello
zero-cost invertita** — il premio cresce col delta, quindi a incasso insufficiente il tetto va
*avvicinato*: dava Sharpe 3,0/3,9 e drift 53%/a; (ii) `dcall=NaN` propagava NaN nella cassa allo
smontaggio anticipato; (iii) **C9 non consapevole della direzione**`S1>S0` non e' "vincente" per
un ciclo **short**: era la causa dell'impossibile *"il pavimento para nello 0,0% dei perdenti"* con
una put a 10 delta; (iv) la base senza opzioni **rollava lo spot** ogni tenor pagando ~3,6%/a di fee
inesistenti (**avrebbe adulato il collar**); (v) al roll delle opzioni chiudevo e riaprivo **anche lo
spot**, che non ha motivo di muoversi.
⚠️ **NON MISURATO, DICHIARATO:** la **lente reale** (quote vere, 123 giorni = ~8 cicli) non e' stata
girata come backtest — sotto-potenziata, e su una finestra in cui BTC e' salito da ~64,7k a ~77,5k,
**avversa a un collar per costruzione**; la catena e' servita a **calibrare**, dove 161.964 quote
hanno potenza. Nessun DSR e nessun `study_family_honest`: il filone cade al **primo** gate (M5), e M2
si spende su cio' che il primo gate lascia in piedi. A6/A7 restano previsioni non misurate. Il
**funding** non e' nel motore (2,16%/a) ma colpisce base e collar quasi allo stesso modo: abbassa
entrambi, **non cambia il segno**.
**VERDETTO: `IL PAVIMENTO FUNZIONA — E' IL TETTO CHE NON SI PUO' PAGARE; E CIO' CHE VINCE SUL BORDO
NON E' LA PROTEZIONE, E' IL PREMIO DI VARIANZA (VRP01)`** — REFUTATO.
**Cosa lo riapre:** un **f di stress misurato su un crash catturato** (la stessa condizione che §3
pone allo short-vol), o un sottostante **senza coda destra grassa** — il tetto costa perche' BTC vive
li'. **Non** lo riaprono tenor, delta o griglie piu' fini: la pendenza e' monotona verso l'angolo, e
l'angolo e' gia' misurato.
---
## 72 — PAVIMENTO-LEVA (il pavimento come licenza di taglia)
`scripts/research/r0901c_pavimento_leva.py` (riusa il motore di §71). **Libro, pesi, cron, config
INVARIATI. Nessun ordine.** Domanda dell'operatore: *"possiamo usare quanto conosciamo del pavimento
per studiare una strategia?"*. L'inversione di §71: non *quanto DD mi risparmia* ma *quanta TAGLIA
mi autorizza a iso-DD* — l'unica cosa che il de-levering non puo' comprare, e la grandezza su cui
vive il tetto di leva del progetto (`n · frac · scala · sl ≤ 0,50` ⇒ 1,67x).
**0/16.** Sola put a premio reale, 4 δ × 2 tenor × 2 gate: **il maxDD SALE in 16/16 celle**
(FORTE 51,83% → 55,4-75,8%, LARGO 71,94% → 74,8-81,7%), quindi `k_f = 1,000` ovunque e non c'e' taglia
da licenziare. Monotono: piu' la put e' vicina, peggio va — **il bleed del premio E' il drawdown**.
🚨 **CORREGGE §71.** Stamattina A1 diceva *"il pavimento funziona"*. **E' il COLLAR a ridurre il DD,
non il pavimento**: a premio zero la put lo riduce (51,83 → 36,76), a premio reale lo peggiora ⇒
tutto il beneficio e' mangiato dal premio, e oltre. Nel collar la riduzione viene dal **tetto** (il
suo premio compensa il bleed, e cappare l'upside **abbassa il picco** da cui il DD si misura).
**Premio di pareggio: 36-79% del reale** (secondo la cella); il DVOL sta 1,32× sopra la RV, quindi
un prezzo equo (~76%) sfiora il pareggio nelle celle migliori e lo manca nelle altre. *Il motivo di
§46 (beta) non si applica a beta 1,0 — la put paga davvero — ma il verdetto di §46, il maxDD SALE,
si riproduce per un motivo diverso.* Corretti §71, il suo diario e la memoria.
📌 **B2, IL MECCANISMO — e' il risultato trasferibile:** **i drawdown di BTC sono GRIND**, non
crolli. maxDD FORTE da 2021-07-20 a 2022-05-06 (**290 giorni**), LARGO fino al 2023-10-16 (**818**).
Una put a 7-14g copre UNA finestra; il DD che conta dura 20-60 finestre; la put scade OTM ogni
settimana (para nel **0-9%** dei cicli, gate FORTE) mentre il premio sanguina. E non protegge
nemmeno la finestra peggiore (14g FORTE: 22,9% nudo → **23,6%** col pavimento: la put a 5δ sta a
~22% dallo spot, al bordo esatto di cio' che e' successo). **Su BTC il pavimento compra protezione
contro la cosa sbagliata.**
🚨 **B3 — LA LICENZA DEL DISASTER-SL E' DI CARTA, e va scritto prima che sia comodo.** Con la put a
5δ/14g a **21,9%** dallo spot, sostituire `sl` con quella distanza nell'invariante darebbe **2,28x**
(1,4× in piu'). Ma l'invariante limita UN episodio, la put limita UNA finestra, e il massimo su
finestre consecutive **non e' limitato da nulla**: 1 finestra 23,6% (coperta), 2 25,9%, 4 28,8%,
**8 finestre 33,6%** (non coperte). A 2,28x un grind di 112 giorni costerebbe il **77%
dell'equity**. ⇒ **`disaster_sl_pct` NON si sostituisce con la distanza di un pavimento.**
**B4 — gated sull'IV** (§46: *"nessuna copertura dinamica e' stata provata"* — ora lo e'): put ON solo
sotto il 25°/50° pctl di DVOL/RV. Migliora molto (FORTE +3,8% → **+8,4%**; LARGO +9,9% → +15,3%),
**ma resta sotto la base** (+10,04% / +17,37%) e il DD resta ≥. L'incollatura ignora lo spread delle
transizioni **a favore** del gated.
⚠️ **Non misurato, dichiarato:** nessun DSR (cade al primo gate); nessuna lente reale; **nessun
pavimento a scadenza lunga** (30-90g, che coprirebbe piu' finestre di grind) perche' l'operatore ha
vincolato la scadenza a **≤15 giorni** — e' l'unica variante che B2 lascia aperta, e sta fuori dal
vincolo. Se il vincolo cadesse, sarebbe la prima misura da fare.
**VERDETTO: `IL PAVIMENTO NON LICENZIA TAGLIA: 0/16 — SU BTC COMPRA PROTEZIONE CONTRO I CROLLI, E
LE PERDITE SONO GRIND`** — REFUTATO. **Cosa lo riapre:** una scadenza che copra il grind (fuori dal
vincolo), o un premio di varianza *negativo* su BTC (mai misurato). Non lo riaprono delta, tenor ≤15
o gate sull'IV.
---
## 73 — SOLDI-FERMI (altri modi, non per forza trading)
`scripts/research/r0901d_soldi_fermi.py`, diario `2026-09-01d-soldi-fermi.md`. **Nessun ordine.**
*Non sono pareri fiscali.* Tassi dal web con fonte e data (cutoff del modello 05/2026).
**On-venue chiuso con l'ELENCO INTERO:** `public/get_currencies` remunera **4 valute su 50**
USDE 4,32 · USDC 3,40 · BUIDL 3,20 · STETH 2,22 — esattamente le quattro chiuse il 31/08 (al tetto ·
Italia esclusa · non acquistabile · e' ETH). ⚠️ **L'USDC su Deribit non e' fermo:** base di sizing
(cap = equity x 0,5) e cuscino del disaster-SL. Il fermo vero sta **fuori**: ~€6.043 in XEON, lo
**split-cassa** (P(perso tutto) 18% → 3,5%).
**In euro netti l'anno su €6.000** (BOT 2,768% asta 08/2026 · DFR 2,25% · conti deposito 3,25-3,50% ·
sUSDe ~9%; fisco 12,5 / 26 / 33%; bollo 0,2%): XEON **81** · BOT **133** · conto deposito **143** ·
sUSDe **350** ma **stesso emittente dell'USDE gia' in conto** ⇒ split distrutto · libro **~600 atteso**
ma trading ⇒ split distrutto. **Il miglior guadagno sicuro e' +€62/anno = €5/mese**, contro €100/mese
di bonifico = +4,07%/anno di drift. BOT e conto deposito distano €10/anno: scelta di liquidita' e
controparte, non di rendimento.
**Decisione dell'operatore (02/09): versare sul libro.** E' il piano del 27/07. Tre precisazioni,
tutte gia' misurate: **€5.000 dentro, ~€1.000 fuori** (la protezione venue **satura a qualunque
quota > 0**: l'ultimo migliaio compra ~€100/anno e butta via l'assicurazione); il «~€600» e' un'attesa
a banda larga, non un tasso (libro a mercato il 22% dei giorni, TWR +10,8% in 70g, hold-out TP01
~+0,05, fisco 30% a 10a); fondo d'emergenza da dichiarare. Operativamente nulla: cap dinamico,
rilevatore di salti collaudato il 25/08. **In attesa di importo e data.**
---
## 74 — LIBRO-NEI-CROLLI (cosa fanno TP01 e SKH01 nei crolli, misurato invece che ricordato)
`scripts/research/r0909_libro_nei_crolli.py`, diario `2026-09-09-crolli-opzioni-monete.md`. **Libro,
pesi, config, cron INVARIATI.** Lente: sleeve di ricerca, ancora canonica, pesi importati da
`src/live/book` (P1); ⚠️ SKH01 marcato all'USCITA del trade (zero nell'87,7% dei giorni). Indice 50/50;
giorno di crollo ≤ 5%; 12 peggiori finestre di 20 g non sovrapposte + 9 episodi nominati prima.
**Nel GIORNO del crollo il libro PERDE** (0,48%/g su 160 giorni, positivo nel 18%; ≤ 10%: 0,51%/g con
SKH01 +0,42). **Per finestra**: 4 POSITIVO / 6 immune / 2 PERDE; libro > 0 in 8/12 e **in 8/8 dal 2022**
(mediana +3,3% contro indice 29%); le due perdite (2019, maggio 2021) sono TP01 long dentro un trend
non girato. **La gamba short di SKH01 fa il 108% dei guadagni** (scomposizione ESATTA long+short==intera,
assert). Beta **+0,0769** sulla finestra di §46 (riprodotto al millesimo). Peggior giorno canonico
2025-10-10 3,38%: un crollo con TP01 a 0,505 (lo squeeze di §33 sta sulla lente hourly, non si riproduce
qui). 37,5/62,5 (§26) batte 12/12 episodi, Sharpe 1,81 vs 1,82, maxDD 12,1% vs 9,4%;
`r0726_reeval_live_weight` da' `gate_pass False` su `delta_insample 0,0026` (cache segnali ferma al
25/07: stesse cifre del 26/07, e la condizione che fallisce e' in-sample).
🚨 **Errore dell'autore smontato in revisione:** la prima stesura provava GUADAGNA prima di immune e
stampava **8/12 GUADAGNA**; nell'ordine dichiarato sono 4+6. *L'ordine delle regole era il verdetto.*
**VERDETTO: `PERDE NEL GIORNO, POSITIVO PER FINESTRA DAL 2022, TUTTO SKH01 SHORT`** — nessun meccanismo
nuovo: quello che guadagna nei crolli e' gia' in produzione, e la leva che lo aumenta (il peso) e' chiusa dal gate.
## 75 — CROLLO-CATTURATO (il f delle opzioni su un crollo a quote vere)
`scripts/research/r0909_crash_catturato.py`. **Nessun ordine.** L'archivio bite copre con bid/ask il crollo
1-5/06/2026 (BTC 25%, ETH 30%; scadenza 19/06, 14-21 DTE, famiglia INVERSE); il campione di §11 partiva
dal fondo. ETH, ingressi pre-crollo (286 snapshot = **4 strutture**): f_net **0,76 [0,71-0,86] = 0,712 del rally** (era 0,74 = 0,714 prima della riparazione di §5.18, la sera stessa; put 1,98× e picco 0,99 idem);
f a scadenza 1,04 ma **tautologico** nell'83% (ambo le gambe ITM ⇒ solo f_net); peggior MTM **1,08**
dopo il taglio alla larghezza; put δ−0,10 costa **1,92×** il modello (punto nuovo sopra l'1,27 di §46)
e paga come il modello (0,94-1,04); ingressi durante il crollo bimodali (perde il 46%). BTC pre-crollo
non misurabile (griglia strike rada). **Crollo a vol BASSA**: picco DVOL al 29°/46° percentile, gate
IV-rank di VRP01 CHIUSO in tutte le ore. ⇒ la condizione di §3 e' soddisfatta nella forma, **non nella
sostanza: non si riapre**. Servono un crollo con IV-rank > 0,30 e la famiglia USDC-lineare.
🚨 **Due conclusioni dell'autore smontate in revisione:** «MTM 2,58× per lo skew (IV 108)» era un
artefatto (quote con 158% di spread a T→0 oltre la larghezza; la IV 108 e' del 18/06 a T→0); «f a
scadenza 1,04 = il modello prezza bene» era una tautologia. **Debito §5.18 RIPARATO la sera stessa** in `cblib`
(spot +1h, e anche il DVOL giornaliero, +1 giorno): ETH pre-crollo f_net **0,76 [0,71-0,86]** (era 0,74),
BTC durante **0,89 [0,74-1,05]** (era 0,79; con DVOL orario 0,83 — nel crollo il DVOL giornaliero stantio
vale 0,04/+0,06 di f), put δ−0,10 1,98× (era 1,92), DVOL pre 36,4 / 49,6 (il 37,3 di BTC era la chiusura del
PRIMO giorno di crollo: contaminato). Picco e percentili invariati. Verdetto invariato.
**VERDETTO: `CATTURATO, MA A VOL BASSA E FUORI DAL GATE: f_net NON CAMBIA COL REGIME`** — nessuna riapertura.
## 76 — XRP-TERZA-GAMBA (l'unica altra moneta liquida di Deribit, coi meccanismi congelati)
`scripts/research/r0909_xrp_terza_gamba.py` (harness IMPORTATO da `r0822_sol_leg`). Dato in
`data/raw/alt_xrp_*` (namespace di ricerca, non rinfrescato dal cron). Certificazione: >1% dal
riferimento 0,06% (2022) · **0,57% (2023)** · 0,01-0,02% (2024+) — Coinbase dal rilisting 2023-07
(delisting SEC), Bitstamp prima; **L-PULITA dal 2024**, come SOL; 5m flat **27-61%**.
dSharpe hold-out **0,169 in 0/24 ancore** (SOL 0,166); FULL 0,206 (L-FULL) / 0,066 (2024+); dCAGR
2,27 / 1,32 pp; de-levering: L-FULL 1,23 vs 2g×0,974 **1,44**, 2024+ 1,54 vs 1,53 pari; per anno 2022
0,55 · 2023 0,66 · **2024 +0,54** · 2025 0,44 · 2026 0,25 (**1/5**). TP01 XRP Sharpe 0,01 /
hold-out 0,49; SKH01 XRP maxDD 40,1% (il criterio di selezione di SKH01-V2-DD fallisce, come SOL);
corr gamba-book +0,35/+0,37. Eseguibilita' non e' il vincolo (min 1 XRP = $1,42).
⚠️ Due difetti del verdetto trovati dai test (Opus) e corretti: campione vuoto stampava «DILUISCE»
(ora NON MISURABILE, P5); criterio per anno cablato al 2024 invece che derivato dalla lente (P1).
**VERDETTO: `XRP DILUISCE COME SOL — UN ANNO BUONO, HOLD-OUT 0/24`** — nessuna proposta.
## 77 — UNIVERSO-DERIBIT (lettura del venue, €0)
`scripts/research/r0909_deribit_universo.py` (API pubblica, JSON con ora in `data/_cache`, `--fresh`).
Lettura 2026-09-09 15:05Z: **32 perpetual USDC** aperti (+6 non aperti, fra cui **APT inactive**);
liquidi ≥ $10M/g **solo XRP ($38M, spread 4 bps), ETH, BTC, SOL ($13M)**; tutto il resto $0,01-7M
(TAO, HYPE, ADA, AVAX, NEAR…). XS01 **13/19** (mancano ARB, OP, APT, INJ, TIA, SEI → §50 resta).
Opzioni USDC-lineari su 7 sottostanti (SOL 676, HYPE 442, XRP 442, TRX, AVAX). Trovati in revisione:
**`BTCDVOL_USDC-30SEP26`** (unico long-vol diretto del venue: vol 0, OI 132, spread 12% → non
negoziabile) e **PAXG perp** (oro, dal 2024-12, $0,18M/g: non misurato, non morto, muri di SOL/XRP).
**VERDETTO: `QUATTRO STRUMENTI LIQUIDI, TRE GIA' NEL LIBRO O MISURATI`** — l'universo "piu' monete" di Deribit e' finito.
+15 -2
View File
@@ -1,8 +1,21 @@
# SPEC — la CHIAVE DI SCALA del libro live (`book_scale_k`)
**Filone SCALE-SPEC, ondata 2026-08-22, branch `research/wave-0822`.**
**Questo documento e' una SPECIFICA. Non e' stata implementata: `src/`, `config/`, `scripts/live/`,
`scripts/cron_*.sh` e `tests/` non sono stati toccati.** Il prototipo che dimostra la meccanica e'
> ✅ **IMPLEMENTATA IL 2026-09-01 — punti 1-4 della checklist §8.** `src/live/book.py`
> (`book_scale_k`, `_scala`, `LEVA_LORDA_MAX`, `SCALA_LADDER`, `DISASTER_SL_BUDGET`,
> `ScalaNonAutorizzata`, `scala` nel report), `scripts/live/book_execute.py` (riga «scala libro» +
> stop-e-allerta), `src/live/scale_watch.py` + `scripts/live/scale_watch.py` in `cron_daily.sh`,
> `data/live/scale_history.jsonl`, `tests/test_book_scale.py` (T1-T11, **18 test verdi**).
> 🚨 **`config/live.json` NON e' stato toccato**, come prescrive il punto 2: la chiave e' assente,
> vale 1,00, e T7 dimostra bit-exact che il libro e' quello di sempre (`max|diff| = 0.0`).
> 📌 **(10/09, issue #5) A2 conta dal 2026-09-01**, primo giorno di `scale_watch`: 180 giorni cadono il
> **2027-02-28**, ed e' la data «non prima del» del gate in CLAUDE.md §4. Il «2026-10-01» scritto il 01/09 era
> il punto 5 (30 giorni di sorvegliante) scambiato per la data del gate: incompatibile con A2 per costruzione.
> **Restano i punti 5-7:** ≥30 giorni a 1,00 col sorvegliante attivo, poi `GATE SCALA-01`, poi
> `r0726_fee_sensitivity` rifatto. Il punto 5 non e' burocrazia — e' l'unico modo di scoprire che il
> sorvegliante e' rotto mentre la leva e' ancora 1,00.
**Questo documento era una SPECIFICA scritta prima dell'implementazione.** Il prototipo che dimostra la meccanica e'
`scripts/research/r0822e_scale_proto.py` (isolato, non importato da niente, non scrive nulla; la
sua sezione (0) verifica entrambe le cose a runtime invece di dichiararle).
+87
View File
@@ -0,0 +1,87 @@
# Revisione settimanale — 2026-09-09
*Scritta il 2026-09-09T21:59:07Z dal modello `claude-fable-5-1`, revisore diverso dall'autore del codice, in SOLA LETTURA: nessun file del repo, nessuna config, nessun ordine e' stato toccato. E' l'opinione di un lettore fallibile (P13): ogni numero e ogni proposta vanno verificati alla fonte prima di agire. Guardia sui numeri: **sospetta** — 11 numeri non presenti nel materiale: 1.011, 1.341, 1.38279, 1.40085, 2.034, 2.43408, 4.47099, 4.47746, 400, 40008, 6879.*
## Sintesi
Revisione del 2026-09-09 21:59Z (primo giro, manuale: il cron e' del lunedi'). **Nessun URGENTE.** Nella settimana 02-08/09 il libro ha fatto 24/24 giri ogni giorno con feed SKH a 0 min, leva lorda 0,26x, equity $4.477,46 e TWR +7,41% (giornale 08/09). Tre cose da verificare, in ordine:
1. **`usde_watch` del 07/09 ha stimato un reward di 400,08 USDE**, dove i giorni normali (08-09/09) danno 0,38. E' la differenza fra i 2.434,08 USDE entrati e i 2.034,0 contati come trade, con `n_trades` fermo a **50**. Il registro `usde_convert.jsonl` mostra 8 + 30 + 16 = 54 ordini: ne mancano 4 da 100 = 400. Un lettore che vede al massimo 50 trade attribuisce al reward cio' che era un acquisto (D2, P2). Se `rendimento()` usa quella finestra, il tasso che `quota_da_decidere()` stampa ogni giorno e' inquinato.
2. **Il cuscino USDC (debito §5.17) resta senza sorvegliante, e la sua misura non e' definita**: a `balance_watch` 21:42Z l'USDC ha balance 1.400,85 ma equity 1.382,79; con l'equity totale 4.470,99 (watermark) il cuscino al 30% vale ~$1.341, quindi lo slack e' **+$59 sul balance o +$41 sull'equity USDC**. Quale dei due conta va deciso prima di scrivere il sorvegliante.
3. **Operatore**: il versamento di $2.410,14 del 04/09 e' solo *rilevato* (salto 13:47→14:47); `movimenti_dichiarati.jsonl` contiene il solo €25 del 25/08 e la Nota di giornale e' vuota 7/7 giorni. CLAUDE.md §2 chiede la dichiarazione il giorno stesso, e §1 («soldi fermi») dice ancora «in attesa di importo e data» per il piano €5.000: non si capisce se il 04/09 ne e' il primo pezzo.
## Cosa non torna
- **«2.434 USDE in 39 ordini»** (diario `2026-09-06-usde-quota-topup` §3, CLAUDE.md §1) non si riproduce dal registro: 8 ordini nel primo record, 30 eventi nella prima sonda, 16 nella seconda. Il totale USDE torna, il conteggio no (N11).
- **Haircut, due affermazioni opposte in CLAUDE.md §1** (M28): una dice che il 5% e' confermato dal passaggio a X:SM (`available_funds` $2.011,09 contro $2.010,90 attesi *con* haircut 5%); poche righe sotto «oggi comunque non si applica», frase del mattino del 31/08 sotto S:SM. `config/live.json` `_nota_usde` dice ancora «il modello ATTIVO e' Segregated» e giustifica `depeg_crit 0.95` come «meta' del buffer di haircut (10%)»: col 5% il criterio dichiarato (P6) non descrive piu' la soglia, e la nota e' una diagnosi cablata (P4).
- **GATE SCALA-01: data e condizione A2 non concordano** (CLAUDE.md §4). «Non prima del 2026-10-01» sono 30 giorni dal 01/09; A2 chiede k0 «≥180 g col criterio passato ogni giorno», e il criterio lo misura solo `scale_watch`, attivo dal 01/09 (`scale_history.jsonl`). 180 giorni dal 01/09 cadono nel 2027; dal 20/06 a fine 2026. Da quando si conta va chiarito prima del 01/10, non quel giorno.
- **Sorveglianti di cui il materiale non prova il funzionamento** (P5): `venue_news.jsonl` ha 5 voci tutte `seed: true` viste il 2026-08-31T07:40Z e nulla dopo; `scale_watch_state.json` non ha timestamp (streak 0 puo' essere «non ho girato»); `edge_watch` e' citato a «stato 27/07» (CLAUDE.md §4); `fee_watch` non compare. `monitor_health` copre 8 monitor, nessuno di questi quattro.
- **Data di arming**: CLAUDE.md §0 «LIVE dal 2026-06-20», `config/live.json` «ARMATO 2026-06-23», primo segmento TWR dal 2026-06-23 (diario `2026-09-02-debito-14`). Una delle tre non e' la stessa data.
- **Richieste dell'analista senza risposta**: i 4 fill del 03/09 («dovrebbero essere ri-taglie»), la conferma del versamento 04/09, il SELL $7 del 06/09, il round-trip a $+0,00 con fee 0,0055 del 08/09. Nessuna Nota. Non e' un guasto: e' un canale che non chiude (N9).
- **CLAUDE.md §13 dice 1.011 test**; il diario `2026-09-09c` ne aggiunge 16 (`test_revisione.py`). E `scripts/cron_review.sh`, `scripts/live/revisione.py`, `src/live/revisione.py` sono `??` in `git status` mentre la crontab li chiama lunedi' 06:15 (P16 li fa girare, P11 non li salva).
## Cosa e' maturo da decidere
| gate | data | criterio scritto | stato nel materiale |
|---|---|---|---|
| STATARB | 2026-09-27 (fra 18 g) | soglie 24/07 + diagnostica sempre-short | monitor OK; Sharpe 1,61 al 26/08, nessuna lettura piu' recente. Si legge il 27/09 |
| SCALA-01 | non prima del 2026-10-01 (22 g) | 9 condizioni tutte necessarie | A8/A9 fatti; A2 non maturo e ambiguo (sopra); A3, A6, A7 non risultano fatti |
| USDE-01 | chiuso 28/08 | — | quota 68,79% (09/09) sotto `quota_max_frac` 0,85: dentro la decisione dell'operatore del 30/08 e 06/09 |
| PREVDAY-01 | **senza data** | 10 condizioni, 8/10 | N10: un gate senza data non si chiude |
XSR01 (23/10) e DVOLSPREAD (24/10) cadono oltre 30 giorni. `edge_watch` e' continuo ma l'ultimo stato e' del 27/07: non determinabile se sia ancora 8/8.
Decisioni dell'operatore, oggi: (a) dichiarare il 04/09; (b) dire se il piano €5.000 e' iniziato; (c) definire il cuscino (balance o equity USDC). Nessuna decisione di §3 ha la colonna «cosa la riapre» soddisfatta: `weights_tilt_null` fallisce ancora (26/07, in-sample); il crollo catturato e' a vol bassa, sotto il gate e inverse (diario 09/09 §2).
## Proposte (nessuna azione eseguita)
1. **Agente**: verificare in `usde_watch` il limite di lettura dei trade e come `rendimento()` tratta la finestra del 07/09; rigenerare la stima con i 54 ordini (P2).
2. **Agente**: riallineare `_nota_usde` (modello attivo X:SM, criterio di `depeg_crit` al 5%, `venue_cap_frac` null) e sciogliere la contraddizione di §1 datando le due frasi (M28).
3. **Agente + operatore**: il sorvegliante del cuscino e' gia' descritto in §5.17 (`USDC cuscino_richiesto` derivato da config, P1); manca solo la scelta balance/equity.
4. **Agente**: uno stato senza timestamp non distingue «zero» da «non ho girato» (P5): `scale_watch_state.json` e `venue_news` meritano l'ora dell'ultimo giro, e i quattro sorveglianti fuori da `monitor_health` una riga li'.
5. **Agente**: committare i file della revisione, aggiornare il conteggio test.
6. **Operatore**: dare una data a PREVDAY-01 o chiuderlo (N10).
7. Nessuna idea di strategia: la settimana ha gia' misurato crolli, opzioni e XRP contro `20-ondate` (diario 09/09 §0) e non ho nulla che batta i motivi elencati li'.
## Cosa ho letto e cosa mi mancava
Letto: CLAUDE.md intero; `config/live.json`; crontab; git log dal 26/08 e status; `monitor_health`; stati di scale_watch, scale_history, vrp_f_watch, usde_watch, usde_convert, balance_watch, venue_news, movimenti_dichiarati, watermark; giornali 02-08/09; diari 02/09 (tre), 06/09, 09/09 (tre).
Mancava: (1) stato con timestamp di `edge_watch`, `fee_watch`, `venue_watch`, `scale_watch`; (2) l'output di `usde_watch.rendimento()` e `quota_da_decidere()`; (3) la lettura corrente di `paper_statarb` (Sharpe a oggi, non al 26/08), dvolspread, xsr; (4) il contenuto di `cron_daily.sh`, cioe' quali sorveglianti chiama; (5) `venue_news` dopo il 31/08; (6) il diff di CLAUDE.md e memoria 40 non committati; (7) il registro fill della settimana (per i 4 fill del 03/09 e il +0,00 del 08/09); (8) un registro depositi del gateway, che «non espone get_deposits».
---
## Firma e materiale
- revisore: `claude-fable-5-1` · cadenza settimanale · giornale 7 g · diari 7 g · commit 14 g
- scritto da `scripts/live/revisione.py` (cron `scripts/cron_review.sh`); l'unico file scritto e' questo
| voce | caratteri | troncata |
|---|---|---|
| CLAUDE.md (stato, book, numeri, decisioni vincolanti, gate, debiti, regole) | 80,310 | |
| config/live.json (unica autorita' dei guardrail live) | 7,184 | |
| crontab installata (crontab -l) | 1,312 | |
| git log dal 2026-08-26 + stato del working tree | 4,543 | |
| monitor_health (i forward-monitor stanno registrando?) | 522 | |
| stato scale_watch_state.json | 149 | |
| stato scale_history.jsonl (ultime 3) | 259 | |
| stato vrp_f_watch/state.json | 453 | |
| stato usde_watch.jsonl (ultime 3) | 1,339 | |
| stato usde_convert.jsonl (ultime 3) | 9,979 | |
| stato balance_watch.jsonl (ultime 2) | 578 | |
| stato venue_news.jsonl (ultime 5) | 1,564 | |
| stato movimenti_dichiarati.jsonl | 405 | |
| stato equity_seen.json (watermark) | 73 | |
| giornale 2026-09-02 | 3,824 | |
| giornale 2026-09-03 | 3,745 | |
| giornale 2026-09-04 | 3,999 | |
| giornale 2026-09-05 | 3,645 | |
| giornale 2026-09-06 | 3,630 | |
| giornale 2026-09-07 | 3,846 | |
| giornale 2026-09-08 | 3,765 | |
| diario 2026-09-02-debito-14-report-twr.md | 6,952 | |
| diario 2026-09-02b-debito-7-cadenza-docstring.md | 6,061 | |
| diario 2026-09-02c-flag-e-pulizia.md | 5,874 | |
| diario 2026-09-06-usde-quota-topup.md | 6,571 | |
| diario 2026-09-09-crolli-opzioni-monete.md | 17,894 | |
| diario 2026-09-09b-debito-18-cblib-causale.md | 14,150 | |
| diario 2026-09-09c-revisione-settimanale.md | 5,514 | |
+17
View File
@@ -0,0 +1,17 @@
#!/bin/bash
# Campionamento ORARIO del BALANCE per valuta (USDC/USDE) — v2.0.0+.
# Serve a rispondere a UNA domanda: l'APR USDC pubblicata da Deribit (3,4000%) e' accreditata
# sul nostro conto? Il balance e' l'unica serie dove si vede: l'equity e' arrotondata a 2
# decimali in trades.db e contiene il P&L non realizzato, e il gateway non espone il
# Transaction Log (debito #11). Cadenza oraria per STRINGERE la finestra intorno all'accredito
# (i reward USDE arrivano in un colpo verso le 12:00 UTC), non per campionare di piu'.
# Minuto :42 = libero fra :25 (catena), :35 (usde), :47 (book); fuori dal minuto tondo
# (rate-limit per-IP). SOLA LETTURA: nessun ordine, nessuna scrittura sul percorso soldi.
export PATH="/home/adriano/.local/bin:$PATH"
cd /opt/docker/PythagorasGoal || exit 1
mkdir -p logs
{
echo "===== $(date -u '+%Y-%m-%dT%H:%M:%SZ') cron_balance ====="
uv run python scripts/live/balance_watch.py || true
echo "===== done $(date -u '+%H:%M:%SZ') ====="
} >> logs/cron_balance.log 2>&1
+21 -5
View File
@@ -5,11 +5,27 @@
# segnale SKH e' preso IN MEMORIA dentro book_execute (livefeed.fresh_5m): NON tocca i dati
# certificati su disco. Esecuzione reale gated da config/live.json (execution_enabled) + --execute.
#
# INSTALLATO AL MINUTO :07, NON :00 (`7 * * * *`, spostato il 2026-07-29 durante l'incidente del
# feed 5m). Motivo: IPOTESI di contesa al minuto tondo (l'ora esatta e' quando parte tutto il
# resto della VPS). ⚠️ NON e' un fix verificato — e' un ripiego da un'osservazione sola, preso
# perche' costa zero. Se il feed torna stantio anche al :07, l'ipotesi e' morta e la causa vera
# la dira' `skh_feed_errors` nel report (instrumentazione del 29/07, vedi src/live/livefeed.py).
# INSTALLATO AL MINUTO :47 (`47 * * * *`). Due vincoli, entrambi misurati — e questa riga deve
# restare d'accordo con la crontab: un commento che dichiara un minuto diverso da quello che gira
# e' lo stesso difetto del docstring "ogni ~230 minuti" (§5.7), e si paga quando qualcuno lo
# "corregge" nel verso sbagliato.
#
# 1. FUORI DAL MINUTO TONDO. Il collettore full-chain al :00 produce 12.186 risposte 429 in 26
# ore, di cui 11.700 (96%) dentro il minuto :00 — ~770 ticker + ~770 orderbook in ~26s da un
# IP solo, che si auto-saturano il rate-limit Deribit per-IP (misura del 2026-07-30). Il
# vincolo vero e' *stare fuori da quei ~26 secondi*: qualunque minuto != :00 lo soddisfa.
# Storia: :00 -> :07 il 2026-07-29 (ipotesi di contesa, dichiarata non verificata), :07 ->
# :47 il 2026-08-25.
# 2. FUORI DALLO SLOT DI RELEASE DERIBIT. Le release Deribit escono il MARTEDI' alle 09:00 UTC,
# annunciate 15-30 minuti. Il :07 cadeva dentro quella finestra, e ci e' caduto 4 volte in 63
# giorni: 2026-07-21 (che uccise anche il giro di ETH), 11/08, 18/08, 25/08. Il :47 lascia
# 47 minuti di margine dall'inizio dello slot, e 22 dal collettore catena del :25.
#
# ⚠️ PREVISIONE DA MISURARE (M12: un follow-up dichiarato contiene una previsione). Sulle 4
# finestre osservate, 3 sono rientrate entro l'ora (21/07, 11/08, 25/08 ~20 min) e una no (18/08,
# giu' anche alle 10:07). Il :47 avrebbe quindi scavalcato 3 episodi su 4. Se al prossimo martedi'
# il :47 becca comunque la manutenzione, la previsione e' sbagliata e lo slot non e' quello che
# credo: rileggere `venue_probe.RELEASE_*` prima di spostare ancora.
export PATH="/home/adriano/.local/bin:$PATH"
cd /opt/docker/PythagorasGoal || exit 1
mkdir -p logs
+1 -1
View File
@@ -8,7 +8,7 @@
# MINUTO :25, e la scelta NON e' arbitraria:
# :00 cerbero-bite sparava la sua spazzata full-chain (~44 chiamate/s) e saturava il rate limit
# Deribit per-IP — 12.186 risposte 429 in 26 ore, il 96% nel minuto tondo.
# :07 cron_book.sh (esecuzione del book live). Il feed 5m di SKH01 passa da li': e' la cosa che
# :47 cron_book.sh (esecuzione del book live; era :07 fino al 25/08). Il feed 5m di SKH01 passa da li': e' la cosa che
# NON deve trovare l'IP occupato.
# :25 nessun altro job. Il giro dura ~3 minuti a 4 chiamate/s, quindi finisce ben prima del :30.
#
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
# Sorveglianza ORARIA del cuscino USDC di regolamento (debito §5.17, issue #3) — v2.0.0+.
# Minuto :53 = DOPO il giro del book (:47, i cui fill si regolano in USDC) e fuori dal minuto
# tondo (rate-limit per-IP), dal :25 (catena), dal :35 (usde) e dal :42 (balance).
# PUO' INVIARE ORDINI: sotto zero di slack lancia usde_convert --esegui (vende USDE), con le
# guardie dichiarate nel docstring di scripts/live/cuscino_watch.py; lo interruttore e' lo
# stesso del libro (execution_enabled in config/live.json).
export PATH="/home/adriano/.local/bin:$PATH"
cd /opt/docker/PythagorasGoal || exit 1
mkdir -p logs
{
echo "===== $(date -u '+%Y-%m-%dT%H:%M:%SZ') cron_cuscino ====="
uv run python scripts/live/cuscino_watch.py --quiet || true
echo "===== done $(date -u '+%H:%M:%SZ') ====="
} >> logs/cron_cuscino.log 2>&1
+19 -2
View File
@@ -31,18 +31,35 @@ mkdir -p logs
# Schema fee Deribit (nuovo dal 2026-08-01, annunciato senza numeri): legge il tier BASE
# dall'endpoint pubblico e applica la regola decisa in anticipo (<=5bps nulla, >10bps peso SKH01).
uv run python scripts/live/fee_watch.py --quiet || true
# Cosa il venue ANNUNCIA (debito #8: la sonda venue_probe legge se RISPONDE, non cosa dichiara).
# Feed RSS pubblico degli exchange-update. NON interpreta: classifica solo l'urgenza, su parole
# DERIVATE da deribit._CONTRACT e config/live.json. In una settimana quattro cambi di policy
# materiali erano arrivati a mano; le specifiche dei perp USDC cambiate il 18/08 erano annunciate
# il 14/08, e check_specs() se ne accorge solo DOPO.
uv run python scripts/live/venue_news.py --quiet || true
# f delle strutture VRP dalle quote REALI (canonico vs candidato bocciato dal gate 30/07).
# Criterio di sufficienza pre-registrato (>=40 coppie, IC95 <=0.12): avvisa da solo quando
# il campione basta, invece di lasciare la misura appesa a un promemoria.
uv run python scripts/live/vrp_f_watch.py --quiet || true
# La CHIAVE DI SCALA del libro: la config dichiara una scala che il giornale non ha autorizzato?
# Il criterio che la autorizzo' passa ancora? Il PRODOTTO frac x scala x n_asset ha superato il
# tetto? Tre domande, tre azioni diverse. Non impedisce la modifica: la rende visibile entro 24h
# e attribuibile — una chiave di scala e' il parametro che si alza "solo un po'" dopo un mese
# buono, e lo Sharpe (invariante alla scala) non lo vedrebbe mai. SPEC-scale-key §6.4.
uv run python scripts/live/scale_watch.py --quiet || true
# I forward-monitor stanno registrando? Tre gate pre-registrati (27/09, 23/10, 24/10) si
# decidono su queste serie: un monitor fermo produce silenzio, e il silenzio sembra uno zero.
# Va DOPO tutti i monitor, altrimenti misura lo stato di ieri.
uv run python scripts/live/monitor_health.py --quiet || true
# Giornale di bordo. Gira alle 00:30 UTC -> chiude IERI (giorno completo) e apre OGGI.
# Giornale di bordo. Gira alle 00:30 UTC -> chiude IERI, che a quest'ora e' un giorno COMPLETO.
# Sola lettura: feed certificato, cron_book.log, trades.db. Il campo `nota` non lo tocca.
uv run python scripts/live/journal.py --giorno "$(date -u -d yesterday +%F)" --quiet || true
uv run python scripts/live/journal.py --quiet || true
# NON si apre piu' la voce di OGGI (tolto 2026-08-25). A quest'ora coprirebbe ~30 minuti e
# nessuno la aggiorna fino alla notte dopo: una pagina con TUTTE le sezioni, che si legge come
# una giornata e ne contiene mezz'ora. Chi vuole lo stato corrente lancia `journal.py` a mano
# (la pagina si dichiara PARZIALE nel titolo) o legge trades.db, che cron_book sincronizza ogni
# ora. Se un giorno servisse la voce sempre fresca, la strada e' RIGENERARLA ogni ora in
# cron_book, non congelarla qui.
# Analista di bordo: un modello scrive la prosa del giorno CHIUSO nel campo `analisi`.
# Scrive SOLO li': non tocca `nota` (operatore) ne' le sezioni misurate, e se non risponde
# la pagina resta senza analisi invece di mostrare quella di ieri. Costo: 1 chiamata/giorno.
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
# Revisione SETTIMANALE in sola lettura (dal 2026-09-09): un revisore diverso dall'autore
# (claude-fable-5-1) rilegge CLAUDE.md, config, crontab, commit, monitor_health, giornale e diari,
# e scrive docs/revisioni/<data>.md + una sintesi Telegram. Propone, non esegue: l'unico file
# scritto e' il rapporto. Cadenza: lunedi' 06:15 UTC — fuori dal minuto :00 (rate-limit) e dallo
# slot di release Deribit (martedi' 09:00), dopo il cron_daily delle 00:30 (giornale di domenica chiuso).
export PATH="/home/adriano/.local/bin:$PATH"
cd /opt/docker/PythagorasGoal || exit 1
mkdir -p logs
{
echo "===== $(date -u '+%Y-%m-%dT%H:%M:%SZ') cron_review ====="
uv run python scripts/live/revisione.py --quiet || true
echo "===== done $(date -u '+%H:%M:%SZ') ====="
} >> logs/cron_review.log 2>&1
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash
# Sorveglianza collaterale USDE (reward/depeg/quota) — v2.0.0+. GIORNALIERA alle 12:35 UTC:
# DOPO la finestra reward Deribit (~12:00), fuori dal minuto :00 (rate-limit per-IP, misura
# 2026-07-30), dal :25 (catena opzioni) e dal :47 (book). Sola lettura, mai blocca nulla.
export PATH="/home/adriano/.local/bin:$PATH"
cd /opt/docker/PythagorasGoal || exit 1
mkdir -p logs
{
echo "===== $(date -u '+%Y-%m-%dT%H:%M:%SZ') cron_usde ====="
uv run python scripts/live/usde_watch.py --quiet || true
echo "===== done $(date -u '+%H:%M:%SZ') ====="
} >> logs/cron_usde.log 2>&1
+42 -2
View File
@@ -4,6 +4,7 @@
uv run python scripts/live/analista.py --giorno 2026-08-21
uv run python scripts/live/analista.py --modello claude-opus-5
uv run python scripts/live/analista.py --secco # stampa e non salva
uv run python scripts/live/analista.py --no-telegram # non manda la notifica
NON tocca `nota` (dell'operatore) e non tocca nessuna sezione misurata: scrive SOLO in `analisi`.
Se il modello non risponde, l'analisi di ieri NON diventa quella di oggi: si registra l'errore
@@ -19,6 +20,7 @@ ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
from src.live import analista as A # noqa: E402
from src.live import notifier as N # noqa: E402
from src.live import journal as J # noqa: E402
from src.live import tradesdb as T # noqa: E402
@@ -48,7 +50,28 @@ def storico(con, g: date, n: int = GIORNI_STORICO) -> str:
return "\n".join(righe)
def scrivi(con, g: date, modello: str, secco: bool = False, verbose: bool = True) -> str:
def invia(con, g: date, voce: dict, analisi: str, stato: str, motivi: list[str],
verbose: bool = True) -> str:
"""Manda su Telegram e REGISTRA l'esito. Tre stati, non due: inviata / non-configurato /
fallita-con-motivo. Un invio perso che non lascia traccia e' peggio di un invio non fatto:
il giorno dopo non si distingue da 'non e' successo niente'."""
testo = A.per_telegram(voce, analisi, stato, motivi)
if not N.is_configured():
esito = "non configurato"
else:
# `tentativi=3`: il tasso misurato di invii persi su questo percorso e' 6,9% (2/29).
# Su un messaggio al giorno, un tentativo solo perde ~25 messaggi in un anno.
esito = "inviata" if N.send(testo, tentativi=3) else f"FALLITA — {N.ultimo_errore()}"
con.execute("UPDATE journal SET inviata_ts=?, inviata_stato=? WHERE giorno=?",
(T.ora(), esito, g.isoformat()))
con.commit()
if verbose:
print(f" telegram: {esito} ({len(testo)} caratteri)")
return esito
def scrivi(con, g: date, modello: str, secco: bool = False, verbose: bool = True,
telegram: bool = True) -> str:
voce = J.costruisci(con, g)
J.salva(con, voce) # le sezioni misurate prima, cosi' il modello le legge
pagina = J.rendi_markdown(con, voce)
@@ -87,13 +110,30 @@ def scrivi(con, g: date, modello: str, secco: bool = False, verbose: bool = True
(modello, T.ora(), stato, "; ".join(motivi) or None, g.isoformat()))
con.commit()
J.scrivi_file(con, voce)
if telegram:
invia(con, g, voce, testo, stato, motivi, verbose=verbose)
return stato
USO = """uso: analista.py [--giorno AAAA-MM-GG] [--modello NOME] [--secco] [--no-telegram] [--quiet]
(nessun flag) scrive l'analisi di OGGI nel campo `analisi` e manda la notifica
--giorno G analizza il giorno G
--modello M modello da usare (default: src.live.analista.MODELLO_DEFAULT)
--secco stampa e NON salva, niente Telegram
--no-telegram salva ma non manda la notifica
--quiet non stampa
SPENDE una chiamata al modello e MANDA un messaggio Telegram, salvo --secco/--no-telegram."""
if __name__ == "__main__":
from src.live.cli import valida
valida("analista.py", USO, flag=("--secco", "--no-telegram", "--quiet"),
con_valore=("--giorno", "--modello"))
con = T.connect()
g = date.fromisoformat(_arg("--giorno", datetime.now(timezone.utc).date().isoformat()))
st = scrivi(con, g, modello=_arg("--modello", A.MODELLO_DEFAULT),
secco="--secco" in sys.argv[1:], verbose="--quiet" not in sys.argv[1:])
secco="--secco" in sys.argv[1:], verbose="--quiet" not in sys.argv[1:],
telegram="--no-telegram" not in sys.argv[1:])
con.close()
sys.exit(0 if st in ("ok", "sospetta") else 1)
+182
View File
@@ -0,0 +1,182 @@
#!/usr/bin/env python
"""balance_watch.py — registra il BALANCE esatto per valuta. SOLA LETTURA, nessun ordine.
PERCHE' ESISTE. Il 2026-08-30 e' emerso che Deribit pubblica una APR anche per l'USDC
(3,4000%, contro 4,1071% dell'USDE): se fosse accreditata, il guadagno del passaggio a USDE
non sarebbe il tasso ma lo SPREAD (0,71 punti), e la pista USDE varrebbe ~$4,6/anno invece di
~$21. La domanda "l'USDC frutta davvero SUL NOSTRO conto?" non era rispondibile, perche':
* il gateway NON espone il Transaction Log (provati get_transaction_log,
get_settlement_history, get_deposits, get_transfers, get_interest_history: tutti 404
e' il debito #11, si ha solo cio' che il gateway espone);
* l'unica serie storica del conto e' `trades.db.equity`, che e' EQUITY **arrotondata a 2
decimali**: $0,13/giorno di interesse atteso ci sparisce dentro, e l'equity contiene
comunque il P&L non realizzato, che vale mille volte tanto.
Il `balance` no: cambia solo per P&L REALIZZATO, fee, funding, movimenti di fondi e interessi.
Su USDE (nessuna posizione denominata in USDE) e' gia' stato il rivelatore dei reward. Questa
serie fa lo stesso per l'USDC — ed e' il dato che mancava, non un'analisi nuova.
COME SI SEPARA L'INTERESSE DAL RESTO (P7: il numero si etichetta con la sua configurazione):
* P&L realizzato e fee -> noti ESATTAMENTE da `trades.db.fills`; ogni record porta il
conteggio dei fill dall'ultimo campione, cosi' una finestra sporca si riconosce invece di
essere mediata dentro;
* funding -> proporzionale al NOZIONALE, quindi **zero quando il libro e' FLAT**. Il libro e'
flat il 28% dei giorni: la lettura pulita arriva da sola, non va costruita;
* interesse -> proporzionale al BALANCE.
Una finestra con 0 fill e nozionale 0 misura l'interesse e basta. Le altre si etichettano.
MISURATO GIA' ORA (30/08, tre letture a 8 decimali durante le conversioni USDE): il balance USDC
si e' mosso **esattamente** dell'importo degli ordini (1555.84720621 - 10.002 = 1545.84520621,
poi -134.0268 = 1411.81840621). Su ~10 minuti con $568 di posizioni aperte: **ne' funding ne'
interesse arrivano in continuo sul balance**. Se l'interesse esiste, e' un accredito PERIODICO
come i reward USDE, che compaiono in un colpo intorno alle 12:00 UTC. Percio' la cadenza e' ORARIA:
serve a stringere la finestra intorno all'accredito, non a campionare di piu'.
uv run python scripts/live/balance_watch.py # un campione + lettura della serie
uv run python scripts/live/balance_watch.py --report # solo la lettura, nessun campione
"""
from __future__ import annotations
import argparse
import json
import sqlite3
import sys
from datetime import datetime, timezone
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
from src.live.deribit import DeribitRead # noqa: E402
STATE = ROOT / "data" / "live" / "balance_watch.jsonl"
TRADES_DB = ROOT / "data" / "live" / "trades.db"
VALUTE = ("USDC", "USDE", "BUIDL")
APR_ATTESE = {"USDC": 0.0340, "USDE": 0.042143, "BUIDL": 0.032018} # public/get_currencies
def fills_da(ts_iso: str | None) -> int | None:
"""Quanti fill dopo `ts_iso` (None se il DB non e' leggibile: non si inventa 0)."""
if ts_iso is None:
return None
try:
con = sqlite3.connect(f"file:{TRADES_DB}?mode=ro", uri=True)
try:
return con.execute("select count(*) from fills where ts_utc > ?", (ts_iso,)).fetchone()[0]
finally:
con.close()
except Exception:
return None
def campiona(c: DeribitRead) -> dict:
"""Un campione: balance per valuta (8 decimali), nozionale lordo, posizioni. Mai solleva."""
rec = {"ts": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), "valute": {}}
for cur in VALUTE:
try:
s = c.account_summary(cur)
rec["valute"][cur] = {"balance": float(s["balance"]), "equity": float(s["equity"])}
except Exception as e:
rec["valute"][cur] = {"errore": f"{type(e).__name__}"}
try:
pos = c.positions("USDC")
rec["nozionale_lordo"] = round(sum(abs(float(p.get("size") or 0)) for p in pos), 2)
rec["n_posizioni"] = sum(1 for p in pos if abs(float(p.get("size") or 0)) > 1)
except Exception:
rec["nozionale_lordo"], rec["n_posizioni"] = None, None
# INDICE usde_usdc, ORARIO (aggiunto 2026-09-02, debito §5.15). `usde_watch` lo registra una
# volta al giorno (12:35Z): troppo rado per il classificatore dei movimenti di capitale, che
# confronta due letture di equity a un'ora di distanza e oggi guarda solo BTC/ETH — con il
# 31% dell'equity in USDE, un depeg sarebbe scorporato come un PRELIEVO. Questa e' la serie
# che manca; il cablaggio nel bound viene dopo, quando ci sara' storia. None se non leggibile
# (P5: "non vedo" non e' "il peg tiene").
try:
from src.live.usde import prezzo_indice
rec["usde_usdc"] = prezzo_indice()
except Exception as e:
rec["usde_usdc"], rec["usde_usdc_errore"] = None, f"{type(e).__name__}"
return rec
def serie() -> list[dict]:
if not STATE.exists():
return []
out = []
for line in STATE.read_text().splitlines():
try:
out.append(json.loads(line))
except Exception:
pass
return out
def leggi(righe: list[dict]) -> None:
print(f" campioni: {len(righe)}"
+ (f" da {righe[0]['ts']} a {righe[-1]['ts']}" if righe else ""))
if len(righe) < 2:
print(" → serve almeno un secondo campione: la prima differenza e' la prima misura.")
return
print(f"\n {'finestra':>34s} {'ore':>5s} {'Δ USDC':>13s} {'Δ USDE':>13s} {'fill':>5s} {'lordo':>9s} lettura")
pulite = []
for a, b in zip(righe, righe[1:]):
ba, bb = a["valute"].get("USDC", {}), b["valute"].get("USDC", {})
ea, eb = a["valute"].get("USDE", {}), b["valute"].get("USDE", {})
if "balance" not in ba or "balance" not in bb:
continue
t0 = datetime.strptime(a["ts"], "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc)
t1 = datetime.strptime(b["ts"], "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc)
ore = (t1 - t0).total_seconds() / 3600
d_usdc = bb["balance"] - ba["balance"]
d_usde = (eb.get("balance", 0) - ea.get("balance", 0)) if "balance" in eb and "balance" in ea else None
nf = b.get("fills_da_ultimo")
lordo = b.get("nozionale_lordo")
pulita = (nf == 0) and (lordo is not None and lordo < 1)
nota = "PULITA (0 fill, libro flat) → il Δ USDC E' interesse" if pulita else (
"sporca: fill nella finestra" if nf else
"0 fill ma libro esposto → Δ = funding + interesse" if nf == 0 else "fill ignoti")
if pulita and ore > 0:
pulite.append((d_usdc, ba["balance"], ore))
print(f" {a['ts'][5:16]}{b['ts'][5:16]:>16s} {ore:5.1f} {d_usdc:+13.8f} "
f"{(f'{d_usde:+13.8f}' if d_usde is not None else ' n/d')} "
f"{('' if nf is None else nf):>5} {('' if lordo is None else f'${lordo:,.0f}'):>9} {nota}")
print()
if pulite:
tot_d = sum(d for d, _, _ in pulite)
tot_h = sum(h for _, _, h in pulite)
bal = sum(b * h for _, b, h in pulite) / tot_h
apr = (tot_d / bal) * (8760 / tot_h) if bal and tot_h else float("nan")
print(f" ⇒ FINESTRE PULITE: {len(pulite)}, {tot_h:.1f} ore, Δ totale {tot_d:+.8f} USDC "
f"su balance medio ${bal:,.2f}")
print(f" APR implicita {apr:+.4%} (attesa da public/get_currencies: "
f"{APR_ATTESE['USDC']:.4%})")
print(f" verdetto: {'FRUTTA' if tot_d > 1e-6 else 'NON frutta (Δ nullo su finestra pulita)'}")
else:
print(" ⇒ nessuna finestra PULITA ancora (serve il libro FLAT e 0 fill: succede il 28% dei giorni).")
print(" Nel frattempo le finestre a 0 fill misurano funding+interesse insieme.")
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--report", action="store_true", help="solo lettura, nessun campione nuovo")
a = ap.parse_args()
righe = serie()
if not a.report:
rec = campiona(DeribitRead())
rec["fills_da_ultimo"] = fills_da(righe[-1]["ts"] if righe else None)
STATE.parent.mkdir(parents=True, exist_ok=True)
with STATE.open("a") as f:
f.write(json.dumps(rec) + "\n")
righe.append(rec)
u = rec["valute"].get("USDC", {}); e = rec["valute"].get("USDE", {})
print(f" campione {rec['ts']}: USDC balance {u.get('balance')} · USDE {e.get('balance')}"
f" · lordo ${rec.get('nozionale_lordo')}")
print("=" * 78)
print(" BALANCE WATCH — l'USDC frutta sul nostro conto?")
print("=" * 78)
leggi(righe)
return 0
if __name__ == "__main__":
raise SystemExit(main())
+149 -39
View File
@@ -10,9 +10,24 @@ DOPPIO GATE DI SICUREZZA (entrambi necessari per inviare ordini reali):
Senza entrambi e' un DRY-RUN (stampa il piano, NON invia). Reconciliation dopo ogni ordine; log in
data/live/book_executions.jsonl.
CADENZA: SKH01 decide su griglia 230m -> questo script va lanciato ogni ~230 minuti con la feed
fresca all'ultima barra chiusa (NON il cron giornaliero, che mancherebbe gli ingressi). Gli exit di
SKH sono SOFTWARE (latenza fino a fine barra 230m); solo il disaster-SL (-30%) e' on-book.
CADENZA: ORARIA `47 * * * *` in `scripts/cron_book.sh` (il minuto e' spiegato li'). SKH01
decide su griglia 230m, ma il giro e' IDEMPOTENTE (riconcilia al target netto corrente; sotto
`min_order_usd` $5 -> HOLD). Girare piu' fitto della griglia costa solo MICRO-ORDINI di ri-taglia:
il target e' ricalcolato ogni ora sull'equity marcata, e 22 dei 48 ordini live sono |delta|<=$10
a segnale invariato spiccioli oggi, ma il deadband e' in valuta ASSOLUTA (regola C2: a capitale
maggiore un movimento dello 0,15% lo supera ogni ora). Compra due cose misurate: (1) gli
ingressi/uscite SOFTWARE di SKH01 arrivano con latenza <=1h CON LA FEED 5m FRESCA: se e' stantia
`livefeed.fresh_5m` ripiega in silenzio sul feed giornaliero (latenza ~1 giorno) e
`skh_feed_max_age_min` 30 ALLERTA, non blocca; (2) il disaster-SL rotolante (-30%, l'unico on-book)
viene CONTROLLATO ogni ora e ri-ancorato solo oltre la tolleranza di `ensure_disaster_sl` (stop a
>5% da quello piazzato, cioe' mark +5,263%/-4,762%, o taglia >10%) — con questa isteresi
(`r0823_sl_anchor.py`): a cadenza 1h in 7-8 anni BTC 0 scatti / ETH 1; a 4h BTC 2; a 24h ETH 5,
con 2 in 30 giorni. Lo stop siede quindi fra -33,5% e -26,5% dal mark corrente (0,665-0,735 x mark),
non a -30% dall'ultima ora: e' rotolante, non un massimo di perdita (CLAUDE.md §1).
🚨 NON "correggere" la cadenza a ~230 minuti: fino al 2026-09-02 questo docstring lo prescriveva,
ed era il docstring a essere sbagliato, non il cron (debito §5.7). `tests/test_book_cadenza.py`
tiene d'accordo docstring, `cron_book.sh` e crontab installata. NON il cron giornaliero: le entrate
di SKH01 le mancherebbe.
uv run python scripts/live/book_execute.py # DRY-RUN (piano, nessun ordine)
uv run python scripts/live/book_execute.py --execute # esegue SOLO se execution_enabled=true
@@ -30,9 +45,10 @@ import pandas as pd
PROJECT_ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(PROJECT_ROOT))
from src.live.book import book_report
from src.live.book import ScalaNonAutorizzata, book_report
from src.live.execution import DeribitTrader
from src.live.notifier import notify
from src.live.venue_probe import diagnose, errori_dal_report
CONFIG = PROJECT_ROOT / "config" / "live.json"
LOG_DIR = PROJECT_ROOT / "data" / "live"
@@ -77,7 +93,20 @@ def _run():
min_order = float(cfg["min_order_usd"])
sl_pct = float(cfg["disaster_sl_pct"])
r = book_report(live_feed=True) # target NETTO + conto/posizioni reali (feed SKH fresco)
try:
r = book_report(live_feed=True) # target NETTO + conto/posizioni reali (feed SKH fresco)
except ScalaNonAutorizzata as e:
# G1(c): NON si taglia al tetto. Un clamp silenzioso farebbe girare una config che dichiara
# un numero e un libro che ne esegue un altro. Si ferma, non invia, e allerta.
msg = (f"🚨 SCALA NON AUTORIZZATA — book_execute FERMO, nessun ordine inviato.\n{e}\n"
f"Ripara: riporta `book_scale_k` a un gradino di SCALA_LADDER dentro il tetto, "
f"oppure rimuovi la chiave (assente = 1,00 = libro di oggi).")
print(msg)
try:
notify(msg)
except Exception as ne: # P3: l'errore si registra dove lo si ingoia
print(f" ⚠️ allerta NON inviata: {type(ne).__name__}: {ne}")
return
equity = r["equity"]
print("=" * 88)
@@ -88,7 +117,15 @@ def _run():
print(f" modo : {mode}")
print(f" gate : execution_enabled={enabled} | --execute={want_execute}")
print(f" conto reale : ${r['real_equity']:,.2f}" if r["real_equity"] else f" conto: {r['eq_basis']}")
print(f" sizing base : ${equity:,.2f} | cap/asset ${r['cap_per_asset']:.0f} | min ${min_order:.0f} | disaster-SL -{sl_pct*100:.0f}%")
_sc = r.get("scala", 1.0)
_lordo = len(r["assets"]) * float(cfg.get("max_notional_per_asset_frac") or 0.0) * _sc
print(f" sizing base : ${equity:,.2f} | cap/asset ${r['cap_per_asset']:.0f} | "
f"min ${min_order:.0f} | disaster-SL -{sl_pct*100:.0f}%")
# La scala e' stampata SEMPRE, anche a 1,00: un parametro che muove il nozionale e non compare
# nel log e' invisibile proprio nel giro in cui e' cambiato (SPEC-scale-key §2.2).
print(f" scala libro : {_sc:.2f}x (tetto {r.get('leva_lorda_max', 1.25):.2f}x) | "
f"leva lorda max {_lordo:.3f}x dell'equity"
+ ("" if _sc != 1.0 else " [chiave assente o 1,00 = libro invariato]"))
print(f" ultima barra : {r['last_data']}\n")
if r.get("skh_error"): # SKH feed fallito -> book.py ha forzato flat IN SILENZIO
@@ -131,18 +168,35 @@ def _run():
# `online` e' falso quando il mark di BTC non viene da mainnet: la ragione sta in
# `mark_src` ("fallback close (<Eccezione>)") ma non veniva MAI stampata, quindi
# l'allerta diceva solo "conto offline" — vero e inutile. Stesso buco del feed SKH.
#
# DAL 2026-08-25 la riga dice anche DI CHI E' IL GUASTO (P4: *cosa* e *perche'*). Fino a
# ieri "conto offline" copriva due cause con azioni opposte: Deribit in manutenzione
# (attesa, rientra da sola, azione nessuna) e il nostro gateway rotto (4 dei 5 traceback
# in 63 giorni, ed e' l'unico pezzo riparabile). Vedi src/live/venue_probe.py.
srcs = "; ".join(f"{a['asset']}: {a.get('mark_src')}" for a in r["assets"])
d = diagnose(errori_dal_report(r))
print(f" conto non leggibile (offline) -> stop, non eseguo a cieco.\n mark: {srcs}")
print(f" diagnosi: {d.riga()}")
if do_execute:
notify("⚠️ BOOK LIVE — conto offline", {"nota": "salto l'esecuzione, non opero a cieco",
"mark": srcs[:300]})
# P9: un allarme MASSIMO speso per un evento ATTESO e' un allarme che non verra'
# letto il giorno che e' vero. `atteso` declassa la gravita', NON registra di meno.
notify(f"{d.gravita} BOOK LIVE — conto offline ({d.verdetto})",
{"nota": "salto l'esecuzione, non opero a cieco",
"perche": d.perche, "atteso": "si'" if d.atteso else "no",
"riparabile_da_noi": "si'" if d.riparabile_da_noi else "no",
"mark": srcs[:200], "prova": d.prova[:200]})
return
if r.get("pos_error"): # ONLINE ma posizione IGNOTA (read fallita -> assunta flat)
d = diagnose(errori_dal_report(r))
print(f" 🛑 POSIZIONE NON LEGGIBILE -> NON eseguo a cieco: {r['pos_error']}")
print(f" diagnosi: {d.riga()}")
if do_execute:
notify("🛑 BOOK LIVE — posizione non leggibile", {"error": r["pos_error"],
"nota": "salto l'esecuzione, non opero a cieco"})
notify(f"{d.gravita} BOOK LIVE — posizione non leggibile ({d.verdetto})",
{"error": r["pos_error"], "nota": "salto l'esecuzione, non opero a cieco",
"perche": d.perche, "atteso": "si'" if d.atteso else "no",
"riparabile_da_noi": "si'" if d.riparabile_da_noi else "no",
"prova": d.prova[:200]})
return
stale_days = _data_age_days(r.get("last_data"))
@@ -189,6 +243,13 @@ def _run():
trader = DeribitTrader() if do_execute else None
actions = []
# ISOLAMENTO PER ASSET (2026-08-25). Prima, un'eccezione dentro il corpo del ciclo risaliva
# fino a `main()` e uccideva il GIRO INTERO: il 2026-07-21 alle 09:00 UTC un 502 dentro
# `ensure_disaster_sl` su BTC ha fatto si' che ETH non venisse nemmeno guardato — niente
# ribilancio e, soprattutto, **nessuna verifica della sua protezione**. Un guasto su un asset
# non deve togliere la rete di sicurezza all'altro.
falliti: list[str] = []
scoperti: list[str] = []
for a in r["assets"]:
asset, inst = a["asset"], a["instrument"]
net, cur, mark = a["net_target"], a["position_usd"], a["mark"]
@@ -206,37 +267,66 @@ def _run():
print(f" {asset:<3} TP {a['tp_frac']:+.3f} · SKH {a['skh_sign']:+d}({sk_txt}) -> net ${net:+,.0f} "
f"| pos ${cur:+,.0f} -> {act}")
if do_execute and order is not None:
fills = trader.rebalance_signed(inst, net, mark, min_usd=min_order)
newpos = trader.position_usd(inst)
for f in fills:
print(f" -> {f.side.upper()} {f.filled:.4f} @ ${f.price or 0:,.1f} fee {f.fee_usdc:.5f} "
f"({'OK' if f.verified else 'NON VERIFICATO: ' + f.notes})")
# `ts_utc` = ORA VERA del fill. Fino al 2026-08-23 qui c'era la data della
# BARRA di segnale (`r['last_data']`): 19 righe su 19 a 00:00:00, e un trade
# registrato SEI GIORNI prima di essere eseguito (ETH 0.04 del 14/07, scritto
# 08/07). La barra resta, sotto il suo nome: `bar_ts`.
log_event(dict(ts_utc=datetime.now(timezone.utc).isoformat(timespec="seconds"),
bar_ts=str(pd.Timestamp(r['last_data'])), asset=asset, action=act,
side=f.side, filled=f.filled, price=f.price, fee=f.fee_usdc,
verified=f.verified, notes=f.notes, net_target=net, pos_after=newpos,
tp_frac=a["tp_frac"], skh_sign=a["skh_sign"]))
det = dict(asset=asset, side=f.side, amount=round(f.filled, 4), price=round(f.price or 0, 1),
fee=round(f.fee_usdc, 5), net=round(net, 0), pos_after=round(newpos, 0))
notify(f"✅ BOOK {act}" if f.verified else "⚠️ BOOK ORDINE NON VERIFICATO",
det if f.verified else {**det, "notes": f.notes})
print(f" reconcile: pos ${newpos:,.0f}")
if do_execute:
ds = trader.ensure_disaster_sl(inst, sl_pct) # bracket su posizione NETTA (adatta long/short)
print(f" disaster-SL: {ds.get('state')}" + (f" @ ${ds['stop']:,.1f}" if ds.get("stop") else ""))
if ds.get("state") == "placed":
notify("🛡️ BOOK disaster-SL piazzato", {"asset": asset, "stop": round(ds.get("stop") or 0, 1),
"amount": round(ds.get("amount") or 0, 4)})
elif ds.get("state") == "place-failed":
notify("⚠️ BOOK disaster-SL FALLITO", {"asset": asset, "notes": ds.get("notes")})
try:
if do_execute and order is not None:
fills = trader.rebalance_signed(inst, net, mark, min_usd=min_order)
newpos = trader.position_usd(inst)
for f in fills:
print(f" -> {f.side.upper()} {f.filled:.4f} @ ${f.price or 0:,.1f} fee {f.fee_usdc:.5f} "
f"({'OK' if f.verified else 'NON VERIFICATO: ' + f.notes})")
# `ts_utc` = ORA VERA del fill. Fino al 2026-08-23 qui c'era la data della
# BARRA di segnale (`r['last_data']`): 19 righe su 19 a 00:00:00, e un trade
# registrato SEI GIORNI prima di essere eseguito (ETH 0.04 del 14/07, scritto
# 08/07). La barra resta, sotto il suo nome: `bar_ts`.
log_event(dict(ts_utc=datetime.now(timezone.utc).isoformat(timespec="seconds"),
bar_ts=str(pd.Timestamp(r['last_data'])), asset=asset, action=act,
side=f.side, filled=f.filled, price=f.price, fee=f.fee_usdc,
verified=f.verified, notes=f.notes, net_target=net, pos_after=newpos,
tp_frac=a["tp_frac"], skh_sign=a["skh_sign"]))
det = dict(asset=asset, side=f.side, amount=round(f.filled, 4), price=round(f.price or 0, 1),
fee=round(f.fee_usdc, 5), net=round(net, 0), pos_after=round(newpos, 0))
notify(f"✅ BOOK {act}" if f.verified else "⚠️ BOOK ORDINE NON VERIFICATO",
det if f.verified else {**det, "notes": f.notes})
print(f" reconcile: pos ${newpos:,.0f}")
if do_execute:
ds = trader.ensure_disaster_sl(inst, sl_pct) # bracket su posizione NETTA (adatta long/short)
stato = ds.get("state")
print(f" disaster-SL: {stato}" + (f" @ ${ds['stop']:,.1f}" if ds.get("stop") else ""))
if stato == "placed":
notify("🛡️ BOOK disaster-SL piazzato", {"asset": asset, "stop": round(ds.get("stop") or 0, 1),
"amount": round(ds.get("amount") or 0, 4)})
elif stato == "place-failed":
notify("⚠️ BOOK disaster-SL FALLITO", {"asset": asset, "notes": ds.get("notes")})
elif stato == "naked":
# NON e' "non sono riuscito a proteggere": e' "HO TOLTO la protezione e non
# sono riuscito a rimetterla". Posizione aperta senza stop on-book -> gravita'
# massima, e MAI declassata da P9: una posizione scoperta non e' mai attesa.
scoperti.append(asset)
print(f" 🚨 {asset}: POSIZIONE SCOPERTA — {ds.get('notes')}")
notify("🚨 BOOK — POSIZIONE SCOPERTA (disaster-SL rimosso e non ripiazzato)",
{"asset": asset, "stop_voluto": round(ds.get("stop") or 0, 1),
"amount": round(ds.get("amount") or 0, 4),
"azione": "ripiazzare il bracket a mano, oppure chiudere la posizione",
"notes": str(ds.get("notes"))[:300]})
except Exception as e: # noqa: BLE001 — isolamento per asset: l'altro deve continuare
d = diagnose(errori_dal_report(r) + [f"{type(e).__name__}: {e}"])
falliti.append(asset)
act = f"{act} [FALLITO]"
print(f" 🛑 {asset}: giro fallito, PASSO ALL'ASSET SUCCESSIVO -> {type(e).__name__}: {e}")
print(f" diagnosi: {d.riga()}")
if do_execute:
notify(f"{d.gravita} BOOK — asset {asset} fallito ({d.verdetto})",
{"asset": asset, "error": f"{type(e).__name__}: {e}"[:200],
"perche": d.perche, "atteso": "si'" if d.atteso else "no",
"riparabile_da_noi": "si'" if d.riparabile_da_noi else "no",
"nota": "gli altri asset del book sono stati comunque elaborati"})
actions.append(act)
print()
if scoperti:
print(f" 🚨 POSIZIONI SCOPERTE (nessun disaster-SL on-book): {', '.join(scoperti)}")
if falliti:
print(f" 🛑 asset falliti in questo giro: {', '.join(falliti)}")
if not do_execute:
print(" => DRY-RUN: nessun ordine inviato." +
("" if enabled else " Per armare: config/live.json execution_enabled=true + --execute."))
@@ -244,15 +334,35 @@ def _run():
print(" => Nessuna azione: conto gia' al target netto del book.")
else:
print(" => Esecuzione completata (vedi data/live/book_executions.jsonl).")
# Uscita non-zero se il giro e' stato DEGRADATO: il log del cron deve poterlo contare senza
# rileggere la prosa. Non cambia nulla operativamente (cron_book.sh non ha `set -e`).
if scoperti or falliti:
sys.exit(2)
def main():
try:
_run()
except Exception as e:
notify("🛑 BOOK LIVE — ERRORE", {"error": f"{type(e).__name__}: {e}"})
# Anche l'ultima rete porta la DIAGNOSI, non solo il tipo d'errore: e' l'allerta che
# arriva quando tutto il resto non ha funzionato, ed e' proprio li' che serve sapere se
# riaprire il gateway o aspettare che rientri il venue (P4).
d = diagnose([f"{type(e).__name__}: {e}"])
notify(f"{d.gravita} BOOK LIVE — ERRORE ({d.verdetto})",
{"error": f"{type(e).__name__}: {e}"[:250], "perche": d.perche,
"atteso": "si'" if d.atteso else "no",
"riparabile_da_noi": "si'" if d.riparabile_da_noi else "no"})
raise
USO = """uso: book_execute.py [--execute]
uv run python scripts/live/book_execute.py # DRY-RUN (piano, nessun ordine)
uv run python scripts/live/book_execute.py --execute # esegue SOLO se execution_enabled=true
Dettaglio nel docstring in testa al file."""
if __name__ == "__main__":
from src.live.cli import valida
valida("book_execute.py", USO, flag=("--execute",), con_valore=())
main()
+8
View File
@@ -93,5 +93,13 @@ def main() -> None:
print(" regime QUIET: carry non raccoglibile (coerente con luglio 2026).")
USO = """uso: cc01_regime_watch.py
(nessun flag) sorveglia il regime di CC01 e stampa lo stato.
Dettaglio nel docstring in testa al file."""
if __name__ == "__main__":
from src.live.cli import valida
valida("cc01_regime_watch.py", USO)
main()
+329
View File
@@ -0,0 +1,329 @@
#!/usr/bin/env python
"""cuscino_watch.py — sorveglianza ORARIA del cuscino USDC di regolamento. PUO' INVIARE ORDINI.
PERCHE' ESISTE (debito §5.17, issue #3). Dal 06/09 il 68,7% dell'equity e' USDE. P&L e funding dei
perp USDC-lineari si regolano in USDC: una perdita del libro consuma il cuscino e fa salire la quota
USDE DA SOLA, senza un ordine. `usde_watch` allerta solo su `quota > quota_max_frac` (0,85): a
68,7% lo slack sul cuscino era ~$41-58, e un saldo USDC negativo costa lo 0,05%/GIORNO (18,25%/anno,
4,3x la resa che l'USDE compra). Nessuno guardava la grandezza giusta.
LA MISURA (decisione dell'operatore, 10/09): l'EQUITY USDC del conto balance piu' P&L non
realizzato perche' e' quella che risponde del regolamento; il balance vedrebbe il buco solo a
trade chiuso. Il cuscino richiesto e' DERIVATO da config/live.json e src/live/book.py tramite
`usde.cuscino_richiesto_usd` (n_asset x frac x disaster_sl_pct x equity totale): la stessa formula
che `usde_convert` applica a ogni piano un sorvegliante che ridichiara il proprio bersaglio non
sta controllando niente (P1, 5 occorrenze nel progetto).
QUATTRO STATI SULLO SLACK (= equity USDC cuscino), in frazioni del cuscino lette da config
(`usde.bande_cuscino`: preavviso 0,10 · margine 0,20 · riacquisto 0,40 P5, P1):
* SCOPERTO slack < 0: 🚨 alla transizione, e VENDITA automatica di USDE fino alla quota di
ripristino q* = usde.quota_ripristino() = 1 0,30 × 1,20 = 0,64 (cuscino + 20%).
* PREAVVISO 0 <= slack < 10% del cuscino: alla transizione. Latenza comprata apposta.
* OK 10% <= slack <= 40%: niente.
* ECCEDENTE slack > 40% del cuscino: ACQUISTO automatico di USDE fino allo stesso q* (issue #7,
decisione dell'operatore 10/09: «riportarla a quota in autonomia»). E' cio' che
riporta la quota a bersaglio dopo un bonifico (+0,7 × importo di slack) o dopo un
tratto di guadagni del libro, senza che nessuno se ne ricordi. 📌 alla transizione.
* BLIND conto non leggibile: alla transizione. "Non vedo" non e' "va tutto bene".
ISTERESI: vendita sotto 0, acquisto sopra 0,40, entrambe verso 0,20. Per oscillare lo slack deve
muoversi di ±0,20 × cuscino = ±6% dell'equity, e siccome slack = 0,7·USDC 0,3·USDE, servono
±8,6% di equity USDC (~$380 oggi); un giro costa ~6 bps sull'importo mosso. Soglie DICHIARATE, non ottimizzate (M8). Il vecchio `quota_target` 0,70
lasciava slack ZERO per costruzione: a quella quota ogni ora in perdita avrebbe venduto.
LE GUARDIE DELLA RICONVERSIONE (tutte dichiarate qui):
* `execution_enabled` in config/live.json deve essere true lo stesso interruttore del libro:
disarmare il libro disarma anche questo, senza un secondo posto da ricordare.
* NON si converte sotto `depeg_warn`, in nessun verso: vendere USDE sotto la pari cristallizza
la perdita del collaterale, comprarne mentre scivola e' peggio; li' decide l'operatore.
* al massimo UN tentativo ogni RIPRISTINO_MIN_ORE (6h), in qualunque verso: un tentativo che si
ripete ogni ora e' spam, e un sorvegliante che si ripete si impara a ignorare (P14).
* dopo un tentativo FALLITO da ECCEDENTE si riprova dopo RITENTATIVO_FALLITO_ORE (24h): USDC
fermo non e' un rischio, e un guasto persistente (tetto del venue tornato, book illeggibile)
diventerebbe 4 Telegram al giorno per sempre (revisione fable 10/09). Da SCOPERTO il ritmo
resta 6h: li' il fallimento e' un rischio che costa 0,05%/giorno.
* `usde_convert` porta le proprie guardie (banda di prezzo, book leggibile, tetto HARD sulla
quota): questo script NON le duplica, le eredita lanciando lo script vero (P15: la regola si
prova contro il codice che la esegue).
* `--secco`: niente ordini, niente Telegram, NIENTE riga nel registro: stampa cosa FAREBBE.
* niente lock: un `usde_convert --esegui` lanciato a mano nello stesso minuto del cron (:53)
puo' vendere due volte verso bersagli diversi. Limite dichiarato (D5), non riparato.
TRASPORTO (lezione del debito §5.2): il marcatore "gia' detto" e' l'esito di `notify` scritto nel
record (`allertato`); se l'invio fallisce la transizione si ripete al giro dopo invece di andare
persa per l'episodio intero.
La serie data/live/cuscino_watch.jsonl e' dentro il perimetro di backup ed e' sorvegliata da
monitor_health (un watch fermo = nessuno guarda il cuscino, e il silenzio si legge come zero).
uv run python scripts/live/cuscino_watch.py # un giro: legge, giudica, agisce
uv run python scripts/live/cuscino_watch.py --quiet # stampa solo se cambia qualcosa
uv run python scripts/live/cuscino_watch.py --secco # nessun ordine, nessun Telegram
"""
from __future__ import annotations
import json
import subprocess
import sys
from datetime import datetime, timezone
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
from src.live import usde as U # noqa: E402
from src.live.notifier import notify # noqa: E402
STATE = ROOT / "data" / "live" / "cuscino_watch.jsonl"
CONVERT = ROOT / "scripts" / "live" / "usde_convert.py"
# Le bande (preavviso / margine / riacquisto) NON stanno qui: si leggono da config via
# usde.bande_cuscino() a ogni giro (P1). Il margine e' il DOPPIO del preavviso per costruzione
# dell'ordine imposto (revisione fable 10/09: a margine == preavviso ogni 🚨 era seguito da un ⚠️).
RIPRISTINO_MIN_ORE = 6.0 # un tentativo di conversione (in qualunque verso) ogni 6 ore
RITENTATIVO_FALLITO_ORE = 24.0 # da ECCEDENTE, dopo un fallimento: un tentativo al giorno
TIMEOUT_CONVERT_S = 900 # usde_convert spazia gli ordini di 12s: 60 ordini = 12 min
def leggi(path: Path = STATE) -> list[dict]:
if not path.exists():
return []
out = []
for ln in path.read_text().splitlines():
ln = ln.strip()
if ln:
out.append(json.loads(ln))
return out
def giudica(eq_usdc: float | None, equity_tot: float | None, cuscino: float | None,
bande: dict | None = None) -> dict:
"""PURA. -> stato, slack, soglie in dollari. `None` in ingresso = BLIND. `bande` = le frazioni
del cuscino (default: config, via usde.bande_cuscino)."""
if eq_usdc is None or equity_tot is None or cuscino is None:
return dict(stato="BLIND", slack=None, preavviso_usd=None, riacquisto_usd=None)
try:
b = bande or U.bande_cuscino()
except Exception as e: # noqa: BLE001 — config rotta = "non vedo", non un crash
return dict(stato="BLIND", slack=None, preavviso_usd=None, riacquisto_usd=None,
motivo=f"bande del cuscino non leggibili: {e}")
slack = eq_usdc - cuscino
preavviso, riacquisto = b["preavviso"] * cuscino, b["riacquisto"] * cuscino
if slack < 0:
stato = "SCOPERTO"
elif slack < preavviso:
stato = "PREAVVISO"
elif slack > riacquisto:
stato = "ECCEDENTE"
else:
stato = "OK"
return dict(stato=stato, slack=round(slack, 2), preavviso_usd=round(preavviso, 2),
riacquisto_usd=round(riacquisto, 2))
AZIONE = {"SCOPERTO": "vendita", "ECCEDENTE": "acquisto"} # gli stati che convertono
def transizione(prev: dict | None, stato: str) -> bool:
"""PURA. Si allerta se lo stato e' cambiato, O se l'ultima allerta per questo stato non e'
partita (`allertato` False): un invio fallito non consuma la transizione (debito §5.2)."""
if stato == "OK":
return False
if prev is None or prev.get("stato") != stato:
return True
return not bool(prev.get("allertato"))
def puo_riconvertire(prev_records: list[dict], now_ms: int, execution_enabled: bool,
px: float | None, depeg_warn: float,
min_ore: float = RIPRISTINO_MIN_ORE, stato: str = "SCOPERTO",
dopo_fallito_ore: float = RITENTATIVO_FALLITO_ORE) -> tuple[bool, str]:
"""PURA. Le guardie della conversione automatica. -> (si', motivo se no). ⚠️ il motivo finisce
in un'allerta: niente '<' (Telegram HTML) — l'escape nel sink lo copre, ma qui non serve."""
if not execution_enabled:
return False, "execution_enabled=false in config/live.json (il libro e' disarmato: anche questo)"
if px is None:
return False, "prezzo USDE non leggibile: non si vende al buio (P5)"
if px < depeg_warn:
return False, f"USDE {px:.4f} sotto depeg_warn {depeg_warn}: ne' vendere (cristallizza) ne' comprare (scivola) — decide l'operatore"
# contano solo i tentativi VERI (subprocess lanciato): un record con `tentata=False` — prezzo
# illeggibile, --secco, interruttore spento — non consuma il budget (revisione fable 10/09)
ultimo = [r for r in prev_records if (r.get("riconversione") or {}).get("tentata")]
if ultimo:
ore = (now_ms - int(ultimo[-1]["ts"])) / 3_600_000
fallito = not (ultimo[-1].get("riconversione") or {}).get("ok")
soglia = dopo_fallito_ore if (fallito and stato == "ECCEDENTE") else min_ore
if ore < soglia:
return False, (f"ultimo tentativo {ore:.1f}h fa, sotto le {soglia:.0f}h"
+ (" (era FALLITO: da ECCEDENTE si riprova una volta al giorno)" if soglia == dopo_fallito_ore else "")
+ ": non si insiste")
return True, ""
def riconverti(quota: float, timeout_s: float = TIMEOUT_CONVERT_S) -> dict:
"""Lancia lo script VERO con le sue guardie. Ritorna esito + coda dell'output (P3)."""
cmd = [sys.executable, str(CONVERT), "--quota", f"{quota:.4f}", "--esegui"]
try:
r = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout_s, cwd=str(ROOT))
coda = (r.stdout + r.stderr).strip().splitlines()[-12:]
return dict(rc=r.returncode, ok=r.returncode == 0, coda=coda, cmd=" ".join(cmd[1:]))
except subprocess.TimeoutExpired:
return dict(rc=None, ok=False, coda=[f"timeout dopo {timeout_s:.0f}s"], cmd=" ".join(cmd[1:]))
except Exception as e: # noqa: BLE001 — si registra, non si ingoia
return dict(rc=None, ok=False, coda=[f"{type(e).__name__}: {e}"], cmd=" ".join(cmd[1:]))
def _safe_client():
try:
from src.live.deribit import DeribitRead
return DeribitRead()
except Exception:
return None
def _execution_enabled() -> bool:
try:
return bool(json.loads((ROOT / "config" / "live.json").read_text()).get("execution_enabled"))
except Exception:
return False
def main() -> int:
quiet, secco = "--quiet" in sys.argv, "--secco" in sys.argv
c = U.cfg()
records = leggi()
prev = records[-1] if records else None
now = datetime.now(timezone.utc)
now_ms = int(now.timestamp() * 1000)
client = _safe_client()
eq_usdc = eq_usde = None
motivo_blind = None
if client is None:
motivo_blind = "gateway non raggiungibile"
else:
try:
eq_usdc = float(client.account_summary("USDC")["equity"])
except Exception as e:
motivo_blind = f"conto USDC non leggibile ({type(e).__name__})"
try:
eq_usde = float(client.account_summary("USDE").get("equity") or 0)
except Exception as e:
motivo_blind = motivo_blind or f"conto USDE non leggibile ({type(e).__name__})"
px, px_fonte = U.prezzo(client)
equity_tot = cuscino = come = None
if eq_usdc is not None and eq_usde is not None:
usd, _ = U.valuta(eq_usde, px)
equity_tot = eq_usdc + usd
cuscino, come = U.cuscino_richiesto_usd(equity_tot)
try:
bande = U.bande_cuscino(c)
except Exception as e: # noqa: BLE001 — si registra e si allerta, non si crasha (P5)
bande, motivo_blind = None, f"config usde: {e}"
g = giudica(eq_usdc, equity_tot, cuscino, bande)
stato = g["stato"]
if stato == "BLIND" and g.get("motivo"):
motivo_blind = motivo_blind or g["motivo"]
rec = dict(ts=now_ms, data=now.strftime("%Y-%m-%dT%H:%M:%SZ"), stato=stato,
motivo_blind=motivo_blind, eq_usdc=eq_usdc, eq_usde=eq_usde, px=px,
px_fonte=px_fonte, equity_tot=equity_tot, cuscino=cuscino, cuscino_come=come,
slack=g["slack"], preavviso_usd=g["preavviso_usd"], riacquisto_usd=g["riacquisto_usd"],
bande=bande, allertato=None, allerta_errore=None, riconversione=None, secco=secco)
# --- conversione automatica: SCOPERTO vende, ECCEDENTE compra, stesso bersaglio q* ---
if stato in AZIONE:
ok, perche = puo_riconvertire(records, now_ms, _execution_enabled(), px, c["depeg_warn"], stato=stato)
quota = U.quota_ripristino(bande["margine"])
if not ok:
rec["riconversione"] = dict(tentata=False, verso=AZIONE[stato], quota=quota, motivo=perche)
elif secco:
rec["riconversione"] = dict(tentata=False, verso=AZIONE[stato], quota=quota,
motivo=f"--secco: avrei lanciato usde_convert --quota {quota:.4f} --esegui ({AZIONE[stato]})")
else:
esito = riconverti(quota)
rec["riconversione"] = dict(tentata=True, verso=AZIONE[stato], quota=quota, **esito)
# --- allarmi: alla transizione, marcatore scritto con l'esito dell'invio ---
# ⚠️ Telegram e' in parse_mode HTML: un '<' nudo nel testo fa fallire l'invio (successo al
# primo giro vero, 13:53Z del 10/09: «(< 10% del cuscino)»). Qui non si scrive mai '<'.
def _esito_azione(r: dict) -> str:
if r.get("tentata"):
return (f"{r['verso']} " + ("ESEGUITA" if r.get("ok") else "FALLITA")
+ f" (quota bersaglio {r['quota']:.0%})")
return f"{r.get('verso', 'conversione')} NON tentata: {r.get('motivo')}"
if transizione(prev, stato):
if secco:
rec["allertato"] = False
elif stato == "SCOPERTO":
rec["allertato"] = notify("🚨 CUSCINO USDC SCOPERTO", {
"USDC equity": f"${eq_usdc:,.2f}", "cuscino richiesto": f"${cuscino:,.2f} ({come})",
"slack": f"${g['slack']:+,.2f}", "azione": _esito_azione(rec["riconversione"] or {}),
"nota": "un USDC negativo costa 0,05%/giorno; il libro si regola in USDC"}, tentativi=3)
elif stato == "PREAVVISO":
rec["allertato"] = notify("⚠️ cuscino USDC in esaurimento", {
"USDC equity": f"${eq_usdc:,.2f}", "cuscino richiesto": f"${cuscino:,.2f}",
"slack": f"${g['slack']:+,.2f} (sotto il {bande['preavviso']:.0%} del cuscino)",
"nota": f"sotto zero vende USDE da solo fino a quota {U.quota_ripristino(bande['margine']):.0%}"})
elif stato == "ECCEDENTE":
rec["allertato"] = notify("📌 cuscino USDC eccedente: riacquisto USDE", {
"USDC equity": f"${eq_usdc:,.2f}", "cuscino richiesto": f"${cuscino:,.2f}",
"slack": f"${g['slack']:+,.2f} (sopra il {bande['riacquisto']:.0%} del cuscino)",
"azione": _esito_azione(rec["riconversione"] or {})})
elif stato == "BLIND":
rec["allertato"] = notify("⚠️ cuscino_watch BLIND", {"motivo": motivo_blind or "?",
"nota": "'non vedo' non e' 'va tutto bene' (P5)"})
elif rec["riconversione"] and rec["riconversione"].get("tentata") and not secco:
# non e' una transizione ma e' un ordine mandato: si dice sempre
r = rec["riconversione"]
rec["allertato"] = notify(f"📌 cuscino USDC: {r['verso']} " + ("eseguita" if r["ok"] else "FALLITA"),
{"quota bersaglio": f"{r['quota']:.0%}", "esito": " | ".join(r["coda"][-3:])})
if rec["allertato"] is False and not secco:
from src.live.notifier import ultimo_errore
rec["allerta_errore"] = ultimo_errore() # P3: si registra dove si ingoia
# --secco e' secco ANCHE sul registro: un giro a mano non deve diventare il `prev` del cron,
# ne' far sembrare vivo un monitor fermo (revisione fable 10/09)
if not secco:
STATE.parent.mkdir(parents=True, exist_ok=True)
with STATE.open("a") as fh:
fh.write(json.dumps(rec) + "\n")
cambiato = prev is None or prev.get("stato") != stato or rec["riconversione"] is not None
if not quiet or cambiato:
print("=" * 78)
print(f" CUSCINO WATCH — {rec['data']} {'(SECCO)' if secco else ''}")
print("=" * 78)
if stato == "BLIND":
print(f" stato : BLIND — {motivo_blind}")
else:
print(f" conto : USDC equity ${eq_usdc:,.2f} + USDE {eq_usde:,.2f} @ {px if px else 'n/d'}"
f" ({px_fonte}) = ${equity_tot:,.2f}")
print(f" cuscino : ${cuscino:,.2f} richiesti ({come})")
print(f" slack : ${g['slack']:+,.2f} (preavviso sotto ${g['preavviso_usd']:,.2f},"
f" riacquisto sopra ${g['riacquisto_usd']:,.2f}, bersaglio quota {U.quota_ripristino(bande['margine']):.0%})")
print(f" stato : {stato}")
if rec["riconversione"]:
r = rec["riconversione"]
print(f" {r.get('verso', 'conversione'):<12}: {'TENTATA' if r.get('tentata') else 'NON tentata'} — quota {r['quota']:.4f}"
+ (f"{r.get('motivo')}" if r.get("motivo") else ""))
for ln in r.get("coda", []):
print(f" {ln}")
if rec["allertato"] is not None:
print(f" allerta : {'inviata' if rec['allertato'] else 'NON inviata (si ripete al giro dopo)'}"
+ (f"{rec['allerta_errore']}" if rec.get("allerta_errore") else ""))
return 0
USO = """uso: cuscino_watch.py [--quiet] [--secco]
cuscino_watch.py sorveglianza ORARIA del cuscino USDC; sotto zero vende USDE, sopra la banda ricompra.
uv run python scripts/live/cuscino_watch.py # un giro: legge, giudica, agisce
uv run python scripts/live/cuscino_watch.py --quiet # stampa solo se cambia qualcosa
uv run python scripts/live/cuscino_watch.py --secco # nessun ordine, nessun Telegram
Dettaglio nel docstring in testa al file."""
if __name__ == "__main__":
from src.live.cli import valida
valida("cuscino_watch.py", USO, flag=("--quiet", "--secco"), con_valore=())
sys.exit(main())
+10
View File
@@ -144,5 +144,15 @@ def main() -> int:
return 0
USO = """uso: edge_watch.py [--quiet]
edge_watch.py sorveglianza dell'EDGE del book live. Riporta e allerta; non tocca nulla.
uv run python scripts/live/edge_watch.py # report
uv run python scripts/live/edge_watch.py --quiet # solo se qualcosa scatta (cron)
Dettaglio nel docstring in testa al file."""
if __name__ == "__main__":
from src.live.cli import valida
valida("edge_watch.py", USO, flag=("--quiet",), con_valore=())
raise SystemExit(main())
+10
View File
@@ -252,5 +252,15 @@ def main() -> int:
return 2 if r["level"] == "AZIONE" else 0
USO = """uso: fee_watch.py [--quiet]
fee_watch.py sorveglia lo schema fee di Deribit e applica la regola DECISA IN ANTICIPO.
uv run python scripts/live/fee_watch.py # report
uv run python scripts/live/fee_watch.py --quiet # stampa/allerta solo se qualcosa cambia
Dettaglio nel docstring in testa al file."""
if __name__ == "__main__":
from src.live.cli import valida
valida("fee_watch.py", USO, flag=("--quiet",), con_valore=())
raise SystemExit(main())
+12
View File
@@ -41,7 +41,19 @@ def una(con, g: date, nota: str | None = None, verbose: bool = True) -> Path:
return f
USO = """uso: journal.py [--giorno AAAA-MM-GG] [--backfill N] [--nota "testo"] [--quiet]
(nessun flag) scrive la voce di OGGI (pagina marcata PARZIALE: il giorno non e' chiuso)
--giorno G scrive la voce del giorno G
--backfill N ricostruisce gli ultimi N giorni, dal piu' vecchio
--nota "..." annota il giorno (campo dell'operatore, mai riscritto da un ricalcolo)
--quiet non stampa la riga di riepilogo
SCRIVE: una riga nel DB e una pagina in docs/journal/. Sola lettura su feed, log e venue."""
if __name__ == "__main__":
from src.live.cli import valida
valida("journal.py", USO, flag=("--quiet",), con_valore=("--giorno", "--backfill", "--nota"))
# sincronizzare PRIMA: altrimenti la sezione 'Libro' (letta dal log) e la sezione 'P&L'
# (letta dal DB) descrivono due istanti diversi e la pagina si contraddice da sola.
sys.path.insert(0, str(ROOT / "scripts" / "live"))
+9
View File
@@ -137,5 +137,14 @@ def main():
raise
USO = """uso: live_execute.py [--execute]
uv run python scripts/live/live_execute.py # DRY-RUN (piano, nessun ordine)
uv run python scripts/live/live_execute.py --execute # esegue SOLO se execution_enabled=true
Dettaglio nel docstring in testa al file."""
if __name__ == "__main__":
from src.live.cli import valida
valida("live_execute.py", USO, flag=("--execute",), con_valore=())
main()
+10
View File
@@ -74,5 +74,15 @@ def main():
(f"{len(r['orders'])} ordine/i costruito/i sopra." if r["orders"] else "Target flat: 0 ordini."))
USO = """uso: live_trend.py [--equity V] [--no-net]
uv run python scripts/live/live_trend.py # shadow su mainnet reale
uv run python scripts/live/live_trend.py --equity 2000 # forza la base di sizing
uv run python scripts/live/live_trend.py --no-net # offline: solo matematica + parita'
Dettaglio nel docstring in testa al file."""
if __name__ == "__main__":
from src.live.cli import valida
valida("live_trend.py", USO, flag=("--no-net",), con_valore=("--equity",))
main()
+9
View File
@@ -88,5 +88,14 @@ def main():
print(" Validato: invio ordine reale, fill, fee reali, reconciliation, ritorno a flat.")
USO = """uso: microtest.py [--live]
uv run python scripts/live/microtest.py # DRY-RUN: nessun ordine inviato
uv run python scripts/live/microtest.py --live # invia il round-trip REALE
Dettaglio nel docstring in testa al file."""
if __name__ == "__main__":
from src.live.cli import valida
valida("microtest.py", USO, flag=("--live",), con_valore=())
main()
+10
View File
@@ -55,5 +55,15 @@ def main() -> int:
return 0
USO = """uso: monitor_health.py [--quiet]
monitor_health.py i forward-monitor stanno registrando? Riporta e allerta, non tocca nulla.
uv run python scripts/live/monitor_health.py # report
uv run python scripts/live/monitor_health.py --quiet # stampa/allerta solo se scatta
Dettaglio nel docstring in testa al file."""
if __name__ == "__main__":
from src.live.cli import valida
valida("monitor_health.py", USO, flag=("--quiet",), con_valore=())
raise SystemExit(main())
+13
View File
@@ -18,6 +18,7 @@ sys.path.insert(0, str(PROJECT_ROOT))
import numpy as np, pandas as pd
from src.portfolio.sleeves import _tp01_returns, _tp01_positions
from src.portfolio.gtaa import gtaa_returns, gtaa_weights
from src.live import paper_guard as PG
STATE_DIR = PROJECT_ROOT / "data" / "paper_combo"
STATE = STATE_DIR / "state.json"
@@ -77,6 +78,10 @@ def advance():
return st
last = pd.Timestamp(st["last"])
nn = naked[naked.index > last]; gg = guard[guard.index > last]
# SOLO giorni CHIUSI. L'audit 22/08 ha trovato questo monitor sano PER CASO (aspetta la
# chiusura di una borsa, non per progetto): il filtro lo rende sano PER COSTRUZIONE.
m = PG.indice_chiuso(nn.index, PG.MS_1D)
nn, gg = nn[m], gg[PG.indice_chiuso(gg.index, PG.MS_1D)]
if len(nn):
e = st["equity"]; pk = st["peak"]; dd = st["max_dd"]
eg = st["equity_g"]; pkg = st["peak_g"]; ddg = st["max_dd_g"]; lines = []
@@ -113,5 +118,13 @@ def main():
print(f" GTAA (IB, asof {asof}): " + ", ".join(f"{k} {v:.0%}" for k, v in gw.items() if v) + f" | cash {cash:.0%}")
USO = """uso: paper_combo.py [--reset] [--status]
uv run python scripts/live/paper_combo.py [--status|--reset]
Dettaglio nel docstring in testa al file."""
if __name__ == "__main__":
from src.live.cli import valida
valida("paper_combo.py", USO, flag=("--status", "--reset"), con_valore=())
main()
+4 -1
View File
@@ -85,6 +85,7 @@ sys.path.insert(0, str(PROJECT_ROOT / "scripts" / "research" / "alt"))
sys.path.insert(0, str(PROJECT_ROOT / "scripts" / "research" / "ortho"))
import altlib as al # noqa: E402
from src.live import paper_guard as PG # noqa: E402
import ortholib as ol # noqa: E402
from r0726_dvolspread_gate import make_book # noqa: E402 (fabbrica ESATTA del T2)
@@ -152,7 +153,9 @@ def init_state(P: dict) -> dict:
def advance(st: dict, P: dict) -> dict:
ts, dt = P["ts"], P["dt"]
new = [i for i in range(len(ts)) if ts[i] > st["last_ts"]]
# SOLO barre CHIUSE: il pannello arriva da `resample_tf` col giorno in corso dentro —
# su uno spread BTC-ETH la barra troncata e' quasi solo rumore (~2 min/g, r0822_monitor_audit)
new = PG.nuove_chiuse(ts, st["last_ts"], PG.MS_1D)
if not new:
return st
hm, hr = st["wb_modeled"], st["wb_real"] # peso BTC TENUTO (w_eth = -w_btc)
+15
View File
@@ -17,6 +17,7 @@ sys.path.insert(0, str(PROJECT_ROOT))
import numpy as np, pandas as pd
from src.portfolio.portfolio import StrategyPortfolio
from src.portfolio.sleeves import active_sleeves
from src.live import paper_guard as PG
STATE_DIR = PROJECT_ROOT / "data" / "paper_portfolio"
STATE = STATE_DIR / "state.json"
@@ -51,6 +52,10 @@ def advance():
return st
last = pd.Timestamp(st["last"])
new = r[r.index > last]
# SOLO giorni CHIUSI: l'ultima riga dei rendimenti giornalieri e' il giorno in corso
# (~19 min/giorno registrati, r0822_monitor_audit). E' un monitor da dashboard, ma la
# regola D2 vale anche qui.
new = new[PG.indice_chiuso(new.index, PG.MS_1D)]
if len(new):
eq = st["equity"]; peak = st["peak"]; dd = st["max_dd"]
lines = []
@@ -82,5 +87,15 @@ def main():
print(f" posizioni correnti: {pf.current_positions()}")
USO = """uso: paper_portfolio.py [--reset] [--status]
uv run python scripts/live/paper_portfolio.py # avanza (init al 1o run)
uv run python scripts/live/paper_portfolio.py --status # solo stato
uv run python scripts/live/paper_portfolio.py --reset # azzera (riparte da ora)
Dettaglio nel docstring in testa al file."""
if __name__ == "__main__":
from src.live.cli import valida
valida("paper_portfolio.py", USO, flag=("--status", "--reset"), con_valore=())
main()
+168 -2
View File
@@ -38,6 +38,7 @@ PROJECT_ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(PROJECT_ROOT))
from src.backtest.harness import load # noqa: E402
from src.live import paper_guard as PG # noqa: E402
from src.strategies.prevday_breakout import target as prevday_target # noqa: E402
from src.strategies import prevday_breakout as pb # noqa: E402
@@ -52,6 +53,21 @@ MODELED_CAPITAL = 2000.0 # nominale, ribilanciamento continuo
REAL_CAPITAL = 600.0 # capitale mainnet reale
MIN_ORDER = 5.0 # min order Deribit -> sotto, il conto vero NON ribilancia
# --- GATE PRE-REGISTRATO `GATE PREVDAY-01` (2026-08-23, RESULTS-0822 §54) -----------------------
# Cablato qui il 2026-09-10 (issue #4): esisteva solo nel testo, e la revisione del 09/09 lo ha
# letto come "gate senza data". Decisione dell'operatore: tenere la data, cablare kill e veto.
GATE_DATE = "2027-06-21" # decisione piena: 7 condizioni (a)-(g), tutte necessarie
KILL_SHARPE = -0.50 # kill: Sharpe forward < -0,50 ...
KILL_MIN_ACTIVE_DAYS = 180 # ... su >= 180 giorni di barre ATTIVE (posizione != 0)
MIN_RECON_FRAC = 0.80 # veto d'integrita': >= 80% di barre ricostruibili dal feed
RECON_TOL = 1.5e-6 # net_modeled e' registrato a 6 decimali: mezzo ulp di tolleranza
RECENTI_GIORNI = 30 # "i minuti non registrati non devono crescere": ultimi 30g vs tutto
MIN_DIV_CRESCITA = 5 # ... ma UNA barra divergente su 1943 non e' una crescita (P14): la
# crescita richiede >= 5 divergenti recenti E tasso doppio del totale
# LENTE: il kill si misura sullo Sharpe GIORNALIERO (somma delle barre orarie per giorno UTC),
# la lente con cui e' misurato ogni altro sleeve del progetto — §54: sulla lente ORARIA lo stesso
# forward vale +2,04 contro +1,56 giornaliero. Al kill la famiglia NON si ri-ottimizza.
def build_bars() -> dict[str, pd.DataFrame]:
return {a: load(a, "1h").reset_index(drop=True) for a in ASSETS}
@@ -93,7 +109,10 @@ def advance(st: dict, dfs: dict) -> dict:
dt=pd.to_datetime(df["datetime"]).values, r=r,
tgt=prevday_target(df))
common = sorted(set(data["BTC"]["ts"]).intersection(data["ETH"]["ts"]))
new_ts = [t for t in common if t > st["last_ts"]]
# SOLO barre 1h CHIUSE: il parquet 1h puo' contenere l'ora in corso (1 barra su 24 —
# r0822_monitor_audit); la si lascia al giro successivo
new_ts = [t for t in common
if t > st["last_ts"] and PG.chiusa(int(t), PG.MS_1H)]
if not new_ts:
return st
idx = {a: {int(t): i for i, t in enumerate(data[a]["ts"])} for a in ASSETS}
@@ -141,6 +160,151 @@ def advance(st: dict, dfs: dict) -> dict:
return st
def _returns_df() -> pd.DataFrame | None:
if not RETURNS_FILE.exists():
return None
r = pd.read_json(RETURNS_FILE, lines=True)
if r.empty:
return None
r["dt"] = pd.to_datetime(r["ts"], unit="ms", utc=True)
return r
def sharpe_giornaliero(r: pd.DataFrame, col: str = "net_modeled") -> tuple[float, int]:
"""PURA. Sharpe annualizzato sui rendimenti GIORNALIERI (somma delle barre orarie per giorno
UTC) -> (sharpe, n_giorni). nan sotto 30 giorni o a varianza nulla."""
d = r.groupby(r["dt"].dt.floor("D"))[col].sum()
if len(d) < 30 or d.std() == 0:
return float("nan"), int(len(d))
return float(d.mean() / d.std() * np.sqrt(365.25)), int(len(d))
def giorni_attivi(r: pd.DataFrame) -> int:
"""PURA. Giorni UTC con almeno una barra a posizione != 0 (il kill conta le barre ATTIVE).
le posizioni registrate sono quelle del libro REAL-$600 (advance scrive `pr`), lo Sharpe
del kill e' MODELED: divergono solo se un ingresso da 0 vale < $5 di nozionale, cosa che a
vol-target 20% non succede (10/09: 81/81 giorni). Dichiarato, non riparato (D5)."""
att = r[(r["pos_btc"] != 0) | (r["pos_eth"] != 0)]
return int(att["dt"].dt.floor("D").nunique())
def kill_check(sharpe: float, n_attivi: int, today: pd.Timestamp) -> tuple[str, str]:
"""PURA. -> (stato, motivo). KILL solo con >= 180 giorni attivi E Sharpe < -0,50 (§54):
a quell'orizzonte P(uccidere un edge vivo a Sharpe 1,2) = 9,7%."""
if n_attivi < KILL_MIN_ACTIVE_DAYS:
return "NON_MATURO", (f"{n_attivi} giorni attivi < {KILL_MIN_ACTIVE_DAYS}: il kill non e' "
f"leggibile (mancano {KILL_MIN_ACTIVE_DAYS - n_attivi} giorni)")
if not np.isfinite(sharpe):
return "NON_MISURABILE", "Sharpe forward non misurabile"
if sharpe < KILL_SHARPE:
return "KILL", f"Sharpe {sharpe:+.2f} < {KILL_SHARPE:+.2f} su {n_attivi} giorni attivi -> RITIRARE, senza ri-ottimizzare"
return "SUPERATO", f"Sharpe {sharpe:+.2f} >= {KILL_SHARPE:+.2f} su {n_attivi} giorni attivi"
def ricostruzione(r: pd.DataFrame, dfs: dict, start_ts: int) -> dict:
"""Rigioca la strategia CONGELATA sul feed di OGGI dallo `start_ts` del monitor e confronta
barra per barra `net_modeled` registrato con quello ricalcolato (ribilanciamento continuo,
la stessa aritmetica di `advance`). Una barra e' RICOSTRUIBILE se coincide entro RECON_TOL.
Cosa misura: quante barre registrate sono riproducibili dal dato certificato. Le divergenti
sono barre calcolate su un feed che il rebuild ha poi rivisto (§54: 67 su 1512, tutte
all'ora del cron) — e' la misura del veto d'integrita' (>= 80%, e i minuti persi non devono
crescere: qui, divergenti/giorno negli ultimi 30g contro l'intera finestra)."""
data = {}
for a in ASSETS:
df = dfs[a]
c = df["close"].values.astype(float)
rr = np.zeros(len(c)); rr[1:] = c[1:] / c[:-1] - 1.0
data[a] = dict(ts=df["timestamp"].values.astype("int64"), r=rr, tgt=prevday_target(df))
idx = {a: {int(t): i for i, t in enumerate(data[a]["ts"])} for a in ASSETS}
# posizione iniziale come init_state: target all'ultima barra <= start_ts
pos = {}
for a in ASSETS:
i0 = max(i for t, i in idx[a].items() if t <= start_ts)
pos[a] = float(data[a]["tgt"][i0])
ricalc, presenti = [], []
for t in r["ts"].astype("int64"):
t = int(t)
if any(t not in idx[a] for a in ASSETS):
ricalc.append(np.nan); presenti.append(False); continue
net = 0.0
for a in ASSETS:
i = idx[a][t]
tgt = float(data[a]["tgt"][i])
net += WEIGHT * (pos[a] * float(data[a]["r"][i]) - FEE_SIDE * abs(tgt - pos[a]))
pos[a] = tgt
ricalc.append(net); presenti.append(True)
ricalc = np.asarray(ricalc, dtype=float)
reg = r["net_modeled"].values.astype(float)
ok = np.isfinite(ricalc) & (np.abs(ricalc - reg) <= RECON_TOL)
n = len(reg)
giorni = r["dt"].dt.floor("D")
span_g = max(1, int(giorni.nunique()))
recenti = r["dt"] >= (r["dt"].max() - pd.Timedelta(days=RECENTI_GIORNI))
div_rec = int((~ok[recenti.values]).sum())
g_rec = max(1, int(giorni[recenti].nunique()))
return dict(n=n, ricostruibili=int(ok.sum()), frac=float(ok.sum() / n) if n else float("nan"),
assenti_nel_feed=int((~np.asarray(presenti)).sum()),
div_per_giorno=float((n - ok.sum()) / span_g),
div_recenti=div_rec, div_per_giorno_recenti=float(div_rec / g_rec),
divergenti_ts=[int(t) for t in r["ts"].values[~ok]][:20])
def veto_check(frac: float, div_g_tot: float, div_g_rec: float, div_rec: int) -> tuple[bool, str]:
"""PURA. Veto d'integrita' (blocca, non decide): sotto l'80% di barre ricostruibili, o con le
divergenze che CRESCONO negli ultimi 30 giorni, la finestra si ESTENDE invece di decidere."""
motivi = []
if not np.isfinite(frac) or frac < MIN_RECON_FRAC:
motivi.append(f"barre ricostruibili {frac:.1%} < {MIN_RECON_FRAC:.0%}")
if div_rec >= MIN_DIV_CRESCITA and div_g_rec > 2.0 * div_g_tot + 1e-12:
motivi.append(f"divergenze in crescita: {div_rec} negli ultimi {RECENTI_GIORNI}g "
f"({div_g_rec:.2f}/g contro {div_g_tot:.2f}/g sull'intera finestra)")
return (not motivi), ("; ".join(motivi) if motivi else
f"ricostruibili {frac:.1%}, divergenti {div_rec} negli ultimi {RECENTI_GIORNI}g "
f"({div_g_rec:.2f}/g contro {div_g_tot:.2f}/g totale)")
def print_gate(st: dict, dfs: dict, today: pd.Timestamp | None = None) -> dict:
"""Stampa lo stato del gate pre-registrato e lo RITORNA (per i test e per chi legge il log).
Non decide niente: al kill e alla data stampa cosa la regola dice, in maiuscolo."""
r = _returns_df()
out = dict(gate_date=GATE_DATE, kill="NON_MISURABILE", veto_ok=None)
print(f"\n GATE PRE-REGISTRATO `PREVDAY-01` (23/08, RESULTS-0822 §54) — decisione {GATE_DATE}")
if r is None or len(r) < 2:
print(" serie forward assente o troppo corta: niente da misurare")
return out
today = today or pd.Timestamp.now(tz="UTC").normalize()
sh, n_g = sharpe_giornaliero(r)
n_att = giorni_attivi(r)
stato, motivo = kill_check(sh, n_att, today)
out.update(sharpe_giornaliero=sh, n_giorni=n_g, n_attivi=n_att, kill=stato, kill_motivo=motivo)
print(f" Sharpe forward GIORNALIERO (MODELED): {sh:+.2f} su {n_g} giorni, {n_att} attivi"
f" [lente oraria {_sharpe_orario(r):+.2f}: non si cita, §54]")
if stato == "KILL":
print(f" *** KILL: {motivo} ***")
else:
print(f" kill (Sharpe < {KILL_SHARPE:+.2f} su >= {KILL_MIN_ACTIVE_DAYS}g attivi): {stato}{motivo}")
rc = ricostruzione(r, dfs, int(st["start_ts"]))
ok, vm = veto_check(rc["frac"], rc["div_per_giorno"], rc["div_per_giorno_recenti"], rc["div_recenti"])
out.update(ricostruzione=rc, veto_ok=ok, veto_motivo=vm)
print(f" veto d'integrita': {'ok' if ok else '*** VETO — la finestra si ESTENDE ***'}{vm}"
f" ({rc['ricostruibili']}/{rc['n']} barre, {rc['assenti_nel_feed']} assenti nel feed)")
if today >= pd.Timestamp(GATE_DATE, tz="UTC"):
print(f" *** DECISIONE DOVUTA ({GATE_DATE}): (a) DSR >= 0,95 sulla famiglia dichiarata "
f"(b) cella al buio == congelata (c) delta libro > +0,05 col funding (d) weights_tilt_null "
f"(e) ADDS+robust_oos+non-hedge sulla cella al buio (f) day_boundary_robust "
f"(g) Sharpe forward > 0 [{sh:+.2f}] — TUTTE necessarie ***")
else:
print(f" decisione fra {(pd.Timestamp(GATE_DATE, tz='UTC') - today).days} giorni; "
f"le condizioni (a)-(b) sono STRUTTURALI e il forward non le cambia (§54)")
return out
def _sharpe_orario(r: pd.DataFrame) -> float:
x = r["net_modeled"].astype(float)
return float(x.mean() / x.std() * np.sqrt(24 * 365.25)) if x.std() > 0 else float("nan")
def print_status(st: dict, dfs: dict):
days = (max(int(dfs[a]["timestamp"].iloc[-1]) for a in ASSETS) - st["start_ts"]) / 86400_000
rm = st["cap_modeled"] / MODELED_CAPITAL - 1
@@ -152,7 +316,9 @@ def print_status(st: dict, dfs: dict):
print(f" MODELED ($2000 nominale): {rm*100:+6.2f}% eq ${st['cap_modeled']:.2f} maxDD {st['dd_modeled']*100:.1f}%")
print(f" REAL-$600 (min-order $5) : {rr*100:+6.2f}% eq ${st['cap_real']:.2f} maxDD {st['dd_real']*100:.1f}%")
print(f" -> fill-haircut MODELED-REAL: {(rm-rr)*100:+.2f} pp (lo scettico l'aveva segnalato)")
print(f" log: {RETURNS_FILE}\n")
print(f" log: {RETURNS_FILE}")
print_gate(st, dfs)
print()
def main():
+238
View File
@@ -0,0 +1,238 @@
"""paper_regen.py — RIGENERA le serie dei forward-monitor rotti da `advance()` (§5.1).
PERCHE' RIGENERARE E NON AZZERARE. L'audit r0822_monitor_audit ha provato due volte, in modo
indipendente, che il replay sul pannello di oggi E' la serie corretta sulla stessa finestra:
(a) `paper_prevday` ha 1427/1488 barre identiche al bit dopo 62 notti di riscrittura del feed;
(b) le 6 barre chiuse di `paper_statarb` recuperate dopo il guasto EPERM coincidono al bit.
Quindi la finestra forward NON va persa: si rigenera dallo STESSO `start_ts` pre-registrato,
con la config congelata e il codice di produzione riparato e nessuna data di gate si sposta.
COSA FA, per ciascun monitor (statarb · dvolspread · xsr · prevday):
1. archivia i file correnti come *.pre_regen_<data>.* (NON li cancella: sono l'evidenza
del difetto, e stanno nel perimetro di backup);
2. ricostruisce lo stato iniziale all'ORIGINALE `start_ts` (stessa costruzione dell'audit);
3. riesegue `advance()` di produzione che ora consuma SOLO barre chiuse (paper_guard);
4. salva lo stato e stampa il verdetto a runtime (N11): barre, Sharpe vecchio vs nuovo,
e la prova che l'ultima barra registrata e' chiusa.
`paper_portfolio` e `paper_combo` NON si rigenerano qui, e la scelta e' dichiarata: la gamba
GTAA legge `ADJUSTED_LAST` di IB ri-aggiustato all'indietro, quindi il replay NON sarebbe la
serie che un monitor sano avrebbe registrato (P12: meglio una storia corrotta e dichiarata di
una riscritta e spacciata per registrata). Sono dashboard senza gate; dal fix in poi scrivono
solo barre chiuse.
uv run python scripts/live/paper_regen.py # tutti e 4
uv run python scripts/live/paper_regen.py --solo paper_statarb
"""
from __future__ import annotations
import importlib.util
import json
import shutil
import sys
from datetime import datetime, timezone
from pathlib import Path
import numpy as np
import pandas as pd
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
ANN = float(np.sqrt(365.0))
SUFFISSO = datetime.now(timezone.utc).strftime("pre_regen_%Y%m%d")
def live(name: str):
spec = importlib.util.spec_from_file_location(f"_regen_{name}",
ROOT / "scripts" / "live" / f"{name}.py")
m = importlib.util.module_from_spec(spec)
spec.loader.exec_module(m)
return m
def sharpe_jsonl(path: Path, campo: str = "net_modeled") -> tuple[float, int]:
if not path.exists():
return float("nan"), 0
r = []
for ln in path.read_text().splitlines():
ln = ln.strip()
if ln:
r.append(float(json.loads(ln)[campo]))
r = np.asarray(r)
if len(r) < 2 or r.std() == 0:
return float("nan"), len(r)
return float(r.mean() / r.std() * ANN), len(r)
def archivia(d: Path, nomi: tuple[str, ...]) -> None:
for n in nomi:
f = d / n
if f.exists():
dest = d / f"{f.stem}.{SUFFISSO}{f.suffix}"
if dest.exists():
raise SystemExit(f" {dest} esiste gia': rigenerazione doppia nello stesso "
"giorno? Verificare a mano prima di riprovare.")
shutil.move(str(f), str(dest))
print(f" archiviato {f.name} -> {dest.name}")
def verdetto(nome: str, d: Path, mod, st: dict, sh_prima: float, n_prima: int,
cadenza_ms: int) -> None:
from src.live.paper_guard import chiusa
sh_dopo, n_dopo = sharpe_jsonl(d / "returns.jsonl")
ult = st["last_ts"] if "last_ts" in st else None
ok_chiusa = chiusa(int(ult), cadenza_ms) if ult else False
print(f" barre: {n_prima} registrate (rotte) -> {n_dopo} rigenerate (chiuse)")
print(f" Sharpe ann. net_modeled: {sh_prima:+.2f} (serie rotta) -> {sh_dopo:+.2f} "
f"(serie vera)")
print(f" ultima barra {pd.Timestamp(int(ult), unit='ms', tz='UTC').date() if ult else '?'} "
f"chiusa: {'SI' if ok_chiusa else 'NO — QUALCOSA NON VA'}")
if not ok_chiusa:
raise SystemExit(f" {nome}: la rigenerazione ha consumato una barra NON chiusa")
def regen_statarb() -> None:
print("\n== paper_statarb (gate 2026-09-27) " + "=" * 50)
d = ROOT / "data" / "paper_statarb"
ps = live("paper_statarb")
st_old = json.loads((d / "state.json").read_text())
sh0, n0 = sharpe_jsonl(d / "returns.jsonl")
j = ps.build_joint("1d")
ts, _, pos, _ = ps._signal(j)
i0 = int(np.where(ts == st_old["start_ts"])[0][0])
archivia(d, ("returns.jsonl", "trades.jsonl", "state.json"))
st = dict(start_ts=st_old["start_ts"], last_ts=st_old["start_ts"], n_bars=0,
pos_modeled=float(pos[i0]), pos_real=float(pos[i0]),
cap_modeled=ps.MODELED_CAPITAL, cap_real=ps.REAL_CAPITAL,
peak_modeled=ps.MODELED_CAPITAL, peak_real=ps.REAL_CAPITAL,
dd_modeled=0.0, dd_real=0.0, n_trades=0)
st = ps.advance(st, j)
ps._state_io(st)
verdetto("paper_statarb", d, ps, st, sh0, n0, 86_400_000)
def regen_dvolspread() -> None:
print("\n== paper_dvolspread (kill 2026-10-24 / decisione 2027-01-24) " + "=" * 24)
d = ROOT / "data" / "paper_dvolspread"
pv = live("paper_dvolspread")
st_old = json.loads((d / "state.json").read_text())
sh0, n0 = sharpe_jsonl(d / "returns.jsonl")
P = pv._panel()
i0 = int(np.where(P["ts"] == st_old["start_ts"])[0][0])
archivia(d, ("returns.jsonl", "state.json"))
st = dict(start_ts=st_old["start_ts"], last_ts=st_old["start_ts"], n_bars=0, n_active=0,
n_flat_signal=0, n_flat_nodata=0,
wb_modeled=float(P["wb"][i0]), wb_real=float(P["wb"][i0]),
cap_modeled=pv.MODELED_CAPITAL, cap_real=pv.REAL_CAPITAL,
peak_modeled=pv.MODELED_CAPITAL, peak_real=pv.REAL_CAPITAL,
dd_modeled=0.0, dd_real=0.0, n_flips=0, frozen=pv.FROZEN)
st = pv.advance(st, P)
pv._state_io(st)
verdetto("paper_dvolspread", d, pv, st, sh0, n0, 86_400_000)
def regen_xsr() -> None:
print("\n== paper_xsr (gate 2026-10-23) " + "=" * 54)
d = ROOT / "data" / "paper_xsr"
px = live("paper_xsr")
st_old = json.loads((d / "state.json").read_text())
sh0, n0 = sharpe_jsonl(d / "returns.jsonl")
archivia(d, ("returns.jsonl", "state.json"))
st = dict(start_ts=st_old["start_ts"], last_ts=st_old["start_ts"], n_bars=0,
syms=st_old["syms"],
modeled=px._book(px.MODELED_CAPITAL, len(st_old["syms"])),
reals={n: px._book(c, len(st_old["syms"])) for c, n in px.REAL_BOOKS})
st = px.advance(st)
px._state_io(st)
verdetto("paper_xsr", d, px, st, sh0, n0, 86_400_000)
def regen_prevday() -> None:
print("\n== paper_prevday (griglia ORARIA, nessun gate a data fissa) " + "=" * 24)
d = ROOT / "data" / "paper_prevday"
pp = live("paper_prevday")
st_old = json.loads((d / "state.json").read_text())
sh0, n0 = sharpe_jsonl(d / "returns.jsonl")
dfs = pp.build_bars()
S = st_old["start_ts"]
pos = {a: pp.pb.current_target(dfs[a][dfs[a]["timestamp"] <= S]) for a in pp.ASSETS}
archivia(d, ("returns.jsonl", "trades.jsonl", "state.json"))
st = dict(start_ts=S, last_ts=S, n_bars=0, pos_modeled=pos, pos_real=dict(pos),
cap_modeled=pp.MODELED_CAPITAL, cap_real=pp.REAL_CAPITAL,
peak_modeled=pp.MODELED_CAPITAL, peak_real=pp.REAL_CAPITAL,
dd_modeled=0.0, dd_real=0.0, n_trades=0)
st = pp.advance(st, dfs)
pp._state_io(st)
verdetto("paper_prevday", d, pp, st, sh0, n0, 3_600_000)
def trim_portfolio() -> None:
"""`paper_portfolio` NON si rigenera (la gamba GTAA legge ADJUSTED_LAST ri-aggiustato:
il replay non sarebbe la serie registrata P12). Ma la sua CODA contiene la riga del
giorno in corso, scritta dal codice vecchio: senza toglierla la guardia PREMATURO
resterebbe accesa per sempre su un difetto gia' riparato (P14). Il taglio e' onesto:
la storia registrata resta com'e' (troncata e DICHIARATA in memoria), va via solo la
riga che dice di essere un giorno e non lo e' — la versione chiusa la riscrive il
prossimo giro del cron col codice riparato."""
from src.live.paper_guard import MS_1D, indice_chiuso
print("\n== paper_portfolio (dashboard — TRIM della coda non chiusa, non rigenerazione) ==")
d = ROOT / "data" / "paper_portfolio"
righe = (d / "equity.csv").read_text().strip().splitlines()
testa, dati = righe[0], righe[1:]
date = pd.DatetimeIndex([r.split(",")[0] for r in dati])
m = indice_chiuso(date, MS_1D)
tolte = [r for r, k in zip(dati, m) if not k]
if not tolte:
print(" coda gia' pulita: niente da fare")
return
archivia(d, ("equity.csv", "state.json"))
dati_ok = [r for r, k in zip(dati, m) if k]
(d / "equity.csv").write_text("\n".join([testa] + dati_ok) + "\n")
eq = [float(r.split(",")[1]) for r in dati_ok]
peak, dd = 0.0, 0.0
for e in eq:
peak = max(peak, e)
dd = max(dd, (peak - e) / peak if peak > 0 else 0.0)
st_old = json.loads((d / f"state.{SUFFISSO}.json").read_text())
st = dict(st_old, last=dati_ok[-1].split(",")[0], equity=eq[-1], peak=peak, max_dd=dd,
n_days=st_old["n_days"] - len(tolte))
(d / "state.json").write_text(json.dumps(st, indent=2))
for r in tolte:
print(f" tolta riga non chiusa: {r}")
print(f" ultima riga ora: {dati_ok[-1]} (peak/max_dd ricalcolati dalla serie)")
REGEN = dict(paper_statarb=regen_statarb, paper_dvolspread=regen_dvolspread,
paper_xsr=regen_xsr, paper_prevday=regen_prevday,
paper_portfolio=trim_portfolio)
def main() -> None:
print("=" * 90)
print(" RIGENERAZIONE forward-monitor — stesso start_ts, config congelata, solo barre chiuse")
print("=" * 90)
solo = None
if "--solo" in sys.argv:
solo = sys.argv[sys.argv.index("--solo") + 1]
if solo not in REGEN:
raise SystemExit(f"monitor sconosciuto: {solo} (validi: {', '.join(REGEN)})")
for nome, fn in REGEN.items():
if solo and nome != solo:
continue
fn()
print("\n fatto. I file *.%s.* sono l'evidenza del difetto e restano nel backup." % SUFFISSO)
USO = """uso: paper_regen.py [--solo V]
paper_regen.py RIGENERA le serie dei forward-monitor rotti da `advance()` (§5.1).
uv run python scripts/live/paper_regen.py # tutti e 4
uv run python scripts/live/paper_regen.py --solo paper_statarb
Dettaglio nel docstring in testa al file."""
if __name__ == "__main__":
from src.live.cli import valida
valida("paper_regen.py", USO, flag=(), con_valore=("--solo",))
main()
+4 -1
View File
@@ -51,6 +51,7 @@ sys.path.insert(0, str(PROJECT_ROOT / "scripts" / "research"))
# Segnale ESATTO dello sweep (nessuna reimplementazione → identità garantita col backtest).
from orthogonal_signals import build_joint, f_statarb_resid, spread_ret # noqa: E402
from src.live import paper_guard as PG # noqa: E402
STATE_DIR = PROJECT_ROOT / "data" / "paper_statarb"
STATE_FILE = STATE_DIR / "state.json"
@@ -99,7 +100,9 @@ def init_state(j: pd.DataFrame) -> dict:
def advance(st: dict, j: pd.DataFrame) -> dict:
ts, dt, pos, sr = _signal(j)
new = [i for i in range(len(ts)) if ts[i] > st["last_ts"]]
# SOLO barre CHIUSE: `build_joint('1d')` passa da `resample_tf`, che NON scarta il giorno
# in corso (1 barra 1h su 24) — consumarlo faceva registrare ~4 min/giorno (r0822_monitor_audit)
new = PG.nuove_chiuse(ts, st["last_ts"], PG.MS_1D)
if not new:
return st
pm, pr = st["pos_modeled"], st["pos_real"] # posizioni TENUTE (decise alla barra precedente)
+4 -1
View File
@@ -65,6 +65,7 @@ sys.path.insert(0, str(PROJECT_ROOT / "scripts" / "research"))
# Segnale ESATTO dello studio (nessuna reimplementazione -> niente drift col backtest).
from r0725_statarb_multi import BASE, MIN_BARS, load_hl, signal, universe # noqa: E402
from src.live import paper_guard as PG # noqa: E402
STATE_DIR = PROJECT_ROOT / "data" / "paper_xsr"
STATE_FILE = STATE_DIR / "state.json"
@@ -158,7 +159,9 @@ def advance(st: dict) -> dict:
print(f" [XSR01] universo cambiato ({len(st['syms'])} -> {len(syms)}): "
"la finestra forward richiede universo costante. Usa --reset per ripartire.")
return st
new = [i for i in range(len(ts)) if ts[i] > st["last_ts"]]
# SOLO barre CHIUSE: l'ultima riga del parquet HL e' il giorno IN CORSO, e consumarla
# e' il difetto che faceva registrare ~41 minuti di mercato al giorno (r0822_monitor_audit)
new = PG.nuove_chiuse(ts, st["last_ts"], PG.MS_1D)
if not new:
return st
for i in new:
+48
View File
@@ -0,0 +1,48 @@
"""Revisione settimanale in SOLA LETTURA: un modello DIVERSO dall'autore rilegge il sistema.
uv run python scripts/live/revisione.py # chiama il revisore, scrive docs/revisioni/<data>.md, Telegram
uv run python scripts/live/revisione.py --secco # solo la taglia del materiale: niente modello, niente file
uv run python scripts/live/revisione.py --quiet # per il cron (scripts/cron_review.sh, lunedi' 06:15 UTC)
Cosa scrive: SOLO il rapporto. Cosa non tocca: config, libro, cron, codice, dati. Le proposte
restano proposte; decide l'operatore. Dettagli e ragioni in `src/live/revisione.py`.
"""
from __future__ import annotations
import sys
from datetime import datetime, timezone
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
USO = """uso: revisione.py [--giorno AAAA-MM-GG] [--modello NOME] [--secco] [--no-telegram] [--quiet]
--giorno G data della revisione (default: adesso, UTC)
--modello M revisore (default: src.live.revisione.MODELLO = claude-fable-5-1 NON il modello che scrive il codice)
--secco stampa la taglia del materiale e si ferma: niente modello, niente file, niente Telegram
--no-telegram scrive il rapporto ma non manda la sintesi
--quiet stampa solo l'esito
SPENDE una chiamata al modello e MANDA un messaggio Telegram, salvo --secco/--no-telegram.
Scrive un solo file: docs/revisioni/<data>.md."""
def _arg(flag: str, default=None):
a = sys.argv[1:]
return a[a.index(flag) + 1] if flag in a and a.index(flag) + 1 < len(a) else default
if __name__ == "__main__":
from src.live.cli import valida
valida("revisione.py", USO, flag=("--secco", "--no-telegram", "--quiet"),
con_valore=("--giorno", "--modello"))
from src.live import revisione as R
g = _arg("--giorno")
adesso = (datetime.fromisoformat(g).replace(tzinfo=timezone.utc) if g
else datetime.now(timezone.utc))
esito = R.scrivi(ROOT, adesso, modello=_arg("--modello", R.MODELLO),
secco="--secco" in sys.argv, telegram="--no-telegram" not in sys.argv,
quiet="--quiet" in sys.argv)
if "--quiet" in sys.argv and not esito.get("secco"):
print(f"revisione {esito['data']}: {esito.get('stato')}"
+ (f"{esito.get('errore')}" if esito.get("errore") else "") + f" {esito.get('file')}")
raise SystemExit(0 if esito.get("secco") or esito.get("stato") in ("ok", "sospetta") else 1)
+72
View File
@@ -0,0 +1,72 @@
#!/usr/bin/env python3
"""Sorveglianza giornaliera della CHIAVE DI SCALA del libro (SPEC-scale-key §6.4).
uv run python scripts/live/scale_watch.py # osserva e stampa, invia se ALLARME
uv run python scripts/live/scale_watch.py --secco # osserva e stampa, NON invia nulla
uv run python scripts/live/scale_watch.py --quiet # una riga sola (per il cron)
Tre domande, tre azioni diverse, tre stati (OK / ALLARME / NON MISURABILE).
Non impedisce la modifica: la rende visibile entro 24 ore e attribuibile.
"""
from __future__ import annotations
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from src.live.notifier import notify # noqa: E402
from src.live.scale_watch import ALLARME, OK, run_once # noqa: E402
def _sender(testo: str):
try:
notify(testo)
return True, "telegram"
except Exception as e: # P3: si registra dove lo si ingoia
return False, f"{type(e).__name__}: {e}"
def main() -> None:
secco = "--secco" in sys.argv
quiet = "--quiet" in sys.argv
rep = run_once(sender=None if secco else _sender)
icona = {OK: "", ALLARME: "🚨"}.get(rep["stato"], "⚠️")
if quiet:
fb = rep["fallback"]
q = f"{100*fb['quota']:.2f}%" if "quota" in fb else "n/d"
print(f" scale_watch: {icona} {rep['stato']} · scala {rep['scala_dichiarata']:.2f}x · "
f"fallback {q} · {rep['invio']}")
return
print("=" * 88)
print(f" SCALE WATCH — la chiave di scala del libro {icona} {rep['stato']}")
print("=" * 88)
print(f" scala dichiarata : {rep['scala_dichiarata']:.2f}x "
f"(scaletta {rep['ladder']}, tetto {rep['tetto']:.2f}x — costanti di CODICE)")
for nome, d in rep["domande"].items():
print(f"\n {nome} [{d['stato']}] {d['motivo']}")
if d.get("azione") and d["azione"] != "nessuna":
print(f" → azione: {d['azione']}")
fb = rep["fallback"]
if fb.get("stato") == "NON MISURABILE":
print(f"\n fallback [{fb['stato']}] {fb.get('motivo')}")
else:
print(f"\n fallback [{fb['stato']}] equity illeggibile in {fb['fallback_veri']}/{fb['giri']} "
f"giri ({100*fb['quota']:.2f}%, soglia {100*fb['soglia']:.0f}%) — "
f"{fb['paper']} giri pre-finanziamento esclusi, li' il libro non invia affatto")
print(f"\n lettore di produzione [{rep['lettore']['stato']}] {rep['lettore']['motivo'][:150]}")
print(f"\n invio: {rep['invio']}")
USO = """uso: scale_watch.py [--quiet] [--secco]
uv run python scripts/live/scale_watch.py # osserva e stampa, invia se ALLARME
uv run python scripts/live/scale_watch.py --secco # osserva e stampa, NON invia nulla
uv run python scripts/live/scale_watch.py --quiet # una riga sola (per il cron)
Dettaglio nel docstring in testa al file."""
if __name__ == "__main__":
from src.live.cli import valida
valida("scale_watch.py", USO, flag=("--quiet", "--secco"), con_valore=())
main()
+9
View File
@@ -146,5 +146,14 @@ def main() -> None:
print("inviato" if send(txt) else "NON inviato (config Telegram assente o rete KO)")
USO = """uso: telegram_daily.py [--dry-run]
uv run python scripts/live/telegram_daily.py # calcola e invia
uv run python scripts/live/telegram_daily.py --dry-run # stampa e basta, non invia
Dettaglio nel docstring in testa al file."""
if __name__ == "__main__":
from src.live.cli import valida
valida("telegram_daily.py", USO, flag=("--dry-run",), con_valore=())
main()
+49 -4
View File
@@ -1,7 +1,7 @@
"""Sincronizza `data/live/trades.db` — il libro di bordo dei trade, allineato col tempo.
uv run python scripts/live/trades_db.py --sync # idempotente, da cron
uv run python scripts/live/trades_db.py --report # stato + P&L a video
uv run python scripts/live/trades_db.py --report # stato + P&L a video (TWR, non e1/e0)
uv run python scripts/live/trades_db.py --reconcile # incrocio delle tre fonti
SOLA LETTURA sul venue e sui log: non manda ordini, non tocca il libro.
@@ -60,7 +60,13 @@ def reconcile() -> None:
t = d.trade_history(ins, limit=100)
print(f" venue {ins:<20}: {len(t)} trade visibili (l'endpoint TRONCA: non e' un backfill)")
except Exception as e:
# "NON LETTO" resta (P5: *non vedo* non e' *zero*), ma da solo non diceva DI CHI e' il
# guasto: il 2026-08-25 questa riga stampava `HTTPError` mentre Deribit era in
# manutenzione annunciata, indistinguibile da un gateway rotto. Ora porta il perche' (P4).
from src.live.venue_probe import diagnose
d = diagnose([f"{type(e).__name__}: {e}"])
print(f" venue: NON LETTO ({type(e).__name__}) — non e' 'zero trade', e' 'non misurato'")
print(f" diagnosi: {d.riga()}")
def report() -> None:
@@ -72,10 +78,41 @@ def report() -> None:
print(f"\n fill registrati : {n} dal {p['a'][:16]} al {p['b'][:16]}")
eq = con.execute("SELECT ts_utc, equity FROM equity ORDER BY ts_utc").fetchall()
if eq:
# Il rendimento NON e' `e1/e0-1` sulla serie grezza: quel numero era per il 96,3% un
# bonifico (debito #14, 2026-09-01). Lo scorporo e' quello del giornale — si CHIAMA,
# non si rifa' (P1): `rendimento_twr` spezza la serie sui movimenti di capitale certi.
from src.live.journal import rendimento_twr
e0, e1 = eq[0]["equity"], eq[-1]["equity"]
picco = max(r["equity"] for r in eq)
print(f" equity : ${e0:,.2f} -> ${e1:,.2f} ({e1 - e0:+.2f}, {100*(e1/e0-1):+.2f}%)"
f" | picco ${picco:,.2f} | {len(eq)} letture")
r = rendimento_twr(con, eq[-1]["ts_utc"])
print(f" equity : ${e0:,.2f} -> ${e1:,.2f} ({e1 - e0:+,.2f} di equity, "
f"movimenti di capitale INCLUSI) | picco ${picco:,.2f} | {len(eq)} letture")
if r["eventi"]:
print(f" movimenti capitale : {r['certi']:+,.2f} certi | {r['ambigui']:+,.2f} ambiguo/i"
f" (restano nel P&L, dichiarati)")
for ev in r["eventi"]:
mk = ev.get("mercato_max_pct")
if ev.get("dichiarazione"):
dettaglio = ev["dichiarazione"]
elif mk is None:
dettaglio = ev.get("mercato_nota") or "mercato non misurabile"
else:
dettaglio = f"mercato max {100 * mk:.2f}% a leva piena, salto {100 * ev['pct']:+.1f}%"
print(f" {ev['ts_dopo'][:16]} {ev['delta']:+,.2f} [{ev['classe']}: {dettaglio}]")
for av in r.get("avvisi") or []:
print(f" ⚠️ {av}")
if r["trading"] is None:
print(f" trading da arming : n/d ({r['motivo']})")
else:
print(f" trading da arming : {r['trading']:+,.2f} (equity al netto dei movimenti "
f"certi; l'ora di un movimento RILEVATO esce intera, quella di un DICHIARATO "
f"resta — vedi journal.rendimento_twr)")
if r["twr"] is None:
print(f" TWR : n/d ({r['motivo']})")
else:
segs = " x ".join(f"{100*s['ret']:+.2f}% [{s['da'][:10]} -> {s['a'][:10]}]"
for s in r["segmenti"])
print(f" TWR : {100*r['twr']:+.2f}% = {segs}")
rt = con.execute("SELECT * FROM roundtrips ORDER BY ts_out").fetchall()
lordo = sum(r["pnl_lordo"] for r in rt)
fee_rt = sum(r["fee_quota"] for r in rt)
@@ -95,8 +132,16 @@ def report() -> None:
con.close()
USO = """uso: trades_db.py [--sync] [--report] [--reconcile] [--quiet]
--sync (default) legge log e jsonl e aggiorna data/live/trades.db idempotente
--report stato del libro di bordo + P&L (TWR, non e1/e0)
--reconcile incrocio delle tre fonti sui fill (log x jsonl x venue)
--quiet con --sync: non stampa il riepilogo"""
if __name__ == "__main__":
args = sys.argv[1:]
from src.live.cli import valida
args = valida("trades_db.py", USO, flag=("--sync", "--report", "--reconcile", "--quiet"))
if "--reconcile" in args:
reconcile()
elif "--report" in args:
+299
View File
@@ -0,0 +1,299 @@
#!/usr/bin/env python
"""usde_convert.py — porta la QUOTA di collaterale USDE a un bersaglio dichiarato. INVIA ORDINI VERI.
PERCHE' ESISTE. La conversione del 26/08 (test di eligibilita', 500 USDE) fu una chiamata a mano al
gateway: nessuno script, nessuna guardia, nessuna traccia se non il diario. Il gate USDE-01 prevede
entrambe le direzioni IDONEO -> si decide la quota; NON IDONEO -> "si riconverte" e nessuna delle
due aveva un attrezzo. Qui c'e', e vale in tutti e due i versi (quota piu' alta = compra USDE, piu'
bassa = vende).
PERCHE' NON PASSA DA DeribitTrader. `execution.ALLOWED` ammette solo i due perp del libro: e' il
guardrail anti-fat-finger del percorso soldi del book e NON va allargato per far entrare uno spot che
col libro non c'entra. Questo script parla al gateway per conto suo e porta le proprie guardie.
LE GUARDIE (tutte BLOCCANTI, tutte dichiarate qui):
* banda di prezzo [0.995, 1.005] la stessa del 26/08. Fuori banda non si converte: un USDE a
0.98 e' esattamente il momento in cui NON si compra, e uno a 1.02 non esiste (si clampa a 1.0).
* quota bersaglio in [0, QUOTA_HARD_MAX] il gate USDE-01 dice "mai 100%: R1 emittente non
recuperabile". Il tetto e' HARD e non si passa da riga di comando.
* TETTO DEL VENUE (`usde.venue_cap_frac`). Deribit rifiuta gli acquisti oltre una frazione
dell'equity totale — ~31,2%, MISURATA il 30-31/08 e non documentata da nessuna parte, col
messaggio fuorviante `not_enough_funds_in_currency`. E' una frazione, non un livello: fatta
scendere l'equity di $8, il tetto e' sceso con lei. Senza questa guardia il piano si dichiara
VALIDO e poi il venue lo rifiuta a raffica che e' esattamente il giro di venti minuti costato
il 30/08. Il valore vive in config, non qui (P1: il bersaglio si deriva, non si ridichiara).
* CUSCINO DI REGOLAMENTO. Il P&L e il funding dei perp USDC-lineari si regolano in USDC, non nel
collaterale: a quota alta il saldo USDC va negativo alla prima perdita del libro e Deribit lo
finanzia a interesse. Il margine NON e' il vincolo (r0830_usde_quota: l'haircut non morde fino
al 90%) il vincolo e' questo. Il cuscino richiesto e' il disaster-SL sulla MASSIMA esposizione
che il libro puo' prendere: n_asset * frac * disaster_sl_pct * equity, tutto letto da
config/live.json (P1: il bersaglio si deriva, non si ridichiara).
* dry-run DI DEFAULT: senza --esegui stampa il piano e non manda niente.
IL TETTO PER-ORDINE (misurato il 30/08, non documentato da nessuna parte). Il venue rifiuta gli
ordini spot grandi con `not_enough_funds_in_currency` ANCHE quando i fondi ci sono: 100 accettato
e 300 rifiutato con $1.442 disponibili (e 500 era passato il 26/08 con piu' saldo). Il gateway non
espone `available_withdrawal_funds` ne' `get_order_book`, quindi la causa vera NON e' leggibile da
qui e il messaggio del venue e' fuorviante: si tratta il tetto come ignoto e si CHUNKA, dimezzando
il blocco a ogni rifiuto. Costa zero: il book ha 3.990 unita' al miglior ask e la commissione spot
e' 0, quindi N fill piccoli e un fill grande pagano lo stesso prezzo.
NON E' NE' LA TAGLIA NE' IL TEMPO: E' IL PREZZO. Diagnosi finale (30/08 20:02, tre ordini da 1
USDE): SELL @1.0000 passa, BUY @1.0003 passa, BUY @1.0002 e BUY market falliscono. L'ask si era
mosso da 1.0002 a 1.0003 e l'ordine aveva smesso di INCROCIARE: Deribit rifiuta un limite BUY non
marcabile sullo spot con `not_enough_funds_in_currency`, che e' un messaggio fuorviante e ha fatto
inseguire per venti minuti due ipotesi sbagliate (tetto di size, rate-limit). Il difetto vero era
qui: il prezzo dell'ordine si derivava dall'INDICE (1.0001 + 1 tick) invece che dal BOOK. Sono due
prezzi diversi con due mestieri diversi l'indice MARCA il collaterale (usde.valuta, mediana
multi-exchange: la lezione Binance 10/10), il book PREZZA lo scambio. Confonderli non sbaglia la
valutazione, sbaglia l'esecuzione.
LO STORICO DELLE IPOTESI SBAGLIATE, tenuto apposta: 943.98 rifiutato per "Invalid params" (vero:
min_trade_amount=1, il passo e' intero) -> 933 rifiutato per fondi con $1.541 disponibili (falso
indizio) -> 100 passa e 300 no (sembrava un tetto di size: era l'ask che si muoveva in mezzo) ->
anche 1 rifiutato otto volte (sembrava rate-limit: era sempre l'ask). Chi rilegge non deve rifare
questo giro. Misurato subito dopo: a raffica il venue rifiuta anche 1 USDE,
otto volte di fila, mentre 100 era appena passato. Il messaggio `not_enough_funds_in_currency` e'
FUORVIANTE il vincolo e' la cadenza (regolamento spot asincrono / rate-limit), non il saldo ne'
la size. Percio' si SPAZIANO gli ordini (`--pausa`) e si fa backoff sul TEMPO, riducendo il blocco
solo dopo rifiuti ripetuti. Dimezzare la size a ogni rifiuto, come faceva la prima versione, e' la
reazione sbagliata al sintomo giusto: consuma i tentativi senza toccare la causa.
uv run python scripts/live/usde_convert.py --quota 0.64 # piano, nessun ordine (0,64 = bersaglio di cuscino_watch)
uv run python scripts/live/usde_convert.py --quota 0.64 --esegui # manda l'ordine
"""
from __future__ import annotations
import argparse
import json
import math
import sys
import time
from datetime import datetime, timezone
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
from src.live import usde as U # noqa: E402
from src.live.deribit import DeribitRead # noqa: E402
LOG = ROOT / "data" / "live" / "usde_convert.jsonl"
PX_MIN, PX_MAX = 0.995, 1.005 # banda di sicurezza sul prezzo (26/08)
QUOTA_HARD_MAX = 0.95 # gate USDE-01: "mai 100%"
MIN_ORDER_USD = 5.0
TOLL = 0.005 # entro mezzo punto di quota si considera gia' a bersaglio
EPS_CUSCINO = 0.01 # 1 centesimo: il criterio e' ">= cuscino", e una quota derivata DAL
# cuscino ci cade sopra esatta (0.30*E da entrambi i lati). Senza
# questo epsilon il rumore float boccia il caso di pareggio.
# Il cuscino di regolamento vive in src/live/usde.py dal 10/09 (issue #3): cuscino_watch lo
# sorveglia ogni ora e questo script lo rispetta a ogni piano — due lettori, UNA formula (P1).
# L'alias resta per r0906_usde_tetto_sonda, che lo importa da qui.
cuscino_richiesto_usd = U.cuscino_richiesto_usd
def book_top(timeout: float = 5.0) -> tuple[float | None, float | None]:
"""Miglior bid/ask dello spot USDE_USDC dall'API PUBBLICA Deribit (tokenless: il gateway non
espone get_order_book). -> (bid, ask), (None, None) se non leggibile. Mai solleva.
NON e' il prezzo di `usde.valuta`: quello e' l'INDICE e serve a marcare il collaterale. Questo
serve a far INCROCIARE l'ordine, ed e' l'unico che il matching engine guarda."""
try:
import requests
r = requests.get("https://www.deribit.com/api/v2/public/get_order_book",
params={"instrument_name": U.SPOT, "depth": 5}, timeout=timeout).json()
res = r.get("result", r)
bid = float(res["bids"][0][0]) if res.get("bids") else None
ask = float(res["asks"][0][0]) if res.get("asks") else None
return bid, ask
except Exception:
return None, None
def piano(eq_usdc: float, eq_usde: float, px: float, quota_target: float,
equity_tot: float, bid: float | None = None, ask: float | None = None) -> dict:
"""PURA. -> piano di conversione, con verdetto e motivo. Non manda niente."""
val_usde, _ = U.valuta(eq_usde, px)
quota_ora = val_usde / equity_tot if equity_tot > 0 else 0.0
val_target = quota_target * equity_tot
delta_usd = val_target - val_usde # >0 compra USDE, <0 vende
side = "buy" if delta_usd > 0 else "sell"
# MARCABILE CON MARGINE. Il book REST pubblico e' in RITARDO sul matching engine: misurato il
# 30/08, un BUY a ask+1tick (1.0003) e' stato rifiutato e uno a 1.0004 accettato un minuto dopo,
# col book che riportava ask 1.0002x3855 in entrambi i casi. Percio' si incrocia con 5 tick di
# margine e un TETTO DURO: il fill avviene al prezzo del LIBRO, non al limite, quindi il margine
# non si paga se il book non si e' mosso, e se si e' mosso il tetto limita il danno a ~9 bps.
# Senza book leggibile si RINUNCIA — non si tira a indovinare (P5).
TICK, MARGINE = 0.0001, 5
CAP_BUY, CAP_SELL = 1.0010, 0.9990
rif = ask if side == "buy" else bid
if rif is None:
px_ord = None
elif side == "buy":
px_ord = round(min(rif + MARGINE * TICK, CAP_BUY), 4)
else:
px_ord = round(max(rif - MARGINE * TICK, CAP_SELL), 4)
# PASSO DI SIZE = 1 USDE, misurato sul venue (30/08): 500 e 10 accettati, 943.98 e 933.99
# rifiutati con "Invalid params". `get_instrument` non e' esposto dal gateway (404), quindi il
# passo e' EMPIRICO e dichiarato tale. Si quantizza per DIFETTO: meno USDE = piu' USDC, cioe'
# dal lato del cuscino di regolamento. Sul lato vendita il difetto va nello stesso verso.
amount = float(math.floor(abs(delta_usd) / (px_ord or px)))
cfg = U.cfg()
cap_frac = float(cfg.get("venue_cap_frac") or 0)
cap_usd = cap_frac * equity_tot if cap_frac else None
cuscino, come = cuscino_richiesto_usd(equity_tot)
usdc_dopo = eq_usdc - delta_usd
motivi = []
if px_ord is None:
motivi.append("book USDE_USDC non leggibile — non si prezza l'ordine sull'indice (l'indice"
" marca il collaterale, il book prezza lo scambio)")
elif not (PX_MIN <= px_ord <= PX_MAX):
motivi.append(f"prezzo d'ordine {px_ord:.4f} fuori banda [{PX_MIN}, {PX_MAX}]")
if not (PX_MIN <= px <= PX_MAX):
motivi.append(f"prezzo {px:.4f} fuori banda [{PX_MIN}, {PX_MAX}]")
if not (0.0 <= quota_target <= QUOTA_HARD_MAX):
motivi.append(f"quota {quota_target:.0%} fuori dal tetto HARD {QUOTA_HARD_MAX:.0%} (gate USDE-01)")
if abs(quota_ora - quota_target) <= TOLL:
motivi.append(f"gia' a bersaglio ({quota_ora:.1%} vs {quota_target:.0%}, toll {TOLL:.1%})")
if abs(delta_usd) < MIN_ORDER_USD:
motivi.append(f"delta ${abs(delta_usd):,.2f} sotto il minimo ${MIN_ORDER_USD:,.2f}")
# il tetto del venue limita l'ACQUISTO di USDE: una VENDITA va sempre verso il tetto, e bloccarla
# renderebbe morta la riconversione automatica di cuscino_watch (revisione fable 10/09)
if side == "buy" and cap_usd is not None and val_target > cap_usd + EPS_CUSCINO:
motivi.append(
f"TETTO DEL VENUE: bersaglio ${val_target:,.2f} sopra ${cap_usd:,.2f} "
f"({cap_frac:.1%} dell'equity, misurato {cfg.get('venue_cap_misurato')}). "
f"Massimo raggiungibile: quota {cap_frac:.1%}")
if usdc_dopo < cuscino - EPS_CUSCINO:
motivi.append(f"cuscino di regolamento: USDC dopo ${usdc_dopo:,.2f} < ${cuscino:,.2f} richiesti ({come})")
return {"cap_usd": cap_usd, "cap_frac": cap_frac,
"quota_ora": quota_ora, "quota_target": quota_target, "val_usde": val_usde,
"equity_tot": equity_tot, "delta_usd": delta_usd, "side": side, "amount": amount,
"px": px, "px_ordine": px_ord, "usdc_dopo": usdc_dopo, "cuscino": cuscino,
"cuscino_come": come, "ok": not motivi, "motivi": motivi}
def leggi_stato(c: DeribitRead) -> tuple[float, float, float | None, str]:
eq_usdc = float(c.account_summary("USDC")["equity"])
try:
eq_usde = float(c.account_summary("USDE")["equity"])
except Exception:
eq_usde = 0.0
px, fonte = U.prezzo(client=c)
return eq_usdc, eq_usde, px, fonte
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--quota", type=float, required=True, help="quota USDE bersaglio, es. 0.70")
ap.add_argument("--esegui", action="store_true", help="manda l'ordine (senza: solo il piano)")
ap.add_argument("--chunk", type=float, default=100.0, help="blocco massimo per ordine (USDE)")
ap.add_argument("--pausa", type=float, default=12.0, help="secondi fra un ordine e il successivo")
a = ap.parse_args()
c = DeribitRead()
eq_usdc, eq_usde, px, fonte = leggi_stato(c)
if px is None:
print(" ✗ prezzo USDE non leggibile — non si converte al buio (P5)")
return 2
val_usde, _ = U.valuta(eq_usde, px)
equity_tot = eq_usdc + val_usde
bid, ask = book_top()
p = piano(eq_usdc, eq_usde, px, a.quota, equity_tot, bid, ask)
print("=" * 78)
print(f" USDE CONVERT — {datetime.now(timezone.utc):%Y-%m-%dT%H:%M:%SZ}"
f" {'ESECUZIONE' if a.esegui else 'PIANO (dry-run)'}")
print("=" * 78)
print(f" prezzo : indice {px:.4f} ({fonte}) · book bid {bid} / ask {ask}"
f" -> ordine @ {p['px_ordine']}, banda [{PX_MIN}, {PX_MAX}]")
print(f" equity : USDC ${eq_usdc:,.2f} + USDE {eq_usde:,.4f} (${val_usde:,.2f})"
f" = ${equity_tot:,.2f}")
print(f" quota : {p['quota_ora']:.1%} -> {p['quota_target']:.0%}"
+ (f" · tetto venue {p['cap_frac']:.1%} = ${p['cap_usd']:,.2f}" if p.get("cap_usd") else ""))
print(f" ordine : {p['side'].upper()} {p['amount']:,.2f} USDE_USDC @ limit {p['px_ordine']:.4f}"
f" (${abs(p['delta_usd']):,.2f})")
slack = p["usdc_dopo"] - p["cuscino"]
print(f" USDC dopo : ${p['usdc_dopo']:,.2f} · cuscino richiesto ${p['cuscino']:,.2f}"
f" ({p['cuscino_come']})")
print(f" margine : ${slack:+,.2f} di slack sul cuscino"
+ (" ⚠️ la quota e' ESATTAMENTE sul limite: zero slack" if abs(slack) <= EPS_CUSCINO else ""))
for m in p["motivi"]:
print(f"{m}")
if not p["ok"]:
print(" → NON si converte.")
return 1
if not a.esegui:
print(" → piano valido. Rilancia con --esegui per mandarlo.")
return 0
LOG.parent.mkdir(parents=True, exist_ok=True)
chunk, fatti, tot_filled, prezzi, falliti = a.chunk, [], 0.0, [], 0
for _ in range(60):
eq_usdc, eq_usde, px, _f = leggi_stato(c)
if px is None:
print(" ✗ prezzo non piu' leggibile — mi fermo qui"); break
val, _ = U.valuta(eq_usde, px)
bid, ask = book_top()
pp = piano(eq_usdc, eq_usde, px, a.quota, eq_usdc + val, bid, ask)
if not pp["ok"]:
print(f" · fine: {pp['motivi'][0]}")
break
amt = float(min(pp["amount"], math.floor(chunk)))
if amt < 1:
print(" ✗ blocco sceso sotto 1 USDE — mi fermo"); break
resp = c._unwrap(c._post("/mcp-deribit/tools/place_order",
{"instrument_name": U.SPOT, "side": pp["side"], "amount": amt,
"type": "limit", "price": pp["px_ordine"],
"label": f"usde-quota-{a.quota:.2f}"})) or {}
if not isinstance(resp, dict) or resp.get("error") or "order" not in resp:
err = resp.get("error", resp) if isinstance(resp, dict) else resp
falliti += 1
attesa = min(a.pausa * (2 ** falliti), 90.0) # backoff sul TEMPO, non sulla taglia
if falliti >= 3: # solo allora sospetto la size
chunk = max(1.0, math.floor(chunk / 2))
print(f" · rifiutato {amt:,.0f}: {err} -> attendo {attesa:.0f}s"
+ (f", blocco a {chunk:,.0f}" if falliti >= 3 else ""))
time.sleep(attesa)
with LOG.open("a") as f:
f.write(json.dumps({"ts": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
"rifiutato": err, "amount": amt, "nuovo_chunk": chunk}) + "\n")
if falliti >= 8:
print(" ✗ troppi rifiuti consecutivi — mi fermo"); break
continue
falliti = 0
order, trades = resp["order"], resp.get("trades", []) or []
filled = float(order.get("filled_amount") or 0) or sum(float(t.get("amount", 0) or 0) for t in trades)
tot_filled += filled
prezzi += [(float(t["price"]), float(t["amount"])) for t in trades if t.get("price") and t.get("amount")]
fatti.append({"order_id": order.get("order_id"), "state": order.get("order_state"),
"amount": amt, "filled": filled})
print(f" · {pp['side']} {amt:,.0f} -> {order.get('order_state')} filled {filled:,.0f}"
f" (quota {pp['quota_ora']:.1%} -> bersaglio {a.quota:.0%})")
if filled <= 0:
print(" ✗ ordine accettato ma non fillato — mi fermo"); break
time.sleep(a.pausa)
px_med = sum(q * v for q, v in prezzi) / sum(v for _, v in prezzi) if prezzi else None
eq_usdc, eq_usde, px, _f = leggi_stato(c)
val, _ = U.valuta(eq_usde, px)
tot = eq_usdc + val
print(f"\n ESITO : {tot_filled:,.0f} USDE in {len(fatti)} ordini"
+ (f" @ {px_med:.4f} medio" if px_med else ""))
print(f" conto : USDC ${eq_usdc:,.2f} + USDE {eq_usde:,.4f} (${val:,.2f}) = ${tot:,.2f}")
print(f" quota : {val / tot:.2%} (bersaglio {a.quota:.0%})")
with LOG.open("a") as f:
f.write(json.dumps({"ts": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
"quota_target": a.quota, "tot_filled": tot_filled, "px_medio": px_med,
"ordini": fatti, "quota_finale": val / tot if tot else None,
"usdc": eq_usdc, "usde": eq_usde}) + "\n")
print(f" registrato : {LOG.relative_to(ROOT)}")
return 0 if tot_filled > 0 else 1
if __name__ == "__main__":
raise SystemExit(main())
+387
View File
@@ -0,0 +1,387 @@
#!/usr/bin/env python
"""usde_watch.py — sorveglianza giornaliera del collaterale USDE: reward, depeg, quota. SOLA LETTURA.
PERCHE' ESISTE. Dal 2026-08-26 il conto tiene USDE come collaterale a rendimento (test di
eligibilita': 500 USDE @ 1.0003, regola pre-registrata nel diario 2026-08-26-usde-analisi).
Tre cose vanno sorvegliate e nessuna aveva un posto:
1. i REWARD giornalieri (~12:00 UTC) sono la ragione della posizione, e il gateway non
espone il Transaction Log: si rilevano per DELTA di equity USDE al netto dei trade spot.
Metodo DICHIARATO, coi suoi limiti: un delta con trade nel mezzo si riporta con
`con_trade=true` e se i trade non sono leggibili il delta resta NON ATTRIBUITO invece
di essere inventato (P12: una riparazione silenziosa e' un'invenzione).
2. il DEPEG soglie e criteri in config/live.json `_nota_usde` (P6: dichiarati prima):
warn = fuori dalla banda operativa dello spot e oltre il clamp per-fonte dell'indice;
crit = meta' del buffer di haircut consumata.
3. la QUOTA sul totale N4: la quota e' l'unica leva contro il rischio emittente
(-100% = 25 anni di resa, non recuperabile). Sopra `quota_max_frac` allerta, e vale
anche per deriva PASSIVA: il libro perde -> la quota sale da sola.
VERDETTO DI ELIGIBILITA' (regola pre-registrata il 26/08, PRIMA dell'esito): >=1 reward
entro il 29/08 -> IDONEO (si apre la decisione di quota, che e' dell'OPERATORE); zero reward
alla lettura del 29/08 dopo la finestra delle 12:00 UTC -> NON IDONEO, si riconverte e la
pista si chiude. Questo script APPLICA la regola, non la decide.
LIMITE DICHIARATO (P6: la latenza si confronta con la durata del fenomeno). Cadenza
giornaliera (cron 12:35 UTC, dopo la finestra reward): il depeg Binance del 10/10/2025 duro'
~8h, questa sorveglianza puo' MANCARLO. Accettato perche' la protezione di SIZING e' oraria
per costruzione (shadow._collaterale_usde legge l'indice a ogni giro del book) e il danno e'
limitato dalla quota. Qui si ALLERTA, non si protegge.
ALLARMI (P9: il massimo si spende per l'evento vero): 🚨 solo depeg<crit, ripetuto finche'
attivo; per warn/quota/BLIND SOLO alla transizione (il giorno che compaiono); 📌 per il
verdetto IDONEO. BLIND = conto non leggibile, registrato e detto (P5: "non vedo" non e'
"va tutto bene"). La serie data/live/usde_watch.jsonl e' dentro il perimetro di backup ed
e' sorvegliata da monitor_health (un watch fermo = niente allarmi depeg, e il silenzio
si legge come "va tutto bene").
uv run python scripts/live/usde_watch.py # report completo
uv run python scripts/live/usde_watch.py --quiet # stampa/allerta solo transizioni
"""
from __future__ import annotations
import json
import sys
from datetime import datetime, timezone
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
from src.live import usde as U # noqa: E402
from src.live.notifier import notify # noqa: E402
STATE = ROOT / "data" / "live" / "usde_watch.jsonl"
EPS_REWARD = 1e-3 # USDE: sotto e' rumore di lettura; il reward atteso a $500 e' ~0.055/giorno
# --- finestra di eligibilita', DALLA REGOLA PRE-REGISTRATA (diario 2026-08-26-usde-analisi) ---
FILL_TS = "2026-08-26T13:04:32Z" # conversione eseguita: 500 USDE @ 1.0003
VERDETTO_DOPO = "2026-08-29T12:30:00Z" # ultima finestra reward del 29/08 conclusa
# La QUOTA e' dell'operatore, e il 2026-08-29 e' stata RINVIATA a lunedi' 31/08 per decidere su
# piu' di una finestra invece che su una (il 27/08 aveva pagato ZERO, il 28/08 +0.052 USDE: fra
# le due letture ci sono 3,80% e 1,90% annuo, cioe' tutta la differenza che conta). N9: una
# decisione rinviata senza un promemoria e' rinviata per sempre — da lunedi' il watch lo dice
# ogni giorno finche' la riga non viene tolta.
DECISIONE_QUOTA_DAL = "2026-08-31" # lunedi': si decide la quota
EPS_GIORNI = 0.5 # sotto mezza giornata l'APR non si annualizza
# --- lettura dei trade spot: i DUE limiti del canale, misurati il 2026-09-10 -----------------
# (1) `count` massimo di Deribit per get_user_trades_by_instrument: 1000. Il 07/09 il lettore
# chiedeva limit=50 e la sonda del 06/09 aveva fatto 54 ordini: i 4 non letti (400 USDE)
# sono finiti nel «reward» (issue #2). Se il venue restituisce ESATTAMENTE `limit` righe la
# lista puo' essere troncata e la somma NON e' leggibile (P12: non si attribuisce).
# (2) il gateway chiama l'endpoint senza `historical`/timestamp: Deribit restituisce solo le
# ultime 24h — DOCUMENTATO ("Accessing historical trades and orders using API": recent
# trades 24h, orders 30 min, count max 1000) e misurato (il fill 08/09 06:47Z invisibile
# alle 12:55Z del 10/09, quello del 09/09 21:47Z visibile). Se l'ultima lettura e' piu'
# vecchia della finestra, i trade fra la lettura e il bordo sono INVISIBILI e la somma non
# e' leggibile.
# ⚠️ BUCO DICHIARATO (D5): il cron gira ogni 24h + pochi secondi; con la tolleranza di 5 min
# i trade nei primi ≤5 min dopo la lettura precedente sarebbero invisibili e sommati come
# «leggibile». Innocuo: nessun attrezzo converte USDE alle 12:35 UTC, e senza tolleranza il
# giro quotidiano sarebbe SEMPRE «non leggibile» e il tasso non avrebbe mai finestre nuove.
TRADE_LIMIT = 1000
FINESTRA_GATEWAY_MS = 24 * 3_600_000
TOLL_FINESTRA_MS = 5 * 60_000
def _parse(ts: str) -> datetime:
return datetime.fromisoformat(ts.replace("Z", "+00:00"))
def leggi(path: Path = STATE) -> list[dict]:
if not path.exists():
return []
out = []
for ln in path.read_text().splitlines():
ln = ln.strip()
if ln:
out.append(json.loads(ln))
return out
def analizza(prev: dict | None, eq_now: float, trades_usde: float | None) -> dict:
"""PURA. Reward per delta di equity USDE al netto dei trade spot fra le due letture.
prev senza equity (primo giro, o giro BLIND) -> baseline: nessun delta da attribuire.
trades_usde None = storia trade non leggibile -> il delta si riporta ma NON si attribuisce
a reward (P12): meglio un giorno di latenza che un reward inventato."""
if prev is None or prev.get("eq_usde") is None:
return dict(delta=None, reward_stimato=None, reward_rilevato=False, con_trade=None)
delta = round(eq_now - float(prev["eq_usde"]), 8)
if trades_usde is None:
return dict(delta=delta, reward_stimato=None, reward_rilevato=False, con_trade=None)
stima = round(delta - trades_usde, 8)
return dict(delta=delta, reward_stimato=stima,
reward_rilevato=bool(stima > EPS_REWARD),
con_trade=bool(abs(trades_usde) > 0))
def verdetto(records: list[dict], now: datetime) -> tuple[str, str]:
"""PURA. Applica la regola pre-registrata del 26/08. -> (stato, motivo)."""
for r in records:
if r.get("reward_rilevato"):
return "IDONEO", (f"reward rilevato il {r.get('data')} "
f"(+{r.get('reward_stimato')} USDE)")
if now >= _parse(VERDETTO_DOPO):
return "NON_IDONEO", "zero reward entro la finestra del 29/08 -> riconvertire, pista chiusa"
return "IN_ATTESA", f"finestre reward ~12:00 UTC, verdetto dopo {VERDETTO_DOPO}"
def rendimento(records: list[dict]) -> dict:
"""PURA. Il rendimento sulle finestre OSSERVATE, col loro NUMERO accanto.
E' il numero che serve alla decisione di quota, e l'unico modo onesto di darlo e' con `n`
attaccato: un pagamento non e' un tasso. Il denominatore e' il TEMPO VERO fra la prima e
l'ultima lettura misurabile, non il conteggio delle finestre — cosi' una finestra che non
paga abbassa la stima invece di sparire (P5: il silenzio in una serie di ritorni si legge
come zero, e qui e' proprio uno zero).
Si escludono le letture con trade nel mezzo (`con_trade`): quel delta non e' solo reward, e
una riparazione silenziosa sarebbe un'invenzione (P12).
"""
usabili = [r for r in records
if r.get("reward_stimato") is not None and not r.get("con_trade")]
pagate = [r for r in usabili if r.get("reward_rilevato")]
if not usabili:
return dict(apr=None, finestre=0, pagate=0, giorni=0.0, tot=0.0,
nota="nessuna finestra misurabile")
tot = float(sum(r["reward_stimato"] for r in usabili))
eq = [float(r["eq_usde"]) for r in usabili if r.get("eq_usde")]
eq_med = sum(eq) / len(eq) if eq else 0.0
# tempo vero coperto: dalla lettura PRIMA della prima misurabile, all'ultima
i0 = records.index(usabili[0])
t0 = records[max(0, i0 - 1)]["ts"]
giorni = (usabili[-1]["ts"] - t0) / 86_400_000.0
if giorni < EPS_GIORNI or eq_med <= 0:
return dict(apr=None, finestre=len(usabili), pagate=len(pagate), giorni=giorni, tot=tot,
nota=f"solo {giorni:.2f} giorni osservati: non si annualizza")
apr = tot / eq_med / giorni * 365.0
return dict(apr=apr, finestre=len(usabili), pagate=len(pagate), giorni=giorni, tot=tot,
nota=f"{len(pagate)}/{len(usabili)} finestre hanno pagato")
def quota_da_decidere(now: datetime, verdetto_corrente: str) -> bool:
"""PURA. La decisione di quota e' dovuta? Solo se il conto e' IDONEO e il rinvio e' scaduto."""
if verdetto_corrente != "IDONEO":
return False
return now.date().isoformat() >= DECISIONE_QUOTA_DAL
def condizioni(rec: dict | None, c: dict) -> set[str]:
"""PURA. Condizioni di allerta attive su un record."""
if not rec:
return set()
if rec.get("stato") == "BLIND":
return {"BLIND"}
att = set()
px = rec.get("px")
if px is not None:
if px < c["depeg_crit"]:
att.add("DEPEG_CRIT")
elif px < c["depeg_warn"]:
att.add("DEPEG_WARN")
q = rec.get("quota")
if q is not None and q > c["quota_max_frac"]:
att.add("QUOTA_OVER")
return att
def _safe_client():
try:
from src.live.deribit import DeribitRead
return DeribitRead()
except Exception:
return None
def trade_copertura(n_righe: int, prev_ts_ms: int, now_ms: int,
limit: int = TRADE_LIMIT, finestra_ms: int = FINESTRA_GATEWAY_MS,
toll_ms: int = TOLL_FINESTRA_MS) -> str | None:
"""PURA. None se la lista di trade COPRE l'intervallo (prev, now]; altrimenti il motivo per cui
NON lo copre e allora la somma non si usa (P12: un reward calcolato su una lista incompleta
e' un'invenzione, il 07/09 valeva 400 USDE)."""
if n_righe >= limit:
return f"lista troncata: {n_righe} righe = limite {limit} del venue"
if now_ms - prev_ts_ms > finestra_ms + toll_ms:
ore = (now_ms - prev_ts_ms) / 3_600_000
return (f"ultima lettura {ore:.1f}h fa, oltre la finestra di {finestra_ms / 3_600_000:.0f}h "
f"del gateway: i trade piu' vecchi sono invisibili")
return None
def _trades_spot_da(client, ts_ms: int, now_ms: int | None = None
) -> tuple[float | None, int | None, str | None]:
"""Somma con segno (buy +, sell -) dell'USDE scambiato spot dopo ts_ms.
-> (somma, n, motivo). somma None = non leggibile, e `motivo` dice perche' (P4)."""
if client is None:
return None, None, "gateway non raggiungibile"
try:
rows = client.trade_history(U.SPOT, limit=TRADE_LIMIT)
except Exception as e:
return None, None, f"trade_history: {type(e).__name__}"
if now_ms is None:
now_ms = int(datetime.now(timezone.utc).timestamp() * 1000)
motivo = trade_copertura(len(rows), ts_ms, now_ms)
if motivo is not None:
return None, None, motivo
tot, n = 0.0, 0
for t in rows:
if int(t.get("timestamp") or 0) <= ts_ms:
continue
amt = float(t.get("amount") or 0)
tot += amt if (t.get("direction") or "").lower() == "buy" else -amt
n += 1
return round(tot, 8), n, None
def main() -> int:
quiet = "--quiet" in sys.argv
c = U.cfg()
records = leggi()
prev = records[-1] if records else None
now = datetime.now(timezone.utc)
client = _safe_client()
stato, motivo_blind, eq_usde, eq_usdc = "OK", None, None, None
if client is None:
stato, motivo_blind = "BLIND", "gateway non raggiungibile"
else:
try:
eq_usde = float(client.account_summary("USDE").get("equity") or 0)
except Exception as e:
stato, motivo_blind = "BLIND", f"conto USDE non leggibile ({type(e).__name__})"
try:
eq_usdc = float(client.account_summary("USDC").get("equity") or 0)
except Exception:
eq_usdc = None # quota non computabile: dichiarato, non 0
px, px_fonte = U.prezzo(client)
usd = None
if eq_usde is not None:
usd, _nota = U.valuta(eq_usde, px)
trades_usde, n_trades, trades_motivo = (None, None, None)
an = dict(delta=None, reward_stimato=None, reward_rilevato=False, con_trade=None)
if stato == "OK" and prev is not None and prev.get("eq_usde") is not None:
trades_usde, n_trades, trades_motivo = _trades_spot_da(
client, int(prev["ts"]), int(now.timestamp() * 1000))
an = analizza(prev, eq_usde, trades_usde)
quota = None
if usd is not None and eq_usdc is not None and (usd + eq_usdc) > 0:
quota = round(usd / (usd + eq_usdc), 4)
rec = dict(
ts=int(now.timestamp() * 1000),
data=now.strftime("%Y-%m-%dT%H:%M:%SZ"),
stato=stato, motivo_blind=motivo_blind,
eq_usde=eq_usde, px=px, px_fonte=px_fonte, usd=usd,
eq_usdc=eq_usdc, quota=quota,
trades_usde=trades_usde, n_trades=n_trades, trades_motivo=trades_motivo, **an,
)
v, v_motivo = verdetto(records + [rec], now)
rec["verdetto"], rec["verdetto_motivo"] = v, v_motivo
STATE.parent.mkdir(parents=True, exist_ok=True)
with STATE.open("a") as fh:
fh.write(json.dumps(rec) + "\n")
# --- allarmi: transizioni (warn/quota/blind), ripetizione solo per il crit ---
att_now, att_prev = condizioni(rec, c), condizioni(prev, c)
nuove = att_now - att_prev
inviati = []
if "DEPEG_CRIT" in att_now:
notify("🚨 USDE DEPEG", {"indice": px, "soglia": c["depeg_crit"],
"collaterale": f"${usd:,.0f}" if usd else "?",
"azione": "valutare riconversione — la quota e' il controllo (N4)"})
inviati.append("DEPEG_CRIT")
if "DEPEG_WARN" in nuove:
notify("⚠️ USDE sotto la pari", {"indice": px, "soglia": c["depeg_warn"],
"nota": "entra gia' nel sizing orario del book"})
inviati.append("DEPEG_WARN")
if "QUOTA_OVER" in nuove:
notify("⚠️ quota USDE sopra il tetto", {"quota": f"{quota:.1%}" if quota else "?",
"tetto": f"{c['quota_max_frac']:.0%}",
"nota": "anche una deriva passiva conta (il libro perde -> quota sale)"})
inviati.append("QUOTA_OVER")
if "BLIND" in nuove:
notify("⚠️ usde_watch BLIND", {"motivo": motivo_blind or "?",
"nota": "'non vedo' non e' 'va tutto bene' (P5)"})
inviati.append("BLIND")
v_prev = prev.get("verdetto") if prev else "IN_ATTESA"
if v != v_prev:
if v == "IDONEO":
notify("📌 USDE: conto IDONEO ai reward", {"dettaglio": v_motivo,
"prossimo passo": "decisione di quota dell'operatore (tetto allerta 50%)"})
elif v == "NON_IDONEO":
notify("⚠️ USDE: conto NON idoneo", {"dettaglio": v_motivo})
inviati.append(f"VERDETTO->{v}")
# La QUOTA e' dell'operatore ed e' stata rinviata a lunedi': da quel giorno lo si dice OGNI
# giorno, non solo alla transizione. Ripetersi qui e' voluto — non e' un allarme che chiede
# un'azione impossibile (P14), e' una domanda che da lunedi' si puo' finalmente rispondere,
# con accanto il numero per rispondere. Si smette togliendo `DECISIONE_QUOTA_DAL`.
rend = rendimento(records + [rec])
if quota_da_decidere(now, v):
apr = "n/d" if rend["apr"] is None else f"{rend['apr']*100:.2f}% annuo"
notify("📌 USDE: la decisione di QUOTA e' dovuta", {
"quota attuale": f"{quota:.1%}" if quota is not None else "n/d",
"tetto di allerta": f"{c['quota_max_frac']:.0%} (mai 100%: R1 emittente non recuperabile)",
"rendimento osservato": f"{apr}{rend['nota']}, {rend['giorni']:.1f} giorni",
"nota": "un pagamento non e' un tasso: leggere l'APR con il suo n"})
inviati.append("QUOTA_DA_DECIDERE")
cambiato = bool(inviati) or v != v_prev
if not quiet or cambiato:
print("=" * 78)
print(f" USDE WATCH — {rec['data']} (config: {c['fonte']})")
print("=" * 78)
if stato == "BLIND":
print(f" stato : BLIND — {motivo_blind}")
else:
print(f" equity USDE : {eq_usde:,.4f} @ {px if px is not None else 'n/d'}"
f" ({px_fonte}) -> ${usd:,.2f}" if usd is not None else " equity USDE : n/d")
if quota is not None:
# Il conto e' Segregated Standard Margin (letto sulla pagina margini il 31/08):
# l'USDE NON fa margine per i perp USDC-settled, e l'haircut non si applica. Il
# margine del book e' il solo silo USDC. Si stampa anche cosa DAREBBE il cross,
# perche' e' la differenza che rende la cosa una decisione invece che un dettaglio.
cross = eq_usdc + usd * (1.0 - c["haircut"])
print(f" quota : {quota:.1%} del totale (tetto allerta {c['quota_max_frac']:.0%})")
print(f" margine : ~${eq_usdc:,.0f} — il solo USDC (conto SEGREGATO: l'USDE non"
f" fa margine). Con cross X:SM sarebbero ~${cross:,.0f} (haircut"
f" {c['haircut']:.0%})")
if an["delta"] is not None:
attr = (f"non attribuibile (trade non leggibili: {trades_motivo})"
if an["reward_stimato"] is None
else f"reward stimato {an['reward_stimato']:+.6f} USDE"
+ (" [con trade nel mezzo]" if an["con_trade"] else ""))
print(f" delta : {an['delta']:+.6f} USDE dall'ultima lettura -> {attr}")
print(f" verdetto : {v}{v_motivo}")
if rend["finestre"]:
apr = "n/d" if rend["apr"] is None else f"{rend['apr']*100:.2f}% annuo"
print(f" rendimento : {apr} su {rend['giorni']:.1f} giorni — {rend['nota']}"
f" (tot {rend['tot']:+.6f} USDE)")
if rend["finestre"] < 4:
print(f" ⚠️ n={rend['finestre']}: un pagamento non e' un tasso")
if v == "IDONEO" and not quota_da_decidere(now, v):
print(f" quota : decisione RINVIATA a {DECISIONE_QUOTA_DAL} (operatore)")
if inviati:
print(f" allarmi : {', '.join(inviati)}")
return 0
USO = """uso: usde_watch.py [--quiet]
usde_watch.py sorveglianza giornaliera del collaterale USDE: reward, depeg, quota. SOLA LETTURA.
uv run python scripts/live/usde_watch.py # report completo
uv run python scripts/live/usde_watch.py --quiet # stampa/allerta solo transizioni
Dettaglio nel docstring in testa al file."""
if __name__ == "__main__":
from src.live.cli import valida
valida("usde_watch.py", USO, flag=("--quiet",), con_valore=())
sys.exit(main())
+173
View File
@@ -0,0 +1,173 @@
#!/usr/bin/env python
"""venue_news.py — legge cosa il venue ANNUNCIA. SOLA LETTURA, non blocca mai nulla.
PERCHE' ESISTE (2026-08-31). Il debito #8 dice, testuale: *«Resta scoperto il Rulebook vero e
proprio: la sonda legge se il venue RISPONDE, non cosa il venue ANNUNCIA.»* Questa e' quella
meta'. In una settimana quattro cambi di policy Deribit materiali sono arrivati **a mano**, perche'
l'operatore ha incollato dei link — non perche' qualcosa li avesse visti:
* tetto sull'USDE (~31,2% dell'equity) * esclusione dell'Italia dai reward USDC
* permesso mancante su BUIDL * fine della Proof of Reserves quotidiana
E il caso che decide la questione: le **specifiche dei perpetual lineari USDC** sono cambiate il
18/08, annunciate il **14/08**. `check_specs()` rileva la deriva DOPO che e' avvenuta ed e' nato
proprio da quella svista; il feed l'avrebbe detto **quattro giorni prima**. Rilevare a posteriori e
sapere in anticipo non sono la stessa cosa: la prima volta e' andata bene solo perche' i cambi
erano RIDUZIONI (un valore piu' grosso resta conforme). Il giorno che Deribit ALZA un minimo, gli
ordini vengono rifiutati.
COSA E' E COSA NON E'. E' un promemoria che legge un feed RSS pubblico e dice *«e' uscito questo,
guardalo»*. **Non interpreta e non decide**: nessun automatismo su un testo di marketing (P13 una
guardia sui NUMERI non copre il RAGIONAMENTO, e la prosa si legge come opinione di un lettore
fallibile). L'unica cosa che classifica e' l'URGENZA, e lo fa su parole DERIVATE dal codice
sorvegliato, non ridichiarate qui (P1): gli strumenti vengono da `deribit._CONTRACT`, le valute di
collaterale da `config/live.json`. Chi aggiunge uno strumento o una valuta allarga da solo la
sorveglianza, senza toccare questo file.
uv run python scripts/live/venue_news.py # report + allerta sui nuovi
uv run python scripts/live/venue_news.py --quiet # solo allerta (per il cron)
"""
from __future__ import annotations
import argparse
import html
import json
import re
import sys
from datetime import datetime, timezone
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
from src.live.notifier import notify # noqa: E402
FEED = "https://insights.deribit.com/exchange-updates/feed/"
STATE = ROOT / "data" / "live" / "venue_news.jsonl"
# Parole che alzano un annuncio a 🚨. Le prime sono FISSE (categorie di rischio che ci toccano
# per costruzione); le altre si DERIVANO dal codice, sotto.
_CHIAVI_FISSE = (
"contract specification", "margin", "collateral", "fee", "delist", "discontinu",
"reserve", "maintenance", "liquidation", "settlement", "withdrawal", "reward",
"jurisdiction", "eligib", "insurance fund", "socialized", "adl", "rulebook",
)
def chiavi() -> list[str]:
"""Parole-chiave DERIVATE dal codice sorvegliato (P1), piu' quelle fisse."""
out = set(_CHIAVI_FISSE)
try:
from src.live.deribit import _CONTRACT
for nome in _CONTRACT: # BTC_USDC-PERPETUAL -> "btc", "usdc"
for pezzo in re.split(r"[_\-]", nome.lower()):
if len(pezzo) >= 3 and pezzo != "perpetual":
out.add(pezzo)
except Exception:
pass
try:
cfg = json.loads((ROOT / "config" / "live.json").read_text())
out.add(str(cfg.get("usde", {}).get("index_name", "")).split("_")[0].lower())
except Exception:
pass
return sorted(k for k in out if k)
def scarica(url: str = FEED, timeout: float = 20.0) -> str | None:
"""Feed RSS pubblico (tokenless). None se non leggibile. Mai solleva."""
try:
import requests
r = requests.get(url, timeout=timeout,
headers={"User-Agent": "PythagorasGoal/venue_news (+read-only)"})
return r.text if r.status_code == 200 else None
except Exception:
return None
def voci(xml: str) -> list[dict]:
"""PURA. Estrae (guid, titolo, data, link) dal RSS. Mai solleva."""
out = []
for blocco in re.findall(r"<item>(.*?)</item>", xml or "", re.S):
def campo(tag):
m = re.search(rf"<{tag}[^>]*>(?:<!\[CDATA\[)?(.*?)(?:\]\]>)?</{tag}>", blocco, re.S)
return html.unescape(m.group(1).strip()) if m else ""
g = campo("guid") or campo("link")
if g:
out.append({"guid": g, "titolo": campo("title"),
"data": campo("pubDate"), "link": campo("link")})
return out
def rilevante(titolo: str, ks: list[str]) -> list[str]:
"""PURA. Quali parole-chiave compaiono nel titolo."""
t = titolo.lower()
return [k for k in ks if k in t]
def gia_visti() -> set[str]:
if not STATE.exists():
return set()
out = set()
for riga in STATE.read_text().splitlines():
try:
out.add(json.loads(riga)["guid"])
except Exception:
pass
return out
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--quiet", action="store_true")
a = ap.parse_args()
xml = scarica()
if xml is None:
if not a.quiet:
print(" ✗ feed non leggibile — 'non vedo' non e' 'niente di nuovo' (P5)")
return 2
ks, viste, nuove = chiavi(), gia_visti(), []
primo_giro = not STATE.exists()
tutte = voci(xml)
STATE.parent.mkdir(parents=True, exist_ok=True)
with STATE.open("a") as f:
for v in tutte:
if v["guid"] in viste:
continue
v["match"] = rilevante(v["titolo"], ks)
v["visto_il"] = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
v["seed"] = primo_giro
f.write(json.dumps(v) + "\n")
nuove.append(v)
# Primo giro: si semina lo storico SENZA allertare (P9: l'allarme massimo non si spende
# per un arretrato di 10 voci, o non verra' letto il giorno che e' vero).
if not primo_giro:
for v in nuove:
if v["match"]:
notify("🚨 annuncio Deribit sul NOSTRO percorso",
{"titolo": v["titolo"], "tocca": ", ".join(v["match"][:6]),
"data": v["data"], "link": v["link"],
"azione": "leggerlo: questo sorvegliante NON interpreta"})
else:
notify("📌 nuovo annuncio Deribit",
{"titolo": v["titolo"], "data": v["data"], "link": v["link"]})
if not a.quiet:
print("=" * 78)
print(f" VENUE NEWS — {len(tutte)} voci nel feed, {len(nuove)} nuove"
+ (" (PRIMO GIRO: storico seminato, nessun allarme)" if primo_giro else ""))
print("=" * 78)
for v in tutte:
m = rilevante(v["titolo"], ks)
segno = "🚨" if m else ("·" if v["guid"] in viste else "📌")
print(f" {segno} {v['data'][:16]:17s} {v['titolo'][:70]}")
if m:
print(f" tocca: {', '.join(m[:8])}")
print(f"\n parole derivate dal codice: {', '.join(ks)}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+39 -15
View File
@@ -16,14 +16,37 @@ ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
from src.live.deribit import check_specs # noqa: E402
from src.live.notifier import notify # noqa: E402
from src.live.notifier import notify, ultimo_errore # noqa: E402
from src.live.venue_watch import (PERSIST_HOURS, THRESHOLD_BPS, # noqa: E402
run_once)
def _titolo(rep: dict) -> str:
"""⚠️ Il titolo dice cosa FARE, non solo cosa e' successo: chi lo legge sul telefono deve
sapere il primo passo senza aprire il repo (runbook completo in src/live/venue_watch.py).
Dal 19/08 DIPENDE dalla gravita'. Prima era sempre il 🚨 col prelievo di prova, anche per
un blocco da manutenzione annunciata: il 18/08 sono usciti quattro messaggi identici che
chiedevano di prelevare, tutti dopo che il book aveva gia' ripreso. Un allarme massimo speso
per un evento atteso e' un allarme che non verra' letto il giorno che e' vero."""
if rep.get("severity") == "warn":
return "⚠️ VENUE WATCH — Deribit non operativa, ma spiegata. Nessuna azione"
return "🚨 VENUE WATCH — possibile stress su Deribit. PRIMO PASSO: prelievo di prova"
def _manda(rep: dict) -> tuple[bool, str]:
"""Il sender iniettato in `run_once`: e' lui a decidere se i marcatori 'gia' detto' si
scrivono. `tentativi=3` perche' su questo percorso il 6,9% degli invii si perde, e qui
perderne uno significa perdere l'episodio intero."""
ok = notify(_titolo(rep), {f"alert {i+1}": a for i, a in enumerate(rep["alerts"])},
tentativi=3)
if ok:
return True, "ok"
return False, (ultimo_errore() or "motivo non registrato")
def main() -> int:
quiet = "--quiet" in sys.argv
rep = run_once()
rep = run_once(sender=_manda)
if not quiet:
print("=" * 78)
@@ -65,24 +88,25 @@ def main() -> int:
print(f" SPEC: {k}: {v}")
if rep["alerts"]:
# ⚠️ Il titolo dice cosa FARE, non solo cosa e' successo: chi lo legge sul telefono deve
# sapere il primo passo senza aprire il repo (runbook completo in src/live/venue_watch.py).
# ⚠️ Dal 19/08 il titolo DIPENDE dalla gravita'. Prima era sempre il 🚨 col prelievo di
# prova, anche per un blocco da manutenzione annunciata: il 18/08 sono usciti quattro
# messaggi identici che chiedevano di prelevare, tutti dopo che il book aveva gia'
# ripreso. Un allarme massimo speso per un evento atteso e' un allarme che non verra'
# letto il giorno che e' vero — ed e' l'unico giorno che conta.
if rep.get("severity") == "warn":
titolo = "⚠️ VENUE WATCH — Deribit non operativa, ma spiegata. Nessuna azione"
else:
titolo = ("🚨 VENUE WATCH — possibile stress su Deribit. "
"PRIMO PASSO: prelievo di prova")
notify(titolo, {f"alert {i+1}": a for i, a in enumerate(rep["alerts"])})
# L'invio l'ha gia' fatto `run_once` attraverso `_manda`: qui si stampa e si riporta
# l'ESITO. Un invio fallito si vede nel log del cron invece di sparire — e lo stato su
# disco e' stato disfatto, quindi l'ora prossima ci riprova da solo.
for a in rep["alerts"]:
print(f" ALERT: {a}")
print(f" telegram: {rep.get('invio')}")
return 2 if rep.get("severity") == "alert" else 0
return 0
USO = """uso: venue_watch.py [--quiet]
venue_watch.py runner ORARIO del tripwire di venue. Allerta su Telegram, NON blocca nulla.
uv run python scripts/live/venue_watch.py # un giro, stampa il report
uv run python scripts/live/venue_watch.py --quiet # solo in caso di allarme (per il cron)
Dettaglio nel docstring in testa al file."""
if __name__ == "__main__":
from src.live.cli import valida
valida("venue_watch.py", USO, flag=("--quiet",), con_valore=())
raise SystemExit(main())
+3
View File
@@ -9,6 +9,9 @@ lontana, che il 30/07 aveva misurato sottoprezzata ~2.3x.
piu' mispriced in RELATIVO (f_long 5.85 contro 2.23) ma pesa il **3.2%** del premio corto invece
del **18.4%**, quindi sul credito NETTO l'effetto e' minore: f_net **0.852** contro **0.718**.
Meccanismo giusto, segno della conclusione sbagliato.
(Numeri del 30/07, misurati con `cblib` che leggeva spot e DVOL di UN'ORA / fino a 24 ORE dopo
l'ingresso — debito §5.18, riparato la sera del 09/09: da allora la tabella stampata da questo
script e' la fonte, e il f canonico e' passato da 0,732 a 0,706 sulle stesse 28 coppie.)
E' pero' una misura su **16 coppie**: la differenza appaiata e' **+0.109 con IC95
[-0.047, +0.193]**, cioe' **compatibile con zero**. Serve piu' campione, e "quando ce ne sara'
+31 -2
View File
@@ -13,6 +13,10 @@ Regola che vale per ogni misura fatta con questo modulo: il f della sola gamba c
dello spread. Il modello sbaglia soprattutto sull'ala che si COMPRA (piu' OTM, IV piu' alta per
skew, prezzata dal modello a vol ATM) -> misurare una gamba sola da' la risposta sbagliata con
segno rassicurante.
Seconda regola (09/09, debito §5.18): spot e DVOL escono da qui gia' CAUSALI — `asof(ts)` da'
l'ultima chiusura nota a ts. Chi costruisce una serie di prezzi altrove usi `causale()`, non
l'indice del feed com'e' (etichettato all'apertura: D6).
"""
from __future__ import annotations
@@ -65,23 +69,48 @@ def puts(asset: str, df: pd.DataFrame | None = None) -> pd.DataFrame:
return d[(d["asset"] == asset) & (d["option_type"] == "P")]
def causale(s: pd.Series, cadenza: str) -> pd.Series:
"""Rietichetta una serie di CHIUSURE dall'APERTURA della barra all'istante in cui la chiusura
e' nota (apertura + cadenza), cosi' `s.asof(ts)` restituisce l'ultima chiusura CONOSCIUTA a ts
e mai una futura. I valori non si toccano, solo le etichette.
🚨 Perche' esiste (debito §5.18, 09/09): i feed del progetto sono etichettati all'apertura
la barra 1h `20:00` chiude col 5m delle 20:55 (verificato: chiusura 1h a T == chiusura 5m a
T+55 nel 100% delle barre) e la riga DVOL del giorno D e' la chiusura delle 23:00 di D
(verificato contro la risoluzione 1h dell'API pubblica). Senza questo spostamento `asof(ts)`
dava lo spot di UN'ORA dopo e il DVOL di FINO A 24 ORE dopo l'ingresso (D6, quinta occorrenza)."""
out = s.copy()
out.index = out.index + pd.Timedelta(cadenza)
return out
@lru_cache(maxsize=8)
def spot_series(asset: str) -> pd.Series:
"""Chiusure 1h del feed certificato, etichettate all'istante in cui sono NOTE (apertura + 1h).
`spot_series(a).asof(ts)` = ultima chiusura oraria conosciuta a `ts`. Conseguenza sul
regolamento: `asof(exp)` alle 08:00 e' la chiusura della barra 07:00, cioe' il prezzo delle
08:00 prima era il prezzo delle 09:00, un'ora DOPO la scadenza."""
from scripts.analysis.research_lab import load_tf
px = load_tf(asset, "1h")
s = pd.Series(px["close"].values.astype(float),
index=pd.to_datetime(px["timestamp"], unit="ms", utc=True)).sort_index()
s.index = pd.DatetimeIndex(s.index).as_unit("ns") # le quote bite hanno microsecondi
return s
return causale(s, "1h")
@lru_cache(maxsize=8)
def dvol_series(asset: str) -> pd.Series:
"""DVOL giornaliero (chiusura), etichettato all'istante in cui e' NOTO (giorno + 1D).
`dvol_series(a).asof(ts)` = ultima chiusura giornaliera del DVOL conosciuta a `ts`: dentro il
giorno D e' la chiusura di D1, quindi vecchia fino a 23 ore. E' il prezzo della causalita'
con un feed giornaliero; l'alternativa (DVOL orario dall'API pubblica) non e' nel feed."""
d = pd.read_parquet(RAW / f"dvol_{asset.lower()}.parquet")
s = pd.Series(d["close"].values.astype(float),
index=pd.to_datetime(d["timestamp"], unit="ms", utc=True)).sort_index()
s.index = pd.DatetimeIndex(s.index).as_unit("ns")
return s
return causale(s, "1D")
# ------------------------------------------------------------------ prezzo
+15 -1
View File
@@ -129,7 +129,21 @@ def main():
sys.exit(2)
ib = IB()
try:
ib.connect("127.0.0.1", 4002, clientId=90, timeout=15)
# `readonly=True`: questo client SCARICA STORICO e non manda ordini mai. Non e' solo
# igiene — in `ib_async.IB.connectAsync` le richieste "open orders" e "completed orders"
# esistono SOLO se il client non e' readonly (`if not readonly: reqs[...]`), e sul gateway
# paper non rispondono: ogni notte, 63 giri su 63, il log del cron si prendeva
# open orders request timed out
# completed orders request timed out
# Due righe d'errore innocue ripetute per sempre sono il modo in cui un errore VERO
# smette di farsi notare (P14). Qui si toglie la CAUSA, non si filtra il messaggio.
# ⚠️ OSSERVATO il 2026-08-28: a meta' pomeriggio il gateway NON serve storico —
# `reqHistoricalData` va in timeout e lo script stampa "0 barre (subscription?)" per ogni
# simbolo. Quattro tentativi fra le 13:05 e le 15:35 UTC, tutti a zero, con e senza
# `readonly`; il giro del cron delle 00:30 riesce ogni notte. La causa sta nel gateway,
# non qui, e non e' stata diagnosticata. Non e' pericoloso: con 0 barre lo script NON
# sovrascrive i parquet, quindi un giro fallito lascia il dato di ieri invece di romperlo.
ib.connect("127.0.0.1", 4002, clientId=90, timeout=15, readonly=True)
except Exception as e:
print(f"[CONNESSIONE FALLITA] 127.0.0.1:4002 -> {repr(e)[:120]}\n Avvia: docker compose up -d ib-gateway")
sys.exit(1)
+26 -4
View File
@@ -11,6 +11,23 @@ deflated-Sharpe 0.985, corr ~0 a tutti e 5 gli sleeve. Sopravvive a tre null fee
test di lag. NON e' nel book per tre motivi dichiarati: storia corta e monotona crescente,
`weights_tilt_null` fallito, e margine di costo sottile con slippage NON modellato.
MODIFICA DICHIARATA 2026-08-26 (diario `2026-08-26-xsr01-gate-riscritto.md`) decisa
dall'operatore PRIMA di vedere l'esito, a 58 giorni dalla data, e in direzione che STRINGE:
1. CAPITALE per il deploy: $5.000 -> $20.000. Non e' tuning sull'esito: riconcilia il gate
(scritto il 25/07) con la decisione vincolante "100% Deribit fino a $20k" (26/07), che e'
POSTERIORE e lo governa sotto $20k nessun capitale esce da Deribit, quindi il criterio
a $5k autorizzava una cosa che un'altra decisione gia' vietava (M28).
2. GAMBA HAIRCUT: il numero decisivo alla decisione e' quello dello script committato
`r0826_xsr_haircut.py` a pavimento $10 (il pavimento VERO misurato, HL-EXEC 22/08 il
libro REAL-$5000 del monitor gira a $5 ed e' ottimista per costruzione), CITATO ACCANTO
alla frazione di ordini eseguiti (P7): a $10 si esegue ~1 ordine su 5, e un haircut
piccolo con pochi ordini eseguiti non e' eseguibilita', e' un libro fermo. La SOGLIA
resta il 40% pre-registrato: nessuna soglia nuova viene scritta oggi coi dati in mano.
3. Il riferimento "in-sample 1.82" era una TERZA lente (XSR-REPRO 22/08): la lente dei gate
da' 1.79 alla scoperta. Corretto il contesto, non il criterio.
La serie forward e' quella RIGENERATA il 26/08 (advance() riparato, §5.1): stessa finestra,
stesso forward-day 0, barre finalmente VERE.
REGOLA (immutabile ogni modifica va motivata nel diario come violazione):
- Data decisione: 2026-10-23 (90 giorni forward dal 2026-07-25). Non prima.
- Metrica primaria: Sharpe annualizzato di `net_modeled` su TUTTA la finestra forward.
@@ -22,7 +39,7 @@ REGOLA (immutabile — ogni modifica va motivata nel diario come violazione):
- Esiti:
Sharpe >= 1.0 AND haircut$5000 <= 40% -> CANDIDATO SLEEVE: proporre peso {5,10}% e
giudicarlo con `weights_tilt_null` (che oggi NON passa) + maxDD combinato.
Deploy solo se il tilt-null passa E il capitale e' >= $5000.
Deploy solo se il tilt-null passa E il capitale e' >= $20.000 (mod. 26/08).
0.3 <= Sh < 1.0 -> ESTENDI una sola volta di 90g (2027-01-21).
Sharpe < 0.3 OR haircut > 40% -> RITIRO dal forward-monitor.
- Guardie accessorie: config invariata (il test `tests/test_paper_xsr.py` la blocca);
@@ -47,7 +64,8 @@ DECISION_EXT = date(2027, 1, 21)
SH_DEPLOY = 1.0
SH_RETIRE = 0.3
HAIRCUT_MAX = 0.40
IS_SHARPE_NET = 1.82 # riferimento in-sample, per contesto
CAPITALE_DEPLOY = 20_000.0 # mod. dichiarata 26/08: riconcilia col muro "100% Deribit" (26/07)
IS_SHARPE_NET = 1.79 # lente dei gate alla scoperta (il "1.82" era una terza lente)
def main() -> None:
@@ -78,7 +96,10 @@ def main() -> None:
print(f" Sharpe fwd (mod) : {sh:+.2f} (in-sample netta era {IS_SHARPE_NET:.2f})")
print(f" maxDD fwd : {dd:.1%}")
print(f" ritorno MODELED : {tot_m*100:+.2f}% REAL-$5000: {tot_5*100:+.2f}%")
print(f" haircut eseguib. : {haircut*100:.1f}% (guardia <= {HAIRCUT_MAX*100:.0f}%)")
print(f" haircut monitor : {haircut*100:.1f}% (pavimento $5 — ottimista per costruzione)")
print(f" guardia <= {HAIRCUT_MAX*100:.0f}%: il numero DECISIVO e' quello di "
f"`r0826_xsr_haircut.py` a pavimento $10,")
print( " citato accanto alla frazione di ordini eseguiti (mod. 26/08)")
print(f" data decisione : {DECISION} (proroga unica: {DECISION_EXT})")
if today < DECISION:
@@ -89,7 +110,8 @@ def main() -> None:
print("\n -> RITIRO: l'haircut di eseguibilita' supera la guardia. E' costo, non edge.")
elif sh >= SH_DEPLOY:
print("\n -> CANDIDATO SLEEVE: proporre peso {5,10}% e passarlo a weights_tilt_null "
"(oggi NON passa) + maxDD combinato. Deploy solo con capitale >= $5000.")
f"(oggi NON passa) + maxDD combinato. Deploy solo con capitale >= "
f"${CAPITALE_DEPLOY:,.0f} (mod. 26/08, allineato a '100% Deribit fino a $20k').")
elif sh >= SH_RETIRE:
print(f"\n -> SOTTO SOGLIA ma >= {SH_RETIRE}: estensione unica fino al {DECISION_EXT}.")
else:
+71 -7
View File
@@ -212,6 +212,54 @@ def _containing(bars: pd.DataFrame, t) -> pd.Series | None:
# ----------------------------------------------------------------------------- stats
def _equity_per_fill(ts_list) -> "pd.Series":
"""Equity del conto AL MOMENTO di ciascun fill, dal libro di bordo (`data/live/trades.db`).
PERCHE' SERVE (2026-08-25). La sezione SCALA divideva TUTTE le partecipazioni per UNA
equity sola. Andava bene finche' il conto stava fermo a ~$636; il giorno del versamento
($667 -> $2.067) qualunque scelta di quel singolo numero diventa sbagliata, perche' i fill
del campione sono stati eseguiti a taglie di conto DIVERSE. Il caso peggiore osservato
(22% di una barra 5m) e' un fill da $74 fatto a ~$650: normalizzarlo sull'equity di oggi lo
sottostima di 3,2x, sull'equity di allora sovrastima gli altri.
La grandezza invariante e' **partecipazione per dollaro di equity**, e si ottiene solo
appaiando ogni fill alla SUA equity.
"""
import sqlite3
import pandas as _pd
f = ROOT / "data" / "live" / "trades.db"
ts = _pd.DatetimeIndex(_pd.to_datetime(list(ts_list), utc=True))
try:
with sqlite3.connect(f"file:{f}?mode=ro", uri=True) as con:
e = _pd.read_sql("SELECT ts_utc, equity FROM equity ORDER BY ts_utc", con)
e["ts_utc"] = _pd.to_datetime(e["ts_utc"], utc=True)
e = e.dropna().set_index("ts_utc")["equity"].sort_index()
# BACKWARD: l'equity in vigore quando il fill e' avvenuto, mai una lettura futura.
out = _pd.Series(e.reindex(ts, method="ffill").values, index=ts)
if out.notna().any():
return out
except Exception as ex: # noqa: BLE001
print(f" ⚠️ equity per fill non leggibile ({type(ex).__name__}): "
f"ricado sull'equity unica")
return _pd.Series([float("nan")] * len(ts), index=ts)
def _equity_osservata(default: float = 636.0) -> float:
"""L'equity reale dal watermark del libro live — DERIVATA, non ridichiarata (P1).
Se il file non c'e' o non e' leggibile si ricade sul valore storico e **lo si dice**: un
default silenzioso qui rimetterebbe esattamente il difetto che questa funzione ripara.
"""
import json
f = ROOT / "data" / "live" / "equity_seen.json"
try:
v = float(json.loads(f.read_text())["real_equity"])
if v > 0:
return v
except Exception as e: # noqa: BLE001
print(f" ⚠️ watermark non leggibile ({type(e).__name__}): uso il default ${default:,.0f}")
return default
def ci_mean(x, boot=True):
x = np.asarray(x, float)
x = x[np.isfinite(x)]
@@ -515,18 +563,34 @@ def main():
# -- COSA CAMBIA CON IL CAPITALE: la partecipazione scala LINEARE, il conto no.
print(f"\n SCALA — l'unica parte di questa misura che ha una data di scadenza:")
eq = 636.0 # equity reale osservata (data/live/equity_seen.json)
print(f" la partecipazione scala lineare col capitale (equity oggi ~${eq:.0f}).")
# ⚠️ 2026-08-25: era CABLATA a 636.0 con un commento che diceva "equity reale osservata
# (data/live/equity_seen.json)" — cioe' DICHIARAVA la fonte senza leggerla. E' P1: un
# misuratore deve DERIVARE il proprio bersaglio, mai ridichiararlo. Il giorno del versamento
# (equity $667 -> $2.067) la riga avrebbe continuato a stampare $636 e l'intera colonna
# "scala" sarebbe stata sbagliata di 3,3x senza che nulla lo segnalasse — proprio nella
# sezione che esiste per dire quando la misura scade.
eq = _equity_osservata()
eq_fill = _equity_per_fill(R.loc[pr.index, "fill_ts"])
if eq_fill.notna().all():
# partecipazione PER DOLLARO di equity: invariante alla taglia del conto al momento
ppd = pr.values / eq_fill.values
base = "per-fill (ogni fill sulla SUA equity)"
else:
ppd = pr.values / eq
base = f"equity unica ${eq:,.0f} — RIPIEGO, i fill hanno taglie di conto diverse"
print(f" la partecipazione scala lineare col capitale. Normalizzazione: {base}.")
print(f" equity al momento dei fill: ${np.nanmin(eq_fill):,.0f} - ${np.nanmax(eq_fill):,.0f}"
if eq_fill.notna().any() else "")
print(f" {'capitale':>10} {'partecip. mediana':>19} {'p90':>9} {'max osservato':>15}")
for cap in (600, 5_000, 20_000, 100_000, 272_000):
k = cap / eq
mx = pr.max() * k * 100
print(f" {'$' + format(cap, ','):>10} {pr.median() * k * 100:>18.2f}% "
f"{pr.quantile(.9) * k * 100:>8.2f}% "
mx = float(np.nanmax(ppd)) * cap * 100
print(f" {'$' + format(cap, ','):>10} {float(np.nanmedian(ppd)) * cap * 100:>18.2f}% "
f"{float(np.nanpercentile(ppd, 90)) * cap * 100:>8.2f}% "
+ (f"{mx:>14.1f}%" if mx <= 100 else f"{'>tutta la barra':>15}"))
print(f" => a $600 l'impatto e' strutturalmente impossibile e questa misura lo conferma.")
_mx = float(np.nanmax(ppd))
print(f" Ma il caso peggiore osservato e' gia' {pr.max() * 100:.0f}% di una barra 5m: a $5k")
print(f" diventa {pr.max() * 5000 / eq * 100:.0f}%, a $20k {pr.max() * 20000 / eq * 100:.0f}%. "
print(f" diventa {_mx * 5000 * 100:.0f}%, a $20k {_mx * 20000 * 100:.0f}%. "
f"**La misura di oggi NON si estrapola**")
print(f" al capitale del piano: va RIFATTA a ogni salto di taglia. E' la stessa")
print(f" forma del muro di eseguibilita' gia' noto (XS01 ~$20k, XSR01 ~$5k), ma")
+20 -12
View File
@@ -310,11 +310,15 @@ def boot_ci(x: np.ndarray, n: int = 4000, seed: int = 822) -> tuple[float, float
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--no-live", action="store_true", help="non interrogare il venue")
ap.add_argument("--al", default=None,
help="data di taglio UTC (riproduzione): esclude le scadenze >= questa data; default adesso")
args = ap.parse_args()
print(__doc__.split("\n\n")[0])
puts = load_puts()
today = pd.Timestamp.now(tz="UTC")
today = pd.Timestamp(args.al, tz="UTC") if args.al else pd.Timestamp.now(tz="UTC")
print(f"\n taglio dei trade: scadenze < {today:%Y-%m-%d %H:%M}Z"
+ (" (--al: riproduzione)" if args.al else " (adesso)"))
venue = venue_read(not args.no_live)
# ------------------------------------------------------------ §1 REGIME
@@ -323,7 +327,7 @@ def main() -> None:
"misurato 4 volte). Se nel campione quel gate non scatta mai, il campione non contiene\n"
"la strategia — contiene la sua astensione.\n")
for a in ASSETS:
V = CB.dvol_series(a)
V = CB.causale(CB.dvol_series(a), "-1D") # calendario: il DVOL DEL giorno D (la serie di cblib e' causale: sta a D+1)
win = V[(V.index >= puts["ts"].min()) & (V.index <= puts["ts"].max())]
hist = V[V.index < win.index[0]].to_numpy(float)
print(f" {a}: DVOL nel campione min {win.min():.1f} / mediana {win.median():.1f} / "
@@ -372,7 +376,7 @@ def main() -> None:
print(" Regola del 19/06: «rivalutare quando la catena cattura un crash». Un criterio senza\n"
" numero non e' un criterio, quindi eccolo (IV-rank espandente causale, come nel sleeve):")
for a in ASSETS:
V = CB.dvol_series(a)
V = CB.causale(CB.dvol_series(a), "-1D") # idem: date di calendario, non etichette causali
vals = V.to_numpy(float)
ivr = np.full(len(vals), np.nan)
for i in range(1, len(vals)):
@@ -753,13 +757,17 @@ def main() -> None:
# ------------------------------------------------------------ §7 VERDETTO
hr("§7 VERDETTO")
print(""" SCARTATO — su questi dati la domanda non e' decidibile. Quattro motivi, indipendenti
print(""" (numeri della stesura del 22/08, RIMISURATI il 09/09 con spot e DVOL causali — debito §5.18:
erano 0.714 [0.690, 0.779] · 32.58 / 1.45 / -0.35 · 3.90 -> 2.41 · -1.35% -> +11.22% · +25% / +44%.
Il verdetto non cambia: le quattro ragioni non dipendono da un'ora di spot.)
SCARTATO su questi dati la domanda non e' decidibile. Quattro motivi, indipendenti
l'uno dall'altro: ciascuno da solo basterebbe.
1. IL CAMPIONE NON CONTIENE LA STRATEGIA. 0 settimane su 19 passano il gate IV-rank>0.30
che E' l'alpha di VRP01 il sleeve sarebbe stato FLAT per l'intero campione. La DVOL
mediana sta al 7 (BTC) / 11 (ETH) percentile della storia 2021+, e il sottostante e'
salito del +25% / +44%: vol implicita bassa E rialzo forte = il regime MIGLIORE
salito del +21% / +40%: vol implicita bassa E rialzo forte = il regime MIGLIORE
possibile per vendere put, non uno neutro. Le settimane sono 10 per asset, non 16
(prima del 2026-06-09 l'archivio seguiva solo la finestra 13-20 DTE: quella 4-10 DTE
non esiste). Esito: 10/10 vincenti su BTC, e la lente giusta non e' lo Sharpe ma la
@@ -770,13 +778,13 @@ def main() -> None:
regime di vol bassa persistente, non davanti a un muro.
2. IL TITOLO NON SOPRAVVIVE ALL'ORA D'INGRESSO. E' un max-of-k mai dichiarato, misurato
qui per la prima volta su questa struttura: su 9 ancore lo Sharpe canonico BTC 32.58
sta all'89 percentile, la MEDIANA ONESTA e' 1.45 e la banda tocca il NEGATIVO (-0.35).
Su ETH 3.90 -> 2.41. Il numero da citare per BTC e' 1.45, non 32.58.
qui per la prima volta su questa struttura: su 9 ancore lo Sharpe canonico BTC 29.61
sta all'89 percentile, la MEDIANA ONESTA e' 2.12 e la banda tocca il NEGATIVO (-0.19).
Su ETH 7.64 -> 3.35. Il numero da citare per BTC e' 2.12, non 29.61.
E il meccanismo non e' quello che avevo scritto prima di guardarlo. Non e' solo la
vol campionaria a ballare: fra le ancore la MEDIA settimanale BTC va da -1.35% a
+11.22% del rischio CAMBIA SEGNO e la deviazione standard di 11x (ETH: media 2.9x,
sd 2.4x). Spostare l'ora d'ingresso cambia lo snapshot e quindi quali strike combaciano
vol campionaria a ballare: fra le ancore la MEDIA settimanale BTC va da -0.66% a
+11.44% del rischio CAMBIA SEGNO e la deviazione standard di 9.2x (ETH: media 2.1x,
sd 3.2x). Spostare l'ora d'ingresso cambia lo snapshot e quindi quali strike combaciano
col delta bersaglio: non e' lo stesso trade un'ora dopo, e' UN'ALTRA STRUTTURA. A 10
settimane questo parametro di disturbo domina il segnale che si vorrebbe misurare.
@@ -793,7 +801,7 @@ def main() -> None:
4. LO SPREAD E' IL COSTO DOMINANTE, E SULLA FAMIGLIA ESEGUIBILE E' PEGGIO. Attraversare
il bid/ask costa ~10% del credito sugli INVERSE; nella regione di strike che serve lo
spread relativo mediano e' 24-37% sugli inverse e 29-59% sulle USDC, con open interest
~0. Il f del credito netto si replica indipendentemente a 0.714 (IC95 [0.690, 0.779],
~0. Il f del credito netto si replica indipendentemente a 0.712 (IC95 [0.664, 0.732],
0/19 osservazioni >= 1.0) conferma dello 0.73 del 30/07 su un campione piu' lungo e
con un percorso diverso.
+11 -10
View File
@@ -17,7 +17,7 @@ SOLA LETTURA. Questo script:
- non importa nulla che invii ordini se non per ISPEZIONARLO (la sottoclasse di §1 ha `_post` che
SOLLEVA: se il codice di produzione toccasse la rete, il test fallirebbe rumorosamente);
- tocca il venue solo con `--venue`, solo `get_open_orders` (lettura), 2 richieste, e si rifiuta
di partire nei minuti :05-:10 (cron_book) e :24-:30 (cron_chain).
di partire nei minuti :45-:50 (cron_book, al :47 dal 25/08) e :24-:30 (cron_chain).
uv run python scripts/research/r0823_sl_anchor.py # attesa + log + simulazione
uv run python scripts/research/r0823_sl_anchor.py --venue # + riscontro sul conto (2 letture)
@@ -283,8 +283,8 @@ def riscontro_venue(runs_info: dict):
diritte di Deribit appartengono a un ALTRO progetto: usarle sarebbe inventare un percorso, e la
domanda non ne ha bisogno. Si usa quindi cio' che il percorso sanzionato offre: `get_open_orders`.""")
m = datetime.now(timezone.utc).minute
if 5 <= m <= 10 or 24 <= m <= 30:
print(f"\n ⏸ minuto :{m:02d} — finestra di cron_book (:07) / cron_chain (:25). Non interrogo.")
if 45 <= m <= 50 or 24 <= m <= 30: # cron_book al :47 dal 2026-08-25 (era :07), cron_chain :25
print(f"\n ⏸ minuto :{m:02d} — finestra di cron_book (:47) / cron_chain (:25). Non interrogo.")
return
from src.live.deribit import DeribitRead
r = DeribitRead()
@@ -395,10 +395,11 @@ def conseguenza():
alta dopo una salita, lo stop siede vicino sotto il mercato, il crollo lo prende, si rientra e
la gamba successiva lo riprende. **Il rischio di accumulo e' funzione della CADENZA DEL CRON,
non di k**: il gradino di leva non lo crea e non lo amplifica.
· E qui c'e' un aggancio operativo: il docstring di `scripts/live/book_execute.py` prescrive
«ogni ~230 minuti» (la griglia di SKH01) mentre il cron gira **ogni ora** (misurato sopra: 1443
giri in 1442 ore). Chi "correggesse" la cadenza verso il docstring sposterebbe il libro dalla
riga 1h alla riga 4h quella in cui BTC raddoppia gli scatti.
· Aggancio operativo (RIPARATO il 2026-09-02, `tests/test_book_cadenza.py`): fino a quel
giorno il docstring di `scripts/live/book_execute.py` prescriveva «ogni ~230 minuti» (la
griglia di SKH01) mentre il cron gira **ogni ora** (misurato sopra: 1443 giri in 1442 ore).
Chi avesse "corretto" il CRON verso il docstring avrebbe spostato il libro dalla riga 1h
(rotolante: 0 scatti) alla riga 4h (2 scatti del rotolante contro 1 del pavimento).
(*) CAVEAT sulla colonna, e sul suo artefatto: in questa lente la posizione e' SEMPRE aperta
dal 2018, quindi in modo `roll` l'"ingresso" e' un prezzo di anni prima e il -48%/-61% e' il
@@ -501,9 +502,9 @@ def costo(out: dict):
E c'e' un accoppiamento nuovo da registrare: l'accumulo di episodi e' funzione della CADENZA
DEL CRON (1h: no · 4h: BTC 2 · 24h: ETH 5 con 2 in 30 giorni). Oggi la cadenza e' misurata sana,
ma nessun sorvegliante la controlla contro questa conseguenza, e il docstring di `book_execute`
ne prescrive una (~230 min) che cade nella riga peggiore. Non e' un'azione richiesta qui: e' il
parametro da rileggere prima del gradino, insieme a G2.""")
e dal 2026-09-02 `tests/test_book_cadenza.py` tiene d'accordo docstring, cron_book.sh e crontab
(il docstring ne prescriveva una, ~230 min, nella riga 4h). Resta il parametro da rileggere prima
del gradino, insieme a G2.""")
def main():
+210
View File
@@ -0,0 +1,210 @@
"""r0825_capitale_fermo.py — il capitale che il libro NON usa: occasione o illusione?
DOMANDA (operatore, 2026-08-25). Il libro live sta **flat** buona parte del tempo (28,0% dei giorni
sull'intera storia; **75,6% nel 2026** e 77,0% nel 2022). Il 07/08 il progetto aveva gia' scelto un
**50/50 book/ETF** per *rimpianto minimo*, su base statistica. La domanda nuova e' meccanica:
**mettere l'ETF proprio nei giorni in cui il libro e' flat** batte il 50/50 statico?
ATTENZIONE A COSA SI STA MISURANDO. "ETF quando TP01 e' flat" e' a tutti gli effetti una
**strategia di timing sull'azionario guidata da un segnale cripto**. Non c'e' nessuna ragione a
priori perche' funzioni, e il rischio e' di misurare due cose diverse credendo di misurarne una:
(a) **UTILIZZO DEL CAPITALE** stare nell'ETF invece che fermi aggiunge rendimento a
prescindere da QUANDO lo si fa. E' aritmetica, non informazione.
(b) **TIMING** la flat-ness di TP01 predice i rendimenti azionari.
Il confronto ingenuo (dinamico vs 50/50) li somma. Per separarli serve il **null a maschera
casuale**: si rifa' la stessa strategia con una maschera flat FINTA della stessa lunghezza e della
stessa struttura a blocchi. Se il dinamico vero non batte quella nuvola, di (b) non c'e' niente e
resta solo (a) che e' comunque un risultato, ma **un altro** risultato.
REGOLE APPLICATE:
* **M6/M5** il confronto e' a **ISO-RISCHIO** (ogni linea riscalata alla stessa vol bersaglio),
mai a iso-nozionale: un veicolo a CAGR piu' basso ma vol piu' bassa perde sempre a iso-nozionale
per costruzione. Il de-levering e' il PRIMO test, non l'ultimo.
* **M14** il null e' a **struttura preservata** (blocchi), non i.i.d.: la flat-ness del libro e'
fortemente autocorrelata (interi mesi), e un null i.i.d. sarebbe banale da battere.
* ** finestra comune** un mix esiste solo dove esistono ENTRAMBE le serie. L'errore gia'
commesso e corretto il 07/08 (dichiarava 30 anni e ne usava 7) qui non si ripete: si taglia
sull'**intersezione** e la si stampa.
* **convenzione GTAA01** per l'azionario: ritorni su griglia di calendario con **0.0 a borsa
chiusa** (senza, lo Sharpe azionario sale del 20% lezione 25/07).
uv run python scripts/research/r0825_capitale_fermo.py
"""
from __future__ import annotations
import sys
from pathlib import Path
import numpy as np
import pandas as pd
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
sys.path.insert(0, str(ROOT / "scripts" / "research"))
import eqlib as EQ # noqa: E402
import r0807_asset_compare as AC # noqa: E402
import r0822d_piano_vero as PV # noqa: E402
VOL_TARGET = 0.11 # ~la vol del libro: la lente iso-rischio del confronto
N_NULL = 400 # estrazioni della maschera finta
BLOCK = 20 # giorni: la stessa taglia di blocco del resto del progetto
SEED = 20260825
def metriche(r: pd.Series | np.ndarray, nome: str = "") -> dict:
r = np.asarray(r, float)
mu, sd = r.mean() * 365.0, r.std() * 365.0 ** 0.5
eq = np.cumprod(1.0 + r)
dd = float((eq / np.maximum.accumulate(eq) - 1.0).min())
return dict(nome=nome, drift=mu, vol=sd, sharpe=mu / sd if sd > 0 else np.nan, maxdd=dd)
def iso(r: np.ndarray, vol_target: float = VOL_TARGET) -> np.ndarray:
"""Riscala la serie alla vol bersaglio. E' il de-levering di M6: senza, un veicolo a vol
bassa perde per costruzione e il confronto non dice niente."""
sd = r.std() * 365.0 ** 0.5
return r * (vol_target / sd) if sd > 0 else r
def maschera_a_blocchi(n: int, quota: float, rng: np.random.Generator,
block: int = BLOCK) -> np.ndarray:
"""Maschera booleana casuale con ~`quota` di True, in BLOCCHI di `block` giorni.
La flat-ness vera del libro dura mesi interi: un null i.i.d. avrebbe una struttura temporale
completamente diversa e sarebbe facile da battere per la ragione sbagliata."""
nb = int(np.ceil(n / block))
scelti = rng.random(nb) < quota
return np.repeat(scelti, block)[:n]
def main() -> None:
print("=" * 100)
print(" r0825 — IL CAPITALE FERMO: l'ETF nei giorni in cui il libro e' flat")
print("=" * 100)
# --- le due serie, sulla FINESTRA COMUNE -------------------------------------------------
B = PV.costruisci_serie()
ix = pd.to_datetime(B.index).tz_localize(None).normalize()
# ⚠️ LA MASCHERA FLAT SI PRENDE DALLA SERIE GREZZA, MAI DA QUELLA DE-LUCKATA.
# `deluck` sottrae una COSTANTE (r - (1-0.89)*r.mean()): un giorno a rendimento esattamente
# zero diventa un numero piccolo ma non nullo, e `abs(r) < 1e-12` non trova piu' NIENTE.
# Prima versione di questo script: maschera VUOTA, dinamico identico al book, e un p-value
# stampato lo stesso. Se ne e' accorta solo la riga che stampa la quota — che e' il motivo
# per cui la si stampa.
grezzo = pd.Series(B["fund"].values.astype(float), index=ix)
book = pd.Series(PV.deluck(B["fund"].values.astype(float)), index=ix)
grezzo = grezzo[~grezzo.index.duplicated(keep="last")]
book = book[~book.index.duplicated(keep="last")]
spy = AC._calendar_daily(EQ.load_eq("SPY")["close"])
idx = book.index.intersection(spy.index)
book, spy, grezzo = book.reindex(idx), spy.reindex(idx), grezzo.reindex(idx)
flat = (grezzo.abs() < 1e-12).values
assert flat.sum() > 0, "maschera flat vuota: e' stata presa dalla serie de-luckata?"
print(f"\n finestra COMUNE: {idx[0].date()} -> {idx[-1].date()} ({len(idx)} giorni, "
f"{len(idx)/365.25:.1f} anni)")
print(f" giorni in cui il LIBRO e' flat: {flat.sum()} = {flat.mean():.1%}")
print(f" quota per anno: " + " · ".join(
f"{y}: {g.mean():.0%}" for y, g in pd.Series(flat, index=idx).groupby(idx.year)))
b, s = book.values, spy.values
linee = {
"BOOK da solo": b,
"ETF da solo": s,
"50/50 statico": 0.5 * b + 0.5 * s,
"DINAMICO (ETF se il libro e' flat)": np.where(flat, s, b),
}
# --- (1) confronto a ISO-NOZIONALE (come lo si guarderebbe d'istinto) --------------------
print(f"\n{'='*100}\n (1) ISO-NOZIONALE — come lo si guarda d'istinto. ⚠️ NON e' il test."
f"\n{'='*100}")
print(f" {'linea':<38}{'drift':>9}{'vol':>9}{'Sharpe':>9}{'maxDD':>9}")
for nome, r in linee.items():
m = metriche(r, nome)
print(f" {nome:<38}{m['drift']:>8.2%}{m['vol']:>9.2%}{m['sharpe']:>9.2f}{m['maxdd']:>9.1%}")
# --- (2) ISO-RISCHIO: il test vero (M6) --------------------------------------------------
print(f"\n{'='*100}\n (2) ISO-RISCHIO a vol {VOL_TARGET:.0%} — il confronto che conta (M6)"
f"\n{'='*100}")
print(f" {'linea':<38}{'drift':>9}{'vol':>9}{'Sharpe':>9}{'maxDD':>9}{'leva impl.':>12}")
iso_m = {}
for nome, r in linee.items():
k = VOL_TARGET / (r.std() * 365 ** 0.5)
m = metriche(iso(r), nome)
iso_m[nome] = m
print(f" {nome:<38}{m['drift']:>8.2%}{m['vol']:>9.2%}{m['sharpe']:>9.2f}"
f"{m['maxdd']:>9.1%}{k:>11.2f}x")
# --- (3) IL NULL: timing o solo utilizzo del capitale? -----------------------------------
print(f"\n{'='*100}\n (3) NULL A MASCHERA CASUALE — separa il TIMING dall'UTILIZZO"
f"\n{'='*100}")
print(f" {N_NULL} maschere finte, stessa quota ({flat.mean():.1%}) e stessa struttura "
f"a blocchi di {BLOCK}g.")
rng = np.random.default_rng(SEED)
nulli = []
for _ in range(N_NULL):
fk = maschera_a_blocchi(len(b), flat.mean(), rng)
nulli.append(metriche(iso(np.where(fk, s, b)))["sharpe"])
nulli = np.array(nulli)
vero = iso_m["DINAMICO (ETF se il libro e' flat)"]["sharpe"]
pct = float((nulli < vero).mean())
print(f" Sharpe iso-rischio del DINAMICO vero : {vero:.3f}")
print(f" nuvola del null: mediana {np.median(nulli):.3f} · "
f"p5 {np.percentile(nulli,5):.3f} · p95 {np.percentile(nulli,95):.3f}")
print(f" percentile del vero dentro il null : {pct:.1%}")
print(f" p-value one-sided (vero <= null) : {1-pct:.3f}")
# --- (3b) IL "MECCANISMO" SI SCOMPONE PER ANNO PRIMA DI CREDERCI (M9) --------------------
print(f"\n{'='*100}\n (3b) L'azionario nei giorni flat — e la scomposizione che la smonta"
f"\n{'='*100}")
sf, sn = spy.values[flat], spy.values[~flat]
print(f" aggregato: SPY|flat {sf.mean()*365:>7.2%} (vol {sf.std()*365**0.5:.2%}) · "
f"SPY|dentro {sn.mean()*365:>7.2%} (vol {sn.std()*365**0.5:.2%}) · "
f"differenza {(sf.mean()-sn.mean())*365:>+7.2%}")
print(f" ⚠️ Sembra un meccanismo («il libro va flat quando l'azionario soffre»). Non lo e':")
print(f" {'anno':>6}{'gg flat':>9}{'differenza SPY (flat - dentro)':>34}")
segni = []
for y, m in spy.groupby(spy.index.year):
mk = flat[spy.index.year == y]
a, b = m.values[mk], m.values[~mk]
if len(a) < 15 or len(b) < 15:
print(f" {y:>6}{len(a):>9}{'campione insufficiente':>34}")
continue
d = (a.mean() - b.mean()) * 365
segni.append(np.sign(d))
print(f" {y:>6}{len(a):>9}{d:>+33.1%}")
su, giu = int(sum(1 for x in segni if x > 0)), int(sum(1 for x in segni if x < 0))
print(f"\n => il segno ALTERNA: {su} anni 'flat meglio', {giu} anni 'flat peggio'.")
print(f" L'aggregato e' un artefatto di COMPOSIZIONE: i giorni flat sono concentrati")
print(f" negli anni a basso rendimento azionario, non nei GIORNI a basso rendimento.")
print(f" Dentro ogni anno l'effetto non c'e'. (M9: un contributo si scompone per anno")
print(f" prima di crederci — '24/24 ancore positive' puo' essere un anno solo.)")
# --- (4) VERDETTO calcolato a runtime (N11) ----------------------------------------------
print(f"\n{'='*100}\n VERDETTO (calcolato, non scritto a mano)\n{'='*100}")
din = iso_m["DINAMICO (ETF se il libro e' flat)"]["sharpe"]
sta = iso_m["50/50 statico"]["sharpe"]
bk = iso_m["BOOK da solo"]["sharpe"]
print(f" a iso-rischio: BOOK {bk:.3f} · 50/50 {sta:.3f} · DINAMICO {din:.3f}")
if din <= sta:
print(f" (a) Il dinamico NON batte il 50/50 statico ({din:.3f} <= {sta:.3f}): "
f"la commutazione non aggiunge nulla che il mix non dia gia'.")
else:
print(f" (a) Il dinamico batte il 50/50 statico di {din-sta:+.3f} di Sharpe.")
if pct < 0.95:
print(f" (b) E il TIMING non e' dimostrato: il vero sta al {pct:.0%} del null, "
f"sotto la soglia del 95%.")
print(f" Quel che si vede e' UTILIZZO DEL CAPITALE, non informazione: qualunque")
print(f" maschera con la stessa quota fa altrettanto.")
else:
print(f" (b) Il timing SOPRAVVIVE al null ({pct:.0%} >= 95%): la flat-ness del libro")
print(f" porta informazione sull'azionario. Da riprovare fuori campione.")
print(f"\n NB: 'battere a iso-rischio' qui vuol dire leva implicita diversa per ogni linea")
print(f" (colonna 'leva impl.'), e la leva NON e' autorizzata oggi (§3, k max 1.40).")
if __name__ == "__main__":
main()
+153
View File
@@ -0,0 +1,153 @@
"""r0825_piano_10a_500.py — €500/mese per 10 anni, dal conto VERO di oggi.
DOMANDA (operatore, 2026-08-25): *"dammi strategia a 10 anni con 500 al mese"*.
PERCHE' NON BASTAVA CITARE LA TABELLA. Le tabelle pubblicate del piano partono da **$600/$635**.
Oggi il conto e' **$2.067** (versamento di 1.400 USDC atterrato alle 11:03:35Z). Il lump e' la
leva piu' grande misurata dopo il versamento mensile stesso (*"€10.000 oggi + €250/m porta P(20a)
da 14% a 45%"*), quindi la riga vecchia sottostima il caso di oggi e non si puo' riusare.
LENTE: **L3 CONGIUNTA** di `r0822d_piano_vero` libro live 75/25, **funding esatto dentro la
serie**, ancora de-luckata x0.89, **fisco d'accumulo** ogni anno dentro il portafoglio (33% sulla
variazione annua di valore, minusvalenze in carry 4 anni, patrimoniale 0,2%). E' la piu' severa
che il progetto abbia, ed e' quella che ha prodotto muro $313k e *"€1.733/mese per 10 anni"*.
DIFETTI EREDITATI, dichiarati perche' vanno **a favore del piano** (registrati il 23/08):
1. `PN.accumula` versa ogni **30 giorni** -> a 10 anni sono **121** versamenti, non 120 (+0,83%);
2. il contatore `versato` e' uno **scalare che non si ferma al traguardo** -> il "versato" a
denominatore e' quello dell'INTERO orizzonte, non quello fino all'arrivo.
Non li ho riparati qui: ripararli cambierebbe i numeri di riferimento e li renderebbe non
confrontabili con tutto il resto della memoria. Vanno LETTI, non dimenticati.
E IL NUMERO DA NON DIMENTICARE MAI accanto a questi: la SE del drift L3 e' **5,151%/anno**, e
il muro e' `prelievo/perpetua` = un **1/x su una quantita' che va a zero** -> la banda e'
esplosiva e **asimmetrica verso l'alto**: $187k (p90) · **$313k (punto)** · $1,14M (p10) ·
a **2 SE il traguardo non esiste a nessun capitale**. Percio' la regola del progetto:
**il piano si dimensiona sul VERSAMENTO, che e' certo, non sul muro** (N1).
uv run python scripts/research/r0825_piano_10a_500.py
"""
from __future__ import annotations
import sys
from pathlib import Path
import numpy as np
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
sys.path.insert(0, str(ROOT / "scripts" / "research"))
import r0725_capcurve as CC # noqa: E402
import r0807_piano_netto as PN # noqa: E402
import r0822d_piano_vero as PV # noqa: E402
import r0823_deposit_timing as DT # noqa: E402
# --- lo stato VERO di oggi, non un nominale -------------------------------------------------
START_OGGI = 2066.88 # equity Deribit letta alle 11:47:01Z del 2026-08-25
START_PUBBL = PV.START # 635.0 — la base delle tabelle pubblicate, per il confronto
DEP = 500.0 # EUR/mese
ORIZZONTI = (5, 10, 15, 20)
N_PATHS = 3000
SEED = PV.SEED_TRAJ # 725: stesso seme delle traiettorie pubblicate
BLOCK = PV.BLOCK # 20 giorni
def rendita_eur_giorno(cap: np.ndarray, perp: float) -> np.ndarray:
"""€/giorno NETTI da un capitale, alla perpetua della lente.
La perpetua L3 (6,35%) ha gia' le imposte dentro: **non si lordizza una seconda volta**
(errore corretto il 07/08)."""
return cap * perp / 365.0 / CC.EURUSD
def main() -> None:
print("=" * 100)
print(" r0825 — €500/MESE PER 10 ANNI, dal conto vero di oggi ($2.067)")
print("=" * 100)
B = PV.costruisci_serie()
r = PV.deluck(B["fund"].values.astype(float)) # L3 CONGIUNTA, ancora x0.89
lente = PV.Lente("L3 congiunta", r, lordizza=False)
lente.perp, lente.muro = PV.muro_di(lente)
print(f"\n serie: {len(r)} giorni · drift {lente.drift:.2%}/anno · vol {lente.vol:.2%}/anno")
print(f" perpetua {lente.perp:.2%} · MURO ${lente.muro:,.0f} "
f"[banda del progetto: $187k (p90) — $1,14M (p10); a 2 SE non esiste]")
print(f" bersaglio di rendita: €{CC.TARGET_EUR_DAY:.0f}/giorno netti")
print(f"\n{'='*100}\n (1) €{DEP:.0f}/MESE DA ${START_OGGI:,.0f} — cosa consegna, per orizzonte"
f"\n{'='*100}")
print(f" {'oriz.':>6} {'capitale mediano':>18} {'p10':>12} {'p90':>13} "
f"{'€/g mediani':>13} {'P(≥50 €/g)':>12} {'versato':>11} {'fin/vers':>9}")
print(" " + "-" * 98)
righe = {}
for anni in ORIZZONTI:
rng = np.random.default_rng(SEED)
ii = DT.boot_idx(len(r), N_PATHS, anni * 365, BLOCK, rng)
paths = r[ii]
out = PN.accumula(paths, DEP, lente.muro, lente.aliq, lente.patr, start=START_OGGI)
cap, versato = out["cap"], out["versato"]
eur = rendita_eur_giorno(cap, lente.perp)
p50 = float((eur >= CC.TARGET_EUR_DAY).mean())
righe[anni] = dict(med=np.median(cap), eur=np.median(eur), p50=p50, versato=versato)
print(f" {anni:>4}a ${np.median(cap):>16,.0f} ${np.percentile(cap,10):>10,.0f} "
f"${np.percentile(cap,90):>11,.0f} {np.median(eur):>12.2f} {p50:>11.1%} "
f"${versato:>9,.0f} {np.median(cap)/versato:>8.2f}x")
print(f"\n{'='*100}\n (2) COSA HA COMPRATO IL VERSAMENTO DI OGGI — $635 (tabelle pubbl.) vs "
f"$2.067 (reale)\n{'='*100}")
print(f" {'oriz.':>6} {'da $635':>14} {'da $2.067':>14} {'guadagno':>11} "
f"{'€/g da $635':>13} {'€/g da $2.067':>15}")
print(" " + "-" * 78)
for anni in ORIZZONTI:
rng = np.random.default_rng(SEED)
ii = DT.boot_idx(len(r), N_PATHS, anni * 365, BLOCK, rng)
paths = r[ii]
a = PN.accumula(paths, DEP, lente.muro, lente.aliq, lente.patr, start=START_PUBBL)["cap"]
b = PN.accumula(paths, DEP, lente.muro, lente.aliq, lente.patr, start=START_OGGI)["cap"]
ea, eb = rendita_eur_giorno(a, lente.perp), rendita_eur_giorno(b, lente.perp)
print(f" {anni:>4}a ${np.median(a):>12,.0f} ${np.median(b):>12,.0f} "
f"{np.median(b)/np.median(a)-1:>10.1%} {np.median(ea):>12.2f} {np.median(eb):>14.2f}")
print(f"\n{'='*100}\n (3) QUANDO ARRIVA IL BERSAGLIO (€50/g = ${lente.muro:,.0f})"
f"\n{'='*100}")
rng = np.random.default_rng(SEED)
ii = DT.boot_idx(len(r), N_PATHS, 25 * 365, BLOCK, rng)
paths25 = r[ii]
for dep in (0, 250, 500, 800, 1_000):
out = PN.accumula(paths25, float(dep), lente.muro, lente.aliq, lente.patr,
start=START_OGGI)
d = PV.leggi(out["colpito"], anni_sim=25)
med = "mai" if not np.isfinite(d["med"]) else f"{d['med']:.1f}a"
cond = "n/d" if not np.isfinite(d["med_cond"]) else f"{d['med_cond']:.1f}a"
print(f"{dep:>5}/m -> mediana {med:>6} (condizionata all'arrivo {cond:>6}, "
f"arriva il {d['p_arr']:.0%}) · P(entro 20a) {d['p20']:>5.0%}")
print(f"\n{'='*100}\n (4) IL VERSAMENTO CHE SERVE DAVVERO PER €50/g, da ${START_OGGI:,.0f}"
f"\n{'='*100}")
for anni in (10, 15, 20):
rng = np.random.default_rng(PV.SEED_DEP)
ii = DT.boot_idx(len(r), PV.N_DEP, anni * 365, BLOCK, rng)
p = r[ii]
riga = []
for conf in (0.50, 0.75, 0.90):
d, tot = PV.dep_per_conf(p, lente, conf, start=START_OGGI)
riga.append(f"P={conf:.0%}: €{d:,.0f}/m" + (f" (tot ${tot:,.0f})" if conf == 0.90 else ""))
print(f" {anni:>2} anni -> " + " · ".join(riga))
print(f"\n{'='*100}\n VERDETTO (calcolato a runtime, non scritto a mano)\n{'='*100}")
d10 = righe[10]
print(f" €500/mese per 10 anni da ${START_OGGI:,.0f}:")
print(f" capitale mediano ${d10['med']:,.0f} · rendita mediana {d10['eur']:.2f} €/giorno")
print(f" P(≥50 €/g a 10 anni) = {d10['p50']:.1%}")
print(f" versato ${d10['versato']:,.0f} -> il rendimento fa il "
f"{max(0.0, 1 - d10['versato']/d10['med']):.0%} del risultato, i bonifici il resto")
verdetto = ("CENTRA" if d10["p50"] >= 0.5 else "NON CENTRA")
print(f"\n => a 10 anni €500/mese {verdetto} il bersaglio €50/giorno.")
print(f" => quello che centra a 10 anni sta nella sezione (4); quello che €500/mese centra,")
print(f" e quando, sta nella (3).")
if __name__ == "__main__":
main()

Some files were not shown because too many files have changed in this diff Show More