diff --git a/CLAUDE.md b/CLAUDE.md index 29494a6..a4d2741 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2831,6 +2831,67 @@ Prima ondata di ricerca onesta su BTC/ETH certificati (5 track, harness condivis 📌 **La riga che chiude cinque ondate e 57 filoni: la ricerca ha smesso di essere il vincolo binding il 26/07, e ora cinque ondate lo hanno CONFERMATO invece che ribaltarlo. I vincoli binding restano due: il capitale che entra e il conto che non sparisce.** +- 📓 **LIBRO DI BORDO — i trade allineati col tempo, il giornale e l'analista (2026-08-23).** + `src/live/{tradesdb,journal,analista}.py`, CLI in `scripts/live/`, voci in `docs/journal/`, + test `tests/test_{tradesdb,journal,analista}.py` (77). **Strategia, pesi, config INVARIATI.** + 🚨 **IL DIFETTO CHE L'HA FATTO NASCERE: i trade erano salvati e NON allineati col tempo.** + `book_execute.py` scriveva `ts_utc = pd.Timestamp(r['last_data'])`, cioe' la data della + **barra di segnale**: 19 righe su 19 a `00:00:00`, e **un trade registrato SEI GIORNI prima di + essere eseguito** (ETH 0.04 @ 1.869,74: fill vero 2026-07-14T14:00, scritto 08/07). L'ora vera + esisteva **solo** in `logs/cron_book.log` — **gitignored, fuori dal backup, ruotabile**: la + cronologia reale del libro live viveva in un file che una rotazione avrebbe cancellato senza + che nessuno se ne accorgesse. Riparato alla sorgente (`ts_utc` = ora vera, `bar_ts` = barra) + **con un test di regressione sul sorgente**: se qualcuno rimette `last_data`, il test lo dice. + 📌 **IL DB** (`data/live/trades.db`, dentro il perimetro del backup): `fills` col contesto del + segnale a quel giro (tp_frac, skh_sign, entry, target, posizione, equity), `roundtrips` + **derivati e ricalcolati da zero**, `equity` oraria, `journal`. Sync **orario** in `cron_book`. + **Le tre fonti si INCROCIANO e non si sovrascrivono:** log (ora vera) x jsonl (i fill) x venue + (autorevole ma **TRONCA** — 1 trade su BTC, 0 su ETH). `reconcile()` riporta le divergenze e + **non ripara niente da solo**: fra due fonti che non concordano, una riparazione silenziosa e' + un'invenzione. E' cosi' che il difetto dell'ora e' stato *isolato* invece che dedotto. + 📌 **QUATTRO LIVELLI IN PAGINA, SEPARATI PER PROVENIENZA** — e la separazione E' il prodotto: + **numeri** (feed + DB) · **Lettura** (12 regole dichiarate, ognuna con l'id stampato accanto + alla riga che ha prodotto, ognuna con **due** test: uno che la accende e uno che la tiene + spenta) · **Analisi** (prosa di un modello, firmata e datata) · **Nota** (l'operatore, mai + riscritta da un ricalcolo). *Un giornale che mescola misura e racconto, a sei mesi, non + permette piu' di sapere quale delle due si stava leggendo.* + 🚨 **L'ANALISTA SBAGLIA, E IL TRIPWIRE NON PUO' PRENDERLO.** `numeri_non_supportati()` verifica + che ogni cifra dell'analisi compaia in cio' che il modello ha ricevuto (oltre 3 numeri liberi: + **rifiutata**), ma **due errori su due giri con sonnet-5 non contenevano cifre nuove** — una + frase su un merge mai raccontato, e un round-trip corretto dichiarato inesistente con una + spiegazione inventata per il proprio dubbio. **Il modello e' stato portato a `claude-opus-5`; + il campione e' di due osservazioni, quindi e' un tentativo di abbassare un tasso, non una + garanzia.** **REGOLA: una guardia sui NUMERI non copre il RAGIONAMENTO — l'analisi si legge + come opinione di un lettore fallibile, mai come parte del registro.** + ⚠️ **CICLO DI RETROAZIONE, trovato al primo giro reale:** il modello leggeva la **propria** + analisi del giorno prima e la commentava (aveva prodotto un paragrafo sull'avviso del tripwire + che si era preso). `senza_analisi()` toglie la sua sezione dalla pagina prima di dargliela; la + `Nota` resta, perche' il contesto dell'operatore serve. **Nessun controllo automatico puo' + distinguere il meta-commento da prosa valida: si vede solo LEGGENDO l'uscita.** + ✅ **TELEGRAM, e due delle tre riparazioni al notifier finalmente fatte.** L'analisi parte ogni + giorno con una testata di numeri; se manca, **il messaggio parte lo stesso dicendo perche'** + (il silenzio si legge come «non e' successo niente»). Il trasporto era il punto singolo di + guasto gia' misurato (**6,9% di invii persi, 2/29**) = ~25 messaggi l'anno su una cadenza + giornaliera: (a) `send(text, tentativi=1)` — retry **opzionale**, default invariato perche' + alzarlo per tutti cambierebbe la latenza degli allarmi di `venue_watch` e `book_execute` su un + percorso con soldi veri; (b) `ultimo_errore()` registra il motivo **nel punto in cui veniva + ingoiato** e non sopravvive a un invio riuscito. **(c) `alerted=True` solo a invio riuscito + resta la decisione dell'operatore**: cambia il comportamento degli allarmi. Esito nel DB in tre + stati: `inviata` / `non configurato` / `FALLITA — motivo`. + ⚠️ **CINQUE difetti trovati dai test o leggendo l'uscita, nessuno a occhio:** (1) il tetto di + leva era **RIDICHIARATO** (`0.5` cablato) invece che letto da `config/live.json` — **quinta + occorrenza** dello schema che il progetto paga da luglio; (2) l'IV-rank era il percentile **a un + anno** etichettato col nome del gate di VRP01, che usa quello **espandente**: davano il verdetto + **opposto** (0,52 «sopra» contro 0,18 «sotto», e il valore giusto combacia con «0/8 settimane + passano il gate» misurato il 30/07); (3) il renderer cadeva in `KeyError` se mancava il blocco + mercato; (4) «24 giri attesi» su un giorno **in corso** = allarme a ogni esecuzione; (5) libro e + P&L leggevano **due istanti diversi** e la stessa pagina mostrava due equity. + 📌 **REGOLE:** (a) **un dato operativo non ricostruibile non puo' vivere in un file gitignored** + — si materializza dove il backup arriva; (b) **una guardia piu' stretta del contratto produce + allarmi che si impara a ignorare** (il tripwire validava sulla sola pagina mentre il prompt + include anche lo storico, e bocciava un'equity vera); (c) **una regola che si accende su $2 di + cumulato insegna a saltare la sezione**: serve una soglia di rilevanza; (d) chi scrive in un + registro va **firmato**, o il registro perde il suo valore probatorio. - 🌊 **SESTA ONDATA 2026-08-23 (12 filoni, §58-69, `research/wave-0822`) — 0 candidati su **69 cumulativi**, 1 finding che riguarda l'OBIETTIVO stesso, 2 appoggi tolti a decisioni gia' prese.** Registro `docs/research/RESULTS-0822.md` §58-69 + sezione di sintesi in fondo; 12 script @@ -3173,6 +3234,11 @@ src/version.py → APP_VERSION (legge il file VERSION) scripts/research/ → ricerca: track{A-I}_*.py + options_vrp_*.py + fetch_dvol.py scripts/portfolio/ → run_portfolio.py (report) + xsec_*.py (ricerca/affinamento XS01) scripts/live/paper_portfolio.py → paper/forward-only del book attivo TP01+XS01 (1d) (no esecuzione reale) +src/live/tradesdb.py → LIBRO DI BORDO: DB sqlite dei trade allineato col tempo (data/live/trades.db) +src/live/journal.py → giornale giornaliero: metriche + LETTURA a regole dichiarate e tracciabili +src/live/analista.py → l'agente che scrive l'ANALISI in prosa + guardie + messaggio Telegram +scripts/live/{trades_db,journal,analista}.py → i tre CLI (sync orario / voce del giorno / analisi) +docs/journal/YYYY-MM-DD.md → una voce al giorno, 4 livelli separati per provenienza scripts/analysis/ → SOLO i tool dati certificati: rebuild_history.py → (ri)costruisce lo storico da Deribit mainnet (base 5m + resample) certify_feed.py → certifica il feed (integrità, coerenza resample, spike, cross-venue) @@ -3198,6 +3264,10 @@ uv run python scripts/analysis/fetch_hyperliquid.py # fetch+certify uni uv run python scripts/portfolio/xsec_research.py # ricerca cross-sectional su Hyperliquid (XS01) uv run python scripts/portfolio/run_portfolio.py # report del PORTAFOGLIO attivo (TP01+XS01) uv run python scripts/live/paper_portfolio.py # avanza il paper del book TP01+XS01 (forward-only, 1d) +uv run python scripts/live/trades_db.py --report # stato del libro di bordo + P&L +uv run python scripts/live/trades_db.py --reconcile # incrocio delle 3 fonti sui fill +uv run python scripts/live/journal.py # voce del giorno (numeri + lettura) +uv run python scripts/live/analista.py --secco # analisi del giorno, senza salvare uv run pytest # test ``` diff --git a/docs/diary/2026-08-23-libro-di-bordo.md b/docs/diary/2026-08-23-libro-di-bordo.md new file mode 100644 index 0000000..12f4be1 --- /dev/null +++ b/docs/diary/2026-08-23-libro-di-bordo.md @@ -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**.