root 391f2c02e0 docs(V2): IBKR integration implementation plan
16 task TDD-disciplinati con 94 step checkbox, riferimento allo spec
2026-05-03. Ogni task: red-green-commit con codice completo nello step.
Copre: settings, OAuth1a signer + DH LST mint, IBKRClient REST + conid
cache + tickle, IBKRWebSocket tick/depth snapshot-on-demand, simple +
complex orders (bracket/OCO/OTO), KeyRotationManager con auto-rollback,
admin endpoints, router wiring, OAuth setup script, docs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 19:55:38 +00:00

Cerbero MCP — V2.0.0

Server MCP unificato multi-exchange per la suite Cerbero. Distribuito come singola immagine Docker; testnet e mainnet sono raggiungibili contemporaneamente attraverso un meccanismo di routing per-request basato sul token bearer fornito dal client.

Caratteristiche

  • Una singola immagine Docker (cerbero-mcp) ospita tutti i router exchange in un unico processo FastAPI
  • Quattro exchange (Deribit, Bybit, Hyperliquid, Alpaca) e due data provider read-only (Macro, Sentiment)
  • Switch testnet/mainnet per-request tramite header Authorization: Bearer <TOKEN>: lo stesso container serve entrambi gli ambienti senza riavvii
  • Configurazione interamente in .env: nessun file JSON di credenziali separato; le URL upstream (live/testnet) di ciascun exchange sono override-abili tramite variabili dedicate (DERIBIT_URL_*, BYBIT_URL_*, HYPERLIQUID_URL_*, ALPACA_URL_*)
  • Documentazione interattiva OpenAPI/Swagger esposta a /apidocs
  • Qualità verificata: 310 test (unit + integration + smoke), mypy pulito, ruff pulito

Avvio rapido (sviluppo, senza Docker)

  1. Copiare il template di configurazione e compilarlo:
    cp .env.example .env
    # editare .env con le proprie credenziali e i due token
    
  2. Generare i token bearer:
    python -c 'import secrets; print("TESTNET_TOKEN=" + secrets.token_urlsafe(32))'
    python -c 'import secrets; print("MAINNET_TOKEN=" + secrets.token_urlsafe(32))'
    
  3. Installare le dipendenze e avviare:
    uv sync
    uv run cerbero-mcp
    
  4. Aprire la documentazione interattiva: http://localhost:9000/apidocs

Avvio con Docker

cp .env.example .env  # compilare valori
docker compose up -d

Il container espone la porta indicata da PORT in .env (default 9000).

Token bearer e ambienti

Token usato Ambiente upstream
Authorization: Bearer $TESTNET_TOKEN URL testnet di ciascun exchange
Authorization: Bearer $MAINNET_TOKEN URL mainnet (live)
Nessun token / token sconosciuto 401 Unauthorized

Le tool puramente read-only (/mcp-macro/* e /mcp-sentiment/*) richiedono comunque un bearer valido, ma il valore (testnet o mainnet) è indifferente perché non hanno endpoint testnet.

Header X-Bot-Tag (identificazione bot)

Tutte le chiamate a /mcp-* richiedono inoltre l'header X-Bot-Tag con una stringa identificativa del bot chiamante (massimo 64 caratteri). Il valore viene loggato negli audit record per tracciare quale bot ha eseguito ogni operazione write. Esempio di richiesta:

Authorization: Bearer $MAINNET_TOKEN
X-Bot-Tag: scanner-alpha-prod

Se l'header è assente o vuoto la risposta è 400 BAD_REQUEST. L'header non è richiesto sugli endpoint pubblici (/health, /apidocs, /openapi.json) né sull'endpoint admin /admin/audit.

Endpoint principali

Path Descrizione
GET /health Liveness check (no auth)
GET /health/ready Readiness check con ping client exchange (no auth)
GET /apidocs Swagger UI (no auth)
GET /openapi.json Schema OpenAPI 3.1 (no auth)
POST /mcp-deribit/tools/{tool} Tool exchange Deribit
POST /mcp-bybit/tools/{tool} Tool exchange Bybit
POST /mcp-hyperliquid/tools/{tool} Tool exchange Hyperliquid
POST /mcp-alpaca/tools/{tool} Tool exchange Alpaca
POST /mcp-macro/tools/{tool} Tool macro/market data
POST /mcp-sentiment/tools/{tool} Tool sentiment/news
GET /admin/audit Query dell'audit log JSONL (bearer richiesto, no X-Bot-Tag)

Observability

Health check

L'applicazione espone due endpoint distinti per il monitoring:

  • GET /health — liveness check semplice. Non richiede autenticazione e ritorna sempre HTTP 200 finché il processo è vivo. Ideale per la liveness probe di Kubernetes o per il pinger di Traefik.
  • GET /health/ready — readiness check evoluto. Itera tutti i client exchange presenti nel registry e per ciascuno tenta una probe leggera (health() se disponibile, fallback su is_testnet()), con timeout di 2 secondi per client. La risposta contiene il campo status con uno dei valori ready (tutti i client rispondono), degraded (almeno uno fallisce) o not_ready (registry vuoto) ed un array clients con un record per ogni coppia (exchange, env) cached. Per default l'endpoint risponde sempre con HTTP 200; impostando la variabile d'ambiente READY_FAILS_ON_DEGRADED=true si forza HTTP 503 quando lo stato non è ready, comportamento utile per la readiness probe di Kubernetes.

Request log

Ogni richiesta HTTP attraversa un middleware che emette una riga JSON sul logger mcp.request con i seguenti campi: request_id, method, path, status_code, duration_ms, actor (testnet o mainnet, solo se autenticato), bot_tag (header X-Bot-Tag se presente), exchange (estratto dal path /mcp-{exchange}/...), tool (nome del tool quando il path è /mcp-X/tools/Y), client_ip, user_agent. Lo stesso request_id viene incluso anche nei record dell'audit log mcp.audit e nell'envelope di errore restituito al client, in modo da poter correlare le tre tracce a parità di richiesta.

Audit log

Vedi la sezione "Audit query" qui sotto per la consultazione del log strutturato delle operazioni di scrittura.

Audit query

GET /admin/audit legge il file JSONL puntato da AUDIT_LOG_FILE e restituisce i record filtrati. Richiede un bearer valido (testnet o mainnet); non richiede l'header X-Bot-Tag.

Parametri di query (tutti opzionali):

  • from, to: ISO 8601 datetime (es. 2026-05-01 o 2026-05-01T12:34:56Z)
  • actor: testnet | mainnet
  • exchange: nome dell'exchange (deribit, bybit, hyperliquid, alpaca)
  • action: nome del tool (es. place_order)
  • bot_tag: identificatore del bot
  • limit: massimo record restituiti, default 1000, massimo 10000

Esempio di chiamata:

curl -H "Authorization: Bearer $MAINNET_TOKEN" \
     "http://localhost:9000/admin/audit?from=2026-05-01&actor=mainnet&action=place_order&limit=100"

Se AUDIT_LOG_FILE non è configurata l'endpoint risponde count: 0 con un campo warning. Per abilitare il sink persistente impostare nel .env:

AUDIT_LOG_FILE=/var/log/cerbero-mcp/audit.jsonl
AUDIT_LOG_BACKUP_DAYS=30

Tool disponibili

Common (cerbero_mcp.common.indicators + options + microstructure + stats)

Tecnici (sma, rsi, macd, atr, adx), volatilità (vol_cone, garch11_forecast), statistici (hurst_exponent, half_life_mean_reversion, cointegration_test), risk (rolling_sharpe, var_cvar), microstructure (orderbook_imbalance), options (oi_weighted_skew, smile_asymmetry, dealer_gamma_profile, vanna_charm_aggregate).

Deribit

DVOL, GEX, P/C ratio, skew_25d, term_structure, iv_rank, realized_vol, indicatori tecnici, find_by_delta, calculate_spread_payoff, get_dealer_gamma_profile, get_vanna_charm, get_oi_weighted_skew, get_smile_asymmetry, get_atm_vs_wings_vol, get_orderbook_imbalance, place_combo_order.

Bybit

Ticker, orderbook, OHLCV, funding rate, open interest, basis spot/perp, indicatori tecnici, place_batch_order, get_orderbook_imbalance, get_basis_term_structure.

Hyperliquid

Account summary, positions, orderbook, historical, indicators, funding rate, basis spot/perp, place_order, set_stop_loss, set_take_profit.

Alpaca

Account, positions, bars, snapshot, option chain, place_order, amend_order, cancel_order, close_position.

Macro

Treasury yields, FRED indicators, equity futures, asset prices, calendar, get_yield_curve_slope, get_breakeven_inflation, get_cot_tff, get_cot_disaggregated, get_cot_extreme_positioning.

Sentiment

News (CryptoPanic/CoinDesk), social (LunarCrush), funding multi-exchange, OI history, get_funding_arb_spread, get_liquidation_heatmap, get_cointegration_pairs.

Deploy su VPS con Traefik

Sul VPS la rete pubblica (TLS, allowlist IP, rate limit) è gestita da Traefik esterno a questo repository. Il container cerbero-mcp non espone porte all'esterno: si registra alla rete docker di Traefik tramite label aggiunte da un override compose esterno (es. docker-compose.override.yml versionato fuori da questo repo). La policy di sicurezza pubblica (allowlist IP per gli endpoint write) è responsabilità di Traefik.

Esempio label minime per Traefik:

labels:
  - "traefik.enable=true"
  - "traefik.http.routers.cerbero.rule=Host(`cerbero-mcp.tielogic.xyz`)"
  - "traefik.http.routers.cerbero.entrypoints=websecure"
  - "traefik.http.routers.cerbero.tls.certresolver=letsencrypt"
  - "traefik.http.services.cerbero.loadbalancer.server.port=9000"

Build & deploy pipeline

Il deploy su VPS avviene per clone diretto del repo, senza passare per un container registry. Lo script scripts/deploy-vps.sh automatizza l'intero flusso: pull del ramo target, rebuild dell'immagine sulla macchina VPS, restart del servizio, healthcheck e rollback automatico in caso di fallimento.

Setup iniziale sul VPS (una sola volta)

# Sul VPS:
sudo mkdir -p /opt/cerbero-mcp
sudo chown -R "$USER":"$USER" /opt/cerbero-mcp
cd /opt/cerbero-mcp
git clone -b V2.0.0 ssh://git@git.tielogic.xyz:222/Adriano/Cerbero-mcp.git .
cp .env.example .env
# editare .env con i token e le credenziali reali

Il branch in produzione è V2.0.0 (non main). Lo script deploy-vps.sh fa default su questo ramo.

Deploy ricorrente

Da qualunque macchina con accesso SSH al VPS:

ssh user@vps 'cd /opt/cerbero-mcp && bash scripts/deploy-vps.sh'

Oppure direttamente dal VPS:

cd /opt/cerbero-mcp
bash scripts/deploy-vps.sh

Lo script:

  1. verifica che il working tree sia pulito e che .env sia presente;
  2. esegue git fetch + reset --hard origin/V2.0.0;
  3. se la SHA non è cambiata, esce senza fare nulla (override con FORCE=1);
  4. ricostruisce l'immagine Docker (docker compose build);
  5. restart graceful del container (docker compose down --timeout 15 seguito da docker compose up -d);
  6. attende /health (timeout 30 s di default);
  7. se l'health fallisce, esegue rollback automatico al SHA precedente.

Variabili d'ambiente accettate: BRANCH (default V2.0.0), PORT (default letto da .env), HEALTH_TIMEOUT_SECONDS, FORCE, SKIP_ROLLBACK.

Smoke test post-deploy

PORT=9000 TESTNET_TOKEN="$TESTNET_TOKEN" bash tests/smoke/run.sh

Sviluppo

uv sync
uv run pytest                   # tutta la suite (310 test attesi)
uv run pytest tests/unit -v     # solo unit
uv run pytest tests/integration -v
uv run ruff check src/ tests/
uv run mypy src/cerbero_mcp

Tutti e quattro i comandi devono ritornare verde prima di committare.

Layout sorgenti

src/cerbero_mcp/
├── __main__.py            # entrypoint cerbero-mcp
├── settings.py            # Pydantic Settings (legge .env)
├── auth.py                # middleware bearer → request.state.environment
├── server.py              # build_app() + Swagger + middleware + handlers
├── client_registry.py     # cache lazy {(exchange, env): client}
├── routers/               # un file per exchange (deribit, bybit, ...)
├── exchanges/             # logica per-exchange: client + tools
└── common/                # indicators, options, microstructure, stats, ...

Migrazione da V1 (1.x → 2.0.0)

Per chi è in produzione su V1:

  1. Backup secrets/ (V2 non li userà ma servono come fonte di copia).

  2. Generare i due nuovi token bearer (vedi sopra).

  3. Compilare .env mappando i campi V1 ai campi V2:

    V1 (file JSON) V2 (variabile .env)
    secrets/deribit.json client_id / client_secret DERIBIT_CLIENT_ID / DERIBIT_CLIENT_SECRET
    secrets/bybit.json api_key / api_secret BYBIT_API_KEY / BYBIT_API_SECRET
    secrets/hyperliquid.json wallet_address / private_key HYPERLIQUID_WALLET_ADDRESS / HYPERLIQUID_PRIVATE_KEY
    secrets/alpaca.json api_key_id / secret_key ALPACA_API_KEY_ID / ALPACA_SECRET_KEY
    secrets/macro.json fred_api_key / finnhub_api_key FRED_API_KEY / FINNHUB_API_KEY
    secrets/sentiment.json cryptopanic_key / lunarcrush_key CRYPTOPANIC_KEY / LUNARCRUSH_KEY
  4. Aggiornare i client bot:

    • i path API restano identici (/mcp-{exchange}/tools/{tool})
    • sostituire core.token / observer.token con TESTNET_TOKEN o MAINNET_TOKEN a seconda dell'ambiente desiderato per la chiamata
  5. Spegnere V1 (docker compose -f <vecchio compose> down) e avviare V2 (docker compose up -d).

  6. Verificare /health e /apidocs.

In caso di necessità è possibile fare rollback pullando i tag immagine V1 (cerbero-mcp-*:1.x); si ricordi però che .env e secrets/ sono formati incompatibili tra V1 e V2 — tenere backup separati.

Architettura

Spec di progettazione e plan di implementazione completi in:

Riepilogo del flusso runtime:

Bot → Authorization: Bearer <TESTNET|MAINNET>_TOKEN
        ↓
   FastAPI middleware auth → request.state.environment ∈ {testnet, mainnet}
        ↓
   Router /mcp-{exchange}/tools/{tool}
        ↓
   ClientRegistry.get(exchange, env) → client cached lazy (HTTP/WS pool riusato)
        ↓
   Tool function (logica pura) → exchange API

Override URL upstream

L'override delle URL upstream da .env è completo per Deribit e Hyperliquid. Per Bybit funziona tramite l'attributo endpoint interno di pybit (workaround documentato nel client). Per Alpaca l'override è applicato al solo trading endpoint: gli endpoint dati (data.alpaca.markets) restano quelli predefiniti dell'SDK.

Licenza

Privato.

S
Description
No description provided
Readme 1.7 MiB
Languages
Python 98.8%
Shell 1.1%
Dockerfile 0.1%