Espone private/get_open_orders_by_currency sul router Deribit (pattern gia'
presente per Hyperliquid/IBKR). Serve al reconciler resting di PythagorasGoal:
TP/DSL attesi dai libri vs ordini realmente in book (caso MR02_BTC 2026-06-12:
TP resting fillato di notte + disaster-SL sparito, scoperti solo al close sim).
NB: per i trigger untriggered interrogare anche type='trigger_all' e fare
merge per order_id.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Stale certificate entries (mattermost/zar/registry.tielogic.xyz) in
Traefik's acme.json triggered repeated NXDOMAIN renewal errors. Script
atomically stops Traefik, backs up acme.json, filters out the listed
domains with JSON validation + auto-rollback, restores 600 root:adriano
perms, and restarts. Domain list is extensible via the STALE array.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The VPS doesn't publish the app port to the host (Traefik reaches it over
the docker network), so curl http://localhost:PORT/health always failed —
a false negative that could trigger an unwanted automatic rollback on a
successful deploy. Check /health from INSIDE the container via
`docker compose exec` instead.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
git mv docs/API_REFERENCE.md → API_REFERENCE.md (history preserved) and
update the CLAUDE.md reference accordingly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move get_instruments off the per-exchange Deribit router and onto the
unified interface, without losing the advanced Deribit filtering.
- /mcp/tools/get_instruments now accepts the Deribit-specific filters
(currency, kind, expiry_from/to, strike_min/max, min_open_interest) and
pagination (offset/limit). Venues that list everything (Hyperliquid)
ignore them. Response adds `meta: {deribit: {total, offset, limit,
has_more}}` surfacing per-source pagination.
- Remove /mcp-deribit/tools/get_instruments (endpoint + tool wrapper +
GetInstrumentsReq schema). The DeribitClient.get_instruments METHOD
stays — it's what the unified normalizer calls.
Tests: cover the enriched filters + meta passthrough; app-boot asserts
/mcp-deribit/tools/get_instruments is gone. Docs (API_REFERENCE/README/
CLAUDE) and smoke updated; also fixed the stale Hyperliquid tool count
(14 → 15) found during the doc check.
325 passed, ruff clean. Verified live: kind=option limit=5 → 5/940
has_more=true; /mcp-deribit/tools/get_instruments → 404.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Consolidate the cross-cutting data tools onto the unified interface and
remove their per-exchange duplicates.
- /mcp/tools/get_historical: single call, `exchange` now optional. Set →
that venue's candles (native symbol); omitted → cross-exchange consensus
(canonical symbol, median OHLC + div_pct). Folds in the former
/mcp-cross consensus, whose router is deleted.
- /mcp/tools/get_indicators: new common tool computing sma/rsi/atr/macd/adx
over the single-or-consensus series. Computation centralized in
common/indicators.py::compute_indicators (was duplicated per exchange).
- Remove get_historical from /mcp-deribit and /mcp-hyperliquid; remove
get_technical_indicators (deribit) and get_indicators (hyperliquid),
including the now-dead client methods, Req schemas and tool wrappers.
The client get_historical METHODS stay (used by the unified dispatch).
Tests: rewrite cross tests into test_unified (instruments, historical
single + consensus + failures, indicators single + consensus); drop the
old CrossClient test; tighten app-boot surface assertions (/mcp owns the
data tools; no /mcp-cross, no per-exchange historical/indicators).
Docs: API_REFERENCE / README / CLAUDE updated; smoke run_unified.sh now
covers consensus + indicators.
324 passed, ruff clean. Verified live against Deribit/Hyperliquid.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
tests/smoke/run_unified.sh exercises the common interface against a live
server + real upstreams (Deribit/Hyperliquid public endpoints):
get_instruments (single + fan-out, asserting fees/history_start),
get_historical for both venues, and a 422 for an unsupported exchange.
Refresh tests/smoke/README.md (drop stale Bybit/Alpaca references).
Verified green end-to-end.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- README: three active exchanges (Deribit/Hyperliquid/IBKR); document the
unified /mcp interface (get_instruments uniform schema + per-exchange
get_historical); drop Bybit/Alpaca from endpoints, tool list, audit
filters, migration table and URL-override notes; update test count (323)
and source layout (routers/unified.py, old/).
- CLAUDE.md: new project guide — commands (uv/pytest/ruff/mypy), runtime
architecture, active exchanges, data interfaces, conventions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a common cross-exchange interface (/mcp) over the integrated venues
(deribit, hyperliquid):
- get_instruments: uniform schema where each row carries its own
`exchange`, `fees` (maker/taker, live from Deribit, null where the
venue has no per-instrument schedule) and `history_start` (listing
date, live from Deribit creation_timestamp), plus type/tick_size and a
lossless `native` blob. Optional `exchange` filter; fan-out otherwise.
- get_historical: generalized to {exchange, instrument, interval,
start_date, end_date}, returning a single chosen venue's candles.
Consensus merge stays available on /mcp-cross.
New: routers/unified.py, exchanges/cross/instruments.py (normalizers),
UnifiedClient in cross/client.py, schemas in cross/tools.py. Deribit
get_instruments now also surfaces maker/taker_commission and
creation_timestamp (additive).
Retire Bybit and Alpaca from the API surface: move clients, routers,
settings classes and their tests under old/ (history preserved via
git mv); drop them from the builder, /mcp-cross dispatch and symbol_map.
Bybit remains a public funding/OI data source in sentiment (not the
trading client). IBKR is intentionally excluded from /mcp for now.
Docs: rewrite API_REFERENCE.md (remove Bybit/Alpaca, document /mcp,
clarify that data_timestamp is injected globally by middleware).
Tests: add unified-interface coverage; update cross/settings/builder/boot
tests for the reduced venue set. Fix a pre-existing flaky assertion in
the Hyperliquid signing test (r/s use eth_utils.to_hex like the official
SDK, so a leading zero byte yields <66 chars ~1/256 of the time).
323 passed, ruff clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
end_date as a bare date now covers the whole UTC day (23:59:59.999)
instead of stopping at midnight, so end_date=today returns intraday up
to the last closed candle. Dates are parsed as explicit UTC and inline
timestamps (YYYY-MM-DDTHH:MM:SS) are honored for precise windows.
Wide ranges are paged in <=5000-candle windows so Deribit's per-call
cap can no longer silently drop start_date and the oldest candles.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Inventory of all 8 MCP namespaces (~142 tools) generated from the
FastAPI router sources — single reference for client/bot integrators.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a unified historical endpoint that fans out to every exchange
supporting the requested (asset_class, symbol) pair, then merges the
results into a single consensus candle series with per-bar divergence
metrics:
- candles[i].close = median across sources
- candles[i].sources = count of contributing exchanges
- candles[i].div_pct = (max-min)/median for that bar's close
Crypto routes BTC/ETH/SOL across Bybit + Hyperliquid + Deribit; equities
route to Alpaca for now (IBKR omitted from MVP because its bars endpoint
takes a relative period instead of start/end). Partial failures return a
warning envelope (failed_sources) instead of failing the whole request;
all sources failing → HTTP 502.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduce common/candles.py with a Pydantic Candle model enforcing OHLC
consistency (high≥max, low≤min), non-negative volume and positive
timestamp. validate_candles() coerces upstream rows, sorts by timestamp
and raises HTTPException(502) on malformed data — surfacing upstream
data corruption as a retryable envelope instead of silently returning
nonsense.
Wired into all five exchange historical endpoints (Bybit, Hyperliquid,
Deribit, Alpaca, IBKR). BREAKING: Alpaca get_bars and IBKR get_bars now
return 'candles' (was 'bars') to align with the other exchanges.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add IBKR to the exchange list, endpoint table, audit filter values, and
Tool disponibili. Bump test count to 366 and reorder IBKR Setup before
Licenza.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Defining the Pydantic body model inside make_admin_router() leaves an
unresolved forward reference under `from __future__ import annotations`,
which breaks /openapi.json generation with PydanticUserError.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cloudflare 5xx pages from Deribit testnet were leaking through the JSON
parser as JSONDecodeError → UNHANDLED_EXCEPTION. Wrap response parsing so
upstream errors surface as a retryable HTTP_502 envelope instead.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Final code-review fixes:
- __main__: lifespan stops IBKRWebSocket singletons before registry close
- close_position: resolve symbol→conid first, match positions on conid
(was matching contractDesc which is a long display string, not ticker)
- close_all_positions: prefer ticker field, fallback to contractDesc
- get_clock: explicit approximate=true + note about US holidays/half-days
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>
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>