Crypto opera 24/7: la cadenza settimanale lunedì-only era un retaggio TradFi senza giustificazione. La nuova cadenza è giornaliera (cron 0 14 * * *), con i gate quantitativi a decidere se entrare o saltare. Cambiamenti principali: * runtime/orchestrator.py — _CRON_ENTRY 0 14 * * * (era MON) * runtime/auto_pause.py — pause_until(days=) (era weeks=); minimo clamp 1 giorno (era 1 settimana) * core/backtest.py — MondayPick→DailyPick, monday_picks→daily_picks (1 pick per calendar-day all'ora target); Sharpe annualization su ~120 trade/anno (era 52) * config/schema.py — default cron daily; max_concurrent_positions 1→5; AutoPauseConfig.pause_weeks→pause_days, default 14 * runtime/option_chain_snapshot_cycle.py + orchestrator — cron */15 per accumulo continuo dataset di backtest empirico Strategy yamls (config_version 1.3.0 → 1.4.0, hash rigenerati): * strategy.yaml — max_concurrent 1→5, cap_aggregate coerente * strategy.aggressiva.yaml — max_concurrent 2→8, cap_aggregate 3200→6400, max_contracts_per_trade invariato a 16 * strategy.conservativa.yaml — max_concurrent 1→3 * tutti — pause_weeks→pause_days: 14 GUI (pages/7_📚_Strategia.py): * slider Trade/anno: range 20-200 (era 8-30), default 110, help riallineato sulla math 365 candidature × pass-rate 30-40% * card profili: versione letta dinamicamente da config_version invece che hard-coded "v1.2.0" * warning "entrambi perdono soldi" ora valuta i P/L effettivi (cons['annual_pl'], aggr['annual_pl']) invece del win_rate grezzo; aggiunto stato intermedio quando solo conservativo è in perdita Tests (450/450 passati): * test_auto_pause: pause_days, clamp ≥1 giorno * test_backtest: rinomina + ridisegno daily picks (assert su calendar-day dedupe e hour filter) * test_sizing_engine: other_open_positions=5 per cap default * test_config_loader: version 1.4.0 Docs (README + 9 file in docs/) — tutti i riferimenti weekly/lunedì allineati a daily/24-7, volume option_chain ricalcolato per cron */15 (~1.1 MB/giorno, ~400 MB/anno). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
12 KiB
01 — Regole della strategia (Cerberus Bite)
Le regole qui formalizzate sono la versione canonica e immutabile che il rule engine deve applicare. Modifiche richiedono una review esplicita con Adriano (e una nuova versione di questo documento).
Sorgente teorica: Cerbero_Office/NewStrategy/strategia-credit-spread-eth.md
- analisi Monte Carlo + cap operativi Cerbero v4.
1. Universo e strumenti
| Parametro | Valore |
|---|---|
| Sottostante | ETH/USD |
| Exchange | Deribit |
| Tipo opzioni | Europee, cash-settled, USDC-margined |
| Strutture ammesse | Bull Put Spread, Bear Call Spread, (Iron Condor solo in regime laterale, vedere §6) |
| Strutture vietate | Naked options, calendar spread, ratio spread, condor sbilanciati |
2. Trigger di apertura (entry)
Il rule engine valuta l'apertura di un nuovo trade una volta al
giorno, alle 14:00 UTC (orario UE pomeridiano stabile, fuori
dai picchi di funding statunitensi). Crypto è 24/7: non c'è un "giorno
buono" intrinseco, sono i gate quantitativi a decidere se entrare o
saltare. Se il giorno è festività italiana e skip_holidays_country
è attivo, l'engine attende il giorno successivo.
Una nuova posizione viene aperta solo se tutte le seguenti condizioni sono vere:
- Nessuna posizione Cerberus Bite già aperta.
- Capitale corrente ≥ 720 USD. Sotto questa soglia il sizing in contratti non può raggiungere 1 unità Deribit.
- DVOL corrente ≥ 35. Sotto 35 il premio è troppo magro per mantenere il rapporto fees/credit accettabile.
- DVOL corrente ≤ 90. Sopra 90 si è in regime di stress, no entry.
- Nessun evento macro (FOMC, CPI USA, NFP, ECB, Powell speech) nei prossimi DTE giorni dall'apertura.
- Funding rate ETH-PERP in valore assoluto annualizzato ≤ 80% (filtra regimi di liquidazioni a cascata imminenti).
- Holdings ETH in
cerbero-portfolionon superiori al 30% del patrimonio totale (correlazione direzionale già alta). - 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 viadealer_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 il giorno successivo.
3. Selezione struttura
3.1 Bias direzionale
Il bias è dedotto deterministicamente da due indicatori, mai in modo discrezionale:
| Indicatore | Calcolo | Soglia bull | Soglia bear |
|---|---|---|---|
| Trend ETH 30g | (ETH_now / ETH_30g_ago - 1) × 100 | ≥ +5% | ≤ -5% |
| Funding cross-exchange | mediana funding annualizzato 4 maggiori exchange | ≥ +20% | ≤ -20% |
Risultato:
- Entrambi bull → Bull Put Spread.
- Entrambi bear → Bear Call Spread.
- Discordanti → no entry (mercato indeciso, edge incerto).
- Entrambi in range neutro
(-5%, +5%)E DVOL ≥ 55 E ADX(14) < 20 → Iron Condor stretto ammesso (vedi §6). - Tutti gli altri casi neutri → no entry.
3.2 Strike short
Si seleziona lo strike short più vicino a delta target, in valore assoluto.
| Parametro | Valore |
|---|---|
| Delta short target | 0.12 |
| Tolleranza delta | [0.10, 0.15] |
| 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)
Si seleziona lo strike a distanza fissa dallo short:
| Parametro | Valore |
|---|---|
| Larghezza spread (in % di spot) | 4% |
| Larghezza minima accettabile | 3% |
| Larghezza massima accettabile | 5% |
Se non esiste lo strike esatto a 4%, si prende il più vicino entro [3%, 5%]; altrimenti no entry.
3.4 Rapporto credito/larghezza
Il credito netto incassato (mid-price del combo) deve essere ≥ 30% della larghezza dello spread. Sotto questa soglia il rapporto rischio/rendimento è inferiore al minimo accettabile. → no entry.
4. Liquidità (filtro hard pre-entry)
Per ciascuno strumento delle due gambe, tutte le condizioni:
| Misura | Soglia |
|---|---|
| Open interest | ≥ 100 contratti |
| Volume 24h sullo strumento | ≥ 20 contratti |
| Spread bid-ask | ≤ 15% del mid (≤ 10% per ATM, irrilevante qui) |
| Profondità book ai primi 3 livelli | ≥ 5 contratti aggregati |
Calcoliamo inoltre uno slippage stimato moltiplicando la larghezza del bid-ask di entrambe le gambe per la size proposta. Se lo slippage totale > 8% del credito atteso → no entry.
5. Sizing
5.1 Calcolo numero contratti
risk_target_usd = capitale_corrente * 0.13 # Quarter Kelly
risk_target_usd = min(risk_target_usd, 200_eur_in_usd) # cap Cerbero
n_contracts = floor(risk_target_usd / max_loss_per_contract)
n_contracts = min(n_contracts, 4) # cap aggregato implicito
max_loss_per_contract = larghezza spread × multiplier ETH (1 ETH).
5.2 Vincolo aggregato
Se n_contracts × max_loss_per_contract + engagement_aperto_totale > 1.000_eur →
riduci n_contracts finché il vincolo è rispettato. Se risulta < 1 → no entry.
5.3 Volatilità di volatilità
Sizing dinamico per regime DVOL:
| DVOL corrente | Aggiustamento n_contracts |
|---|---|
| < 45 | × 1.0 (base) |
| 45–60 | × 0.85 |
| 60–80 | × 0.65 |
| > 80 | (no entry, vedi §2) |
Arrotondamento sempre per difetto a intero, minimo 1.
6. Iron Condor (ammesso solo in regime laterale)
Se le condizioni del §3.1 attivano IC, struttura:
- Bull Put Spread alle stesse regole §3.2/§3.3
- Bear Call Spread speculare (delta 0.12, 4% width)
- Sizing:
n_contractscalcolato come sopra ma dimezzato per gamba (rischio totale = somma max loss dei due lati, ma su ETH i due lati non possono essere entrambi violati, quindi rischio effettivo = max loss del lato peggiore + credito del lato vincente) - Profit take: 25% del credito totale (più aggressivo)
- Tutto il resto delle regole §7-§9 si applica per lato.
7. Gestione attiva (decision loop ogni 12 ore)
Per ogni posizione aperta, il rule engine valuta in ordine:
- Profit take — se mark price del combo ≤ 50% del credito incassato,
decision = CLOSE_PROFIT. - Stop loss stretto — se mark price del combo ≥ 250% del credito
(perdita = 1.5× credito),
decision = CLOSE_STOP. - Vol stop — se DVOL corrente ≥ DVOL all'entry + 10,
decision = CLOSE_VOL. - Time stop — se
(scadenza - oggi) ≤ 7 giorni,decision = CLOSE_TIME(a meno che non siamo già al 50% di profit: in quel caso teniamo per regola 1 in arrivo). - Strike testato — se
|delta_short_corrente| ≥ 0.30,decision = CLOSE_DELTA. Su ETH non si difende, si esce. - Movimento esplosivo — se
|return_4h_ETH| ≥ 5%direzione avversa,decision = CLOSE_AVERSE(azione conservativa, può essere finalizzata alla prossima fenestra). - Altrimenti
decision = HOLD.
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) EDVOL_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.enablede P/L cumulato delle ultimelookback_tradesposizioni chiuse <−max_drawdown_pct × capitale_corrente, l'engine si auto-mette in pausa perpause_daysgiorni (skip-day 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%, 14
giorni di pausa.
8. Esecuzione di apertura
- Engine costruisce combo order Deribit (un solo ordine atomico con due gambe). Mai due ordini separati.
- Limit price = mid-price calcolato dall'engine.
- Ordine inviato come limit GTC con time-in-force che scade alle 16:00 UTC dello stesso giorno.
- Se non riempito entro 30 min, engine alza limit di 1 tick verso ask combinato fino a un massimo di 3 step (poi cancella e log "no fill").
- Riempimento → engine persiste lo stato (vedi
05-data-model.md) includendo: spot ETH, DVOL, strike, scadenza, credit incassato, timestamp.
9. Esecuzione di chiusura
- Stessa logica di combo, in direzione inversa.
- Limit di partenza al mid-price.
- Se non riempito entro 30 min: alza limit fino a 3 step verso il
bid combinato. Su trigger
CLOSE_STOPoCLOSE_VOLoCLOSE_DELTAl'engine può accettare slippage maggiore (fino a 5 step) perché l'urgenza prevale sul prezzo. - Riempimento → posizione marcata
CLOSED, P&L calcolato e loggato, record archiviato per Kelly recalibration mensile.
10. Cosa NON fa il rule engine — esplicito
- Non rolla mai una posizione (regola §7.5: si esce e basta).
- Non aggiunge gambe a una posizione esistente (no conversione in IC durante un trade aperto).
- Non aggiusta strike o size dopo l'apertura.
- Non apre nuovi trade per "compensare" perdite recenti.
- Non opera fuori dalla finestra delle 14:00 UTC, eccetto chiusure.
- Non deroga ai cap nemmeno per "opportunità eccezionali".
- Non si aggiorna automaticamente: nuovo set di regole = nuovo deploy con review esplicita.
11. Riepilogo soglie (parametri di config)
Tutti i numeri sopra sono parametrizzati in strategy.yaml (vedi
10-config-spec.md). Modificarli è una decisione di Adriano con
giustificazione scritta nel commit message. La regola di default
fissata da questo documento è la golden config che il sistema parte
con al primo deploy.