# 07 — Risk Controls Controlli di sicurezza trasversali che non sono parte della strategia ma proteggono il sistema da bug, dati corrotti, fallimenti infrastrutturali o decisioni umane fuori posto. ## Filosofia - **Default deny**: in caso di dubbio, il sistema non fa nulla. - **Disarm manuale**: ogni kill switch viene disarmato esplicitamente da Adriano via CLI o comando Telegram, mai automaticamente. - **Visibilità**: ogni evento di sicurezza viene loggato e notificato immediatamente. - **No silent close**: una posizione viene chiusa solo a seguito di decisione esplicita dei trigger di strategia, mai per "evento sospetto". ## Kill switch ### Stato `system_state.kill_switch ∈ {0, 1}`. Quando = 1, l'engine: - continua i flussi di **sola lettura** (health check, monitoring di stato, log) - **non** invia istruzioni di apertura - **non** invia istruzioni di chiusura **automatiche** (richiede intervento manuale via CLI con flag `--force-close-position`) - continua a notificare i trigger di uscita ad Adriano via Telegram con escalation manuale ### Trigger automatici | Causa | Auto-arm | Note | |---|---|---| | MCP `cerbero-deribit` versione mismatch | Sì | Non procede senza schema atteso | | MCP `cerbero-macro` non risponde per >30 min | Sì | No entry possibile | | MCP `cerbero-memory` non risponde per >5 min | Sì | No esecuzione possibile | | Stato SQLite incoerente con broker | Sì | Diff tra DB e Deribit positions | | 3 health check consecutivi falliti | Sì | Engine instabile | | Perdita giornaliera > 3% equity | Sì | Hard prohibition Cerbero v4 | | Numero posizioni concorrenti > cap | Sì | Bug del sizing engine | | Push instruction respinta da Cerbero core | Sì | Dopo 2 retry | | Schema config invalido al reload | Sì | Mai partire con config rotta | | Comando Adriano `/kill` su Telegram | Sì | Trigger volontario | ### Disarm ```bash cerbero-bite kill-switch --disarm --reason "" ``` oppure via Telegram: ``` /disarm ``` In entrambi i casi il sistema chiede una conferma esplicita ("yes I am sure"). ## Cap di rischio (oltre alle regole di strategia) Questi cap sono ridondanti rispetto a quelli del §5 della strategia, ma sono applicati in modo difensivo come **ultima linea**: | Misura | Limite | Comportamento se superato | |---|---|---| | Notional combo singolo | 200 EUR | Sizing engine reject | | Engagement totale aperto | 1.000 EUR | Sizing engine reject | | Posizioni concorrenti CB | 4 (default 1 per strategia) | Reject pre-push | | Trade aperti per giorno | 6 (intera Cerbero suite) | Lookup `cerbero-memory.daily_trade_count` | | Perdita giornaliera | 3% equity | Kill switch | | Distance short strike | < 15% spot OTM | Combo builder reject | | Credit / width | < 0.30 | Combo builder reject | Doppia verifica: il sizing_engine applica il cap, **e** il watchdog runtime ricontrolla il payload prima di chiamare `push_user_instruction`. Se le due misure divergono → kill switch + alert. ## Dead-man switch Se l'engine non scrive un evento `HEALTH_OK` per **15 minuti** consecutivi: 1. Un processo separato `data/dead-man.sh` (cron in user crontab, indipendente dall'engine) rileva il silenzio. 2. Invia alert su canale Telegram di backup. 3. Marca SQLite con `system_state.kill_switch=1`. 4. Adriano interviene manualmente. Il dead-man è scritto in shell minimale (no dipendenze Python) per sopravvivere a corruzioni dell'env Python. ## Audit log immutabile Oltre al log JSONL standard, ogni decisione di trading produce una linea append-only in `data/audit.log` con il **digest SHA-256** della linea precedente (chain di hash, stile blockchain semplificato). Esempio: ``` 2026-04-27T14:00:01Z|ENTRY_PROPOSED|{...full payload...}|prev_hash=abc123...|hash=def456... ``` Verificabile retroattivamente con `cerbero-bite audit verify`. Una discrepanza dell'hash chain è trattata come tampering e arma il kill switch. ## Dry-run mode Tutti i flussi possono essere eseguiti in modalità `--dry-run`: - Tutti i tool MCP **read-only** vengono chiamati normalmente. - Tool MCP **write** (`push_user_instruction`, `kb_write`, `send`) vengono loggati ma **non** chiamati. - `state.create_position` viene scritto in tabella `dry_positions` (schema identico a `positions` ma separata). Usato per: - Testing in produzione prima di andare live. - Replay di giornate storiche per validazione. - Test di nuove versioni di config o algoritmi. ## Versionamento config Ogni `strategy.yaml` ha: ```yaml config_version: "1.0.0" config_hash: "" last_review: "2026-04-26" last_reviewer: "Adriano" ``` All'avvio l'engine verifica che `config_hash` corrisponda al contenuto. Mismatch → kill switch (qualcuno ha modificato la config senza aggiornare l'hash, possibile tampering o errore umano). ## Escalation tree ``` Evento anomalo │ ├── Severity LOW (es. 1 health check fallito) │ └── Log WARNING, continua │ ├── Severity MEDIUM (es. MCP timeout occasionale) │ ├── Log WARNING + Telegram digest giornaliero │ └── Continua, retry next cycle │ ├── Severity HIGH (es. 3 health check consecutivi falliti) │ ├── Kill switch ARM │ ├── Telegram alert immediato │ └── Adriano interviene │ └── Severity CRITICAL (es. stato incoerente, hash chain rotto) ├── Kill switch ARM ├── Telegram + canale backup BotPapà ├── Engine si mette in idle (no decisioni, solo monitoring) └── Richiede intervento umano per disarmo ``` ## Test di resilienza obbligatori Prima del go-live e ad ogni release minor: 1. **Chaos test MCP**: simula timeout/errori su ogni MCP, verifica che il comportamento documentato in `04-mcp-integration.md` sia rispettato. 2. **State corruption test**: corrompi una riga `positions` e verifica che il riconciliatore lo rilevi. 3. **Hash chain test**: modifica una linea audit e verifica che `audit verify` fallisca. 4. **Replay test**: rigioca una giornata storica in dry-run, confronta le decisioni con un set golden. 5. **Cap saturation test**: simula 4 posizioni concorrenti, verifica che il quinto trade venga rifiutato. I risultati sono documentati in `tests/golden/results-YYYY-MM-DD.md`. ## Cosa NON è un risk control Per chiarezza, queste cose **non** sono cap né kill switch — sono parte della strategia, gestite altrove: - Profit take 50%: regola di strategia. - Stop loss 1.5×: regola di strategia. - Vol stop +10 DVOL: regola di strategia. - Time stop 7 DTE: regola di strategia. I risk control proteggono il **sistema**. La strategia protegge il **capitale**. Sono livelli diversi.