- pyproject.toml with uv, deps for runtime + gui + backtest + dev - ruff/mypy strict config, pre-commit hooks for ruff/mypy/pytest - src/cerbero_bite/ layout with empty modules ready for Phase 1+ - structlog JSONL logger with daily rotation - click CLI with placeholder subcommands (status, start, kill-switch, gui, replay, config hash, audit verify) - 6 smoke tests passing, mypy --strict clean, ruff clean Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.6 KiB
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
cerbero-bite kill-switch --disarm --reason "<motivo>"
oppure via Telegram:
/disarm <motivo>
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:
- Un processo separato
data/dead-man.sh(cron in user crontab, indipendente dall'engine) rileva il silenzio. - Invia alert su canale Telegram di backup.
- Marca SQLite con
system_state.kill_switch=1. - 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_positionviene scritto in tabelladry_positions(schema identico apositionsma 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:
config_version: "1.0.0"
config_hash: "<sha256 del file senza questa riga>"
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:
- Chaos test MCP: simula timeout/errori su ogni MCP, verifica
che il comportamento documentato in
04-mcp-integration.mdsia rispettato. - State corruption test: corrompi una riga
positionse verifica che il riconciliatore lo rilevi. - Hash chain test: modifica una linea audit e verifica che
audit verifyfallisca. - Replay test: rigioca una giornata storica in dry-run, confronta le decisioni con un set golden.
- 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.