Compare commits
97 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bebfa63213 | |||
| ae887a3d98 | |||
| d34b0a81d8 | |||
| cfb0e1e57f | |||
| 39adfbf267 | |||
| 81b495eb10 | |||
| 28a6d79e4f | |||
| ac6ad7f116 | |||
| ddcd3773a7 | |||
| 213dd12bab | |||
| 3af6e1f0c3 | |||
| 829ebc7f98 | |||
| 52a905d497 | |||
| 12291709ed | |||
| 8e75e9c9a7 | |||
| 13c136cf4b | |||
| e82f2ee163 | |||
| d80323ff59 | |||
| d59aea7a0a | |||
| 31ec767ff5 | |||
| 95cfb396e7 | |||
| 604fde689e | |||
| 841d4c78cd | |||
| 0e6cf17081 | |||
| dab8a88e1c | |||
| ed4f8fd39b | |||
| 8405fe2a7d | |||
| 7234625863 | |||
| 9ce924d97c | |||
| 7c72308852 | |||
| 65a7f0d8fc | |||
| f79fb54221 | |||
| 4725839041 | |||
| 5acc31e390 | |||
| dda5d41f38 | |||
| f0193f013d | |||
| 0c19c804d1 | |||
| 2bf4f9cceb | |||
| b8c2bba320 | |||
| ac21e1c97d | |||
| cfc42653cc | |||
| 1a7f670966 | |||
| e4efdaaa53 | |||
| b46041d11d | |||
| 4b04831352 | |||
| 8007158eb5 | |||
| 31f5915a41 | |||
| e42ab5369f | |||
| a6a49d9c65 | |||
| a4e942c8bb | |||
| 5b72a4b188 | |||
| a2b03aa2b6 | |||
| 37d1565a1e | |||
| f82f685528 | |||
| 79afe41ec2 | |||
| 8f2e6c5c2c | |||
| 0a2f780d3c | |||
| bb40e87d13 | |||
| 9f55652d99 | |||
| 95424f0755 | |||
| de83909db9 | |||
| 1279ea605a | |||
| 835e0c8666 | |||
| 861cc7fc27 | |||
| 2f701b8469 | |||
| bc43218a12 | |||
| d95fabf060 | |||
| 1c0549080c | |||
| ec8478308f | |||
| 05816c49f9 | |||
| 5bcf212923 | |||
| 8d391ed29a | |||
| 62025e7840 | |||
| 4705d71ceb | |||
| fd7595e819 | |||
| c8be83ef6e | |||
| 30286ea033 | |||
| 41f83b5dd8 | |||
| 7b8e35633d | |||
| f151316bdf | |||
| c250c04371 | |||
| a45794d2cb | |||
| 3cf884fa39 | |||
| 035bd086a2 | |||
| fc24ee5f52 | |||
| bb33df472f | |||
| 993d17556a | |||
| 2beb11b764 | |||
| 32e1649dcb | |||
| 418f523be7 | |||
| 21fe289383 | |||
| 801bd13f10 | |||
| c21a0d4058 | |||
| 31f82e1730 | |||
| cb40ab3e20 | |||
| 05a698a83e | |||
| a6b5756cc0 |
@@ -79,6 +79,12 @@ data/fee_watch/
|
||||
# battuta di cuore del collettore catena (stato runtime, come gli altri monitor)
|
||||
data/chain_collect/
|
||||
data/vrp_f_watch/
|
||||
# giri misurati del trigger di regime CC01 (letti da monitor_health, #12)
|
||||
data/cc01_regime/
|
||||
# giri misurati dei sorveglianti generalizzati da cc01 (#16), letti da monitor_health
|
||||
data/edge_watch/
|
||||
data/scale_watch/
|
||||
data/venue_news/
|
||||
|
||||
# log esecuzioni del book live (stato runtime, contiene fill/fee del conto reale)
|
||||
data/live/
|
||||
|
||||
@@ -17,8 +17,8 @@ chi la riapre deve battere il motivo, non ripetere la misura.
|
||||
| `docs/memory/40-produzione-e-deploy.md` | esecutore, tripwire, monitor, libro di bordo, PRIIPs/UCITS | tocchi cio' che gira con soldi veri |
|
||||
| `docs/memory/50-dati-e-feed.md` | difetti del dato trovati e riparati, catena opzioni | tocchi i feed |
|
||||
| `docs/memory/60-metodo-e-gate.md` | i gate codificati in `altlib.py` | valuti un candidato |
|
||||
| `docs/research/RESULTS-0822.md` | registro per filone §1-70 | vuoi il dettaglio di un filone |
|
||||
| `docs/diary/` (123 voci) | la sessione originale, per data | vuoi il contesto completo |
|
||||
| `docs/research/RESULTS-0822.md` | registro per filone §1-77 | vuoi il dettaglio di un filone |
|
||||
| `docs/diary/` (146 voci) | la sessione originale, per data | vuoi il contesto completo |
|
||||
|
||||
---
|
||||
|
||||
@@ -32,7 +32,13 @@ Documento di fondazione: `docs/diary/2026-06-19-deribit-history.md`.
|
||||
- Lo storico e' **ricostruito da Deribit mainnet e certificato**. Universo affidabile = **solo
|
||||
BTC/ETH** (tutti i TF); gli alt sono esclusi (illiquidi/divergenti/non certificabili).
|
||||
- Tutto il codice pre-reset e' archiviato in `Old/` (preservato in git, non cancellato).
|
||||
- **L'esecuzione e' ARMATA e LIVE** su Deribit mainnet dal 2026-06-20. Capitale reale **~$635**
|
||||
- 🚨 **Il README e' rimasto fermo al 2026-06-04 fino al 2026-09-02**, cioe' *quindici giorni prima*
|
||||
del reset: per 75 giorni la prima pagina del repo ha pubblicato la libreria pre-reset e i suoi
|
||||
numeri (PORT06 Sharpe 7,84/10,06, CAGR ~79%) come risultati correnti, mentre il progetto li aveva
|
||||
gia' dichiarati artefatti. Riscritto contro lo stato vero. **La lezione non e' "aggiornare il
|
||||
README": e' che un reset invalida anche i documenti che nessuno rilegge** — l'inventario di cosa
|
||||
cita numeri morti va fatto il giorno del reset, non quando qualcuno ci inciampa.
|
||||
- **L'esecuzione e' ARMATA e LIVE** su Deribit mainnet: **TP01 da solo dal 2026-06-20** (commit `4650aa7`), **il BOOK TP01+SKH01 dal 2026-06-23** (commit `db738bc`, prima lettura di equity 23/06 22:00Z — e' la data d'arming che il TWR e `config/live.json` usano). Capitale reale **~$4.495** dal 04/09 (era ~$635 fino al 25/08, ~$2.060 fino al 04/09)
|
||||
(NON i €2.000 nominali del paper trader).
|
||||
- Si riparte dalla ricerca di strategie NUOVE, su dati certi, con la metodologia della §8.
|
||||
|
||||
@@ -53,6 +59,32 @@ dal 2026-08-25; era `:07`). Due vincoli, entrambi misurati: fuori dai ~26s del m
|
||||
annunciati), che il `:07` beccava — 4 volte in 63 giorni. **GTAA01, XS01, VRP01, XSR01 NON sono
|
||||
nel book live.**
|
||||
|
||||
**OMBRA (non e' questo repo, ma gira sullo stesso conto).** Dal 2026-09-13 la crontab ha anche
|
||||
`44 * * * * /opt/AI-OS/automations/TradingBook/scripts/cron_ombra.sh`: il **book traslocato**
|
||||
(`Adriano/TradingBook`, stesso codice di questo repo al 13/09) calcola ogni ora il target e lo
|
||||
scrive in un registro, **senza inviare**. Finestra dichiarata fino al **30/09/2026**; confronto col
|
||||
`:47` di qui con `TradingBook/scripts/confronta_giornata.py`. Verificato il 16/09 (issue #11):
|
||||
- **cosa legge**: il conto Deribit tramite il gateway interno di TradingBook, con
|
||||
`TradingBook/secrets/deribit.json`, il cui `client_id` **coincide** (confronto per hash) con
|
||||
`DERIBIT_CLIENT_ID_LIVE` di cerbero-mcp, cioe' **la chiave con cui questo libro manda gli ordini**;
|
||||
il feed tramite `TradingBook/data/raw`, un **collegamento** a `data/raw` di qui (sola lettura).
|
||||
- **dove scrive**: solo nel suo albero, `logs/cron_ombra.log`, `data/live/ombra.jsonl` e
|
||||
`data/live/equity_seen.json`. Sui giri delle 18:17, 19:17 e 20:44 del 16/09 **nessun file** di
|
||||
`data/` o `logs/` di questo progetto e' cambiato: il watermark di qui non lo tocca (P16).
|
||||
- **perche' non invia**: `book_ombra.py` non contiene il percorso d'invio, e
|
||||
`TradingBook/tests/test_ombra_non_invia.py` lo verifica sul sorgente. ✅ **Dal 21/09 anche il
|
||||
PERMESSO** (TradingBook#19): il gateway chiede a Deribit un token con scope
|
||||
`account:read trade:read wallet:none block_trade:none block_rfq:none custody:none` e rifiuta il token
|
||||
se lo scope concesso contiene un `:read_write`. Misurato il 21/09: senza scope la chiave dava
|
||||
**tutto in read_write, anche `wallet` e `custody`**; con lo scope ridotto Deribit concede
|
||||
`trade:read account:read` e risponde `13021 forbidden` a un `private/cancel`. 🚨 **Il segreto resta
|
||||
quello pieno**: chi legge `TradingBook/secrets/deribit.json` puo' chiedere un token senza scope. Una
|
||||
chiave Deribit di **sola lettura** per l'ombra la crea solo l'operatore.
|
||||
- **orario**: `:44` con `timeout -k 10 150`, quindi finisce entro le `:46:40`, prima del giro di qui.
|
||||
Era `:17` fino al 16/09 e cadeva nello slot di release del martedi' (15/09 09:17: manutenzione
|
||||
11051, due BUY calcolati sul paper capital nel registro d'ombra); da TradingBook#16 un conto o una
|
||||
posizione illeggibile danno un giro **saltato**, come qui danno lo stop.
|
||||
|
||||
**RICERCA (paper).** Portafoglio a 5 sleeve `src/portfolio/sleeves.active_sleeves`:
|
||||
TP01 33 / XS01 15 / VRP01 12 / SKH01 20 / GTAA01 20. **Non e' il book live** — quasi tutti i numeri
|
||||
di portafoglio del progetto sono su questa serie, non su quella che gira.
|
||||
@@ -67,21 +99,59 @@ lorda massima 1,0x**, e nessun livello di capitale cambia il profilo di rischio
|
||||
`min_order_usd` 5 · `disaster_sl_pct` 0.30 on-book sulla posizione **netta** · staleness-gate feed
|
||||
2 giorni (blocca) · `skh_feed_max_age_min` 30 (allerta, **non** blocca) · alert Telegram.
|
||||
|
||||
📌 **Dal 2026-08-26 l'equity del book e' il TOTALE cross-collateral** (USDC + USDE valutato
|
||||
all'indice pubblico `usde_usdc`): il conto tiene **500 USDE** (test di eligibilita' reward, verdetto
|
||||
29/08 — GATE USDE-01 in §4). Autorita' dei parametri: sezione `usde` di `config/live.json`
|
||||
(haircut 10%, tetto ALLERTA quota 50%, soglie depeg 0,99/0,95 coi criteri in `_nota_usde`), letta
|
||||
📌 **Modello di margine: `Cross: Standard Margin` (X:SM) dal 2026-08-31**, cambiato dall'operatore. Prima era **Segregated** (S:SM), e sotto quello l'USDE **non faceva margine**: e' il motivo per cui la misura dell'haircut trovava $0,0000 accantonati — si cercava un parametro non in vigore. Il passaggio e' verificabile dal gateway: `available_funds` USDC e' passato da ~$1.400 a **$2.011,09**, contro $2.010,90 attesi con haircut 5% (scarto **$0,19** — seconda conferma indipendente del 5%). ⚠️ **Cosa ha comprato, misurato**: **$611** di margine utilizzabile — inutile a 0,28x di leva, serve solo sopra ~1,4x che non e' autorizzata — e **+11 USDE (~$11)** di capienza sul tetto. **Cosa e' costato**: sotto segregato l'USDE era **ring-fenced** dalle perdite del libro (solo il silo USDC rispondeva delle posizioni); sotto cross **l'intero conto risponde**. Non morde oggi (perdita massima plausibile ~$617 col disaster-SL, dentro il solo USDC), ma il rischio strutturale e' cambiato verso: **se un giorno il cross non servisse piu', tornare a S:SM ri-recinta l'USDE**. 📌 **X:PM valutato e SCARTATO il 31/08 senza provarlo** — la risposta era gia' nella pagina margini: **Available Balance $1.935,14 contro $2.011,73** di X:SM (−$76,59) e margini piu' alti su entrambe le misure, **MM 3,43% contro 0,37% (~9×)**. Il PM e' basato su scenari e premia il rischio che si **compensa**: due long direzionali nudi sono il suo caso peggiore, e paga lo scenario di stress invece dell'aliquota piatta. **Cosa lo riapre**: il deploy di VRP01 o di un altro sleeve di opzioni — allora il rischio inizia a compensarsi e il confronto cambia di segno. ✅ **Confermato il 31/08 a X:SM attivo** e su misure pulite: la KB da' l'USDe al 5% sotto entrambi i modelli, quindi l'haircut si cancella e l'inversa della riga CROSS da' il margine di POSIZIONE — **$88,23 sotto PM contro $11,64 sotto SM, 7,6×**, e 9,3× la MM. La riga X:SM era una **proiezione di un modello inattivo** e oggi si riproduce al centesimo ⇒ anche la riga X:PM era affidabile: decidere senza provare era corretto. 🚨 **L'«IM %» della pagina margini NON e' il margine delle posizioni**: e' `(margin_balance − available)/margin_balance`, e da noi e' **74% haircut** (e' salita da 2,13% a 2,15% *perche' abbiamo comprato USDE*, non perche' sia salito il rischio). La **MM** e' pulita e ha base diversa. 📌 **La pagina margini non serve piu' come schermata**: la riga CROSS **e'** `available_funds` del gateway (scarto $0,01) e `r0831_margini_conto.live()` la ricostruisce, haircut compreso. 📌 **L'equity che il book somma e' la RICCHEZZA, non la capacita' di margine** — giusta per accorgersi di un'uscita di fondi (il difetto del 26/08), sbagliata se la si usasse per dire quanto margine c'e'.
|
||||
|
||||
📌 **Dal 2026-08-26 l'equity del book somma USDC + USDE** (valutato
|
||||
all'indice pubblico `usde_usdc`): il conto tiene **2.851,9 USDE, quota 64,0%** dal 10/09 14:16Z (allineato al bersaglio di `cuscino_watch` su ordine dell'operatore: 238 USDE venduti in 3 ordini a 1,0000, slack +$266 = OK; era 3.088,7 / 68,7% dal 06/09) (**IDONEO** dal 28/08;
|
||||
500 → 644 il 30/08 → **3.088,7 il 06/09** su autorizzazione dell'operatore *«usa piu' USDE»* e *«cerca la %
|
||||
massima raggiungibile»*, dopo che il versamento di **$2.410,14 del 04/09** aveva dimezzato la quota a 14,6%).
|
||||
🚨 **Il TETTO del venue del 30-31/08 (~31,2-31,9%) il 06/09 NON C'ERA**: sonda a saldo crescente
|
||||
(`r0906_usde_tetto_sonda.py`, passi 100→20→4→1, stesso metodo del 31/08) da 31,8% a **68,7% senza un solo
|
||||
rifiuto**, 2.434 USDE in 39 ordini @ 1,0005 (~$1,2 di spread); si e' fermata al **cuscino di regolamento**
|
||||
(70% = `2 × 0,5 × disaster_sl_pct`, derivato da config), cioe' al NOSTRO limite, non al venue. **«Il 70% non
|
||||
e' raggiungibile ne' ora ne' mai» (31/08) e' falsificato**; il tetto e' **sparito, non scalato** — e alle
|
||||
20:58 dello stesso giorno avevo scritto qui «terza conferma della frazione» sulla base di *nessun rifiuto
|
||||
fino al 31,8%*, che dimostra ≥, non =. Non spiegato (D5): puo' tornare. `venue_cap_frac` → **null**,
|
||||
`quota_max_frac` → **0,85** (il valore che l'operatore aveva scelto il 30/08 per questo scenario).
|
||||
✅ **Il cuscino USDC e' sorvegliato dal 10/09** (`cuscino_watch`, §5.17): una perdita del libro (che si regola in USDC)
|
||||
fa salire la quota da sola, e sotto zero di slack il sorvegliante vende USDE; prima nessuno lo guardava — a −$58 di USDC
|
||||
il cuscino era gia' scoperto. GATE USDE-01 in §4). ⚠️ **Ogni versamento abbassa la quota**: dopo un bonifico va riportata al bersaglio, e
|
||||
**dal 10/09 lo fa `cuscino_watch` da solo** (stato ECCEDENTE: slack > 40% del cuscino ⇒ ricompra USDE fino a quota **0,64**, issue #7); prima si rilanciava `usde_convert --quota 0.70` a mano, e il 04/09 nessuno l'aveva fatto. Autorita' dei parametri: sezione `usde` di `config/live.json`
|
||||
(haircut **5%** — ✅ divergenza chiusa il 31/08, la config diceva 10% ed era sbagliato (`r0831_margini_conto.py`); oggi comunque **non si applica**, vedi sopra — tetto ALLERTA `quota_max_frac` **0,85** (era 50% fino al 06/09; 0,50 resta solo come default di codice in `src/live/usde.py`, la config lo sovrascrive), soglie depeg 0,99/0,95 coi criteri in `_nota_usde`), letta
|
||||
da `src/live/usde.py` — shadow (sizing orario) e `usde_watch` (sorveglianza 12:35 UTC, cron
|
||||
proprio, sotto `monitor_health`) derivano entrambi da li'. ⚠️ Il difetto d'origine: `shadow._equity`
|
||||
leggeva SOLO il conto USDC — la prima conversione avrebbe causato un falso "USCITA DI FONDI −24%"
|
||||
e vendite indesiderate. Chi aggiunge una valuta di collaterale DEVE passare da `usde.py`/shadow.
|
||||
|
||||
🚨 **Una capacita' PUBBLICATA dal venue non e' una capacita' del CONTO: si verifica sul conto, prima che entri in un piano — e costa un ordine da $1.** Due affermazioni di Deribit smentite dal conto in due giorni: la **APR USDC 3,40%** (l'Italia e' nella lista delle giurisdizioni escluse → i nostri $1.412 di USDC rendono **zero**) e «**all Deribit users are permitted to buy and sell BUIDL**» (11 ordini rifiutati, fino a **1 unita' a limite 1,0100 contro ask 1,0004 con 10.730 di profondita'**, tenendo zero BUIDL e $1.412 disponibili → **GATE BUIDL-01 chiuso: NON ENTRABILE**, costo $0, diario `2026-08-31-buidl-test-eligibilita`). ⇒ **L'USDE e' l'unico collaterale a rendimento che il conto puo' usare**, ed e' al suo tetto: le altre tre vie sono chiuse per **giurisdizione** (USDC), **permesso** (BUIDL) e perche' **non e' un dollaro** (stETH 2,23%, che e' ETH: coprirlo e' CC01, gia' parcheggiato a scala ~$20k+). 📌 **`not_enough_funds_in_currency` e' il messaggio GENERICO di Deribit per "non puoi acquisire altra di questa valuta"** — permesso o tetto, mai i fondi. Chi lo incontra salti subito a *questa valuta la posso acquisire, e fino a quanto?* invece di inseguire taglia, cadenza e prezzo come il 30/08.
|
||||
|
||||
⚠️ **Il nome "disaster-SL −30%" inganna:** e' **rotolante e simmetrico**, non un massimo di perdita
|
||||
per trade. Su cicli a ingresso proprio lascia passare perdite oltre −30% senza scattare, e il
|
||||
peggior DD dall'ingresso misurato e' **−60,6% BTC / −61,5% ETH**. Dettaglio in `40-produzione`.
|
||||
|
||||
📌 **Soldi fermi (01-02/09):** on-venue **chiuso con l'elenco intero** — Deribit remunera 4 valute su 50,
|
||||
le quattro gia' chiuse il 31/08. L'USDC sul conto **non e' fermo** (base di sizing + cuscino). Il fermo
|
||||
vero sta fuori (~€6k in XEON = lo split-cassa): le opzioni sicure valgono **+€52-62/anno** (BOT 12,5% /
|
||||
conto deposito 26%), sUSDe rende 2x ma e' **lo stesso emittente dell'USDE** e distrugge lo split.
|
||||
✅ **L'operatore ha scelto di VERSARE** (piano 27/07: **€5.000 dentro, ~€1.000 fuori** — la protezione
|
||||
venue satura a qualunque quota > 0). ✅ **VERSATO il 04/09: $2.414,68 di USDC** (dal balance, fra 13:42Z e
|
||||
14:42Z), **dichiarato il 10/09** in `movimenti_dichiarati.jsonl` — e **l'operatore ha detto che e' l'INTERO
|
||||
versamento previsto: il piano €5.000 e' chiuso qui**, non resta nulla «in attesa». Per sei giorni era stato solo
|
||||
*rilevato* (+$2.410,14 dal salto di equity, col mercato dell'ora dentro): la regola di §2 dice il giorno stesso.
|
||||
`r0901d_soldi_fermi.py`.
|
||||
|
||||
**Candidati in forward-monitor** (nessuno nel book): XSR01 · DVOLSPREAD · STATARB · PREVDAY.
|
||||
|
||||
📌 **Revisione SETTIMANALE in sola lettura (dal 2026-09-09).** Lunedi' 06:15 UTC `scripts/cron_review.sh` →
|
||||
`scripts/live/revisione.py` → `src/live/revisione.py`: un revisore **diverso dall'autore** (`claude-fable-5-1`)
|
||||
rilegge CLAUDE.md, config, crontab, commit, `monitor_health`, 7 giorni di giornale e di diari, stato dei sorveglianti
|
||||
(~195k caratteri), e scrive `docs/revisioni/<data>.md` **firmato** + la Sintesi su Telegram. **Propone, non esegue:**
|
||||
l'unico file che scrive e' il rapporto; le decisioni vincolanti (§3) non le ripropone; un'idea «nuova» la propone solo
|
||||
citando la memoria che ha ucciso la simile. Il rapporto e' opinione di un lettore fallibile (P13): guardia sui numeri
|
||||
come `analista`. Un modello muto produce un rapporto che lo dice, e un 🚨 (P5). Deciso dall'operatore il 09/09 sera
|
||||
(«fai») dopo aver **scartato** autoregolazione dei parametri e generazione automatica di strategie: la ricerca non e'
|
||||
il vincolo binding dal 26/07, e un generatore e' una grid search senza fondo (M3).
|
||||
|
||||
---
|
||||
|
||||
## 2. I numeri da citare — e quelli da NON citare
|
||||
@@ -97,11 +167,12 @@ sono all'ancora fortunata; le stime oneste sono la **mediana della banda d'ancor
|
||||
| TP01 hold-out | 0,31 | **~+0,05** — il valore di TP01 e' il **taglio del DD ~6× vs buy&hold**, non il ritorno |
|
||||
| XS01 standalone | 1,50 / 1,71 / DD 11% | ensemble di fase **1,25 / 1,31 / 10,9%**; maxDD fuori campione **21%** |
|
||||
| SKH01 | i numeri di ammissione | de-luckato e' **il meno affidabile dei cinque** (l'ancora regala 2/3 del FULL) |
|
||||
| VRP01 | f = 1,00 | **f = 0,73** misurato sulle quote reali (IC95 [0,698; 0,780]) |
|
||||
| VRP01 | f = 1,00 | **f = 0,73** misurato sulle quote reali (IC95 [0,698; 0,780], 30/07); **0,71 [0,680; 0,756]** rigirando lo stesso `r0730` con `cblib` causale (09/09 sera, n=27, 0/27 ≥ 1) |
|
||||
| muro capitale-rendita | $272k (lordo) | **$313k** al netto di fisco **e** funding — banda **[$187k – $1,14M]** |
|
||||
| P(≥50 €/g), canale funded, 36 mesi | 42% | **2,6%** [1,5 – 4,7], P(zero) 40,3% |
|
||||
| XSR01 | Sharpe 1,82 (e' una **terza** lente, divisore fisso 50) | **1,79** alla scoperta / **1,56-1,63** a oggi — lente dei gate, sole barre chiuse. Citare sempre la coppia (lente, ultima barra chiusa) |
|
||||
| soffitto direzionale BTC/ETH | ~1,3 | **~1,15** col funding dentro |
|
||||
| performance del libro LIVE | «+243%» di equity (e' **96,3% un bonifico**) · e **+11,98%** (lettura 06/09 20:47Z, PRIMA della dichiarazione del versamento di prova) | **TWR +6,66%** spezzato sui TRE movimenti in QUATTRO tratti — +8,14% fino al 25/08 08:00, −0,62% / −0,12% / **−0,63%** dopo (lettura 10/09 13:04Z, dopo la dichiarazione del versamento del 04/09 a **$2.414,68** dal balance); trading da arming **+$13,82**. Era +7,83% / +$62,45 il 06/09: la coda e' la marcatura delle due long dal picco del 06/09, non trade chiusi. Dal 02/09 lo stampa `trades_db.py --report` (debito 14 chiuso). 🚨 **Il 06/09 l'operatore ha dichiarato un versamento di PROVA di €25 il 25/08 alle 08:00Z**, ~$24,9: +3,3% su $647, **sotto la soglia del 10%** del rilevatore, quindi per 12 giorni e' stato contato come trading (un terzo del «trading da arming» e 4 punti di TWR). Ora sta in `data/live/movimenti_dichiarati.jsonl`, letto da `journal.movimenti_capitale` (fonte `dichiarato`, importo dell'operatore, il mercato dell'ora resta nel rendimento). **Regola: ogni versamento, anche di prova, va dichiarato li' il giorno stesso** — il rilevatore vede solo i salti ≥10% |
|
||||
|
||||
📌 **Il libro a k=1 rende MENO dell'S&P 500** (15,19% contro 17,40%, stessa finestra): il vantaggio
|
||||
sta nello **Sharpe** (1,35 vs 0,89), e **senza leva non si converte in rendimento**. A iso-rischio
|
||||
@@ -119,6 +190,23 @@ tutto il drift del libro). A 10 anni i bonifici fanno il **59%** del risultato.
|
||||
un tetto di 1,0x. **Il cap non ha mai morso: il vincolo e' il segnale.** Riempire quel vuoto con
|
||||
l'ETF e' stato **misurato e SCARTATO** il 25/08 (`20-ondate`).
|
||||
|
||||
📌 **Il libro nei crolli (misurato 09/09, `r0909_libro_nei_crolli.py`): nel GIORNO del crollo PERDE**
|
||||
(−0,48%/g sui 160 giorni ≤ −5%, positivo nel 18%); per finestra di 20 giorni e' immune-o-positivo e
|
||||
**dal 2022 chiude > 0 in 8/8 peggiori finestre**, tutto dalla gamba **short di SKH01** (108% dei
|
||||
guadagni; marcata all'USCITA del trade, quindi arriva *dopo* il giorno). Le 2 finestre perse (2019,
|
||||
maggio 2021) sono TP01 long dentro un trend non ancora girato. Beta +0,0769 (riproduce §46). ⚠️ Un
|
||||
solo aggettivo ("guadagna"/"immune") e' l'ordine delle regole: la prima stesura diceva 8/12 GUADAGNA,
|
||||
con le classi nell'ordine dichiarato sono 4 + 6 immune. **La leva che aumenterebbe il guadagno e' il
|
||||
peso di SKH01** (37,5/62,5 batte 12/12 episodi ma maxDD 12,1% vs 9,4%): vincolato (§3), gate
|
||||
`weights_tilt_null` fallisce su una condizione **in-sample** (delta −0,0026, 26/07; cache segnali ferma
|
||||
al 25/07). **Crollo CATTURATO a quote vere (1-5/06/2026, archivio bite, `r0909_crash_catturato.py`)**:
|
||||
f_net del put credit spread **0,76 [0,71-0,86] = 0,712 del rally** (rimisurato la sera del 09/09 con spot e DVOL
|
||||
causali, debito 18; era 0,74 = 0,714); put δ−0,10 costa **1,98×** il modello all'ask ma paga come il modello nel
|
||||
crollo (0,99-1,04); f al peggior MTM 1,08 (il 2,58 della prima stesura era un
|
||||
artefatto di quote oltre la larghezza). **NON e' il f di stress della regola del 19/06**: crollo a vol
|
||||
BASSA (picco DVOL al 29°/46° percentile), gate IV-rank di VRP01 CHIUSO, famiglia INVERSE, 4 strutture.
|
||||
Diario `2026-09-09-crolli-opzioni-monete.md`.
|
||||
|
||||
🚨 **Tre baseline diverse girano sotto lo stesso nome "libro 75/25"**, e lo spread fra loro (0,12 di
|
||||
Sharpe, 1,7pp di maxDD) e' **piu' grande di quasi tutti gli effetti che il progetto misura**:
|
||||
`hourly` **1,683 / 11,22%** (la lente di **ogni muro e ogni traiettoria**) · `canonical` 1,800 / 9,56%
|
||||
@@ -148,18 +236,38 @@ CLAUDE.md", la risposta e' la colonna *cosa la riapre*.
|
||||
| decisione | data | non si ri-discute | cosa la riapre |
|
||||
|---|---|---|---|
|
||||
| **100% Deribit fino a $20k** — accettato P(perso tutto) 10/18/34/64% a p=0,5/1/2/5% invece di 0/0/4/27% | 26/07 | la soglia $3k | **$20k**, o un cambio di piano, o `p` che diventa stimabile invece che assunto |
|
||||
| **SOL escluso** — universo direzionale resta BTC/ETH (presidiato da un test) | 22/08 | SOL su un book a 2 gambe con questi dati | ~3 anni di storia SOL certificata (**non prima del 2028**), o un meccanismo che non sia TP01/SKH01 congelati |
|
||||
| **Niente short-vol da modello in deploy** — e niente long-vol scalp da modello | 19/06 | — | un `f` di stress reale misurato su un crash catturato |
|
||||
| **SOL escluso** — universo direzionale resta BTC/ETH (presidiato da un test) | 22/08 | SOL su un book a 2 gambe con questi dati | ~3 anni di storia SOL certificata (**non prima del 2028**), o un meccanismo che non sia TP01/SKH01 congelati. **XRP misurato il 09/09 con lo stesso harness: diluisce uguale** (hold-out −0,169 in 0/24, un anno buono, 5m flat 27-61%); su Deribit i perp liquidi (≥$10M/g) sono SOLO BTC/ETH/XRP/SOL — `r0909_deribit_universo.py` |
|
||||
| **Niente short-vol da modello in deploy** — e niente long-vol scalp da modello | 19/06 | — | un `f` di stress reale misurato su un crash catturato **con IV-rank > 0,30 e sulla famiglia USDC-lineare** — il crollo del 1-5/06/2026 (misurato 09/09) e' catturato ma a vol bassa, sotto il gate e inverse: soddisfa la forma, non la sostanza |
|
||||
| **Non de-esporre il weekend** (porta il 38% del gross di TP01 nel 31% del tempo) | 17/07 | — | ogni proposta "risk-off weekend" parte **REFUTED** salvo null de-levering superato |
|
||||
| **Peso 75/25 confermato 3 volte** (24/07, 26/07, 24/07-follow-up) | 26/07 | — | `weights_tilt_null` superato, che finora **fallisce** |
|
||||
| **Fee Deribit: ≤5 bps/lato → non si tocca nulla; >10 bps → rivedere il peso SKH01** (regola decisa *prima* di vedere il numero) | 26/07 | — | il sorvegliante `fee_watch` allerta da solo |
|
||||
| **Gradino di leva NON autorizzato oggi.** 1,25x autorizzabile *a condizione*; **1,50x BOCCIATO** (peggior giorno strutturale 21,48% > 20%); **k max difendibile 1,40** | 23/08 | il 1,50x | costruire prima una **chiave di scala esplicita** in config **e** rifare `r0726_fee_sensitivity` |
|
||||
| **GTAA01: decisione tenere/bloccare RINVIATA a $15k di book** — sotto quella soglia lo sleeve **non e' deployabile** (`GTAA_MIN_CAPITAL` $3.000 allocati al peso 20%), quindi finora e' manutenzione senza beneficio incassabile. Contributo misurato a iso-rischio **+0,095 / +0,124** di Sharpe secondo la fase: **reale e sempre positivo**, e delle stesse dimensioni dello spread fra le baseline (0,12) | 28/08 | «GTAA01 non contribuisce» — la misura dice il contrario | **il book a $15k**, che il giornale sorveglia da solo (`journal.SOGLIE_CAPITALE`) |
|
||||
| **Gradino di leva NON autorizzato oggi.** 1,25x autorizzabile *a condizione*; **1,50x BOCCIATO** (peggior giorno strutturale 21,48% > 20%); **k max difendibile 1,40** | 23/08 | il 1,50x | ✅ la **chiave di scala e' costruita** (01/09, inerte a 1,00): restano **GATE SCALA-01** (§4) e il rifacimento di `r0726_fee_sensitivity` al nuovo lordo — la sua conclusione *"liquidation fee 1% irrilevante"* era condizionata a lordo ≤1x, a 1,25x costerebbe **1,25%** |
|
||||
| **Quota USDE = 0,64, DERIVATA dal cuscino, con isteresi automatica** — bersaglio unico `1 − frazione_cuscino × (1 + cuscino_margine_frac 0,20)`; vende sotto zero di slack, ricompra sopra il 40% del cuscino (`cuscino_watch`, cron :53). Sostituisce il **0,70 del 30/08**, che lasciava slack zero per costruzione. Costa ~$10/anno di resa rispetto a 0,70; accettato | 10/09 | «portare la quota al massimo» — il massimo e' 0,70 e vende alla prima ora in perdita | un cambio di `cuscino_margine_frac` in config (0,10 ⇒ 0,67, al prezzo di un ⚠️ dopo ogni vendita), o un cambio di `disaster_sl_pct`/`WEIGHT` che sposta il cuscino stesso |
|
||||
|
||||
⚠️ **La regola *"ogni cambio di scala passa dal cap di config, non da `target_vol`"* NON E'
|
||||
IMPLEMENTABILE COME SCRITTA:** verificato che in `config/live.json` **non esiste una chiave di scala**
|
||||
— servirebbe toccare `WEIGHT`/`W_TP01`/`W_SKH` in `src/live/book.py`, cioe' codice su un percorso con
|
||||
soldi veri. Specifica pronta in `docs/research/SPEC-scale-key.md` (`GATE SCALA-01`, 9 condizioni, con
|
||||
il **tetto sul PRODOTTO** `n_asset · frac · scala · disaster_sl_pct ≤ 0,50` — non sulla singola chiave).
|
||||
✅ **La chiave di scala ESISTE dal 2026-09-01** (era il debito che rendeva la regola *"ogni cambio di
|
||||
scala passa dal cap di config"* non implementabile). `book_scale_k`, letta da `src/live/book._scala`
|
||||
e applicata **DOPO il clamp** in `book_net_target` — l'ordine non e' di comodo: applicandola PRIMA il
|
||||
cap se la mangerebbe proprio nei giorni di massima convinzione (k_eff tornerebbe a 1,00 a ogni k),
|
||||
cioe' sarebbe un cambio di **FORMA** travestito da cambio di taglia, che la curva `g(k)` con cui il
|
||||
gradino viene autorizzato **non descrive**. 🚨 **La chiave e' INERTE: assente = 1,00 = il libro di
|
||||
sempre, bit-exact** (T7, `max|diff| = 0.0`) — `config/live.json` **non e' stato toccato**. Guardie:
|
||||
`LEVA_LORDA_MAX` 1,25 e `SCALA_LADDER` (1,00 · 1,25) sono **costanti di CODICE, non di config** (un
|
||||
tetto nello stesso file della chiave sarebbe un lucchetto con la chiave attaccata: il gradino a 1,50
|
||||
richiede una modifica di codice, quindi una review); il tetto e' **sul PRODOTTO**
|
||||
`n_asset · frac · scala` e l'invariante del disaster-SL e' `n_asset · frac · scala · disaster_sl_pct
|
||||
≤ 0,50` (la guardia che morde per **prima**: da' scala ≤ 1,67x e scatta se qualcuno allarga lo stop —
|
||||
🚨 e **`disaster_sl_pct` NON si sostituisce con la distanza di un pavimento di put**: darebbe 2,28x
|
||||
sulla carta, ma la put limita UNA finestra e i drawdown di BTC sono grind di 290-818 giorni, su 8
|
||||
finestre −33,6% → 77% dell'equity a 2,28x. Misurato in §72, scritto qui prima che sia comodo);
|
||||
una scala fuori scaletta o fuori tetto **NON viene tagliata** — `book_execute` si ferma, non invia e
|
||||
allerta (`ScalaNonAutorizzata`); e **la scala vive solo sul percorso fidato** (equity reale
|
||||
illeggibile ⇒ 1,00, cosi' "il fallback non e' piu' permissivo" e' vero per costruzione).
|
||||
Sorvegliante `src/live/scale_watch.py` (giornaliero in `cron_daily`, 3 domande / 3 azioni / 3 stati,
|
||||
una allerta per streak, marcatore scritto solo dopo invio riuscito), giornale append-only
|
||||
`data/live/scale_history.jsonl`. Test **T1-T11 in `tests/test_book_scale.py` (18, tutti verdi)**.
|
||||
📌 **Cio' che il sorvegliante NON puo' fare: impedire la modifica** — la rende visibile entro 24h e
|
||||
attribuibile. Come `venue_watch`: non protegge il saldo, compra tempo.
|
||||
|
||||
---
|
||||
|
||||
@@ -170,12 +278,15 @@ sull'hold-out; cambiarne le soglie guardando l'esito, pure.
|
||||
|
||||
| gate | data | criterio | stato |
|
||||
|---|---|---|---|
|
||||
| **USDE-01 (eligibilita' reward)** | 2026-08-29 | ≥1 reward entro il 29/08 (rilevati da `usde_watch` per delta di equity USDE al netto dei trade spot) → IDONEO e si apre la decisione di QUOTA (dell'operatore; tetto allerta 50%, mai 100% — R1 emittente non recuperabile); zero → si riconverte e la pista si chiude | ✅ pre-registrato il 26/08 PRIMA dell'esito (diario `2026-08-26-usde-analisi`); 500 USDE in conto dalle 13:04Z, baseline watch 14:17Z, verdetto applicato dal cron 12:35 |
|
||||
| **STATARB** | 2026-09-27 | soglie 24/07 (non toccate) + diagnostica statica sempre-short | ✅ monitor riparato e rigenerato il 26/08: la serie vera da' Sharpe **−1,61** a oggi → coi criteri invariati punta al **RITIRO**. Si legge il 27/09, non prima |
|
||||
| **USDE-01 (eligibilita' reward)** | 2026-08-29 | ≥1 reward entro il 29/08 → IDONEO e si apre la decisione di QUOTA (dell'operatore; tetto allerta 50%, mai 100% — R1 emittente non recuperabile); zero → si riconverte e la pista si chiude | ✅ **CHIUSO IDONEO** il 2026-08-28 12:35Z: primo reward **+0,052055 USDE** rilevato per delta al netto dei trade, coi criteri pre-registrati il 26/08 PRIMA dell'esito. **QUOTA DECISA il 30/08** (l'operatore ha anticipato il rinvio: *"porta in usde tutto il capitale che non viene usato"*): bersaglio **70%**, `usde.quota_target` in config (📌 **dal 10/09 la quota decisa e' 0,64, derivata dalle bande del cuscino: §3 e §5.17**). Tasso a 4 finestre (30/08): **3,26% annuo, banda bootstrap [1,13% – 4,51%]** — 3/4 hanno pagato (0,00 · 3,80 · 4,51 · 4,51%), e la banda larga 3,4 punti **e' il risultato**: a n=4 il tasso non e' misurato. `usde_watch.rendimento()` lo stampa **col suo n**; `quota_da_decidere()` lo chiede ogni giorno da lunedi'. 🚨 **L'EV non puo' scegliere la quota** — e' lineare in q, quindi il suo argmax e' sempre un angolo (0% o 100%): ogni quota interna nasce da un criterio sulla **coda**, dichiarato non ottimizzato (M8). Il resto e' misurato (`r0830_usde_quota.py`, diario 30/08): **hazard di pareggio = l'APR** (EV-positiva sse Ethena sta sotto il 3,3%/anno, numero **assunto** come il `p` di Deribit) · l'haircut **non morde a nessuna quota** fino al 90%, nemmeno a leva 1,50x con USDE marcato a 0,95 (29× di cuscino): **il vincolo non e' il margine** · la resa di oggi vale **$16/anno**, meno di UN mese di versamento ⇒ la leva binding resta il **bonifico** · gia' a 24,2% l'evento emittente vale **3,1× il maxDD dell'intero libro**. 📌 **Aspettare costa $1,33**: alzare a 50% fra 4 settimane invece che subito lascia sul tavolo $1,33 di resa (conversione reversibile a ~6 bps) e porta il campione da **4 a 32 finestre**, stringendo la banda ~2,8×. 🚨 **DUE FATTI DEL 30/08 CHE CAMBIANO IL GATE** (diario `2026-08-30-usde-quota-tetto-venue`). **(a) TETTO DEL VENUE ≈31-32% dell'equity — e' una FRAZIONE, non un livello.** 🚨 **SUPERATO IL 06/09: il
|
||||
tetto non c'era piu'** — quota portata al **68,7%** con 39 ordini senza rifiuti, fermata dal cuscino di
|
||||
regolamento (§1). Quanto segue e' la misura del 30-31/08, vera allora, tenuta perche' il tetto puo' tornare. Ogni acquisto oltre il tetto e' rifiutato con `not_enough_funds_in_currency` pur avendo **$1.400 disponibili**: messaggio **fuorviante**, che ha fatto inseguire tre ipotesi sbagliate (taglia, cadenza, prezzo). Che sia un tetto e non un problema d'ordine e' provato a saldo neutro (BUY rifiutato → SELL OK → BUY riaccettato → BUY rifiutato); che sia una **frazione** e' provato il 31/08 lasciando scendere l'equity di $8: **il tetto e' sceso con lei** — 30/08 in [644,18·645,18) con equity $2.063,79 = **31,21-31,26%**, 31/08 in [643,18·644,18) con equity $2.055,56 = **31,29-31,34%** (0,05pp di scarto, dentro il rumore dell'equity che oscilla ±$2-8/ora). ⚠️ **Ri-sondato il 31/08 dopo il passaggio a X:SM** (l'ipotesi era che il tetto dipendesse dal modello segregato): **[654,18-655,18) con equity $2.054,90 = 31,84-31,88%**. Il modello ENTRA nel tetto ma **non lo spiega** — ha comprato **+0,55pp = +11 USDE (~$11)**, non le centinaia che servirebbero. ⇒ **il tetto SCALA col conto**: la quota resta ~31% e il valore in dollari cresce col capitale, ma **il 70% non e' raggiungibile ne' ora ne' mai**. 🚨 **Deribit non lo documenta**: l'articolo ufficiale *Yield/reward bearing coins* descrive un **Cap ETHENA** che diluisce il **TASSO** a livello di exchange (`User APR = min(bal_exchange, cap)/bal_exchange × Ethena APR`) e **nessun limite sulle quantita' detenibili** da un conto. Ora e' in config (`usde.venue_cap_frac` 0.312, bordo basso dei bracket) e `usde_convert` **rifiuta il piano prima di sparare** invece di scoprirlo dal venue. **(b) L'USDC NON frutta SUL NOSTRO CONTO — misurato, e la premessa del gate e' RESTAURATA.** `public/get_currencies` dice USDC 3,4000% (USDE 4,1071%) e per qualche ora quel numero e' stato scritto qui come se fosse nostro: **era un listino del venue, non un accredito**. Deribit paga i reward USDC **una volta al MESE** («paid out as a single monthly payment early in the following month», accrual 00:00 UTC sulla **minima equity**) — non ogni giorno come l'USDE, ed e' per questo che una sorveglianza giornaliera non poteva vederli. Verificato sui **due confini di mese a libro FLAT**, dove `equity == balance == USDC` e ogni scalino sarebbe un accredito: **29/06→08/07 equity 598,06 costante, 237/237 ore ferme, 0 scalini** (attesi $0,45) e **31/07→03/08 equity 596,92 costante per 4 giorni** (attesi **$1,72 contro una risoluzione di $0,01, 172×**). **Zero misurato, non zero sotto soglia.** ⇒ il guadagno di tenere USDE e' il **tasso pieno ~4,1%**, non uno spread: **~$26/anno** sui $644. 🚨 **Il motivo ora e' DOCUMENTATO, e non e' MiCA come avevo ipotizzato**: l'articolo ufficiale elenca le giurisdizioni escluse dai reward USDC e **l'ITALIA c'e'** (con Canada e Giappone: non e' una lista MiCA, e' policy di giurisdizione Deribit). ⚠️ **E la finestra di pagamento e' piu' larga di quella che avevo controllato**: «within the **first two weeks** of the following month». Rifatta la verifica sulle due settimane vere: **luglio e' conclusivo** (atteso $1,72 contro un'escursione TOTALE dell'equity di **$0,48** in 1-16/08, zero scalini compatibili), **giugno NO** (il libro ha iniziato a operare a meta' mese e il rumore e' della taglia del segnale). Quindi: **una finestra conclusiva piu' la lista ufficiale**, non due finestre — il «172× la risoluzione» del 30/08 era sovra-affermato. 📌 **Un tasso pubblicato non e' un tasso incassato: si verifica sul CONTO** — ed e' costato una query su una serie che avevamo gia'. Strumento: **`scripts/live/balance_watch.py`** + `cron_balance.sh` (orario :42, sola lettura) registra il **balance a 8 decimali**, la serie che mancava. 📌 **Il vincolo che l'analisi del margine non aveva guardato**: P&L e funding dei perp USDC-lineari **si regolano in USDC**, quindi la quota massima difendibile non e' data dall'haircut ma dal **cuscino di regolamento** = disaster-SL sulla massima esposizione lorda (`n_asset × frac × disaster_sl_pct` = **30% dell'equity**), che lascia esattamente il **70%** — coincidenza per costruzione, zero slack (M8: non e' un argmax). 📌 **Il costo del NON averlo e' ora documentato e vale il criterio**: un'equity negativa in una valuta paga una *collateral fee* dello **0,05% AL GIORNO** — **18,25%/anno**, addebitata al secondo, **4,3× la resa USDE che si starebbe comprando** (fonte: KB *Cross collateral specifications*). Il ribilanciamento automatico NON salva: scatta solo a **$1M** assoluti o al **100% della cross equity**, soglie che a $2k non si toccano mai — si sanguina la fee e basta. Il 30/08 avevo scritto «Deribit lo finanzia a interesse» **senza il numero**: il numero e' questo, e rende il cuscino piu' giustificato, non meno. Attrezzo: **`scripts/live/usde_convert.py`** (dry-run di default, guardie proprie; NON passa da `execution.ALLOWED`, che resta ai soli due perp) |
|
||||
| **STATARB** | 2026-09-27 | soglie 24/07 (non toccate), **copiate verbatim** da `scripts/research/r0724_statarb_deploy_gate.py` (docstring e costanti; diario `2026-07-24-goal-50eur-roadmap.md` §Migliorie 1). Metrica: «Sharpe annualizzato di net_modeled su TUTTA la finestra forward (nessuna sottofinestra), da data/paper_statarb/returns.jsonl». «Sharpe >= 0.5 -> CANDIDATO AL DEPLOY: proporre peso {10,15,20}% del book Deribit, giudicato con weights_tilt_null + stima maxDD combinata < 15%. Deploy solo se il tilt-null passa.» · «0 <= Sh < 0.5 -> ESTENDI una sola volta di altri 90g (decisione 2026-12-26). Alla seconda scadenza: o >=0.5 o RITIRO. Niente terza proroga.» · «Sharpe < 0 -> RITIRO dal forward-monitor». Guardie «tutte richieste per il deploy»: «fill-haircut cumulato \|MODELED - REAL\| < 0.5 pp» · «config invariata (W=45, sgn=+1)» · «maxDD forward < 10%». Addendum 25/07: Sharpe della **statica sempre-short** a pari vol-target sulla stessa finestra — «Non e' una soglia binding», diagnostica obbligatoria; renderla binding spetta all'operatore. La lettura si fa lanciando lo script (`uv run python scripts/research/r0724_statarb_deploy_gate.py`), che applica la regola dal 27/09 | ✅ monitor riparato e rigenerato il 26/08: la serie vera da' Sharpe **−1,61** a oggi → coi criteri invariati punta al **RITIRO**. Si legge il 27/09, non prima |
|
||||
| **XSR01** | 2026-10-23 | Sharpe ≥1,0 **E** haircut ≤40% — dal 26/08 il numero decisivo e' `r0826_xsr_haircut.py` a **pavimento $10**, citato con la frazione di ordini eseguiti — poi `weights_tilt_null` e **capitale ≥$20k** | ✅ **riscritto il 26/08 su decisione dell'operatore** (dichiarato prima dell'esito, stringe: diario `2026-08-26-xsr01-gate-riscritto`): capitale allineato a "100% Deribit fino a $20k"; serie forward rigenerata (31 barre vere, Sharpe −2,72). Sotto la lente RENDITA il sleeve resta **eguagliato da un conto remunerato** (📌 sotto) |
|
||||
| **DVOLSPREAD** | kill 2026-10-24 se Sh<−0,50; decisione 2027-01-24 | (a) Sharpe>0 (b) marginale ADDS+robust_oos+insample_edge (c) **DSR ≥0,95** (d) `weights_tilt_null`; **veto** se barre attive <80% → si estende, non si decide | ✅ serie rigenerata il 26/08 (−4,41 a oggi, era −14,73 sulla serie rotta); il veto barre-attive ora conta barre VERE e la guardia PREMATURO sorveglia il monitor |
|
||||
| **GATE PROP-01** | chiuso 22-23/08 | (a) listino 13/13 ✅ (b) ancora SKH01 23/23 ✅ (c) economica+sopravvivenza 2/2 ✅ | **chiuso 3/3** — ma il numero operativo e' sceso 42% → 7,8% → 4,4% → 2,6% |
|
||||
| **GATE PREVDAY-01** | scritto 23/08 | 10 condizioni | **8/10**: la cella che GIRA non e' quella che la selezione onesta sceglie, e quella viva **fallisce il DSR** (0,905) |
|
||||
| **GATE PREVDAY-01** | decisione **2027-06-21** (scritto 23/08, `RESULTS-0822` §54); kill leggibile da ~18/12/2026 | 7 condizioni (a)-(g) tutte necessarie + **veto d'integrita'** (≥80% barre ricostruibili, divergenze non crescenti) + **kill** Sharpe forward **giornaliero** < −0,50 su ≥180 giorni attivi, senza ri-ottimizzare | **5/7** (= l'«8/10» di §54 e di `20-ondate`, contato su una base a 10 voci: stessa sostanza, base diversa): la cella che GIRA non e' quella che la selezione onesta sceglie, e quella viva **fallisce il DSR** (0,905) — (a) e (b) sono STRUTTURALI, nessun giorno di forward le cambia. ✅ **Kill e veto cablati il 10/09** in `paper_prevday.py` (issue #4; fino ad allora esistevano solo nel testo e la revisione del 09/09 lesse «senza data»): al 10/09 Sharpe giornaliero +0,95 su 81 giorni (MDE ±4,25), 1942/1943 barre ricostruibili, kill NON MATURO. Decisione dell'operatore (10/09): si tiene la data — verso i 50 €/g PREVDAY vale centesimi, non si spende altro |
|
||||
| **GATE SCALA-01** (portare `book_scale_k` da 1,00 a 1,25) | non prima del **2027-02-28** (= 180 giorni dal **01/09**, primo giorno di `scale_watch`: A2 conta da quando il criterio e' MISURATO, non dall'arming — decisione dell'operatore 10/09, issue #5. «Non prima del 2026-10-01», scritto il 01/09, era incompatibile con A2 per costruzione) | 9 condizioni **tutte necessarie** (`SPEC-scale-key` §6.2): A1 gradino di `SCALA_LADDER` · A2 k0 in produzione ≥180g col criterio passato ogni giorno · A3 **C1-C5 di WORST-DAY ricalcolati OGGI**, non ereditati · A4 invariante disaster-SL · A5 tetto sul prodotto · **A6 ANTI-RECENCY** (il criterio deve passare anche **escludendo gli ultimi 90 giorni**) · A7 `r0726_fee_sensitivity` rifatto · A8 la modifica e' **una riga di config + una di giornale, zero codice** · A9 T1-T11 verdi | ◐ **A8/A9 FATTI il 01/09** (chiave inerte + 18 test); **A2 non maturo** — la specifica impone ≥30 giorni a 1,00 col sorvegliante attivo *prima* di eseguire il gate: «l'unico modo di scoprire che il sorvegliante e' rotto mentre la leva e' ancora 1,00». 📌 **A6 oggi NON morde** (k ammesso ~3,5x contro un gradino di 1,25x): il vincolo che morde e' il **disaster-SL**. E' scritto ora **perche' non e' comodo per nessuno**, non quando servira' |
|
||||
| **`edge_watch`** (kill del book live) | continuo | **(A)** Sharpe rolling 36m < −0,5 → si riapre `weights_tilt_null`+DSR, il book **non si spegne da solo**; **(B)** in un anno con DD buy&hold >10%, il DD di TP01 deve restare <75% di quello | stato 27/07: Sharpe 36m +1,51, protezione **8/8** |
|
||||
|
||||
📌 **XSR01 sotto la lente RENDITA (25/08): il meccanismo vale ~0.** Mescolarne i rendimenti da' lo
|
||||
@@ -202,32 +313,69 @@ d'ancora in modo diverso (nella differenza si cancella in parte, nel livello per
|
||||
`paper_portfolio` NON rigenerato (GTAA su ADJUSTED_LAST: replay ≠ serie registrata, P12) —
|
||||
storia pre-fix dichiarata corrotta, coda non chiusa tolta. ⚠️ Nel fix, D6 pagata di nuovo:
|
||||
`asi8` in pandas 3 e' in `us`, non `ns` — blindato con test su tre risoluzioni.
|
||||
2. 🚨 **Il trasporto degli allarmi e' un punto singolo di guasto** — 6,9% di invii falliti (2/29).
|
||||
Fatte il 23/08: retry opzionale in `send()` e registrazione del motivo nel punto in cui veniva
|
||||
ingoiato. **Resta:** `alerted=True` marcato **prima** dell'invio → un 🚨 perso e' perso per
|
||||
l'episodio intero (gli episodi storici durano 200-2.324 ore). Cambiarlo **cambia il comportamento**
|
||||
→ decisione dell'operatore.
|
||||
2. ✅ **RISOLTO (2026-08-28, decisione dell'operatore).** Il trasporto degli allarmi era un punto
|
||||
singolo di guasto — 6,9% di invii falliti (2/29) e `alerted=True` marcato **prima** dell'invio,
|
||||
quindi un 🚨 perso era perso per l'**episodio intero** (200-2.324 ore). Ora `venue_watch.run_once`
|
||||
riceve il **sender iniettato** e il marcatore «gia' detto» si scrive **solo dopo un invio
|
||||
riuscito**; su fallimento si disfano i soli marcatori — non le misure — e per il lock **le ore
|
||||
continuano a correre**, cosi' una manutenzione che sfora la grazia sale ad ALERT anche col
|
||||
trasporto giu'. `notify()` accetta `tentativi` (venue_watch: 3) e l'esito finisce nel log.
|
||||
⚠️ Resta scoperto **ogni altro chiamante di `notify()`**: la disciplina «commit dopo l'invio»
|
||||
e' oggi solo in `venue_watch`.
|
||||
3. ✅ **RISOLTO (2026-08-26, decisione dell'operatore: opzione "riscrivere adesso").** Gate
|
||||
XSR01 riscritto in modo dichiarato, prima dell'esito, in direzione che stringe: capitale
|
||||
$5k → **$20k** (riconcilia con la decisione 26/07, che e' posteriore e governa), gamba haircut
|
||||
letta da `r0826_xsr_haircut.py` a pavimento $10 con la frazione di ordini eseguiti accanto
|
||||
(misurata: haircut FULL −1,1% con **22% di eseguiti** — la guardia da sola era vacua), soglie
|
||||
numeriche INVARIATE. Diario `2026-08-26-xsr01-gate-riscritto.md`.
|
||||
4. ❓ **Due domande al commercialista, aperte** (valgono ~$22k di muro e ~€30/mese di versamento):
|
||||
(a) i derivati Deribit (inverse, margine e regolamento in cripto, sede extra-UE) stanno in
|
||||
`c-sexies` (33%) o `c-quater` (26%)? E il collaterale sconta il 2‰ al 31/12? (b) i payout di una
|
||||
prop firm: **ne' `c-sexies` ne' `c-quater`** — le fonti convergono su **lavoro autonomo**, nessuna
|
||||
prassi ufficiale. *Non sono pareri fiscali: sono domande da porre.*
|
||||
4. ◐ **(a) RISPOSTA TROVATA ALLA FONTE (2026-08-28), (b) resta aperta.** *Non sono pareri fiscali.*
|
||||
**(a) I derivati stanno in `c-quater` → 26%, non in `c-sexies` → 33%.** Circolare AdE **30/E del
|
||||
27/10/2023**, §2.3.2 e §3, verbatim: «In generale, i redditi derivanti da **contratti derivati,
|
||||
ancorche' aventi come sottostante cripto-attivita'**, costituiscono redditi diversi ai sensi
|
||||
dell'articolo 67, comma 1, **lettera c-quater)**» e «qualora le cripto-attivita' costituiscano il
|
||||
sottostante di un contratto derivato (ad es. contract for difference o **future**) i redditi
|
||||
derivanti da tali strumenti finanziari costituiscono redditi diversi ai sensi dell'articolo
|
||||
c-quater)». La circolare conferma esplicitamente che la riforma 2023 non sposta nulla: «tali
|
||||
contratti **non rientrano** nell'ambito di applicazione della lettera c-sexies». ⚠️ Conseguenza:
|
||||
**le minusvalenze su derivati NON si compensano con le plusvalenze cripto** (categorie diverse) e
|
||||
non c'e' franchigia. **Il collaterale sconta il 2‰**: Deribit non e' intermediario residente,
|
||||
quindi non applica il bollo e scatta l'*imposta sul valore delle cripto-attivita'* sul valore al
|
||||
31/12, in RW (a $2.068 fa **~$4/anno**: obbligo dichiarativo, non un costo). 📌 E la conversione
|
||||
**USDC→USDE non e' fattispecie realizzativa** — §3.1: «dal 1° gennaio 2023 non costituisce
|
||||
fattispecie realizzativa lo scambio di una cripto-valuta con un'altra», col valore di carico che
|
||||
si trasferisce. **Resta da chiedere:** se il perpetual *inverse* regolato in cripto sia il caso
|
||||
letterale della circolare (che nomina CFD e future) o serva un passaggio in piu'.
|
||||
**(b) I payout di una prop firm: ancora aperta.** Le fonti convergono su **lavoro autonomo**
|
||||
(partita IVA, ATECO, forfettario 5% per i primi 5 anni poi 15%, imponibile da coefficiente di
|
||||
redditivita'), **non** redditi diversi al 26%. **Nessuna prassi ufficiale**, e il coefficiente
|
||||
esatto dipende dal codice ATECO.
|
||||
5. ✅ **FATTO (2026-08-26):** `test_leva_massima_da_config...` cancellato con nota in
|
||||
`tests/test_fee_sensitivity.py` — misurava `frac · n_asset`, con una chiave di scala avrebbe
|
||||
continuato a passare smettendo di controllare. La guardia giusta e' il tetto sul PRODOTTO di
|
||||
GATE SCALA-01, da cablare nel codice che leggera' la chiave quando esistera'.
|
||||
continuato a passare smettendo di controllare. ✅ **CHIUSO il 2026-09-01:** la chiave esiste e la
|
||||
sostituzione e' cablata — **T2** (tetto sul PRODOTTO `n_asset · frac · scala ≤ LEVA_LORDA_MAX`) e
|
||||
**T3** (invariante disaster-SL) in `tests/test_book_scale.py`, che importano i bersagli da
|
||||
`src.live.book` invece di ridichiararli (P1). Il vecchio test resta **cancellato**, non rilassato,
|
||||
e **T11 lo verifica**.
|
||||
6. ⚠️ **TLT ha 13,5 anni di storia in meno** (parte 2016-02 invece del 2002) → **GTAA01 gira su
|
||||
CINQUE gambe prima del 2016**, e l'assente e' quella obbligazionaria. Non e' un fetch da rifare (IB
|
||||
ritorna 0 barre). ⇒ in-sample e hold-out di GTAA01 **non sono la stessa strategia**.
|
||||
7. ⚠️ **Il docstring di `book_execute.py` prescrive "ogni ~230 minuti", il cron gira OGNI ORA.**
|
||||
**Il docstring e' sbagliato e la configurazione che gira e' quella giusta** — chi lo "correggesse"
|
||||
sposterebbe il libro sulla riga 4h, dove BTC **raddoppia** gli scatti del disaster-SL.
|
||||
7. ✅ **RIPARATO (2026-09-02).** Il docstring di `book_execute.py` prescriveva "ogni ~230 minuti"
|
||||
mentre il cron gira **ogni ora** (`47 * * * *`): era il docstring a essere sbagliato, e chi
|
||||
avesse "corretto" il **CRON** verso il docstring avrebbe spostato il libro dalla riga 1h (rotolante:
|
||||
BTC 0 scatti / ETH 1 in 7-8 anni) alla riga 4h (BTC 2, contro 1 del pavimento); la peggiore
|
||||
misurata e' 24h (ETH 5, con 2 in 30 giorni) — `r0823_sl_anchor.py`. Ora il docstring dichiara
|
||||
**CADENZA: ORARIA** con la ragione (giro idempotente: latenza SKH01 ≤1h; il rotolante e'
|
||||
**controllato** ogni ora e ri-ancorato solo oltre la tolleranza di `ensure_disaster_sl`, mark
|
||||
+5,263%/−4,762% o taglia >10%, quindi lo stop siede fra **−33,5% e −26,5%** dal mark corrente —
|
||||
«si ri-ancora ogni ora» era una frase sbagliata, corretta in revisione il 02/09; e «girare piu'
|
||||
fitto non costa ordini» pure: 22 dei 48 ordini live sono ri-taglie |Δ|≤$10, deadband in valuta
|
||||
assoluta, C2) e il divieto esplicito. **`tests/test_book_cadenza.py` (10 test)** tiene
|
||||
d'accordo le tre dichiarazioni — docstring, riga in `cron_book.sh`, **crontab installata**
|
||||
(letta con `crontab -l`, **tutte** le righe attive: due righe sono due esecuzioni; tre stati —
|
||||
illeggibile ⇒ SALTATO, leggibile senza questo progetto ⇒ SALTATO, leggibile col progetto ma senza
|
||||
`cron_book.sh` attivo ⇒ **ROSSO**) — verifica 60 min < `skyhook.LTF_MIN` 230 < 240 (riga 4h),
|
||||
minuto ≠ :00 **e fuori dallo slot di release** (`venue_probe.in_release_window`, col :07 come
|
||||
controllo positivo). Diario `2026-09-02b-debito-7-cadenza-docstring.md`.
|
||||
8. ⚠️ **Il Rulebook Deribit** (ADL, perdita socializzata, *emergency powers*, conti dormienti) **non
|
||||
lo sorveglia nessuno**; `MAINT_GRACE_HOURS`=2 **presume** gli annunci invece di leggerli.
|
||||
✅ *Ridotto in parte il 2026-08-25:* `src/live/venue_probe.py` interroga l'API **pubblica**
|
||||
@@ -260,8 +408,103 @@ d'ancora in modo diverso (nella differenza si cancella in parte, nel livello per
|
||||
definizione, patchare la costante non bastava) e `docs/journal/`; per `book_executions.jsonl`
|
||||
(caricato ad-hoc via importlib, irraggiungibile da conftest) impronta a inizio suite e verifica
|
||||
a fine suite, col falso positivo possibile (fill reale del cron :47) dichiarato nel messaggio.
|
||||
13. ⚠️ **GTAA01 gira su una finestra che ROTOLA, e la fase di ribilanciamento cambia da sola ogni
|
||||
notte** (misurato 2026-08-28, `r0828_gtaa_band_phase.py`). IB serve una finestra di **30 anni**
|
||||
e SPY e' al muro: la sua prima barra avanza di un giorno di borsa ogni notte (1996-07-02 il
|
||||
24/06 → 1996-09-04 il 28/08). `gtaa._gated_returns` ribilancia su `i % every == 0`, cioe' sulla
|
||||
**posizione nell'array**: se la prima barra scivola si ri-fasa ogni decisione di trent'anni.
|
||||
Effetto misurato: su una finestra **chiusa** (pre-2015) lo Sharpe si e' mosso fino a **0,076**
|
||||
a codice fermo, e la banda scelta al buio cambia fra 25% / 40% / 60% da una notte all'altra.
|
||||
**Ha gia' ucciso due criteri del gate (A)** (07/08 e 28/08). **Quantificato e innocuo OGGI**
|
||||
(D5): a livello di portafoglio l'escursione sulle 5 fasi vale **0,029** di Sharpe, dentro la
|
||||
banda dichiarata [1,81-2,12]. QQQ e IWM arriveranno allo stesso muro **fra 2,5 e 3,7 anni**.
|
||||
Riparazione (ancorare la fase al calendario) rinviata con la decisione su GTAA01: cambierebbe
|
||||
**tutti** i numeri registrati dello sleeve.
|
||||
14. ✅ **RIPARATO (2026-09-02).** `trades_db.py --report` stampava `e1/e0-1` sulla serie grezza —
|
||||
«**+243%**», per il **96,3% un bonifico** (il versamento di $1.399,39 del 25/08 11:47Z; misurato
|
||||
il 01/09, diario `2026-09-01-stato-trades.md`). La riparazione esisteva gia' nel giornale
|
||||
(`journal.movimenti_capitale`) e **non aveva attraversato il confine fra i due lettori della
|
||||
stessa serie** — variante di P1. Ora **`journal.rendimento_twr`** e' la funzione che entrambi
|
||||
chiamano: spezza la serie sui movimenti CERTI (gli ambigui restano dentro, dichiarati) e il
|
||||
report stampa **TWR +10,61% = +11,61% × −0,89%**, `trading da arming +$50,95`, i movimenti
|
||||
elencati, e il delta $ grezzo etichettato «movimenti INCLUSI» — il `%` grezzo **non compare piu'**.
|
||||
Test: 5 in `test_journal.py` (fra cui la riproduzione del +10,80% del diario 01/09, M23) + 2 in
|
||||
`test_trades_report.py` sul testo stampato, con `connect()` deviato in tmp. Diario
|
||||
`2026-09-02-debito-14-report-twr.md`. ⚠️ **Due limiti dichiarati dalla revisione del 02/09** (D5): (a) **l'intervallo che contiene un movimento certo esce INTERO dal rendimento** — il suo P&L di mercato finisce in `certi` e fuori da `trading`, con errore massimo pari a META' del movimento riconosciuto per costruzione del classificatore (il 25/08: ~$0,5 su $1.399); non si stima, si dichiara (P12); (b) **a base di equity zero `twr` E `trading` sono None**: il salto 0→X del primo versamento e' invisibile al classificatore e `trading` varrebbe l'intero conto. 🚨 **Seconda tornata (02/09, 15:30Z): il classificatore era CIECO ~23 ore al
|
||||
giorno.** Il feed 1h certificato si ferma alle 00:00 (rebuild 00:30): per le letture successive
|
||||
`asof` dava la stessa barra a t0 e t1, mercato "fermo" = 0, e **qualunque calo ≥10% del giorno
|
||||
sarebbe stato un «movimento»** — un crash o un depeg USDE stampato come prelievo, nel giornale
|
||||
(dal 25/08) e nel report. Riprodotto su copia del DB. Ora un salto fuori dalla copertura del feed
|
||||
(o con feed assente) e' **«mercato non misurabile»** ⇒ `ambiguo` con la ragione, e `twr`/`trading`
|
||||
sono **None** con motivo (prima, a feed assente, `e1/e0-1` tornava sotto l'etichetta TWR). Il
|
||||
«limite ereditato +10% a mercato fermo» scritto qui la mattina era un **artefatto della fixture
|
||||
piatta**, non un limite del classificatore (con mercato misurato il trading non puo' superare
|
||||
2·max_mkt): cancellato. E `pnl_giorno` ora **chiama** `rendimento_twr` (prima l'affermazione
|
||||
«la funzione che entrambi chiamano» era falsa: il giornale rifaceva `cum − certi`); la pagina
|
||||
stampa il TWR accanto al cumulato e la testata Telegram dell'analista qualifica il cumulato
|
||||
«di cui versati». `leva_tetto` del classificatore include la scala e il tetto di codice (P1).
|
||||
|
||||
15. ⚠️ **Il bound di mercato del classificatore dei movimenti guarda solo BTC/ETH, ma dal 26/08 il
|
||||
31% dell'equity e' USDE** (trovato in revisione il 02/09). Un depeg dell'USDE a BTC/ETH fermi:
|
||||
−1,5% (indice 0,95) resta sotto soglia e nel P&L; una rottura piena (−31%) supera 2× un mercato
|
||||
"fermo" e diventerebbe un **«movimento» scorporato come prelievo**, nel giornale e nel report —
|
||||
l'invenzione silenziosa che P12 vieta. Il bound corretto somma `quota_usde × |Δ indice usde_usdc|`,
|
||||
ma l'indice era registrato **una volta al giorno** (`usde_watch`, 12:35Z). ◐ **Primo passo fatto
|
||||
il 02/09:** `balance_watch` (orario, sola lettura) registra ora `usde_usdc` a ogni campione —
|
||||
None con la ragione se l'indice non e' leggibile, mai 1,0. **Manca il cablaggio nel bound**, che
|
||||
si fa quando la serie ha storia. Intanto: chi legge un «movimento» negativo senza bonifico in
|
||||
uscita **lo verifichi contro `balance_watch.jsonl`** prima di crederci.
|
||||
|
||||
16. ✅ **RIPARATO (2026-09-02).** Nessuno dei **18** script di `scripts/live/` usava argparse: un
|
||||
flag sconosciuto non era un errore, era **il ramo `else`**. Costo misurato lo stesso giorno
|
||||
durante una revisione: `trades_db.py --help` non stampava l'uso, cadeva in `sync()` e riscriveva
|
||||
`meta.ultimo_sync`; sugli altri due sarebbe stato peggio — `journal.py --help` avrebbe scritto
|
||||
pagina e riga di DB, `analista.py --help` avrebbe speso una chiamata al modello e mandato un
|
||||
Telegram. Ora `src/live/cli.valida` e' la **prima istruzione** di ogni `__main__`: `--help`
|
||||
stampa l'uso ed esce **0**, un flag ignoto esce **2** con l'elenco di quelli previsti (P4).
|
||||
Niente argparse: cambierebbe messaggi, codici d'uscita e `--help` di script che il cron gia'
|
||||
chiama — qui si lascia intatto cio' che gira. **`tests/test_cli_flag.py` (30)**: l'elenco degli
|
||||
script si **deriva dalla cartella** (uno nuovo senza guardia fa fallire il test), verifica che
|
||||
`valida` sia la prima istruzione, che l'uso documenti i flag accettati, e che **i flag che il
|
||||
cron usa davvero restino accettati** (P15/P16). Diario `2026-09-02c-flag-e-pulizia.md`.
|
||||
|
||||
17. ✅ **RIPARATO (2026-09-10, issue #3).** Nessuno sorvegliava il CUSCINO USDC di regolamento (aperto il 06/09
|
||||
con la quota USDE al 68,7%): P&L e funding dei perp si regolano in USDC, una perdita del libro lo consuma e fa
|
||||
salire la quota da sola; `usde_watch` allerta solo su `quota > 0,85`, e un USDC negativo costa lo 0,05%/giorno.
|
||||
Ora **`scripts/live/cuscino_watch.py`** (cron **:53**, orario, `cron_cuscino.sh`, sorvegliato da `monitor_health`)
|
||||
legge l'**EQUITY USDC** (decisione dell'operatore: e' quella che risponde del regolamento, il balance vede il buco
|
||||
solo a trade chiuso), deriva il cuscino da `usde.cuscino_richiesto_usd` — **la formula si e' spostata in
|
||||
`src/live/usde.py`, e `usde_convert` la importa da li'** (due lettori, una formula: P1) — e ha cinque stati:
|
||||
OK · **PREAVVISO** (slack < 10% del cuscino, ⚠️ alla transizione) · **ECCEDENTE** (sotto) · BLIND · **SCOPERTO** (slack < 0: 🚨 e
|
||||
**riconversione automatica** `usde_convert --quota usde.quota_ripristino(0,20) --esegui`, cioe' **0,64**, che lascia
|
||||
il cuscino piu' un 20% — il DOPPIO del preavviso, altrimenti ogni 🚨 sarebbe seguito da un ⚠️ per costruzione). Guardie: `execution_enabled` del libro (disarmare il libro disarma anche questo), niente
|
||||
vendita sotto `depeg_warn` (decide l'operatore), un tentativo ogni 6h, le guardie proprie di `usde_convert`;
|
||||
marcatore «gia' detto» = esito dell'invio (§5.2). **Primo giro (13:00Z): gia' PREAVVISO, slack +$26** su $1.335
|
||||
richiesti — la marcatura del libro dal 06/09 lo aveva consumato senza che nessuno lo vedesse. ✅ **Ratchet chiuso lo stesso giorno (issue #7, «riportarla a quota in autonomia»)**: quarto stato **ECCEDENTE** (slack > 40% del cuscino ⇒ **acquisto** di USDE fino allo stesso bersaglio 0,64). Le tre bande stanno in `config/live.json` (`cuscino_preavviso_frac` 0,10 · `cuscino_margine_frac` 0,20 · `cuscino_riacquisto_frac` 0,40, `usde.bande_cuscino` verifica l'ordine) e **`quota_target` 0,70 non esiste piu'**: a 0,70 lo slack era ZERO per costruzione e ogni ora in perdita avrebbe venduto. Isteresi [0 ; 0,40]×cuscino con bersaglio unico 0,20: per oscillare servono ±6% di equity di slack = **±8,6% di equity USDC (~$380)**, un giro costa ~6 bps sull'importo mosso; un bonifico alza lo slack di 0,7×importo e sopra ~9% dell'equity fa scattare il riacquisto da solo. Soglie dichiarate, non ottimizzate (M8). ⚠️ Trovato al primo giro vero (13:53Z): l'allerta PREAVVISO **non e' partita** — Telegram in HTML rifiuta un `<` nudo («(< 10%…)»); l'escape sta nel **sink** (`notifier.notify`, `html.escape` su titolo e valori: copre tutti i 45 chiamanti, incluso il campo `motivo` che la revisione ha trovato ancora scoperto), `allerta_errore` registrato nel record (P3). Un guasto persistente da ECCEDENTE (tetto del venue tornato, book illeggibile) si ritenta **una volta al giorno**, da SCOPERTO ogni 6h; una config con bande fuori ordine da' **BLIND con motivo**, non un traceback ingoiato dal cron. ✅ **0,64 e' la quota DECISA dall'operatore il 10/09** (§3): derivata dal margine 0,20 sul cuscino, sostituisce lo 0,70 del 30/08. 📌 **Allineato il 10/09 14:16Z su ordine dell'operatore**: `usde_convert --quota 0.64 --esegui`, 238 USDE in 3 ordini @ 1,0000, quota 69,4% → 64,02%, slack +$266 = **OK** (era PREAVVISO a +$29). Il tetto del venue in `usde_convert.piano` ora vale **solo in acquisto**: bloccava anche la vendita, e con `venue_cap_frac` rimesso in config la riconversione sarebbe stata morta. `tests/test_cuscino_watch.py` (24).
|
||||
|
||||
18. ✅ **RIPARATO (2026-09-09 sera).** `cblib.spot_series` + `asof(ts)` guardava **un'ora avanti** (feed
|
||||
1h etichettato all'apertura: verificato, chiusura 1h a T == chiusura 5m a T+55 nel 100% delle barre)
|
||||
**e `dvol_series` fino a 24 ore avanti** (la riga DVOL del giorno D e' la chiusura delle 23:00 di D:
|
||||
verificato contro la risoluzione 1h dell'API pubblica) — seconda serie con lo stesso difetto, trovata
|
||||
riparando la prima (D6, quinta occorrenza). Ora `cblib.causale(s, cadenza)` rietichetta le chiusure
|
||||
all'istante in cui sono note e le due serie escono **gia' causali** (spot +1h, DVOL +1 giorno);
|
||||
`r0909` non ri-sposta. **§11 rimisurato allo stesso taglio (22/08, n=19)**: prima riprodotto al
|
||||
millesimo su worktree HEAD (0,714 [0,690-0,779]), poi **0,712 [0,664-0,732]** — le due correzioni
|
||||
hanno **segno opposto** (solo spot 0,721 · solo DVOL 0,693 · entrambe 0,712; M26) e un DVOL **orario**
|
||||
di controllo da' 0,717. Il verdetto di §11 non cambia; cambiano i numeri di contorno (Sharpe canonico
|
||||
BTC 32,58 → 29,61, mediana onesta 1,45 → **2,12**; ETH 3,90 → 7,64; fee 1,44×/1,80× → 1,51×/1,87×;
|
||||
il regolamento e' ora il prezzo delle 08:00, non delle 09:00: P&L +3,3%). Sul crollo (§75): ETH pre
|
||||
0,74 → **0,76 [0,71-0,86]**, BTC durante 0,79 → **0,89** (DVOL orario 0,83: nel crollo il DVOL
|
||||
giornaliero stantio vale −0,04/+0,06 di f). `vrp_f_watch` (cron): f canonico 0,732 → **0,706**, differenza
|
||||
col candidato +0,054 → **+0,097 [+0,042, +0,140]** — ora esclude lo zero e il criterio (b) e'
|
||||
soddisfatto, (a) no (28/40). ⚠️ **Resta aperto, con costo misurato**: il DVOL nel feed e' giornaliero,
|
||||
quindi causale = **stantio fino a 23 ore** (mediana 0,2 pt di vol, max 3 pt; nel crollo 0,06 di f):
|
||||
la via giusta e' un feed DVOL **orario** (API pubblica, gratuito, `resolution=3600`) con la sua riga
|
||||
in `cron_daily` — non fatto, e' una decisione sul feed; e lo spot causale resta **stantio ≤25 min** (snapshot
|
||||
al :25, chiusura al :00; il feed 5m certificato lo ridurrebbe a ≤5). Trovati in revisione altri due lettori:
|
||||
**`r0730_vrp_real_quotes`** (il f 0,73 di §2 → **0,71**, dichiarato in §2) e **`r0901_btc_collar`** (join
|
||||
per giorno: riallineato con `causale(·, "-1D")`, bit-exact a prima). `options_vrp_calibrate.spot_series`
|
||||
(copia propria, numeri del 20/06) **non toccato**. Diario `2026-09-09b-debito-18-cblib-causale.md`.
|
||||
---
|
||||
|
||||
## 6. Le regole di prim'ordine
|
||||
@@ -286,7 +529,7 @@ nei file di memoria.
|
||||
- **M5** **Null del de-levering (7 occorrenze):** ogni claim "meno drawdown" si testa a **iso-rischio** prima di crederci — e' il primo test, non l'ultimo. Se la variante e' una pura ri-scalatura il null e' **degenere** (`sh(k·base) ≡ sh(base)`): serve iso-peso sullo Sharpe.
|
||||
- **M6** Un diversificatore a **basso CAGR** si giudica a **iso-rischio**, mai a iso-nozionale.
|
||||
- **M7** Su offset/ancore appaiate la statistica e' la **mediana delle differenze**, non la differenza delle mediane. E **se si de-lucka una strategia va de-luckato anche il suo DEGRADO**.
|
||||
- **M8** Un **argmax dentro un plateau non e' una decisione**; e la mediana da sola puo' nascondere il fatto decisivo (p90 su, p10 giu' = si compra dipendenza dall'ancora, non Sharpe).
|
||||
- **M8** Un **argmax dentro un plateau non e' una decisione**; e la mediana da sola puo' nascondere il fatto decisivo (p90 su, p10 giu' = si compra dipendenza dall'ancora, non Sharpe). E un **argmax sul BORDO della griglia non e' una cella: e' una pendenza** — si segue fino al suo limite prima di leggerne il valore, perche' il limite puo' essere un oggetto diverso da quello che si stava studiando (COLLAR01: le 3 celle vincenti su 48 stavano tutte sul bordo, e il limite era una **covered call**, cioe' fuori dalla domanda posta).
|
||||
- **M9** Un contributo positivo si **scompone per anno** prima di crederci: "24/24 ancore positive" puo' essere **un anno solo**.
|
||||
- **M10** **Due punti non fanno una tendenza** nemmeno quando la meccanica sembra spiegarla.
|
||||
- **M11** Un **blocco dichiarato e' un'ipotesi, non un fatto**: si ri-verifica prima di costruirci sopra o di rimandare (3 occorrenze, ogni volta il blocco non esisteva).
|
||||
@@ -307,12 +550,13 @@ nei file di memoria.
|
||||
- **M26** **Non contare su una compensazione fra correzioni misurate separatamente** — la si misura, e in piu' di una **moneta** (un'interazione piccola cambia segno con la moneta).
|
||||
- **M27** Prima di dichiarare che un campo di un dataset e' sbagliato, **aprire il codice che lo produce**. E una **fonte normativa citata in un commento si verifica come un numero**.
|
||||
- **M28** Quando **due affermazioni della stessa memoria si contraddicono, la contraddizione e' informazione**: una delle due e' stata scritta guardando i dati.
|
||||
- **M29** 🚨 Un edge da **opzioni prezzate a modello** si **riprezza alla volatilita' REALIZZATA** prima di crederci: se sparisce, non era la struttura — era il **premio di varianza**, cioe' VRP01, che il progetto ha gia'. Su BTC il **DVOL sta il 32% sopra la RV-forward** a 7 giorni (**77% dei giorni**), quindi *qualunque* struttura che vende vol **stampa per costruzione del prezzatore**: in COLLAR01 il **72-100%** del drift della cella migliore era questo, e l'angolo cadeva da Sharpe **1,471 a 0,511** — che e' il **0,47** gia' pubblicato di VRP01. Vale anche al contrario: un edge che *compra* vol a modello e' sottostimato dallo stesso fattore.
|
||||
|
||||
### Sui costi e l'eseguibilita'
|
||||
- **C1** Il costo di un venue va modellato nella sua **FORMA** (fisso vs proporzionale), non solo nel livello: un pavimento fisso e' una **tassa regressiva** e due venue a pari "costo medio" danno esiti **opposti** al variare del capitale.
|
||||
- **C2** Un parametro d'esecuzione espresso in **valuta assoluta** ha effetto che dipende dal capitale: il controllo non e' lo Sharpe ma la **quota di tempo a mercato**.
|
||||
- **C3** Prima di misurare il rendimento a un capitale dato, misurare il **lotto minimo del venue** — e sapere **quale famiglia di strumenti** si sta guardando (inverse vs USDC-lineare: lotti 7,5× diversi).
|
||||
- **C4** Un **fattore di errore si giudica moltiplicato per il suo peso** (5,85 al 3% fa meno danno di 2,23 al 18%). Il `f` di una struttura multi-gamba **non e' il `f` di una sua gamba**.
|
||||
- **C4** Un **fattore di errore si giudica moltiplicato per il suo peso** (5,85 al 3% fa meno danno di 2,23 al 18%). Il `f` di una struttura multi-gamba **non e' il `f` di una sua gamba**. E **il segno dell'asimmetria di `f` dipende dal VERSO della gamba**: *"un roll anticipato non paga `f`"* (§46) vale per una copertura **solo LONG** — se c'e' una **gamba VENDUTA da ricomprare**, uscire prima paga `f` *esattamente* sulla parte che a scadenza si regolava gratis, e piu' spesso. **L'asimmetria si INVERTE** (COLLAR01: uscire al 50% del tempo costa 5,6 punti di drift col gate forte, 10,2 col largo).
|
||||
- **C5** **L'open interest NON misura la negoziabilita'** (su Deribit USDC sono quasi anti-correlati). Prima di credere a un rapporto estremo su un prezzo piccolo, **contare i tick**.
|
||||
- **C6** La negoziabilita' **sul conto reale** va verificata quando lo sleeve entra in **RICERCA**, non quando entra nel book (PRIIPs costo': 5 settimane di misure). *"Non posso comprare" ≠ "non posso vedere"*: il segnale puo' girare su una serie che non si puo' negoziare.
|
||||
- **C7** Si cerca per **ISIN, non per ticker**; si prende la linea nella **valuta del proprio saldo**; un costo di conversione e' proprieta' della **coppia strumento-conto**, non dello strumento.
|
||||
@@ -446,7 +690,9 @@ src/portfolio/ → portfolio.py (combina N sleeve + weights_tilt_n
|
||||
src/backtest/harness.py → harness ONESTO (load BTC/ETH, backtest_signals no-leakage, OOS)
|
||||
src/live/ → book.py (esecutore netto TP01+SKH01), shadow.py, deribit.py,
|
||||
livefeed.py, venue_watch.py, monitor_health.py, notifier.py,
|
||||
tradesdb.py · journal.py · analista.py (libro di bordo)
|
||||
scale_watch.py (chiave di scala: 3 domande, 3 stati),
|
||||
tradesdb.py · journal.py · analista.py (libro di bordo),
|
||||
revisione.py (revisione settimanale, sola lettura, revisore fable)
|
||||
src/data/eq_splits.py, eq_crosscheck.py → guardie sul feed equity
|
||||
scripts/research/ → ricerca (r<data>_*.py). Harness condiviso: alt/altlib.py
|
||||
scripts/analysis/ → SOLO tool sui dati certificati: rebuild_history, certify_feed,
|
||||
@@ -454,11 +700,13 @@ scripts/analysis/ → SOLO tool sui dati certificati: rebuild_history
|
||||
scripts/live/ → book_execute · venue_watch · fee_watch · edge_watch ·
|
||||
monitor_health · collect_chain · paper_* · trades_db ·
|
||||
journal · analista
|
||||
scripts/cron_{book,daily,chain,opt_snapshot,vol_term}.sh
|
||||
scripts/cron_{book,daily,chain,opt_snapshot,vol_term,review}.sh
|
||||
(la crontab ha anche cron_ombra.sh di TradingBook: vedi §1, OMBRA)
|
||||
docs/memory/ → LA MEMORIA (indice in testa a questo file)
|
||||
docs/research/ → BRIEF-0822 · RESULTS-0822 (§1-69) · SPEC-scale-key
|
||||
docs/diary/YYYY-MM-DD.md → una voce per esperimento (123)
|
||||
docs/research/ → BRIEF-0822 · RESULTS-0822 (§1-77) · SPEC-scale-key
|
||||
docs/diary/YYYY-MM-DD.md → una voce per esperimento (146)
|
||||
docs/journal/YYYY-MM-DD.md → libro di bordo, una voce al giorno, 4 livelli per provenienza
|
||||
docs/revisioni/YYYY-MM-DD.md → revisione settimanale firmata (lunedi'), opinione del revisore
|
||||
data/live/trades.db → i trade allineati col tempo (dentro il perimetro di backup)
|
||||
Old/ → ARCHIVIO pre-reset
|
||||
VERSION → semver
|
||||
@@ -472,11 +720,13 @@ uv run python scripts/analysis/rebuild_history.py --asset BTC ETH # storico da
|
||||
uv run python scripts/analysis/certify_feed.py [--local] # certifica i feed
|
||||
uv run python scripts/portfolio/run_portfolio.py # report del portafoglio (ricerca)
|
||||
uv run python scripts/live/paper_portfolio.py # avanza il paper (forward-only)
|
||||
uv run python scripts/live/trades_db.py --report # stato libro di bordo + P&L
|
||||
uv run python scripts/live/trades_db.py --report # stato libro di bordo + P&L (TWR, non e1/e0)
|
||||
uv run python scripts/live/<script>.py --help # uso e flag (dal 02/09: un flag ignoto esce 2)
|
||||
uv run python scripts/live/trades_db.py --reconcile # incrocio delle 3 fonti sui fill
|
||||
uv run python scripts/live/journal.py # voce del giorno (numeri + lettura)
|
||||
uv run python scripts/live/analista.py --secco # analisi del giorno, senza salvare
|
||||
uv run pytest # test (775, tutti verdi dal 26/08)
|
||||
uv run python scripts/live/revisione.py --secco # taglia del materiale della revisione settimanale (niente modello)
|
||||
uv run pytest # test (1070, tutti verdi al 10/09)
|
||||
```
|
||||
|
||||
```python
|
||||
|
||||
@@ -1,423 +1,153 @@
|
||||
# PythagorasGoal
|
||||
|
||||
Sistema di riconoscimento pattern frattali e predizione per il trading di criptovalute (BTC, ETH), ispirato al framework teorico di Serleto & Malanga (*Pythagoras Trading Prediction*).
|
||||
Ricerca e esecuzione di strategie algoritmiche su BTC/ETH, con **un libro che gira con soldi veri**
|
||||
su Deribit mainnet dal 20 giugno 2026.
|
||||
|
||||
## Obiettivo
|
||||
> 🚨 **v2.0.0 — RESET del 2026-06-19. Tutto ciò che questo README diceva prima è archiviato in
|
||||
> `Old/` e non è fidato.** L'intera libreria di strategie "validata out-of-sample" (le famiglie
|
||||
> FADE/HONEST/PAIRS/TSMOM/SHAPE, i portafogli PORT01-06, gli Sharpe fra 6 e 10) era un **artefatto
|
||||
> di uno storico contaminato**: print fantasma di un feed *testnet* più storico Binance/USDT.
|
||||
> Ri-testate sul feed reale ricostruito da Deribit mainnet, **perdono ogni anno**. Documento di
|
||||
> fondazione: `docs/diary/2026-06-19-deribit-history.md`.
|
||||
>
|
||||
> Questo README descrive il progetto **dopo** il reset. È stato riscritto il 2026-09-02, dopo
|
||||
> essere rimasto fermo al 4 giugno — quindici giorni prima del reset — mentre pubblicava i numeri
|
||||
> che il progetto aveva già dichiarato falsi.
|
||||
|
||||
Partendo da un capitale iniziale di €1.000, raggiungere un profitto medio di €50 al giorno entro 6–8 mesi, tramite un portafoglio di strategie algoritmiche poco correlate fra loro — mean-reversion, trend/rotazione e spread market-neutral — validate out-of-sample e fee-aware.
|
||||
**L'autorità sui numeri e sulle decisioni è `CLAUDE.md`**, e il racconto completo sta in
|
||||
`docs/memory/`. Questo file è l'ingresso, non la fonte.
|
||||
|
||||
## Risultati
|
||||
---
|
||||
|
||||
> ⚠️ **Revisione 2026-05-28.** La famiglia squeeze-breakout (SQ/MT/ML/AD/CM/PD, con
|
||||
> accuracy storiche dichiarate 76-82%) è stata **scartata**: quei numeri erano un
|
||||
> **artefatto di look-ahead**. I backtest decidevano la direzione dalla candela di
|
||||
> breakout `close[i]` ma entravano a `close[i-1]` — impossibile dal vivo. Sotto
|
||||
> ingresso onesto (`close[i]`) e fee reali, l'edge sparisce e tutte perdono, anche
|
||||
> a fee zero. Dettagli e prove: `scripts/analysis/oos_validation.py`.
|
||||
## Cos'è vivo, adesso
|
||||
|
||||
Dopo una validazione **out-of-sample, fee-aware** di molte famiglie di strategie,
|
||||
emergono cinque famiglie con edge netto reale, tutte radicate nella stessa lezione
|
||||
(in cripto la **mean-reversion** funziona, la continuazione no) o nella diversificazione:
|
||||
| | |
|
||||
|---|---|
|
||||
| libro live | **TP01 + SKH01 a 75/25**, nettati in software su una sola posizione per asset (50/50 BTC/ETH) |
|
||||
| venue | Deribit mainnet, perpetual lineari USDC. Esecuzione **armata** dal 2026-06-20 |
|
||||
| capitale | ~$2.050 (USDC + USDE), non i €2.000 nominali di nessun paper trader |
|
||||
| cadenza | **oraria**, minuto `:47` (`scripts/cron_book.sh`) |
|
||||
| leva | lorda ~0,28x su un tetto di 1,00x. Il cap non ha mai morso: **il vincolo è il segnale** |
|
||||
| protezione on-book | un solo disaster-SL rotolante al −30% sulla posizione netta |
|
||||
|
||||
| Famiglia | Meccanismo | Strategie | Profilo (netto OOS) |
|
||||
|----------|-----------|-----------|---------------------|
|
||||
| **FADE** | mean-reversion intraday 1h (long/short, BTC/ETH) | MR01 Bollinger, MR02 Donchian, MR07 Return-reversal | Acc 52-55%, DD 18-34% |
|
||||
| **HONEST** | long-only multi-regime multi-crypto | DIP01 dip-buy, TR01 EMA-trend, ROT02 dual-momentum | CAGR 31-56%, DD 15-27% |
|
||||
| **PAIRS** | spread reversion *market-neutral* (2 gambe) | PR01 ETH/BTC, LTC/ETH, ADA/ETH, BTC/LTC, ETH/SOL | Sharpe 2.0-4.4, corr col mercato ~0.05 |
|
||||
| **TSMOM** | time-series momentum multi-orizzonte | TSM01 (3/6/12m + risk-off) | diversificatore, DD 15-22% |
|
||||
| **SHAPE** | ML walk-forward su feature di *forma* del prezzo | SH01 (LogisticRegression, orizzonte 12 barre) | diversificatore, corr +0.08 col resto |
|
||||
**Non** sono nel libro live: XS01, VRP01, GTAA01, XSR01. Vivono nel portafoglio di **ricerca**
|
||||
(paper, 5 sleeve), che è una serie diversa da quella che gira — quasi tutti i numeri di portafoglio
|
||||
del progetto sono su quella, non su questa.
|
||||
|
||||
Tutti i numeri sono **netti** dopo fee realistiche (Deribit 0.10% RT single-leg, 0.20%
|
||||
RT/coppia sui pairs), leva 3x, su finestra held-out. Le strategie sono robuste su griglia
|
||||
parametri, sweep fee 0.00-0.20% RT e — per i pairs — validate con **walk-forward** e
|
||||
config universale (niente cherry-picking).
|
||||
## I numeri, con la loro lente
|
||||
|
||||
### Portafoglio combinato (la vera leva anti-drawdown)
|
||||
Ogni numero va scritto con la lente e la banda che gli appartengono. La tabella completa
|
||||
("non citare" ↔ "citare") è in `CLAUDE.md` §2; qui i tre che contano.
|
||||
|
||||
Le famiglie sono **quasi scorrelate fra loro** (~0.05). Combinandole in un unico
|
||||
portafoglio equipesato il drawdown crolla sotto quello di ogni singola sleeve:
|
||||
| grandezza | valore onesto |
|
||||
|---|---|
|
||||
| rendimento del libro live | **TWR +10,6%** dall'armamento, spezzato sul versamento del 25/08: +11,61% prima, −0,9% dopo |
|
||||
| crescita di trading | **+$50** in 71 giorni, su 30 round-trip chiusi e $0,87 di fee totali |
|
||||
| Sharpe del portafoglio di ricerca | **1,95** [1,81 – 2,12] full, **1,54** [1,11 – 1,91] hold-out |
|
||||
|
||||
| Portafoglio | CAGR | Max DD | Sharpe |
|
||||
|-------------|------|--------|--------|
|
||||
| FADE (6 sleeve) | ~46% | 8% | 3.9 |
|
||||
| HONEST (3 sleeve) | ~46% | 13% | 2.2 |
|
||||
| **MASTER** (FADE + HONEST, 9) | ~47% | **5%** | 4.2 |
|
||||
| **MASTER + PAIRS + TSM01** (15) | ~67% | ~5% | ~6 |
|
||||
| **PORT06 live** (17 sleeve, cap pairs 33%, leva 2×, config EXIT-16) | ~79% | **2.6%** | 7.8 (FULL) / 10.1 (OOS) |
|
||||
⚠️ Il rendimento dell'equity **non** è la performance: il 96% della crescita del conto è un
|
||||
bonifico. Chi legge una percentuale su questo progetto deve sapere se è un TWR o un rapporto fra
|
||||
due saldi — è stato un difetto reale, riparato il 2026-09-02.
|
||||
|
||||
> 🔎 **Numeri sobri (anti-overfit).** L'OOS singolo cade nel regime favorevole 2024-25:
|
||||
> i valori di Sharpe/DD sopra sono ottimistici di circa il 50%. Da pianificare per le
|
||||
> decisioni: **Sharpe atteso ~5**, **worst-drawdown su 90 giorni ~6%**, profilo che regge
|
||||
> a leva 2x con slippage raddoppiato. Configurazione raccomandata: equal-weight, leva 2x,
|
||||
> con un cap sull'allocazione ai pairs (~30-35%, poiché concentrano ~57% del rischio).
|
||||
> Tutto resta da confermare nel paper trading live.
|
||||
## La riga che ordina tutto il resto
|
||||
|
||||
## Come funziona
|
||||
> La ricerca ha smesso di essere il vincolo il 2026-07-26, e **sei ondate successive lo hanno
|
||||
> confermato invece che ribaltarlo**. I vincoli sono **il capitale che entra** e **il conto che non
|
||||
> sparisce**.
|
||||
|
||||
### MR01 — Bollinger Fade (mean-reversion)
|
||||
Misurato: il miglior candidato nuovo vale **+0,046 €/giorno**; versare €500/mese invece di €250
|
||||
porta la probabilità di arrivare al traguardo in 20 anni **dal 14% all'85%**. E €100/mese in più
|
||||
equivalgono a **+4,07%/anno di drift**, cioè più di tutta la leva autorizzabile.
|
||||
|
||||
La strategia attiva sfrutta il fatto, emerso dai dati, che su BTC/ETH a 1h gli estremi
|
||||
di prezzo **rientrano verso la media** più di quanto proseguano:
|
||||
## Metodo — cosa deve superare una strategia nuova
|
||||
|
||||
1. **Bollinger Bands** (window `n`, `k` deviazioni standard) sul close.
|
||||
2. **Entry** — quando il close esce *sotto* la banda inferiore → **long** (o *sopra* la superiore → **short**). Ingresso a `close[i]`, eseguibile dal vivo.
|
||||
3. **Take-profit** alla media mobile (il rientro atteso).
|
||||
4. **Stop-loss** a `sl_atr × ATR` oltre l'estremo; **time-limit** a `max_bars`.
|
||||
Sei requisiti, nessuno negoziabile (`CLAUDE.md` §8, gate in `scripts/research/alt/altlib.py`):
|
||||
|
||||
Nessun look-ahead: direzione e livelli sono calcolati con dati fino a `close[i]`.
|
||||
1. **ingresso eseguibile** — direzione e prezzo da dati fino a `close[i]`, mai l'estremo di una candela;
|
||||
2. **backtest netto** dopo fee Deribit realistiche, più la leva;
|
||||
3. **out-of-sample** held-out, robustezza su griglia, sweep fee;
|
||||
4. **liquidità e plausibilità** — un edge su un book fermo o su wick fantasma non è un edge;
|
||||
5. i **gate**: `marginal_vs_tp01` (Sharpe marginale, non assoluto), `study_family_honest` con
|
||||
deflated-Sharpe ≥ 0,95, `day_boundary_robust`, `anchor_luck_band`, `weights_tilt_null`;
|
||||
6. codice, test e diario.
|
||||
|
||||
### Le altre famiglie
|
||||
Il progetto ha **73 regole di prim'ordine** (`CLAUDE.md` §6) — sul dato, sul metodo, sui costi,
|
||||
sulla produzione e sul piano — ognuna pagata almeno una volta. La più
|
||||
ricorrente, cinque occorrenze: *un sorvegliante deve derivare il proprio bersaglio dal codice
|
||||
sorvegliato, mai ridichiararlo — un controllo puntato su una configurazione diversa da quella che
|
||||
gira passa sempre, e non sta controllando niente.*
|
||||
|
||||
- **FADE** (oltre MR01): MR02 fada la rottura del canale Donchian verso il centro;
|
||||
MR07 fada il movimento di barra estremo misurato in deviazioni standard dei
|
||||
rendimenti. Stessa logica di reversione, indicatori indipendenti.
|
||||
- **HONEST** (long-only, multi-crypto): DIP01 compra i dip estremi e rivende al
|
||||
recupero; TR01 segue il trend con incrocio di EMA su un paniere; ROT02 ruota ogni
|
||||
giorno sui tre asset col momentum più forte, andando in cash quando BTC è sotto la
|
||||
sua media (risk-off). Coprono i regimi di trend e rotazione, complementari alle fade.
|
||||
- **PAIRS** (market-neutral): scommette sul rientro verso la media del log-ratio fra
|
||||
due cripto (z-score). Long su una, short sull'altra: l'esposizione netta al mercato è
|
||||
quasi nulla (correlazione ~0.02), il che la rende un diversificatore eccellente.
|
||||
- **TSMOM**: tiene gli asset con momentum positivo persistente su più orizzonti
|
||||
(3/6/12 mesi), con overlay risk-off. Rende meno ma è poco correlato, utile in ensemble.
|
||||
## Il dato
|
||||
|
||||
### Perché lo squeeze breakout è stato abbandonato
|
||||
- **La verità è Deribit mainnet**, perché è dove si esegue. Binance è un audit indipendente, mai
|
||||
un'ancora per "ripulire": è USDT, ~10 bps fuori, fino al 3% sotto depeg.
|
||||
- Universo certificato: **solo BTC/ETH**, ogni timeframe. Gli alt sono esclusi.
|
||||
- Lo storico si aggiorna **solo** con `rebuild_history.py` e si certifica **sempre** con
|
||||
`certify_feed.py`. Il vecchio downloader è la causa del reset.
|
||||
- La catena opzioni si **raccoglie** ogni ora (`collect_chain.py`, minuto `:25`): un'ora non
|
||||
raccolta è persa per sempre.
|
||||
|
||||
L'ipotesi originale era opposta — *continuazione* dopo la compressione di volatilità
|
||||
(Bollinger dentro Keltner → breakout direzionale). Su dati storici sembrava dare
|
||||
76-82% di accuracy, ma era un **artefatto di look-ahead**: il backtest entrava a
|
||||
`close[i-1]` con direzione decisa da `close[i]`. Replicando l'esecuzione reale
|
||||
(ingresso a `close[i]`) l'edge collassa al ~47% (lancio di moneta) e i costi fanno
|
||||
il resto. Il test sui breakout intra-barra a 5m conferma che il movimento *rientra*
|
||||
subito (mean-reversion), giustificando MR01. Tutta la famiglia squeeze è in `scripts/waste/`.
|
||||
|
||||
### Lezione metodologica
|
||||
|
||||
Ogni nuova strategia deve passare: (1) **ingresso eseguibile** senza look-ahead,
|
||||
(2) backtest **netto** dopo fee realistiche (0.10% RT Deribit), (3) validazione
|
||||
**out-of-sample** + robustezza su griglia parametri + sweep fee. Strumenti in
|
||||
`scripts/analysis/` (`strategy_research.py`, `oos_validation.py`, `intrabar_test.py`).
|
||||
|
||||
## Struttura progetto
|
||||
## Struttura
|
||||
|
||||
```
|
||||
PythagorasGoal/
|
||||
├── src/
|
||||
│ ├── data/ # Download e gestione dati (Cerbero MCP + Binance)
|
||||
│ ├── fractal/ # Indicatori frattali: Hurst, Higuchi FD, self-similarity
|
||||
│ ├── backtest/ # Motore di backtesting con fee e metriche
|
||||
│ ├── strategies/ # Classe base Strategy ABC + indicatori condivisi
|
||||
│ │ ├── base.py # Strategy, Signal, BacktestResult, YearlyStats
|
||||
│ │ └── indicators.py # keltner_ratio, detect_squeezes, ema, atr, rv, corr
|
||||
│ ├── live/ # Paper trading live su Deribit testnet
|
||||
│ │ ├── multi_runner.py # Orchestratore multi-strategia (strategie + pairs)
|
||||
│ │ ├── strategy_worker.py # Worker single-leg con stato persistente
|
||||
│ │ ├── pairs_worker.py # Worker a 2 gambe per i pairs (market-neutral)
|
||||
│ │ ├── strategy_loader.py # Import dinamico classi Strategy
|
||||
│ │ ├── cerbero_client.py # Client HTTP per Cerbero MCP
|
||||
│ │ ├── signal_engine.py # Squeeze + ML real-time (legacy) + validazione OOS
|
||||
│ │ └── telegram_notifier.py
|
||||
│ └── portfolio/ # Portafogli di prima classe (capitale condiviso, backtest + live)
|
||||
│ ├── base.py # SleeveSpec, Portfolio (.backtest), load_active_portfolio
|
||||
│ ├── weighting.py # Schemi di ponderazione: equal, cap, inverse_vol, cluster_rp, manual
|
||||
│ ├── sleeves.py # Builder unificato equity-per-sleeve (fonte unica, parità report)
|
||||
│ ├── ledger.py # PortfolioLedger: PnL/DD aggregati, persistenza e resume
|
||||
│ └── runner.py # PortfolioRunner live (Cerbero v2, sizing, ribilancio giornaliero)
|
||||
├── scripts/
|
||||
│ ├── strategies/ # Strategie con edge validato OOS (FADE, HONEST, PAIRS, TSMOM + portafogli)
|
||||
│ ├── portfolios/ # Definizioni PORT01-06 e report run() dei portafogli di prima classe
|
||||
│ ├── waste/ # Strategie scartate (squeeze SQ/MT/ML/AD/CM/PD, MR03, ROT01, W01-W28)
|
||||
│ └── analysis/ # Ricerca/validazione OOS fee-aware, gestione rischio, report
|
||||
├── strategies.yml # Config multi-strategy paper trader
|
||||
├── data/
|
||||
│ ├── raw/ # Parquet OHLCV (gitignored, ~70 MB)
|
||||
│ └── regime/ # DVOL + funding (Deribit mainnet) + cache feature regime (gitignored)
|
||||
├── VERSION # versione semver (cotta nell'immagine, mostrata nei msg Telegram)
|
||||
├── docs/
|
||||
│ ├── diary/ # Diario di ricerca giornaliero
|
||||
│ └── specs/ # Specifiche di design
|
||||
├── Dockerfile
|
||||
├── docker-compose.yml
|
||||
└── pyproject.toml
|
||||
src/
|
||||
data/downloader.py load_data(asset, tf) sui parquet certificati
|
||||
strategies/ trend_portfolio.py (TP01) · skyhook.py (SKH01) · base · indicators
|
||||
portfolio/ portfolio.py (N sleeve + weights_tilt_null) · sleeves.py · gtaa.py
|
||||
backtest/harness.py backtest onesto, senza look-ahead
|
||||
live/ book.py (esecutore netto) · deribit · livefeed · usde
|
||||
venue_watch · venue_probe · venue_news · monitor_health · scale_watch
|
||||
tradesdb · journal · analista · notifier · cli
|
||||
scripts/
|
||||
live/ book_execute · trades_db · journal · analista · balance_watch
|
||||
paper_* (forward-monitor) · usde_watch · usde_convert · fee_watch
|
||||
research/ r<data>_*.py — un file per esperimento, harness in alt/altlib.py
|
||||
analysis/ rebuild_history · certify_feed · audit_feed · multi_source_check
|
||||
cron_{book,daily,chain,balance,usde,opt_snapshot,vol_term}.sh
|
||||
docs/
|
||||
memory/ LA MEMORIA — 6 file, indicizzati in testa a CLAUDE.md
|
||||
research/ RESULTS-0822 (§1-77, un registro per filone) · BRIEF-0822 · SPEC-scale-key
|
||||
diary/ una voce per esperimento (144)
|
||||
journal/ libro di bordo, una voce al giorno, 4 livelli per provenienza
|
||||
tests/ 1008, tutti verdi
|
||||
Old/ archivio pre-reset — consultabile, non fidato
|
||||
```
|
||||
|
||||
## Strategie attive
|
||||
|
||||
Le strategie single-asset estendono `src.strategies.base.Strategy`
|
||||
(`generate_signals() → backtest()`); i pairs hanno un worker dedicato a 2 gambe.
|
||||
|
||||
| Codice | Script | Famiglia | Descrizione |
|
||||
|--------|--------|----------|-------------|
|
||||
| **MR01** | `MR01_bollinger_fade.py` | FADE | Fada la banda di Bollinger, TP alla media, SL ad ATR |
|
||||
| **MR02** | `MR02_donchian_fade.py` | FADE | Fada la rottura del canale Donchian, TP al centro |
|
||||
| **MR07** | `MR07_return_reversal.py` | FADE | Fada il movimento di barra estremo (z dei rendimenti) |
|
||||
| **DIP01** | `DIP01_dip_reversion.py` | HONEST | Dip-buy long-only su z-score estremo |
|
||||
| **TR01** | `TR01_ema_trend.py` | HONEST | EMA 20/100 trend-following su paniere cripto (4h) |
|
||||
| **ROT02** | `ROT02_dual_momentum.py` | HONEST | Rotazione cross-sectional top-3 + risk-off (1d) |
|
||||
| **PR01** | `PR01_pairs_reversion.py` | PAIRS | Spread reversion market-neutral su 5 coppie |
|
||||
| **TSM01** | `tsmom_research.py` | TSMOM | Time-series momentum multi-orizzonte + risk-off |
|
||||
| **SH01** | `SH01_shape_ml.py` | SHAPE | LogisticRegression walk-forward su 17 feature di forma, orizzonte 12 barre (diversificatore) |
|
||||
|
||||
Le fade applicano tre protezioni live: un **filtro trend** (`trend_max`/`ema_long`,
|
||||
salta i segnali col prezzo troppo esteso rispetto alla EMA200), un **loss-guard Hurst**
|
||||
(`hurst_max=0.55`, salta i segnali in regime persistente/trending dove si concentrano gli stop-loss
|
||||
— dimezza il drawdown del portafoglio, calcolato dalle sole close) e l'**EXIT-16 close-confirm SL**
|
||||
(`sl_confirm_atr=0.5`, 2026-06-04: lo stop scatta solo se la barra *chiude* oltre `sl ∓ 0.5·ATR14` —
|
||||
gli stop intrabar da wick erano falsi negativi, l'overshoot che buca lo stop è proprio il movimento
|
||||
che la fade fada; a livello PORT06 porta l'OOS Sharpe da 8.82 a 10.06). Più un filtro `min_tp_frac`
|
||||
che scarta i micro-scalp col take-profit entro il costo delle fee. Le tre protezioni sono
|
||||
complementari: Hurst toglie il regime tossico, il trend-filter gli ingressi sovra-estesi, il
|
||||
close-confirm i falsi stop. Portafogli pronti: `PORT01`
|
||||
(honest), `PORT02` (fade), `PORT03` (master fade+honest), **`PORT06`** (master esteso, default live).
|
||||
|
||||
**Scartate** (in `scripts/waste/`): la famiglia squeeze (SQ01-04, ML01, MT01, PD01,
|
||||
CM01, AD01 — artefatto di look-ahead), MR03 Keltner (debole/ridondante con MR01) e
|
||||
ROT01 (dominata da ROT02).
|
||||
|
||||
### Comandi utili
|
||||
## Comandi
|
||||
|
||||
```bash
|
||||
# Backtest di una strategia
|
||||
uv run python scripts/strategies/MR01_bollinger_fade.py
|
||||
uv run python scripts/strategies/PR01_pairs_reversion.py
|
||||
|
||||
# Ricerca e validazione fee-aware out-of-sample
|
||||
uv run python scripts/analysis/strategy_research.py # screening famiglie + deep-dive fade
|
||||
uv run python scripts/analysis/strategy_research_v2.py # MR02 / MR03 / MR07
|
||||
uv run python scripts/analysis/oos_validation.py # perche' la famiglia squeeze e' scartata
|
||||
uv run python scripts/analysis/pairs_research.py # ricerca + verifica no-look-ahead dei pairs
|
||||
|
||||
# Gestione rischio, combinazione, report
|
||||
uv run python scripts/analysis/risk_management.py # filtro trend + portafoglio fade
|
||||
uv run python scripts/analysis/combine_portfolio.py # combinare fade + honest
|
||||
uv run python scripts/analysis/combine_v2.py # master esteso con pairs + TSM01
|
||||
uv run python scripts/analysis/report_families.py # report per anno di tutte le famiglie
|
||||
|
||||
# Validazione dei worker live (replay == backtest)
|
||||
uv run python scripts/analysis/validate_worker_mr01.py # worker single-leg su MR01
|
||||
uv run python scripts/analysis/validate_worker_pairs.py # worker a 2 gambe sui pairs
|
||||
uv run python scripts/analysis/live_smoke_pairs.py # smoke test feed live reale dei pairs
|
||||
uv sync # dipendenze
|
||||
uv run python scripts/analysis/rebuild_history.py --asset BTC ETH # storico da Deribit mainnet
|
||||
uv run python scripts/analysis/certify_feed.py # certifica i feed
|
||||
uv run python scripts/live/trades_db.py --report # stato del libro + TWR
|
||||
uv run python scripts/live/journal.py # voce del giorno
|
||||
uv run python scripts/live/analista.py --secco # analisi del giorno, senza salvare
|
||||
uv run python scripts/portfolio/run_portfolio.py # portafoglio di ricerca
|
||||
uv run pytest # 1008 test
|
||||
```
|
||||
|
||||
## Paper Trading Live
|
||||
Ogni script di `scripts/live/` accetta `--help` e **rifiuta un flag che non conosce** (esce 2): fino
|
||||
al 2026-09-02 un flag sbagliato eseguiva l'azione di default, e su tre script quell'azione scriveva.
|
||||
|
||||
Il multi-strategy runner esegue N strategie in parallelo su dati live da Cerbero MCP,
|
||||
ognuna con €1000 USDC virtuali indipendenti. Gestisce due tipi di worker:
|
||||
## Gate aperti
|
||||
|
||||
- **Single-leg** (`strategy_worker.py`): per le strategie direzionali. Se un `Signal`
|
||||
porta `tp`/`sl`/`max_bars` in `metadata` (come le fade), chiude su take-profit /
|
||||
stop-loss / time-limit; altrimenti usa il fallback `hold_bars`/stop -2%.
|
||||
- **Due gambe** (`pairs_worker.py`): per i pairs market-neutral. Apre long su una gamba
|
||||
e short sull'altra, esce sul rientro dello z-score o per time-limit, conta le fee su
|
||||
entrambe le gambe. Validato: il replay storico coincide *esattamente* col backtest.
|
||||
Un gate si decide **alla data**, coi criteri scritti **prima**. Elenco completo in `CLAUDE.md` §4.
|
||||
|
||||
### Avvio
|
||||
| gate | data | dove punta oggi |
|
||||
|---|---|---|
|
||||
| STATARB | 2026-09-27 | ritiro (Sharpe −1,61 sulla serie vera) |
|
||||
| SCALA-01 — leva 1,00 → 1,25 | non prima del 2026-10-01 | chiave costruita e **inerte**; 9 condizioni, 2 fatte |
|
||||
| XSR01 | 2026-10-23 | sotto la lente rendita il meccanismo vale ~0 |
|
||||
| DVOLSPREAD | kill 2026-10-24 | serie −4,41 |
|
||||
| GTAA01 tenere/bloccare | al book a $15k | contributo reale +0,10/+0,12 di Sharpe, non incassabile sotto la soglia |
|
||||
|
||||
```bash
|
||||
# Locale
|
||||
uv run python -m src.live.multi_runner
|
||||
## Obiettivo, e cosa lo rende difficile
|
||||
|
||||
# Docker
|
||||
docker compose up -d
|
||||
```
|
||||
Il target dichiarato è **€50/giorno partendo da €1.000**. Non è raggiungibile a questo capitale:
|
||||
servono ~$313k (banda [$187k – $1,14M]) o **€1.733/mese per dieci anni** per averlo al 90% di
|
||||
probabilità. La leva non è la scorciatoia — quella difendibile vale quanto €100-150/mese di
|
||||
versamento. La via è target-vol, capitale e tempo.
|
||||
|
||||
### Configurazione
|
||||
|
||||
Le strategie attive sono definite in `strategies.yml`:
|
||||
|
||||
```yaml
|
||||
defaults:
|
||||
capital: 1000
|
||||
position_size: 0.15
|
||||
leverage: 3
|
||||
|
||||
strategies: # strategie single-leg
|
||||
- name: MR01_bollinger_fade
|
||||
asset: BTC
|
||||
tf: 1h
|
||||
enabled: true
|
||||
params: { bb_window: 50, k: 2.5, sl_atr: 2.0, max_bars: 24, trend_max: 3.0, ema_long: 200 }
|
||||
|
||||
pairs: # strategie a 2 gambe (market-neutral)
|
||||
- name: PR01_pairs_reversion
|
||||
a: ETH
|
||||
b: BTC
|
||||
tf: 1h
|
||||
enabled: true
|
||||
params: { n: 50, z_in: 2.0, z_exit: 0.75, max_bars: 72, jump_max: 0.08 }
|
||||
```
|
||||
|
||||
Per aggiungere una strategia: nuova riga in `strategies.yml` (sezione `strategies` o
|
||||
`pairs`), poi `docker compose restart`. Lo storico delle strategie esistenti rimane intatto.
|
||||
|
||||
### Persistenza
|
||||
|
||||
Ogni strategia ha la sua directory in `data/paper_trades/`:
|
||||
|
||||
```
|
||||
data/paper_trades/
|
||||
MR01_bollinger_fade__BTC__1h/
|
||||
trades.jsonl # Storico trade append-only
|
||||
status.json # Stato corrente (resume al restart, include tp/sl/max_bars)
|
||||
```
|
||||
|
||||
Notifiche Telegram per ogni trade (richiede `TELEGRAM_BOT_TOKEN` e `TELEGRAM_CHAT_ID` in `.env`).
|
||||
|
||||
## Paper Trading a Portafoglio
|
||||
|
||||
Accanto al multi-strategy runner originale — in cui ogni strategia gestisce autonomamente il proprio conto virtuale da €1.000 — il progetto dispone ora di un **paper trader a portafoglio** (`src/portfolio/`) che tratta l'insieme delle strategie come un unico organismo con un capitale condiviso.
|
||||
|
||||
### Come funziona
|
||||
|
||||
La definizione di un portafoglio (`SleeveSpec` + schema di peso) ha due facce sulla stessa sorgente dati:
|
||||
|
||||
- **Backtest** (`.backtest()`): ricostruisce le equity-curve di ogni sleeve tramite il builder unificato in `sleeves.py`, le pondera secondo lo schema scelto e calcola le metriche aggregate (CAGR, Sharpe, max DD). La parità con i report prodotti da `report_families.py` è garantita dalla fonte unica.
|
||||
- **Live** (`PortfolioRunner`): ogni ora il runner scarica le candele aggiornate via Cerbero v2, calcola i pesi correnti, avvia i worker appropriati per ogni sleeve attiva e registra il PnL aggregato nel ledger (`data/portfolios/{code}/`). Il ledger persiste tra i riavvii.
|
||||
|
||||
### Schemi di ponderazione
|
||||
|
||||
Il modulo `weighting.py` mette a disposizione cinque schemi: `equal` (default), `cap` (tetto per famiglia — p.es. `pairs: 0.33` per limitare la concentrazione), `inverse_vol` (pesi inversamente proporzionali alla volatilità storica), `cluster_rp` (equal tra cluster naturali poi inverse-vol all'interno del cluster) e `manual` (pesi liberi). Lo schema si specifica in `portfolios.yml` insieme al codice portafoglio e alla leva.
|
||||
|
||||
### Portafoglio di default: PORT06
|
||||
|
||||
La configurazione raccomandata è **PORT06** (`scripts/portfolios/PORT06_master_shape.py`): portafoglio master esteso che include tutte e sei le famiglie (FADE, HONEST, PAIRS, TSMOM, SHAPE), con schema `cap` che limita i pairs al 33% del capitale per moderare la loro concentrazione di rischio. Backtest canonico (dati al 2026-05-28): Sharpe 6.47 (FULL) / 8.82 (OOS), drawdown massimo 4.10% (FULL) / 1.30% (OOS), leva 2×; **con la config live attuale (EXIT-16 close-confirm): Sharpe 7.84 / 10.06, DD 2.60% / 1.15%**.
|
||||
|
||||
### Scope live
|
||||
|
||||
Il runner esegue **tutti e 17 gli sleeve** di PORT06: **fade** (MR01, MR02, MR07 × BTC/ETH),
|
||||
**honest** (DIP01, TR01-basket 4h, ROT02-rotation 1d), **pairs** (PR01, cinque coppie),
|
||||
**TSMOM** (TSM01 1d) e **shape** (SH01 × BTC/ETH). Worker dedicati: `StrategyWorker` (single-leg, fade/
|
||||
dip/**shape**), `PairsWorker` (2 gambe), `BasketTrendWorker`, `RotationWorker`, `TsmomWorker`. Il runner
|
||||
fetcha 1h da Cerbero v2 e resampla a 4h/1d; il pool di capitale, il ribilancio giornaliero e il ledger
|
||||
sono validati == backtest.
|
||||
|
||||
> **SH01 (2026-06-01):** gira come `StrategyWorker` normale (il walk-forward è interno a
|
||||
> `generate_signals`). Il vecchio `MLWorkerWrapper` usava il `SignalEngine` **squeeze scartato** —
|
||||
> rimosso. **Loss-guard Hurst (2026-06-02):** le fade saltano i segnali in regime persistente
|
||||
> (rolling-Hurst ≥ 0.55), dove si concentrano gli stop-loss — dimezza il drawdown del portafoglio
|
||||
> (FULL 4.1%→2.4%; stop-loss fade −67% in numero, perdite totali −68%). Calcolato dalle sole close,
|
||||
> attivo live (`hurst_max` nei params). Il report orario su Telegram **monitora lo stop-rate fade
|
||||
> prima/dopo l'attivazione** e dà il verdetto automatico quando il campione è sufficiente.
|
||||
|
||||
### Esecuzione reale (shadow, Deribit testnet)
|
||||
|
||||
Sette sleeve single-leg — le **6 fade** (MR01/MR02/MR07 × BTC/ETH) e **DIP01** (dal 2026-06-04) —
|
||||
eseguono ordini **reali su Deribit testnet** accanto al fill simulato (*shadow*: il sim resta la
|
||||
verità che guida le decisioni; il reale misura la fattibilità). Punti chiave:
|
||||
|
||||
- **Strumenti lineari USDC** (`BTC_USDC`/`ETH_USDC-PERPETUAL`): payoff lineare = matematica del
|
||||
backtest; fee e PnL in USDC. Quantizzazione `Decimal` di amount (step) e prezzi (tick).
|
||||
- **Take-profit reale = limit reduce-only AL livello** (v1.0.7): piazzato all'apertura, copre la
|
||||
sola quota del worker (gli strumenti sono condivisi fra worker e nettati per conto); alla
|
||||
chiusura il worker cancella il resting, riconcilia i fill dal trade history per `order_id` e
|
||||
chiude a market solo il residuo. Fix della divergenza misurata: il market-on-poll usciva
|
||||
+235 bps oltre il livello TP. Fill da resting = fee maker (~0%).
|
||||
- **Stop-loss close-confirm** (v1.1.0): uscita al close che sfonda il livello → market
|
||||
reduce-only al poll (nessun ordine stop sul book, per scelta: i trigger Deribit generano un
|
||||
nuovo order_id allo scatto, non verificabile, e i wick non devono stoppare).
|
||||
- **Verifica sul trade** (order_id in `get_trade_history`), fee reali dai `trades[]`, ledger
|
||||
reale parallelo persistito (`real_capital`), eventi `REAL_OPEN`/`REAL_TP_RESTING`/`REAL_CLOSE`
|
||||
nel log + alert Telegram (`REAL_EXEC_LIVE`, `REAL_OPEN_FAIL`).
|
||||
- Config in `portfolios.yml` → `overrides.execution {enabled, sleeves, instruments}`.
|
||||
**Pairs/rotation/TSMOM/shape restano simulati**: i pairs richiedono un executor a 2 gambe
|
||||
(leg-risk), i multi-asset un rebalance-to-target; roadmap nel diario.
|
||||
|
||||
### Versione & deploy
|
||||
|
||||
Ogni deploy ha una **versione** (file `VERSION`, semver) che compare nei messaggi Telegram (notifiche
|
||||
trade + report orario), così correli ogni messaggio al codice che l'ha generato. Il sorgente è **cotto
|
||||
nell'immagine** → per aggiornare il live serve un **rebuild**, non un semplice restart:
|
||||
|
||||
```bash
|
||||
./scripts/deploy.sh # bump patch (1.0.0 → 1.0.1) + commit + rebuild + ricrea container
|
||||
./scripts/deploy.sh minor # 1.0.x → 1.1.0
|
||||
```
|
||||
|
||||
Il volume `data/` persiste tra i deploy → i worker fanno RESUME dello stato (capitale, posizioni aperte).
|
||||
|
||||
### Avvio del paper trader a portafoglio
|
||||
|
||||
```bash
|
||||
# Backtest del portafoglio di default (PORT06)
|
||||
uv run python scripts/portfolios/PORT06_master_shape.py
|
||||
|
||||
# Paper trading live a portafoglio
|
||||
uv run python -m src.portfolio.runner
|
||||
|
||||
# Report orario su Telegram (stato + stop-rate fade prima/dopo loss-guard) — via cron
|
||||
uv run python scripts/portfolios/hourly_report.py
|
||||
|
||||
# Smoke test del data layer Cerbero v2
|
||||
uv run python scripts/analysis/smoke_portfolio.py
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
# Clona e installa
|
||||
git clone <repo-url> && cd PythagorasGoal
|
||||
uv sync
|
||||
|
||||
# Scarica dati storici (~70 MB)
|
||||
uv run python -m src.data.downloader
|
||||
|
||||
# Backtest strategia attiva
|
||||
uv run python scripts/strategies/MR01_bollinger_fade.py
|
||||
|
||||
# Paper trading live
|
||||
uv run python -m src.live.multi_runner
|
||||
```
|
||||
|
||||
### Requisiti
|
||||
|
||||
- Python ≥ 3.11
|
||||
- [uv](https://docs.astral.sh/uv/) come package manager
|
||||
- Accesso a Cerbero MCP (`cerbero-mcp.tielogic.xyz`) per dati Deribit live
|
||||
- Docker (opzionale, per deploy su VPS)
|
||||
|
||||
## Dati
|
||||
|
||||
| Asset | Timeframe | Copertura |
|
||||
|-------|-----------|-----------|
|
||||
| BTC, ETH | 5m / 15m / 1h | 2018-01 → oggi |
|
||||
| SOL, LTC, ADA, XRP, BNB, DOGE | 15m / 1h | 2019-2022 → oggi (variabile per asset) |
|
||||
|
||||
Fonte primaria: perpetual Deribit via Cerbero MCP. Fallback: Binance spot via ccxt.
|
||||
Formato: Apache Parquet (in `data/raw/`, gitignored).
|
||||
|
||||
> **Nota sul naming Deribit (per il feed live).** I major sono perpetui *inverse*
|
||||
> (`BTC-PERPETUAL`, `ETH-PERPETUAL`); gli altcoin sono perpetui *lineari USDC*
|
||||
> (`SOL_USDC-PERPETUAL`, `LTC_USDC-PERPETUAL`, …) con storia dal 2022. Attenzione:
|
||||
> `LTC-PERPETUAL`/`ADA-PERPETUAL` non esistono e `SOL-PERPETUAL` restituisce dati
|
||||
> errati — per gli altcoin usare sempre la forma `_USDC-PERPETUAL`.
|
||||
|
||||
### Discovery & validazione strumenti
|
||||
|
||||
`src/data/instruments.py` scopre e **valida** gli strumenti disponibili sugli
|
||||
exchange implementati — **Deribit** e **Hyperliquid** (esclusi Alpaca/stocks e
|
||||
**Bybit**, feed testnet inaffidabile). Ogni perpetuo viene testato sui dati
|
||||
storici realmente raccoglibili: esistenza, congruenza OHLC, contratto non-morto,
|
||||
liquidità e **congruenza prezzo cross-exchange** (mediana per base-coin, tolleranza
|
||||
5%) — così feed farlocchi e contratti sbagliati (es. `SOL-PERPETUAL`=9.6) vengono
|
||||
scartati. Il risultato è `data/instruments_registry.json` (strumenti validi +
|
||||
timeframe + data d'inizio).
|
||||
|
||||
**Solo gli strumenti validati possono essere scaricati**: il downloader ha un gate
|
||||
(`_download_cerbero_range`) che rifiuta quelli non nel registry. Rigenera con:
|
||||
|
||||
```bash
|
||||
uv run python -m src.data.instruments
|
||||
```
|
||||
|
||||
Simboli Deribit: BTC/ETH = `<COIN>-PERPETUAL` (inverse); altcoin =
|
||||
`<COIN>_USDC-PERPETUAL` (lineari USDC). Registry attuale (testnet): Deribit 18/106
|
||||
validi (major liquidi, BTC dal 2018), Hyperliquid 66/74.
|
||||
|
||||
## Riferimenti
|
||||
|
||||
- Serleto, L. & Malanga, C. — *Pythagoras Trading Prediction* (2024)
|
||||
- Serleto, L. & Malanga, C. — *Libro dei Frattali* (2024)
|
||||
|
||||
## Licenza
|
||||
|
||||
Uso privato. Non destinato alla distribuzione.
|
||||
**Onestà prima di tutto**: nessun numero va creduto finché non è netto fee, out-of-sample, robusto
|
||||
su griglia, e su dati certificati, liquidi ed eseguibili. Il resto di questo repo esiste per rendere
|
||||
quella frase verificabile invece che dichiarata.
|
||||
|
||||
+10
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"_nota": "Config esecuzione LIVE del BOOK DERIBIT (TP01+SKH01 nettati in software). execution_enabled=true + --execute -> ordini REALI. ARMATO 2026-06-23: esecutore scripts/live/book_execute.py via cron ORARIO scripts/cron_book.sh (SKH01 e' a 230m). disaster-SL on-book -30% sulla posizione netta. Tutto flat all'arming -> nessun ordine finche' un segnale non arma.",
|
||||
"_nota_cap": "Cap notional per-asset DINAMICO (frontiera 2026-07-03): con max_notional_per_asset_frac=0.5 il cap = equity/2, cosi' cresce col capitale e un deposito non resta strozzato. AGGIORNATO 2026-07-26: max_notional_per_asset_usd alzato 300 -> 3000 in previsione del versamento (EUR 5.000 + 500/mese -> equity ~$6.050, equity/2 ~$3.025). \u26a0\ufe0f Alzarlo NON e' pericoloso perche' dal 2026-07-26 il cap di FALLBACK (equity reale non leggibile) e' min(questo valore, ultima_equity_reale_osservata * frac) \u2014 vedi src/live/book._cap e il watermark data/live/equity_seen.json. Senza quel legame, un cap da $3.000 su un conto da $597 avrebbe permesso $2.000 di nozionale lordo = 3.35x di leva nel momento peggiore. Questo rende inutile l'azione manuale 'al deposito alzare il cap' (pre-registrata 2026-07-02).",
|
||||
"_nota_cap": "Cap notional per-asset DINAMICO (frontiera 2026-07-03): con max_notional_per_asset_frac=0.5 il cap = equity/2, cosi' cresce col capitale e un deposito non resta strozzato. AGGIORNATO 2026-07-26: max_notional_per_asset_usd alzato 300 -> 3000 in previsione del versamento (EUR 5.000 + 500/mese -> equity ~$6.050, equity/2 ~$3.025). ⚠️ Alzarlo NON e' pericoloso perche' dal 2026-07-26 il cap di FALLBACK (equity reale non leggibile) e' min(questo valore, ultima_equity_reale_osservata * frac) — vedi src/live/book._cap e il watermark data/live/equity_seen.json. Senza quel legame, un cap da $3.000 su un conto da $597 avrebbe permesso $2.000 di nozionale lordo = 3.35x di leva nel momento peggiore. Questo rende inutile l'azione manuale 'al deposito alzare il cap' (pre-registrata 2026-07-02).",
|
||||
"execution_enabled": true,
|
||||
"max_notional_per_asset_usd": 3000,
|
||||
"max_notional_per_asset_frac": 0.5,
|
||||
@@ -10,11 +10,17 @@
|
||||
"max_data_age_days": 2,
|
||||
"_nota_skh_feed": "Freschezza del feed 5m usato per il segnale SKH01 (2026-07-26). fresh_5m ricade sul feed certificato IN SILENZIO se il fetch pubblico Deribit fallisce, e il certificato si rigenera 1x/giorno: senza controllo la latenza d'uscita di SKH01 passa da ~1h a ~1 giorno senza segnalazione. Sopra soglia book_execute ALLERTA e NON blocca (bloccare fermerebbe anche TP01, nettato sullo stesso strumento, per un guasto di rete). Diario 2026-07-26-t1-esecuzione-skh-live.md.",
|
||||
"skh_feed_max_age_min": 30,
|
||||
"_nota_usde": "Collaterale USDE a rendimento (test di eligibilita' 2026-08-26: 500 USDE, verdetto entro il 29/08 — diario 2026-08-26-usde-analisi). L'autorita' che LEGGE questa sezione e' src/live/usde.py; la usano shadow._collaterale_usde (equity oraria del book) e scripts/live/usde_watch.py (sorveglianza giornaliera 12:35 UTC). Criteri delle soglie, dichiarati (P6): depeg_warn 0.99 = fuori dalla banda operativa dello spot (~3 bps) e oltre il clamp +-0.5% per fonte dell'indice usde_usdc — a quel prezzo non e' rumore di book; depeg_crit 0.95 = meta' del buffer di haircut (10%) consumata. quota_max_frac 0.50 = tetto di ALLERTA sulla quota USDE/equity totale (N4: la quota e' l'unica leva contro il rischio emittente, -100% = 25 anni di resa, non recuperabile); la quota EFFETTIVA la decide l'operatore, e vale anche per deriva PASSIVA (il libro perde -> la quota sale da sola). haircut 0.10 verificato sul venue il 26/08 (USDC 0%): il 90% dell'USDE fa margine.",
|
||||
"_nota_usde": "Collaterale USDE a rendimento (test di eligibilita' 2026-08-26: 500 USDE, verdetto entro il 29/08 — diario 2026-08-26-usde-analisi). L'autorita' che LEGGE questa sezione e' src/live/usde.py; la usano shadow._collaterale_usde (equity oraria del book) e scripts/live/usde_watch.py (sorveglianza giornaliera 12:35 UTC). Criteri delle soglie, dichiarati (P6): depeg_warn 0.99 = fuori dalla banda operativa dello spot (~3 bps) e oltre il clamp +-0.5% per fonte dell'indice usde_usdc — a quel prezzo non e' rumore di book; depeg_crit 0.95 = meta' del buffer di haircut (10%) consumata. quota_target 0.70 (CHIAVE TOLTA il 2026-09-10, issue #7: il bersaglio operativo e' DERIVATO dalle bande del cuscino, vedi usde._nota_cuscino — quanto segue e' la storia della decisione) = la quota DECISA dall'operatore il 2026-08-30 (la decisione di quota che il gate USDE-01 apriva; anticipata di un giorno sul rinvio al 31/08, su richiesta esplicita dell'operatore \"porta in usde tutto il capitale che non viene usato\"). Il 70% NON e' un argmax (M8): e' il massimo compatibile col CUSCINO DI REGOLAMENTO, cioe' il vincolo che r0830_usde_quota non aveva guardato — il P&L e il funding dei perp USDC-lineari si regolano in USDC, non nel collaterale, quindi a quota alta il saldo USDC va negativo alla prima perdita del libro e Deribit lo finanzia a interesse. Il cuscino richiesto e' il disaster-SL sulla massima esposizione lorda: n_asset x frac x disaster_sl_pct = 2 x 0.5 x 0.30 = 30% dell'equity, che lascia esattamente il 70%. quota_max_frac 0.50 = tetto di ALLERTA sulla quota USDE/equity totale (N4: la quota e' l'unica leva contro il rischio emittente, -100% = 25 anni di resa, non recuperabile). Alzato a 0.85 il 2026-08-30 in previsione della quota al 70% e RIMESSO A 0.50 lo stesso giorno perche' il 70% pareva irraggiungibile; RIMESSO A 0.85 il 2026-09-06, quando la quota e' arrivata al 68,7% su autorizzazione dell'operatore e la sonda non ha trovato tetto. 🚨 venue_cap_frac: il 06/09 il tetto NON C'ERA (null, vedi venue_cap_misurato) — quanto segue e' la storia del 30-31/08, tenuta perche' il tetto puo' tornare: venue_cap_frac 0.318 = TETTO DEL VENUE sull'USDE, misurato e NON documentato da Deribit (ne' 'Cross collateral specifications' ne' 'Yield-generating collateral' prevedono un limite sulle quantita' detenibili; il Cap ETHENA che esiste diluisce il TASSO a livello di exchange, non limita gli acquisti). Ogni acquisto oltre il tetto e' rifiutato con `not_enough_funds_in_currency` pur avendo $1.400 disponibili: messaggio FUORVIANTE. E' una FRAZIONE dell'equity, non un livello: provato il 31/08 lasciando scendere l'equity di $8, il tetto e' sceso con lei. ⚠️ DIPENDE DAL MODELLO DI MARGINE, ma pochissimo — misurato a saldo neutro sotto entrambi: SEGREGATO S:SM [643,18-644,18) con equity $2.055,56 = 31,29-31,34%; CROSS X:SM [654,18-655,18) con equity $2.054,90 = 31,84-31,88%. Il passaggio a cross ha comprato +0,55pp = **+11 USDE (~$11)**: il modello entra nel tetto, ma non lo spiega. ⇒ la quota resta ~31-32% sotto qualunque configurazione e il 70% di quota_target NON e' raggiungibile (manca un fattore ~2,2x). Il valore 0.318 e' il bordo BASSO del bracket CROSS, che e' il modello attivo: fa fallire il piano PRIMA dell'ordine. Se si torna a S:SM va rimesso a 0.312. haircut 0.05 — ✅ DIVERGENZA CHIUSA il 2026-08-31 (era 0.10, SBAGLIATO). La pagina margini del conto non espone l'haircut come numero: si RICAVA per differenza fra le due righe, perche' il modello CROSS conta l'USDE scontato e il SEGREGATO non lo conta affatto. Contributo dell'USDE al cross $610,81 su $643,05 di valore -> 5,0137%: l'ipotesi 5% torna a $0,09, la 10% sbaglia di $32,06. Riproducibile: `scripts/research/r0831_margini_conto.py` (N11). Il 26/08 il 10% fu registrato come 'verificato sul venue' senza lasciare traccia di come, e non lo era. 🚨 SCOPERTO NELLA STESSA LETTURA, e vale piu' dell'haircut: il modello di margine ATTIVO e' **Segregated: Standard Margin (S:SM)**, NON cross-collateral. Nella tabella del modello attivo l'USDE **non compare**: non fa margine per i perp USDC-settled del book. Percio' l'haircut oggi non si applica affatto — ed e' esattamente il motivo per cui la misura del 31/08 trovava $0,0000 accantonati. Passare a X:SM aggiungerebbe $610,87 di margine utilizzabile: e' una decisione dell'operatore, non un refactor, e porta con se' la meccanica cross (collateral fee 0,05%/giorno sul saldo negativo, ribilanciamento automatico).",
|
||||
"usde": {
|
||||
"index_name": "usde_usdc",
|
||||
"haircut": 0.10,
|
||||
"quota_max_frac": 0.50,
|
||||
"haircut": 0.05,
|
||||
"_nota_cuscino": "Dal 2026-09-10 (issue #7) la quota USDE NON e' un numero scelto: e' DERIVATA dal cuscino USDC di regolamento (n_asset x frac x disaster_sl_pct = 30% dell'equity) e da queste tre frazioni del cuscino, lette da src/live/usde.py per scripts/live/cuscino_watch.py (cron :53). cuscino_margine_frac 0.20 = bersaglio unico di ogni conversione: quota = 1 - 0.30 x 1.20 = 0.64 (il vecchio quota_target 0.70 lasciava slack ZERO: ogni ora in perdita avrebbe venduto). cuscino_preavviso_frac 0.10 = sotto, allerta. cuscino_riacquisto_frac 0.40 = sopra, si RICOMPRA USDE fino al bersaglio (un bonifico alza lo slack di 0,7 x importo e fa scattare il riacquisto da solo). Isteresi [0 ; 0.40] x cuscino con bersaglio a 0.20: oscillare richiede +-6% di equity di slack, cioe' +-8,6% di equity USDC (slack = 0,7 USDC - 0,3 USDE). Soglie DICHIARATE, non ottimizzate (M8): un giro costa ~6 bps sull'importo mosso. Ordine obbligato: 0 < preavviso < margine < riacquisto (usde.bande_cuscino lo verifica).",
|
||||
"cuscino_preavviso_frac": 0.1,
|
||||
"cuscino_margine_frac": 0.2,
|
||||
"cuscino_riacquisto_frac": 0.4,
|
||||
"venue_cap_frac": null,
|
||||
"venue_cap_misurato": "2026-09-06: NESSUN tetto fino al 68,7% (fermata dal cuscino di regolamento al 70%, non dal venue): +2.434 USDE in 39 ordini a saldo crescente, zero rifiuti (r0906_usde_tetto_sonda.py). Il 30-31/08 il tetto c'era: [643-644) e [654-655) USDE su ~$2.055 = 31,3-31,9%. Non spiegato: e' SPARITO, non e' scalato. null = nessun tetto noto; usde_convert si affida al chunk+backoff sui rifiuti",
|
||||
"quota_max_frac": 0.85,
|
||||
"depeg_warn": 0.99,
|
||||
"depeg_crit": 0.95
|
||||
}
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
# 2026-08-28 — La fase che ruota, gli allarmi che si perdevano, e una risposta fiscale
|
||||
|
||||
*Scritto il 2026-08-28. Numeri riprodotti dagli script citati; le letture in prosa sono firmate
|
||||
come tali (P13).*
|
||||
|
||||
## In una riga
|
||||
|
||||
Un test rosso su GTAA01 non parlava di GTAA01: parlava del fatto che **la finestra "congelata"
|
||||
pre-2015 rotola da sola ogni notte**. Nel frattempo si e' chiuso il debito del trasporto degli
|
||||
allarmi, e la prima delle due domande al commercialista ha trovato una risposta alla fonte.
|
||||
|
||||
---
|
||||
|
||||
## 1. Il gate (A) della banda GTAA01 e' una moneta — la finestra rotola
|
||||
|
||||
`r0828_gtaa_band_phase.py`. Replica bit-exact della produzione verificata (max|Δ| = 0.00e+00 su
|
||||
7.544 barre). Codice fermo dal 07/08: nessun commit su `src/portfolio/gtaa.py` ne' su
|
||||
`r0727_gtaa_band_gate.py`. Si e' mosso il DATO.
|
||||
|
||||
**(0) La finestra in-sample non e' ferma.** IB serve una finestra **rotolante di 30 anni**. SPY e'
|
||||
l'unica gamba al muro (30,0 anni) e nel `logs/cron_daily.log` la sua data d'inizio avanza di un
|
||||
giorno di borsa ogni notte: `1996-07-02` il 24/06 → `1996-09-04` oggi. QQQ e IWM arriveranno allo
|
||||
stesso muro fra **2,5 e 3,7 anni**.
|
||||
|
||||
**(1) L'amplificatore e' la fase.** `_gated_returns` ribilancia su `i % every == 0`, dove `i` e' la
|
||||
**posizione nell'array**. Se la prima barra scivola, si ri-fasa ogni decisione di ribilanciamento
|
||||
di trent'anni di storia.
|
||||
|
||||
| misura | valore |
|
||||
|---|---|
|
||||
| spostamento max dello Sharpe in-sample dal 07/08 (finestra CHIUSA, codice fermo) | **0,076** |
|
||||
| banda scelta al buio in 10 notti consecutive | 25% · 40% · 60% |
|
||||
| notti col margine sotto il pavimento dichiarato (0,01) | **4/10** (minimo 0,0026) |
|
||||
| a dato FISSO, muovendo solo la fase | 25% su 3 fasi, 60% su 2 |
|
||||
|
||||
Il criterio decideva su ~0,007-0,015 di Sharpe mentre il rumore che lo scuote vale fino a 0,076.
|
||||
E' lo stesso modo di fallire del criterio a ranghi ritirato il 07/08 (che decideva su 0,00116), in
|
||||
un costume nuovo.
|
||||
|
||||
## 2. Criterio (C): la mediana delle 5 fasi
|
||||
|
||||
`r0828_gtaa_band_median_phase.py`. ⚠️ **Scelto DOPO aver visto la tabella delle fasi**: misura lo
|
||||
strumento, non valida la banda. Pavimento tenuto a 0,01, invariato.
|
||||
|
||||
- (i) scelta stabile su 10 notti: **SI** — `60%` in tutte e dieci
|
||||
- (ii) margine sempre ≥ 0,01: **SI** — minimo 0,0197
|
||||
- (iii) potenza (al buio ≠ hold-out): **SI** — 60% contro 40%
|
||||
|
||||
Escursione dello Sharpe in-sample su 10 notti: **−74/−99%** per ogni banda. Lo strumento regge.
|
||||
**Ma la cella che sceglie non e' la proposta:** al buio esce il 60%, il 25% vince **0 notti su 10**.
|
||||
Da tenere accanto: sull'hold-out il 60% e' **penultimo** (0,7430 contro 0,9315 del 40%), coerente
|
||||
con lo Spearman IS/OOS ~0 misurato il 07/08 — la scelta in-sample **non predice**.
|
||||
|
||||
**Contorno che vale piu' del verdetto:** le 5 serie di fase correlano **0,988** → **N_eff 1,01**.
|
||||
La mediana di 5 fasi e' UNA osservazione: toglie l'artefatto, non compra precisione.
|
||||
|
||||
### Le due domande che erano state confuse
|
||||
|
||||
Il gate (A) puo' chiedere due cose diverse, e il 07/08 coincidevano **per fortuna di fase**:
|
||||
|
||||
| forma | difetto | quanto e' grave |
|
||||
|---|---|---|
|
||||
| «la banda e' la scelta che si fa al buio?» | **non ha risoluzione** | cambia verdetto ogni notte |
|
||||
| «la banda non e' stata selezionata sull'hold-out?» | **non ha potenza** | 1 cella su 30 e' l'argmax dell'hold-out → **29/30 lo passano per costruzione** |
|
||||
|
||||
📌 *Lettura (agente, fallibile).* Il 25% non viene dalla griglia affatto: viene dall'argomento
|
||||
**strutturale** del 27/07 — una banda in dollari assoluti degenera al variare del capitale, una
|
||||
frazione no. Sotto la mediana delle fasi il 25% e' **2° in-sample e 3° sull'hold-out**: decente
|
||||
ovunque, primo da nessuna parte, che e' la firma di un parametro NON selezionato sui dati. A un
|
||||
parametro scelto per ragioni strutturali non si applica un gate di *selezione* ma uno di
|
||||
*robustezza* — «fa danno da qualche parte?». Risposta: no.
|
||||
|
||||
**Esito:** gate (A) **ritirato** come pass/fail, congelando il MOTIVO (come il 07/08), piu' una
|
||||
guardia sulla CAUSA che si rompe il giorno che la fase venisse ancorata al calendario.
|
||||
|
||||
## 3. Cosa cambia togliendo GTAA01 — e la decisione dell'operatore
|
||||
|
||||
`r0828_senza_gtaa.py`. Portafoglio di **ricerca** a 5 sleeve; NON il book live.
|
||||
|
||||
| | Sharpe | CAGR | maxDD | vol |
|
||||
|---|---|---|---|---|
|
||||
| CON GTAA01 | **2,28** | +19,2% | 6,1% | 7,8% |
|
||||
| SENZA, iso-nozionale | 2,16 | **+23,8%** | 7,8% | 10,1% |
|
||||
| SENZA, **iso-rischio** (×0,77) | 2,16 | +18,0% | 6,0% | 7,8% |
|
||||
|
||||
A iso-nozionale togliendolo si guadagna **drift** e si compra **volatilita'**. A iso-rischio — la
|
||||
sola lente onesta per un diversificatore a basso CAGR (M6) — **Δ Sharpe +0,121**, maxDD invariato.
|
||||
Hold-out 2025+: **Δ +0,247**. Aiuta in **6 anni su 8**. Correlazione col resto **+0,087**: e'
|
||||
davvero altro. Standalone Sharpe 1,06, CAGR +5,8%.
|
||||
|
||||
⚠️ **Con la sua fascia di fase:** il Δ sulle 5 fasi va da **+0,095 a +0,124** e supera la soglia
|
||||
dichiarata (0,12) in **2 fasi su 5**. Il **segno e' stabile**, il verdetto **binario** no.
|
||||
|
||||
### La decisione: rinviata a $15k di book
|
||||
|
||||
Discussa con l'operatore. L'argomento che ha deciso non e' «GTAA01 non contribuisce» — la misura
|
||||
dice il contrario — ma che **a questo capitale lo sleeve non e' accendibile**: `GTAA_MIN_CAPITAL`
|
||||
e' **$3.000 allocati**, che al peso 20% significa **$15.000 di book** contro i **$2.068** attuali.
|
||||
Sotto quella soglia e' manutenzione senza beneficio incassabile.
|
||||
|
||||
Due ragioni registrate **contro** il blocco immediato, entrambe regole del progetto:
|
||||
- **N7** — uno sleeve difensivo si giudica **sul sinistro, non sul premio**, e la finestra
|
||||
2019-2026 non contiene il sinistro per cui esiste un GTAA a sei gambe;
|
||||
- **N4** — il rischio di venue si compra con un **conto**, non con uno sleeve: togliere GTAA01
|
||||
renderebbe il portafoglio di ricerca **100% cripto su un venue solo**.
|
||||
|
||||
E un costo che sarebbe andato pagato, non saltato: GTAA01 e' uno dei **quattro** nomi di
|
||||
`r0726_wall_fixedpoint.W_DEPLOY`, da cui escono i **$313k** e tutta la pianificazione da €500/mese.
|
||||
|
||||
**Perche' la decisione non si dimentichi** (N9), la soglia e' sorvegliata dal giornale:
|
||||
`journal.SOGLIE_CAPITALE` legge l'equity ogni giorno e il giorno che supera $15k la voce lo dice,
|
||||
con la domanda da rispondere accanto. Stessa tabella, seconda riga: **$20k**, dove si riapre
|
||||
«100% Deribit fino a $20k».
|
||||
|
||||
## 4. Allarmi: il marcatore «gia' detto» si scrive DOPO l'invio
|
||||
|
||||
Debito §5.2 chiuso su decisione dell'operatore. `venue_watch.run_once` salvava lo stato coi
|
||||
marcatori `alerted` gia' a True e l'invio lo faceva il chiamante **dopo**: col 6,9% di invii
|
||||
falliti misurato (2/29), un 🚨 perso restava perso per l'**episodio intero** (200-2.324 ore).
|
||||
|
||||
Ora il sender e' **iniettato** (la funzione resta testabile senza rete d'uscita, che era la ragione
|
||||
del disegno precedente) e su fallimento si disfano i **soli marcatori**, non le misure:
|
||||
asset in ALERT → ri-allerta l'ora dopo; lock MAINT/ALERT → flag azzerati **ma le ore continuano a
|
||||
correre**, cosi' una manutenzione che sfora la grazia sale ad ALERT anche col trasporto giu';
|
||||
lock RIENTRATO → stato ripristinato, perche' il rientro si annuncia una volta sola.
|
||||
`notify()` accetta `tentativi` (venue_watch: 3) e l'esito finisce nel log del cron.
|
||||
|
||||
⚠️ Resta scoperto **ogni altro chiamante di `notify()`**: la disciplina e' oggi solo in venue_watch.
|
||||
|
||||
## 5. L'analista del 27/08, e la riga di diagnosi che mancava
|
||||
|
||||
La voce del 27/08 era senza analisi. Causa, dal transcript della sessione headless:
|
||||
`Failed to authenticate: OAuth session expired and could not be refreshed`, exit 1 alle
|
||||
`00:37:00.98Z`. La meccanica aveva retto (nessuna analisi vecchia spacciata per nuova, esito nel DB,
|
||||
notifica partita) ma il motivo registrato era `uscita 1: ` — vuoto: `interroga()` leggeva il solo
|
||||
**stderr**, e la CLI aveva scritto su **stdout**. Riparato (`motivo_uscita`), analisi recuperata.
|
||||
Guasto **transitorio**: il refresh token era valido, unico caso su 24 sessioni.
|
||||
|
||||
## 6. Fisco: la domanda (a) ha una risposta alla fonte
|
||||
|
||||
Circolare AdE **30/E del 27/10/2023**, letta direttamente dal PDF ufficiale. **I derivati con
|
||||
sottostante cripto stanno in `c-quater` → 26%**, non in `c-sexies` → 33%, e la circolare dice
|
||||
esplicitamente che la riforma 2023 non sposta nulla. Il **collaterale** sconta il **2‰** al 31/12
|
||||
(intermediario non residente → imposta sul valore delle cripto-attivita', ~$4/anno a questo
|
||||
capitale). E la conversione **USDC→USDE non e' fattispecie realizzativa**. Dettaglio e citazioni
|
||||
verbatim in CLAUDE.md §5.4. La (b), i payout di una prop firm, **resta aperta**.
|
||||
|
||||
## Cosa cambia
|
||||
|
||||
- `CLAUDE.md`: §3 nuova riga (GTAA01 rinviato a $15k) · §5.2 risolto · §5.4 riscritto · §5.13 nuovo
|
||||
debito (la fase che ruota, quantificata e innocua oggi).
|
||||
- `src/live/journal.py`: `SOGLIE_CAPITALE` + regola `soglia_capitale`.
|
||||
- `src/live/venue_watch.py`, `src/live/notifier.py`, `scripts/live/venue_watch.py`: commit dopo l'invio.
|
||||
- `src/live/analista.py`: `motivo_uscita` da stdout E stderr.
|
||||
- `tests/test_gtaa_band_gate.py`: gate (A) ritirato, motivo e causa congelati.
|
||||
- Script nuovi: `r0828_gtaa_band_phase.py`, `r0828_gtaa_band_median_phase.py`, `r0828_senza_gtaa.py`.
|
||||
|
||||
**Suite: 789 passati, 0 falliti.**
|
||||
@@ -0,0 +1,67 @@
|
||||
# 2026-08-29 — USDE idoneo, la quota rinviata a lunedì, e il fix IB confermato
|
||||
|
||||
*Scritto il 2026-08-29. Numeri letti dalle serie citate.*
|
||||
|
||||
## 1. GATE USDE-01: CHIUSO **IDONEO**
|
||||
|
||||
Il 2026-08-28 alle 12:35Z `usde_watch` ha rilevato il primo reward:
|
||||
|
||||
```
|
||||
equity USDE : 500.0521 @ 1.0001 (indice) -> $500.05
|
||||
delta : +0.052055 USDE dall'ultima lettura
|
||||
verdetto : IDONEO — reward rilevato il 2026-08-28T12:35:01Z
|
||||
```
|
||||
|
||||
La regola era pre-registrata il 26/08, **prima** dell'esito: ≥1 reward entro il 29/08 → IDONEO,
|
||||
e si apre la decisione di QUOTA, che è dell'operatore. Il gate ha fatto quello che doveva.
|
||||
|
||||
## 2. Il tasso, e perché la quota è stata rinviata
|
||||
|
||||
| lettura | tasso implicito |
|
||||
|---|---|
|
||||
| il solo pagamento (0,052055 su 500) | **3,80% annuo** |
|
||||
| su **due** finestre — il 27/08 ha pagato **zero** | **1,90% annuo** |
|
||||
| annunciato da Deribit (ricerca 26/08) | 9% |
|
||||
|
||||
**Decisione dell'operatore (29/08): la quota si decide lunedì 31/08**, su tre-quattro finestre
|
||||
invece che su una. Il motivo è nella tabella: fra 3,80% e 1,90% c'è tutta la decisione, e il
|
||||
campione è **un pagamento**. Il costo dell'attesa è zero — i 500 USDE rendono comunque.
|
||||
|
||||
📌 Perché il numero non venga letto male: `usde_watch.rendimento()` calcola l'APR sulle finestre
|
||||
**osservate**, col denominatore sul **tempo vero** e non sul numero di finestre pagate — così una
|
||||
finestra che non paga **abbassa** la stima invece di sparire (P5: quel silenzio è uno zero). Le
|
||||
letture con trade nel mezzo si escludono (P12). L'APR si stampa sempre **col suo `n`**, e sotto
|
||||
4 finestre la riga dice *«un pagamento non è un tasso»*. Sulle letture vere di oggi:
|
||||
**1,97% annuo su 1,9 giorni, 1/2 finestre pagate**.
|
||||
|
||||
📌 Perché la decisione non si dimentichi (N9): `quota_da_decidere()` è vera **solo** se il conto è
|
||||
IDONEO **e** il rinvio è scaduto. Da lunedì il watch manda il 📌 **ogni giorno** — con quota
|
||||
attuale, tetto di allerta e APR osservato accanto — finché non si decide. Si smette togliendo
|
||||
`DECISIONE_QUOTA_DAL`. È lo stesso schema della soglia $15k di GTAA01, con la data al posto
|
||||
del capitale.
|
||||
|
||||
⚠️ Nota di contorno che vale una lezione: il 📌 dell'IDONEO era partito su Telegram il 28/08 alle
|
||||
12:35Z, **in mezzo ai messaggi-spazzatura dei test** delle 14:47 e 14:52 (difetto riparato lo
|
||||
stesso giorno). L'unico messaggio che contava è arrivato nel giorno in cui il canale era sporco.
|
||||
È P9 in forma concreta.
|
||||
|
||||
## 3. Il fix IB è confermato
|
||||
|
||||
Il giro del cron delle 00:30 di stanotte: **zero** righe `open orders request timed out` (erano
|
||||
2 per giro, 63 giri su 63) e tutti e sei gli ETF scaricati — `SPY n=7541`, `QQQ n=6912`,
|
||||
`IWM n=6602`, `TLT n=2658`, `GLD n=5476`, `HYG n=4877`, aggiornati al 28/08. Il `readonly=True`
|
||||
resta.
|
||||
|
||||
📌 E una conferma involontaria del debito §5.13: **SPY è passato da `1996-09-04` a `1996-09-05`**.
|
||||
La finestra rotolante ha perso un altro giorno di borsa in una notte, esattamente come misurato
|
||||
il 28/08 — il difetto si legge a occhio nudo nel log del cron.
|
||||
|
||||
## Cosa cambia
|
||||
|
||||
- `scripts/live/usde_watch.py`: `rendimento()` (APR sulle finestre osservate, col suo n) ·
|
||||
`quota_da_decidere()` · `DECISIONE_QUOTA_DAL = "2026-08-31"` · il 📌 giornaliero da lunedì.
|
||||
- `tests/test_usde_watch.py`: 6 test nuovi, incluso il controllo positivo (*la finestra sola
|
||||
darebbe il doppio*) che è la ragione per cui l'APR si stampa col suo `n`.
|
||||
- `CLAUDE.md`: §1 e §4 aggiornati — gate CHIUSO IDONEO, quota rinviata a lunedì.
|
||||
|
||||
**Suite: 795 passati, 0 falliti.**
|
||||
@@ -0,0 +1,318 @@
|
||||
# 2026-08-30 — "porta in USDE tutto il capitale che non viene usato": il venue dice 31%
|
||||
|
||||
**Richiesta dell'operatore**, verbatim: *"porta in usde tutto il capitale che non viene usato"*.
|
||||
Arriva il 30/08, cioè **un giorno prima** della data a cui l'operatore stesso aveva rinviato la
|
||||
decisione di quota (gate USDE-01, rinvio al 31/08). L'anticipo è esplicito e vale come decisione.
|
||||
|
||||
## 1. "Il capitale che non viene usato" non è una quantità piccola: è quasi tutto
|
||||
|
||||
Stato letto dal gateway alle 19:50Z: USDC **$1.563,09** (di cui **$11,36** impegnati a margine),
|
||||
USDE 500,1757 → **$500,18**, totale **$2.063,26**, quota **24,2%**. Posizioni BTC $354,72 + ETH
|
||||
$213,36 = **$568,08** lordi (0,275x).
|
||||
|
||||
Il margine consuma **$11 su $2.063**, e l'USDE è cross-collateral: convertirlo non lo toglie
|
||||
dall'uso. Anche a piena esposizione del libro (lordo 1,0x) il margine sarebbe ~$41. Presa alla
|
||||
lettera la richiesta è una quota **~99%** — il 100% che il gate esclude per nome ("mai 100%: R1
|
||||
emittente non recuperabile"). Le due letture plausibili ("non a margine" e "non esposto dal libro")
|
||||
convergono entrambe su ~97-99%: non è un'ambiguità che si risolve scegliendo, è una che si porta
|
||||
all'operatore.
|
||||
|
||||
## 2. Il vincolo che `r0830_usde_quota` non aveva guardato: il cuscino di REGOLAMENTO
|
||||
|
||||
L'analisi del 30/08 aveva misurato il **margine** e concluso — correttamente — che l'haircut non
|
||||
morde a nessuna quota fino al 90%, nemmeno a leva 1,50x con USDE a 0,95. Vero, e irrilevante:
|
||||
il P&L e il funding dei perp **USDC-lineari si regolano in USDC**, non nel collaterale. Lo si vede
|
||||
nel conto: `equity − balance = $7,24` di floating, tutto nel secchio USDC.
|
||||
|
||||
A quota ~99% resterebbero **$41 di USDC** contro un libro che, col disaster-SL a −30% sulla massima
|
||||
esposizione, può perderne **~$619**: il saldo va negativo e Deribit lo finanzia a interesse, che si
|
||||
mangia la resa che si stava comprando. **Il vincolo non è il margine, ma non è nemmeno assente.**
|
||||
|
||||
> 📌 **Corretto e QUANTIFICATO il 31/08** (KB *Cross collateral specifications*): «*a collateral fee
|
||||
> will be charged […] **default = 0.05% per day***», al secondo, sulla valuta negativa — cioè
|
||||
> **18,25%/anno, 4,3× la resa USDE** che si starebbe comprando. Il ribilanciamento automatico non
|
||||
> salva: scatta a **$1M** assoluti o al **100% della cross equity**, soglie irraggiungibili a $2k.
|
||||
> Qui il 30/08 avevo scritto «lo finanzia a interesse» **senza il numero**: il numero rende il
|
||||
> criterio del cuscino più giustificato, non meno.
|
||||
|
||||
Criterio dichiarato: cuscino USDC ≥ disaster-SL sulla massima esposizione lorda del libro =
|
||||
`n_asset × frac × disaster_sl_pct` = 2 × 0,5 × 0,30 = **30% dell'equity** — tutto derivato da
|
||||
`config/live.json` e `src/live/book.py`, mai ridichiarato (P1). Che lascia esattamente **70%**.
|
||||
Non è un argmax (M8): la quota massima e il criterio coincidono per costruzione, e infatti il 70%
|
||||
cade **esattamente sul limite, con zero slack**.
|
||||
|
||||
Tabella portata all'operatore (APR allora creduta 3,26% [1,13-4,51], n=4 — vedi §5, è sbagliata):
|
||||
|
||||
| quota | converti | USDC che resta | resa/anno | evento emittente vs maxDD libro |
|
||||
|---|---|---|---|---|
|
||||
| 24,2% (allora) | — | $1.563 | $16 | 3,1× |
|
||||
| 50% | $531 | $1.032 | $34 | 6,3× |
|
||||
| **70%** | **$944** | **$619** | **$47** | **8,8×** |
|
||||
| ~99% | $1.522 | $41 | $66 | 12,3× |
|
||||
|
||||
**Scelta dell'operatore: 70%**, il massimo compatibile col cuscino.
|
||||
|
||||
## 3. Esecuzione: da 500 a 644 USDE, poi il muro
|
||||
|
||||
Nuovo attrezzo **`scripts/live/usde_convert.py`** (dry-run di default). Non passa da
|
||||
`DeribitTrader`: `execution.ALLOWED` ammette solo i due perp del libro ed è il guardrail
|
||||
anti-fat-finger del percorso soldi — **non si allarga** per farci entrare uno spot che col libro
|
||||
non c'entra. Lo script porta le proprie guardie: banda prezzo [0,995 – 1,005], tetto HARD di quota
|
||||
0,95, il cuscino di regolamento, dry-run.
|
||||
|
||||
Convertiti **+144 USDE** (500,1757 → **644,175691**), quota **24,24% → 31,21%**, tutti i fill a
|
||||
1,0002-1,0003, **fee 0**. Poi ogni acquisto ha smesso di passare.
|
||||
|
||||
## 4. `not_enough_funds_in_currency` è un messaggio FUORVIANTE — la cronaca delle ipotesi sbagliate
|
||||
|
||||
Si tiene apposta, perché chi rilegge non deve rifare questo giro. Tre ipotesi, tutte plausibili,
|
||||
tutte sbagliate:
|
||||
|
||||
1. **"È la taglia."** 943,98 rifiutato per `Invalid params` → vero: `min_trade_amount: 1` e
|
||||
`contract_size: 1`, il passo è **intero** (confermato da `public/get_instrument`; il gateway
|
||||
non espone quel tool, la risposta è arrivata dall'API pubblica). Quantizzato a intero: 933
|
||||
rifiutato per **fondi**, con **$1.541 disponibili**. Falso indizio.
|
||||
2. **"È il rate-limit."** 100 passava e 300 no; poi anche **1** rifiutato otto volte di fila.
|
||||
Aggiunto backoff sul tempo fino a 90s: rifiutato lo stesso. Falso.
|
||||
3. **"È il prezzo."** Diagnosi a tre ordini da 1 USDE: SELL @1,0000 OK, BUY **@1,0003 OK**,
|
||||
BUY @1,0002 e BUY market rifiutati. Vero **in parte**: il book REST pubblico è in **ritardo**
|
||||
sul matching engine, e un limite a `ask+1 tick` a volte non incrocia davvero. Difetto mio, e
|
||||
istruttivo: prezzavo l'ordine sull'**indice** invece che sul **book**. Sono due prezzi con due
|
||||
mestieri — l'indice **marca** il collaterale (`usde.valuta`, mediana multi-exchange: la lezione
|
||||
Binance 10/10), il book **prezza** lo scambio. Corretto con 5 tick di margine e tetto duro
|
||||
1,0010: il fill avviene al prezzo del libro, quindi il margine non si paga.
|
||||
|
||||
Ma corretto il prezzo, **200 e 800 continuavano a fallire con 4.552 di profondità all'ask**.
|
||||
|
||||
## 5. 🚨 Il fatto vero: un TETTO DEL VENUE sull'USDE, a 644,175691 (31,21%)
|
||||
|
||||
Esperimento **a saldo neutro**, quattro ordini di fila (20:14Z):
|
||||
|
||||
```
|
||||
BUY 20 @1.0007 -> not_enough_funds_in_currency USDE 644.1757
|
||||
SELL 20 @0.9996 -> OK USDE 624.1757
|
||||
BUY 20 @1.0007 -> OK USDE 644.1757
|
||||
BUY 5 @1.0007 -> not_enough_funds_in_currency USDE 644.1757
|
||||
```
|
||||
|
||||
È un **tetto sul LIVELLO**: non taglia, non prezzo, non liquidità, non cadenza, non fondi
|
||||
($1.408 disponibili). Lo stesso ordine che viene rifiutato passa subito dopo una vendita di pari
|
||||
taglia. **La quota del 70% non è raggiungibile**, e nemmeno il 50%.
|
||||
|
||||
La formula del tetto **non è leggibile da qui**: `public/get_currencies` non espone un cap
|
||||
(`in_cross_collateral_pool: true`, nient'altro), e il gateway non espone né `get_order_book` né
|
||||
`available_withdrawal_funds` — è il debito #11, il gateway è l'unico pezzo della catena che
|
||||
possediamo e si ha solo quel che espone. Registrato come fatto misurato, non spiegato (D5: un buco
|
||||
quantificato è un risultato, uno taciuto è un debito).
|
||||
|
||||
## 6. 🚨 E la resa non è quella che credevamo: **l'USDC paga 3,40%**
|
||||
|
||||
`public/get_currencies`, letta oggi:
|
||||
|
||||
| valuta | APR pubblicata |
|
||||
|---|---|
|
||||
| USDE | **4,1071%** |
|
||||
| USDC | **3,4000%** |
|
||||
|
||||
I nostri reward USDE misurati (+0,052055 · +0,061815 · +0,061821 al giorno su 500 → **~4,5%/anno**)
|
||||
**confermano** che la APR pubblicata di USDE è reale. Il che rende credibile anche l'altra riga.
|
||||
|
||||
Se l'USDC frutta già 3,40%, il guadagno del passaggio a USDE **non è il tasso: è lo spread**,
|
||||
**0,71 punti**. Sui $644 che teniamo vale **~$4,6/anno**, non $21; sui $144 convertiti oggi,
|
||||
**~$1,0/anno**. Il gate USDE-01 ha misurato con cura il reward dell'USDE e **non ha mai chiesto
|
||||
cosa facesse l'USDC fermo**: è il controfattuale mancante — M1 in un'altra veste, si giudica il
|
||||
**marginale**, non il livello. E cambia il verso della decisione: si prende un rischio emittente
|
||||
**non recuperabile** (a 31,2% vale 3,9× il maxDD dell'intero libro) per **0,71 punti**.
|
||||
|
||||
⚠️ **Quello che NON è dimostrato:** che la APR USDC sia effettivamente accreditata *sul nostro
|
||||
conto*. Sull'USDE l'abbiamo vista arrivare per delta; sull'USDC il P&L di trading copre $0,13/giorno
|
||||
di interesse e il gateway non espone il Transaction Log. **Va verificato su un giorno senza trade
|
||||
prima di trattarlo come misura** — qui è una APR pubblicata dal venue più una conferma indiretta,
|
||||
non un reward osservato.
|
||||
|
||||
## 7. Cosa resta a terra
|
||||
|
||||
- **Conto**: USDC $1.419,61 · USDE 644,175691 ($644,18) · totale **$2.063,79** · quota **31,21%**.
|
||||
Nessun ordine spot appeso (restano i due `tp01-disaster` reduce_only). Posizioni invariate.
|
||||
Equity vista dal book **$2.063,86** ("mainnet USDC + USDE 644 @ 1.0000"): sizing corretto,
|
||||
nessun falso "uscita di fondi".
|
||||
- **`config/live.json`**: aggiunto `quota_target: 0.70` (la decisione dell'operatore, oggi bloccata
|
||||
dal venue). `quota_max_frac` alzato a 0,85 e **rimesso a 0,50** nella stessa sessione: la soglia
|
||||
larga presupponeva un 70% che non esiste, e lasciarla avrebbe **disarmato la guardia per uno
|
||||
scenario che non si è verificato. Con la quota reale al 31%, il 50% morde di nuovo.**
|
||||
- **Non fatto**: portare la quota al 70%. Il venue non lo consente. Non è una rinuncia
|
||||
discrezionale, è un rifiuto misurato e riproducibile.
|
||||
|
||||
---
|
||||
|
||||
# APPENDICE (stessa sera, 23:40-23:55Z) — **l'USDC NON frutta sul nostro conto: §6 era sbagliata**
|
||||
|
||||
Richiesta dell'operatore: *"verifica se l'USDC frutta davvero sul nostro conto"*. Verificato.
|
||||
**La risposta ribalta la §6 di questo stesso diario**, che va letta con questa appendice accanto.
|
||||
|
||||
## Perché la domanda sembrava dover aspettare, e invece no
|
||||
|
||||
Il gateway non espone il Transaction Log (provati `get_transaction_log`, `get_settlement_history`,
|
||||
`get_deposits`, `get_transfers`, `get_interest_history`: **tutti 404** — debito #11). L'unica serie
|
||||
storica del conto è `trades.db.equity`: **oraria ma arrotondata a 2 decimali**, e contiene il P&L
|
||||
non realizzato, che a posizioni aperte oscilla di ±$2-8/ora. $0,13/giorno di interesse ci sparisce.
|
||||
|
||||
Poi la fonte ufficiale Deribit ha spostato il problema dal *rumore* al *calendario*:
|
||||
|
||||
> «Every day at 00:00 UTC, Deribit calculates the **minimum equity** of USDC that a user has been
|
||||
> holding over the previous 24 hours. […] After the month is over, the rewards from each day are
|
||||
> summed together and **paid out as a single monthly payment early in the following month**.»
|
||||
> — `insights.deribit.com/education/usdc-rewards-now-paid-on-deribit/`
|
||||
|
||||
**I reward USDC si pagano UNA VOLTA AL MESE**, non ogni giorno come quelli USDE. Ecco perché non li
|
||||
avevamo mai visti: guardavamo a cadenza giornaliera, dove l'USDE si vede e l'USDC per costruzione no.
|
||||
E un accredito mensile da qualche dollaro **si vede benissimo anche a 2 decimali** — purché il libro
|
||||
sia FLAT, perché allora `equity == balance == USDC` e ogni scalino è un accredito.
|
||||
|
||||
## La misura: due confini di mese, entrambi a libro flat
|
||||
|
||||
| finestra | punti orari | equity min | equity max | ore senza alcun movimento | scalini >1 cent | reward atteso se idoneo |
|
||||
|---|---|---|---|---|---|---|
|
||||
| **29/06 → 08/07** | 238 | **598,06** | **598,06** | **237 / 237** | **0** | $0,45 (8 gg dal 23/06) |
|
||||
| **31/07 → 03/08** | 96 | **596,92** | **596,92** | **tutte** | **0** | **$1,72** (luglio intero) |
|
||||
|
||||
Il secondo è il caso decisivo: **$1,72 attesi contro una risoluzione di $0,01 — 172×** — e l'equity
|
||||
non si muove di un centesimo per quattro giorni pieni, coprendo tutta la finestra "early in the
|
||||
following month". Non è un'assenza sotto la soglia di rilevabilità: è uno zero misurato.
|
||||
|
||||
⇒ **Il conto NON riceve i reward USDC.** N=2 confini indipendenti, entrambi puliti per costruzione.
|
||||
|
||||
## Perché, e perché è coerente col fatto che l'USDE invece paga
|
||||
|
||||
Deribit: *«A user's eligibility to receive USDC is based on their location»*. L'ipotesi che spiega
|
||||
entrambe le osservazioni è **MiCA**: USDC è un *e-money token* regolamentato, e a un residente UE
|
||||
non se ne può corrispondere rendimento; **USDe non è un EMT**, e infatti i suoi reward arrivano
|
||||
(li abbiamo visti per delta: +0,052055 · +0,061815 · +0,061821). ⚠️ *Questa è la spiegazione
|
||||
plausibile, non una fonte normativa verificata* (P13/M27: una norma citata si verifica come un
|
||||
numero). **Il fatto misurato è lo zero, non il suo motivo.**
|
||||
|
||||
## Cosa cambia (e cosa la §6 aveva sbagliato)
|
||||
|
||||
- **La premessa originale del gate USDE-01 è RESTAURATA.** Il guadagno di tenere USDE **non è lo
|
||||
spread 0,71 punti: è il tasso pieno ~4,1%**, perché l'alternativa sul nostro conto rende **0**.
|
||||
Sui $644 che teniamo: **~$26/anno** (e ~$29 al ritmo misurato di ~4,5%), non i ~$4,6 di §6.
|
||||
- **Cosa avevo sbagliato, e la lezione.** Ho letto `apr: 3.4` in `public/get_currencies` e l'ho
|
||||
trattato come una proprietà **del nostro conto**. È una proprietà **del venue**: un listino, non
|
||||
un accredito. La conferma indiretta che invocavo ("i reward USDE misurati coincidono con la loro
|
||||
APR pubblicata") provava che il listino è reale **per l'USDE**, e non diceva nulla sull'idoneità
|
||||
dell'USDC. 📌 **Un tasso pubblicato non è un tasso incassato: si verifica sul CONTO, e la verifica
|
||||
costava una query sulla serie che avevamo già.** È N10 in una veste nuova — la verifica a €0 va
|
||||
fatta *prima*, e si fa **sul venue, non sul sito**; qui perfino il venue non bastava, serviva il
|
||||
conto.
|
||||
- Resta vero e non toccato: il **tetto del venue al 31,21%**, il **cuscino di regolamento** (30%
|
||||
dell'equity, che lascia il 70%), e che a 31,2% l'evento emittente vale ~3,9× il maxDD del libro.
|
||||
La decisione "tenere o no i $644" cambia però di segno rispetto a come l'avevo chiusa: si compra
|
||||
**$26/anno**, non $4,6.
|
||||
|
||||
## Strumento lasciato in piedi
|
||||
|
||||
**`scripts/live/balance_watch.py`** + **`scripts/cron_balance.sh`** (orario al minuto **:42**, libero
|
||||
fra :25/:35/:47; sola lettura). Registra il **balance a 8 decimali** per valuta — la serie che
|
||||
mancava — con il conteggio dei fill dall'ultimo campione e il nozionale lordo, così una finestra
|
||||
sporca si riconosce invece di essere mediata dentro. Etichetta PULITA le finestre a 0 fill e libro
|
||||
flat, dove il Δ USDC **è** l'interesse, e ne stampa l'APR implicita accanto a quella attesa.
|
||||
Serve a sorvegliare che lo zero resti zero (o che cambi, se l'idoneità cambia): l'inizio di
|
||||
settembre è il prossimo confine di mese, ed è già strumentato.
|
||||
|
||||
---
|
||||
|
||||
# APPENDICE 2 (2026-08-31) — la fonte ufficiale: **due correzioni alle mie conclusioni**
|
||||
|
||||
L'operatore ha passato l'articolo `support.deribit.com/.../Yield-reward-bearing-coins`. WebFetch lo
|
||||
prende 403 (Cloudflare); si legge dall'**API Help Center** in JSON:
|
||||
`support.deribit.com/api/v2/help_center/en-us/articles/31424939199261.json` → HTTP 200.
|
||||
*Aggiornato dal venue il 2026-08-20.* Contiene tre cose che il progetto non sapeva.
|
||||
|
||||
## 1. L'ITALIA è nella lista degli esclusi dai reward USDC — e non è MiCA
|
||||
|
||||
> «The following jurisdictions are **not eligible** to receive USDC rewards: Austria, Belarus,
|
||||
> Belgium, Bulgaria, Canada, […] **Italy**, Japan, […]»
|
||||
|
||||
Lo zero misurato è **confermato dalla fonte**. Ma l'ipotesi che avevo scritto — MiCA, USDC è un
|
||||
e-money token e USDe no — **è sbagliata**: la lista contiene Canada e Giappone, non è il perimetro
|
||||
MiCA. È policy di giurisdizione di Deribit. *M27: una fonte normativa citata si verifica come un
|
||||
numero — e io l'avevo dedotta invece di leggerla.*
|
||||
|
||||
## 2. La finestra di pagamento è di DUE SETTIMANE, non di tre giorni — il «172×» era sovra-affermato
|
||||
|
||||
> «The monthly USDC rewards payment is then made **within the first two weeks** of the following
|
||||
> month.»
|
||||
|
||||
Avevo verificato su 29/06→08/07 e 31/07→03/08: **8 giorni su 14 e 3 su 14**. Rifatto sulle finestre
|
||||
vere:
|
||||
|
||||
| accredito | finestra vera | letture | escursione TOTALE equity | scalini ≥70% dell'atteso | esito |
|
||||
|---|---|---|---|---|---|
|
||||
| giugno ($0,45) | 01→16/07 | 374 | **$2,74** | 7 | ❌ **NON conclusivo** |
|
||||
| luglio ($1,72) | 01→16/08 | 382 | **$0,48** | **0** | ✅ **conclusivo** |
|
||||
|
||||
Luglio regge da solo: il credito atteso è **3,6× l'intera escursione dell'equity** su due settimane.
|
||||
Giugno no — il libro ha iniziato a operare a metà mese e il rumore ha la taglia del segnale.
|
||||
⇒ La conclusione **non cambia** (una finestra conclusiva + la lista ufficiale), ma l'evidenza è
|
||||
**una** finestra, non due. *Avevo citato la sotto-finestra piatta e chiamato «zero misurato» ciò che
|
||||
la finestra completa non copriva: scegliere la sotto-finestra che fa vedere lo zero è la stessa
|
||||
mossa che il progetto vieta a un backtest.*
|
||||
|
||||
## 3. Il tetto NON è un livello: è una FRAZIONE dell'equity, e scala col conto
|
||||
|
||||
L'articolo documenta un **Cap ETHENA** che diluisce il **tasso** a livello di exchange —
|
||||
`User APR = min(min_exchange_USDe_balance, Ethena Cap) / min_exchange_USDe_balance × Ethena APR` —
|
||||
e **nessun limite sulle quantità detenibili da un conto**. Quindi il muro non aveva base
|
||||
documentale, e andava ri-sondato. Fatto (31/08, giorno UTC nuovo → non è un limite giornaliero):
|
||||
|
||||
```
|
||||
BUY 5 -> not_enough_funds_in_currency USDE 644.175691 (muro ancora li')
|
||||
SELL 10 -> OK USDE 634.175691
|
||||
BUY 10 -> not_enough_funds_in_currency USDE 634.175691 <-- ieri 644,18 passava!
|
||||
BUY 5 -> OK USDE 639.175691
|
||||
BUY 3 -> OK USDE 642.175691
|
||||
BUY 1 -> OK USDE 643.175691
|
||||
BUY 1 -> not_enough_funds_in_currency USDE 643.175691
|
||||
```
|
||||
|
||||
**Ieri si tornava a 644,18 e oggi no**, con l'equity scesa di $8. Il tetto si è mosso con lei:
|
||||
|
||||
| | tetto | equity | frazione |
|
||||
|---|---|---|---|
|
||||
| 30/08 | [644,18 · 645,18) | $2.063,79 | 31,21% – 31,26% |
|
||||
| 31/08 | [643,18 · 644,18) | $2.055,56 | 31,29% – 31,34% |
|
||||
|
||||
I bracket distano **0,05pp**, dentro il rumore dell'equity (±$2-8/ora). Candidato pulito **5/16 =
|
||||
31,25%**, ma con questa risoluzione non si distingue da una regola sul collaterale scontato
|
||||
dell'haircut (~29%): **non si sceglie quella che conviene, si cita la banda** (M25).
|
||||
|
||||
⇒ **Il tetto SCALA col conto.** La quota resta ~31% per sempre, il valore in dollari cresce col
|
||||
capitale, e il 70% non è raggiungibile né ora né mai. Conseguenza che ieri non avevo visto: essendo
|
||||
pinnati al tetto, **il rischio emittente resta una frazione COSTANTE del conto** — non si diluisce
|
||||
crescendo, a meno di vendere apposta.
|
||||
|
||||
## 4. E l'USDe ha un fee del 5% che non avevamo mai nominato
|
||||
|
||||
> «Deribit Fee: A percentage deducted from the reward before distribution (**currently 5%**)»
|
||||
> · reward su **minima equity 00:00→24:00 UTC**, distribuiti **~12:00 UTC**, «visible inside
|
||||
> Transaction Log» (che il gateway non espone: da lì il metodo per delta).
|
||||
|
||||
Il nostro **misurato** (~4,5%/anno) è già netto del fee: è quello che arriva sul conto, ed è
|
||||
l'unico numero da citare. La `apr` di `get_currencies` (4,1071%) è un listino, non un incasso —
|
||||
la stessa confusione che mi era costata la §6.
|
||||
|
||||
## 5. Cablato, così il tetto smette di vivere in prosa
|
||||
|
||||
Alla domanda dell'operatore «dove è scritto il valore del tetto» la risposta era: **in tre note di
|
||||
testo e in nessun posto che il codice legga**, tanto che `usde_convert --quota 0.70` dichiarava
|
||||
«piano valido» per un ordine che il venue avrebbe rifiutato. Ora:
|
||||
|
||||
- `config/live.json` → `usde.venue_cap_frac` **0.312** (bordo BASSO dei bracket: fa fallire il
|
||||
piano *prima* dell'ordine invece che dopo il rifiuto) + `venue_cap_misurato` con la data;
|
||||
- `src/live/usde.py` lo porta nei default — unica autorità, come tutto il resto della sezione (P1);
|
||||
- `usde_convert.piano()` rifiuta il bersaglio sopra il tetto e stampa il massimo raggiungibile.
|
||||
Verificato: `--quota 0.70` → *«TETTO DEL VENUE: bersaglio $1.438,90 sopra $641,34 (31,2%
|
||||
dell'equity, misurato 2026-08-31)»*, nessun ordine inviato.
|
||||
|
||||
**Stato finale**: USDE **643,175691** (quota **31,29%**, cioè al tetto), USDC $1.412,40,
|
||||
totale $2.055,57.
|
||||
@@ -0,0 +1,128 @@
|
||||
# 2026-08-30 — La quota USDE: l'EV non puo' sceglierla, e aspettare costa $1,33
|
||||
|
||||
*Scritto 2026-08-30T17:32:57Z. Script: `scripts/research/r0830_usde_quota.py` (verdetto a runtime, N11).
|
||||
Stato conto congelato nello script alla lettura del 2026-08-30T17:05Z.*
|
||||
|
||||
## La domanda
|
||||
|
||||
Il gate USDE-01 e' **chiuso IDONEO** dal 28/08. Resta la decisione di **quota**, rinviata
|
||||
dall'operatore a **lunedi' 31/08**: quanto del conto tenere in USDE.
|
||||
|
||||
## Il risultato strutturale — dichiarato prima di guardare i numeri
|
||||
|
||||
Il valore atteso e' **lineare in q**. Un massimo lineare sta sempre in un **angolo** (0% o
|
||||
100%). ⇒ **l'EV non puo' scegliere una quota interna**: puo' solo dirne il segno. Ogni quota
|
||||
intermedia nasce da un criterio sulla **coda**, che va **dichiarato**, non ottimizzato (M8).
|
||||
|
||||
Per questo lo script non sceglie: mette il **prezzo accanto a ogni criterio**.
|
||||
|
||||
## (A) Il rendimento, col suo n
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| finestre misurabili | **4** (pagate 3) |
|
||||
| per finestra | 0,00% · 3,80% · 4,51% · 4,51% |
|
||||
| APR di `usde_watch` | **3,26%** annuo (tempo vero, 3,93 giorni) — *il numero che gira* |
|
||||
| APR per-finestra | 3,21% (n finestre, 4,00 giorni) |
|
||||
| banda bootstrap 95% | **[1,13% – 4,51%]** (200.000 ricampionamenti) |
|
||||
|
||||
📌 La banda e' larga **3,4 punti** su un livello di 3,2%: a n=4 il tasso **non e' misurato, e'
|
||||
abbozzato**. La divergenza fra i due denominatori (0,06 punti) e' **dichiarata, non appianata**
|
||||
(P12), e il punto stimato si **deriva** da `usde_watch.rendimento()` invece di essere
|
||||
ridichiarato (P1).
|
||||
|
||||
## (B) L'hazard di pareggio — l'unico criterio che non richiede un criterio
|
||||
|
||||
Perdita totale **non recuperabile**, resa 3,26%/anno ⇒ **hazard annuo di pareggio = APR**.
|
||||
|
||||
- **3,26%/anno** (banda 1,13% – 4,51%)
|
||||
- recupero di una perdita totale con la sola resa: **30,6 anni**, *indipendente dalla quota*
|
||||
- ⇒ la quota e' EV-positiva **se e solo se** si crede che Ethena abbia meno del **3,3% annuo**
|
||||
di probabilita' di evento catastrofico. Quel numero e' **assunto, non stimato** — esattamente
|
||||
come il `p` di Deribit della decisione 26/07.
|
||||
|
||||
## (C) Il rischio marginale, dato il 100%-Deribit gia' accettato
|
||||
|
||||
Framework **ereditato** dalla decisione 26/07, non re-inventato: `P = 1-(1-p)^20`. Lo script lo
|
||||
**riproduce** (M23: far riprodurre alla macchina il numero vecchio prima di pubblicarne uno nuovo):
|
||||
10/18/33/64% contro i 10/18/34/64% registrati — **OK** a 1,5 punti.
|
||||
|
||||
Due letture che **non si annullano**, e la contraddizione e' informazione (M28):
|
||||
|
||||
- ✅ L'USDE fa perdere soldi **solo** nel mondo in cui Ethena salta **e Deribit sopravvive**. Nel
|
||||
mondo in cui salta Deribit, USDC e USDE si perdono insieme e la quota e' irrilevante. Il rischio
|
||||
**aggiunto** e' quindi di **secondo ordine** rispetto a quello gia' accettato.
|
||||
- ⚠️ Ma e' un **terzo strato sullo stesso conto**, e **N4** dice che un rischio di venue si compra
|
||||
con un **secondo CONTO**, non aumentando l'esposizione al primo.
|
||||
|
||||
La prima dice che la quota costa poco; la seconda che **non e' li' che si compra sicurezza**.
|
||||
|
||||
## (D) I vincoli operativi: nessuno morde
|
||||
|
||||
Il libro gira al **2,00% di margine** ($11,37 su $568,64 di nozionale lordo). Al tetto di leva
|
||||
1,00x servirebbero ~$41 su un collaterale di ~$2.000.
|
||||
|
||||
| quota | margine utilizzabile | serve al tetto | morde? |
|
||||
|---|---|---|---|
|
||||
| 24,2% | $2.014 | $41 | no |
|
||||
| 50,0% | $1.961 | $41 | no |
|
||||
| 90,0% | $1.878 | $41 | no |
|
||||
|
||||
⇒ **l'haircut 10% non morde a nessuna quota testata**, tre ordini di grandezza di margine. D5:
|
||||
buco quantificato e innocuo. **Il vincolo non e' il margine.**
|
||||
|
||||
Churn da depeg: a soglia critica 0,95 l'equity cala di 5%·q ⇒ a quota 50% sono **2,50%**, cioe'
|
||||
~$52 di nozionale da girare. Il libro **de-risca mentre l'USDE e' a sconto e ri-carica al ritorno
|
||||
del peg** — compra alto e vende basso — ma per importi di **secondo ordine**. Non e' il vincolo.
|
||||
|
||||
## (E) Il menu dei criteri, ciascuno col suo prezzo
|
||||
|
||||
| criterio | quota | USDE | resa $/a | drift libro | == €/mese | mesi di bonifico a rischio |
|
||||
|---|---|---|---|---|---|---|
|
||||
| **C0** status quo | 24,2% | $500 | $16 | 0,79% | 19 | **2,0** |
|
||||
| **C1** ≤ maxDD del libro (7,94%) | 7,9% | $164 | $5 | 0,26% | 6 | 0,7 |
|
||||
| **C2** ≤ 6 mesi di versamento | 72,7% | $1.500 | $49 | 2,37% | 58 | 6,0 |
|
||||
| **C3** ≤ 12 mesi di versamento | 100% | $2.064 | $67 | 3,26% | 80 | 8,3 |
|
||||
| **C4** tetto di ALLERTA di config | 50,0% | $1.032 | $34 | 1,63% | 40 | 4,1 |
|
||||
|
||||
⚠️ **Due monete, non si sommano** (N8): *resa $/a* e' **cassa** vera; *== €/mese* e' l'equivalente
|
||||
in **versamento** via l'equivalenza registrata (100 €/mese == +4,07%/anno di drift), che e' una
|
||||
capitalizzazione a 10 anni, **non un flusso**. Semplificazione dichiarata: EUR e USD 1:1 (a ~1,08
|
||||
sposta i mesi di ~8%, non muove nessuna soglia).
|
||||
|
||||
## (F) Cosa costa aspettare — il numero decisivo
|
||||
|
||||
La conversione e' su spot `USDE_USDC`: **fee 0, spread ~3 bps** ⇒ andata+ritorno **~6 bps**. La
|
||||
decisione e' **reversibile a costo quasi nullo** — non e' una porta a senso unico e va decisa come
|
||||
tale.
|
||||
|
||||
| attendere | finestre | andata/ritorno | resa persa | costo totale |
|
||||
|---|---|---|---|---|
|
||||
| 0 sett | 4 | $0,32 | $0,00 | $0,32 |
|
||||
| 2 sett | 18 | $0,32 | $0,67 | $0,98 |
|
||||
| **4 sett** | **32** | $0,32 | $1,33 | **$1,65** |
|
||||
| 8 sett | 60 | $0,32 | $2,66 | $2,98 |
|
||||
|
||||
📌 **Portare la quota a 50% fra quattro settimane invece che domani costa $1,33 di resa non
|
||||
incassata e compra 8× le finestre** (n da 4 a 32), stringendo la banda di ~2,8× — da ~3,4 punti a
|
||||
~1,2. **$1,33 per sapere se il tasso e' 1,1% o 4,5%.** E' l'applicazione diretta di N10: una data
|
||||
si giustifica col **costo della misura**, non con la sua difficolta' percepita.
|
||||
|
||||
## Verdetto (calcolato a runtime)
|
||||
|
||||
- **RISOLUZIONE INSUFFICIENTE PER ALZARE** — n=4, banda larga 3,4 punti. Alzare ora e' comprare un
|
||||
tasso che non e' ancora misurato.
|
||||
- **NESSUN VINCOLO OPERATIVO** — l'haircut non morde a nessuna quota; il vincolo non e' il margine.
|
||||
- **SCALA** — la resa alla quota di oggi vale **$16/anno**, meno di **un mese** di versamento. La
|
||||
leva binding resta il **bonifico** (N1/N3), non la quota.
|
||||
- **ASIMMETRIA** — gia' a 24,2% l'evento emittente vale **3,1× il maxDD dell'intero libro** (7,94%),
|
||||
che e' il rischio che il progetto ha passato due anni a limare.
|
||||
- **REVERSIBILE E QUASI GRATIS DA RIMANDARE** — $1,33 + 6 bps per 8× il campione.
|
||||
|
||||
## Cosa NON dice questa analisi
|
||||
|
||||
- Non stima `p` per Ethena. Nessuna fonte qui lo misura: e' **assunto**, e lo script lo tratta come
|
||||
parametro, non come risultato.
|
||||
- Non sceglie la quota. **La scelta e' dell'operatore** (N4).
|
||||
- Il tasso osservato viene da un metodo **indiretto** (delta di equity al netto dei trade): il
|
||||
gateway non espone il Transaction Log. Vale col suo limite gia' dichiarato in `usde_watch`.
|
||||
@@ -0,0 +1,181 @@
|
||||
# 2026-08-31 — GATE BUIDL-01: test di eligibilità sul collaterale BlackRock
|
||||
|
||||
⚠️ **Questo documento è scritto PRIMA di eseguire e PRIMA di conoscere l'esito, e committato
|
||||
separatamente dall'esecuzione**: l'ordine dei fatti è verificabile in git, non sulla mia parola.
|
||||
È la stessa disciplina che ha reso valido il test USDE del 26/08 (M13, N11).
|
||||
|
||||
## Da dove nasce
|
||||
|
||||
L'operatore chiedeva se convenisse aggiungere **stETH** e i custodi Fireblocks/Sygnum/Komainu.
|
||||
Risposta: no a entrambi — stETH rende **2,2254%** (metà dell'USDE) e non è un dollaro ma **ETH**,
|
||||
quindi è esposizione direzionale che `book.py` non netta e che nessun gate autorizza; coprirla con
|
||||
uno short perp è **CC01**, già misurato il 26/06 e parcheggiato come *"LEAD a scala ~$20k+"*.
|
||||
I custodi sono istituzionali, fuori portata a $2k e coperti dalla decisione *"100% Deribit fino a
|
||||
$20k"* — e comunque **nessuna via custodiale restituisce i reward USDC**, perché l'esclusione
|
||||
italiana è per giurisdizione.
|
||||
|
||||
Ma la ricerca ha trovato la voce che conta. Le valute a rendimento su Deribit sono quattro:
|
||||
|
||||
| valuta | APR pubblicata | emittente / natura | note |
|
||||
|---|---|---|---|
|
||||
| USDE | 4,2143% | Ethena, sintetico delta-hedged | **al tetto** (~31,2% dell'equity) |
|
||||
| USDC | 3,4000% | Circle / custodia Coinbase | **Italia esclusa → 0 sul nostro conto** |
|
||||
| **BUIDL** | **3,2018%** | **BlackRock, Treasury USA tokenizzati** | `BUIDL_USDC` liquido |
|
||||
| STETH | 2,2254% | Lido | è ETH, non un dollaro |
|
||||
|
||||
**Il punto non è il tasso, è l'emittente.** Oggi il conto ha **$1.412,71 di USDC che rendono
|
||||
esattamente zero** e un rischio emittente concentrato al 100% su Ethena. BUIDL è un R1
|
||||
**completamente diverso** (Treasury USA): non aggiunge un grammo di esposizione Ethena e va
|
||||
esattamente nella direzione che N4 indica — *la quota è l'unica leva contro il rischio emittente*.
|
||||
|
||||
## Cosa si compra e cosa resta
|
||||
|
||||
- USDC $1.412,71 · USDE 643,175691 ($643,18) · **totale $2.055,88**
|
||||
- cuscino di regolamento richiesto (`n_asset × frac × disaster_sl_pct` = 30% dell'equity): **$616,76**
|
||||
- **USDC libero sopra il cuscino: $795,94**
|
||||
|
||||
**Taglia del test: 500 BUIDL (~$500)** — la stessa del test USDE, per confrontabilità. Lascia USDC
|
||||
a ~$912, cioè **$295 di slack sopra il cuscino**: il vincolo di regolamento resta rispettato con
|
||||
margine. Costo: spread 4 bps ≈ **$0,20**, reversibile.
|
||||
|
||||
## Meccanica dichiarata (dall'articolo ufficiale, non dedotta)
|
||||
|
||||
> «BUIDL rewards are paid based on the **minimum equity** of BUIDL held in an account over the
|
||||
> preceding day. The minimum equity held over the preceding day must be **at least 1 BUIDL**.
|
||||
> The minimum equity calculation period is the **24 hours up to 20:00 UTC** each day. […] BUIDL
|
||||
> rewards are **only paid on weekdays** […] between **20:00 UTC and 23:00 UTC**. […] Deribit
|
||||
> charges an administrative fee of **5%**.»
|
||||
|
||||
Comprando **lunedì 31/08 mattina**, la finestra 20:00 dom → 20:00 lun ha minimo **0** (prima
|
||||
dell'acquisto non tenevamo BUIDL): **nessun reward atteso lunedì sera**. La prima finestra piena è
|
||||
20:00 lun → 20:00 mar, pagata **martedì 01/09 fra le 20:00 e le 23:00 UTC**.
|
||||
|
||||
Reward atteso su 500 BUIDL: `500 × 3,2018%/365` = **0,04386/giorno** lordo, ~**0,0417 netto** del
|
||||
fee 5%. BUIDL ha **6 decimali**: il segnale è ~40× la soglia di rumore che usiamo per l'USDE (1e-3).
|
||||
⚠️ *La APR pubblicata è un listino, non un incasso* — lezione pagata il 30/08 sull'USDC. Il numero
|
||||
da citare sarà quello **misurato**, con il suo `n`.
|
||||
|
||||
## 🔒 CRITERI PRE-REGISTRATI — dichiarati adesso, prima dell'esito
|
||||
|
||||
**GATE BUIDL-01, si legge il 2026-09-04.**
|
||||
|
||||
1. **ELIGIBILITÀ** *(la domanda primaria: la giurisdizione italiana esclude l'USDC, e per BUIDL
|
||||
l'articolo non pubblica alcuna lista — quindi è ignoto, non noto)*
|
||||
**≥1 reward BUIDL rilevato entro le 23:00 UTC del 2026-09-03 → IDONEO**; si apre la decisione
|
||||
di quota, che è dell'**operatore**. **Zero reward → NON IDONEO**: si riconverte in USDC e la
|
||||
pista si chiude, come si sarebbe chiusa quella USDE.
|
||||
Finestra: tre giorni feriali pieni (mar 01, mer 02, gio 03), robusta a una finestra mancata.
|
||||
Rilevazione **per delta** sull'equity BUIDL al netto dei trade — il gateway non espone il
|
||||
Transaction Log (debito #11), stesso metodo dichiarato dell'USDE, coi suoi limiti.
|
||||
Soglia di rilevazione **1e-3 BUIDL** (il reward atteso è 40×).
|
||||
|
||||
2. **TETTO DEL VENUE** — si sonda subito, col metodo **a saldo neutro** già validato sull'USDE
|
||||
(BUY rifiutato → SELL → BUY riaccettato → BUY rifiutato). Si registra il bracket **con l'equity
|
||||
del momento**, perché sull'USDE il tetto si è rivelato una **frazione** e non un livello: senza
|
||||
l'equity accanto il numero non è interpretabile (P7).
|
||||
|
||||
3. **HAIRCUT**: **dichiarato NON misurabile a questa taglia** e non vincolante — sull'USDE è stato
|
||||
misurato che non morde fino al 90% di quota, e qui il margine impegnato è ~$11 su $2.056.
|
||||
Non lo si stima al buio: si scrive che non lo sappiamo (D5).
|
||||
|
||||
**Cosa NON decide questo test**: la quota. Decide solo se la pista esiste. La quota è
|
||||
dell'operatore, e sull'USDE si è vista arrivare comunque dal venue.
|
||||
|
||||
## Sorveglianza
|
||||
|
||||
`balance_watch.py` (orario, minuto :42) viene esteso a **BUIDL**: registra il balance a 6-8
|
||||
decimali accanto a USDC e USDE, col conteggio dei fill dall'ultimo campione. È già dentro il
|
||||
perimetro di backup. La finestra dei reward BUIDL (20:00-23:00 UTC) è coperta da 4 campioni orari.
|
||||
|
||||
*Esito: da scrivere dopo il 2026-09-03.*
|
||||
|
||||
---
|
||||
|
||||
## ESITO (stesso giorno, 07:35-07:45Z) — **GATE BUIDL-01 CHIUSO: NON ENTRABILE**
|
||||
|
||||
Il gate si chiude in dieci minuti, e con un esito che **non era fra i due previsti**. I criteri
|
||||
pre-registrati contemplavano *"≥1 reward → IDONEO"* e *"zero reward → NON IDONEO"*. La realtà è una
|
||||
terza cosa: **non si riesce a comprare BUIDL affatto**, quindi la domanda sull'eligibilità ai reward
|
||||
è priva di oggetto. *Un gate può fallire sulla sua precondizione invece che sul suo criterio, e va
|
||||
scritto così invece di essere forzato in una delle caselle previste.*
|
||||
|
||||
### La misura
|
||||
|
||||
Undici ordini rifiutati, tutti con `not_enough_funds_in_currency`:
|
||||
|
||||
```
|
||||
BUY 500 · 250 · 125 · 62 · 31 · 15 · 7 · 3 @1.0009 -> rifiutati (scala per dimezzamento)
|
||||
BUY 1 @1.0006 (ask 1.0004 x 10.730) -> rifiutato
|
||||
BUY 1 @1.0024 (ask 1.0004 x 10.730) -> rifiutato
|
||||
BUY 1 @1.0100 (ask 1.0004 x 10.730) -> rifiutato
|
||||
```
|
||||
|
||||
Con **BUIDL 0,000000 in mano** e **USDC $1.412,32 disponibili**, per comprare **$1**. Esclusi uno
|
||||
per uno tutti i sospetti che sull'USDE avevano portato fuori strada:
|
||||
|
||||
| sospetto | escluso perché |
|
||||
|---|---|
|
||||
| prezzo non marcabile | limite **1,0100** contro ask **1,0004** — incrocia di 96 bps |
|
||||
| liquidità | **10.730** unità al miglior ask, 23.416 tre livelli sotto |
|
||||
| taglia | rifiutata **1 unità**, il minimo dello strumento |
|
||||
| fondi | **$1.412** disponibili per un ordine da **$1** |
|
||||
| tetto sul livello | **teniamo zero**: non c'è nulla da superare |
|
||||
| wallet inesistente | `account_summary("BUIDL")` risponde, equity 0.0 |
|
||||
|
||||
⇒ **Il conto non è abilitato ad acquisire BUIDL.** Costo del test: **$0** (nessun fill).
|
||||
|
||||
### 🚨 E la frase pubblicata è FALSA per il nostro conto
|
||||
|
||||
> «**All Deribit users are permitted to buy and sell BUIDL tokens in the spot markets on Deribit,
|
||||
> with no extra requirements.**» — `insights.deribit.com/education/buidl-launches-on-deribit/`
|
||||
|
||||
Non per noi. **Il fatto misurato è il rifiuto, non il suo motivo** (stessa disciplina dello zero
|
||||
USDC). Le spiegazioni candidate sono due, e da fuori **non sono distinguibili**:
|
||||
|
||||
- **(a) giurisdizione** — BUIDL è un **titolo** (fondo BlackRock emesso via Securitize), la cui
|
||||
distribuzione è ristretta a monte dell'exchange. L'articolo di marketing non lo dice.
|
||||
- **(b) limiti di spot per TIER DI CONTO** — la documentazione BUIDL, sezione *Limits and
|
||||
Requirements*: «*Default non-margin spot order limits apply across assets. Check your account
|
||||
tiers […] for specific maximum open order size configurations.*» Un tier che per questo asset
|
||||
vale **zero** produrrebbe esattamente ciò che vediamo.
|
||||
|
||||
⚠️ Ma i nostri dati **restringono**: lo **spot USDE funziona** — ne abbiamo comprati 644 lo stesso
|
||||
giorno, sullo stesso conto, con lo stesso codice. Quindi **non è un blocco generale sullo spot né
|
||||
un limite di tier trasversale: è SPECIFICO DELL'ASSET.** Il che è compatibile con entrambe le
|
||||
ipotesi (una giurisdizione per-asset, o un tier per-asset a zero) e non ne elegge nessuna.
|
||||
*Restringere le ipotesi con l'evidenza che si ha vale più che sceglierne una con l'evidenza che
|
||||
non si ha.*
|
||||
|
||||
Irrilevante per noi ma registrato: «*Deribit only supports the ERC-20 version of BUIDL on the
|
||||
Ethereum blockchain*» — riguarda i trasferimenti on-chain, e la nostra uscita sarebbe comunque
|
||||
una vendita sullo spot, non un prelievo.
|
||||
|
||||
📌 **Due affermazioni pubblicate di Deribit smentite dal conto in due giorni**: la APR USDC al
|
||||
3,40% (30/08) e ora «all users can buy BUIDL». Non è sfortuna, è una **regola**:
|
||||
|
||||
> **Una capacità pubblicata dal venue non è una capacità del conto. Si verifica sul CONTO, prima
|
||||
> che entri in un piano — e costa un ordine da $1.**
|
||||
|
||||
È N10 portata un passo più in là: la verifica a €0 va fatta prima, si fa **sul venue e non sul
|
||||
sito** — e adesso si sa che nemmeno il venue basta: serve il **conto**.
|
||||
|
||||
### Il sottoprodotto che vale più del test
|
||||
|
||||
`not_enough_funds_in_currency` è il **messaggio generico di Deribit per «non puoi acquisire altra
|
||||
di questa valuta»**, qualunque sia il motivo — permesso, giurisdizione, o tetto. Lo si è visto ora
|
||||
su BUIDL (permesso assente, zero in mano) e il 30-31/08 su USDE (tetto a ~31,2% dell'equity). Il
|
||||
messaggio parla di **fondi** e i fondi non c'entrano mai. Chi lo incontra in futuro deve saltare
|
||||
direttamente alla domanda giusta — *questa valuta la posso acquisire, e fino a quanto?* — invece di
|
||||
inseguire taglia, cadenza e prezzo come è successo il 30/08.
|
||||
|
||||
### Cosa resta
|
||||
|
||||
- **Pista BUIDL chiusa.** Non riapribile da noi: dipende da un permesso del venue, non da una
|
||||
nostra scelta. La riapre solo un cambio lato Deribit.
|
||||
- **`balance_watch` tiene BUIDL nella lista** (costo: una lettura oraria). Se l'accesso si aprisse,
|
||||
un balance diverso da zero lo direbbe da solo — la stessa logica per cui l'USDC resta sorvegliato
|
||||
malgrado lo zero misurato.
|
||||
- **L'USDE resta l'unico collaterale a rendimento che il conto può usare**, al suo tetto di ~31,2%,
|
||||
e i **$1.412 di USDC continuano a rendere zero**. Non per mancanza di alternative: perché le tre
|
||||
alternative sono chiuse una per giurisdizione (USDC), una per permesso (BUIDL) e una perché non è
|
||||
un dollaro (stETH).
|
||||
@@ -0,0 +1,426 @@
|
||||
# 2026-08-31 — Fine della Proof of Reserves, e il guardiano che mancava
|
||||
|
||||
L'operatore ha passato l'annuncio *Deribit to Discontinue Daily Proof of Reserves Publication*.
|
||||
La notizia in sé vale poco per noi. Quello che è emerso cercandola vale molto di più.
|
||||
|
||||
## 1. La notizia: frequenza giù, sostanza su, per noi nulla cambia
|
||||
|
||||
Dal **1 settembre 2026** la pagina Proof of Reserves sparisce e gli aggiornamenti quotidiani
|
||||
cessano. Al loro posto, sotto il regime **VARA** di Dubai: audit **annuale** indipendente delle
|
||||
riserve, audit annuale del bilancio, obbligo di **copertura 100%** e **segregazione** degli attivi
|
||||
dei clienti. E: «*approximately 90% of client assets have been migrated to Coinbase, which acts as
|
||||
a custodian for Deribit*».
|
||||
|
||||
**Non è un peggioramento netto, ed è importante non raccontarlo come tale.** Si perde un segnale
|
||||
**ad alta frequenza e debole** (una PoR auto-pubblicata prova che gli attivi esistono in un istante,
|
||||
non che coprano le passività, ed è aggirabile attorno allo snapshot) e si guadagna un segnale **a
|
||||
bassa frequenza e più forte** (audit indipendente obbligatorio invece di pubblicazione volontaria),
|
||||
più un miglioramento **sostanziale** di custodia: 90% degli attivi presso un custode terzo regolato
|
||||
invece che nei wallet dell'exchange.
|
||||
|
||||
**Per noi non cambia nulla di operativo**, e per una ragione che va detta: *la PoR non l'abbiamo
|
||||
mai sorvegliata*. Zero occorrenze in tutto il codice. `venue_probe` legge se il venue **risponde**,
|
||||
non cosa **dichiara**. Si perde un segnale che non stavamo usando.
|
||||
|
||||
Ho provato a catturare l'ultimo dato prima che la pagina sparisse — è la mossa giusta per un dato
|
||||
non ricostruibile con una scadenza. **Rinunciato di proposito**: la pagina è una SPA e
|
||||
`get_proof_of_reserves` non esiste come metodo API («Method not found»), ma soprattutto uno
|
||||
snapshot singolo e auto-riportato non regge lo standard di prova di questo progetto e **non
|
||||
permetterebbe comunque di stimare `p`**. Un dato che non cambierebbe una decisione non vale il
|
||||
lavoro per prenderlo.
|
||||
|
||||
## 2. 📌 La conseguenza vera: il riapritore della decisione più grande si è ristretto
|
||||
|
||||
La decisione vincolante **«100% Deribit fino a $20k»** (26/07) ha come riapritori dichiarati:
|
||||
*«**$20k**, o un cambio di piano, o `p` che diventa **stimabile** invece che assunto»*.
|
||||
|
||||
Dal 1 settembre il segnale pubblico di solvibilità passa da **quotidiano ad annuale**. Il terzo
|
||||
riapritore non è formalmente chiuso — un audit indipendente è semmai evidenza più forte — ma
|
||||
diventa **praticamente irraggiungibile**: da una serie giornaliera si può costruire una stima, da
|
||||
un punto all'anno no. ⇒ **In pratica quella decisione è ora gated SOLO dal capitale**: si riapre a
|
||||
$20k, o non si riapre. Non cambia la decisione di oggi (fu presa con `p` esplicitamente **assunto**,
|
||||
non osservato), ma chiude l'unica uscita non-capitale che aveva.
|
||||
|
||||
## 3. 🚨 Quello che ho trovato cercando: quattro annunci materiali che nessuno leggeva
|
||||
|
||||
Il debito #8 dice, testuale: *«Resta scoperto il Rulebook vero e proprio: la sonda legge se il
|
||||
venue RISPONDE, non cosa il venue ANNUNCIA»*. Esiste un feed RSS pubblico
|
||||
(`insights.deribit.com/exchange-updates/feed/`) con 10 voci. Alla prima lettura:
|
||||
|
||||
| data | annuncio | perché ci tocca |
|
||||
|---|---|---|
|
||||
| 27/08 | Discontinue Daily Proof of Reserves | §1 e §2 di questo diario |
|
||||
| **14/08** | **Contract Specifications change for Linear USDC Perpetuals** | **i nostri due strumenti** |
|
||||
| 05/08 | New SM Margin Model On Deribit | leva per taglia, tiered |
|
||||
| **31/07** | **USDC Rewards Available In More Countries** | tocca una conclusione viva |
|
||||
| 29/06 | New Fee Schedule On Deribit | `fee_watch` esiste per questo |
|
||||
|
||||
**Il caso che decide la questione è il 14/08.** Le specifiche dei perpetual USDC sono cambiate il
|
||||
**18/08**; l'annuncio è del **14/08**. Il codice lo racconta già con onestà: la tabella «*non se
|
||||
n'era accorta […] è andata bene per la direzione del cambiamento, non perché ce ne fossimo
|
||||
accorti*» — i cambi erano **riduzioni** (tick BTC 0,5→0,1, ETH 0,05→0,01, min/step ETH
|
||||
0,001→0,0001) e un valore più grosso resta conforme. `check_specs()` è **nato da quella svista** e
|
||||
oggi gira ogni ora. Ma rileva la deriva **dopo**; il feed l'avrebbe detta **quattro giorni prima**.
|
||||
*Il giorno che Deribit ALZA un minimo, «dopo» significa ordini rifiutati.*
|
||||
|
||||
Il 31/07 chiude un cerchio aperto ieri: l'espansione delle giurisdizioni idonee ai reward USDC
|
||||
**non contiene l'Italia**, né alcun paese UE — mentre **San Marino e Città del Vaticano** (i due
|
||||
microstati europei fuori dall'UE) **sono idonei**. La lista che avevo letto (aggiornata 20/08) è
|
||||
posteriore all'espansione: **la conclusione dello zero USDC è confermata due volte**. Il pattern
|
||||
UE-fuori/microstati-dentro suggerisce un driver regolatorio, ma la lista contiene anche Canada e
|
||||
Giappone: **non asserisco una causa**, il fatto è l'esclusione.
|
||||
|
||||
## 4. Costruito: `venue_news.py`
|
||||
|
||||
Sorveglianza giornaliera del feed, dentro `cron_daily.sh` accanto a `fee_watch` (suo parente
|
||||
stretto). Scelte che contano:
|
||||
|
||||
- **NON interpreta.** Dice *«è uscito questo, guardalo»*. Nessun automatismo su un testo di
|
||||
marketing: P13 — una guardia sui NUMERI non copre il RAGIONAMENTO, e la prosa si legge come
|
||||
opinione di un lettore fallibile. L'unica cosa che classifica è l'**urgenza**.
|
||||
- **Le parole-chiave sono DERIVATE, non ridichiarate** (P1, il difetto più ricorrente del
|
||||
progetto): gli strumenti vengono da `deribit._CONTRACT`, la valuta di collaterale da
|
||||
`config/live.json`. *Chi aggiunge un asset al book allarga la sorveglianza senza toccare questo
|
||||
file* — ed è esattamente il test che lo blinda.
|
||||
- **Primo giro semina senza allertare** (P9: l'allarme massimo non si spende per un arretrato di
|
||||
10 voci, o non verrà letto il giorno che è vero).
|
||||
- **Feed illeggibile → codice 2 e nessun silenzio implicito** (P5: «non vedo» non è «niente di
|
||||
nuovo»).
|
||||
|
||||
5 test sulle funzioni pure. La rete no: `scarica()` ritorna `None` su qualunque errore, ed è quello
|
||||
il contratto.
|
||||
|
||||
**Cosa NON copre**, e va detto: il **Rulebook** vero e proprio (ADL, perdita socializzata,
|
||||
*emergency powers*, conti dormienti) non ha un feed. Il debito #8 si restringe, non si chiude.
|
||||
|
||||
---
|
||||
|
||||
## 5. La Knowledge Base risponde a due domande aperte — e corregge due cose mie
|
||||
|
||||
L'operatore ha passato anche *yield-generating-collateral-usde-buidl-and-more*, che **non** documenta
|
||||
tetti né haircut ma rimanda alla Knowledge Base. Interrogata via API Zendesk
|
||||
(`support.deribit.com/api/v2/help_center/articles/search.json`), l'articolo giusto è
|
||||
**«Cross collateral specifications»** (aggiornato 2026-02-20).
|
||||
|
||||
### (a) 🚨 Il costo del saldo negativo: **0,05% al GIORNO**
|
||||
|
||||
> «*While the equity of a currency in an account remains negative, a **collateral fee** will be
|
||||
> charged to that account. This fee is charged daily in the same currency as the negative balance
|
||||
> (**default = 0.05% per day**). The fee is charged based on the amount of time the negative equity
|
||||
> is held, down to a granularity of seconds.*»
|
||||
|
||||
**18,25% annuo**, cioè **4,3× la resa USDE** che si starebbe comprando tenendo meno USDC. Il 30/08
|
||||
avevo scritto «Deribit lo finanzia a interesse» **senza il numero** — l'affermazione era giusta e
|
||||
vuota. Con il numero il criterio del **cuscino di regolamento** smette di essere prudenza e diventa
|
||||
aritmetica: scendere sotto il cuscino per tenere più USDE è **−14 punti**.
|
||||
|
||||
E il ribilanciamento automatico **non salva**: scatta solo oltre **$1.000.000** assoluti o il
|
||||
**100% della cross equity** (default tabulati), soglie che a $2k non si toccano mai. Non veniamo
|
||||
ribilanciati: **sanguiniamo la fee**. *La cosa che sembrava un paracadute è, alla nostra taglia,
|
||||
esattamente l'assenza di un paracadute.*
|
||||
|
||||
### (b) 🚨 Haircut USDe: la fonte dice **5%**, noi abbiamo registrato **10%**
|
||||
|
||||
| valuta | haircut X:PM | X:SM |
|
||||
|---|---|---|
|
||||
| BTC · ETH · **USDC** | — | — |
|
||||
| USDT · USYC · **BUIDL** | 2% | 2% |
|
||||
| PAXG | 2,5% | 5% |
|
||||
| **USDe** | **5%** | **5%** |
|
||||
| stETH | 7,5% | 7,5% |
|
||||
| SOL | — | 15% |
|
||||
|
||||
`config/live.json` dice `haircut: 0.10`, e il diario del 26/08 lo dà per «verificato sul venue»
|
||||
senza lasciare traccia di **come**. L'articolo è **anteriore** a quella data, quindi non si sa
|
||||
quale delle due sia stale.
|
||||
|
||||
**Non l'ho riparato** (P12: fra due fonti che non concordano, una riparazione silenziosa è
|
||||
un'invenzione; M28: la contraddizione è informazione). Si tiene **0,10** per tre ragioni dichiarate:
|
||||
è il lato **conservativo** (sottostima il margine utilizzabile), **non è sul percorso soldi** (non
|
||||
entra nel sizing — lo usa solo il report di `usde_watch`), ed è già misurato che **non morde a
|
||||
nessuna quota fino al 90%**. Si chiude leggendo la pagina margini del conto, che è dove Deribit
|
||||
stesso dice di guardare.
|
||||
|
||||
### (c) Un dato di lato che vale per il futuro
|
||||
|
||||
**BUIDL ha haircut 2%** — sarebbe stato il *miglior* collaterale a rendimento del listino (contro
|
||||
il 5% dell'USDe), se solo lo si potesse comprare. E **stETH 7,5%**, il peggiore: terza ragione
|
||||
indipendente per lasciarlo stare, dopo il tasso dimezzato e l'esposizione ETH.
|
||||
|
||||
📌 **Nessuna delle due fonti documenta un tetto sulle quantità detenibili.** Il ~31,2% sull'USDE
|
||||
resta **misurato e non spiegato** — e ora si sa che non è una svista di lettura: non è scritto da
|
||||
nessuna parte.
|
||||
|
||||
### (d) Tentata la chiusura della divergenza sull'haircut: **non è raggiungibile da qui**
|
||||
|
||||
Deribit dice: «*Haircut rates can be seen on the margin page in your account*». Quella pagina è web
|
||||
UI, e le credenziali vivono solo dentro il gateway (debito #11). Provato comunque:
|
||||
|
||||
- `account_summary` per la valuta **USD** (la "riga USD" che il doc menziona) → `Invalid currency`;
|
||||
- parametro **`extended`** (che su Deribit apre i dettagli di margine) → **ignorato**;
|
||||
- il gateway filtra a **8 campi**: niente `initial_margin`, niente `maintenance_margin`.
|
||||
|
||||
E non è che si sia guardato nel secchio sbagliato — **la contabilità torna esatta senza haircut**:
|
||||
|
||||
```
|
||||
USDC equity 1412.281074 available 1400.728093 riservato 11.5530
|
||||
USDE equity 643.175691 available 643.175691 riservato 0.0000
|
||||
posizioni lorde $577.65
|
||||
|
||||
riservato in USDC ........... $11.5530
|
||||
IM di posizione al 2% ....... $11.5530
|
||||
RESIDUO ..................... $-0.0000
|
||||
```
|
||||
|
||||
Un haircut al **5%** chiederebbe **$32,16** accantonati, al **10%** ne chiederebbe **$64,32**: non
|
||||
ci sono, e il secchio USDE riserva `0.000000`. ⇒ **L'haircut non è osservabile in alcun campo
|
||||
esposto.** O è applicato solo nella vista cross/USD che il gateway filtra via, o non è applicato al
|
||||
nostro conto: da qui **le due cose non si distinguono**, e non le si sceglie tirando a indovinare.
|
||||
|
||||
**La divergenza resta APERTA**, ma ora con la ragione misurata invece che supposta. La chiudono due
|
||||
cose, entrambe dell'operatore: **30 secondi sulla pagina margini** della web UI, oppure delle
|
||||
**chiavi API Deribit** — che è la decisione già dichiarata nel debito #11, non un refactor.
|
||||
Nel frattempo **non morde nulla di osservabile**: l'IM è il 2% del nozionale e il valore pieno
|
||||
dell'USDE resta disponibile, quindi 5% o 10% non cambia una singola cifra operativa.
|
||||
|
||||
📌 **Sottoprodotto non cercato**: $11,5530 / $577,65 = **2,0000% = esattamente 1/50**. È il
|
||||
`C1 = 50` (start leverage, tier 1) del **nuovo modello di margine SM** annunciato il 05/08 — quello
|
||||
che `venue_news` ha appena tirato fuori dal feed. **Confermato sul nostro conto senza averlo
|
||||
cercato**, ed è la prima volta che un annuncio del venue viene verificato contro il conto invece
|
||||
che creduto.
|
||||
|
||||
### (e) L'operatore dichiara: il conto è **Standard Margin** — due conferme incrociate
|
||||
|
||||
Lo screenshot della pagina margini non è arrivato fin qui (sta sul desktop dell'operatore, non
|
||||
sulla VPS: il percorso non esiste su questa macchina). Ma il fatto dichiarato — **«è attivo
|
||||
Standard margin»** — aggancia due cose che erano sospese:
|
||||
|
||||
1. **Quale colonna leggere.** La tabella KB ha `Haircut (X:PM)` e `Haircut (X:SM)`. Il conto è
|
||||
**X:SM**, quindi vale la seconda — che per USDe dice **5%**, come la PM. La divergenza col
|
||||
nostro `0.10` resta quindi intatta, ma ora si sa con certezza *quale* numero ufficiale la
|
||||
contraddice, invece di doverne scegliere uno fra due colonne.
|
||||
2. **Perché l'IM misurata era esattamente 1/50.** Il nuovo modello di margine del 05/08 si applica,
|
||||
testuale, ai *«standard margin accounts»*. Il conto è standard margin, e noi abbiamo misurato
|
||||
IM = **2,0000%** del nozionale = **1/50** = il `C1 = 50` di tier 1. **Le due cose si confermano
|
||||
a vicenda**: un fatto dichiarato dall'operatore e una misura fatta sul conto senza conoscerlo.
|
||||
|
||||
*Vale la pena notarlo perché è raro: in tutta questa settimana ogni affermazione pubblicata dal
|
||||
venue si è rivelata falsa per il nostro conto (APR USDC, «all users can buy BUIDL»). Questa è la
|
||||
prima che il conto conferma.*
|
||||
|
||||
**Cosa manca ancora**, e resta l'unica cosa che chiude la divergenza: il numero di haircut per USDe
|
||||
**mostrato sulla pagina Standard Margin del conto**. Se dice 5%, la nostra config è stale e il 26/08
|
||||
registrò male; se dice 10%, la KB è stale e la config ha ragione. Finché non c'è, si tiene 0.10 —
|
||||
lato conservativo, fuori dal percorso soldi, e **senza un solo effetto operativo misurabile**.
|
||||
|
||||
---
|
||||
|
||||
## 6. La pagina margini arriva davvero — e la seconda cosa che dice vale più della prima
|
||||
|
||||
Lo screenshot era su **Wasabi** (`rclone` remote `wasabi:`, bucket `adp-work`, cartella `_scambio`),
|
||||
non sulla VPS: per questo il percorso non esisteva. Scaricato e letto.
|
||||
Riconciliazione riproducibile in **`scripts/research/r0831_margini_conto.py`** (N11).
|
||||
|
||||
### (a) ✅ Haircut = **5%**. La nostra config aveva torto.
|
||||
|
||||
La pagina **non espone l'haircut come numero**: si ricava per differenza, perché il modello CROSS
|
||||
conta l'USDE scontato e il SEGREGATO non lo conta affatto.
|
||||
|
||||
```
|
||||
S:SM (attivo) USDC Available 1,400.86 · USDC equity 1,412.28 − IM 11.55 = 1,400.73 (scarto $0.13)
|
||||
X:SM CROSS Available 2,011.73
|
||||
contributo USDE al cross = 2,011.73 − (1,412.28 + 0.20 − 11.55) = $610.81 su $643.05 di valore
|
||||
→ haircut implicito 5,0137%
|
||||
ipotesi 5% (KB) → atteso $610.89 scarto $ 0.09 ✅
|
||||
ipotesi 10% (nostra) → atteso $578.74 scarto $32.06 ❌
|
||||
```
|
||||
|
||||
`config/live.json` passa da `0.10` a **`0.05`**. Il 26/08 il 10% fu registrato come «verificato sul
|
||||
venue» **senza lasciare traccia di come**, e non lo era. *Una nota di provenienza che dice «verificato»
|
||||
senza dire con quale lettura non è provenienza: è la stessa parola usata come garanzia.*
|
||||
|
||||
### (b) 🚨 Il conto **non è cross-collateral**. È `Segregated: Standard Margin`.
|
||||
|
||||
Lo dice la schermata in cima — *Current status: **Segregated: Standard Margin*** — e la tabella del
|
||||
modello attivo elenca **BTC, ETH, USDC. L'USDE non c'è.**
|
||||
|
||||
⇒ **L'USDE non fa margine per i perp USDC-settled del book**, e l'haircut **oggi non si applica
|
||||
affatto**. Il che spiega, a posteriori e in modo pulito, perché la misura di stamattina trovava
|
||||
**$0,0000** accantonati: non stavo guardando nel secchio sbagliato, *stavo cercando un parametro
|
||||
che sul nostro conto non è in vigore*.
|
||||
|
||||
**NON MORDE**, e va detto subito per non allarmare: al massimo lordo del libro (1,0x ≈ $2.056 di
|
||||
nozionale) l'IM sarebbe ~$41 contro **$1.400 di USDC disponibile** — 34× di copertura. Nessuna
|
||||
decisione operativa cambia oggi.
|
||||
|
||||
**Ma la premessa scritta in CLAUDE.md era falsa**, e il modo in cui era falsa è istruttivo. Diceva:
|
||||
*«l'equity del book è il TOTALE cross-collateral»*. Sono due cose diverse messe sotto un nome solo:
|
||||
|
||||
- come **ricchezza**, sommare USDC+USDE è **giusto** — ed era il punto della riparazione del 26/08,
|
||||
che evitò il falso «USCITA DI FONDI −24%» e una vendita indesiderata;
|
||||
- come **capacità di margine**, è **sbagliato**: nel modello attivo l'USDE vale zero.
|
||||
|
||||
Finché il margine non morde le due coincidono nell'uso, e infatti non è mai emerso. *Un errore che
|
||||
non ha conseguenze finché una terza cosa resta vera è un debito, non un'assoluzione.* Corretto in
|
||||
CLAUDE.md, e corretta anche la riga di `usde_watch` che stampava «margine utilizzabile ~$2.013
|
||||
(haircut 10%)»: era falsa due volte insieme.
|
||||
|
||||
### (c) Come cambia il modo di pensare alla quota USDE
|
||||
|
||||
Non è «collaterale diversificato»: è **cassa messa da parte a rendimento, fuori dal sistema di
|
||||
margine**. Il che rende il tetto del venue al ~31,2% meno una limitazione e più un caso fortunato —
|
||||
tiene automaticamente dentro al 31% la parte di conto che non lavora come margine.
|
||||
|
||||
E apre una **decisione dell'operatore**, non mia: passare a **X:SM** aggiungerebbe **$610,87** di
|
||||
margine utilizzabile, ma porta con sé la meccanica cross — *collateral fee 0,05%/giorno* sul saldo
|
||||
negativo e ribilanciamento automatico. Oggi non serve (34× di copertura); servirebbe solo a leve
|
||||
che non sono autorizzate.
|
||||
|
||||
📌 **Ipotesi nuova sul tetto del ~31,2%**, non verificata: potrebbe dipendere proprio dal modello
|
||||
segregato. Si saprebbe passando a X:SM e ri-sondando — un esperimento che ora ha un senso, dove
|
||||
prima non si sapeva nemmeno cosa variare.
|
||||
|
||||
---
|
||||
|
||||
## 7. Passaggio a X:SM e ri-sondaggio del tetto — l'ipotesi regge tecnicamente e cade in pratica
|
||||
|
||||
L'operatore è passato a **Cross: Standard Margin** e ha chiesto di ri-sondare, per verificare
|
||||
l'ipotesi lasciata aperta in §6(c): *il tetto del ~31,2% dipende dal modello segregato?*
|
||||
|
||||
### Il passaggio è verificabile dal gateway, senza fidarsi di una dichiarazione
|
||||
|
||||
`available_funds` USDC è passato da **~$1.400** a **$2.011,09**, contro **$2.010,90** attesi
|
||||
sommando l'USDE scontato al 5% — **scarto $0,19**. È anche la **seconda conferma indipendente
|
||||
dell'haircut al 5%**, da una lettura completamente diversa dallo screenshot: due strade, stesso
|
||||
numero.
|
||||
|
||||
### Il risultato
|
||||
|
||||
| modello | tetto misurato | equity | frazione |
|
||||
|---|---|---|---|
|
||||
| Segregato **S:SM** (31/08) | [643,18 – 644,18) | $2.055,56 | 31,29% – 31,34% |
|
||||
| Cross **X:SM** (31/08, dopo) | [654,18 – 655,18) | $2.054,90 | **31,84% – 31,88%** |
|
||||
|
||||
**+0,55pp = +11 USDE ≈ $11.** ⇒ **L'ipotesi è vera e inutile**: il modello di margine *entra* nel
|
||||
tetto, ma non lo spiega. Per arrivare al 70% mancano ancora ~38 punti, un fattore **2,2×**.
|
||||
*Un'ipotesi confermata al terzo decimale e falsa all'ordine di grandezza va archiviata come falsa:
|
||||
il tetto resta misurato e non spiegato.*
|
||||
|
||||
### 🚨 La lezione di metodo, che vale più del risultato
|
||||
|
||||
Il **primo probe fu un BUY 20, rifiutato**. Da solo avrebbe chiuso la questione con *"tetto
|
||||
invariato, l'ipotesi è refutata"* — e sarebbe stato **falso**. Il tetto si era mosso di 11, cioè
|
||||
meno della taglia del probe. È stato il test a **saldo neutro con passi piccoli** (BUY 1 → SELL 10
|
||||
→ BUY 10 → BUY 1, tutti accettati) a rivelarlo.
|
||||
|
||||
📌 **Un probe unico di taglia sbagliata produce un falso negativo che si legge come risultato.**
|
||||
La taglia del sondaggio va scelta **sulla risoluzione dell'effetto che si cerca**, non su ciò che
|
||||
è comodo — ed è M13 in una veste nuova: *un criterio si misura sulla sua risoluzione prima che sul
|
||||
suo esito*.
|
||||
|
||||
### Cosa ha comprato il passaggio, e cosa è costato
|
||||
|
||||
- **Comprato**: **$611** di margine utilizzabile — inutile a 0,28x di leva, serve solo sopra ~1,4x
|
||||
che non è autorizzata — più **$11** di capienza sul tetto.
|
||||
- **Costato**: sotto segregato l'USDE era **ring-fenced** dalle perdite del libro (solo il silo
|
||||
USDC rispondeva delle posizioni); sotto cross **risponde l'intero conto**. Non morde oggi (perdita
|
||||
massima plausibile ~$617 col disaster-SL, dentro il solo USDC), ma **il rischio strutturale ha
|
||||
cambiato verso**. Se un giorno il cross non servisse più, tornare a S:SM ri-recinta l'USDE.
|
||||
|
||||
`config`: `venue_cap_frac` 0.312 → **0.318** (bordo basso del bracket CROSS, che è il modello
|
||||
attivo). Se si torna a S:SM va rimesso a 0.312.
|
||||
|
||||
## 8. **X:PM valutato e SCARTATO** — la risposta era già nello screenshot
|
||||
|
||||
L'operatore ha chiesto di provare **Cross: Portfolio Margin**. Non serve provarlo: il confronto è
|
||||
nella schermata di §6.
|
||||
|
||||
| modello | Available Balance | IM | MM |
|
||||
|---|---|---|---|
|
||||
| **X:SM** (attivo) | **$2.011,73** | 2,13% | **0,37%** |
|
||||
| **X:PM** | **$1.935,14** | 5,85% | **3,43%** |
|
||||
|
||||
Sulla colonna il cui significato è inequivocabile — stessa riga, stesse unità — **X:PM dà $76,59
|
||||
di collaterale utilizzabile in MENO**, e chiede più margine. ⚠️ *La frase «su entrambe le misure»
|
||||
è stata corretta in §9: l'«IM %» stampata dalla pagina non è il margine delle posizioni.
|
||||
La conclusione non cambia — migliora.*
|
||||
|
||||
**Perché**, ed è strutturale e non contingente: il Portfolio Margin è **basato su scenari di
|
||||
rischio** e premia i portafogli in cui il rischio si **compensa**, tipicamente i book di opzioni.
|
||||
Il nostro è **due long direzionali nudi senza nulla che compensi** — il caso peggiore per PM, che
|
||||
addebita lo scenario di stress invece di un'aliquota piatta.
|
||||
|
||||
**La riga che decide è la MM: 3,43% contro 0,37%, ~9×.** Il maintenance margin è ciò che innesca la
|
||||
liquidazione: non morde a 0,28x, ma sposta il punto di liquidazione molto più vicino su un libro
|
||||
con soldi veri. E il beneficio atteso sul tetto è noto per analogia: il salto S:SM→X:SM ne ha
|
||||
comprato **$11**.
|
||||
|
||||
⇒ **SCARTATO.** ⚠️ *Non "PM è peggio", ma "PM è peggio PER QUESTO portafoglio"*:
|
||||
**cosa lo riapre** — il giorno che VRP01 o un altro sleeve di opzioni entra in deploy, il rischio
|
||||
inizia a compensarsi e quel confronto **cambia di segno**. Va rifatto allora, non prima.
|
||||
|
||||
---
|
||||
|
||||
## 9. X:SM attivo: la pagina si ricostruisce dal gateway — e una mia lettura era sbagliata
|
||||
|
||||
L'operatore ha incollato la riga CROSS col modello **attivo** invece che proiettato:
|
||||
`Available $2.010,81 · IM 2,15% · MM 0,37%`. Il gateway, letto **allo stesso minuto** (08:34Z), dice
|
||||
`available_funds` = **2010,82299383**. **Scarto $0,01.**
|
||||
|
||||
### (a) Lo screenshot non serve più
|
||||
|
||||
⇒ **La riga CROSS della pagina È `available_funds`**, che leggiamo da soli a ogni giro. Ieri quella
|
||||
schermata era l'**unica** fonte per l'haircut e per il modello attivo, e per averla è servito un
|
||||
passaggio dall'operatore e da Wasabi. Oggi non serve a niente: `r0831_margini_conto.live()` la
|
||||
ricostruisce.
|
||||
|
||||
### (b) Terza conferma del 5%, e la prima ESATTA
|
||||
|
||||
1.400,71 + 0,95 × 654,176 + 0,20 dust − 11,553 IM = $2.010,82 pagina $2.010,81
|
||||
|
||||
**Scarto $0,007.** E il 10% della vecchia config non è *improbabile*: invertendo la stessa riga dà
|
||||
**IM = −$21,16**, cioè **aritmeticamente impossibile**. Le tre strade — differenza fra le righe
|
||||
della pagina (31/08 07:50), salto di `available_funds` al passaggio S:SM→X:SM ($0,19), e ora questa
|
||||
ricostruzione al centesimo — sono indipendenti e danno lo stesso numero.
|
||||
|
||||
### (c) 🚨 L'«IM %» della pagina NON è il margine delle posizioni — mia lettura sbagliata
|
||||
|
||||
`(margin_balance − available)/margin_balance = 2,1532%`, ed è **esattamente** ciò che la pagina
|
||||
stampa come «IM 2,15%». Ma si scompone così:
|
||||
|
||||
| voce | USD | % di margin_balance |
|
||||
|---|---|---|
|
||||
| haircut sull'USDE (5% × 654,18) | $32,71 | **1,592%** |
|
||||
| IM vera delle due posizioni | $11,55 | 0,562% |
|
||||
| **totale riservato** | **$44,26** | **2,153%** |
|
||||
|
||||
⇒ **il 74% di quella percentuale è haircut, non rischio.** È salita da 2,13% a 2,15% **perché
|
||||
abbiamo comprato collaterale a rendimento** — cioè per un motivo che con l'esposizione del libro
|
||||
non c'entra nulla. *Letta come misura di rischio direbbe che ieri sera abbiamo alzato la leva
|
||||
comprando USDE: l'opposto di quello che è successo.*
|
||||
|
||||
La **MM invece è pulita**: 0,37% = $7,60, e non può contenere l'haircut ($32,71 da solo la
|
||||
renderebbe negativa). **Le due colonne hanno basi diverse**, e la pagina non lo dice.
|
||||
|
||||
📌 Regola: *una percentuale letta da una schermata va **invertita nella sua definizione** prima di
|
||||
essere confrontata.* Due colonne affiancate, con la stessa unità e lo stesso aspetto, possono avere
|
||||
denominatori — e qui numeratori — diversi. È P7 su una superficie nuova: un numero si etichetta
|
||||
con la sua configurazione, e una pagina del venue non è obbligata a farlo per noi.
|
||||
|
||||
### (d) La decisione su X:PM esce RAFFORZATA
|
||||
|
||||
La KB dà **USDe al 5% sotto entrambi i modelli** (§6b): nel confronto l'haircut **si cancella**, e
|
||||
l'inversa restituisce il margine di posizione puro sulle stesse due posizioni, stesso istante:
|
||||
|
||||
| modello | available | **IM di posizione** |
|
||||
|---|---|---|
|
||||
| X:SM | $2.011,73 | **$11,64** |
|
||||
| X:PM | $1.935,14 | **$88,23** |
|
||||
|
||||
**PM chiede 7,6× il margine iniziale e 9,3× la MM.** Due misure ora pulite, stesso verso: la
|
||||
bocciatura di §8 **regge, meglio fondata di quando l'ho scritta**.
|
||||
|
||||
E c'è una conferma che ieri mancava: la riga X:SM era una **proiezione** di un modello **inattivo**,
|
||||
e oggi che è attivo la stessa aritmetica la riproduce al centesimo. ⇒ **anche la riga X:PM, che
|
||||
resta una proiezione, è affidabile**: la decisione presa senza provare il modello era presa su un
|
||||
numero buono. *Non provare X:PM è stato corretto, e ora si sa perché e non solo che.*
|
||||
@@ -0,0 +1,238 @@
|
||||
# 2026-09-01 — COLLAR01: il collar riduce il DD, il tetto lo paga troppo, e ciò che vince è VRP01 travestito
|
||||
|
||||
> ⚠️ **Titolo corretto lo stesso giorno.** Diceva *«il pavimento funziona»*: la riduzione di DD è del
|
||||
> **collar**, non del pavimento — la put da sola lo peggiora in 16/16 celle (§72, sezione A1 sotto).
|
||||
|
||||
*Scritto il 2026-09-01. Ogni numero è riprodotto da `scripts/research/r0901_btc_collar.py`.
|
||||
**Libro, pesi, cron, config INVARIATI. Nessun ordine.***
|
||||
|
||||
## 0. La domanda, come è arrivata
|
||||
|
||||
> *«crea una strategia di hold BTC in long o short con copertura con options (max 15gg)»*
|
||||
> *«opzione deve essere al max di 15gg»*
|
||||
> *«voglio ridurre la vincita, ma bloccare la perdita»*
|
||||
> *«ovviamente l'entrata deve essere gestita da una strategia confortata da indicatori (es. è in forte bull)»*
|
||||
> *«dalle opzioni dobbiamo uscire prima del termine (tra 50% e 75% del tempo)»*
|
||||
|
||||
Le tre precisazioni cambiano l'oggetto, e vanno lette insieme: **non è una put protettiva, è un
|
||||
COLLAR** (pavimento comprato, tetto venduto per finanziarlo), su un **hold gated da indicatori**,
|
||||
con **scadenza ≤15 giorni**.
|
||||
|
||||
## 1. Perché questo filone si poteva riaprire
|
||||
|
||||
§46 (TAIL-HEDGE, 2026-08-23) è **REFUTATO** e la memoria dice di battere il motivo, non di
|
||||
ripetere la misura. Il motivo di §46, verbatim:
|
||||
|
||||
> *il maxDD **SALE in 162/162 celle**, a ogni lente di f, perché il **beta del libro al sottostante
|
||||
> è +0,076**: non si assicura un libro che nei crash è già quasi piatto.*
|
||||
|
||||
**Quel motivo non si applica qui.** §46 assicurava il *libro* (TP01+SKH01, piatto il 28% dei
|
||||
giorni); qui il sottostante è un **hold di BTC, beta 1,0 per costruzione**. E §46 dichiara di non
|
||||
aver provato proprio questo: *«Nessuna copertura dinamica (gated su regime) è stata provata: la
|
||||
domanda era la statica»*.
|
||||
|
||||
Riusati e non rimisurati: metriche e `k_for_same_dd` da `r0823_tail_hedge`, il listino fee opzioni
|
||||
Deribit, la catena via `cblib.load_chain()`.
|
||||
|
||||
## 2. L'entrata, presa dal progetto invece che inventata
|
||||
|
||||
`trend_portfolio.tsmom_blend` media tre `np.sign()` sugli orizzonti (30, 90, 180), quindi assume
|
||||
**solo** i valori {−1, −1/3, +1/3, +1} (il bucket 2/3 non esiste — errore già corretto in §57).
|
||||
Questo dà a **"forte bull" una definizione che non aggiunge nemmeno un parametro nuovo**:
|
||||
|
||||
- **gate FORTE** = `|blend| == 1`, tutti e tre gli orizzonti concordi → a mercato **52,5%** dei giorni
|
||||
- **gate LARGO** = `|blend| ≥ 1/3`, confronto dichiarato → a mercato **97,1%** dei giorni
|
||||
|
||||
Il segno dà la direzione, quindi **long e short** sono entrambi coperti come chiesto. Si entra il
|
||||
giorno **dopo** il segnale (eseguibile, §8.1).
|
||||
|
||||
## 3. La calibrazione: quello che il modello non poteva assumere
|
||||
|
||||
161.964 quote a due lati su 95 giorni (2026-05-07 → 2026-09-01), scadenze 1-15 giorni.
|
||||
|
||||
**Lo skew, ed è il fatto strutturale del filone:**
|
||||
|
||||
| \|δ\| | IV put / ATM | IV call / ATM | la put costa |
|
||||
|---|---|---|---|
|
||||
| 0,10 | 1,249 | 0,959 | **+30%** della call |
|
||||
| 0,20 | 1,132 | 0,950 | **+19%** |
|
||||
| 0,30 | 1,072 | 0,960 | **+12%** |
|
||||
|
||||
**Un collar delta-simmetrico è un debito netto**: compro l'ala cara e vendo quella a buon mercato.
|
||||
Non è un dettaglio di prezzo, è la ragione per cui "gratis" non esiste a delta simmetrici.
|
||||
Spread (mezza forchetta / mid): put 1,9-8,3%, call 2,1-10,0% secondo il delta. Struttura a termine
|
||||
IV/DVOL30 ≈ 0,92-0,94 a 2-14 giorni.
|
||||
|
||||
📌 **A5 confermata, e corregge un muro di §46:** il tick da **5 USDC** che lì era «il secondo muro,
|
||||
strutturale» vale per la famiglia **USDC**. La catena che raccogliamo è **100% inverse**
|
||||
(`BTC-31JUL26-45000-P`), col tick in BTC. Il muro di §46 **non si applica a questo filone**.
|
||||
|
||||
## 4. Il risultato, in ordine di come uccide
|
||||
|
||||
Lente lunga 2021-03-24 → 2026-09-01 (1.988 giorni, 5,44 anni). BTC buy&hold nudo: Sharpe 0,413 ·
|
||||
maxDD 76,73% · drift +7,82%/a. Griglia dichiarata prima: 3 δput × 3 δcall × 2 tenor × 2 gate = **48
|
||||
celle**, più 12 varianti zero-cost dichiarate a parte.
|
||||
|
||||
### ✅ A1 CONFERMATA — il COLLAR riduce il maxDD (36/48) — ⚠️ ma NON è il pavimento a farlo
|
||||
|
||||
**maxDD scende in 36/48 celle.** Con beta 1,0 la put para davvero: è il risultato che §46 non
|
||||
poteva ottenere.
|
||||
|
||||
🚨 **CORREZIONE (stesso giorno, `r0901c_pavimento_leva.py`, §72).** Questa riga diceva *«il
|
||||
pavimento funziona davvero»* e attribuiva la riduzione al pavimento. **È il COLLAR a ridurre il DD,
|
||||
non il pavimento**: la put **da sola**, a premio reale, **peggiora il maxDD in 16/16 celle**
|
||||
(FORTE 51,83% → 55,4-75,8%, LARGO 71,94% → 74,8-81,7%). Nel collar la riduzione viene dal **tetto**
|
||||
— il suo premio compensa il bleed della put, e cappare l'upside **abbassa il picco** da cui il DD si
|
||||
misura. Il pavimento pareggerebbe il DD nudo solo se le put costassero il **36-79% del reale**.
|
||||
Quindi: *il motivo di §46 (beta) non si applica, ma il suo verdetto — il maxDD SALE — si riproduce
|
||||
a beta 1,0 per un motivo diverso:* **i drawdown di BTC sono grind di 290-818 giorni, e una put a
|
||||
≤15 giorni copre una finestra.** Dettaglio in `2026-09-01c-pavimento-leva.md`.
|
||||
|
||||
### ❌ A3 CONFERMATA — ma il de-levering lo fa meglio in 45/48 celle
|
||||
|
||||
| gate | base gated senza opzioni | |
|
||||
|---|---|---|
|
||||
| FORTE | Sharpe 0,442 · maxDD **51,83%** · drift **+10,04%**/a | ← il null da battere |
|
||||
| LARGO | Sharpe 0,578 · maxDD **71,94%** · drift **+17,37%**/a | |
|
||||
|
||||
Il null è **lo stesso hold gated, senza opzioni, scalato a iso-maxDD** — generato dallo stesso
|
||||
motore, non ridichiarato (P1). Il collar lo batte in **3 celle su 48**.
|
||||
|
||||
### ❌ A2 CONFERMATA — ogni punto di DD risparmiato costa 2-8 punti di drift
|
||||
|
||||
Δdrift/ΔmaxDD mediano: **7,90** col gate FORTE, **1,98** col LARGO. Il tetto costa più di quanto
|
||||
il pavimento renda, e non di poco.
|
||||
|
||||
### 🚨 C9 — non è protezione, è troncatura
|
||||
|
||||
Cella migliore, 196 cicli: **il tetto taglia nel 46,2% dei cicli vincenti, il pavimento para nel
|
||||
6,7% dei cicli perdenti.** Scatta **7 volte più spesso sui vincenti che sui perdenti** — la
|
||||
definizione letterale di C9. *La regola dell'operatore («ridurre la vincita, bloccare la perdita»)
|
||||
è implementata fedelmente: il problema è che su BTC quel baratto è pagato male.*
|
||||
|
||||
### ❌ M1 — dentro il libro non aggiunge nulla
|
||||
|
||||
Collar Sharpe 0,508 contro TP01 **0,852**, corr +0,486. TP01 + 10% di collar: Sharpe **+0,000** e
|
||||
maxDD **+2,32pp**. A 25%: **−0,065** di Sharpe e **+6,57pp** di maxDD. *Peggiora proprio la cosa
|
||||
che dovrebbe proteggere.*
|
||||
|
||||
## 5. 🚨 Il fatto che vale più del verdetto
|
||||
|
||||
**Tutte e 3 le celle vincenti stanno sul BORDO** della griglia (δput al minimo, δcall al massimo).
|
||||
M8: un argmax sul bordo non è una decisione. Ho esteso la famiglia (M4) con 36 trial dichiarati
|
||||
verso l'angolo — e lì **vince in 35/36 celle**, con il massimo *nell'angolo*:
|
||||
|
||||
> gate FORTE, 7 giorni, **δput 0,02 · δcall 0,50** → Sharpe **1,471** · maxDD **18,12%** · drift **+29,99%**/a
|
||||
|
||||
Il limite di quell'angolo è *nessun pavimento, tetto ATM*: **una covered call**. Cioè **la pendenza
|
||||
porta fuori da ciò che l'operatore ha chiesto e dentro lo short-vol.**
|
||||
|
||||
E quel Sharpe 1,471 non è una scoperta — **è il mio prezzatore che si paga da solo**:
|
||||
|
||||
| | DVOL / RV-forward | DVOL sta sopra |
|
||||
|---|---|---|
|
||||
| a 7 giorni | **1,320** | **76,9%** dei giorni |
|
||||
| a 14 giorni | **1,253** | **74,8%** dei giorni |
|
||||
|
||||
Riprezzando le opzioni alla **volatilità effettivamente realizzata** (diagnostica con look-ahead
|
||||
dichiarato — è il valore equo ex-post, non una strategia):
|
||||
|
||||
| cella | a DVOL | a vol realizzata | il VRP valeva |
|
||||
|---|---|---|---|
|
||||
| FORTE 7g δ0,02/0,50 | Sh **1,471** · +29,99% | Sh **0,511** · +8,45% | **+21,54 pp (72%)** |
|
||||
| LARGO 7g δ0,02/0,50 | Sh **1,141** · +32,17% | Sh **0,114** · **−1,10%** | **+33,26 pp (tutto)** |
|
||||
| FORTE 7g δ0,10/0,30 | VINCE | **perde** | +6,75 pp |
|
||||
| LARGO 14g δ0,10/0,30 | perde | perde | +8,02 pp |
|
||||
|
||||
**Il 72-100% dell'edge dell'angolo è il premio di varianza**, non la struttura. E ciò che
|
||||
sopravvive alla riprezzatura — **Sharpe 0,511** — è, entro il rumore, **il numero che il progetto
|
||||
ha già**: VRP01 a f=0,73 vale **Sharpe 0,47**. *Non ho trovato una strategia nuova: ho ri-scoperto
|
||||
VRP01 per una strada più lunga.* E §3 lo blocca comunque: **«niente short-vol da modello in
|
||||
deploy»** — questo è esattamente short-vol da modello.
|
||||
|
||||
## 5-bis. L'uscita anticipata: chiesta, implementata, e costa
|
||||
|
||||
L'operatore ha chiesto di **uscire dalle opzioni fra il 50% e il 75% del tempo**. Implementato come
|
||||
`exit_frac`, su tenor 14 giorni (⇒ uscita a 7 / 9 / 10 giorni). **La mia ipotesi a priori era che
|
||||
migliorasse**: uscire presto recupera valore temporale sulla put e rinuncia al theta più veloce
|
||||
sulla call, cioè proprio alla parte che stampava il premio di varianza. **Metà giusta, metà no.**
|
||||
|
||||
| cella `δ0,10/0,30`, gate FORTE | uscita | Sharpe | drift | null | esito | VRP residuo |
|
||||
|---|---|---|---|---|---|---|
|
||||
| a scadenza | 1,000 | 0,488 | **+9,48%** | +8,07% | VINCE | +3,38pp |
|
||||
| 75% | 0,750 | 0,457 | +8,59% | +7,72% | VINCE | +4,93pp |
|
||||
| 62,5% | 0,625 | 0,366 | +6,11% | +7,98% | **perde** | +2,51pp |
|
||||
| 50% | 0,500 | 0,278 | **+3,84%** | +8,04% | **perde** | **+1,27pp** |
|
||||
|
||||
**Il meccanismo previsto c'è** — il VRP residuo scende da +3,38 a +1,27pp, quindi uscire presto
|
||||
*davvero* rinuncia allo short-vol — **ma lo spread lo travolge**: −5,6 punti di drift sul gate
|
||||
FORTE, −10,2 sul LARGO (12,06% → 1,88%), e su `δ0,20/0,20` da −4,37% a −8,58%.
|
||||
|
||||
🚨 **E qui §46 va corretto nella sua applicazione.** §46 misurò: *«f si paga solo sulla parte di
|
||||
valore che converge a intrinseco, quindi un roll anticipato non lo paga»*. **Vero per una copertura
|
||||
SOLO LONG.** In un collar c'è una **gamba venduta da ricomprare**: uscire prima paga f *esattamente*
|
||||
sulla parte che a scadenza si sarebbe regolata gratis, e si fa ~2× più spesso per unità di tempo.
|
||||
**L'asimmetria di §46 si inverte quando la struttura ha una gamba corta.** È il risultato
|
||||
trasferibile di questa richiesta.
|
||||
|
||||
**Nella banda chiesta (50-75%) il verdetto si spacca:** a **0,75** la cella onesta passa ancora il
|
||||
null (di poco); a **0,625 e sotto** non lo passa più. Nessun `exit_frac` ribalta il verdetto del
|
||||
filone — lo peggiora.
|
||||
|
||||
## 6. I controlli dell'apparato (M15) — 3/3
|
||||
|
||||
§46 insegna che *«un controllo positivo rotto dichiara guasto l'apparato»*. Prima di credere a un
|
||||
verdetto negativo ho verificato che l'apparato sappia riconoscere un successo:
|
||||
|
||||
| controllo | atteso | misurato | |
|
||||
|---|---|---|---|
|
||||
| pavimento a **premio zero** (pranzo gratis) | deve VINCERE | maxDD 51,83%→**36,76%**, drift +10,04%→**+35,69%** | ✅ |
|
||||
| premio **×10** | deve PERDERE | drift **−33,96%**/a | ✅ |
|
||||
| zero-cost costruibile e finito | non NaN | drift −3,50%/a | ✅ |
|
||||
|
||||
## 7. Tre difetti miei, catturati dai controlli e non a occhio
|
||||
|
||||
1. **Bisezione dello zero-cost invertita** (`hi = md` dove serve `lo = md`): il premio cresce col
|
||||
delta, quindi se incasso troppo poco il tetto va *avvicinato*. Dava Sharpe −3,0/−3,9 e drift
|
||||
−53%/a — spazzatura che al primo giro avevo quasi pubblicato.
|
||||
2. **`dcall=NaN`** nella variante zero-cost propagava NaN nella cassa allo smontaggio anticipato.
|
||||
3. **C9 non consapevole della direzione:** usavo `S1 > S0` come proxy di "ciclo vincente", ma per
|
||||
un ciclo **short** un prezzo che sale è una **perdita**. Era la causa del risultato impossibile
|
||||
*«il pavimento para nello 0,0% dei cicli perdenti»* con una put a 10 delta.
|
||||
|
||||
E due difetti di contabilità trovati prima di misurare, che avrebbero **adulato il collar**:
|
||||
la base senza opzioni rollava lo spot ogni `tenor` giorni pagando ~3,6%/anno di fee inesistenti;
|
||||
e al roll delle opzioni chiudevo e riaprivo anche lo spot, che non ha motivo di muoversi.
|
||||
|
||||
## 8. Cosa NON ho misurato, dichiarato
|
||||
|
||||
- **La lente reale (quote vere, 2026-05→09) non è stata girata come backtest.** 123 giorni = ~8
|
||||
cicli non sovrapposti: sotto-potenziata per costruzione, e su una finestra in cui BTC è salito da
|
||||
~64,7k a ~77,5k — **avversa a un collar per costruzione**. La catena è servita a **calibrare**
|
||||
(skew, termine, spread), che è l'uso in cui 161.964 quote hanno potenza.
|
||||
- **Nessun DSR, nessun `study_family_honest`.** Non servono: il filone cade al **primo** gate (M5),
|
||||
e M2 si spende su ciò che il primo gate lascia in piedi.
|
||||
- **A6 e A7 non verificate** (lente reale non girata; il verso short è dentro i gate ma non
|
||||
separato). Restano previsioni non misurate, non risultati.
|
||||
- **Il funding dei perp non è nel motore**, come in ogni backtest del progetto: −2,16%/a di drift.
|
||||
Colpisce base e collar quasi allo stesso modo (stessa esposizione spot), quindi **non cambia il
|
||||
segno del confronto**, ma abbassa entrambi.
|
||||
|
||||
## 9. Verdetto
|
||||
|
||||
> **`IL PAVIMENTO FUNZIONA — E' IL TETTO CHE NON SI PUO' PAGARE. E CIO' CHE VINCE SUL BORDO NON E'
|
||||
> LA PROTEZIONE: E' IL PREMIO DI VARIANZA, CIOE' VRP01`** — **REFUTATO come chiesto.**
|
||||
|
||||
Con una postilla che è il vero risultato trasferibile: **la domanda «bloccare la perdita» ha
|
||||
risposta positiva sul maxDD (36/48) e negativa sul prezzo (45/48).** Su BTC il pavimento si compra
|
||||
meglio **tenendo meno BTC** che comprando una put e vendendo una call — e il de-levering, a
|
||||
differenza del collar, non tocca il rendimento nella coda destra dove BTC vive.
|
||||
|
||||
## 10. Cosa lo riaprirebbe
|
||||
|
||||
- Un **f di stress misurato su un crash catturato** — la stessa condizione che §3 pone allo
|
||||
short-vol. Con quello, l'angolo covered-call diventa discutibile invece che escluso.
|
||||
- Un sottostante **senza coda destra grassa**: il tetto costa perché BTC vive lì. Su un asset a
|
||||
distribuzione più simmetrica il baratto cambia di segno, e il conto va rifatto.
|
||||
- **Non** lo riapre un tenor diverso, un delta diverso o una griglia più fine: la pendenza è
|
||||
monotona verso l'angolo short-vol, e l'angolo è già misurato.
|
||||
@@ -0,0 +1,92 @@
|
||||
# 2026-09-01 — Stato trades, e la riparazione che non si è propagata
|
||||
|
||||
*Scritto il 2026-09-01, misure lette fra le 09:35Z e le 16:47Z. Numeri dalle serie citate.*
|
||||
|
||||
## 1. Lo stato, in breve
|
||||
|
||||
| | valore | fonte |
|
||||
|---|---|---|
|
||||
| fill registrati | **45**, dal 2026-07-14T14:00 al **2026-08-31T15:47** | `trades.db` |
|
||||
| round-trip chiusi | **29** (21 in utile) — lordo +41,43 · fee allocate 0,66 · **netto +40,77** | `trades.db` |
|
||||
| fee totali pagate | **$0,8617** | `trades.db` |
|
||||
| equity (lettura 16:47Z) | **$2.051,84** · picco $2.073,59 | serie `equity`, 1.657 letture |
|
||||
| posizione BTC | 0,0045 @ medio $79.208,11, dal 25/08 | libro di bordo |
|
||||
| posizione ETH | 0,0872 @ medio $2.473,04, dal 25/08 | libro di bordo |
|
||||
| non realizzato (mark 16:38Z) | **−$11,72** — BTC −7,91 (−2,22%) · ETH −3,80 (−1,76%) | gateway |
|
||||
| gross nozionale | $560,36 → **leva 0,27x** su un tetto di 1,0x | gateway |
|
||||
|
||||
**Nessun fill da 25 ore, e non è un blocco.** Il log del cron lo dice a ogni giro fino alle 15:47:
|
||||
`=> Nessuna azione: conto gia' al target netto del book`. TP01 fermo a +0,461 (BTC) e +0,278 (ETH),
|
||||
SKH01 flat su entrambi, scarto posizione-target sotto il `min_order_usd` di $5 ($356 contro $351,
|
||||
$214 contro $213). Feed SKH fresco (0 min), disaster-SL armati a $55.428,0 e $1.708,9.
|
||||
|
||||
`monitor_health`: **8/8 OK**, coperture 98-129%. Riconcilio a 3 fonti: **44/45 concordano, 0 prezzi
|
||||
divergenti**; l'unica coppia scoperta è il difetto già noto — stesso fill (ETH buy 0,04 @ 1869,74)
|
||||
timbrato `2026-07-14T14:00` nel log del cron e `2026-07-08` nel jsonl, i sei giorni di scarto della
|
||||
lezione già registrata. Non è un fill perso.
|
||||
|
||||
## 2. Il difetto: `--report` stampa un rendimento che non è un rendimento
|
||||
|
||||
`scripts/live/trades_db.py --report` stampa questa riga:
|
||||
|
||||
```
|
||||
equity : $598.06 -> $2,051.84 (+1453.78, +243.08%) | picco $2,073.59 | 1657 letture
|
||||
```
|
||||
|
||||
Accanto a una riga di P&L che dice `netto +40,77`. **Il +243% non è rendimento: è quasi tutto un
|
||||
versamento.** La serie di 1.657 letture orarie contiene **un solo salto**, il 2026-08-25 alle 11:47Z:
|
||||
$667,49 → $2.066,88, **+$1.399,39**.
|
||||
|
||||
Non è una mia classificazione a occhio: è quella del progetto. `journal.movimenti_capitale()`, che
|
||||
importa la soglia viva da `book.EQUITY_JUMP_ALERT` (0,10) e confronta il salto col massimo che il
|
||||
**mercato misurato** avrebbe potuto produrre in quell'ora a tetto di leva, restituisce
|
||||
`certi 1399.39 · ambigui 0.0`, un solo evento, classe `movimento`.
|
||||
|
||||
Al netto:
|
||||
|
||||
| lettura | valore |
|
||||
|---|---|
|
||||
| periodo 1 — 23/06 22:00Z → 25/08 10:47Z ($598,06 → $667,49) | **+11,61%** |
|
||||
| periodo 2 — 25/08 11:47Z → 01/09 16:47Z ($2.066,88 → $2.051,84) | **−0,73%** |
|
||||
| **TWR spezzato sul versamento** | **+10,80%** |
|
||||
| crescita di equity al netto del versamento, 69 giorni | **+$54,39** |
|
||||
|
||||
**Il numero regge al controllo incrociato.** Il giornale del 31/08, che lo scorporo lo fa già,
|
||||
scrive: *«cumulato dall'arming: $+1.461,39 di equity — di cui $+1.399,39 versati/prelevati ->
|
||||
trading $+62,00»*. Stessa base ($598,06), stesso versamento; la differenza fra i suoi +62,00 e i
|
||||
miei +54,39 è **−7,61 di marcatura** fra l'ultima lettura del 31/08 e quella di oggi. Le due misure
|
||||
concordano.
|
||||
|
||||
### Perché è un difetto e non un dettaglio di stampa
|
||||
|
||||
La riparazione **esiste già, e in questo stesso repo**. `src/live/journal.py:121` la porta scritta
|
||||
nel docstring, col caso d'origine:
|
||||
|
||||
> *«Il P&L di giornale e' un delta di equity, quindi un versamento ci finisce dentro come se fosse
|
||||
> profitto: la voce del 2026-08-25 dichiarava «giorno: +$1.414,57» quando $1.399 erano un deposito
|
||||
> USDC, e il «cumulato dall'arming» avrebbe mentito per sempre.»*
|
||||
|
||||
Quel difetto fu trovato e riparato nel giornale. **`trades_db.py:83` non ha mai ricevuto la
|
||||
riparazione**: calcola `100*(e1/e0-1)` sulla serie grezza e non chiama `movimenti_capitale()`, che
|
||||
è a un import di distanza e restituisce già `certi` e `ambigui` pronti da scorporare.
|
||||
|
||||
È una variante di **P1** — non un sorvegliante che ridichiara il proprio bersaglio, ma **una
|
||||
riparazione che non ha attraversato il confine fra due lettori della stessa serie**. Il primo
|
||||
strumento dice la verità; il secondo, interrogato con lo stesso comando pubblicato in CLAUDE.md §13
|
||||
(`trades_db.py --report`), stampa un numero che si legge come una performance ed è per il **96,3%**
|
||||
un bonifico.
|
||||
|
||||
**Danno oggi: nessuno sui soldi** — `--report` è in sola lettura e non decide niente. Il danno è di
|
||||
citazione: è esattamente la classe di numeri che §2 raccoglie sotto *«non citare»*, ed è l'unico che
|
||||
il progetto produce **su richiesta esplicita di un comando documentato**.
|
||||
|
||||
## 3. Cosa NON ho fatto
|
||||
|
||||
**Non ho toccato il codice.** La riparazione è piccola — chiamare `movimenti_capitale()` e stampare
|
||||
la riga scorporata accanto a quella grezza, come fa il giornale — ma sta su uno script che legge il
|
||||
libro di bordo vivo, e la richiesta era di aggiornare i documenti. Registrata come debito aperto.
|
||||
|
||||
Non ho scomposto la differenza fra il P&L dei round-trip (+$40,77) più il non realizzato (−$11,72)
|
||||
= **+$29,05** e i +$54,39 di equity al netto del versamento. Lo scarto di ~$25 sta in voci che il
|
||||
conteggio round-trip non copre — funding dei perp (non modellato in nessun backtest, §2), reward
|
||||
USDE, marcatura dell'USDE all'indice. A questa taglia non vale il costo della misura: si dichiara.
|
||||
@@ -0,0 +1,166 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,137 @@
|
||||
# 2026-09-01 — PAVIMENTO-LEVA: il pavimento non licenzia taglia, e corregge quanto scritto stamattina
|
||||
|
||||
*Scritto il 2026-09-01. Ogni numero è riprodotto da `scripts/research/r0901c_pavimento_leva.py`,
|
||||
che riusa il motore di `r0901_btc_collar.py`. **Libro, pesi, cron, config INVARIATI. Nessun ordine.***
|
||||
|
||||
## 0. La domanda
|
||||
|
||||
> *«possiamo usare quanto conosciamo del pavimento per studiare una strategia»*
|
||||
|
||||
Cosa sapevamo, da COLLAR01 (§71, poche ore prima): il collar riduce il maxDD in 36/48 celle; a
|
||||
premio **zero** il pavimento vale moltissimo (maxDD 51,83% → 36,76%, drift +10,04% → +35,69%); ciò
|
||||
che uccise il collar fu il **tetto**, non il pavimento (C9); la put è l'ala cara.
|
||||
|
||||
**L'inversione.** COLLAR01 chiese *«quanto DD mi risparmia il pavimento a taglia fissa?»* e perse
|
||||
contro il de-levering. La domanda speculare è *«quanta TAGLIA mi autorizza il pavimento a DD
|
||||
fisso?»*. Non è la stessa, per due ragioni misurabili:
|
||||
|
||||
1. de-levare riduce taglia e rischio **in proporzione**; un pavimento cambia la **forma** — se taglia
|
||||
la coda sinistra più di quanto costi, scalare *in su* la struttura protetta può battere la base
|
||||
nuda alla stessa DD. È l'unica cosa che il de-levering **non può** comprare;
|
||||
2. il tetto di leva del progetto non è fissato dallo Sharpe ma dal **costo di un episodio** di
|
||||
disaster-SL (`n · frac · scala · sl ≤ 0,50` ⇒ k ≤ 1,67x). Il disaster-SL è **rotolante** e non
|
||||
limita la perdita (peggior DD dall'ingresso misurato −60,6%). Una put la limita per costruzione —
|
||||
*dentro la sua finestra*.
|
||||
|
||||
Quattro attese scritte prima di misurare (B1-B4). **Tutte e quattro confermate.**
|
||||
|
||||
## 1. Il risultato: 0 celle su 16
|
||||
|
||||
Griglia dichiarata: 4 δput × 2 tenor × 2 gate. Struttura: hold gated + **sola put**, premio reale
|
||||
(DVOL × skew × termine dalla catena — realistico per chi *compra*: M29 corregge chi vende a
|
||||
modello, non chi compra).
|
||||
|
||||
| gate | base nuda | pavimento a premio reale (16 celle) |
|
||||
|---|---|---|
|
||||
| FORTE | maxDD **51,83%** · drift +10,04% | maxDD **55,4% → 75,8%** · drift +3,8% → −17,0% |
|
||||
| LARGO | maxDD **71,94%** · drift +17,37% | maxDD **74,8% → 81,7%** · drift +9,9% → −13,9% |
|
||||
|
||||
**Il pavimento peggiora il maxDD in 16 celle su 16.** Quindi `k_f = 1,000` ovunque: non c'è taglia
|
||||
da licenziare, perché non c'è DD da spendere. Il null M5 classico concorda (0/16). Monotono nella
|
||||
protezione: più la put è vicina, peggio va — il bleed del premio *è* il drawdown.
|
||||
|
||||
## 2. 🚨 La correzione a COLLAR01
|
||||
|
||||
Stamattina ho scritto, in A1: *«il pavimento funziona davvero (e §46 è battuto sul suo motivo)»*.
|
||||
**La prima metà è sbagliata nell'attribuzione.** Il DD lo riduce **il collar**, non il pavimento:
|
||||
|
||||
- la put da sola, a premio reale, **peggiora** il DD (questo filone);
|
||||
- a premio zero lo riduce (il controllo del pranzo gratis) — quindi **tutto il beneficio del
|
||||
pavimento è mangiato dal suo premio, e oltre**;
|
||||
- nel collar la riduzione viene dal **tetto**: il suo premio compensa il bleed della put, e cappare
|
||||
l'upside **abbassa il picco** da cui il DD si misura — che non è protezione, è un picco più basso.
|
||||
|
||||
**Il premio di pareggio** lo quantifica: il pavimento inizia a ridurre il DD nudo solo se le put
|
||||
costano il **36-79% del reale** (secondo la cella). Il DVOL sta 1,32× sopra la vol realizzata, quindi
|
||||
un prezzo *equo* sarebbe ~76% del reale: **anche a prezzo equo il pareggio si sfiora nelle celle
|
||||
migliori e si manca nelle altre**. E il mercato non vende a prezzo equo.
|
||||
|
||||
La seconda metà della frase regge, ma va detta con precisione: **il motivo di §46 non si applica**
|
||||
(a beta 1,0 la put paga davvero: para nel 2-20% dei cicli), **ma il verdetto di §46 — *il maxDD
|
||||
SALE* — si riproduce a beta 1,0 per un motivo diverso.** Il motivo è la sezione seguente.
|
||||
|
||||
Corretti stesso giorno: il diario di COLLAR01 (titolo e A1), RESULTS §71, la memoria. M28:
|
||||
*quando due affermazioni della stessa memoria si contraddicono, la contraddizione è informazione* —
|
||||
qui erano due mie affermazioni a poche ore di distanza, e la seconda ha corretto la prima.
|
||||
|
||||
## 3. B2 — Il meccanismo: la put copre una finestra, BTC scende in grind
|
||||
|
||||
| gate | maxDD della base | da → a | durata |
|
||||
|---|---|---|---|
|
||||
| FORTE | −51,83% | 2021-07-20 → 2022-05-06 | **290 giorni** |
|
||||
| LARGO | −71,94% | 2021-07-20 → 2023-10-16 | **818 giorni** |
|
||||
|
||||
Una put a 7-14 giorni copre **una** finestra. Il drawdown che conta è lungo **20-60 finestre**. In
|
||||
un grind la put scade OTM settimana dopo settimana — il pavimento para nel **0-9%** dei cicli col
|
||||
gate FORTE — mentre il premio sanguina. E non protegge nemmeno la finestra peggiore: peggior 14
|
||||
giorni FORTE **−22,9% nudo → −23,6% col pavimento**, perché la put a 5 delta sta a ~22% dallo spot,
|
||||
cioè esattamente al bordo di ciò che è successo, e il premio la rende netta negativa.
|
||||
|
||||
**Su BTC il pavimento compra protezione contro la cosa sbagliata**: i crolli veloci dentro una
|
||||
finestra, mentre le perdite che definiscono il maxDD sono grind di mesi. Questo è ciò che §46 non
|
||||
poteva vedere (a beta 0,076 il libro non aveva né crolli né grind da proteggere) e che a beta 1,0
|
||||
diventa il fatto dominante.
|
||||
|
||||
## 4. B3 — La licenza del disaster-SL è di carta
|
||||
|
||||
L'invariante `n · frac · scala · sl ≤ 0,50` con `sl = 0,30` dà **1,67x**. Con la put a 5δ/14g il
|
||||
pavimento sta a **21,9%** dallo spot: sostituire `sl` con quella distanza darebbe **2,28x** —
|
||||
*1,4× in più*. È la proposta che qualcuno farà, e va uccisa ora:
|
||||
|
||||
| finestre consecutive | giorni | peggior perdita col pavimento | la put… |
|
||||
|---|---|---|---|
|
||||
| 1 | 14 | −23,6% | copre |
|
||||
| 2 | 28 | −25,9% | **non copre** |
|
||||
| 4 | 56 | −28,8% | **non copre** |
|
||||
| 8 | 112 | **−33,6%** | **non copre** |
|
||||
|
||||
L'invariante limita **un episodio**; la put limita **una finestra**; il massimo su finestre
|
||||
consecutive **non è limitato da nulla**. A 2,28x un grind di 112 giorni costerebbe il **77%
|
||||
dell'equity**. ⇒ **`disaster_sl_pct` non si sostituisce con la distanza di un pavimento**, e va
|
||||
scritto prima che sia comodo per qualcuno.
|
||||
|
||||
## 5. B4 — Il pavimento gated sull'IV: migliora, non basta
|
||||
|
||||
§46 dichiarava *«nessuna copertura dinamica gated su regime è stata provata»*. Provata: put ON solo
|
||||
quando DVOL/RV trailing sta sotto il 25° o 50° percentile dell'anno (il premio di varianza è
|
||||
sottile).
|
||||
|
||||
| gate | soglia | ON (giorni a mercato) | drift | vs base |
|
||||
|---|---|---|---|---|
|
||||
| FORTE | 25° pctl | 27% | **+8,39%** | +10,04% → **perde** |
|
||||
| FORTE | 50° pctl | 47% | +6,77% | perde |
|
||||
| LARGO | 25° pctl | 25% | **+15,31%** | +17,37% → **perde** |
|
||||
| LARGO | 50° pctl | 50% | +13,93% | perde |
|
||||
|
||||
Migliora molto rispetto al pavimento sempre acceso (+3,8% → +8,4% col gate FORTE) e il DD resta
|
||||
≥ della base (k_f = 1,000). ⚠️ L'incollatura ignora lo spread delle transizioni ON/OFF, **a favore**
|
||||
del gated: se perde così, perde a maggior ragione.
|
||||
|
||||
## 6. Cosa NON ho fatto, dichiarato
|
||||
|
||||
- Nessun DSR, nessun `study_family_honest`: il filone cade al primo gate, 0/16.
|
||||
- Nessuna lente reale (123 giorni, ~8 cicli): stessa ragione di COLLAR01.
|
||||
- Non ho provato il pavimento **a scadenza lunga** (30-90 giorni), che coprirebbe più di una
|
||||
finestra di grind — perché l'operatore ha vincolato la scadenza a **≤15 giorni**. È l'unica
|
||||
variante che B2 lascia aperta, e sta fuori dal vincolo dichiarato. Va detto: se il vincolo cadesse,
|
||||
sarebbe la prima cosa da misurare, e la memoria di §46 ha già l'apparato (tenor 30).
|
||||
|
||||
## 7. Verdetto
|
||||
|
||||
> **`IL PAVIMENTO NON LICENZIA TAGLIA A PREMIO REALE: 0/16. Ciò che il de-levering non può comprare,
|
||||
> la put lo vende a un prezzo che nessuna cella ripaga — e su BTC compra protezione contro i crolli
|
||||
> mentre le perdite sono grind.`** — REFUTATO.
|
||||
|
||||
**Cosa lo riapre:** una scadenza che copra il grind (fuori dal vincolo ≤15g), o un mercato che venda
|
||||
la put al 36-79% del prezzo attuale — cioè un premio di varianza *negativo*, che su BTC non si è
|
||||
mai misurato. Non lo riaprono delta, tenor ≤15 o gate sull'IV: sono misurati.
|
||||
@@ -0,0 +1,91 @@
|
||||
# 2026-09-01 — Soldi fermi: i modi esistono, valgono €5/mese, e uno di loro distruggerebbe lo split
|
||||
|
||||
*Scritto il 2026-09-01. Numeri da `scripts/research/r0901d_soldi_fermi.py`; tassi dal web con fonte e
|
||||
data (cutoff del modello 05/2026: non li sapevo). **Non sono pareri fiscali.** Nessun ordine.*
|
||||
|
||||
## 0. La domanda
|
||||
|
||||
> *«troviamo altri modi per fare guadagnare con soldi che sarebbero fermi. non per forza trading»*
|
||||
|
||||
## 1. Dove sta il capitale fermo — e cosa NON lo è
|
||||
|
||||
**Deribit (lettura 20:42Z):** USDC $1.406 · USDE 654 · libro a 0,27x. ⚠️ **L'USDC non è fermo**: è
|
||||
la base di sizing del libro (cap = equity × 0,5) e il cuscino di regolamento del disaster-SL. Spostarlo
|
||||
fuori rimpicciolisce le posizioni, non libera capitale.
|
||||
|
||||
**Fuori:** ~€6.043 in XEON «a ~0% reale netto» (memoria 27/07). È lo **split-cassa** — la protezione
|
||||
dal rischio venue, P(perso tutto) 18% → 3,5% a p=1% — e la memoria dichiara di non sapere quanto sia
|
||||
fondo d'emergenza. **Assunzione dichiarata: €6.000 investibili a 12+ mesi.**
|
||||
|
||||
## 2. On-venue: chiuso, e stavolta con l'elenco intero
|
||||
|
||||
`public/get_currencies` oggi: **4 valute su 50** con APR > 0 — USDE 4,32 · USDC 3,40 · BUIDL 3,20 ·
|
||||
STETH 2,22. Sono esattamente le quattro chiuse il 31/08 (al tetto · Italia esclusa · non
|
||||
acquistabile · è ETH). Non era un campione: era la lista.
|
||||
|
||||
## 3. I modi, in euro netti l'anno su €6.000
|
||||
|
||||
| opzione | lordo | fisco | **netto/anno** | vs XEON | rischio | split |
|
||||
|---|---|---|---|---|---|---|
|
||||
| XEON (status quo) | 2,10% | 26% | **€81** | — | ~0 | intatto |
|
||||
| **BOT 12 mesi** | 2,77% | **12,5%** | **€133** | **+€52** | ~0 (Stato) | intatto |
|
||||
| **Conto deposito vinc. 12m** | 3,50% | 26% | **€143** | **+€62** | ~0 (FITD) | intatto |
|
||||
| sUSDe diretto (Ethena) | ~9% | 33% | €350 | +€269 | **emittente + smart contract** | **distrutto** |
|
||||
| versare sul libro | ~10% atteso | — | ~€600 | +€519 | maxDD 11%, venue | **distrutto** |
|
||||
|
||||
Fonti: BOT 2,768% asta agosto 2026 (Banca d'Italia 14/08); BCE DFR 2,25% (23/07) ⇒ €STR ~2,2%;
|
||||
conti deposito 3,25-3,50% (Aidexa, Banca Progetto, Cherry, settembre 2026); sUSDe «high single
|
||||
digits» Q2 2026 (9,4% 7g / 11,8% 90g ad aprile). Bollo 0,2% su tutto.
|
||||
|
||||
## 4. La lettura
|
||||
|
||||
- **Fra le opzioni che lasciano intatto lo split, la migliore vale +€62/anno = €5/mese.** BOT e conto
|
||||
deposito distano €10/anno: il 12,5% del BOT compensa quasi tutto il lordo in meno. Dentro il
|
||||
rumore di *una* asta.
|
||||
- **sUSDe diretto rende ~2× Deribit** (che trattiene metà dell'APR Ethena) — ma metterebbe i €6k
|
||||
sullo **stesso emittente** dei 654 USDE già in conto. Lo split-cassa esiste per non essere
|
||||
correlato al venue e al crypto: questa opzione lo trasforma in concentrazione. Già a 24,2% di quota
|
||||
l'evento emittente vale 3,1× il maxDD dell'intero libro (memoria 30/08).
|
||||
- **Versare sul libro** è l'alternativa che la memoria indica come dominante (€10k = 2,45× in 13
|
||||
anni) — ma è trading, e distrugge lo split. Non risponde alla domanda: la cambia.
|
||||
|
||||
## 5. La taglia
|
||||
|
||||
Il miglior guadagno *sicuro* sullo status quo è **€62/anno**. **€100/mese di bonifico valgono
|
||||
+4,07%/anno di drift.** Il modo esiste, è reale, ed è due ordini di grandezza sotto la leva che il
|
||||
progetto ha misurato sei volte come binding.
|
||||
|
||||
## 6. Cosa resta all'operatore
|
||||
|
||||
1. **Quanto dei €6.043 è fondo d'emergenza.** Con quello, la tabella si applica al resto.
|
||||
2. Fra BOT e conto deposito: **preferenza di liquidità** (MOT vs vincolo 12 mesi) e di controparte
|
||||
(Stato vs FITD), non di rendimento — la differenza è €10/anno.
|
||||
3. On-venue non c'è niente da decidere. La chiave di scala a 1,25x (GATE SCALA-01, ottobre)
|
||||
libererebbe ~$400 → ~$10/anno: lo dico perché è l'unica via strutturale, non perché valga.
|
||||
|
||||
## 7. La conclusione dell'operatore (02/09, 06:00Z) — e le tre precisazioni
|
||||
|
||||
> *«quindi la miglior cosa è mettere gli XEON su deribit con il nostro sistema attivo»*
|
||||
|
||||
**Sì, ed è la conclusione che la memoria aveva già scritto il 27/07** (`r0727_lumpsum_split.py`:
|
||||
piano «€5.000 dei €6.043 in XEON + €500/mese»; il 25/08 sono entrati $1.399, non €5.000). La
|
||||
lettura della tabella è quella giusta: versare batte tenere fermo, ed è la leva binding. Ma il
|
||||
*come* ha tre vincoli, tutti già misurati:
|
||||
|
||||
1. **Non tutti: €5.000, non €6.043.** La protezione dal rischio venue **satura a qualunque quota
|
||||
> 0** (P(perso tutto) 18% → 3,5% tenendo fuori il 10%, e resta 3,5% al 25% e al 40%). L'ultimo
|
||||
migliaio dentro compra ~€100/anno attesi e butta via l'intera assicurazione. **~€1.000 restano
|
||||
fuori: è lo split, e costa zero.**
|
||||
2. **Il «~€600» non è un tasso.** È un'attesa a banda larga su una strategia a mercato il 22% dei
|
||||
giorni (live: 14/64, leva mediana 0,00x). Numeri onesti: TWR **+10,8% in 70 giorni**, Sharpe
|
||||
hold-out TP01 **~+0,05**, fisco d'accumulo **−30% a 10 anni**. Un anno piatto è possibile; il
|
||||
conto deposito non lo ha.
|
||||
3. **Il fondo d'emergenza** — non dichiarato dall'operatore, non noto alla memoria. Se una parte
|
||||
dei €6.043 lo è, resta fuori.
|
||||
|
||||
**Operativamente non serve nulla:** il sistema è live dal 20/06, il cap è dinamico (`equity × 0,5`),
|
||||
il rilevatore di salti ha già gestito il 25/08 senza falsi allarmi. Il deposito viene assorbito al
|
||||
giro successivo delle :47. Resta da scrivere **una riga di giornale con data e importo** — la fonte
|
||||
del cumulato dall'arming va scritta prima, non ricostruita dopo (debito #14). **In attesa di importo
|
||||
e data dall'operatore.** N9: le opzioni sicure (+€52-62/anno) sono state viste e messe da parte
|
||||
*con motivo*, non ignorate.
|
||||
@@ -0,0 +1,76 @@
|
||||
# 2026-09-02 — Debito 14: il report stampa il TWR, non il bonifico
|
||||
|
||||
*Scritto il 2026-09-02 fra le 14:16Z e le 14:22Z (commit `2f701b8`, 14:22:50Z; la prima stesura diceva «fino alle 14:30Z» senza averlo letto — corretto in revisione). Numeri dalla lettura di equity delle 13:47:01Z (1.678 letture) e dal report rilanciato dopo la riparazione.*
|
||||
|
||||
## Cosa c'era
|
||||
|
||||
`trades_db.py --report` calcolava `100*(e1/e0-1)` sulla serie grezza di equity e lo stampava come
|
||||
performance: **+243%**. La serie contiene un solo salto, il versamento di $1.399,39 del 25/08
|
||||
11:47Z: il 96,3% di quel numero era un bonifico. Il classificatore che lo scorpora esisteva già
|
||||
(`journal.movimenti_capitale`, dal 25/08) e non era mai arrivato al secondo lettore della stessa
|
||||
serie. Trovato e registrato il 01/09 (`2026-09-01-stato-trades.md`), non riparato perché lo script
|
||||
legge il libro vivo.
|
||||
|
||||
## Cosa ho fatto
|
||||
|
||||
| pezzo | dove | cosa |
|
||||
|---|---|---|
|
||||
| funzione unica | `src/live/journal.py` → `rendimento_twr(con, fino_ts)` | chiama `movimenti_capitale`, spezza la serie all'equity PRIMA e DOPO ogni movimento **certo**, moltiplica i segmenti. Gli **ambigui non spezzano** (P12): restano nel rendimento, dichiarati. Tre stati: numero, o `None` con `motivo` |
|
||||
| il report | `scripts/live/trades_db.py` → `report()` | chiama `rendimento_twr` (P1: non rifà il conto). Stampa TWR con i segmenti datati, i movimenti elencati con la classe, `trading da arming`, e il delta $ grezzo etichettato «movimenti di capitale INCLUSI». Il `%` grezzo non compare più |
|
||||
| test | `tests/test_journal.py` (+5) · `tests/test_trades_report.py` (+2) | versamento spezzato e grezzo no · senza movimenti = rendimento semplice · ambiguo non spezza · serie vuota → `None` con motivo · **riproduzione del +10,80% del diario 01/09** (M23) · il testo stampato contiene TWR e non il grezzo · `connect()` nudo deviato in tmp (conftest), verificato nel test |
|
||||
|
||||
## Il report, prima e dopo
|
||||
|
||||
```
|
||||
prima: equity : $598.06 -> $2,051.84 (+1453.78, +243.08%)
|
||||
|
||||
dopo: equity : $598.06 -> $2,048.40 (+1,450.34 di equity, movimenti di capitale INCLUSI) | picco $2,073.59 | 1678 letture
|
||||
movimenti capitale : +1,399.39 certi | +0.00 ambiguo/i (restano nel P&L, dichiarati)
|
||||
2026-08-25T11:47 +1,399.39 [movimento]
|
||||
trading da arming : +50.95 (equity al netto dei movimenti certi)
|
||||
TWR : +10.61% = +11.61% [2026-06-23 -> 2026-08-25] x -0.89% [2026-08-25 -> 2026-09-02]
|
||||
```
|
||||
|
||||
Il giornale del giorno concorda: «cumulato dall'arming $+1.450,34 — di cui $+1.399,39 versati ->
|
||||
trading **$+50,95**». Stessa base, stesso versamento, stessa funzione.
|
||||
|
||||
## Una cosa imparata riparando
|
||||
|
||||
Quattro test sono nati rossi con scenari a **+10% di trading** fra due letture: la soglia del
|
||||
rilevatore (`book.EQUITY_JUMP_ALERT`) è esattamente 0,10, e a mercato piatto un +10% supera «2× il
|
||||
massimo che il mercato poteva fare» (che è zero) — viene classificato **movimento**. Non è un difetto
|
||||
della riparazione: è il limite già dichiarato del rilevatore live (D5, nel docstring di
|
||||
`movimenti_capitale`). Nella serie vera non morde — fra due letture orarie consecutive il trading non
|
||||
fa +10% — ma un test che lo tocca lo rende visibile. I test ora usano +5%, e il limite è scritto
|
||||
nel test, nel CLAUDE.md e qui.
|
||||
|
||||
## Cosa NON ho fatto
|
||||
|
||||
- Il giornale (`pnl_giorno`, pagina markdown) **non stampa il TWR**: stampa già lo scorporo in
|
||||
dollari e la sua pagina ha una forma testata. Aggiungerlo è una riga, ma non era il debito.
|
||||
- Non ho scomposto lo scarto (~$25) fra round-trip + non realizzato e il trading al netto: era già
|
||||
fuori scope il 01/09 e lo resta.
|
||||
|
||||
## Revisione del codice, prima tornata (15:15Z, commit `835e0c8` 15:17:05Z) — tre cose trovate, tutte riparate
|
||||
|
||||
| trovato | vero? | riparazione |
|
||||
|---|---|---|
|
||||
| l'intervallo che contiene un movimento certo esce intero dal rendimento: il suo P&L di mercato finisce in `certi` e fuori da `trading`, e nessun documento lo diceva | sì, per costruzione. Errore massimo = metà del movimento riconosciuto (margine del classificatore). Il 25/08: ~$263 lordi a ±0,2% → ~$0,5 | dichiarato nel docstring, nel report, in CLAUDE.md §5.14 e in memoria 40. Non si stima (P12) |
|
||||
| a base di equity zero `trading` era un numero mentre `twr` era None, e sbagliato: il salto 0→X è invisibile al classificatore, `trading` valeva l'intero conto sotto l'etichetta "al netto dei versamenti" | sì. Non raggiungibile dal cron (non scrive mai 0), ma raggiungibile da un backfill | tre stati veri: `twr` e `trading` entrambi None con motivo; il report stampa n/d. Test in `test_journal.py` e `test_trades_report.py` |
|
||||
| movimento nel primo intervallo o due consecutivi → segmento `+0.00% [x -> x]` | sì, cosmetico | i segmenti a lunghezza zero non si producono; nessun tempo a mercato → TWR 0,0 dichiarato numero. Test |
|
||||
|
||||
Il numero pubblicato non cambia: +10,61% alle 14:02Z, +10,56% alle 14:47Z per la marcatura.
|
||||
|
||||
## Seconda tornata (15:30Z) — la piu' importante di tutte
|
||||
|
||||
| trovato | vero? | riparazione |
|
||||
|---|---|---|
|
||||
| il classificatore dei movimenti era **cieco ~23 ore al giorno**: il feed 1h si ferma alle 00:00, `asof` dava la stessa barra alle due letture, mercato «fermo» = 0, qualunque calo ≥10% del giorno diventava «movimento». Riprodotto dalla revisione su copia del DB: −12% appeso alle 14:47 → `certi` 1.399 → 1.153, TWR invariato invece di −2,71% | si'. Verificato: ultima barra 00:00, adesso 15:26 | nuovo stato **«mercato non misurabile»** (feed fermo, assente, o barra mancante) ⇒ `ambiguo` con la ragione; `twr` e `trading` None con motivo |
|
||||
| con `data/raw` assente (host ripristinato prima del rebuild) tutto era «ambiguo», `certi` 0, e il report stampava **+243% sotto l'etichetta TWR** | si' | stesso stato: n/d, mai il grezzo |
|
||||
| «`rendimento_twr` e' la funzione che entrambi chiamano» era **falso**: `pnl_giorno` rifaceva `cum − certi`, e la testata Telegram mandava il cumulato grezzo | si' | `pnl_giorno` chiama `rendimento_twr`; la pagina stampa il TWR accanto al cumulato; la testata dice «di cui versati → trading» |
|
||||
| `leva_tetto` del classificatore ridichiarava `frac × n` ignorando `book_scale_k` e `LEVA_LORDA_MAX` (P1) | si' | `min(frac × n × scala, LEVA_LORDA_MAX)`; fallback = tetto di codice |
|
||||
| il «limite ereditato: +10% di trading a mercato fermo» scritto la mattina era un **artefatto della fixture piatta** (a mercato misurato il trading non supera 2·max_mkt) | si' | cancellato ovunque; il limite vero e' quello sopra |
|
||||
| il bound di mercato guarda solo BTC/ETH, ma il 31% dell'equity e' USDE: un depeg pieno sarebbe un «prelievo» | si' | **non riparato**: l'indice USDE e' registrato una volta al giorno. Debito **§5.15** |
|
||||
| l'ora del report scritta in §2 (14:02Z) era l'ora di esecuzione, non quella della lettura (13:47:01Z); i tempi «scritto» dei diari erano posteriori ai commit | si' | corretti coi tempi veri |
|
||||
|
||||
Il numero pubblicato: TWR +10,61% alla lettura delle 13:47Z; +10,56% alle 14:47Z.
|
||||
@@ -0,0 +1,67 @@
|
||||
# 2026-09-02 — Debito 7: il docstring diceva 230 minuti, il cron gira ogni ora
|
||||
|
||||
*Scritto il 2026-09-02 fra le 14:36Z e le 14:38Z (commit `861cc7f`, 14:38:53Z; la prima stesura diceva «alle 14:45Z» senza averlo letto — corretto in revisione). Cadenza misurata dal log (`r0823_sl_anchor.py`: 1.443 giri in
|
||||
1.442 ore) e dalla crontab installata, letta dal test.*
|
||||
|
||||
## Il difetto
|
||||
|
||||
| fonte | cosa diceva | dal |
|
||||
|---|---|---|
|
||||
| `scripts/live/book_execute.py`, docstring | «va lanciato ogni ~230 minuti» | 20/06 |
|
||||
| `scripts/cron_book.sh`, intestazione | cadenza ORARIA, `47 * * * *` | 25/08 (prima `:07`, sempre orario) |
|
||||
| crontab della VPS | `47 * * * *` | 25/08 |
|
||||
|
||||
Era il **docstring** a sbagliare. E non era una svista innocua: sulla riga 4h (la più vicina ai 230
|
||||
minuti) BTC **raddoppia** gli scatti del disaster-SL rotolante (2 contro 1; a 24h ETH arriva a 5, con
|
||||
2 in 30 giorni). Il rischio era un lettore diligente che "allineasse" il cron al docstring.
|
||||
|
||||
## La riparazione
|
||||
|
||||
1. **Docstring riscritto**: `CADENZA: ORARIA`, la riga di crontab citata, la ragione (giro
|
||||
idempotente → girare più fitto della griglia non costa ordini; latenza ≤1h sugli ingressi/uscite
|
||||
software di SKH01; ri-ancoraggio orario del rotolante) e il divieto esplicito, con la data.
|
||||
2. **`tests/test_book_cadenza.py`, 10 test.** P1: non ridichiara «60 minuti», **deriva** le tre
|
||||
fonti e le confronta fra loro:
|
||||
- il docstring non contiene più «ogni ~230» e dichiara ORARIA;
|
||||
- la riga di crontab citata nel docstring è quella dichiarata in `cron_book.sh`;
|
||||
- la riga dichiarata è oraria (parser a 5 campi, solo casi regolari, rifiuta il resto),
|
||||
sotto i 230 della griglia e lontana dai 240 della riga peggiore, e fuori dal minuto `:00`;
|
||||
- **la crontab installata** (`crontab -l`) coincide con la dichiarata. Dove la crontab non è
|
||||
leggibile il test è **saltato**, non verde (P5). Sulla VPS oggi gira e passa.
|
||||
|
||||
## Numeri
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| test nuovi | 10, tutti verdi; suite dei moduli che toccano `book_execute`: 231 verdi |
|
||||
| cadenza dichiarata / installata | 60 min / 60 min |
|
||||
| griglia SKH01 | 230 min (la riga da non usare) |
|
||||
| riga peggiore misurata | 240 min (BTC 2 scatti contro 1) |
|
||||
|
||||
## Cosa NON ho fatto
|
||||
|
||||
- Non ho toccato il cron né `cron_book.sh`: la configurazione che gira era quella giusta.
|
||||
- Non ho aggiunto un sorvegliante a runtime sulla cadenza (contare i giri per ora nel log): il
|
||||
giornale già stampa «giri mancanti» ogni giorno, e il test copre la deriva di configurazione.
|
||||
|
||||
## Revisione del codice, prima tornata (15:15Z, commit `835e0c8`) — due cose trovate, tutte riparate
|
||||
|
||||
| trovato | vero? | riparazione |
|
||||
|---|---|---|
|
||||
| il docstring nuovo diceva «il disaster-SL rotolante si ri-ancora ogni ora»: falso. `ensure_disaster_sl` lascia il bracket com'è finché lo stop voluto è entro il 5% da quello piazzato e la taglia entro il 10%; il giro orario **controlla**, ri-ancora oltre la tolleranza (mark +5,263% / −4,762%). I «BTC 0 / ETH 1» di `r0823` sono misurati **con** questa isteresi | sì, verificato in `execution.py:233-235`. Propagato anche a CLAUDE.md §5.7 e memoria 40 | riscritto in tutti e tre i posti: «controllato ogni ora, ri-ancorato solo oltre la tolleranza; lo stop siede fra −26,3% e −33,3% dal mark corrente». Frase sbagliata su un meccanismo di sicurezza vivo: era la segnalazione più importante |
|
||||
| la guardia del test cercava due letterali («ogni ~230»): «ogni quattro ore» sarebbe passato | sì | la guardia estrae **ogni** prescrizione «ogni/every N unità» e pretende 60 minuti; controllo positivo (M15) su cinque frasi. Limite dichiarato (P13): una prosa che prescrive senza «ogni» passa |
|
||||
|
||||
## Seconda tornata (15:30Z)
|
||||
|
||||
| trovato | vero? | riparazione |
|
||||
|---|---|---|
|
||||
| la frase nuova di CLAUDE.md §5.7 era **invertita** («chi lo avesse corretto nel verso del cron»): correggere il docstring verso il cron e' proprio la riparazione; il pericolo e' correggere il CRON verso il docstring | si' | riscritta nel verso giusto |
|
||||
| la banda dello stop era sbagliata: con isteresi 5% sullo stop (0,70·mark) lo stop siede a 0,665–0,735 × mark, cioe' **−33,5% / −26,5%**, non −26,3/−33,3 | si' | corretta in docstring, CLAUDE.md, memoria |
|
||||
| «BTC raddoppia gli scatti» leggeva male `r0823`: il «2 contro 1» e' rotolante contro pavimento **dentro la riga 4h**; a 1h il rotolante ne fa 0, e la riga peggiore misurata e' **24h** (ETH 5) | si' (la revisione ha eseguito `r0823.simula`) | riscritto ovunque coi conteggi; nel test `RIGA_4H_MIN`, non «peggiore»; `LTF_MIN` importato da `skyhook` (P1) |
|
||||
| «girare piu' fitto non costa ordini» era falso: 22 dei 48 ordini live sono ri-taglie |Δ|≤$10 a segnale invariato; il deadband e' in valuta assoluta (C2) | si' (contati) | riscritto: «costa solo micro-ordini di ri-taglia», con la regola C2 |
|
||||
| «latenza ≤1h» senza condizione: vale solo con la feed 5m fresca; `fresh_5m` ripiega in silenzio sul giornaliero e `skh_feed_max_age_min` allerta e basta | si' | condizione scritta |
|
||||
| `_cron_installato` leggeva la **prima** riga: una seconda riga attiva sotto (`17 * * * *`, dentro lo slot di release) era invisibile | si' (simulato) | si contano **tutte** le righe attive, e devono essere una |
|
||||
| lo skip collassava tre stati: crontab illeggibile, leggibile senza il progetto, leggibile col progetto ma senza `cron_book.sh` attivo (= libro non schedulato) | si' | tre stati: SALTATO, SALTATO, **ROSSO** |
|
||||
| solo il vincolo 1 del `:47` era testato (minuto ≠ 00); il vincolo 2 (fuori dallo slot di release del martedi') no | si' | test con `venue_probe.in_release_window`, il :07 come controllo positivo |
|
||||
| `_cron_dichiarato` accettava solo `N * * * *` e falliva come «assente» su `*/30`; `cadenza_minuti` accettava `*/45`, `0 */5`, `*/0` | si' | regex a 5 campi qualunque, regolarita' e passo > 0 imposti, parametrizzati |
|
||||
| `r0823_sl_anchor.py` si rifiutava di girare nella finestra del **vecchio** `:07` e stampava a runtime che il docstring «prescrive ~230 min»; `cron_chain.sh` giustificava il :25 «fuori dal :07» | si' | guardia sul :47, prosa al passato, commento aggiornato |
|
||||
@@ -0,0 +1,103 @@
|
||||
# 2026-09-02 — Un flag sconosciuto non è l'azione di default
|
||||
|
||||
*Scritto fra le 15:45Z e le 16:05Z. Il difetto è stato misurato, non ipotizzato: la revisione del
|
||||
codice di questa sessione ha eseguito `trades_db.py --help` come sonda di tempi e lo ha visto
|
||||
sincronizzare il DB.*
|
||||
|
||||
## 1. Il difetto
|
||||
|
||||
Nessuno dei 18 script di `scripts/live/` usava argparse. Leggevano gli argomenti così:
|
||||
|
||||
```python
|
||||
if "--reconcile" in args: ...
|
||||
elif "--report" in args: ...
|
||||
else: sync() # <- qui finiva QUALUNQUE cosa non riconosciuta
|
||||
```
|
||||
|
||||
Un flag sbagliato non era un errore: era il ramo finale. Costo osservato e costi possibili:
|
||||
|
||||
| script | `--help` faceva | gravità |
|
||||
|---|---|---|
|
||||
| `trades_db.py` | `sync()`: riscriveva `meta.ultimo_sync` | osservato oggi, danno nullo |
|
||||
| `journal.py` | scriveva la pagina di oggi e una riga nel DB | non osservato, ma scrive |
|
||||
| `analista.py` | spendeva una chiamata al modello e mandava un Telegram | non osservato, ma spende |
|
||||
|
||||
## 2. La riparazione, e perché non è argparse
|
||||
|
||||
`src/live/cli.valida(nome, uso, flag=..., con_valore=...)`, chiamata come **prima istruzione** di
|
||||
ogni blocco `__main__` — prima di `connect()`, prima del sync, prima della rete.
|
||||
|
||||
- `--help` / `-h` → stampa l'uso, esce **0**;
|
||||
- flag non dichiarato, o valore mancante, o posizionale → stderr con **cosa** e **cosa si poteva**
|
||||
(P4), esce **2** (errore d'uso, distinto dall'1 con cui gli script segnalano un esito negativo);
|
||||
- tutto il resto invariato.
|
||||
|
||||
**Argparse no.** Cambierebbe messaggi d'errore, codici d'uscita e il comportamento di `--help` su
|
||||
18 script che il cron già invoca: romperebbe ciò che gira per riparare ciò che non gira mai. Qui
|
||||
serviva l'opposto — lasciare intatti i flag esistenti e rifiutare solo l'ignoto.
|
||||
|
||||
## 3. Il test guarda entrambe le metà del contratto
|
||||
|
||||
`tests/test_cli_flag.py`, 30 test:
|
||||
|
||||
- **l'elenco degli script si deriva dalla cartella** (P1): uno script nuovo che legge `sys.argv` a
|
||||
mano e non chiama `valida` fa fallire il test, non passa inosservato perché nessuno ha aggiornato
|
||||
una lista;
|
||||
- `valida` dev'essere la **prima istruzione** di `__main__` (uscire dopo l'effetto non è uscire);
|
||||
- l'uso deve **nominare** i flag che lo script accetta;
|
||||
- **i flag che il cron usa davvero devono restare accettati** (P15/P16, letti dai `cron_*.sh`): una
|
||||
guardia che ferma il libro di bordo la notte stessa sarebbe peggio del difetto;
|
||||
- end-to-end sui tre script che scrivono: `--help` esce 0 e **non tocca `trades.db`**; un flag
|
||||
ignoto esce 2 e non lo tocca (M15: il caso che ha causato il difetto, non un caso vicino).
|
||||
|
||||
Verificato a mano anche il contrario: `monitor_health.py --quiet`, `trades_db.py --sync --quiet`
|
||||
e `book_execute.py` in dry-run escono 0 come prima.
|
||||
|
||||
## 4. Il resto della sessione
|
||||
|
||||
**Indice USDE orario** (debito §5.15, primo passo). Il classificatore dei movimenti di capitale
|
||||
confronta due letture di equity a un'ora di distanza e il suo bound guarda solo BTC/ETH, mentre il
|
||||
31% dell'equity è USDE: un depeg sarebbe scorporato come un prelievo. `usde_watch` registra
|
||||
l'indice una volta al giorno — troppo rado. Ora `balance_watch` (orario, sola lettura) lo registra
|
||||
a ogni campione, `None` con la ragione se non leggibile, mai 1,0. Il cablaggio nel bound viene
|
||||
quando la serie ha storia: prima serve il dato, poi la regola.
|
||||
|
||||
**Pulizia lasciata dalla revisione.** `tests/helpers.carica_script` sostituisce la dodicesima copia
|
||||
del caricatore `importlib` (15 in giro, con nomi di modulo diversi per lo stesso file e differenze
|
||||
silenziose su `sys.modules`); il fill di prova si scrive con `upsert_fills` invece che con un
|
||||
`INSERT` a mano che lasciava `verified` NULL, una forma che la produzione non produce; le
|
||||
asserzioni sul testo del report non sono più ancorate al padding; `movimenti_capitale` accetta le
|
||||
righe già lette, così il report non fa due SELECT sulla stessa tabella a un'ora del cron — due
|
||||
letture ai due lati di una scrittura descriverebbero due istanti diversi.
|
||||
|
||||
## 5. Il README era fermo a quindici giorni prima del reset
|
||||
|
||||
Chiesto un aggiornamento della documentazione, ho aperto il README e ho trovato un documento del
|
||||
**2026-06-04**. Il reset v2.0.0 è del **19 giugno**. Per 75 giorni la prima pagina del repository ha
|
||||
descritto la libreria pre-reset come se fosse il presente:
|
||||
|
||||
| cosa diceva | stato vero |
|
||||
|---|---|
|
||||
| famiglie FADE / HONEST / PAIRS / TSMOM / SHAPE, strategie MR01…SH01 | archiviate in `Old/`, artefatto di feed contaminato |
|
||||
| PORT06: Sharpe **7,84 / 10,06**, DD 2,60%, CAGR ~79% | numeri che `CLAUDE.md` §2 elenca sotto «non citare» |
|
||||
| paper trader su `strategies.yml`, portafogli in `portfolios.yml` | **file inesistenti** |
|
||||
| `scripts/waste/`, `scripts/portfolios/`, `src/live/multi_runner.py` | **inesistenti** |
|
||||
| esecuzione shadow su Deribit **testnet** | il testnet è *la causa del reset*; oggi si esegue su mainnet con soldi veri |
|
||||
|
||||
Riscritto contro lo stato vero: cosa gira adesso, i numeri nella lente di §2 (TWR +10,6%, non la
|
||||
crescita del conto), il metodo e i sei requisiti, la struttura verificata file per file, i gate con
|
||||
le loro date, l'obiettivo con la sua onestà. Ogni percorso citato è stato controllato: esiste.
|
||||
Da 423 righe a 152.
|
||||
|
||||
**La lezione che ho registrato non è «aggiornare il README».** È che un reset invalida anche i
|
||||
documenti che nessuno rilegge, e l'inventario di cosa cita numeri morti va fatto il giorno del
|
||||
reset — non 75 giorni dopo, per caso, mentre si fa altro.
|
||||
|
||||
## 6. Cosa NON ho fatto
|
||||
|
||||
- I loader `importlib` dei test sulla **ricerca** (moduli in `scripts/research/`) restano come
|
||||
sono: sono a livello di modulo, con radici diverse, e toccarli non ripara niente.
|
||||
- Il bound USDE nel classificatore: manca la storia, non il codice.
|
||||
|
||||
- Non ho cercato altri documenti fermi al pre-reset fuori da `README.md` e `docs/`: l'inventario
|
||||
completo (ogni file che cita un numero morto) resta da fare, ed è la vera coda di questa scoperta.
|
||||
@@ -0,0 +1,91 @@
|
||||
# 2026-09-06 — USDE: dal 14,6% al 68,7%. Il tetto del venue del 30-31/08 non c'era piu'; il versamento di prova del 25/08 dichiarato
|
||||
|
||||
**Ore:** 20:56-21:16Z. **Autorizzazioni dell'operatore, in sessione:** *«usa piu' USDE se ne hai bisogno»*,
|
||||
poi *«cerca % massima raggiungibile in USDE»*, sulla decisione gia' presa il 30/08 (quota bersaglio 70%,
|
||||
`usde.quota_target`). E, a meta' sessione: *«i 25 euro erano un versamento di prova»*.
|
||||
|
||||
## 1. Il versamento del 04/09 aveva dimezzato la quota
|
||||
|
||||
Il versamento di **$2.410,14 del 04/09 14:47Z** (riconosciuto dal classificatore: mercato max 0,56% a leva
|
||||
piena contro un salto del +116,8%) ha portato l'equity a **~$4.495** e la quota USDE da 31,3% a **14,6%**.
|
||||
Nessun automatismo la riportava su.
|
||||
|
||||
## 2. Primo passo: al tetto di config (31,8%) — e la lettura SBAGLIATA che ne ho dato
|
||||
|
||||
`usde_convert.py --quota 0.318 --esegui`: 774 USDE in 8 ordini @ 1,0005 medio, zero rifiuti, quota 31,79%.
|
||||
Ho scritto in CLAUDE.md «il tetto ha tenuto la stessa frazione a equity 2,2× maggiore: terza conferma».
|
||||
**Falso**: nessun rifiuto fino al 31,8% dimostra che il tetto e' **≥** 31,8%, non che e' **=** 31,8%. Il
|
||||
dato non conteneva la conferma; l'ho letta perche' me l'aspettavo. Corretto venti minuti dopo, quando
|
||||
l'operatore ha chiesto la % massima e la sonda ha mostrato che la conferma non c'era.
|
||||
|
||||
## 3. La sonda: nessun tetto fino al 68,7%
|
||||
|
||||
`scripts/research/r0906_usde_tetto_sonda.py` — a saldo crescente, passi piccoli, stesso metodo dei diari
|
||||
30-31/08 (un probe unico di taglia sbagliata produce un falso negativo): sale finche' il venue non rifiuta,
|
||||
ripete il rifiuto dopo aver riletto il book (il messaggio `not_enough_funds_in_currency` e' lo stesso di un
|
||||
limite che non incrocia), scende di passo a ogni doppio rifiuto, e conferma il muro a saldo neutro
|
||||
(SELL 1 → BUY 1 ok → BUY 1 rifiutato). Guardie: prezzo dal book (ask+5 tick, tetto 1,0010), quota mai oltre
|
||||
il 70% e **USDC mai sotto il cuscino di regolamento** derivato da config (`usde_convert.cuscino_richiesto_usd`).
|
||||
|
||||
| corsa | passi | da → a (USDE) | quota | rifiuti |
|
||||
|---|---|---|---|---|
|
||||
| 21:02-21:09 | 30 × 2 | 1.428,7 → 1.488,7 | 31,8% → 33,1% | **0** (fermata dal mio limite +60) |
|
||||
| 21:11-21:15 | 16 × 100 | 1.488,7 → **3.088,7** | 33,1% → **68,7%** | **0** (fermata dal cuscino: +100 avrebbe superato il 70%) |
|
||||
|
||||
**Totale del giorno: 2.434 USDE in 39 ordini @ 1,0005, ~$1,2 di spread.** Conto finale: USDC **$1.407,40** +
|
||||
USDE **3.088,72** = $4.496; cuscino richiesto $1.349 → slack **+$58**. Registro: `data/live/usde_convert.jsonl`
|
||||
(due record `sonda_tetto`, con ogni ordine).
|
||||
|
||||
## 4. Cosa dice il risultato — e cosa non dice
|
||||
|
||||
1. **Il tetto del 30-31/08 era vero allora ed e' sparito ora.** Tre bracket riproducibili a 1 USDE (31,21-31,26% ·
|
||||
31,29-31,34% · 31,84-31,88%) contro 2.434 USDE accettati oggi senza un rifiuto. **Non e' scalato, e'
|
||||
sparito**: la frase del 31/08 «il 70% non e' raggiungibile ne' ora ne' mai» e' falsificata.
|
||||
2. **Non spiegato** (D5). Ipotesi non verificabili da qui (il gateway non espone il transaction log ne' i
|
||||
parametri del cap): un'allocazione per-conto del Cap ETHENA che varia col saldo aggregato dell'exchange;
|
||||
un vincolo sui fondi depositati di recente (ma l'USDC del 04/09 e' stato convertibile a 2 giorni); un
|
||||
cambio di policy. **Il tetto puo' tornare**: quando tornera', bloccherebbe gli ACQUISTI, mai le vendite
|
||||
(tutte le SELL sono sempre passate). `venue_cap_frac` → **null** con la misura in `venue_cap_misurato`;
|
||||
`usde_convert` si affida al chunk+backoff sui rifiuti, che e' cio' che ha sempre fatto.
|
||||
3. **Il limite che morde e' NOSTRO: il cuscino di regolamento.** P&L e funding dei perp si regolano in USDC;
|
||||
a 68,7% restano $58 sopra il cuscino. ⚠️ **Nessuno sorveglia il cuscino**: `usde_watch` allerta sulla
|
||||
quota (`quota_max_frac`, riportata a **0,85**, il valore che l'operatore aveva scelto il 30/08 «in previsione
|
||||
della quota al 70%»), non sull'USDC. Una perdita del libro fa salire la quota da sola: a −$58 il cuscino e'
|
||||
scoperto, e Deribit finanzia un saldo USDC negativo allo 0,05%/giorno (18%/anno). Debito nuovo, §5.17.
|
||||
4. **Rischio emittente**: a 24,2% l'evento Ethena valeva 3,1× il maxDD del libro (30/08); a 68,7% vale **~8,8×**.
|
||||
E' la decisione del 30/08 presa con l'informazione completa (r0830_usde_quota), oggi eseguita. Resa attesa
|
||||
~4,1% × $3.089 ≈ **$127/anno** (era $26): ancora meno di un mese di versamento.
|
||||
|
||||
## 5. Il versamento di prova del 25/08: dichiarato, scorporato, e il TWR scende di 4 punti
|
||||
|
||||
Trovato stamattina per differenza (equity 07:07→08:07 **+$21,11** con le posizioni a **−$3,3/−4,3**) e
|
||||
confermato dall'operatore: **€25 di prova** prima del bonifico da $1.399,39 delle 11:47. Il rilevatore non
|
||||
poteva vederlo (+3,3% contro soglia 10%) e abbassare la soglia farebbe di ogni ora di mercato un candidato:
|
||||
**l'unica fonte che sa l'importo e' l'operatore**. Costruito il canale:
|
||||
|
||||
- `data/live/movimenti_dichiarati.jsonl` (append-only, nel perimetro di backup — P11), letto da
|
||||
`journal.movimenti_dichiarati()`; `movimenti_capitale` lo fonde coi rilevati: fonte `dichiarato`, importo
|
||||
dell'operatore (non il salto), `dopo = prima + importo` cosi' il mercato dell'ora **resta** nel rendimento;
|
||||
se coincide con un salto rilevato l'importo dichiarato vince (`dichiarato+rilevato`); fuori dalle letture →
|
||||
`avvisi`, non applicato; riga rotta → `avvisi`, le altre restano (P3).
|
||||
- Importo registrato **$24,90 ± 0,50**: stimato per differenza (salto +21,11, mercato −3,3/−4,3 dal log del
|
||||
book, fee 0,03) — il gateway non espone `get_deposits`, quindi il numero USDC esatto non e' leggibile; la
|
||||
banda sta nel record e nel report (P12).
|
||||
- `tests/test_journal.py`: +6 (scorporo sotto soglia · dichiarato+rilevato · fuori letture · riga rotta ·
|
||||
senza file niente cambia · la nota di giornale lo dice); `conftest` isola il file nei test.
|
||||
|
||||
| | prima (20:47Z) | dopo (21:14Z) |
|
||||
|---|---|---|
|
||||
| movimenti certi | $3.809,53 | **$3.834,43** |
|
||||
| trading da arming | +$87,35 | **+$62,45** |
|
||||
| TWR | +11,98% | **+7,83%** = +8,14% × −0,62% × −0,12% × +0,46% |
|
||||
|
||||
**Un terzo del «trading» era un bonifico da 25 euro.** Regola nuova (CLAUDE.md §2): ogni versamento, anche
|
||||
di prova, si dichiara nel file il giorno stesso.
|
||||
|
||||
## 6. File toccati
|
||||
|
||||
`config/live.json` (usde: venue_cap_frac null, quota_max_frac 0.85, nota) · `src/live/journal.py` ·
|
||||
`scripts/live/trades_db.py` · `tests/conftest.py` · `tests/test_journal.py` (+6) ·
|
||||
`scripts/research/r0906_usde_tetto_sonda.py` (nuovo) · `data/live/movimenti_dichiarati.jsonl` (nuovo) ·
|
||||
`CLAUDE.md` §1 §2 §4 §5.
|
||||
@@ -0,0 +1,219 @@
|
||||
# 2026-09-09 — Guadagnare nei crolli: revisione dei sistemi, il crollo catturato, piu' monete su Deribit
|
||||
|
||||
**Richiesta dell'operatore:** *"fai analisi e revisione dei sistemi adottati e trova soluzione per
|
||||
guadagnare anche nei crolli (usa le opzioni se serve), anche + monete ma sempre in deribit"*.
|
||||
Analisi e revisione col modello `fable` (revisore fresco, agente separato), test con Opus.
|
||||
|
||||
**Esito in una riga:** nel **giorno** del crollo il libro live **perde** (−0,48%/g, positivo nel 18%
|
||||
dei giorni ≤ −5%); per **finestra** e' immune-o-positivo e **dal 2022 chiude in utile 8/8 peggiori
|
||||
finestre**, tutte per la gamba SHORT di SKH01 marcata all'uscita; la sola leva che aumenterebbe quel
|
||||
guadagno e' il peso di SKH01, che il gate del progetto boccia su una condizione in-sample; le
|
||||
opzioni, misurate per la prima volta su un **crollo catturato a quote vere**, danno un rapporto di
|
||||
credito identico al rally (f_net 0,74 vs 0,714) — **non** il `f` di stress che la regola del 19/06
|
||||
aspettava (crollo a vol bassa, gate di VRP01 chiuso, famiglia inverse); XRP, l'unica "altra
|
||||
moneta" liquida e non misurata di Deribit, **diluisce** esattamente come SOL. **Libro, pesi,
|
||||
config, cron INVARIATI. Nessun ordine.**
|
||||
|
||||
## 0. Prima di misurare: cosa era gia' morto (memoria letta, non riaperta)
|
||||
|
||||
| meccanismo "guadagnare nei crolli" | esito | perche' e' morto (chi riapre deve battere questo) |
|
||||
|---|---|---|
|
||||
| gamba short del trend a 1d (§42) | SCARTATO | −1,91%/anno di drift in 0/24 ancore; il 2022 lo tiene a galla a meta' |
|
||||
| put deep-OTM statica (§46) | REFUTATO 0/162 | beta del libro +0,076: non si assicura un libro gia' piatto |
|
||||
| collar ≤15g (§71) · pavimento come licenza (§72) | REFUTATI | il DD lo riduce il TETTO (troncatura, C9); la put sola peggiora il DD 16/16 |
|
||||
| gamma scalping long-vol | SCARTATO | paga il VRP ogni anno, ogni frequenza |
|
||||
| DVOL direzionale · gate macro · skew de-risk | HEDGE / RIDONDANTI | TP01 e' gia' flat nei crolli (Δ 0,00) |
|
||||
| SOL terza gamba (22/08) | ESCLUSO dall'operatore | hold-out −0,166 in 0/24; un anno solo (2023) |
|
||||
| XS01 su Deribit (§50) | MORTO per ampiezza | 11 gambe con ≥1 anno: 1,265 → 0,116 |
|
||||
|
||||
Condizione di riapertura scritta tre volte (§3 short-vol, §71, §72): **un `f` di stress reale su
|
||||
un crash catturato**. E' la cosa che oggi si poteva misurare e non era stata misurata.
|
||||
|
||||
## 1. Il libro nei crolli — `r0909_libro_nei_crolli.py`
|
||||
|
||||
Lente: sleeve di ricerca, ancora canonica, fee 0,10% RT, funding fuori; pesi importati da
|
||||
`src/live/book` (P1). ⚠️ **SKH01 e' marcato all'USCITA del trade** (`harness.backtest_signals`):
|
||||
la serie giornaliera e' zero nell'87,7% dei giorni, e uno short aperto nella finestra e chiuso
|
||||
fuori e' accreditato fuori — per questo la tabella per GIORNO mostra SKH01 ≈ 0 e quella per
|
||||
FINESTRA il guadagno. Indice 50/50 BTC/ETH; giorno di crollo ≤ −5%; episodio = 12 peggiori
|
||||
finestre di 20 giorni non sovrapposte + 9 episodi nominati prima. Classi ESCLUSIVE nell'ordine
|
||||
dichiarato: immune (|libro| < |idx|/10, col segno) → POSITIVO → PERDE.
|
||||
|
||||
| misura | valore |
|
||||
|---|---|
|
||||
| beta del libro all'indice | **+0,0769 sulla finestra di §46** (2021-03-24+; memoria +0,076: riprodotto al millesimo) · +0,086 su tutta la storia · +0,002 nei giorni ≤ −3% |
|
||||
| 160 giorni ≤ −5% (indice −7,76%/g) | libro **−0,48%/g**: TP01 −0,63, SKH01 +0,15; **libro > 0 nel 18%** dei giorni; somme per anno: 2024 −11,9%, 2025 −7,3%, 2022 +8,2%, 2026 +4,7% |
|
||||
| 24 giorni ≤ −10% (indice −14,4%/g) | libro −0,51%/g: TP01 −0,93, SKH01 **+0,42** |
|
||||
| 12 peggiori finestre 20g | **4 POSITIVO / 6 immune (4 col segno +) / 2 PERDE**; libro > 0 in 8/12; **dal 2022 in 8/8** (mediana +3,3% contro indice −29%); le 2 perdite sono 2019 e maggio 2021 (TP01 ancora long: il trend non si era girato) |
|
||||
| 9 episodi nominati | libro > 0 in LUNA (+4,3%), giugno 2022 (+3,1%), FTX (+0,7%), agosto 2024 (+0,4%), 5/02/2026 (+4,4%), 1-5/06/2026 (+3,3%); COVID −0,04%, feb 2025 −1,5%, maggio 2021 −3,4% |
|
||||
| da dove viene il guadagno | la gamba **SHORT di SKH01 = 108%** dei guadagni negli episodi con libro > 0 (scomposizione ESATTA: long + short == intera su 2735/2735 giorni, verificato con assert; TP01 contribuisce in negativo); per anno al peso 0,25: 2022 **+10,7%**, 2026 **+7,7%** |
|
||||
| peggior giorno del libro (lente canonica) | 2025-10-10 −3,38%, tutto TP01 con esposizione **0,505**, indice −9,7%: **e' un giorno di crollo con TP01 mezzo long** (lo squeeze di §33 sta sulla lente hourly e qui non si riproduce: dichiarato nel docstring) |
|
||||
| riga informativa 37,5/62,5 (§26) | batte il 75/25 in 12/12 episodi, **ma** Sharpe 1,81 vs 1,82 e maxDD 12,1% vs 9,4%: paga i crolli con gli squeeze |
|
||||
|
||||
🚨 **Errore catturato in revisione (fable):** la prima stesura provava "GUADAGNA" prima di
|
||||
"immune" e stampava **8/12 GUADAGNA**; con le classi nell'ordine dichiarato sono **4 POSITIVO +
|
||||
6 immune**. *L'ordine delle regole era il verdetto.* Ora il verdetto e' tre affermazioni separate
|
||||
(giorno / finestra / provenienza) e il conteggio nudo libro > 0 e' stampato accanto.
|
||||
|
||||
📌 **Il meccanismo "guadagnare nei crolli" esiste ed e' in produzione: e' SKH01 short** — ma
|
||||
guadagna **dopo** il giorno del crollo (marcato all'uscita), non dentro. Nel senso stretto della
|
||||
domanda dell'operatore il libro **perde nel giorno** e recupera nella finestra. Le due perdite
|
||||
storiche per finestra sono di TP01 nei crolli che arrivano *dentro* un trend rialzista (2019,
|
||||
2021): e' il costo del long-flat (§42 ha misurato che la short del trend lo aggrava).
|
||||
|
||||
**La leva che aumenterebbe il guadagno e' il peso di SKH01, e il gate la boccia:**
|
||||
`r0726_reeval_live_weight.py` rilanciato: argmax 0,35, plateau [0,25-0,50] (il live e' dentro),
|
||||
`weights_tilt_null` 0,25→0,35: `delta_insample −0,0026`, `pctl_hold 31,2`, **`gate_pass False`**.
|
||||
⚠️ Precisazione della revisione: lo script legge la cache `data/_cache/skh_sigtab/*` **ferma al
|
||||
2026-07-25** — sono le stesse cifre del diario 26/07, non un ri-esame coi segnali di oggi. La
|
||||
condizione che fallisce e' pero' **in-sample (pre-2025)**, che 46 giorni di dati nuovi non possono
|
||||
cambiare: la decisione 75/25 resta vincolante e la sua condizione di riapertura resta non
|
||||
soddisfatta, ma la data da citare e' il 26/07.
|
||||
|
||||
## 2. Il crollo catturato — `r0909_crash_catturato.py`
|
||||
|
||||
**Fatto nuovo:** `bite_archive.parquet` copre con bid E ask il crollo del **1-5 giugno 2026**
|
||||
(BTC −25% / ETH −30% dal 31/05 al minimo; ETH −11,1% il 05/06) su una scadenza (19/06, 14-21
|
||||
DTE). Il campione del 22/08 (§11) partiva **dopo**, dal fondo. Limiti dichiarati prima: n = 1
|
||||
crollo, 1 scadenza, tenor 14-21 g (VRP01 e' a 7), spot dal feed 1h certificato, regolamento allo
|
||||
spot dell'ora di scadenza, **famiglia INVERSE** (premi in ETH: VRP01 opererebbe sulla USDC-lineare,
|
||||
che il conto margina e che NON e' nell'archivio). Ancora d'ingresso = ogni snapshot 30/05-04/06;
|
||||
mediana e banda.
|
||||
|
||||
⚠️ **Non e' un crollo a vol alta:** DVOL pre 37 (BTC) / 49 (ETH) all'IV-rank 0,05 / 0,10; il
|
||||
**picco** del crollo (49 / 69) sta al **29° / 46° percentile storico** — sotto la mediana. Il gate
|
||||
IV-rank ≥ 0,30 di VRP01 e' **chiuso in tutte le ore** e il modello del sleeve segna 0,0 su tutte le
|
||||
settimane di giugno: VRP01 non sarebbe stato a mercato.
|
||||
|
||||
| ETH, ingressi PRE-crollo (286 snapshot = **4 strutture** di strike, δ −0,27/−0,09, larghezza 200) | reale | modello (BS su DVOL) | f |
|
||||
|---|---|---|---|
|
||||
| credito del put credit spread | | | **0,74** [0,65-0,82] — 22/08 in rally: 0,714 |
|
||||
| P&L a scadenza per unita' | **−$174** | −$168 | 1,04 — **tautologico nell'83%** dei trade: entrambe le gambe ITM ⇒ payoff = larghezza per reale e modello, resta solo f_net |
|
||||
| perdita sul rischio (fee incluse) | 101% [65-102] | | = perdita **MASSIMA** dello spread (+ fee); 65% per la 1900/1600 |
|
||||
| peggior MTM, chiusura **tagliata alla larghezza** | **−$172** | −$161 | **1,08** [1,07-1,31]; a quell'ora IV corta 68 = DVOL 68 |
|
||||
| put δ−0,10 comprata all'ask: costo | | | **1,92×** il modello (§46 a δ−0,10 su BTC_USDC: 1,27 — punto NUOVO e piu' alto, ETH inverse) |
|
||||
| put δ−0,10 a scadenza (ITM di $4: K 1700, ST 1694) | −$6 | ≈ $0 | 0,96 sul solo sottoinsieme con payoff modellato ≥ $5 |
|
||||
| put δ−0,10 al picco del percorso (look-ahead, limite sup.) | +$163 | +$173 | 0,94 |
|
||||
| debit spread −0,28/−0,10 a scadenza | +$169 | +$163 | 1,04 (stessa tautologia, specchio) |
|
||||
| ingressi DURANTE il crollo (02-04/06, 283 snapshot) | mediana +$5, **perde nel 46%**, p10 −$133 / p90 +$26 | | bimodale: la mediana nasconde le perdite |
|
||||
|
||||
BTC: **non misurabile pre-crollo** — la griglia di strike dell'archivio il 30/05-01/06 non
|
||||
contiene la δ−0,10 (78 snapshot fuori geometria, 0 in geometria prima del 02/06). Durante:
|
||||
247 snapshot, f_net 0,79, perde nel 37% (p10 −$1.718 / p90 +$818 su larghezza 5000).
|
||||
|
||||
🚨 **Tre correzioni della revisione, applicate:**
|
||||
1. *"Il percorso costa 2,6× il modello (f_mtm 2,58), la IV della gamba corta e' esplosa a 108 per lo
|
||||
skew"* — **artefatto di quote.** Il costo di chiusura `(ask corta − bid lunga)` superava la
|
||||
**larghezza** dello spread (una quota 0,208/0,376 ETH su un intrinseco di $296: 158% di spread
|
||||
relativo, a T→0) e `min()` pescava li'; uno spread a rischio definito non costa mai piu' della
|
||||
larghezza per chiudere. Tagliato: **f_mtm 1,08**. E la IV 108 e' del **18/06 alle 17:00** (deep
|
||||
ITM, T→0), non del crollo: all'ora del peggior MTM la IV e' 68 = DVOL.
|
||||
2. *"f a scadenza 1,04: il modello prezza bene la perdita"* — **tautologia**: con entrambe le gambe
|
||||
ITM il payoff e' la larghezza per entrambi; f_exp e' una funzione di f_net. E la "banda su 286
|
||||
snapshot" sono 4 strutture (N_eff = 1 crollo × 4).
|
||||
3. `S.asof(ts)` sul feed 1h **guardava un'ora avanti**: la barra e' etichettata all'apertura (la
|
||||
20:00 chiude col 5m delle 20:55). Corretto con `spot_causale` (indice +1h). ⚠️ **`cblib.spot_series`
|
||||
ha lo stesso difetto** → il f 0,714 di §11 e ogni numero di `cblib` con `asof` sullo spot; qui
|
||||
innocuo (ambo le gambe ITM per ogni ST < 1700) e nel MTM giocava contro l'autore. **Debito nuovo,
|
||||
registrato in CLAUDE.md §5.** Non toccato: cambierebbe numeri pubblicati.
|
||||
|
||||
📌 **Cosa dice davvero il crollo catturato (n = 1):** il **rapporto di credito reale/modello non
|
||||
cambia col regime** (0,74 nel crollo contro 0,714 nel rally); la put comprata paga nel crollo
|
||||
cio' che il modello dice (0,94-1,04) ma **costa 1,9× il modello all'ingresso** — il bleed e' dove
|
||||
il modello sbaglia, il payoff no (e' il motivo di §46/§72 visto dal lato del sinistro); il gate di
|
||||
VRP01 lo ha tenuto fuori. **La condizione di riapertura di §3 e' soddisfatta nella forma** (un
|
||||
archivio bite con bid/ask su un crollo) **e non nella sostanza**: crollo a vol bassa, sotto il gate,
|
||||
tenor 14-21, famiglia inverse, e l'unico f non tautologico (MTM) vale 1,08 su 4 strutture. **Non si
|
||||
riapre.** Primo punto di una distribuzione che `collect_chain` riempira' da solo; il secondo punto
|
||||
utile e' un crollo **con IV-rank > 0,30**.
|
||||
|
||||
## 3. Piu' monete, sempre in Deribit — `r0909_deribit_universo.py` · `r0909_xrp_terza_gamba.py`
|
||||
|
||||
**Il venue letto oggi (15:05Z):** 32 perpetual USDC-lineari aperti (+6 non aperti, fra cui **APT
|
||||
inactive**: e' il mancante rispetto al 23/08). Volume 24h ≥ $10M solo per **XRP ($38M, il piu'
|
||||
scambiato del venue, spread 4 bps, listato 2022-03-16), ETH, BTC, SOL ($13M)**; tutto il resto fra
|
||||
$0,01M e $7M (TAO, HYPE, ADA, AVAX, NEAR…) con spread 2-20 bps. XS01: **13/19** gambe — mancano
|
||||
ARB, OP, APT, INJ, TIA, SEI → §50 resta: morto per ampiezza. Opzioni USDC-lineari su 7 sottostanti
|
||||
(SOL 676, HYPE 442, XRP 442, TRX, AVAX) con la liquidita' misurata il 22/08 (SOL: 9% delle put a
|
||||
due lati). Trovati dalla revisione, registrati perche' il controllo costa €0 e nessuno li riapra:
|
||||
**`BTCDVOL_USDC-30SEP26`** (l'unico strumento long-vol diretto del venue: volume 0, OI 132, bid/ask
|
||||
39,1/43,9 = spread 12% → non negoziabile, e in contango sanguina come la put) e **PAXG perpetual**
|
||||
(oro, dal 2024-12: $0,18M/g, OI $8,3M — non misurato, non morto, stessi muri di SOL/XRP: 9 mesi di
|
||||
storia contro i 3 anni della decisione 22/08).
|
||||
|
||||
⇒ **L'unica "altra moneta" liquida su Deribit mai misurata come gamba direzionale e' XRP.** Misurata
|
||||
col harness di `r0822_sol_leg` (TP01 e SKH01 **congelati**, 24 ancore, differenze appaiate,
|
||||
de-levering, per anno), ipotesi a priori dichiarata: diluisce.
|
||||
|
||||
Certificazione (Coinbase dal rilisting 2023-07, Bitstamp prima — Coinbase aveva delistato XRP per
|
||||
la causa SEC): >1% dal riferimento 0,06% (2022) · **0,57% (2023)** · 0,01-0,02% (2024+); mediana
|
||||
5-6 bps. ⇒ **L-PULITA dal 2024** (stessa soglia e stesso anno sporco di SOL). ⚠️ Barre 5m flat
|
||||
**27-61%** (BTC/ETH 2021+: 0,1-2%; SOL 21%): il 5m su cui gira SKH01 e' 20-100× piu' flat.
|
||||
|
||||
| | L-FULL (2022-03+) | L-PULITA (2024+) |
|
||||
|---|---|---|
|
||||
| dSharpe FULL (mediana, 24 ancore) | **−0,206** [−0,26, −0,16], >0 in 0% | −0,066, >0 in 12% |
|
||||
| dSharpe hold-out | **−0,169** [−0,32, −0,15], **>0 in 0%** | −0,169, 0% |
|
||||
| dCAGR | −2,27 pp, 0% | −1,32 pp, 4% |
|
||||
| null del de-levering | Sh 3g 1,23 vs 2g×0,974 **1,44** → de-levering | 1,54 vs 1,53 → pari |
|
||||
| per anno (dSh) | 2022 −0,55 · 2023 −0,66 · **2024 +0,54** · 2025 −0,44 · 2026 −0,25 → **1/5** | |
|
||||
| corr(gamba XRP, book) | +0,35 / +0,37 (SOL 0,40-0,56) | |
|
||||
|
||||
**XRP DILUISCE** — lo stesso −0,17 di hold-out di SOL, in 0/24 ancore, con **un anno buono** (2024).
|
||||
TP01 XRP da solo: Sharpe −0,01, hold-out −0,49; SKH01 XRP maxDD 40,1% (il criterio di selezione di
|
||||
SKH01-V2-DD, maxDD<30%, fallisce anche qui, come SOL). Eseguibilita' non e' il vincolo (min 1 XRP =
|
||||
$1,42). Non misurato: **SKH01 solo-short su una terza moneta** (la domanda riguarda i crolli, dove
|
||||
paga lo short) — ma il filtro di direzione e' una famiglia nuova (M20) e il 5m di XRP e' peggio di
|
||||
SOL: il muro e' lo stesso. **Nessuna proposta.** Il dato vive in `data/raw/alt_xrp_*` (namespace di
|
||||
ricerca, non rinfrescato dal cron, `load_data("XRP")` fallisce; `tests/test_sol_leg.py` lo presidia
|
||||
via `DERIBIT_INSTR`). La cache del riferimento in `data/_cache` e' gitignored e fuori backup: la
|
||||
certificazione si riproduce **con rete** (N11 vale con rete).
|
||||
|
||||
## 4. Cosa cambia una decisione — niente; cosa si registra
|
||||
|
||||
| voce | prima | oggi |
|
||||
|---|---|---|
|
||||
| il libro nei crolli | "immune, beta 0,076" | perde nel GIORNO (−0,48%/g, 18% positivi), immune-o-positivo per finestra, **> 0 in 8/8 dal 2022** per SKH01 short marcato all'uscita; beta 0,0769 riprodotto |
|
||||
| f di stress delle opzioni | non misurato ("serve un crash catturato") | crollo catturato **a vol bassa, sotto il gate, inverse**: f_net 0,74 = rally; f_mtm 1,08; ali 1,92×. **Condizione non soddisfatta nella sostanza** |
|
||||
| peso SKH01 | gate fallisce (26/07) | invariato: la condizione che fallisce e' in-sample; cache segnali ferma al 25/07 |
|
||||
| XRP | mai misurato | diluisce come SOL (−0,169 hold-out, 0/24) |
|
||||
| universo Deribit | 14/19 (23/08) | 13/19 (APT inactive); liquidi solo BTC/ETH/XRP/SOL; BTCDVOL future non negoziabile; PAXG non misurato |
|
||||
| debito nuovo | — | `cblib.spot_series` + `asof` guarda un'ora avanti (feed 1h etichettato all'apertura) |
|
||||
|
||||
**Cosa riaprirebbe qualcosa:** (a) un secondo crollo catturato **a IV-rank > 0,30** (VRP01 a
|
||||
mercato), sulla famiglia USDC-lineare, per il f di stress; (b) `weights_tilt_null` che passa — si
|
||||
rigenera `skh_sigtab` e si rilancia `r0726_reeval_live_weight`, non si ridiscute; (c) per le
|
||||
"altre monete", un meccanismo che non sia TP01/SKH01 congelati, o 3 anni di dato 5m liquido.
|
||||
|
||||
## 5. Errori catturati in sessione (prima della revisione)
|
||||
|
||||
- Percorso ora per ora in un ciclo `groupby` per ancora: >10 minuti; riscritto su una tabella
|
||||
ora×strumento (`Quote`), ~3 minuti.
|
||||
- BTC "in geometria" senza guardia: la griglia rada faceva scegliere a `pick_legs` una δ−0,02 al
|
||||
posto della δ−0,10 (K72000/55000). Guardia `TOL_D` (±0,08/±0,06), scritta dopo aver visto BTC ma
|
||||
che per ETH scarta 2/288 e non cambia nessun verdetto (verificato dal revisore).
|
||||
- Coinbase restituiva un batch vuoto per XRP 2022 e il ciclo si fermava: certificazione vuota.
|
||||
Riscritto con avanzamento su batch vuoto e riferimento Bitstamp dove Coinbase manca.
|
||||
|
||||
## 6. Revisione (`fable`, agente fresco) — 21 segnalazioni, tutte verificate
|
||||
|
||||
Applicate: #1 artefatto del MTM (taglio alla larghezza; IV 108 e' a T→0) · #2 f_exp tautologico
|
||||
(quota stampata, N_eff = strutture) · #3 "101%" = perdita massima + fee · #4 `asof` un'ora avanti
|
||||
(`spot_causale`; debito su `cblib`) · #5 famiglia inverse dichiarata · #8 "durante" bimodale (p10/p90,
|
||||
quota che perde) · #9 ordine delle classi (immune → POSITIVO → PERDE, tre affermazioni nel verdetto)
|
||||
· #10 peggior giorno = crollo con TP01 0,505, docstring (c) corretto · #11 beta sulla finestra di §46
|
||||
(0,0769, tolleranza 0,01) · #12 scomposizione esatta con assert · #13 lente "marcata all'uscita"
|
||||
dichiarata · #14 confronto bit-exact con `_skyhook_returns` · #15 cache `skh_sigtab` ferma al 25/07
|
||||
· #16 1,92 e' un punto nuovo, non "gia' noto" · #17 il crollo e' sotto la mediana del DVOL: verdetto
|
||||
riscritto · #18-19 BTCDVOL future e PAXG registrati · #21 APT inactive, cache con rete.
|
||||
Non applicate: nessuna. Il revisore ha trovato **due conclusioni che non reggevano** (il 2,6× e
|
||||
l'"8/12 guadagna") e le ha smontate con numeri riprodotti: la regola *"chi rivede non e' chi ha
|
||||
scritto"* ha pagato in una sessione.
|
||||
|
||||
**Test (Opus, agente separato): `tests/test_r0909_*.py`, 92 test, suite intera 1008/1008 verdi**
|
||||
(erano 916). Coprono: ordine delle classi e riproduzione verbatim dei verdetti, `spot_causale` col
|
||||
controllo positivo sul difetto che ripara, taglio alla larghezza, geometria `in_geom` derivata da
|
||||
`VRP_CFG`, regola L-PULITA (anno pulito isolato ignorato), `_fetch_1h` su batch vuoti, P1 per
|
||||
identita' (`W_TP01`, `XS_UNIVERSE`), zero rete e zero scritture in `data/`. **Due difetti trovati da
|
||||
Opus nel verdetto di XRP, corretti:** un campione vuoto stampava "DILUISCE" (ora "NON MISURABILE",
|
||||
P5) e il criterio per anno era cablato al 2024 invece che derivato dall'anno della lente (P1).
|
||||
@@ -0,0 +1,182 @@
|
||||
# 2026-09-09b — Debito §5.18 riparato: `cblib` causale (spot +1h, DVOL +1 giorno) e §11 rimisurato
|
||||
|
||||
**Richiesta dell'operatore:** *"ripara il debito §5.18 su cblib e rimisura §11"*. Revisione col modello
|
||||
`fable` (agente fresco), come da regola.
|
||||
|
||||
**Esito in una riga:** riparato in `cblib` — spot e DVOL escono **gia' causali** — e §11 rimisurato allo
|
||||
stesso taglio: **0,714 [0,690-0,779] → 0,712 [0,664-0,732]** (n=19). Il numero non si muove perche' le due
|
||||
correzioni hanno **segno opposto**; **nessun verdetto cambia**; riparando la prima serie ne e' uscita una
|
||||
**seconda con lo stesso difetto** (il DVOL giornaliero, fino a 24 ore avanti); il DVOL orario resta un
|
||||
passo aperto **con costo misurato**. Libro, config, cron: invariati. Nessun ordine.
|
||||
|
||||
## 0. Il difetto, verificato prima di ripararlo (D6: si controlla col lag, non si assume)
|
||||
|
||||
| serie | come e' etichettata | prova | look-ahead di `asof(ts)` |
|
||||
|---|---|---|---|
|
||||
| feed 1h certificato | all'**apertura**: la barra T contiene la chiusura di T+1h | chiusura 1h a T == chiusura 5m a **T+55** nel **100%** delle barre (BTC ed ETH, 1-5/06); a T−5 nell'1,7% / 0% | **+1 ora** |
|
||||
| `dvol_{asset}.parquet` (giornaliero, `fetch_dvol`, `resolution=86400`) | al giorno D contiene la **chiusura delle 23:00 di D** | API pubblica a risoluzione 1h, 1-4/06: `close` 1D del giorno D == `close` 1h delle 23:00 di D, e `open` 1D di D+1 == `close` di D | **fino a +24 ore** |
|
||||
|
||||
La seconda riga non era nel debito: e' uscita controllando la convenzione dell'altra serie di
|
||||
contesto che `cblib` legge con lo stesso `asof`. Il sleeve (`_vrp_weekly_asset`) **non ha il difetto**:
|
||||
lavora a cadenza giornaliera e accosta la chiusura del giorno D al DVOL del giorno D, che a quella
|
||||
cadenza e' coerente. Il difetto e' solo di chi entra **a un'ora** con una serie giornaliera.
|
||||
|
||||
## 1. La riparazione
|
||||
|
||||
`cblib.causale(s, cadenza)` sposta le **etichette** (mai i valori) dall'apertura all'istante in cui la
|
||||
chiusura e' nota. `spot_series` → `causale(·, "1h")`, `dvol_series` → `causale(·, "1D")`. Conseguenze:
|
||||
|
||||
- `asof(ts)` = ultima chiusura **conosciuta** a ts (prima della prima chiusura nota: NaN, non un valore).
|
||||
- il regolamento `ST = S.asof(exp)` alle 08:00 e' ora la chiusura della barra 07:00, cioe' **il prezzo
|
||||
delle 08:00** — prima era la chiusura della barra 08:00, cioe' il prezzo delle **09:00**, un'ora dopo la
|
||||
scadenza. Deribit regola su una TWAP dei 30 minuti prima delle 08:00: l'approssimazione e' ora dalla
|
||||
parte giusta dell'ora.
|
||||
- `r0909_crash_catturato` **non ri-sposta** (lo `spot_causale` locale della mattina e' rimosso: applicato
|
||||
due volte darebbe la chiusura di **due** ore prima — lo stesso errore col segno opposto). Test di
|
||||
non-regressione sul sorgente.
|
||||
- chi usa `cblib`: `r0822_vrp_real_quotes` (§11), `r0822_skew` (§5), `r0909` (§75), **`vrp_f_watch`**
|
||||
(cron giornaliero, `data/vrp_f_watch/state.json`), e — trovati dal revisore, non da me —
|
||||
**`r0730_vrp_real_quotes`** (il f 0,73 [0,706-0,805] di CLAUDE.md §2: rigirato stampa **0,71
|
||||
[0,680-0,756]**, n=27, 0/27 ≥ 1, f gamba lunga 1,66 → 1,49; verdetto invariato, numero dichiarato in §2)
|
||||
e **`r0901_btc_collar`** (join del DVOL **per giorno**: con la serie causale avrebbe accostato l'IV di D
|
||||
al DVOL di D−1 — riallineato con `causale(·, "-1D")`, bit-exact a prima, §71 non si muove).
|
||||
`options_vrp_calibrate.py` ha una **copia propria** di `spot_series` (numeri del 20/06): **non toccato**,
|
||||
dichiarato.
|
||||
- la sezione di REGIME di `r0822` (date di calendario: «ultimo giorno aperto», IV-rank per anno) e la
|
||||
tabella di contesto di `r0909` usano `causale(V, "-1D")`: li' serve il DVOL **del** giorno, non
|
||||
l'etichetta causale — altrimenti ogni data stampata slitta di un giorno (trovato dal revisore).
|
||||
- `r0822_vrp_real_quotes` ha ora il flag `--al <data>` (taglio delle scadenze) per riprodurre una misura
|
||||
a una data: senza, "riprodurre il 22/08" era impossibile perche' la catena cresce ogni ora.
|
||||
|
||||
## 2. Prima di pubblicare il numero nuovo, la macchina ha riprodotto quello vecchio (M23)
|
||||
|
||||
Worktree di HEAD (`git worktree add`, `data/raw` in symlink), stesso script con `--al 2026-08-22`:
|
||||
**pooled 0,714 (IC95 [0,690, 0,779], n=19)** — al millesimo, IC compreso. Poi lo stesso comando sul
|
||||
codice riparato.
|
||||
|
||||
## 3. Scomposizione: due correzioni di segno opposto (M26) e un controllo orario
|
||||
|
||||
| taglio | spot | DVOL | pooled f | IC95 | n |
|
||||
|---|---|---|---|---|---|
|
||||
| 22/08 | grezzo | grezzo (com'era) | **0,714** | [0,690, 0,779] | 19 |
|
||||
| 22/08 | causale | grezzo | 0,721 | [0,685, 0,758] | 19 |
|
||||
| 22/08 | grezzo | causale (giornaliero) | 0,693 | [0,670, 0,748] | 19 |
|
||||
| 22/08 | causale | causale (giornaliero) = **`cblib` oggi** | **0,712** | [0,664, 0,732] | 19 |
|
||||
| 22/08 | causale | causale **orario** (controllo, API pubblica) | 0,717 | [0,667, 0,732] | 19 |
|
||||
| 09/09 | grezzo | grezzo | 0,720 | [0,717, 0,952] | 23 |
|
||||
| 09/09 | causale | causale (giornaliero) | **0,712** | [0,677, 0,741] | 23 |
|
||||
| 09/09 | causale | causale orario | 0,726 | [0,680, 0,738] | 23 |
|
||||
|
||||
Il DVOL di fine giornata **alzava** f: nel rally la vol scende dentro il giorno, la chiusura di D sta
|
||||
**sotto** quella di D−1 (+1,05 pt di vol mediana su 19/19 trade, misura del revisore) → denominatore piu'
|
||||
basso → f piu' alto; causale: −0,021. Lo spot di un'ora dopo, invece, **non e' un meccanismo ma rumore**:
|
||||
lo scarto per trade ha segno misto (mediana −0,02% BTC / −0,11% ETH, banda −0,6…+0,9%) e sposta f fino a
|
||||
**±0,15 per trade** (lo spread δ−0,28/−0,10 ha delta netto ~0,18: 0,5% di spot vale 15-20% del credito
|
||||
modellato); il +0,007 netto e' la somma di 19 segni. ⚠️ Corollario: lo spot causale resta **stantio fino
|
||||
a 25 minuti** (snapshot al :25, chiusura nota al :00), cioe' ±0,05-0,10 di f per osservazione; il feed
|
||||
**5m certificato** lo porterebbe a ≤5 minuti. Non fatto: dichiarato in §6. Il DVOL giornaliero causale e' **stantio fino a
|
||||
23 ore** (agli ingressi delle 08:00: 8 ore): scarto mediano dall'orario **0,2 punti di vol**, max 1,1
|
||||
(22/08) / 3,0 (oggi). Su §11 vale 0,005 di f.
|
||||
|
||||
## 4. Cosa cambia in §11 (taglio 22/08, stesso campione)
|
||||
|
||||
| grandezza | prima | dopo | perche' |
|
||||
|---|---|---|---|
|
||||
| f pooled | 0,714 [0,690-0,779] | **0,712 [0,664-0,732]** | §3 |
|
||||
| f BTC / ETH | 0,706 / 0,715 | 0,720 / 0,692 | |
|
||||
| Sharpe canonico BTC (titolo, non citabile) | 32,58 | 29,61 | regolamento alle 08:00 |
|
||||
| **mediana onesta** BTC (9 ancore) | 1,45 | **2,12** | banda [−0,19, 29,61] (era [−0,35, 32,58]) |
|
||||
| Sharpe canonico ETH → mediana onesta | 3,90 → 2,41 | 7,64 → 3,35 | una settimana ETH cambia taglia: 26/06 −27,38 → −12,84 $ (BTC: 31/07 19,88 → 31,69, 14/08 12,94 → 8,68) |
|
||||
| media BTC fra le ancore | −1,35% → +11,22% | −0,66% → +11,44% | **cambia ancora segno**; sd 11,1× → 9,2× |
|
||||
| forfait fee vs listino | 1,44× / 1,80× | 1,51× / 1,87× | credito modellato a DVOL causale |
|
||||
| peggior mark: mediana / minimo / minimo fra le vincenti | −6,4% / −91,0% / −56,9% | −6,4% / −91,5% / −55,2% | |
|
||||
| P&L somma 19 settimane | $2.733 | $2.822 (+3,3%) | tutto dal regolamento |
|
||||
| sottostante nel campione | +25,1% / +44,2% | +21,1% / +40,1% | il vecchio regolamento del 21/08 era il prezzo delle **09:00**, che sta **+3,9%** sopra quello delle 08:00 (76.311 → 79.316): un numero a due estremi pesa un'ora intera. (La prima stesura diceva «−1,8%»: avevo letto l'ora dopo, 09:00 → 10:00. Corretto dal revisore) |
|
||||
|
||||
**Verdetto di §11: SCARTATO, per le stesse quattro ragioni** (il campione non contiene la strategia; il
|
||||
titolo non sopravvive all'ora d'ingresso; la famiglia con storia non e' quella eseguibile; lo spread e'
|
||||
il costo dominante). Nessuna delle quattro dipende da un'ora di spot. Il testo del verdetto nello script
|
||||
riporta i numeri nuovi con i vecchi accanto.
|
||||
|
||||
## 5. Effetti collaterali, misurati (tutto cio' che legge `cblib`)
|
||||
|
||||
**§75, crollo catturato** (`r0909`, rigirato): ETH pre-crollo f_net **0,74 [0,65-0,82] → 0,76
|
||||
[0,71-0,86]**; peggior MTM 1,08 [1,07-1,31] → 1,08 [1,06-1,27]; put δ−0,10 1,92× → 1,98×; al picco 0,94 →
|
||||
0,99; BTC durante **0,79 → 0,89 [0,74-1,05]**, ETH durante 0,71 → 0,78. Con il DVOL **orario** BTC durante fa **0,83**: nel crollo
|
||||
il DVOL sale di 5-6 punti al giorno, quindi il giornaliero di fine giornata (com'era) sottostima f e
|
||||
quello del giorno prima (causale) lo sovrastima — **−0,04/+0,06 di f e' il prezzo della stantiezza in un
|
||||
crollo**, ed e' l'argomento per il feed orario. DVOL "pre" 37,3 → **36,4** (BTC): il 37,3 era la chiusura
|
||||
del **primo giorno di crollo** (01/06, −3,5%), non del giorno prima — IV-rank pre 0,05 → 0,03. Picco e
|
||||
percentili **invariati** (49,1 / 69,2; 29° / 46°) dopo aver corretto la tabella di contesto (§7).
|
||||
Verdetto invariato.
|
||||
|
||||
**§5, skew** (`r0822_skew`, rigirato, 66 s): la scomposizione sul panel si muove di **≤0,005** per fattore
|
||||
(term 0,880 · skew 0,917 · spread 0,910 · fit 0,995 · f_tot 0,729); Q1/Q2 (lag prezzo/skew) **non si
|
||||
toccano** (serie proprie ancorate a `ts_max`). Agli ingressi il controllo `f_markfit` passa da 1,045 a
|
||||
**0,991** e le osservazioni che lo superano da 22/28 a **25/28**: l'ora di spot in piu' stava **nel
|
||||
controllo**, non nell'effetto — riparare ha reso il controllo piu' pulito, non il risultato diverso.
|
||||
|
||||
**`vrp_f_watch`** (sorvegliante in `cron_daily`, criteri pre-registrati il 30/07 e **non toccati**):
|
||||
|
||||
| | prima (state.json del 09/09 mattina) | dopo |
|
||||
|---|---|---|
|
||||
| coppie | 28 | 28 |
|
||||
| f canonico | 0,732 [0,702-0,784] | **0,706 [0,678-0,745]** |
|
||||
| f candidato | 0,803 [0,741-0,842] | 0,805 [0,771-0,840] |
|
||||
| differenza | +0,054 [−0,027, +0,105], 18/28 > 0 | **+0,097 [+0,042, +0,140]**, 21/28 > 0 |
|
||||
| ampiezza IC95 (b ≤ 0,12) | 0,131 — no | **0,098 — OK** |
|
||||
| coppie (a ≥ 40) | no | no |
|
||||
| `ready` | False | False |
|
||||
|
||||
Lo `state.json` e' stato **riscritto** dal mio lancio (21:14Z, senza `--quiet`; nessun Telegram: notifica
|
||||
solo a `ready`); il «prima» completo e' la colonna qui sopra (n_positive 18, `esclude_zero` False), e il
|
||||
cron lo rigenera domani. La differenza ora **esclude lo zero**. Non decide niente: (a) non e' soddisfatto e il candidato resta
|
||||
bocciato sul deflated-Sharpe, che non dipende da f. Si registra perche' fra un mese qualcuno leggera'
|
||||
"esclude lo zero" e deve sapere da quando e perche'.
|
||||
|
||||
## 6. Cosa resta aperto, col suo costo
|
||||
|
||||
| voce | costo di non farlo | costo di farlo |
|
||||
|---|---|---|
|
||||
| **DVOL orario** nel feed (`fetch_dvol --res 3600`, riga in `cron_daily`, `cblib` che lo legge) | su §11 0,005 di f; **su un crollo ±0,05**; ogni misura oraria su opzioni a DVOL giornaliero eredita lo scarto | un fetch pubblico gratuito (3.163 barre da maggio in 2 chiamate), una riga di cron, e una decisione: e' un feed nuovo dentro il perimetro (D1, certificazione, backup) |
|
||||
| **spot causale stantio ≤25 min** (snapshot :25, chiusura :00): ±0,05-0,10 di f per osservazione | `cblib.spot_series` sul feed **5m** certificato (`causale(·, "5m")`): cambia di nuovo ogni numero di `cblib`, quindi con rimisura dichiarata |
|
||||
| `options_vrp_calibrate.spot_series` (copia, 20/06) | i numeri del 20/06 (f 0,73) restano con l'ora in piu'; §11 li ha gia' sostituiti | riscriverlo su `cblib` |
|
||||
| la tabella pubblicata di §5 (x0,869 / x0,904 / x0,920) | ≤0,005 per fattore: sotto la risoluzione del campione (14+14 scadenze) | nulla: nota aggiunta |
|
||||
|
||||
## 7. Errori dell'autore in sessione
|
||||
|
||||
- **`asi8` in microsecondi** scrivendo il test della causalita' (pandas 3): i timestamp sintetici finivano
|
||||
nel 1970 e il test falliva per la fixture, non per il codice. E' la trappola del debito 1, ripagata
|
||||
scrivendo un test *sul* debito 18. Corretto con `(idx − epoch) // Timedelta(ms)`, la convenzione di
|
||||
`load_tf`.
|
||||
- **La tabella di contesto di `r0909` si e' spostata di un giorno** dopo la riparazione: mostrava il DVOL
|
||||
del giorno D−1 accanto al prezzo del giorno D, e il picco del crollo cambiava (69,2 → 68,3, 46° → 43°)
|
||||
non per una misura ma per un'etichetta. Trovato **col diff degli output**, non da un test: una tabella
|
||||
per umani non ha test. Ora il contesto usa la chiusura *del* giorno (`causale(V, "-1D")`, dichiarato
|
||||
nel commento) e `dvol_pre` / `ivr_pre` restano causali.
|
||||
- Il verdetto §7 di `r0822_vrp_real_quotes` era **prosa con numeri cablati**: sarebbe rimasto a 0,714 per
|
||||
sempre (N11 al contrario). Aggiornato con i vecchi accanto; il difetto di forma resta (i numeri del
|
||||
verdetto andrebbero calcolati, non scritti).
|
||||
|
||||
## 8. Revisione (`fable`, agente fresco) — 16 segnalazioni, verdetto «correggi e committa»
|
||||
|
||||
Ha **riverificato in proprio** la convenzione delle due serie (1h vs 5m su 120 barre: 100%; DVOL contro
|
||||
l'API oraria) e riprodotto ogni numero dei documenti lanciando gli script. Applicate: **#2** «−1,8% in
|
||||
un'ora» era falso (avevo letto 09:00 → 10:00; il fatto e' +3,9% 08:00 → 09:00) · **#3** due delle tre
|
||||
settimane «ETH» erano BTC · **#4** la parentesi sul DVOL era invertita (la chiusura di D e' piu' BASSA,
|
||||
non piu' alta) · **#5** lo spot non e' un meccanismo ma rumore, e resta stantio ≤25 min · **#6/#7** due
|
||||
lettori di `cblib` non dichiarati: `r0730` (0,73 → 0,71, ora in CLAUDE.md §2) e `r0901` (riallineato) ·
|
||||
**#8** le date di calendario di `r0822` §1 slittavano di un giorno · **#9** il test anti-doppio-shift non
|
||||
catturava `causale(spot_series, "1h")`: ora conta i `causale(` · **#10** ancora 0,74 → 0,76 nel test
|
||||
d'integrazione · **#11** teste di §75 e memoria 20 §5 con i numeri vecchi come correnti (M28) · **#12**
|
||||
«±0,05» → «−0,04/+0,06» · **#13** `state.json` riscritto: dichiarato · **#15** lo skew agli ingressi cambia
|
||||
piu' del panel (correlazioni che cambiano segno): dichiarato in §5. Non applicate: nessuna. Confermati
|
||||
senza rilievo (#1, #14, #16): verso e taglia degli shift, nessun doppio spostamento, `ST` alle 08:00
|
||||
coerente con la TWAP 07:30-08:00, tutti i numeri di §3-§5.
|
||||
|
||||
## 9. Test
|
||||
|
||||
`tests/test_cb_chain_vrp.py`: +3 (causale; `spot_series` causale sul feed 1h con controllo positivo sulla
|
||||
serie grezza; `dvol_series` causale sul giornaliero). `tests/test_r0909_crash_catturato.py`: il test dello
|
||||
shift locale diventa "lo script usa le serie di `cblib` senza ri-spostarle" (sorgente + controllo positivo
|
||||
su `cblib.causale`). Suite intera: **1011 passati, 0 falliti** (erano 1008), rilanciata dopo le correzioni della revisione: 1011/1011.
|
||||
@@ -0,0 +1,98 @@
|
||||
# 2026-09-09c — Revisione settimanale in sola lettura: cosa si costruisce e cosa no
|
||||
|
||||
**Richiesta dell'operatore:** *«schedulare ogni x tempo un processo di auto revisione dei dati e della
|
||||
configurazione del sistema in modo che migliori con il tempo … autoregolazione e autoaggiornamento dei
|
||||
sistemi o anche creare nuovi sistemi … valutando dati macroeconomici … e poi informa me tramite
|
||||
Telegram»*. Dopo la discussione: *«fai»*.
|
||||
|
||||
**Esito in una riga:** costruita la **rilettura settimanale in sola lettura** con revisore diverso
|
||||
dall'autore (`claude-fable-5-1`), rapporto firmato in `docs/revisioni/` e Sintesi su Telegram, lunedi'
|
||||
06:15 UTC; **scartati** — con la memoria del progetto, non per prudenza generica — autoregolazione dei
|
||||
parametri, generazione automatica di strategie e macro da internet. Libro, config, pesi: invariati.
|
||||
|
||||
## 1. Perche' tre pezzi su quattro sono no
|
||||
|
||||
| pezzo | cosa dice la memoria | esito |
|
||||
|---|---|---|
|
||||
| auto-revisione periodica | nove sorveglianti, ognuno sulla sua grandezza; nessuno rilegge l'insieme; la revisione col secondo modello ha trovato oggi due conclusioni false e un look-ahead | **si'** |
|
||||
| autoregolazione dei parametri | TP01/SKH01 congelati (M20), 75/25 confermato 3 volte, `weights_tilt_null` fallisce; `scale_watch` esiste per rendere visibile ogni cambio di config; A8: una riga di config + una di giornale, decisione dell'operatore | **no** |
|
||||
| generazione e test di strategie nuove | 69 filoni in 6 ondate; ogni candidato e' un trial (M3) e deflaziona tutto; la ricerca non e' il vincolo binding dal 26/07 (+0,046 €/g il miglior lead contro +4,07%/anno per €100/mese) | **no, salvo freno**: prima si apre la memoria e si batte il motivo della morte; i candidati vanno in forward-monitor con gate e data, mai nel book |
|
||||
| dati macro da internet | gate macro / DVOL direzionale / skew de-risk: HEDGE o ridondanti (TP01 gia' flat nei crolli); un dato dal web non e' certificato (D1-D4; la APR USDC 3,40% pubblicata e mai incassata) | **no** |
|
||||
|
||||
Il centro dell'argomento: **il progetto ha gia' misurato che piu' ricerca non sposta il traguardo, i
|
||||
bonifici si'**. Un ciclo automatico che cerca strategie ottimizza la leva sbagliata e paga in trial bruciati.
|
||||
|
||||
## 2. Cosa gira
|
||||
|
||||
`scripts/cron_review.sh` (lunedi' **06:15 UTC**: fuori dal :00, fuori dal martedi' 09:00, dopo il
|
||||
`cron_daily` delle 00:30 cosi' il giornale di domenica e' chiuso) → `scripts/live/revisione.py --quiet`
|
||||
→ `src/live/revisione.scrivi`. Riga installata in crontab (`15 6 * * 1`), letta dal test.
|
||||
|
||||
**Materiale** (sola lettura, ~195k caratteri, 27 voci): CLAUDE.md intero · `config/live.json` ·
|
||||
`crontab -l` · `git log` 14 g + `git status` · `monitor_health` · stato dei sorveglianti (scale, vrp_f,
|
||||
usde, balance, venue_news, movimenti dichiarati, watermark) · 7 pagine di giornale · diari della
|
||||
settimana. Gli stati stanno **prima** delle voci grandi: alla prima stesura il tetto totale (320k) aveva
|
||||
tagliato `usde_convert.jsonl` a 2 caratteri — un sorvegliante invisibile per un limite di lunghezza.
|
||||
|
||||
**Prompt**: dieci regole vincolanti (sola lettura · ogni segnalazione cita la fonte · solo numeri del
|
||||
materiale · le decisioni di §3 non si ripropongono se la colonna «cosa la riapre» non e' soddisfatta ·
|
||||
un'idea nuova solo citando la memoria che ha ucciso la simile · i gate con data e criterio, senza
|
||||
anticipare · misurato ≠ dedotto · niente previsioni · URGENTE per cio' che serve all'operatore subito ·
|
||||
cinque titoli fissi, ~1.000 parole). L'ultimo titolo, «Cosa ho letto e cosa mi mancava», e' l'elenco da
|
||||
cui il materiale della settimana dopo migliora: e' l'unico «miglioramento col tempo» che questo sistema
|
||||
si concede, e passa da una modifica al codice, cioe' da una revisione.
|
||||
|
||||
**Uscita**: `docs/revisioni/<data>.md` firmato (modello, ora, «SOLA LETTURA», «lettore fallibile P13»,
|
||||
guardia sui numeri come `analista` con soglia 12, tabella del materiale letto). Telegram: la sola
|
||||
Sintesi, taglio dichiarato sotto i 4.096. Modello muto o risposta senza i titoli → rapporto «NON
|
||||
eseguita» col motivo e 🚨: il silenzio non e' una revisione (P5).
|
||||
|
||||
**Cosa non puo' fare, per costruzione**: scrivere altro che il rapporto (test sull'albero dei file:
|
||||
`--secco` non scrive niente, un giro scrive UN file), eseguire proposte, cambiare config. Il modello e'
|
||||
`claude-fable-5-1` per costante, e un test verifica che sia diverso da `analista.MODELLO_DEFAULT`.
|
||||
|
||||
## 3. Test — `tests/test_revisione.py` (16)
|
||||
|
||||
Finestra del materiale e file assenti dichiarati · ordine stati-prima-delle-voci-grandi · troncamento
|
||||
dichiarato · regole nel prompt · guardia (vuoto, titoli mancanti, numeri fuori con controllo positivo) ·
|
||||
rapporto firmato · modello muto → rapporto + 🚨 · Telegram con HTML neutralizzato e taglio dichiarato ·
|
||||
`--secco` non chiama e non scrive · un giro scrive un solo file · `--no-telegram` · revisore ≠ autore ·
|
||||
cadenza: il `.sh` dichiara «lunedi' HH:MM UTC», minuto ≠ 0, fuori dallo slot di release, e la crontab
|
||||
installata ha gli stessi cinque campi (skip se illeggibile). `test_cli_flag` ha preso lo script da solo
|
||||
(guardia `valida`, USO coi flag, flag del cron accettati).
|
||||
|
||||
## 4. Errori in sessione
|
||||
|
||||
- `_tronca(testo, n=MAX_CHARS_VOCE)`: il default e' catturato alla definizione, il test che patchava la
|
||||
costante non vedeva niente — la stessa trappola del debito 12 (`connect`). Letto a runtime.
|
||||
- Il tetto totale tagliava in coda: i sorveglianti erano gli ultimi e i primi a sparire. Riordinato, e
|
||||
il test lo presidia.
|
||||
|
||||
## 5. Primo giro reale (22:03Z) — e i tre difetti che solo il giro reale poteva trovare
|
||||
|
||||
Il primo lancio e' **morto prima di chiamare il modello**: `OSError: Argument list too long` — 195k
|
||||
caratteri di prompt passati come argomento alla CLI superano il limite del sistema, e `analista.interroga`
|
||||
non lo catturava: zero rapporto, zero Telegram, **il silenzio che il modulo esiste per vietare**. Nessun
|
||||
test l'avrebbe trovato (i test iniettano `interroga`): e' un difetto di trasporto, e il trasporto si valida
|
||||
sul trasporto (P2). Tre correzioni: (1) `revisione.interroga` proprio, prompt su **stdin**, ogni eccezione
|
||||
restituita come errore; (2) `scrivi` incapsula chiamata e invio: un'eccezione diventa un rapporto «NON
|
||||
eseguita» + 🚨; (3) `_cmd` (crontab, git) dichiara qualunque eccezione invece di propagarla. Test con
|
||||
controllo positivo su `OSError(7)`. Terzo difetto, trovato dal test: `analista.pulisci` **toglie le righe
|
||||
che iniziano con `#`**, cioe' i cinque titoli che la guardia richiede — usarlo avrebbe reso ogni revisione
|
||||
«rifiutata: titoli mancanti». Sostituito con `strip()`.
|
||||
|
||||
Secondo lancio: **rapporto scritto, stato `sospetta`**, 1.435 parole, `docs/revisioni/2026-09-09.md`.
|
||||
La guardia ha segnato 11 numeri «non nel materiale»: sono cifre in **formato italiano** («4.477,46»,
|
||||
«1.341») contro il materiale in formato USA («$4,477.46») — la normalizzazione di `analista` conosce una
|
||||
convenzione sola. E' un limite dichiarato, non un errore del revisore: si aggiusta quando si vede quanto
|
||||
spesso scatta. Contenuto: **nessun URGENTE**; tre verifiche in cima (una possibile attribuzione a reward di
|
||||
400 USDE che erano un acquisto in `usde_watch` del 07/09 — `n_trades` fermo a 50 contro 54 ordini nel
|
||||
registro; il cuscino USDC del debito §5.17 senza definizione balance/equity; il versamento del 04/09 non
|
||||
dichiarato in `movimenti_dichiarati.jsonl`); una contraddizione in CLAUDE.md §1 sull'haircut (M28); la data
|
||||
di arming che differisce fra §0, config e TWR; quattro sorveglianti senza timestamp o fuori da
|
||||
`monitor_health`; e, come richiesto dalla regola 5, **zero idee di strategia**. «Cosa mi mancava»: stati
|
||||
con ora di `edge/fee/venue/scale_watch`, l'output di `usde_watch.rendimento()`, i paper a oggi,
|
||||
`cron_daily.sh`, il registro fill della settimana — l'elenco da cui il materiale della prossima settimana
|
||||
migliora. Le segnalazioni sono dell'operatore e della prossima sessione: **nessuna e' stata applicata qui**
|
||||
(sarebbe l'agente che esegue le proposte del revisore nello stesso giro, cioe' il ciclo che si e' scartato).
|
||||
Costo: una chiamata, ~4 minuti.
|
||||
@@ -0,0 +1,216 @@
|
||||
# I cinque punti della revisione del 09/09 — decisi e fatti
|
||||
|
||||
**Data:** 2026-09-10 (sessione 12:08Z → 13:10Z; i timestamp qui dentro sono letti, non ricordati)
|
||||
**Origine:** la prima revisione settimanale (`docs/revisioni/2026-09-09.md`, revisore `fable`) e il
|
||||
giornale del 09/09. Ogni punto e' stato spiegato all'operatore con pro e contro e **deciso da lui**
|
||||
(§3-style: decisioni con l'informazione completa). Issue sul Gitea di casa #2-#6, chiuse con la
|
||||
soluzione scritta (regola globale del 10/09).
|
||||
|
||||
## Le decisioni, in una tabella
|
||||
|
||||
| # | punto | decisione dell'operatore | issue |
|
||||
|---|---|---|---|
|
||||
| 1 | `usde_watch` contava 400 USDE come reward (07/09) | ripara ora + issue | #2 bug |
|
||||
| 2 | cuscino USDC senza sorvegliante (§5.17) | **equity USDC, riconverte da solo** | #3 sviluppo |
|
||||
| 3 | versamento 04/09 solo rilevato; piano €5.000 «in attesa» | **e' l'INTERO versamento previsto**: dichiarato, piano chiuso | #6 sviluppo |
|
||||
| 4 | SCALA-01 A2: da quando contano i 180 giorni | **dal 01/09** (sorvegliante attivo) → gate non prima del **2027-02-28** | #5 bug |
|
||||
| 5 | PREVDAY-01 «senza data» | **tieni 21/06/2027, cabla kill e veto, correggi §4** | #4 bug |
|
||||
|
||||
Sul punto 5 la domanda giusta e' stata «ricorda dove dobbiamo arrivare (50 €/g)»: PREVDAY
|
||||
de-luckato al 15% vale +0,10 di Sharpe sul libro e la somma di *tutti* i lead dell'ondata di agosto
|
||||
vale +0,036 €/giorno. Verso i 50 €/g il candidato vale centesimi in qualunque esito del gate:
|
||||
non si spende una sessione di ricerca per anticipare (a)+(b), e non si butta l'unica gamba short
|
||||
scorrelata oltre a SKH01 per risparmiare un cron. Costo scelto: mezza giornata, una volta.
|
||||
|
||||
## Punto 1 — il lettore dei trade spot aveva DUE limiti, non uno
|
||||
|
||||
Il primo era noto dalla revisione: `trade_history(limit=50)` con 54 ordini. Il secondo l'ho
|
||||
trovato sondando il gateway per ripararlo: `get_user_trades_by_instrument` chiamato senza
|
||||
`historical` restituisce **solo le ultime ~24 ore** — il fill BTC dell'08/09 06:47Z e' invisibile
|
||||
alle 12:55Z del 10/09, quello del 09/09 21:47Z si vede. Il cron gira ogni 24h alle 12:35: sta
|
||||
dentro la finestra per pochi secondi. Un giro saltato avrebbe reso invisibili i trade fra 48h e
|
||||
24h fa, e il delta sarebbe stato attribuito al reward *esattamente come il 07/09*, con un
|
||||
meccanismo diverso.
|
||||
|
||||
Ora `trade_copertura()` dichiara **non leggibile** (somma `None`, e `analizza` non attribuisce:
|
||||
P12) in entrambi i casi — lista lunga quanto il limite (troncata) o ultima lettura oltre 24h+5min —
|
||||
e il record porta `trades_motivo` (P4: il perche'). `TRADE_LIMIT = 1000` e' il `count` massimo di
|
||||
Deribit. Controllo positivo nei test: 54 ordini si sommano tutti.
|
||||
|
||||
La riga del 07/09 in `data/live/usde_watch.jsonl` e' **corretta in loco** con campo `correzione`
|
||||
(originale conservato dentro, copia del file in `usde_watch.jsonl.pre_fix_20260910`): 54 ordini
|
||||
= 8 + 30 + 16 dal registro `usde_convert.jsonl`, 2.434 USDE, reward 0,080934. Il tasso non
|
||||
cambia — la riga era gia' esclusa per `con_trade` — e `rendimento()` oggi da' **3,94% su 14
|
||||
finestre, 12 pagate**.
|
||||
|
||||
## Punto 2 — `cuscino_watch`: la grandezza che nessuno guardava
|
||||
|
||||
Il cuscino richiesto e' `2 × 0,5 × 0,30 × equity_tot` = 30% dell'equity, **derivato** da
|
||||
config e `book.WEIGHT`. La formula e' passata da `usde_convert.py` a `src/live/usde.py`, e
|
||||
`usde_convert` la importa da li': due lettori, una formula (P1 — cinque occorrenze nel
|
||||
progetto di sorveglianti puntati su un bersaglio ridichiarato).
|
||||
|
||||
| stato | criterio | azione |
|
||||
|---|---|---|
|
||||
| OK | slack ≥ 10% del cuscino | niente |
|
||||
| PREAVVISO | 0 ≤ slack < 10% | ⚠️ alla transizione |
|
||||
| SCOPERTO | slack < 0 | 🚨 + `usde_convert --quota 0,64 --esegui` (cuscino + 20% di margine, alzato dal 10% in revisione) |
|
||||
| BLIND | conto non leggibile | ⚠️ alla transizione |
|
||||
|
||||
Guardie della riconversione: `execution_enabled` del libro (un solo interruttore), niente vendita
|
||||
sotto `depeg_warn` (li' decide l'operatore, e l'allarme lo dice), un tentativo ogni 6h, le guardie
|
||||
proprie di `usde_convert` (banda di prezzo, book leggibile, tetto HARD) ereditate lanciando lo
|
||||
script vero (P15). Il marcatore «gia' detto» e' l'esito di `notify`: un invio fallito non consuma
|
||||
la transizione (lezione del debito §5.2). Cron `:53` = dopo il giro del book (`:47`), fuori dal
|
||||
minuto tondo. `monitor_health` lo sorveglia (max 3h).
|
||||
|
||||
📌 **Primo giro a secco (13:00Z): gia' PREAVVISO.** USDC equity $1.361,23 contro $1.335,26
|
||||
richiesti: **slack +$25,97**. Il 09/09 il revisore aveva stimato +$41/+$59; la marcatura del
|
||||
libro dal picco del 06/09 ne aveva consumati altri 15-30 senza che nessuno lo vedesse. E' il
|
||||
caso d'uso del sorvegliante, arrivato prima del sorvegliante.
|
||||
|
||||
## Punto 3 — il versamento del 04/09, dal balance e non dall'equity
|
||||
|
||||
Il salto di equity 13:47→14:47Z diceva +$2.410,14 col mercato dell'ora dentro. Il **balance
|
||||
USDC** di `balance_watch` (cambia solo per P&L realizzato, fee, funding e movimenti) dice
|
||||
1.417,73455581 → 3.832,41432481 = **+$2.414,68**, nessun fill nella finestra, funding su $541
|
||||
di nozionale ~$0,01. Dichiarato con banda $0,05. Il report ora lo mostra come dichiarato e
|
||||
il TWR passa a **+6,66%** (era +7,22% stamattina, +7,83% il 06/09): la coda −0,63% dal 04/09 e'
|
||||
marcatura delle due long, trading da arming **+$13,82**.
|
||||
|
||||
L'operatore ha detto che era **l'intero versamento previsto**: la voce «in attesa di importo e
|
||||
data» di §1 e' chiusa. Resta la lezione di §2: per sei giorni il movimento e' stato solo
|
||||
rilevato, la regola diceva il giorno stesso.
|
||||
|
||||
## Punto 4 — SCALA-01: la data era il punto 5 della SPEC scambiato per il gate
|
||||
|
||||
«Non prima del 2026-10-01» erano i 30 giorni di sorvegliante del punto 5 (§7 della SPEC); A2
|
||||
chiede **≥180 giorni** col criterio passato ogni giorno, e il criterio lo misura solo
|
||||
`scale_watch`, attivo dal 01/09. Le tre letture possibili, con pro e contro, sono nel diario di
|
||||
sessione; l'operatore ha scelto **dal 01/09 → 2027-02-28**. Contare dall'arming avrebbe dato
|
||||
un criterio ricostruito a posteriori, che non prova nulla sul sorvegliante; ridurre A2 a 30 giorni
|
||||
avrebbe allentato un gate senza un costo d'attesa che lo giustifichi (il gradino 1,25x vale meno
|
||||
di €100/mese di versamento).
|
||||
|
||||
Riconciliata anche la data d'arming: **20/06 = TP01 da solo** (commit `4650aa7`), **23/06 = il
|
||||
BOOK** (commit `db738bc`, prima lettura di equity 23/06 22:00Z). CLAUDE.md §0 diceva solo la
|
||||
prima, `config/live.json` e il TWR usano la seconda: erano due eventi, non un errore.
|
||||
|
||||
## Punto 5 — PREVDAY-01 aveva la data; non l'aveva la tabella
|
||||
|
||||
`RESULTS-0822` §54: decisione **2027-06-21**, kill, veto. La tabella §4 diceva «scritto 23/08 ·
|
||||
10 condizioni», e il revisore ha letto quella. Ora la riga ha data, kill, veto e stato (5/7:
|
||||
(a) e (b) sono strutturali, nessun forward le cambia).
|
||||
|
||||
Cablato in `paper_prevday.py` (prima esisteva solo nel testo, a differenza di `paper_dvolspread`):
|
||||
|
||||
| misura | valore al 10/09 |
|
||||
|---|---|
|
||||
| Sharpe forward **giornaliero** (lente del kill; l'oraria +1,19 non si cita, §54) | **+0,95** su 81 giorni, 81 attivi |
|
||||
| kill (< −0,50 su ≥180 g attivi) | **NON MATURO**, leggibile dal ~18/12/2026 |
|
||||
| barre ricostruibili dal feed di oggi | **1.942/1.943** (99,9%); l'unica divergente e' il 26/08 00:00, il giorno del fix di `advance()` |
|
||||
| veto | ok |
|
||||
|
||||
⚠️ **Errore mio, catturato prima di committare:** la prima stesura del veto leggeva «divergenze
|
||||
non crescenti» come `tasso_recente > tasso_totale`, e con UNA barra divergente negli ultimi 30
|
||||
giorni (0,03/g contro 0,01/g) dichiarava VETO. Una guardia piu' stretta del contratto produce
|
||||
allarmi che si impara a ignorare (P14): ora la crescita richiede **≥5 barre recenti E tasso
|
||||
doppio**, dichiarato nel codice e provato nel test con i numeri della serie vera.
|
||||
|
||||
La ricostruzione al 99,9% contro il 95,6% di §54: la misura del 23/08 era stata fatta *prima*
|
||||
del fix di `advance()` del 26/08 (le 67 barre divergenti erano «all'ora del cron», cioe' barre
|
||||
parziali); oggi il feed rivisto e la serie coincidono tranne quella barra.
|
||||
|
||||
## Cose trovate per strada
|
||||
|
||||
- Due timestamp «di comodo» scritti a mano (13:05Z e 13:07Z) invece dell'ora letta (12:57:43Z e
|
||||
13:04:09Z): corretti subito. E' esattamente la regola globale «la data si LEGGE» — vale anche per
|
||||
chi la scrive nel campo `correzione`.
|
||||
- `issue --json` va **prima** del sottocomando; il percorso nella regola globale era
|
||||
`/opt/docker/AI-OS` invece di `/opt/AI-OS` (corretto la mattina, nel test dello strumento).
|
||||
|
||||
## Verifica
|
||||
|
||||
`uv run pytest`: **1060 passati** (erano 1031). Nuovi: `test_cuscino_watch.py` (14),
|
||||
`test_paper_prevday_gate.py` (8), +5 in `test_usde_watch.py`. Revisione del diff affidata a
|
||||
`fable` (agente fresco): esito e correzioni applicate in coda a questo diario.
|
||||
|
||||
## Revisione `fable` (13:10-13:18Z): 15 segnalazioni, 4 medie — tutte verificate, 12 applicate
|
||||
|
||||
| # | segnalazione | verifica | esito |
|
||||
|---|---|---|---|
|
||||
| 1 | `puo_riconvertire` contava anche i NON tentativi (`tentata=False`): un giro `--secco` o un prezzo illeggibile bloccava la riconversione vera per 6h | riprodotta | filtro su `tentata` + test |
|
||||
| 2 | il tetto del venue in `usde_convert.piano` bloccava anche la **vendita**: con `venue_cap_frac` rimesso in config la riconversione automatica sarebbe stata morta | riprodotta (`side=sell`, `ok=False`) | vale solo in acquisto |
|
||||
| 3 | `MARGINE_RIPRISTINO` 10% == `PREAVVISO_FRAC` 10%: dopo ogni 🚨 un ⚠️ per costruzione (P14) | riprodotta col floor di 1 USDE | margine **20%** → quota **0,64**, test |
|
||||
| 4 | `--secco` scriveva nel registro vivo (veicolo del punto 1, e maschera un cron fermo) | riprodotta (riga 13:00:15Z) | non scrive; riga tolta |
|
||||
| 5 | due bersagli di quota: `quota_target` 0,70 in config contro 0,64 del ripristino; il ripristino e' un **ratchet verso il basso** | letto | dichiarato in CLAUDE.md §5.17 e nel docstring: decisione dell'operatore (N9) |
|
||||
| 7 | buco di ≤5 min dentro la tolleranza della finestra 24h | letto | dichiarato (D5) + fonte Deribit documentata |
|
||||
| 8 | `giorni_attivi` legge posizioni REAL, il kill e' MODELED | letto | dichiarato nel docstring (81/81 oggi) |
|
||||
| 10 | il test del cron leggeva un commento nello `.sh`, non la crontab | letto | legge `crontab -l` come `test_book_cadenza` |
|
||||
| 11 | `prevday_target` patchato senza `monkeypatch` | letto | `monkeypatch` |
|
||||
| 13 | «QUATTRO movimenti» in §2: sono TRE, in quattro tratti | letto | corretto |
|
||||
| 14 | «5/7» contro «8/10» di §54: base diversa non dichiarata | letto | dichiarata |
|
||||
| 6, 9, 12, 15 | verifiche che reggono (`sys.executable`, `cwd`, `piano()` in SCOPERTO, ricostruzione ≡ `advance`) | — | — |
|
||||
|
||||
Non applicato: il lock fra cron e conversione a mano (DUBBIO 2) — dichiarato come limite nel
|
||||
docstring. Il DUBBIO 1 (ratchet) e' la domanda aperta per l'operatore.
|
||||
|
||||
## Coda (13:55Z →): il riacquisto automatico — issue #7
|
||||
|
||||
Alla domanda aperta (ratchet verso il basso) l'operatore ha risposto «crea una strategia per
|
||||
riportarla a quota in autonomia». La risposta ingenua — ricomprare fino a `quota_target` 0,70 —
|
||||
e' sbagliata per costruzione: a 0,70 lo slack e' zero, la prima ora in perdita vende, il
|
||||
riacquisto ricompra, e cosi' via a 6 bps al giro. Serve **isteresi con un bersaglio unico**:
|
||||
|
||||
| slack / cuscino | stato | azione |
|
||||
|---|---|---|
|
||||
| < 0 | SCOPERTO | 🚨 vende USDE → 0,20 |
|
||||
| 0 – 0,10 | PREAVVISO | ⚠️ |
|
||||
| 0,10 – 0,40 | OK | — |
|
||||
| > 0,40 | **ECCEDENTE** | 📌 compra USDE → 0,20 |
|
||||
|
||||
Bersaglio 0,20 ⇒ quota **0,64**. Per oscillare lo slack deve muoversi di ±0,20×cuscino = ±6%
|
||||
dell'equity, cioe' **±8,6% di equity USDC (~$380)** perche' slack = 0,7·USDC − 0,3·USDE (la prima
|
||||
stesura diceva ±6%/$270: corretta in revisione): a leva 0,26x sono ~33% di mercato. Un bonifico alza lo slack di
|
||||
0,7×importo: sopra ~9% dell'equity (~$380) il riacquisto scatta da solo — la regola di §1
|
||||
«dopo un bonifico si rilancia `usde_convert`» non dipende piu' da qualcuno che se ne ricordi.
|
||||
Le tre frazioni vivono in `config/live.json` (`usde.bande_cuscino` verifica 0 < preavviso <
|
||||
margine < riacquisto), `quota_target` 0,70 e' stato tolto. Soglie dichiarate, non ottimizzate
|
||||
(M8): il costo di un giro e' ~$0,2, la frequenza attesa qualche giro al mese nei tratti mossi.
|
||||
|
||||
📌 **Trovato al primo giro vero del cron (13:53Z): l'allerta PREAVVISO non e' partita.** Il token
|
||||
era valido (`getMe` ok); il testo conteneva «(< 10% del cuscino)» e Telegram in `parse_mode=HTML`
|
||||
rifiuta un `<` nudo. Testi riscritti senza `<`, `allerta_errore` registrato nel record (P3),
|
||||
test che legge il sorgente delle `notify`. Un sorvegliante nuovo che non riesce a parlare al
|
||||
primo giro e' il caso di P2: si valida sul segnale E sul trasporto.
|
||||
|
||||
Stato al giro a secco delle 14:0xZ: quota 69,3%, slack +$30 = PREAVVISO. Il piano a 0,64 e'
|
||||
valido (SELL 237 USDE, slack dopo +$267): il sorvegliante lo esegue alla prima ora sotto zero;
|
||||
allinearlo subito e' una riga a mano.
|
||||
|
||||
### Seconda revisione `fable` (14:04Z): 10 segnalazioni, 1 ALTA, 3 MEDIE — applicate 9
|
||||
|
||||
| # | segnalazione | esito |
|
||||
|---|---|---|
|
||||
| 1 ALTA | il `<` arrivava ancora a Telegram dal campo `motivo` («(< 6h)») dentro l'allerta 🚨: il 🚨 piu' importante perso fino a 4h | escape nel **sink** `notifier.notify` (`html.escape` su titolo e valori; nessuno dei 45 chiamanti passa tag: grep) + motivi senza `<` |
|
||||
| 2 MEDIA | il test sul sorgente era parziale (codice morto, cieco ai valori interpolati) | sostituito: cattura la POST e verifica `<` |
|
||||
| 3 MEDIA | ECCEDENTE con guasto persistente = «acquisto FALLITA» ogni 6h per sempre | da ECCEDENTE dopo un fallimento si riprova ogni 24h; da SCOPERTO resta 6h |
|
||||
| 4 MEDIA | bande fuori ordine ⇒ traceback ingoiato dal cron, nessun record, «fermo» invece di «rotto» | `giudica` ⇒ BLIND con motivo; record e allerta scritti |
|
||||
| 5-7 | CLAUDE.md §1 diceva ancora «nessuno sorveglia»; §5.17 «tre stati»; `_nota_usde` con `quota_target` | corretti |
|
||||
| 8 | «±6% di equity USDC» sbagliato di 0,7: slack = 0,7·USDC − 0,3·USDE ⇒ ±8,6% (~$380) | corretto in 4 posti |
|
||||
| 9 | `TOLL` ridichiarato nel test | importato da `usde_convert` |
|
||||
| 10 | esempi `--quota 0.70` nel docstring di `usde_convert` | 0,64 |
|
||||
|
||||
Verifiche numeriche del revisore (loop su `piano()` con passo 1 USDE, TOLL, min order): bonifico
|
||||
+$2.000 → 11 acquisti → OK, slack +$387; perdita −$50 → 3 vendite → OK, +$264; conto tutto USDC →
|
||||
20 acquisti → OK. **Nessuna sequenza vendita→acquisto→vendita.** Non applicato: `min(q*, cap)` con
|
||||
il tetto del venue (dubbio 3: un conto al tetto resta ECCEDENTE e ora allerta una volta al giorno).
|
||||
Dubbio 4, che vale una riga in §5.17: **0,64 e' una derivazione dell'autore, non la quota decisa
|
||||
dall'operatore il 30/08 (0,70)**; ~$10/anno di resa di differenza; da confermare.
|
||||
|
||||
### Allineamento eseguito (14:16:38Z, ordine dell'operatore «allinea subito a 0.64, esegui»)
|
||||
|
||||
`usde_convert --quota 0.64 --esegui`: SELL 100 + 100 + 38 USDE a 1,0000 medio (limite 0,9995,
|
||||
fill al book), quota 69,4% → **64,02%**, conto USDC $1.602,79 + USDE 2.851,95 = $4.454,74.
|
||||
Slack dopo **+$266** su $1.336 di cuscino: `cuscino_watch --secco` dice **OK** (era PREAVVISO a
|
||||
+$29). Costo: ~$0,1 di spread, fee zero. Registro in `data/live/usde_convert.jsonl`.
|
||||
@@ -0,0 +1,52 @@
|
||||
# Risk-off ±2h intorno a FOMC e CPI su TP01 — REFUTED, e il segno e' l'opposto
|
||||
|
||||
**Data:** 2026-09-10 14:52-14:55Z · **Script:** `scripts/research/r0910_macro_riskoff.py` (N11: il
|
||||
verdetto lo calcola lo script) · **Issue:** #9 · **Origine:** domanda dell'operatore da un articolo su
|
||||
EA forex a griglia con «agente fondamentale» che approva/rifiuta i segnali intorno ai dati macro.
|
||||
|
||||
## Cosa si e' misurato
|
||||
|
||||
TP01 canonico (posizione decisa alla chiusura giornaliera, tenuta 24 barre 1h), 2019-03 → 2026-09,
|
||||
65.676 ore. Variante RISK-OFF: esposizione zero nelle barre che intersecano [t−2h, t+2h] intorno a
|
||||
**61 dichiarazioni FOMC** (14:00 ET, piu' le due d'emergenza di marzo 2020, notation vote esclusi) e
|
||||
**88 release CPI** (08:30 ET), con fee di chiusura e riapertura. Calendario **letto dal web** (Fed e
|
||||
BLS), non ricordato; l'archivio BLS raggruppa per anno di riferimento e le release di gennaio/febbraio
|
||||
vanno spostate di +1 anno (verificato sulla schedule 2026). 684 ore in finestra = **1,04%** del tempo.
|
||||
|
||||
Verdetto pre-registrato prima del primo numero: LEAD solo se (a) ΔSharpe con fee > 0, (b) Δ al ≥95°
|
||||
percentile del null location-matched (stessi conteggi per anno, stesse ore del giorno, giorni a caso,
|
||||
1.000 estrazioni — M18), (c) Δ > 0 in ≥70% degli anni (M9).
|
||||
|
||||
## Numeri
|
||||
|
||||
| | Sharpe giornaliero | drift/anno |
|
||||
|---|---|---|
|
||||
| TP01 base | 1,302 | +15,73% |
|
||||
| risk-off con fee | 1,205 | +14,49% |
|
||||
| risk-off senza fee | 1,228 | +14,77% |
|
||||
| **Δ** | **−0,097** | **−1,24%** ≈ **−0,14 €/giorno** a $4.455 |
|
||||
|
||||
Per tipo: FOMC ΔSh −0,012 (P&L rinunciato +0,79%), **CPI −0,084 (+6,36%)**. Per anno: Δ negativo in
|
||||
6/8, positivo solo 2024 e 2026 (+0,02/+0,03).
|
||||
|
||||
Null location-matched: ΔSh mediana −0,033 [p05 −0,095, p95 +0,027]. **Il Δ vero sta al 4,2°
|
||||
percentile**: le finestre vere sono fra le PEGGIORI da appiattire, cioe' fra le migliori in cui
|
||||
essere esposti. Dentro la finestra TP01 fa **+1,05 bp/ora contro +0,17 fuori** (t = 1,57 su 684 ore).
|
||||
|
||||
(a) FAIL · (b) FAIL · (c) 2/8 FAIL ⇒ **REFUTED**.
|
||||
|
||||
## Lettura
|
||||
|
||||
1. **Il risk-off toglie P&L, non rischio.** TP01 e' long-flat vol-targeted: nelle ore dei dati macro
|
||||
il trend che sta cavalcando accelera piu' spesso di quanto si rovesci, e l'1% del tempo in finestra
|
||||
porta il 7% del guadagno cumulato. E' la stessa lezione del weekend (§3, 17/07: il 38% del gross
|
||||
nel 31% del tempo): **la coda del trend vive nelle ore «pericolose»**.
|
||||
2. **Il segno opposto non e' un lead.** «Esporsi DI PIU' intorno ai dati» sarebbe un'ipotesi nuova
|
||||
(M12), con t = 1,57 e 684 ore: sotto qualunque MDE, e la selezione sull'esito la vieta. Non si apre.
|
||||
3. Il gate «fondamentale» dell'articolo, applicato al nostro libro, costerebbe **−1,24%/anno di drift**
|
||||
— la stessa taglia del funding non modellato (−2,16%), ma con segno scelto da chi lo propone.
|
||||
4. Coerente con `macro-regime-gate` (29/06, ridondante col trend) e con la conclusione di §3: **ogni
|
||||
risk-off parte REFUTED** e questo lo e' rimasto.
|
||||
|
||||
Non modellato (dichiarato): funding (identico nelle due varianti salvo l'1% delle ore); slippage
|
||||
della finestra (rende il risk-off *piu'* costoso, non meno). Costo: 3 minuti di calcolo, 0 ordini.
|
||||
@@ -0,0 +1,58 @@
|
||||
# Giornale di bordo — 2026-08-26
|
||||
|
||||
*Scritto 2026-08-27T00:37:47+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $79,031.00 | +0.67% | +14.00% | +24.05% | 42.0% | ↑ ↑ ↑ (3/3 su) | 40.4 (40° pctl 1a) |
|
||||
| **ETH** | $2,507.65 | +2.64% | +11.34% | +32.61% | 67.8% | ↑ ↑ ↑ (3/3 su) | 56.1 (41° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$2,065.45** · nozionale lordo $582 · leva lorda 0.28x · barra dati 2026-08-26 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.464 | flat | $+359 | $+364 | SELL $-5 |
|
||||
| **ETH** | +0.278 | flat | $+215 | $+218 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$+8.86** di equity (24 letture)
|
||||
- realizzato: +0.29 netto su 2 round-trip chiusi · 4 fill · fee 0.0138
|
||||
- cumulato dall'arming: **$+1,467.39** di equity — di cui $+1,399.39 versati/prelevati -> trading **$+68.00**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+364 (TP01 +0.464, SKH01 flat) e ETH $+218 (TP01 +0.278, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.28x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.72x.
|
||||
- `[pnl]` Equity $+8.86 con 4 fill e 2 round-trip chiusi (realizzato $+0.29 netto).
|
||||
- `[concentrazione]` 📌 **Il 101% di tutto il P&L cumulato ($+68.00) viene dagli ultimi 7 giorni.** Oltre il 100% perche' **il periodo precedente era in perdita**: senza questi 7 giorni il libro sarebbe sotto. Un risultato concentrato in una finestra non e' un tasso di rendimento: e' un evento.
|
||||
- `[vol]` BTC: implicita 40.4 contro realizzata 30g 42.0 (sotto); DVOL al 40° pctl di un anno; IV-rank espandente 0.14 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 56.1 contro realizzata 30g 67.8 (sotto); DVOL al 41° pctl di un anno; IV-rank espandente 0.17 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **64 giorni, 22 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-08-27T00:38:49+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
Il fatto nuovo non sta nei numeri di oggi: e' il denominatore, cambiato ieri. L'equity passa da 642.02 (24/08) a 2,056.59 (25/08) a $2,065.45, ma il nozionale lordo resta $582 e le posizioni si muovono appena — BTC da $+345 a $+364, ETH da $+212 a $+218. La leva 0.28x su un tetto di 1.00x non e' quindi confrontabile con quella dei giorni precedenti: stessa esposizione, denominatore molto piu' grande. Chi rilegge fra mesi guardi $+1,399.39 versati e $+68.00 di trading prima di guardare l'equity.
|
||||
|
||||
La settimana che porta il 101% del cumulato e' la stessa in cui BTC ha fatto +14.00% e ETH +11.34%, TSMOM 3/3 su entrambi e il libro lungo su entrambe le gambe. «L'edge ha lavorato» e «un trend-following era lungo mentre il mercato saliva» spiegano quel dato ugualmente bene, e a 64 giorni e 22 round-trip non si separano. Anche l'$+8.86 di oggi e' quasi tutto rivalutazione: il realizzato e' $+0.29 su 2 round-trip, in un giorno da +0.67% e +2.64%.
|
||||
|
||||
Due cose insolite: SKH01 e' flat su entrambi gli asset dopo un mese da +24.05% e +32.61%, quindi il rischio e' tutto TP01; e l'implicita sta sotto la realizzata su entrambi (40.4 contro 42.0, 56.1 contro 67.8), con DVOL al 40° e 41° percentile.
|
||||
|
||||
Da controllare a mano: 4 fill in una giornata che chiude con un solo SELL $-5. Cosa sia successo fra i 24 giri non e' misurabile qui.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,60 @@
|
||||
# Giornale di bordo — 2026-08-27
|
||||
|
||||
*Scritto 2026-08-28T08:28:09+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $80,266.00 | +1.56% | +9.94% | +25.66% | 42.1% | ↑ ↑ ↑ (3/3 su) | 41.5 (47° pctl 1a) |
|
||||
| **ETH** | $2,511.75 | +0.16% | +7.96% | +30.79% | 67.9% | ↑ ↑ ↑ (3/3 su) | 57.1 (45° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$2,070.23** · nozionale lordo $573 · leva lorda 0.28x · barra dati 2026-08-27 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.463 | flat | $+359 | $+361 | HOLD (a target) |
|
||||
| **ETH** | +0.277 | flat | $+215 | $+212 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$+4.78** di equity (24 letture)
|
||||
- realizzato: +0.13 netto su 1 round-trip chiusi · 1 fill · fee 0.0019
|
||||
- cumulato dall'arming: **$+1,472.17** di equity — di cui $+1,399.39 versati/prelevati -> trading **$+72.78**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+361 (TP01 +0.463, SKH01 flat) e ETH $+212 (TP01 +0.277, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.28x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.72x.
|
||||
- `[pnl]` Equity $+4.78 con 1 fill e 1 round-trip chiusi (realizzato $+0.13 netto).
|
||||
- `[concentrazione]` 📌 **Il 82% di tutto il P&L cumulato ($+72.78) viene dagli ultimi 7 giorni.** Un risultato concentrato in una finestra non e' un tasso di rendimento: e' un evento.
|
||||
- `[vol]` BTC: implicita 41.5 contro realizzata 30g 42.1 (sotto); DVOL al 47° pctl di un anno; IV-rank espandente 0.16 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 57.1 contro realizzata 30g 67.9 (sotto); DVOL al 45° pctl di un anno; IV-rank espandente 0.19 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **65 giorni, 23 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-08-28T08:29:00+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
Il fatto nuovo della settimana non sta nel P&L ma nella colonna equity: il salto del 25/08 da $642.02 a $2,056.59 è capitale entrato, non performance. Dei $+1,472.17 cumulati dall'arming, $+1,399.39 sono versamenti e solo $+72.78 è trading. Chi rileggerà la curva fra mesi non la legga come rendimento.
|
||||
|
||||
Merita un occhio umano il fatto che l'equity sia quasi triplicata ma il nozionale no: BTC $+361 / ETH $+212 oggi, contro $+345/$+212 del 25/08 e $+364/$+218 del 26/08 — praticamente fermi, con leva lorda scesa a 0.28x su un tetto di 1.00x. Se il target segua l'equity o l'intensità del segnale (TP01 +0.463 e +0.277) da questa pagina non è determinabile.
|
||||
|
||||
Il rischio è tutto su TP01: SKH01 flat su entrambe le gambe. Giornata di attività minima, 1 fill e 1 round-trip, $+4.78 di equity; quanto di quel numero sia mark-to-market delle gambe non è scomponibile qui.
|
||||
|
||||
Lettura alternativa della concentrazione: l'82% del cumulato sta negli ultimi 7 giorni, che sono anche i 7 giorni di BTC +9.94% ed ETH +7.96%. Un long-flat che guadagna in una settimana di trend sta facendo il suo mestiere; a 65 giorni e 23 round-trip non è separabile da un buon tratto di mercato.
|
||||
|
||||
Salute pulita: 24/24 giri, feed a 0 min. Su entrambi gli asset l'implicita sta sotto la realizzata, ma il gate di VRP01 blocca su un altro asse.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,58 @@
|
||||
# Giornale di bordo — 2026-08-28
|
||||
|
||||
*Scritto 2026-08-29T00:36:42+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $77,850.00 | -3.01% | -0.64% | +21.77% | 44.1% | ↑ ↑ ↑ (3/3 su) | 38.3 (25° pctl 1a) |
|
||||
| **ETH** | $2,443.35 | -2.72% | -2.93% | +27.98% | 68.9% | ↑ ↑ ↑ (3/3 su) | 50.9 (16° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$2,052.36** · nozionale lordo $571 · leva lorda 0.28x · barra dati 2026-08-28 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.464 | flat | $+357 | $+357 | HOLD (a target) |
|
||||
| **ETH** | +0.278 | flat | $+214 | $+214 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$-17.87** di equity (24 letture)
|
||||
- realizzato: +0.00 netto su 0 round-trip chiusi · 2 fill · fee 0.0054
|
||||
- cumulato dall'arming: **$+1,454.30** di equity — di cui $+1,399.39 versati/prelevati -> trading **$+54.91**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+357 (TP01 +0.464, SKH01 flat) e ETH $+214 (TP01 +0.278, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.28x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.72x.
|
||||
- `[pnl]` Equity $-17.87 con 2 fill e 0 round-trip chiusi (realizzato $+0.00 netto).
|
||||
- `[drawdown]` 📌 Equity **-1.02%** sotto il picco ($2,073.59). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
|
||||
- `[vol]` BTC: implicita 38.3 contro realizzata 30g 44.1 (sotto); DVOL al 25° pctl di un anno; IV-rank espandente 0.09 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 50.9 contro realizzata 30g 68.9 (sotto); DVOL al 16° pctl di un anno; IV-rank espandente 0.12 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **66 giorni, 23 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-08-29T00:37:39+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
Prima giornata in rosso dal versamento: dopo +8.86 e +4.78, l'equity fa $-17.87 con zero round-trip chiusi. Il libro non ha deciso niente — BTC $+357 e ETH $+214 restano a target, dove erano ieri ($+361 e $+212) — quindi la perdita e' marcatura di mercato sul giorno a -3.01% BTC e -2.72% ETH, non esecuzione; e con SKH01 flat su entrambi e' esposizione interamente TP01. I 2 fill con fee 0.0054 sono aggiustamenti: se siano ritocchi al target o altro, non e' distinguibile qui. Salute pulita (24/24 giri, feed a 0 min), quindi non spiega nulla della giornata.
|
||||
|
||||
Cio' che e' cambiato davvero e' la scala. Dal 25/08 l'equity e' $2,052.36, e il movimento di un solo giorno ($-17.87) e' dello stesso ordine dei $+54.91 di trading accumulati dall'arming in 66 giorni. Il P&L giornaliero e' ora piu' rumoroso della cosa che dovrebbe misurare, mentre i round-trip restano 23.
|
||||
|
||||
Due letture reggono ugualmente sugli stessi numeri: TSMOM 3/3 su entrambi con 30 giorni a +21.77% e +27.98% dice trend intatto e ritracciamento; i 7 giorni (-0.64% e -2.93%) dicono che la spinta si era gia' fermata e oggi se ne vede la parte visibile. Con questi dati non si sceglie.
|
||||
|
||||
Merita un'occhiata umana il picco $2,073.59: non compare fra le chiusure giornaliere, e il -1.02% e' misurato contro quella lettura oraria.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,58 @@
|
||||
# Giornale di bordo — 2026-08-29
|
||||
|
||||
*Scritto 2026-08-30T00:37:12+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $78,243.00 | +0.50% | +1.48% | +20.85% | 44.0% | ↑ ↑ ↑ (3/3 su) | 37.4 (19° pctl 1a) |
|
||||
| **ETH** | $2,458.25 | +0.61% | +1.45% | +28.20% | 68.9% | ↑ ↑ ↑ (3/3 su) | 51.1 (18° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$2,056.87** · nozionale lordo $560 · leva lorda 0.27x · barra dati 2026-08-29 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.444 | flat | $+342 | $+344 | HOLD (a target) |
|
||||
| **ETH** | +0.273 | flat | $+211 | $+216 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$+4.51** di equity (24 letture)
|
||||
- realizzato: +0.02 netto su 1 round-trip chiusi · 1 fill · fee 0.0054
|
||||
- cumulato dall'arming: **$+1,458.81** di equity — di cui $+1,399.39 versati/prelevati -> trading **$+59.42**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+344 (TP01 +0.444, SKH01 flat) e ETH $+216 (TP01 +0.273, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.27x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.73x.
|
||||
- `[pnl]` Equity $+4.51 con 1 fill e 1 round-trip chiusi (realizzato $+0.02 netto).
|
||||
- `[drawdown]` 📌 Equity **-0.81%** sotto il picco ($2,073.59). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
|
||||
- `[vol]` BTC: implicita 37.4 contro realizzata 30g 44.0 (sotto); DVOL al 19° pctl di un anno; IV-rank espandente 0.07 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 51.1 contro realizzata 30g 68.9 (sotto); DVOL al 18° pctl di un anno; IV-rank espandente 0.12 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **67 giorni, 24 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-08-30T00:38:12+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
Giornata silenziosa: 1 fill, 1 round-trip, entrambe le gambe a target e posizioni ferme dal 25/08 ($+344 su BTC e $+216 su ETH, contro $+345 e $+212). Cio' che e' cambiato non e' il libro ma il denominatore: nozionale lordo $560 con equity $2,056.87 contro i $642.02 del 24/08, e leva lorda 0.27x su un tetto di 1.00x. Il capitale nuovo non ha mosso l'esposizione.
|
||||
|
||||
L'insolito sta nel mercato: TSMOM 3/3 su entrambi gli asset e +20.85% / +28.20% a 30 giorni, con l'implicita al 19° e 18° percentile dell'anno e sotto la realizzata (37.4 contro 44.0; 51.1 contro 68.9). Un rally con implicita compressa; VRP01 resta fermo per costruzione, IV-rank 0.07 e 0.12 sotto 0.30.
|
||||
|
||||
Lettura alternativa altrettanto compatibile con gli stessi numeri: il $+4.51 di oggi e il $-17.87 di ieri, a posizioni ferme, sono marcature di prezzo piu' che decisioni — il realizzato vale $+0.02. Su 67 giorni e 24 round-trip, dei $+1,458.81 cumulati il trading e' $+59.42.
|
||||
|
||||
Merita un occhio umano il drawdown: -0.81% dal picco $2,073.59 e' misurato su una serie che il 25/08 e' saltata di $+1,414.57, quindi ha pochi giorni di storia alla scala attuale. Da quanto SKH01 sia flat su entrambe le gambe non e' misurabile qui. Salute pulita: 24/24 giri, feed a 0 min.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,58 @@
|
||||
# Giornale di bordo — 2026-08-30
|
||||
|
||||
*Scritto 2026-08-31T00:36:38+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $77,702.00 | -0.69% | -0.06% | +23.66% | 42.3% | ↑ ↑ ↑ (3/3 su) | 37.0 (15° pctl 1a) |
|
||||
| **ETH** | $2,417.45 | -1.66% | -1.89% | +29.87% | 68.1% | ↑ ↑ ↑ (3/3 su) | 51.8 (20° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$2,047.39** · nozionale lordo $553 · leva lorda 0.27x · barra dati 2026-08-30 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.463 | flat | $+355 | $+348 | BUY $+8 |
|
||||
| **ETH** | +0.278 | flat | $+214 | $+205 | BUY $+9 |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$-9.48** di equity (24 letture)
|
||||
- realizzato: -0.08 netto su 2 round-trip chiusi · 5 fill · fee 0.0164
|
||||
- cumulato dall'arming: **$+1,449.33** di equity — di cui $+1,399.39 versati/prelevati -> trading **$+49.94**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+348 (TP01 +0.463, SKH01 flat) e ETH $+205 (TP01 +0.278, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.27x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.73x.
|
||||
- `[pnl]` Equity $-9.48 con 5 fill e 2 round-trip chiusi (realizzato $-0.08 netto).
|
||||
- `[drawdown]` 📌 Equity **-1.26%** sotto il picco ($2,073.59). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
|
||||
- `[vol]` BTC: implicita 37.0 contro realizzata 30g 42.3 (sotto); DVOL al 15° pctl di un anno; IV-rank espandente 0.06 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 51.8 contro realizzata 30g 68.1 (sotto); DVOL al 20° pctl di un anno; IV-rank espandente 0.13 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **68 giorni, 26 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-08-31T00:37:31+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
Il fatto nuovo non è la perdita ma la sua composizione: $-9.48 di equity contro $-0.08 di realizzato su 2 round-trip. Quasi tutto è mark-to-market su due long in una giornata con BTC -0.69% ed ETH -1.66%; una lettura altrettanto compatibile è che il libro stia semplicemente respirando col beta dei due asset, senza che il segnale abbia detto nulla. È il terzo segno meno da quando l'elenco comincia il 20/08, dopo il 22/08 ($-9.00) e il 28/08 ($-17.87). Insolito è invece il conto dei fill: cinque, con fee 0.0164, per spostare $+8 su BTC e $+9 su ETH, contro l'uno o due dei giorni scorsi. Vale un'occhiata umana a quale giro li abbia generati.
|
||||
|
||||
La posizione ETH scende da $+216 a $+205 mentre BTC sale da $+344 a $+348: se a muoverla sia il segnale TP01 o il prezzo non è separabile da questa pagina.
|
||||
|
||||
L'anomalia di mercato sta nella vol: +23.66% a 30 giorni su BTC e +29.87% su ETH, con DVOL al 15° e al 20° percentile dell'anno e implicita sotto la realizzata su entrambi. Il rally è arrivato senza che le opzioni lo prezzassero.
|
||||
|
||||
Il trading resta $+49.94 su 68 giorni e 26 round-trip: troppo poco per essere altro che rumore.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,56 @@
|
||||
# Giornale di bordo — 2026-08-31
|
||||
|
||||
*Scritto 2026-09-01T00:36:58+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $78,564.00 | +1.11% | -0.53% | +25.15% | 42.3% | ↑ ↑ ↑ (3/3 su) | 37.6 (21° pctl 1a) |
|
||||
| **ETH** | $2,467.80 | +2.08% | -0.62% | +33.88% | 67.9% | ↑ ↑ ↑ (3/3 su) | 51.3 (19° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$2,059.45** · nozionale lordo $569 · leva lorda 0.28x · barra dati 2026-08-31 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.461 | flat | $+356 | $+354 | HOLD (a target) |
|
||||
| **ETH** | +0.278 | flat | $+214 | $+215 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$+12.06** di equity (24 letture)
|
||||
- realizzato: -0.48 netto su 3 round-trip chiusi · 4 fill · fee 0.0100
|
||||
- cumulato dall'arming: **$+1,461.39** di equity — di cui $+1,399.39 versati/prelevati -> trading **$+62.00**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+354 (TP01 +0.461, SKH01 flat) e ETH $+215 (TP01 +0.278, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.28x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.72x.
|
||||
- `[pnl]` Equity $+12.06 con 4 fill e 3 round-trip chiusi (realizzato $-0.48 netto).
|
||||
- `[drawdown]` 📌 Equity **-0.68%** sotto il picco ($2,073.59). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
|
||||
- `[vol]` BTC: implicita 37.6 contro realizzata 30g 42.3 (sotto); DVOL al 21° pctl di un anno; IV-rank espandente 0.07 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 51.3 contro realizzata 30g 67.9 (sotto); DVOL al 19° pctl di un anno; IV-rank espandente 0.12 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **69 giorni, 29 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-09-01T00:38:01+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
Il $+12.06 di oggi non nasce da una decisione: le posizioni sono rimaste a target (BTC $+354 contro $+356, ETH $+215 contro $+214) e i 3 round-trip chiusi hanno realizzato $-0.48. Il guadagno e' marcatura di due long in una giornata di BTC +1.11% e ETH +2.08%; la lettura alternativa altrettanto compatibile e' che il libro sia stato semplicemente beta lungo, e in pagina non c'e' nulla che separi le due cose.
|
||||
|
||||
E' il delta piu' grande dal 25/08, che pero' era il versamento: un solo giorno pesa quindi molto sui $+62.00 di trading cumulato dall'arming, su un campione di 69 giorni e 29 round-trip, tre dei quali chiusi oggi. Rispetto a -17.87 del 28/08 e -9.48 del 30/08 il segno cambia, ma le posizioni sono ferme dal 25/08 fra $+344 e $+364 su BTC e fra $+205 e $+218 su ETH: si muove il prezzo, non il libro.
|
||||
|
||||
Insolito, e non confrontabile coi giorni precedenti che la volatilita' non la riportano: implicita sotto realizzata su entrambi (37.6 contro 42.3, 51.3 contro 67.9) con DVOL al 21° e 19° percentile dell'anno. Merita un'occhiata che siano serviti 4 fill per lasciare le posizioni dov'erano, e che il picco $2,073.59 da cui si misura il -0.68% non compaia in nessuna chiusura giornaliera.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,58 @@
|
||||
# Giornale di bordo — 2026-09-01
|
||||
|
||||
*Scritto 2026-09-02T00:36:47+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $77,432.50 | -1.44% | -1.36% | +21.89% | 42.9% | ↑ ↑ ↑ (3/3 su) | 37.9 (23° pctl 1a) |
|
||||
| **ETH** | $2,418.40 | -2.00% | -1.02% | +28.37% | 68.5% | ↑ ↑ ↑ (3/3 su) | 51.8 (21° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$2,050.77** · nozionale lordo $568 · leva lorda 0.28x · barra dati 2026-09-01 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.461 | flat | $+355 | $+357 | HOLD (a target) |
|
||||
| **ETH** | +0.278 | flat | $+213 | $+211 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$-8.68** di equity (24 letture)
|
||||
- realizzato: +0.00 netto su 0 round-trip chiusi · 1 fill · fee 0.0027
|
||||
- cumulato dall'arming: **$+1,452.71** di equity — di cui $+1,399.39 versati/prelevati -> trading **$+53.32**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+357 (TP01 +0.461, SKH01 flat) e ETH $+211 (TP01 +0.278, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.28x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.72x.
|
||||
- `[pnl]` Equity $-8.68 con 1 fill e 0 round-trip chiusi (realizzato $+0.00 netto).
|
||||
- `[drawdown]` 📌 Equity **-1.10%** sotto il picco ($2,073.59). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
|
||||
- `[vol]` BTC: implicita 37.9 contro realizzata 30g 42.9 (sotto); DVOL al 23° pctl di un anno; IV-rank espandente 0.08 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 51.8 contro realizzata 30g 68.5 (sotto); DVOL al 21° pctl di un anno; IV-rank espandente 0.13 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **70 giorni, 29 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-09-02T00:37:44+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
La giornata non contiene decisioni: 1 fill, 0 round-trip chiusi, realizzato $+0.00, entrambe le gambe HOLD a target. Gli $-8.68 sono quindi marcatura di posizioni ferme, non esito di operazioni — ed e' la prima giornata senza alcun round-trip chiuso dal 28/08, dopo il 30/08 e il 31/08 che ne avevano chiusi 2 e 3 con 5 e 4 fill. Anche le posizioni cambiano poco: BTC $+357 ed ETH $+211 restano dentro la banda tenuta dal 26/08 (BTC $+344–364, ETH $+205–218), con leva 0.28x molto sotto l'1.00x.
|
||||
|
||||
Cio' che le regole non vedono: il rosso arriva con BTC −1.44% ed ETH −2.00% mentre i tre TSMOM restano 3/3 su e i 30 giorni segnano +21.89% e +28.37%. Lo stesso dato regge due letture — rumore dentro un mese in tendenza, oppure l'avvio di un rientro — e da qui non sono distinguibili: non misurabile con una giornata.
|
||||
|
||||
Insolito il quadro volatilita': implicita sotto la realizzata su entrambi (37.9 contro 42.9; 51.8 contro 68.5) e DVOL al 23°/21° percentile, quindi VRP01 resta fermo per l'IV-rank, non per il livello.
|
||||
|
||||
Vale un'occhiata umana il fatto che il trading cumulato, $+53.32 in 70 giorni, sia della stessa taglia delle singole oscillazioni giornaliere recenti (−17.87 il 28/08, +12.06 il 31/08).
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,60 @@
|
||||
# Giornale di bordo — 2026-09-02
|
||||
|
||||
*Scritto 2026-09-03T00:36:49+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $77,312.50 | -0.15% | -2.17% | +21.78% | 42.9% | ↑ ↑ ↑ (3/3 su) | 37.2 (16° pctl 1a) |
|
||||
| **ETH** | $2,391.55 | -1.11% | -4.63% | +28.63% | 68.5% | ↑ ↑ ↑ (3/3 su) | 50.9 (17° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$2,045.82** · nozionale lordo $560 · leva lorda 0.27x · barra dati 2026-09-02 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.454 | flat | $+348 | $+347 | HOLD (a target) |
|
||||
| **ETH** | +0.276 | flat | $+212 | $+213 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$-4.95** di equity (24 letture)
|
||||
- realizzato: -0.35 netto su 1 round-trip chiusi · 2 fill · fee 0.0045
|
||||
- cumulato dall'arming: **$+1,447.76** di equity — di cui $+1,399.39 versati/prelevati -> trading **$+48.37** · TWR **+10.47%**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+347 (TP01 +0.454, SKH01 flat) e ETH $+213 (TP01 +0.276, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.27x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.73x.
|
||||
- `[pnl]` Equity $-4.95 con 2 fill e 1 round-trip chiusi (realizzato $-0.35 netto).
|
||||
- `[drawdown]` 📌 Equity **-1.34%** sotto il picco ($2,073.59). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
|
||||
- `[vol]` BTC: implicita 37.2 contro realizzata 30g 42.9 (sotto); DVOL al 16° pctl di un anno; IV-rank espandente 0.06 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 50.9 contro realizzata 30g 68.5 (sotto); DVOL al 17° pctl di un anno; IV-rank espandente 0.12 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **71 giorni, 30 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-09-03T00:37:49+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
Il fatto nuovo e' l'equity a $2,045.82: sotto ogni chiusura registrata dopo il versamento del 25/08, dove il minimo era $2,047.39 del 30/08. Non viene dal trading — realizzato $-0.35 su un round-trip, fee 0.0045, due fill — ma dal prezzo su due posizioni tenute a target per tutte le 24 letture.
|
||||
|
||||
Il picco da cui si misura il -1.34% ($2,073.59) non compare fra le chiusure giornaliere: e' una lettura oraria, quindi quel drawdown vive su una griglia piu' fine della tabella dei giorni.
|
||||
|
||||
La tensione del giorno e' fra le scale: TSMOM 3/3 su su entrambi gli asset, ma 1g e 7g negativi (BTC -2.17%, ETH -4.63% a 7g) contro 30g ancora +21.78% e +28.63%. Il libro resta long perche' la gamba lunga tiene; una lettura altrettanto compatibile e' che i 30g stiano semplicemente restituendo il rialzo accumulato. Quale delle due, non e' misurabile qui.
|
||||
|
||||
Insolito il pacchetto volatilita': implicita sotto la realizzata su entrambi (37.2 contro 42.9, 50.9 contro 68.5) con DVOL al 16° e 17° percentile dell'anno, mentre il gate di VRP01 legge l'IV-rank (0.06 e 0.12) e resta fermo — guarda una cosa diversa da quello scarto.
|
||||
|
||||
Meriterebbe un occhio umano il perche' il $-4.95 arrivi con salute perfetta, 24/24 giri e feed a 0 min. Il campione resta 71 giorni e 30 round-trip, con $+48.37 di trading sui $+1,447.76 cumulati.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,58 @@
|
||||
# Giornale di bordo — 2026-09-03
|
||||
|
||||
*Scritto 2026-09-04T00:37:16+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $81,286.00 | +5.14% | +1.27% | +26.90% | 45.5% | ↑ ↑ ↑ (3/3 su) | 39.8 (38° pctl 1a) |
|
||||
| **ETH** | $2,507.90 | +4.87% | -0.15% | +34.20% | 69.8% | ↑ ↑ ↑ (3/3 su) | 53.3 (27° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$2,074.42** · nozionale lordo $562 · leva lorda 0.27x · barra dati 2026-09-03 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.453 | flat | $+352 | $+349 | HOLD (a target) |
|
||||
| **ETH** | +0.274 | flat | $+213 | $+213 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$+28.60** di equity (24 letture)
|
||||
- realizzato: -0.14 netto su 4 round-trip chiusi · 4 fill · fee 0.0095
|
||||
- cumulato dall'arming: **$+1,476.36** di equity — di cui $+1,399.39 versati/prelevati -> trading **$+76.97** · TWR **+12.02%**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+349 (TP01 +0.453, SKH01 flat) e ETH $+213 (TP01 +0.274, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.27x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.73x.
|
||||
- `[pnl]` Equity $+28.60 con 4 fill e 4 round-trip chiusi (realizzato $-0.14 netto).
|
||||
- `[vol]` BTC: implicita 39.8 contro realizzata 30g 45.5 (sotto); DVOL al 38° pctl di un anno; IV-rank espandente 0.14 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 53.3 contro realizzata 30g 69.8 (sotto); DVOL al 27° pctl di un anno; IV-rank espandente 0.14 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evento]` 📌 BTC: giornata a +5.14%, **2.2 deviazioni** giornaliere (sd implicita dalla RV30 = 2.38%).
|
||||
- `[evidenza]` 📌 Campione a oggi: **72 giorni, 34 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-09-04T00:38:30+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
Il fatto nuovo non e' il segno, e' la taglia: $+28.60 e un'equity a $2,074.42, la piu' alta delle dieci sedute mostrate, dopo giorni in cui il delta si e' mosso fra −17.87 e +12.06. Arriva da un BTC a +5.14%, 2.2 deviazioni, mentre il libro era gia' a target su entrambe le gambe: con nozionale lordo $562 e leva 0.27x, l'aumento e' rivalutazione di una posizione ferma, non l'esito di una decisione presa oggi. Il realizzato lo conferma — −0.14 netto su 4 round-trip, rumore di fee (0.0095) — e le posizioni restano $+349 e $+213, in linea con le sedute precedenti.
|
||||
|
||||
Insolito il quadro vol: entrambe le implicite sotto la realizzata 30g (39.8 contro 45.5, 53.3 contro 69.8), coi DVOL al 38° e 27° percentile dell'anno, in una giornata a due deviazioni. Non misurabile qui se sia salita la realizzata oggi o se l'implicita non abbia seguito.
|
||||
|
||||
Lettura alternativa, ugualmente compatibile: BTC fa +5.14% in un giorno ma +1.27% in sette, ed ETH −0.15%; buona parte del guadagno e' recupero dentro la settimana, non nuova direzione, benche' TSMOM sia 3/3 su su entrambi.
|
||||
|
||||
Un solo giorno pesa molto su un trading da $+76.97 in 72 giorni e 34 round-trip. Varrebbe un controllo umano sui 4 fill di una giornata chiusa a target: dovrebbero essere ri-taglie.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,58 @@
|
||||
# Giornale di bordo — 2026-09-04
|
||||
|
||||
*Scritto 2026-09-05T00:36:37+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $79,660.50 | -2.00% | +2.33% | +23.30% | 46.6% | ↑ ↑ ↑ (3/3 su) | 38.0 (24° pctl 1a) |
|
||||
| **ETH** | $2,457.05 | -2.03% | +0.56% | +28.81% | 70.5% | ↑ ↑ ↑ (3/3 su) | 51.1 (18° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$4,483.73** · nozionale lordo $1,173 · leva lorda 0.26x · barra dati 2026-09-04 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.428 | flat | $+719 | $+717 | HOLD (a target) |
|
||||
| **ETH** | +0.270 | flat | $+455 | $+456 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$+2,409.31** di equity (24 letture) — di cui **$+2,410.14 movimenti di capitale** -> trading **$-0.83**
|
||||
- realizzato: -0.02 netto su 2 round-trip chiusi · 5 fill · fee 0.2328
|
||||
- cumulato dall'arming: **$+3,885.67** di equity — di cui $+3,809.53 versati/prelevati -> trading **$+76.14** · TWR **+11.70%**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[movimento]` 📌 **Movimento di capitale $+2,410.14** fra le 13:47 e le 14:47 UTC: salto +116.8% contro un massimo spiegabile dal mercato di ±0.6% al tetto di leva. Scorporato dal P&L di trading qui sotto.
|
||||
- `[stato]` A mercato su BTC $+717 (TP01 +0.428, SKH01 flat) e ETH $+456 (TP01 +0.270, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.26x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.74x.
|
||||
- `[pnl]` P&L di trading $-0.83 (equity $+2409.31 al lordo del movimento di capitale) con 5 fill e 2 round-trip chiusi (realizzato $-0.02 netto).
|
||||
- `[vol]` BTC: implicita 38.0 contro realizzata 30g 46.6 (sotto); DVOL al 24° pctl di un anno; IV-rank espandente 0.08 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 51.1 contro realizzata 30g 70.5 (sotto); DVOL al 18° pctl di un anno; IV-rank espandente 0.12 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **73 giorni, 36 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-09-05T00:37:58+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
Il fatto della giornata non è il mercato ma la cassa: l'equity passa da $2,074.42 a $4,483.73, e il salto di $+2,410.14 fra le 13:47 e le 14:47 UTC è il secondo dopo il delta +1,414.57 del 25/08. Il libro l'ha assorbito subito: le posizioni, ferme per dieci giorni fra $+344 e $+364 su BTC e fra $+205 e $+218 su ETH, oggi valgono $+717 e $+456, entrambe a target, a leva lorda 0.26x su un tetto di 1.00x. Nessuna scelta discrezionale: TP01 +0.428 e +0.270, SKH01 flat.
|
||||
|
||||
Il numero da maneggiare con cura è il $-0.83 di trading. BTC e ETH chiudono a -2.00% e -2.03% con $1,173 di nozionale lordo lungo: l'intervallo che contiene il versamento esce intero dal conteggio, quindi quel $-0.83 non copre la giornata piena. Il realizzato, $-0.02 netto su 2 round-trip e 0.2328 di fee, è compatibile sia con una giornata quasi neutra sia con una perdita di mercato finita fuori dal segmento — qui non si separa.
|
||||
|
||||
Insolito il quadro vol: implicita sotto la realizzata su entrambi (38.0 contro 46.6; 51.1 contro 70.5), DVOL al 24° e 18° percentile; VRP01 resta fermo per l'IV-rank (0.08 e 0.12), non per questo.
|
||||
|
||||
Salute piena, 24/24 giri e feed a 0 min; campione 73 giorni e 36 round-trip. Varrebbe che l'operatore confermi a mano importo e ora del versamento: la Nota è vuota.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,57 @@
|
||||
# Giornale di bordo — 2026-09-05
|
||||
|
||||
*Scritto 2026-09-06T00:37:28+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $79,835.00 | +0.22% | +2.03% | +24.19% | 46.4% | ↑ ↑ ↑ (3/3 su) | 38.7 (29° pctl 1a) |
|
||||
| **ETH** | $2,481.10 | +0.98% | +0.93% | +30.40% | 70.4% | ↑ ↑ ↑ (3/3 su) | 51.7 (21° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$4,490.66** · nozionale lordo $1,161 · leva lorda 0.26x · barra dati 2026-09-05 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.420 | flat | $+708 | $+711 | HOLD (a target) |
|
||||
| **ETH** | +0.268 | flat | $+451 | $+450 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$+6.93** di equity (24 letture)
|
||||
- realizzato: -0.16 netto su 3 round-trip chiusi · 3 fill · fee 0.0068
|
||||
- cumulato dall'arming: **$+3,892.60** di equity — di cui $+3,809.53 versati/prelevati -> trading **$+83.07** · TWR **+11.87%**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+711 (TP01 +0.420, SKH01 flat) e ETH $+450 (TP01 +0.268, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.26x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.74x.
|
||||
- `[pnl]` Equity $+6.93 con 3 fill e 3 round-trip chiusi (realizzato $-0.16 netto).
|
||||
- `[vol]` BTC: implicita 38.7 contro realizzata 30g 46.4 (sotto); DVOL al 29° pctl di un anno; IV-rank espandente 0.10 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 51.7 contro realizzata 30g 70.4 (sotto); DVOL al 21° pctl di un anno; IV-rank espandente 0.13 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **74 giorni, 39 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-09-06T00:38:25+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
Il fatto della giornata non è di oggi: il 04/09 l'equity è passata da $2,074.42 a $4,483.73, +$2,409.31 in un giorno, e questa è la prima pagina intera scritta sul libro nuovo. La forma non è cambiata — leva lorda 0.26x, HOLD a target su entrambe le gambe, TP01 +0.420 e +0.268, SKH01 flat su tutte e due — ma le posizioni sì: $+711 e $+450 contro i $+347 e $+213 del 02/09. È cambiata la taglia, non il comportamento; e il $+6.93 di oggi va letto su una base doppia, dove la stessa cifra in dollari pesa meno di quanto pesava a fine agosto.
|
||||
|
||||
Il cumulato lo dice più chiaramente: $+3,892.60, di cui $+3,809.53 versati o prelevati e $+83.07 di trading in 74 giorni e 39 round-trip. La cifra citabile resta il TWR, +11.87%.
|
||||
|
||||
Insolito, e non spiegabile da questa pagina: implicita sotto realizzata su entrambi (38.7 contro 46.4, 51.7 contro 70.4) con DVOL al 29° e 21° percentile, mentre i 30 giorni segnano +24.19% e +30.40%. Lettura alternativa altrettanto compatibile col +$6.93: il mark-to-market di due long in una giornata salita (+0.22% e +0.98%), non un merito di selezione — con 3 fill e realizzato $-0.16 netto, qui non è distinguibile.
|
||||
|
||||
Un controllo umano lo merita il movimento del 04/09: che abbia la sua riga di giornale, non ricostruita dopo.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,57 @@
|
||||
# Giornale di bordo — 2026-09-06
|
||||
|
||||
*Scritto 2026-09-07T00:36:43+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $80,376.00 | +0.68% | +3.44% | +23.88% | 46.4% | ↑ ↑ ↑ (3/3 su) | 39.3 (35° pctl 1a) |
|
||||
| **ETH** | $2,515.10 | +1.37% | +4.04% | +31.44% | 70.4% | ↑ ↑ ↑ (3/3 su) | 53.2 (27° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$4,502.93** · nozionale lordo $1,175 · leva lorda 0.26x · barra dati 2026-09-06 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.420 | flat | $+710 | $+717 | SELL $-7 |
|
||||
| **ETH** | +0.268 | flat | $+453 | $+458 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$+12.27** di equity (24 letture)
|
||||
- realizzato: +0.13 netto su 1 round-trip chiusi · 1 fill · fee 0.0028
|
||||
- cumulato dall'arming: **$+3,904.87** di equity — di cui $+3,834.43 versati/prelevati -> trading **$+70.44** · TWR **+8.02%**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+717 (TP01 +0.420, SKH01 flat) e ETH $+458 (TP01 +0.268, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.26x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.74x.
|
||||
- `[pnl]` Equity $+12.27 con 1 fill e 1 round-trip chiusi (realizzato $+0.13 netto).
|
||||
- `[vol]` BTC: implicita 39.3 contro realizzata 30g 46.4 (sotto); DVOL al 35° pctl di un anno; IV-rank espandente 0.12 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 53.2 contro realizzata 30g 70.4 (sotto); DVOL al 27° pctl di un anno; IV-rank espandente 0.14 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **75 giorni, 40 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-09-07T00:37:44+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
Il fatto del giorno non e' il $+12.27: e' che dopo il salto del 04/09 il libro ha ripreso la sua forma abituale a una taglia nuova. Le posizioni sono cresciute con l'equity ($+717 e $+458 contro $+349 e $+213 del 03/09), non col segnale: TP01 sta a 0.420 e 0.268, parziale su entrambe le gambe, e la leva lorda resta 0.26x su un tetto di 1.00x. SKH01 e' flat su BTC e su ETH — il quarto del libro che fa breakout oggi non porta nulla, e da quanti giorni sia cosi' non e' misurabile qui.
|
||||
|
||||
L'attivita' e' scesa: 1 fill e 1 round-trip contro i 3-5 dei giorni scorsi, e l'unica azione e' un SELL $-7 su una posizione da $+717. Vale un'occhiata umana, senza allarme: le soglie d'ordine sono in dollari assoluti e l'equity ha cambiato scala.
|
||||
|
||||
Cumulato $+3,904.87, di cui $+3,834.43 versati; il trading vale $+70.44 su 75 giorni e 40 round-trip — abbastanza poco che un movimento di capitale scorporato male se lo mangerebbe intero.
|
||||
|
||||
Una lettura alternativa dello stesso dato: implicite sotto le realizzate su entrambi (39.3 contro 46.4, 53.2 contro 70.4) e DVOL al 35° e 27° percentile dopo trenta giorni a +23.88% e +31.44%. Le regole ci leggono il gate VRP01 chiuso; e' altrettanto compatibile un rialzo che il mercato delle opzioni non sta pagando.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,57 @@
|
||||
# Giornale di bordo — 2026-09-07
|
||||
|
||||
*Scritto 2026-09-08T00:36:58+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $79,100.00 | -1.59% | +0.68% | +21.85% | 47.1% | ↑ ↑ ↑ (3/3 su) | 38.8 (31° pctl 1a) |
|
||||
| **ETH** | $2,490.35 | -0.98% | +0.91% | +29.98% | 70.6% | ↑ ↑ ↑ (3/3 su) | 52.6 (25° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$4,484.18** · nozionale lordo $1,156 · leva lorda 0.26x · barra dati 2026-09-07 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.420 | flat | $+707 | $+704 | HOLD (a target) |
|
||||
| **ETH** | +0.268 | flat | $+451 | $+452 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$-18.75** di equity (24 letture)
|
||||
- realizzato: +0.10 netto su 1 round-trip chiusi · 3 fill · fee 0.0066
|
||||
- cumulato dall'arming: **$+3,886.12** di equity — di cui $+3,834.43 versati/prelevati -> trading **$+51.69** · TWR **+7.57%**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+704 (TP01 +0.420, SKH01 flat) e ETH $+452 (TP01 +0.268, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.26x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.74x.
|
||||
- `[pnl]` Equity $-18.75 con 3 fill e 1 round-trip chiusi (realizzato $+0.10 netto).
|
||||
- `[vol]` BTC: implicita 38.8 contro realizzata 30g 47.1 (sotto); DVOL al 31° pctl di un anno; IV-rank espandente 0.11 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 52.6 contro realizzata 30g 70.6 (sotto); DVOL al 25° pctl di un anno; IV-rank espandente 0.13 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **76 giorni, 41 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-09-08T00:37:50+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
La giornata rompe tre chiusure positive di fila (+28.60, +6.93, +12.27) con $-18.75, la perdita giornaliera più grande della serie qui riportata. È quasi tutta valutazione, non attività: 3 fill, 1 round-trip, realizzato $+0.10 netto, con BTC -1.59% e ETH -0.98% e il libro long su entrambi. Il segno torna; la taglia non è verificabile qui, perché la pagina non scompone il P&L per asset.
|
||||
|
||||
Ciò che è cambiato davvero risale al salto del 04/09: le posizioni sono passate dai $+357 / $+214 di fine agosto ai $+704 / $+452 di oggi e ci restano, mentre la leva lorda resta 0.26x. A parità di rischio relativo la stessa percentuale di mercato si scrive con un numero di dollari più grande: le prossime giornate rosse sembreranno peggiori senza esserlo.
|
||||
|
||||
Insolito il quadro della volatilità: implicita sotto la realizzata su entrambi (38.8 contro 47.1; 52.6 contro 70.6) con DVOL al 31° e al 25° percentile di un anno. Il gate di VRP01 legge l'IV-rank (0.11 e 0.13) e resta chiuso, ma una lettura altrettanto compatibile è che il mercato tratti il movimento a 30 giorni (+21.85%, +29.98%) come già avvenuto.
|
||||
|
||||
TP01 porta segnali parziali (0.420, 0.268) e SKH01 è flat malgrado TSMOM 3/3 su: c'è tendenza, non breakout. Il cumulato $+3,886.12 è quasi tutto versato ($+3,834.43); resta $+51.69 di trading su 76 giorni e 41 round-trip, TWR +7.57% — campione che non separa edge e fortuna. Salute piena, 24/24 giri e feed a 0 min: niente da controllare a mano oggi.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,58 @@
|
||||
# Giornale di bordo — 2026-09-08
|
||||
|
||||
*Scritto 2026-09-09T00:36:48+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $78,470.50 | -0.80% | +1.34% | +20.97% | 47.3% | ↑ ↑ ↑ (3/3 su) | 39.9 (40° pctl 1a) |
|
||||
| **ETH** | $2,485.45 | -0.20% | +2.77% | +30.16% | 70.6% | ↑ ↑ ↑ (3/3 su) | 53.9 (33° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$4,477.46** · nozionale lordo $1,149 · leva lorda 0.26x · barra dati 2026-09-08 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.415 | flat | $+697 | $+698 | HOLD (a target) |
|
||||
| **ETH** | +0.268 | flat | $+449 | $+451 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$-6.72** di equity (24 letture)
|
||||
- realizzato: +0.00 netto su 1 round-trip chiusi · 2 fill · fee 0.0055
|
||||
- cumulato dall'arming: **$+3,879.40** di equity — di cui $+3,834.43 versati/prelevati -> trading **$+44.97** · TWR **+7.41%**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+698 (TP01 +0.415, SKH01 flat) e ETH $+451 (TP01 +0.268, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.26x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.74x.
|
||||
- `[pnl]` Equity $-6.72 con 2 fill e 1 round-trip chiusi (realizzato $+0.00 netto).
|
||||
- `[drawdown]` 📌 Equity **-0.57%** sotto il picco ($4,502.93). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
|
||||
- `[vol]` BTC: implicita 39.9 contro realizzata 30g 47.3 (sotto); DVOL al 40° pctl di un anno; IV-rank espandente 0.14 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 53.9 contro realizzata 30g 70.6 (sotto); DVOL al 33° pctl di un anno; IV-rank espandente 0.15 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **77 giorni, 42 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-09-09T00:37:27+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
Terzo giorno sotto il picco del 06/09 ($4,502.93), e secondo consecutivo in rosso dopo i $-18.75 del 07/09: oggi $-6.72. La differenza rispetto alla settimana precedente al versamento non e' il segno ma la scala — le posizioni sono passate da BTC $+347/ETH $+213 a BTC $+698/ETH $+451, quindi oscillazioni di equity dello stesso ordine in dollari valgono ora meno in percentuale. Con realizzato $+0.00 su 1 round-trip e HOLD a target su entrambe le gambe, la perdita del giorno e' rivalutazione delle posizioni aperte contro BTC -0.80% ed ETH -0.20%, non attivita' di trading.
|
||||
|
||||
L'elemento insolito e' altrove: TSMOM 3/3 su su entrambi gli asset, BTC +20.97% e ETH +30.16% a 30 giorni, e nonostante questo TP01 sta a +0.415 e +0.268 con SKH01 flat — leva lorda 0.26x su un tetto di 1.00x. Il libro e' poco investito dentro il trend che dovrebbe essere il suo caso favorevole; se questo sia disciplina di sizing o segnale debole non e' misurabile qui.
|
||||
|
||||
Lettura alternativa altrettanto compatibile: a 0.26x di esposizione il P&L giornaliero e' quasi interamente beta di mercato, e i 77 giorni / 42 round-trip non permettono di distinguerlo dall'edge.
|
||||
|
||||
Merita un'occhiata umana il round-trip chiuso con realizzato esattamente $+0.00 a fronte di fee 0.0055.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,58 @@
|
||||
# Giornale di bordo — 2026-09-09
|
||||
|
||||
*Scritto 2026-09-10T00:38:03+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $78,283.50 | -0.24% | +1.26% | +22.45% | 46.8% | ↑ ↑ ↑ (3/3 su) | 40.2 (42° pctl 1a) |
|
||||
| **ETH** | $2,467.95 | -0.70% | +3.19% | +31.89% | 70.1% | ↑ ↑ ↑ (3/3 su) | 53.7 (32° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$4,472.10** · nozionale lordo $1,157 · leva lorda 0.26x · barra dati 2026-09-09 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.418 | flat | $+701 | $+704 | HOLD (a target) |
|
||||
| **ETH** | +0.270 | flat | $+453 | $+453 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$-5.36** di equity (24 letture)
|
||||
- realizzato: +0.00 netto su 0 round-trip chiusi · 2 fill · fee 0.0047
|
||||
- cumulato dall'arming: **$+3,874.04** di equity — di cui $+3,834.43 versati/prelevati -> trading **$+39.61** · TWR **+7.28%**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+704 (TP01 +0.418, SKH01 flat) e ETH $+453 (TP01 +0.270, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.26x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.74x.
|
||||
- `[pnl]` Equity $-5.36 con 2 fill e 0 round-trip chiusi (realizzato $+0.00 netto).
|
||||
- `[drawdown]` 📌 Equity **-0.68%** sotto il picco ($4,502.93). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
|
||||
- `[vol]` BTC: implicita 40.2 contro realizzata 30g 46.8 (sotto); DVOL al 42° pctl di un anno; IV-rank espandente 0.14 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 53.7 contro realizzata 30g 70.1 (sotto); DVOL al 32° pctl di un anno; IV-rank espandente 0.15 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **78 giorni, 42 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-09-10T00:39:20+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
Il fatto nuovo non e' il segno del giorno ma la sua composizione: $-5.36 di equity con 2 fill, zero round-trip chiusi e realizzato $+0.00. La perdita e' tutta marcatura su posizioni aperte, e le posizioni sono a target su entrambe le gambe ($+704 contro $+701, $+453 contro $+453): il libro non ha deciso niente, ha ri-tagliato. Nella serie qui sopra l'unico altro giorno senza round-trip chiusi e' il 01/09; e' il terzo giorno di equity in discesa dopo il picco del 06/09 a $4,502.93, con BTC -0.24% e ETH -0.70%.
|
||||
|
||||
L'esposizione la porta interamente TP01 (+0.418 su BTC, +0.270 su ETH) mentre SKH01 e' flat su entrambi, e questo con TSMOM 3/3 su e 30 giorni a +22.45% e +31.89%: il trend c'e', il breakout no. Esecuzione pulita, 24/24 giri e feed SKH a 0 min.
|
||||
|
||||
L'implicita sotto la realizzata (40.2 contro 46.8; 53.7 contro 70.1) si legge in due modi ugualmente compatibili — vol a sconto, oppure realizzata gonfiata dalla corsa dei 30 giorni. Quale dei due, non e' misurabile qui.
|
||||
|
||||
Da guardare senza urgenza: il cumulato $+3,874.04 e' quasi tutto versamento ($+3,834.43), il trading vale $+39.61 su 42 round-trip in 78 giorni. A quella taglia il TWR +7.28% non e' ancora una misura di edge.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,58 @@
|
||||
# Giornale di bordo — 2026-09-10
|
||||
|
||||
*Scritto 2026-09-11T00:36:36+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $76,549.50 | -2.22% | -5.83% | +20.44% | 47.7% | ↑ ↑ ↑ (3/3 su) | 40.2 (43° pctl 1a) |
|
||||
| **ETH** | $2,437.50 | -1.23% | -2.81% | +29.57% | 70.5% | ↑ ↑ ↑ (3/3 su) | 53.4 (30° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$4,454.86** · nozionale lordo $1,147 · leva lorda 0.26x · barra dati 2026-09-10 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.419 | flat | $+700 | $+698 | HOLD (a target) |
|
||||
| **ETH** | +0.269 | flat | $+449 | $+449 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$-17.24** di equity (24 letture)
|
||||
- realizzato: +0.00 netto su 0 round-trip chiusi · 1 fill · fee 0.0027
|
||||
- cumulato dall'arming: **$+3,856.80** di equity — di cui $+3,838.97 versati/prelevati -> trading **$+17.83** · TWR **+6.76%**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+698 (TP01 +0.419, SKH01 flat) e ETH $+449 (TP01 +0.269, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.26x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.74x.
|
||||
- `[pnl]` Equity $-17.24 con 1 fill e 0 round-trip chiusi (realizzato $+0.00 netto).
|
||||
- `[drawdown]` 📌 Equity **-1.07%** sotto il picco ($4,502.93). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
|
||||
- `[vol]` BTC: implicita 40.2 contro realizzata 30g 47.7 (sotto); DVOL al 43° pctl di un anno; IV-rank espandente 0.15 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 53.4 contro realizzata 30g 70.5 (sotto); DVOL al 30° pctl di un anno; IV-rank espandente 0.14 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **79 giorni, 42 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-09-11T00:38:42+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
La giornata prolunga una sequenza iniziata prima. Dal picco di $4,502.93 del 2026-09-06 ogni giornata ha chiuso in perdita, e i $-17.24 di oggi sono vicini ai $-18.75 del 2026-09-07. Il confronto più istruttivo però è un altro. Il trading cumulato dall'arming, $+17.83, ha ormai la stessa taglia della perdita di una sola giornata come questa. È la forma concreta dell'avvertenza sul campione di 79 giorni.
|
||||
|
||||
Dal salto di equity del 2026-09-04 le posizioni in dollari sono rimaste quasi ferme: BTC fra $+717 e $+698, ETH fra $+458 e $+449. Nello stesso periodo BTC ha segnato -5.83% ed ETH -2.81% a 7 giorni, con i 30 giorni ancora a +20.44% e +29.57%. Il libro non ha quindi ridotto l'esposizione nel calo. Il quadro è compatibile con due letture: un ritracciamento dentro un trend che tutte le finestre TSMOM vedono intatto, oppure un cambio di regime che finestre da 30 giorni in su non possono ancora registrare. La pagina non le distingue.
|
||||
|
||||
Non è misurabile qui quanto dei $-17.24 venga dalla marcatura delle due long e quanto da funding o collaterale. Non lo è nemmeno l'asset dell'unico fill, visto che a fine giornata entrambe le gambe erano a target.
|
||||
|
||||
Merita un controllo umano la descrizione del libro nel prompt dell'analista. Indica un capitale molto inferiore all'equity di $4,454.86 e andrebbe aggiornata.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,46 @@
|
||||
# Giornale di bordo — 2026-09-11
|
||||
|
||||
*Scritto 2026-09-12T00:36:53+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $77,220.50 | +0.88% | -3.06% | +21.72% | 47.6% | ↑ ↑ ↑ (3/3 su) | 36.8 (14° pctl 1a) |
|
||||
| **ETH** | $2,516.70 | +3.25% | +2.43% | +34.00% | 70.8% | ↑ ↑ ↑ (3/3 su) | 51.8 (22° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$4,466.16** · nozionale lordo $1,131 · leva lorda 0.25x · barra dati 2026-09-11 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.410 | flat | $+688 | $+687 | HOLD (a target) |
|
||||
| **ETH** | +0.268 | flat | $+449 | $+444 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$+11.30** di equity (24 letture)
|
||||
- realizzato: +21.66 netto su 13 round-trip chiusi · 8 fill · fee 0.4046
|
||||
- cumulato dall'arming: **$+3,868.10** di equity — di cui $+3,838.97 versati/prelevati -> trading **$+29.13** · TWR **+7.03%**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+687 (TP01 +0.410, SKH01 flat) e ETH $+444 (TP01 +0.268, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.25x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.75x.
|
||||
- `[pnl]` Equity $+11.30 con 8 fill e 13 round-trip chiusi (realizzato $+21.66 netto).
|
||||
- `[drawdown]` 📌 Equity **-0.83%** sotto il picco ($4,503.52). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
|
||||
- `[vol]` BTC: implicita 36.8 contro realizzata 30g 47.6 (sotto); DVOL al 14° pctl di un anno; IV-rank espandente 0.05 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 51.8 contro realizzata 30g 70.8 (sotto); DVOL al 22° pctl di un anno; IV-rank espandente 0.13 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **80 giorni, 55 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,58 @@
|
||||
# Giornale di bordo — 2026-09-12
|
||||
|
||||
*Scritto 2026-09-13T00:37:30+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $77,288.50 | +0.09% | -3.19% | +21.80% | 47.6% | ↑ ↑ ↑ (3/3 su) | 37.0 (15° pctl 1a) |
|
||||
| **ETH** | $2,526.45 | +0.39% | +1.83% | +34.02% | 70.8% | ↑ ↑ ↑ (3/3 su) | 52.6 (26° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$4,468.59** · nozionale lordo $1,134 · leva lorda 0.25x · barra dati 2026-09-12 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.411 | flat | $+688 | $+688 | HOLD (a target) |
|
||||
| **ETH** | +0.267 | flat | $+447 | $+446 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$+2.43** di equity (24 letture)
|
||||
- realizzato: +0.00 netto su 0 round-trip chiusi · 0 fill · fee 0.0000
|
||||
- cumulato dall'arming: **$+3,870.53** di equity — di cui $+3,838.97 versati/prelevati -> trading **$+31.56** · TWR **+7.09%**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+688 (TP01 +0.411, SKH01 flat) e ETH $+446 (TP01 +0.267, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.25x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.75x.
|
||||
- `[pnl]` Equity $+2.43 **senza operare**: e' mark-to-market sulle posizioni gia' aperte.
|
||||
- `[drawdown]` 📌 Equity **-0.78%** sotto il picco ($4,503.52). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
|
||||
- `[vol]` BTC: implicita 37.0 contro realizzata 30g 47.6 (sotto); DVOL al 15° pctl di un anno; IV-rank espandente 0.06 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 52.6 contro realizzata 30g 70.8 (sotto); DVOL al 26° pctl di un anno; IV-rank espandente 0.14 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **81 giorni, 55 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-09-13T00:38:21+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
Il fatto nuovo è l'assenza di attività: zero fill e zero round-trip, dopo gli 8 fill e i 13 round-trip dell'11/09 — 13 dei 55 round-trip dell'intero campione di 81 giorni stanno in quella sola giornata. Oggi il libro ha solo tenuto: BTC $+688 ed ETH $+446, entrambi a target, e i $+2.43 sono marcatura su posizioni già aperte.
|
||||
|
||||
L'esposizione è tutta TP01: SKH01 è flat su entrambe le gambe, quindi la leva 0.25x contro un tetto di 1.00x descrive un solo sleeve dei due, non un libro deliberatamente piccolo.
|
||||
|
||||
Il −0.78% dal picco di $4,503.52 ammette due letture ugualmente compatibili: le quattro giornate negative dal 07 al 10/09, oppure la semplice marcatura di due long mentre BTC fa −3.19% su sette giorni contro +21.80% su trenta. Nulla di quel calo è stato realizzato — oggi zero fill, e il trading dall'arming resta $+31.56. Quale delle due pesi di più non è misurabile qui.
|
||||
|
||||
Salute piena: 24/24 giri, feed SKH a 0 minuti. Per l'occhio umano: il TWR +7.09% convive con $+3,838.97 di versamenti contro $+31.56 di trading, ed è il secondo numero, non il primo, a dire qualcosa del libro.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,60 @@
|
||||
# Giornale di bordo — 2026-09-13
|
||||
|
||||
*Scritto 2026-09-14T00:37:10+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $76,808.50 | -0.62% | -4.44% | +21.93% | 47.6% | ↑ ↑ ↑ (3/3 su) | 38.9 (34° pctl 1a) |
|
||||
| **ETH** | $2,476.90 | -1.96% | -1.52% | +31.73% | 71.4% | ↑ ↑ ↑ (3/3 su) | 54.3 (39° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$4,455.99** · nozionale lordo $1,134 · leva lorda 0.25x · barra dati 2026-09-13 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.412 | flat | $+689 | $+691 | HOLD (a target) |
|
||||
| **ETH** | +0.267 | flat | $+446 | $+443 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$-12.60** di equity (24 letture)
|
||||
- realizzato: -0.19 netto su 1 round-trip chiusi · 4 fill · fee 0.0101
|
||||
- cumulato dall'arming: **$+3,857.93** di equity — di cui $+3,838.97 versati/prelevati -> trading **$+18.96** · TWR **+6.79%**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+691 (TP01 +0.412, SKH01 flat) e ETH $+443 (TP01 +0.267, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.25x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.75x.
|
||||
- `[pnl]` Equity $-12.60 con 4 fill e 1 round-trip chiusi (realizzato $-0.19 netto).
|
||||
- `[drawdown]` 📌 Equity **-1.06%** sotto il picco ($4,503.52). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
|
||||
- `[vol]` BTC: implicita 38.9 contro realizzata 30g 47.6 (sotto); DVOL al 34° pctl di un anno; IV-rank espandente 0.11 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 54.3 contro realizzata 30g 71.4 (sotto); DVOL al 39° pctl di un anno; IV-rank espandente 0.16 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **82 giorni, 56 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-09-14T00:38:00+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
Quarta giornata di fila senza decisione nuova: TP01 resta pieno su entrambe le gambe (+0.412 BTC, +0.267 ETH), SKH01 flat, posizioni a target, leva 0.25x. Il $-12.60 è quasi tutto marcatura — il realizzato è $-0.19 su 1 round-trip, 4 fill, fee 0.0101 — e ha la stessa forma dei cali del 07-10/09 (-18.75, -6.72, -5.36, -17.24). A $4,455.99 il libro sta appena sopra il minimo del 10/09 ($4,454.86) e -1.06% sotto il picco del 06/09.
|
||||
|
||||
L'insolito sta nell'attività: 13 round-trip l'11/09, zero il 12/09, 1 oggi, a posizione sostanzialmente ferma. Sono ri-taglie attorno al target, non cambi di vista.
|
||||
|
||||
Il contrasto che vale la pena registrare: BTC -4.44% su 7 giorni ed ETH -1.96% nel giorno, ma TSMOM 3/3 su per entrambi e +21.93%/+31.73% a 30 giorni. Il segnale lungo non vede la settimana — per costruzione. Lo stesso dato regge due letture ugualmente compatibili: ritirata dentro un trend intatto, oppure inizio di un giro che il filtro a 30/90/180 giorni vedrà con ritardo. Oggi non si separano.
|
||||
|
||||
Sulla vol, implicita sotto realizzata su entrambi (38.9 vs 47.6; 54.3 vs 71.4) con DVOL al 34°/39° percentile: VRP01 resta chiuso dal gate (0.11 e 0.16 contro 0.30).
|
||||
|
||||
Salute piena, 24/24 giri, feed a 0 min. Nulla da controllare a mano. Con 82 giorni e 56 round-trip, questi cali restano rumore, non evidenza.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,58 @@
|
||||
# Giornale di bordo — 2026-09-14
|
||||
|
||||
*Scritto 2026-09-15T00:37:38+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $78,192.50 | +1.80% | -1.15% | +24.05% | 47.7% | ↑ ↑ ↑ (3/3 su) | 38.6 (30° pctl 1a) |
|
||||
| **ETH** | $2,516.15 | +1.58% | +1.04% | +33.78% | 71.4% | ↑ ↑ ↑ (3/3 su) | 54.9 (42° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$4,477.74** · nozionale lordo $1,130 · leva lorda 0.25x · barra dati 2026-09-14 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.410 | flat | $+689 | $+689 | HOLD (a target) |
|
||||
| **ETH** | +0.265 | flat | $+444 | $+441 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$+21.75** di equity (24 letture)
|
||||
- realizzato: -0.69 netto su 5 round-trip chiusi · 7 fill · fee 0.0189
|
||||
- cumulato dall'arming: **$+3,879.68** di equity — di cui $+3,838.97 versati/prelevati -> trading **$+40.71** · TWR **+7.31%**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+689 (TP01 +0.410, SKH01 flat) e ETH $+441 (TP01 +0.265, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.25x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.75x.
|
||||
- `[pnl]` Equity $+21.75 con 7 fill e 5 round-trip chiusi (realizzato $-0.69 netto).
|
||||
- `[drawdown]` 📌 Equity **-0.57%** sotto il picco ($4,503.52). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
|
||||
- `[vol]` BTC: implicita 38.6 contro realizzata 30g 47.7 (sotto); DVOL al 30° pctl di un anno; IV-rank espandente 0.10 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 54.9 contro realizzata 30g 71.4 (sotto); DVOL al 42° pctl di un anno; IV-rank espandente 0.17 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **83 giorni, 61 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-09-15T00:38:16+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
Il +$21.75 è il delta giornaliero più ampio fra i dieci giorni elencati — il migliore precedente era +$12.27 del 06/09 — e arriva interamente dalla marcatura: il realizzato è **negativo**, $-0.69 su 5 round-trip chiusi con 7 fill e fee 0.0189. Lo stesso dato regge due letture ugualmente compatibili: il libro ha catturato il +1.80% di BTC e il +1.58% di ETH stando long su entrambi, oppure due posizioni ferme sono state semplicemente rivalutate da un mercato che saliva. Con 83 giorni e 61 round-trip non si distingue.
|
||||
|
||||
Il fatto insolito è la direzione del nozionale: dal 04/09 BTC scende da $+717 a $+689 e ETH da $+456 a $+441 mentre i prezzi salgono e TSMOM resta 3/3 su entrambi. Le cause — segnale che si assottiglia, equity che si è ritirata dal picco di $4,503.52 — non sono separabili qui: i segnali dei giorni precedenti non compaiono nella pagina.
|
||||
|
||||
Resta l'incoerenza di fondo con cui convivere: l'equity è a $4,477.74 e il cumulato $+3,879.68, ma il trading da arming vale $+40.71 con TWR +7.31%.
|
||||
|
||||
Meriterebbe uno sguardo perché cinque round-trip chiusi lascino un realizzato negativo in una giornata in salita: se sono ritagli di taglia va bene, se sono uscite no. Salute piena, 24/24 giri, feed a 0 minuti.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,58 @@
|
||||
# Giornale di bordo — 2026-09-15
|
||||
|
||||
*Scritto 2026-09-16T00:36:40+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $75,607.00 | -3.31% | -3.65% | +20.31% | 49.7% | ↑ ↑ ↑ (3/3 su) | 39.0 (35° pctl 1a) |
|
||||
| **ETH** | $2,397.35 | -4.72% | -3.54% | +27.89% | 74.1% | ↑ ↑ ↑ (3/3 su) | 55.2 (43° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$4,433.75** · nozionale lordo $568 · leva lorda 0.13x · barra dati 2026-09-15 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.410 | SHORT @ 75,707.5 | $+128 | $+129 | HOLD (a target) |
|
||||
| **ETH** | +0.265 | flat | $+440 | $+439 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$-43.99** di equity (24 letture)
|
||||
- realizzato: -22.53 netto su 11 round-trip chiusi · 8 fill · fee 0.2114
|
||||
- cumulato dall'arming: **$+3,835.69** di equity — di cui $+3,838.97 versati/prelevati -> trading **$-3.28** · TWR **+6.25%**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+129 (TP01 +0.410, SKH01 short) e ETH $+439 (TP01 +0.265, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.13x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.87x.
|
||||
- `[pnl]` Equity $-43.99 con 8 fill e 11 round-trip chiusi (realizzato $-22.53 netto).
|
||||
- `[drawdown]` 📌 Equity **-1.55%** sotto il picco ($4,503.52). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
|
||||
- `[vol]` BTC: implicita 39.0 contro realizzata 30g 49.7 (sotto); DVOL al 35° pctl di un anno; IV-rank espandente 0.11 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 55.2 contro realizzata 30g 74.1 (sotto); DVOL al 43° pctl di un anno; IV-rank espandente 0.17 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **84 giorni, 72 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-09-16T00:37:25+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
Il fatto nuovo non è la perdita, è la forma della posizione: il netto su BTC scende da $+689 a $+129 perché SKH01 è passato SHORT a 75,707.5 contro il long TP01 (+0.410), e la leva lorda cade a 0.13x. Il libro è a target su entrambe le gambe, non tagliato da un cap; ETH resta $+439 con SKH01 flat.
|
||||
|
||||
I $-43.99 di equity sono il giorno peggiore della finestra mostrata — il precedente era $-18.75 il 07/09 — e solo metà è realizzata: $-22.53 netti su 11 round-trip e 8 fill, fee 0.2114. Il resto è marcatura, in una giornata da BTC -3.31% e ETH -4.72% con i TSMOM ancora 3/3 su e il 30g a +20.31% e +27.89%: il libro perde dentro un trend che per le sue regole non è girato. L'implicita resta sotto la realizzata su entrambi (39.0 contro 49.7, 55.2 contro 74.1) e il gate di VRP01 resta chiuso.
|
||||
|
||||
Lettura alternativa altrettanto compatibile: lo short su BTC ha ridotto l'esposizione proprio mentre il prezzo scendeva. Se abbia attenuato o aggravato la giornata non è misurabile qui — la pagina non scompone il P&L per sleeve.
|
||||
|
||||
Merita un occhio umano: il trading dall'arming è ora $-3.28, negativo, mentre il TWR resta +6.25%. A 84 giorni e 72 round-trip è esattamente quanto il campione permette di dire.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,56 @@
|
||||
# Giornale di bordo — 2026-09-16
|
||||
|
||||
*Scritto 2026-09-17T00:38:45+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $76,167.50 | +0.74% | -2.70% | +18.11% | 49.1% | ↑ ↑ ↑ (3/3 su) | 36.2 (11° pctl 1a) |
|
||||
| **ETH** | $2,416.45 | +0.80% | -2.09% | +26.36% | 74.0% | ↑ ↑ ↑ (3/3 su) | 52.8 (27° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$4,438.59** · nozionale lordo $537 · leva lorda 0.12x · barra dati 2026-09-16 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.399 | SHORT @ 75,707.5 | $+109 | $+107 | HOLD (a target) |
|
||||
| **ETH** | +0.257 | flat | $+427 | $+430 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$+4.84** di equity (24 letture)
|
||||
- realizzato: -2.08 netto su 2 round-trip chiusi · 2 fill · fee 0.0123
|
||||
- cumulato dall'arming: **$+3,840.53** di equity — di cui $+3,838.97 versati/prelevati -> trading **$+1.56** · TWR **+6.37%**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+107 (TP01 +0.399, SKH01 short) e ETH $+430 (TP01 +0.257, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.12x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.88x.
|
||||
- `[pnl]` Equity $+4.84 con 2 fill e 2 round-trip chiusi (realizzato $-2.08 netto).
|
||||
- `[drawdown]` 📌 Equity **-1.44%** sotto il picco ($4,503.52). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
|
||||
- `[vol]` BTC: implicita 36.2 contro realizzata 30g 49.1 (sotto); DVOL al 11° pctl di un anno; IV-rank espandente 0.04 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 52.8 contro realizzata 30g 74.0 (sotto); DVOL al 27° pctl di un anno; IV-rank espandente 0.14 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **85 giorni, 74 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-09-17T00:39:34+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
Il cambiamento non è di oggi ma del 2026-09-15. Quel giorno la posizione BTC scende da $+689 a $+129, con 8 fill e la variazione peggiore della serie, -43.99. Oggi la posizione vale $+107, con SKH01 SHORT @ 75,707.5 contro un TP01 ancora a +0.399. La lettura più semplice è che lo short del breakout netti quasi tutto il long di trend. Però la pagina mostra i segnali solo per oggi: quando sia entrato lo short, e quanto dei -43.99 venga dal mercato e quanto dall'esecuzione, non è misurabile qui. L'esposizione è ora quasi tutta su ETH ($+430), e la chiusura BTC a $76,167.50 sta sopra l'ingresso dello short.
|
||||
|
||||
Dal 2026-09-06 l'equity è passata da 4,502.93 a 4,438.59. Il libro ha tenuto i long mentre BTC ed ETH segnavano -2.70% e -2.09% a 7 giorni. È compatibile con una pausa dentro un trend che a 30 giorni resta +18.11% e +26.36%, ma anche con il suo esaurimento: i dati non distinguono le due letture. Il trading a $+1.56 accanto al TWR +6.37% non è una contraddizione: il TWR pesa i periodi, i dollari pesano il capitale di ciascun periodo.
|
||||
|
||||
Merita un controllo umano il conteggio dei round-trip. Il 2026-09-11 e il 2026-09-15 supera quello dei fill (13 su 8, 11 su 8). Inoltre la taglia di capitale scritta nelle istruzioni dell'analista non coincide con l'equity della pagina.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,58 @@
|
||||
# Giornale di bordo — 2026-09-17
|
||||
|
||||
*Scritto 2026-09-18T00:39:56+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $76,380.00 | +0.28% | -0.22% | +18.06% | 49.1% | ↑ ↑ ↑ (3/3 su) | 33.6 (0° pctl 1a) |
|
||||
| **ETH** | $2,446.05 | +1.22% | +0.35% | +27.59% | 74.0% | ↑ ↑ ↑ (3/3 su) | 50.1 (13° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$4,444.68** · nozionale lordo $537 · leva lorda 0.12x · barra dati 2026-09-17 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.399 | SHORT @ 75,707.5 | $+109 | $+107 | HOLD (a target) |
|
||||
| **ETH** | +0.257 | flat | $+428 | $+430 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$+6.09** di equity (24 letture)
|
||||
- realizzato: -0.37 netto su 1 round-trip chiusi · 1 fill · fee 0.0018
|
||||
- cumulato dall'arming: **$+3,846.62** di equity — di cui $+3,838.97 versati/prelevati -> trading **$+7.65** · TWR **+6.52%**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+107 (TP01 +0.399, SKH01 short) e ETH $+430 (TP01 +0.257, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.12x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.88x.
|
||||
- `[pnl]` Equity $+6.09 con 1 fill e 1 round-trip chiusi (realizzato $-0.37 netto).
|
||||
- `[drawdown]` 📌 Equity **-1.31%** sotto il picco ($4,503.52). ⚠️ e' un DD su letture ORARIE, non sul minimo intra-giorno.
|
||||
- `[vol]` BTC: implicita 33.6 contro realizzata 30g 49.1 (sotto); DVOL al 0° pctl di un anno; IV-rank espandente 0.00 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 50.1 contro realizzata 30g 74.0 (sotto); DVOL al 13° pctl di un anno; IV-rank espandente 0.11 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **86 giorni, 75 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-09-18T00:40:39+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
Il cambiamento che conta non è di oggi ma del 15/09. Quel giorno la posizione BTC è passata da $+689 a $+129, e oggi è a $+107. ETH invece resta fra $+430 e $+453, come dal 07/09. TP01 BTC è ancora a +0.399 e SKH01 è SHORT @ 75,707.5. Il calo è quindi compatibile con la gamba breakout che compensa quella di trend. Se sia sceso anche TP01 non è misurabile qui, perché i giorni precedenti non riportano i segnali. Con leva lorda a 0.12x, oggi il libro è quasi solo ETH. BTC chiude a $76,380.00, sopra il prezzo d'ingresso dello short, con TSMOM 3/3 su: la gamba SKH01 va contro il trend che TP01 segue.
|
||||
|
||||
Il $+6.09 del giorno non si può dividere qui fra le due gambe e il collaterale. Dal 07/09 l'equity scende da $4,484.18 a $4,444.68. Il solo 15/09 (-43.99) pesa più di tutto il trading dall'arming, che vale $+7.65 contro $+3,838.97 versati: basta un giorno a cambiare il segno del risultato cumulato.
|
||||
|
||||
È insolito il DVOL BTC al 0° pctl di un anno con una RV30 al 49.1%. Due letture sono ugualmente compatibili: o l'implicita è compressa, o la RV30 contiene ancora il +18.06% del mese mentre la settimana è piatta (-0.22%).
|
||||
|
||||
Da controllare: l'11/09 e il 15/09 i round-trip (13 e 11) sono più dei fill (8 e 8). Va chiarito se dipende da come vengono abbinati i fill o da un errore di conteggio.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,60 @@
|
||||
# Giornale di bordo — 2026-09-18
|
||||
|
||||
*Scritto 2026-09-19T00:39:16+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $80,907.00 | +5.93% | +4.77% | +16.70% | 47.4% | ↑ ↑ ↑ (3/3 su) | 35.5 (7° pctl 1a) |
|
||||
| **ETH** | $2,612.25 | +6.79% | +3.80% | +15.98% | 53.9% | ↑ ↑ ↑ (3/3 su) | 49.9 (13° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$4,526.26** · nozionale lordo $1,444 · leva lorda 0.32x · barra dati 2026-09-18 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.452 | flat | $+768 | $+769 | HOLD (a target) |
|
||||
| **ETH** | +0.401 | flat | $+681 | $+675 | BUY $+6 |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$+81.58** di equity (24 letture)
|
||||
- realizzato: -0.70 netto su 8 round-trip chiusi · 12 fill · fee 0.3354
|
||||
- cumulato dall'arming: **$+3,928.20** di equity — di cui $+3,838.97 versati/prelevati -> trading **$+89.23** · TWR **+8.47%**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+769 (TP01 +0.452, SKH01 flat) e ETH $+675 (TP01 +0.401, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.32x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.68x.
|
||||
- `[pnl]` Equity $+81.58 con 12 fill e 8 round-trip chiusi (realizzato $-0.70 netto).
|
||||
- `[concentrazione]` 📌 **Il 78% di tutto il P&L cumulato ($+89.23) viene dagli ultimi 7 giorni.** Un risultato concentrato in una finestra non e' un tasso di rendimento: e' un evento.
|
||||
- `[vol]` BTC: implicita 35.5 contro realizzata 30g 47.4 (sotto); DVOL al 7° pctl di un anno; IV-rank espandente 0.02 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 49.9 contro realizzata 30g 53.9 (sotto); DVOL al 13° pctl di un anno; IV-rank espandente 0.11 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evento]` 📌 BTC: giornata a +5.93%, **2.4 deviazioni** giornaliere (sd implicita dalla RV30 = 2.48%).
|
||||
- `[evento]` 📌 ETH: giornata a +6.79%, **2.4 deviazioni** giornaliere (sd implicita dalla RV30 = 2.82%).
|
||||
- `[evidenza]` 📌 Campione a oggi: **87 giorni, 83 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-09-19T00:40:10+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
Il cambiamento della giornata sta nelle posizioni. Dal 15 al 17 settembre BTC era sceso a $+129 e poi a $+107, mentre ETH restava intorno a $+430. Oggi entrambe le gambe sono tornate a taglia piena, BTC $+769 ed ETH $+675, con SKH01 flat su tutti e due. La pagina non dice perché BTC fosse stato ridotto, né se il ritorno sia avvenuto prima o dopo il rialzo. Se la riduzione del 15 sia costata o abbia protetto non è misurabile qui.
|
||||
|
||||
I $+81.58 del giorno vengono dalla marcatura delle posizioni aperte, non dalle chiusure: gli 8 round-trip hanno realizzato $-0.70 netti. Da soli pesano quasi quanto l'intero trading dall'arming, $+89.23. La concentrazione segnalata è quindi, in sostanza, quella di un solo giorno. Esiste una lettura altrettanto compatibile: un libro lungo su entrambi gli asset ha semplicemente incassato una giornata da 2.4 deviazioni, e questo non dice nulla sul segnale.
|
||||
|
||||
È insolito anche il contesto di volatilità. Il DVOL sta al 7° e al 13° percentile di un anno, sotto la realizzata, proprio nel giorno di un movimento ampio. Se l'implicita abbia reagito non si vede, perché la pagina non riporta i DVOL dei giorni precedenti.
|
||||
|
||||
Da controllare: il capitale indicato nelle istruzioni dell'analista non corrisponde all'equity di $4,526.26 di questa pagina.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,58 @@
|
||||
# Giornale di bordo — 2026-09-19
|
||||
|
||||
*Scritto 2026-09-20T00:37:23+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $81,279.50 | +0.46% | +5.16% | +11.33% | 44.3% | ↑ ↑ ↑ (3/3 su) | 36.3 (13° pctl 1a) |
|
||||
| **ETH** | $2,632.95 | +0.79% | +4.22% | +13.16% | 53.0% | ↑ ↑ ↑ (3/3 su) | 50.8 (17° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$4,536.47** · nozionale lordo $1,385 · leva lorda 0.31x · barra dati 2026-09-19 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.443 | flat | $+754 | $+756 | HOLD (a target) |
|
||||
| **ETH** | +0.372 | flat | $+632 | $+629 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$+10.21** di equity (24 letture)
|
||||
- realizzato: +1.00 netto su 5 round-trip chiusi · 5 fill · fee 0.0316
|
||||
- cumulato dall'arming: **$+3,938.41** di equity — di cui $+3,838.97 versati/prelevati -> trading **$+99.44** · TWR **+8.72%**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+756 (TP01 +0.443, SKH01 flat) e ETH $+629 (TP01 +0.372, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.31x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.69x.
|
||||
- `[pnl]` Equity $+10.21 con 5 fill e 5 round-trip chiusi (realizzato $+1.00 netto).
|
||||
- `[concentrazione]` 📌 **Il 70% di tutto il P&L cumulato ($+99.44) viene dagli ultimi 7 giorni.** Un risultato concentrato in una finestra non e' un tasso di rendimento: e' un evento.
|
||||
- `[vol]` BTC: implicita 36.3 contro realizzata 30g 44.3 (sotto); DVOL al 13° pctl di un anno; IV-rank espandente 0.04 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 50.8 contro realizzata 30g 53.0 (sotto); DVOL al 17° pctl di un anno; IV-rank espandente 0.12 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **88 giorni, 88 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-09-20T00:37:51+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
Giornata di consolidamento dopo lo strappo del 18/09: il libro resta a target su entrambe le gambe (BTC $+756 contro $+754, ETH $+629 contro $+632) e i 5 fill valgono $+1.00 realizzato netto con fee 0.0316 — ritagli, non decisioni. Il $+10.21 di equity è quasi tutto marcatura delle due long.
|
||||
|
||||
Quel che le regole non vedono è la forma della settimana. Dal 15 al 17 settembre la gamba BTC era ridotta a $+129 e poi $+107: il libro era sostanzialmente fuori dal rischio direzionale sull'asset principale. Il rientro pieno è del 18, lo stesso giorno del $+81.58. Da qui la concentrazione segnalata dalla regola `[concentrazione]` ha due letture ugualmente compatibili: TP01 ha ripreso un trend che ora conferma 3/3 su entrambi gli asset, oppure il rientro ha semplicemente coinciso con un rimbalzo e il merito è del calendario. Con 88 giorni e 88 round-trip non è distinguibile qui.
|
||||
|
||||
L'elemento insolito è la volatilità: implicita sotto la realizzata su entrambi (36.3 contro 44.3, 50.8 contro 53.0) con DVOL al 13° e 17° percentile dell'anno. Il gate di VRP01 resta chiuso e nessuna azione ne discende, ma la combinazione merita un'occhiata umana se persiste.
|
||||
|
||||
Salute piena: 24/24 giri, feed a 0 min.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -0,0 +1,58 @@
|
||||
# Giornale di bordo — 2026-09-20
|
||||
|
||||
*Scritto 2026-09-21T00:39:45+00:00. Numeri misurati; nessuna interpretazione automatica.*
|
||||
|
||||
## Mercato
|
||||
|
||||
| | chiusura | 1g | 7g | 30g | RV30 ann. | TSMOM 30/90/180 | DVOL |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **BTC** | $81,197.50 | -0.10% | +5.71% | +3.63% | 37.1% | ↑ ↑ ↑ (3/3 su) | 35.6 (8° pctl 1a) |
|
||||
| **ETH** | $2,645.45 | +0.47% | +6.80% | +5.10% | 45.7% | ↑ ↑ ↑ (3/3 su) | 51.5 (21° pctl 1a) |
|
||||
|
||||
## Libro
|
||||
|
||||
Equity **$4,536.18** · nozionale lordo $1,648 · leva lorda 0.36x · barra dati 2026-09-20 · 24 giri
|
||||
|
||||
| | TP01 | SKH01 | target | posizione | azione |
|
||||
|---|---|---|---|---|---|
|
||||
| **BTC** | +0.533 | flat | $+906 | $+909 | HOLD (a target) |
|
||||
| **ETH** | +0.435 | flat | $+739 | $+739 | HOLD (a target) |
|
||||
|
||||
## P&L
|
||||
|
||||
- giorno: **$-0.29** di equity (24 letture)
|
||||
- realizzato: +0.10 netto su 1 round-trip chiusi · 5 fill · fee 0.0985
|
||||
- cumulato dall'arming: **$+3,938.12** di equity — di cui $+3,838.97 versati/prelevati -> trading **$+99.15** · TWR **+8.71%**
|
||||
|
||||
## Salute
|
||||
|
||||
- giri di `book_execute`: 24/24
|
||||
- eta' feed SKH all'ultimo giro: 0 min
|
||||
|
||||
## Lettura
|
||||
|
||||
*Generata da regole dichiarate in `src/live/journal.py` — combina i numeri qui sopra e nient'altro. L'id fra parentesi quadre dice quale regola ha parlato. Non e' un giudizio: quello sta nella Nota.*
|
||||
|
||||
- `[stato]` A mercato su BTC $+909 (TP01 +0.533, SKH01 flat) e ETH $+739 (TP01 +0.435, SKH01 flat).
|
||||
- `[leva]` Leva lorda **0.36x** su un tetto di 1.00x (`max_notional_per_asset_frac` 0.50 x 2 asset, letto da `config/live.json`): margine 0.64x.
|
||||
- `[pnl]` Equity $-0.29 con 5 fill e 1 round-trip chiusi (realizzato $+0.10 netto).
|
||||
- `[concentrazione]` 📌 **Il 69% di tutto il P&L cumulato ($+99.15) viene dagli ultimi 7 giorni.** Un risultato concentrato in una finestra non e' un tasso di rendimento: e' un evento.
|
||||
- `[vol]` BTC: implicita 35.6 contro realizzata 30g 37.1 (sotto); DVOL al 8° pctl di un anno; IV-rank espandente 0.03 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[vol]` ETH: implicita 51.5 contro realizzata 30g 45.7 (sopra); DVOL al 21° pctl di un anno; IV-rank espandente 0.13 sotto la soglia 0.30 del gate di VRP01 (sleeve fermo).
|
||||
- `[evidenza]` 📌 Campione a oggi: **89 giorni, 89 round-trip**. A questa taglia il P&L non distingue l'edge dalla fortuna — il criterio di morte del libro (`edge_watch`) lavora su una finestra di 36 mesi.
|
||||
|
||||
## Analisi (agente)
|
||||
|
||||
*Scritta da `claude-opus-5` il 2026-09-21T00:40:36+00:00. Prosa di un modello: puo' sbagliare, e non e' una misura. Controllo sui numeri: **ok**.*
|
||||
|
||||
Il fatto nuovo non e' il P&L — $-0.29 su 24 letture — ma l'esposizione: BTC $+909 ed ETH $+739 sono i valori piu' alti dei dieci giorni elencati, e arrivano con l'equity ferma (ieri $4,536.47). Non e' capitale nuovo: e' TP01 che spinge (+0.533 e +0.435) dopo aver quasi lasciato BTC fra il 15 e il 17/09 ($+129, poi $+107). Il libro ha ricomprato quel rischio tre giorni dopo, e resta a 0.36x contro un tetto di 1.00x: il vincolo qui e' il segnale, non il cap.
|
||||
|
||||
Sulla concentrazione la regola dice il fatto, non la causa. Il 69% del cumulato $+99.15 cade nei sette giorni in cui BTC ha fatto +5.71% ed ETH +6.80% con il libro long: «un direzionale pagato da un rally» e' compatibile con questi numeri quanto «l'edge che si vede». A 89 giorni e 89 round-trip le due letture non si separano, e non sono separabili qui.
|
||||
|
||||
I due asset non concordano di segno sulla volatilita': implicita sotto la realizzata su BTC (35.6 contro 37.1), sopra su ETH (51.5 contro 45.7). VRP01 resta comunque fermo per l'IV-rank (0.03 e 0.13 contro 0.30), cioe' per l'espansione, non per il livello.
|
||||
|
||||
Merita un occhio umano il rapporto fra realizzato $+0.10 e fee 0.0985 su 5 fill. Salute pulita: 24/24 giri, feed a 0 min.
|
||||
|
||||
## Nota
|
||||
|
||||
*(vuota — campo dell'operatore)*
|
||||
@@ -69,7 +69,11 @@ chi lo riapre deve battere il motivo, non ripetere l'esperimento.
|
||||
solo LOWVOL 19-major regge standalone (FULL/HOLD 1.07) ma deflated-Sharpe 0.13 + storia 2.5a → **DEBOLE/
|
||||
forward STAT-MODE** (`2026-06-29-xsec-v2-nonmom.md`). (D) **MACRO regime-gate** (equity/credito/oro/tassi →
|
||||
de-risk crypto): **RIDONDANTE col trend** (corr→TP01 0.989; il gate lavora solo nel 2-3% dei giorni, TP01
|
||||
già flat nei crash) → SCARTATO (`2026-06-29-macro-regime-gate.md`).
|
||||
già flat nei crash) → SCARTATO (`2026-06-29-macro-regime-gate.md`). (D-bis, 10/09) **MACRO RISK-OFF ±2h intorno a
|
||||
FOMC/CPI su TP01** (`r0910_macro_riskoff.py`, §78): **REFUTED 0/3** — ΔSh −0,097, drift −1,24%/anno, Δ vero al
|
||||
4,2° pctl del null location-matched: nell'1% delle ore in finestra TP01 fa il 7% del guadagno (+1,05 bp/h
|
||||
contro +0,17). Come il weekend: la coda del trend vive nelle ore «pericolose». Un gate «fondamentale»
|
||||
che rifiuta i segnali intorno ai dati costerebbe quanto il funding, con segno scelto da chi lo propone.
|
||||
|
||||
- **Sweep strategie a 5 thread (2026-06-29) — 0 nuovi sleeve, 1 LEAD che rompe 2 muri su 3.** Ricerca
|
||||
parallela onesta su aree inesplorate (harness `altlib`+`xsec_v2_nonmom`, tutti i gate incl. il nuovo
|
||||
@@ -701,7 +705,7 @@ chi lo riapre deve battere il motivo, non ripetere l'esperimento.
|
||||
**0,93** (alto) e **supera 1 in backwardation** mentre `f_skew` resta piatto -> **la parte maggiore
|
||||
del difetto si annulla proprio dove il gate fa tradare il sleeve**, quindi f=0,73 e' plausibilmente
|
||||
**conservativo li'**. Non misurato (0/22 ingressi passano il gate) e in un crash i due pezzi vanno
|
||||
in **direzioni contrarie**. **`VRP_CFG["f"]` NON cambiato.** Replica indipendente del f: **0,714**.
|
||||
in **direzioni contrarie**. **`VRP_CFG["f"]` NON cambiato.** Replica indipendente del f: **0,714** (0,712 dal 09/09 sera, `cblib` causale; il 0,73 del 30/07 stampa 0,71).
|
||||
📌 **(7) CHIUSURE DI FAMIGLIA.** **Il filone funding si chiude sul QUARTO lato** (affollamento):
|
||||
su **53.430 ore / 3 anni** non predice ne' direzione ne' volatilita'. E i futures datati **non sono
|
||||
un quinto lato**: il loro basis **E'** il funding del perp (+7,33% implicito contro **+6,48%**
|
||||
@@ -1452,3 +1456,100 @@ una maschera a caso della stessa frequenza**.
|
||||
dall'aria seria. Se ne è accorta solo la riga `giorni in cui il LIBRO e' flat: 0 = 0.0%`.
|
||||
**REGOLA: stampare sempre la CARDINALITÀ di una maschera prima di usarla** — un filtro vuoto non
|
||||
solleva, produce il caso degenere e lo veste da risultato.
|
||||
|
||||
---
|
||||
|
||||
- **COLLAR01 "hold BTC gated dal trend + collar di opzioni <=15 giorni" — REFUTATO (2026-09-01)** —
|
||||
`scripts/research/r0901_btc_collar.py`, diario `2026-09-01-collar-btc.md`, registro §71.
|
||||
Chiesto dall'operatore in quattro battute: *hold BTC long o short coperto in opzioni* + *opzione
|
||||
max 15gg* + ***"ridurre la vincita, ma bloccare la perdita"*** (⇒ collar, non put protettiva) +
|
||||
*entrata gestita da indicatori (es. forte bull)*.
|
||||
🚨 **Si poteva riaprire dopo §46 perche' ne batte il MOTIVO, non perche' ne ripete la misura:**
|
||||
§46 cadde sul **beta +0,076 del libro** (*"non si assicura un libro che nei crash e' gia' quasi
|
||||
piatto"*), qui il sottostante e' un **hold di BTC, beta 1,0**. E §46 dichiarava non provata la
|
||||
copertura **gated su regime**, che e' questa.
|
||||
**L'entrata non ha aggiunto un solo parametro:** `tsmom_blend` media tre `np.sign()` su (30,90,180)
|
||||
⇒ valori in {−1,−1/3,+1/3,+1}, quindi *"forte bull"* = `|blend|==1` (52,5% dei giorni) contro il
|
||||
confronto dichiarato `|blend|>=1/3` (97,1%); il segno da' long **e** short.
|
||||
✅ **A1 CONFERMATA — il COLLAR riduce il maxDD: 36/48 celle** (§46: saliva in 162/162).
|
||||
🚨 **Corretto lo stesso giorno da PAVIMENTO-LEVA (§72): NON e' il pavimento a farlo.** La put da
|
||||
sola, a premio reale, **peggiora il DD in 16/16 celle**; nel collar la riduzione viene dal TETTO
|
||||
(compensa il bleed, e cappare l'upside abbassa il picco da cui il DD si misura).
|
||||
❌ **Ma il de-levering lo fa meglio in 45/48**, e il baratto costa **2-8 punti di drift per punto
|
||||
di DD** (Δdrift/ΔmaxDD **7,90** gate forte, **1,98** largo).
|
||||
🚨 **C9 in forma pura: il tetto taglia il 46,2% dei cicli VINCENTI, il pavimento para il 6,7% dei
|
||||
PERDENTI** — 7x piu' spesso sui vincenti: *troncatura, non protezione*.
|
||||
❌ **M1:** collar Sharpe 0,508 vs TP01 **0,852**; TP01+10% ⇒ **+0,000** di Sharpe e **+2,32pp** di
|
||||
maxDD — peggiora proprio cio' che dovrebbe proteggere.
|
||||
🚨 **IL RISULTATO TRASFERIBILE STA NELL'ESTENSIONE, NON NELLA GRIGLIA.** Le 3 celle vincenti
|
||||
stavano tutte **sul bordo**; estesa la famiglia vince **35/36** con il massimo **nell'angolo**
|
||||
(δput 0,02 / δcall 0,50, Sharpe **1,471**) — e il limite di quell'angolo e' *nessun pavimento,
|
||||
tetto ATM* = **una covered call**: **la pendenza porta fuori dalla domanda posta e dentro lo
|
||||
short-vol**. E quel 1,471 era **il prezzatore che si paga da solo**: **DVOL/RV-forward 1,320 a 7g,
|
||||
sopra nel 76,9% dei giorni** ⇒ riprezzato alla vol vera l'angolo cade a **0,511** (VRP = **72%**
|
||||
del drift; col gate largo **il 100%**, drift **−1,10%**). E **0,511 e' VRP01** (ShFULL 0,47 a
|
||||
f=0,73): *non una scoperta, VRP01 ri-trovato per una strada piu' lunga*. §3 lo blocca comunque.
|
||||
🚨 **USCITA ANTICIPATA (chiesta: 50-75% del tempo) — implementata e COSTA**, e produce il secondo
|
||||
risultato trasferibile. Cella onesta, gate FORTE: drift **+9,48% (scadenza) -> +3,84% (uscita al
|
||||
50%)**, esito da VINCE a **perde sotto 0,75**; gate LARGO **12,06% -> 1,88%**. Il meccanismo
|
||||
previsto c'e' (il VRP residuo scende **+3,38 -> +1,27pp**: uscire presto rinuncia davvero allo
|
||||
short-vol) **ma lo spread lo travolge**. 📌 **Corregge l'APPLICAZIONE di §46:** *"un roll anticipato
|
||||
non paga f"* vale per una copertura **solo LONG**; in un collar la **gamba venduta va RICOMPRATA**,
|
||||
quindi uscire prima paga f *esattamente* sulla parte che a scadenza si regolava gratis, ~2x piu'
|
||||
spesso. **L'asimmetria di §46 si INVERTE quando la struttura ha una gamba corta.**
|
||||
✅ **Controlli dell'apparato 3/3** (la lezione di §46): pranzo gratis riconosciuto, premio x10
|
||||
rifiutato, zero-cost finito.
|
||||
⚠️ **Cinque difetti miei catturati dai controlli:** bisezione zero-cost **invertita** (dava Sharpe
|
||||
−3,9); `dcall=NaN` che propagava NaN nella cassa; **C9 non consapevole della direzione** (`S1>S0`
|
||||
non e' "vincente" per un ciclo short — dava l'impossibile *"pavimento 0,0%"*); e due di
|
||||
contabilita' che **avrebbero adulato il collar** (la base che rollava lo spot pagando ~3,6%/a di
|
||||
fee inesistenti, e il roll che chiudeva lo spot senza motivo).
|
||||
📌 **Corregge un muro di §46:** il **tick da 5 USDC** e' della famiglia **USDC**; la catena che
|
||||
raccogliamo e' **100% inverse** ⇒ quel muro **non si applica** a questo filone.
|
||||
**Cosa lo riapre:** un **f di stress su un crash catturato** (la condizione che §3 pone allo
|
||||
short-vol), o un sottostante **senza coda destra grassa**. **Non** lo riaprono tenor, delta o
|
||||
griglie piu' fini: la pendenza e' monotona verso l'angolo, e l'angolo e' gia' misurato.
|
||||
|
||||
- **PAVIMENTO-LEVA "il pavimento come licenza di taglia" — REFUTATO 0/16 (2026-09-01)** —
|
||||
`r0901c_pavimento_leva.py`, diario `2026-09-01c-pavimento-leva.md`, registro §72. L'inverso di
|
||||
COLLAR01: non *quanto DD risparmia* ma *quanta TAGLIA autorizza a iso-DD*, l'unica cosa che il
|
||||
de-levering non puo' comprare. **La sola put a premio reale PEGGIORA il maxDD in 16/16 celle**
|
||||
⇒ `k_f = 1,000`, niente da licenziare. 🚨 **Corregge COLLAR01 lo stesso giorno**: il DD lo
|
||||
riduceva il **tetto**, non il pavimento (compensa il bleed e abbassa il picco). Pareggio del DD
|
||||
solo a **36-79% del premio reale**; prezzo equo ≈76%: non basta.
|
||||
📌 **Il meccanismo, trasferibile: i drawdown di BTC sono GRIND (290-818 giorni), la put copre
|
||||
UNA finestra** e para nel 0-9% dei cicli: compra protezione contro la cosa sbagliata.
|
||||
🚨 **La licenza del disaster-SL e' di CARTA**: sostituire `sl` con la distanza del pavimento
|
||||
darebbe 2,28x, ma su 8 finestre consecutive la perdita e' −33,6% → **77% dell'equity**.
|
||||
`disaster_sl_pct` NON si sostituisce con la distanza di un pavimento.
|
||||
**Gated sull'IV sottile** (la copertura dinamica che §46 non aveva provato): migliora, perde.
|
||||
**Cosa lo riapre:** scadenza lunga che copra il grind (fuori dal vincolo ≤15g dell'operatore),
|
||||
o un premio di varianza negativo su BTC. Non delta, tenor ≤15, gate IV: misurati.
|
||||
|
||||
- **CROLLI / CROLLO CATTURATO / XRP — 0 candidati, 1 debito, 2 conclusioni smontate in revisione (2026-09-09)** —
|
||||
`r0909_{libro_nei_crolli,crash_catturato,xrp_terza_gamba,deribit_universo}.py`, diario
|
||||
`2026-09-09-crolli-opzioni-monete.md`. Domanda dell'operatore: *"guadagnare anche nei crolli (opzioni se
|
||||
serve), piu' monete ma sempre in Deribit"*. **(1) Il libro nei crolli**: perde nel GIORNO (−0,48%/g su 160
|
||||
giorni ≤ −5%, positivo nel 18%), immune-o-positivo per finestra (4 POSITIVO / 6 immune / 2 PERDE su 12), **> 0
|
||||
in 8/8 peggiori finestre dal 2022** per la gamba short di SKH01 (108% dei guadagni, scomposizione esatta,
|
||||
marcata all'uscita). Beta 0,0769 sulla finestra di §46 (riprodotto). Peggior giorno canonico 2025-10-10
|
||||
−3,38%: crollo con TP01 a 0,505 (lo squeeze di §33 e' sulla lente hourly). 37,5/62,5 batte 12/12 episodi
|
||||
ma maxDD 12,1% vs 9,4%; gate fallisce in-sample (cache `skh_sigtab` ferma al 25/07). **(2) Crollo
|
||||
catturato**: l'archivio bite copre con bid/ask il 1-5/06/2026 (BTC −25%, ETH −30%), scadenza 19/06, inverse.
|
||||
ETH pre-crollo (4 strutture): f_net **0,76 [0,71-0,86] = 0,712 del rally** (rimisurato la sera con `cblib` causale; era 0,74 = 0,714); f a scadenza 1,04 ma **tautologico** (ambo le
|
||||
gambe ITM ⇒ solo f_net); peggior MTM **1,08** dopo il taglio alla larghezza — la prima stesura diceva 2,58 con
|
||||
"IV corta esplosa a 108 per lo skew": **artefatto** (quote 158% di spread a T→0; la IV 108 e' del 18/06, a T→0);
|
||||
put δ−0,10 costa **1,92×** il modello (punto nuovo, sopra l'1,27 di §46) e paga come il modello (0,94-1,04);
|
||||
ingressi durante il crollo bimodali (perde il 46%). **Crollo a vol BASSA** (picco DVOL 29°/46° pctl), gate
|
||||
IV-rank di VRP01 chiuso: **la condizione di §3 e' soddisfatta nella forma, non nella sostanza → non si
|
||||
riapre**. BTC pre-crollo non misurabile (griglia strike rada). **(3) XRP terza gamba** (harness di
|
||||
`r0822_sol_leg`, congelato): **diluisce come SOL** — hold-out −0,169 in 0/24, FULL −0,206 (L-FULL) /
|
||||
−0,066 (2024+), un anno buono (2024), de-levering; L-PULITA dal 2024 (2023 0,57% >1% dal riferimento;
|
||||
Coinbase delisto' XRP 2021-01→2023-07, riferimento Bitstamp prima); 5m flat 27-61%. Dato in
|
||||
`data/raw/alt_xrp_*`. **(4) Venue**: 32 perp USDC, liquidi ≥$10M/g solo XRP/ETH/BTC/SOL; XS01 13/19 (APT
|
||||
inactive); `BTCDVOL_USDC` future esiste ma vol 0 / spread 12%; PAXG perp non misurato (9 mesi, $0,18M/g).
|
||||
**Debito §5.18** (`cblib.spot_series` + `asof` un'ora avanti) **riparato la sera stessa**, insieme al DVOL
|
||||
(fino a 24h avanti): §11 0,714 → 0,712, verdetti invariati; `vrp_f_watch` f canonico 0,732 → 0,706 e la
|
||||
differenza col candidato +0,097 [+0,042, +0,140] ora esclude lo zero (criterio (a) 28/40 ancora aperto). **Errori dell'autore smontati dal revisore `fable`**: il 2,6× (artefatto) e "8/12 GUADAGNA"
|
||||
(l'ordine delle classi era il verdetto: immune → POSITIVO da' 4+6). Nessun cambio a libro/pesi/config.
|
||||
|
||||
|
||||
@@ -765,3 +765,18 @@ veicolo di RENDITA**, dove serve **2,5× meno capitale** ($254k contro $646k) pe
|
||||
vive sul drawdown. La scelta del veicolo dipende da quale dei due è l'obiettivo (N6).
|
||||
⚠️ E il fisco favorisce l'ETF: UCITS ad accumulazione paga **26% alla vendita**, il book **33%
|
||||
ogni anno**. Il differimento è un vantaggio strutturale reale.
|
||||
|
||||
- 💰 **SOLDI FERMI — «altri modi, non per forza trading» (2026-09-01/02).** `r0901d_soldi_fermi.py`,
|
||||
diario `2026-09-01d-soldi-fermi.md`. **On-venue chiuso con l'ELENCO INTERO:** `get_currencies`
|
||||
remunera **4 valute su 50** (USDE 4,32 · USDC 3,40 · BUIDL 3,20 · STETH 2,22) = le quattro gia'
|
||||
chiuse il 31/08 — non era un campione. ⚠️ **L'USDC su Deribit non e' fermo**: e' la base di sizing
|
||||
(cap = equity x 0,5) e il cuscino del disaster-SL; spostarlo rimpicciolisce il libro. Il capitale
|
||||
fermo vero sta **fuori**: ~€6.043 in XEON (27/07), che e' lo **split-cassa**. Tassi dal web
|
||||
(BOT 2,768% asta 08/2026 · DFR 2,25% · conti deposito 3,25-3,50% · sUSDe ~9% Q2 2026), fisco dalla
|
||||
memoria (12,5 / 26 / 33%). **In euro netti su €6k:** XEON 81 · BOT **133** · conto deposito
|
||||
**143** · sUSDe 350 ma **stesso emittente dell'USDE gia' in conto: split distrutto** · libro
|
||||
~600 atteso ma **trading, split distrutto**. **Il miglior guadagno sicuro vale +€62/anno = €5/mese**,
|
||||
contro €100/mese di bonifico = +4,07%/anno di drift. ✅ **L'operatore ha scelto di versare** (02/09),
|
||||
che e' il piano del 27/07: **€5.000 dentro, ~€1.000 fuori** (la protezione satura a qualunque
|
||||
quota > 0), il «~€600» letto come attesa e non come tasso, fondo d'emergenza da dichiarare. Nessuna
|
||||
azione di config: il cap e' dinamico. **In attesa di importo e data.**
|
||||
|
||||
@@ -692,6 +692,27 @@ e i vincoli di deploy (PRIIPs/UCITS/broker).
|
||||
📌 **IL DB** (`data/live/trades.db`, dentro il perimetro del backup): `fills` col contesto del
|
||||
segnale a quel giro (tp_frac, skh_sign, entry, target, posizione, equity), `roundtrips`
|
||||
**derivati e ricalcolati da zero**, `equity` oraria, `journal`. Sync **orario** in `cron_book`.
|
||||
📌 **IL RENDIMENTO DEL LIBRO E' UN TWR, NON `e1/e0-1`** (debito 14, trovato il 01/09 e chiuso il
|
||||
02/09/2026). `--report` stampava «+243%» su una serie che conteneva **un solo salto**, il
|
||||
versamento di $1.399,39 del 25/08: il 96,3% del numero era un bonifico. La riparazione
|
||||
(`movimenti_capitale`, che classifica ogni salto oltre `EQUITY_JUMP_ALERT` come *movimento* se
|
||||
supera 2× il massimo che il mercato misurato poteva fare a tetto di leva, altrimenti *ambiguo*)
|
||||
viveva gia' nel giornale e **non era arrivata al secondo lettore della stessa serie** — una
|
||||
variante di P1 che non e' un sorvegliante che ridichiara, ma una riparazione che non si propaga.
|
||||
Ora `journal.rendimento_twr` e' la funzione unica: spezza la serie all'equity PRIMA e DOPO ogni
|
||||
movimento certo e moltiplica i segmenti (gli ambigui non spezzano: restano nel rendimento,
|
||||
dichiarati accanto — P12). Il report stampa TWR, segmenti con le date, movimenti elencati,
|
||||
trading al netto, e il delta $ grezzo solo etichettato «movimenti INCLUSI». Verifica M23: la
|
||||
macchina riproduce il +10,80% del diario 01/09 sui suoi quattro punti. **Due limiti trovati in revisione (02/09):** l'intervallo che contiene un movimento certo esce INTERO dal rendimento (il suo P&L di mercato va in `certi`; errore massimo META' del movimento, per costruzione; il 25/08 ~$0,5) e a base di equity zero `twr` e `trading` sono entrambi None, perche' il salto 0→X del primo versamento e' invisibile al classificatore. I segmenti a lunghezza zero (movimento nel primo intervallo, o due consecutivi) non si stampano. **Seconda tornata di revisione, stesso giorno:** il classificatore era **cieco ~23 ore al giorno** — il
|
||||
feed 1h si ferma alle 00:00, `asof` dava la stessa barra alle due letture, mercato «fermo» = 0, e
|
||||
qualunque calo ≥10% del giorno sarebbe stato un movimento (un crash stampato come prelievo, dal
|
||||
25/08); con feed assente tutto era «ambiguo» e il report stampava `e1/e0-1` sotto l'etichetta TWR.
|
||||
Ora «mercato non misurabile» è uno stato: `ambiguo` con la ragione, `twr`/`trading` None con motivo.
|
||||
Il «limite +10% a mercato fermo» scritto la mattina era un artefatto della fixture piatta, cancellato.
|
||||
`pnl_giorno` chiama `rendimento_twr` (la frase «entrambi la chiamano» è vera solo da questa tornata),
|
||||
la pagina stampa il TWR, la testata Telegram qualifica il cumulato «di cui versati», e il bound di
|
||||
leva include la scala e il tetto di codice. Resta aperto (§5.15): il bound guarda solo BTC/ETH, e il
|
||||
31% dell'equity è USDE — un depeg pieno sarebbe scorporato come prelievo.
|
||||
**Le tre fonti si INCROCIANO e non si sovrascrivono:** log (ora vera) x jsonl (i fill) x venue
|
||||
(autorevole ma **TRONCA** — 1 trade su BTC, 0 su ETH). `reconcile()` riporta le divergenze e
|
||||
**non ripara niente da solo**: fra due fonti che non concordano, una riparazione silenziosa e'
|
||||
@@ -901,6 +922,56 @@ Sulle 4 finestre osservate 3 sono rientrate entro l'ora → il `:47` ne avrebbe
|
||||
**Se al prossimo martedì il `:47` becca comunque la manutenzione, la previsione è sbagliata** e lo
|
||||
slot non è quello descritto in `venue_probe.RELEASE_*`: rileggerlo prima di spostare ancora.
|
||||
|
||||
### La cadenza è ORARIA, e ora lo dicono tutte e tre le fonti (debito §5.7, chiuso il 2026-09-02)
|
||||
|
||||
Il docstring di `book_execute.py` prescriveva «ogni ~230 minuti» (la griglia di SKH01) mentre il cron
|
||||
gira ogni ora. Non era cosmetico: `r0823_sl_anchor.py` misura che l'accumulo di scatti del disaster-SL
|
||||
rotolante è funzione della **cadenza del cron** (1h: BTC 0 / ETH 1 in 7-8 anni · 4h: BTC 2 · 24h: ETH 5
|
||||
con 2 in 30 giorni), quindi chi avesse "corretto" il CRON verso il docstring avrebbe spostato il libro
|
||||
dalla riga 1h (rotolante: BTC 0 / ETH 1) alla riga 4h (BTC 2 contro 1 del pavimento; la peggiore misurata è 24h, ETH 5). La riparazione è il docstring stesso — cadenza ORARIA, la ragione (giro
|
||||
idempotente: girare più fitto della griglia non costa ordini, e compra latenza ≤1h per gli
|
||||
ingressi/uscite software di SKH01 e un **controllo** orario del rotolante — che si ri-ancora solo oltre la tolleranza di `ensure_disaster_sl`, mark +5,263%/−4,762% o taglia >10%, quindi lo stop siede fra −33,5% e −26,5% dal mark corrente: la prima stesura diceva «si ri-ancora ogni ora» ed era sbagliata, corretta in revisione lo stesso giorno) e il divieto scritto —
|
||||
più `tests/test_book_cadenza.py`, che **deriva** (P1) le tre dichiarazioni e le confronta: la riga
|
||||
citata nel docstring, quella dichiarata nell'intestazione di `cron_book.sh` (`47 * * * *`) e la
|
||||
crontab **installata** (`crontab -l`; se non è leggibile il test è SALTATO, non verde — P5). Il
|
||||
parser accetta solo espressioni regolari e rifiuta il resto. Vincolo del minuto tondo verificato
|
||||
(≠ `:00`). La regola generale è la stessa del `:47`: **una cadenza si difende nella sua ragione, e
|
||||
un commento che dichiara una cadenza diversa da quella che gira si paga quando qualcuno lo "corregge"
|
||||
nel verso sbagliato.**
|
||||
|
||||
### Il README ha pubblicato per 75 giorni la libreria che il progetto aveva dichiarato falsa
|
||||
|
||||
Ultimo commit del README: **2026-06-04**. Reset v2.0.0: **2026-06-19**. In mezzo, quindici giorni; dopo,
|
||||
75 in cui la prima pagina del repository descriveva FADE/HONEST/PAIRS/TSMOM/SHAPE, i portafogli PORT01-06,
|
||||
il paper trader su `strategies.yml` e l'esecuzione shadow su testnet — con Sharpe 7,84/10,06 e CAGR ~79%
|
||||
presentati come risultati correnti. Metà dei file citati non esisteva più (`strategies.yml`,
|
||||
`portfolios.yml`, `scripts/waste/`, `scripts/portfolios/`, `src/live/multi_runner.py`); i numeri erano
|
||||
esattamente quelli che `CLAUDE.md` §2 elenca sotto «non citare», e provenivano dalla libreria che il
|
||||
reset aveva dichiarato artefatto di un feed contaminato.
|
||||
|
||||
Nessun danno sui soldi — il README non decide niente. Il danno è di **citazione**, ed è la forma peggiore:
|
||||
un lettore esterno (o un agente nuovo) parte da lì. **La lezione non è "tenere aggiornato il README"**,
|
||||
che nessuno fa più di quanto già non faccia: è che **un reset invalida anche i documenti che nessuno
|
||||
rilegge**, e l'inventario di cosa cita numeri morti va fatto il giorno del reset, quando si sa che sono
|
||||
morti — non 75 giorni dopo, quando qualcuno ci inciampa per caso. Riscritto il 2026-09-02 contro lo stato
|
||||
vero, con ogni riferimento a file verificato e i numeri nella lente di §2.
|
||||
|
||||
### Un flag sconosciuto non è l'azione di default (2026-09-02)
|
||||
|
||||
Nessuno dei 18 script di `scripts/live/` usava argparse: leggevano `sys.argv` con `in` e `index`,
|
||||
quindi un flag sbagliato non era un errore — era il ramo `else`. Il costo si è misurato lo stesso
|
||||
giorno, dentro una revisione: `trades_db.py --help` è caduto in `sync()` e ha riscritto
|
||||
`meta.ultimo_sync`. Sui due script che scrivono davvero sarebbe stato peggio: la pagina di giornale
|
||||
e una riga di DB, oppure una chiamata al modello più un Telegram. **La riparazione non è argparse** —
|
||||
cambierebbe messaggi d'errore, codici d'uscita e `--help` di script che il cron già chiama, cioè
|
||||
romperebbe ciò che gira per riparare ciò che non gira mai. È una funzione di venti righe
|
||||
(`src/live/cli.valida`) che rifiuta ciò che non è dichiarato ed esce **prima di qualunque effetto**:
|
||||
`--help` → 0, flag ignoto → 2 con l'elenco dei previsti (P4). Il test deriva l'elenco degli script
|
||||
dalla cartella (P1: un file nuovo senza guardia fallisce, non passa inosservato), controlla che
|
||||
`valida` sia la prima istruzione di `__main__` (uscire dopo `connect()` sarebbe uscire dopo
|
||||
l'effetto) e — l'altra metà del contratto, P15/P16 — che **i flag che il cron usa davvero restino
|
||||
accettati**: una guardia che ferma il libro di bordo la notte stessa sarebbe peggio del difetto.
|
||||
|
||||
### Cosa resta scoperto
|
||||
|
||||
1. **La sonda dice di chi è il guasto, non lo aggira.** Col gateway giù il libro continua ad
|
||||
@@ -940,3 +1011,39 @@ impedire.** Danno reale quel giorno: nessuno — alle 09:47 l'equity era leggibi
|
||||
che si accende se la fixture viene rimossa.
|
||||
- ⚠️ **Resta il principio più largo:** oggi è deviato **solo** il watermark. `data/live/trades.db` e
|
||||
`data/live/book_executions.jsonl` sono esposti allo stesso errore (§5.12).
|
||||
|
||||
### Revisione settimanale in sola lettura (2026-09-09)
|
||||
|
||||
L'operatore ha chiesto «un processo di auto-revisione dei dati e della configurazione, che si autoregola,
|
||||
si autoaggiorna, crea sistemi nuovi valutando mercato e dati macro, e mi informa su Telegram». La risposta,
|
||||
misurata contro la memoria, e' stata in quattro pezzi: **la rilettura periodica manca e vale** (i nove
|
||||
sorveglianti guardano ognuno la sua grandezza; nessuno rilegge il sistema intero, e la revisione con un
|
||||
secondo modello ha trovato in una sessione due conclusioni false e un look-ahead); **l'autoregolazione dei
|
||||
parametri no** (e' cio' che `scale_watch`/`venue_watch` trattano come anomalia; A8 della chiave di scala:
|
||||
una riga di config, una di giornale, decisione dell'operatore); **la generazione automatica di strategie
|
||||
solo con un freno** (ogni candidato e' un trial, M3, e la ricerca non e' il vincolo binding dal 26/07: il
|
||||
miglior lead vale +0,046 €/giorno contro +4,07%/anno di drift per €100/mese in piu'); **il macro da
|
||||
internet no** (gate macro, DVOL direzionale e skew de-risk sono gia' HEDGE/ridondanti; un dato dal web
|
||||
non e' certificato — la APR USDC «pubblicata» e mai incassata). L'operatore ha scelto la prima («fai»).
|
||||
|
||||
Cosa gira: `scripts/cron_review.sh` (lunedi' 06:15 UTC, fuori dal :00 e dal martedi' 09:00, dopo il
|
||||
`cron_daily` delle 00:30) → `scripts/live/revisione.py --quiet` → `src/live/revisione.scrivi`. Materiale
|
||||
(~195k caratteri, sola lettura, ordine: regole e stato dichiarato, cio' che gira, cio' che e' successo):
|
||||
CLAUDE.md intero, `config/live.json`, `crontab -l`, `git log` 14 g + `git status`, `monitor_health`,
|
||||
stato dei sorveglianti (scale, vrp_f, usde, balance, venue_news, movimenti dichiarati, watermark — messi
|
||||
PRIMA delle voci grandi, cosi' il tetto totale non li taglia mai: alla prima stesura `usde_convert` era
|
||||
finito a 2 caratteri), 7 pagine di giornale, i diari della settimana. Prompt con dieci regole: sola
|
||||
lettura, ogni segnalazione cita la fonte, solo numeri del materiale (guardia `numeri_non_supportati` di
|
||||
`analista`, soglia 12), le decisioni vincolanti di §3 non si ripropongono, un'idea nuova si propone solo
|
||||
citando la memoria che ha ucciso la simile, i gate si elencano con data e criterio senza anticipare il
|
||||
verdetto, misurato ≠ dedotto, niente previsioni, URGENTE per cio' che serve all'operatore subito, cinque
|
||||
titoli fissi. Rapporto in `docs/revisioni/<data>.md` firmato (modello, data, «lettore fallibile P13»,
|
||||
tabella del materiale letto con caratteri e troncature); Telegram = la sola Sintesi, con taglio dichiarato.
|
||||
Modello `claude-fable-5-1` via `analista.interroga` (CLI `claude -p`, `--allowed-tools ""`, timeout 900 s):
|
||||
**non il modello che scrive il codice**, ed e' un test (`test_il_revisore_non_e_il_modello_che_scrive_il_codice`).
|
||||
Un modello muto o una risposta senza i titoli produce un rapporto «NON eseguita» col motivo e un 🚨: il
|
||||
silenzio non e' una revisione (P5). Guardie: `valida` di `cli` come prima istruzione (`test_cli_flag` lo
|
||||
deriva dalla cartella), `--secco` non chiama e non scrive (test sull'albero dei file), un giro scrive **UN
|
||||
solo file** (test), la cadenza e' dichiarata nel `.sh` e letta dalla crontab installata (`test_revisione`,
|
||||
stesso schema di `test_book_cadenza`). Costo: una chiamata a settimana. Cio' che il sorvegliante non puo'
|
||||
fare: eseguire. E' voluto.
|
||||
|
||||
@@ -157,3 +157,25 @@
|
||||
`fresh_5m`). **REGOLA: un guasto IN CORSO si misura al GIORNO PEGGIORE, non in media** — la prima
|
||||
stesura confrontava "tutto" con "ultimi 7g" e diluiva un guasto di 2 giorni al 13.5%, commettendo
|
||||
a 7 giorni lo stesso errore che dichiarava di evitare a 3 mesi. Diario `2026-07-30-vrp-quote-reali.md`.
|
||||
|
||||
- 🚨 **IL FEED 1h E' ETICHETTATO ALL'APERTURA, e `asof(ts)` guarda un'ora avanti (trovato in revisione
|
||||
il 2026-09-09).** La barra 1h etichettata 20:00 chiude col 5m delle 20:55 (verificato: close 1h 20:00 =
|
||||
close 5m 20:55 = 1603,5, mentre il 5m 19:55 fa 1572,95). Quindi `S.asof(ts)` su una serie indicizzata
|
||||
con quell'etichetta restituisce la chiusura di **ts+1h**: un look-ahead di un'ora senza eccezioni e
|
||||
senza NaN (D6, quarta occorrenza). Colpisce `cblib.spot_series` — cioe' il f 0,714 di §11 e ogni numero
|
||||
di `cblib` con lo spot, compreso `ST = S.asof(exp)` che e' il prezzo di un'ora DOPO la scadenza.
|
||||
✅ **RIPARATO la sera stessa in `cblib`** (`causale(s, cadenza)`: spot +1h, e **anche il DVOL
|
||||
giornaliero, +1 giorno** — la riga del giorno D e' la chiusura delle 23:00 di D, verificato contro la
|
||||
risoluzione 1h dell'API pubblica: seconda serie con lo stesso difetto, quinta occorrenza di D6). §11
|
||||
riprodotto al millesimo su worktree HEAD e rimisurato: 0,714 → **0,712** (le correzioni hanno segno
|
||||
opposto; DVOL orario di controllo 0,717). Il DVOL causale da feed giornaliero e' **stantio fino a 23 ore**
|
||||
(0,2 pt mediana, 3 pt max; nel crollo ±0,05 di f): il feed DVOL orario e' il passo aperto. Diario
|
||||
`2026-09-09b-debito-18-cblib-causale.md`.
|
||||
- **XRP** (`XRP/USDC:USDC`, dal 2022-03-16) ricostruito il 2026-09-09 con `rebuild_history --asset XRP` e
|
||||
spostato in **`data/raw/alt_xrp_*.parquet`** (namespace di ricerca come SOL: fuori dal feed attivo, non
|
||||
rinfrescato dal cron, `load_data("XRP")` fallisce, presidiato da `test_sol_leg` via `DERIBIT_INSTR`).
|
||||
Certificazione per anno vs riferimento USD: **Coinbase non ha XRP fra 2021-01 e 2023-07** (delisting per
|
||||
la causa SEC) → riferimento **Bitstamp** prima, Coinbase dopo; >1% 0,06% (2022) · 0,57% (2023) · ≤0,02%
|
||||
(2024+); mediana 5-6 bps; **5m flat 27-61%** (BTC/ETH 0,1-2%, SOL 21%): il 5m e' illiquido. Cache del
|
||||
riferimento in `data/_cache/` (gitignored, fuori backup: si riproduce con rete).
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@ null de-levering superato + eseguibilita' al capitale dichiarato.
|
||||
| 8 | VOL-SIZE | **LEAD RIDIMENSIONATO** — gate 22/12 **gia' fallito oggi** | dare a SKH01 una size per-trade regge a **23/23 ancore**, 8/8 anni, null di permutazione e trasferimento su V1 — ma vale **+0,07 di Sharpe di libro, un terzo della fortuna d'ancora del libro stesso (+0,196)**. Il vol-target di **libro** e' invece falsificato: compra peso SKH gia' respinto e peggiora l'eseguibilita' |
|
||||
| — | **XSR-REPRO** (integrita') | 🚨 **DIFETTO DI PRODUZIONE** | il numero 1.82 e' SPIEGATO e non era sbagliato (era su una **terza** lente, e su una barra non ancora chiusa) — ma cercandone la causa e' emerso che **`paper_xsr` registra ~41 minuti di mercato al giorno**, non un giorno. Tre gate pre-registrati leggono serie costruite cosi' |
|
||||
| 70 | **XSR-RENDITA** | **SCARTATO sotto la lente RENDITA** (non come sleeve) | il muro scende del 20,6% a iso-rischio, ma **mescolare i rendimenti di XSR01 da' lo stesso muro** (il meccanismo vale 0,8%, dentro la risoluzione MC) e un **conto remunerato allo stesso tasso lo eguaglia a vol 0 e senza secondo venue**. A drift zero il muro **sale**: si compra un drift scorrelato, non la scorrelazione. E l'haircut non pareggia un conto al 4% **nemmeno a haircut ZERO** |
|
||||
| 71 | **COLLAR01** | hold BTC gated dal trend, coperto da un collar (pavimento comprato, tetto venduto) a scadenza <=15g: "ridurre la vincita, bloccare la perdita" batte il de-levering? | **REFUTATO** — ✅ il pavimento FUNZIONA (maxDD scende in **36/48**, §46 battuto sul suo motivo: qui beta=1,0) ❌ ma costa **2-8 punti di drift per punto di DD** e il de-levering vince **45/48**. 🚨 Le 3 celle vincenti stanno sul **bordo**: estesa la famiglia vince 35/36 **nell'angolo covered-call**, e **il 72-100% di quell'edge e' il PREMIO DI VARIANZA** (DVOL sta **32% sopra** la RV-forward): riprezzato alla vol vera l'angolo cade da Sharpe **1,471 a 0,511** — che e' VRP01 (0,47), non una scoperta |
|
||||
| 72 | **PAVIMENTO-LEVA** | il pavimento (sola put, ≤15g) come LICENZA DI TAGLIA a iso-DD — l'inverso di §71 | **REFUTATO 0/16**, e **corregge §71**: la put da sola a premio reale **peggiora il maxDD in 16/16** (FORTE 51,8% → 55-76%; LARGO 71,9% → 75-82%) — nel collar il DD lo riduceva il **TETTO**, non il pavimento. Meccanismo (B2): **i drawdown di BTC sono grind di 290-818 giorni**, la put copre una finestra e para nel 0-9% dei cicli. Pareggio del DD solo a **36-79% del premio reale** (prezzo equo ≈76%: non basta). **La licenza del disaster-SL e' di carta** (B3): sostituire `sl` con la distanza del pavimento darebbe 2,28x, ma su 8 finestre la perdita e' −33,6% → **77% dell'equity**. Gated su IV sottile: migliora (+3,8 → +8,4%) ma **perde** ancora |
|
||||
| 73 | **SOLDI-FERMI** | altri modi per far rendere il capitale fermo, non per forza trading | **on-venue CHIUSO con l'elenco intero** (4/50 valute = le 4 del 31/08); l'USDC sul conto non e' fermo (sizing + cuscino); fuori, su €6k netti/anno: XEON 81 · **BOT 133** · **conto deposito 143** · sUSDe 350 (**stesso emittente dell'USDE: split distrutto**) · libro ~600 (trading). Miglior guadagno sicuro **+€62/anno**. ✅ L'operatore ha scelto di **versare** (piano 27/07: €5k dentro, ~€1k fuori) |
|
||||
| 5 | SKEW | **SCARTATO** (Q1, Q2) + **LEAD** (Q3, gate 2027-02-22) | il prezzo muove lo skew (t 3,1-9,4 su 8/8 test), **non il contrario** (max |t| in avanti 2,35 contro 2,08 atteso dal rumore). Ma Q3 e' grosso: **il f=0,73 di VRP01 e' per il 42% STRUTTURA A TERMINE e solo per il 25% skew** |
|
||||
| — | **HL-EXEC** (audit di fatto) | **3 falsificazioni misurate** | il pavimento vero e' **$10 (non $5)** e il taker **4,50 bps (non 5,0)** — ma il *"XS01 serve ~$20k"* e' **refutato del tutto** (nessuna soglia da min-order), il *"XSR01 ~$5.000"* e' **conservativo di 1,7x** (vero ~$3.000), e lo **slippage "rischio #1" di XSR01 e' refutato** con margine **21x** |
|
||||
| — | **SLIP-AUDIT** (audit di fatto) | **SCARTATO** = nessun costo nascosto a questa taglia | i backtest a 10 bps RT restano **conservativi di ~1,5 bps/lato**; nessuna evidenza di impatto sopravvive al null (p=0,274; estremi avversi **1/18 contro 4,7 attesi**). **Ma la misura ha una data di scadenza** |
|
||||
@@ -194,6 +197,18 @@ lati contro il **93%** di BTC_USDC, e il suo miglior bid sta a **un tick**.
|
||||
DVOL alla mediana del 7°/11° percentile storico, sottostante +25%/+44%.
|
||||
- ✅ **Replica indipendente del f del 30/07 su campione piu' lungo: f = 0,714 pooled** (IC95
|
||||
[0,690, 0,779], **0/19 osservazioni >= 1,0**). Attraversare lo spread costa **~10% del credito**.
|
||||
✅ **RIMISURATO il 2026-09-09 sera** (debito §5.18: `cblib` dava lo spot di un'ora DOPO e il DVOL di
|
||||
fino a 24 ore DOPO l'ingresso): allo stesso taglio (22/08, n=19) **f = 0,712 [0,664, 0,732]** — le due
|
||||
correzioni hanno segno opposto (solo spot 0,721, solo DVOL 0,693) e un DVOL orario di controllo da'
|
||||
0,717; a oggi (n=23) 0,712 [0,677, 0,741]. **Il verdetto non cambia.** Cambiano i numeri di
|
||||
contorno: Sharpe canonico BTC 32,58 → 29,61 con mediana onesta **2,12** (era 1,45) e banda
|
||||
[−0,19, 29,61]; ETH 3,90 → 7,64 (mediana onesta 3,35); media BTC fra le ancore −0,66% → +11,44%
|
||||
(cambia ancora segno); forfait fee 1,51× (BTC) / 1,87× (ETH); il regolamento e' il prezzo delle
|
||||
08:00 e non delle 09:00 (P&L +3,3%); sottostante nel campione +21% / +40% (era +25% / +44%: il vecchio
|
||||
regolamento del 21/08 era il prezzo delle 09:00, **+3,9%** sopra quello delle 08:00 — quanto pesa un'ora su
|
||||
un numero a due estremi). Lo stesso `cblib` rigira anche `r0730`: il f 0,73 [0,706-0,805] del 30/07 stampa
|
||||
ora **0,71 [0,680-0,756]** (n=27, 0/27 ≥ 1).
|
||||
Diario `2026-09-09b-debito-18-cblib-causale.md`.
|
||||
- ⚠️ **Correzione a un numero pubblicato:** il forfait fee del sleeve sovrastima il listino vero di
|
||||
**1,44x (BTC) / 1,80x (ETH)**, non di "~2x". Il segno (conservativo) regge, la taglia no.
|
||||
- ⚠️ **Rischio sul MINIMO, non sulle chiusure:** peggior mark infra-settimana mediana −6,4% del
|
||||
@@ -371,6 +386,13 @@ nuovo: **va decisa PRIMA del 23/10, non quel giorno.**
|
||||
(il collettore li accumula da solo), revisione comunque **2027-02-22**.
|
||||
- ✅ Confound di modello **ESCLUSO e misurato**: `f_markfit` = **0,992** (la mark IV di Deribit
|
||||
riprezza i propri mid entro l'1%) -> RR/BF sono un fatto di **prezzo**, non l'output del fit.
|
||||
📌 **Rigirato il 2026-09-09 sera con spot e DVOL causali** (debito §5.18): la scomposizione sul panel si
|
||||
muove di **≤0,005** per fattore (term 0,880 · skew 0,917 · spread 0,910 · fit 0,995 · f_tot 0,729) e Q1/Q2
|
||||
non si toccano (serie proprie); agli **ingressi** il controllo `f_markfit` passa da 1,045 a **0,991** e
|
||||
le osservazioni che lo superano da 22/28 a **25/28** — l'ora di spot in piu' stava nel controllo, non
|
||||
nell'effetto. Agli ingressi (n=28) si muove di piu': f_term 0,910 → 0,875, f_tot 0,732 → 0,706, e le
|
||||
correlazioni `corr(f_tot_mid, rr7)` +0,21 → −0,06 e `atm7` +0,02 → +0,34 **cambiano segno/ordine** — sono
|
||||
22-25 punti, dichiarate (P12), non usate.
|
||||
- ✅ **Controllo positivo cablato nello script:** con l'ancora **assunta** a :30 invece del `ts_max`
|
||||
osservato compare un **falso lead** a +15m (0,102 contro 0,031) — *"e' l'errore che avrei
|
||||
pubblicato senza quel controllo"*.
|
||||
@@ -3799,3 +3821,251 @@ rendita non e' lo strumento**, e che il gate del 23/10 sta per decidere su una g
|
||||
capitale **non e' comprabile**.
|
||||
|
||||
**Con questa sono 70 filoni e 0 candidati promossi.**
|
||||
|
||||
---
|
||||
|
||||
## 71 — COLLAR01 (hold BTC gated dal trend, coperto da un collar a <=15 giorni)
|
||||
|
||||
`scripts/research/r0901_btc_collar.py`. **Libro, pesi, cron, config INVARIATI. Nessun ordine.**
|
||||
Domanda dell'operatore, con tre precisazioni in corso d'opera che ne cambiano l'oggetto: *"hold BTC
|
||||
long o short con copertura in opzioni"* + *"opzione max 15gg"* + *"ridurre la vincita, ma bloccare
|
||||
la perdita"* (⇒ **collar**, non put protettiva) + *"l'entrata gestita da indicatori (es. forte bull)"*.
|
||||
|
||||
🚨 **PERCHE' SI POTEVA RIAPRIRE DOPO §46.** §46 fu refutato perche' **il beta del libro al
|
||||
sottostante e' +0,076** — *"non si assicura un libro che nei crash e' gia' quasi piatto"*. Qui il
|
||||
sottostante e' un **hold di BTC nudo, beta 1,0**: il motivo non si applica. E §46 dichiara di non
|
||||
aver provato la copertura **gated su regime**, che e' esattamente questa.
|
||||
|
||||
**L'entrata non aggiunge parametri:** `tsmom_blend` media tre `np.sign()` su (30, 90, 180) ⇒ valori
|
||||
in {−1, −1/3, +1/3, +1}, quindi *"forte bull"* = `|blend|==1` (3/3 orizzonti, a mercato **52,5%**
|
||||
dei giorni) contro il confronto dichiarato `|blend|>=1/3` (**97,1%**). Il segno da' la direzione:
|
||||
long **e** short, come chiesto.
|
||||
|
||||
**CALIBRAZIONE dalla catena vera** (161.964 quote a due lati, 95 giorni, DTE 1-15). **Lo skew e' il
|
||||
fatto strutturale:** IV_put/ATM contro IV_call/ATM = **1,249 vs 0,959** a |δ|0,10 · 1,132 vs 0,950 a
|
||||
0,20 · 1,072 vs 0,960 a 0,30 ⇒ **un collar delta-simmetrico e' un DEBITO netto**, si compra l'ala
|
||||
cara e si vende quella a buon mercato. 📌 **Corregge un muro di §46:** il **tick da 5 USDC** e' della
|
||||
famiglia **USDC**; la catena che raccogliamo e' **100% inverse** ⇒ **quel muro non si applica qui**.
|
||||
|
||||
| esito | misura |
|
||||
|---|---|
|
||||
| ✅ **A1 — il COLLAR riduce il maxDD** | scende in **36/48** celle (§46: saliva in 162/162). 🚨 **Corretto da §72 lo stesso giorno: NON e' il pavimento** — la put da sola, a premio reale, **peggiora il DD in 16/16**; la riduzione viene dal TETTO (compensa il bleed e abbassa il picco) |
|
||||
| ❌ **A3 — ma il de-levering lo fa meglio** | il collar batte il null a iso-maxDD in **3/48** |
|
||||
| ❌ **A2 — il prezzo del baratto** | Δdrift/ΔmaxDD mediano **7,90** (gate forte) / **1,98** (largo) |
|
||||
| 🚨 **C9 — troncatura, non protezione** | il tetto taglia il **46,2%** dei cicli **vincenti**, il pavimento para il **6,7%** dei **perdenti**: 7x piu' spesso sui vincenti |
|
||||
| ❌ **M1 — dentro il libro non aggiunge** | collar Sharpe 0,508 vs TP01 **0,852**; TP01+10% ⇒ Sharpe **+0,000** e maxDD **+2,32pp** |
|
||||
|
||||
🚨 **IL FATTO CHE VALE PIU' DEL VERDETTO — e che si vede solo estendendo la griglia (M4/M8).** Tutte
|
||||
e 3 le celle vincenti stanno sul **BORDO** (δput min, δcall max). Estesa la famiglia con 36 trial
|
||||
dichiarati verso l'angolo: **vince 35/36**, massimo **nell'angolo** (FORTE 7g, δput 0,02 / δcall
|
||||
0,50) = Sharpe **1,471**, maxDD 18,12%, drift **+29,99%/a**. Il limite di quell'angolo e' *nessun
|
||||
pavimento, tetto ATM* = **una covered call**: **la pendenza porta FUORI da cio' che l'operatore ha
|
||||
chiesto e DENTRO lo short-vol.**
|
||||
🚨 **E quel Sharpe e' il prezzatore che si paga da solo.** **DVOL / RV-forward mediana 1,320 a 7g**
|
||||
(DVOL sta sopra nel **76,9%** dei giorni), 1,253 a 14g. Riprezzando alla vol **realizzata**
|
||||
(look-ahead dichiarato, valore equo ex-post): l'angolo cade **1,471 -> 0,511** di Sharpe (VRP
|
||||
**+21,54pp = 72%** del drift) col gate FORTE, e **1,141 -> 0,114** col LARGO (VRP **+33,26pp = tutto**;
|
||||
drift **−1,10%**). Le celle con un pavimento VERO (δ0,10/0,30) passano da VINCE a **perde**.
|
||||
📌 **Cio' che sopravvive — Sharpe 0,511 — e' il numero che il progetto GIA' HA:** VRP01 a f=0,73 vale
|
||||
ShFULL **0,47**. *Non e' una strategia nuova: e' VRP01 ri-scoperto per una strada piu' lunga*, e §3
|
||||
lo blocca comunque (**"niente short-vol da modello in deploy"**).
|
||||
|
||||
🚨 **USCITA ANTICIPATA (chiesta dall'operatore: 50-75% del tempo) — IMPLEMENTATA, E COSTA.**
|
||||
`exit_frac` su tenor 14g (uscita a 7/9/10 giorni). Cella onesta δ0,10/0,30 gate FORTE: drift
|
||||
**+9,48% (scadenza) -> +8,59% (0,75) -> +6,11% (0,625) -> +3,84% (0,50)**, e l'esito passa da VINCE a
|
||||
**perde sotto 0,75**; gate LARGO **12,06% -> 1,88%**. ⚠️ **Il meccanismo previsto c'e'** — il VRP
|
||||
residuo scende da +3,38 a **+1,27pp**, quindi uscire presto rinuncia davvero allo short-vol — **ma lo
|
||||
spread lo travolge**. 📌 **E CORREGGE L'APPLICAZIONE DI §46:** *"f si paga solo sulla parte di valore
|
||||
che converge a intrinseco, quindi un roll anticipato non lo paga"* vale per una copertura **SOLO
|
||||
LONG**; in un collar c'e' una **gamba VENDUTA da RICOMPRARE**, e uscire prima paga f *esattamente*
|
||||
sulla parte che a scadenza si sarebbe regolata gratis, ~2x piu' spesso per unita' di tempo.
|
||||
**L'asimmetria di §46 si INVERTE quando la struttura ha una gamba corta.**
|
||||
|
||||
✅ **CONTROLLI DELL'APPARATO 3/3** (M15, la lezione di §46): pavimento a **premio zero** riconosciuto
|
||||
come vittoria (maxDD 51,83%→**36,76%**, drift +10,04%→**+35,69%**); premio **x10** rifiutato (drift
|
||||
**−33,96%/a**); zero-cost costruibile e finito.
|
||||
|
||||
⚠️ **CINQUE DIFETTI MIEI, tutti catturati dai controlli e non a occhio.** (i) **bisezione dello
|
||||
zero-cost invertita** — il premio cresce col delta, quindi a incasso insufficiente il tetto va
|
||||
*avvicinato*: dava Sharpe −3,0/−3,9 e drift −53%/a; (ii) `dcall=NaN` propagava NaN nella cassa allo
|
||||
smontaggio anticipato; (iii) **C9 non consapevole della direzione** — `S1>S0` non e' "vincente" per
|
||||
un ciclo **short**: era la causa dell'impossibile *"il pavimento para nello 0,0% dei perdenti"* con
|
||||
una put a 10 delta; (iv) la base senza opzioni **rollava lo spot** ogni tenor pagando ~3,6%/a di fee
|
||||
inesistenti (**avrebbe adulato il collar**); (v) al roll delle opzioni chiudevo e riaprivo **anche lo
|
||||
spot**, che non ha motivo di muoversi.
|
||||
|
||||
⚠️ **NON MISURATO, DICHIARATO:** la **lente reale** (quote vere, 123 giorni = ~8 cicli) non e' stata
|
||||
girata come backtest — sotto-potenziata, e su una finestra in cui BTC e' salito da ~64,7k a ~77,5k,
|
||||
**avversa a un collar per costruzione**; la catena e' servita a **calibrare**, dove 161.964 quote
|
||||
hanno potenza. Nessun DSR e nessun `study_family_honest`: il filone cade al **primo** gate (M5), e M2
|
||||
si spende su cio' che il primo gate lascia in piedi. A6/A7 restano previsioni non misurate. Il
|
||||
**funding** non e' nel motore (−2,16%/a) ma colpisce base e collar quasi allo stesso modo: abbassa
|
||||
entrambi, **non cambia il segno**.
|
||||
|
||||
**VERDETTO: `IL PAVIMENTO FUNZIONA — E' IL TETTO CHE NON SI PUO' PAGARE; E CIO' CHE VINCE SUL BORDO
|
||||
NON E' LA PROTEZIONE, E' IL PREMIO DI VARIANZA (VRP01)`** — REFUTATO.
|
||||
**Cosa lo riapre:** un **f di stress misurato su un crash catturato** (la stessa condizione che §3
|
||||
pone allo short-vol), o un sottostante **senza coda destra grassa** — il tetto costa perche' BTC vive
|
||||
li'. **Non** lo riaprono tenor, delta o griglie piu' fini: la pendenza e' monotona verso l'angolo, e
|
||||
l'angolo e' gia' misurato.
|
||||
|
||||
---
|
||||
|
||||
## 72 — PAVIMENTO-LEVA (il pavimento come licenza di taglia)
|
||||
|
||||
`scripts/research/r0901c_pavimento_leva.py` (riusa il motore di §71). **Libro, pesi, cron, config
|
||||
INVARIATI. Nessun ordine.** Domanda dell'operatore: *"possiamo usare quanto conosciamo del pavimento
|
||||
per studiare una strategia?"*. L'inversione di §71: non *quanto DD mi risparmia* ma *quanta TAGLIA
|
||||
mi autorizza a iso-DD* — l'unica cosa che il de-levering non puo' comprare, e la grandezza su cui
|
||||
vive il tetto di leva del progetto (`n · frac · scala · sl ≤ 0,50` ⇒ 1,67x).
|
||||
|
||||
❌ **0/16.** Sola put a premio reale, 4 δ × 2 tenor × 2 gate: **il maxDD SALE in 16/16 celle**
|
||||
(FORTE 51,83% → 55,4-75,8%, LARGO 71,94% → 74,8-81,7%), quindi `k_f = 1,000` ovunque e non c'e' taglia
|
||||
da licenziare. Monotono: piu' la put e' vicina, peggio va — **il bleed del premio E' il drawdown**.
|
||||
|
||||
🚨 **CORREGGE §71.** Stamattina A1 diceva *"il pavimento funziona"*. **E' il COLLAR a ridurre il DD,
|
||||
non il pavimento**: a premio zero la put lo riduce (51,83 → 36,76), a premio reale lo peggiora ⇒
|
||||
tutto il beneficio e' mangiato dal premio, e oltre. Nel collar la riduzione viene dal **tetto** (il
|
||||
suo premio compensa il bleed, e cappare l'upside **abbassa il picco** da cui il DD si misura).
|
||||
**Premio di pareggio: 36-79% del reale** (secondo la cella); il DVOL sta 1,32× sopra la RV, quindi
|
||||
un prezzo equo (~76%) sfiora il pareggio nelle celle migliori e lo manca nelle altre. *Il motivo di
|
||||
§46 (beta) non si applica a beta 1,0 — la put paga davvero — ma il verdetto di §46, il maxDD SALE,
|
||||
si riproduce per un motivo diverso.* Corretti §71, il suo diario e la memoria.
|
||||
|
||||
📌 **B2, IL MECCANISMO — e' il risultato trasferibile:** **i drawdown di BTC sono GRIND**, non
|
||||
crolli. maxDD FORTE da 2021-07-20 a 2022-05-06 (**290 giorni**), LARGO fino al 2023-10-16 (**818**).
|
||||
Una put a 7-14g copre UNA finestra; il DD che conta dura 20-60 finestre; la put scade OTM ogni
|
||||
settimana (para nel **0-9%** dei cicli, gate FORTE) mentre il premio sanguina. E non protegge
|
||||
nemmeno la finestra peggiore (14g FORTE: −22,9% nudo → **−23,6%** col pavimento: la put a 5δ sta a
|
||||
~22% dallo spot, al bordo esatto di cio' che e' successo). **Su BTC il pavimento compra protezione
|
||||
contro la cosa sbagliata.**
|
||||
|
||||
🚨 **B3 — LA LICENZA DEL DISASTER-SL E' DI CARTA, e va scritto prima che sia comodo.** Con la put a
|
||||
5δ/14g a **21,9%** dallo spot, sostituire `sl` con quella distanza nell'invariante darebbe **2,28x**
|
||||
(1,4× in piu'). Ma l'invariante limita UN episodio, la put limita UNA finestra, e il massimo su
|
||||
finestre consecutive **non e' limitato da nulla**: 1 finestra −23,6% (coperta), 2 −25,9%, 4 −28,8%,
|
||||
**8 finestre −33,6%** (non coperte). A 2,28x un grind di 112 giorni costerebbe il **77%
|
||||
dell'equity**. ⇒ **`disaster_sl_pct` NON si sostituisce con la distanza di un pavimento.**
|
||||
|
||||
**B4 — gated sull'IV** (§46: *"nessuna copertura dinamica e' stata provata"* — ora lo e'): put ON solo
|
||||
sotto il 25°/50° pctl di DVOL/RV. Migliora molto (FORTE +3,8% → **+8,4%**; LARGO +9,9% → +15,3%),
|
||||
**ma resta sotto la base** (+10,04% / +17,37%) e il DD resta ≥. L'incollatura ignora lo spread delle
|
||||
transizioni **a favore** del gated.
|
||||
|
||||
⚠️ **Non misurato, dichiarato:** nessun DSR (cade al primo gate); nessuna lente reale; **nessun
|
||||
pavimento a scadenza lunga** (30-90g, che coprirebbe piu' finestre di grind) perche' l'operatore ha
|
||||
vincolato la scadenza a **≤15 giorni** — e' l'unica variante che B2 lascia aperta, e sta fuori dal
|
||||
vincolo. Se il vincolo cadesse, sarebbe la prima misura da fare.
|
||||
|
||||
**VERDETTO: `IL PAVIMENTO NON LICENZIA TAGLIA: 0/16 — SU BTC COMPRA PROTEZIONE CONTRO I CROLLI, E
|
||||
LE PERDITE SONO GRIND`** — REFUTATO. **Cosa lo riapre:** una scadenza che copra il grind (fuori dal
|
||||
vincolo), o un premio di varianza *negativo* su BTC (mai misurato). Non lo riaprono delta, tenor ≤15
|
||||
o gate sull'IV.
|
||||
|
||||
---
|
||||
|
||||
## 73 — SOLDI-FERMI (altri modi, non per forza trading)
|
||||
|
||||
`scripts/research/r0901d_soldi_fermi.py`, diario `2026-09-01d-soldi-fermi.md`. **Nessun ordine.**
|
||||
*Non sono pareri fiscali.* Tassi dal web con fonte e data (cutoff del modello 05/2026).
|
||||
|
||||
✅ **On-venue chiuso con l'ELENCO INTERO:** `public/get_currencies` remunera **4 valute su 50** —
|
||||
USDE 4,32 · USDC 3,40 · BUIDL 3,20 · STETH 2,22 — esattamente le quattro chiuse il 31/08 (al tetto ·
|
||||
Italia esclusa · non acquistabile · e' ETH). ⚠️ **L'USDC su Deribit non e' fermo:** base di sizing
|
||||
(cap = equity x 0,5) e cuscino del disaster-SL. Il fermo vero sta **fuori**: ~€6.043 in XEON, lo
|
||||
**split-cassa** (P(perso tutto) 18% → 3,5%).
|
||||
|
||||
**In euro netti l'anno su €6.000** (BOT 2,768% asta 08/2026 · DFR 2,25% · conti deposito 3,25-3,50% ·
|
||||
sUSDe ~9%; fisco 12,5 / 26 / 33%; bollo 0,2%): XEON **81** · BOT **133** · conto deposito **143** ·
|
||||
sUSDe **350** ma **stesso emittente dell'USDE gia' in conto** ⇒ split distrutto · libro **~600 atteso**
|
||||
ma trading ⇒ split distrutto. **Il miglior guadagno sicuro e' +€62/anno = €5/mese**, contro €100/mese
|
||||
di bonifico = +4,07%/anno di drift. BOT e conto deposito distano €10/anno: scelta di liquidita' e
|
||||
controparte, non di rendimento.
|
||||
|
||||
✅ **Decisione dell'operatore (02/09): versare sul libro.** E' il piano del 27/07. Tre precisazioni,
|
||||
tutte gia' misurate: **€5.000 dentro, ~€1.000 fuori** (la protezione venue **satura a qualunque
|
||||
quota > 0**: l'ultimo migliaio compra ~€100/anno e butta via l'assicurazione); il «~€600» e' un'attesa
|
||||
a banda larga, non un tasso (libro a mercato il 22% dei giorni, TWR +10,8% in 70g, hold-out TP01
|
||||
~+0,05, fisco −30% a 10a); fondo d'emergenza da dichiarare. Operativamente nulla: cap dinamico,
|
||||
rilevatore di salti collaudato il 25/08. **In attesa di importo e data.**
|
||||
|
||||
---
|
||||
|
||||
## 74 — LIBRO-NEI-CROLLI (cosa fanno TP01 e SKH01 nei crolli, misurato invece che ricordato)
|
||||
|
||||
`scripts/research/r0909_libro_nei_crolli.py`, diario `2026-09-09-crolli-opzioni-monete.md`. **Libro,
|
||||
pesi, config, cron INVARIATI.** Lente: sleeve di ricerca, ancora canonica, pesi importati da
|
||||
`src/live/book` (P1); ⚠️ SKH01 marcato all'USCITA del trade (zero nell'87,7% dei giorni). Indice 50/50;
|
||||
giorno di crollo ≤ −5%; 12 peggiori finestre di 20 g non sovrapposte + 9 episodi nominati prima.
|
||||
|
||||
**Nel GIORNO del crollo il libro PERDE** (−0,48%/g su 160 giorni, positivo nel 18%; ≤ −10%: −0,51%/g con
|
||||
SKH01 +0,42). **Per finestra**: 4 POSITIVO / 6 immune / 2 PERDE; libro > 0 in 8/12 e **in 8/8 dal 2022**
|
||||
(mediana +3,3% contro indice −29%); le due perdite (2019, maggio 2021) sono TP01 long dentro un trend
|
||||
non girato. **La gamba short di SKH01 fa il 108% dei guadagni** (scomposizione ESATTA long+short==intera,
|
||||
assert). Beta **+0,0769** sulla finestra di §46 (riprodotto al millesimo). Peggior giorno canonico
|
||||
2025-10-10 −3,38%: un crollo con TP01 a 0,505 (lo squeeze di §33 sta sulla lente hourly, non si riproduce
|
||||
qui). 37,5/62,5 (§26) batte 12/12 episodi, Sharpe 1,81 vs 1,82, maxDD 12,1% vs 9,4%;
|
||||
`r0726_reeval_live_weight` da' `gate_pass False` su `delta_insample −0,0026` (cache segnali ferma al
|
||||
25/07: stesse cifre del 26/07, e la condizione che fallisce e' in-sample).
|
||||
🚨 **Errore dell'autore smontato in revisione:** la prima stesura provava GUADAGNA prima di immune e
|
||||
stampava **8/12 GUADAGNA**; nell'ordine dichiarato sono 4+6. *L'ordine delle regole era il verdetto.*
|
||||
**VERDETTO: `PERDE NEL GIORNO, POSITIVO PER FINESTRA DAL 2022, TUTTO SKH01 SHORT`** — nessun meccanismo
|
||||
nuovo: quello che guadagna nei crolli e' gia' in produzione, e la leva che lo aumenta (il peso) e' chiusa dal gate.
|
||||
|
||||
## 75 — CROLLO-CATTURATO (il f delle opzioni su un crollo a quote vere)
|
||||
|
||||
`scripts/research/r0909_crash_catturato.py`. **Nessun ordine.** L'archivio bite copre con bid/ask il crollo
|
||||
1-5/06/2026 (BTC −25%, ETH −30%; scadenza 19/06, 14-21 DTE, famiglia INVERSE); il campione di §11 partiva
|
||||
dal fondo. ETH, ingressi pre-crollo (286 snapshot = **4 strutture**): f_net **0,76 [0,71-0,86] = 0,712 del rally** (era 0,74 = 0,714 prima della riparazione di §5.18, la sera stessa; put 1,98× e picco 0,99 idem);
|
||||
f a scadenza 1,04 ma **tautologico** nell'83% (ambo le gambe ITM ⇒ solo f_net); peggior MTM **1,08**
|
||||
dopo il taglio alla larghezza; put δ−0,10 costa **1,92×** il modello (punto nuovo sopra l'1,27 di §46)
|
||||
e paga come il modello (0,94-1,04); ingressi durante il crollo bimodali (perde il 46%). BTC pre-crollo
|
||||
non misurabile (griglia strike rada). **Crollo a vol BASSA**: picco DVOL al 29°/46° percentile, gate
|
||||
IV-rank di VRP01 CHIUSO in tutte le ore. ⇒ la condizione di §3 e' soddisfatta nella forma, **non nella
|
||||
sostanza: non si riapre**. Servono un crollo con IV-rank > 0,30 e la famiglia USDC-lineare.
|
||||
🚨 **Due conclusioni dell'autore smontate in revisione:** «MTM 2,58× per lo skew (IV 108)» era un
|
||||
artefatto (quote con 158% di spread a T→0 oltre la larghezza; la IV 108 e' del 18/06 a T→0); «f a
|
||||
scadenza 1,04 = il modello prezza bene» era una tautologia. **Debito §5.18 RIPARATO la sera stessa** in `cblib`
|
||||
(spot +1h, e anche il DVOL giornaliero, +1 giorno): ETH pre-crollo f_net **0,76 [0,71-0,86]** (era 0,74),
|
||||
BTC durante **0,89 [0,74-1,05]** (era 0,79; con DVOL orario 0,83 — nel crollo il DVOL giornaliero stantio
|
||||
vale −0,04/+0,06 di f), put δ−0,10 1,98× (era 1,92), DVOL pre 36,4 / 49,6 (il 37,3 di BTC era la chiusura del
|
||||
PRIMO giorno di crollo: contaminato). Picco e percentili invariati. Verdetto invariato.
|
||||
**VERDETTO: `CATTURATO, MA A VOL BASSA E FUORI DAL GATE: f_net NON CAMBIA COL REGIME`** — nessuna riapertura.
|
||||
|
||||
## 76 — XRP-TERZA-GAMBA (l'unica altra moneta liquida di Deribit, coi meccanismi congelati)
|
||||
|
||||
`scripts/research/r0909_xrp_terza_gamba.py` (harness IMPORTATO da `r0822_sol_leg`). Dato in
|
||||
`data/raw/alt_xrp_*` (namespace di ricerca, non rinfrescato dal cron). Certificazione: >1% dal
|
||||
riferimento 0,06% (2022) · **0,57% (2023)** · 0,01-0,02% (2024+) — Coinbase dal rilisting 2023-07
|
||||
(delisting SEC), Bitstamp prima; **L-PULITA dal 2024**, come SOL; 5m flat **27-61%**.
|
||||
dSharpe hold-out **−0,169 in 0/24 ancore** (SOL −0,166); FULL −0,206 (L-FULL) / −0,066 (2024+); dCAGR
|
||||
−2,27 / −1,32 pp; de-levering: L-FULL 1,23 vs 2g×0,974 **1,44**, 2024+ 1,54 vs 1,53 pari; per anno 2022
|
||||
−0,55 · 2023 −0,66 · **2024 +0,54** · 2025 −0,44 · 2026 −0,25 (**1/5**). TP01 XRP Sharpe −0,01 /
|
||||
hold-out −0,49; SKH01 XRP maxDD 40,1% (il criterio di selezione di SKH01-V2-DD fallisce, come SOL);
|
||||
corr gamba-book +0,35/+0,37. Eseguibilita' non e' il vincolo (min 1 XRP = $1,42).
|
||||
⚠️ Due difetti del verdetto trovati dai test (Opus) e corretti: campione vuoto stampava «DILUISCE»
|
||||
(ora NON MISURABILE, P5); criterio per anno cablato al 2024 invece che derivato dalla lente (P1).
|
||||
**VERDETTO: `XRP DILUISCE COME SOL — UN ANNO BUONO, HOLD-OUT 0/24`** — nessuna proposta.
|
||||
|
||||
## 77 — UNIVERSO-DERIBIT (lettura del venue, €0)
|
||||
|
||||
`scripts/research/r0909_deribit_universo.py` (API pubblica, JSON con ora in `data/_cache`, `--fresh`).
|
||||
Lettura 2026-09-09 15:05Z: **32 perpetual USDC** aperti (+6 non aperti, fra cui **APT inactive**);
|
||||
liquidi ≥ $10M/g **solo XRP ($38M, spread 4 bps), ETH, BTC, SOL ($13M)**; tutto il resto $0,01-7M
|
||||
(TAO, HYPE, ADA, AVAX, NEAR…). XS01 **13/19** (mancano ARB, OP, APT, INJ, TIA, SEI → §50 resta).
|
||||
Opzioni USDC-lineari su 7 sottostanti (SOL 676, HYPE 442, XRP 442, TRX, AVAX). Trovati in revisione:
|
||||
**`BTCDVOL_USDC-30SEP26`** (unico long-vol diretto del venue: vol 0, OI 132, spread 12% → non
|
||||
negoziabile) e **PAXG perp** (oro, dal 2024-12, $0,18M/g: non misurato, non morto, muri di SOL/XRP).
|
||||
**VERDETTO: `QUATTRO STRUMENTI LIQUIDI, TRE GIA' NEL LIBRO O MISURATI`** — l'universo "piu' monete" di Deribit e' finito.
|
||||
|
||||
## 78 — MACRO-RISKOFF (TP01 flat ±2h intorno a FOMC e CPI: vale qualcosa?)
|
||||
|
||||
`scripts/research/r0910_macro_riskoff.py` (10/09, issue #9, ~3 min). Calendario letto dal web (Fed + BLS,
|
||||
61 FOMC + 88 CPI, 2019-03 → 2026-09), 684 ore in finestra = 1,04% del tempo. Verdetto pre-registrato
|
||||
(ΔSh>0 con fee · ≥95° pctl del null location-matched a 1.000 estrazioni · positivo in ≥70% degli anni).
|
||||
**REFUTED 0/3**: ΔSharpe giornaliero **−0,097**, drift **−1,24%/anno** (≈ −0,14 €/g a $4.455), negativo in
|
||||
6/8 anni, e il Δ vero sta al **4,2° percentile** del null — le ore intorno ai dati macro sono fra le MIGLIORI
|
||||
in cui TP01 e' esposto (+1,05 bp/h contro +0,17, t = 1,57): l'1% del tempo porta il 7% del guadagno. Stessa
|
||||
lezione del weekend (§3): la coda del trend vive nelle ore «pericolose». Il segno opposto NON si apre
|
||||
(ipotesi nuova, sotto l'MDE, selezionata sull'esito). Diario `2026-09-10b-macro-riskoff.md`.
|
||||
|
||||
|
||||
@@ -1,8 +1,21 @@
|
||||
# SPEC — la CHIAVE DI SCALA del libro live (`book_scale_k`)
|
||||
|
||||
**Filone SCALE-SPEC, ondata 2026-08-22, branch `research/wave-0822`.**
|
||||
**Questo documento e' una SPECIFICA. Non e' stata implementata: `src/`, `config/`, `scripts/live/`,
|
||||
`scripts/cron_*.sh` e `tests/` non sono stati toccati.** Il prototipo che dimostra la meccanica e'
|
||||
> ✅ **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`).
|
||||
> 📌 **(10/09, issue #5) A2 conta dal 2026-09-01**, primo giorno di `scale_watch`: 180 giorni cadono il
|
||||
> **2027-02-28**, ed e' la data «non prima del» del gate in CLAUDE.md §4. Il «2026-10-01» scritto il 01/09 era
|
||||
> il punto 5 (30 giorni di sorvegliante) scambiato per la data del gate: incompatibile con A2 per costruzione.
|
||||
> **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).
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
# Revisione settimanale — 2026-09-09
|
||||
|
||||
*Scritta il 2026-09-09T21:59:07Z dal modello `claude-fable-5-1`, revisore diverso dall'autore del codice, in SOLA LETTURA: nessun file del repo, nessuna config, nessun ordine e' stato toccato. E' l'opinione di un lettore fallibile (P13): ogni numero e ogni proposta vanno verificati alla fonte prima di agire. Guardia sui numeri: **sospetta** — 11 numeri non presenti nel materiale: 1.011, 1.341, 1.38279, 1.40085, 2.034, 2.43408, 4.47099, 4.47746, 400, 40008, 6879.*
|
||||
|
||||
## Sintesi
|
||||
|
||||
Revisione del 2026-09-09 21:59Z (primo giro, manuale: il cron e' del lunedi'). **Nessun URGENTE.** Nella settimana 02-08/09 il libro ha fatto 24/24 giri ogni giorno con feed SKH a 0 min, leva lorda 0,26x, equity $4.477,46 e TWR +7,41% (giornale 08/09). Tre cose da verificare, in ordine:
|
||||
|
||||
1. **`usde_watch` del 07/09 ha stimato un reward di 400,08 USDE**, dove i giorni normali (08-09/09) danno 0,38. E' la differenza fra i 2.434,08 USDE entrati e i 2.034,0 contati come trade, con `n_trades` fermo a **50**. Il registro `usde_convert.jsonl` mostra 8 + 30 + 16 = 54 ordini: ne mancano 4 da 100 = 400. Un lettore che vede al massimo 50 trade attribuisce al reward cio' che era un acquisto (D2, P2). Se `rendimento()` usa quella finestra, il tasso che `quota_da_decidere()` stampa ogni giorno e' inquinato.
|
||||
2. **Il cuscino USDC (debito §5.17) resta senza sorvegliante, e la sua misura non e' definita**: a `balance_watch` 21:42Z l'USDC ha balance 1.400,85 ma equity 1.382,79; con l'equity totale 4.470,99 (watermark) il cuscino al 30% vale ~$1.341, quindi lo slack e' **+$59 sul balance o +$41 sull'equity USDC**. Quale dei due conta va deciso prima di scrivere il sorvegliante.
|
||||
3. **Operatore**: il versamento di $2.410,14 del 04/09 e' solo *rilevato* (salto 13:47→14:47); `movimenti_dichiarati.jsonl` contiene il solo €25 del 25/08 e la Nota di giornale e' vuota 7/7 giorni. CLAUDE.md §2 chiede la dichiarazione il giorno stesso, e §1 («soldi fermi») dice ancora «in attesa di importo e data» per il piano €5.000: non si capisce se il 04/09 ne e' il primo pezzo.
|
||||
|
||||
## Cosa non torna
|
||||
|
||||
- **«2.434 USDE in 39 ordini»** (diario `2026-09-06-usde-quota-topup` §3, CLAUDE.md §1) non si riproduce dal registro: 8 ordini nel primo record, 30 eventi nella prima sonda, 16 nella seconda. Il totale USDE torna, il conteggio no (N11).
|
||||
- **Haircut, due affermazioni opposte in CLAUDE.md §1** (M28): una dice che il 5% e' confermato dal passaggio a X:SM (`available_funds` $2.011,09 contro $2.010,90 attesi *con* haircut 5%); poche righe sotto «oggi comunque non si applica», frase del mattino del 31/08 sotto S:SM. `config/live.json` `_nota_usde` dice ancora «il modello ATTIVO e' Segregated» e giustifica `depeg_crit 0.95` come «meta' del buffer di haircut (10%)»: col 5% il criterio dichiarato (P6) non descrive piu' la soglia, e la nota e' una diagnosi cablata (P4).
|
||||
- **GATE SCALA-01: data e condizione A2 non concordano** (CLAUDE.md §4). «Non prima del 2026-10-01» sono 30 giorni dal 01/09; A2 chiede k0 «≥180 g col criterio passato ogni giorno», e il criterio lo misura solo `scale_watch`, attivo dal 01/09 (`scale_history.jsonl`). 180 giorni dal 01/09 cadono nel 2027; dal 20/06 a fine 2026. Da quando si conta va chiarito prima del 01/10, non quel giorno.
|
||||
- **Sorveglianti di cui il materiale non prova il funzionamento** (P5): `venue_news.jsonl` ha 5 voci tutte `seed: true` viste il 2026-08-31T07:40Z e nulla dopo; `scale_watch_state.json` non ha timestamp (streak 0 puo' essere «non ho girato»); `edge_watch` e' citato a «stato 27/07» (CLAUDE.md §4); `fee_watch` non compare. `monitor_health` copre 8 monitor, nessuno di questi quattro.
|
||||
- **Data di arming**: CLAUDE.md §0 «LIVE dal 2026-06-20», `config/live.json` «ARMATO 2026-06-23», primo segmento TWR dal 2026-06-23 (diario `2026-09-02-debito-14`). Una delle tre non e' la stessa data.
|
||||
- **Richieste dell'analista senza risposta**: i 4 fill del 03/09 («dovrebbero essere ri-taglie»), la conferma del versamento 04/09, il SELL $−7 del 06/09, il round-trip a $+0,00 con fee 0,0055 del 08/09. Nessuna Nota. Non e' un guasto: e' un canale che non chiude (N9).
|
||||
- **CLAUDE.md §13 dice 1.011 test**; il diario `2026-09-09c` ne aggiunge 16 (`test_revisione.py`). E `scripts/cron_review.sh`, `scripts/live/revisione.py`, `src/live/revisione.py` sono `??` in `git status` mentre la crontab li chiama lunedi' 06:15 (P16 li fa girare, P11 non li salva).
|
||||
|
||||
## Cosa e' maturo da decidere
|
||||
|
||||
| gate | data | criterio scritto | stato nel materiale |
|
||||
|---|---|---|---|
|
||||
| STATARB | 2026-09-27 (fra 18 g) | soglie 24/07 + diagnostica sempre-short | monitor OK; Sharpe −1,61 al 26/08, nessuna lettura piu' recente. Si legge il 27/09 |
|
||||
| SCALA-01 | non prima del 2026-10-01 (22 g) | 9 condizioni tutte necessarie | A8/A9 fatti; A2 non maturo e ambiguo (sopra); A3, A6, A7 non risultano fatti |
|
||||
| USDE-01 | chiuso 28/08 | — | quota 68,79% (09/09) sotto `quota_max_frac` 0,85: dentro la decisione dell'operatore del 30/08 e 06/09 |
|
||||
| PREVDAY-01 | **senza data** | 10 condizioni, 8/10 | N10: un gate senza data non si chiude |
|
||||
|
||||
XSR01 (23/10) e DVOLSPREAD (24/10) cadono oltre 30 giorni. `edge_watch` e' continuo ma l'ultimo stato e' del 27/07: non determinabile se sia ancora 8/8.
|
||||
|
||||
Decisioni dell'operatore, oggi: (a) dichiarare il 04/09; (b) dire se il piano €5.000 e' iniziato; (c) definire il cuscino (balance o equity USDC). Nessuna decisione di §3 ha la colonna «cosa la riapre» soddisfatta: `weights_tilt_null` fallisce ancora (26/07, in-sample); il crollo catturato e' a vol bassa, sotto il gate e inverse (diario 09/09 §2).
|
||||
|
||||
## Proposte (nessuna azione eseguita)
|
||||
|
||||
1. **Agente**: verificare in `usde_watch` il limite di lettura dei trade e come `rendimento()` tratta la finestra del 07/09; rigenerare la stima con i 54 ordini (P2).
|
||||
2. **Agente**: riallineare `_nota_usde` (modello attivo X:SM, criterio di `depeg_crit` al 5%, `venue_cap_frac` null) e sciogliere la contraddizione di §1 datando le due frasi (M28).
|
||||
3. **Agente + operatore**: il sorvegliante del cuscino e' gia' descritto in §5.17 (`USDC − cuscino_richiesto` derivato da config, P1); manca solo la scelta balance/equity.
|
||||
4. **Agente**: uno stato senza timestamp non distingue «zero» da «non ho girato» (P5): `scale_watch_state.json` e `venue_news` meritano l'ora dell'ultimo giro, e i quattro sorveglianti fuori da `monitor_health` una riga li'.
|
||||
5. **Agente**: committare i file della revisione, aggiornare il conteggio test.
|
||||
6. **Operatore**: dare una data a PREVDAY-01 o chiuderlo (N10).
|
||||
7. Nessuna idea di strategia: la settimana ha gia' misurato crolli, opzioni e XRP contro `20-ondate` (diario 09/09 §0) e non ho nulla che batta i motivi elencati li'.
|
||||
|
||||
## Cosa ho letto e cosa mi mancava
|
||||
|
||||
Letto: CLAUDE.md intero; `config/live.json`; crontab; git log dal 26/08 e status; `monitor_health`; stati di scale_watch, scale_history, vrp_f_watch, usde_watch, usde_convert, balance_watch, venue_news, movimenti_dichiarati, watermark; giornali 02-08/09; diari 02/09 (tre), 06/09, 09/09 (tre).
|
||||
|
||||
Mancava: (1) stato con timestamp di `edge_watch`, `fee_watch`, `venue_watch`, `scale_watch`; (2) l'output di `usde_watch.rendimento()` e `quota_da_decidere()`; (3) la lettura corrente di `paper_statarb` (Sharpe a oggi, non al 26/08), dvolspread, xsr; (4) il contenuto di `cron_daily.sh`, cioe' quali sorveglianti chiama; (5) `venue_news` dopo il 31/08; (6) il diff di CLAUDE.md e memoria 40 non committati; (7) il registro fill della settimana (per i 4 fill del 03/09 e il +0,00 del 08/09); (8) un registro depositi del gateway, che «non espone get_deposits».
|
||||
|
||||
---
|
||||
## Firma e materiale
|
||||
|
||||
- revisore: `claude-fable-5-1` · cadenza settimanale · giornale 7 g · diari 7 g · commit 14 g
|
||||
- scritto da `scripts/live/revisione.py` (cron `scripts/cron_review.sh`); l'unico file scritto e' questo
|
||||
|
||||
| voce | caratteri | troncata |
|
||||
|---|---|---|
|
||||
| CLAUDE.md (stato, book, numeri, decisioni vincolanti, gate, debiti, regole) | 80,310 | |
|
||||
| config/live.json (unica autorita' dei guardrail live) | 7,184 | |
|
||||
| crontab installata (crontab -l) | 1,312 | |
|
||||
| git log dal 2026-08-26 + stato del working tree | 4,543 | |
|
||||
| monitor_health (i forward-monitor stanno registrando?) | 522 | |
|
||||
| stato scale_watch_state.json | 149 | |
|
||||
| stato scale_history.jsonl (ultime 3) | 259 | |
|
||||
| stato vrp_f_watch/state.json | 453 | |
|
||||
| stato usde_watch.jsonl (ultime 3) | 1,339 | |
|
||||
| stato usde_convert.jsonl (ultime 3) | 9,979 | |
|
||||
| stato balance_watch.jsonl (ultime 2) | 578 | |
|
||||
| stato venue_news.jsonl (ultime 5) | 1,564 | |
|
||||
| stato movimenti_dichiarati.jsonl | 405 | |
|
||||
| stato equity_seen.json (watermark) | 73 | |
|
||||
| giornale 2026-09-02 | 3,824 | |
|
||||
| giornale 2026-09-03 | 3,745 | |
|
||||
| giornale 2026-09-04 | 3,999 | |
|
||||
| giornale 2026-09-05 | 3,645 | |
|
||||
| giornale 2026-09-06 | 3,630 | |
|
||||
| giornale 2026-09-07 | 3,846 | |
|
||||
| giornale 2026-09-08 | 3,765 | |
|
||||
| diario 2026-09-02-debito-14-report-twr.md | 6,952 | |
|
||||
| diario 2026-09-02b-debito-7-cadenza-docstring.md | 6,061 | |
|
||||
| diario 2026-09-02c-flag-e-pulizia.md | 5,874 | |
|
||||
| diario 2026-09-06-usde-quota-topup.md | 6,571 | |
|
||||
| diario 2026-09-09-crolli-opzioni-monete.md | 17,894 | |
|
||||
| diario 2026-09-09b-debito-18-cblib-causale.md | 14,150 | |
|
||||
| diario 2026-09-09c-revisione-settimanale.md | 5,514 | |
|
||||
@@ -0,0 +1,40 @@
|
||||
# Revisione settimanale — 2026-09-14
|
||||
|
||||
## Revisione NON eseguita
|
||||
|
||||
Il modello `claude-fable-5-1` non ha risposto: **uscita 1: You've reached your Fable limit. Switch to another model, or manage usage credits at claude.ai/settings/usage?from=cc_cli_limit_message, to continue.**. Non c'e' una revisione questa settimana, e questo file esiste perche' un lunedi' senza rapporto si leggerebbe come «tutto bene» (P5).
|
||||
|
||||
---
|
||||
## Firma e materiale
|
||||
|
||||
- revisore: `claude-fable-5-1` · cadenza settimanale · giornale 7 g · diari 7 g · commit 14 g
|
||||
- scritto da `scripts/live/revisione.py` (cron `scripts/cron_review.sh`); l'unico file scritto e' questo
|
||||
|
||||
| voce | caratteri | troncata |
|
||||
|---|---|---|
|
||||
| CLAUDE.md (stato, book, numeri, decisioni vincolanti, gate, debiti, regole) | 85,904 | |
|
||||
| config/live.json (unica autorita' dei guardrail live) | 8,439 | |
|
||||
| crontab installata (crontab -l) | 1,753 | |
|
||||
| git log dal 2026-08-31 + stato del working tree | 3,693 | |
|
||||
| monitor_health (i forward-monitor stanno registrando?) | 610 | |
|
||||
| stato scale_watch_state.json | 149 | |
|
||||
| stato scale_history.jsonl (ultime 3) | 259 | |
|
||||
| stato vrp_f_watch/state.json | 453 | |
|
||||
| stato usde_watch.jsonl (ultime 3) | 1,415 | |
|
||||
| stato usde_convert.jsonl (ultime 3) | 9,520 | |
|
||||
| stato balance_watch.jsonl (ultime 2) | 579 | |
|
||||
| stato venue_news.jsonl (ultime 5) | 1,564 | |
|
||||
| stato movimenti_dichiarati.jsonl | 1,101 | |
|
||||
| stato equity_seen.json (watermark) | 73 | |
|
||||
| giornale 2026-09-07 | 3,846 | |
|
||||
| giornale 2026-09-08 | 3,765 | |
|
||||
| giornale 2026-09-09 | 3,705 | |
|
||||
| giornale 2026-09-10 | 3,833 | |
|
||||
| giornale 2026-09-11 | 2,348 | |
|
||||
| giornale 2026-09-12 | 3,611 | |
|
||||
| giornale 2026-09-13 | 3,823 | |
|
||||
| diario 2026-09-09-crolli-opzioni-monete.md | 17,894 | |
|
||||
| diario 2026-09-09b-debito-18-cblib-causale.md | 14,150 | |
|
||||
| diario 2026-09-09c-revisione-settimanale.md | 8,031 | |
|
||||
| diario 2026-09-10-cinque-punti-revisione.md | 15,020 | |
|
||||
| diario 2026-09-10b-macro-riskoff.md | 3,150 | |
|
||||
@@ -0,0 +1,86 @@
|
||||
# Revisione settimanale — 2026-09-16
|
||||
|
||||
*Scritta il 2026-09-16T19:39:30Z dal modello `claude-fable-5-1`, revisore diverso dall'autore del codice, in SOLA LETTURA: nessun file del repo, nessuna config, nessun ordine e' stato toccato. E' l'opinione di un lettore fallibile (P13): ogni numero e ogni proposta vanno verificati alla fonte prima di agire. Guardia sui numeri: **sospetta** — 7 numeri non presenti nel materiale: 1583.06, 1605.12, 1621.12, 5.11, 5.12, 5.16, 5.7.*
|
||||
|
||||
## Sintesi
|
||||
|
||||
- **URGENTE (conferma, non allarme sul saldo).** La crontab installata contiene dal 13/09 una riga nuova: `17 * * * * /opt/AI-OS/automations/TradingBook/scripts/cron_ombra.sh` («giro d'ombra del book TRASLOCATO … NON invia»). Non compare in CLAUDE.md (§1, §12), in nessun commit dal 02/09, ne' in `monitor_health`. Che non invii lo dice solo un commento. L'operatore confermi cosa legge (stesso gateway e stesso token del debito §5.11?), dove scrive, e che non tocchi `data/live` di questo progetto (P16).
|
||||
- **Un sorvegliante morto 13 notti senza che nessuno lo vedesse**: commit `a6a49d9` (16/09) «cc01_regime_watch … moriva ogni notte dal 03/09 su from src.live.cli». Non e' nella tabella di `monitor_health`; e' un effetto collaterale della riparazione del debito §5.16 (02/09). P5: «non vedo» letto come «va bene».
|
||||
- **Libro**: settimana di marcatura negativa; trading da arming **$-3.28** al 15/09 (giornale 2026-09-15), TWR +6.25%; SKH01 SHORT su BTC @ 75,707.5 dal 15/09, leva 0.13x. Esecuzione 24/24 ogni giorno, feed SKH 0 min. Nessun gate scade prima del **27/09 (STATARB)**.
|
||||
- Il giornale **2026-09-11 non ha la sezione «Analisi (agente)»**: modello muto o errore, non determinabile dal materiale.
|
||||
- Ripetute dal 09/09 e ancora aperte: contraddizione sull'haircut (M28); sorveglianti senza ora dell'ultimo giro.
|
||||
|
||||
## Cosa non torna
|
||||
|
||||
1. **Riga di cron non registrata** (crontab, commento datato 13/09/2026). Il progetto ha una regola scritta per questo caso: `tests/test_book_cadenza.py` legge la crontab «tutte le righe attive» ma cerca solo `cron_book.sh` (CLAUDE.md §5.7); un secondo processo orario sullo stesso conto e' fuori da ogni guardia. Il git log dal 02/09 non contiene un commit che lo descriva.
|
||||
2. **`cc01_regime_watch`** (commit `a6a49d9`): 13 notti di traceback dal 03/09 al 16/09. `monitor_health` elenca 9 monitor e non questo. `tests/test_cli_flag.py` verifica che `valida` sia la prima istruzione (§5.16), non che l'import riesca lanciando lo script come fa il cron. Non determinabile se altri job di `cron_daily.sh` abbiano lo stesso difetto: lo `.sh` non e' nel materiale.
|
||||
3. **Haircut, due testi che si contraddicono (M28).** CLAUDE.md §1: X:SM dal 31/08, «available_funds … $2.011,09, contro $2.010,90 attesi con haircut 5%» — cioe' l'haircut si applica. Piu' sotto, stesso §1: «haircut 5% … oggi comunque non si applica, vedi sopra». E `config/live.json` `_nota_usde` dice ancora «il modello di margine ATTIVO e' Segregated: Standard Margin (S:SM) … l'haircut oggi non si applica affatto», scritto prima del passaggio a cross. Segnalato il 09/09 (diario 2026-09-09c §5), non fra i cinque punti del 10/09.
|
||||
4. **Giornale 2026-09-11: 13 round-trip chiusi con 8 fill, fee 0.4046**, posizioni a fine giornata quasi ferme (BTC $+688 → $+687, ETH $+449 → $+444). Il 15/09, con gli stessi 8 fill, la fee e' 0.2114 e il netto BTC e' passato da $+689 a $+129. **Dedotto**: l'11/09 c'e' stato un ciclo intraday che la pagina, fotografia di fine giornata, non mostra. L'analista del 14/09 ha posto la stessa domanda («se sono ritagli di taglia va bene, se sono uscite no») e resta senza risposta.
|
||||
5. **Prompt dell'analista stantio**: l'Analisi del giornale 2026-09-10 scrive che la descrizione del libro nel prompt «indica un capitale molto inferiore all'equity di $4,454.86». Nessun commit successivo lo tocca; non determinabile se corretto.
|
||||
6. **Sorveglianti senza prova di vita.** `scale_watch_state.json` non ha un timestamp e `scale_history.jsonl` ha la sola riga del 01/09: per A2 di SCALA-01 («criterio passato ogni giorno», §4) non c'e' nel materiale un'evidenza giornaliera. `venue_news.jsonl`: ultimo `visto_il` 2026-08-31, tutte righe `seed`: o nessuna notizia in 16 giorni o il lettore e' fermo, non distinguibile. `edge_watch`: ultimo stato citato 27/07 (§4), 51 giorni fa.
|
||||
7. **Working tree**: sei pagine di giornale (10-15/09) e `docs/revisioni/2026-09-14.md` non committate, mentre `79afe41` mostra che i giornali si committano. La revisione del 14/09 non e' nel materiale: non determinabile se le sue segnalazioni siano state trattate.
|
||||
8. **Cuscino USDC in erosione, dato mancante**: `usde_watch` eq_usdc 1621.12 (14/09) → 1605.12 (15/09) → 1583.06 (16/09), quota 0.6377 → 0.6431. `monitor_health` dice `cuscino_watch` OK, ma il suo record (slack, stato) non e' nel materiale. Non e' un allarme: e' un buco di lettura.
|
||||
|
||||
## Cosa e' maturo da decidere
|
||||
|
||||
| gate | data | criterio scritto | stato che il materiale mostra |
|
||||
|---|---|---|---|
|
||||
| **STATARB** | **2026-09-27** (fra 11 giorni) | soglie 24/07 + diagnostica statica sempre-short (§4) | Sharpe **−1,61** al 26/08; nessun valore piu' recente nel materiale; `paper_statarb` OK. Cade fra la revisione del 21/09 e quella del 28/09: **chi lo legge il 27/09 non e' stabilito** |
|
||||
| `edge_watch` | continuo | (A) Sharpe 36m < −0,5 · (B) DD TP01 < 75% del buy&hold | ultimo stato 27/07 (+1,51, 8/8); nulla di piu' recente |
|
||||
| USDE-01, PROP-01 | passati | — | chiusi (§4) |
|
||||
|
||||
Prossimi, fuori dai 30 giorni: XSR01 23/10, DVOLSPREAD kill 24/10. Nessuna condizione della colonna «cosa la riapre» di §3 risulta soddisfatta: `weights_tilt_null` fallisce ancora (26/07, cache al 25/07, diario 2026-09-09 §1); book a $4,433.75 contro la soglia $15k di GTAA01; stato di `fee_watch` assente dal materiale.
|
||||
|
||||
Decisioni dell'operatore: (a) registrare o togliere il giro d'ombra; (b) nominare chi legge STATARB il 27/09.
|
||||
|
||||
## Proposte (nessuna azione eseguita)
|
||||
|
||||
1. **Ogni job lanciato dal cron sotto `monitor_health`** o con un «ultimo esito + ora» leggibile, inclusi quelli interni a `cron_daily.sh` e il giro d'ombra. Verifica preliminare: confrontare l'elenco dei job in `cron_daily.sh` con le 9 righe di `monitor_health`.
|
||||
2. **Un test che lanci ogni script di `scripts/live/` come lo lancia il cron** (processo separato, cwd diverso, `--help`): e' il caso #10 reso strutturale, nello spirito del debito §5.12 («non si chiede a ogni autore di ricordarsene»).
|
||||
3. **Chiudere la contraddizione haircut** in §1 e in `_nota_usde`: un solo testo che dica quale modello e' attivo e se l'haircut si applica.
|
||||
4. **Fill del giorno nella pagina di giornale** (ora, asset, verso, importo): chiesto il 09/09 come «registro fill della settimana»; avrebbe risposto da solo ai punti 4 e all'analista del 14/09.
|
||||
5. **Timestamp dell'ultimo giro** in `scale_watch_state.json` e in `venue_news`: senza, A2 di SCALA-01 non e' dimostrabile alla sua data.
|
||||
6. **Committare** giornali e revisione del 14/09.
|
||||
|
||||
Nessuna idea di strategia: nulla nel materiale batte i motivi di morte di `docs/memory/20-ondate-e-scartati.md`; `r0910_macro_riskoff` (REFUTED 0/3, diario 2026-09-10b) lo conferma per l'ennesimo risk-off.
|
||||
|
||||
## Cosa ho letto e cosa mi mancava
|
||||
|
||||
**Letto**: CLAUDE.md intero; `config/live.json`; crontab; git log dal 02/09 e status; `monitor_health`; stati di scale_watch, scale_history, vrp_f_watch, usde_watch, usde_convert, balance_watch, venue_news, movimenti dichiarati, watermark; giornali 09-15/09; diari 2026-09-09, 09b, 09c, 10, 10b. Questo giro e' di mercoledi' 19:39Z, non del lunedi' 06:15: e' fuori cadenza.
|
||||
|
||||
**Mancava**: `docs/revisioni/2026-09-14.md` e l'esito delle sue segnalazioni · il contenuto di `cron_daily.sh` e di `cron_ombra.sh` · il record di `cuscino_watch` (slack e stato) · stati con ora di `edge_watch`, `fee_watch`, `venue_watch`, `scale_watch` · l'output di `usde_watch.rendimento()` col suo n · il valore a oggi delle serie paper (STATARB in particolare, a 11 giorni dal gate) · i fill della settimana · l'esito della suite dopo `a6a49d9` · il testo corrente del prompt dell'analista.
|
||||
|
||||
---
|
||||
## Firma e materiale
|
||||
|
||||
- revisore: `claude-fable-5-1` · cadenza settimanale · giornale 7 g · diari 7 g · commit 14 g
|
||||
- scritto da `scripts/live/revisione.py` (cron `scripts/cron_review.sh`); l'unico file scritto e' questo
|
||||
|
||||
| voce | caratteri | troncata |
|
||||
|---|---|---|
|
||||
| CLAUDE.md (stato, book, numeri, decisioni vincolanti, gate, debiti, regole) | 85,904 | |
|
||||
| config/live.json (unica autorita' dei guardrail live) | 8,439 | |
|
||||
| crontab installata (crontab -l) | 1,753 | |
|
||||
| git log dal 2026-09-02 + stato del working tree | 1,680 | |
|
||||
| monitor_health (i forward-monitor stanno registrando?) | 610 | |
|
||||
| stato scale_watch_state.json | 149 | |
|
||||
| stato scale_history.jsonl (ultime 3) | 259 | |
|
||||
| stato vrp_f_watch/state.json | 454 | |
|
||||
| stato usde_watch.jsonl (ultime 3) | 1,407 | |
|
||||
| stato usde_convert.jsonl (ultime 3) | 9,520 | |
|
||||
| stato balance_watch.jsonl (ultime 2) | 581 | |
|
||||
| stato venue_news.jsonl (ultime 5) | 1,564 | |
|
||||
| stato movimenti_dichiarati.jsonl | 1,101 | |
|
||||
| stato equity_seen.json (watermark) | 73 | |
|
||||
| giornale 2026-09-09 | 3,705 | |
|
||||
| giornale 2026-09-10 | 3,833 | |
|
||||
| giornale 2026-09-11 | 2,348 | |
|
||||
| giornale 2026-09-12 | 3,611 | |
|
||||
| giornale 2026-09-13 | 3,823 | |
|
||||
| giornale 2026-09-14 | 3,736 | |
|
||||
| giornale 2026-09-15 | 3,731 | |
|
||||
| diario 2026-09-09-crolli-opzioni-monete.md | 17,894 | |
|
||||
| diario 2026-09-09b-debito-18-cblib-causale.md | 14,150 | |
|
||||
| diario 2026-09-09c-revisione-settimanale.md | 8,031 | |
|
||||
| diario 2026-09-10-cinque-punti-revisione.md | 15,020 | |
|
||||
| diario 2026-09-10b-macro-riskoff.md | 3,150 | |
|
||||
@@ -0,0 +1,94 @@
|
||||
# Revisione settimanale — 2026-09-21
|
||||
|
||||
*Scritta il 2026-09-21T06:15:02Z dal modello `claude-fable-5-1`, revisore diverso dall'autore del codice, in SOLA LETTURA: nessun file del repo, nessuna config, nessun ordine e' stato toccato. E' l'opinione di un lettore fallibile (P13): ogni numero e ogni proposta vanno verificati alla fonte prima di agire. Guardia sui numeri: **sospetta** — 9 numeri non presenti nel materiale: 2253, 3.83897, 4.55027, 5.8, 6333, 75.7075, 80.907, 871, 9915.*
|
||||
|
||||
## Sintesi
|
||||
|
||||
- **URGENTE (credenziali, operatore).** L'ombra di TradingBook gira ogni ora con la stessa chiave che manda gli ordini del libro. La garanzia che non invii è solo il codice (CLAUDE.md §1 OMBRA: «la chiave può fare trading»). La chiave di sola lettura può crearla solo l'operatore. Nel materiale non c'è traccia che esista, e la finestra d'ombra dura fino al 30/09.
|
||||
- Il libro è sano sui numeri: 24/24 giri ogni giorno dal 14/09 al 20/09. Equity, trading cumulato e conteggio dei round-trip si riconciliano giorno su giorno, per sottrazione mia, senza scarti. I versati ($3.838,97) coincidono con `movimenti_dichiarati.jsonl` più i $1.399,39 del 25/08.
|
||||
- `cc01_regime_watch` è morto ogni notte dal 03/09 al 16/09 senza che nessuno lo vedesse (commit `a6a49d9`). `edge_watch`, `fee_watch`, `scale_watch`, `venue_watch`, `vrp_f_watch` e `venue_news` non compaiono nella tabella `monitor_health` del materiale. Due di loro reggono una decisione vincolante e il kill del libro.
|
||||
- Il gate STATARB si legge fra 6 giorni, domenica 27/09. Le soglie non sono scritte in §4 e la prossima revisione cade il 28/09, dopo la lettura.
|
||||
- Da due pagine di giornale l'analista segnala due anomalie senza risposta: le Note dell'operatore sono vuote 7/7 e non c'è nessun commit dopo il 17/09.
|
||||
|
||||
## Cosa non torna
|
||||
|
||||
| # | Cosa | Fonte | Tipo |
|
||||
|---|---|---|---|
|
||||
| 1 | **Sorveglianti fuori da `monitor_health`.** Il guasto di cc01 (`from src.live.cli`) parte il giorno dopo la riparazione del debito 16 del 02/09 (`cli.valida` in ogni script), e `test_cli_flag.py` non l'ha visto. La tabella `monitor_health` copre 10 voci, fra cui nessuno dei sorveglianti elencati nella Sintesi. Eppure §3 (fee) dice «`fee_watch` allerta da solo», e in §4 lo stato di `edge_watch` è del **27/07**. | commit `a6a49d9`, `31f5915`; CLAUDE.md §5.16, §3, §4; tabella monitor_health | Misurato: gli assenti dalla tabella. Dedotto: che possano essere morti allo stesso modo. |
|
||||
| 2 | **Round-trip maggiori dei fill.** L'11/09 sono 13 su 8, il 15/09 sono 11 su 8. L'analista lo segnala due volte. Il 20/09 il rapporto si rovescia: 1 round-trip su 5 fill. Può essere l'abbinamento a lotti, ma nessuno l'ha verificato. | giornali 16/09, 17/09, 20/09 | Misurato il conteggio, non determinabile la causa. |
|
||||
| 3 | **Capitale sbagliato nelle istruzioni dell'analista.** «La taglia di capitale scritta nelle istruzioni dell'analista non coincide con l'equity della pagina.» È un valore ridichiarato invece che derivato, cioè la forma di P1. | giornali 16/09 e 18/09; CLAUDE.md §6 P1 | Misurato dall'analista, non da me. |
|
||||
| 4 | **Il P&L di SKH01 non è attribuibile.** Il 15/09 SKH01 va SHORT a 75.707,5 e BTC netto scende da $+689 a $+129, con realizzato −22,53. Il 18/09 SKH01 è flat, BTC chiude a $80.907,00 e il realizzato è solo −0,70. Col netting in software, l'uscita dello short è un riacquisto di long e il suo costo non appare in nessun «realizzato». SKH01 è «il meno affidabile dei cinque» (§2) e pesa 25%, ma dal giornale la sua resa live non si misura. | giornali 15-18/09; CLAUDE.md §1, §2 | Dedotto dal meccanismo di netting. |
|
||||
| 5 | **Giornali 17-20/09 non committati.** Dopo `8007158` non c'è nessun commit. Le pagine 10-15/09 erano già entrate in blocco (`e42ab53`). `docs/journal` non è nell'elenco di backup di §7, e l'Analisi del modello non è ricostruibile (P11). | git status; CLAUDE.md §7 | Misurato. |
|
||||
| 6 | **§2 invecchiato sul libro live.** «Esposto 14 giorni su 64, max 0,52x» è scritto al giorno 64. Oggi il campione è di 89 giorni e il libro è a mercato in tutte le pagine lette. La riga TWR (+6,66%, +$13,82, lettura 10/09) sta oggi a +8,71% e +$99,15. Il 69% di questi $99,15 cade negli ultimi 7 giorni: è un evento, non un tasso, e non va promosso a «numero da citare». | CLAUDE.md §2; giornale 20/09 | Misurato. |
|
||||
| 7 | **Contraddizione interna a §1.** Nella parentesi sull'autorità dei parametri si legge «tetto ALLERTA quota 50%», ma nello stesso paragrafo e in config `quota_max_frac` è **0,85** (M28). C'è anche una frase spezzata: «dopo un bonifico si **dal 10/09 lo fa…». | CLAUDE.md §1; config `usde` | Misurato. |
|
||||
| 8 | **Annuncio Deribit non letto.** `venue_news` ha registrato il 16/09 «Deribit Launches Its Biggest-Ever Product Expansion» con `match: []`. Un'espansione di prodotto può toccare margini, strumenti o collaterale senza contenere le parole chiave. Il debito 8 (Rulebook non sorvegliato) resta aperto. | venue_news.jsonl; CLAUDE.md §5.8 | Dedotto. |
|
||||
| 9 | **Revisioni saltate o dubbie.** Quella del 14/09 è «NON eseguita, limite Fable», quella del 16/09 ha «esito sospetta». Il testo non è nel materiale, quindi non posso dire se i loro punti siano stati chiusi. | commit `e42ab53` | Non determinabile. |
|
||||
|
||||
Sul cuscino: la quota USDE è 0,6333 (usde_watch 20/09), sotto il bersaglio 0,64, e `cuscino_watch` risulta OK in monitor_health. Lo slack corrente non è determinabile, perché il registro di `cuscino_watch` non è nel materiale.
|
||||
|
||||
## Cosa e' maturo da decidere
|
||||
|
||||
| Gate o scadenza | Data | Criterio scritto | Stato nel materiale |
|
||||
|---|---|---|---|
|
||||
| **STATARB** | 27/09 (fra 6 giorni) | «soglie 24/07 (non toccate) + diagnostica statica sempre-short» | Il monitor registra (OK). Lo Sharpe più recente che ho è −1,61 del 26/08. Le soglie numeriche **non stanno in §4**. Non anticipo il verdetto. Da decidere prima: chi lo legge domenica, e da quale documento prende le soglie. |
|
||||
| **Finestra OMBRA** | 30/09 | Nessun criterio di esito scritto: §1 cita solo lo strumento di confronto. | Esito dei confronti non nel materiale. Senza un criterio dichiarato prima, il 30/09 si decide guardando l'esito, e §4 lo vieta per i gate. Decide l'operatore. |
|
||||
| XSR01 · kill DVOLSPREAD | 23/10 · 24/10 | §4 | Cadono a 32 e 33 giorni, appena fuori dalla finestra dei 30. Entrambi i monitor sono OK. |
|
||||
| USDE-01, PROP-01 | passati | — | Chiusi (§4). La frase «`quota_da_decidere()` lo chiede ogni giorno da lunedì» è superata dalla quota decisa il 10/09. |
|
||||
| `vrp_f_watch` | — | criteri (a) e (b), §5.18 | pairs 30 (erano 28/40 il 09/09), `esclude_zero` true, `ready` false. Nessuna azione: la decisione §3 sul short-vol non è riaperta (IV-rank 0,03 e 0,13 contro 0,30, giornale 20/09). |
|
||||
|
||||
Le soglie di capitale ($15k GTAA01, $20k) non sono toccate: il watermark è $4.550,27.
|
||||
|
||||
## Proposte (nessuna azione eseguita)
|
||||
|
||||
1. **Operatore:** crei la chiave Deribit di sola lettura per l'ombra (§1) e dichiari entro il 30/09 il criterio con cui la finestra si chiude.
|
||||
2. **Agente:** verifichi che ogni script lanciato da `cron_daily` e dagli altri cron termini davvero, non solo cc01. Valuti anche di portare sotto `monitor_health` i sorveglianti che reggono una decisione (`fee_watch`, `edge_watch`, `scale_watch`), e di aggiornare in §4 lo stato di `edge_watch`.
|
||||
3. **Agente:** riporti in §4 le soglie STATARB del 24/07, copiate verbatim dalla fonte e senza toccarle, prima del 27/09.
|
||||
4. **Agente:** apra una issue per ciascuno dei punti 2 e 3 della tabella «Cosa non torna»: l'abbinamento dei round-trip e il capitale nel prompt dell'analista, che andrebbe derivato.
|
||||
5. **Attribuzione del P&L per sleeve** (registro virtuale TP01/SKH01 accanto al netto). È strumentazione, non una strategia. Prima di proporla va verificato in `docs/memory/40-produzione-e-deploy.md` se è già stata valutata.
|
||||
6. **Operatore o agente:** legga l'annuncio Deribit del 16/09, così da confermare che non tocchi i perp USDC, i margini X:SM o l'USDE.
|
||||
7. **Agente:** committi i giornali 17-20/09, chiarisca se il commit è automatico o no, e corregga §1 (50% contro 0,85, frase spezzata) e le due righe «live» di §2.
|
||||
|
||||
## Cosa ho letto e cosa mi mancava
|
||||
|
||||
**Letto:** CLAUDE.md per intero, `config/live.json`, crontab, git log dal 07/09 e git status, tabella monitor_health, gli stati di scale_watch, vrp_f_watch, usde_watch, usde_convert, balance_watch, venue_news, movimenti_dichiarati e equity_seen, i giornali dal 14/09 al 20/09. Nessun diario della settimana era nel materiale.
|
||||
|
||||
**Mi mancava:**
|
||||
- il testo delle revisioni del 09/09 e del 16/09, per seguire i punti ancora aperti;
|
||||
- gli ultimi record di `cuscino_watch`, `edge_watch`, `fee_watch` e `venue_watch`;
|
||||
- lo Sharpe corrente e il numero di barre dei quattro forward-monitor (STATARB soprattutto);
|
||||
- le soglie STATARB del 24/07;
|
||||
- l'output di `confronta_giornata.py` dell'ombra;
|
||||
- il giornale dell'11/09, citato dall'analista;
|
||||
- l'ora e il prezzo d'uscita dello short SKH01 del 15-18/09;
|
||||
- l'n corrente di `usde_watch.rendimento()`;
|
||||
- l'elenco degli script chiamati da `cron_daily`, con il loro codice d'uscita.
|
||||
|
||||
---
|
||||
## Firma e materiale
|
||||
|
||||
- revisore: `claude-fable-5-1` · cadenza settimanale · giornale 7 g · diari 7 g · commit 14 g
|
||||
- scritto da `scripts/live/revisione.py` (cron `scripts/cron_review.sh`); l'unico file scritto e' questo
|
||||
|
||||
| voce | caratteri | troncata |
|
||||
|---|---|---|
|
||||
| CLAUDE.md (stato, book, numeri, decisioni vincolanti, gate, debiti, regole) | 87,916 | |
|
||||
| config/live.json (unica autorita' dei guardrail live) | 8,439 | |
|
||||
| crontab installata (crontab -l) | 1,860 | |
|
||||
| git log dal 2026-09-07 + stato del working tree | 1,775 | |
|
||||
| monitor_health (i forward-monitor stanno registrando?) | 717 | |
|
||||
| stato scale_watch_state.json | 149 | |
|
||||
| stato scale_history.jsonl (ultime 3) | 259 | |
|
||||
| stato vrp_f_watch/state.json | 452 | |
|
||||
| stato usde_watch.jsonl (ultime 3) | 1,399 | |
|
||||
| stato usde_convert.jsonl (ultime 3) | 9,520 | |
|
||||
| stato balance_watch.jsonl (ultime 2) | 583 | |
|
||||
| stato venue_news.jsonl (ultime 5) | 1,571 | |
|
||||
| stato movimenti_dichiarati.jsonl | 1,101 | |
|
||||
| stato equity_seen.json (watermark) | 74 | |
|
||||
| giornale 2026-09-14 | 3,736 | |
|
||||
| giornale 2026-09-15 | 3,731 | |
|
||||
| giornale 2026-09-16 | 3,809 | |
|
||||
| giornale 2026-09-17 | 3,816 | |
|
||||
| giornale 2026-09-18 | 4,055 | |
|
||||
| giornale 2026-09-19 | 3,774 | |
|
||||
| giornale 2026-09-20 | 3,767 | |
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
# Campionamento ORARIO del BALANCE per valuta (USDC/USDE) — v2.0.0+.
|
||||
# Serve a rispondere a UNA domanda: l'APR USDC pubblicata da Deribit (3,4000%) e' accreditata
|
||||
# sul nostro conto? Il balance e' l'unica serie dove si vede: l'equity e' arrotondata a 2
|
||||
# decimali in trades.db e contiene il P&L non realizzato, e il gateway non espone il
|
||||
# Transaction Log (debito #11). Cadenza oraria per STRINGERE la finestra intorno all'accredito
|
||||
# (i reward USDE arrivano in un colpo verso le 12:00 UTC), non per campionare di piu'.
|
||||
# Minuto :42 = libero fra :25 (catena), :35 (usde), :47 (book); fuori dal minuto tondo
|
||||
# (rate-limit per-IP). SOLA LETTURA: nessun ordine, nessuna scrittura sul percorso soldi.
|
||||
export PATH="/home/adriano/.local/bin:$PATH"
|
||||
cd /opt/docker/PythagorasGoal || exit 1
|
||||
mkdir -p logs
|
||||
{
|
||||
echo "===== $(date -u '+%Y-%m-%dT%H:%M:%SZ') cron_balance ====="
|
||||
uv run python scripts/live/balance_watch.py || true
|
||||
echo "===== done $(date -u '+%H:%M:%SZ') ====="
|
||||
} >> logs/cron_balance.log 2>&1
|
||||
@@ -35,10 +35,16 @@ mkdir -p logs
|
||||
# partire anche quando book_execute fallisce per la stessa ragione. Non blocca (exit 2
|
||||
# = allarme inviato); l'azione a un allarme e' manuale, vedi src/live/venue_watch.py.
|
||||
uv run python scripts/live/venue_watch.py --quiet || true
|
||||
uv run python scripts/live/book_execute.py --execute
|
||||
# LOCK SUL CONTO (#36): cuscino_watch (cron_cuscino.sh, :53) puo' mandare ordini spot USDE
|
||||
# sullo stesso conto — separati solo dal tempo, giri fino a 659s misurati. `flock -n` (mai
|
||||
# `-w`, un SIGTERM a meta' ordine perde la riga di log): se cuscino_watch tiene il lock questo
|
||||
# giro si SALTA (rc 75) invece di sovrapporsi. Niente kill/timeout sul comando.
|
||||
flock -n -E 75 logs/.conto.lock uv run python scripts/live/book_execute.py --execute; rc=$?
|
||||
[ $rc -eq 75 ] && echo " book: SALTATO — conto occupato da un altro giro (logs/.conto.lock)"
|
||||
# Libro di bordo: materializza in data/live/trades.db (che il backup COPRE) l'ora vera dei
|
||||
# fill, che altrimenti vive solo in questo log — gitignored, fuori dal backup e ruotabile.
|
||||
# Va DOPO book_execute: dentro lo stesso blocco vede le righe appena scritte.
|
||||
# Va DOPO book_execute: dentro lo stesso blocco vede le righe appena scritte. Fuori dal lock:
|
||||
# e' sola lettura, non tocca il conto.
|
||||
uv run python scripts/live/trades_db.py --sync --quiet || true
|
||||
echo "===== done $(date -u '+%H:%M:%SZ') ====="
|
||||
} >> logs/cron_book.log 2>&1
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# MINUTO :25, e la scelta NON e' arbitraria:
|
||||
# :00 cerbero-bite sparava la sua spazzata full-chain (~44 chiamate/s) e saturava il rate limit
|
||||
# Deribit per-IP — 12.186 risposte 429 in 26 ore, il 96% nel minuto tondo.
|
||||
# :07 cron_book.sh (esecuzione del book live). Il feed 5m di SKH01 passa da li': e' la cosa che
|
||||
# :47 cron_book.sh (esecuzione del book live; era :07 fino al 25/08). Il feed 5m di SKH01 passa da li': e' la cosa che
|
||||
# NON deve trovare l'IP occupato.
|
||||
# :25 nessun altro job. Il giro dura ~3 minuti a 4 chiamate/s, quindi finisce ben prima del :30.
|
||||
#
|
||||
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
# Sorveglianza ORARIA del cuscino USDC di regolamento (debito §5.17, issue #3) — v2.0.0+.
|
||||
# Minuto :53 = DOPO il giro del book (:47, i cui fill si regolano in USDC) e fuori dal minuto
|
||||
# tondo (rate-limit per-IP), dal :25 (catena), dal :35 (usde) e dal :42 (balance).
|
||||
# PUO' INVIARE ORDINI: sotto zero di slack lancia usde_convert --esegui (vende USDE), con le
|
||||
# guardie dichiarate nel docstring di scripts/live/cuscino_watch.py; lo interruttore e' lo
|
||||
# stesso del libro (execution_enabled in config/live.json).
|
||||
# LOCK SUL CONTO (#36): cuscino_watch e book_execute (cron_book.sh, :47) sono separati solo dal
|
||||
# tempo e possono mandare ordini sullo stesso conto — giri fino a 659s sono stati misurati.
|
||||
# `flock -n` (mai `-w`: un'attesa farebbe scivolare il giro orario) su logs/.conto.lock: se
|
||||
# book_execute lo tiene, questo giro si SALTA (rc 75) invece di sovrapporsi.
|
||||
export PATH="/home/adriano/.local/bin:$PATH"
|
||||
cd /opt/docker/PythagorasGoal || exit 1
|
||||
mkdir -p logs
|
||||
{
|
||||
echo "===== $(date -u '+%Y-%m-%dT%H:%M:%SZ') cron_cuscino ====="
|
||||
flock -n -E 75 logs/.conto.lock uv run python scripts/live/cuscino_watch.py --quiet; rc=$?
|
||||
[ $rc -eq 75 ] && echo " cuscino: SALTATO — conto occupato (logs/.conto.lock)"
|
||||
# (#30) giro orario dei sorveglianti a max_age<=6h: notifica solo il NUOVO, marcatore in
|
||||
# data/live/monitor_health_detto.json. Fuori dal lock: e' sola lettura, non tocca il conto.
|
||||
uv run python scripts/live/monitor_health.py --quiet --solo-orari || true
|
||||
echo "===== done $(date -u '+%H:%M:%SZ') ====="
|
||||
} >> logs/cron_cuscino.log 2>&1
|
||||
@@ -31,10 +31,22 @@ mkdir -p logs
|
||||
# Schema fee Deribit (nuovo dal 2026-08-01, annunciato senza numeri): legge il tier BASE
|
||||
# dall'endpoint pubblico e applica la regola decisa in anticipo (<=5bps nulla, >10bps peso SKH01).
|
||||
uv run python scripts/live/fee_watch.py --quiet || true
|
||||
# Cosa il venue ANNUNCIA (debito #8: la sonda venue_probe legge se RISPONDE, non cosa dichiara).
|
||||
# Feed RSS pubblico degli exchange-update. NON interpreta: classifica solo l'urgenza, su parole
|
||||
# DERIVATE da deribit._CONTRACT e config/live.json. In una settimana quattro cambi di policy
|
||||
# materiali erano arrivati a mano; le specifiche dei perp USDC cambiate il 18/08 erano annunciate
|
||||
# il 14/08, e check_specs() se ne accorge solo DOPO.
|
||||
uv run python scripts/live/venue_news.py --quiet || true
|
||||
# f delle strutture VRP dalle quote REALI (canonico vs candidato bocciato dal gate 30/07).
|
||||
# Criterio di sufficienza pre-registrato (>=40 coppie, IC95 <=0.12): avvisa da solo quando
|
||||
# il campione basta, invece di lasciare la misura appesa a un promemoria.
|
||||
uv run python scripts/live/vrp_f_watch.py --quiet || true
|
||||
# La CHIAVE DI SCALA del libro: la config dichiara una scala che il giornale non ha autorizzato?
|
||||
# Il criterio che la autorizzo' passa ancora? Il PRODOTTO frac x scala x n_asset ha superato il
|
||||
# tetto? Tre domande, tre azioni diverse. Non impedisce la modifica: la rende visibile entro 24h
|
||||
# e attribuibile — una chiave di scala e' il parametro che si alza "solo un po'" dopo un mese
|
||||
# buono, e lo Sharpe (invariante alla scala) non lo vedrebbe mai. SPEC-scale-key §6.4.
|
||||
uv run python scripts/live/scale_watch.py --quiet || true
|
||||
# I forward-monitor stanno registrando? Tre gate pre-registrati (27/09, 23/10, 24/10) si
|
||||
# decidono su queste serie: un monitor fermo produce silenzio, e il silenzio sembra uno zero.
|
||||
# Va DOPO tutti i monitor, altrimenti misura lo stato di ieri.
|
||||
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
# Revisione SETTIMANALE in sola lettura (dal 2026-09-09): un revisore diverso dall'autore
|
||||
# (claude-fable-5-1) rilegge CLAUDE.md, config, crontab, commit, monitor_health, giornale e diari,
|
||||
# e scrive docs/revisioni/<data>.md + una sintesi Telegram. Propone, non esegue: l'unico file
|
||||
# scritto e' il rapporto. Cadenza: lunedi' 06:15 UTC — fuori dal minuto :00 (rate-limit) e dallo
|
||||
# slot di release Deribit (martedi' 09:00), dopo il cron_daily delle 00:30 (giornale di domenica chiuso).
|
||||
export PATH="/home/adriano/.local/bin:$PATH"
|
||||
cd /opt/docker/PythagorasGoal || exit 1
|
||||
mkdir -p logs
|
||||
{
|
||||
echo "===== $(date -u '+%Y-%m-%dT%H:%M:%SZ') cron_review ====="
|
||||
uv run python scripts/live/revisione.py --quiet || true
|
||||
echo "===== done $(date -u '+%H:%M:%SZ') ====="
|
||||
} >> logs/cron_review.log 2>&1
|
||||
@@ -73,7 +73,8 @@ def invia(con, g: date, voce: dict, analisi: str, stato: str, motivi: list[str],
|
||||
def scrivi(con, g: date, modello: str, secco: bool = False, verbose: bool = True,
|
||||
telegram: bool = True) -> str:
|
||||
voce = J.costruisci(con, g)
|
||||
J.salva(con, voce) # le sezioni misurate prima, cosi' il modello le legge
|
||||
if not secco: # --secco: stampa e NON salva (#34) — niente scrittura
|
||||
J.salva(con, voce) # le sezioni misurate prima, cosi' il modello le legge
|
||||
pagina = J.rendi_markdown(con, voce)
|
||||
st = storico(con, g)
|
||||
prompt = A.costruisci_prompt(pagina, st)
|
||||
@@ -115,7 +116,21 @@ def scrivi(con, g: date, modello: str, secco: bool = False, verbose: bool = True
|
||||
return stato
|
||||
|
||||
|
||||
USO = """uso: analista.py [--giorno AAAA-MM-GG] [--modello NOME] [--secco] [--no-telegram] [--quiet]
|
||||
|
||||
(nessun flag) scrive l'analisi di OGGI nel campo `analisi` e manda la notifica
|
||||
--giorno G analizza il giorno G
|
||||
--modello M modello da usare (default: src.live.analista.MODELLO_DEFAULT)
|
||||
--secco stampa e NON salva, niente Telegram
|
||||
--no-telegram salva ma non manda la notifica
|
||||
--quiet non stampa
|
||||
|
||||
⚠️ SPENDE una chiamata al modello e MANDA un messaggio Telegram, salvo --secco/--no-telegram."""
|
||||
|
||||
if __name__ == "__main__":
|
||||
from src.live.cli import valida
|
||||
valida("analista.py", USO, flag=("--secco", "--no-telegram", "--quiet"),
|
||||
con_valore=("--giorno", "--modello"))
|
||||
con = T.connect()
|
||||
g = date.fromisoformat(_arg("--giorno", datetime.now(timezone.utc).date().isoformat()))
|
||||
st = scrivi(con, g, modello=_arg("--modello", A.MODELLO_DEFAULT),
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env python
|
||||
"""balance_watch.py — registra il BALANCE esatto per valuta. SOLA LETTURA, nessun ordine.
|
||||
|
||||
PERCHE' ESISTE. Il 2026-08-30 e' emerso che Deribit pubblica una APR anche per l'USDC
|
||||
(3,4000%, contro 4,1071% dell'USDE): se fosse accreditata, il guadagno del passaggio a USDE
|
||||
non sarebbe il tasso ma lo SPREAD (0,71 punti), e la pista USDE varrebbe ~$4,6/anno invece di
|
||||
~$21. La domanda "l'USDC frutta davvero SUL NOSTRO conto?" non era rispondibile, perche':
|
||||
|
||||
* il gateway NON espone il Transaction Log (provati get_transaction_log,
|
||||
get_settlement_history, get_deposits, get_transfers, get_interest_history: tutti 404 —
|
||||
e' il debito #11, si ha solo cio' che il gateway espone);
|
||||
* l'unica serie storica del conto e' `trades.db.equity`, che e' EQUITY **arrotondata a 2
|
||||
decimali**: $0,13/giorno di interesse atteso ci sparisce dentro, e l'equity contiene
|
||||
comunque il P&L non realizzato, che vale mille volte tanto.
|
||||
|
||||
Il `balance` no: cambia solo per P&L REALIZZATO, fee, funding, movimenti di fondi e interessi.
|
||||
Su USDE (nessuna posizione denominata in USDE) e' gia' stato il rivelatore dei reward. Questa
|
||||
serie fa lo stesso per l'USDC — ed e' il dato che mancava, non un'analisi nuova.
|
||||
|
||||
COME SI SEPARA L'INTERESSE DAL RESTO (P7: il numero si etichetta con la sua configurazione):
|
||||
* P&L realizzato e fee -> noti ESATTAMENTE da `trades.db.fills`; ogni record porta il
|
||||
conteggio dei fill dall'ultimo campione, cosi' una finestra sporca si riconosce invece di
|
||||
essere mediata dentro;
|
||||
* funding -> proporzionale al NOZIONALE, quindi **zero quando il libro e' FLAT**. Il libro e'
|
||||
flat il 28% dei giorni: la lettura pulita arriva da sola, non va costruita;
|
||||
* interesse -> proporzionale al BALANCE.
|
||||
Una finestra con 0 fill e nozionale 0 misura l'interesse e basta. Le altre si etichettano.
|
||||
|
||||
MISURATO GIA' ORA (30/08, tre letture a 8 decimali durante le conversioni USDE): il balance USDC
|
||||
si e' mosso **esattamente** dell'importo degli ordini (1555.84720621 - 10.002 = 1545.84520621,
|
||||
poi -134.0268 = 1411.81840621). Su ~10 minuti con $568 di posizioni aperte: **ne' funding ne'
|
||||
interesse arrivano in continuo sul balance**. Se l'interesse esiste, e' un accredito PERIODICO —
|
||||
come i reward USDE, che compaiono in un colpo intorno alle 12:00 UTC. Percio' la cadenza e' ORARIA:
|
||||
serve a stringere la finestra intorno all'accredito, non a campionare di piu'.
|
||||
|
||||
uv run python scripts/live/balance_watch.py # un campione + lettura della serie
|
||||
uv run python scripts/live/balance_watch.py --report # solo la lettura, nessun campione
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sqlite3
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from src.live.deribit import DeribitRead # noqa: E402
|
||||
|
||||
STATE = ROOT / "data" / "live" / "balance_watch.jsonl"
|
||||
TRADES_DB = ROOT / "data" / "live" / "trades.db"
|
||||
VALUTE = ("USDC", "USDE", "BUIDL")
|
||||
APR_ATTESE = {"USDC": 0.0340, "USDE": 0.042143, "BUIDL": 0.032018} # public/get_currencies
|
||||
|
||||
|
||||
def fills_da(ts_iso: str | None) -> int | None:
|
||||
"""Quanti fill dopo `ts_iso` (None se il DB non e' leggibile: non si inventa 0)."""
|
||||
if ts_iso is None:
|
||||
return None
|
||||
try:
|
||||
con = sqlite3.connect(f"file:{TRADES_DB}?mode=ro", uri=True)
|
||||
try:
|
||||
return con.execute("select count(*) from fills where ts_utc > ?", (ts_iso,)).fetchone()[0]
|
||||
finally:
|
||||
con.close()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def campiona(c: DeribitRead) -> dict:
|
||||
"""Un campione: balance per valuta (8 decimali), nozionale lordo, posizioni. Mai solleva."""
|
||||
rec = {"ts": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), "valute": {}}
|
||||
for cur in VALUTE:
|
||||
try:
|
||||
s = c.account_summary(cur)
|
||||
rec["valute"][cur] = {"balance": float(s["balance"]), "equity": float(s["equity"])}
|
||||
except Exception as e:
|
||||
rec["valute"][cur] = {"errore": f"{type(e).__name__}"}
|
||||
try:
|
||||
pos = c.positions("USDC")
|
||||
rec["nozionale_lordo"] = round(sum(abs(float(p.get("size") or 0)) for p in pos), 2)
|
||||
rec["n_posizioni"] = sum(1 for p in pos if abs(float(p.get("size") or 0)) > 1)
|
||||
except Exception:
|
||||
rec["nozionale_lordo"], rec["n_posizioni"] = None, None
|
||||
# INDICE usde_usdc, ORARIO (aggiunto 2026-09-02, debito §5.15). `usde_watch` lo registra una
|
||||
# volta al giorno (12:35Z): troppo rado per il classificatore dei movimenti di capitale, che
|
||||
# confronta due letture di equity a un'ora di distanza e oggi guarda solo BTC/ETH — con il
|
||||
# 31% dell'equity in USDE, un depeg sarebbe scorporato come un PRELIEVO. Questa e' la serie
|
||||
# che manca; il cablaggio nel bound viene dopo, quando ci sara' storia. None se non leggibile
|
||||
# (P5: "non vedo" non e' "il peg tiene").
|
||||
try:
|
||||
from src.live.usde import prezzo_indice
|
||||
rec["usde_usdc"] = prezzo_indice()
|
||||
except Exception as e:
|
||||
rec["usde_usdc"], rec["usde_usdc_errore"] = None, f"{type(e).__name__}"
|
||||
return rec
|
||||
|
||||
|
||||
def serie() -> list[dict]:
|
||||
if not STATE.exists():
|
||||
return []
|
||||
out = []
|
||||
for line in STATE.read_text().splitlines():
|
||||
try:
|
||||
out.append(json.loads(line))
|
||||
except Exception:
|
||||
pass
|
||||
return out
|
||||
|
||||
|
||||
def leggi(righe: list[dict]) -> None:
|
||||
print(f" campioni: {len(righe)}"
|
||||
+ (f" da {righe[0]['ts']} a {righe[-1]['ts']}" if righe else ""))
|
||||
if len(righe) < 2:
|
||||
print(" → serve almeno un secondo campione: la prima differenza e' la prima misura.")
|
||||
return
|
||||
print(f"\n {'finestra':>34s} {'ore':>5s} {'Δ USDC':>13s} {'Δ USDE':>13s} {'fill':>5s} {'lordo':>9s} lettura")
|
||||
pulite = []
|
||||
for a, b in zip(righe, righe[1:]):
|
||||
ba, bb = a["valute"].get("USDC", {}), b["valute"].get("USDC", {})
|
||||
ea, eb = a["valute"].get("USDE", {}), b["valute"].get("USDE", {})
|
||||
if "balance" not in ba or "balance" not in bb:
|
||||
continue
|
||||
t0 = datetime.strptime(a["ts"], "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc)
|
||||
t1 = datetime.strptime(b["ts"], "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc)
|
||||
ore = (t1 - t0).total_seconds() / 3600
|
||||
d_usdc = bb["balance"] - ba["balance"]
|
||||
d_usde = (eb.get("balance", 0) - ea.get("balance", 0)) if "balance" in eb and "balance" in ea else None
|
||||
nf = b.get("fills_da_ultimo")
|
||||
lordo = b.get("nozionale_lordo")
|
||||
pulita = (nf == 0) and (lordo is not None and lordo < 1)
|
||||
nota = "PULITA (0 fill, libro flat) → il Δ USDC E' interesse" if pulita else (
|
||||
"sporca: fill nella finestra" if nf else
|
||||
"0 fill ma libro esposto → Δ = funding + interesse" if nf == 0 else "fill ignoti")
|
||||
if pulita and ore > 0:
|
||||
pulite.append((d_usdc, ba["balance"], ore))
|
||||
print(f" {a['ts'][5:16]}→{b['ts'][5:16]:>16s} {ore:5.1f} {d_usdc:+13.8f} "
|
||||
f"{(f'{d_usde:+13.8f}' if d_usde is not None else ' n/d')} "
|
||||
f"{('' if nf is None else nf):>5} {('' if lordo is None else f'${lordo:,.0f}'):>9} {nota}")
|
||||
print()
|
||||
if pulite:
|
||||
tot_d = sum(d for d, _, _ in pulite)
|
||||
tot_h = sum(h for _, _, h in pulite)
|
||||
bal = sum(b * h for _, b, h in pulite) / tot_h
|
||||
apr = (tot_d / bal) * (8760 / tot_h) if bal and tot_h else float("nan")
|
||||
print(f" ⇒ FINESTRE PULITE: {len(pulite)}, {tot_h:.1f} ore, Δ totale {tot_d:+.8f} USDC "
|
||||
f"su balance medio ${bal:,.2f}")
|
||||
print(f" APR implicita {apr:+.4%} (attesa da public/get_currencies: "
|
||||
f"{APR_ATTESE['USDC']:.4%})")
|
||||
print(f" verdetto: {'FRUTTA' if tot_d > 1e-6 else 'NON frutta (Δ nullo su finestra pulita)'}")
|
||||
else:
|
||||
print(" ⇒ nessuna finestra PULITA ancora (serve il libro FLAT e 0 fill: succede il 28% dei giorni).")
|
||||
print(" Nel frattempo le finestre a 0 fill misurano funding+interesse insieme.")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--report", action="store_true", help="solo lettura, nessun campione nuovo")
|
||||
a = ap.parse_args()
|
||||
righe = serie()
|
||||
if not a.report:
|
||||
rec = campiona(DeribitRead())
|
||||
rec["fills_da_ultimo"] = fills_da(righe[-1]["ts"] if righe else None)
|
||||
STATE.parent.mkdir(parents=True, exist_ok=True)
|
||||
with STATE.open("a") as f:
|
||||
f.write(json.dumps(rec) + "\n")
|
||||
righe.append(rec)
|
||||
u = rec["valute"].get("USDC", {}); e = rec["valute"].get("USDE", {})
|
||||
print(f" campione {rec['ts']}: USDC balance {u.get('balance')} · USDE {e.get('balance')}"
|
||||
f" · lordo ${rec.get('nozionale_lordo')}")
|
||||
print("=" * 78)
|
||||
print(" BALANCE WATCH — l'USDC frutta sul nostro conto?")
|
||||
print("=" * 78)
|
||||
leggi(righe)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
+161
-34
@@ -10,9 +10,24 @@ DOPPIO GATE DI SICUREZZA (entrambi necessari per inviare ordini reali):
|
||||
Senza entrambi e' un DRY-RUN (stampa il piano, NON invia). Reconciliation dopo ogni ordine; log in
|
||||
data/live/book_executions.jsonl.
|
||||
|
||||
⚠️ CADENZA: SKH01 decide su griglia 230m -> questo script va lanciato ogni ~230 minuti con la feed
|
||||
fresca all'ultima barra chiusa (NON il cron giornaliero, che mancherebbe gli ingressi). Gli exit di
|
||||
SKH sono SOFTWARE (latenza fino a fine barra 230m); solo il disaster-SL (-30%) e' on-book.
|
||||
⚠️ CADENZA: ORARIA — `47 * * * *` in `scripts/cron_book.sh` (il minuto e' spiegato li'). SKH01
|
||||
decide su griglia 230m, ma il giro e' IDEMPOTENTE (riconcilia al target netto corrente; sotto
|
||||
`min_order_usd` $5 -> HOLD). Girare piu' fitto della griglia costa solo MICRO-ORDINI di ri-taglia:
|
||||
il target e' ricalcolato ogni ora sull'equity marcata, e 22 dei 48 ordini live sono |delta|<=$10
|
||||
a segnale invariato — spiccioli oggi, ma il deadband e' in valuta ASSOLUTA (regola C2: a capitale
|
||||
maggiore un movimento dello 0,15% lo supera ogni ora). Compra due cose misurate: (1) gli
|
||||
ingressi/uscite SOFTWARE di SKH01 arrivano con latenza <=1h — CON LA FEED 5m FRESCA: se e' stantia
|
||||
`livefeed.fresh_5m` ripiega in silenzio sul feed giornaliero (latenza ~1 giorno) e
|
||||
`skh_feed_max_age_min` 30 ALLERTA, non blocca; (2) il disaster-SL rotolante (-30%, l'unico on-book)
|
||||
viene CONTROLLATO ogni ora e ri-ancorato solo oltre la tolleranza di `ensure_disaster_sl` (stop a
|
||||
>5% da quello piazzato, cioe' mark +5,263%/-4,762%, o taglia >10%) — con questa isteresi
|
||||
(`r0823_sl_anchor.py`): a cadenza 1h in 7-8 anni BTC 0 scatti / ETH 1; a 4h BTC 2; a 24h ETH 5,
|
||||
con 2 in 30 giorni. Lo stop siede quindi fra -33,5% e -26,5% dal mark corrente (0,665-0,735 x mark),
|
||||
non a -30% dall'ultima ora: e' rotolante, non un massimo di perdita (CLAUDE.md §1).
|
||||
🚨 NON "correggere" la cadenza a ~230 minuti: fino al 2026-09-02 questo docstring lo prescriveva,
|
||||
ed era il docstring a essere sbagliato, non il cron (debito §5.7). `tests/test_book_cadenza.py`
|
||||
tiene d'accordo docstring, `cron_book.sh` e crontab installata. NON il cron giornaliero: le entrate
|
||||
di SKH01 le mancherebbe.
|
||||
|
||||
uv run python scripts/live/book_execute.py # DRY-RUN (piano, nessun ordine)
|
||||
uv run python scripts/live/book_execute.py --execute # esegue SOLO se execution_enabled=true
|
||||
@@ -30,8 +45,8 @@ import pandas as pd
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
from src.live.book import book_report
|
||||
from src.live.execution import DeribitTrader
|
||||
from src.live.book import ScalaNonAutorizzata, book_report
|
||||
from src.live.execution import DeribitTrader, RebalanceParziale
|
||||
from src.live.notifier import notify
|
||||
from src.live.venue_probe import diagnose, errori_dal_report
|
||||
|
||||
@@ -70,6 +85,33 @@ def log_event(rec: dict):
|
||||
f.write(json.dumps(rec) + "\n")
|
||||
|
||||
|
||||
def _registra_fill(f, *, asset: str, act: str, net: float, bar_ts, tp_frac: float, skh_sign: int,
|
||||
pos_after: float | None = None):
|
||||
"""Registra UN fill (print + log_event + notify). SUBITO dopo l'ordine, PRIMA di leggere la
|
||||
posizione risultante: un ordine eseguito non deve dipendere dalla lettura della posizione
|
||||
successiva per essere salvato (#23 — un 502 su `position_usd()` dopo un fill riuscito lo
|
||||
faceva sparire dal log, perche' la lettura veniva PRIMA del giro `for f in fills`).
|
||||
|
||||
`pos_after` arriva dal chiamante (#39 — regressione di #23): nel ramo normale di `_run` si
|
||||
legge SUBITO dopo il rebalance, in un try che isola la lettura dalla registrazione dei fill;
|
||||
resta None quando la lettura solleva o quando il passo e' un `RebalanceParziale` (a meta')."""
|
||||
print(f" -> {f.side.upper()} {f.filled:.4f} @ ${f.price or 0:,.1f} fee {f.fee_usdc:.5f} "
|
||||
f"({'OK' if f.verified else 'NON VERIFICATO: ' + f.notes})")
|
||||
# `ts_utc` = ORA VERA del fill. Fino al 2026-08-23 qui c'era la data della
|
||||
# BARRA di segnale (`r['last_data']`): 19 righe su 19 a 00:00:00, e un trade
|
||||
# registrato SEI GIORNI prima di essere eseguito (ETH 0.04 del 14/07, scritto
|
||||
# 08/07). La barra resta, sotto il suo nome: `bar_ts`.
|
||||
log_event(dict(ts_utc=datetime.now(timezone.utc).isoformat(timespec="seconds"),
|
||||
bar_ts=str(pd.Timestamp(bar_ts)), asset=asset, action=act,
|
||||
side=f.side, filled=f.filled, price=f.price, fee=f.fee_usdc,
|
||||
verified=f.verified, notes=f.notes, net_target=net, pos_after=pos_after,
|
||||
tp_frac=tp_frac, skh_sign=skh_sign))
|
||||
det = dict(asset=asset, side=f.side, amount=round(f.filled, 4), price=round(f.price or 0, 1),
|
||||
fee=round(f.fee_usdc, 5), net=round(net, 0))
|
||||
notify(f"✅ BOOK {act}" if f.verified else "⚠️ BOOK ORDINE NON VERIFICATO",
|
||||
det if f.verified else {**det, "notes": f.notes})
|
||||
|
||||
|
||||
def _run():
|
||||
cfg = load_config()
|
||||
want_execute = "--execute" in sys.argv[1:]
|
||||
@@ -78,7 +120,20 @@ def _run():
|
||||
min_order = float(cfg["min_order_usd"])
|
||||
sl_pct = float(cfg["disaster_sl_pct"])
|
||||
|
||||
r = book_report(live_feed=True) # target NETTO + conto/posizioni reali (feed SKH fresco)
|
||||
try:
|
||||
r = book_report(live_feed=True) # target NETTO + conto/posizioni reali (feed SKH fresco)
|
||||
except ScalaNonAutorizzata as e:
|
||||
# G1(c): NON si taglia al tetto. Un clamp silenzioso farebbe girare una config che dichiara
|
||||
# un numero e un libro che ne esegue un altro. Si ferma, non invia, e allerta.
|
||||
msg = (f"🚨 SCALA NON AUTORIZZATA — book_execute FERMO, nessun ordine inviato.\n{e}\n"
|
||||
f"Ripara: riporta `book_scale_k` a un gradino di SCALA_LADDER dentro il tetto, "
|
||||
f"oppure rimuovi la chiave (assente = 1,00 = libro di oggi).")
|
||||
print(msg)
|
||||
try:
|
||||
notify(msg)
|
||||
except Exception as ne: # P3: l'errore si registra dove lo si ingoia
|
||||
print(f" ⚠️ allerta NON inviata: {type(ne).__name__}: {ne}")
|
||||
return
|
||||
equity = r["equity"]
|
||||
|
||||
print("=" * 88)
|
||||
@@ -89,12 +144,22 @@ def _run():
|
||||
print(f" modo : {mode}")
|
||||
print(f" gate : execution_enabled={enabled} | --execute={want_execute}")
|
||||
print(f" conto reale : ${r['real_equity']:,.2f}" if r["real_equity"] else f" conto: {r['eq_basis']}")
|
||||
print(f" sizing base : ${equity:,.2f} | cap/asset ${r['cap_per_asset']:.0f} | min ${min_order:.0f} | disaster-SL -{sl_pct*100:.0f}%")
|
||||
_sc = r.get("scala", 1.0)
|
||||
_lordo = len(r["assets"]) * float(cfg.get("max_notional_per_asset_frac") or 0.0) * _sc
|
||||
print(f" sizing base : ${equity:,.2f} | cap/asset ${r['cap_per_asset']:.0f} | "
|
||||
f"min ${min_order:.0f} | disaster-SL -{sl_pct*100:.0f}%")
|
||||
# La scala e' stampata SEMPRE, anche a 1,00: un parametro che muove il nozionale e non compare
|
||||
# nel log e' invisibile proprio nel giro in cui e' cambiato (SPEC-scale-key §2.2).
|
||||
print(f" scala libro : {_sc:.2f}x (tetto {r.get('leva_lorda_max', 1.25):.2f}x) | "
|
||||
f"leva lorda max {_lordo:.3f}x dell'equity"
|
||||
+ ("" if _sc != 1.0 else " [chiave assente o 1,00 = libro invariato]"))
|
||||
print(f" ultima barra : {r['last_data']}\n")
|
||||
|
||||
if r.get("skh_error"): # SKH feed fallito -> book.py ha forzato flat IN SILENZIO
|
||||
print(f" ⚠️ SKH FEED ERRORE (SKH forzato flat!): {r['skh_error']}")
|
||||
notify("⚠️ BOOK — SKH feed fallito (sleeve forzato flat)", {"error": r["skh_error"]})
|
||||
# (#22) Non e' piu' "SKH forzato flat!": book_execute non manda PIU' ordini basati su
|
||||
# quel segnale ignoto (modo SOLO PROTEZIONE, sotto, che porta anche questo errore su
|
||||
# Telegram — qui solo il print, per non mandare due notify per lo stesso guasto).
|
||||
print(f" ⚠️ SKH FEED ERRORE (SKH non calcolato: nessun ordine, solo protezione): {r['skh_error']}")
|
||||
|
||||
# --- FRESCHEZZA del feed 5m usato per il segnale SKH (2026-07-26) --------------------------
|
||||
# `fresh_5m` ricade sul feed certificato IN SILENZIO se il fetch pubblico Deribit fallisce, e
|
||||
@@ -184,10 +249,27 @@ def _run():
|
||||
"azione": "rebuild_history.py --asset BTC ETH"})
|
||||
return
|
||||
|
||||
if r.get("eq_fallback"): # equity reale non leggibile -> sizing su paper_cap
|
||||
print(f" ⚠️ EQUITY FALLBACK (sizing su paper_cap, NON blocco): {r['eq_fallback']}")
|
||||
if do_execute: # solo diagnostica: l'hard-cap $/asset limita il downside
|
||||
notify("⚠️ BOOK LIVE — equity fallback (sizing su paper_cap)", {"nota": r["eq_fallback"]})
|
||||
if r.get("eq_fallback"): # equity reale non leggibile -> niente sizing sul conto
|
||||
# (#21) qui solo il print: il notify e' nel blocco SOLO PROTEZIONE sotto, che porta anche
|
||||
# questo errore su Telegram — un solo avviso per lo stesso guasto, non due.
|
||||
print(f" ⚠️ EQUITY FALLBACK (nessun ordine, solo protezione): {r['eq_fallback']}")
|
||||
|
||||
# --- SOLO PROTEZIONE (#21 equity ignota, #22 SKH ignoto) ------------------------------------
|
||||
# Principio unico: quando il giro non sa QUANTO vale il conto o QUALE e' il segnale, NON manda
|
||||
# ordini di ribilancio (dimensionare un ordine su un numero che non si conosce e' peggio che
|
||||
# non ordinare) -> ma CONTROLLA LO STESSO il disaster-SL di ogni asset: la protezione dipende
|
||||
# da posizione e mark, non da equity o segnale. Stesso principio che pos_error applica gia'
|
||||
# bloccando l'esecuzione, qui senza saltare la protezione.
|
||||
solo_protezione: str | None = r.get("eq_fallback") or r.get("skh_error")
|
||||
# Calcolato una volta sola: serve sia nell'azione per-asset del ciclo sotto sia nel
|
||||
# riepilogo finale (#21, #22) — un solo motivo, non due troncamenti che potrebbero divergere.
|
||||
motivo_breve = (solo_protezione if solo_protezione and len(solo_protezione) <= 60
|
||||
else (solo_protezione[:60] + "…" if solo_protezione else None))
|
||||
if solo_protezione and do_execute:
|
||||
titolo = ("⚠️ BOOK LIVE — equity non leggibile: nessun ordine, solo protezione"
|
||||
if r.get("eq_fallback") else
|
||||
"⚠️ BOOK LIVE — SKH non calcolato: nessun ordine, solo protezione")
|
||||
notify(titolo, {"nota": solo_protezione})
|
||||
|
||||
# Salto di equity: un versamento (conferma che il sizing lo ha seguito) oppure un movimento
|
||||
# NON richiesto (prelievo, perdita anomala). In entrambi i casi va detto subito, e con il
|
||||
@@ -214,13 +296,18 @@ def _run():
|
||||
# non deve togliere la rete di sicurezza all'altro.
|
||||
falliti: list[str] = []
|
||||
scoperti: list[str] = []
|
||||
non_verificati: list[str] = []
|
||||
for a in r["assets"]:
|
||||
asset, inst = a["asset"], a["instrument"]
|
||||
net, cur, mark = a["net_target"], a["position_usd"], a["mark"]
|
||||
sk = a["skh_state"]
|
||||
sk_txt = "flat" if sk == "flat" else f"{sk['dir']}@{sk.get('entry')}"
|
||||
order = a["order"]
|
||||
if order is None:
|
||||
if solo_protezione:
|
||||
# (#21, #22) nessun rebalance: l'azione stampata lo dice, non e' "a target" — e'
|
||||
# "non tocco perche' non so quanto vale il conto / qual e' il segnale".
|
||||
act = f"HOLD (solo protezione: {motivo_breve})"
|
||||
elif order is None:
|
||||
act = "HOLD (a target)"
|
||||
elif order.get("is_close"):
|
||||
act = f"CLOSE ${cur:,.0f}"
|
||||
@@ -232,26 +319,37 @@ def _run():
|
||||
f"| pos ${cur:+,.0f} -> {act}")
|
||||
|
||||
try:
|
||||
if do_execute and order is not None:
|
||||
fills = trader.rebalance_signed(inst, net, mark, min_usd=min_order)
|
||||
newpos = trader.position_usd(inst)
|
||||
if do_execute and order is not None and not solo_protezione:
|
||||
# (#18) tetto di apertura derivato dal cap di config del libro. Se cap_per_asset*scala
|
||||
# o mark sono <=0 solleva ValueError: e' giusto, finisce nell'isolamento per asset
|
||||
# (except sotto), non deve fermare il giro intero.
|
||||
trader.set_open_cap(inst, r["cap_per_asset"] * r.get("scala", 1.0), mark)
|
||||
try:
|
||||
fills = trader.rebalance_signed(inst, net, mark, min_usd=min_order)
|
||||
except RebalanceParziale as rp:
|
||||
# (#23) almeno un fill e' passato prima che il resto del passo sollevasse
|
||||
# (es. flip: close ok, open KO) -> si registra quel fill SUBITO (non e' un
|
||||
# fill perso), poi si rilancia la causa nell'handler di isolamento esistente.
|
||||
for f in rp.fills:
|
||||
_registra_fill(f, asset=asset, act=act, net=net, bar_ts=r["last_data"],
|
||||
tp_frac=a["tp_frac"], skh_sign=a["skh_sign"])
|
||||
raise rp.causa
|
||||
# newpos si legge SUBITO dopo il rebalance, in un try che ISOLA la lettura dalla
|
||||
# registrazione dei fill (#39, regressione di #23: dal merge di #23 pos_after era
|
||||
# sempre None, perche' la lettura restava un side-effect informativo dopo il ciclo
|
||||
# e il suo risultato non arrivava piu' a `_registra_fill`). Se solleva, i fill si
|
||||
# registrano comunque, con pos_after=None — il vincolo #23 (un fill non si perde
|
||||
# per un reconcile mancato) resta: il try isola solo la LETTURA, non la scrittura.
|
||||
try:
|
||||
newpos = trader.position_usd(inst)
|
||||
pos_after = newpos
|
||||
print(f" reconcile: pos ${newpos:,.0f}")
|
||||
except Exception as e: # noqa: BLE001 — non e' un fill perso, e' un reconcile mancato
|
||||
pos_after = None
|
||||
print(f" reconcile: pos illeggibile ({type(e).__name__})")
|
||||
for f in fills:
|
||||
print(f" -> {f.side.upper()} {f.filled:.4f} @ ${f.price or 0:,.1f} fee {f.fee_usdc:.5f} "
|
||||
f"({'OK' if f.verified else 'NON VERIFICATO: ' + f.notes})")
|
||||
# `ts_utc` = ORA VERA del fill. Fino al 2026-08-23 qui c'era la data della
|
||||
# BARRA di segnale (`r['last_data']`): 19 righe su 19 a 00:00:00, e un trade
|
||||
# registrato SEI GIORNI prima di essere eseguito (ETH 0.04 del 14/07, scritto
|
||||
# 08/07). La barra resta, sotto il suo nome: `bar_ts`.
|
||||
log_event(dict(ts_utc=datetime.now(timezone.utc).isoformat(timespec="seconds"),
|
||||
bar_ts=str(pd.Timestamp(r['last_data'])), asset=asset, action=act,
|
||||
side=f.side, filled=f.filled, price=f.price, fee=f.fee_usdc,
|
||||
verified=f.verified, notes=f.notes, net_target=net, pos_after=newpos,
|
||||
tp_frac=a["tp_frac"], skh_sign=a["skh_sign"]))
|
||||
det = dict(asset=asset, side=f.side, amount=round(f.filled, 4), price=round(f.price or 0, 1),
|
||||
fee=round(f.fee_usdc, 5), net=round(net, 0), pos_after=round(newpos, 0))
|
||||
notify(f"✅ BOOK {act}" if f.verified else "⚠️ BOOK ORDINE NON VERIFICATO",
|
||||
det if f.verified else {**det, "notes": f.notes})
|
||||
print(f" reconcile: pos ${newpos:,.0f}")
|
||||
_registra_fill(f, asset=asset, act=act, net=net, bar_ts=r["last_data"],
|
||||
tp_frac=a["tp_frac"], skh_sign=a["skh_sign"], pos_after=pos_after)
|
||||
if do_execute:
|
||||
ds = trader.ensure_disaster_sl(inst, sl_pct) # bracket su posizione NETTA (adatta long/short)
|
||||
stato = ds.get("state")
|
||||
@@ -272,6 +370,15 @@ def _run():
|
||||
"amount": round(ds.get("amount") or 0, 4),
|
||||
"azione": "ripiazzare il bracket a mano, oppure chiudere la posizione",
|
||||
"notes": str(ds.get("notes"))[:300]})
|
||||
elif stato == "unknown":
|
||||
# (#24) posizione o open_orders illeggibili: NON e' "naked" (non si e' tolto
|
||||
# nulla, ensure_disaster_sl non cancella ne' piazza su dati a meta'). E' un
|
||||
# giro degradato lo stesso: non si sa se la protezione esistente e' ancora
|
||||
# coerente con la posizione attuale.
|
||||
non_verificati.append(asset)
|
||||
print(f" ⚠️ {asset}: protezione NON VERIFICATA — {ds.get('notes')}")
|
||||
notify("⚠️ BOOK — disaster-SL non verificabile (lettura posizione/ordini fallita)",
|
||||
{"asset": asset, "notes": str(ds.get("notes"))[:300]})
|
||||
except Exception as e: # noqa: BLE001 — isolamento per asset: l'altro deve continuare
|
||||
d = diagnose(errori_dal_report(r) + [f"{type(e).__name__}: {e}"])
|
||||
falliti.append(asset)
|
||||
@@ -291,16 +398,27 @@ def _run():
|
||||
print(f" 🚨 POSIZIONI SCOPERTE (nessun disaster-SL on-book): {', '.join(scoperti)}")
|
||||
if falliti:
|
||||
print(f" 🛑 asset falliti in questo giro: {', '.join(falliti)}")
|
||||
if non_verificati:
|
||||
print(f" ⚠️ disaster-SL NON VERIFICATO (lettura posizione/ordini fallita): {', '.join(non_verificati)}")
|
||||
if not do_execute:
|
||||
print(" => DRY-RUN: nessun ordine inviato." +
|
||||
("" if enabled else " Per armare: config/live.json execution_enabled=true + --execute."))
|
||||
elif solo_protezione:
|
||||
# (#21, #22) non e' "Nessuna azione: conto gia' al target" — il conto potrebbe NON esserci
|
||||
# per niente al target, semplicemente non lo si sa. Distinto dal ramo HOLD sotto.
|
||||
print(f" => SOLO PROTEZIONE: nessun ordine di ribilancio ({motivo_breve}), disaster-SL controllato.")
|
||||
elif all(x.startswith("HOLD") for x in actions):
|
||||
print(" => Nessuna azione: conto gia' al target netto del book.")
|
||||
else:
|
||||
print(" => Esecuzione completata (vedi data/live/book_executions.jsonl).")
|
||||
# Uscita non-zero se il giro e' stato DEGRADATO: il log del cron deve poterlo contare senza
|
||||
# rileggere la prosa. Non cambia nulla operativamente (cron_book.sh non ha `set -e`).
|
||||
if scoperti or falliti:
|
||||
# (#21, #22) SOLO PROTEZIONE e' degradato anche senza asset falliti/scoperti: il giro non ha
|
||||
# ribilanciato nulla perche' non sapeva quanto vale il conto o qual era il segnale. Solo se
|
||||
# il giro era ARMATO: in DRY-RUN non si e' comunque tentato nulla, e' solo un'anteprima.
|
||||
# (#24) `non_verificati` e' degradato allo stesso modo: la protezione esistente non e' stata
|
||||
# controllata, indipendentemente da solo_protezione.
|
||||
if scoperti or falliti or non_verificati or (solo_protezione and do_execute):
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
@@ -319,5 +437,14 @@ def main():
|
||||
raise
|
||||
|
||||
|
||||
USO = """uso: book_execute.py [--execute]
|
||||
|
||||
uv run python scripts/live/book_execute.py # DRY-RUN (piano, nessun ordine)
|
||||
uv run python scripts/live/book_execute.py --execute # esegue SOLO se execution_enabled=true
|
||||
|
||||
Dettaglio nel docstring in testa al file."""
|
||||
|
||||
if __name__ == "__main__":
|
||||
from src.live.cli import valida
|
||||
valida("book_execute.py", USO, flag=("--execute",), con_valore=())
|
||||
main()
|
||||
|
||||
@@ -13,16 +13,29 @@ Hyperliquid (tokenless, stessa fonte certificata di fetch_hl_funding.py) e stamp
|
||||
che resta comunque vincolato a ~$20k+ di capitale e venue con funding eseguibile).
|
||||
|
||||
Uso: `uv run python scripts/live/cc01_regime_watch.py`
|
||||
(candidato a una riga in cron_daily.sh: e' read-only, nessun ordine, nessuno stato.)
|
||||
(in cron_daily.sh: read-only verso il mercato, nessun ordine.)
|
||||
|
||||
REGISTRA OGNI GIRO MISURATO (dal 2026-09-16, #12) in `data/cc01_regime/runs.jsonl`: `ts` in ms,
|
||||
stato, funding 30g/7g per asset. Serve a `monitor_health`, che ne legge il percorso da `RUNS`
|
||||
(P1). Dal 03/09 al 16/09 lo script e' morto 14 notti di fila (#10) e nessuno l'ha visto, perche'
|
||||
non lasciava niente da sorvegliare. Un giro che non misura entrambi gli asset NON scrive: non e'
|
||||
una barra, e se lo fosse un cc01 cieco resterebbe «OK».
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime as dt
|
||||
import json
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import requests
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
RUNS = ROOT / "data" / "cc01_regime" / "runs.jsonl"
|
||||
HL_INFO = "https://api.hyperliquid.xyz/info"
|
||||
HOURS_PER_YEAR = 24 * 365
|
||||
WARN_ANN = 0.10 # >=10% ann. su media 30g -> WARN
|
||||
@@ -70,6 +83,7 @@ def main() -> None:
|
||||
print(" CC01 REGIME WATCH — funding medio 1-mese (Hyperliquid, orario, tokenless)")
|
||||
print("=" * 78)
|
||||
worst = "QUIET"
|
||||
misure = {}
|
||||
for coin in ("BTC", "ETH"):
|
||||
f = fetch_recent_funding(coin)
|
||||
if len(f) < 24 * 7:
|
||||
@@ -81,8 +95,16 @@ def main() -> None:
|
||||
"WARN" if ann30 >= WARN_ANN else "QUIET")
|
||||
if state == "ALERT" or (state == "WARN" and worst == "QUIET"):
|
||||
worst = state
|
||||
misure[coin] = {"ann30": ann30, "ann7": ann7, "stato": state}
|
||||
print(f" {coin}: media 30g {ann30:+7.1%} ann. media 7g {ann7:+7.1%} ann. -> {state}")
|
||||
print("-" * 78)
|
||||
if len(misure) < 2:
|
||||
# P5: «non vedo» non e' «QUIET». Nessuna riga: monitor_health lo vedra' FERMO.
|
||||
print(f" NON VEDO: misurati {len(misure)} asset su 2 — nessuna riga registrata in {RUNS.name}.")
|
||||
return
|
||||
RUNS.parent.mkdir(parents=True, exist_ok=True)
|
||||
with RUNS.open("a", encoding="utf-8") as fh:
|
||||
fh.write(json.dumps({"ts": int(time.time() * 1000), "stato": worst, **misure}) + "\n")
|
||||
if worst == "ALERT":
|
||||
print(" >>> ALERT CC01: funding 30g >= 15% ann. — regime ricco SOSTENUTO.")
|
||||
print(" Riaprire il dossier cash-and-carry (vincoli invariati: ~$20k+,")
|
||||
@@ -93,5 +115,13 @@ def main() -> None:
|
||||
print(" regime QUIET: carry non raccoglibile (coerente con luglio 2026).")
|
||||
|
||||
|
||||
USO = """uso: cc01_regime_watch.py
|
||||
|
||||
(nessun flag) sorveglia il regime di CC01 e stampa lo stato.
|
||||
|
||||
Dettaglio nel docstring in testa al file."""
|
||||
|
||||
if __name__ == "__main__":
|
||||
from src.live.cli import valida
|
||||
valida("cc01_regime_watch.py", USO)
|
||||
main()
|
||||
|
||||
Executable
+378
@@ -0,0 +1,378 @@
|
||||
#!/usr/bin/env python
|
||||
"""cuscino_watch.py — sorveglianza ORARIA del cuscino USDC di regolamento. PUO' INVIARE ORDINI.
|
||||
|
||||
PERCHE' ESISTE (debito §5.17, issue #3). Dal 06/09 il 68,7% dell'equity e' USDE. P&L e funding dei
|
||||
perp USDC-lineari si regolano in USDC: una perdita del libro consuma il cuscino e fa salire la quota
|
||||
USDE DA SOLA, senza un ordine. `usde_watch` allerta solo su `quota > quota_max_frac` (0,85): a
|
||||
68,7% lo slack sul cuscino era ~$41-58, e un saldo USDC negativo costa lo 0,05%/GIORNO (18,25%/anno,
|
||||
4,3x la resa che l'USDE compra). Nessuno guardava la grandezza giusta.
|
||||
|
||||
LA MISURA (decisione dell'operatore, 10/09): l'EQUITY USDC del conto — balance piu' P&L non
|
||||
realizzato — perche' e' quella che risponde del regolamento; il balance vedrebbe il buco solo a
|
||||
trade chiuso. Il cuscino richiesto e' DERIVATO da config/live.json e src/live/book.py tramite
|
||||
`usde.cuscino_richiesto_usd` (n_asset x frac x disaster_sl_pct x equity totale): la stessa formula
|
||||
che `usde_convert` applica a ogni piano — un sorvegliante che ridichiara il proprio bersaglio non
|
||||
sta controllando niente (P1, 5 occorrenze nel progetto).
|
||||
|
||||
QUATTRO STATI SULLO SLACK (= equity USDC − cuscino), in frazioni del cuscino lette da config
|
||||
(`usde.bande_cuscino`: preavviso 0,10 · margine 0,20 · riacquisto 0,40 — P5, P1):
|
||||
* SCOPERTO — slack < 0: 🚨 alla transizione, e VENDITA automatica di USDE fino alla quota di
|
||||
ripristino q* = usde.quota_ripristino() = 1 − 0,30 × 1,20 = 0,64 (cuscino + 20%).
|
||||
* PREAVVISO — 0 <= slack < 10% del cuscino: ⚠️ alla transizione. Latenza comprata apposta.
|
||||
* OK — 10% <= slack <= 40%: niente.
|
||||
* ECCEDENTE — slack > 40% del cuscino: ACQUISTO automatico di USDE fino allo stesso q* (issue #7,
|
||||
decisione dell'operatore 10/09: «riportarla a quota in autonomia»). E' cio' che
|
||||
riporta la quota a bersaglio dopo un bonifico (+0,7 × importo di slack) o dopo un
|
||||
tratto di guadagni del libro, senza che nessuno se ne ricordi. 📌 alla transizione.
|
||||
* BLIND — conto non leggibile: ⚠️ alla transizione. "Non vedo" non e' "va tutto bene".
|
||||
ISTERESI: vendita sotto 0, acquisto sopra 0,40, entrambe verso 0,20. Per oscillare lo slack deve
|
||||
muoversi di ±0,20 × cuscino = ±6% dell'equity, e siccome slack = 0,7·USDC − 0,3·USDE, servono
|
||||
±8,6% di equity USDC (~$380 oggi); un giro costa ~6 bps sull'importo mosso. Soglie DICHIARATE, non ottimizzate (M8). Il vecchio `quota_target` 0,70
|
||||
lasciava slack ZERO per costruzione: a quella quota ogni ora in perdita avrebbe venduto.
|
||||
|
||||
LE GUARDIE DELLA RICONVERSIONE (tutte dichiarate qui):
|
||||
* `execution_enabled` in config/live.json deve essere true — lo stesso interruttore del libro:
|
||||
disarmare il libro disarma anche questo, senza un secondo posto da ricordare.
|
||||
* NON si converte sotto `depeg_warn`, in nessun verso: vendere USDE sotto la pari cristallizza
|
||||
la perdita del collaterale, comprarne mentre scivola e' peggio; li' decide l'operatore.
|
||||
* al massimo UN tentativo ogni RIPRISTINO_MIN_ORE (6h), in qualunque verso: un tentativo che si
|
||||
ripete ogni ora e' spam, e un sorvegliante che si ripete si impara a ignorare (P14).
|
||||
* dopo un tentativo FALLITO da ECCEDENTE si riprova dopo RITENTATIVO_FALLITO_ORE (24h): USDC
|
||||
fermo non e' un rischio, e un guasto persistente (tetto del venue tornato, book illeggibile)
|
||||
diventerebbe 4 Telegram al giorno per sempre (revisione fable 10/09). Da SCOPERTO il ritmo
|
||||
resta 6h: li' il fallimento e' un rischio che costa 0,05%/giorno.
|
||||
* `usde_convert` porta le proprie guardie (banda di prezzo, book leggibile, tetto HARD sulla
|
||||
quota): questo script NON le duplica, le eredita lanciando lo script vero (P15: la regola si
|
||||
prova contro il codice che la esegue).
|
||||
* `--secco`: niente ordini, niente Telegram, NIENTE riga nel registro: stampa cosa FAREBBE.
|
||||
* niente lock: un `usde_convert --esegui` lanciato a mano nello stesso minuto del cron (:53)
|
||||
puo' vendere due volte verso bersagli diversi. Limite dichiarato (D5), non riparato.
|
||||
|
||||
TRASPORTO (lezione del debito §5.2): il marcatore "gia' detto" e' l'esito di `notify` scritto nel
|
||||
record (`allertato`); se l'invio fallisce la transizione si ripete al giro dopo invece di andare
|
||||
persa per l'episodio intero.
|
||||
|
||||
La serie data/live/cuscino_watch.jsonl e' dentro il perimetro di backup ed e' sorvegliata da
|
||||
monitor_health (un watch fermo = nessuno guarda il cuscino, e il silenzio si legge come zero).
|
||||
|
||||
uv run python scripts/live/cuscino_watch.py # un giro: legge, giudica, agisce
|
||||
uv run python scripts/live/cuscino_watch.py --quiet # stampa solo se cambia qualcosa
|
||||
uv run python scripts/live/cuscino_watch.py --secco # nessun ordine, nessun Telegram
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from src.live import usde as U # noqa: E402
|
||||
from src.live.notifier import notify # noqa: E402
|
||||
|
||||
STATE = ROOT / "data" / "live" / "cuscino_watch.jsonl"
|
||||
CONVERT = ROOT / "scripts" / "live" / "usde_convert.py"
|
||||
# Le bande (preavviso / margine / riacquisto) NON stanno qui: si leggono da config via
|
||||
# usde.bande_cuscino() a ogni giro (P1). Il margine e' il DOPPIO del preavviso per costruzione
|
||||
# dell'ordine imposto (revisione fable 10/09: a margine == preavviso ogni 🚨 era seguito da un ⚠️).
|
||||
RIPRISTINO_MIN_ORE = 6.0 # un tentativo di conversione (in qualunque verso) ogni 6 ore
|
||||
RITENTATIVO_FALLITO_ORE = 24.0 # da ECCEDENTE, dopo un fallimento: un tentativo al giorno
|
||||
TIMEOUT_CONVERT_S = 900 # usde_convert: 12s di pausa fra ordini + le GET per giro (~14s/giro
|
||||
# coi 12s di pausa); 60 ordini ~= 840s: il tetto e' stretto sul caso
|
||||
# massimo, non largo come "60 ordini = 12 min" lasciava credere (#35 B10)
|
||||
|
||||
|
||||
def leggi(path: Path = STATE) -> list[dict]:
|
||||
if not path.exists():
|
||||
return []
|
||||
out = []
|
||||
for ln in path.read_text().splitlines():
|
||||
ln = ln.strip()
|
||||
if ln:
|
||||
out.append(json.loads(ln))
|
||||
return out
|
||||
|
||||
|
||||
def giudica(eq_usdc: float | None, equity_tot: float | None, cuscino: float | None,
|
||||
bande: dict | None = None) -> dict:
|
||||
"""PURA. -> stato, slack, soglie in dollari. `None` in ingresso = BLIND. `bande` = le frazioni
|
||||
del cuscino (default: config, via usde.bande_cuscino)."""
|
||||
if eq_usdc is None or equity_tot is None or cuscino is None:
|
||||
return dict(stato="BLIND", slack=None, preavviso_usd=None, riacquisto_usd=None)
|
||||
try:
|
||||
b = bande or U.bande_cuscino()
|
||||
except Exception as e: # noqa: BLE001 — config rotta = "non vedo", non un crash
|
||||
return dict(stato="BLIND", slack=None, preavviso_usd=None, riacquisto_usd=None,
|
||||
motivo=f"bande del cuscino non leggibili: {e}")
|
||||
slack = eq_usdc - cuscino
|
||||
preavviso, riacquisto = b["preavviso"] * cuscino, b["riacquisto"] * cuscino
|
||||
if slack < 0:
|
||||
stato = "SCOPERTO"
|
||||
elif slack < preavviso:
|
||||
stato = "PREAVVISO"
|
||||
elif slack > riacquisto:
|
||||
stato = "ECCEDENTE"
|
||||
else:
|
||||
stato = "OK"
|
||||
return dict(stato=stato, slack=round(slack, 2), preavviso_usd=round(preavviso, 2),
|
||||
riacquisto_usd=round(riacquisto, 2))
|
||||
|
||||
|
||||
AZIONE = {"SCOPERTO": "vendita", "ECCEDENTE": "acquisto"} # gli stati che convertono
|
||||
|
||||
|
||||
def transizione(prev: dict | None, stato: str) -> bool:
|
||||
"""PURA. Si allerta se lo stato e' cambiato, O se l'ultima allerta per questo stato non e'
|
||||
partita (`allertato` False): un invio fallito non consuma la transizione (debito §5.2)."""
|
||||
if stato == "OK":
|
||||
return False
|
||||
if prev is None or prev.get("stato") != stato:
|
||||
return True
|
||||
return not bool(prev.get("allertato"))
|
||||
|
||||
|
||||
def puo_riconvertire(prev_records: list[dict], now_ms: int, execution_enabled: bool,
|
||||
px: float | None, depeg_warn: float,
|
||||
min_ore: float = RIPRISTINO_MIN_ORE, stato: str = "SCOPERTO",
|
||||
dopo_fallito_ore: float = RITENTATIVO_FALLITO_ORE) -> tuple[bool, str]:
|
||||
"""PURA. Le guardie della conversione automatica. -> (si', motivo se no). ⚠️ il motivo finisce
|
||||
in un'allerta: niente '<' (Telegram HTML) — l'escape nel sink lo copre, ma qui non serve."""
|
||||
if not execution_enabled:
|
||||
return False, "execution_enabled=false in config/live.json (il libro e' disarmato: anche questo)"
|
||||
if px is None:
|
||||
return False, "prezzo USDE non leggibile: non si vende al buio (P5)"
|
||||
if px < depeg_warn:
|
||||
return False, f"USDE {px:.4f} sotto depeg_warn {depeg_warn}: ne' vendere (cristallizza) ne' comprare (scivola) — decide l'operatore"
|
||||
# contano solo i tentativi VERI (subprocess lanciato): un record con `tentata=False` — prezzo
|
||||
# illeggibile, --secco, interruttore spento — non consuma il budget (revisione fable 10/09)
|
||||
ultimo = [r for r in prev_records if (r.get("riconversione") or {}).get("tentata")]
|
||||
if ultimo:
|
||||
ore = (now_ms - int(ultimo[-1]["ts"])) / 3_600_000
|
||||
fallito = not (ultimo[-1].get("riconversione") or {}).get("ok")
|
||||
soglia = dopo_fallito_ore if (fallito and stato == "ECCEDENTE") else min_ore
|
||||
if ore < soglia:
|
||||
return False, (f"ultimo tentativo {ore:.1f}h fa, sotto le {soglia:.0f}h"
|
||||
+ (" (era FALLITO: da ECCEDENTE si riprova una volta al giorno)" if soglia == dopo_fallito_ore else "")
|
||||
+ ": non si insiste")
|
||||
return True, ""
|
||||
|
||||
|
||||
def _leggi_quota_usde() -> float | None:
|
||||
"""Quota USDE sul totale, STESSA formula di usde_watch.py:275 (usd/(usd+eq_usdc)), stessi
|
||||
mattoni di main() qui sotto (_safe_client, account_summary, U.prezzo, U.valuta): nessuna
|
||||
lettura nuova. None se il conto non e' leggibile — chi chiama tratta None come "non lo so",
|
||||
non come zero (#35 B10)."""
|
||||
client = _safe_client()
|
||||
if client is None:
|
||||
return None
|
||||
try:
|
||||
eq_usdc = float(client.account_summary("USDC")["equity"])
|
||||
eq_usde = float(client.account_summary("USDE").get("equity") or 0)
|
||||
except Exception:
|
||||
return None
|
||||
px, _ = U.prezzo(client)
|
||||
usd, _ = U.valuta(eq_usde, px)
|
||||
try:
|
||||
return round(usd / (usd + eq_usdc), 4)
|
||||
except ZeroDivisionError:
|
||||
return None
|
||||
|
||||
|
||||
def riconverti(quota: float, timeout_s: float = TIMEOUT_CONVERT_S) -> dict:
|
||||
"""Lancia lo script VERO con le sue guardie. Ritorna esito + coda dell'output (P3).
|
||||
|
||||
Rilegge la quota USDE prima e dopo il subprocess, ANCHE su timeout o eccezione: un rc!=0 con
|
||||
la quota mossa e' una conversione PARZIALE (fill gia' avvenuti prima del kill a timeout_s),
|
||||
non "FALLITA" come diceva `ok = rc==0` (#35 B10). `esito` in "ok" | "parziale" | "fallita"* —
|
||||
"ok" solo per compatibilita' resta anche in `ok` (bool)."""
|
||||
quota_prima = _leggi_quota_usde()
|
||||
cmd = [sys.executable, str(CONVERT), "--quota", f"{quota:.4f}", "--esegui"]
|
||||
try:
|
||||
r = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout_s, cwd=str(ROOT))
|
||||
rc = r.returncode
|
||||
coda = (r.stdout + r.stderr).strip().splitlines()[-12:]
|
||||
except subprocess.TimeoutExpired:
|
||||
rc, coda = None, [f"timeout dopo {timeout_s:.0f}s"]
|
||||
except Exception as e: # noqa: BLE001 — si registra, non si ingoia
|
||||
rc, coda = None, [f"{type(e).__name__}: {e}"]
|
||||
quota_dopo = _leggi_quota_usde()
|
||||
|
||||
if rc == 0:
|
||||
esito = "ok"
|
||||
elif quota_dopo is None:
|
||||
esito = "fallita (quota dopo non leggibile)"
|
||||
else:
|
||||
mossa = quota_prima is not None and abs(quota_dopo - quota_prima) > 0.005
|
||||
esito = "parziale" if mossa else "fallita"
|
||||
|
||||
return dict(rc=rc, ok=esito == "ok", esito=esito, quota_prima=quota_prima, quota_dopo=quota_dopo,
|
||||
coda=coda, cmd=" ".join(cmd[1:]))
|
||||
|
||||
|
||||
def _safe_client():
|
||||
try:
|
||||
from src.live.deribit import DeribitRead
|
||||
return DeribitRead()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _execution_enabled() -> bool:
|
||||
try:
|
||||
return bool(json.loads((ROOT / "config" / "live.json").read_text()).get("execution_enabled"))
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def main() -> int:
|
||||
quiet, secco = "--quiet" in sys.argv, "--secco" in sys.argv
|
||||
c = U.cfg()
|
||||
records = leggi()
|
||||
prev = records[-1] if records else None
|
||||
now = datetime.now(timezone.utc)
|
||||
now_ms = int(now.timestamp() * 1000)
|
||||
client = _safe_client()
|
||||
|
||||
eq_usdc = eq_usde = None
|
||||
motivo_blind = None
|
||||
if client is None:
|
||||
motivo_blind = "gateway non raggiungibile"
|
||||
else:
|
||||
try:
|
||||
eq_usdc = float(client.account_summary("USDC")["equity"])
|
||||
except Exception as e:
|
||||
motivo_blind = f"conto USDC non leggibile ({type(e).__name__})"
|
||||
try:
|
||||
eq_usde = float(client.account_summary("USDE").get("equity") or 0)
|
||||
except Exception as e:
|
||||
motivo_blind = motivo_blind or f"conto USDE non leggibile ({type(e).__name__})"
|
||||
px, px_fonte = U.prezzo(client)
|
||||
equity_tot = cuscino = come = None
|
||||
if eq_usdc is not None and eq_usde is not None:
|
||||
usd, _ = U.valuta(eq_usde, px)
|
||||
equity_tot = eq_usdc + usd
|
||||
cuscino, come = U.cuscino_richiesto_usd(equity_tot)
|
||||
try:
|
||||
bande = U.bande_cuscino(c)
|
||||
except Exception as e: # noqa: BLE001 — si registra e si allerta, non si crasha (P5)
|
||||
bande, motivo_blind = None, f"config usde: {e}"
|
||||
g = giudica(eq_usdc, equity_tot, cuscino, bande)
|
||||
stato = g["stato"]
|
||||
if stato == "BLIND" and g.get("motivo"):
|
||||
motivo_blind = motivo_blind or g["motivo"]
|
||||
|
||||
rec = dict(ts=now_ms, data=now.strftime("%Y-%m-%dT%H:%M:%SZ"), stato=stato,
|
||||
motivo_blind=motivo_blind, eq_usdc=eq_usdc, eq_usde=eq_usde, px=px,
|
||||
px_fonte=px_fonte, equity_tot=equity_tot, cuscino=cuscino, cuscino_come=come,
|
||||
slack=g["slack"], preavviso_usd=g["preavviso_usd"], riacquisto_usd=g["riacquisto_usd"],
|
||||
bande=bande, allertato=None, allerta_errore=None, riconversione=None, secco=secco)
|
||||
|
||||
# --- conversione automatica: SCOPERTO vende, ECCEDENTE compra, stesso bersaglio q* ---
|
||||
if stato in AZIONE:
|
||||
ok, perche = puo_riconvertire(records, now_ms, _execution_enabled(), px, c["depeg_warn"], stato=stato)
|
||||
quota = U.quota_ripristino(bande["margine"])
|
||||
if not ok:
|
||||
rec["riconversione"] = dict(tentata=False, verso=AZIONE[stato], quota=quota, motivo=perche)
|
||||
elif secco:
|
||||
rec["riconversione"] = dict(tentata=False, verso=AZIONE[stato], quota=quota,
|
||||
motivo=f"--secco: avrei lanciato usde_convert --quota {quota:.4f} --esegui ({AZIONE[stato]})")
|
||||
else:
|
||||
ris = riconverti(quota)
|
||||
rec["riconversione"] = dict(tentata=True, verso=AZIONE[stato], quota=quota, **ris)
|
||||
|
||||
# --- allarmi: alla transizione, marcatore scritto con l'esito dell'invio ---
|
||||
# ⚠️ Telegram e' in parse_mode HTML: un '<' nudo nel testo fa fallire l'invio (successo al
|
||||
# primo giro vero, 13:53Z del 10/09: «(< 10% del cuscino)»). Qui non si scrive mai '<'.
|
||||
def _esito_azione(r: dict) -> str:
|
||||
if not r.get("tentata"):
|
||||
return f"{r.get('verso', 'conversione')} NON tentata: {r.get('motivo')}"
|
||||
esito = r.get("esito", "ok" if r.get("ok") else "fallita")
|
||||
if esito == "parziale":
|
||||
qp, qd = r.get("quota_prima"), r.get("quota_dopo")
|
||||
qp_s = f"{qp:.0%}" if qp is not None else "n/d"
|
||||
qd_s = f"{qd:.0%}" if qd is not None else "n/d"
|
||||
return f"{r['verso']} PARZIALE: quota da {qp_s} a {qd_s}"
|
||||
return (f"{r['verso']} " + ("ESEGUITA" if esito == "ok" else "FALLITA")
|
||||
+ f" (quota bersaglio {r['quota']:.0%})")
|
||||
|
||||
if transizione(prev, stato):
|
||||
if secco:
|
||||
rec["allertato"] = False
|
||||
elif stato == "SCOPERTO":
|
||||
rec["allertato"] = notify("🚨 CUSCINO USDC SCOPERTO", {
|
||||
"USDC equity": f"${eq_usdc:,.2f}", "cuscino richiesto": f"${cuscino:,.2f} ({come})",
|
||||
"slack": f"${g['slack']:+,.2f}", "azione": _esito_azione(rec["riconversione"] or {}),
|
||||
"nota": "un USDC negativo costa 0,05%/giorno; il libro si regola in USDC"}, tentativi=3)
|
||||
elif stato == "PREAVVISO":
|
||||
rec["allertato"] = notify("⚠️ cuscino USDC in esaurimento", {
|
||||
"USDC equity": f"${eq_usdc:,.2f}", "cuscino richiesto": f"${cuscino:,.2f}",
|
||||
"slack": f"${g['slack']:+,.2f} (sotto il {bande['preavviso']:.0%} del cuscino)",
|
||||
"nota": f"sotto zero vende USDE da solo fino a quota {U.quota_ripristino(bande['margine']):.0%}"})
|
||||
elif stato == "ECCEDENTE":
|
||||
rec["allertato"] = notify("📌 cuscino USDC eccedente: riacquisto USDE", {
|
||||
"USDC equity": f"${eq_usdc:,.2f}", "cuscino richiesto": f"${cuscino:,.2f}",
|
||||
"slack": f"${g['slack']:+,.2f} (sopra il {bande['riacquisto']:.0%} del cuscino)",
|
||||
"azione": _esito_azione(rec["riconversione"] or {})})
|
||||
elif stato == "BLIND":
|
||||
rec["allertato"] = notify("⚠️ cuscino_watch BLIND", {"motivo": motivo_blind or "?",
|
||||
"nota": "'non vedo' non e' 'va tutto bene' (P5)"})
|
||||
elif rec["riconversione"] and rec["riconversione"].get("tentata") and not secco:
|
||||
# non e' una transizione ma e' un ordine mandato: si dice sempre
|
||||
r = rec["riconversione"]
|
||||
esito = r.get("esito", "ok" if r.get("ok") else "fallita")
|
||||
verbo = {"ok": "eseguita", "parziale": "PARZIALE"}.get(esito, "FALLITA")
|
||||
rec["allertato"] = notify(f"📌 cuscino USDC: {r['verso']} {verbo}",
|
||||
{"quota bersaglio": f"{r['quota']:.0%}", "esito": " | ".join(r["coda"][-3:])})
|
||||
if rec["allertato"] is False and not secco:
|
||||
from src.live.notifier import ultimo_errore
|
||||
rec["allerta_errore"] = ultimo_errore() # P3: si registra dove si ingoia
|
||||
|
||||
# --secco e' secco ANCHE sul registro: un giro a mano non deve diventare il `prev` del cron,
|
||||
# ne' far sembrare vivo un monitor fermo (revisione fable 10/09)
|
||||
if not secco:
|
||||
STATE.parent.mkdir(parents=True, exist_ok=True)
|
||||
with STATE.open("a") as fh:
|
||||
fh.write(json.dumps(rec) + "\n")
|
||||
|
||||
cambiato = prev is None or prev.get("stato") != stato or rec["riconversione"] is not None
|
||||
if not quiet or cambiato:
|
||||
print("=" * 78)
|
||||
print(f" CUSCINO WATCH — {rec['data']} {'(SECCO)' if secco else ''}")
|
||||
print("=" * 78)
|
||||
if stato == "BLIND":
|
||||
print(f" stato : BLIND — {motivo_blind}")
|
||||
else:
|
||||
print(f" conto : USDC equity ${eq_usdc:,.2f} + USDE {eq_usde:,.2f} @ {px if px else 'n/d'}"
|
||||
f" ({px_fonte}) = ${equity_tot:,.2f}")
|
||||
print(f" cuscino : ${cuscino:,.2f} richiesti ({come})")
|
||||
print(f" slack : ${g['slack']:+,.2f} (preavviso sotto ${g['preavviso_usd']:,.2f},"
|
||||
f" riacquisto sopra ${g['riacquisto_usd']:,.2f}, bersaglio quota {U.quota_ripristino(bande['margine']):.0%})")
|
||||
print(f" stato : {stato}")
|
||||
if rec["riconversione"]:
|
||||
r = rec["riconversione"]
|
||||
print(f" {r.get('verso', 'conversione'):<12}: {'TENTATA' if r.get('tentata') else 'NON tentata'} — quota {r['quota']:.4f}"
|
||||
+ (f" — {r.get('motivo')}" if r.get("motivo") else ""))
|
||||
for ln in r.get("coda", []):
|
||||
print(f" {ln}")
|
||||
if rec["allertato"] is not None:
|
||||
print(f" allerta : {'inviata' if rec['allertato'] else 'NON inviata (si ripete al giro dopo)'}"
|
||||
+ (f" — {rec['allerta_errore']}" if rec.get("allerta_errore") else ""))
|
||||
return 0
|
||||
|
||||
|
||||
USO = """uso: cuscino_watch.py [--quiet] [--secco]
|
||||
|
||||
cuscino_watch.py — sorveglianza ORARIA del cuscino USDC; sotto zero vende USDE, sopra la banda ricompra.
|
||||
uv run python scripts/live/cuscino_watch.py # un giro: legge, giudica, agisce
|
||||
uv run python scripts/live/cuscino_watch.py --quiet # stampa solo se cambia qualcosa
|
||||
uv run python scripts/live/cuscino_watch.py --secco # nessun ordine, nessun Telegram
|
||||
|
||||
Dettaglio nel docstring in testa al file."""
|
||||
|
||||
if __name__ == "__main__":
|
||||
from src.live.cli import valida
|
||||
valida("cuscino_watch.py", USO, flag=("--quiet", "--secco"), con_valore=())
|
||||
sys.exit(main())
|
||||
@@ -42,10 +42,13 @@ import pandas as pd
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from src.live.giri import registra_giro # noqa: E402
|
||||
from src.live.notifier import notify # noqa: E402
|
||||
from src.portfolio.portfolio import combine_outer # noqa: E402
|
||||
from src.portfolio.sleeves import deribit_book_sleeves, tp01_sleeve # noqa: E402
|
||||
|
||||
RUNS = ROOT / "data" / "edge_watch" / "runs.jsonl"
|
||||
|
||||
# --- soglie CONGELATE (cambiarle invalida la taratura di r0726_edge_death.py) ---
|
||||
WIN_MONTHS = 36
|
||||
SHARPE_KILL = -0.5
|
||||
@@ -119,6 +122,7 @@ def run() -> dict:
|
||||
|
||||
def main() -> int:
|
||||
r = run()
|
||||
registra_giro(RUNS, sharpe=r["sharpe"], n_alert=len(r["alerts"]))
|
||||
if "--quiet" not in sys.argv:
|
||||
print("=" * 78)
|
||||
print(" EDGE WATCH — il book live sta ancora funzionando?")
|
||||
@@ -136,7 +140,7 @@ def main() -> int:
|
||||
print(f"\n {n_ok}/{len(r['protection'])} anni di sinistro superati · "
|
||||
f"fallimenti consecutivi in corso: {r['consec_fail']}")
|
||||
if r["alerts"]:
|
||||
notify("🚨 EDGE WATCH — criterio di kill scattato sul book LIVE",
|
||||
notify("🚨 EDGE WATCH — criterio di kill scattato sul book (serie backtest dei sleeve, non il P&L eseguito)",
|
||||
{f"alert {i+1}": a for i, a in enumerate(r["alerts"])})
|
||||
for a in r["alerts"]:
|
||||
print(f" ALERT: {a}")
|
||||
@@ -144,5 +148,15 @@ def main() -> int:
|
||||
return 0
|
||||
|
||||
|
||||
USO = """uso: edge_watch.py [--quiet]
|
||||
|
||||
edge_watch.py — sorveglianza dell'EDGE del book live. Riporta e allerta; non tocca nulla.
|
||||
uv run python scripts/live/edge_watch.py # report
|
||||
uv run python scripts/live/edge_watch.py --quiet # solo se qualcosa scatta (cron)
|
||||
|
||||
Dettaglio nel docstring in testa al file."""
|
||||
|
||||
if __name__ == "__main__":
|
||||
from src.live.cli import valida
|
||||
valida("edge_watch.py", USO, flag=("--quiet",), con_valore=())
|
||||
raise SystemExit(main())
|
||||
|
||||
@@ -49,9 +49,11 @@ ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from src.live.book import INSTRUMENT as BOOK_INSTRUMENT # noqa: E402
|
||||
from src.live.giri import registra_giro # noqa: E402
|
||||
from src.live.notifier import notify # noqa: E402
|
||||
|
||||
STATE = ROOT / "data" / "fee_watch" / "state.json"
|
||||
RUNS = ROOT / "data" / "fee_watch" / "runs.jsonl"
|
||||
# ⚠️ CORREZIONE 2026-08-21. Qui c'era la tupla CABLATA ("BTC-PERPETUAL", "ETH-PERPETUAL") — gli
|
||||
# INVERSE, regolati in BTC/ETH — mentre il book esegue sui LINEARI USDC (BTC_USDC-PERPETUAL).
|
||||
# Sono due linee di prodotto distinte e Deribit le cambia in modo indipendente: il 18/08 ha
|
||||
@@ -77,6 +79,7 @@ SOGLIA_OK = 5.0 # <= -> nessuna azione
|
||||
SOGLIA_AZIONE = 10.0 # > -> rivedere il peso di SKH01
|
||||
D_SHARPE_PER_BPS = -0.017 # sensibilita' marginale del BOOK, misurata
|
||||
D_CAGR_PER_BPS = -0.0023
|
||||
SOGLIA_DIVERGENZA_BPS = 1.0 # #37 B9: |fee realizzata - tier pubblico| oltre questo -> notify
|
||||
|
||||
|
||||
def fetch_fees(instrument: str) -> dict:
|
||||
@@ -207,14 +210,30 @@ def run() -> dict:
|
||||
changes = diff_vs(prev, cur)
|
||||
worst = max(c["taker_bps"] for c in cur.values())
|
||||
lvl, why = verdict(worst)
|
||||
# issue #37 (B9). realized_fee_bps() girava SOLO dentro il blocco di stampa: in cron
|
||||
# (--quiet, il caso normale) non girava mai e una fee realmente pagata diversa dal tier
|
||||
# pubblico non avvisava nessuno. Qui si calcola SEMPRE ed entra nel record del giro
|
||||
# (registra_giro, sotto). {} -> None: "non misurata" (nessun trade recente), mai "zero".
|
||||
real = realized_fee_bps() or None
|
||||
divergenze = []
|
||||
if real:
|
||||
for ins, bps in real.items():
|
||||
tier = cur.get(ins, {}).get("taker_bps")
|
||||
if tier is None:
|
||||
continue
|
||||
d = bps - tier
|
||||
if abs(d) > SOGLIA_DIVERGENZA_BPS:
|
||||
divergenze.append(
|
||||
f"{ins}: realizzata {bps:.2f} bps vs tier pubblico {tier:.2f} ({d:+.2f})")
|
||||
return dict(current=cur, changes=changes, worst_taker_bps=worst, level=lvl, reason=why,
|
||||
first_read=not prev)
|
||||
first_read=not prev, realized=real, divergenze=divergenze,
|
||||
divergenza_gia_detta=bool(prev.get("divergenza_detta")))
|
||||
|
||||
|
||||
def main() -> int:
|
||||
r = run()
|
||||
quiet = "--quiet" in sys.argv
|
||||
interessante = bool(r["changes"]) or r["level"] != "OK"
|
||||
interessante = bool(r["changes"]) or r["level"] != "OK" or bool(r.get("divergenze"))
|
||||
|
||||
if not quiet or interessante:
|
||||
print("=" * 86)
|
||||
@@ -230,7 +249,7 @@ def main() -> int:
|
||||
print(" (prima lettura: stato inizializzato, nessun confronto possibile)")
|
||||
for c in r["changes"]:
|
||||
print(f" CAMBIO: {c}")
|
||||
real = realized_fee_bps()
|
||||
real = r.get("realized") # calcolato una volta in run(), non piu' qui (#37 B9)
|
||||
if real:
|
||||
print("\n cross-check sui trade REALI del conto (fonte autorevole):")
|
||||
for ins, bps in real.items():
|
||||
@@ -238,19 +257,61 @@ def main() -> int:
|
||||
nota = ""
|
||||
if tier is not None:
|
||||
d = bps - tier
|
||||
nota = f" (tier taker {tier:.2f} → {d:+.2f})" + (" ⚠️ DIVERGE" if abs(d) > 1.0 else "")
|
||||
nota = (f" (tier taker {tier:.2f} → {d:+.2f})"
|
||||
+ (" ⚠️ DIVERGE" if abs(d) > SOGLIA_DIVERGENZA_BPS else ""))
|
||||
print(f" {ins:<22}{bps:>9.2f} bps/lato effettivi{nota}")
|
||||
else:
|
||||
print("\n cross-check sui trade reali: NON MISURATO (nessun trade recente leggibile)")
|
||||
|
||||
if r["changes"] or r["level"] == "AZIONE":
|
||||
notify("💸 FEE WATCH — schema fee Deribit", {
|
||||
# issue #29 (B8): save_state veniva chiamato SEMPRE, anche se l'invio necessario falliva —
|
||||
# il giro dopo `diff_vs` non avrebbe piu' visto il cambiamento (lo stato salvato era gia'
|
||||
# quello nuovo) e il cambio andava perso per sempre invece che ritentare al giro dopo.
|
||||
serve_invio = bool(r["changes"] or r["level"] == "AZIONE")
|
||||
inviato = True
|
||||
if serve_invio:
|
||||
inviato = notify("💸 FEE WATCH — schema fee Deribit", {
|
||||
"verdetto": f"[{r['level']}] {r['reason']}",
|
||||
**{f"cambio {i+1}": c for i, c in enumerate(r["changes"])},
|
||||
})
|
||||
save_state(r["current"])
|
||||
if not inviato and (not quiet or interessante):
|
||||
print(" ⚠️ invio FALLITO — stato NON salvato: il prossimo giro ritenta")
|
||||
# issue #37 (B9): fee realmente pagata che diverge dal tier pubblico atteso -> notify UNA
|
||||
# volta per episodio. Marcatore "divergenza_detta" nello stesso state.json (stessa regola di
|
||||
# B8: si scrive solo se l'invio e' riuscito), azzerato quando la divergenza torna sotto
|
||||
# soglia. E' un evento indipendente dal cambio di tier: se qui serve un invio e quello di B8
|
||||
# e' appena fallito, save_state sotto non gira (stessa gate di B8) e il marcatore non si
|
||||
# scrive — al giro dopo si ridice, ridondante ma mai persa, la stessa scelta di B8.
|
||||
divergenze = r.get("divergenze") or []
|
||||
gia_detta = bool(r.get("divergenza_gia_detta"))
|
||||
marcatore = gia_detta
|
||||
if divergenze and not gia_detta:
|
||||
inviata_div = notify("💸 FEE WATCH — fee realizzata diverge dal tier pubblico", {
|
||||
f"divergenza {i+1}": d for i, d in enumerate(divergenze)
|
||||
})
|
||||
if inviata_div:
|
||||
marcatore = True
|
||||
elif not quiet or interessante:
|
||||
print(" ⚠️ invio divergenza FALLITO — marcatore non scritto: il prossimo giro ritenta")
|
||||
elif not divergenze:
|
||||
marcatore = False
|
||||
|
||||
stato_da_salvare = dict(r["current"])
|
||||
stato_da_salvare["divergenza_detta"] = marcatore
|
||||
if not serve_invio or inviato:
|
||||
save_state(stato_da_salvare)
|
||||
registra_giro(RUNS, level=r["level"], realized=r.get("realized"))
|
||||
return 2 if r["level"] == "AZIONE" else 0
|
||||
|
||||
|
||||
USO = """uso: fee_watch.py [--quiet]
|
||||
|
||||
fee_watch.py — sorveglia lo schema fee di Deribit e applica la regola DECISA IN ANTICIPO.
|
||||
uv run python scripts/live/fee_watch.py # report
|
||||
uv run python scripts/live/fee_watch.py --quiet # stampa/allerta solo se qualcosa cambia
|
||||
|
||||
Dettaglio nel docstring in testa al file."""
|
||||
|
||||
if __name__ == "__main__":
|
||||
from src.live.cli import valida
|
||||
valida("fee_watch.py", USO, flag=("--quiet",), con_valore=())
|
||||
raise SystemExit(main())
|
||||
|
||||
@@ -41,7 +41,19 @@ def una(con, g: date, nota: str | None = None, verbose: bool = True) -> Path:
|
||||
return f
|
||||
|
||||
|
||||
USO = """uso: journal.py [--giorno AAAA-MM-GG] [--backfill N] [--nota "testo"] [--quiet]
|
||||
|
||||
(nessun flag) scrive la voce di OGGI (pagina marcata PARZIALE: il giorno non e' chiuso)
|
||||
--giorno G scrive la voce del giorno G
|
||||
--backfill N ricostruisce gli ultimi N giorni, dal piu' vecchio
|
||||
--nota "..." annota il giorno (campo dell'operatore, mai riscritto da un ricalcolo)
|
||||
--quiet non stampa la riga di riepilogo
|
||||
|
||||
⚠️ SCRIVE: una riga nel DB e una pagina in docs/journal/. Sola lettura su feed, log e venue."""
|
||||
|
||||
if __name__ == "__main__":
|
||||
from src.live.cli import valida
|
||||
valida("journal.py", USO, flag=("--quiet",), con_valore=("--giorno", "--backfill", "--nota"))
|
||||
# sincronizzare PRIMA: altrimenti la sezione 'Libro' (letta dal log) e la sezione 'P&L'
|
||||
# (letta dal DB) descrivono due istanti diversi e la pagina si contraddice da sola.
|
||||
sys.path.insert(0, str(ROOT / "scripts" / "live"))
|
||||
|
||||
@@ -137,5 +137,14 @@ def main():
|
||||
raise
|
||||
|
||||
|
||||
USO = """uso: live_execute.py [--execute]
|
||||
|
||||
uv run python scripts/live/live_execute.py # DRY-RUN (piano, nessun ordine)
|
||||
uv run python scripts/live/live_execute.py --execute # esegue SOLO se execution_enabled=true
|
||||
|
||||
Dettaglio nel docstring in testa al file."""
|
||||
|
||||
if __name__ == "__main__":
|
||||
from src.live.cli import valida
|
||||
valida("live_execute.py", USO, flag=("--execute",), con_valore=())
|
||||
main()
|
||||
|
||||
@@ -74,5 +74,15 @@ def main():
|
||||
(f"{len(r['orders'])} ordine/i costruito/i sopra." if r["orders"] else "Target flat: 0 ordini."))
|
||||
|
||||
|
||||
USO = """uso: live_trend.py [--equity V] [--no-net]
|
||||
|
||||
uv run python scripts/live/live_trend.py # shadow su mainnet reale
|
||||
uv run python scripts/live/live_trend.py --equity 2000 # forza la base di sizing
|
||||
uv run python scripts/live/live_trend.py --no-net # offline: solo matematica + parita'
|
||||
|
||||
Dettaglio nel docstring in testa al file."""
|
||||
|
||||
if __name__ == "__main__":
|
||||
from src.live.cli import valida
|
||||
valida("live_trend.py", USO, flag=("--no-net",), con_valore=("--equity",))
|
||||
main()
|
||||
|
||||
@@ -88,5 +88,14 @@ def main():
|
||||
print(" Validato: invio ordine reale, fill, fee reali, reconciliation, ritorno a flat.")
|
||||
|
||||
|
||||
USO = """uso: microtest.py [--live]
|
||||
|
||||
uv run python scripts/live/microtest.py # DRY-RUN: nessun ordine inviato
|
||||
uv run python scripts/live/microtest.py --live # invia il round-trip REALE
|
||||
|
||||
Dettaglio nel docstring in testa al file."""
|
||||
|
||||
if __name__ == "__main__":
|
||||
from src.live.cli import valida
|
||||
valida("microtest.py", USO, flag=("--live",), con_valore=())
|
||||
main()
|
||||
|
||||
@@ -10,20 +10,91 @@ monitor e la soglia di copertura 0.80 (la stessa del veto d'integrita' di DVOLSP
|
||||
|
||||
uv run python scripts/live/monitor_health.py # report
|
||||
uv run python scripts/live/monitor_health.py --quiet # stampa/allerta solo se scatta
|
||||
uv run python scripts/live/monitor_health.py --solo-orari # (#30) solo i sorveglianti a
|
||||
# max_age<=6h (cuscino_watch, venue_watch, collect_chain), notifica solo il NUOVO
|
||||
# rispetto a data/live/monitor_health_detto.json — pensato per girare ogni ora
|
||||
# (cron_cuscino.sh), non per sostituire il giro giornaliero completo.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from src.live.monitor_health import MIN_COVERAGE, alerts_from, check_all # noqa: E402
|
||||
from src.live.monitor_health import ( # noqa: E402
|
||||
MIN_COVERAGE,
|
||||
alert_nuovi,
|
||||
alerts_from,
|
||||
check_all,
|
||||
marcatore_aggiornato,
|
||||
monitors_orari,
|
||||
)
|
||||
from src.live.notifier import notify # noqa: E402
|
||||
|
||||
# (#30) marcatore "gia' detto" del giro orario: {"detti": [<chiave alert>], "ts": ...}. Vive fuori
|
||||
# da src/ perche' e' I/O, non parte della misura pura.
|
||||
DETTO = ROOT / "data" / "live" / "monitor_health_detto.json"
|
||||
|
||||
|
||||
def _carica_detti(path: Path = DETTO) -> set[str]:
|
||||
if not path.exists():
|
||||
return set()
|
||||
try:
|
||||
return set(json.loads(path.read_text()).get("detti", []))
|
||||
except Exception:
|
||||
return set()
|
||||
|
||||
|
||||
def _salva_detti(detti: set[str], path: Path = DETTO) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(
|
||||
{"detti": sorted(detti), "ts": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")},
|
||||
indent=2))
|
||||
|
||||
|
||||
def main_orari() -> int:
|
||||
"""(#30) Giro orario: solo i sorveglianti a max_age<=6h, notifica solo il NUOVO rispetto al
|
||||
marcatore. Exit 2 se c'e' un alert (anche gia' detto), 0 altrimenti — il giro giornaliero
|
||||
resta l'unico posto dove "nessun alert nuovo" vuol dire davvero "tutto OK"."""
|
||||
quiet = "--quiet" in sys.argv
|
||||
rows = check_all(ROOT, monitors=monitors_orari())
|
||||
alerts = alerts_from(rows)
|
||||
detti = _carica_detti(DETTO) # DETTO letto qui, non nel default del parametro (monkeypatch)
|
||||
nuovi = alert_nuovi(rows, detti)
|
||||
aggiornato = marcatore_aggiornato(rows, detti)
|
||||
|
||||
if not quiet or alerts:
|
||||
print("=" * 92)
|
||||
print(" MONITOR HEALTH — giro orario (sorveglianti con max_age <= 6h)")
|
||||
print("=" * 92)
|
||||
for r in rows:
|
||||
cov = f"{r['coverage']:.0%}" if r["coverage"] is not None else "-"
|
||||
age = f"{r['age_h']:.0f}h" if r["age_h"] is not None else "-"
|
||||
print(f" {r['name']:<20}{r['status']:>10}{age:>13}{cov:>10}")
|
||||
|
||||
inviato = True
|
||||
if nuovi:
|
||||
msgs = alerts_from(nuovi)
|
||||
inviato = notify("🩺 MONITOR HEALTH — un sorvegliante orario non sta registrando",
|
||||
{f"alert {i+1}": m for i, m in enumerate(msgs)})
|
||||
for m in msgs:
|
||||
print(f" ALERT (nuovo): {m}")
|
||||
if not inviato:
|
||||
print(" ⚠️ invio FALLITO — marcatore NON aggiornato: il prossimo giro ritenta")
|
||||
if not nuovi or inviato:
|
||||
_salva_detti(aggiornato, DETTO)
|
||||
|
||||
return 2 if alerts else 0
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if "--solo-orari" in sys.argv:
|
||||
return main_orari()
|
||||
|
||||
rows = check_all(ROOT)
|
||||
alerts = alerts_from(rows)
|
||||
|
||||
@@ -55,5 +126,17 @@ def main() -> int:
|
||||
return 0
|
||||
|
||||
|
||||
USO = """uso: monitor_health.py [--quiet] [--solo-orari]
|
||||
|
||||
monitor_health.py — i forward-monitor stanno registrando? Riporta e allerta, non tocca nulla.
|
||||
uv run python scripts/live/monitor_health.py # report
|
||||
uv run python scripts/live/monitor_health.py --quiet # stampa/allerta solo se scatta
|
||||
uv run python scripts/live/monitor_health.py --solo-orari # (#30) solo i sorveglianti a
|
||||
max_age<=6h, notifica solo il NUOVO rispetto al marcatore
|
||||
|
||||
Dettaglio nel docstring in testa al file."""
|
||||
|
||||
if __name__ == "__main__":
|
||||
from src.live.cli import valida
|
||||
valida("monitor_health.py", USO, flag=("--quiet", "--solo-orari"), con_valore=())
|
||||
raise SystemExit(main())
|
||||
|
||||
@@ -118,5 +118,13 @@ def main():
|
||||
print(f" GTAA (IB, asof {asof}): " + ", ".join(f"{k} {v:.0%}" for k, v in gw.items() if v) + f" | cash {cash:.0%}")
|
||||
|
||||
|
||||
USO = """uso: paper_combo.py [--reset] [--status]
|
||||
|
||||
uv run python scripts/live/paper_combo.py [--status|--reset]
|
||||
|
||||
Dettaglio nel docstring in testa al file."""
|
||||
|
||||
if __name__ == "__main__":
|
||||
from src.live.cli import valida
|
||||
valida("paper_combo.py", USO, flag=("--status", "--reset"), con_valore=())
|
||||
main()
|
||||
|
||||
@@ -87,5 +87,15 @@ def main():
|
||||
print(f" posizioni correnti: {pf.current_positions()}")
|
||||
|
||||
|
||||
USO = """uso: paper_portfolio.py [--reset] [--status]
|
||||
|
||||
uv run python scripts/live/paper_portfolio.py # avanza (init al 1o run)
|
||||
uv run python scripts/live/paper_portfolio.py --status # solo stato
|
||||
uv run python scripts/live/paper_portfolio.py --reset # azzera (riparte da ora)
|
||||
|
||||
Dettaglio nel docstring in testa al file."""
|
||||
|
||||
if __name__ == "__main__":
|
||||
from src.live.cli import valida
|
||||
valida("paper_portfolio.py", USO, flag=("--status", "--reset"), con_valore=())
|
||||
main()
|
||||
|
||||
@@ -53,6 +53,21 @@ MODELED_CAPITAL = 2000.0 # nominale, ribilanciamento continuo
|
||||
REAL_CAPITAL = 600.0 # capitale mainnet reale
|
||||
MIN_ORDER = 5.0 # min order Deribit -> sotto, il conto vero NON ribilancia
|
||||
|
||||
# --- GATE PRE-REGISTRATO `GATE PREVDAY-01` (2026-08-23, RESULTS-0822 §54) -----------------------
|
||||
# Cablato qui il 2026-09-10 (issue #4): esisteva solo nel testo, e la revisione del 09/09 lo ha
|
||||
# letto come "gate senza data". Decisione dell'operatore: tenere la data, cablare kill e veto.
|
||||
GATE_DATE = "2027-06-21" # decisione piena: 7 condizioni (a)-(g), tutte necessarie
|
||||
KILL_SHARPE = -0.50 # kill: Sharpe forward < -0,50 ...
|
||||
KILL_MIN_ACTIVE_DAYS = 180 # ... su >= 180 giorni di barre ATTIVE (posizione != 0)
|
||||
MIN_RECON_FRAC = 0.80 # veto d'integrita': >= 80% di barre ricostruibili dal feed
|
||||
RECON_TOL = 1.5e-6 # net_modeled e' registrato a 6 decimali: mezzo ulp di tolleranza
|
||||
RECENTI_GIORNI = 30 # "i minuti non registrati non devono crescere": ultimi 30g vs tutto
|
||||
MIN_DIV_CRESCITA = 5 # ... ma UNA barra divergente su 1943 non e' una crescita (P14): la
|
||||
# crescita richiede >= 5 divergenti recenti E tasso doppio del totale
|
||||
# LENTE: il kill si misura sullo Sharpe GIORNALIERO (somma delle barre orarie per giorno UTC),
|
||||
# la lente con cui e' misurato ogni altro sleeve del progetto — §54: sulla lente ORARIA lo stesso
|
||||
# forward vale +2,04 contro +1,56 giornaliero. Al kill la famiglia NON si ri-ottimizza.
|
||||
|
||||
|
||||
def build_bars() -> dict[str, pd.DataFrame]:
|
||||
return {a: load(a, "1h").reset_index(drop=True) for a in ASSETS}
|
||||
@@ -145,6 +160,151 @@ def advance(st: dict, dfs: dict) -> dict:
|
||||
return st
|
||||
|
||||
|
||||
def _returns_df() -> pd.DataFrame | None:
|
||||
if not RETURNS_FILE.exists():
|
||||
return None
|
||||
r = pd.read_json(RETURNS_FILE, lines=True)
|
||||
if r.empty:
|
||||
return None
|
||||
r["dt"] = pd.to_datetime(r["ts"], unit="ms", utc=True)
|
||||
return r
|
||||
|
||||
|
||||
def sharpe_giornaliero(r: pd.DataFrame, col: str = "net_modeled") -> tuple[float, int]:
|
||||
"""PURA. Sharpe annualizzato sui rendimenti GIORNALIERI (somma delle barre orarie per giorno
|
||||
UTC) -> (sharpe, n_giorni). nan sotto 30 giorni o a varianza nulla."""
|
||||
d = r.groupby(r["dt"].dt.floor("D"))[col].sum()
|
||||
if len(d) < 30 or d.std() == 0:
|
||||
return float("nan"), int(len(d))
|
||||
return float(d.mean() / d.std() * np.sqrt(365.25)), int(len(d))
|
||||
|
||||
|
||||
def giorni_attivi(r: pd.DataFrame) -> int:
|
||||
"""PURA. Giorni UTC con almeno una barra a posizione != 0 (il kill conta le barre ATTIVE).
|
||||
⚠️ le posizioni registrate sono quelle del libro REAL-$600 (advance scrive `pr`), lo Sharpe
|
||||
del kill e' MODELED: divergono solo se un ingresso da 0 vale < $5 di nozionale, cosa che a
|
||||
vol-target 20% non succede (10/09: 81/81 giorni). Dichiarato, non riparato (D5)."""
|
||||
att = r[(r["pos_btc"] != 0) | (r["pos_eth"] != 0)]
|
||||
return int(att["dt"].dt.floor("D").nunique())
|
||||
|
||||
|
||||
def kill_check(sharpe: float, n_attivi: int, today: pd.Timestamp) -> tuple[str, str]:
|
||||
"""PURA. -> (stato, motivo). KILL solo con >= 180 giorni attivi E Sharpe < -0,50 (§54):
|
||||
a quell'orizzonte P(uccidere un edge vivo a Sharpe 1,2) = 9,7%."""
|
||||
if n_attivi < KILL_MIN_ACTIVE_DAYS:
|
||||
return "NON_MATURO", (f"{n_attivi} giorni attivi < {KILL_MIN_ACTIVE_DAYS}: il kill non e' "
|
||||
f"leggibile (mancano {KILL_MIN_ACTIVE_DAYS - n_attivi} giorni)")
|
||||
if not np.isfinite(sharpe):
|
||||
return "NON_MISURABILE", "Sharpe forward non misurabile"
|
||||
if sharpe < KILL_SHARPE:
|
||||
return "KILL", f"Sharpe {sharpe:+.2f} < {KILL_SHARPE:+.2f} su {n_attivi} giorni attivi -> RITIRARE, senza ri-ottimizzare"
|
||||
return "SUPERATO", f"Sharpe {sharpe:+.2f} >= {KILL_SHARPE:+.2f} su {n_attivi} giorni attivi"
|
||||
|
||||
|
||||
def ricostruzione(r: pd.DataFrame, dfs: dict, start_ts: int) -> dict:
|
||||
"""Rigioca la strategia CONGELATA sul feed di OGGI dallo `start_ts` del monitor e confronta
|
||||
barra per barra `net_modeled` registrato con quello ricalcolato (ribilanciamento continuo,
|
||||
la stessa aritmetica di `advance`). Una barra e' RICOSTRUIBILE se coincide entro RECON_TOL.
|
||||
|
||||
Cosa misura: quante barre registrate sono riproducibili dal dato certificato. Le divergenti
|
||||
sono barre calcolate su un feed che il rebuild ha poi rivisto (§54: 67 su 1512, tutte
|
||||
all'ora del cron) — e' la misura del veto d'integrita' (>= 80%, e i minuti persi non devono
|
||||
crescere: qui, divergenti/giorno negli ultimi 30g contro l'intera finestra)."""
|
||||
data = {}
|
||||
for a in ASSETS:
|
||||
df = dfs[a]
|
||||
c = df["close"].values.astype(float)
|
||||
rr = np.zeros(len(c)); rr[1:] = c[1:] / c[:-1] - 1.0
|
||||
data[a] = dict(ts=df["timestamp"].values.astype("int64"), r=rr, tgt=prevday_target(df))
|
||||
idx = {a: {int(t): i for i, t in enumerate(data[a]["ts"])} for a in ASSETS}
|
||||
# posizione iniziale come init_state: target all'ultima barra <= start_ts
|
||||
pos = {}
|
||||
for a in ASSETS:
|
||||
i0 = max(i for t, i in idx[a].items() if t <= start_ts)
|
||||
pos[a] = float(data[a]["tgt"][i0])
|
||||
ricalc, presenti = [], []
|
||||
for t in r["ts"].astype("int64"):
|
||||
t = int(t)
|
||||
if any(t not in idx[a] for a in ASSETS):
|
||||
ricalc.append(np.nan); presenti.append(False); continue
|
||||
net = 0.0
|
||||
for a in ASSETS:
|
||||
i = idx[a][t]
|
||||
tgt = float(data[a]["tgt"][i])
|
||||
net += WEIGHT * (pos[a] * float(data[a]["r"][i]) - FEE_SIDE * abs(tgt - pos[a]))
|
||||
pos[a] = tgt
|
||||
ricalc.append(net); presenti.append(True)
|
||||
ricalc = np.asarray(ricalc, dtype=float)
|
||||
reg = r["net_modeled"].values.astype(float)
|
||||
ok = np.isfinite(ricalc) & (np.abs(ricalc - reg) <= RECON_TOL)
|
||||
n = len(reg)
|
||||
giorni = r["dt"].dt.floor("D")
|
||||
span_g = max(1, int(giorni.nunique()))
|
||||
recenti = r["dt"] >= (r["dt"].max() - pd.Timedelta(days=RECENTI_GIORNI))
|
||||
div_rec = int((~ok[recenti.values]).sum())
|
||||
g_rec = max(1, int(giorni[recenti].nunique()))
|
||||
return dict(n=n, ricostruibili=int(ok.sum()), frac=float(ok.sum() / n) if n else float("nan"),
|
||||
assenti_nel_feed=int((~np.asarray(presenti)).sum()),
|
||||
div_per_giorno=float((n - ok.sum()) / span_g),
|
||||
div_recenti=div_rec, div_per_giorno_recenti=float(div_rec / g_rec),
|
||||
divergenti_ts=[int(t) for t in r["ts"].values[~ok]][:20])
|
||||
|
||||
|
||||
def veto_check(frac: float, div_g_tot: float, div_g_rec: float, div_rec: int) -> tuple[bool, str]:
|
||||
"""PURA. Veto d'integrita' (blocca, non decide): sotto l'80% di barre ricostruibili, o con le
|
||||
divergenze che CRESCONO negli ultimi 30 giorni, la finestra si ESTENDE invece di decidere."""
|
||||
motivi = []
|
||||
if not np.isfinite(frac) or frac < MIN_RECON_FRAC:
|
||||
motivi.append(f"barre ricostruibili {frac:.1%} < {MIN_RECON_FRAC:.0%}")
|
||||
if div_rec >= MIN_DIV_CRESCITA and div_g_rec > 2.0 * div_g_tot + 1e-12:
|
||||
motivi.append(f"divergenze in crescita: {div_rec} negli ultimi {RECENTI_GIORNI}g "
|
||||
f"({div_g_rec:.2f}/g contro {div_g_tot:.2f}/g sull'intera finestra)")
|
||||
return (not motivi), ("; ".join(motivi) if motivi else
|
||||
f"ricostruibili {frac:.1%}, divergenti {div_rec} negli ultimi {RECENTI_GIORNI}g "
|
||||
f"({div_g_rec:.2f}/g contro {div_g_tot:.2f}/g totale)")
|
||||
|
||||
|
||||
def print_gate(st: dict, dfs: dict, today: pd.Timestamp | None = None) -> dict:
|
||||
"""Stampa lo stato del gate pre-registrato e lo RITORNA (per i test e per chi legge il log).
|
||||
Non decide niente: al kill e alla data stampa cosa la regola dice, in maiuscolo."""
|
||||
r = _returns_df()
|
||||
out = dict(gate_date=GATE_DATE, kill="NON_MISURABILE", veto_ok=None)
|
||||
print(f"\n GATE PRE-REGISTRATO `PREVDAY-01` (23/08, RESULTS-0822 §54) — decisione {GATE_DATE}")
|
||||
if r is None or len(r) < 2:
|
||||
print(" serie forward assente o troppo corta: niente da misurare")
|
||||
return out
|
||||
today = today or pd.Timestamp.now(tz="UTC").normalize()
|
||||
sh, n_g = sharpe_giornaliero(r)
|
||||
n_att = giorni_attivi(r)
|
||||
stato, motivo = kill_check(sh, n_att, today)
|
||||
out.update(sharpe_giornaliero=sh, n_giorni=n_g, n_attivi=n_att, kill=stato, kill_motivo=motivo)
|
||||
print(f" Sharpe forward GIORNALIERO (MODELED): {sh:+.2f} su {n_g} giorni, {n_att} attivi"
|
||||
f" [lente oraria {_sharpe_orario(r):+.2f}: non si cita, §54]")
|
||||
if stato == "KILL":
|
||||
print(f" *** KILL: {motivo} ***")
|
||||
else:
|
||||
print(f" kill (Sharpe < {KILL_SHARPE:+.2f} su >= {KILL_MIN_ACTIVE_DAYS}g attivi): {stato} — {motivo}")
|
||||
rc = ricostruzione(r, dfs, int(st["start_ts"]))
|
||||
ok, vm = veto_check(rc["frac"], rc["div_per_giorno"], rc["div_per_giorno_recenti"], rc["div_recenti"])
|
||||
out.update(ricostruzione=rc, veto_ok=ok, veto_motivo=vm)
|
||||
print(f" veto d'integrita': {'ok' if ok else '*** VETO — la finestra si ESTENDE ***'} — {vm}"
|
||||
f" ({rc['ricostruibili']}/{rc['n']} barre, {rc['assenti_nel_feed']} assenti nel feed)")
|
||||
if today >= pd.Timestamp(GATE_DATE, tz="UTC"):
|
||||
print(f" *** DECISIONE DOVUTA ({GATE_DATE}): (a) DSR >= 0,95 sulla famiglia dichiarata "
|
||||
f"(b) cella al buio == congelata (c) delta libro > +0,05 col funding (d) weights_tilt_null "
|
||||
f"(e) ADDS+robust_oos+non-hedge sulla cella al buio (f) day_boundary_robust "
|
||||
f"(g) Sharpe forward > 0 [{sh:+.2f}] — TUTTE necessarie ***")
|
||||
else:
|
||||
print(f" decisione fra {(pd.Timestamp(GATE_DATE, tz='UTC') - today).days} giorni; "
|
||||
f"le condizioni (a)-(b) sono STRUTTURALI e il forward non le cambia (§54)")
|
||||
return out
|
||||
|
||||
|
||||
def _sharpe_orario(r: pd.DataFrame) -> float:
|
||||
x = r["net_modeled"].astype(float)
|
||||
return float(x.mean() / x.std() * np.sqrt(24 * 365.25)) if x.std() > 0 else float("nan")
|
||||
|
||||
|
||||
def print_status(st: dict, dfs: dict):
|
||||
days = (max(int(dfs[a]["timestamp"].iloc[-1]) for a in ASSETS) - st["start_ts"]) / 86400_000
|
||||
rm = st["cap_modeled"] / MODELED_CAPITAL - 1
|
||||
@@ -156,7 +316,9 @@ def print_status(st: dict, dfs: dict):
|
||||
print(f" MODELED ($2000 nominale): {rm*100:+6.2f}% eq ${st['cap_modeled']:.2f} maxDD {st['dd_modeled']*100:.1f}%")
|
||||
print(f" REAL-$600 (min-order $5) : {rr*100:+6.2f}% eq ${st['cap_real']:.2f} maxDD {st['dd_real']*100:.1f}%")
|
||||
print(f" -> fill-haircut MODELED-REAL: {(rm-rr)*100:+.2f} pp (lo scettico l'aveva segnalato)")
|
||||
print(f" log: {RETURNS_FILE}\n")
|
||||
print(f" log: {RETURNS_FILE}")
|
||||
print_gate(st, dfs)
|
||||
print()
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -224,5 +224,15 @@ def main() -> None:
|
||||
print("\n fatto. I file *.%s.* sono l'evidenza del difetto e restano nel backup." % SUFFISSO)
|
||||
|
||||
|
||||
USO = """uso: paper_regen.py [--solo V]
|
||||
|
||||
paper_regen.py — RIGENERA le serie dei forward-monitor rotti da `advance()` (§5.1).
|
||||
uv run python scripts/live/paper_regen.py # tutti e 4
|
||||
uv run python scripts/live/paper_regen.py --solo paper_statarb
|
||||
|
||||
Dettaglio nel docstring in testa al file."""
|
||||
|
||||
if __name__ == "__main__":
|
||||
from src.live.cli import valida
|
||||
valida("paper_regen.py", USO, flag=(), con_valore=("--solo",))
|
||||
main()
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
"""Revisione settimanale in SOLA LETTURA: un modello DIVERSO dall'autore rilegge il sistema.
|
||||
|
||||
uv run python scripts/live/revisione.py # chiama il revisore, scrive docs/revisioni/<data>.md, Telegram
|
||||
uv run python scripts/live/revisione.py --secco # solo la taglia del materiale: niente modello, niente file
|
||||
uv run python scripts/live/revisione.py --quiet # per il cron (scripts/cron_review.sh, lunedi' 06:15 UTC)
|
||||
|
||||
Cosa scrive: SOLO il rapporto. Cosa non tocca: config, libro, cron, codice, dati. Le proposte
|
||||
restano proposte; decide l'operatore. Dettagli e ragioni in `src/live/revisione.py`.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
USO = """uso: revisione.py [--giorno AAAA-MM-GG] [--modello NOME] [--secco] [--no-telegram] [--quiet]
|
||||
--giorno G data della revisione (default: adesso, UTC)
|
||||
--modello M revisore (default: src.live.revisione.MODELLO = claude-fable-5-1 — NON il modello che scrive il codice)
|
||||
--secco stampa la taglia del materiale e si ferma: niente modello, niente file, niente Telegram
|
||||
--no-telegram scrive il rapporto ma non manda la sintesi
|
||||
--quiet stampa solo l'esito
|
||||
⚠️ SPENDE una chiamata al modello e MANDA un messaggio Telegram, salvo --secco/--no-telegram.
|
||||
Scrive un solo file: docs/revisioni/<data>.md."""
|
||||
|
||||
|
||||
def _arg(flag: str, default=None):
|
||||
a = sys.argv[1:]
|
||||
return a[a.index(flag) + 1] if flag in a and a.index(flag) + 1 < len(a) else default
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from src.live.cli import valida
|
||||
valida("revisione.py", USO, flag=("--secco", "--no-telegram", "--quiet"),
|
||||
con_valore=("--giorno", "--modello"))
|
||||
from src.live import revisione as R
|
||||
g = _arg("--giorno")
|
||||
adesso = (datetime.fromisoformat(g).replace(tzinfo=timezone.utc) if g
|
||||
else datetime.now(timezone.utc))
|
||||
esito = R.scrivi(ROOT, adesso, modello=_arg("--modello", R.MODELLO),
|
||||
secco="--secco" in sys.argv, telegram="--no-telegram" not in sys.argv,
|
||||
quiet="--quiet" in sys.argv)
|
||||
if "--quiet" in sys.argv and not esito.get("secco"):
|
||||
print(f"revisione {esito['data']}: {esito.get('stato')}"
|
||||
+ (f" — {esito.get('errore')}" if esito.get("errore") else "") + f" {esito.get('file')}")
|
||||
raise SystemExit(0 if esito.get("secco") or esito.get("stato") in ("ok", "sospetta") else 1)
|
||||
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Sorveglianza giornaliera della CHIAVE DI SCALA del libro (SPEC-scale-key §6.4).
|
||||
|
||||
uv run python scripts/live/scale_watch.py # osserva e stampa, invia se ALLARME
|
||||
uv run python scripts/live/scale_watch.py --secco # osserva e stampa, NON invia nulla
|
||||
uv run python scripts/live/scale_watch.py --quiet # una riga sola (per il cron)
|
||||
|
||||
Tre domande, tre azioni diverse, tre stati (OK / ALLARME / NON MISURABILE).
|
||||
Non impedisce la modifica: la rende visibile entro 24 ore e attribuibile.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from src.live.giri import registra_giro # noqa: E402
|
||||
from src.live.notifier import notify # noqa: E402
|
||||
from src.live.scale_watch import ALLARME, OK, run_once # noqa: E402
|
||||
|
||||
RUNS = ROOT / "data" / "scale_watch" / "runs.jsonl"
|
||||
|
||||
|
||||
def _sender(testo: str):
|
||||
try:
|
||||
notify(testo)
|
||||
return True, "telegram"
|
||||
except Exception as e: # P3: si registra dove lo si ingoia
|
||||
return False, f"{type(e).__name__}: {e}"
|
||||
|
||||
|
||||
def main() -> None:
|
||||
secco = "--secco" in sys.argv
|
||||
quiet = "--quiet" in sys.argv
|
||||
rep = run_once(sender=None if secco else _sender)
|
||||
registra_giro(RUNS, stato=rep["stato"])
|
||||
icona = {OK: "✅", ALLARME: "🚨"}.get(rep["stato"], "⚠️")
|
||||
if quiet:
|
||||
fb = rep["fallback"]
|
||||
q = f"{100*fb['quota']:.2f}%" if "quota" in fb else "n/d"
|
||||
print(f" scale_watch: {icona} {rep['stato']} · scala {rep['scala_dichiarata']:.2f}x · "
|
||||
f"fallback {q} · {rep['invio']}")
|
||||
return
|
||||
print("=" * 88)
|
||||
print(f" SCALE WATCH — la chiave di scala del libro {icona} {rep['stato']}")
|
||||
print("=" * 88)
|
||||
print(f" scala dichiarata : {rep['scala_dichiarata']:.2f}x "
|
||||
f"(scaletta {rep['ladder']}, tetto {rep['tetto']:.2f}x — costanti di CODICE)")
|
||||
for nome, d in rep["domande"].items():
|
||||
print(f"\n {nome} [{d['stato']}] {d['motivo']}")
|
||||
if d.get("azione") and d["azione"] != "nessuna":
|
||||
print(f" → azione: {d['azione']}")
|
||||
fb = rep["fallback"]
|
||||
if fb.get("stato") == "NON MISURABILE":
|
||||
print(f"\n fallback [{fb['stato']}] {fb.get('motivo')}")
|
||||
else:
|
||||
print(f"\n fallback [{fb['stato']}] equity illeggibile in {fb['fallback_veri']}/{fb['giri']} "
|
||||
f"giri ({100*fb['quota']:.2f}%, soglia {100*fb['soglia']:.0f}%) — "
|
||||
f"{fb['paper']} giri pre-finanziamento esclusi, li' il libro non invia affatto")
|
||||
print(f"\n lettore di produzione [{rep['lettore']['stato']}] {rep['lettore']['motivo'][:150]}")
|
||||
print(f"\n invio: {rep['invio']}")
|
||||
|
||||
|
||||
USO = """uso: scale_watch.py [--quiet] [--secco]
|
||||
|
||||
uv run python scripts/live/scale_watch.py # osserva e stampa, invia se ALLARME
|
||||
uv run python scripts/live/scale_watch.py --secco # osserva e stampa, NON invia nulla
|
||||
uv run python scripts/live/scale_watch.py --quiet # una riga sola (per il cron)
|
||||
|
||||
Dettaglio nel docstring in testa al file."""
|
||||
|
||||
if __name__ == "__main__":
|
||||
from src.live.cli import valida
|
||||
valida("scale_watch.py", USO, flag=("--quiet", "--secco"), con_valore=())
|
||||
main()
|
||||
@@ -25,17 +25,63 @@ import pandas as pd
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from src.live.monitor_health import MONITORS
|
||||
from src.live.notifier import send
|
||||
|
||||
HORIZONS = (30, 90, 180)
|
||||
GATES = [("STATARB-RESID", date(2026, 9, 27), "data/paper_statarb/returns.jsonl", "net_modeled"),
|
||||
("XSR01", date(2026, 10, 23), "data/paper_xsr/returns.jsonl", "net_modeled")]
|
||||
|
||||
|
||||
def _gate_date(nome_monitor: str, cablata: date) -> date:
|
||||
"""(#35 C14) la data del gate viene da monitor_health.MONITORS (`MonitorSpec.gate`), la
|
||||
STESSA tabella che "METTE A RISCHIO il gate" negli alert dei sorveglianti: una fonte, non
|
||||
una seconda dichiarazione duplicata di CLAUDE.md §4. Il campo e' prosa libera
|
||||
("YYYY-MM-DD — descrizione"): si legge la data solo se e' nella forma attesa. `cablata' e'
|
||||
rete di sicurezza se il monitor sparisse o il campo cambiasse forma — non deve succedere per
|
||||
i due gate qui sotto, oggi allineati (verificato a mano contro CLAUDE.md §4)."""
|
||||
for m in MONITORS:
|
||||
if m.name == nome_monitor and m.gate:
|
||||
try:
|
||||
return date.fromisoformat(m.gate[:10])
|
||||
except ValueError:
|
||||
break
|
||||
return cablata
|
||||
|
||||
|
||||
GATES = [("STATARB-RESID", _gate_date("paper_statarb", date(2026, 9, 27)),
|
||||
"data/paper_statarb/returns.jsonl", "net_modeled"),
|
||||
("XSR01", _gate_date("paper_xsr", date(2026, 10, 23)),
|
||||
"data/paper_xsr/returns.jsonl", "net_modeled")]
|
||||
|
||||
|
||||
def _fmt_usd(x: float) -> str:
|
||||
return f"${x:,.2f}"
|
||||
|
||||
|
||||
def _soglia_accensione(segni: list[int], neg_refs: list[float]) -> float | None:
|
||||
"""(#35 C3) Quale riferimento, superato, accende TP01 (mean(segni) > 0)?
|
||||
|
||||
I segni sono sempre in {-1,+1}, mai 0 (trend_state li costruisce con `1 if px>refs[h]
|
||||
else -1`: nessun pareggio possibile). Con k segni negativi da far diventare positivi, la
|
||||
somma cresce di 2 per flip: serve il piu' piccolo k tale che sum(segni)+2k > 0. La soglia
|
||||
e' il k-esimo riferimento negativo piu' basso (k=1 -> il minimo, come prima del fix; k=2 ->
|
||||
il secondo, ecc.) — non sempre il minimo: con TUTTI i riferimenti negativi, superare solo il
|
||||
piu' basso lascia la media <= 0 e TP01 a 0 (bug segnalato in #35 C3).
|
||||
|
||||
None se la media e' gia' positiva (nessuna soglia da dare) o se k supera il numero di
|
||||
riferimenti negativi disponibili (non dovrebbe accadere: con n segni, flippare tutti gli n
|
||||
negativi porta comunque la somma a +n > 0, quindi k <= len(neg_refs) sempre — resta una
|
||||
guardia difensiva, non un caso noto)."""
|
||||
s = sum(segni)
|
||||
if s > 0 or not neg_refs:
|
||||
return None
|
||||
k = 0
|
||||
while s + 2 * k <= 0:
|
||||
k += 1
|
||||
if k > len(neg_refs):
|
||||
return None
|
||||
return sorted(neg_refs)[k - 1]
|
||||
|
||||
|
||||
def trend_state() -> list[str]:
|
||||
"""Stato TSMOM per asset + quanto manca all'accensione (2 orizzonti su 3 positivi).
|
||||
|
||||
@@ -59,32 +105,51 @@ def trend_state() -> list[str]:
|
||||
tgt = max(0.0, m)
|
||||
line = f" {a} {px:,.0f} · segni " + "/".join(f"{sg[h]:+d}" for h in HORIZONS) + \
|
||||
f" → TP01 {tgt:.2f}"
|
||||
neg = [(h, refs[h]) for h in HORIZONS if sg[h] < 0]
|
||||
if neg and tgt == 0.0:
|
||||
h, r = min(neg, key=lambda t: t[1])
|
||||
line += f"\n accensione a {r:,.0f} ({(r/px-1)*100:+.1f}%)"
|
||||
if tgt == 0.0:
|
||||
r = _soglia_accensione(list(sg.values()), [refs[h] for h in HORIZONS if sg[h] < 0])
|
||||
if r is not None:
|
||||
line += f"\n accensione a {r:,.0f} ({(r/px-1)*100:+.1f}%)"
|
||||
out.append(line)
|
||||
except Exception as e:
|
||||
out.append(f" {a}: stato trend non calcolabile ({type(e).__name__})")
|
||||
return out
|
||||
|
||||
|
||||
def book_state() -> list[str]:
|
||||
"""Conto reale, posizioni e target netto correnti. Sola lettura."""
|
||||
def book_state() -> tuple[list[str], bool | None]:
|
||||
"""Conto reale, posizioni e target netto correnti. Sola lettura.
|
||||
|
||||
(#35 C14) Ritorna anche se il libro e' DAVVERO flat (nessuna posizione, nessun target: stessa
|
||||
condizione che questa funzione gia' usa riga per riga per stampare "flat" invece di "pos ->
|
||||
target"): serve a `build()` per decidere se l'header "Perche' non opera" e' vero o
|
||||
fuorviante, SENZA una lettura nuova — None se lo stato del libro non e' leggibile (in quel
|
||||
caso non sappiamo dire se opera)."""
|
||||
try:
|
||||
from src.live.book import book_report
|
||||
r = book_report()
|
||||
eq = r.get("equity")
|
||||
lines = [f" equity {_fmt_usd(eq)}" if eq else " equity non leggibile"]
|
||||
flat = True
|
||||
for a in r.get("assets", []):
|
||||
pos, net = a.get("position_usd", 0.0), a.get("net_target", 0.0)
|
||||
stato = "flat" if abs(pos) < 1 and abs(net) < 1 else f"pos {_fmt_usd(pos)} → target {_fmt_usd(net)}"
|
||||
is_flat = abs(pos) < 1 and abs(net) < 1
|
||||
flat = flat and is_flat
|
||||
stato = "flat" if is_flat else f"pos {_fmt_usd(pos)} → target {_fmt_usd(net)}"
|
||||
lines.append(f" {a['asset']}: {stato} (TP {a.get('tp_frac', 0):+.2f} · SKH {a.get('skh_sign', 0):+d})")
|
||||
if r.get("skh_error"):
|
||||
lines.append(f" ⚠️ SKH feed KO: {r['skh_error']}")
|
||||
return lines
|
||||
return lines, flat
|
||||
except Exception as e:
|
||||
return [f" ⚠️ stato libro non leggibile: {type(e).__name__}: {e}"]
|
||||
return [f" ⚠️ stato libro non leggibile: {type(e).__name__}: {e}"], None
|
||||
|
||||
|
||||
def _fmt_giorni(giorni: int) -> str:
|
||||
"""(#35 C14) tre casi distinti, non un segno "−" cablato davanti a un intero che puo' essere
|
||||
negativo: con giorni=-1 quel cablaggio stampava "(−-1g)", un doppio segno illeggibile."""
|
||||
if giorni > 0:
|
||||
return f"-{giorni}g"
|
||||
if giorni == 0:
|
||||
return "oggi"
|
||||
return f"scaduto da {-giorni}g"
|
||||
|
||||
|
||||
def monitors() -> list[str]:
|
||||
@@ -92,20 +157,21 @@ def monitors() -> list[str]:
|
||||
for name, dec, path, key in GATES:
|
||||
p = ROOT / path
|
||||
giorni = (dec - date.today()).days
|
||||
gate_txt = f"gate {dec} ({_fmt_giorni(giorni)})"
|
||||
if not p.exists():
|
||||
out.append(f" {name}: in attesa della prima barra · gate {dec} (−{giorni}g)")
|
||||
out.append(f" {name}: in attesa della prima barra · {gate_txt}")
|
||||
continue
|
||||
try:
|
||||
rows = [json.loads(x) for x in p.read_text().splitlines() if x.strip()]
|
||||
if not rows:
|
||||
out.append(f" {name}: 0 barre · gate {dec} (−{giorni}g)")
|
||||
out.append(f" {name}: 0 barre · {gate_txt}")
|
||||
continue
|
||||
r = np.array([x[key] for x in rows], float)
|
||||
sh = float(r.mean() / r.std() * np.sqrt(365)) if r.std() > 0 else 0.0
|
||||
tot = float(np.prod(1 + r) - 1) * 100
|
||||
out.append(f" {name}: {len(rows)}g · {tot:+.2f}% · Sh {sh:+.2f} · gate {dec} (−{giorni}g)")
|
||||
out.append(f" {name}: {len(rows)}g · {tot:+.2f}% · Sh {sh:+.2f} · {gate_txt}")
|
||||
except Exception:
|
||||
out.append(f" {name}: log illeggibile · gate {dec} (−{giorni}g)")
|
||||
out.append(f" {name}: log illeggibile · {gate_txt}")
|
||||
return out
|
||||
|
||||
|
||||
@@ -129,8 +195,13 @@ def last_trade() -> str:
|
||||
def build() -> str:
|
||||
oggi = datetime.now(timezone.utc).strftime("%d/%m %H:%M UTC")
|
||||
L = [f"📊 <b>Book Deribit — {oggi}</b>", "", "<b>Conto</b>"]
|
||||
L += book_state()
|
||||
L += ["", "<b>Perche' non opera</b>"]
|
||||
righe_conto, flat = book_state()
|
||||
L += righe_conto
|
||||
# (#35 C14) "Perche' non opera" e' vero solo se il libro e' DAVVERO flat (nessuna posizione,
|
||||
# nessun target: gia' letto sopra da book_state, nessuna lettura nuova). Se non e' flat (ha
|
||||
# posizioni o un target diverso da zero) o non si sa (book non leggibile), l'header fisso
|
||||
# mentirebbe: si usa un titolo neutro.
|
||||
L += ["", "<b>Perche' non opera</b>" if flat else "<b>Stato trend</b>"]
|
||||
L += trend_state()
|
||||
L += ["", "<b>Ordini</b>", last_trade()]
|
||||
L += ["", "<b>Monitor in osservazione</b>"]
|
||||
@@ -146,5 +217,14 @@ def main() -> None:
|
||||
print("inviato" if send(txt) else "NON inviato (config Telegram assente o rete KO)")
|
||||
|
||||
|
||||
USO = """uso: telegram_daily.py [--dry-run]
|
||||
|
||||
uv run python scripts/live/telegram_daily.py # calcola e invia
|
||||
uv run python scripts/live/telegram_daily.py --dry-run # stampa e basta, non invia
|
||||
|
||||
Dettaglio nel docstring in testa al file."""
|
||||
|
||||
if __name__ == "__main__":
|
||||
from src.live.cli import valida
|
||||
valida("telegram_daily.py", USO, flag=("--dry-run",), con_valore=())
|
||||
main()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Sincronizza `data/live/trades.db` — il libro di bordo dei trade, allineato col tempo.
|
||||
|
||||
uv run python scripts/live/trades_db.py --sync # idempotente, da cron
|
||||
uv run python scripts/live/trades_db.py --report # stato + P&L a video
|
||||
uv run python scripts/live/trades_db.py --report # stato + P&L a video (TWR, non e1/e0)
|
||||
uv run python scripts/live/trades_db.py --reconcile # incrocio delle tre fonti
|
||||
|
||||
SOLA LETTURA sul venue e sui log: non manda ordini, non tocca il libro.
|
||||
@@ -17,6 +17,7 @@ ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from src.live import tradesdb as T # noqa: E402
|
||||
from src.live.notifier import notify # noqa: E402
|
||||
|
||||
|
||||
def sync(verbose: bool = True) -> dict:
|
||||
@@ -25,16 +26,51 @@ def sync(verbose: bool = True) -> dict:
|
||||
fills = [f for r in runs for f in r.fills]
|
||||
n_f = T.upsert_fills(con, fills)
|
||||
n_e = T.upsert_equity(con, [(r.ts_utc, r.equity, "cron_book") for r in runs if r.equity is not None])
|
||||
|
||||
# #40 (TradingBook#11): arricchimento PRIMA dei round-trip, cosi' il FIFO vede gia' i prezzi
|
||||
# pieni e le ore vere delle righe abbinate al jsonl, non quelle (arrotondate, di inizio giro)
|
||||
# di cron_book.log. `righe_jsonl` si legge una volta sola e serve anche al controllo di
|
||||
# conteggio (#37 C11) qui sotto.
|
||||
righe_jsonl = T.parse_executions_jsonl(T.EXEC_JSONL.read_text()) if T.EXEC_JSONL.exists() else []
|
||||
n_arricchiti, n_ambigui = T.arricchisci_da_jsonl(con, righe_jsonl)
|
||||
|
||||
n_r = T.rebuild_roundtrips(con)
|
||||
if runs:
|
||||
T.set_meta(con, "ultimo_giro", runs[-1].ts_utc)
|
||||
T.set_meta(con, "ultimo_sync", T.ora())
|
||||
tot = con.execute("SELECT COUNT(*) c FROM fills").fetchone()["c"]
|
||||
tot_e = con.execute("SELECT COUNT(*) c FROM equity").fetchone()["c"]
|
||||
|
||||
# issue #37 (C11). Dal 2026-08-24T01:07Z (T.CUTOFF_TS_VERO) book_executions.jsonl porta l'ora
|
||||
# VERA per-fill (docstring di tradesdb.py). Un conteggio diverso in quella finestra segnala
|
||||
# che una delle due fonti sta perdendo fill — questo controllo resta indipendente
|
||||
# dall'arricchimento sopra (che abbina riga per riga, non conta): un conteggio uguale non
|
||||
# esclude un abbinamento ambiguo, e viceversa. Notify UNA volta per episodio: marcatore in
|
||||
# `meta` (stessa regola di fee_watch #37 B9 — scritto solo se l'invio riesce, azzerato
|
||||
# quando i conteggi tornano uguali).
|
||||
n_jsonl_dal = sum(1 for r in righe_jsonl if str(r.get("ts_utc", "")) >= T.CUTOFF_TS_VERO)
|
||||
n_db_dal = con.execute("SELECT COUNT(*) c FROM fills WHERE ts_utc >= ?",
|
||||
(T.CUTOFF_TS_VERO,)).fetchone()["c"]
|
||||
diverge = n_jsonl_dal != n_db_dal
|
||||
gia_detto = T.get_meta(con, "conteggio_diverso_detto") == "1"
|
||||
if diverge:
|
||||
msg = f"⚠️ trades.db: {n_jsonl_dal} fill nel jsonl, {n_db_dal} nel DB dal 24/08"
|
||||
if verbose:
|
||||
print(f" {msg}")
|
||||
if not gia_detto:
|
||||
if notify("⚠️ trades.db — conteggio fill diverso dal jsonl dal 24/08",
|
||||
{"fill nel jsonl": n_jsonl_dal, "fill nel DB": n_db_dal}):
|
||||
T.set_meta(con, "conteggio_diverso_detto", "1")
|
||||
elif gia_detto:
|
||||
T.set_meta(con, "conteggio_diverso_detto", "0")
|
||||
|
||||
if verbose:
|
||||
print(f" fills: +{n_f} (totale {tot}) | equity: +{n_e} (totale {tot_e}) | round-trip {n_r}")
|
||||
print(f" arricchiti dal jsonl: {n_arricchiti} | ambigui: {n_ambigui}")
|
||||
con.close()
|
||||
return dict(nuovi_fill=n_f, nuovi_equity=n_e, roundtrip=n_r, totale_fill=tot)
|
||||
return dict(nuovi_fill=n_f, nuovi_equity=n_e, roundtrip=n_r, totale_fill=tot,
|
||||
jsonl_dal_24_08=n_jsonl_dal, db_dal_24_08=n_db_dal,
|
||||
arricchiti=n_arricchiti, ambigui=n_ambigui)
|
||||
|
||||
|
||||
def reconcile() -> None:
|
||||
@@ -42,8 +78,8 @@ def reconcile() -> None:
|
||||
fills = [f for r in runs for f in r.fills]
|
||||
righe = T.parse_executions_jsonl(T.EXEC_JSONL.read_text()) if T.EXEC_JSONL.exists() else []
|
||||
rec = T.reconcile(fills, righe)
|
||||
print(f"\n cron_book.log : {len(fills)} fill (con ORA VERA)")
|
||||
print(f" book_executions : {len(righe)} righe (senza ora)")
|
||||
print(f"\n cron_book.log : {len(fills)} fill (ora di inizio giro)")
|
||||
print(f" book_executions : {len(righe)} righe (ora VERA per-fill dal 24/08, #37 C11)")
|
||||
print(f" concordano : {len(rec['ok'])}")
|
||||
print(f" solo nel log : {len(rec['solo_log'])}")
|
||||
print(f" solo nel jsonl : {len(rec['solo_jsonl'])}")
|
||||
@@ -78,10 +114,41 @@ def report() -> None:
|
||||
print(f"\n fill registrati : {n} dal {p['a'][:16]} al {p['b'][:16]}")
|
||||
eq = con.execute("SELECT ts_utc, equity FROM equity ORDER BY ts_utc").fetchall()
|
||||
if eq:
|
||||
# Il rendimento NON e' `e1/e0-1` sulla serie grezza: quel numero era per il 96,3% un
|
||||
# bonifico (debito #14, 2026-09-01). Lo scorporo e' quello del giornale — si CHIAMA,
|
||||
# non si rifa' (P1): `rendimento_twr` spezza la serie sui movimenti di capitale certi.
|
||||
from src.live.journal import rendimento_twr
|
||||
e0, e1 = eq[0]["equity"], eq[-1]["equity"]
|
||||
picco = max(r["equity"] for r in eq)
|
||||
print(f" equity : ${e0:,.2f} -> ${e1:,.2f} ({e1 - e0:+.2f}, {100*(e1/e0-1):+.2f}%)"
|
||||
f" | picco ${picco:,.2f} | {len(eq)} letture")
|
||||
r = rendimento_twr(con, eq[-1]["ts_utc"])
|
||||
print(f" equity : ${e0:,.2f} -> ${e1:,.2f} ({e1 - e0:+,.2f} di equity, "
|
||||
f"movimenti di capitale INCLUSI) | picco ${picco:,.2f} | {len(eq)} letture")
|
||||
if r["eventi"]:
|
||||
print(f" movimenti capitale : {r['certi']:+,.2f} certi | {r['ambigui']:+,.2f} ambiguo/i"
|
||||
f" (restano nel P&L, dichiarati)")
|
||||
for ev in r["eventi"]:
|
||||
mk = ev.get("mercato_max_pct")
|
||||
if ev.get("dichiarazione"):
|
||||
dettaglio = ev["dichiarazione"]
|
||||
elif mk is None:
|
||||
dettaglio = ev.get("mercato_nota") or "mercato non misurabile"
|
||||
else:
|
||||
dettaglio = f"mercato max {100 * mk:.2f}% a leva piena, salto {100 * ev['pct']:+.1f}%"
|
||||
print(f" {ev['ts_dopo'][:16]} {ev['delta']:+,.2f} [{ev['classe']}: {dettaglio}]")
|
||||
for av in r.get("avvisi") or []:
|
||||
print(f" ⚠️ {av}")
|
||||
if r["trading"] is None:
|
||||
print(f" trading da arming : n/d ({r['motivo']})")
|
||||
else:
|
||||
print(f" trading da arming : {r['trading']:+,.2f} (equity al netto dei movimenti "
|
||||
f"certi; l'ora di un movimento RILEVATO esce intera, quella di un DICHIARATO "
|
||||
f"resta — vedi journal.rendimento_twr)")
|
||||
if r["twr"] is None:
|
||||
print(f" TWR : n/d ({r['motivo']})")
|
||||
else:
|
||||
segs = " x ".join(f"{100*s['ret']:+.2f}% [{s['da'][:10]} -> {s['a'][:10]}]"
|
||||
for s in r["segmenti"])
|
||||
print(f" TWR : {100*r['twr']:+.2f}% = {segs}")
|
||||
rt = con.execute("SELECT * FROM roundtrips ORDER BY ts_out").fetchall()
|
||||
lordo = sum(r["pnl_lordo"] for r in rt)
|
||||
fee_rt = sum(r["fee_quota"] for r in rt)
|
||||
@@ -94,15 +161,25 @@ def report() -> None:
|
||||
if ap:
|
||||
print(" posizioni aperte :")
|
||||
for a, d in sorted(ap.items()):
|
||||
print(f" {a} {d['qty']:.4f} @ medio ${d['prezzo_medio']:,.2f} (dal {d['dal'][:16]})")
|
||||
marca = "" if d.get("direzione", "long") == "long" else " SHORT" # #32 C2
|
||||
print(f" {a}{marca} {d['qty']:.4f} @ medio ${d['prezzo_medio']:,.2f} "
|
||||
f"(dal {d['dal'][:16]})")
|
||||
else:
|
||||
print(" posizioni aperte : nessuna (flat)")
|
||||
print(f" ultimo sync : {T.get_meta(con, 'ultimo_sync', 'mai')}")
|
||||
con.close()
|
||||
|
||||
|
||||
USO = """uso: trades_db.py [--sync] [--report] [--reconcile] [--quiet]
|
||||
|
||||
--sync (default) legge log e jsonl e aggiorna data/live/trades.db — idempotente
|
||||
--report stato del libro di bordo + P&L (TWR, non e1/e0)
|
||||
--reconcile incrocio delle tre fonti sui fill (log x jsonl x venue)
|
||||
--quiet con --sync: non stampa il riepilogo"""
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = sys.argv[1:]
|
||||
from src.live.cli import valida
|
||||
args = valida("trades_db.py", USO, flag=("--sync", "--report", "--reconcile", "--quiet"))
|
||||
if "--reconcile" in args:
|
||||
reconcile()
|
||||
elif "--report" in args:
|
||||
|
||||
@@ -0,0 +1,379 @@
|
||||
#!/usr/bin/env python
|
||||
"""usde_convert.py — porta la QUOTA di collaterale USDE a un bersaglio dichiarato. INVIA ORDINI VERI.
|
||||
|
||||
PERCHE' ESISTE. La conversione del 26/08 (test di eligibilita', 500 USDE) fu una chiamata a mano al
|
||||
gateway: nessuno script, nessuna guardia, nessuna traccia se non il diario. Il gate USDE-01 prevede
|
||||
entrambe le direzioni — IDONEO -> si decide la quota; NON IDONEO -> "si riconverte" — e nessuna delle
|
||||
due aveva un attrezzo. Qui c'e', e vale in tutti e due i versi (quota piu' alta = compra USDE, piu'
|
||||
bassa = vende).
|
||||
|
||||
PERCHE' NON PASSA DA DeribitTrader. `execution.ALLOWED` ammette solo i due perp del libro: e' il
|
||||
guardrail anti-fat-finger del percorso soldi del book e NON va allargato per far entrare uno spot che
|
||||
col libro non c'entra. Questo script parla al gateway per conto suo e porta le proprie guardie.
|
||||
|
||||
LE GUARDIE (tutte BLOCCANTI, tutte dichiarate qui):
|
||||
* banda di prezzo [0.995, 1.005] — la stessa del 26/08. Fuori banda non si converte: un USDE a
|
||||
0.98 e' esattamente il momento in cui NON si compra, e uno a 1.02 non esiste (si clampa a 1.0).
|
||||
* quota bersaglio in [0, QUOTA_HARD_MAX] — il gate USDE-01 dice "mai 100%: R1 emittente non
|
||||
recuperabile". Il tetto e' HARD e non si passa da riga di comando.
|
||||
* TETTO DEL VENUE (`usde.venue_cap_frac`). Deribit rifiuta gli acquisti oltre una frazione
|
||||
dell'equity totale — ~31,2%, MISURATA il 30-31/08 e non documentata da nessuna parte, col
|
||||
messaggio fuorviante `not_enough_funds_in_currency`. E' una frazione, non un livello: fatta
|
||||
scendere l'equity di $8, il tetto e' sceso con lei. Senza questa guardia il piano si dichiara
|
||||
VALIDO e poi il venue lo rifiuta a raffica — che e' esattamente il giro di venti minuti costato
|
||||
il 30/08. Il valore vive in config, non qui (P1: il bersaglio si deriva, non si ridichiara).
|
||||
* CUSCINO DI REGOLAMENTO. Il P&L e il funding dei perp USDC-lineari si regolano in USDC, non nel
|
||||
collaterale: a quota alta il saldo USDC va negativo alla prima perdita del libro e Deribit lo
|
||||
finanzia a interesse. Il margine NON e' il vincolo (r0830_usde_quota: l'haircut non morde fino
|
||||
al 90%) — il vincolo e' questo. Il cuscino richiesto e' il disaster-SL sulla MASSIMA esposizione
|
||||
che il libro puo' prendere: n_asset * frac * disaster_sl_pct * equity, tutto letto da
|
||||
config/live.json (P1: il bersaglio si deriva, non si ridichiara).
|
||||
* dry-run DI DEFAULT: senza --esegui stampa il piano e non manda niente.
|
||||
|
||||
IL TETTO PER-ORDINE (misurato il 30/08, non documentato da nessuna parte). Il venue rifiuta gli
|
||||
ordini spot grandi con `not_enough_funds_in_currency` ANCHE quando i fondi ci sono: 100 accettato
|
||||
e 300 rifiutato con $1.442 disponibili (e 500 era passato il 26/08 con piu' saldo). Il gateway non
|
||||
espone `available_withdrawal_funds` ne' `get_order_book`, quindi la causa vera NON e' leggibile da
|
||||
qui e il messaggio del venue e' fuorviante: si tratta il tetto come ignoto e si CHUNKA, dimezzando
|
||||
il blocco a ogni rifiuto. Costa zero: il book ha 3.990 unita' al miglior ask e la commissione spot
|
||||
e' 0, quindi N fill piccoli e un fill grande pagano lo stesso prezzo.
|
||||
|
||||
NON E' NE' LA TAGLIA NE' IL TEMPO: E' IL PREZZO. Diagnosi finale (30/08 20:02, tre ordini da 1
|
||||
USDE): SELL @1.0000 passa, BUY @1.0003 passa, BUY @1.0002 e BUY market falliscono. L'ask si era
|
||||
mosso da 1.0002 a 1.0003 e l'ordine aveva smesso di INCROCIARE: Deribit rifiuta un limite BUY non
|
||||
marcabile sullo spot con `not_enough_funds_in_currency`, che e' un messaggio fuorviante e ha fatto
|
||||
inseguire per venti minuti due ipotesi sbagliate (tetto di size, rate-limit). Il difetto vero era
|
||||
qui: il prezzo dell'ordine si derivava dall'INDICE (1.0001 + 1 tick) invece che dal BOOK. Sono due
|
||||
prezzi diversi con due mestieri diversi — l'indice MARCA il collaterale (usde.valuta, mediana
|
||||
multi-exchange: la lezione Binance 10/10), il book PREZZA lo scambio. Confonderli non sbaglia la
|
||||
valutazione, sbaglia l'esecuzione.
|
||||
|
||||
LO STORICO DELLE IPOTESI SBAGLIATE, tenuto apposta: 943.98 rifiutato per "Invalid params" (vero:
|
||||
min_trade_amount=1, il passo e' intero) -> 933 rifiutato per fondi con $1.541 disponibili (falso
|
||||
indizio) -> 100 passa e 300 no (sembrava un tetto di size: era l'ask che si muoveva in mezzo) ->
|
||||
anche 1 rifiutato otto volte (sembrava rate-limit: era sempre l'ask). Chi rilegge non deve rifare
|
||||
questo giro. Misurato subito dopo: a raffica il venue rifiuta anche 1 USDE,
|
||||
otto volte di fila, mentre 100 era appena passato. Il messaggio `not_enough_funds_in_currency` e'
|
||||
FUORVIANTE — il vincolo e' la cadenza (regolamento spot asincrono / rate-limit), non il saldo ne'
|
||||
la size. Percio' si SPAZIANO gli ordini (`--pausa`) e si fa backoff sul TEMPO, riducendo il blocco
|
||||
solo dopo rifiuti ripetuti. Dimezzare la size a ogni rifiuto, come faceva la prima versione, e' la
|
||||
reazione sbagliata al sintomo giusto: consuma i tentativi senza toccare la causa.
|
||||
|
||||
uv run python scripts/live/usde_convert.py --quota 0.64 # piano, nessun ordine (0,64 = bersaglio di cuscino_watch)
|
||||
uv run python scripts/live/usde_convert.py --quota 0.64 --esegui # manda l'ordine
|
||||
|
||||
LOCK SUL CONTO (#36). Un lancio a MANO con --esegui condivide il conto con cuscino_watch e
|
||||
book_execute (cron_cuscino.sh/cron_book.sh, entrambi dentro `flock -n logs/.conto.lock`): senza
|
||||
lo stesso lock un ordine a mano nello stesso minuto del cron puo' convertire due volte verso
|
||||
bersagli diversi (limite D5 gia' dichiarato sopra). Si lancia quindi con:
|
||||
|
||||
flock logs/.conto.lock uv run python scripts/live/usde_convert.py --quota 0.64 --esegui
|
||||
|
||||
⚠️ Qui NON senza `-n`: un lancio a mano puo' aspettare il turno invece di saltare. Questo script
|
||||
stesso NON prende il lock (vedi cuscino_watch.py): se lo facesse e girasse come figlio di
|
||||
cuscino_watch (che il lock lo tiene gia'), un secondo `flock -n` lo troverebbe occupato e
|
||||
fallirebbe subito, per costruzione, ogni singola volta.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import math
|
||||
import sys
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from src.live import usde as U # noqa: E402
|
||||
from src.live.deribit import DeribitRead # noqa: E402
|
||||
|
||||
LOG = ROOT / "data" / "live" / "usde_convert.jsonl"
|
||||
PX_MIN, PX_MAX = 0.995, 1.005 # banda di sicurezza sul prezzo (26/08)
|
||||
QUOTA_HARD_MAX = 0.95 # gate USDE-01: "mai 100%"
|
||||
MIN_ORDER_USD = 5.0
|
||||
TOLL = 0.005 # entro mezzo punto di quota si considera gia' a bersaglio
|
||||
EPS_CUSCINO = 0.01 # 1 centesimo: il criterio e' ">= cuscino", e una quota derivata DAL
|
||||
# cuscino ci cade sopra esatta (0.30*E da entrambi i lati). Senza
|
||||
# questo epsilon il rumore float boccia il caso di pareggio.
|
||||
|
||||
|
||||
# Il cuscino di regolamento vive in src/live/usde.py dal 10/09 (issue #3): cuscino_watch lo
|
||||
# sorveglia ogni ora e questo script lo rispetta a ogni piano — due lettori, UNA formula (P1).
|
||||
# L'alias resta per r0906_usde_tetto_sonda, che lo importa da qui.
|
||||
cuscino_richiesto_usd = U.cuscino_richiesto_usd
|
||||
|
||||
# issue #26: label dei NOSTRI ordini sullo spot, per riconoscere i residui che questo script
|
||||
# stesso ha lasciato aperti (mai altri: un ordine con label diversa non e' nostro e non si tocca).
|
||||
LABEL_PREFIX = "usde-quota-"
|
||||
|
||||
|
||||
def _cancella(c, order_id: str) -> tuple[bool, str]:
|
||||
"""Cancella UN ordine. -> (ok, motivo se no). NON solleva: il chiamante decide (P3) — la
|
||||
lettura degli ordini aperti solleva (P5), la cancellazione di un singolo ordine no, perche'
|
||||
il chiamante deve poter fermarsi con un messaggio invece di un traceback."""
|
||||
try:
|
||||
resp = c._unwrap(c._post("/mcp-deribit/tools/cancel_order", {"order_id": order_id})) or {}
|
||||
except Exception as e: # noqa: BLE001 — si registra (P3)
|
||||
return False, f"{type(e).__name__}: {e}"
|
||||
if not isinstance(resp, dict) or resp.get("error") or resp.get("state") == "error":
|
||||
return False, str(resp.get("error", resp)) if isinstance(resp, dict) else str(resp)
|
||||
return True, ""
|
||||
|
||||
|
||||
def cancella_residui(c) -> list[str]:
|
||||
"""issue #26 — GTC mai cancellati. Il gateway cerbero-mcp NON accetta `time_in_force`
|
||||
(PlaceOrderReq non ha il campo, extra ignorati: gitea:Adriano/Cerbero-mcp#1): un limit
|
||||
riempito solo in parte resta APERTO per sempre e si accumula a ogni giro invece di sparire
|
||||
con l'ordine. Qui si legge lo spot U.SPOT e si cancella SOLO cio' che porta la NOSTRA label
|
||||
(`usde-quota-*`): un ordine con altra label non e' nostro e non si tocca.
|
||||
|
||||
-> lista degli order_id cancellati. Se la LETTURA degli ordini aperti fallisce SOLLEVA: non
|
||||
si piazza un nuovo ordine senza sapere cosa c'e' gia' aperto (P5, non si ordina al buio)."""
|
||||
# non e' verificato sotto quale valuta Deribit elenchi lo spot USDE_USDC (quotazione USDC o
|
||||
# base USDE): si interrogano entrambe e si unisce per order_id, cosi' non si perde il residuo
|
||||
# se il venue lo elenca sotto la base.
|
||||
visti = {}
|
||||
for valuta in ("USDC", "USDE"):
|
||||
out = c._unwrap(c._post("/mcp-deribit/tools/get_open_orders",
|
||||
{"currency": valuta, "kind": "spot", "type": "all"}))
|
||||
ordini = out if isinstance(out, list) else (out.get("orders", []) if isinstance(out, dict) else [])
|
||||
for o in ordini:
|
||||
if isinstance(o, dict) and o.get("order_id") is not None:
|
||||
visti[o["order_id"]] = o
|
||||
ordini = list(visti.values())
|
||||
cancellati = []
|
||||
for o in ordini:
|
||||
if not isinstance(o, dict):
|
||||
continue
|
||||
instrument = o.get("instrument") or o.get("instrument_name")
|
||||
label = o.get("label") or ""
|
||||
if instrument == U.SPOT and label.startswith(LABEL_PREFIX):
|
||||
oid = o.get("order_id")
|
||||
ok, motivo = _cancella(c, oid)
|
||||
if not ok:
|
||||
raise RuntimeError(f"cancellazione del residuo {oid} ({label}) fallita: {motivo}")
|
||||
cancellati.append(oid)
|
||||
return cancellati
|
||||
|
||||
|
||||
def book_top(timeout: float = 5.0) -> tuple[float | None, float | None]:
|
||||
"""Miglior bid/ask dello spot USDE_USDC dall'API PUBBLICA Deribit (tokenless: il gateway non
|
||||
espone get_order_book). -> (bid, ask), (None, None) se non leggibile. Mai solleva.
|
||||
|
||||
NON e' il prezzo di `usde.valuta`: quello e' l'INDICE e serve a marcare il collaterale. Questo
|
||||
serve a far INCROCIARE l'ordine, ed e' l'unico che il matching engine guarda."""
|
||||
try:
|
||||
import requests
|
||||
r = requests.get("https://www.deribit.com/api/v2/public/get_order_book",
|
||||
params={"instrument_name": U.SPOT, "depth": 5}, timeout=timeout).json()
|
||||
res = r.get("result", r)
|
||||
bid = float(res["bids"][0][0]) if res.get("bids") else None
|
||||
ask = float(res["asks"][0][0]) if res.get("asks") else None
|
||||
return bid, ask
|
||||
except Exception:
|
||||
return None, None
|
||||
|
||||
|
||||
def piano(eq_usdc: float, eq_usde: float, px: float, quota_target: float,
|
||||
equity_tot: float, bid: float | None = None, ask: float | None = None) -> dict:
|
||||
"""PURA. -> piano di conversione, con verdetto e motivo. Non manda niente."""
|
||||
val_usde, _ = U.valuta(eq_usde, px)
|
||||
quota_ora = val_usde / equity_tot if equity_tot > 0 else 0.0
|
||||
val_target = quota_target * equity_tot
|
||||
delta_usd = val_target - val_usde # >0 compra USDE, <0 vende
|
||||
side = "buy" if delta_usd > 0 else "sell"
|
||||
# MARCABILE CON MARGINE. Il book REST pubblico e' in RITARDO sul matching engine: misurato il
|
||||
# 30/08, un BUY a ask+1tick (1.0003) e' stato rifiutato e uno a 1.0004 accettato un minuto dopo,
|
||||
# col book che riportava ask 1.0002x3855 in entrambi i casi. Percio' si incrocia con 5 tick di
|
||||
# margine e un TETTO DURO: il fill avviene al prezzo del LIBRO, non al limite, quindi il margine
|
||||
# non si paga se il book non si e' mosso, e se si e' mosso il tetto limita il danno a ~9 bps.
|
||||
# Senza book leggibile si RINUNCIA — non si tira a indovinare (P5).
|
||||
TICK, MARGINE = 0.0001, 5
|
||||
CAP_BUY, CAP_SELL = 1.0010, 0.9990
|
||||
rif = ask if side == "buy" else bid
|
||||
if rif is None:
|
||||
px_ord = None
|
||||
elif side == "buy":
|
||||
px_ord = round(min(rif + MARGINE * TICK, CAP_BUY), 4)
|
||||
else:
|
||||
px_ord = round(max(rif - MARGINE * TICK, CAP_SELL), 4)
|
||||
# PASSO DI SIZE = 1 USDE, misurato sul venue (30/08): 500 e 10 accettati, 943.98 e 933.99
|
||||
# rifiutati con "Invalid params". `get_instrument` non e' esposto dal gateway (404), quindi il
|
||||
# passo e' EMPIRICO e dichiarato tale. Si quantizza per DIFETTO: meno USDE = piu' USDC, cioe'
|
||||
# dal lato del cuscino di regolamento. Sul lato vendita il difetto va nello stesso verso.
|
||||
amount = float(math.floor(abs(delta_usd) / (px_ord or px)))
|
||||
|
||||
cfg = U.cfg()
|
||||
cap_frac = float(cfg.get("venue_cap_frac") or 0)
|
||||
cap_usd = cap_frac * equity_tot if cap_frac else None
|
||||
cuscino, come = cuscino_richiesto_usd(equity_tot)
|
||||
usdc_dopo = eq_usdc - delta_usd
|
||||
motivi = []
|
||||
if px_ord is None:
|
||||
motivi.append("book USDE_USDC non leggibile — non si prezza l'ordine sull'indice (l'indice"
|
||||
" marca il collaterale, il book prezza lo scambio)")
|
||||
elif not (PX_MIN <= px_ord <= PX_MAX):
|
||||
motivi.append(f"prezzo d'ordine {px_ord:.4f} fuori banda [{PX_MIN}, {PX_MAX}]")
|
||||
if not (PX_MIN <= px <= PX_MAX):
|
||||
motivi.append(f"prezzo {px:.4f} fuori banda [{PX_MIN}, {PX_MAX}]")
|
||||
if not (0.0 <= quota_target <= QUOTA_HARD_MAX):
|
||||
motivi.append(f"quota {quota_target:.0%} fuori dal tetto HARD {QUOTA_HARD_MAX:.0%} (gate USDE-01)")
|
||||
if abs(quota_ora - quota_target) <= TOLL:
|
||||
motivi.append(f"gia' a bersaglio ({quota_ora:.1%} vs {quota_target:.0%}, toll {TOLL:.1%})")
|
||||
if abs(delta_usd) < MIN_ORDER_USD:
|
||||
motivi.append(f"delta ${abs(delta_usd):,.2f} sotto il minimo ${MIN_ORDER_USD:,.2f}")
|
||||
# il tetto del venue limita l'ACQUISTO di USDE: una VENDITA va sempre verso il tetto, e bloccarla
|
||||
# renderebbe morta la riconversione automatica di cuscino_watch (revisione fable 10/09)
|
||||
if side == "buy" and cap_usd is not None and val_target > cap_usd + EPS_CUSCINO:
|
||||
motivi.append(
|
||||
f"TETTO DEL VENUE: bersaglio ${val_target:,.2f} sopra ${cap_usd:,.2f} "
|
||||
f"({cap_frac:.1%} dell'equity, misurato {cfg.get('venue_cap_misurato')}). "
|
||||
f"Massimo raggiungibile: quota {cap_frac:.1%}")
|
||||
if usdc_dopo < cuscino - EPS_CUSCINO:
|
||||
motivi.append(f"cuscino di regolamento: USDC dopo ${usdc_dopo:,.2f} < ${cuscino:,.2f} richiesti ({come})")
|
||||
|
||||
return {"cap_usd": cap_usd, "cap_frac": cap_frac,
|
||||
"quota_ora": quota_ora, "quota_target": quota_target, "val_usde": val_usde,
|
||||
"equity_tot": equity_tot, "delta_usd": delta_usd, "side": side, "amount": amount,
|
||||
"px": px, "px_ordine": px_ord, "usdc_dopo": usdc_dopo, "cuscino": cuscino,
|
||||
"cuscino_come": come, "ok": not motivi, "motivi": motivi}
|
||||
|
||||
|
||||
def leggi_stato(c: DeribitRead) -> tuple[float, float, float | None, str]:
|
||||
eq_usdc = float(c.account_summary("USDC")["equity"])
|
||||
try:
|
||||
eq_usde = float(c.account_summary("USDE")["equity"])
|
||||
except Exception:
|
||||
eq_usde = 0.0
|
||||
px, fonte = U.prezzo(client=c)
|
||||
return eq_usdc, eq_usde, px, fonte
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--quota", type=float, required=True, help="quota USDE bersaglio, es. 0.70")
|
||||
ap.add_argument("--esegui", action="store_true", help="manda l'ordine (senza: solo il piano)")
|
||||
ap.add_argument("--chunk", type=float, default=100.0, help="blocco massimo per ordine (USDE)")
|
||||
ap.add_argument("--pausa", type=float, default=12.0, help="secondi fra un ordine e il successivo")
|
||||
a = ap.parse_args()
|
||||
|
||||
c = DeribitRead()
|
||||
eq_usdc, eq_usde, px, fonte = leggi_stato(c)
|
||||
if px is None:
|
||||
print(" ✗ prezzo USDE non leggibile — non si converte al buio (P5)")
|
||||
return 2
|
||||
val_usde, _ = U.valuta(eq_usde, px)
|
||||
equity_tot = eq_usdc + val_usde
|
||||
bid, ask = book_top()
|
||||
p = piano(eq_usdc, eq_usde, px, a.quota, equity_tot, bid, ask)
|
||||
|
||||
print("=" * 78)
|
||||
print(f" USDE CONVERT — {datetime.now(timezone.utc):%Y-%m-%dT%H:%M:%SZ}"
|
||||
f" {'ESECUZIONE' if a.esegui else 'PIANO (dry-run)'}")
|
||||
print("=" * 78)
|
||||
print(f" prezzo : indice {px:.4f} ({fonte}) · book bid {bid} / ask {ask}"
|
||||
f" -> ordine @ {p['px_ordine']}, banda [{PX_MIN}, {PX_MAX}]")
|
||||
print(f" equity : USDC ${eq_usdc:,.2f} + USDE {eq_usde:,.4f} (${val_usde:,.2f})"
|
||||
f" = ${equity_tot:,.2f}")
|
||||
print(f" quota : {p['quota_ora']:.1%} -> {p['quota_target']:.0%}"
|
||||
+ (f" · tetto venue {p['cap_frac']:.1%} = ${p['cap_usd']:,.2f}" if p.get("cap_usd") else ""))
|
||||
print(f" ordine : {p['side'].upper()} {p['amount']:,.2f} USDE_USDC @ limit {p['px_ordine']:.4f}"
|
||||
f" (${abs(p['delta_usd']):,.2f})")
|
||||
slack = p["usdc_dopo"] - p["cuscino"]
|
||||
print(f" USDC dopo : ${p['usdc_dopo']:,.2f} · cuscino richiesto ${p['cuscino']:,.2f}"
|
||||
f" ({p['cuscino_come']})")
|
||||
print(f" margine : ${slack:+,.2f} di slack sul cuscino"
|
||||
+ (" ⚠️ la quota e' ESATTAMENTE sul limite: zero slack" if abs(slack) <= EPS_CUSCINO else ""))
|
||||
for m in p["motivi"]:
|
||||
print(f" ✗ {m}")
|
||||
if not p["ok"]:
|
||||
print(" → NON si converte.")
|
||||
return 1
|
||||
if not a.esegui:
|
||||
print(" → piano valido. Rilancia con --esegui per mandarlo.")
|
||||
return 0
|
||||
|
||||
LOG.parent.mkdir(parents=True, exist_ok=True)
|
||||
residui = cancella_residui(c)
|
||||
print(f" residui cancellati all'avvio: {len(residui)}"
|
||||
+ (f" ({', '.join(residui)})" if residui else ""))
|
||||
chunk, fatti, tot_filled, prezzi, falliti = a.chunk, [], 0.0, [], 0
|
||||
for _ in range(60):
|
||||
eq_usdc, eq_usde, px, _f = leggi_stato(c)
|
||||
if px is None:
|
||||
print(" ✗ prezzo non piu' leggibile — mi fermo qui"); break
|
||||
val, _ = U.valuta(eq_usde, px)
|
||||
bid, ask = book_top()
|
||||
pp = piano(eq_usdc, eq_usde, px, a.quota, eq_usdc + val, bid, ask)
|
||||
if not pp["ok"]:
|
||||
print(f" · fine: {pp['motivi'][0]}")
|
||||
break
|
||||
amt = float(min(pp["amount"], math.floor(chunk)))
|
||||
if amt < 1:
|
||||
print(" ✗ blocco sceso sotto 1 USDE — mi fermo"); break
|
||||
resp = c._unwrap(c._post("/mcp-deribit/tools/place_order",
|
||||
{"instrument_name": U.SPOT, "side": pp["side"], "amount": amt,
|
||||
"type": "limit", "price": pp["px_ordine"],
|
||||
"label": f"usde-quota-{a.quota:.2f}"})) or {}
|
||||
if not isinstance(resp, dict) or resp.get("error") or "order" not in resp:
|
||||
err = resp.get("error", resp) if isinstance(resp, dict) else resp
|
||||
falliti += 1
|
||||
attesa = min(a.pausa * (2 ** falliti), 90.0) # backoff sul TEMPO, non sulla taglia
|
||||
if falliti >= 3: # solo allora sospetto la size
|
||||
chunk = max(1.0, math.floor(chunk / 2))
|
||||
print(f" · rifiutato {amt:,.0f}: {err} -> attendo {attesa:.0f}s"
|
||||
+ (f", blocco a {chunk:,.0f}" if falliti >= 3 else ""))
|
||||
time.sleep(attesa)
|
||||
with LOG.open("a") as f:
|
||||
f.write(json.dumps({"ts": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"rifiutato": err, "amount": amt, "nuovo_chunk": chunk}) + "\n")
|
||||
if falliti >= 8:
|
||||
print(" ✗ troppi rifiuti consecutivi — mi fermo"); break
|
||||
continue
|
||||
falliti = 0
|
||||
order, trades = resp["order"], resp.get("trades", []) or []
|
||||
filled = float(order.get("filled_amount") or 0) or sum(float(t.get("amount", 0) or 0) for t in trades)
|
||||
tot_filled += filled
|
||||
prezzi += [(float(t["price"]), float(t["amount"])) for t in trades if t.get("price") and t.get("amount")]
|
||||
fatti.append({"order_id": order.get("order_id"), "state": order.get("order_state"),
|
||||
"amount": amt, "filled": filled})
|
||||
print(f" · {pp['side']} {amt:,.0f} -> {order.get('order_state')} filled {filled:,.0f}"
|
||||
f" (quota {pp['quota_ora']:.1%} -> bersaglio {a.quota:.0%})")
|
||||
# issue #26: un GTC riempito solo in parte (o per niente) resta APERTO — il gateway non
|
||||
# accetta time_in_force, quindi la cancellazione e' tutta qui. Se la cancel fallisce si
|
||||
# FERMA il ciclo: mai un secondo ordine mentre il residuo del primo e' ancora vivo.
|
||||
if filled < amt:
|
||||
oid = order.get("order_id")
|
||||
ok, motivo = _cancella(c, oid)
|
||||
fatti[-1]["cancellato"] = True if ok else f"errore: {motivo}"
|
||||
if not ok:
|
||||
print(f" ✗ cancellazione del residuo {oid} FALLITA: {motivo}"
|
||||
" — mi fermo (mai un secondo ordine con un residuo vivo)")
|
||||
break
|
||||
print(f" · residuo cancellato: order_id {oid} (filled {filled:,.0f}/{amt:,.0f})")
|
||||
if filled <= 0:
|
||||
print(" ✗ ordine accettato ma non fillato — mi fermo"); break
|
||||
time.sleep(a.pausa)
|
||||
|
||||
px_med = sum(q * v for q, v in prezzi) / sum(v for _, v in prezzi) if prezzi else None
|
||||
eq_usdc, eq_usde, px, _f = leggi_stato(c)
|
||||
val, _ = U.valuta(eq_usde, px)
|
||||
tot = eq_usdc + val
|
||||
print(f"\n ESITO : {tot_filled:,.0f} USDE in {len(fatti)} ordini"
|
||||
+ (f" @ {px_med:.4f} medio" if px_med else ""))
|
||||
print(f" conto : USDC ${eq_usdc:,.2f} + USDE {eq_usde:,.4f} (${val:,.2f}) = ${tot:,.2f}")
|
||||
print(f" quota : {val / tot:.2%} (bersaglio {a.quota:.0%})")
|
||||
with LOG.open("a") as f:
|
||||
f.write(json.dumps({"ts": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"quota_target": a.quota, "tot_filled": tot_filled, "px_medio": px_med,
|
||||
"ordini": fatti, "quota_finale": val / tot if tot else None,
|
||||
"usdc": eq_usdc, "usde": eq_usde}) + "\n")
|
||||
print(f" registrato : {LOG.relative_to(ROOT)}")
|
||||
return 0 if tot_filled > 0 else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
+161
-31
@@ -58,6 +58,27 @@ EPS_REWARD = 1e-3 # USDE: sotto e' rumore di lettura; il reward atteso a $500
|
||||
FILL_TS = "2026-08-26T13:04:32Z" # conversione eseguita: 500 USDE @ 1.0003
|
||||
VERDETTO_DOPO = "2026-08-29T12:30:00Z" # ultima finestra reward del 29/08 conclusa
|
||||
|
||||
EPS_GIORNI = 0.5 # sotto mezza giornata l'APR non si annualizza
|
||||
|
||||
# --- lettura dei trade spot: i DUE limiti del canale, misurati il 2026-09-10 -----------------
|
||||
# (1) `count` massimo di Deribit per get_user_trades_by_instrument: 1000. Il 07/09 il lettore
|
||||
# chiedeva limit=50 e la sonda del 06/09 aveva fatto 54 ordini: i 4 non letti (400 USDE)
|
||||
# sono finiti nel «reward» (issue #2). Se il venue restituisce ESATTAMENTE `limit` righe la
|
||||
# lista puo' essere troncata e la somma NON e' leggibile (P12: non si attribuisce).
|
||||
# (2) il gateway chiama l'endpoint senza `historical`/timestamp: Deribit restituisce solo le
|
||||
# ultime 24h — DOCUMENTATO ("Accessing historical trades and orders using API": recent
|
||||
# trades 24h, orders 30 min, count max 1000) e misurato (il fill 08/09 06:47Z invisibile
|
||||
# alle 12:55Z del 10/09, quello del 09/09 21:47Z visibile). Se l'ultima lettura e' piu'
|
||||
# vecchia della finestra, i trade fra la lettura e il bordo sono INVISIBILI e la somma non
|
||||
# e' leggibile.
|
||||
# ⚠️ BUCO DICHIARATO (D5): il cron gira ogni 24h + pochi secondi; con la tolleranza di 5 min
|
||||
# i trade nei primi ≤5 min dopo la lettura precedente sarebbero invisibili e sommati come
|
||||
# «leggibile». Innocuo: nessun attrezzo converte USDE alle 12:35 UTC, e senza tolleranza il
|
||||
# giro quotidiano sarebbe SEMPRE «non leggibile» e il tasso non avrebbe mai finestre nuove.
|
||||
TRADE_LIMIT = 1000
|
||||
FINESTRA_GATEWAY_MS = 24 * 3_600_000
|
||||
TOLL_FINESTRA_MS = 5 * 60_000
|
||||
|
||||
|
||||
def _parse(ts: str) -> datetime:
|
||||
return datetime.fromisoformat(ts.replace("Z", "+00:00"))
|
||||
@@ -102,6 +123,39 @@ def verdetto(records: list[dict], now: datetime) -> tuple[str, str]:
|
||||
return "IN_ATTESA", f"finestre reward ~12:00 UTC, verdetto dopo {VERDETTO_DOPO}"
|
||||
|
||||
|
||||
def rendimento(records: list[dict]) -> dict:
|
||||
"""PURA. Il rendimento sulle finestre OSSERVATE, col loro NUMERO accanto.
|
||||
|
||||
E' il numero che serve alla decisione di quota, e l'unico modo onesto di darlo e' con `n`
|
||||
attaccato: un pagamento non e' un tasso. Il denominatore e' il TEMPO VERO fra la prima e
|
||||
l'ultima lettura misurabile, non il conteggio delle finestre — cosi' una finestra che non
|
||||
paga abbassa la stima invece di sparire (P5: il silenzio in una serie di ritorni si legge
|
||||
come zero, e qui e' proprio uno zero).
|
||||
|
||||
Si escludono le letture con trade nel mezzo (`con_trade`): quel delta non e' solo reward, e
|
||||
una riparazione silenziosa sarebbe un'invenzione (P12).
|
||||
"""
|
||||
usabili = [r for r in records
|
||||
if r.get("reward_stimato") is not None and not r.get("con_trade")]
|
||||
pagate = [r for r in usabili if r.get("reward_rilevato")]
|
||||
if not usabili:
|
||||
return dict(apr=None, finestre=0, pagate=0, giorni=0.0, tot=0.0,
|
||||
nota="nessuna finestra misurabile")
|
||||
tot = float(sum(r["reward_stimato"] for r in usabili))
|
||||
eq = [float(r["eq_usde"]) for r in usabili if r.get("eq_usde")]
|
||||
eq_med = sum(eq) / len(eq) if eq else 0.0
|
||||
# tempo vero coperto: dalla lettura PRIMA della prima misurabile, all'ultima
|
||||
i0 = records.index(usabili[0])
|
||||
t0 = records[max(0, i0 - 1)]["ts"]
|
||||
giorni = (usabili[-1]["ts"] - t0) / 86_400_000.0
|
||||
if giorni < EPS_GIORNI or eq_med <= 0:
|
||||
return dict(apr=None, finestre=len(usabili), pagate=len(pagate), giorni=giorni, tot=tot,
|
||||
nota=f"solo {giorni:.2f} giorni osservati: non si annualizza")
|
||||
apr = tot / eq_med / giorni * 365.0
|
||||
return dict(apr=apr, finestre=len(usabili), pagate=len(pagate), giorni=giorni, tot=tot,
|
||||
nota=f"{len(pagate)}/{len(usabili)} finestre hanno pagato")
|
||||
|
||||
|
||||
def condizioni(rec: dict | None, c: dict) -> set[str]:
|
||||
"""PURA. Condizioni di allerta attive su un record."""
|
||||
if not rec:
|
||||
@@ -121,6 +175,20 @@ def condizioni(rec: dict | None, c: dict) -> set[str]:
|
||||
return att
|
||||
|
||||
|
||||
def nuove_condizioni(att_now: set[str], att_prev: set[str], prev: dict | None) -> set[str]:
|
||||
"""PURA (issue #29 B2). Le condizioni da notificare in questo giro.
|
||||
|
||||
Fino a qui lo stato si salvava PRIMA dell'invio e il ritorno di notify() non si leggeva: un
|
||||
invio fallito restava "gia' detto" per l'episodio intero (l'ora dopo att_prev diceva ancora
|
||||
che la condizione c'era, quindi non era piu' "nuova"). Schema di cuscino_watch.py: una
|
||||
condizione conta come "gia' detta" solo se era attiva l'ultima volta E il suo invio e'
|
||||
RIUSCITO — cioe' compare in `allertati` del record precedente. Un record vecchio senza
|
||||
`allertati` (scritto prima di questo fix) vale come "tutto allertato": nessun rispam
|
||||
retroattivo delle condizioni gia' viste."""
|
||||
confermate_prev = set((prev or {}).get("allertati", att_prev))
|
||||
return att_now - (att_prev & confermate_prev)
|
||||
|
||||
|
||||
def _safe_client():
|
||||
try:
|
||||
from src.live.deribit import DeribitRead
|
||||
@@ -129,14 +197,36 @@ def _safe_client():
|
||||
return None
|
||||
|
||||
|
||||
def _trades_spot_da(client, ts_ms: int) -> tuple[float | None, int | None]:
|
||||
"""Somma con segno (buy +, sell -) dell'USDE scambiato spot dopo ts_ms. None = non leggibile."""
|
||||
def trade_copertura(n_righe: int, prev_ts_ms: int, now_ms: int,
|
||||
limit: int = TRADE_LIMIT, finestra_ms: int = FINESTRA_GATEWAY_MS,
|
||||
toll_ms: int = TOLL_FINESTRA_MS) -> str | None:
|
||||
"""PURA. None se la lista di trade COPRE l'intervallo (prev, now]; altrimenti il motivo per cui
|
||||
NON lo copre — e allora la somma non si usa (P12: un reward calcolato su una lista incompleta
|
||||
e' un'invenzione, il 07/09 valeva 400 USDE)."""
|
||||
if n_righe >= limit:
|
||||
return f"lista troncata: {n_righe} righe = limite {limit} del venue"
|
||||
if now_ms - prev_ts_ms > finestra_ms + toll_ms:
|
||||
ore = (now_ms - prev_ts_ms) / 3_600_000
|
||||
return (f"ultima lettura {ore:.1f}h fa, oltre la finestra di {finestra_ms / 3_600_000:.0f}h "
|
||||
f"del gateway: i trade piu' vecchi sono invisibili")
|
||||
return None
|
||||
|
||||
|
||||
def _trades_spot_da(client, ts_ms: int, now_ms: int | None = None
|
||||
) -> tuple[float | None, int | None, str | None]:
|
||||
"""Somma con segno (buy +, sell -) dell'USDE scambiato spot dopo ts_ms.
|
||||
-> (somma, n, motivo). somma None = non leggibile, e `motivo` dice perche' (P4)."""
|
||||
if client is None:
|
||||
return None, None
|
||||
return None, None, "gateway non raggiungibile"
|
||||
try:
|
||||
rows = client.trade_history(U.SPOT, limit=50)
|
||||
except Exception:
|
||||
return None, None
|
||||
rows = client.trade_history(U.SPOT, limit=TRADE_LIMIT)
|
||||
except Exception as e:
|
||||
return None, None, f"trade_history: {type(e).__name__}"
|
||||
if now_ms is None:
|
||||
now_ms = int(datetime.now(timezone.utc).timestamp() * 1000)
|
||||
motivo = trade_copertura(len(rows), ts_ms, now_ms)
|
||||
if motivo is not None:
|
||||
return None, None, motivo
|
||||
tot, n = 0.0, 0
|
||||
for t in rows:
|
||||
if int(t.get("timestamp") or 0) <= ts_ms:
|
||||
@@ -144,7 +234,7 @@ def _trades_spot_da(client, ts_ms: int) -> tuple[float | None, int | None]:
|
||||
amt = float(t.get("amount") or 0)
|
||||
tot += amt if (t.get("direction") or "").lower() == "buy" else -amt
|
||||
n += 1
|
||||
return round(tot, 8), n
|
||||
return round(tot, 8), n, None
|
||||
|
||||
|
||||
def main() -> int:
|
||||
@@ -173,10 +263,11 @@ def main() -> int:
|
||||
if eq_usde is not None:
|
||||
usd, _nota = U.valuta(eq_usde, px)
|
||||
|
||||
trades_usde, n_trades = (None, None)
|
||||
trades_usde, n_trades, trades_motivo = (None, None, None)
|
||||
an = dict(delta=None, reward_stimato=None, reward_rilevato=False, con_trade=None)
|
||||
if stato == "OK" and prev is not None and prev.get("eq_usde") is not None:
|
||||
trades_usde, n_trades = _trades_spot_da(client, int(prev["ts"]))
|
||||
trades_usde, n_trades, trades_motivo = _trades_spot_da(
|
||||
client, int(prev["ts"]), int(now.timestamp() * 1000))
|
||||
an = analizza(prev, eq_usde, trades_usde)
|
||||
|
||||
quota = None
|
||||
@@ -189,46 +280,64 @@ def main() -> int:
|
||||
stato=stato, motivo_blind=motivo_blind,
|
||||
eq_usde=eq_usde, px=px, px_fonte=px_fonte, usd=usd,
|
||||
eq_usdc=eq_usdc, quota=quota,
|
||||
trades_usde=trades_usde, n_trades=n_trades, **an,
|
||||
trades_usde=trades_usde, n_trades=n_trades, trades_motivo=trades_motivo, **an,
|
||||
)
|
||||
v, v_motivo = verdetto(records + [rec], now)
|
||||
rec["verdetto"], rec["verdetto_motivo"] = v, v_motivo
|
||||
|
||||
STATE.parent.mkdir(parents=True, exist_ok=True)
|
||||
with STATE.open("a") as fh:
|
||||
fh.write(json.dumps(rec) + "\n")
|
||||
|
||||
# --- allarmi: transizioni (warn/quota/blind), ripetizione solo per il crit ---
|
||||
# --- allarmi: transizioni (warn/quota/blind), ripetizione solo per il crit -----------------
|
||||
# issue #29 (B2): fino a qui lo stato si salvava PRIMA dell'invio e il ritorno di notify()
|
||||
# non si leggeva — un invio fallito restava "gia' detto" per l'episodio intero. Schema di
|
||||
# cuscino_watch.py: `allertati` nel record = le condizioni il cui invio E' RIUSCITO. Lo stato
|
||||
# si scrive una volta sola, a fine giro, con `allertati` gia' dentro (vedi sotto).
|
||||
att_now, att_prev = condizioni(rec, c), condizioni(prev, c)
|
||||
nuove = att_now - att_prev
|
||||
nuove = nuove_condizioni(att_now, att_prev, prev)
|
||||
confermate_prev = set((prev or {}).get("allertati", att_prev))
|
||||
inviati = []
|
||||
allertati = (att_now & att_prev & confermate_prev) - {"DEPEG_CRIT"} # attive, gia' confermate
|
||||
if "DEPEG_CRIT" in att_now:
|
||||
notify("🚨 USDE DEPEG", {"indice": px, "soglia": c["depeg_crit"],
|
||||
"collaterale": f"${usd:,.0f}" if usd else "?",
|
||||
"azione": "valutare riconversione — la quota e' il controllo (N4)"})
|
||||
ok = notify("🚨 USDE DEPEG", {"indice": px, "soglia": c["depeg_crit"],
|
||||
"collaterale": f"${usd:,.0f}" if usd else "?",
|
||||
"azione": "valutare riconversione — la quota e' il controllo (N4)"})
|
||||
inviati.append("DEPEG_CRIT")
|
||||
if ok:
|
||||
allertati.add("DEPEG_CRIT")
|
||||
if "DEPEG_WARN" in nuove:
|
||||
notify("⚠️ USDE sotto la pari", {"indice": px, "soglia": c["depeg_warn"],
|
||||
"nota": "entra gia' nel sizing orario del book"})
|
||||
ok = notify("⚠️ USDE sotto la pari", {"indice": px, "soglia": c["depeg_warn"],
|
||||
"nota": "entra gia' nel sizing orario del book"})
|
||||
inviati.append("DEPEG_WARN")
|
||||
if ok:
|
||||
allertati.add("DEPEG_WARN")
|
||||
if "QUOTA_OVER" in nuove:
|
||||
notify("⚠️ quota USDE sopra il tetto", {"quota": f"{quota:.1%}" if quota else "?",
|
||||
"tetto": f"{c['quota_max_frac']:.0%}",
|
||||
"nota": "anche una deriva passiva conta (il libro perde -> quota sale)"})
|
||||
ok = notify("⚠️ quota USDE sopra il tetto", {"quota": f"{quota:.1%}" if quota else "?",
|
||||
"tetto": f"{c['quota_max_frac']:.0%}",
|
||||
"nota": "anche una deriva passiva conta (il libro perde -> quota sale)"})
|
||||
inviati.append("QUOTA_OVER")
|
||||
if ok:
|
||||
allertati.add("QUOTA_OVER")
|
||||
if "BLIND" in nuove:
|
||||
notify("⚠️ usde_watch BLIND", {"motivo": motivo_blind or "?",
|
||||
"nota": "'non vedo' non e' 'va tutto bene' (P5)"})
|
||||
ok = notify("⚠️ usde_watch BLIND", {"motivo": motivo_blind or "?",
|
||||
"nota": "'non vedo' non e' 'va tutto bene' (P5)"})
|
||||
inviati.append("BLIND")
|
||||
if ok:
|
||||
allertati.add("BLIND")
|
||||
rec["allertati"] = sorted(allertati)
|
||||
|
||||
v_prev = prev.get("verdetto") if prev else "IN_ATTESA"
|
||||
if v != v_prev:
|
||||
if v == "IDONEO":
|
||||
notify("📌 USDE: conto IDONEO ai reward", {"dettaglio": v_motivo,
|
||||
"prossimo passo": "decisione di quota dell'operatore (tetto allerta 50%)"})
|
||||
"prossimo passo": f"decisione di quota dell'operatore (tetto allerta {c['quota_max_frac']:.0%})"})
|
||||
elif v == "NON_IDONEO":
|
||||
notify("⚠️ USDE: conto NON idoneo", {"dettaglio": v_motivo})
|
||||
inviati.append(f"VERDETTO->{v}")
|
||||
|
||||
rend = rendimento(records + [rec])
|
||||
|
||||
STATE.parent.mkdir(parents=True, exist_ok=True)
|
||||
with STATE.open("a") as fh:
|
||||
fh.write(json.dumps(rec) + "\n")
|
||||
|
||||
cambiato = bool(inviati) or v != v_prev
|
||||
if not quiet or cambiato:
|
||||
print("=" * 78)
|
||||
@@ -240,19 +349,40 @@ def main() -> int:
|
||||
print(f" equity USDE : {eq_usde:,.4f} @ {px if px is not None else 'n/d'}"
|
||||
f" ({px_fonte}) -> ${usd:,.2f}" if usd is not None else " equity USDE : n/d")
|
||||
if quota is not None:
|
||||
margine = eq_usdc + usd * (1.0 - c["haircut"])
|
||||
print(f" quota : {quota:.1%} del totale (tetto allerta {c['quota_max_frac']:.0%})"
|
||||
f" · margine utilizzabile ~${margine:,.0f} (haircut {c['haircut']:.0%})")
|
||||
# Il modello di margine ATTIVO e' Cross X:SM dal 2026-08-31 (decisione
|
||||
# dell'operatore, config._nota_usde): l'USDE fa margine, scontato dell'haircut —
|
||||
# NON e' piu' il Segregated Standard Margin di prima di quella data.
|
||||
cross = eq_usdc + usd * (1.0 - c["haircut"])
|
||||
print(f" quota : {quota:.1%} del totale (tetto allerta {c['quota_max_frac']:.0%})")
|
||||
print(f" margine : ~${cross:,.0f} USDC + USDE scontato (cross X:SM, haircut"
|
||||
f" {c['haircut']:.0%})")
|
||||
if an["delta"] is not None:
|
||||
attr = ("non attribuibile (trade non leggibili)" if an["reward_stimato"] is None
|
||||
attr = (f"non attribuibile (trade non leggibili: {trades_motivo})"
|
||||
if an["reward_stimato"] is None
|
||||
else f"reward stimato {an['reward_stimato']:+.6f} USDE"
|
||||
+ (" [con trade nel mezzo]" if an["con_trade"] else ""))
|
||||
print(f" delta : {an['delta']:+.6f} USDE dall'ultima lettura -> {attr}")
|
||||
print(f" verdetto : {v} — {v_motivo}")
|
||||
if rend["finestre"]:
|
||||
apr = "n/d" if rend["apr"] is None else f"{rend['apr']*100:.2f}% annuo"
|
||||
print(f" rendimento : {apr} su {rend['giorni']:.1f} giorni — {rend['nota']}"
|
||||
f" (tot {rend['tot']:+.6f} USDE)")
|
||||
if rend["finestre"] < 4:
|
||||
print(f" ⚠️ n={rend['finestre']}: un pagamento non e' un tasso")
|
||||
if inviati:
|
||||
print(f" allarmi : {', '.join(inviati)}")
|
||||
return 0
|
||||
|
||||
|
||||
USO = """uso: usde_watch.py [--quiet]
|
||||
|
||||
usde_watch.py — sorveglianza giornaliera del collaterale USDE: reward, depeg, quota. SOLA LETTURA.
|
||||
uv run python scripts/live/usde_watch.py # report completo
|
||||
uv run python scripts/live/usde_watch.py --quiet # stampa/allerta solo transizioni
|
||||
|
||||
Dettaglio nel docstring in testa al file."""
|
||||
|
||||
if __name__ == "__main__":
|
||||
from src.live.cli import valida
|
||||
valida("usde_watch.py", USO, flag=("--quiet",), con_valore=())
|
||||
sys.exit(main())
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
#!/usr/bin/env python
|
||||
"""venue_news.py — legge cosa il venue ANNUNCIA. SOLA LETTURA, non blocca mai nulla.
|
||||
|
||||
PERCHE' ESISTE (2026-08-31). Il debito #8 dice, testuale: *«Resta scoperto il Rulebook vero e
|
||||
proprio: la sonda legge se il venue RISPONDE, non cosa il venue ANNUNCIA.»* Questa e' quella
|
||||
meta'. In una settimana quattro cambi di policy Deribit materiali sono arrivati **a mano**, perche'
|
||||
l'operatore ha incollato dei link — non perche' qualcosa li avesse visti:
|
||||
|
||||
* tetto sull'USDE (~31,2% dell'equity) * esclusione dell'Italia dai reward USDC
|
||||
* permesso mancante su BUIDL * fine della Proof of Reserves quotidiana
|
||||
|
||||
E il caso che decide la questione: le **specifiche dei perpetual lineari USDC** sono cambiate il
|
||||
18/08, annunciate il **14/08**. `check_specs()` rileva la deriva DOPO che e' avvenuta ed e' nato
|
||||
proprio da quella svista; il feed l'avrebbe detto **quattro giorni prima**. Rilevare a posteriori e
|
||||
sapere in anticipo non sono la stessa cosa: la prima volta e' andata bene solo perche' i cambi
|
||||
erano RIDUZIONI (un valore piu' grosso resta conforme). Il giorno che Deribit ALZA un minimo, gli
|
||||
ordini vengono rifiutati.
|
||||
|
||||
COSA E' E COSA NON E'. E' un promemoria che legge un feed RSS pubblico e dice *«e' uscito questo,
|
||||
guardalo»*. **Non interpreta e non decide**: nessun automatismo su un testo di marketing (P13 — una
|
||||
guardia sui NUMERI non copre il RAGIONAMENTO, e la prosa si legge come opinione di un lettore
|
||||
fallibile). L'unica cosa che classifica e' l'URGENZA, e lo fa su parole DERIVATE dal codice
|
||||
sorvegliato, non ridichiarate qui (P1): gli strumenti vengono da `deribit._CONTRACT`, le valute di
|
||||
collaterale da `config/live.json`. Chi aggiunge uno strumento o una valuta allarga da solo la
|
||||
sorveglianza, senza toccare questo file.
|
||||
|
||||
uv run python scripts/live/venue_news.py # report + allerta sui nuovi
|
||||
uv run python scripts/live/venue_news.py --quiet # solo allerta (per il cron)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import html
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from src.live.giri import registra_giro # noqa: E402
|
||||
from src.live.notifier import notify # noqa: E402
|
||||
|
||||
FEED = "https://insights.deribit.com/exchange-updates/feed/"
|
||||
STATE = ROOT / "data" / "live" / "venue_news.jsonl"
|
||||
RUNS = ROOT / "data" / "venue_news" / "runs.jsonl"
|
||||
|
||||
# Parole che alzano un annuncio a 🚨. Le prime sono FISSE (categorie di rischio che ci toccano
|
||||
# per costruzione); le altre si DERIVANO dal codice, sotto.
|
||||
_CHIAVI_FISSE = (
|
||||
"contract specification", "margin", "collateral", "fee", "delist", "discontinu",
|
||||
"reserve", "maintenance", "liquidation", "settlement", "withdrawal", "reward",
|
||||
"jurisdiction", "eligib", "insurance fund", "socialized", "adl", "rulebook",
|
||||
)
|
||||
|
||||
|
||||
def chiavi() -> list[str]:
|
||||
"""Parole-chiave DERIVATE dal codice sorvegliato (P1), piu' quelle fisse."""
|
||||
out = set(_CHIAVI_FISSE)
|
||||
try:
|
||||
from src.live.deribit import _CONTRACT
|
||||
for nome in _CONTRACT: # BTC_USDC-PERPETUAL -> "btc", "usdc"
|
||||
for pezzo in re.split(r"[_\-]", nome.lower()):
|
||||
if len(pezzo) >= 3 and pezzo != "perpetual":
|
||||
out.add(pezzo)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
cfg = json.loads((ROOT / "config" / "live.json").read_text())
|
||||
out.add(str(cfg.get("usde", {}).get("index_name", "")).split("_")[0].lower())
|
||||
except Exception:
|
||||
pass
|
||||
return sorted(k for k in out if k)
|
||||
|
||||
|
||||
def scarica(url: str = FEED, timeout: float = 20.0) -> str | None:
|
||||
"""Feed RSS pubblico (tokenless). None se non leggibile. Mai solleva."""
|
||||
try:
|
||||
import requests
|
||||
r = requests.get(url, timeout=timeout,
|
||||
headers={"User-Agent": "PythagorasGoal/venue_news (+read-only)"})
|
||||
return r.text if r.status_code == 200 else None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def voci(xml: str) -> list[dict]:
|
||||
"""PURA. Estrae (guid, titolo, data, link) dal RSS. Mai solleva."""
|
||||
out = []
|
||||
for blocco in re.findall(r"<item>(.*?)</item>", xml or "", re.S):
|
||||
def campo(tag):
|
||||
m = re.search(rf"<{tag}[^>]*>(?:<!\[CDATA\[)?(.*?)(?:\]\]>)?</{tag}>", blocco, re.S)
|
||||
return html.unescape(m.group(1).strip()) if m else ""
|
||||
g = campo("guid") or campo("link")
|
||||
if g:
|
||||
out.append({"guid": g, "titolo": campo("title"),
|
||||
"data": campo("pubDate"), "link": campo("link")})
|
||||
return out
|
||||
|
||||
|
||||
def rilevante(titolo: str, ks: list[str]) -> list[str]:
|
||||
"""PURA. Quali parole-chiave compaiono nel titolo."""
|
||||
t = titolo.lower()
|
||||
return [k for k in ks if k in t]
|
||||
|
||||
|
||||
def gia_visti() -> set[str]:
|
||||
if not STATE.exists():
|
||||
return set()
|
||||
out = set()
|
||||
for riga in STATE.read_text().splitlines():
|
||||
try:
|
||||
out.add(json.loads(riga)["guid"])
|
||||
except Exception:
|
||||
pass
|
||||
return out
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--quiet", action="store_true")
|
||||
a = ap.parse_args()
|
||||
|
||||
xml = scarica()
|
||||
if xml is None:
|
||||
if not a.quiet:
|
||||
print(" ✗ feed non leggibile — 'non vedo' non e' 'niente di nuovo' (P5)")
|
||||
return 2
|
||||
|
||||
ks, viste, nuove = chiavi(), gia_visti(), []
|
||||
primo_giro = not STATE.exists()
|
||||
tutte = voci(xml)
|
||||
STATE.parent.mkdir(parents=True, exist_ok=True)
|
||||
with STATE.open("a") as f:
|
||||
for v in tutte:
|
||||
if v["guid"] in viste:
|
||||
continue
|
||||
v["match"] = rilevante(v["titolo"], ks)
|
||||
v["visto_il"] = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
v["seed"] = primo_giro
|
||||
f.write(json.dumps(v) + "\n")
|
||||
nuove.append(v)
|
||||
registra_giro(RUNS, voci=len(tutte), nuove=len(nuove))
|
||||
|
||||
# Primo giro: si semina lo storico SENZA allertare (P9: l'allarme massimo non si spende
|
||||
# per un arretrato di 10 voci, o non verra' letto il giorno che e' vero).
|
||||
if not primo_giro:
|
||||
for v in nuove:
|
||||
if v["match"]:
|
||||
notify("🚨 annuncio Deribit sul NOSTRO percorso",
|
||||
{"titolo": v["titolo"], "tocca": ", ".join(v["match"][:6]),
|
||||
"data": v["data"], "link": v["link"],
|
||||
"azione": "leggerlo: questo sorvegliante NON interpreta"})
|
||||
else:
|
||||
notify("📌 nuovo annuncio Deribit",
|
||||
{"titolo": v["titolo"], "data": v["data"], "link": v["link"]})
|
||||
|
||||
if not a.quiet:
|
||||
print("=" * 78)
|
||||
print(f" VENUE NEWS — {len(tutte)} voci nel feed, {len(nuove)} nuove"
|
||||
+ (" (PRIMO GIRO: storico seminato, nessun allarme)" if primo_giro else ""))
|
||||
print("=" * 78)
|
||||
for v in tutte:
|
||||
m = rilevante(v["titolo"], ks)
|
||||
segno = "🚨" if m else ("·" if v["guid"] in viste else "📌")
|
||||
print(f" {segno} {v['data'][:16]:17s} {v['titolo'][:70]}")
|
||||
if m:
|
||||
print(f" tocca: {', '.join(m[:8])}")
|
||||
print(f"\n parole derivate dal codice: {', '.join(ks)}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
+45
-15
@@ -16,14 +16,42 @@ ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from src.live.deribit import check_specs # noqa: E402
|
||||
from src.live.notifier import notify # noqa: E402
|
||||
from src.live.giri import registra_giro # noqa: E402
|
||||
from src.live.notifier import notify, ultimo_errore # noqa: E402
|
||||
from src.live.venue_watch import (PERSIST_HOURS, THRESHOLD_BPS, # noqa: E402
|
||||
run_once)
|
||||
|
||||
RUNS = ROOT / "data" / "venue_watch" / "runs.jsonl"
|
||||
|
||||
|
||||
def _titolo(rep: dict) -> str:
|
||||
"""⚠️ Il titolo dice cosa FARE, non solo cosa e' successo: chi lo legge sul telefono deve
|
||||
sapere il primo passo senza aprire il repo (runbook completo in src/live/venue_watch.py).
|
||||
⚠️ Dal 19/08 DIPENDE dalla gravita'. Prima era sempre il 🚨 col prelievo di prova, anche per
|
||||
un blocco da manutenzione annunciata: il 18/08 sono usciti quattro messaggi identici che
|
||||
chiedevano di prelevare, tutti dopo che il book aveva gia' ripreso. Un allarme massimo speso
|
||||
per un evento atteso e' un allarme che non verra' letto il giorno che e' vero."""
|
||||
if rep.get("severity") == "blind":
|
||||
return "⚠️ venue_watch CIECO — referenze non leggibili, nessun giudizio sul venue"
|
||||
if rep.get("severity") == "warn":
|
||||
return "⚠️ VENUE WATCH — Deribit non operativa, ma spiegata. Nessuna azione"
|
||||
return "🚨 VENUE WATCH — possibile stress su Deribit. PRIMO PASSO: prelievo di prova"
|
||||
|
||||
|
||||
def _manda(rep: dict) -> tuple[bool, str]:
|
||||
"""Il sender iniettato in `run_once`: e' lui a decidere se i marcatori 'gia' detto' si
|
||||
scrivono. `tentativi=3` perche' su questo percorso il 6,9% degli invii si perde, e qui
|
||||
perderne uno significa perdere l'episodio intero."""
|
||||
ok = notify(_titolo(rep), {f"alert {i+1}": a for i, a in enumerate(rep["alerts"])},
|
||||
tentativi=3)
|
||||
if ok:
|
||||
return True, "ok"
|
||||
return False, (ultimo_errore() or "motivo non registrato")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
quiet = "--quiet" in sys.argv
|
||||
rep = run_once()
|
||||
rep = run_once(sender=_manda)
|
||||
|
||||
if not quiet:
|
||||
print("=" * 78)
|
||||
@@ -47,6 +75,7 @@ def main() -> int:
|
||||
# che manda ordini. Il 18/08 Deribit ha cambiato tick e size dei perpetual lineari USDC e
|
||||
# niente se n'era accorto: e' andata bene perche' i cambi erano riduzioni.
|
||||
spec = check_specs()
|
||||
registra_giro(RUNS, livelli=rep["levels"])
|
||||
if spec["divergenze"]:
|
||||
rifiuto = [d for d in spec["divergenze"] if d["rischio"] == "rifiuto"]
|
||||
righe = {f"{d['strumento']} {d['campo']}": f"dichiarato {d['dichiarato']} / "
|
||||
@@ -65,24 +94,25 @@ def main() -> int:
|
||||
print(f" SPEC: {k}: {v}")
|
||||
|
||||
if rep["alerts"]:
|
||||
# ⚠️ Il titolo dice cosa FARE, non solo cosa e' successo: chi lo legge sul telefono deve
|
||||
# sapere il primo passo senza aprire il repo (runbook completo in src/live/venue_watch.py).
|
||||
# ⚠️ Dal 19/08 il titolo DIPENDE dalla gravita'. Prima era sempre il 🚨 col prelievo di
|
||||
# prova, anche per un blocco da manutenzione annunciata: il 18/08 sono usciti quattro
|
||||
# messaggi identici che chiedevano di prelevare, tutti dopo che il book aveva gia'
|
||||
# ripreso. Un allarme massimo speso per un evento atteso e' un allarme che non verra'
|
||||
# letto il giorno che e' vero — ed e' l'unico giorno che conta.
|
||||
if rep.get("severity") == "warn":
|
||||
titolo = "⚠️ VENUE WATCH — Deribit non operativa, ma spiegata. Nessuna azione"
|
||||
else:
|
||||
titolo = ("🚨 VENUE WATCH — possibile stress su Deribit. "
|
||||
"PRIMO PASSO: prelievo di prova")
|
||||
notify(titolo, {f"alert {i+1}": a for i, a in enumerate(rep["alerts"])})
|
||||
# L'invio l'ha gia' fatto `run_once` attraverso `_manda`: qui si stampa e si riporta
|
||||
# l'ESITO. Un invio fallito si vede nel log del cron invece di sparire — e lo stato su
|
||||
# disco e' stato disfatto, quindi l'ora prossima ci riprova da solo.
|
||||
for a in rep["alerts"]:
|
||||
print(f" ALERT: {a}")
|
||||
print(f" telegram: {rep.get('invio')}")
|
||||
return 2 if rep.get("severity") == "alert" else 0
|
||||
return 0
|
||||
|
||||
|
||||
USO = """uso: venue_watch.py [--quiet]
|
||||
|
||||
venue_watch.py — runner ORARIO del tripwire di venue. Allerta su Telegram, NON blocca nulla.
|
||||
uv run python scripts/live/venue_watch.py # un giro, stampa il report
|
||||
uv run python scripts/live/venue_watch.py --quiet # solo in caso di allarme (per il cron)
|
||||
|
||||
Dettaglio nel docstring in testa al file."""
|
||||
|
||||
if __name__ == "__main__":
|
||||
from src.live.cli import valida
|
||||
valida("venue_watch.py", USO, flag=("--quiet",), con_valore=())
|
||||
raise SystemExit(main())
|
||||
|
||||
@@ -9,6 +9,9 @@ lontana, che il 30/07 aveva misurato sottoprezzata ~2.3x.
|
||||
piu' mispriced in RELATIVO (f_long 5.85 contro 2.23) ma pesa il **3.2%** del premio corto invece
|
||||
del **18.4%**, quindi sul credito NETTO l'effetto e' minore: f_net **0.852** contro **0.718**.
|
||||
Meccanismo giusto, segno della conclusione sbagliato.
|
||||
(Numeri del 30/07, misurati con `cblib` che leggeva spot e DVOL di UN'ORA / fino a 24 ORE dopo
|
||||
l'ingresso — debito §5.18, riparato la sera del 09/09: da allora la tabella stampata da questo
|
||||
script e' la fonte, e il f canonico e' passato da 0,732 a 0,706 sulle stesse 28 coppie.)
|
||||
|
||||
E' pero' una misura su **16 coppie**: la differenza appaiata e' **+0.109 con IC95
|
||||
[-0.047, +0.193]**, cioe' **compatibile con zero**. Serve piu' campione, e "quando ce ne sara'
|
||||
@@ -48,9 +51,11 @@ sys.path.insert(0, str(PROJECT_ROOT / "scripts" / "research"))
|
||||
from cblib import ( # noqa: E402
|
||||
dvol_series, f_factors, load_chain, pick_legs, puts, spot_series,
|
||||
)
|
||||
from src.live.giri import registra_giro # noqa: E402
|
||||
from src.live.notifier import notify # noqa: E402
|
||||
|
||||
STATE = PROJECT_ROOT / "data" / "vrp_f_watch" / "state.json"
|
||||
RUNS = PROJECT_ROOT / "data" / "vrp_f_watch" / "runs.jsonl"
|
||||
|
||||
# --- config CONGELATA il 2026-07-30 ---------------------------------------
|
||||
STRUCTS = {
|
||||
@@ -187,6 +192,8 @@ def main() -> int:
|
||||
a["notified"] = bool(prev.get("notified", False))
|
||||
|
||||
_save_state(a)
|
||||
if not R.empty:
|
||||
registra_giro(RUNS, pairs=a["pairs"], ready=a["ready"])
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
@@ -13,6 +13,10 @@ Regola che vale per ogni misura fatta con questo modulo: il f della sola gamba c
|
||||
dello spread. Il modello sbaglia soprattutto sull'ala che si COMPRA (piu' OTM, IV piu' alta per
|
||||
skew, prezzata dal modello a vol ATM) -> misurare una gamba sola da' la risposta sbagliata con
|
||||
segno rassicurante.
|
||||
|
||||
Seconda regola (09/09, debito §5.18): spot e DVOL escono da qui gia' CAUSALI — `asof(ts)` da'
|
||||
l'ultima chiusura nota a ts. Chi costruisce una serie di prezzi altrove usi `causale()`, non
|
||||
l'indice del feed com'e' (etichettato all'apertura: D6).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -65,23 +69,48 @@ def puts(asset: str, df: pd.DataFrame | None = None) -> pd.DataFrame:
|
||||
return d[(d["asset"] == asset) & (d["option_type"] == "P")]
|
||||
|
||||
|
||||
def causale(s: pd.Series, cadenza: str) -> pd.Series:
|
||||
"""Rietichetta una serie di CHIUSURE dall'APERTURA della barra all'istante in cui la chiusura
|
||||
e' nota (apertura + cadenza), cosi' `s.asof(ts)` restituisce l'ultima chiusura CONOSCIUTA a ts
|
||||
e mai una futura. I valori non si toccano, solo le etichette.
|
||||
|
||||
🚨 Perche' esiste (debito §5.18, 09/09): i feed del progetto sono etichettati all'apertura —
|
||||
la barra 1h `20:00` chiude col 5m delle 20:55 (verificato: chiusura 1h a T == chiusura 5m a
|
||||
T+55 nel 100% delle barre) e la riga DVOL del giorno D e' la chiusura delle 23:00 di D
|
||||
(verificato contro la risoluzione 1h dell'API pubblica). Senza questo spostamento `asof(ts)`
|
||||
dava lo spot di UN'ORA dopo e il DVOL di FINO A 24 ORE dopo l'ingresso (D6, quinta occorrenza)."""
|
||||
out = s.copy()
|
||||
out.index = out.index + pd.Timedelta(cadenza)
|
||||
return out
|
||||
|
||||
|
||||
@lru_cache(maxsize=8)
|
||||
def spot_series(asset: str) -> pd.Series:
|
||||
"""Chiusure 1h del feed certificato, etichettate all'istante in cui sono NOTE (apertura + 1h).
|
||||
|
||||
`spot_series(a).asof(ts)` = ultima chiusura oraria conosciuta a `ts`. Conseguenza sul
|
||||
regolamento: `asof(exp)` alle 08:00 e' la chiusura della barra 07:00, cioe' il prezzo delle
|
||||
08:00 — prima era il prezzo delle 09:00, un'ora DOPO la scadenza."""
|
||||
from scripts.analysis.research_lab import load_tf
|
||||
px = load_tf(asset, "1h")
|
||||
s = pd.Series(px["close"].values.astype(float),
|
||||
index=pd.to_datetime(px["timestamp"], unit="ms", utc=True)).sort_index()
|
||||
s.index = pd.DatetimeIndex(s.index).as_unit("ns") # le quote bite hanno microsecondi
|
||||
return s
|
||||
return causale(s, "1h")
|
||||
|
||||
|
||||
@lru_cache(maxsize=8)
|
||||
def dvol_series(asset: str) -> pd.Series:
|
||||
"""DVOL giornaliero (chiusura), etichettato all'istante in cui e' NOTO (giorno + 1D).
|
||||
|
||||
`dvol_series(a).asof(ts)` = ultima chiusura giornaliera del DVOL conosciuta a `ts`: dentro il
|
||||
giorno D e' la chiusura di D−1, quindi vecchia fino a 23 ore. E' il prezzo della causalita'
|
||||
con un feed giornaliero; l'alternativa (DVOL orario dall'API pubblica) non e' nel feed."""
|
||||
d = pd.read_parquet(RAW / f"dvol_{asset.lower()}.parquet")
|
||||
s = pd.Series(d["close"].values.astype(float),
|
||||
index=pd.to_datetime(d["timestamp"], unit="ms", utc=True)).sort_index()
|
||||
s.index = pd.DatetimeIndex(s.index).as_unit("ns")
|
||||
return s
|
||||
return causale(s, "1D")
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ prezzo
|
||||
|
||||
@@ -129,7 +129,21 @@ def main():
|
||||
sys.exit(2)
|
||||
ib = IB()
|
||||
try:
|
||||
ib.connect("127.0.0.1", 4002, clientId=90, timeout=15)
|
||||
# `readonly=True`: questo client SCARICA STORICO e non manda ordini mai. Non e' solo
|
||||
# igiene — in `ib_async.IB.connectAsync` le richieste "open orders" e "completed orders"
|
||||
# esistono SOLO se il client non e' readonly (`if not readonly: reqs[...]`), e sul gateway
|
||||
# paper non rispondono: ogni notte, 63 giri su 63, il log del cron si prendeva
|
||||
# open orders request timed out
|
||||
# completed orders request timed out
|
||||
# Due righe d'errore innocue ripetute per sempre sono il modo in cui un errore VERO
|
||||
# smette di farsi notare (P14). Qui si toglie la CAUSA, non si filtra il messaggio.
|
||||
# ⚠️ OSSERVATO il 2026-08-28: a meta' pomeriggio il gateway NON serve storico —
|
||||
# `reqHistoricalData` va in timeout e lo script stampa "0 barre (subscription?)" per ogni
|
||||
# simbolo. Quattro tentativi fra le 13:05 e le 15:35 UTC, tutti a zero, con e senza
|
||||
# `readonly`; il giro del cron delle 00:30 riesce ogni notte. La causa sta nel gateway,
|
||||
# non qui, e non e' stata diagnosticata. Non e' pericoloso: con 0 barre lo script NON
|
||||
# sovrascrive i parquet, quindi un giro fallito lascia il dato di ieri invece di romperlo.
|
||||
ib.connect("127.0.0.1", 4002, clientId=90, timeout=15, readonly=True)
|
||||
except Exception as e:
|
||||
print(f"[CONNESSIONE FALLITA] 127.0.0.1:4002 -> {repr(e)[:120]}\n Avvia: docker compose up -d ib-gateway")
|
||||
sys.exit(1)
|
||||
|
||||
@@ -310,11 +310,15 @@ def boot_ci(x: np.ndarray, n: int = 4000, seed: int = 822) -> tuple[float, float
|
||||
def main() -> None:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--no-live", action="store_true", help="non interrogare il venue")
|
||||
ap.add_argument("--al", default=None,
|
||||
help="data di taglio UTC (riproduzione): esclude le scadenze >= questa data; default adesso")
|
||||
args = ap.parse_args()
|
||||
|
||||
print(__doc__.split("\n\n")[0])
|
||||
puts = load_puts()
|
||||
today = pd.Timestamp.now(tz="UTC")
|
||||
today = pd.Timestamp(args.al, tz="UTC") if args.al else pd.Timestamp.now(tz="UTC")
|
||||
print(f"\n taglio dei trade: scadenze < {today:%Y-%m-%d %H:%M}Z"
|
||||
+ (" (--al: riproduzione)" if args.al else " (adesso)"))
|
||||
venue = venue_read(not args.no_live)
|
||||
|
||||
# ------------------------------------------------------------ §1 REGIME
|
||||
@@ -323,7 +327,7 @@ def main() -> None:
|
||||
"misurato 4 volte). Se nel campione quel gate non scatta mai, il campione non contiene\n"
|
||||
"la strategia — contiene la sua astensione.\n")
|
||||
for a in ASSETS:
|
||||
V = CB.dvol_series(a)
|
||||
V = CB.causale(CB.dvol_series(a), "-1D") # calendario: il DVOL DEL giorno D (la serie di cblib e' causale: sta a D+1)
|
||||
win = V[(V.index >= puts["ts"].min()) & (V.index <= puts["ts"].max())]
|
||||
hist = V[V.index < win.index[0]].to_numpy(float)
|
||||
print(f" {a}: DVOL nel campione min {win.min():.1f} / mediana {win.median():.1f} / "
|
||||
@@ -372,7 +376,7 @@ def main() -> None:
|
||||
print(" Regola del 19/06: «rivalutare quando la catena cattura un crash». Un criterio senza\n"
|
||||
" numero non e' un criterio, quindi eccolo (IV-rank espandente causale, come nel sleeve):")
|
||||
for a in ASSETS:
|
||||
V = CB.dvol_series(a)
|
||||
V = CB.causale(CB.dvol_series(a), "-1D") # idem: date di calendario, non etichette causali
|
||||
vals = V.to_numpy(float)
|
||||
ivr = np.full(len(vals), np.nan)
|
||||
for i in range(1, len(vals)):
|
||||
@@ -753,13 +757,17 @@ def main() -> None:
|
||||
|
||||
# ------------------------------------------------------------ §7 VERDETTO
|
||||
hr("§7 VERDETTO")
|
||||
print(""" SCARTATO — su questi dati la domanda non e' decidibile. Quattro motivi, indipendenti
|
||||
print(""" (numeri della stesura del 22/08, RIMISURATI il 09/09 con spot e DVOL causali — debito §5.18:
|
||||
erano 0.714 [0.690, 0.779] · 32.58 / 1.45 / -0.35 · 3.90 -> 2.41 · -1.35% -> +11.22% · +25% / +44%.
|
||||
Il verdetto non cambia: le quattro ragioni non dipendono da un'ora di spot.)
|
||||
|
||||
SCARTATO — su questi dati la domanda non e' decidibile. Quattro motivi, indipendenti
|
||||
l'uno dall'altro: ciascuno da solo basterebbe.
|
||||
|
||||
1. IL CAMPIONE NON CONTIENE LA STRATEGIA. 0 settimane su 19 passano il gate IV-rank>0.30
|
||||
che E' l'alpha di VRP01 — il sleeve sarebbe stato FLAT per l'intero campione. La DVOL
|
||||
mediana sta al 7 (BTC) / 11 (ETH) percentile della storia 2021+, e il sottostante e'
|
||||
salito del +25% / +44%: vol implicita bassa E rialzo forte = il regime MIGLIORE
|
||||
salito del +21% / +40%: vol implicita bassa E rialzo forte = il regime MIGLIORE
|
||||
possibile per vendere put, non uno neutro. Le settimane sono 10 per asset, non 16
|
||||
(prima del 2026-06-09 l'archivio seguiva solo la finestra 13-20 DTE: quella 4-10 DTE
|
||||
non esiste). Esito: 10/10 vincenti su BTC, e la lente giusta non e' lo Sharpe ma la
|
||||
@@ -770,13 +778,13 @@ def main() -> None:
|
||||
regime di vol bassa persistente, non davanti a un muro.
|
||||
|
||||
2. IL TITOLO NON SOPRAVVIVE ALL'ORA D'INGRESSO. E' un max-of-k mai dichiarato, misurato
|
||||
qui per la prima volta su questa struttura: su 9 ancore lo Sharpe canonico BTC 32.58
|
||||
sta all'89 percentile, la MEDIANA ONESTA e' 1.45 e la banda tocca il NEGATIVO (-0.35).
|
||||
Su ETH 3.90 -> 2.41. Il numero da citare per BTC e' 1.45, non 32.58.
|
||||
qui per la prima volta su questa struttura: su 9 ancore lo Sharpe canonico BTC 29.61
|
||||
sta all'89 percentile, la MEDIANA ONESTA e' 2.12 e la banda tocca il NEGATIVO (-0.19).
|
||||
Su ETH 7.64 -> 3.35. Il numero da citare per BTC e' 2.12, non 29.61.
|
||||
⚠️ E il meccanismo non e' quello che avevo scritto prima di guardarlo. Non e' solo la
|
||||
vol campionaria a ballare: fra le ancore la MEDIA settimanale BTC va da -1.35% a
|
||||
+11.22% del rischio — CAMBIA SEGNO — e la deviazione standard di 11x (ETH: media 2.9x,
|
||||
sd 2.4x). Spostare l'ora d'ingresso cambia lo snapshot e quindi quali strike combaciano
|
||||
vol campionaria a ballare: fra le ancore la MEDIA settimanale BTC va da -0.66% a
|
||||
+11.44% del rischio — CAMBIA SEGNO — e la deviazione standard di 9.2x (ETH: media 2.1x,
|
||||
sd 3.2x). Spostare l'ora d'ingresso cambia lo snapshot e quindi quali strike combaciano
|
||||
col delta bersaglio: non e' lo stesso trade un'ora dopo, e' UN'ALTRA STRUTTURA. A 10
|
||||
settimane questo parametro di disturbo domina il segnale che si vorrebbe misurare.
|
||||
|
||||
@@ -793,7 +801,7 @@ def main() -> None:
|
||||
4. LO SPREAD E' IL COSTO DOMINANTE, E SULLA FAMIGLIA ESEGUIBILE E' PEGGIO. Attraversare
|
||||
il bid/ask costa ~10% del credito sugli INVERSE; nella regione di strike che serve lo
|
||||
spread relativo mediano e' 24-37% sugli inverse e 29-59% sulle USDC, con open interest
|
||||
~0. Il f del credito netto si replica indipendentemente a 0.714 (IC95 [0.690, 0.779],
|
||||
~0. Il f del credito netto si replica indipendentemente a 0.712 (IC95 [0.664, 0.732],
|
||||
0/19 osservazioni >= 1.0) — conferma dello 0.73 del 30/07 su un campione piu' lungo e
|
||||
con un percorso diverso.
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ SOLA LETTURA. Questo script:
|
||||
- non importa nulla che invii ordini se non per ISPEZIONARLO (la sottoclasse di §1 ha `_post` che
|
||||
SOLLEVA: se il codice di produzione toccasse la rete, il test fallirebbe rumorosamente);
|
||||
- tocca il venue solo con `--venue`, solo `get_open_orders` (lettura), 2 richieste, e si rifiuta
|
||||
di partire nei minuti :05-:10 (cron_book) e :24-:30 (cron_chain).
|
||||
di partire nei minuti :45-:50 (cron_book, al :47 dal 25/08) e :24-:30 (cron_chain).
|
||||
|
||||
uv run python scripts/research/r0823_sl_anchor.py # attesa + log + simulazione
|
||||
uv run python scripts/research/r0823_sl_anchor.py --venue # + riscontro sul conto (2 letture)
|
||||
@@ -283,8 +283,8 @@ def riscontro_venue(runs_info: dict):
|
||||
diritte di Deribit appartengono a un ALTRO progetto: usarle sarebbe inventare un percorso, e la
|
||||
domanda non ne ha bisogno. Si usa quindi cio' che il percorso sanzionato offre: `get_open_orders`.""")
|
||||
m = datetime.now(timezone.utc).minute
|
||||
if 5 <= m <= 10 or 24 <= m <= 30:
|
||||
print(f"\n ⏸ minuto :{m:02d} — finestra di cron_book (:07) / cron_chain (:25). Non interrogo.")
|
||||
if 45 <= m <= 50 or 24 <= m <= 30: # cron_book al :47 dal 2026-08-25 (era :07), cron_chain :25
|
||||
print(f"\n ⏸ minuto :{m:02d} — finestra di cron_book (:47) / cron_chain (:25). Non interrogo.")
|
||||
return
|
||||
from src.live.deribit import DeribitRead
|
||||
r = DeribitRead()
|
||||
@@ -395,10 +395,11 @@ def conseguenza():
|
||||
alta dopo una salita, lo stop siede vicino sotto il mercato, il crollo lo prende, si rientra e
|
||||
la gamba successiva lo riprende. **Il rischio di accumulo e' funzione della CADENZA DEL CRON,
|
||||
non di k**: il gradino di leva non lo crea e non lo amplifica.
|
||||
· ⚠️ E qui c'e' un aggancio operativo: il docstring di `scripts/live/book_execute.py` prescrive
|
||||
«ogni ~230 minuti» (la griglia di SKH01) mentre il cron gira **ogni ora** (misurato sopra: 1443
|
||||
giri in 1442 ore). Chi "correggesse" la cadenza verso il docstring sposterebbe il libro dalla
|
||||
riga 1h alla riga 4h — quella in cui BTC raddoppia gli scatti.
|
||||
· ⚠️ Aggancio operativo (RIPARATO il 2026-09-02, `tests/test_book_cadenza.py`): fino a quel
|
||||
giorno il docstring di `scripts/live/book_execute.py` prescriveva «ogni ~230 minuti» (la
|
||||
griglia di SKH01) mentre il cron gira **ogni ora** (misurato sopra: 1443 giri in 1442 ore).
|
||||
Chi avesse "corretto" il CRON verso il docstring avrebbe spostato il libro dalla riga 1h
|
||||
(rotolante: 0 scatti) alla riga 4h (2 scatti del rotolante contro 1 del pavimento).
|
||||
|
||||
(*) ⚠️ CAVEAT sulla colonna, e sul suo artefatto: in questa lente la posizione e' SEMPRE aperta
|
||||
dal 2018, quindi in modo `roll` l'"ingresso" e' un prezzo di anni prima e il -48%/-61% e' il
|
||||
@@ -501,9 +502,9 @@ def costo(out: dict):
|
||||
|
||||
⚠️ E c'e' un accoppiamento nuovo da registrare: l'accumulo di episodi e' funzione della CADENZA
|
||||
DEL CRON (1h: no · 4h: BTC 2 · 24h: ETH 5 con 2 in 30 giorni). Oggi la cadenza e' misurata sana,
|
||||
ma nessun sorvegliante la controlla contro questa conseguenza, e il docstring di `book_execute`
|
||||
ne prescrive una (~230 min) che cade nella riga peggiore. Non e' un'azione richiesta qui: e' il
|
||||
parametro da rileggere prima del gradino, insieme a G2.""")
|
||||
e dal 2026-09-02 `tests/test_book_cadenza.py` tiene d'accordo docstring, cron_book.sh e crontab
|
||||
(il docstring ne prescriveva una, ~230 min, nella riga 4h). Resta il parametro da rileggere prima
|
||||
del gradino, insieme a G2.""")
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
#!/usr/bin/env python
|
||||
"""r0828_gtaa_band_median_phase.py — criterio (C): la banda giudicata sulla MEDIANA delle 5 fasi.
|
||||
|
||||
⚠️ DA LEGGERE PRIMA DI CITARE QUALUNQUE NUMERO DI QUI.
|
||||
Questo criterio e' stato scelto **DOPO** aver visto la tabella delle fasi di
|
||||
`r0828_gtaa_band_phase.py`, che mostra il criterio a fase singola cambiare verdetto da una notte
|
||||
all'altra. Sceglierne uno nuovo guardando l'esito del vecchio e' selezione, esattamente come
|
||||
scegliere una cella guardando l'hold-out. Quindi:
|
||||
|
||||
· questo script NON valida la proposta del 27/07 e NON puo' essere citato come sua conferma;
|
||||
· l'unica domanda che puo' porre onestamente e' quella sullo STRUMENTO — «un criterio costruito
|
||||
cosi' avrebbe risoluzione e potenza?» — che si risponde senza guardare se la cella che vince
|
||||
e' quella che ci piace;
|
||||
· se la risposta e' si', il criterio va poi DICHIARATO e la validazione RIFATTA da capo su di
|
||||
esso, con la sua data. Il verdetto di oggi sulla banda resta *non decidibile*.
|
||||
|
||||
L'IDEA. Il difetto misurato in `r0828_gtaa_band_phase.py` e' che `gtaa._gated_returns` fasa il
|
||||
ribilanciamento sulla POSIZIONE nell'array (`i % every == 0`), e la prima barra di SPY scivola di
|
||||
un giorno ogni notte perche' IB serve una finestra rotolante di 30 anni. La fase canonica quindi
|
||||
cambia da sola. La mediana sulle 5 fasi toglie di mezzo la componente di FASE — e' la stessa
|
||||
mossa che il progetto fa gia' in `r0726_loo_deluck` (M7: su ancore appaiate la statistica e' la
|
||||
mediana). Resta la componente di DATO: le barre perse. La domanda e' se basti.
|
||||
|
||||
I CRITERI, DICHIARATI QUI PRIMA DI GUARDARE I NUMERI. Il pavimento resta **0.01**, quello che il
|
||||
test si diede il 07/08: non lo si ritocca dopo aver visto l'esito, o si sta tarando il metro sul
|
||||
risultato. Il criterio (C) ha risoluzione e potenza se e solo se, sulla statistica mediana:
|
||||
(i) la banda scelta al buio e' LA STESSA in tutte le NOTTI notti simulate;
|
||||
(ii) il margine sulla seconda resta >= 0.01 in tutte;
|
||||
(iii) POTENZA — la banda scelta sull'hold-out e' DIVERSA da quella scelta al buio. Se
|
||||
coincidono, il gate lo passerebbe anche una proposta selezionata sull'hold-out, e non
|
||||
distingue niente (controllo positivo, come il 07/08).
|
||||
E una lettura di contorno che vale piu' del verdetto: la mediana di 5 fasi non e' una mediana di
|
||||
5 osservazioni. Si stampa la correlazione media fra le serie di fase e l'N_eff che ne segue
|
||||
(§2 del CLAUDE.md: «positivo in N/N ancore NON e' N osservazioni»).
|
||||
|
||||
uv run python scripts/research/r0828_gtaa_band_median_phase.py
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
sys.path.insert(0, str(ROOT / "scripts" / "research"))
|
||||
sys.path.insert(0, str(ROOT / "scripts" / "research" / "alt"))
|
||||
|
||||
import r0727_gtaa_band_gate as BG # noqa: E402
|
||||
import src.portfolio.gtaa as G # noqa: E402
|
||||
|
||||
NOTTI = 10
|
||||
MARGINE_MINIMO = 0.01 # invariato dal 07/08: il metro non si ritocca sul risultato
|
||||
EVERY = BG.PROPOSTA[0]
|
||||
FASI = tuple(range(EVERY))
|
||||
PROPOSTA_BANDA = BG.PROPOSTA[1]
|
||||
ROTOLANTE = ("SPY",) # l'unica gamba al muro dei 30 anni (misurato in r0828_gtaa_band_phase)
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def _serie(sym: str, taglio: int):
|
||||
"""(prezzi, esposizione) di una gamba, tagliata in testa di `taglio` barre. In cache: e' il
|
||||
pezzo caro (l'esposizione e' un doppio ciclo su 30 anni) e non dipende da banda ne' fase."""
|
||||
close = G._close(sym)
|
||||
if taglio:
|
||||
close = close.iloc[taglio:]
|
||||
ex = np.nan_to_num(np.asarray(G._exposure(close).values, float))
|
||||
return close, ex
|
||||
|
||||
|
||||
def _leg(sym: str, cap_leg: float, band_usd: float, ph: int, taglio: int) -> pd.Series:
|
||||
close, ex = _serie(sym, taglio)
|
||||
px = close.values.astype(float)
|
||||
ret = np.zeros(len(px))
|
||||
ret[1:] = px[1:] / px[:-1] - 1.0
|
||||
held = np.empty(len(ex))
|
||||
comm = np.zeros(len(ex))
|
||||
cur = 0.0
|
||||
for i in range(len(ex)):
|
||||
if i % EVERY == ph:
|
||||
notional = abs(ex[i] - cur) * cap_leg
|
||||
if notional >= max(band_usd, G.IB_MIN_TRADE_USD):
|
||||
comm[i] = G.ib_commission(notional, px[i]) / cap_leg
|
||||
cur = ex[i]
|
||||
held[i] = cur
|
||||
pos = np.zeros(len(held))
|
||||
pos[1:] = held[:-1]
|
||||
net = pos * ret - comm
|
||||
net[0] = 0.0
|
||||
return pd.Series(net, index=close.index)
|
||||
|
||||
|
||||
def cella(frac: float, ph: int, taglio: int) -> pd.Series:
|
||||
cap_leg = BG.CAP_REF / len(G.EQ_UNIVERSE)
|
||||
band = frac * BG.CAP_REF / len(G.EQ_UNIVERSE)
|
||||
cols = {a: _leg(a, cap_leg, band,
|
||||
ph if a in ROTOLANTE else 0,
|
||||
taglio if a in ROTOLANTE else 0)
|
||||
for a in G.EQ_UNIVERSE}
|
||||
return pd.concat(cols, axis=1, sort=True).sort_index().mean(axis=1, skipna=True).dropna()
|
||||
|
||||
|
||||
def sh(frac: float, ph: int, taglio: int, finestra: str) -> float:
|
||||
s = cella(frac, ph, taglio)
|
||||
s = s.loc[: BG.HOLDOUT] if finestra == "in" else s.loc[BG.HOLDOUT:]
|
||||
return BG.met(s)["sharpe"]
|
||||
|
||||
|
||||
def mediana_fasi(frac: float, taglio: int, finestra: str = "in") -> float:
|
||||
return float(np.median([sh(frac, ph, taglio, finestra) for ph in FASI]))
|
||||
|
||||
|
||||
def scelta(valori: dict) -> tuple[float, float]:
|
||||
best = max(valori, key=valori.get)
|
||||
return best, valori[best] - max(v for f, v in valori.items() if f != best)
|
||||
|
||||
|
||||
def sanity() -> bool:
|
||||
mia = cella(PROPOSTA_BANDA, 0, 0)
|
||||
vera = BG.run_cell(EVERY, PROPOSTA_BANDA, BG.CAP_REF)
|
||||
j = pd.concat({"m": mia, "v": vera}, axis=1, join="outer")
|
||||
d = float((j["m"] - j["v"]).abs().max())
|
||||
ok = bool(np.isfinite(d) and d < 1e-12 and len(j) == len(vera))
|
||||
print(f"[SANITY] replica (fase 0, taglio 0) vs produzione: max|Δ| = {d:.2e} -> {'OK' if ok else 'DIVERGE'}")
|
||||
return ok
|
||||
|
||||
|
||||
def main() -> int:
|
||||
print(__doc__.split("\n\n")[0])
|
||||
print("\n⚠️ criterio scelto DOPO aver visto l'esito del precedente: misura lo STRUMENTO, "
|
||||
"non valida la banda.\n")
|
||||
if not sanity():
|
||||
return 2
|
||||
|
||||
# ---------------------------------------------------------------- contorno: quante osservazioni
|
||||
print("=" * 100)
|
||||
print("CONTORNO — la mediana di 5 fasi, quante osservazioni sono davvero?")
|
||||
print("=" * 100)
|
||||
serie = {ph: cella(PROPOSTA_BANDA, ph, 0).loc[: BG.HOLDOUT] for ph in FASI}
|
||||
M = pd.concat(serie, axis=1, join="inner").dropna()
|
||||
C = M.corr().values
|
||||
rho = float((C.sum() - len(FASI)) / (len(FASI) * (len(FASI) - 1)))
|
||||
n_eff = len(FASI) / (1.0 + (len(FASI) - 1) * rho)
|
||||
print(f" correlazione media fra le 5 serie di fase (banda {PROPOSTA_BANDA:.0%}, in-sample): {rho:.3f}")
|
||||
print(f" N_eff = {len(FASI)} / (1 + {len(FASI)-1}·ρ) = {n_eff:.2f}")
|
||||
print(" -> la mediana media su ~2 osservazioni indipendenti, non su 5: toglie la fase,")
|
||||
print(" non compra precisione. Va citata come robustezza alla fase, mai come campione.")
|
||||
|
||||
# ---------------------------------------------------------------- (i) e (ii)
|
||||
print("\n" + "=" * 100)
|
||||
print(f"(C) LA MEDIANA DELLE 5 FASI — banda scelta al buio, su {NOTTI} notti di finestra rotolante")
|
||||
print("=" * 100)
|
||||
spy = G._close("SPY")
|
||||
righe, righe_singola = [], []
|
||||
for k in range(NOTTI):
|
||||
med = {f: mediana_fasi(f, k) for f in BG.FRAC_GRID}
|
||||
b, m = scelta(med)
|
||||
righe.append(dict(notte=k, inizio_SPY=str(spy.index[k].date()), scelta=f"{b:.0%}",
|
||||
margine=round(m, 4), **{f"{f:.0%}": round(med[f], 4) for f in BG.FRAC_GRID}))
|
||||
sing = {f: sh(f, 0, k, "in") for f in BG.FRAC_GRID}
|
||||
bs, ms = scelta(sing)
|
||||
righe_singola.append(dict(notte=k, scelta=f"{bs:.0%}", margine=round(ms, 4)))
|
||||
D = pd.DataFrame(righe)
|
||||
pd.set_option("display.width", 250)
|
||||
print(D.to_string(index=False))
|
||||
|
||||
scelte = set(D.scelta)
|
||||
quota = float((D.scelta == f"{PROPOSTA_BANDA:.0%}").mean())
|
||||
print(f"\n celle distinte scelte: {sorted(scelte)} | la proposta ({PROPOSTA_BANDA:.0%}) vince {quota:.0%} delle notti")
|
||||
print(f" margine: min {D.margine.min():.4f} mediana {D.margine.median():.4f} max {D.margine.max():.4f}")
|
||||
print(f" notti sotto il pavimento {MARGINE_MINIMO}: {int((D.margine < MARGINE_MINIMO).sum())}/{NOTTI}")
|
||||
|
||||
S = pd.DataFrame(righe_singola)
|
||||
print("\n confronto diretto col criterio a FASE SINGOLA (quello che fallisce oggi):")
|
||||
print(f" fase singola : celle {sorted(set(S.scelta))} "
|
||||
f"| sotto pavimento {int((S.margine < MARGINE_MINIMO).sum())}/{NOTTI} "
|
||||
f"| margine min {S.margine.min():.4f}")
|
||||
print(f" mediana fasi : celle {sorted(scelte)} "
|
||||
f"| sotto pavimento {int((D.margine < MARGINE_MINIMO).sum())}/{NOTTI} "
|
||||
f"| margine min {D.margine.min():.4f}")
|
||||
esc_s = {f: 0.0 for f in BG.FRAC_GRID}
|
||||
for f in BG.FRAC_GRID:
|
||||
vs = [sh(f, 0, k, "in") for k in range(NOTTI)]
|
||||
vm = [D[f"{f:.0%}"].iloc[k] for k in range(NOTTI)]
|
||||
esc_s[f] = (max(vs) - min(vs), max(vm) - min(vm))
|
||||
print("\n escursione dello Sharpe in-sample su 10 notti, per banda (fase singola -> mediana):")
|
||||
for f in BG.FRAC_GRID:
|
||||
a, b = esc_s[f]
|
||||
print(f" banda {f:>4.0%}: {a:.4f} -> {b:.4f} ({'−' if b < a else '+'}{abs(b-a)/a*100:4.0f}%)")
|
||||
|
||||
# ---------------------------------------------------------------- (iii) potenza
|
||||
print("\n" + "=" * 100)
|
||||
print("(iii) POTENZA — chi guardasse l'HOLD-OUT sceglierebbe la stessa banda?")
|
||||
print("=" * 100)
|
||||
med_in = {f: mediana_fasi(f, 0, "in") for f in BG.FRAC_GRID}
|
||||
med_oos = {f: mediana_fasi(f, 0, "oos") for f in BG.FRAC_GRID}
|
||||
b_in, m_in = scelta(med_in)
|
||||
b_oos, m_oos = scelta(med_oos)
|
||||
T = pd.DataFrame([dict(banda=f"{f:.0%}", med_in_sample=round(med_in[f], 4),
|
||||
med_hold_out=round(med_oos[f], 4)) for f in BG.FRAC_GRID])
|
||||
print(T.to_string(index=False))
|
||||
print(f"\n scelta al buio (pre-2015): {b_in:.0%} (margine {m_in:.4f})")
|
||||
print(f" scelta sull'hold-out : {b_oos:.0%} (margine {m_oos:.4f})")
|
||||
potenza = b_in != b_oos
|
||||
print(f" -> {'DIVERSE: il gate ha potenza' if potenza else 'UGUALI: il gate NON ha potenza'}")
|
||||
|
||||
# ---------------------------------------------------------------- verdetto
|
||||
print("\n" + "=" * 100)
|
||||
print("VERDETTO — coi criteri dichiarati in testa, calcolati adesso")
|
||||
print("=" * 100)
|
||||
stabile = len(scelte) == 1
|
||||
sopra = bool(D.margine.min() >= MARGINE_MINIMO)
|
||||
print(f" (i) scelta stabile su {NOTTI} notti : {'SI' if stabile else 'NO'} — {sorted(scelte)}")
|
||||
print(f" (ii) margine sempre >= {MARGINE_MINIMO} : {'SI' if sopra else 'NO'} — minimo {D.margine.min():.4f}")
|
||||
print(f" (iii) potenza (in ≠ hold-out) : {'SI' if potenza else 'NO'}")
|
||||
if stabile and sopra and potenza:
|
||||
print("\n -> (C) e' uno strumento utilizzabile. NON e' una validazione della banda:")
|
||||
print(" va DICHIARATO come criterio, con la sua data, e la validazione va RIFATTA su di esso.")
|
||||
return 0
|
||||
manca = [n for n, v in (("i", stabile), ("ii", sopra), ("iii", potenza)) if not v]
|
||||
print(f"\n -> (C) NON basta: cade su ({'), ('.join(manca)}).")
|
||||
print(" La mediana toglie la FASE ma non le barre perse: se il verdetto si muove ancora,")
|
||||
print(" l'instabilita' e' nel DATO, e nessun criterio la aggira. Si ripara la fonte.")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,239 @@
|
||||
#!/usr/bin/env python
|
||||
"""r0828_gtaa_band_phase.py — il gate (A) della banda GTAA01 fallisce di nuovo: e' la proposta, o e' ANCORA il gate?
|
||||
|
||||
CONTESTO. Il 27/07 fu proposta la banda al 25% della gamba. Il gate (A) — SELEZIONE IN-SAMPLE —
|
||||
la valido' due volte, con due criteri diversi, e li ha rotti entrambi senza che nessuno toccasse
|
||||
il codice:
|
||||
|
||||
· 27/07 criterio `rank_in <= rank_oos`. Rotto il 07/08. Motivo (r0807_gtaa_gate_resolution):
|
||||
lo passa ~meta' della griglia PER COSTRUZIONE, e sulla proposta decideva su 0.00116
|
||||
di Sharpe. Una moneta. RITIRATO.
|
||||
· 07/08 criterio sostitutivo: «a cadenza di produzione, la banda scelta guardando SOLO il
|
||||
pre-2015 e' la proposta». Passava col 25% e un margine di +0.0151 sulla seconda —
|
||||
13x il criterio precedente. Rotto il 28/08: al buio esce il 60%, margine 0.0074,
|
||||
SOTTO il pavimento di 0.01 che il test stesso si era dato.
|
||||
|
||||
Il codice e' fermo (`git log src/portfolio/gtaa.py scripts/research/r0727_gtaa_band_gate.py`
|
||||
non ha commit dopo il 07/08). Quindi si e' mosso il DATO. Questo script chiede, per la terza
|
||||
volta e sullo stesso parametro, se il criterio misura cio' che dichiara.
|
||||
|
||||
LE DUE COSE CHE SI SONO TROVATE
|
||||
-------------------------------
|
||||
(0) **LA FINESTRA IN-SAMPLE NON E' FERMA.** IB ritorna una finestra ROTOLANTE di 30 anni. SPY e'
|
||||
l'unica gamba al muro (quotato 1993, storia servita dal 1996), e nel `logs/cron_daily.log`
|
||||
la sua data d'inizio avanza di un giorno di borsa OGNI NOTTE: 1996-07-02 il 24/06,
|
||||
1996-09-04 oggi. Il pre-2015 — che tutto il progetto tratta come una finestra congelata —
|
||||
perde il suo giorno piu' vecchio ogni notte. (QQQ e IWM colpiranno lo stesso muro nel
|
||||
2029-2030: non e' un difetto di SPY, e' il muro che avanza.)
|
||||
|
||||
(1) **L'AMPLIFICATORE E' LA FASE.** `gtaa._gated_returns` ribilancia su `i % every == 0`: l'indice
|
||||
e' la POSIZIONE NELL'ARRAY, quindi la fase e' ancorata alla prima barra del file. Se la prima
|
||||
barra scivola di un giorno, si ri-fasa ogni decisione di ribilanciamento di trent'anni di
|
||||
storia. Il progetto sa gia' che la fase conta — `r0726_loo_deluck.gtaa01_at(ph)` la tratta
|
||||
come un'ancora a 5 valori e ci de-lucka sopra — ma la assumeva FISSA alla fase canonica 0.
|
||||
Non lo e': **la fase canonica cambia da sola ogni notte**.
|
||||
|
||||
I DUE ESPERIMENTI, in quest'ordine
|
||||
----------------------------------
|
||||
(A) **LE NOTTI.** Si taglia la testa di SPY di k=0..N-1 barre, che e' esattamente cio' che la
|
||||
finestra rotolante fa da sola in N notti, e si guarda la banda scelta al buio.
|
||||
(B) **LA FASE PURA.** A dato FISSO, si sposta la sola fase di ribilanciamento di SPY (ph=0..4).
|
||||
Serve ad attribuire: se il verdetto si muove anche qui, l'imputato e' la fase, non le barre
|
||||
perse. Col controllo su TUTTE le gambe accanto, che dice quanto sarebbe peggio quando anche
|
||||
QQQ e IWM arriveranno al muro.
|
||||
|
||||
CRITERIO DEL VERDETTO, dichiarato PRIMA (M13: un criterio si misura sulla sua RISOLUZIONE prima
|
||||
che sul suo esito). Il gate (A) ha risoluzione se e solo se:
|
||||
(i) la banda scelta al buio e' LA STESSA in tutte le notti e in tutte le fasi, e
|
||||
(ii) il margine sulla seconda resta sopra MARGINE_MINIMO in tutte.
|
||||
Se (i) o (ii) cadono, il criterio non distingue la proposta da una cella qualunque, e va ritirato
|
||||
come il suo predecessore — si congela il MOTIVO, non l'esito.
|
||||
|
||||
uv run python scripts/research/r0828_gtaa_band_phase.py
|
||||
"""
|
||||
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))
|
||||
sys.path.insert(0, str(ROOT / "scripts" / "research"))
|
||||
sys.path.insert(0, str(ROOT / "scripts" / "research" / "alt"))
|
||||
|
||||
import r0727_gtaa_band_gate as BG # noqa: E402
|
||||
import src.portfolio.gtaa as G # noqa: E402
|
||||
|
||||
NOTTI = 10 # notti di finestra rotolante da simulare
|
||||
MARGINE_MINIMO = 0.01 # il pavimento che il test si e' dato il 07/08, non ritoccato qui
|
||||
MURO_IB_ANNI = 30.0 # finestra massima servita da IB: chi la tocca rotola
|
||||
PROPOSTA_BANDA = BG.PROPOSTA[1]
|
||||
|
||||
# Tabella PUBBLICATA il 07/08 (docs/diary/2026-08-07-gate-gtaa-e-storia-troncata.md, sezione (2)).
|
||||
# Serve a misurare di quanto si e' mossa una finestra che NON puo' acquisire dati nuovi.
|
||||
SH_IN_0807 = {0.00: 0.5396, 0.05: 0.5714, 0.10: 0.5956, 0.25: 0.6398, 0.40: 0.6247, 0.60: 0.5874}
|
||||
|
||||
|
||||
def _leg(sym: str, cap_leg: float, band_usd: float, every: int, ph: int,
|
||||
taglio: int = 0) -> pd.Series:
|
||||
"""`gtaa._gated_returns` con due manopole: la FASE (`ph`) e il TAGLIO in testa alla serie.
|
||||
|
||||
Copia dichiarata, non riuso: la produzione non espone ne' l'una ne' l'altra. La replica a
|
||||
(ph=0, taglio=0) deve tornare bit-exact quella di produzione — lo verifica `sanity()`.
|
||||
"""
|
||||
close = G._close(sym)
|
||||
if taglio:
|
||||
close = close.iloc[taglio:]
|
||||
ex = np.nan_to_num(np.asarray(G._exposure(close).values, float))
|
||||
px = close.values.astype(float)
|
||||
ret = np.zeros(len(px))
|
||||
ret[1:] = px[1:] / px[:-1] - 1.0
|
||||
held = np.empty(len(ex))
|
||||
comm = np.zeros(len(ex))
|
||||
cur = 0.0
|
||||
for i in range(len(ex)):
|
||||
if i % every == ph:
|
||||
notional = abs(ex[i] - cur) * cap_leg
|
||||
if notional >= max(band_usd, G.IB_MIN_TRADE_USD):
|
||||
comm[i] = G.ib_commission(notional, px[i]) / cap_leg
|
||||
cur = ex[i]
|
||||
held[i] = cur
|
||||
pos = np.zeros(len(held))
|
||||
pos[1:] = held[:-1]
|
||||
net = pos * ret - comm
|
||||
net[0] = 0.0
|
||||
return pd.Series(net, index=close.index)
|
||||
|
||||
|
||||
def cella(frac: float, ph: int = 0, taglio: int = 0, tutte: bool = False) -> pd.Series:
|
||||
"""La cella (cadenza di produzione, banda `frac`). `ph`/`taglio` si applicano alla sola SPY
|
||||
salvo `tutte`: e' SPY l'unica gamba al muro dei 30 anni, e il resto della griglia e' fermo."""
|
||||
cap_leg = BG.CAP_REF / len(G.EQ_UNIVERSE)
|
||||
band = frac * BG.CAP_REF / len(G.EQ_UNIVERSE)
|
||||
cols = {a: _leg(a, cap_leg, band, BG.PROPOSTA[0],
|
||||
ph if (tutte or a == "SPY") else 0,
|
||||
taglio if (tutte or a == "SPY") else 0)
|
||||
for a in G.EQ_UNIVERSE}
|
||||
return pd.concat(cols, axis=1, sort=True).sort_index().mean(axis=1, skipna=True).dropna()
|
||||
|
||||
|
||||
def riga(ph: int = 0, taglio: int = 0, tutte: bool = False) -> dict:
|
||||
sh = {f: BG.met(cella(f, ph, taglio, tutte).loc[: BG.HOLDOUT])["sharpe"] for f in BG.FRAC_GRID}
|
||||
best = max(sh, key=sh.get)
|
||||
secondo = max(v for f, v in sh.items() if f != best)
|
||||
return dict(scelta=best, margine=sh[best] - secondo, sh=sh)
|
||||
|
||||
|
||||
def sanity() -> bool:
|
||||
"""La replica a (fase 0, taglio 0) DEVE essere quella di produzione. Senza questo, ogni
|
||||
numero sotto misura la mia copia invece dello sleeve che gira."""
|
||||
mia = cella(PROPOSTA_BANDA, 0, 0)
|
||||
vera = BG.run_cell(BG.PROPOSTA[0], PROPOSTA_BANDA, BG.CAP_REF)
|
||||
j = pd.concat({"m": mia, "v": vera}, axis=1, join="outer")
|
||||
delta = float((j["m"] - j["v"]).abs().max())
|
||||
ok = bool(np.isfinite(delta) and delta < 1e-12 and len(j) == len(vera))
|
||||
print(f"[SANITY] replica (fase 0, taglio 0) vs produzione: max|Δ| = {delta:.2e} "
|
||||
f"barre {len(j)} vs {len(vera)} -> {'OK' if ok else 'DIVERGE'}")
|
||||
return ok
|
||||
|
||||
|
||||
def storia() -> pd.DataFrame:
|
||||
"""(0) Quali gambe sono al muro dei 30 anni — cioe' quali hanno una finestra ROTOLANTE."""
|
||||
rows = []
|
||||
for a in G.EQ_UNIVERSE:
|
||||
s = G._close(a)
|
||||
anni = (s.index[-1] - s.index[0]).days / 365.25
|
||||
rows.append(dict(gamba=a, dal=str(s.index[0].date()), barre=len(s), anni=round(anni, 1),
|
||||
al_muro="SI — rotola" if anni >= MURO_IB_ANNI - 0.15 else "no",
|
||||
anni_al_muro=round(MURO_IB_ANNI - anni, 1)))
|
||||
return pd.DataFrame(rows)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
print(__doc__.split("\n\n")[0])
|
||||
print()
|
||||
if not sanity():
|
||||
print("!! la replica non riproduce la produzione: i numeri sotto non sono confrontabili")
|
||||
return 2
|
||||
|
||||
print("\n" + "=" * 96)
|
||||
print("(0) LA FINESTRA E' ROTOLANTE — quali gambe sono al muro dei 30 anni di IB")
|
||||
print("=" * 96)
|
||||
H = storia()
|
||||
print(H.to_string(index=False))
|
||||
rotolanti = list(H[H.al_muro.str.startswith("SI")].gamba)
|
||||
print(f"\n gambe con finestra rotolante: {rotolanti or 'nessuna'} — per queste, il pre-2015 "
|
||||
"perde il giorno piu' vecchio ogni notte.")
|
||||
|
||||
print("\n" + "=" * 96)
|
||||
print(" quanto si e' mossa una finestra che NON puo' acquisire dati nuovi (07/08 -> oggi)")
|
||||
print("=" * 96)
|
||||
oggi = riga(0, 0)["sh"]
|
||||
D = pd.DataFrame([dict(banda=f"{f:.0%}", sh_in_0807=SH_IN_0807[f], sh_in_oggi=round(oggi[f], 4),
|
||||
delta=round(oggi[f] - SH_IN_0807[f], 4)) for f in BG.FRAC_GRID])
|
||||
print(D.to_string(index=False))
|
||||
print(f"\n spostamento massimo in valore assoluto: {D.delta.abs().max():.4f} di Sharpe, "
|
||||
f"su una finestra CHIUSA (pre-2015) e a codice fermo.")
|
||||
|
||||
print("\n" + "=" * 96)
|
||||
print(f"(A) LE NOTTI — banda scelta al buio in {NOTTI} notti consecutive di finestra rotolante")
|
||||
print("=" * 96)
|
||||
spy = G._close("SPY")
|
||||
A = []
|
||||
for k in range(NOTTI):
|
||||
r = riga(0, k)
|
||||
A.append(dict(notte=k, inizio_SPY=str(spy.index[k].date()), scelta=f"{r['scelta']:.0%}",
|
||||
margine=round(r["margine"], 4),
|
||||
**{f"{f:.0%}": round(r["sh"][f], 4) for f in BG.FRAC_GRID}))
|
||||
DA = pd.DataFrame(A)
|
||||
print(DA.to_string(index=False))
|
||||
scelte_A = set(DA.scelta)
|
||||
quota_proposta = float((DA.scelta == f"{PROPOSTA_BANDA:.0%}").mean())
|
||||
print(f"\n celle distinte scelte: {sorted(scelte_A)} "
|
||||
f"| la proposta ({PROPOSTA_BANDA:.0%}) vince {quota_proposta:.0%} delle notti")
|
||||
print(f" margine: min {DA.margine.min():.4f} mediana {DA.margine.median():.4f} "
|
||||
f"max {DA.margine.max():.4f} (pavimento {MARGINE_MINIMO})")
|
||||
print(f" notti sotto il pavimento: {int((DA.margine < MARGINE_MINIMO).sum())}/{NOTTI}")
|
||||
|
||||
print("\n" + "=" * 96)
|
||||
print("(B) LA FASE PURA — dato FISSO, si muove solo la fase di ribilanciamento")
|
||||
print("=" * 96)
|
||||
for tutte, eti in ((False, f"fase mossa solo su {rotolanti or ['SPY']} (cio' che succede DAVVERO oggi)"),
|
||||
(True, "fase mossa su TUTTE le gambe (controllo: quando anche QQQ e IWM saranno al muro)")):
|
||||
B = []
|
||||
for ph in range(BG.PROPOSTA[0]):
|
||||
r = riga(ph, 0, tutte)
|
||||
B.append(dict(fase=ph, scelta=f"{r['scelta']:.0%}", margine=round(r["margine"], 4),
|
||||
**{f"{f:.0%}": round(r["sh"][f], 4) for f in BG.FRAC_GRID}))
|
||||
DB = pd.DataFrame(B)
|
||||
print(f"\n {eti}")
|
||||
print(DB.to_string(index=False))
|
||||
print(f" celle distinte: {sorted(set(DB.scelta))} | margine min {DB.margine.min():.4f}")
|
||||
if not tutte:
|
||||
scelte_B, marg_B = set(DB.scelta), DB.margine
|
||||
|
||||
print("\n" + "=" * 96)
|
||||
print("VERDETTO — coi criteri dichiarati in testa allo script, calcolati adesso")
|
||||
print("=" * 96)
|
||||
stabile = len(scelte_A) == 1 and len(scelte_B) == 1
|
||||
sopra = bool(DA.margine.min() >= MARGINE_MINIMO and marg_B.min() >= MARGINE_MINIMO)
|
||||
print(f" (i) scelta stabile su notti e fasi : {'SI' if stabile else 'NO'} "
|
||||
f"— notti {sorted(scelte_A)}, fasi {sorted(scelte_B)}")
|
||||
print(f" (ii) margine sempre >= {MARGINE_MINIMO} : {'SI' if sopra else 'NO'} "
|
||||
f"— minimo osservato {min(DA.margine.min(), marg_B.min()):.4f}")
|
||||
if stabile and sopra:
|
||||
print("\n -> il gate (A) HA risoluzione: il fallimento del test e' un fatto sulla PROPOSTA.")
|
||||
return 0
|
||||
print("\n -> il gate (A) NON ha risoluzione. Il verdetto non e' una proprieta' della banda:")
|
||||
print(" e' una proprieta' della notte in cui lo si legge. Come il criterio a ranghi")
|
||||
print(" ritirato il 07/08, va ritirato — congelando il MOTIVO, non l'esito.")
|
||||
print(f" La banda della proposta ({PROPOSTA_BANDA:.0%}) resta la scelta al buio in "
|
||||
f"{quota_proposta:.0%} delle notti: ne' confermata ne' smentita, NON DECIDIBILE cosi'.")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,204 @@
|
||||
#!/usr/bin/env python
|
||||
"""r0828_senza_gtaa.py — cosa cambia nel portafoglio di RICERCA se si toglie GTAA01.
|
||||
|
||||
PERCHE'. Il 28/08 si e' misurato che il gate (A) della banda GTAA01 non ha risoluzione, e che
|
||||
lo sleeve gira su una finestra che rotola da sola (r0828_gtaa_band_phase). Prima di decidere
|
||||
cosa fare del gate conviene sapere quanto pesa lo sleeve: se togliendolo non cambia niente, la
|
||||
domanda sul gate e' accademica.
|
||||
|
||||
⚠️ QUESTO NON E' IL LIBRO LIVE. Il book che gira e' TP01+SKH01 75/25 su Deribit: GTAA01 non c'e'.
|
||||
Qui si guarda il portafoglio a 5 sleeve di `sleeves.active_sleeves` (33/15/12/20/20), che e' la
|
||||
serie su cui stanno quasi tutti i numeri di portafoglio del progetto.
|
||||
|
||||
LE DUE LENTI, ed e' la seconda quella che decide (M6: un diversificatore a BASSO CAGR si giudica
|
||||
a iso-rischio, mai a iso-nozionale; M5: ogni claim "meno drawdown" si testa a iso-rischio prima
|
||||
di crederci, o si sta comprando de-levering e chiamandolo diversificazione):
|
||||
· ISO-NOZIONALE — si toglie GTAA01 e si ridistribuisce il suo 20% agli altri quattro in
|
||||
proporzione. E' cio' che succede materialmente.
|
||||
· ISO-RISCHIO — la stessa serie, riscalata alla volatilita' del portafoglio a 5. Risponde alla
|
||||
domanda giusta: a parita' di rischio preso, quanto rende in piu' o in meno?
|
||||
|
||||
Si stampano FULL e HOLD-OUT (la data e' quella del progetto, `portfolio.HOLDOUT`), il per-anno
|
||||
(M9: un contributo si scompone per anno prima di crederci) e la correlazione di GTAA01 col resto.
|
||||
Verdetto calcolato a runtime coi criteri dichiarati qui sotto.
|
||||
|
||||
CRITERIO, dichiarato prima: GTAA01 "porta qualcosa" se, A ISO-RISCHIO, toglierlo peggiora lo
|
||||
Sharpe FULL di almeno SOGLIA_SHARPE **e** l'hold-out non lo smentisce. Sotto quella soglia il
|
||||
contributo e' dentro lo spread fra le tre baseline che il progetto gia' si porta dietro (0.12 di
|
||||
Sharpe, §2 del CLAUDE.md), quindi non e' distinguibile dalla scelta della lente.
|
||||
|
||||
uv run python scripts/research/r0828_senza_gtaa.py
|
||||
"""
|
||||
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 src.portfolio.portfolio import HOLDOUT, combine_outer, metrics, to_daily, yearly # noqa: E402
|
||||
from src.portfolio.sleeves import active_sleeves # noqa: E402
|
||||
|
||||
SOGLIA_SHARPE = 0.12 # spread fra le tre baseline "libro 75/25" (§2): sotto, non si vede
|
||||
FUORI = "GTAA01_eq_trend"
|
||||
|
||||
|
||||
def serie_sleeve() -> tuple[dict, dict]:
|
||||
cols, w = {}, {}
|
||||
for s in active_sleeves():
|
||||
cols[s.name] = s.daily().dropna()
|
||||
w[s.name] = float(s.weight)
|
||||
return cols, w
|
||||
|
||||
|
||||
def senza(w: dict, fuori: str) -> dict:
|
||||
"""I pesi degli altri, rinormalizzati: il 20% di GTAA01 va agli altri in proporzione."""
|
||||
resto = {k: v for k, v in w.items() if k != fuori}
|
||||
tot = sum(resto.values())
|
||||
return {k: v / tot for k, v in resto.items()}
|
||||
|
||||
|
||||
def riga(nome: str, m: dict) -> str:
|
||||
return (f" {nome:<22} Sh {m['sharpe']:>5.2f} CAGR {m['cagr']*100:>+6.1f}% "
|
||||
f"maxDD {m['maxdd']*100:>5.1f}% vol {m.get('vol', float('nan'))*100:>5.1f}% n {m['n']}")
|
||||
|
||||
|
||||
def con_vol(s: pd.Series) -> dict:
|
||||
m = metrics(s)
|
||||
m["vol"] = float(np.asarray(s.dropna().values, float).std() * np.sqrt(365.0))
|
||||
return m
|
||||
|
||||
|
||||
def main() -> int:
|
||||
print(__doc__.split("\n\n")[0])
|
||||
cols, w5 = serie_sleeve()
|
||||
if FUORI not in cols:
|
||||
print(f"!! {FUORI} non e' fra gli sleeve attivi: niente da togliere")
|
||||
return 2
|
||||
w4 = senza(w5, FUORI)
|
||||
cols4 = {k: v for k, v in cols.items() if k != FUORI}
|
||||
|
||||
print(f"\n pesi CON : " + " ".join(f"{k.split('_')[0]} {v:.0%}" for k, v in w5.items()))
|
||||
print(f" pesi SENZA: " + " ".join(f"{k.split('_')[0]} {v:.0%}" for k, v in w4.items()))
|
||||
|
||||
con = combine_outer(cols, w5)
|
||||
sen = combine_outer(cols4, w4)
|
||||
# allineate sullo STESSO calendario: senza questo si confronterebbero due finestre diverse
|
||||
J = pd.concat({"con": con, "senza": sen}, axis=1, join="inner").dropna()
|
||||
con, sen = J["con"], J["senza"]
|
||||
|
||||
m_con, m_sen = con_vol(con), con_vol(sen)
|
||||
k = m_con["vol"] / m_sen["vol"] if m_sen["vol"] > 0 else 1.0
|
||||
sen_iso = sen * k
|
||||
m_iso = con_vol(sen_iso)
|
||||
|
||||
print("\n" + "=" * 96)
|
||||
print(f" FULL ({con.index[0].date()} -> {con.index[-1].date()})")
|
||||
print("=" * 96)
|
||||
print(riga("CON GTAA01", m_con))
|
||||
print(riga("SENZA (iso-nozionale)", m_sen))
|
||||
print(riga(f"SENZA (iso-rischio ×{k:.2f})", m_iso))
|
||||
d_full = m_con["sharpe"] - m_iso["sharpe"]
|
||||
print(f"\n Δ Sharpe a iso-rischio (con − senza): {d_full:+.3f} "
|
||||
f"[soglia dichiarata {SOGLIA_SHARPE}]")
|
||||
print(f" Δ maxDD a iso-rischio: {(m_con['maxdd']-m_iso['maxdd'])*100:+.2f} pp")
|
||||
|
||||
print("\n" + "=" * 96)
|
||||
print(f" HOLD-OUT ({HOLDOUT.date()}+)")
|
||||
print("=" * 96)
|
||||
hc, hs = con[con.index >= HOLDOUT], sen[sen.index >= HOLDOUT]
|
||||
mhc, mhs = con_vol(hc), con_vol(hs)
|
||||
kh = mhc["vol"] / mhs["vol"] if mhs["vol"] > 0 else 1.0
|
||||
mhi = con_vol(hs * kh)
|
||||
print(riga("CON GTAA01", mhc))
|
||||
print(riga(f"SENZA (iso-rischio ×{kh:.2f})", mhi))
|
||||
d_hold = mhc["sharpe"] - mhi["sharpe"]
|
||||
print(f"\n Δ Sharpe a iso-rischio: {d_hold:+.3f}")
|
||||
|
||||
print("\n" + "=" * 96)
|
||||
print(" PER ANNO — un contributo positivo si scompone prima di crederci (M9)")
|
||||
print("=" * 96)
|
||||
yc, yi = yearly(con), yearly(sen * k)
|
||||
print(f" {'anno':<6} {'CON ret':>9} {'SENZA ret':>10} {'Δ ret':>8} {'CON DD':>7} {'SENZA DD':>9}")
|
||||
pos = 0
|
||||
for a in sorted(set(yc) & set(yi)):
|
||||
d = yc[a]["ret"] - yi[a]["ret"]
|
||||
pos += d > 0
|
||||
print(f" {a:<6} {yc[a]['ret']*100:>+8.1f}% {yi[a]['ret']*100:>+9.1f}% {d*100:>+7.1f}pp "
|
||||
f"{yc[a]['dd']*100:>6.1f}% {yi[a]['dd']*100:>8.1f}%")
|
||||
n_anni = len(set(yc) & set(yi))
|
||||
print(f"\n anni in cui GTAA01 aiuta: {pos}/{n_anni}")
|
||||
|
||||
print("\n" + "=" * 96)
|
||||
print(" CORRELAZIONE — quanto e' davvero 'altro' rispetto al resto")
|
||||
print("=" * 96)
|
||||
g = cols[FUORI]
|
||||
altro = combine_outer(cols4, w4)
|
||||
Jg = pd.concat({"g": g, "altro": altro}, axis=1, join="inner").dropna()
|
||||
print(f" GTAA01 vs resto del portafoglio: corr {Jg['g'].corr(Jg['altro']):+.3f} "
|
||||
f"({len(Jg)} giorni in comune)")
|
||||
print(f" GTAA01 standalone: " + riga("", con_vol(g)).strip())
|
||||
|
||||
# ---------------------------------------------------------------- la banda di FASE sul Δ
|
||||
print("\n" + "=" * 96)
|
||||
print(" LA FASCIA DI FASE SUL Δ — il Δ e' una misura, o una proprieta' della notte?")
|
||||
print("=" * 96)
|
||||
print(" GTAA01 ribilancia su `i % 5` a partire dalla PRIMA barra del file, e la prima barra")
|
||||
print(" di SPY scivola ogni notte (finestra IB rotolante a 30 anni: r0828_gtaa_band_phase).")
|
||||
print(" Quindi il Δ qui sopra va letto accanto alla sua escursione sulle 5 fasi.")
|
||||
sys.path.insert(0, str(ROOT / "scripts" / "research"))
|
||||
try:
|
||||
from r0726_loo_deluck import gtaa01_at # replica ESATTA dello sleeve, per fase
|
||||
except Exception as e: # noqa: BLE001
|
||||
print(f" non misurabile qui: {type(e).__name__}: {e}")
|
||||
gtaa01_at = None
|
||||
if gtaa01_at is not None:
|
||||
base = gtaa01_at(0)
|
||||
j0 = pd.concat({"a": base, "b": cols[FUORI]}, axis=1, join="inner").dropna()
|
||||
d0 = float((j0["a"] - j0["b"]).abs().max())
|
||||
print(f"\n [SANITY] fase 0 == sleeve di produzione: max|Δ| = {d0:.2e} "
|
||||
f"-> {'OK' if d0 < 1e-12 else 'DIVERGE (i numeri sotto non valgono)'}")
|
||||
deltas = []
|
||||
for ph in range(5):
|
||||
c = dict(cols); c[FUORI] = gtaa01_at(ph)
|
||||
cc = combine_outer(c, w5)
|
||||
Jp = pd.concat({"con": cc, "senza": sen}, axis=1, join="inner").dropna()
|
||||
mc = con_vol(Jp["con"])
|
||||
ms = con_vol(Jp["senza"])
|
||||
kk = mc["vol"] / ms["vol"] if ms["vol"] > 0 else 1.0
|
||||
d = mc["sharpe"] - con_vol(Jp["senza"] * kk)["sharpe"]
|
||||
deltas.append(d)
|
||||
print(f" fase {ph}: Sharpe CON {mc['sharpe']:.3f} Δ a iso-rischio {d:+.3f}"
|
||||
+ (" <- la fase che gira stanotte" if ph == 0 else ""))
|
||||
lo, hi = min(deltas), max(deltas)
|
||||
print(f"\n Δ su 5 fasi: da {lo:+.3f} a {hi:+.3f} (ampiezza {hi-lo:.3f}, "
|
||||
f"mediana {float(np.median(deltas)):+.3f})")
|
||||
sopra = sum(d >= SOGLIA_SHARPE for d in deltas)
|
||||
print(f" fasi in cui il Δ supera la soglia {SOGLIA_SHARPE}: {sopra}/5")
|
||||
if 0 < sopra < 5:
|
||||
print(" ⚠️ IL VERDETTO DIPENDE DALLA FASE: non e' una proprieta' di GTAA01,")
|
||||
print(" e' una proprieta' della notte in cui lo si legge.")
|
||||
|
||||
print("\n" + "=" * 96)
|
||||
print(" VERDETTO — criterio dichiarato in testa, calcolato adesso")
|
||||
print("=" * 96)
|
||||
porta = d_full >= SOGLIA_SHARPE and d_hold > -SOGLIA_SHARPE
|
||||
print(f" Δ Sharpe FULL a iso-rischio {d_full:+.3f} >= {SOGLIA_SHARPE}? "
|
||||
f"{'SI' if d_full >= SOGLIA_SHARPE else 'NO'}")
|
||||
print(f" l'hold-out non lo smentisce (Δ {d_hold:+.3f} > {-SOGLIA_SHARPE})? "
|
||||
f"{'SI' if d_hold > -SOGLIA_SHARPE else 'NO'}")
|
||||
if porta:
|
||||
print("\n -> GTAA01 PORTA: toglierlo costa piu' dello spread fra le baseline.")
|
||||
return 0
|
||||
print("\n -> il contributo di GTAA01 NON si distingue dalla scelta della lente.")
|
||||
print(" Non e' 'GTAA01 e' inutile': e' 'con questi dati non si vede'. E cio' che")
|
||||
print(" non si vede non giustifica di tenere in piedi un gate che non decide.")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,303 @@
|
||||
#!/usr/bin/env python
|
||||
"""r0830_usde_quota.py — la decisione di QUOTA USDE, dovuta lunedi' 31/08. SOLA LETTURA.
|
||||
|
||||
COSA DECIDE E COSA NO. Il gate USDE-01 e' CHIUSO IDONEO (28/08): la pista e' aperta. Resta
|
||||
da scegliere *quanto* del conto tenere in USDE. Questo script NON sceglie: mette accanto ai
|
||||
criteri candidati la quota che ciascuno implica e il suo prezzo, perche' la scelta e'
|
||||
dell'operatore (N4: il rischio emittente non e' diversificabile dagli sleeve).
|
||||
|
||||
IL RISULTATO STRUTTURALE, DICHIARATO PRIMA DI GUARDARE I NUMERI (N6/P6). Il valore atteso
|
||||
e' LINEARE in q: EV(q) = q*E*(apr - hazard*...). Un massimo lineare sta sempre in un ANGOLO
|
||||
(0% o 100%). ⇒ **l'EV non puo' scegliere una quota interna**: puo' solo dire se il segno e'
|
||||
positivo. La quota interna nasce SOLO da un criterio sulla CODA, e quel criterio va
|
||||
dichiarato, non ottimizzato (M8: un argmax dentro un plateau non e' una decisione).
|
||||
|
||||
Quindi la struttura e':
|
||||
(A) il rendimento osservato, CON LA SUA BANDA e il suo n — un pagamento non e' un tasso;
|
||||
(B) l'hazard di PAREGGIO: sopra quello ogni quota e' EV-negativa. Criterio senza criterio;
|
||||
(C) il rischio MARGINALE, dato che il 100%-Deribit e' gia' stato accettato il 26/07;
|
||||
(D) i vincoli operativi: il haircut morde? il depeg costa churn?
|
||||
(E) il menu dei criteri di coda, ciascuno con la quota che implica e cosa costa in resa.
|
||||
|
||||
LENTI (N8: quando le leve hanno ordini di grandezza diversi, dirlo). La resa si legge in DUE
|
||||
monete e NON vanno sommate: (1) CASSA, gli euro veri all'anno; (2) DRIFT-EQUIVALENTE, via
|
||||
l'equivalenza registrata in CLAUDE.md 100 EUR/mese == +4,07%/anno di drift, che e' una
|
||||
capitalizzazione a 10 anni, non un flusso.
|
||||
|
||||
SEMPLIFICAZIONE DICHIARATA: EUR e USD trattati 1:1. A ~1,08 sposta i "mesi di versamento" di
|
||||
~8% (su 2,0 mesi = 0,16 mesi): non cambia nessuna soglia di questa tabella.
|
||||
|
||||
uv run python scripts/research/r0830_usde_quota.py
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from src.live import usde as U # noqa: E402
|
||||
|
||||
# P1: il tasso puntuale si DERIVA dal sorvegliante che gira, non si ridichiara qui. Il bootstrap
|
||||
# (banda) resta locale perche' usde_watch non lo fa — e i due denominatori NON coincidono:
|
||||
# rendimento() divide per il TEMPO VERO fra le letture, questo per il NUMERO di finestre.
|
||||
# La divergenza si stampa invece di essere appianata (P12).
|
||||
_spec = importlib.util.spec_from_file_location(
|
||||
"usde_watch", ROOT / "scripts" / "live" / "usde_watch.py")
|
||||
_uw = importlib.util.module_from_spec(_spec)
|
||||
sys.modules["usde_watch"] = _uw
|
||||
_spec.loader.exec_module(_uw)
|
||||
|
||||
WATCH = ROOT / "data" / "live" / "usde_watch.jsonl"
|
||||
|
||||
# --- stato del conto, letto dal venue il 2026-08-30T17:05Z e CONGELATO qui per riproducibilita' ---
|
||||
EQ_USDC = 1563.66151017
|
||||
AVAIL_USDC = 1552.28862113 # -> margine in uso = equity - available
|
||||
EQ_USDE = 500.175691
|
||||
GROSS_NOTIONAL = 355.327065 + 213.317387
|
||||
|
||||
# --- framework di rischio EREDITATO dalla decisione 26/07, non re-inventato ---
|
||||
# "100% Deribit fino a $20k: accettato P(perso tutto) 10/18/34/64% a p=0,5/1/2/5%"
|
||||
# riprodotto esattamente da P = 1-(1-p)^T con T=20. Il test lo verifica.
|
||||
ORIZZONTE_ANNI = 20
|
||||
P_DERIBIT = [0.005, 0.01, 0.02, 0.05]
|
||||
|
||||
# --- piano di versamento: la leva CERTA (N1). Baseline dichiarata in CLAUDE.md ---
|
||||
VERSAMENTO_BASE = 250.0 # EUR/mese, piano corrente
|
||||
VERSAMENTO_ALT = 500.0 # EUR/mese, l'alternativa gia' misurata
|
||||
EUR_PER_100_DRIFT = 4.07 / 100.0 # 100 EUR/mese == +4,07%/anno di drift
|
||||
|
||||
# --- ancore del libro, dalla banda onesta di CLAUDE.md §2 ---
|
||||
MAXDD_BOOK_HI = 0.0794 # maxDD FULL book, estremo alto della banda [5,99-7,94]
|
||||
MARGINE_PERP = 0.02 # tasso di margine iniziale osservato (verificato sotto)
|
||||
|
||||
SPREAD_BPS = 3.0 # spot USDE_USDC, osservato sul venue il 26/08 (fee 0)
|
||||
N_BOOT = 200_000 # M23: la risoluzione del MC si dichiara
|
||||
RNG = np.random.default_rng(20260830)
|
||||
|
||||
|
||||
def finestre(records: list[dict]) -> list[float]:
|
||||
"""I reward per finestra, in USDE. Uno zero E' un'osservazione (P5), non un buco."""
|
||||
return [float(r["reward_stimato"]) for r in records
|
||||
if r.get("reward_stimato") is not None and not r.get("con_trade")]
|
||||
|
||||
|
||||
def apr_banda(rew: list[float], base: float) -> dict:
|
||||
"""APR puntuale e banda bootstrap sulle finestre. Con n piccolo la banda E' il risultato."""
|
||||
n = len(rew)
|
||||
if n == 0:
|
||||
return dict(apr=None, lo=None, hi=None, n=0)
|
||||
a = np.array(rew, dtype=float)
|
||||
punt = a.mean() / base * 365.0
|
||||
draws = RNG.choice(a, size=(N_BOOT, n), replace=True).mean(axis=1) / base * 365.0
|
||||
lo, hi = np.percentile(draws, [2.5, 97.5])
|
||||
return dict(apr=punt, lo=float(lo), hi=float(hi), n=n,
|
||||
per_finestra=[x / base * 365.0 for x in rew])
|
||||
|
||||
|
||||
def p_rovina(p: float, anni: int = ORIZZONTE_ANNI) -> float:
|
||||
return 1.0 - (1.0 - p) ** anni
|
||||
|
||||
|
||||
def main() -> int:
|
||||
recs = [json.loads(l) for l in WATCH.read_text().splitlines() if l.strip()]
|
||||
c = U.cfg()
|
||||
px = float(recs[-1]["px"])
|
||||
usde_usd, _ = U.valuta(EQ_USDE, px)
|
||||
E = EQ_USDC + usde_usd
|
||||
q0 = usde_usd / E
|
||||
|
||||
rew = finestre(recs)
|
||||
R = apr_banda(rew, EQ_USDE)
|
||||
UFF = _uw.rendimento(recs) # il numero che gira, derivato
|
||||
R["apr_uff"] = UFF["apr"]
|
||||
R["giorni_uff"] = UFF["giorni"]
|
||||
|
||||
print("=" * 79)
|
||||
print(" ANALISI QUOTA USDE — decisione dovuta 2026-08-31")
|
||||
print(f" config: {c['fonte']} · stato conto congelato al 2026-08-30T17:05Z")
|
||||
print("=" * 79)
|
||||
|
||||
# ---------------------------------------------------------------- (A) il rendimento
|
||||
print("\n(A) IL RENDIMENTO, COL SUO n")
|
||||
print(f" finestre misurabili : {R['n']} (pagate {sum(1 for x in rew if x > 0)})")
|
||||
print(" per finestra : " + ", ".join(f"{x*100:.2f}%" for x in R["per_finestra"]))
|
||||
print(f" APR di usde_watch : {R['apr_uff']*100:.2f}% annuo"
|
||||
f" (denominatore = tempo vero, {R['giorni_uff']:.2f} giorni) <- IL NUMERO CHE GIRA")
|
||||
print(f" APR per-finestra : {R['apr']*100:.2f}% annuo"
|
||||
f" (denominatore = n finestre, {R['n']}.00 giorni)")
|
||||
print(f" divergenza : {abs(R['apr_uff']-R['apr'])*100:.2f} punti — i due"
|
||||
f" denominatori differiscono di {R['n']-R['giorni_uff']:.2f} giorni. Dichiarata (P12),")
|
||||
print(" non appianata: sotto si usa il puntuale di usde_watch.")
|
||||
print(f" banda bootstrap 95% : [{R['lo']*100:.2f}% - {R['hi']*100:.2f}%]"
|
||||
f" (n={R['n']}, {N_BOOT:,} ricampionamenti)")
|
||||
APR = R["apr_uff"]
|
||||
larghezza = R["hi"] - R["lo"]
|
||||
print(f" ⚠️ la banda e' larga {larghezza*100:.2f} punti su un livello di "
|
||||
f"{R['apr']*100:.2f}% (per-finestra): a n={R['n']} il tasso NON e' misurato, e' abbozzato.")
|
||||
|
||||
# ---------------------------------------------------------------- (B) hazard di pareggio
|
||||
print("\n(B) L'HAZARD DI PAREGGIO — l'unico criterio che non richiede un criterio")
|
||||
print(f" Perdita totale non recuperabile, resa {APR*100:.2f}%/anno.")
|
||||
print(f" ⇒ hazard annuo di pareggio = APR = {APR*100:.2f}%/anno")
|
||||
print(f" (banda: {R['lo']*100:.2f}% - {R['hi']*100:.2f}%)")
|
||||
print(f" ⇒ recupero di una perdita totale con la sola resa: {1/APR:.1f} anni"
|
||||
f" — INDIPENDENTE dalla quota.")
|
||||
print(f" Letto sul verso della decisione: la quota e' EV-positiva SE E SOLO SE si crede")
|
||||
print(f" che Ethena abbia meno del {APR*100:.1f}% annuo di probabilita' di evento")
|
||||
print(f" catastrofico. Quel numero e' ASSUNTO, non stimato — come il p di Deribit (26/07).")
|
||||
|
||||
# ---------------------------------------------------------------- (C) rischio marginale
|
||||
print("\n(C) IL RISCHIO MARGINALE, dato il 100%-Deribit gia' accettato (26/07)")
|
||||
print(f" Framework ereditato: P = 1-(1-p)^{ORIZZONTE_ANNI}. Riproduzione della riga 26/07:")
|
||||
riga = " p annuo ".ljust(14) + "".join(f"{p*100:>7.1f}%" for p in P_DERIBIT)
|
||||
print(riga)
|
||||
print(" P(perso tutto)".ljust(14) + "".join(f"{p_rovina(p)*100:>7.0f}%" for p in P_DERIBIT))
|
||||
print(" atteso 26/07 " + "".join(f"{v:>7}" for v in ("10%", "18%", "34%", "64%")))
|
||||
ok_repro = all(abs(p_rovina(p) * 100 - v) < 1.5
|
||||
for p, v in zip(P_DERIBIT, (10, 18, 34, 64)))
|
||||
print(f" riproduzione : {'OK' if ok_repro else 'DIVERGE'} (tolleranza 1,5 punti)")
|
||||
print(" ⇒ L'USDE fa perdere soldi SOLO nel mondo in cui Ethena salta E Deribit sopravvive.")
|
||||
print(" Nel mondo in cui salta Deribit, USDC e USDE si perdono insieme: la quota e'")
|
||||
print(" irrilevante. Il rischio AGGIUNTO dalla quota e' quindi di SECONDO ordine")
|
||||
print(" rispetto a quello gia' accettato — ma e' un TERZO strato sullo STESSO conto,")
|
||||
print(" e N4 dice che un rischio di venue si compra con un secondo CONTO, non")
|
||||
print(" aumentando l'esposizione al primo. Le due letture non si annullano: la prima")
|
||||
print(" dice che la quota costa poco, la seconda che non e' li' che si compra sicurezza.")
|
||||
|
||||
# ---------------------------------------------------------------- (D) vincoli operativi
|
||||
print("\n(D) I VINCOLI OPERATIVI — il haircut morde? il depeg costa?")
|
||||
margine_uso = EQ_USDC - AVAIL_USDC
|
||||
tasso_margine = margine_uso / GROSS_NOTIONAL
|
||||
print(f" nozionale lordo : ${GROSS_NOTIONAL:,.2f}")
|
||||
print(f" margine in uso : ${margine_uso:,.2f} ⇒ tasso {tasso_margine*100:.2f}%")
|
||||
print(f" tetto del libro : leva lorda 1,00x ⇒ nozionale max ${E:,.0f}"
|
||||
f" ⇒ margine max ~${E*tasso_margine:,.0f}")
|
||||
print(f" haircut USDE {c['haircut']:.0%} : il {1-c['haircut']:.0%} dell'USDE fa margine")
|
||||
print("\n quota USDC USDE margine utilizzabile serve al tetto morde?")
|
||||
binding = []
|
||||
for q in (q0, 0.25, 0.50, 0.75, 0.90):
|
||||
usde_q, usdc_q = E * q, E * (1 - q)
|
||||
util = usdc_q + usde_q * (1 - c["haircut"])
|
||||
serve = E * tasso_margine
|
||||
morde = util < serve
|
||||
binding.append(morde)
|
||||
print(f" {q:5.1%} ${usdc_q:9,.0f} ${usde_q:9,.0f} ${util:14,.0f}"
|
||||
f" ${serve:12,.0f} {'SI' if morde else 'no'}")
|
||||
print(f" ⇒ il haircut NON morde a nessuna quota testata: il libro gira al"
|
||||
f" {tasso_margine*100:.1f}% di margine,")
|
||||
print(" tre ordini di grandezza sotto il collaterale. D5: buco quantificato e innocuo.")
|
||||
depeg_eq = (1.0 - c["depeg_crit"])
|
||||
print(f"\n churn da depeg: a soglia critica {c['depeg_crit']} l'equity cala di"
|
||||
f" {depeg_eq:.0%}*q,")
|
||||
for q in (q0, 0.50):
|
||||
print(f" a quota {q:.1%} ⇒ equity {depeg_eq*q*100:.2f}% ⇒ target di posizione"
|
||||
f" -{depeg_eq*q*100:.2f}% (~${E*depeg_eq*q*1.0:,.0f} di nozionale da girare)")
|
||||
print(" ⇒ il libro de-risca mentre l'USDE e' a sconto e ri-carica al ritorno del peg:")
|
||||
print(" compra alto e vende basso, ma per importi di secondo ordine. Non e' il vincolo.")
|
||||
|
||||
# ---------------------------------------------------------------- (E) il menu dei criteri
|
||||
print("\n(E) IL MENU DEI CRITERI DI CODA — ciascuno con la quota che implica e il suo prezzo")
|
||||
print(" (l'EV e' lineare in q: NON produce un ottimo interno. Questi si', ma sono")
|
||||
print(" scelte di tolleranza, non misure.)")
|
||||
criteri = [
|
||||
("C0 status quo, nessuna decisione",
|
||||
q0, "la quota di oggi, arrivata li' per la taglia del test di eligibilita'"),
|
||||
("C1 <= maxDD del libro (7,94%)",
|
||||
MAXDD_BOOK_HI, "un evento emittente non deve superare cio' che la strategia gia' fa"),
|
||||
("C2 <= 6 mesi di versamento",
|
||||
6 * VERSAMENTO_BASE / E, f"a {VERSAMENTO_BASE:.0f} EUR/mese: il piano arretra <= mezzo anno"),
|
||||
("C3 <= 12 mesi di versamento",
|
||||
12 * VERSAMENTO_BASE / E, f"a {VERSAMENTO_BASE:.0f} EUR/mese: il piano arretra <= un anno"),
|
||||
("C4 tetto di ALLERTA di config",
|
||||
c["quota_max_frac"], "non e' un criterio: e' la soglia oltre cui il watch grida"),
|
||||
]
|
||||
print(f"\n {'criterio':38s} {'quota':>7s} {'USDE $':>9s} {'resa $/a':>9s}"
|
||||
f" {'drift bk':>9s} {'== EUR/m':>9s} {'perdita':>9s} {'mesi vers':>9s}")
|
||||
for nome, q, _ in criteri:
|
||||
q = min(max(q, 0.0), 1.0)
|
||||
usde_q = E * q
|
||||
resa = usde_q * APR
|
||||
drift = q * APR
|
||||
eur_mese = drift / EUR_PER_100_DRIFT * 100.0
|
||||
mesi = usde_q / VERSAMENTO_BASE
|
||||
print(f" {nome:38s} {q:6.1%} {usde_q:9,.0f} {resa:9,.2f}"
|
||||
f" {drift*100:8.2f}% {eur_mese:9,.0f} {usde_q:9,.0f} {mesi:9.1f}")
|
||||
print("\n note sulle colonne (N8: due monete, non si sommano):")
|
||||
print(" · 'resa $/a' = CASSA vera all'anno, alla taglia di oggi")
|
||||
print(" · '== EUR/m' = quanto varrebbe in VERSAMENTO l'equivalente di drift, lente 10 anni")
|
||||
print(" · 'mesi vers' = quanti mesi di bonifico brucia una perdita TOTALE")
|
||||
|
||||
# ---------------------------------------------------------------- (F) attesa e reversibilita'
|
||||
print("\n(F) COSA COSTA ASPETTARE — N10: una data si giustifica col COSTO della misura,")
|
||||
print(" non con la sua difficolta' percepita.")
|
||||
print(f" La conversione e' su spot {U.SPOT}: fee 0, spread osservato ~{SPREAD_BPS:.0f} bps")
|
||||
print(f" (26/08) ⇒ andata+ritorno ~{2*SPREAD_BPS:.0f} bps. La decisione e' REVERSIBILE"
|
||||
" a costo quasi nullo:")
|
||||
print(" non e' una porta a senso unico, e va decisa come tale.")
|
||||
print(f"\n {'attendere':>10s} {'finestre':>9s} {'costo andata/ritorno':>21s}"
|
||||
f" {'resa persa':>11s} {'costo totale':>13s}")
|
||||
for sett, q_target in ((0, 0.50), (2, 0.50), (4, 0.50), (8, 0.50)):
|
||||
incremento = E * q_target - usde_usd
|
||||
giorni = sett * 7
|
||||
persa = incremento * APR * giorni / 365.0
|
||||
rt = incremento * 2 * SPREAD_BPS / 10_000.0
|
||||
n_fin = R["n"] + giorni
|
||||
print(f" {sett:>7d} sett {n_fin:>9d} {rt:>20,.2f}$ {persa:>10,.2f}$"
|
||||
f" {rt+persa:>12,.2f}$")
|
||||
print(f"\n ⇒ portare la quota da {q0:.1%} a 50% fra QUATTRO settimane invece che domani")
|
||||
inc = E * 0.50 - usde_usd
|
||||
costo_4 = inc * APR * 28 / 365.0
|
||||
print(f" costa ${costo_4:,.2f} di resa non incassata, e compra"
|
||||
f" {R['n']+28}/{R['n']} = {(R['n']+28)/R['n']:.0f}x le finestre.")
|
||||
print(" A n=32 la banda si stringe di ~sqrt(8) ≈ 2,8x: da ~3,4 punti a ~1,2.")
|
||||
print(f" ${costo_4:,.2f} per sapere se il tasso e' 1,1% o 4,5% e' il miglior rapporto")
|
||||
print(" misura/costo che il progetto abbia incontrato (cfr. i due gate d'attesa da mesi")
|
||||
print(" eliminati in una sera perche' nessuno aveva contato i secondi, N10).")
|
||||
|
||||
# ---------------------------------------------------------------- verdetto a runtime (N11)
|
||||
print("\n" + "=" * 79)
|
||||
print(" VERDETTO calcolato a runtime")
|
||||
print("=" * 79)
|
||||
eur_mese_q0 = q0 * APR / EUR_PER_100_DRIFT * 100.0
|
||||
cassa_q0 = usde_usd * APR
|
||||
mesi_q0 = usde_usd / VERSAMENTO_BASE
|
||||
inc50 = E * 0.50 - usde_usd
|
||||
costo_attesa_4s = inc50 * APR * 28 / 365.0
|
||||
verdetti = []
|
||||
if R["n"] < 8:
|
||||
verdetti.append(f"RISOLUZIONE INSUFFICIENTE PER ALZARE: n={R['n']} finestre, banda"
|
||||
f" [{R['lo']*100:.2f}%-{R['hi']*100:.2f}%] larga {larghezza*100:.1f} punti."
|
||||
f" Alzare la quota ora e' comprare un tasso che non e' ancora misurato.")
|
||||
if not any(binding):
|
||||
verdetti.append("NESSUN VINCOLO OPERATIVO: il haircut non morde a nessuna quota"
|
||||
" testata; il vincolo non e' il margine.")
|
||||
if cassa_q0 < VERSAMENTO_BASE:
|
||||
verdetti.append(f"SCALA: la resa alla quota di oggi vale ${cassa_q0:,.0f}/anno,"
|
||||
f" cioe' meno di UN mese di versamento ({VERSAMENTO_BASE:.0f})."
|
||||
f" La leva binding resta il bonifico (N1/N3), non la quota.")
|
||||
if MAXDD_BOOK_HI < q0:
|
||||
verdetti.append(f"ASIMMETRIA: gia' alla quota di oggi ({q0:.1%}) l'evento emittente"
|
||||
f" vale {q0/MAXDD_BOOK_HI:.1f}x il maxDD dell'intero libro"
|
||||
f" ({MAXDD_BOOK_HI:.2%}), che e' il rischio che il progetto"
|
||||
f" ha passato due anni a limare.")
|
||||
verdetti.append(
|
||||
f"REVERSIBILE E QUASI GRATIS DA RIMANDARE: alzare a 50% fra 4 settimane invece che"
|
||||
f" domani costa ${costo_attesa_4s:,.2f} di resa e {2*SPREAD_BPS:.0f} bps di andata/ritorno,"
|
||||
f" e porta il campione da {R['n']} a {R['n']+28} finestre.")
|
||||
for v in verdetti:
|
||||
print(f" · {v}")
|
||||
print(f"\n Quota di oggi {q0:.1%} = ${usde_usd:,.0f} · resa ${cassa_q0:,.0f}/anno"
|
||||
f" · == {eur_mese_q0:,.0f} EUR/mese di drift · {mesi_q0:.1f} mesi di bonifico a rischio")
|
||||
print(" La SCELTA resta dell'operatore: questo script mette il prezzo accanto a ogni")
|
||||
print(" criterio, non ne sceglie uno (N4, e la colonna 'cosa la riapre' della §3).")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env python
|
||||
"""r0831_margini_conto.py — riconcilia la pagina margini del conto. Riproduce due numeri.
|
||||
|
||||
PERCHE'. Due domande erano rimaste aperte e nessuna era rispondibile dal gateway (debito #11:
|
||||
`account_summary` filtrato a 8 campi, niente initial_margin, `extended` ignorato):
|
||||
|
||||
1. l'HAIRCUT sull'USDe: la KB ufficiale dice 5%, `config/live.json` diceva 10% "verificato sul
|
||||
venue il 26/08" senza traccia di come;
|
||||
2. quale MODELLO DI MARGINE e' attivo — che nessuno aveva mai chiesto.
|
||||
|
||||
Lo screenshot della pagina `Standard Margin` (2026-08-31 07:50:30 UTC, fornito dall'operatore) le
|
||||
chiude entrambe. Qui la riconciliazione, perche' un numero pubblicato deve avere uno script che lo
|
||||
riproduce (N11) e perche' l'haircut non si legge nella pagina: si RICAVA per differenza fra due
|
||||
righe, ed e' un conto che va mostrato invece che asserito.
|
||||
|
||||
uv run python scripts/research/r0831_margini_conto.py
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
# --- dalla schermata (2026-08-31 07:50:30 UTC) ---
|
||||
SEG_USDC = 1400.86 # S:SM (ATTIVO) -> riga USDC, Available Balance. USDE NON e' in tabella.
|
||||
CROSS_XSM = 2011.73 # X:SM -> riga CROSS, Available Balance
|
||||
PX_INDICE = 0.9998 # prezzo mostrato in cima alla pagina
|
||||
|
||||
# --- dal conto, letti via gateway alle ~07:55 dello stesso giorno ---
|
||||
USDC_EQ = 1412.281074
|
||||
USDE_EQ = 643.175691
|
||||
IM_MIS = 11.5530 # riservato in USDC = equity - available (misurato)
|
||||
ETH_DUST_USD = 0.000078 * 2500 # riga ETH della schermata, ~$0.20
|
||||
|
||||
TOLL = 2.0 # $: sotto questo scarto due ipotesi non si distinguono
|
||||
|
||||
|
||||
def haircut_implicito(cross: float, usdc: float, eth_dust: float, im: float,
|
||||
usde: float, px: float) -> tuple[float, float]:
|
||||
"""PURA. -> (contributo dell'USDE al cross in USD, haircut implicito).
|
||||
|
||||
L'haircut non e' scritto da nessuna parte nella pagina: si ricava perche' il CROSS conta
|
||||
l'USDE scontato e il SEGREGATO non lo conta affatto. La differenza fra le due righe, tolto
|
||||
cio' che e' comune, E' il contributo dell'USDE."""
|
||||
contributo = cross - (usdc + eth_dust - im)
|
||||
return contributo, 1.0 - contributo / (usde * px)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
print("=" * 78)
|
||||
print(" PAGINA MARGINI — riconciliazione (screenshot 2026-08-31 07:50:30Z)")
|
||||
print("=" * 78)
|
||||
|
||||
atteso_seg = USDC_EQ - IM_MIS
|
||||
print(f"\n [1] S:SM, il modello ATTIVO — solo il silo USDC")
|
||||
print(f" USDC equity {USDC_EQ:,.2f} - IM {IM_MIS:,.2f} = {atteso_seg:,.2f}"
|
||||
f" schermata {SEG_USDC:,.2f} scarto ${abs(atteso_seg-SEG_USDC):.2f}")
|
||||
print(f" → l'USDE non compare in questa tabella: nel modello attivo NON fa margine.")
|
||||
|
||||
contrib, h = haircut_implicito(CROSS_XSM, USDC_EQ, ETH_DUST_USD, IM_MIS, USDE_EQ, PX_INDICE)
|
||||
print(f"\n [2] X:SM — l'haircut si RICAVA per differenza")
|
||||
print(f" contributo USDE al cross = ${contrib:,.2f} su ${USDE_EQ*PX_INDICE:,.2f} di valore")
|
||||
print(f" → haircut implicito {h:.4%}")
|
||||
for cand, nome in ((0.05, "KB ufficiale"), (0.10, "config al 30/08")):
|
||||
att = USDE_EQ * PX_INDICE * (1 - cand)
|
||||
ok = abs(att - contrib) < TOLL
|
||||
print(f" {cand:.0%} ({nome:16s}) → atteso ${att:,.2f}, scarto ${abs(att-contrib):6.2f}"
|
||||
f" {'TORNA' if ok else 'NON torna'}")
|
||||
|
||||
verdetto_h = "5%" if abs(USDE_EQ*PX_INDICE*0.95 - contrib) < TOLL else "indeterminato"
|
||||
delta_modelli = CROSS_XSM - SEG_USDC
|
||||
print(f"\n VERDETTO (calcolato a runtime, N11):")
|
||||
print(f" haircut USDe = {verdetto_h} → `config/live.json` diceva 10%: SBAGLIATO")
|
||||
print(f" modello attivo = SEGREGATO (S:SM) → l'USDE non e' collaterale del book")
|
||||
print(f" passare a X:SM aggiungerebbe ${delta_modelli:,.2f} di margine utilizzabile")
|
||||
print(f"\n{'=' * 78}")
|
||||
live()
|
||||
return 0
|
||||
|
||||
|
||||
# --- 2026-08-31 08:34Z: X:SM e' ATTIVO. La pagina non serve piu': si RICOSTRUISCE. --------------
|
||||
#
|
||||
# L'operatore ha incollato la riga CROSS a X:SM attivo: Available $2.010,81, IM 2,15%, MM 0,37%.
|
||||
# Il gateway, letto allo stesso minuto, dice `available_funds` = 2010.82299383 — **scarto $0,01**.
|
||||
# ⇒ la riga CROSS della pagina E' `available_funds`, che leggiamo da soli. Lo screenshot che ieri
|
||||
# era l'unica fonte per l'haircut e per il modello attivo NON e' piu' necessario a nulla.
|
||||
PAG_AVAIL, PAG_IM_PCT, PAG_MM_PCT = 2010.81, 0.0215, 0.0037 # incollati dall'operatore, 08:34Z
|
||||
GW_AVAIL, GW_MB = 2010.82299383, 2055.07348077 # gateway, stesso minuto
|
||||
GW_USDC_EQ, GW_USDE = 1400.70809113, 654.175691
|
||||
XPM_AVAIL = 1935.14 # riga X:PM della schermata delle 07:50 (proiezione, modello inattivo)
|
||||
|
||||
|
||||
def im_di_posizione(avail: float, usdc_eq: float, usde: float, px: float, h: float,
|
||||
dust: float = ETH_DUST_USD) -> float:
|
||||
"""PURA. -> IM di POSIZIONE in USD, ripulita dall'haircut.
|
||||
|
||||
Inversa esatta della riga CROSS: available = usdc_eq + (1-h)*usde*px + dust - IM.
|
||||
Serve perche' la percentuale stampata dalla pagina NON e' questa (vedi live())."""
|
||||
return usdc_eq + (1 - h) * usde * px + dust - avail
|
||||
|
||||
|
||||
def live() -> None:
|
||||
print(" X:SM ATTIVO (2026-08-31 08:34Z) — la pagina si ricostruisce dal gateway")
|
||||
print("=" * 78)
|
||||
ric = GW_USDC_EQ + 0.95 * GW_USDE + ETH_DUST_USD - IM_MIS
|
||||
print(f"\n [3] la riga CROSS, ricostruita con haircut 5%")
|
||||
print(f" {GW_USDC_EQ:,.2f} + 0,95x{GW_USDE:,.3f} + {ETH_DUST_USD:.2f} dust"
|
||||
f" - {IM_MIS:.3f} IM = ${ric:,.2f}")
|
||||
print(f" pagina ${PAG_AVAIL:,.2f} · gateway ${GW_AVAIL:,.2f} → scarto ${abs(ric-PAG_AVAIL):.4f}")
|
||||
for h in (0.05, 0.10):
|
||||
im = im_di_posizione(GW_AVAIL, GW_USDC_EQ, GW_USDE, 1.0, h)
|
||||
print(f" haircut {h:5.0%} → IM implicita ${im:9,.2f}"
|
||||
f" {'coerente con gli $11,55 misurati' if im > 0 else 'IMPOSSIBILE: IM negativa'}")
|
||||
print(" → terza conferma del 5%, ed e' la prima ESATTA. Il 10% non e' improbabile:"
|
||||
"\n e' aritmeticamente impossibile.")
|
||||
|
||||
riserv = GW_MB - GW_AVAIL
|
||||
hc = 0.05 * GW_USDE
|
||||
print(f"\n [4] 🚨 l'\"IM %\" della pagina NON e' il margine delle posizioni")
|
||||
print(f" (margin_balance - available)/margin_balance = {riserv/GW_MB:.4%}"
|
||||
f" → la pagina stampa {PAG_IM_PCT:.2%}")
|
||||
print(f" scomposta: haircut USDE ${hc:,.2f} ({hc/GW_MB:.3%})"
|
||||
f" + IM vera ${IM_MIS:,.2f} ({IM_MIS/GW_MB:.3%})")
|
||||
print(f" ⇒ il {hc/riserv:.0%} di quella percentuale e' HAIRCUT, non rischio.")
|
||||
print(f" E' salita da 2,13% a 2,15% perche' abbiamo COMPRATO COLLATERALE A RENDIMENTO.")
|
||||
print(f" La MM invece e' pulita: {PAG_MM_PCT:.2%} = ${PAG_MM_PCT*GW_MB:,.2f}, e non puo'"
|
||||
f" contenere\n l'haircut (${hc:,.2f} da solo la renderebbe negativa):"
|
||||
" le due colonne hanno basi DIVERSE.")
|
||||
|
||||
print(f"\n [5] X:PM — il confronto rifatto sulla misura pulita")
|
||||
print(f" La KB da' USDe al 5% sotto ENTRAMBI i modelli: l'haircut si CANCELLA nel confronto.")
|
||||
for nome, av in (("X:SM", CROSS_XSM), ("X:PM", XPM_AVAIL)):
|
||||
im = im_di_posizione(av, USDC_EQ, USDE_EQ, PX_INDICE, 0.05)
|
||||
print(f" {nome}: available ${av:,.2f} → IM di POSIZIONE ${im:6,.2f}")
|
||||
im_sm = im_di_posizione(CROSS_XSM, USDC_EQ, USDE_EQ, PX_INDICE, 0.05)
|
||||
im_pm = im_di_posizione(XPM_AVAIL, USDC_EQ, USDE_EQ, PX_INDICE, 0.05)
|
||||
print(f" → PM chiede {im_pm/im_sm:.1f}x il margine iniziale sulle STESSE due posizioni,"
|
||||
f"\n e {0.0343/PAG_MM_PCT:.1f}x la MM. Due misure pulite, stesso verso: SCARTATO confermato.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user