# 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.