tetto di leva letto con tre formule (usde cuscino, journal leva, journal movimenti): a scala 1,25 cuscino scoperto #28

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

FILE: pg:src/live/usde.py:115-121 (usato da scripts/live/cuscino_watch.py:216 e scripts/live/usde_convert.py:147)
COSA: il cuscino di regolamento ignora la chiave di scala book_scale_k: frazione = N_ASSET x WEIGHT x sl, mentre l'esposizione massima del libro e' N x frac x SCALA (book.book_net_target moltiplica per scala).
SCENARIO: GATE SCALA-01 autorizza 1,25 (gradino ammesso da SCALA_LADDER e da LEVA_LORDA_MAX 1,25) -> esposizione max 1,25E, disaster-SL 0,375E; il cuscino resta 0,30E e il bersaglio quota 0,64 lascia USDC 0,36E < 0,375E: il cuscino e' scoperto per costruzione mentre cuscino_watch dice OK e usde_convert approva gli acquisti. Oggi latente (chiave assente in config/live.json).
PROVA: VERIFICATO: lettura — usde.frazione_cuscino non legge book_scale_k; book.py:226-227 return scala * max(-cap, min(cap, raw)); grep "scala|book_scale_k" in usde.py/cuscino_watch.py/usde_convert.py -> nessun risultato.
PROPOSTA: frazione = N_ASSET x min(WEIGHT, frac) x book._scala(real_equity=1.0, cfg=cfg) x sl (la scala dichiarata, validata dallo stesso lettore); test che incrocia con scale_watch D3.

Verifica fable B5 VERDETTO: CONFERMATO

GRAVITA': medio (latente: book_scale_k assente in config/live.json)
PERCHE': src/live/usde.py:115-121 frazione_cuscino = N_ASSET x B.WEIGHT x sl, nessuna lettura di book_scale_k; src/live/book.py:226-227 return scala * max(-cap, min(cap, raw)) -> esposizione max per asset = scala x equity x min(frac, WEIGHT); _scala (110-127) ammette 1,25 con frac 0,5 (leva 1,25 = LEVA_LORDA_MAX). A scala 1,25 il cuscino richiesto e' 0,375E e la formula dice 0,30E; bersaglio quota 0,64 -> USDC 0,36E < 0,375E. Nessuna nota in CLAUDE.md/config che dichiari il cuscino a scala 1,00 come scelta. Il giornale (journal.py:198-203) invece include la scala nel tetto di leva: due lettori, due formule (viola P1 che usde.py dichiara).
CORREZIONE: proposta ok nella sostanza, con un'avvertenza: book._scala(real_equity=1.0, cfg=cfg) SOLLEVA ScalaNonAutorizzata su config fuori scaletta/tetto -> in frazione_cuscino va catturata e resa come frazione al tetto di codice (LEVA_LORDA_MAX x sl) con come che lo dichiara, altrimenti cuscino_watch muore e il cuscino resta senza sorveglianza proprio quando la config e' sbagliata (P5).

Rilievo A8 [pulizia]

FILE: PG:src/live/usde.py:112,115-121
COSA: il cuscino di regolamento usa B.WEIGHT (peso 50/50 degli asset) e N_ASSET=2 cablato, non max_notional_per_asset_frac ne' book_scale_k: coincide oggi (0,5 = 0,5, k assente) ma e' la grandezza sbagliata — la massima esposizione lorda e' n_assetcap = nfracscalaequity.
SCENARIO: book_scale_k = 1,25 (gradino ammesso da SCALA_LADDER) -> esposizione lorda max 1,25x, cuscino richiesto 37,5%, ma cuscino_watch continua a puntare a quota USDE 0,64 (cuscino 30%): il saldo USDC va negativo gia' dentro il disaster-SL.
PROVA: VERIFICATO: lettura usde.py:115-121 e book.py:106-133 (_scala valida frac*scala ma usde non lo usa); config _nota_usde dice "n_asset x frac x disaster_sl_pct".
PROPOSTA: frazione_cuscino = len(ASSETS) * max_notional_per_asset_frac * scala_configurata * disaster_sl_pct.

Verifica fable A8 VERDETTO: CONFERMATO

GRAVITA': basso
PERCHE': usde.py:112 N_ASSET = 2, :119 frac = float(B.WEIGHT) — non max_notional_per_asset_frac ne' la scala; il commento :107 e config _nota_usde dichiarano "n_asset x frac x disaster_sl_pct". Coincide oggi (WEIGHT 0,5 = frac 0,5, scala 1,0); diverge al primo gradino di book_scale_k o cambio di frac.
CORREZIONE: ok (usare len(ASSETS), max_notional_per_asset_frac e la scala configurata).

Rilievo C16 [pulizia] (latente)

FILE: PG:src/live/journal.py:553-558 (regola leva) vs journal.py:198-203 (movimenti_capitale)
COSA: due tetti di leva nello stesso modulo: la regola leva usa max_notional_per_asset_frac × n_asset e ignora book_scale_k e book.LEVA_LORDA_MAX; il classificatore dei movimenti li include. Con scala 1,25 (tetto di codice) la pagina stampa un «margine» negativo con un libro dentro i limiti.
PROVA: VERIFICATO (lettura); oggi scala 1,00 (cron_book.log «scala libro 1.00x (tetto 1.25x)»), quindi non morde.
PROPOSTA: una funzione leva_tetto(cfg) usata da entrambe.

COPERTURA: src/live/tradesdb.py, scripts/live/trades_db.py, src/live/analista.py, scripts/live/analista.py, src/live/revisione.py, scripts/live/revisione.py, scripts/live/telegram_daily.py, scripts/cron_daily.sh, scripts/cron_review.sh, src/live/dashboard.py (tutti per intero); src/live/journal.py; CLAUDE.md §5.12-16 e grep mirati; test eseguiti, non letti per intero.

Verifica fable C16 VERDETTO: CONFERMATO

GRAVITA': basso (latente: scala 1,00)
PERCHE': journal.py:553-558 regola leva: tetto = cap_frac x n_asset (1,00), senza book_scale_k e senza min(..., LEVA_LORDA_MAX); journal.py:198-203 movimenti_capitale usa min(frac x n x scala, LEVA_LORDA_MAX). A scala 1,25 la pagina stamperebbe margine negativo con libro entro il tetto. Stessa famiglia di B5 (tre lettori del tetto di leva: usde.py, journal leva, journal movimenti).
CORREZIONE: ok (leva_tetto(cfg) unica, in book, usata da journal x2 e da usde.frazione_cuscino — chiude B5 e C16 insieme).

Revisione 2026-09-27 (goldrake): rilievo Opus 5.5, verifica avversariale fable. Sola lettura, nessun codice toccato. ### Rilievo B5 [bug] FILE: pg:src/live/usde.py:115-121 (usato da scripts/live/cuscino_watch.py:216 e scripts/live/usde_convert.py:147) COSA: il cuscino di regolamento ignora la chiave di scala `book_scale_k`: frazione = N_ASSET x WEIGHT x sl, mentre l'esposizione massima del libro e' N x frac x SCALA (book.book_net_target moltiplica per `scala`). SCENARIO: GATE SCALA-01 autorizza 1,25 (gradino ammesso da SCALA_LADDER e da LEVA_LORDA_MAX 1,25) -> esposizione max 1,25E, disaster-SL 0,375E; il cuscino resta 0,30E e il bersaglio quota 0,64 lascia USDC 0,36E < 0,375E: il cuscino e' scoperto per costruzione mentre cuscino_watch dice OK e usde_convert approva gli acquisti. Oggi latente (chiave assente in config/live.json). PROVA: VERIFICATO: lettura — usde.frazione_cuscino non legge `book_scale_k`; book.py:226-227 `return scala * max(-cap, min(cap, raw))`; grep "scala|book_scale_k" in usde.py/cuscino_watch.py/usde_convert.py -> nessun risultato. PROPOSTA: frazione = N_ASSET x min(WEIGHT, frac) x book._scala(real_equity=1.0, cfg=cfg) x sl (la scala dichiarata, validata dallo stesso lettore); test che incrocia con scale_watch D3. ### Verifica fable B5 VERDETTO: CONFERMATO GRAVITA': medio (latente: `book_scale_k` assente in `config/live.json`) PERCHE': `src/live/usde.py:115-121` `frazione_cuscino = N_ASSET x B.WEIGHT x sl`, nessuna lettura di `book_scale_k`; `src/live/book.py:226-227` `return scala * max(-cap, min(cap, raw))` -> esposizione max per asset = scala x equity x min(frac, WEIGHT); `_scala` (110-127) ammette 1,25 con frac 0,5 (leva 1,25 = LEVA_LORDA_MAX). A scala 1,25 il cuscino richiesto e' 0,375E e la formula dice 0,30E; bersaglio quota 0,64 -> USDC 0,36E < 0,375E. Nessuna nota in CLAUDE.md/config che dichiari il cuscino a scala 1,00 come scelta. Il giornale (`journal.py:198-203`) invece include la scala nel tetto di leva: due lettori, due formule (viola P1 che usde.py dichiara). CORREZIONE: proposta ok nella sostanza, con un'avvertenza: `book._scala(real_equity=1.0, cfg=cfg)` SOLLEVA `ScalaNonAutorizzata` su config fuori scaletta/tetto -> in `frazione_cuscino` va catturata e resa come frazione al tetto di codice (LEVA_LORDA_MAX x sl) con `come` che lo dichiara, altrimenti cuscino_watch muore e il cuscino resta senza sorveglianza proprio quando la config e' sbagliata (P5). ### Rilievo A8 [pulizia] FILE: PG:src/live/usde.py:112,115-121 COSA: il cuscino di regolamento usa `B.WEIGHT` (peso 50/50 degli asset) e N_ASSET=2 cablato, non `max_notional_per_asset_frac` ne' `book_scale_k`: coincide oggi (0,5 = 0,5, k assente) ma e' la grandezza sbagliata — la massima esposizione lorda e' n_asset*cap = n*frac*scala*equity. SCENARIO: book_scale_k = 1,25 (gradino ammesso da SCALA_LADDER) -> esposizione lorda max 1,25x, cuscino richiesto 37,5%, ma cuscino_watch continua a puntare a quota USDE 0,64 (cuscino 30%): il saldo USDC va negativo gia' dentro il disaster-SL. PROVA: VERIFICATO: lettura usde.py:115-121 e book.py:106-133 (_scala valida frac*scala ma usde non lo usa); config _nota_usde dice "n_asset x frac x disaster_sl_pct". PROPOSTA: frazione_cuscino = len(ASSETS) * max_notional_per_asset_frac * scala_configurata * disaster_sl_pct. ### Verifica fable A8 VERDETTO: CONFERMATO GRAVITA': basso PERCHE': usde.py:112 `N_ASSET = 2`, :119 `frac = float(B.WEIGHT)` — non `max_notional_per_asset_frac` ne' la scala; il commento :107 e config `_nota_usde` dichiarano "n_asset x frac x disaster_sl_pct". Coincide oggi (WEIGHT 0,5 = frac 0,5, scala 1,0); diverge al primo gradino di book_scale_k o cambio di frac. CORREZIONE: ok (usare len(ASSETS), max_notional_per_asset_frac e la scala configurata). ### Rilievo C16 [pulizia] (latente) FILE: PG:src/live/journal.py:553-558 (regola `leva`) vs journal.py:198-203 (movimenti_capitale) COSA: due tetti di leva nello stesso modulo: la regola `leva` usa `max_notional_per_asset_frac × n_asset` e ignora `book_scale_k` e `book.LEVA_LORDA_MAX`; il classificatore dei movimenti li include. Con scala 1,25 (tetto di codice) la pagina stampa un «margine» negativo con un libro dentro i limiti. PROVA: VERIFICATO (lettura); oggi scala 1,00 (cron_book.log «scala libro 1.00x (tetto 1.25x)»), quindi non morde. PROPOSTA: una funzione `leva_tetto(cfg)` usata da entrambe. COPERTURA: src/live/tradesdb.py, scripts/live/trades_db.py, src/live/analista.py, scripts/live/analista.py, src/live/revisione.py, scripts/live/revisione.py, scripts/live/telegram_daily.py, scripts/cron_daily.sh, scripts/cron_review.sh, src/live/dashboard.py (tutti per intero); src/live/journal.py; CLAUDE.md §5.12-16 e grep mirati; test eseguiti, non letti per intero. ### Verifica fable C16 VERDETTO: CONFERMATO GRAVITA': basso (latente: scala 1,00) PERCHE': `journal.py:553-558` regola `leva`: `tetto = cap_frac x n_asset` (1,00), senza `book_scale_k` e senza `min(..., LEVA_LORDA_MAX)`; `journal.py:198-203` `movimenti_capitale` usa `min(frac x n x scala, LEVA_LORDA_MAX)`. A scala 1,25 la pagina stamperebbe margine negativo con libro entro il tetto. Stessa famiglia di B5 (tre lettori del tetto di leva: usde.py, journal leva, journal movimenti). CORREZIONE: ok (`leva_tetto(cfg)` unica, in `book`, usata da journal x2 e da usde.frazione_cuscino — chiude B5 e C16 insieme).
Adriano added the bug label 2026-09-27 13:12:12 +00:00
Author
Owner

ANALISI (passo 1, Opus, 27/09/2026)

ANALISI U7 — gitea:Adriano/PythagorasGoal#28 (B5, A8, C16) + #35 A7
CARTELLA: /home/adriano/lavoro/PG-u7 RAMO: lavoro/goldrake-u7 (da main 841d4c7)
PERCHE': il tetto di leva lorda e' letto con tre formule diverse (usde.frazione_cuscino, journal regola leva, journal movimenti_capitale). A scala 1,25 il cuscino USDC risulta 0,30E mentre servirebbe 0,375E. Serve una sola funzione in book.

MODIFICHE

  1. src/live/book.py — nuova funzione pubblica, accanto a _scala:
    def leva_tetto(cfg: dict | None = None) -> tuple[float, str]:
    Leva lorda massima che il libro puo' prendere = len(ASSETS) x min(WEIGHT, frac) x scala, al piu' LEVA_LORDA_MAX.
    • frac = cfg["max_notional_per_asset_frac"] se presente, altrimenti WEIGHT. Motivo: |book_net_target| <= scalamin(cap, WEIGHTequity), perche' |raw| <= weightequity(W_TP01+W_SKH).
    • scala = _scala(real_equity=1.0, cfg=cfg): la scala DICHIARATA, gia' validata.
    • Su ScalaNonAutorizzata: ritorna (LEVA_LORDA_MAX, "config fuori scaletta/tetto (): tetto di codice LEVA_LORDA_MAX"). Non solleva: cuscino_watch non deve morire proprio quando la config e' sbagliata (verifica fable B5).
    • Se il risultato supera LEVA_LORDA_MAX viene troncato a LEVA_LORDA_MAX, e come lo dice.
    • come = testo leggibile, es. "2 asset x frac 0.5 x scala 1.0 = 1.00x".
    • cfg None -> legge CONFIG come fa _scala.
  2. src/live/usde.py::frazione_cuscino — leva, come_leva = B.leva_tetto(cfg), poi return leva * sl, f"{come_leva} x SL {sl:.0%}".
    • Rimuovere N_ASSET = 2 solo se non ha altri usi (grep in src/ scripts/ tests/). Se ne ha, lasciarlo e dirlo nel rapporto.
    • Aggiornare il commento del blocco (righe ~101-109): la formula e' book.leva_tetto x sl.
  3. src/live/journal.py::movimenti_capitale (~198-202) — leva_tetto = _book.leva_tetto(cfg)[0]. Resta il try/except con il fallback LEVA_LORDA_MAX sul config illeggibile.
  4. src/live/journal.py regola leva (~576-580) — tetto, come = _book.leva_tetto(cfg).
    • Testo: f"Leva lorda {lv:.2f}x su un tetto di {tetto:.2f}x ({come}, da config/live.json e book.leva_tetto): margine {tetto - lv:.2f}x."
    • Il ramo except resta com'e'.
  5. #35 A7 — src/live/book.py:318 build_book_order(..., min_usd=5.0): leggere min_order_usd dalla config gia' letta in book_report (default 5.0 se assente, come fa book_execute.py:62). Oggi config = 5, quindi nessun cambio di comportamento.

TEST (nuovo tests/test_leva_tetto.py)

  • scala assente e frac 0.5 -> (1.0, ...).
  • book_scale_k 1.25 e frac 0.5 -> 1.25; frazione_cuscino con sl 0.30 -> 0.375. Monkeypatch di CFG_PATH/CONFIG verso un tmp_path: vedi come fanno i test esistenti su usde e _scala (grep "CFG_PATH|CONFIG" tests/).
  • scala fuori scaletta (es. 1.1) -> nessuna eccezione, valore LEVA_LORDA_MAX, come contiene "tetto di codice".
  • frac assente -> usa WEIGHT.
  • journal regola leva con scala 1.25 e lv 1.2 -> margine positivo.
  • A7: book_report passa min_order_usd dalla config. Se la configurazione del test e' troppo costosa, basta un test su build_book_order chiamato col valore di config; dichiaralo.
    Suite: cd /home/adriano/lavoro/PG-u7 && /opt/docker/PythagorasGoal/.venv/bin/python -m pytest -q -p no:cacheprovider. Baseline main: 1166 passed, 3 skipped.

NON TOCCARE: la logica di _scala e di book_net_target; cuscino_watch/usde_convert (leggono gia' frazione_cuscino); config/live.json.
COMMIT: "book: leva_tetto unica per cuscino USDE e giornale (#28)" e "book: min_order_usd da config nel report (#35 A7)".

ANALISI (passo 1, Opus, 27/09/2026) ANALISI U7 — gitea:Adriano/PythagorasGoal#28 (B5, A8, C16) + #35 A7 CARTELLA: /home/adriano/lavoro/PG-u7 RAMO: lavoro/goldrake-u7 (da main 841d4c7) PERCHE': il tetto di leva lorda e' letto con tre formule diverse (usde.frazione_cuscino, journal regola `leva`, journal movimenti_capitale). A scala 1,25 il cuscino USDC risulta 0,30E mentre servirebbe 0,375E. Serve una sola funzione in book. MODIFICHE 1. src/live/book.py — nuova funzione pubblica, accanto a `_scala`: def leva_tetto(cfg: dict | None = None) -> tuple[float, str]: Leva lorda massima che il libro puo' prendere = len(ASSETS) x min(WEIGHT, frac) x scala, al piu' LEVA_LORDA_MAX. - frac = cfg["max_notional_per_asset_frac"] se presente, altrimenti WEIGHT. Motivo: |book_net_target| <= scala*min(cap, WEIGHT*equity), perche' |raw| <= weight*equity*(W_TP01+W_SKH). - scala = _scala(real_equity=1.0, cfg=cfg): la scala DICHIARATA, gia' validata. - Su ScalaNonAutorizzata: ritorna (LEVA_LORDA_MAX, "config fuori scaletta/tetto (<msg breve>): tetto di codice LEVA_LORDA_MAX"). Non solleva: cuscino_watch non deve morire proprio quando la config e' sbagliata (verifica fable B5). - Se il risultato supera LEVA_LORDA_MAX viene troncato a LEVA_LORDA_MAX, e `come` lo dice. - `come` = testo leggibile, es. "2 asset x frac 0.5 x scala 1.0 = 1.00x". - cfg None -> legge CONFIG come fa `_scala`. 2. src/live/usde.py::frazione_cuscino — leva, come_leva = B.leva_tetto(cfg), poi return leva * sl, f"{come_leva} x SL {sl:.0%}". - Rimuovere N_ASSET = 2 solo se non ha altri usi (grep in src/ scripts/ tests/). Se ne ha, lasciarlo e dirlo nel rapporto. - Aggiornare il commento del blocco (righe ~101-109): la formula e' book.leva_tetto x sl. 3. src/live/journal.py::movimenti_capitale (~198-202) — leva_tetto = _book.leva_tetto(cfg)[0]. Resta il try/except con il fallback LEVA_LORDA_MAX sul config illeggibile. 4. src/live/journal.py regola `leva` (~576-580) — tetto, come = _book.leva_tetto(cfg). - Testo: f"Leva lorda **{lv:.2f}x** su un tetto di {tetto:.2f}x ({come}, da `config/live.json` e `book.leva_tetto`): margine {tetto - lv:.2f}x." - Il ramo except resta com'e'. 5. #35 A7 — src/live/book.py:318 `build_book_order(..., min_usd=5.0)`: leggere `min_order_usd` dalla config gia' letta in book_report (default 5.0 se assente, come fa book_execute.py:62). Oggi config = 5, quindi nessun cambio di comportamento. TEST (nuovo tests/test_leva_tetto.py) - scala assente e frac 0.5 -> (1.0, ...). - book_scale_k 1.25 e frac 0.5 -> 1.25; frazione_cuscino con sl 0.30 -> 0.375. Monkeypatch di CFG_PATH/CONFIG verso un tmp_path: vedi come fanno i test esistenti su usde e _scala (grep "CFG_PATH\|CONFIG" tests/). - scala fuori scaletta (es. 1.1) -> nessuna eccezione, valore LEVA_LORDA_MAX, `come` contiene "tetto di codice". - frac assente -> usa WEIGHT. - journal regola leva con scala 1.25 e lv 1.2 -> margine positivo. - A7: book_report passa min_order_usd dalla config. Se la configurazione del test e' troppo costosa, basta un test su build_book_order chiamato col valore di config; dichiaralo. Suite: `cd /home/adriano/lavoro/PG-u7 && /opt/docker/PythagorasGoal/.venv/bin/python -m pytest -q -p no:cacheprovider`. Baseline main: 1166 passed, 3 skipped. NON TOCCARE: la logica di _scala e di book_net_target; cuscino_watch/usde_convert (leggono gia' frazione_cuscino); config/live.json. COMMIT: "book: leva_tetto unica per cuscino USDE e giornale (#28)" e "book: min_order_usd da config nel report (#35 A7)".
Author
Owner

ESITO: fatto sul ramo, non in produzione
RIF: gitea:Adriano/PythagorasGoal#28
FILE: commit 52a905d su lavoro/goldrake (pushato, HEAD d34b0a8)
PROVA: /home/adriano/lavoro/PythagorasGoal pytest -q → 1220 passed, 3 skipped
MANCA: merge su main con ok di Adriano; poi issue chiudi --soluzione

ESITO: fatto sul ramo, non in produzione RIF: gitea:Adriano/PythagorasGoal#28 FILE: commit 52a905d su lavoro/goldrake (pushato, HEAD d34b0a8) PROVA: /home/adriano/lavoro/PythagorasGoal pytest -q → 1220 passed, 3 skipped MANCA: merge su main con ok di Adriano; poi issue chiudi --soluzione
Author
Owner

Soluzione

src/live/book.py leva_tetto(cfg) = len(ASSETS) x min(WEIGHT,frac) x scala (<= LEVA_LORDA_MAX; su ScalaNonAutorizzata torna il tetto di codice senza sollevare). Usata da usde.frazione_cuscino e dal giornale (movimenti_capitale e regola leva). Commit 52a905d.
Su main d34b0a8 dal 27/09/2026 17:31Z, produzione aggiornata. Test 1220 passed.

**Soluzione** src/live/book.py leva_tetto(cfg) = len(ASSETS) x min(WEIGHT,frac) x scala (<= LEVA_LORDA_MAX; su ScalaNonAutorizzata torna il tetto di codice senza sollevare). Usata da usde.frazione_cuscino e dal giornale (movimenti_capitale e regola leva). Commit 52a905d. Su main d34b0a8 dal 27/09/2026 17:31Z, produzione aggiornata. Test 1220 passed.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Adriano/PythagorasGoal#28