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

6.6 KiB
Raw Blame History

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 Non procede senza schema atteso
MCP cerbero-macro non risponde per >30 min No entry possibile
MCP cerbero-memory non risponde per >5 min No esecuzione possibile
Stato SQLite incoerente con broker Diff tra DB e Deribit positions
3 health check consecutivi falliti Engine instabile
Perdita giornaliera > 3% equity Hard prohibition Cerbero v4
Numero posizioni concorrenti > cap Bug del sizing engine
Push instruction respinta da Cerbero core Dopo 2 retry
Schema config invalido al reload Mai partire con config rotta
Comando Adriano /kill su Telegram 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:

  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:

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.