Files
Cerbero-Bite/docs/07-risk-controls.md
T
Adriano 881bc8a1bf Phase 0: project skeleton
- 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>
2026-04-26 23:10:30 +02:00

194 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 "<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:
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: "<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:
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.