Files
Cerbero-Bite/docs/06-operational-flow.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

8.5 KiB
Raw Blame History

06 — Flussi operativi

Tre flussi principali, tutti orchestrati dallo scheduler interno APScheduler.

Flusso 1 — Avvio engine

1. Carica strategy.yaml + strategy.local.yaml (override)
2. Validazione schema (pydantic) — fail veloce
3. Acquisisci lock file (data/.lockfile)
4. Apri SQLite, esegui migrations
5. Health check MCP server (versioni + ping)
6. Riconciliatore stato:
     a. legge positions con status in {awaiting_fill, open, closing}
     b. per ognuna chiama cerbero-memory.is_acknowledged + Deribit get_positions
     c. allinea SQLite alla realtà del broker (sempre la realtà vince)
     d. log delle discrepanze come WARNING
7. Health check sistema OK?  →  arma scheduler
                     KO?     →  kill switch + alert + idle
8. Log ENGINE_START

L'avvio è progettato per essere safe: se qualcosa non torna, il sistema si rifiuta di operare. Mai partire con uno stato dubbio.

Flusso 2 — Settimanale (entry)

Trigger: cron 0 14 * * MON (lunedì 14:00 UTC).

START
 ├── safety.system_healthy()?               → no  → log + skip
 ├── state.has_open_position()?             → yes → log + skip
 ├── snapshot dati di mercato (parallel):
 │     spot, dvol, funding_perp, funding_cross, macro_calendar,
 │     holdings_pct, capital
 ├── entry_validator.validate_entry         → fail → log ENTRY_REJECTED + reasons
 ├── entry_validator.compute_bias           → None → log ENTRY_REJECTED ("no_bias")
 ├── deribit.get_options_chain (DTE 14-21)
 ├── combo_builder.select_strikes           → None → log ENTRY_REJECTED ("no_strike")
 ├── deribit.get_orderbook per le 2 gambe
 ├── liquidity_gate.check                   → fail → log ENTRY_REJECTED ("illiquid")
 ├── sizing_engine.compute_contracts        → 0    → log ENTRY_REJECTED ("undersize")
 ├── combo_builder.build → ComboProposal
 ├── greeks_aggregator.aggregate
 ├── reporting.pre_trade(proposal) → testo Telegram
 ├── telegram.request_confirmation(timeout=60min)
 │     ├── confermato:
 │     │     ├── state.create_position (status="proposed")
 │     │     ├── memory.push_user_instruction
 │     │     ├── state.update(status="awaiting_fill")
 │     │     └── log ENTRY_CONFIRMED
 │     ├── rifiutato:
 │     │     └── log ENTRY_REJECTED_BY_USER
 │     └── timeout:
 │           └── log ENTRY_TIMEOUT
 └── END

Tempo previsto end-to-end

  • Snapshot dati: 3-5 secondi
  • Algoritmi puri: < 0.5 secondi
  • Conferma utente: variabile (max 60 min)

Il critical path automatico è sotto 10 secondi. La latenza umana non rallenta i prezzi: se Adriano risponde dopo 30 minuti, l'engine prima di inviare l'istruzione rivaluta spot, mid e dvol e abortisce l'apertura se le condizioni sono cambiate (slippage > 8% o dvol fuori banda o macro nuova).

Flusso 3 — Monitoring (12h)

Trigger: cron 0 2,14 * * * (02:00 e 14:00 UTC, ogni giorno).

START
 ├── safety.system_healthy()? → no  → kill switch (no auto-close ciechi)
 ├── per ogni position con status="open":
 │     ├── snapshot:
 │     │     spot, dvol, mark_combo, delta_short, return_4h
 │     ├── exit_decision.evaluate
 │     ├── action == HOLD:
 │     │     └── log EXIT_EVALUATED("hold")
 │     ├── action == CLOSE_*:
 │     │     ├── reporting.exit_proposal(position, decision)
 │     │     ├── telegram.request_confirmation(timeout=30min)
 │     │     │     ├── confermato:
 │     │     │     │     ├── memory.push_user_instruction (close)
 │     │     │     │     ├── state.update(status="closing")
 │     │     │     │     └── log EXIT_CONFIRMED
 │     │     │     ├── rifiutato:
 │     │     │     │     └── log EXIT_DEFERRED (resterà in HOLD fino a prossimo ciclo)
 │     │     │     └── timeout:
 │     │     │           └── escalation: per CLOSE_STOP/CLOSE_VOL/CLOSE_DELTA
 │     │     │                 → invia comunque l'istruzione (urgenza prevale)
 │     │     │                 per altri motivi → log EXIT_TIMEOUT, attende prossimo ciclo
 │     └── continue
 └── END

Politica di escalation

I trigger di uscita non sono uguali. Alcuni hanno urgenza alta che giustifica l'esecuzione anche senza conferma utente entro la finestra:

Trigger Urgenza Comportamento su timeout
CLOSE_PROFIT Bassa Attendi prossimo ciclo (può migliorare)
CLOSE_STOP Alta Esegui chiusura senza conferma esplicita
CLOSE_VOL Alta Esegui chiusura senza conferma
CLOSE_DELTA Alta Esegui chiusura senza conferma
CLOSE_TIME Media Attendi 1 ciclo extra; al secondo timeout esegui
CLOSE_AVERSE Media Attendi prossimo ciclo

Su escalation hard, Telegram riceve un messaggio post-fact: "ho chiuso la posizione X per stop loss perché non ho ricevuto risposta in 30 min e l'urgenza non consentiva attesa".

Flusso 4 — Mensile (Kelly recalibration)

Trigger: cron 0 12 1 * * (primo di ogni mese alle 12:00 UTC).

1. Carica trades chiusi negli ultimi 365 giorni
2. kelly_recalibration.recalibrate
3. Genera report mensile:
     - performance vs simulazione MC
     - win rate, avg win/loss, edge
     - kelly suggerito vs corrente
     - violazioni regole (deve essere 0)
4. telegram.send (informativo, no conferma)
5. brain_bridge.write_note(
     path="strategies/cerberus-bite/monthly-report-YYYY-MM.md",
     content=report
   )
6. log KELLY_RECALIBRATED

L'aggiornamento di strategy.yaml resta manuale: Adriano legge il report, discute con Milito, e committa il nuovo file di config (con giustificazione nel commit message). Nessun auto-update.

Flusso 5 — Health check periodico

Trigger: ogni 5 minuti.

1. ping ogni MCP usato → ms latency
2. SQLite read-write probe (transazione fittizia)
3. lock file ancora valido
4. ultima fill di Cerbero core ricevuta entro tempo atteso
5. state.system_state.kill_switch == 0
Se qualsiasi passo fallisce:
  - log HEALTH_DEGRADED
  - se 3 fallimenti consecutivi → kill_switch + alert
  - se 5 fallimenti consecutivi → restart automatico (1 sola volta al giorno)

Flusso 6 — Recovery dopo crash

Quando l'engine riparte:

  1. Riapre SQLite + log della giornata.
  2. Per ogni posizione awaiting_fill:
    • Chiede a cerbero-memory.get_status(instruction_id)
    • Se filled → aggiorna a open
    • Se cancelled → aggiorna a cancelled
    • Se ancora pending → mantiene stato e riprende il monitoraggio
  3. Per ogni posizione closing:
    • Stessa logica
  4. Per ogni posizione open:
    • Verifica che esista realmente sul broker (Deribit get_positions)
    • Se non esiste → flag state_inconsistent + alert + kill switch
  5. Riconciliazione completata → ENGINE_START.

Il sistema mai prende decisioni di trading durante recovery: solo allinea lo stato. Il primo decision loop avviene al prossimo trigger naturale.

Diagramma di stato di una posizione

                        ┌─ rejected_by_user ──→ cancelled
                        │
         (entry)        ├─ timeout ──────────→ cancelled
proposed ─────────────► │
                        ├─ confirmed
                        ▼
                  awaiting_fill
                        │
                        ├─ no_fill ──────────→ cancelled
                        ├─ filled ───────────► open
                        │                       │
                        │           (exit_decision != HOLD)
                        │                       │
                        │                       ▼
                        │                    closing
                        │                       │
                        │                       ├─ no_fill (escalated) → manual_intervention
                        │                       └─ filled ──────────→ closed

Cron summary

Cron Trigger Frequenza
0 14 * * MON Entry evaluation Settimanale
0 2,14 * * * Position monitoring 2× giorno
0 12 1 * * Kelly recalibration Mensile
*/5 * * * * Health check 5 min
0 0 * * * Backup SQLite + rotation log Giornaliero
0 8 * * * Daily digest Telegram Giornaliero

Tutti gli orari in UTC.