Chiude il debito che CLAUDE.md dichiarava da settimane: la regola "ogni cambio di scala passa dal cap di config, non da target_vol" NON era implementabile perche' una chiave di scala non esisteva — qualunque cambio sarebbe finito su WEIGHT/W_TP01/W_SKH, cioe' 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). Specifica gia' scritta in docs/research/SPEC-scale-key.md (618 righe, 9 condizioni di gate, prototipo). Non ho progettato: ho eseguito i punti 1-4. 🚨 config/live.json NON E' STATO TOCCATO. La chiave e' assente, vale 1,00, e T7 dimostra bit-exact che il libro e' quello di ieri (max|diff| = 0.0). Verificato anche a runtime: book_execute in dry-run da' gli stessi target del cron delle 15:47 (BTC $+355, ETH $+214). LE QUATTRO DECISIONI CHE NON SONO DI COMODO - La scala si applica DOPO il clamp. Prima, il cap se la mangerebbe proprio nei giorni di massima convinzione (a tp=1/sg=+1 il grezzo vale esattamente cap => k_eff tornerebbe a 1,00 a ogni k): sarebbe un cambio di FORMA travestito da cambio di taglia, e la curva g(k) con cui il gradino viene autorizzato non descriverebbe quel libro. Prezzo dichiarato: il cap diventa il tetto del libro UNITARIO, e la guardia sulla leva lorda va ricostruita. - Il tetto e' sul PRODOTTO e sta nel CODICE. Sulla sola chiave lascerebbe aperta la porta accanto (frac 0,625 x scala 1,25 = 1,562x); in config sarebbe un lucchetto con la chiave attaccata. LEVA_LORDA_MAX 1,25 in src/live/book.py => il gradino a 1,50 richiede codice, quindi review. - Fuori scaletta o fuori tetto = STOP, non clamp. book_execute si ferma, non invia, allerta (ScalaNonAutorizzata). E SCALA_LADDER (1,00 · 1,25) rende INESPRIMIBILE "solo un po'": 1,05 non e' prudente, e' fuori scaletta. - La scala vive solo sul percorso fidato (equity illeggibile => 1,00), cosi' "il fallback non e' piu' permissivo" e' vero per costruzione. Ma la VALIDAZIONE avviene sempre: una config rotta non si nasconde dietro un giro in cui l'equity non era leggibile. LA GUARDIA CHE MORDE PER PRIMA non e' il peggior giorno (k <= 3,49x) ma il COSTO di un disaster-SL (k <= 1,67x, 2,1x piu' stringente): l'invariante n_asset x frac x scala x disaster_sl_pct <= 0,50 scatta anche se qualcuno allarga lo stop invece di alzare la scala. TEST T1-T11 (tests/test_book_scale.py, 18 verdi). Il piu' importante e' T1b: a k=1 l'implementazione simmetrica e quella asimmetrica danno lo STESSO numero, quindi un test di simmetria scritto sul caso di default ha potenza ZERO. T1b verifica che le due coincidano a k=1 (il rischio e' reale) e che fuori da k=1 l'asserzione le SEPARI, con un'implementazione asimmetrica scritta nel test apposta perche' fallisca. SORVEGLIANTE scale_watch (cron_daily, 3 domande / 3 azioni / 3 stati, una allerta per streak, marcatore scritto solo dopo invio riuscito — debito #2). Riporta la frequenza del ramo di fallback, che sopra il 2% in 90 giorni invaliderebbe la regola: misurata 0/1.676, coi 19 giri "paper capital" (pre-finanziamento, dove il libro non invia) contati e dichiarati a parte. Non puo' impedire la modifica: la rende visibile entro 24h e attribuibile. CHIUDE il debito #5 di §5: T2/T3 sostituiscono il vecchio test_leva_massima_da_config (che misurava frac x n_asset mentre la grandezza vera e' frac x n_asset x scala), e T11 verifica che sia rimasto cancellato. NON FATTO, deliberato: la chiave in config (punto 2 lo vieta), GATE SCALA-01 (A2 richiede >=30 giorni a 1,00 col sorvegliante attivo — "l'unico modo di scoprire che il sorvegliante e' rotto mentre la leva e' ancora 1,00"), r0726_fee_sensitivity rifatto (A7: serve solo al gradino; a 1,25x una liquidazione costerebbe 1,25% non 1,00%, e ereditarlo sarebbe l'errore). Nessun ordine. Suite: 825 passati. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
9.9 KiB
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.jsonnon 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.