venue: la taratura ri-misurata — «zero falsi allarmi in 8 anni» non descriveva la produzione
B1 (memoria) + B2 (misura). Il 19/08 stava solo in un diario e in un commit: la
sessione che ha cambiato il tripwire di venue, la tabella _CONTRACT e le referenze
non era in CLAUDE.md — 2a occorrenza dopo edge_watch, e riguarda di nuovo qualcosa
che gira con soldi veri. Ora c'e', insieme al debito che dichiarava.
B2: il debito era «la taratura non e' ri-misurata a tre referenze». Ri-misurandola
(scripts/research/r0821_venue_refs.py, riusa dislocation/episodes/zero_fp_frontier
di r0726) il problema non era dove ci si aspettava.
1. «Zero falsi allarmi in 8 anni» veniva dal consenso Coinbase+Bitstamp+BITFINEX
dello script di ricerca; il sorvegliante live gira su Coinbase+Bitstamp. Due
liste di referenze in due posti diversi, e nessun test poteva accorgersene.
Sull'insieme reale i falsi allarmi sono 1: 2020-03-13 07:00-10:00 UTC, 4 ore a
-418 bps di picco su BTC = il crash COVID, cioe' proprio l'evento che CLAUDE.md
elencava come esempio di cio' su cui NON scattava. La firma che lo dimostra: le
"65.043 ore BTC" citate in memoria sono esattamente le ore utili del set con
bitfinex; sul set reale sono 69.633.
2. Kraken non lo ripara: su 8 anni porta un mese. Il tetto ~700 candele
dell'endpoint pubblico e' stato ri-verificato oggi sulla rete (704 barre su
70.286 richieste = 1,00%), non creduto da un commento del 26/07.
3. Perche' spariva a 3 referenze, ed e' il punto trasferibile: con bitfinex dentro
1 delle 4 ore diventa BLIND, lo streak si azzera e l'episodio non esiste — ma la
dislocazione e' ancora li' (mediana -343 bps). Lo zero non veniva da un consenso
piu' accurato, veniva da un'ora buttata (ore utilizzabili 93,4% vs 100,0%).
4. La direzione dichiarata il 19/08 ("piu' BLIND, allerta di meno") e' confermata ma
la sua taglia dipende dal venue, non dal numero tre: confronto appaiato sulle
stesse ore, con bitfinex -13,91 pp di ore utilizzabili, con kraken +0,00 pp.
5. Controllo positivo intatto: Bitfinex 2018-19, 22 episodi, il piu' lungo 2.324h,
picco 1.136 bps = 11,4x la soglia.
DECISIONE: (100 bps, 4h) NON si tocca, ma la giustificazione cambia. 1 falso allarme
in 8 anni costa 0,031%/anno di equity attesa contro il 100% che un vero positivo
evita (ogni p plausibile e' 16-160x sopra il break-even); alzare a 150 bps per
ripristinare lo zero porterebbe il margine su FTX da 3,0x a 2,0x, sotto la gamba (b)
del criterio dichiarato prima di guardare i dati. La memoria si contraddiceva da
sola — «zero falsi allarmi» al punto (2), «a 1 ogni 8 anni serve p > 0,031%» al
punto (4) — e la meta' giusta era quella dell'economia.
Congelato il MECCANISMO, non il numero: tests/test_venue_watch.py 35 -> 37, due test
puri (lo spread max-min e' monotono nel numero di referenze; una sola ora BLIND
spezza lo streak, con un caso di controllo che DEVE scattare).
Soglie, book, pesi, config, cron: INVARIATI. Suite 621 verdi, 1 rosso noto
(test_gtaa_band_gate, altro asse). Diario: docs/diary/2026-08-21-venue-taratura-*.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1120,25 +1120,31 @@ Prima ondata di ricerca onesta su BTC/ETH certificati (5 track, harness condivis
|
||||
decisione appena presa (100% Deribit fino a $20k): se non si puo' ridurre l'ESPOSIZIONE, l'unica
|
||||
leva e' il **TEMPO**. Script `r0726_venue_tripwire.py` (segnale) + `r0726_venue_response.py`
|
||||
(costo della risposta); produzione `src/live/venue_watch.py` + `scripts/live/venue_watch.py` in
|
||||
`cron_book.sh`; test `tests/test_venue_watch.py` (23); diario `2026-07-26-venue-tripwire.md`.
|
||||
`cron_book.sh`; test `tests/test_venue_watch.py` (**37**); diari `2026-07-26-venue-tripwire.md`,
|
||||
`2026-08-19-venue-watch-disciplina-allarmi.md`, `2026-08-21-venue-taratura-tre-referenze.md`.
|
||||
**Book, pesi, config INVARIATI.**
|
||||
(1) **Il segnale:** un venue che gata i prelievi **rompe l'arbitraggio** → il suo prezzo si stacca
|
||||
dal consenso e ci RESTA. Il segnale e' **|scarto|, non il segno** (Mt.Gox andava a *premio*, un
|
||||
venue in fuga a *sconto*: dicono la stessa cosa). Consenso = venue **USD indipendenti**
|
||||
(Coinbase, Bitstamp); mai USDT (depeg 2022 → falsi allarmi giganti). Deribit sta a **3 bps** dal
|
||||
consenso in mediana su 8 anni (65.043 ore BTC + 64.541 ETH) — fondo di rumore bassissimo, ed e'
|
||||
cio' che rende possibile una soglia con margine.
|
||||
venue in fuga a *sconto*: dicono la stessa cosa). Consenso = **mediana** di venue **USD
|
||||
indipendenti** (Coinbase, Bitstamp, **e Kraken dal 19/08** — vedi sotto); mai USDT (depeg 2022 →
|
||||
falsi allarmi giganti). Deribit sta a **3 bps** dal consenso in mediana su 8 anni — fondo di
|
||||
rumore bassissimo, ed e' cio' che rende possibile una soglia con margine. ⚠️ Le "65.043 ore BTC"
|
||||
citate qui fino al 21/08 erano le ore del consenso **con bitfinex dentro**, che la produzione non
|
||||
ha mai avuto: sull'insieme reale sono **69.633** (ETH 64.541 invariato).
|
||||
(2) **Taratura CONGELATA = 100 bps persistenti 4h a segno costante.** Criterio **dichiarato
|
||||
prima**, perche' i due ovvi sbagliano in versi opposti (provati entrambi): *minimi bps* → 25bps/24h
|
||||
consuma 24 delle ~72h che diede FTX; *minime ore* → 500bps/2h **manca FTX** (margine 0.6x).
|
||||
Regola adottata: (a) zero falsi allarmi su entrambi gli asset in 8 anni; (b) margine ≥3x sul caso
|
||||
storico **piu' debole** (FTX ~300bps) → soglia ≤100bps; (c) a quei vincoli, minima latenza.
|
||||
Margine finale **3x FTX / 5x QuadrigaCX / 10-20x Mt.Gox**, con **zero falsi allarmi** inclusi
|
||||
crash COVID 2020-03, maggio 2021, LUNA e novembre 2022.
|
||||
Margine finale **3x FTX / 5x QuadrigaCX / 10-20x Mt.Gox**.
|
||||
⚠️ **CORREZIONE 2026-08-21 — la gamba (a) NON e' soddisfatta dalla configurazione che gira, e la
|
||||
frase "zero falsi allarmi inclusi crash COVID 2020-03" era FALSA: il falso allarme E' il COVID.**
|
||||
Vedi il blocco **(8)** in fondo. Il punto (100 bps, 4h) **resta**, per economia e per la gamba (b).
|
||||
(3) ✅ **CONTROLLO POSITIVO superato** (obbligatorio: un rilevatore tarato per non segnalare e'
|
||||
indistinguibile da uno rotto). Puntato su **Bitfinex 2018-19** (problemi bancari/Tether):
|
||||
**22 episodi**, il piu' lungo **2.324 ore consecutive** a +447bps di picco, altri a 1.151h/+663bps
|
||||
e 496h/+1136bps. **22 scatti dove il problema c'era, 0 su Deribit in 8 anni.** E la DURATA
|
||||
e 496h/+1136bps. **22 scatti dove il problema c'era, 1 solo su Deribit in 8 anni**
|
||||
(era dichiarato 0 — vedi **(8)**). E la DURATA
|
||||
risponde alla domanda vera: un venue gated resta dislocato per **settimane** → 4h di latenza
|
||||
costano una frazione trascurabile del preavviso.
|
||||
(4) **Economia della risposta:** costo ATTESO di un falso allarme (flat 3 giorni, misurato sul
|
||||
@@ -1168,6 +1174,73 @@ Prima ondata di ricerca onesta su BTC/ETH certificati (5 track, harness condivis
|
||||
**l'outer-join con referenze di lunghezza diversa e' una trappola ricorrente**); (f) un controllo
|
||||
positivo finito dentro il ramo `else` **non gira mai** (trovato in sessione: era esattamente il
|
||||
difetto che doveva prevenire).
|
||||
✅ **(7) DISCIPLINA DEGLI ALLARMI + TERZA REFERENZA + SPECIFICHE CONTRATTO (2026-08-19)** —
|
||||
*(bullet scritto il 21/08: la sessione era in un diario e in un commit e NON in memoria operativa,
|
||||
2ª occorrenza dopo `edge_watch`)*. Nato da 4 🚨 identici il 18/08 per una **manutenzione Deribit
|
||||
annunciata** (14/08 per il 18/08 09:00 UTC, downtime dichiarato 15-30 min) che ha **sforato fino
|
||||
alle 14:07 UTC**. **Il book si era comportato bene** (astensione alle 09:07 e 10:07, *"conto non
|
||||
leggibile → non eseguo a cieco"*): il difetto era negli allarmi. (a) Il blocco piattaforma era un
|
||||
`if` secco **senza memoria**, accanto a un rilevatore che allerta una volta per streak → ora passa
|
||||
da **`lock_step()`** pura: manutenzione entro `MAINT_GRACE_HOURS`=2 → ⚠️ **una volta**; che sfora
|
||||
→ 🚨 una volta («ha SFORATO»); blocco **senza** manutenzione dichiarata → 🚨 subito; rientro
|
||||
annunciato una volta; **`public/status` illeggibile → NIENTE** (dichiarare «e' rientrato» perche'
|
||||
non si e' riusciti a guardare sarebbe la bugia peggiore). *Un allarme massimo speso per un evento
|
||||
atteso e' un allarme che non verra' letto il giorno che e' vero.* (b) Il messaggio stampava
|
||||
`locked=true` **cablato** mentre il parser accetta anche `partial` → dichiarava un valore che non
|
||||
aveva letto, e il runbook manda a controllare **proprio quel campo**; ora stampa e **salva** il
|
||||
grezzo. (c) ⚠️ **Il difetto che poteva costare:** il 18/08 Deribit ha cambiato **tick e size dei
|
||||
perpetual lineari USDC** (BTC tick 0.5→**0.1**, ETH 0.05→**0.01**, ETH min/step 0.001→**0.0001**)
|
||||
e la tabella `_CONTRACT` di `deribit.py`, **cablata a mano**, non se n'e' accorta. Nessun ordine
|
||||
rifiutato **e non per merito nostro**: erano *riduzioni*, e un valore piu' grosso resta conforme
|
||||
(costo effettivo: granularita', incremento minimo ETH $1.92 invece di $0.19). **Il giorno che
|
||||
Deribit ALZA un minimo la stessa cecita' fa rifiutare gli ordini.** Cablato **`check_specs()`**
|
||||
nel venue_watch orario, **fuori dal percorso ordini**: la tabella dichiarata resta l'AUTORITA'
|
||||
per costruire un ordine, il venue e' il **controllore** (prendere i valori dall'API dentro
|
||||
l'esecuzione renderebbe l'ordine dipendente da come ha risposto una GET = non ricostruibile).
|
||||
Verdetti: `granularita'` (dichiarato piu' grosso → conforme, ⚠️) vs `rifiuto` (dichiarato piu'
|
||||
fine → 🚨). Uno strumento **non letto** finisce in `non_letti`, non conta come «combacia».
|
||||
(d) Aggiunta **Kraken** come terza referenza: **Coinbase ha comprato Deribit** e una referenza
|
||||
che e' la casa madre non misura piu' se Deribit scolla dal mondo.
|
||||
⚠️ **(8) LA TARATURA RI-MISURATA (2026-08-21) — «zero falsi allarmi in 8 anni» non ha mai
|
||||
descritto la produzione, e il falso allarme e' il COVID.** Script `r0821_venue_refs.py`, diario
|
||||
`2026-08-21-venue-taratura-tre-referenze.md`. **Soglie, book, config INVARIATI.**
|
||||
**(i)** Il numero pubblicato veniva dal consenso **Coinbase+Bitstamp+Bitfinex** dello script di
|
||||
ricerca; il sorvegliante live girava su **Coinbase+Bitstamp**. Due liste di referenze in due
|
||||
posti diversi, e **nessun test poteva accorgersene**. Sull'insieme reale: **1 falso allarme in
|
||||
8 anni**, il **2020-03-13 07:00-10:00 UTC**, 4 ore a **−418 bps** di picco su BTC — cioe' proprio
|
||||
l'evento che questo bullet elencava come esempio di cio' su cui NON scattava. La soglia minima a
|
||||
zero falsi allarmi a 4h e' **150 bps** (BTC), non 100.
|
||||
**(ii) Kraken non lo ripara:** su 8 anni porta **un mese** (tetto ~700 candele dell'endpoint
|
||||
pubblico, **ri-verificato oggi**: 704 barre su 70.286 richieste = 1,00%) → LIVE-post ≡ LIVE-pre
|
||||
sulla storia lunga, numeri **identici**.
|
||||
**(iii) Perche' spariva a 3 referenze, ed e' il punto trasferibile:** con bitfinex dentro **1
|
||||
delle 4 ore diventa BLIND** → lo streak si azzera e l'episodio non esiste, **ma la dislocazione
|
||||
e' ancora li'** (mediana −343 bps). **Lo zero non veniva da un consenso piu' accurato, veniva da
|
||||
un'ora buttata** (ore utilizzabili 93,4% contro 100,0%).
|
||||
**(iv) La direzione dichiarata il 19/08 e' confermata, la sua TAGLIA dipende dal venue** (confronto
|
||||
appaiato, stesse ore): con **bitfinex** le ore non-BLIND passano 99,95% → 86,04% = **−13,91 pp**;
|
||||
con **kraken** **+0,00 pp** (|scarto| mediano appaiato −0,04 bps). Non e' una proprieta' del
|
||||
numero tre: e' **quanto la terza referenza e' d'accordo con le altre**.
|
||||
**(v) Controllo positivo intatto:** Bitfinex 2018-19 → 22 episodi, il piu' lungo 2.324h, picco
|
||||
**1.136 bps = 11,4x** la soglia.
|
||||
**(vi) DECISIONE: (100 bps, 4h) NON si tocca, ma la giustificazione cambia.** 1 falso allarme in
|
||||
8 anni = 0,125/anno × 0,248% = **0,031%/anno** di equity attesa contro il **100%** che un vero
|
||||
positivo evita, e ogni `p` plausibile (0,5-5%) e' **16-160x sopra** il break-even; alzare a 150 bps
|
||||
porterebbe il margine su FTX da **3,0x a 2,0x**, sotto la gamba (b) dichiarata prima di guardare i
|
||||
dati. **Il numero da citare e' «1 in 8 anni»** — che e' esattamente l'esempio gia' usato al punto
|
||||
(4): **la memoria si contraddiceva da sola e la meta' giusta era quella dell'economia.**
|
||||
**REGOLE NUOVE:** (g) **un numero di taratura si etichetta con la CONFIGURAZIONE, non solo con la
|
||||
finestra** — «zero falsi allarmi in 8 anni» era vero e inutile perche' descriveva un consenso che
|
||||
la produzione non ha mai avuto; (h) **uno "zero" si legge accanto alla quota di campione
|
||||
utilizzabile**: meno falsi allarmi perche' si vede meglio e meno perche' si vede di meno hanno lo
|
||||
stesso valore stampato e valore opposto; (i) **quando due affermazioni della stessa memoria si
|
||||
contraddicono, la contraddizione e' informazione** — una delle due e' stata scritta guardando i
|
||||
dati; (j) **una nota che dichiara un debito puo' sottodimensionarlo**: il 19/08 diceva «il numero
|
||||
non e' ri-misurato», e il numero era sbagliato **prima** della modifica che lo aveva fatto
|
||||
dichiarare. **RESTA APERTO:** il Rulebook Deribit del 12/08 (ADL, perdita socializzata, *emergency
|
||||
powers*, conti dormienti) non lo sorveglia nessuno; `MAINT_GRACE_HOURS`=2 **presume** gli annunci
|
||||
invece di leggerli (due `locked=true` in 4 giorni: 18/08 ~4h, 21/08 ~1h); la terza referenza non
|
||||
e' validabile sulla storia.
|
||||
- ⚖️ **RIVALUTAZIONE DELLA STRATEGIA (2026-07-26, fine giornata) — 0 cambi, e il peso 75/25
|
||||
confermato per la TERZA volta.** Script `r0726_reeval_live_weight.py`, test
|
||||
`tests/test_reeval_live_weight.py` (6), diario `2026-07-26-reeval-strategia.md`.
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
# 2026-08-21 — «Zero falsi allarmi in 8 anni» non descriveva il sistema che gira
|
||||
|
||||
**Richiesta:** *"analizza e fai B1 e B2"* — B1: portare in memoria operativa la sessione del 19/08,
|
||||
che stava solo nel diario e nel commit. B2: ri-misurare la taratura del tripwire di venue dopo
|
||||
l'aggiunta della terza referenza, debito dichiarato quel giorno e mai chiuso.
|
||||
|
||||
**Toccati:** `scripts/research/r0821_venue_refs.py` (nuovo), `tests/test_venue_watch.py` (35 → 37),
|
||||
`CLAUDE.md`. **Book, pesi, `config/live.json`, `THRESHOLD_BPS`, `PERSIST_HOURS`: INVARIATI.**
|
||||
|
||||
---
|
||||
|
||||
## 0. Il debito, come era stato dichiarato
|
||||
|
||||
Il 19/08 e' stata aggiunta **Kraken** come terza referenza, perche' Coinbase ha comprato Deribit e
|
||||
una referenza che e' la casa madre non misura piu' se Deribit scolla dal mondo. Il diario fu onesto
|
||||
sul limite: *«"Zero falsi allarmi in 8 anni" e' stato misurato con l'insieme di referenze di allora
|
||||
… quel numero non e' stato ri-misurato»*, e dichiarava la sola **direzione** dell'errore — con la
|
||||
mediana di tre il consenso e' piu' robusto e lo spread max-min si allarga, quindi si va piu' spesso
|
||||
in BLIND, che e' lo stato morbido: il modo di sbagliare e' «allerta di meno».
|
||||
|
||||
La direzione era giusta. Ma ri-misurando salta fuori un fatto piu' grosso, che non era nel verso
|
||||
previsto perche' non riguardava la modifica del 19/08 **ma la taratura originale**.
|
||||
|
||||
## 1. Il fatto che struttura tutto: la profondita' delle referenze
|
||||
|
||||
| asset | venue | barre | da | a |
|
||||
|---|---|---|---|---|
|
||||
| BTC | coinbase | 69.643 | 2018-08-14 | 2026-07-26 |
|
||||
| BTC | bitstamp | 69.663 | 2018-08-14 | 2026-07-26 |
|
||||
| BTC | **bitfinex** | 33.000 | 2018-08-14 | **2022-05-21** |
|
||||
| BTC | **kraken** | **702** | 2026-06-26 | 2026-07-26 |
|
||||
| ETH | coinbase | 64.554 | 2019-03-14 | 2026-07-26 |
|
||||
| ETH | bitstamp | 64.572 | 2019-03-14 | 2026-07-26 |
|
||||
| ETH | **bitfinex** | **0** | — | — |
|
||||
| ETH | kraken | 702 | 2026-06-26 | 2026-07-26 |
|
||||
|
||||
Il tetto di Kraken e' stato **ri-verificato oggi sulla rete**, non creduto da un commento del 26/07:
|
||||
chieste 70.286 ore, ricevute **704** (l'1,00% del richiesto). L'endpoint OHLC pubblico ritorna le
|
||||
ultime ~700 candele qualunque `since`.
|
||||
|
||||
**Conseguenza: la taratura a 8 anni con Kraken dentro non e' ottenibile.** Non per pigrizia — per
|
||||
struttura della fonte. E non e' un problema per il sorvegliante live, a cui servono le ultime ore.
|
||||
|
||||
## 2. Il falso allarme che c'era, e che nessuno aveva visto
|
||||
|
||||
Frontiera a zero falsi allarmi, per insieme di referenze, punto in produzione (100 bps, 4h):
|
||||
|
||||
| insieme | asset | ore utili | non-BLIND | soglia minima a 4h | falsi allarmi a (100, 4h) |
|
||||
|---|---|---|---|---|---|
|
||||
| **LIVE-pre (CB+BS)** | BTC | 69.633 | 100,0% | **150 bps** | **1** |
|
||||
| | ETH | 64.541 | 100,0% | 100 bps | 0 |
|
||||
| CALIB (CB+BS+BF) | BTC | **65.043** | 93,4% | 75 bps | **0** |
|
||||
| | ETH | 64.541 | 100,0% | 100 bps | 0 |
|
||||
| **LIVE-post (CB+BS+KR)** | BTC | 69.633 | 100,0% | **150 bps** | **1** |
|
||||
| | ETH | 64.541 | 100,0% | 100 bps | 0 |
|
||||
|
||||
Due cose in questa tabella.
|
||||
|
||||
**(a) «Zero falsi allarmi in 8 anni» non ha mai descritto la configurazione in produzione.** Quel
|
||||
numero viene dalla riga CALIB, che ha `bitfinex` nel consenso — e il sorvegliante live non l'ha mai
|
||||
avuta. Il 65.043 e' la firma: e' esattamente il numero di ore che CLAUDE.md cita al punto (1) del
|
||||
bullet VENUE WATCH. Sul consenso reale le ore sono 69.633 e i falsi allarmi sono **1**.
|
||||
|
||||
**(b) Aggiungere Kraken non lo ripara**, perche' su 8 anni Kraken porta un mese: LIVE-post e
|
||||
LIVE-pre danno numeri **identici** sulla storia lunga.
|
||||
|
||||
Il falso allarme e':
|
||||
|
||||
```
|
||||
2020-03-13 07:00 -> 10:00 UTC 4 ore picco -418,7 bps segno -1
|
||||
```
|
||||
|
||||
Il **crash COVID**. Cioe' proprio l'evento che la memoria elencava come esempio di cio' su cui il
|
||||
tripwire *non* scattava: *«zero falsi allarmi inclusi crash COVID 2020-03, maggio 2021, LUNA e
|
||||
novembre 2022»*.
|
||||
|
||||
## 3. Perche' spariva a tre referenze — e perche' non e' una buona notizia
|
||||
|
||||
Nella stessa finestra, con `bitfinex` nel consenso, **1 delle 4 ore diventa BLIND** (utilizzabili
|
||||
75%) → il run si spezza, non arriva mai a 4 ore consecutive, l'episodio non esiste. Ma la
|
||||
dislocazione **e' ancora li'**: mediana **−343 bps** su quelle ore.
|
||||
|
||||
⚠️ **Lo zero non veniva da un consenso piu' accurato, veniva da un'ora buttata.** «Zero falsi
|
||||
allarmi» puo' voler dire *piu' preciso* oppure *piu' cieco*, e le due si distinguono solo guardando
|
||||
la quota di ore utilizzabili — che nella riga CALIB e' 93,4% contro il 100,0% delle altre.
|
||||
|
||||
Congelato in due test puri (`tests/test_venue_watch.py`), perche' e' il meccanismo e non il numero
|
||||
a essere trasferibile:
|
||||
|
||||
- `test_una_terza_referenza_non_puo_aumentare_le_ore_utilizzabili` — lo spread max-min e' monotono
|
||||
nel numero di referenze, quindi «piu' referenze» non e' gratis: sposta verso BLIND;
|
||||
- `test_una_sola_ora_BLIND_spezza_lo_streak_e_impedisce_l_allarme` — con un caso di controllo che
|
||||
DEVE scattare, altrimenti il test non proverebbe nulla.
|
||||
|
||||
## 4. La direzione dichiarata il 19/08 e' confermata, e la sua TAGLIA dipende dal venue
|
||||
|
||||
Confronto **appaiato** (stesse ore, mai due campioni diversi):
|
||||
|
||||
| | ore | non-BLIND 2 ref | 3 ref | Δ | \|scarto\| mediano, differenza appaiata |
|
||||
|---|---|---|---|---|---|
|
||||
| BTC + bitfinex | 33.000 | 99,95% | 86,04% | **−13,91 pp** | −0,04 bps |
|
||||
| BTC + kraken | 702 | 100,00% | 100,00% | **+0,00 pp** | −0,04 bps |
|
||||
| ETH + kraken | 702 | 100,00% | 100,00% | +0,00 pp | +0,03 bps |
|
||||
|
||||
«Piu' referenze → piu' BLIND» e' vero, ma con Bitfinex costa **14 punti** di ore utilizzabili e con
|
||||
Kraken **zero**. Non e' una proprieta' del numero tre: e' una proprieta' di **quanto la terza
|
||||
referenza e' d'accordo con le altre**. Bitfinex nel 2018-2022 era il venue con i problemi bancari —
|
||||
metterlo nel consenso e' metterci dentro il rumore che il controllo positivo usa come *segnale*.
|
||||
|
||||
## 5. Controllo positivo: intatto
|
||||
|
||||
Bitfinex 2018-19 come bersaglio, consenso Coinbase+Bitstamp: **22 episodi**, il piu' lungo **2.324
|
||||
ore**, picco **1.136 bps = 11,4x** la soglia. Il rilevatore vede i casi veri.
|
||||
|
||||
## 6. Decisione: la soglia NON si tocca, ma la giustificazione cambia
|
||||
|
||||
Il criterio dichiarato in anticipo il 26/07 aveva tre gambe: **(a) zero falsi allarmi in 8 anni**,
|
||||
(b) margine ≥3x sul caso storico piu' debole (FTX ~300 bps) → soglia ≤100 bps, (c) minima latenza.
|
||||
**La gamba (a) non e' soddisfatta** dalla configurazione reale. Le altre due reggono, e l'economia
|
||||
del 26/07 risolve da sola:
|
||||
|
||||
- 1 falso allarme in 8 anni = 0,125/anno × 0,248% di costo = **0,031%/anno** di equity attesa,
|
||||
contro il **100%** che un vero positivo evita → break-even a `p > 0,031%`, e ogni `p` plausibile
|
||||
(0,5-5%) e' 16-160 volte sopra;
|
||||
- alzare a 150 bps per ripristinare lo zero porterebbe il margine su FTX da **3,0x a 2,0x**, sotto
|
||||
il vincolo (b) dichiarato prima di guardare i dati.
|
||||
|
||||
**Quindi (100 bps, 4h) resta**, ma perche' **un falso allarme ogni 8 anni e' economico**, non
|
||||
perche' non ce ne siano. E il numero da citare e' **1 in 8 anni** — che e', ironicamente, proprio
|
||||
l'esempio che il punto (4) del bullet usava per il break-even: la memoria si contraddiceva da sola
|
||||
e la meta' giusta era quella dell'economia.
|
||||
|
||||
## 7. Cio' che resta aperto
|
||||
|
||||
- **La terza referenza non e' validabile sulla storia.** Kraken e' verificata su un mese e su quel
|
||||
mese non cambia nulla. Se serve una validazione lunga, va cercata una fonte che pagini davvero
|
||||
(il tetto ~700 e' dell'endpoint pubblico, non di ccxt).
|
||||
- **Il Rulebook Deribit del 12/08 non lo sorveglia nessuno** (ADL, perdita socializzata, *emergency
|
||||
powers*, conti dormienti) — invariato dal 19/08.
|
||||
- **`MAINT_GRACE_HOURS = 2` presume gli annunci invece di leggerli.** Non e' teorico: due
|
||||
`locked=true` in quattro giorni (18/08 ~4h, 21/08 ~1h).
|
||||
|
||||
## REGOLE
|
||||
|
||||
- **Un numero di taratura va etichettato con la CONFIGURAZIONE su cui e' stato misurato**, non solo
|
||||
con la finestra. «Zero falsi allarmi in 8 anni» era vero e inutile: descriveva un consenso che la
|
||||
produzione non ha mai avuto, e nessun test poteva accorgersene perche' i due percorsi (ricerca e
|
||||
live) tenevano la lista delle referenze in due posti diversi.
|
||||
- **Uno "zero" si legge sempre accanto alla quota di campione utilizzabile.** Meno falsi allarmi
|
||||
perche' si vede meglio e meno perche' si vede di meno hanno lo stesso valore stampato e valore
|
||||
opposto.
|
||||
- **Quando due affermazioni della stessa memoria si contraddicono** (qui «zero falsi allarmi» e «a
|
||||
1 ogni 8 anni serve p > 0,031%»), la contraddizione e' un'informazione: una delle due e' stata
|
||||
scritta guardando i dati.
|
||||
- **Il debito dichiarato il 19/08 era corretto ma sottodimensionato:** diceva «il numero non e'
|
||||
ri-misurato» e il numero era sbagliato **prima** della modifica che lo aveva fatto dichiarare.
|
||||
@@ -0,0 +1,192 @@
|
||||
#!/usr/bin/env python
|
||||
"""r0821_venue_refs.py — la taratura del tripwire di venue, RI-MISURATA a tre referenze.
|
||||
|
||||
DEBITO CHIUSO QUI (dichiarato nel diario 2026-08-19 §4 e nel commento di `src/live/venue_watch.py`):
|
||||
il 19/08 e' stata aggiunta **Kraken** come terza referenza, perche' Coinbase ha comprato Deribit e
|
||||
una referenza che e' la casa madre non misura piu' se Deribit scolla dal mondo. THRESHOLD_BPS e
|
||||
PERSIST_HOURS non furono toccati, ma la frase «zero falsi allarmi in 8 anni» era stata misurata con
|
||||
l'insieme di referenze di allora — e **quel numero non e' stato ri-misurato**. Il diario dichiarava
|
||||
solo la DIREZIONE dell'errore («allerta di meno», perche' la mediana e' robusta e lo spread max-min
|
||||
si allarga -> piu' BLIND, che e' lo stato morbido).
|
||||
|
||||
COSA MISURA (riusa il nucleo di r0726_venue_tripwire: `dislocation`, `episodes`,
|
||||
`zero_fp_frontier` sono IMPORTATE, non riscritte — c'e' un test d'identita' in tests/):
|
||||
|
||||
1. la PROFONDITA' REALE di ogni referenza, che e' il fatto che struttura tutto il resto;
|
||||
2. il limite di Kraken verificato OGGI sulla rete, non creduto da un commento del 26/07;
|
||||
3. il confronto APPAIATO 2 referenze vs 3 sulle STESSE ore (mai due campioni diversi:
|
||||
lezione del 26/07, la statistica e' la differenza appaiata, non la differenza fra due mediane)
|
||||
su: quota di ore utilizzabili (BLIND), distribuzione di |scarto|, frontiera a zero falsi allarmi;
|
||||
4. il controllo POSITIVO (Bitfinex 2018-19) sotto la regola nuova: un rilevatore tarato per NON
|
||||
segnalare e' indistinguibile da uno rotto finche' non gli si mette davanti un caso vero.
|
||||
|
||||
uv run python scripts/research/r0821_venue_refs.py [--refresh-kraken]
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from scripts.research.r0726_venue_tripwire import ( # noqa: E402
|
||||
_fetch_1h, dislocation, episodes, load_refs, zero_fp_frontier,
|
||||
)
|
||||
from src.data.downloader import load_data # noqa: E402
|
||||
from src.live.venue_watch import PERSIST_HOURS, THRESHOLD_BPS # noqa: E402
|
||||
|
||||
ASSETS = ("BTC", "ETH")
|
||||
# insiemi di consenso da confrontare. 'LIVE-pre' e 'LIVE-post' sono le due configurazioni
|
||||
# REALI del sorvegliante, prima e dopo il 19/08.
|
||||
SETS = {
|
||||
"LIVE-pre (CB+BS)": ("coinbase", "bitstamp"),
|
||||
"CALIB (CB+BS+BF)": ("coinbase", "bitstamp", "bitfinex"),
|
||||
"LIVE-post (CB+BS+KR)": ("coinbase", "bitstamp", "kraken"),
|
||||
}
|
||||
|
||||
|
||||
def _ts(idx) -> pd.DatetimeIndex:
|
||||
return pd.to_datetime(idx, unit="ms", utc=True)
|
||||
|
||||
|
||||
def profondita(refs: dict) -> None:
|
||||
print("\n [1/5] PROFONDITA' REALE delle referenze (e' il fatto che struttura tutto il resto)")
|
||||
print(f"\n {'asset':<6}{'venue':<11}{'barre':>9} {'da':>12} {'a':>12}")
|
||||
for a in ASSETS:
|
||||
for eid in ("coinbase", "bitstamp", "bitfinex", "kraken"):
|
||||
s = refs.get((a, eid), pd.Series(dtype=float))
|
||||
if len(s):
|
||||
i = _ts(s.index)
|
||||
print(f" {a:<6}{eid:<11}{len(s):>9,} {str(i[0].date()):>12} {str(i[-1].date()):>12}")
|
||||
else:
|
||||
print(f" {a:<6}{eid:<11}{0:>9} {'—':>12} {'—':>12} <-- VUOTA")
|
||||
print("\n ⚠️ Da leggere prima di ogni numero sotto: su ETH `bitfinex` e' VUOTA e `kraken` copre")
|
||||
print(" un mese -> sulla storia lunga il consenso di ETH e' SEMPRE a due referenze; su BTC")
|
||||
print(" e' a tre solo fino al 2022-05 (fine della serie bitfinex).")
|
||||
|
||||
|
||||
def limite_kraken(refresh: bool) -> None:
|
||||
print("\n [2/5] IL LIMITE DI KRAKEN, verificato OGGI sulla rete (non creduto da un commento)")
|
||||
if not refresh:
|
||||
print(" saltato (--refresh-kraken per misurarlo); resta il dato in cache del 26/07: 702 barre")
|
||||
return
|
||||
d = load_data("BTC", "1h")
|
||||
s_ms, e_ms = int(d["timestamp"].iloc[0]), int(d["timestamp"].iloc[-1])
|
||||
s = _fetch_1h("kraken", "BTC/USD", s_ms, e_ms, 720)
|
||||
if not len(s):
|
||||
print(" kraken: nessuna barra (fetch fallito) — nessuna conclusione")
|
||||
return
|
||||
i = _ts(s.index)
|
||||
attese = (e_ms - s_ms) // 3_600_000
|
||||
print(f" chieste {attese:,} ore dal {str(_ts([s_ms])[0].date())}; ricevute {len(s):,} barre "
|
||||
f"({i[0].date()} -> {i[-1].date()}) = {len(s)/attese*100:.2f}% del richiesto")
|
||||
print(" -> il tetto ~700 candele e' CONFERMATO: la storia lunga con Kraken non e' ottenibile,")
|
||||
print(" e questo non e' un problema per il LIVE (gli servono le ultime ore), lo e' per la TARATURA.")
|
||||
|
||||
|
||||
def _frame(a: str, refs: dict, venues: tuple[str, ...]) -> pd.DataFrame:
|
||||
d = load_data(a, "1h")
|
||||
der = pd.Series(d["close"].astype(float).values, index=d["timestamp"].astype(int).values)
|
||||
rr = [refs[(a, e)] for e in venues if len(refs.get((a, e), []))]
|
||||
return dislocation(der, rr) if len(rr) >= 2 else pd.DataFrame()
|
||||
|
||||
|
||||
def confronto_appaiato(refs: dict) -> None:
|
||||
"""2 referenze vs 3, sulle STESSE ore. Mai due campioni diversi."""
|
||||
print("\n [3/5] CONFRONTO APPAIATO 2 referenze vs 3 — sulle stesse ore, mai due campioni diversi")
|
||||
for a in ASSETS:
|
||||
for nome3, v3 in (("BF (2018-2022)", ("coinbase", "bitstamp", "bitfinex")),
|
||||
("KR (1 mese)", ("coinbase", "bitstamp", "kraken"))):
|
||||
f2 = _frame(a, refs, ("coinbase", "bitstamp"))
|
||||
f3 = _frame(a, refs, v3)
|
||||
terza = v3[2]
|
||||
s3 = refs.get((a, terza), pd.Series(dtype=float))
|
||||
if not len(f3) or not len(s3):
|
||||
print(f"\n {a} + {nome3}: terza referenza assente -> confronto NON possibile")
|
||||
continue
|
||||
# ore in cui la TERZA referenza esiste davvero: e' li' che il confronto ha senso
|
||||
comune = f2.index.intersection(f3.index).intersection(s3.index)
|
||||
if len(comune) < 100:
|
||||
print(f"\n {a} + {nome3}: solo {len(comune)} ore in comune -> non si conclude")
|
||||
continue
|
||||
A, B = f2.loc[comune], f3.loc[comune]
|
||||
u2, u3 = A["usable"].mean(), B["usable"].mean()
|
||||
ent = A["usable"] & B["usable"]
|
||||
d_bps = (B.loc[ent, "bps"].abs() - A.loc[ent, "bps"].abs())
|
||||
print(f"\n {a} + {nome3} ore in comune {len(comune):,} "
|
||||
f"({_ts(comune)[0].date()} -> {_ts(comune)[-1].date()})")
|
||||
print(f" utilizzabili (non-BLIND) 2 ref {u2*100:6.2f}% 3 ref {u3*100:6.2f}%"
|
||||
f" -> {(u3-u2)*100:+.2f} pp")
|
||||
print(f" |scarto| mediano 2 ref {A.loc[ent,'bps'].abs().median():6.2f} "
|
||||
f"3 ref {B.loc[ent,'bps'].abs().median():6.2f} bps"
|
||||
f" -> differenza APPAIATA mediana {d_bps.median():+.2f} bps")
|
||||
print(f" |scarto| p99 2 ref {A.loc[ent,'bps'].abs().quantile(.99):6.2f} "
|
||||
f"3 ref {B.loc[ent,'bps'].abs().quantile(.99):6.2f} bps")
|
||||
for et, F in (("2 ref", A), ("3 ref", B)):
|
||||
ep = episodes(F["bps"], F["usable"], THRESHOLD_BPS, PERSIST_HOURS)
|
||||
print(f" episodi a ({THRESHOLD_BPS:.0f} bps, {PERSIST_HOURS}h) {et}: {len(ep)}")
|
||||
|
||||
|
||||
def frontiera(refs: dict) -> None:
|
||||
print(f"\n [4/5] FRONTIERA A ZERO FALSI ALLARMI, per insieme di referenze "
|
||||
f"(punto in produzione: {THRESHOLD_BPS:.0f} bps / {PERSIST_HOURS}h)")
|
||||
for nome, v in SETS.items():
|
||||
print(f"\n {nome}")
|
||||
for a in ASSETS:
|
||||
f = _frame(a, refs, v)
|
||||
if not len(f) or f["usable"].sum() < 100:
|
||||
print(f" {a}: campione insufficiente ({0 if not len(f) else int(f['usable'].sum())} ore utili)")
|
||||
continue
|
||||
i = _ts(f.index)
|
||||
front = dict((h, b) for b, h in zero_fp_frontier(f["bps"], f["usable"]))
|
||||
ep = episodes(f["bps"], f["usable"], THRESHOLD_BPS, PERSIST_HOURS)
|
||||
b4 = front.get(PERSIST_HOURS)
|
||||
print(f" {a}: {int(f['usable'].sum()):,} ore utili "
|
||||
f"({i[0].date()}->{i[-1].date()}, {f['usable'].mean()*100:.1f}% non-BLIND) "
|
||||
f"soglia minima a {PERSIST_HOURS}h = {b4 if b4 else 'nessuna'} bps "
|
||||
f"falsi allarmi a ({THRESHOLD_BPS:.0f},{PERSIST_HOURS}h) = {len(ep)}")
|
||||
|
||||
|
||||
def controllo_positivo(refs: dict) -> None:
|
||||
print("\n [5/5] CONTROLLO POSITIVO — Bitfinex 2018-19 come BERSAGLIO (consenso CB+BS)")
|
||||
print(" Un rilevatore tarato per non segnalare e' indistinguibile da uno rotto.")
|
||||
a = "BTC"
|
||||
tgt = refs.get((a, "bitfinex"), pd.Series(dtype=float))
|
||||
base = [refs[(a, e)] for e in ("coinbase", "bitstamp") if len(refs.get((a, e), []))]
|
||||
if not len(tgt) or len(base) < 2:
|
||||
print(" serie insufficienti -> controllo NON eseguito (e questo e' un fallimento, non un ok)")
|
||||
return
|
||||
f = dislocation(tgt, base)
|
||||
ep = episodes(f["bps"], f["usable"], THRESHOLD_BPS, PERSIST_HOURS)
|
||||
if not ep:
|
||||
print(" ✋ ZERO episodi su Bitfinex: il rilevatore NON vede un caso noto -> taratura da rivedere")
|
||||
return
|
||||
dur = sorted((e["hours"] for e in ep), reverse=True)
|
||||
pk = max(abs(e["peak_bps"]) for e in ep)
|
||||
print(f" {len(ep)} episodi · piu' lungo {dur[0]:,} ore · picco {pk:,.0f} bps "
|
||||
f"= {pk/THRESHOLD_BPS:.1f}x la soglia")
|
||||
print(f" durate delle prime 5: {dur[:5]}")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
refresh = "--refresh-kraken" in sys.argv
|
||||
print("=" * 100)
|
||||
print(" r0821 — TARATURA DEL TRIPWIRE DI VENUE, RI-MISURATA A TRE REFERENZE")
|
||||
print("=" * 100)
|
||||
print(f" regola in produzione: |scarto| > {THRESHOLD_BPS:.0f} bps persistente {PERSIST_HOURS}h "
|
||||
"a segno costante, consenso = MEDIANA delle referenze concordi (>=2).")
|
||||
refs = load_refs()
|
||||
profondita(refs)
|
||||
limite_kraken(refresh)
|
||||
confronto_appaiato(refs)
|
||||
frontiera(refs)
|
||||
controllo_positivo(refs)
|
||||
print("\n" + "=" * 100)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,8 +1,16 @@
|
||||
"""Test del tripwire di venue (src/live/venue_watch.py + scripts/research/r0726_venue_tripwire.py).
|
||||
|
||||
Il test che conta di piu' e' `test_controllo_positivo_*`: un rilevatore che non segnala mai nulla
|
||||
e' indistinguibile da uno rotto, e questo qui e' TARATO per non segnalare (zero falsi allarmi su
|
||||
8 anni). Senza un controllo positivo, "non e' mai scattato" non e' una buona notizia.
|
||||
e' indistinguibile da uno rotto, e questo qui e' TARATO per non segnalare. Senza un controllo
|
||||
positivo, "non e' mai scattato" non e' una buona notizia.
|
||||
|
||||
⚠️ CORREZIONE 2026-08-21 (`scripts/research/r0821_venue_refs.py`): la taratura NON e' "zero falsi
|
||||
allarmi in 8 anni" sull'insieme di referenze che gira in PRODUZIONE. Quel numero fu misurato con
|
||||
`bitfinex` nel consenso, che il sorvegliante live non ha mai avuto; sul consenso reale (Coinbase +
|
||||
Bitstamp, e da oggi + Kraken) i falsi allarmi in 8 anni sono **1**, il 2020-03-13 (crash COVID,
|
||||
4 ore a -418 bps di picco su BTC) — cioe' proprio l'evento che la memoria citava come esempio di
|
||||
cio' su cui NON scattava. Il punto (100 bps, 4h) resta invariato per economia, non per assenza di
|
||||
falsi allarmi: 1 ogni 8 anni costa ~0.031%/anno di equity attesa contro il 100% che evita.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -354,3 +362,57 @@ def test_la_tabella_dichiarata_combacia_col_venue_oggi():
|
||||
"BTC-PERPETUAL": {"tick_size": 0.5, "min_trade_amount": 10.0, "contract_size": 10.0},
|
||||
"ETH-PERPETUAL": {"tick_size": 0.05, "min_trade_amount": 1.0, "contract_size": 1.0},
|
||||
}) == []
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# MECCANICA scoperta il 2026-08-21 ri-misurando la taratura a tre referenze.
|
||||
# Sono i due fatti PURI che spiegano i numeri di r0821_venue_refs.py; i numeri
|
||||
# empirici stanno nel diario, riprodotti da quello script.
|
||||
# ===========================================================================
|
||||
def test_una_terza_referenza_non_puo_aumentare_le_ore_utilizzabili():
|
||||
"""Aggiungere una referenza puo' solo ALLARGARE lo spread max-min, mai stringerlo ->
|
||||
la quota di ore utilizzabili e' monotona NON crescente nel numero di referenze.
|
||||
|
||||
E' la ragione strutturale per cui "piu' referenze" non e' gratis: la modifica del 19/08
|
||||
sposta il sistema verso BLIND, che e' lo stato morbido (allerta di MENO, non di piu').
|
||||
"""
|
||||
base = [100.0, 100.4] # spread 40 bps: concordi
|
||||
ok, n, spread = dislocation_bps(100.2, base)
|
||||
assert ok is not None and n == 2 and spread < THRESHOLD_BPS
|
||||
|
||||
# una terza referenza lontana allarga lo spread oltre la soglia -> BLIND, non allarme
|
||||
fuori, n3, spread3 = dislocation_bps(100.2, base + [102.0])
|
||||
assert n3 == 3 and spread3 > spread, "lo spread deve essere monotono nel numero di referenze"
|
||||
assert fuori is None, "referenze in disaccordo -> si SCARTA il campione, non si allerta"
|
||||
|
||||
# e una terza referenza CONCORDE non toglie nulla: il caso Kraken misurato il 21/08
|
||||
dentro, n3b, spread3b = dislocation_bps(100.2, base + [100.2])
|
||||
assert dentro is not None and n3b == 3 and spread3b >= spread
|
||||
|
||||
|
||||
def test_una_sola_ora_BLIND_spezza_lo_streak_e_impedisce_l_allarme():
|
||||
"""⚠️ Il fatto che rende ambiguo un "zero falsi allarmi" misurato con piu' referenze.
|
||||
|
||||
Lo streak si AZZERA su un'ora BLIND (non si accumula evidenza su dati che non parlano).
|
||||
Quindi un insieme di referenze che manda in BLIND una sola ora dentro una finestra di
|
||||
dislocazione fa SPARIRE l'episodio senza che la dislocazione sia diminuita: e' esattamente
|
||||
cio' che e' successo al falso allarme del 2020-03-13, presente a 2 referenze e assente a 3
|
||||
perche' 1 delle 4 ore diventava BLIND (dislocazione mediana ancora -343 bps).
|
||||
|
||||
Morale: "zero falsi allarmi" puo' voler dire "piu' accurato" oppure "piu' cieco", e le due
|
||||
si distinguono solo guardando la quota di ore utilizzabili.
|
||||
"""
|
||||
st = AssetState()
|
||||
# 4 ore consecutive oltre soglia a segno costante -> ALERT alla quarta
|
||||
for _ in range(PERSIST_HOURS - 1):
|
||||
st, lvl = step(st, -400.0)
|
||||
assert lvl == "WATCH"
|
||||
st_alert, lvl = step(st, -400.0)
|
||||
assert lvl == "ALERT", "il caso di controllo deve scattare, altrimenti il test non prova nulla"
|
||||
|
||||
# stessa dislocazione, ma con UNA ora cieca in mezzo -> nessun allarme
|
||||
st2 = AssetState()
|
||||
for bps in (-400.0, -400.0, None, -400.0):
|
||||
st2, lvl2 = step(st2, bps)
|
||||
assert lvl2 != "ALERT"
|
||||
assert st2.streak_hours < PERSIST_HOURS, "l'ora BLIND deve azzerare lo streak"
|
||||
|
||||
Reference in New Issue
Block a user