book: leva_tetto unica per cuscino USDE e giornale (#28)

La leva lorda massima del libro era ridichiarata con tre formule diverse:
usde.frazione_cuscino (N_ASSET fisso, senza scala), la regola `leva` del
giornale (senza scala) e journal.movimenti_capitale (con scala). A scala
1,25 il cuscino USDC risultava 0,30 dell'equity invece di 0,375.

Nuova src/live/book.leva_tetto(cfg) = len(ASSETS) x min(WEIGHT, frac) x
scala, al piu' LEVA_LORDA_MAX; su ScalaNonAutorizzata non solleva, ritorna
il tetto di codice (cuscino_watch non deve morire sulla config sbagliata).
usde.frazione_cuscino e la regola `leva` del giornale ora la derivano;
movimenti_capitale idem, nel suo try/except esistente.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-27 15:17:01 +00:00
parent 841d4c78cd
commit 52a905d497
4 changed files with 182 additions and 20 deletions
+36
View File
@@ -136,6 +136,42 @@ def _scala(real_equity: float | None = None, eq_fallback: str | None = None,
return s
def leva_tetto(cfg: dict | None = None) -> tuple[float, str]:
"""Leva lorda massima che il libro PUO' prendere. UNA formula, non tre (#28): prima era
ridichiarata in `usde.frazione_cuscino` (N_ASSET fisso, senza scala) e nella regola `leva` del
giornale (senza scala) -> a scala 1,25 il cuscino USDC risultava 0,30 dell'equity invece di
0,375, e la regola `leva` mostrava un tetto diverso da quello che il libro rispetta davvero.
leva = len(ASSETS) x min(WEIGHT, frac) x scala, al piu' LEVA_LORDA_MAX. -> (leva, come).
- frac = cfg["max_notional_per_asset_frac"] se presente, altrimenti WEIGHT. Il min(WEIGHT, frac)
viene dal fatto che `book_net_target` non supera mai weight*equity*(W_TP01+W_SKH) =
weight*equity (i due pesi sommano a 1): |raw| <= weight*equity, quindi |net_target| <=
scala*min(cap, weight*equity) -- un frac oltre WEIGHT alza il cap nominale ma non la leva
REALE che il libro puo' esprimere.
- scala = la scala DICHIARATA (`_scala(real_equity=1.0, cfg=cfg)`, gia' validata): un tetto
descrive quanto la config AUTORIZZA, non quanto un giro con equity illeggibile ha preso.
- Su `ScalaNonAutorizzata` (config fuori scaletta o fuori tetto) NON si solleva: un
sorvegliante (`cuscino_watch`) non deve morire proprio quando la config e' sbagliata
(verifica fable B5) -> si ritorna il tetto di CODICE, dichiarando l'anomalia in `come`.
"""
if cfg is None:
cfg = json.loads(CONFIG.read_text()) if CONFIG.exists() else {}
try:
scala = _scala(real_equity=1.0, cfg=cfg)
except ScalaNonAutorizzata as e:
breve = str(e).splitlines()[0]
breve = breve if len(breve) <= 80 else breve[:77] + "..."
return LEVA_LORDA_MAX, (f"config fuori scaletta/tetto ({breve}): "
f"tetto di codice LEVA_LORDA_MAX")
raw_frac = cfg.get("max_notional_per_asset_frac")
frac = float(raw_frac) if raw_frac is not None else WEIGHT
frac_eff = min(WEIGHT, frac)
leva = len(ASSETS) * frac_eff * scala
base = f"{len(ASSETS)} asset x frac {frac_eff} x scala {scala} = {leva:.2f}x"
if leva > LEVA_LORDA_MAX + 1e-12:
return LEVA_LORDA_MAX, f"{base}, troncato al tetto di codice {LEVA_LORDA_MAX}x"
return leva, base
def _read_watermark() -> float | None:
"""Ultima equity REALE osservata sul conto. None se mai vista o file illeggibile."""