- 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>
8.4 KiB
04 — MCP Integration
Tutti i server MCP sono già configurati a livello di CerberoSuite (vedi
Cerbero_Office/.mcp.json). Cerbero Bite vi si connette come client
MCP usando l'SDK ufficiale mcp per Python.
Configurazione di connessione
Cerbero Bite legge ~/.config/cerbero-suite/mcp.json (o, in dev, da
.mcp.json locale puntato via env var CERBERO_BITE_MCP_CONFIG). I
server vengono risolti per nome dichiarato nel file di config.
# clients/_base.py — abstract
class McpClient:
name: str # "cerbero-deribit", ecc.
timeout_s: float = 8.0
retry_max: int = 3
retry_base_delay: float = 1.0 # esponenziale
async def call(self, tool: str, **params) -> dict: ...
Ogni wrapper concreto eredita McpClient ed espone metodi tipizzati.
La logica di retry e timeout è centralizzata.
Server MCP usati
cerbero-deribit
Tool consumati:
| Tool | Uso | Frequenza |
|---|---|---|
get_index_price(asset="ETH") |
Spot ETH per calcolo strike | Ogni ciclo entry + monitor |
get_dvol() |
Volatilità implicita aggregata ETH | Ogni ciclo entry + monitor |
get_options_chain(asset, expiry_window) |
Lista strumenti per dato DTE | Solo entry |
get_instrument(instrument_name) |
Mid, bid, ask, greche su singolo strumento | Entry + monitor |
get_orderbook(instrument_name, depth=5) |
Profondità per liquidity_gate e slippage | Solo entry |
get_combo_mark(legs) |
Mark price del combo (debito di chiusura) | Solo monitor |
get_account_summary(currency="USDC") |
Equity Deribit, margin libero | Periodico |
Wrapper:
# clients/deribit.py
class DeribitClient(McpClient):
name = "cerbero-deribit"
async def index_price(self, asset: str) -> Decimal: ...
async def dvol(self) -> Decimal: ...
async def options_chain(self, asset: str, dte_min: int, dte_max: int) -> list[InstrumentSnapshot]: ...
async def instrument(self, name: str) -> InstrumentSnapshot: ...
async def orderbook(self, name: str, depth: int = 5) -> OrderbookSnapshot: ...
async def combo_mark(self, legs: list[OptionLeg]) -> Decimal: ...
async def account_summary(self) -> AccountSummary: ...
Note:
- Tutti i prezzi sono ricevuti come float dal MCP, convertiti in
Decimalconquantizea 6 cifre nel wrapper. - Greche convertite con la stessa quantizzazione.
- Se
mark_iv = 7%o300%obid = 0su orderbook ATM su tutti gli strumenti → wrapper sollevaDeribitDataAnomalyError(probabile testnet o feed rotto). Il decision orchestrator cattura, alert, skippa il ciclo.
cerbero-hyperliquid
| Tool | Uso |
|---|---|
get_perp_funding_rate(asset="ETH") |
Filtro entry §2.6 |
get_perp_summary(asset="ETH") |
Volume 24h, conferma liquidità correlata |
get_account_summary() |
Solo per coerenza, non usato in decision loop |
class HyperliquidClient(McpClient):
async def funding_rate_annualized(self, asset: str) -> Decimal: ...
async def perp_summary(self, asset: str) -> PerpSummary: ...
cerbero-sentiment
| Tool | Uso |
|---|---|
get_funding_cross_exchange(asset="ETH") |
Bias direzionale §3.1 (mediana 4 maggiori) |
Le news qualitative non sono usate nel decision loop (no LLM). Vengono eventualmente lette da Adriano in occasione del report settimanale.
class SentimentClient(McpClient):
async def funding_cross_median(self, asset: str) -> Decimal: ...
cerbero-macro
| Tool | Uso |
|---|---|
get_calendar(days_ahead=18) |
Filtro eventi macro pre-entry |
Eventi rilevanti (filtra per severity = high e country in {US, EU}):
FOMC, FED minutes, CPI, NFP, ECB, GDP, Powell speech, Lagarde speech.
class MacroClient(McpClient):
async def upcoming_events(self, days_ahead: int) -> list[MacroEvent]: ...
async def first_high_severity_within(self, days: int) -> int | None:
"""Days until first high-severity event, None if none in window."""
cerbero-portfolio
| Tool | Uso |
|---|---|
get_holdings() |
Capitale corrente complessivo |
get_holdings_by_asset() |
Filtro entry §2.7 (ETH < 30% portfolio) |
get_correlation() |
Sanity check, non bloccante |
class PortfolioClient(McpClient):
async def total_equity_usd(self) -> Decimal: ...
async def asset_pct(self, asset: str) -> Decimal: ...
cerbero-memory
| Tool | Uso |
|---|---|
push_user_instruction(payload, source="cerbero-bite") |
Invio istruzione apertura/chiusura a Cerbero core |
get_pending(source="cerbero-bite") |
Verifica ack di Cerbero core |
class MemoryClient(McpClient):
async def push_instruction(self, instruction: CerberoInstruction) -> str:
"""Returns instruction_id."""
async def is_acknowledged(self, instruction_id: str) -> bool: ...
Payload CerberoInstruction (schema condiviso con Cerbero core,
documentato in Cerbero/prompt.base v4):
{
"source": "cerbero-bite",
"kind": "open_combo" | "close_combo",
"exchange": "deribit",
"asset": "ETH",
"proposal_id": "uuid-...",
"legs": [
{"instrument": "ETH-13MAY26-1900-P", "side": "SELL", "size": 2,
"limit_price_eth": "0.0048"},
{"instrument": "ETH-13MAY26-1810-P", "side": "BUY", "size": 2,
"limit_price_eth": "0.0021"}
],
"limit_combo_eth": "0.0027",
"tif": "GTC",
"expires_at": "2026-04-27T16:00:00Z",
"max_slippage_eth": "0.0005",
"reason": "weekly_open" | "profit_take" | "stop_loss" | "vol_stop" |
"time_stop" | "delta_breach" | "adverse_move",
"milestone": "advisory_only" | "approved_by_user"
}
Cerbero core deduplica per proposal_id (idempotenza in caso di retry).
cerbero-telegram
| Tool | Uso |
|---|---|
send_message(text, parse_mode="MarkdownV2") |
Report pre/post trade, alert |
send_with_buttons(text, buttons) |
Conferma ad Adriano (yes/no) |
Le conferme devono ritornare entro 60 minuti (entry) o 30 minuti (exit).
Implementazione: l'engine si mette in await su una coda interna
alimentata dal callback Telegram via webhook locale.
class TelegramClient(McpClient):
async def send(self, text: str, parse_mode: str = "MarkdownV2") -> int: ...
async def request_confirmation(self, text: str, timeout_s: int) -> bool: ...
cerbero-brain-bridge
| Tool | Uso |
|---|---|
kb_search(query) |
Lookup pre-trade su pattern simili (consultivo) |
kb_read(path) |
Lettura nota wiki specifica |
kb_write(path, content) |
Salvataggio learning post-trade |
Importante: il brain-bridge non partecipa al decision loop. Le
chiamate kb_search sono consultive e i risultati allegati al
report di Adriano per contesto, mai consumati come input ai filtri
deterministici.
class BrainBridgeClient(McpClient):
async def search(self, query: str, limit: int = 5) -> list[KbHit]: ...
async def write_note(self, path: str, content: str) -> None: ...
cerbero-scheduler
Non usato dal decision loop. Cerbero Bite ha il proprio scheduler APScheduler interno. Il MCP scheduler resta a disposizione del core Cerbero per altre routine.
Errori e degradation
| Server down | Comportamento |
|---|---|
cerbero-deribit |
Skip ciclo entry; per monitor → alert e marca posizione come unknown_state (non chiude alla cieca) |
cerbero-hyperliquid |
Skip filtro funding §2.6 con warning; entry può proseguire se altre condizioni soddisfatte |
cerbero-sentiment |
Bias §3.1 cade in no_entry per default (no funding cross → niente direzione) |
cerbero-macro |
Hard fail: senza calendar non si apre. È un filtro irrinunciabile |
cerbero-portfolio |
Skip filtro §2.7 con warning; sizing usa ultimo capitale noto da SQLite con warning |
cerbero-memory |
Hard fail per esecuzione: senza push_user_instruction non si può aprire/chiudere |
cerbero-telegram |
Skip ciclo: senza canale di conferma niente proposta |
cerbero-brain-bridge |
Skip lookup, log warning. Mai bloccante |
Ogni "hard fail" → alert sonoro su Telegram via canale di backup (BotPapà), kill switch armato fino al ripristino.
Versioning
Cerbero Bite verifica all'avvio la versione di ciascun MCP via
get_version() (tool standard). Schema di versioning attesa:
EXPECTED_MCP_VERSIONS = {
"cerbero-deribit": "^2.0.0",
"cerbero-hyperliquid": "^1.5.0",
"cerbero-memory": "^4.0.0",
"cerbero-portfolio": "^1.2.0",
...
}
Mismatch → kill switch e alert manuale. Mai partire con MCP a versione incompatibile.