lettura USDE fallita -> equity USDC-sola fidata: falso -64%, vendite e watermark sovrascritto #20

Closed
opened 2026-09-27 13:12:10 +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 A2 [bug]

FILE: PG:src/live/shadow.py:104-106 (_collaterale_usde) + tests/test_shadow_usde.py:76-78
COSA: se la lettura del conto USDE fallisce, l'USDE vale 0 e l'equity USDC-sola viene trattata come REALE e fidata (niente eq_fallback): e' lo stesso falso "-64%" che usde.py dichiara di voler evitare per il prezzo, e il test lo blinda come corretto.
SCENARIO: quota USDE 64% (2.852 USDE), USDC equity ~$1.650. Un 502 del gateway solo su get_account_summary(USDE) -> equity $1.650 invece di $4.583 -> cap = $825, target BTC da $813 a ~$293, ETH da $756 a ~$272 -> due SELL reduce_only da ~$500 ciascuno inviate (gate online/pos_error non scattano), watermark sovrascritto a $1.650, alert "USCITA DI FONDI -64%"; il giro dopo, se la lettura torna, ricompra tutto (+64% "VERSAMENTO"). Costo: round-trip ~$2.000 di nozionale + falso registro di un'uscita di fondi.
PROVA: VERIFICATO: lettura shadow.py:103-106 (except -> return 0.0, "") e test_gateway_usde_giu_non_rompe_la_lettura_usdc (asserisce eq == USDC solo, src "mainnet USDC"); usde.py:16-17 dichiara "contare 0 ricrea il falso -24%". Mai accaduto nei 2296 giri di logs/cron_book.log (grep "USCITA DI FONDI": 1 sola, 25/08, altra causa).
PROPOSTA: eccezione sulla lettura USDE -> real_eq None (percorso eq_fallback) oppure, meglio, stop del giro come per pos_error ("equity non leggibile, non opero"); aggiornare il test.

Verifica fable A2 VERDETTO: CONFERMATO

GRAVITA': alto
PERCHE': shadow.py:105-107 except Exception: return 0.0, "" -> _equity :119-122 ritorna eq USDC-sola con src "mainnet USDC" -> real_eq NON None, eq_fallback None -> book.py _cap trusted (equity x 0,5) e write_equity_watermark sovrascrive il watermark col valore dimezzato; target ridotti -> SELL reduce_only reali (nessun gate scatta: online e pos ok). tests/test_shadow_usde.py:77-79 test_gateway_usde_giu_non_rompe_la_lettura_usdc blinda proprio questo comportamento. usde.py:16-17 dichiara il principio opposto per il prezzo. Mai accaduto nel log.
CORREZIONE: ok (eccezione USDE -> real_eq None / stop del giro); in piu' NON scrivere il watermark quando la lettura USDE e' fallita, altrimenti il cap di fallback resta dimezzato anche ai giri dopo.

Revisione 2026-09-27 (goldrake): rilievo Opus 5.5, verifica avversariale fable. Sola lettura, nessun codice toccato. ### Rilievo A2 [bug] FILE: PG:src/live/shadow.py:104-106 (_collaterale_usde) + tests/test_shadow_usde.py:76-78 COSA: se la lettura del conto USDE fallisce, l'USDE vale 0 e l'equity USDC-sola viene trattata come REALE e fidata (niente eq_fallback): e' lo stesso falso "-64%" che usde.py dichiara di voler evitare per il prezzo, e il test lo blinda come corretto. SCENARIO: quota USDE 64% (2.852 USDE), USDC equity ~$1.650. Un 502 del gateway solo su get_account_summary(USDE) -> equity $1.650 invece di $4.583 -> cap = $825, target BTC da $813 a ~$293, ETH da $756 a ~$272 -> due SELL reduce_only da ~$500 ciascuno inviate (gate online/pos_error non scattano), watermark sovrascritto a $1.650, alert "USCITA DI FONDI -64%"; il giro dopo, se la lettura torna, ricompra tutto (+64% "VERSAMENTO"). Costo: round-trip ~$2.000 di nozionale + falso registro di un'uscita di fondi. PROVA: VERIFICATO: lettura shadow.py:103-106 (except -> return 0.0, "") e test_gateway_usde_giu_non_rompe_la_lettura_usdc (asserisce eq == USDC solo, src "mainnet USDC"); usde.py:16-17 dichiara "contare 0 ricrea il falso -24%". Mai accaduto nei 2296 giri di logs/cron_book.log (grep "USCITA DI FONDI": 1 sola, 25/08, altra causa). PROPOSTA: eccezione sulla lettura USDE -> real_eq None (percorso eq_fallback) oppure, meglio, stop del giro come per pos_error ("equity non leggibile, non opero"); aggiornare il test. ### Verifica fable A2 VERDETTO: CONFERMATO GRAVITA': alto PERCHE': shadow.py:105-107 `except Exception: return 0.0, ""` -> `_equity` :119-122 ritorna eq USDC-sola con src "mainnet USDC" -> real_eq NON None, eq_fallback None -> book.py `_cap` trusted (equity x 0,5) e `write_equity_watermark` sovrascrive il watermark col valore dimezzato; target ridotti -> SELL reduce_only reali (nessun gate scatta: online e pos ok). tests/test_shadow_usde.py:77-79 `test_gateway_usde_giu_non_rompe_la_lettura_usdc` blinda proprio questo comportamento. usde.py:16-17 dichiara il principio opposto per il prezzo. Mai accaduto nel log. CORREZIONE: ok (eccezione USDE -> real_eq None / stop del giro); in piu' NON scrivere il watermark quando la lettura USDE e' fallita, altrimenti il cap di fallback resta dimezzato anche ai giri dopo.
Adriano added the bug label 2026-09-27 13:12:10 +00:00
Author
Owner

ANALISI (passo 1, Opus) — unita' U2, ramo lavoro/goldrake-u2, repo gitea:Adriano/PythagorasGoal
RIF: #20 (A2), #21 (A3), #22 (A10), #25 (A6), #23 (A4)
FILE: src/live/shadow.py, src/live/book.py, scripts/live/book_execute.py, src/live/execution.py (SOLO rebalance_signed, per #23), tests/

Principio unico (dichiarato): quando il giro non sa QUANTO vale il conto o QUALE e' il segnale, NON manda ordini di ribilancio ma CONTROLLA LO STESSO il disaster-SL di ogni asset (la protezione dipende da posizione e mark, non da equity o segnale). Come gia' fa pos_error, ma senza saltare la protezione.

  1. #20 — shadow._collaterale_usde: un'eccezione sulla lettura del conto USDE NON deve valere 0. Ritorna (None, "USDE illeggibile: : "); _equity, se riceve None, ritorna (None, "") -> real_eq None -> eq_fallback valorizzato. Equity USDE <= 0 letta con successo resta 0 (conto senza USDE: legittimo). Il watermark non si scrive (write_equity_watermark riceve None: gia' cosi', verificalo con un test). Aggiornare tests/test_shadow_usde.py::test_gateway_usde_giu_non_rompe_la_lettura_usdc al nuovo comportamento (e dirlo nel rapporto).
  2. #21 — eq_fallback: oggi dimensiona su FALLBACK_CAPITAL $2.000 (file paper ritirato il 17/07) e prosegue. Nuovo, in book_execute._run: se r["eq_fallback"] -> modo "SOLO PROTEZIONE": nessun rebalance_signed, ensure_disaster_sl per ogni asset, notify "⚠️ BOOK LIVE — equity non leggibile: nessun ordine, solo protezione" con la nota. Aggiornare i commenti che dicono "NON blocca (l'hard-cap $300/asset limita il downside)" in shadow.py e book_execute.py.
  3. #22 — skh_error: stesso modo SOLO PROTEZIONE (non forzare SKH flat con ordini). book.book_report continua a esporre skh_error; la stampa "SKH forzato flat!" diventa "SKH non calcolato: nessun ordine, solo protezione". Il report mostra comunque i target (sono informativi).
    Implementazione: in _run una variabile solo_protezione: str | None (motivo) calcolata dopo i gate esistenti (offline, pos_error, feed stantio restano come sono: return). Nel ciclo per asset: se solo_protezione, act = "HOLD (solo protezione: )" e niente rebalance; il blocco ensure_disaster_sl resta identico. Il riepilogo finale e l'exit code: exit 2 anche quando solo_protezione (giro degradato).
  4. #25 — shadow_report: online = client is not None and TUTTI i mark_src == "mainnet" (oggi solo BTC). Il messaggio offline in book_execute gia' stampa i mark di tutti gli asset: ok.
  5. #23 — fill persi:
    a) execution.DeribitTrader.rebalance_signed: se dopo almeno un fill eseguito (es. flip: close fatto) un passo successivo solleva, sollevare RebalanceParziale(fills, causa) (nuova eccezione in execution.py, sottoclasse RuntimeError, attributi .fills e .causa). Nessun'altra modifica a execution.py (U1 lavora su _submit/open_orders/position_usd nello stesso file: non toccarli).
    b) book_execute: estrarre la registrazione di un fill (print " -> SIDE ...", log_event, notify) in una funzione _registra_fill(...); chiamarla per ogni fill SUBITO dopo rebalance_signed e PRIMA di leggere newpos; newpos letto in try separato (None se illeggibile: pos_after=None nel jsonl, e la riga "reconcile: pos" diventa "reconcile: pos illeggibile ()"). Su RebalanceParziale: registra i fill di e.fills, poi rilancia la causa nell'handler di isolamento esistente.
    ⚠️ Il formato ESATTO delle righe stampate " -> BUY 0.0100 @ $84,000.0 fee 0.00000 (OK)" e "reconcile" e' letto da src/live/tradesdb.py (parse_cron_log, regex _RE_FILL ecc.): leggi quelle regex e NON cambiare il testo delle righe che parse legge. L'ordine delle righe per un giro normale deve restare: fill..., poi reconcile. Test che parse_cron_log legge un log prodotto dal nuovo codice (giro normale e giro con RebalanceParziale).
  6. set_open_cap (da U1, stesso rilascio): subito prima di rebalance_signed chiamare if hasattr(trader, "set_open_cap"): trader.set_open_cap(inst, r["cap_per_asset"] * r.get("scala", 1.0), mark). hasattr perche' U1 e U2 si uniscono dopo; un commento dice che l'hasattr si toglie al merge.

TEST (tests/test_book_u2.py, pattern dei test esistenti in tests/test_book_live.py / test_book_resilienza_venue.py, tutto mockato):

  • USDE che solleva -> real_equity None, eq_fallback valorizzato, watermark non scritto.
  • eq_fallback -> nessuna chiamata a rebalance_signed, ensure_disaster_sl chiamato per BTC ed ETH, notify con "solo protezione", exit code 2.
  • skh_error -> idem.
  • mark ETH in fallback, BTC mainnet -> online False -> nessun ordine.
  • flip con open() che solleva dopo close() eseguito -> il fill del close e' in book_executions.jsonl e nella riga "-> ..." del log; l'asset risulta fallito; l'altro asset elaborato.
  • position_usd che solleva dopo un fill -> fill registrato, pos_after None.
    Suite completa: cd <worktree> && /opt/docker/PythagorasGoal/.venv/bin/python -m pytest -q -p no:cacheprovider (baseline 1098 passed, 3 skipped).

NON TOCCARE: src/live/deribit.py; in execution.py tutto tranne rebalance_signed e la nuova eccezione; config/live.json; la sequenza e i testi dei gate offline/pos_error/feed stantio; /opt/docker/PythagorasGoal. Nessuna rete, nessun ordine.

ANALISI (passo 1, Opus) — unita' U2, ramo lavoro/goldrake-u2, repo gitea:Adriano/PythagorasGoal RIF: #20 (A2), #21 (A3), #22 (A10), #25 (A6), #23 (A4) FILE: src/live/shadow.py, src/live/book.py, scripts/live/book_execute.py, src/live/execution.py (SOLO rebalance_signed, per #23), tests/ Principio unico (dichiarato): quando il giro non sa QUANTO vale il conto o QUALE e' il segnale, NON manda ordini di ribilancio ma CONTROLLA LO STESSO il disaster-SL di ogni asset (la protezione dipende da posizione e mark, non da equity o segnale). Come gia' fa pos_error, ma senza saltare la protezione. 1. #20 — shadow._collaterale_usde: un'eccezione sulla lettura del conto USDE NON deve valere 0. Ritorna (None, "USDE illeggibile: <Tipo>: <msg>"); _equity, se riceve None, ritorna (None, "<quella nota>") -> real_eq None -> eq_fallback valorizzato. Equity USDE <= 0 letta con successo resta 0 (conto senza USDE: legittimo). Il watermark non si scrive (write_equity_watermark riceve None: gia' cosi', verificalo con un test). Aggiornare tests/test_shadow_usde.py::test_gateway_usde_giu_non_rompe_la_lettura_usdc al nuovo comportamento (e dirlo nel rapporto). 2. #21 — eq_fallback: oggi dimensiona su FALLBACK_CAPITAL $2.000 (file paper ritirato il 17/07) e prosegue. Nuovo, in book_execute._run: se r["eq_fallback"] -> modo "SOLO PROTEZIONE": nessun rebalance_signed, ensure_disaster_sl per ogni asset, notify "⚠️ BOOK LIVE — equity non leggibile: nessun ordine, solo protezione" con la nota. Aggiornare i commenti che dicono "NON blocca (l'hard-cap $300/asset limita il downside)" in shadow.py e book_execute.py. 3. #22 — skh_error: stesso modo SOLO PROTEZIONE (non forzare SKH flat con ordini). book.book_report continua a esporre skh_error; la stampa "SKH forzato flat!" diventa "SKH non calcolato: nessun ordine, solo protezione". Il report mostra comunque i target (sono informativi). Implementazione: in _run una variabile `solo_protezione: str | None` (motivo) calcolata dopo i gate esistenti (offline, pos_error, feed stantio restano come sono: return). Nel ciclo per asset: se solo_protezione, act = "HOLD (solo protezione: <motivo breve>)" e niente rebalance; il blocco ensure_disaster_sl resta identico. Il riepilogo finale e l'exit code: exit 2 anche quando solo_protezione (giro degradato). 4. #25 — shadow_report: online = client is not None and TUTTI i mark_src == "mainnet" (oggi solo BTC). Il messaggio offline in book_execute gia' stampa i mark di tutti gli asset: ok. 5. #23 — fill persi: a) execution.DeribitTrader.rebalance_signed: se dopo almeno un fill eseguito (es. flip: close fatto) un passo successivo solleva, sollevare `RebalanceParziale(fills, causa)` (nuova eccezione in execution.py, sottoclasse RuntimeError, attributi .fills e .causa). Nessun'altra modifica a execution.py (U1 lavora su _submit/open_orders/position_usd nello stesso file: non toccarli). b) book_execute: estrarre la registrazione di un fill (print " -> SIDE ...", log_event, notify) in una funzione `_registra_fill(...)`; chiamarla per ogni fill SUBITO dopo rebalance_signed e PRIMA di leggere newpos; newpos letto in try separato (None se illeggibile: pos_after=None nel jsonl, e la riga "reconcile: pos" diventa "reconcile: pos illeggibile (<Tipo>)"). Su RebalanceParziale: registra i fill di e.fills, poi rilancia la causa nell'handler di isolamento esistente. ⚠️ Il formato ESATTO delle righe stampate " -> BUY 0.0100 @ $84,000.0 fee 0.00000 (OK)" e "reconcile" e' letto da src/live/tradesdb.py (parse_cron_log, regex _RE_FILL ecc.): leggi quelle regex e NON cambiare il testo delle righe che parse legge. L'ordine delle righe per un giro normale deve restare: fill..., poi reconcile. Test che parse_cron_log legge un log prodotto dal nuovo codice (giro normale e giro con RebalanceParziale). 6. set_open_cap (da U1, stesso rilascio): subito prima di rebalance_signed chiamare `if hasattr(trader, "set_open_cap"): trader.set_open_cap(inst, r["cap_per_asset"] * r.get("scala", 1.0), mark)`. hasattr perche' U1 e U2 si uniscono dopo; un commento dice che l'hasattr si toglie al merge. TEST (tests/test_book_u2.py, pattern dei test esistenti in tests/test_book_live.py / test_book_resilienza_venue.py, tutto mockato): - USDE che solleva -> real_equity None, eq_fallback valorizzato, watermark non scritto. - eq_fallback -> nessuna chiamata a rebalance_signed, ensure_disaster_sl chiamato per BTC ed ETH, notify con "solo protezione", exit code 2. - skh_error -> idem. - mark ETH in fallback, BTC mainnet -> online False -> nessun ordine. - flip con open() che solleva dopo close() eseguito -> il fill del close e' in book_executions.jsonl e nella riga "-> ..." del log; l'asset risulta fallito; l'altro asset elaborato. - position_usd che solleva dopo un fill -> fill registrato, pos_after None. Suite completa: `cd <worktree> && /opt/docker/PythagorasGoal/.venv/bin/python -m pytest -q -p no:cacheprovider` (baseline 1098 passed, 3 skipped). NON TOCCARE: src/live/deribit.py; in execution.py tutto tranne rebalance_signed e la nuova eccezione; config/live.json; la sequenza e i testi dei gate offline/pos_error/feed stantio; /opt/docker/PythagorasGoal. Nessuna rete, nessun ordine.
Author
Owner

ESITO: fatto sul ramo, non in produzione
RIF: gitea:Adriano/PythagorasGoal#20
COSA: correzione su lavoro/goldrake (pushato), analisi Opus, codice Sonnet, controllo finale Opus
FILE: commit cfc4265
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#20 COSA: correzione su lavoro/goldrake (pushato), analisi Opus, codice Sonnet, controllo finale Opus FILE: commit cfc4265 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:36 +00:00
Author
Owner

Soluzione

src/live/shadow.py _collaterale_usde: USDE illeggibile -> (None, motivo), _equity propaga equity ignota invece di contare 0.
COMMIT: cfc4265, su main dal 27/09/2026 (merge con ok di Adriano). Produzione aggiornata.

**Soluzione** src/live/shadow.py _collaterale_usde: USDE illeggibile -> (None, motivo), _equity propaga equity ignota invece di contare 0. COMMIT: cfc4265, su main dal 27/09/2026 (merge con ok di Adriano). Produzione aggiornata.
Adriano removed the attesa:adriano label 2026-09-27 14:27:53 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Adriano/PythagorasGoal#20