tradesdb FIFO: short e inversioni scartati in silenzio, fill NON VERIFICATO a qty 0 diventano round-trip #32

Closed
opened 2026-09-27 13:12:14 +00:00 by Adriano · 3 comments
Owner

Revisione 2026-09-27 (goldrake): rilievo Opus 5.5, verifica avversariale fable. Sola lettura, nessun codice toccato.

Rilievo C2 [registro-falso] (latente)

FILE: PG:src/live/tradesdb.py:264-290 (fifo_roundtrips, usato da rebuild_roundtrips e stato_aperto)
COSA: FIFO solo-long. Una vendita senza lotti aperti (short netto) o oltre i lotti (flip long->short) viene scartata in silenzio; il riacquisto successivo diventa un lotto LONG aperto.
SCENARIO: SKH01 puo' andare SHORT (book.py:14, giornale 15/09) e con TP01 a 0 il netto diventa negativo. sell 0.01@80000, buy 0.01@78000 -> 0 round-trip, "posizioni aperte: BTC 0.01 @78000" a libro flat. Flip buy 0.01@80000, sell 0.03@81000, buy 0.02@79000 -> P&L del tratto short (+40) perso, report dice long 0.02 aperto. Oggi non morde: nel log net negativo 0 volte su 2296 giri.
PROVA: VERIFICATO: chiamata pura a T.fifo_roundtrips con i due scenari -> short: rts [] aperti {'BTC':[qty 0.01 @78000]}; flip: n rts 1 [10.0] aperti {'BTC':[qty 0.02 @79000]}.
PROPOSTA: lotti con segno (deque per lato: una buy chiude prima gli short aperti, il resto apre long; simmetrico per sell). Test short e flip.

Verifica fable C2 VERDETTO: CONFERMATO

GRAVITA': medio (latente: net negativo 0 volte nel log)
PERCHE': src/live/tradesdb.py:264-290: sell consuma solo lotti long; while resto>1e-12 and lotti[...] esce con resto scartato, nessun avviso; il buy successivo apre un lotto long. book.py:226 raw = weight*equity*(0.75*max(tp,0) + 0.25*sg): con tp=0 e sg=-1 il netto e' negativo -> lo short e' ammesso dal libro.
CORREZIONE: ok (lotti con segno); in piu' reconcile/stato_aperto dovrebbero segnalare resto>0 non allocato come avviso, non scartarlo.

Rilievo C10 [bug] (basso)

FILE: PG:src/live/tradesdb.py:172-173, 214-222 + scripts/live/book_execute.py:275-276, src/live/execution.py:79-80
COSA: un ordine fallito (place_order error, o market non eseguito) stampa -> BUY 0.0000 @ $0.0 … (NON VERIFICATO: …) e il parser lo registra come fill (qty 0, price 0, verified 0). Entra nel conteggio «fill» del giornale e produce round-trip a qty 0 nel FIFO (conteggio round-trip gonfiato).
PROVA: VERIFICATO (lettura): Fill(…, 0.0, None, …) + ${f.price or 0:,.1f}; _RE_FILL accetta NON VERIFICATO; rebuild_roundtrips non filtra verified. Nel log attuale 0 occorrenze.
PROPOSTA: in parse_cron_log scartare (o marcare stato='non-eseguito') i fill con qty==0; rebuild_roundtrips/stato_aperto/pnl_giorno su qty>0.

Verifica fable C10 VERDETTO: CONFERMATO

GRAVITA': basso (latente: 0 occorrenze)
PERCHE': src/live/execution.py:79-81 su errore ritorna Fill(..., 0.0, None, ...); book_execute.py:275-276 stampa -> BUY 0.0000 @ $0.0 fee 0.00000 (NON VERIFICATO: place_order error...); tradesdb.py:172-173 _RE_FILL accetta NON VERIFICATO[^)]* -> Fill(qty=0, price=0, verified=0) (214-222); fifo_roundtrips con lotto qty 0 produce un round-trip a qty 0 (usa=0, lotto rimosso).
CORREZIONE: ok (scartare o marcare stato='non-eseguito' i fill a qty 0 nel parser; FIFO/stato su qty>0).

Revisione 2026-09-27 (goldrake): rilievo Opus 5.5, verifica avversariale fable. Sola lettura, nessun codice toccato. ### Rilievo C2 [registro-falso] (latente) FILE: PG:src/live/tradesdb.py:264-290 (fifo_roundtrips, usato da rebuild_roundtrips e stato_aperto) COSA: FIFO solo-long. Una vendita senza lotti aperti (short netto) o oltre i lotti (flip long->short) viene scartata in silenzio; il riacquisto successivo diventa un lotto LONG aperto. SCENARIO: SKH01 puo' andare SHORT (book.py:14, giornale 15/09) e con TP01 a 0 il netto diventa negativo. sell 0.01@80000, buy 0.01@78000 -> 0 round-trip, "posizioni aperte: BTC 0.01 @78000" a libro flat. Flip buy 0.01@80000, sell 0.03@81000, buy 0.02@79000 -> P&L del tratto short (+40) perso, report dice long 0.02 aperto. Oggi non morde: nel log `net` negativo 0 volte su 2296 giri. PROVA: VERIFICATO: chiamata pura a `T.fifo_roundtrips` con i due scenari -> `short: rts [] aperti {'BTC':[qty 0.01 @78000]}`; `flip: n rts 1 [10.0] aperti {'BTC':[qty 0.02 @79000]}`. PROPOSTA: lotti con segno (deque per lato: una buy chiude prima gli short aperti, il resto apre long; simmetrico per sell). Test short e flip. ### Verifica fable C2 VERDETTO: CONFERMATO GRAVITA': medio (latente: net negativo 0 volte nel log) PERCHE': `src/live/tradesdb.py:264-290`: `sell` consuma solo lotti long; `while resto>1e-12 and lotti[...]` esce con `resto` scartato, nessun avviso; il `buy` successivo apre un lotto long. `book.py:226` `raw = weight*equity*(0.75*max(tp,0) + 0.25*sg)`: con tp=0 e sg=-1 il netto e' negativo -> lo short e' ammesso dal libro. CORREZIONE: ok (lotti con segno); in piu' `reconcile`/`stato_aperto` dovrebbero segnalare `resto>0` non allocato come avviso, non scartarlo. ### Rilievo C10 [bug] (basso) FILE: PG:src/live/tradesdb.py:172-173, 214-222 + scripts/live/book_execute.py:275-276, src/live/execution.py:79-80 COSA: un ordine fallito (place_order error, o market non eseguito) stampa `-> BUY 0.0000 @ $0.0 … (NON VERIFICATO: …)` e il parser lo registra come fill (qty 0, price 0, verified 0). Entra nel conteggio «fill» del giornale e produce round-trip a qty 0 nel FIFO (conteggio round-trip gonfiato). PROVA: VERIFICATO (lettura): `Fill(…, 0.0, None, …)` + `${f.price or 0:,.1f}`; `_RE_FILL` accetta `NON VERIFICATO`; `rebuild_roundtrips` non filtra `verified`. Nel log attuale 0 occorrenze. PROPOSTA: in `parse_cron_log` scartare (o marcare stato='non-eseguito') i fill con qty==0; `rebuild_roundtrips`/`stato_aperto`/`pnl_giorno` su `qty>0`. ### Verifica fable C10 VERDETTO: CONFERMATO GRAVITA': basso (latente: 0 occorrenze) PERCHE': `src/live/execution.py:79-81` su errore ritorna `Fill(..., 0.0, None, ...)`; `book_execute.py:275-276` stampa `-> BUY 0.0000 @ $0.0 fee 0.00000 (NON VERIFICATO: place_order error...)`; `tradesdb.py:172-173` `_RE_FILL` accetta `NON VERIFICATO[^)]*` -> `Fill(qty=0, price=0, verified=0)` (214-222); `fifo_roundtrips` con lotto qty 0 produce un round-trip a qty 0 (usa=0, lotto rimosso). CORREZIONE: ok (scartare o marcare `stato='non-eseguito'` i fill a qty 0 nel parser; FIFO/stato su qty>0).
Adriano added the bug label 2026-09-27 13:12:14 +00:00
Author
Owner

ANALISI (passo 1, Opus) — unita' U4, ramo lavoro/goldrake-u4, repo gitea:Adriano/PythagorasGoal
RIF: #27 (C1), #32 (C2, C10), #34 (C6), #35 (solo C7, C9, C15)
FILE: src/live/journal.py, src/live/tradesdb.py, scripts/live/analista.py, tests/

  1. #27 — journal.py ~246-252 (movimenti dichiarati): gia cerca un evento per ts_dopo senza filtrare fonte, il secondo dichiarato nello stesso intervallo sovrascrive il primo. Nuovo: cercare solo eventi con fonte == "rilevato" per l'aggancio "dichiarato+rilevato"; se esiste gia' un evento DICHIARATO nello stesso intervallo, SOMMARE i delta e concatenare le dichiarazioni (fonte resta "dichiarato"). Test: +25 e +30 dichiarati nello stesso intervallo, trading reale +5 -> trading risulta +5 e TWR coerente; un dichiarato che coincide con un salto rilevato -> "dichiarato+rilevato" come oggi.
  2. #32 C2 — tradesdb.fifo_roundtrips (~264-290): lotti con segno. Un sell senza lotti long apre un lotto SHORT; un buy chiude prima i lotti short (P&L = (entry - exit) * qty) e l'eccedenza apre long; inversione long->short in un solo fill: chiude i long e apre short con il resto. Nessun resto scartato in silenzio. Leggi tutti i chiamanti (reconcile, stato_aperto, report) e adegua dove leggono i lotti (segno/direzione). Test: long->chiuso (invariato, bit-exact con i test esistenti), short aperto e chiuso, inversione in un fill.
    #32 C10 — fill "NON VERIFICATO" a qty 0 (place_order error): nel parser (o subito dopo) i fill con qty <= 0 NON entrano nel FIFO ne' in stato_aperto; restano nel DB marcati (colonna esistente verified=0; se serve un campo nuovo, niente migrazioni distruttive: ALTER TABLE ADD COLUMN idempotente). Test.
    ⚠️ NON cambiare le regex di parse_cron_log ne' il formato che leggono (un'altra unita' lavora su book_execute e ne rispetta il formato).
  3. #34 — analista.py --secco: oggi chiama J.salva prima del return del secco. Nuovo: con --secco non si chiama salva ne' scrivi_file; la pagina si rende da voce (verificare che rendi_markdown funzioni senza la riga in DB; se legge nota/analisi dal DB, passarle vuote). Test: --secco su DB temporaneo -> DB invariato.
  4. #35 C9 — journal.py ~388: atteso = adesso.hour + 1 conta il giro dell'ora corrente prima del :47. Nuovo: adesso.hour + (1 if adesso.minute >= MINUTO_GIRO + 1 else 0) con MINUTO_GIRO = 47 costante di modulo commentata "deve restare d'accordo con la crontab e scripts/cron_book.sh". Test ai minuti 10 e 50.
    #35 C15 — finestra "ultimi 7 giorni" (~594-597) copre 8 giorni di calendario: baseline = ultima lettura <= giorno-7 fine giornata, oppure la finestra dichiarata nel testo come e' davvero: scegli la correzione che rende VERO il testo e dillo. Test.
    #35 C7 — drawdown (~596-615) sulla serie grezza: un prelievo certo diventa drawdown, un versamento alza il picco. Calcolare il DD sulla serie al netto dei movimenti certi (gli stessi che la pagina gia' scorpora per il TWR). Test con versamento e con prelievo.

TEST: nuovi in tests/test_journal_u4.py, tests/test_tradesdb_u4.py; DB sqlite temporanei (tmp_path). Suite completa: cd <worktree> && /opt/docker/PythagorasGoal/.venv/bin/python -m pytest -q -p no:cacheprovider (baseline 1098 passed, 3 skipped).

NON TOCCARE: parse_cron_log e le sue regex; scripts/live/book_execute.py; data/ (nessun DB vero: non esiste nel worktree e non va creato); /opt/docker/PythagorasGoal. Nessuna rete, nessun Telegram.

ANALISI (passo 1, Opus) — unita' U4, ramo lavoro/goldrake-u4, repo gitea:Adriano/PythagorasGoal RIF: #27 (C1), #32 (C2, C10), #34 (C6), #35 (solo C7, C9, C15) FILE: src/live/journal.py, src/live/tradesdb.py, scripts/live/analista.py, tests/ 1. #27 — journal.py ~246-252 (movimenti dichiarati): `gia` cerca un evento per ts_dopo senza filtrare fonte, il secondo dichiarato nello stesso intervallo sovrascrive il primo. Nuovo: cercare solo eventi con fonte == "rilevato" per l'aggancio "dichiarato+rilevato"; se esiste gia' un evento DICHIARATO nello stesso intervallo, SOMMARE i delta e concatenare le dichiarazioni (fonte resta "dichiarato"). Test: +25 e +30 dichiarati nello stesso intervallo, trading reale +5 -> trading risulta +5 e TWR coerente; un dichiarato che coincide con un salto rilevato -> "dichiarato+rilevato" come oggi. 2. #32 C2 — tradesdb.fifo_roundtrips (~264-290): lotti con segno. Un sell senza lotti long apre un lotto SHORT; un buy chiude prima i lotti short (P&L = (entry - exit) * qty) e l'eccedenza apre long; inversione long->short in un solo fill: chiude i long e apre short con il resto. Nessun `resto` scartato in silenzio. Leggi tutti i chiamanti (reconcile, stato_aperto, report) e adegua dove leggono i lotti (segno/direzione). Test: long->chiuso (invariato, bit-exact con i test esistenti), short aperto e chiuso, inversione in un fill. #32 C10 — fill "NON VERIFICATO" a qty 0 (place_order error): nel parser (o subito dopo) i fill con qty <= 0 NON entrano nel FIFO ne' in stato_aperto; restano nel DB marcati (colonna esistente verified=0; se serve un campo nuovo, niente migrazioni distruttive: ALTER TABLE ADD COLUMN idempotente). Test. ⚠️ NON cambiare le regex di parse_cron_log ne' il formato che leggono (un'altra unita' lavora su book_execute e ne rispetta il formato). 3. #34 — analista.py --secco: oggi chiama J.salva prima del return del secco. Nuovo: con --secco non si chiama salva ne' scrivi_file; la pagina si rende da `voce` (verificare che rendi_markdown funzioni senza la riga in DB; se legge nota/analisi dal DB, passarle vuote). Test: --secco su DB temporaneo -> DB invariato. 4. #35 C9 — journal.py ~388: atteso = adesso.hour + 1 conta il giro dell'ora corrente prima del :47. Nuovo: `adesso.hour + (1 if adesso.minute >= MINUTO_GIRO + 1 else 0)` con MINUTO_GIRO = 47 costante di modulo commentata "deve restare d'accordo con la crontab e scripts/cron_book.sh". Test ai minuti 10 e 50. #35 C15 — finestra "ultimi 7 giorni" (~594-597) copre 8 giorni di calendario: baseline = ultima lettura <= giorno-7 fine giornata, oppure la finestra dichiarata nel testo come e' davvero: scegli la correzione che rende VERO il testo e dillo. Test. #35 C7 — drawdown (~596-615) sulla serie grezza: un prelievo certo diventa drawdown, un versamento alza il picco. Calcolare il DD sulla serie al netto dei movimenti certi (gli stessi che la pagina gia' scorpora per il TWR). Test con versamento e con prelievo. TEST: nuovi in tests/test_journal_u4.py, tests/test_tradesdb_u4.py; DB sqlite temporanei (tmp_path). Suite completa: `cd <worktree> && /opt/docker/PythagorasGoal/.venv/bin/python -m pytest -q -p no:cacheprovider` (baseline 1098 passed, 3 skipped). NON TOCCARE: parse_cron_log e le sue regex; scripts/live/book_execute.py; data/ (nessun DB vero: non esiste nel worktree e non va creato); /opt/docker/PythagorasGoal. Nessuna rete, nessun Telegram.
Author
Owner

ESITO: fatto sul ramo, non in produzione
RIF: gitea:Adriano/PythagorasGoal#32
COSA: correzione su lavoro/goldrake (pushato), analisi Opus, codice Sonnet, controllo finale Opus
FILE: commit b8c2bba
PROVA: /home/adriano/lavoro/PythagorasGoal pytest -q → 1166 passed, 3 skipped
MANCA: merge su main — richiede ok esplicito di Adriano (libro vivo). Chiusura con --soluzione dopo il merge.
PROSSIMO: ok di Adriano → merge lavoro/goldrake su main → git pull in /opt/docker/PythagorasGoal

ESITO: fatto sul ramo, non in produzione RIF: gitea:Adriano/PythagorasGoal#32 COSA: correzione su lavoro/goldrake (pushato), analisi Opus, codice Sonnet, controllo finale Opus FILE: commit b8c2bba PROVA: /home/adriano/lavoro/PythagorasGoal pytest -q → 1166 passed, 3 skipped MANCA: merge su main — richiede ok esplicito di Adriano (libro vivo). Chiusura con --soluzione dopo il merge. PROSSIMO: ok di Adriano → merge lavoro/goldrake su main → git pull in /opt/docker/PythagorasGoal
Adriano added the attesa:adriano label 2026-09-27 14:17:42 +00:00
Author
Owner

Soluzione

src/live/tradesdb.py: lotti FIFO con segno (short), fill qty<=0 esclusi, stato_aperto con direzione.
COMMIT: b8c2bba, su main dal 27/09/2026 (merge con ok di Adriano). Produzione aggiornata.

**Soluzione** src/live/tradesdb.py: lotti FIFO con segno (short), fill qty<=0 esclusi, stato_aperto con direzione. COMMIT: b8c2bba, su main dal 27/09/2026 (merge con ok di Adriano). Produzione aggiornata.
Adriano removed the attesa:adriano label 2026-09-27 14:28:00 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Adriano/PythagorasGoal#32