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>
This commit is contained in:
2026-04-26 23:10:30 +02:00
commit 881bc8a1bf
40 changed files with 6018 additions and 0 deletions
+193
View File
@@ -0,0 +1,193 @@
# 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.