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:
@@ -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.
|
||||
Reference in New Issue
Block a user