ec8478308f
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>
167 lines
9.9 KiB
Markdown
167 lines
9.9 KiB
Markdown
# 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.
|