Files
PythagorasGoal/docs/research/SPEC-scale-key.md
T
Adriano Dal Pastro ec8478308f GATE SCALA-01: la chiave di scala esiste, ed e' INERTE (SPEC §8 punti 1-4)
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>
2026-09-01 17:44:02 +00:00

629 lines
38 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SPEC — la CHIAVE DI SCALA del libro live (`book_scale_k`)
**Filone SCALE-SPEC, ondata 2026-08-22, branch `research/wave-0822`.**
> ✅ **IMPLEMENTATA IL 2026-09-01 — punti 1-4 della checklist §8.** `src/live/book.py`
> (`book_scale_k`, `_scala`, `LEVA_LORDA_MAX`, `SCALA_LADDER`, `DISASTER_SL_BUDGET`,
> `ScalaNonAutorizzata`, `scala` nel report), `scripts/live/book_execute.py` (riga «scala libro» +
> stop-e-allerta), `src/live/scale_watch.py` + `scripts/live/scale_watch.py` in `cron_daily.sh`,
> `data/live/scale_history.jsonl`, `tests/test_book_scale.py` (T1-T11, **18 test verdi**).
> 🚨 **`config/live.json` NON e' stato toccato**, come prescrive il punto 2: la chiave e' assente,
> vale 1,00, e T7 dimostra bit-exact che il libro e' quello di sempre (`max|diff| = 0.0`).
> **Restano i punti 5-7:** ≥30 giorni a 1,00 col sorvegliante attivo, poi `GATE SCALA-01`, poi
> `r0726_fee_sensitivity` rifatto. Il punto 5 non e' burocrazia — e' l'unico modo di scoprire che il
> sorvegliante e' rotto mentre la leva e' ancora 1,00.
**Questo documento era una SPECIFICA scritta prima dell'implementazione.** Il prototipo che dimostra la meccanica e'
`scripts/research/r0822e_scale_proto.py` (isolato, non importato da niente, non scrive nulla; la
sua sezione (0) verifica entrambe le cose a runtime invece di dichiararle).
Nasce da `RESULTS-0822.md` §33 (WORST-DAY), che ha misurato che il gradino di leva e' l'unica leva
grande rimasta e che **C6 (processo) FALLISCE**: la regola del progetto — *«ogni cambio di SCALA
passa dal cap di config, non da `target_vol`»* — **non e' implementabile come scritta**, perche' il
cap e' un CLAMP e non un moltiplicatore.
---
## 0. IL RISCHIO CHE QUESTA SPECIFICA DEVE RENDERE IMPOSSIBILE
> **Una chiave di scala e' esattamente il tipo di parametro che si alza «solo un po'» dopo un mese
> buono.** E' un numero, sta in un file di config, non richiede di capire niente per cambiarlo, e il
> suo effetto e' immediato e piacevole.
Il progetto ha regole contro questa forma di deriva su ogni altro asse — `weights_tilt_null` per i
pesi, gate pre-registrati per i candidati, `select_cell_insample` per le griglie, `anchor_luck_band`
per le ancore — e **nessuna per la scala**. Non per dimenticanza: **per aritmetica.**
Lo **Sharpe e' invariante alla scala**. Misurato nel prototipo (sez. 7a), sulla serie vera del libro:
| k | Sharpe | drift | vol | maxDD | g(k) esatto |
|---|---|---|---|---|---|
| 1,00 | **1,5051** | 17,11% | 11,37% | 12,17% | 17,90% |
| 1,25 | **1,5051** | 21,39% | 14,21% | 15,01% | 22,61% |
| 2,00 | **1,5051** | 34,22% | 22,74% | 23,10% | 37,24% |
`deflated_sharpe` e `marginal_vs_tp01` leggono la prima colonna. **Non falliscono: non vedono la
variabile.** Un raddoppio della leva passa ogni gate esistente del progetto senza far scattare nulla,
e produce lo stesso identico numero di sintesi che il progetto usa per giudicare tutto il resto.
Da qui discendono i tre vincoli non negoziabili del disegno:
1. **il tetto sta nel CODICE, non in config** (una config puo' essere modificata da chiunque, in
qualunque momento, senza review; una costante di codice no);
2. **il tetto sta sul PRODOTTO, non sulla chiave** (`frac` e `scala` moltiplicano: un tetto sulla
sola chiave lascia aperta la porta accanto — vedi §4.2, tabella);
3. **il gate e' uno SCRIPT che stampa PASSA/FALLISCE, non un giudizio** (§6), e c'e' un
**sorvegliante giornaliero** che se ne accorge *dopo il fatto* se qualcuno lo salta (§6.4).
⚠️ **Correzione dovuta al mandato di questo filone.** Il gradino non vale quello che dice la frase
che circola. `RESULTS-0822.md` §2 misura **1,00x → 1,25x = 14,7a → 12,9a**, e **1,00x → 1,50x =
14,7a → 11,6a**; il «~€300/mese di versamenti equivalenti» appartiene al gradino **1,50x**.
Ricalcolato sulla tabella netta pubblicata (`r0807_piano_netto`: €500/m → 14,7a, €800/m → 11,4a,
interpolazione lineare **dichiarata**):
| gradino | anni al capitale-rendita | versamento mensile equivalente | delta |
|---|---|---|---|
| 1,00x (oggi) | 14,7a | €500/m | — |
| **1,25x** | **12,9a** | ~€664/m | **+€164/m** |
| 1,50x | 11,6a | ~€782/m | +€282/m |
L'interpolazione riproduce il «~€300/mese» pubblicato per 1,50x (€282) → è un controllo, non solo
un conto. **Questa specifica autorizza il gradino a 1,25x e vale ~€165/mese, non ~€300.**
---
## 1. COSA SPECIFICA, E COSA NON FA
**Specifica:** dove va la chiave, in che ordine si applica rispetto al cap, quali guardie servono,
quali test devono esistere **prima** della modifica, quale gate deve essere superato per alzarla e
con quale cadenza si sorveglia.
**NON fa:** non autorizza il gradino, non implementa niente, non tocca `config/live.json`. Con la
chiave assente o `= 1.0` il libro e' **bit-exact quello di oggi** (§5, test T7): la modifica e'
inerte finche' qualcuno non scrive un numero, e quel numero passa dal gate del §6.
---
## 2. DOVE VA LA CHIAVE, ESATTAMENTE
### 2.1 La catena, letta dal sorgente vero
Il prototipo la stampa leggendo i file (sez. 2), cosi' le righe qui sotto sono verificabili e non
citate a memoria:
| stadio | file:riga | cosa produce |
|---|---|---|
| segnale TP01 | `src/live/shadow.py:119` `targets = {a: tp.current_target(dfs[a])...}` | una **frazione** ≥0, long-flat |
| segnale SKH01 | `src/live/book.py:217` `skh = _skyhook_positions(...)``:228` `sign = _skh_sign(st)` | un **segno** +1/1/0 |
| cap per-asset | `src/live/book.py:107` `def _cap(...)``:194` `cap = _cap(...)` | il **clamp**, legato al watermark |
| **TARGET NETTO** | **`src/live/book.py:138` `def book_net_target(...)`** → **`:229` `net = book_net_target(tp_frac, sign, equity, cap)`** | **← QUI VA LA SCALA** |
| ordine | `src/live/book.py:153` `def build_book_order(...)``:232` | un **delta** verso il target |
| invio | `scripts/live/book_execute.py:208` `trader.rebalance_signed(inst, net, mark, ...)` | il fill |
### 2.2 La firma proposta
```python
# src/live/book.py
def book_net_target(tp_frac: float, skh_sign: int, equity: float, cap: float,
weight: float = WEIGHT, scala: float = 1.0) -> float:
raw = weight * equity * (W_TP01 * max(tp_frac, 0.0) + W_SKH * float(skh_sign))
return scala * max(-cap, min(cap, raw)) # <-- la scala e' FUORI dal clamp: vedi §3
```
più, accanto a `_cap()`, un lettore dedicato:
```python
LEVA_LORDA_MAX = 1.25 # COSTANTE DI CODICE, non di config. Vedi §4.1.
SCALA_LADDER = (1.00, 1.25) # i soli valori esprimibili. Vedi §4.1 e §6.
def _scala(real_equity, eq_fallback) -> float:
"""Scala del libro. 1.0 (= il libro di oggi) in ogni stato degradato: vedi §4.3."""
if real_equity is None or eq_fallback:
return 1.0
... # legge config, valida contro LEVA_LORDA_MAX e SCALA_LADDER, ALZA se fuori (§4.1)
```
e in `book_report`, una riga sotto `cap = _cap(...)`:
```python
scala = _scala(sh.get("real_equity"), sh.get("eq_fallback"))
...
net = book_net_target(tp_frac, sign, equity, cap, scala=scala)
```
`book_report` deve **esporre `scala` nel dict di ritorno** accanto a `cap_per_asset`, e
`book_execute` deve **stamparla nella riga «sizing base»**: un parametro che agisce sul nozionale e
non compare nel report e' la forma esatta del difetto che questo progetto ha gia' pagato tre volte
(docstring che dichiarano una causalita' che il codice non ha).
### 2.3 Perche' **li'** e non altrove — e la dimostrazione
`book_net_target` e' **l'unico punto della catena in cui le due gambe si incontrano con una TAGLIA**:
sopra di lei TP01 da' una frazione e SKH01 da' **solo un segno**; sotto di lei si calcolano delta e
ordini. Percio' e' l'unico punto in cui un moltiplicatore e' un puro cambio di scala.
Il prototipo (sez. 3) lo misura sulla funzione di produzione stessa — la copia e' **bit-exact**
(`max|copia produzione| = 0.00e+00` su 480 combinazioni):
| k | solo TP01 (tp=1, sg=0) | solo SKH01 (tp=0, sg=1) | rapporto TP/SKH | peso implicito TP01 |
|---|---|---|---|---|
| 1,00 | 2.267,62 | 755,88 | **3,000000** | **0,750000** |
| 1,25 | 2.834,53 | 944,84 | **3,000000** | **0,750000** |
| 2,00 | 4.535,25 | 1.511,75 | **3,000000** | **0,750000** |
`max|target(k) k·target(1)| = 0.00e+00` su 90 combinazioni. **Il mix 75/25 e' invariante a ogni k:
e' una scala, non un tilt.**
Le alternative, e perche' cadono:
| dove | perche' NO |
|---|---|
| `TrendPortfolio.target_vol` | scala il **75%** del libro e lascia fermo il 25% → e' anche un **tilt di pesi**, e **fallisce `weights_tilt_null`**. Misurato nel prototipo come **controllo positivo**: a k=1,25 il peso implicito di TP01 passa da 0,750 a **0,789**. |
| `_skyhook_positions` | non c'e' niente da scalare: produce un **segno**. |
| `WEIGHT` (=0,5) | e' lo **split BTC/ETH**, ed e' **condiviso** con `shadow_report`/`build_rebalance_order` (`shadow.py:144`) e con la dashboard, cioe' con il percorso **TP01-only**: cambiarlo cambierebbe anche un altro libro. E confonderebbe in un solo numero «quanti asset» e «quanta leva». |
| `W_TP01` / `W_SKH` | sono il **rapporto** 75/25 e sono legati per test ai pesi del backtest (`test_weights_match_backtest_sleeves` asserisce `W_TP01+W_SKH == 1.0` e la parita' con `deribit_book_sleeves`). Moltiplicarli entrambi romperebbe quella parita' e renderebbe impossibile distinguere scala e mix. |
| `build_book_order` | scalerebbe il **delta**, non il target: cambia la **velocita' di convergenza**, non la taglia, e il reconciler litigherebbe con se' stesso a ogni giro. |
| dentro `DeribitTrader` | il report, il dry-run e `book_executions.jsonl` stamperebbero un target **diverso** da quello eseguito. |
---
## 3. L'INTERAZIONE COL CAP — il punto sottile
### 3.1 Il fatto geometrico
Il cap e' un **clamp**: `net = clamp(0.5·E·(0,75·tp + 0,25·sg), ±cap)` con `cap = frac·E = 0,50·E`.
A piena convinzione (`tp=1, sg=+1`) il grezzo vale **esattamente** `0,5·E·1,0 = 0,50·E = cap`:
**il clamp e' tangente al libro proprio nei giorni di massima esposizione.**
### 3.2 I due ordini, misurati (prototipo sez. 4, su 5.648 giorni-asset veri)
| k | ordine | clampati | quota | k_eff medio | **k_eff a piena convinzione** | lordo max |
|---|---|---|---|---|---|---|
| 1,25 | **PRIMA** del clamp | 32 | 0,57% | 1,2487 | **1,0000** | 1,000 |
| 1,25 | **DOPO** il clamp | 0 | 0,00% | **1,2500** | **1,2500** | 1,250 |
| 2,00 | PRIMA | 793 | 14,04% | 1,9235 | **1,0000** | 1,000 |
| 2,00 | DOPO | 0 | 0,00% | **2,0000** | **2,0000** | 2,000 |
**L'ordine corretto e' DOPO il clamp**, e la ragione non e' di comodo:
- **PRIMA**: il clamp mangia la scala **proprio nei giorni di massima convinzione** (k_eff = 1,00 a
piena convinzione, a ogni k). Il risultato non e' «il libro a leva k»: e' un libro che alza i
giorni piccoli e lascia fermi i grandi, cioe' un **appiattimento del profilo di convinzione**. E'
un cambio di **FORMA** travestito da cambio di taglia — e allora **la curva g(k) di
`r0822_growth_policy` / WORST-DAY non descrive quel libro**, perche' quella curva e' calcolata su
`k·r`, cioe' sull'ordine DOPO. Autorizzare il gradino con una curva e implementarne un'altra e'
il modo piu' silenzioso di sbagliare che questa specifica possa produrre.
- **DOPO**: `k_eff = k` esattamente, in ogni giorno e in ogni stato del segnale. E' l'unico ordine
per cui «leva k» significa la stessa cosa nel codice e nel backtest.
### 3.3 Cosa implica per il SIGNIFICATO del cap — e va detto, perche' e' il prezzo
Con l'ordine DOPO, **il cap smette di essere il tetto assoluto del nozionale**. Diventa il tetto
del **libro unitario**: limita la *forma* (nessun asset oltre `frac·E` di esposizione unitaria),
mentre la *taglia* la decide la scala. Il lordo massimo passa da `n_asset·frac` a
**`n_asset·frac·scala`** = 1,250x l'equity a k=1,25.
E il cap **non smette di fare le altre due cose che fa oggi**, che sono quelle che contano davvero:
1. resta **proporzionale all'equity** (`cap = equity·frac` sul percorso fidato) → un deposito non
strozza il libro e un prelievo lo rimpicciolisce da solo;
2. resta **legato al watermark** sul percorso di fallback (`min(fisso, watermark·frac)`) → il caso
che il 26/07 valeva «3,35x di leva su un conto da $597» resta chiuso (§4.3).
⚠️ **Cio' che il cap NON puo' piu' fare da solo e' essere la guardia di sicurezza sulla leva
lorda.** Quella guardia va ricostruita esplicitamente, ed e' il §4.
### 3.4 Il test che si rompe — e **deve** rompersi
`tests/test_fee_sensitivity.py::test_leva_massima_da_config_resta_sotto_o_uguale_a_1x`:
```python
leva_max = frac * len(FS.ASSETS)
assert leva_max <= 1.0, ("leva massima ...: la liquidation fee 1% torna rilevante, "
"rivedere r0726_fee_sensitivity e il diario 2026-07-26-fee-deribit.md")
```
**Si rompe? No — e questo e' il punto.** Con `frac` invariato a 0,50 il test continua a **passare**,
perche' misura `frac · n_asset` mentre la grandezza vera diventa `frac · n_asset · scala`.
**Non si rompe, e smette di controllare cio' che era stato scritto per controllare.**
E' esattamente la forma di difetto che il progetto ha catturato **tre volte in un giorno** il
2026-08-21: *un controllo puntato su una configurazione diversa da quella che gira passa sempre, e
non sta controllando niente* (`fee_watch` sugli inverse, `venue_watch` con bitfinex, i test di
`book_live` a libro flat).
**Quindi va sostituito, non aggiustato**, e la sostituzione e' una coppia:
- **T2** (§5) — `test_la_leva_lorda_da_config_resta_sotto_il_tetto`: asserisce
`len(ASSETS) * frac * scala <= LEVA_LORDA_MAX`, cioe' **il prodotto**, e importa la scala **dallo
stesso posto da cui la importa la produzione** (regola del 21/08: *un sorvegliante deve DERIVARE
il proprio bersaglio dal codice sorvegliato, mai ridichiararlo*).
- **prerequisito del gate** (§6): `r0726_fee_sensitivity` va **rifatto** al nuovo lordo, perche' la
sua conclusione *«liquidation fee 1% irrilevante»* era esplicitamente condizionata a nozionale
lordo ≤1x. Al gradino il lordo e' 1,25x → una liquidazione costerebbe **1,25%** dell'equity, non
1,00%. Non e' un problema; e' un numero che va **ricalcolato e ripubblicato**, non ereditato.
Il vecchio test va **cancellato con una nota** che dice perche' (e rimandi a questo documento), non
modificato in silenzio: un test di decisione che cambia soglia senza lasciare traccia e' un test di
decisione perso.
---
## 4. LE GUARDIE
### 4.1 G1 — il tetto: **sul PRODOTTO, in CODICE, e non silenzioso**
```python
LEVA_LORDA_MAX = 1.25 # src/live/book.py — costante di CODICE
SCALA_LADDER = (1.00, 1.25)
```
Tre proprieta', tutte necessarie:
**(a) Sul prodotto.** `frac` e `scala` **moltiplicano**. Un tetto sulla sola chiave lascia aperta la
porta accanto (prototipo sez. 6):
| frac | scala | leva lorda | tetto «scala ≤ 1,25» | **tetto sul PRODOTTO** |
|---|---|---|---|---|
| 0,500 | 1,25 | 1,250 | passa | passa |
| **0,625** | **1,25** | **1,562** | **passa** | **BLOCCA** |
| **0,750** | **1,25** | **1,875** | **passa** | **BLOCCA** |
| 0,500 | 2,00 | 2,000 | BLOCCA | BLOCCA |
Ed e' un rischio **concreto, non teorico**: WORST-DAY ha appena pubblicato che «alzare `frac` da
0,50 a 0,625 non produce k=1,25», il che rende `frac` un candidato naturale al prossimo ritocco —
salvo che, **combinato con la scala, lo produce eccome**.
**(b) In codice.** Il tetto di config sarebbe modificabile dalla stessa mano, nello stesso file, nel
momento in cui si modifica la chiave: sarebbe 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, quindi un gate. **Il tetto e' basso apposta: e' il meccanismo del cricchetto, non
un'opinione sulla leva massima sopportabile** (che dalla §4.5 sarebbe 1,67x).
**(c) Non silenzioso.** Una scala fuori tetto (o fuori `SCALA_LADDER`) **NON viene tagliata al
tetto**: `book_execute` **si ferma, non invia ordini, e allerta**. Un clamp silenzioso farebbe
girare una config che dichiara un numero e un libro che ne esegue un altro — la stessa classe di
difetto del §3.4. `SCALA_LADDER` esiste perche' **una scala a gradini rende inesprimibile «solo un
po'»**: 1,05 non e' un valore prudente, e' un valore fuori scaletta, e viene rifiutato.
### 4.2 G2 — l'invariante che lega TRE chiavi
```python
assert len(ASSETS) * frac * scala * disaster_sl_pct <= 0.50
```
Vedi §4.5: e' la guardia che morde per prima, e cattura chi allarga il disaster-SL.
### 4.3 G3/G4 — il watermark e l'equity non leggibile
**Regola proposta: la scala vive SOLO sul percorso fidato.**
```python
scala_effettiva = scala se l'equity REALE e' leggibile, altrimenti 1.00
```
Motivo: **una scala e' una decisione di rischio presa conoscendo il conto; quando non si sa quanto
vale il conto, non si prende.** Cosi' «il fallback non e' piu' permissivo» e' vero **per
costruzione**, non per aritmetica — e non dipende dal fatto che il conto degli invarianti torni.
Misurato (prototipo sez. 6-bis; watermark reale letto da disco, $634,996; copia di `_cap`
verificata bit-exact contro la produzione):
| stato | cap | scala eff. | lordo max | **/ equity vera** | tetto |
|---|---|---|---|---|---|
| normale (equity reale letta) | 317,50 | **1,25** | 793,74 | **1,250x** | si |
| online ma equity illeggibile | 317,50 | **1,00** | 635,00 | **1,000x** | si |
| watermark assente (1° avvio) | 300,00 (`CAP_UNKNOWN_USD`) | **1,00** | 600,00 | 0,945x | si |
| watermark vecchio e basso | 50,00 | **1,00** | 100,00 | 0,157x | si |
| dopo un deposito a $6.047 | 3.023,50 | **1,25** | 7.558,75 | 1,250x | si |
**In nessuno stato degradato la leva supera quella dello stato normale**, e il caso «watermark
assente» ha **due protezioni indipendenti** (cap $300 **e** scala 1,00), non una sola.
⚠️ **Prezzo dichiarato.** Nei giri in cui l'equity non e' leggibile il libro gira a k=1 e al giro
dopo torna a k → un ribilancio in piu' per oscillazione, di `(k1)·frac·E` per asset = **~$79 a
testa** sul conto di oggi, cioe' **~$0,056** di fee a 3,5 bps. **Frequenza misurata** su
`logs/cron_book.log`: **0 giri su 1.442 (0,00%)**; i 21 giri «conto non leggibile» sono OFFLINE, e
li' il libro non manda ordini affatto, quindi la scala non si pone. → **il prezzo esiste, e' noto,
ed e' nullo sul campione osservato.**
⚠️ **E questo e' anche il limite dichiarato della regola:** se un giorno il ramo di fallback
diventasse frequente, il libro girerebbe a una leva **mista** e la curva `g(k)` smetterebbe di
descriverlo. → **il sorvegliante del §6.4 deve riportare quella frequenza**, e se supera il **2%
dei giri in 90 giorni** la regola va ridiscussa (soglia dichiarata qui, prima di vedere il caso).
**Alternativa scartata:** congelare l'ultimo target scalato durante il fallback. Aggiunge **stato**
a un percorso con soldi veri — cioe' un nuovo modo di essere sbagliati — per risparmiare $0,056.
### 4.4 G5 — il `min_order_usd` non e' un problema, e va detto
La scala **aumenta** i delta, quindi allontana dal pavimento $5 invece di avvicinarcisi. Nessuna
azione. (Lo si dice perche' «non e' un problema» misurato vale quanto un problema trovato: evita
che la domanda torni.)
### 4.5 G6 — il disaster-SL: **la guardia che morde per prima, e non era stata calcolata**
Il disaster-SL e' un movimento di **prezzo** (30% sul mark), non una perdita in equity. L'impatto
in equity e' quel movimento **moltiplicato per il lordo**, quindi **cresce con k anche se
`disaster_sl_pct` non cambia**. Prototipo sez. 6, tutte le righe scalate dai numeri **replicati**
di WORST-DAY (§7):
| k | lordo max | A) peggior giorno possibile | **B) costo disaster-SL** | C) x_liq | min intra·k | margine C |
|---|---|---|---|---|---|---|
| 1,00 | 0,864 | 14,32% | **30,00%** | 115,8% | 7,33% | 15,8x |
| **1,25** | **1,081** | **17,90%** | **37,50%** | **92,5%** | **9,16%** | **10,1x** |
| 1,50 | 1,297 | 21,48% | 45,00% | 76,9% | 11,00% | 7,0x |
| 2,00 | 1,729 | 28,64% | **60,00%** ← sfonda | 57,4% | 14,66% | 3,9x |
📌 **La guardia che morde per prima NON e' il peggior giorno possibile, e' il disaster-SL:**
- da (A): `k ≤ 50% / 14,32%` = **3,49x**
- da (B): `k ≤ 50% / (2 · 0,50 · 30%)` = **1,67x****2,1x piu' stringente**
A k=1,25 il margine di (A) e' **2,8x** (il numero che WORST-DAY pubblica come C1) mentre quello di
(B) e' **1,33x**. **Il criterio C1 di WORST-DAY guardava la grandezza sbagliata**: guardava il
peggior giorno del *segnale*, non il costo del dispositivo di emergenza che il libro ha davvero
armato sul conto. Da qui l'invariante G2 (§4.2), che con `sl_pct=30%` e `frac=0,50` da'
**scala ≤ 1,67x** — e che scatta se qualcuno allarga il disaster-SL.
**Decisione: `disaster_sl_pct` NON si tocca.** Stringerlo a 24% per tenere invariante l'impatto in
equity lo trasformerebbe da dispositivo di catastrofe in **stop di strategia**: verrebbe toccato dal
rumore, e il progetto ha gia' misurato cosa succede quando uno stop stretto incontra un gap (SKH01,
`sl 2% modellato → 11/23% realizzato`). Si accetta il 37,5% e **lo si scrive**.
⚠️ **Osservazione da LETTURA DEL CODICE, non misurata, e va citata cosi'.**
`src/live/execution.py:196-223` **ri-ancora** il disaster-SL al mark a ogni giro: se lo stop esistente
dista piu' del **5%** relativo da `mark·(1sl_pct)`, viene **cancellato e ripiazzato**. Per un long
questo significa che lo stop **segue il prezzo anche in discesa**: il 30% e' un 30% **rotolante
dall'ultimo piazzamento**, non un pavimento assoluto sul trade. Quindi **B e' il costo di UN
episodio**, e un ribasso lento puo' produrne piu' d'uno.
Evidenza circostanziale, non prova: nel log ci sono **15 `placed` contro 259 `ok`** su asset con
posizione aperta — coerente con qualche ri-ancoraggio, ma i `placed` includono anche le prime
aperture e **non sono separabili dal log**. → **e' una proprieta' di oggi che il gradino amplifica,
non una che il gradino crea**, ed e' un follow-up con una domanda precisa: *quante volte il bracket
e' stato ri-piazzato mentre la posizione restava aperta?* (misurabile aggiungendo `pos_prima` al log,
non ricostruibile da quello attuale).
---
## 5. I TEST CHE DEVONO ESISTERE **PRIMA** DELLA MODIFICA
Scritti come specifiche eseguibili: **nome + cosa asserisce + il caso che deve far fallire.**
File proposto: `tests/test_book_scale.py` (piu' le modifiche dichiarate a
`tests/test_fee_sensitivity.py`).
| # | test | asserisce | **deve fallire se** |
|---|---|---|---|
| **T1** | `test_la_scala_e_identica_sui_due_sleeve` | su una **griglia di k** (1,00/1,10/1,25/1,50/2,00) × tp_frac × segno, con cap disattivato: `target(k) == k·target(1)` **e** `target(tp=1,sg=0)/target(tp=0,sg=1) == W_TP01/W_SKH` costante | qualcuno applica la scala a **una sola** gamba (o dentro `TrendPortfolio`) |
| **T1b** | `test_il_test_di_simmetria_smaschera_una_scala_asimmetrica` | **CONTROLLO POSITIVO**: la stessa asserzione, applicata a un'implementazione asimmetrica scritta nel test, **fallisce** | il test T1 gira **solo a k=1** (dove le due implementazioni **coincidono**: potenza **zero** — misurato nel prototipo) |
| **T2** | `test_la_leva_lorda_da_config_resta_sotto_il_tetto` | `len(ASSETS) * frac * scala <= LEVA_LORDA_MAX`, con `frac` e `scala` **letti da `config/live.json`** e `LEVA_LORDA_MAX`/`ASSETS` **importati dal modulo di produzione** | qualcuno alza `frac`, o la scala, **o entrambe di poco** |
| **T3** | `test_l_invariante_disaster_sl_tiene_meta_equity` | `len(ASSETS) * frac * scala * disaster_sl_pct <= 0.50` | qualcuno allarga `disaster_sl_pct` **o** alza la scala fino a 1,67x |
| **T4** | `test_il_fallback_non_e_piu_permissivo_del_percorso_fidato` | in **ogni** stato degradato (equity illeggibile / watermark assente / watermark vecchio) il lordo massimo **/ equity vera** ≤ quello dello stato normale | qualcuno applica la scala anche in fallback, o slega il cap dal watermark |
| **T5** | `test_il_cap_continua_a_limitare_cio_che_limitava` | il target per-asset non supera mai `cap·scala`; e il clamp **morde ancora** sulle stesse osservazioni di prima (le 3 su 5.650) | qualcuno inverte l'ordine (§3.2) o toglie il clamp «tanto c'e' la scala» |
| **T6** | `test_una_scala_fuori_tetto_ferma_l_esecuzione_e_non_viene_tagliata` | con `scala` fuori `SCALA_LADDER` o fuori tetto: `book_execute` **non invia**, allerta, e il valore **non** viene silenziosamente clampato | qualcuno «rende robusto» il lettore con un `min(scala, LEVA_LORDA_MAX/…)` |
| **T7** | `test_senza_la_chiave_il_libro_e_quello_di_oggi` | con chiave assente **o** `= 1.0`, `book_net_target` e' **bit-exact** alla versione attuale su una griglia (`max|diff| == 0.0`) | la modifica cambia qualcosa **prima** che qualcuno decida di cambiarlo |
| **T8** | `test_il_report_e_il_log_dichiarano_la_scala` | `book_report()['scala']` esiste e `book_execute` la stampa | un parametro che muove il nozionale resta invisibile nel report |
| **T9** | `test_la_scala_e_derivata_dal_codice_non_ridichiarata` | il sorvegliante e i test importano `LEVA_LORDA_MAX`, `ASSETS`, `SCALA_LADDER` **da `src/live/book`** e non li riscrivono | due liste in due file (regola del 21/08, 4 occorrenze) |
| **T10** | `test_il_sorvegliante_segnala_una_scala_non_autorizzata` | **CONTROLLO POSITIVO** del §6.4: con una config che dichiara una scala assente dal giornale, il sorvegliante **allerta**; con config e giornale concordi, **tace** | il sorvegliante e' tarato per non segnalare mai (regola del 26/07: *un rilevatore tarato per non segnalare e' indistinguibile da uno rotto*) |
| **T11** | `test_il_vecchio_test_di_leva_e_stato_sostituito_non_rilassato` | `test_leva_massima_da_config_resta_sotto_o_uguale_a_1x` **non esiste piu'** e T2/T3 esistono | qualcuno alza la soglia del vecchio test da 1,0 a 1,25 e chiude li' |
⚠️ **T1b e' il test piu' importante dell'elenco**, ed e' la ragione per cui questo paragrafo esiste:
il prototipo (sez. 3) mostra che **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 — la stessa firma dei test di `book_live` scoperti senza potenza a libro flat.
---
## 6. IL GATE MANCANTE — `GATE SCALA-01`
### 6.1 Perche' serve un gate NUOVO
Vedi §0: nessun gate esistente vede la scala. Il gate deve quindi vivere sull'unico asse su cui la
scala si vede: **crescita e rovina** (`g(k)`, maxDD, distanza dalla liquidazione), non Sharpe.
### 6.2 Le condizioni — tutte necessarie
Per portare `book_scale_k` da `k0` a `k1`:
| | condizione | misurata come |
|---|---|---|
| **A1** | `k1` e' il **gradino successivo** di `SCALA_LADDER` (1,00 → 1,25). Nessun valore intermedio. | confronto con la costante di codice |
| **A2** | `k0` e' in produzione da **≥180 giorni** con il criterio passato **ogni giorno** | giornale `data/live/scale_history.jsonl` (append-only) + registrazioni del sorvegliante |
| **A3** | **C1C5 di WORST-DAY** ricalcolati **oggi** a `k1`, tutti PASSA — non ereditati dal 22/08 | ri-esecuzione di `r0822c_worst_day` sui dati correnti |
| **A4** | **C7 = G2**: `n_asset · frac · k1 · disaster_sl_pct ≤ 0,50` | §4.5 |
| **A5** | **C8 = G1**: `n_asset · frac · k1 ≤ LEVA_LORDA_MAX` | §4.1 |
| **A6** | **ANTI-RECENCY**: il criterio deve passare **anche escludendo gli ultimi 90 giorni**: `k_ammesso(r[:-90]) ≥ k1` | §6.3 |
| **A7** | `r0726_fee_sensitivity` **rifatto** al nuovo lordo e la conclusione «liquidation fee irrilevante» **ri-derivata o ritirata** | §3.4 |
| **A8** | la modifica e' **una riga di config + una riga di giornale**, zero codice. Se serve toccare `LEVA_LORDA_MAX`, **non e' una decisione di config** | diff |
| **A9** | i test T1T11 (§5) esistono e passano **prima** | pytest |
### 6.3 A6 — l'anti-recency, e come si misura davvero
La forma verificabile e': **il gradino dev'essere autorizzato anche dai dati che non contengono la
finestra recente che ha motivato la richiesta.** E' l'analogo, sull'asse della scala, di
`select_cell_insample`.
Il prototipo (sez. 7b) misura la meccanica su `k_ammesso = ½·k*(drift a 2 SE, coda troncata al
bound strutturale)`:
| finestra | n | drift punto | drift 2SE | k ammesso | ≥1,25x? |
|---|---|---|---|---|---|
| tutta la storia | 2.719 | 17,11% | 8,78% | **3,52** | SI |
| esclusi ultimi 90 g | 2.629 | 16,70% | 8,15% | **3,20** | SI |
| esclusi ultimi 365 g | 2.354 | 19,52% | 10,20% | 3,80 | SI |
| solo ultimi 365 g | 365 | 1,59% | 13,69% | 0,03 | NO |
e la sensibilita' a un tratto recente **artificialmente buono** (ultimi 90 giorni riscritti a un
drift dato, sull'intera storia):
| ultimi 90 g portati a | drift 2SE | k ammesso | delta |
|---|---|---|---|
| 0%/anno | 7,82% | 3,12 | 0,40 |
| +50%/anno | 9,47% | 3,80 | +0,28 |
| **+100%/anno** | 11,11% | **4,45** | **+0,93** |
| +200%/anno | 14,35% | 5,63 | +2,10 |
**Il 2 SE fa gran parte del lavoro** (un tratto eccezionale alza insieme il drift *e* la sua
incertezza), **ma non basta**: il criterio resta una funzione dei dati recenti, e la protezione vera
e' la **doppia valutazione** di A6.
⚠️ **Tre onesta' obbligatorie su questa sezione:**
1. `k_ammesso` del prototipo e' **deliberatamente semplificato** (troncatura al bound, nessuna GPD):
serve a mostrare la **meccanica**, non a produrre il numero. Il gate reale usa la versione di
`r0822c_worst_day`.
2. **Oggi A6 non morde**: `k_ammesso ≈ 3,5x` contro un gradino di 1,25x. Il vincolo che morde e'
quello del §4.5 (disaster-SL). **A6 serve al gradino successivo, non a questo** — e va scritto
ora, quando non e' comodo per nessuno, non quando servira'.
3. La prima stesura di questa sezione presentava «solo ultimi 365 giorni» come l'esempio di *«un anno
buono»*: **falso**, quell'anno e' stato debole (drift 1,59%). Quella riga mostra solo che un
campione corto ha un SE enorme. La riga che risponde alla domanda vera e' la tabella dello shock.
### 6.4 La cadenza — `scale_watch`
Sorvegliante giornaliero (proposto: `src/live/scale_watch.py` + `scripts/live/scale_watch.py` in
`cron_daily.sh`; **non implementato**), che risponde a **tre domande diverse** — e sono davvero tre,
perche' hanno **tre azioni diverse** e nessuna e' deducibile dalle altre:
| domanda | esito | azione |
|---|---|---|
| la config dichiara una scala che il **giornale** non ha mai autorizzato? | 🚨 | ricostruire come ci e' arrivata; scendere a `k0` |
| il criterio che autorizzo' la scala **corrente** passa ancora oggi? | 🚨 | **scendere di un gradino** |
| il **prodotto** `frac·scala·n_asset` ha superato `LEVA_LORDA_MAX`? | 🚨 | bloccare l'esecuzione |
Piu' due requisiti che il progetto ha gia' imparato a pagare:
- **tre stati, non due**: `OK` / `ALLARME` / **`NON MISURABILE`** (serie troppo corta, dati mancanti).
*«Non vedo» non e' «va tutto bene»*`paper_dvolspread`, `venue_watch`.
- **disciplina degli allarmi**: una allerta per streak (`lock_step`), non una per giro; e il
messaggio deve dire **cosa** (decide) **e perche'** (ripara), con il valore **letto**, mai cablato.
E deve **riportare la frequenza del ramo di fallback** (§4.3), che oggi e' 0/1.442 e che se salisse
sopra il **2% in 90 giorni** invalida la regola «scala solo sul percorso fidato».
⚠️ **Cio' che il sorvegliante NON puo' fare: impedire la modifica.** Chi ha accesso al file puo'
scriverci dentro. Il sorvegliante rende la modifica **visibile entro 24 ore** e **attribuibile**.
E' la stessa onesta' di `venue_watch`: non protegge il saldo, compra tempo.
---
## 7. IL COSTO DI NON FARLO, E IL RISCHIO DI FARLO
### 7.1 Il costo di restare a 1,00x
**~€165/mese di versamenti equivalenti, a costo zero, per sempre** (§0): 14,7a → **12,9a** al
capitale-rendita a €500/m netto fisco. Sulla scala delle leve misurate dal progetto sta **sotto**
«versare o no» (da *mai* a 16 anni) e «quando versare» (fattore 6), e **sopra** tutto cio' che la
ricerca ha prodotto in due mesi: l'ottimizzazione del peso SKH01 vale **+0,030 di Sharpe** e ha
fallito il suo gate; il gradino vale **1,8 anni**.
Ed e' **l'unica leva grande che non richiede soldi nuovi**.
### 7.2 Il rischio di farlo, in una tabella
| grandezza | k=1,00 | **k=1,25** | fonte |
|---|---|---|---|
| peggior giorno **possibile** (chiusura) | 14,32% | **17,90%** | replicato, §4.5 |
| peggior giorno **vissuto** (chiusura) | 3,95% | 4,94% | replicato |
| peggior **minimo** intra-giorno | 7,33% | 9,16% | WORST-DAY (lente accoppiata) |
| **costo di un disaster-SL** | 30,0% | **37,5%** | §4.5 ← **la guardia che morde** |
| margine di liquidazione | 15,8x | 10,1x | §4.5 |
| maxDD storico | 12,17% | 15,01% | §0 |
| costo funding non modellato | ~1,53%/a | ~1,91%/a | FUNDING §35 |
| costo di una liquidazione (fee 1%) | 1,00% eq | 1,25% eq | §3.4, da ricalcolare |
### 7.3 La riga che WORST-DAY ha dichiarato, e che va tenuta in cima alla decisione
> Se si credono **entrambe** le riserve insieme — drift a 2 SE **e** un giorno a 10% ogni anno —
> il drift atteso e' **0,61%/anno**, e **a quel punto non e' il gradino a essere sbagliato, e' il
> libro**: chi usa quella combinazione sta chiedendo di spegnere anche il conto di oggi.
Questa specifica non risolve quel dilemma e non deve: lo rende **esplicito**. Chi rifiuta il gradino
in nome di entrambe le riserve deve dire cosa propone di fare del libro a k=1.
### 7.4 Cosa questa specifica **non** rende sicuro
- **La lente e' close-only** dove non usa i numeri intra-giorno di WORST-DAY. Su una regola a **un
giorno** close-only e' **esattamente cieca** (25/07, riconfermato dallo scettico il 22/08). Sul
canale **prop/funded** — dove il vincolo binding e' una barriera di DD giornaliera — **questa
specifica non si applica** e la lente accoppiata resta obbligatoria.
- **`g(k)` assume ritorno indipendente dalla size.** A $635 e' innocuo (SLIP-AUDIT: nessun impatto
misurabile, e un fill ha preso il 21,9% del volume della sua barra 5m in un range di 1,08 bps), ma
**quella misura ha una data di scadenza** — la partecipazione scala lineare col capitale.
- **Il funding non e' in nessuna serie di questo documento.** Il libro paga gia' ~1,5%/anno che
nessun backtest sottrae; il gradino lo amplifica del 25%. E' dentro C5, ma il **livello a k=1**
resta un difetto aperto del progetto, non di questa specifica.
- **La leva non compra probabilita', compra fretta** (PROP-ALLOC). Il gradino accorcia la mediana;
non alza `P(arrivare)` in modo proporzionale, e **alza la varianza del percorso**.
---
## 8. PROCEDURA DI ATTIVAZIONE (checklist, in ordine)
1. Scrivere `tests/test_book_scale.py` con T1T11 (§5). **Prima.** T7 deve passare **contro il
codice attuale** (la chiave non esiste ancora → default 1,0 → bit-exact).
2. Implementare §2.2 (`scala` in `book_net_target`, `_scala()`, `LEVA_LORDA_MAX`, `SCALA_LADDER`,
`scala` nel report e nel print di `book_execute`). **Senza toccare `config/live.json`**: la chiave
assente vale 1,0, il libro non cambia, i test lo dimostrano.
3. Cancellare `test_leva_massima_da_config_resta_sotto_o_uguale_a_1x` **con la nota** (§3.4).
4. Scrivere `scale_watch` (§6.4) + `data/live/scale_history.jsonl` con la riga iniziale
`{"scala": 1.00, "dal": "...", "gate": "iniziale, nessun gradino"}`, e aggiungerlo a
`cron_daily.sh` **e** al backup di `/opt/docker/scripts/backup.sh` (il giornale non e' ricostruibile).
5. **Lasciare girare a 1,00 per almeno 30 giorni** con il sorvegliante attivo. Una manopola nuova si
guarda funzionare **prima** di girarla.
6. Solo allora: eseguire `GATE SCALA-01` (§6.2). Se PASSA, una riga di config + una di giornale.
7. Rifare `r0726_fee_sensitivity` al nuovo lordo (A7) e ripubblicare la conclusione.
**Il punto 5 non e' burocrazia: e' l'unico modo di scoprire che il sorvegliante e' rotto mentre la
leva e' ancora 1,00.**
---
## 9. NODI APERTI — dichiarati, non nascosti
1. **Il disaster-SL rotolante** (§4.5, ⚠️): letto nel codice, non misurato. Il log **non permette**
di separare i ri-piazzamenti dalle prime aperture. Se davvero rotola, `B` e' il costo di *un*
episodio e un ribasso lento ne produce piu' d'uno. **E' il nodo piu' serio di questo documento**,
perche' tocca la guardia che morde per prima. Follow-up: aggiungere `pos_prima` al log di
`ensure_disaster_sl` e ri-misurare — **non ricostruibile a posteriori**.
2. **`LEVA_LORDA_MAX = 1.25` e' una scelta di processo, non una misura.** Dall'invariante del §4.5 il
massimo sostenibile sarebbe 1,67x. Il valore basso e' il cricchetto. Chi vuole 1,50x deve toccare
codice — **ed e' voluto**.
3. **La banda d'ancora non e' stata girata su nessun numero di questo documento.** Tutte le serie
sono all'ancora canonica con la correzione ×0,89 sul drift. Le grandezze usate per decidere
(bound strutturale, lordo, invarianti) sono **aritmetiche** e non ancorate; `g(k)` e `k_ammesso`
**lo sono** — e vanno lette con la banda del libro (Sharpe FULL de-luckato 1,95, banda [1,81; 2,12]).
4. **Il gradino non e' stato autorizzato qui.** Questo documento specifica come si autorizza.
---
## 10. IL PROTOTIPO
`scripts/research/r0822e_scale_proto.py`**0,9 s**, nessuna rete, nessuna scrittura.
```
nice -n 19 timeout 900 uv run python scripts/research/r0822e_scale_proto.py
```
Sezioni: (0) isolamento verificato · (1) sanity bit-exact vs produzione · (2) la catena letta dal
sorgente · (3) simmetria + controllo positivo · (4) l'ordine col clamp · (5) replica di WORST-DAY ·
(6) guardie · (6-bis) stati dell'equity · (7) il gate mancante.
**Replica a k=1,00 (sez. 5) — il motivo per cui le righe scalate di questo documento non sono
numeri copiati:**
| grandezza | prototipo | pubblicato | scarto |
|---|---|---|---|
| drift de-luckato ×0,89 | 17,11% | 17,11% | 0,00% |
| vol annua | 11,37% | 11,37% | 0,00% |
| Sharpe de-luckato | 1,505 | 1,510 | 0,005 |
| peggior giorno (chiusura) | 3,95% | 3,94% | 0,01% |
| data del peggior giorno | **2020-03-13** | 2020-03-13 | coincide |
| morso del clamp | **3 / 5.648** | 3 / 5.650 | 2 righe di bordo* |
| bound strutturale | **14,32%** | 14,32% | 0,00% |
\* differenza di costruzione: il prototipo scarta la prima barra per asset (serve `vol_prev`).
---
*Documento scritto senza modificare nulla in produzione. Il branch `research/wave-0822` e' quello da
cui il cron esegue: ogni riga di `src/`, `config/`, `scripts/live/`, `scripts/cron_*.sh` e `tests/`
e' rimasta identica.*