docs: aggiornamento Phase 5 — IV-RV gate, F+D+A, backtest, option chain

- 01-strategy-rules.md:
  * §2.8 (filtri quant: dealer gamma + liquidation risk)
  * §2.9 (IV richness gate, opt-in, default disabled)
  * §3.2 — variante delta_by_dvol step-function
  * §7-bis.1 (vol-collapse harvest D)
  * §7-bis.2 (graduated profit-take C — scaffolding)
  * §7-bis.3 (auto-pause su drawdown F)

- 05-data-model.md:
  * `system_state.auto_pause_until / _reason` (migration 0004)
  * Nuova tabella `option_chain_snapshots` (migration 0005)
  * Tabella migrations completa (1→5)

- 13-strategia-spiegata.md:
  * §4-quinquies — catena opzioni storica (Phase 5):
    cosa raccoglie, cosa sblocca, CLI `option-chain
    trigger|analyze`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
root
2026-05-01 21:29:00 +00:00
parent 7fdd8b47a5
commit dabcc8d15b
3 changed files with 189 additions and 6 deletions
+85
View File
@@ -41,6 +41,34 @@ sono vere:
patrimonio totale (correlazione direzionale già alta).
8. **Liquidità degli strike candidati** entro le soglie del §3.
### 2.8 Filtri quant (introdotti in Phase 4)
Due gate aggiuntivi che leggono i campi del `market_snapshot`:
- **Dealer net gamma > 0** (default: `dealer_gamma_min: 0`). Long-gamma
regime = dealer hedge che sopprime la vol → ideale per vendere
credit spread. Short-gamma = vol-amplifying flow, statisticamente
perdente. Disattivabile via `dealer_gamma_filter_enabled: false`.
- **Liquidation risk ≠ high** (default: `liquidation_filter_enabled:
true`). Salta entry quando il modulo sentiment flagga uno squeeze
imminente su long o short side.
### 2.9 IV richness gate (introdotto in Phase 5, opt-in)
Filtro a maggior impatto sul win-rate. **Disabilitato** di default
nella golden config — abilitato esplicitamente nel profilo
`strategy.aggressiva.yaml`:
- `iv_minus_rv_filter_enabled: true|false` — master switch.
- `iv_minus_rv_min: <pt vol>` — soglia: l'entry passa solo se la
IV implicita 30g supera la realized vol 30g di almeno tot punti.
Default 0; valori sensati 3-5 dopo calibrazione sui dati raccolti.
Razionale: il selling vol nudo è strutturalmente neutro a win-rate
70-72%. L'edge della strategia esiste solo quando il premio è
"ricco" — IV30 > RV30 + N. Vedere `13-strategia-spiegata.md §4-quater`
per il razionale completo.
Se anche **una sola** condizione fallisce → **no entry**, log con motivo,
ritento la settimana successiva.
@@ -77,6 +105,20 @@ assoluto.
| Distanza minima OTM | 15% (anche se delta è dentro tolleranza) |
| Distanza massima OTM | 25% |
**Variante dinamica per regime DVOL (Phase 5, opt-in).** Il campo
`short_strike.delta_by_dvol` (lista step-function) sostituisce il
delta target singolo con bande ordinate per `dvol_under`: a DVOL
bassa il bot prende delta più alto (più premio), a DVOL alta sceglie
delta più basso (più safety distance). Lista vuota = comportamento
classico col delta target sopra. Esempio bande nel profilo
`strategy.aggressiva.yaml`:
| dvol_under | delta_target | delta_min | delta_max |
|---|---|---|---|
| 50 | 0.15 | 0.13 | 0.17 |
| 70 | 0.12 | 0.10 | 0.15 |
| 90 | 0.10 | 0.08 | 0.12 |
Se nessuno strike disponibile rientra in entrambe le tolleranze → no entry.
### 3.3 Strike long (protezione)
@@ -179,6 +221,49 @@ Per ogni posizione aperta, il rule engine valuta in ordine:
L'ordine è importante: il primo trigger soddisfatto vince.
## 7-bis. Estensioni opzionali (Phase 5)
Tre miglioramenti opt-in al decision loop. Tutti **disabled** di
default nella golden config; il profilo `strategy.aggressiva.yaml`
li abilita.
### 7-bis.1 Vol-collapse harvest (D)
Inserito tra il profit-take §7.1 e lo stop-loss §7.2:
> Se `vol_harvest_dvol_decrease > 0`, siamo in profit
> (`debit < credit`) E `DVOL_now ≤ DVOL_entry vol_harvest_dvol_decrease`,
> `decision = CLOSE_VOL_HARVEST`.
Razionale: edge IV-RV già catturato, vol attesa rientrata, non c'è
motivo di tenere fino al profit-take. Default disabilitato (`0`);
profilo aggressivo: `15` punti vol.
### 7-bis.2 Profit-take graduale (C — scaffolding)
Schema in place per chiusure parziali; pipeline runtime di
chiusura partial-close NON ancora wirata. Default vuoto. Quando
popolato, ogni livello `{mark_at_pct_credit, close_pct_of_initial_contracts}`
emette un'azione `CLOSE_PROFIT_PARTIAL` advisory che il runtime
attualmente ignora. Il completamento richiede refactor del position
model (contracts_open vs contracts_initial) — PR dedicato.
### 7-bis.3 Auto-pause su drawdown (F)
Circuit breaker sopra il kill-switch tecnico. Valutato all'inizio di
ogni entry-cycle:
> Se `auto_pause.enabled` e P/L cumulato delle ultime
> `lookback_trades` posizioni chiuse < `max_drawdown_pct ×
> capitale_corrente`, l'engine si auto-mette in pausa per
> `pause_weeks` settimane (skip-week mode).
Difende dai regime change non rilevati dai filtri quant. La pausa
si annulla automaticamente alla scadenza, oppure manualmente con
`UPDATE system_state SET auto_pause_until = NULL`. Default
disabilitato; profilo aggressivo: lookback 5 trade, soglia 15%, 2
settimane di pausa.
## 8. Esecuzione di apertura
1. Engine costruisce **combo order Deribit** (un solo ordine atomico
+63 -6
View File
@@ -202,7 +202,9 @@ CREATE TABLE system_state (
last_kelly_calib TEXT,
config_version TEXT NOT NULL,
started_at TEXT NOT NULL,
last_audit_hash TEXT -- aggiunto dalla migration 0002
last_audit_hash TEXT, -- aggiunto dalla migration 0002
auto_pause_until TEXT, -- aggiunto dalla migration 0004 (§7-bis.3)
auto_pause_reason TEXT -- aggiunto dalla migration 0004
);
```
@@ -212,6 +214,54 @@ Al boot l'orchestrator confronta questo valore con il tail del file
`audit.log`: discrepanza → kill switch CRITICAL, vedi
`07-risk-controls.md`.
I campi `auto_pause_until` / `auto_pause_reason` implementano il
circuit breaker §7-bis.3 (pausa automatica su drawdown rolling).
NULL = engine attivo.
### `option_chain_snapshots`
Snapshot della catena opzioni Deribit prelevata settimanalmente
(cron `55 13 * * MON`, 5 minuti prima del trigger entry). Ogni
tick contiene un quote per strumento entro la finestra
`[dte_min, dte_max]` di config; tutti i quote prelevati nello stesso
tick condividono ``timestamp``. Migration `0005`.
```sql
CREATE TABLE option_chain_snapshots (
timestamp TEXT NOT NULL,
asset TEXT NOT NULL,
instrument_name TEXT NOT NULL,
strike TEXT NOT NULL,
expiry TEXT NOT NULL,
option_type TEXT NOT NULL CHECK (option_type IN ('C','P')),
bid TEXT,
ask TEXT,
mid TEXT,
iv TEXT,
delta TEXT,
gamma TEXT,
theta TEXT,
vega TEXT,
open_interest INTEGER,
volume_24h INTEGER,
book_depth_top3 INTEGER,
PRIMARY KEY (timestamp, instrument_name)
) WITHOUT ROWID;
```
Indici: `(asset, timestamp DESC)` per listing recenti, `(asset,
expiry)` per query per scadenza specifica. ``book_depth_top3`` è
NULL by design — il collector non chiama l'order book per ogni
strike per non saturare l'API; lo legge il liquidity gate live solo
sugli strike candidati al picker.
**Sblocca**: il backtest non-stilizzato (modulo `core/backtest.py`
con prezzi reali invece di Black-Scholes), la calibrazione empirica
dello skew premium, la validazione ex-post dello strike picker.
Volume atteso: ~50 strike × 3 scadenze × 1 snapshot/settimana ×
17 colonne ≈ 12 KB/settimana, ~600 KB/anno.
## Log file
Sotto `data/log/` un file per giorno: `cerbero-bite-YYYY-MM-DD.jsonl`.
@@ -289,11 +339,18 @@ da altri processi (es. CLI `state inspect`) non vedano stati parziali.
## Migrations
Lo schema viene tracciato con il counter `PRAGMA user_version`. La
prima volta `0001_init.sql` viene applicato e versione → 1; alla
seconda esecuzione (o su DB già a versione 1) `0002_audit_anchor.sql`
viene applicato e versione → 2. `state.db.run_migrations` è
idempotente. Nessun rollback supportato (migrations forward-only).
Lo schema viene tracciato con il counter `PRAGMA user_version`.
`state.db.run_migrations` applica in ordine ogni file
`NNNN_<name>.sql` con versione superiore a quella corrente,
idempotente, forward-only:
| Versione | File | Cosa aggiunge |
|---|---|---|
| 1 | `0001_init.sql` | tabelle base (positions, decisions, ...) |
| 2 | `0002_audit_anchor.sql` | `system_state.last_audit_hash` |
| 3 | `0003_market_snapshots.sql` | tabella `market_snapshots` |
| 4 | `0004_auto_pause.sql` | `system_state.auto_pause_until / _reason` |
| 5 | `0005_option_chain_snapshots.sql` | tabella `option_chain_snapshots` |
## Backup
+41
View File
@@ -595,6 +595,47 @@ questa logica: muovi da 0.72 a 0.78 e vedi l'APR scattare.
3. **Multi-asset (ETH+BTC)**: come da §4-ter, sblocca il
moltiplicatore 2× sulle opportunità a parità di filtri.
## 4-quinquies. Catena opzioni storica (Phase 5)
In aggiunta a `market_snapshots` (cron `*/15`), il bot raccoglie
ora una seconda fonte di dati: la **catena opzioni Deribit completa**
ogni lunedì alle 13:55 UTC (5 minuti prima del trigger entry).
Tabella `option_chain_snapshots` — vedi `05-data-model.md` per lo
schema. Cosa registra per ogni strumento entro la finestra
`[dte_min, dte_max]`:
| Campo | Cosa misura | A che serve |
|---|---|---|
| `bid` / `ask` / `mid` | Prezzi reali Deribit in ETH | P/L reale per backtest |
| `iv` | IV implicita allo strike | Calibrazione skew premium |
| `delta`/`gamma`/`theta`/`vega` | Greeks | Sizing + risk monitoring |
| `open_interest` / `volume_24h` | Liquidità | Validazione gate §4 |
**Cosa sblocca:**
- **Backtest non-stilizzato**: il modulo `core/backtest.py` può
sostituire la stima Black-Scholes (con `skew_premium` hardcoded a
1.5×) leggendo i `mid` reali. Numeri da "stime ex-ante per
ranking" a "P/L empirico per dimensionare capitale".
- **Calibrazione empirica dello skew**: rapporto fra prezzi
Deribit reali e BS pulito → valore data-driven invece di stima a
istinto. Va fatto dopo 4-8 settimane di accumulo.
- **Validazione ex-post strike picker**: "il delta-0.12 era davvero
a 25% OTM in quella settimana?" diventa una `SELECT`.
**Strumenti CLI:**
- `cerbero-bite option-chain trigger` — esegue UNA volta il
collector senza aspettare il cron. Utile per test e per popolare
prima del primo lunedì utile.
- `cerbero-bite option-chain analyze [--bias bull_put|bear_call]`
legge l'ultimo snapshot, simula il selector di strike con la
strategy passata e stampa: short/long strike, delta, width,
credito reale, ratio credit/width, PASS/FAIL del gate
`credit_to_width_ratio_min`. Risponde in tempo reale: "il rule
engine aprirebbe un trade adesso?".
## 5. Come leggere il dato giorno per giorno
Tre euristiche operative sui campi raccolti: