# 2026-09-01 — La chiave di scala esiste, ed è inerte *Scritto il 2026-09-01. Implementazione dei punti 1-4 di `docs/research/SPEC-scale-key.md` §8.* **Nessun ordine. `config/live.json` NON è stato toccato: il libro che gira è bit-exact quello di ieri.** ## 0. Da dove nasce Il progetto aveva una regola — *«ogni cambio di scala passa dal cap di config, non da `target_vol`»* — e CLAUDE.md la accompagnava da settimane con questa nota: > ⚠️ **NON È IMPLEMENTABILE COME SCRITTA:** in `config/live.json` non esiste una chiave di scala. Era un debito preciso: la regola prescriveva un percorso che non esisteva, quindi qualunque cambio di scala sarebbe finito su `WEIGHT`/`W_TP01`/`W_SKH` in `src/live/book.py` — cioè codice su un percorso con soldi veri, e per giunta nel posto sbagliato (`W_TP01`/`W_SKH` sono il **rapporto** 75/25, non la taglia: moltiplicarli romperebbe la parità coi pesi del backtest). La specifica era già scritta, 618 righe, con nove condizioni di gate e il prototipo che ne dimostra la meccanica. **Non ho progettato niente: ho eseguito.** ## 1. Il rischio che l'implementazione doveva rendere impossibile La §0 della specifica lo dice meglio di come lo direi io: > *Una chiave di scala è esattamente il tipo di parametro che si alza «solo un po'» dopo un mese > buono. È un numero, sta in un file di config, non richiede di capire niente per cambiarlo, e il > suo effetto è immediato e piacevole.* E il motivo per cui il progetto non aveva già una guardia non è dimenticanza, è **aritmetica**: **lo Sharpe è invariante alla scala.** `deflated_sharpe` e `marginal_vs_tp01` leggono un numero che a k=1,00 e a k=2,00 è identico. Non falliscono: **non vedono**. ## 2. Cosa ho costruito | pezzo | dove | cosa fa | |---|---|---| | `book_scale_k` | `config/live.json` — **assente** | la chiave. Assente = 1,00 = libro di sempre | | `_scala()` | `src/live/book.py` | legge, **valida sempre**, applica solo sul percorso fidato | | `book_net_target(..., scala=)` | `src/live/book.py` | applica la scala **DOPO il clamp** | | `LEVA_LORDA_MAX` 1,25 · `SCALA_LADDER` (1,00 · 1,25) · `DISASTER_SL_BUDGET` 0,50 | `src/live/book.py` | **costanti di CODICE** | | `ScalaNonAutorizzata` | `src/live/book.py` | eccezione, **non** un clamp | | riga «scala libro» + stop-e-allerta | `scripts/live/book_execute.py` | la scala è visibile a ogni giro | | `scale_watch` | `src/live/` + `scripts/live/` + `cron_daily.sh` | 3 domande, 3 azioni, 3 stati | | `scale_history.jsonl` | `data/live/` | giornale append-only (già nel backup) | | T1-T11 | `tests/test_book_scale.py` | **18 test, tutti verdi** | ### 2.1 Le quattro decisioni che non sono di comodo **(a) La scala si applica DOPO il clamp.** Applicandola prima, il cap se la mangerebbe *proprio nei giorni di massima convinzione*: a `tp=1, sg=+1` il grezzo vale esattamente `cap`, quindi `k_eff` tornerebbe a 1,00 a ogni k. Il risultato non sarebbe «il libro a leva k» ma un libro che alza i giorni piccoli e lascia fermi i grandi — un cambio di **forma** travestito da cambio di taglia. E allora la curva `g(k)` con cui il gradino viene autorizzato **non descriverebbe quel libro**. *Autorizzare con una curva e implementarne un'altra è il modo più silenzioso di sbagliare.* Il prezzo, dichiarato: il cap smette di essere il tetto assoluto del nozionale e diventa il tetto del libro **unitario**. La guardia sulla leva lorda è ricostruita esplicitamente altrove. **(b) Il tetto è sul PRODOTTO, e sta nel CODICE.** Un tetto sulla sola chiave lascia aperta la porta accanto: `frac` 0,625 × scala 1,25 = **1,562x**, che passerebbe. E un tetto in config sarebbe modificabile dalla stessa mano, nello stesso file, nello stesso momento — *un lucchetto con la chiave attaccata*. Con `LEVA_LORDA_MAX` in `src/live/book.py`, il gradino 1,25 → 1,50 richiede una modifica di codice, quindi una review. **Il tetto è basso apposta: è il meccanismo del cricchetto.** **(c) Fuori scaletta o fuori tetto = STOP, non clamp.** `book_execute` si ferma, non invia, allerta. Tagliare silenziosamente farebbe girare una config che *dichiara* un numero e un libro che ne *esegue* un altro. E `SCALA_LADDER` esiste perché **una scala a gradini rende inesprimibile «solo un po'»**: 1,05 non è un valore prudente, è un valore fuori scaletta, e viene rifiutato. **(d) La scala vive solo sul percorso fidato.** Equity reale illeggibile ⇒ scala 1,00. *Una scala è una decisione di rischio presa conoscendo il conto; quando non si sa quanto vale il conto, non si prende.* Così «il fallback non è più permissivo» è vero **per costruzione**, non per aritmetica. ⚠️ Ma la **validazione** avviene comunque, anche sul percorso degradato: una config rotta è un errore di configurazione, non una condizione di mercato, e non deve nascondersi dietro un giro in cui l'equity non era leggibile. ### 2.2 La guardia che morde per prima, e non è quella che sembra Il disaster-SL è un movimento di **prezzo** (−30% sul mark), ma il suo impatto in equity è quel movimento **moltiplicato per il lordo** — quindi cresce con k anche se `disaster_sl_pct` non cambia: - dal peggior giorno possibile: `k ≤ 3,49x` - **dal costo di un disaster-SL: `k ≤ 1,67x`** ← **2,1× più stringente** Da qui l'invariante `n_asset · frac · scala · disaster_sl_pct ≤ 0,50`, che scatta anche se qualcuno allarga lo stop invece di alzare la scala. ## 3. Il test che conta più degli altri **T1b**, e la ragione per cui la specifica gli dedica un paragrafo: **a k=1 l'implementazione simmetrica e quella asimmetrica danno lo stesso identico numero.** Un test di simmetria scritto sul caso di default passa sempre e non controlla niente — potenza **zero**, la stessa firma dei test di `book_live` scoperti senza potenza a libro flat. T1b quindi verifica due cose: *(a)* che a k=1 le due implementazioni siano davvero indistinguibili (cioè che il rischio sia reale), e *(b)* che fuori da k=1 l'asserzione di T1 le **separi**. Nel test c'è scritta un'implementazione asimmetrica apposta perché fallisca. Gli altri: T2/T3 i due tetti letti da config coi bersagli **importati** dalla produzione; T4 il fallback mai più permessivo in ogni stato degradato; T5 il cap che morde ancora; T6 lo stop non clampato (più T6b sul prodotto e T6c sul disaster-SL); **T7 l'inerzia bit-exact**; T8 il report che la dichiara; T9 i bersagli derivati e non ridichiarati; T10 il **controllo positivo del sorvegliante** (con una config non autorizzata deve parlare, con config e giornale concordi deve tacere) più T10b sulla disciplina degli allarmi e T10c sui tre stati; T11 che il vecchio test sia stato **sostituito, non rilassato**. ## 4. Il sorvegliante, e cosa non può fare `scale_watch` gira in `cron_daily` e risponde a tre domande che hanno **tre azioni diverse**: | domanda | azione se 🚨 | |---|---| | la config dichiara una scala che il **giornale** non ha mai autorizzato? | ricostruire come ci si è arrivati, scendere a k0 | | il criterio che autorizzò la scala **corrente** passa ancora oggi? | scendere di un gradino | | il **prodotto** `frac · scala · n_asset` ha superato il tetto? | bloccare l'esecuzione | Tre stati (`OK` / `ALLARME` / **`NON MISURABILE`**), una allerta per streak, e il marcatore «già detto» scritto **solo dopo un invio riuscito** (il debito #2 del 28/08: un 🚨 perso prima era perso per l'episodio intero). Riporta anche la **frequenza del ramo di fallback**, che se superasse il 2% in 90 giorni invaliderebbe la regola (d): il libro girerebbe a una leva **mista** e `g(k)` smetterebbe di descriverlo. Misurata oggi: **0 giri su 1.676**. ⚠️ I 19 giri senza equity reale sono tutti «paper capital», cioè **pre-finanziamento**, dove il libro non invia affatto — il sorvegliante li conta e li dichiara separatamente invece di gonfiare la quota. 🚨 **Ciò che il sorvegliante NON può fare: impedire la modifica.** Chi ha accesso al file può scriverci dentro. Rende la modifica visibile entro 24 ore e attribuibile. È la stessa onestà di `venue_watch`: non protegge il saldo, **compra tempo**. ## 5. Verifica che il libro non è cambiato ``` sizing base : $2,052.22 | cap/asset $1026 | min $5 | disaster-SL -30% scala libro : 1.00x (tetto 1.25x) | leva lorda max 1.000x dell'equity [chiave assente o 1,00 = libro invariato] BTC TP +0.461 · SKH +0(flat) -> net $+355 | pos $+357 -> HOLD (a target) ETH TP +0.278 · SKH +0(flat) -> net $+214 | pos $+212 -> HOLD (a target) ``` Gli stessi target del cron delle 15:47. **Suite: 825 passati** (807 + 18 nuovi). ## 6. Cosa NON ho fatto, ed è deliberato **Non ho scritto la chiave in config.** Il punto 2 della checklist lo prescrive esplicitamente, e T7 dimostra bit-exact che senza la chiave il libro è quello di prima. **Non ho eseguito GATE SCALA-01**, e non era possibile: A2 richiede che k0 sia in produzione con il criterio passato ogni giorno, e il punto 5 impone **≥30 giorni a 1,00 col sorvegliante attivo prima** di eseguire il gate. Cito la specifica perché la frase è il motivo: > *Il punto 5 non è burocrazia: è l'unico modo di scoprire che il sorvegliante è rotto mentre la > leva è ancora 1,00.* **Non ho rifatto `r0726_fee_sensitivity`** (A7). Serve solo *se* e *quando* il gradino viene chiesto: la sua conclusione «liquidation fee 1% irrilevante» era condizionata a lordo ≤1x, e a 1,25x una liquidazione costerebbe **1,25%** dell'equity. Non è un problema, è un numero da ricalcolare — e ereditarlo sarebbe l'errore. 📌 **A6 (anti-recency) oggi non morde**: `k_ammesso ≈ 3,5x` contro un gradino di 1,25x, e il vincolo che morde è il disaster-SL. È scritto adesso **perché adesso non è comodo per nessuno** — che è l'unico momento in cui una regola del genere si può scrivere onestamente. ## 7. Stato del gate `GATE SCALA-01` è ora in CLAUDE.md §4 con data **non prima del 2026-10-01**. A8 e A9 sono fatti; A2 matura col tempo; A1/A4/A5 sono aritmetica già cablata; A3/A6/A7 si eseguono il giorno del gate, sui dati di quel giorno, non su questi.