Files
Cerbero-Bite/docs/04-mcp-integration.md
T
Adriano ce158a92dd feat(mcp+runtime): allineamento a Cerbero MCP V2 e flag operativi
Adegua Cerbero Bite alla nuova versione 2.0.0 del server MCP unificato
(testnet/mainnet routing per token, header X-Bot-Tag obbligatorio) e
introduce due interruttori operativi indipendenti per separare la
raccolta dati dall'esecuzione di strategia.

Auth e collegamento MCP
- Token bearer letto dalla nuova variabile CERBERO_BITE_MCP_TOKEN; il
  valore sceglie l'ambiente upstream (testnet vs mainnet) sul server.
  Rimosso il caricamento da file (`secrets/core.token`,
  CERBERO_BITE_CORE_TOKEN_FILE, Docker secret /run/secrets/core_token).
- Aggiunto header X-Bot-Tag (default `BOT__CERBERO_BITE`, override via
  CERBERO_BITE_MCP_BOT_TAG) su ogni call MCP, con validazione lato client
  (non vuoto, ≤ 64 caratteri).
- Cartella `secrets/` rimossa, `.gitignore` ripulito, Dockerfile e
  docker-compose.yml aggiornati con env passthrough e fail-fast quando
  manca il token.

Modalità operativa (RuntimeFlags)
- Nuovo modulo `config/runtime_flags.py` con `RuntimeFlags(
  data_analysis_enabled, strategy_enabled)` e loader che parserizza
  CERBERO_BITE_ENABLE_DATA_ANALYSIS e CERBERO_BITE_ENABLE_STRATEGY
  (true/false/yes/no/on/off/enabled/disabled, case-insensitive).
- L'orchestratore espone i flag, audita e logga la modalità al boot
  (`engine started: env=… data_analysis=… strategy=…`), e in
  `install_scheduler` esclude i job `entry`/`monitor` quando strategy è
  off e il job `market_snapshot` quando data analysis è off. I job di
  infrastruttura (health, backup, manual_actions) restano sempre attivi.
- Default profile = "solo analisi dati" (data_analysis=true,
  strategy=false), pensato per la finestra di soak post-deploy.

GUI saldi
- `gui/live_data.py::_fetch_deribit_currency` riconosce il campo soft
  `error` nel payload V2 (HTTP 200 con `error` valorizzato dal server
  quando l'auth Deribit fallisce) e lo propaga come `BalanceRow.error`,
  evitando di mostrare un fuorviante equity = 0,00.

CLI
- Sostituita l'opzione `--token-file` con `--token` (stringa) sui comandi
  start/dry-run/ping; il default proviene dall'env. Le chiamate al
  builder dell'orchestrator passano anche `bot_tag` e `flags`.

Documentazione
- `docs/04-mcp-integration.md`: descrizione del nuovo flusso di auth V2
  (token = ambiente, X-Bot-Tag nell'audit) e router unificati.
- `docs/06-operational-flow.md`: nuova sezione "Modalità operativa" con
  i tre profili canonici e tabella di gating per ogni job; aggiunto
  `market_snapshot` al cron summary.
- `docs/10-config-spec.md`: nuova sezione "Variabili d'ambiente"
  tabellare con tutti gli env, comprese le bool dei flag operativi.
- `docs/02-architecture.md`: layout del repo aggiornato (`secrets/`
  rimosso, `runtime_flags.py` aggiunto), descrizione di `config/`
  estesa.

Test
- 5 nuovi test su `_fetch_deribit_currency` (soft-error, payload pulito,
  eccezione, error blank, signature parity).
- 7 nuovi test su `load_runtime_flags` (default, override, parsing
  truthy/falsy, blank fallback, valore invalido).
- 4 nuovi test su `HttpToolClient` (X-Bot-Tag default e custom, blank e
  troppo lungo rifiutati).
- 3 nuovi test integration sull'orchestratore (gating dei job in base
  ai flag).
- Test esistenti su token/CLI ping/orchestrator aggiornati al nuovo
  schema. Suite intera: 404 passed, 1 skipped (sqlite3 CLI assente
  sull'host di sviluppo).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 17:14:40 +02:00

212 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 04 — MCP Integration
Cerbero Bite consuma quattro router MCP HTTP della suite Cerbero MCP V2
(`Cerbero_mcp`): `mcp-deribit`, `mcp-hyperliquid`, `mcp-macro`,
`mcp-sentiment`. Dalla V2 i quattro router vivono nello stesso processo
FastAPI dietro lo stesso host (default in-cluster
`http://cerbero-mcp:9000/mcp-{exchange}`, gateway pubblico
`https://cerbero-mcp.tielogic.xyz/mcp-{exchange}`). Cerbero Bite non
utilizza l'SDK Python `mcp`: ogni router espone gli endpoint REST
`POST <base_url>/tools/<tool_name>` con autenticazione Bearer e header
`X-Bot-Tag`, e Cerbero Bite vi si collega tramite `httpx.AsyncClient`
long-lived (`clients/_base.py`).
Telegram e Portfolio, in passato esposti come servizi MCP condivisi,
sono stati rimossi dal layer MCP e gestiti **in-process** da ogni bot
della suite: il client Telegram chiama direttamente la Bot API
pubblica e l'aggregatore di portafoglio compone equity ed esposizioni
dai client di scambio (Deribit + Hyperliquid) convertendo in EUR
attraverso `cerbero-macro.get_asset_price("EURUSD")`.
## Configurazione di connessione
Le URL sono risolte da `cerbero_bite.config.mcp_endpoints.load_endpoints`,
con default che corrispondono al DNS della rete Docker
`cerbero-suite` (`http://mcp-deribit:9011`, `http://mcp-macro:9013`,
ecc.). Ogni servizio può essere sovrascritto da una variabile
d'ambiente dedicata, utile in sviluppo:
| Servizio | Variabile d'ambiente | Default Docker DNS legacy |
|---|---|---|
| Deribit | `CERBERO_BITE_MCP_DERIBIT_URL` | `http://mcp-deribit:9011` |
| Hyperliquid | `CERBERO_BITE_MCP_HYPERLIQUID_URL` | `http://mcp-hyperliquid:9012` |
| Macro | `CERBERO_BITE_MCP_MACRO_URL` | `http://mcp-macro:9013` |
| Sentiment | `CERBERO_BITE_MCP_SENTIMENT_URL` | `http://mcp-sentiment:9014` |
I default mostrati sopra sono il legacy della topologia V1 (un container
per servizio). Sulla V2 unificata ogni URL deve includere il prefisso di
router, ad esempio `http://cerbero-mcp:9000/mcp-deribit` o
`https://cerbero-mcp.tielogic.xyz/mcp-deribit`. Le URL effettive sono
configurate in `.env`.
Telegram (notify-only) viene configurato direttamente via due
variabili d'ambiente, lette al boot dal client in-process:
| Variabile | Uso |
|---|---|
| `CERBERO_BITE_TELEGRAM_BOT_TOKEN` | Token del bot fornito da BotFather |
| `CERBERO_BITE_TELEGRAM_CHAT_ID` | Identificativo della chat o del gruppo destinatario |
Quando una delle due manca, il client Telegram entra in modalità
**disabled** e ogni `notify_*` diventa un no-op a livello di DEBUG.
Il bearer token per le chiamate è letto dalla variabile d'ambiente
`CERBERO_BITE_MCP_TOKEN` (vedi `.env`). Sulla V2 il valore del token
decide quale ambiente upstream serve la richiesta: lo stesso server MCP
fronteggia testnet e mainnet contemporaneamente, e si passa da uno
all'altro semplicemente sostituendo il valore della variabile e
riavviando il bot. Il token non viene mai loggato.
A ogni chiamata Cerbero Bite aggiunge anche l'header `X-Bot-Tag`, con
valore di default `BOT__CERBERO_BITE` (override via
`CERBERO_BITE_MCP_BOT_TAG`). Il server MCP scrive il valore nell'audit
record di ogni operazione di scrittura, così ogni write resta
attribuibile al bot d'origine.
```python
# clients/_base.py — sintesi
class HttpToolClient:
service: str # "deribit", "macro", ...
base_url: str # "https://cerbero-mcp.tielogic.xyz/mcp-deribit"
token: str # bearer (testnet o mainnet, scelto da env)
bot_tag: str = "BOT__CERBERO_BITE" # X-Bot-Tag header
timeout_s: float = 8.0
retry_max: int = 3 # esponenziale 1s/5s/30s
client: httpx.AsyncClient | None # condiviso dal RuntimeContext
async def call(self, tool: str, body: dict | None = None) -> Any: ...
```
Ogni wrapper concreto compone un `HttpToolClient` e ritorna i record
Pydantic consumati direttamente dagli algoritmi `core/`.
## Server MCP usati
### `cerbero-deribit`
Sorgente di tutti i dati di mercato sulle opzioni e canale di
esecuzione: Cerbero Bite invia gli ordini combo direttamente al broker
attraverso questo MCP, senza intermediazioni.
| Tool | Uso | Frequenza |
|---|---|---|
| `environment_info` | Verifica al boot: testnet/mainnet, base_url, max_leverage | Boot + ogni ciclo health |
| `get_ticker(instrument_name)` | Spot proxy via `ETH-PERPETUAL.mark_price`, mid/bid/ask + greche per le leg | Ogni ciclo entry + monitor |
| `get_ticker_batch(instrument_names)` | Quotes in batch per la chain candidata (max 20) | Solo entry |
| `get_dvol(currency="ETH", start_date, end_date)` | Latest DVOL per filtro §2.3 e bias §3.1 | Ogni ciclo entry + monitor |
| `get_instruments(currency, kind="option", expiry_from, expiry_to, min_open_interest)` | Lista strike per il DTE window | Solo entry |
| `get_orderbook(instrument_name, depth=3)` | `book_depth_top3` per liquidity gate | Solo entry |
| `get_historical(instrument, start_date, end_date, resolution)` | Spot 30g fa per bias direzionale + bootstrap return_4h | Entry + monitor (fallback) |
| `get_technical_indicators(instrument, indicators=["adx"], ...)` | ADX(14) per il filtro Iron Condor §3.1 | Solo entry |
| `get_account_summary(currency="USDC")` | Equity Deribit, margin libero (informativo) | Boot + monitor |
| `get_positions(currency="USDC")` | Riconciliazione stato dopo crash | Boot |
| `place_combo_order(legs, side, amount, type, price, label)` | **Esecuzione**: combo atomico via `private/create_combo` + `private/buy/sell` sul combo creato | Entry + monitor (close) |
| `cancel_order(order_id)` | Repricing e annullamenti | Solo monitor |
Note operative:
- Tutti i prezzi e le greche sono restituiti come `float` dal server e
convertiti in `Decimal` ad alta precisione nel wrapper, mai usati
come `float` nel motore decisionale.
- Se la chain risponde con `mark_iv` palesemente fuori range
(es. 7% o 300%) o tutti i `bid == 0` la chiamata viene segnalata come
`McpDataAnomalyError`; l'orchestrator emette un alert e salta il
ciclo.
- L'invio di `place_combo_order` è atomico: la creazione del combo e
l'ordine eseguito sul combo viaggiano in sequenza ma all'interno di
un'unica chiamata MCP, senza esposizione a leg risk.
### `cerbero-hyperliquid`
| Tool | Uso |
|---|---|
| `get_funding_rate(instrument="ETH")` | Funding rate ETH-PERP (annualizzato × 8760) per il filtro entry §2.6 |
### `cerbero-sentiment`
| Tool | Uso |
|---|---|
| `get_cross_exchange_funding(assets=["ETH"])` | Mediana funding annualizzato (Binance/Bybit/OKX 1095, Hyperliquid 8760) per bias direzionale §3.1 |
Le news qualitative non vengono consumate dal decision loop:
Cerbero Bite è deterministico e non interpreta testi liberi.
### `cerbero-macro`
| Tool | Uso |
|---|---|
| `get_macro_calendar(days, country_filter, importance_min)` | Filtro entry §2.5: zero eventi `high` in `country_filter` (default `["US","EU"]`) entro la finestra DTE |
| `get_asset_price(ticker="EURUSD")` | Tasso di cambio EUR/USD usato dall'aggregatore di portafoglio per convertire l'equity USD degli scambi in EUR |
## Componenti in-process
### Portfolio aggregator (`clients/portfolio.py`)
Il client `PortfolioClient` non chiama più un servizio MCP dedicato;
compone i dati dei due exchange usati dal bot e applica il cambio
EUR/USD letto da `cerbero-macro`.
| Metodo | Comportamento |
|---|---|
| `total_equity_eur()` | Somma `equity` USD di Deribit (USDC) e Hyperliquid, divide per `EURUSD` per ottenere il capitale in EUR consumato dal sizing engine |
| `asset_pct_of_portfolio(ticker)` | Somma il notional USD assoluto delle posizioni aperte su entrambi gli scambi il cui `instrument`/`coin` contiene `ticker`, e lo divide per l'equity totale USD. Usato dal filtro §2.7 (`eth_holdings_pct_max`) |
**Nota di scope**: la vista è la *slice* del singolo bot. Holdings su
exchange esterni, in cold storage, o gestiti da altri bot della suite
non vengono contati. Il filtro §2.7 va quindi inteso come cap
per-bot, non come cap suite-wide.
### Telegram client (`clients/telegram.py`)
Cerbero Bite usa Telegram in modalità **notify-only**: nessuna
conferma manuale, nessun callback. L'engine apre e chiude le
posizioni automaticamente quando le regole sono soddisfatte; il
client invia il messaggio al `chat_id` configurato chiamando
direttamente `https://api.telegram.org/bot<TOKEN>/sendMessage`.
| Metodo | Uso |
|---|---|
| `notify(message, priority, tag)` | Alert MEDIUM o messaggi informativi |
| `notify_position_opened(instrument, side, size, strategy, greeks, expected_pnl)` | Notifica di entry placed |
| `notify_position_closed(instrument, realized_pnl, reason)` | Notifica di exit filled |
| `notify_alert(source, message, priority)` | Alert HIGH (kill switch) |
| `notify_system_error(message, component, priority)` | Alert CRITICAL |
Quando le credenziali env non sono configurate, il client è in
modalità disabled e ogni invio diventa un no-op silente: il ciclo
decisionale non viene bloccato.
## Errori e degradation
| Componente fuori uso | Comportamento |
|---|---|
| `cerbero-deribit` | **Hard fail**: senza dati di mercato e canale di esecuzione il ciclo viene saltato; in monitor le posizioni esistenti restano nello stato corrente, alert HIGH e kill switch |
| `cerbero-hyperliquid` | Skip del filtro funding §2.6 con warning; il ciclo prosegue se le altre condizioni sono soddisfatte |
| `cerbero-sentiment` | Bias §3.1 cade su `no_entry` per default (senza funding cross il bias non può fissare la direzione) |
| `cerbero-macro` | Hard fail per il filtro §2.5 e per la conversione EUR/USD del portfolio aggregator; senza calendar/FX non si apre |
| Portfolio aggregator (deribit o hyperliquid down) | I metodi di `PortfolioClient` propagano l'eccezione dell'exchange sottostante; il sizing engine si comporta come per un guasto MCP del livello inferiore |
| Telegram client | Errore HTTP o `ok=false` dalla Bot API → `TelegramError` propagata dal chiamante. In modalità disabled (env mancanti) tutti i `notify_*` sono no-op silenti e il ciclo decisionale prosegue |
I trigger HIGH e CRITICAL armano il kill switch e propagano un alert
in audit chain.
## Verifica ambiente al boot
All'avvio l'orchestrator (`runtime/orchestrator.boot`) chiama
`cerbero-deribit.environment_info` e confronta il campo `environment`
con `strategy.execution.environment`. Un `mismatch` (per esempio engine
configurato per `testnet` ma server agganciato a `mainnet`) produce un
alert CRITICAL e arma il kill switch prima che qualsiasi ciclo
trading parta. La stessa verifica viene ripetuta dal probe periodico
(ogni 5 minuti) di `runtime/health_check.HealthCheck`.
## Versioning
I server MCP non espongono attualmente un endpoint `get_version()`
formale; il check di compatibilità si limita a `environment_info` per
Deribit e a un round-trip lightweight sui tool read-only degli altri
servizi nel job di health check. Quando i server pubblicheranno il
versionamento esplicito, l'orchestrator confronterà al boot le
versioni con la tabella `EXPECTED_MCP_VERSIONS` e armerà il kill
switch su mismatch.