Commit Graph

116 Commits

Author SHA1 Message Date
root 92cc45c896 refactor(V2): IBKR settings — TypedDict return + docstrings
Code review polish:
- credentials() returns IBKRCredentials TypedDict (was bare dict)
- Method docstring matching Deribit pattern
- Inline comment explaining account_id env-only design

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 20:04:08 +00:00
root 3a85ff05e6 feat(V2): IBKR settings + env-specific credentials
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 20:00:15 +00:00
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
root 109b8e4686 docs(V2): IBKR integration design spec
Approach A2 (Client Portal Web API + OAuth 1.0a Self-Service): fully
unattended REST con setup iniziale one-time, niente sidecar Java.
Scope V1 include: simple+complex orders (bracket/OCO/OTO), WebSocket
streaming snapshot-on-demand (tick+depth), key rotation semi-automatica
con auto-rollback. 8-commit plan atomico, ~6-8 giorni dev.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 19:23:08 +00:00
root 1ca1687c9b feat(V2): Deribit credenziali per env (CLIENT_ID/SECRET _TESTNET / _LIVE)
DeribitSettings ora supporta coppie credenziali distinte per testnet e
mainnet via DERIBIT_CLIENT_ID_TESTNET/_LIVE e DERIBIT_CLIENT_SECRET_TESTNET/_LIVE.
Le coppie env-specifiche prevalgono sulla coppia base
DERIBIT_CLIENT_ID/DERIBIT_CLIENT_SECRET (mantenuta per backward compat).

build_client risolve la coppia giusta tramite settings.deribit.credentials(env);
ValueError esplicito se nessuna coppia configurata per l'env richiesto.

+4 test (legacy single, per-env, override, missing). Fix anche isolation
da .env reale via monkeypatch.chdir(tmp_path).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 14:46:47 +00:00
root 8a0f37ebc2 fix(V2): get_account_summary error path → numeric fields None invece di 0
Su errore di Deribit (auth fallita, ecc.) i campi equity/balance/margin/
available/unrealized_pnl/total_pnl ora sono None: signal chiaro di "valore
ignoto" vs "saldo realmente a zero". Risolve ambiguità lato client che
leggevano equity=0 senza accorgersi del campo error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 13:00:57 +00:00
root 6640ede3df fix(V2): Deribit _authenticate gestisce error envelope (no più KeyError 'result')
Quando Deribit risponde con {"error": {...}} su public/auth (creds errate,
scope mancante, env mismatch), il client esplodeva con KeyError: 'result' →
500 UNHANDLED_EXCEPTION sui tool privati (get_account_summary, get_positions).
Ora _authenticate solleva DeribitAuthError tipizzata, _request la converte
in error envelope coerente con il resto del flusso.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 12:52:43 +00:00
root d8136713b9 feat(V2): integrazione Traefik con TLS + watchtower, rimosso port mapping diretto
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 09:21:52 +00:00
AdrianoDev 9e7b98579b chore(V2): branch V2.0.0 come default deploy (no merge in main)
deploy-vps.sh: BRANCH default V2.0.0 invece di main.
README: clone con -b V2.0.0, nota che il branch in produzione è V2.0.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 10:31:58 +02:00
AdrianoDev 51081f4e18 feat(V2): deploy-vps.sh per deploy via clone (no registry)
Il deploy ora avviene clonando il repo direttamente sul VPS, costruendo
l'immagine in loco e riavviando il container. Sostituisce il workflow
build & push verso registry + Watchtower.

Lo script automatizza:
- git fetch + reset --hard origin/<branch>
- docker compose build
- restart graceful (down 15s + up -d)
- attesa healthcheck con timeout configurabile
- rollback automatico al SHA precedente se /health fallisce

Variabili: BRANCH, PORT, HEALTH_TIMEOUT_SECONDS, FORCE, SKIP_ROLLBACK.

Rimosso scripts/build-push.sh (workflow registry abbandonato).
README aggiornato con la nuova procedura.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 09:05:26 +02:00
AdrianoDev 8ecc1a24a9 feat(V2): /health/ready con ping client + middleware request log strutturato + request_id correlation
- /health/ready: ping di tutti i client (exchange, env) cached con
  timeout 2s, status ready|degraded|not_ready, opt-in 503 via
  READY_FAILS_ON_DEGRADED.
- Middleware mcp.request: 1 riga JSON per HTTP request con request_id,
  method, path, status_code, duration_ms, actor, bot_tag, exchange,
  tool, client_ip, user_agent.
- request_id propagato in request.state, audit log e error envelope per
  correlazione cross-cutting.
- Aggiunto async health() come probe minimo a bybit/alpaca/macro/
  sentiment/deribit (hyperliquid lo aveva già).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 09:03:28 +02:00
AdrianoDev 9afd087152 docs(V2): aggiorna conteggio test 259 → 310 nel README
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 08:52:11 +02:00
AdrianoDev 69ac878893 feat(V2): X-Bot-Tag header obbligatorio + endpoint /admin/audit con filtri
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 08:51:40 +02:00
AdrianoDev bd6b03ce43 feat(V2): cabla audit logging nei write endpoint dei 4 router exchange
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 08:44:28 +02:00
AdrianoDev 43bf8fc461 chore(V2): rimuovi SDK obsoleti (pybit, alpaca-py, hyperliquid-python-sdk)
Sweep finale del task #14: tutti e 4 i client exchange ora usano httpx
puro (deribit già lo era; bybit/alpaca/hyperliquid riscritti nei commit
precedenti). Rimosso anche l'override mypy per i moduli SDK che non
servono più.

Quality gate finale:
- 292 test passano (tutti)
- mypy: 0 issues
- ruff: clean
- Nessun import SDK in src/

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 01:39:53 +02:00
AdrianoDev c0b4cb5d5c refactor(V2): hyperliquid client da SDK a httpx + eth-account EIP-712 (parità V1)
Riscritto interamente HyperliquidClient su httpx puro + eth-account per la
firma EIP-712 L1 (chainId 1337, phantom agent source 'a'/'b' per
mainnet/testnet). Bit-parity verificata contro hyperliquid.utils.signing
in test_signing_parity_with_canonical_sdk.

16 metodi pubblici, 26 test passanti. Aggiunte deps: eth-account, msgpack,
eth-utils. hyperliquid-python-sdk ancora presente nel pyproject; rimossa
nel sweep finale.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 01:39:23 +02:00
AdrianoDev 44c7a18d3e refactor(V2): alpaca client da alpaca-py a httpx puro (parità V1)
Riscrive `AlpacaClient` su `httpx.AsyncClient` rimuovendo ogni dipendenza
runtime da `alpaca-py`. 4 endpoint base distinti (trading paper/live,
stock data, crypto data, options data) gestiti via helper `_request`
con header `APCA-API-KEY-ID` / `APCA-API-SECRET-KEY`. Firma costruttore
e attributi pubblici (`paper`, `base_url`) invariati; `base_url` override
applica al solo trading endpoint. Nuovo `aclose()` per cleanup connessioni.

Test riscritti su `pytest-httpx` (29 test alpaca + leverage cap).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 01:38:23 +02:00
AdrianoDev 6097dde4e4 refactor(V2): bybit client da pybit a httpx puro (parità V1) 2026-05-01 01:35:26 +02:00
AdrianoDev 95b8bcfe96 docs(V2): aggiorna README con override URL .env, layout src, quality gate
- Aggiunte caratteristiche: override URL upstream da DERIBIT_URL_*, BYBIT_URL_*, ecc.
- Aggiunto badge qualità: 259 test, mypy clean, ruff clean
- Aggiunta sezione layout src/cerbero_mcp/
- Documentate quirk SDK su override URL (Bybit pybit endpoint, Alpaca trading-only)
- Linkato anche il plan oltre alla spec

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 00:04:50 +02:00
AdrianoDev 697d118522 chore(V2): mypy clean — fix radice V2 nuovo + suppress mirato V1 legacy
- settings.py: lambda factory + type:ignore[call-arg] per env-loaded models
- routers/*.py (6 file): cast esplicito Environment / Client per request.state
- __main__.py: cast Literal env in builder, type:ignore Settings()
- server.py: type:ignore[method-assign] su app.openapi
- deribit/tools.py: assert su validator-normalized fields, list return type
- deribit/client.py: type:ignore mirato no-any-return / has-type, rinomina types→types_list
- hyperliquid/{client,tools}.py: assert su validator-normalized fields, var-annotated
- alpaca/client.py: type:ignore mirato per SDK quirks (assignment, no-any-return, arg-type, union-attr)
- {macro,sentiment}/fetchers.py: type:ignore mirato no-any-return / operator / union-attr

Mypy: 68 → 0 errors. Test: 259 passing. Ruff: clean.
2026-04-30 20:43:03 +02:00
AdrianoDev 436dfd6f5a feat(V2): URL exchange configurabili da .env (DERIBIT_URL_*, BYBIT_URL_*, ecc.) 2026-04-30 20:36:31 +02:00
AdrianoDev b71c66917c chore(V2): cleanup quality gate
- ruff: contextlib.suppress al posto di try/except/pass (client_registry, test_env_routing)
- rimozione services/ legacy (residuo da git rm)
- fix integration test fixture: rimosso sys.modules.pop che inquinava module references nei test successivi (test_audit, test_client_init_default_http)

254 test passano. Ruff: clean. Mypy: 68 warning preesistenti dal codice V1 migrato (strict=false).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 19:02:55 +02:00
AdrianoDev b552127479 docs(V2): README riscritto per architettura V2.0.0
- Singola immagine Docker, routing testnet/mainnet via bearer token
- Configurazione interamente in .env
- Swagger /apidocs
- Sezione migrazione V1 → V2 con tabella mapping campi
- Riferimento alla spec di design

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 19:00:33 +02:00
AdrianoDev 50bc6b64b4 chore(V2): build-push.sh costruisce 1 sola immagine V2.0.0; rimosso deploy-noclone.sh
Lo script ora pubblica un solo tag cerbero-mcp:2.0.0 + :latest + :sha-<short>.
deploy-noclone.sh era specifico del workflow V1 multi-image.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 18:59:27 +02:00
AdrianoDev ec42d141bd chore(V2): rimuovi compose overlay V1 (prod, local, traefik) e DEPLOYMENT.md
Contenuti utili di DEPLOYMENT.md saranno integrati nel nuovo README V2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 18:58:51 +02:00
AdrianoDev 6d19165d9e chore(V2): rimuovi services/, gateway/, secrets/, docker/ (legacy V1)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 18:58:11 +02:00
AdrianoDev 1c1b3e1570 test(V2): smoke script con bearer testnet 2026-04-30 18:57:07 +02:00
AdrianoDev cee7f7ca2f feat(V2): docker-compose.yml minimo (1 servizio, env_file .env) 2026-04-30 18:55:23 +02:00
AdrianoDev 6148461ac1 feat(V2): Dockerfile unico multi-stage in root
Build multi-stage builder/runtime, uv sync --frozen, utente non-root uid 1000,
healthcheck su /health, CMD cerbero-mcp. Smoke test passato: /health 200 OK,
immagine cerbero-mcp:2.0.0 229MB content size.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 18:54:38 +02:00
AdrianoDev f34452b2dd test(V2): integration env routing per ogni exchange (constructor spy)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 18:51:30 +02:00
AdrianoDev a53efb7a29 feat(V2): __main__ con lifespan + 6 router + integration test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 18:48:56 +02:00
AdrianoDev f56df197e1 feat(V2): migrazione sentiment completa (read-only, env ignored)
- exchanges/sentiment/{client,fetchers,tools}.py: SentimentClient wrapper stateless (cryptopanic_key, lunarcrush_key)
- routers/sentiment.py: 9 tool POST sotto /mcp-sentiment (news, social, funding, OI, liquidations, cointegration)
- exchanges/__init__.py: branch builder per sentiment (env ignored)
- tests/unit/exchanges/sentiment: migrato test_fetchers, scartato test_server_acl V1-only
- tests/unit/test_exchanges_builder.py: aggiunto test_build_client_sentiment_no_env_distinction
- fetchers.py: env var lookup allineato a LUNARCRUSH_KEY (con fallback LUNARCRUSH_API_KEY)

241 test passano.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 18:46:48 +02:00
AdrianoDev 88bd4e7bde feat(V2): migrazione macro completa (read-only, env ignored)
- exchanges/macro: cot.py + cot_contracts.py + fetchers.py copiati 1:1 con
  rewrite import mcp_common -> cerbero_mcp.common, mcp_macro -> cerbero_mcp.exchanges.macro
- nuovo MacroClient stateless wrapper: trasporta solo fred_api_key/finnhub_api_key,
  niente HTTP session (i fetchers usano async_client ad-hoc)
- tools.py: 11 tool (get_treasury_yields, get_yield_curve_slope,
  get_breakeven_inflation, get_economic_indicators, get_macro_calendar,
  get_market_overview, get_equity_futures, get_asset_price, get_cot_tff,
  get_cot_disaggregated, get_cot_extreme_positioning) — niente write,
  niente leverage_cap
- routers/macro.py: prefix /mcp-macro, 11 route POST /tools/*
- builder branch macro: stesse credenziali per testnet/mainnet (env ignorato);
  registry istanzia 2 entry, costo trascurabile (wrapper stateless)
- test migrati: test_cot.py + test_fetchers.py (test_server_acl.py skippato V1-only)
- nuovo test test_build_client_macro_no_env_distinction in test_exchanges_builder.py

Suite: 224 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 18:42:55 +02:00
AdrianoDev 1b8ba0ef9c feat(V2): migrazione alpaca completa
Task 6.7: porting alpaca da services/mcp-alpaca a src/cerbero_mcp.
client.py + leverage_cap.py copiati 1:1 (default cap 1 cash).
tools.py: 17 tool senza ACL/Principal/audit. Router /mcp-alpaca con 18
route (env_info + 17 tool). Builder branch alpaca: paper=(env=="testnet"),
api_key viene da settings.alpaca.api_key_id. Test client + leverage_cap
migrati (15 test alpaca pass). Test builder con stub SDK alpaca-py.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 18:39:25 +02:00
AdrianoDev 8dbaf3a0e4 feat(V2): migrazione hyperliquid completa
- exchanges/hyperliquid/{client,leverage_cap,tools}.py
- routers/hyperliquid.py con 16 endpoint /mcp-hyperliquid/tools/*
- builder hyperliquid in exchanges/__init__.py
- test migrati: test_client, test_leverage_cap (skip V1: server_acl, environment_info)
- test builder hyperliquid (testnet vs mainnet base_url)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 18:35:46 +02:00
AdrianoDev 5e42ce9c69 feat(V2): migrazione bybit completa (client, tools, router, test, builder)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 18:31:51 +02:00
AdrianoDev a8d970233e feat(V2): builder client centralizzato (solo deribit per ora)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 18:27:50 +02:00
AdrianoDev d3ec2ee588 feat(V2): router deribit + test migrati
Router /mcp-deribit/* monta 34 tool (28 read + 6 write) come endpoint
POST /mcp-deribit/tools/{tool_name}, con DI per env (request.state) e
client (ClientRegistry). Write tools costruiscono creds minimale
{max_leverage, client_id} da settings per leverage cap enforcement.

Test deribit migrati: test_client.py + test_leverage_cap.py riassegnati
sotto tests/unit/exchanges/deribit/ con import rewrite mcp_* -> cerbero_mcp.*.
Skip dei legacy V1-only test_environment_info / test_server_acl / test_env_validation
(ACL e resolve_environment eliminati in V2).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 18:26:34 +02:00
AdrianoDev daa4e02971 feat(V2): migrazione deribit (client, leverage_cap, tools)
Task 6.1 V2.0.0: copia client.py + leverage_cap.py da services/mcp-deribit
con import riscritti (mcp_common -> cerbero_mcp.common, mcp_deribit ->
cerbero_mcp.exchanges.deribit). Estratte 34 tool async (28 endpoint +
is_testnet/environment_info + helpers) in tools.py: pure logica senza
FastAPI/ACL. Audit calls per ora rimossi (TODO: cabling via router su
request.state.environment).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 18:23:44 +02:00
AdrianoDev 2a268b3a33 feat(V2): build_app con swagger /apidocs + middleware + handlers
Aggiunge /docs e /redoc alla whitelist auth (path disabilitati, nessun rischio sicurezza).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 18:20:17 +02:00
AdrianoDev 73f880e7f2 feat(V2): ClientRegistry lazy con lock per chiave
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 18:18:18 +02:00
AdrianoDev 80a4a88cb1 feat(V2): error envelope module estratto da server.py 2026-04-30 18:17:15 +02:00
AdrianoDev 993326136b test(V2): migrazione test common/
Copiati e aggiornati i test da services/common/tests/ a tests/unit/common/.
Import aggiornati da mcp_common a cerbero_mcp.common. Eliminati test di
funzionalità V1-only (app_factory, environment, auth/Principal, server_base).
Refactored test_audit.py (principal→actor str) e test_mcp_bridge.py
(TokenStore→valid_tokens set). 71/71 test passano.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 18:16:26 +02:00
AdrianoDev 1a1f9c43ba refactor(V2): audit.py usa actor:str invece di Principal, rimuovi legacy common/auth.py
- Eliminato src/cerbero_mcp/common/auth.py (V1 Principal/TokenStore/ACL)
- audit_write_op: parametro principal:Principal → actor:str|None
- mcp_bridge.py: TokenStore → valid_tokens:set[str] (V2 bearer model)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 18:14:10 +02:00
AdrianoDev 3868ba60ce feat(V2): migrazione common/ (indicators, options, microstructure, stats, http, audit, logging, mcp_bridge + auth) 2026-04-30 18:12:11 +02:00
AdrianoDev 04a34fc179 fix(V2): hoist fastapi Request import, ripristina importlib mode 2026-04-30 18:10:41 +02:00
AdrianoDev 2934a2d26a feat(V2): bearer auth middleware con compare_digest
Implementa install_auth_middleware con whitelist /health /apidocs /openapi.json,
token timing-safe via secrets.compare_digest, request.state.environment injection.
Fix pyproject: --import-mode=prepend (importlib + PEP563 rompe FastAPI Request injection).
Rimosso from __future__ import annotations da test_auth.py per stesso motivo.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 18:09:21 +02:00
AdrianoDev 97d93a5139 feat(V2): pydantic settings con secret str + test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 18:04:40 +02:00
AdrianoDev 005300205b chore(V2): .env.example consolidato, .env gitignored 2026-04-30 18:03:22 +02:00
AdrianoDev 8df64b5176 chore(V2): scheletro src/cerbero_mcp + tests/
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 18:02:22 +02:00