Files
Cerbero-Bite/docs/02-architecture.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

13 KiB

02 — Architettura

Vista a blocchi

                        ┌─────────────────────────────────┐
                        │        ADRIANO (utente)         │
                        └──────┬──────────────────────────┘
                               │ Telegram (report + conferma)
                               ▼
┌─────────────────────────────────────────────────────────────────┐
│                       CERBERO BITE (rule engine)                │
│                                                                 │
│  ┌──────────────┐    ┌──────────────┐   ┌──────────────────┐    │
│  │  scheduler   │    │  state store │   │   audit logger   │    │
│  │  (APScheduler)│   │   (SQLite)   │   │   (append-only)  │    │
│  └──────┬───────┘    └──────┬───────┘   └──────┬───────────┘    │
│         │                   │                  │                │
│         ▼                   ▼                  ▼                │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │              decision orchestrator (core/)              │    │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐ │    │
│  │  │  entry   │  │  sizing  │  │  exit    │  │  greeks  │ │    │
│  │  │validator │  │  engine  │  │ decision │  │ aggregator│ │    │
│  │  └──────────┘  └──────────┘  └──────────┘  └──────────┘ │    │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐               │    │
│  │  │liquidity │  │  combo   │  │ kelly    │               │    │
│  │  │  gate    │  │ builder  │  │ recalib  │               │    │
│  │  └──────────┘  └──────────┘  └──────────┘               │    │
│  └─────────────────────────────────────────────────────────┘    │
│         │                                                       │
│         ▼                                                       │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                MCP client wrappers (clients/)           │    │
│  │   Deribit │ Hyperliquid │ Macro │ Sentiment │ Portfolio │    │
│  │   Memory  │ Telegram    │ Brain-Bridge                  │    │
│  └─────────────────────────────────────────────────────────┘    │
└────────────────────────┬────────────────────────────────────────┘
                         │ MCP JSON-RPC
                         ▼
        ┌───────────────────────────────┐    ┌───────────────────┐
        │        MCP servers            │    │   Cerbero core    │
        │  (esistenti in CerberoSuite)  │───▶│ (esecuzione)      │
        └───────────────────────────────┘    └───────────────────┘
                         │
                         ▼
                ┌────────────────┐
                │     Deribit    │
                │      API       │
                └────────────────┘

Stack tecnologico

Strato Scelta Motivazione
Linguaggio Python 3.12+ Coerente con mcp_cerbero_brain e l'ecosistema Cerbero
Async runtime asyncio Necessario per i client MCP
Scheduler APScheduler Cron-like + interval-based, in-process
Persistenza stato SQLite (file singolo) Zero deploy overhead, transazionale, sufficiente per ≤ 4 posizioni
Persistenza log File .jsonl rotativi (gzip > 30g) Audit-friendly, append-only, parseable
Validazione config pydantic v2 Schema a runtime su strategy.yaml
Test pytest + pytest-asyncio + hypothesis (property-based) TDD imposto
Type checking mypy --strict Disciplina, niente sorprese a runtime
Format/lint ruff Standard del progetto
Dependency manager uv Coerente con mcp_cerbero_brain
MCP client SDK ufficiale mcp Connessione ai server già configurati in .mcp.json
Notifiche MCP cerbero-telegram Riusa canale esistente
GUI streamlit ≥ 1.40 + plotly Dashboard locale, processo separato (vedi 11-gui-streamlit.md)

Layout cartelle

Cerbero_Bite/
├── README.md
├── pyproject.toml
├── uv.lock
├── strategy.yaml                      # config golden
├── strategy.local.yaml.example        # override locale (gitignored)
├── docs/                               # questa documentazione
├── src/cerbero_bite/
│   ├── __init__.py
│   ├── __main__.py                     # entry point CLI
│   ├── core/                           # algoritmi puri (no I/O)
│   │   ├── entry_validator.py
│   │   ├── sizing_engine.py
│   │   ├── exit_decision.py
│   │   ├── greeks_aggregator.py
│   │   ├── liquidity_gate.py
│   │   ├── combo_builder.py
│   │   └── kelly_recalibration.py
│   ├── clients/                        # wrapper MCP
│   │   ├── deribit.py
│   │   ├── hyperliquid.py
│   │   ├── macro.py
│   │   ├── sentiment.py
│   │   ├── portfolio.py
│   │   ├── memory.py
│   │   ├── telegram.py
│   │   └── brain_bridge.py
│   ├── runtime/                        # I/O, scheduling, orchestrazione
│   │   ├── scheduler.py
│   │   ├── orchestrator.py
│   │   ├── monitoring.py
│   │   └── alert_manager.py
│   ├── state/                          # persistenza
│   │   ├── repository.py
│   │   ├── models.py                   # SQLAlchemy o dataclasses
│   │   └── migrations/
│   ├── config/                         # caricamento e validazione yaml
│   │   ├── schema.py
│   │   └── loader.py
│   ├── reporting/                      # report umani
│   │   ├── pre_trade.py
│   │   ├── post_trade.py
│   │   └── daily_digest.py
│   ├── gui/                            # Streamlit dashboard (vedi 11-gui-streamlit.md)
│   │   ├── main.py
│   │   ├── pages/
│   │   ├── components/
│   │   └── data_layer.py
│   └── safety/                         # kill switch, dead man, audit
│       ├── kill_switch.py
│       ├── dead_man.py
│       └── audit_log.py
├── tests/
│   ├── unit/                           # test puri sui moduli core/
│   ├── integration/                    # test con MCP fake
│   ├── golden/                         # scenari deterministici di riferimento
│   └── fixtures/
├── scripts/
│   ├── reset_state.py
│   ├── replay_day.py                   # replay forensico di una giornata
│   └── recalibrate_kelly.py
└── data/
    ├── state.sqlite                    # gitignored
    └── log/                            # gitignored

Principio di separazione

  • core/ contiene funzioni pure: input → output, niente I/O, niente MCP, niente time. Testabili con dati statici. Sono il cuore della strategia e devono restare riproducibili al byte.
  • clients/ contiene wrapper sui server MCP. Ogni client espone una API tipizzata che usa internamente JSON-RPC. Mai logica di business qui.
  • runtime/ orchestrazione: composizione di clients/ + core/ + state/. È l'unico strato che può fare I/O e ha effetti collaterali.
  • state/ persistenza. Mai logica di business. Solo CRUD.
  • config/ caricamento di strategy.yaml, validazione, esposizione immutabile dei parametri.
  • reporting/ generazione di stringhe per Telegram. Niente logica di trading, solo formatting.
  • safety/ controlli trasversali (vedere 07-risk-controls.md).

Decision orchestrator — sequenza tipo per "Lunedì 14:00 UTC"

async def evaluate_entry():
    # 1. Stato sistema
    if not safety.system_healthy(): return
    if state.has_open_position(): return

    # 2. Dati di mercato
    spot = await deribit.get_index_price("ETH")
    dvol = await deribit.get_dvol()
    funding = await sentiment.get_funding_cross_exchange("ETH")
    macro = await macro.get_calendar(days=18)
    holdings = await portfolio.get_holdings()

    # 3. Algoritmi puri
    bias = entry_validator.compute_bias(spot, trend_30d, funding)
    if bias == "no_entry": return log_and_skip()

    capital = await portfolio.get_capital()
    chain = await deribit.get_options_chain("ETH", dte_target=18)
    short_strike, long_strike = combo_builder.select_strikes(
        chain, bias, spot, config
    )
    if short_strike is None: return log_and_skip("no_strike")

    if not liquidity_gate.passes(short_strike, long_strike, deribit_book):
        return log_and_skip("illiquid")

    n = sizing_engine.compute_contracts(
        capital, max_loss_per_contract, dvol, config
    )
    if n < 1: return log_and_skip("undersize")

    proposal = combo_builder.build(short_strike, long_strike, n, mid_price)

    # 4. Conferma
    await telegram.send(reporting.pre_trade(proposal))
    confirmation = await wait_user_confirmation(timeout="60min")
    if not confirmation.accepted: return log_and_skip("rejected")

    # 5. Esecuzione via Cerbero core
    instruction = combo_builder.to_cerbero_instruction(proposal)
    await memory.push_user_instruction(instruction, source="cerbero-bite")

    # 6. Stato
    state.create_position(proposal, dvol_at_entry=dvol)
    audit_logger.log("ENTRY_PROPOSED", proposal)

Sequenza tipo per "monitoring 12h"

async def evaluate_open_positions():
    if not safety.system_healthy(): return
    for position in state.list_open_positions():
        spot = await deribit.get_index_price("ETH")
        dvol = await deribit.get_dvol()
        mark = await deribit.get_combo_mark(position.legs)
        delta_short = await deribit.get_instrument(position.short_leg).delta

        decision = exit_decision.evaluate(
            position=position,
            spot=spot,
            dvol_now=dvol,
            mark_now=mark,
            delta_short_now=delta_short,
            now=datetime.now(timezone.utc),
            config=config,
        )

        if decision.action == "HOLD":
            audit_logger.log("HOLD", position.id, decision.reason)
            continue

        await telegram.send(reporting.exit_proposal(position, decision))
        confirmation = await wait_user_confirmation(timeout="30min")
        if not confirmation.accepted:
            audit_logger.log("EXIT_DEFERRED", position.id)
            continue

        await memory.push_user_instruction(
            combo_builder.close_instruction(position),
            source="cerbero-bite"
        )
        state.mark_closing(position.id)

Failure modes e retry

Modalità Risposta
MCP non risponde (timeout) Retry esponenziale 3 tentativi (1s, 5s, 30s); poi alert + skip ciclo
MCP risponde con dato palesemente rotto (es. orderbook tutti a 0) Skip ciclo, alert
Confidence Adriano scaduta Skip apertura; le chiusure restano in coda con alta priorità
Stato SQLite corrotto Kill switch attivato, alert manuale richiesto
Cerbero core non riceve l'istruzione Engine si aspetta ack via cerbero-memory.get_pending; se entro 5 min ack assente, alert e blocco apertura

Vedi 07-risk-controls.md per il dettaglio completo dei kill switch.

Concorrenza e idempotenza

  • Una sola istanza dell'engine alla volta (file lock su data/.lockfile).
  • Tutti i ticker di scheduler sono idempotenti: se il sistema crasha durante un'apertura, al riavvio il monitoring ricostruisce lo stato da SQLite + Cerbero core (cerbero-memory.get_state) e prosegue.
  • Ogni proposta ha un proposal_id UUID. Cerbero core ignora duplicati con lo stesso id.