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,243 @@
|
||||
# 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.
|
||||
|
||||
```python
|
||||
# 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:
|
||||
|
||||
```python
|
||||
# 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
|
||||
`Decimal` con `quantize` a 6 cifre nel wrapper.
|
||||
- Greche convertite con la stessa quantizzazione.
|
||||
- Se `mark_iv = 7%` o `300%` o `bid = 0` su orderbook ATM su tutti gli
|
||||
strumenti → wrapper solleva `DeribitDataAnomalyError` (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 |
|
||||
|
||||
```python
|
||||
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.
|
||||
|
||||
```python
|
||||
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.
|
||||
|
||||
```python
|
||||
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 |
|
||||
|
||||
```python
|
||||
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 |
|
||||
|
||||
```python
|
||||
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`):
|
||||
|
||||
```json
|
||||
{
|
||||
"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.
|
||||
|
||||
```python
|
||||
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.
|
||||
|
||||
```python
|
||||
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:
|
||||
|
||||
```python
|
||||
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.
|
||||
Reference in New Issue
Block a user