refactor(V2): get_historical & get_indicators are common-only on /mcp
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>
This commit is contained in:
+67
-69
@@ -50,16 +50,16 @@ richiede `X-Bot-Tag`.
|
||||
|
||||
| Namespace | Tool | Tipo | Note |
|
||||
|---|---|---|---|
|
||||
| `/mcp/tools/*` | 2 | **interfaccia comune** | schema strumenti uniforme + storico per-exchange |
|
||||
| `/mcp-deribit/tools/*` | 33 | exchange (options-first) | DVOL, GEX, dealer gamma, spread payoff |
|
||||
| `/mcp-hyperliquid/tools/*` | 16 | exchange (perp DEX) | L1 signing, leverage cap |
|
||||
| `/mcp/tools/*` | 3 | **interfaccia comune** | `get_instruments` + `get_historical` + `get_indicators` (single exchange o consensus) |
|
||||
| `/mcp-deribit/tools/*` | 32 | exchange (options-first) | DVOL, GEX, dealer gamma, spread payoff |
|
||||
| `/mcp-hyperliquid/tools/*` | 14 | exchange (perp DEX) | L1 signing, leverage cap |
|
||||
| `/mcp-ibkr/tools/*` | 24 | exchange (multi-asset broker) | OAuth 1.0a, streaming WS, bracket/OCO/OTO |
|
||||
| `/mcp-macro/tools/*` | 11 | data provider (read-only) | yields, FRED, COT |
|
||||
| `/mcp-sentiment/tools/*` | 9 | data provider (read-only) | news, social, funding cross-exchange |
|
||||
| `/mcp-cross/tools/*` | 1 | aggregator | storico consensus multi-exchange |
|
||||
|
||||
I router per-exchange (`/mcp-deribit`, …) restano disponibili durante la
|
||||
transizione; per nuovi consumatori è raccomandata l'interfaccia comune `/mcp`.
|
||||
`get_historical` e `get_indicators` vivono **solo** sull'interfaccia comune
|
||||
`/mcp` (non più per-exchange). I restanti tool per-exchange (ticker, ordini,
|
||||
analytics options, …) restano sui rispettivi router `/mcp-{exchange}`.
|
||||
|
||||
---
|
||||
|
||||
@@ -101,23 +101,46 @@ Risposta: `{ "instruments": [...], "failed_sources": [{exchange, error}] }`.
|
||||
> `creation_timestamp` → `history_start`); **Hyperliquid** ha fee a livello
|
||||
> account e nessuna data di listing per-strumento → entrambi `null`.
|
||||
|
||||
### `get_historical` — storico di un singolo exchange
|
||||
### `get_historical` — unica chiamata storica comune
|
||||
|
||||
Request body:
|
||||
`get_historical` esiste **solo qui** (non più per-exchange). Request body:
|
||||
|
||||
| Campo | Tipo | Default | Note |
|
||||
|---|---|---|---|
|
||||
| `exchange` | `"deribit"\|"hyperliquid"` | — | **obbligatorio**: sceglie la sorgente |
|
||||
| `instrument` | str | — | simbolo nativo sull'exchange scelto |
|
||||
| `exchange` | `"deribit"\|"hyperliquid"` \| null | `null` | set → singolo exchange · omesso → **consenso** multi-exchange |
|
||||
| `instrument` | str | — | simbolo *nativo* (modalità singola) o *canonico* `BTC/ETH/SOL` (consenso) |
|
||||
| `interval` | str | `"1h"` | `1m`/`5m`/`15m`/`1h`/`4h`/`1d` |
|
||||
| `start_date` | str | — | `YYYY-MM-DD` o ISO datetime (UTC) |
|
||||
| `end_date` | str | — | idem |
|
||||
| `asset_class` | str | `"crypto"` | routing del consenso (solo se `exchange` omesso) |
|
||||
|
||||
Risposta: `{ "exchange", "instrument", "interval", "candles": [...] }` con la
|
||||
chiave uniforme `candles: [{timestamp(ms), open, high, low, close, volume}]`.
|
||||
- **Singolo** (`exchange` valorizzato) → `{ exchange, instrument, interval, candles, sources_used:[exchange] }`.
|
||||
- **Consenso** (`exchange` omesso) → fan-out agli exchange che supportano il
|
||||
simbolo + merge per-bar: `{ exchange:null, instrument, asset_class, interval,
|
||||
candles, sources_used, failed_sources }`. Ogni bar consenso porta `sources`
|
||||
(n. exchange) e `div_pct = (max-min)/median` come quality gate. Se *tutti*
|
||||
gli upstream falliscono → `502` retryable.
|
||||
|
||||
Per il **consenso** multi-exchange (mediana cross-venue) usare invece
|
||||
`/mcp-cross/tools/get_historical` (sezione 11).
|
||||
Tutte le candele usano la chiave uniforme
|
||||
`candles: [{timestamp(ms), open, high, low, close, volume}]`.
|
||||
|
||||
> **Semantica `start_date`/`end_date`** (UTC). Date nude `YYYY-MM-DD`:
|
||||
> `start_date` = `00:00:00`, `end_date` = `23:59:59.999` dello stesso giorno
|
||||
> (giorno intero incluso → `end_date=oggi` dà l'intraday fino all'ultima candela
|
||||
> chiusa). Timestamp con orario (`2026-05-28T14:00:00`) sono onorati così come
|
||||
> sono (naive = UTC). Range ampi su Deribit sono paginati internamente (no cap
|
||||
> ~5000), così `start_date` è sempre rispettato.
|
||||
|
||||
### `get_indicators` — indicatori sulla stessa serie
|
||||
|
||||
Stessa selezione sorgente di `get_historical` (single o consensus); calcola
|
||||
indicatori tecnici sulle candele risultanti. Request body: i campi di
|
||||
`get_historical` più `indicators` (lista o stringa CSV, es. `["rsi","atr"]` o
|
||||
`"rsi,atr,macd"`; default `["rsi","atr","macd","adx"]`). Supportati:
|
||||
`sma`, `rsi`, `atr`, `macd`, `adx` (nomi ignoti → `null`).
|
||||
|
||||
Risposta: `{ exchange, instrument, interval, indicators:{...}, candles_used,
|
||||
sources_used }` (+ `failed_sources` in modalità consenso).
|
||||
|
||||
---
|
||||
|
||||
@@ -131,20 +154,11 @@ Per il **consenso** multi-exchange (mediana cross-venue) usare invece
|
||||
- `get_ticker`, `get_ticker_batch`
|
||||
- `get_instruments`
|
||||
- `get_orderbook`, `get_orderbook_imbalance`
|
||||
- `get_historical` (chiave `candles` uniforme)
|
||||
- `get_trade_history`
|
||||
|
||||
> **`get_historical` — semantica di `start_date` / `end_date`** (vale anche per
|
||||
> `get_dvol` e `get_technical_indicators`). Tutti i timestamp sono in **UTC**.
|
||||
> - Date nude `YYYY-MM-DD`: `start_date` = `00:00:00`, `end_date` =
|
||||
> `23:59:59.999` dello stesso giorno (inclusivo dell'intero giorno). Quindi
|
||||
> `end_date = oggi` restituisce l'intraday fino all'ultima candela chiusa, non
|
||||
> solo fino a mezzanotte.
|
||||
> - Sono accettati anche timestamp con orario (`2026-05-28T14:00:00`), onorati
|
||||
> così come sono per finestre intraday precise; un valore *naive* è trattato
|
||||
> come UTC.
|
||||
> - Nessun cap a ~5000 righe: i range ampi sono **paginati** internamente
|
||||
> finestra-per-finestra, così `start_date` è sempre rispettato.
|
||||
> Lo storico OHLCV e gli indicatori tecnici NON sono più qui: usa
|
||||
> `/mcp/tools/get_historical` e `/mcp/tools/get_indicators` (sezione 5) con
|
||||
> `exchange="deribit"`.
|
||||
|
||||
### Account
|
||||
- `get_positions`
|
||||
@@ -160,7 +174,6 @@ Per il **consenso** multi-exchange (mediana cross-venue) usare invece
|
||||
- `find_by_delta`, `calculate_spread_payoff`
|
||||
|
||||
### Technicals
|
||||
- `get_technical_indicators`
|
||||
- `run_backtest`
|
||||
|
||||
### Write (richiede leverage cap)
|
||||
@@ -178,15 +191,15 @@ Per il **consenso** multi-exchange (mediana cross-venue) usare invece
|
||||
|
||||
### Market data
|
||||
- `get_markets`, `get_ticker`, `get_orderbook`
|
||||
- `get_historical`, `get_trade_history`
|
||||
- `get_trade_history`
|
||||
- `get_funding_rate`, `basis_spot_perp`
|
||||
|
||||
> Storico e indicatori: usa `/mcp/tools/get_historical` e
|
||||
> `/mcp/tools/get_indicators` (sezione 5) con `exchange="hyperliquid"`.
|
||||
|
||||
### Account
|
||||
- `get_positions`, `get_account_summary`, `get_open_orders`
|
||||
|
||||
### Technicals
|
||||
- `get_indicators`
|
||||
|
||||
### Write (L1 signing + leverage cap)
|
||||
- `place_order`, `cancel_order`
|
||||
- `set_stop_loss`, `set_take_profit`
|
||||
@@ -269,27 +282,7 @@ unattended. Setup one-time per ambiente (paper + live) — vedi sezione
|
||||
|
||||
---
|
||||
|
||||
## 11. `/mcp-cross/tools/*`
|
||||
|
||||
### `get_historical`
|
||||
Aggregatore cross-exchange. Fan-out a tutti gli exchange che supportano
|
||||
`(symbol, asset_class)`, poi consensus per-bar:
|
||||
|
||||
- `close` = mediana delle close
|
||||
- `open`/`high`/`low` = mediana corrispondente
|
||||
- `sources` = numero di exchange che hanno contribuito al bar
|
||||
- `div_pct = (max - min) / median` → quality gate per i bot
|
||||
|
||||
**Crypto** (`asset_class: "crypto"`): BTC, ETH via Hyperliquid + Deribit;
|
||||
SOL via Hyperliquid.
|
||||
|
||||
In caso di fallimento parziale ritorna i bar disponibili più
|
||||
`failed_sources: [...]`. Se *tutti* gli upstream falliscono → `502
|
||||
Bad Gateway` retryable.
|
||||
|
||||
---
|
||||
|
||||
## 12. Observability
|
||||
## 11. Observability
|
||||
|
||||
### Request log (`mcp.request`)
|
||||
Una riga JSON per richiesta con: `request_id`, `method`, `path`,
|
||||
@@ -310,15 +303,21 @@ Ogni risposta JSON sotto `/tools/*` riceve dal middleware un campo
|
||||
|
||||
---
|
||||
|
||||
## 13. Esempio chiamata
|
||||
## 12. Esempio chiamata
|
||||
|
||||
```bash
|
||||
# Interfaccia comune: storico Deribit
|
||||
# Singolo exchange: storico Deribit
|
||||
curl -X POST http://localhost:9000/mcp/tools/get_historical \
|
||||
-H "Authorization: Bearer $MAINNET_TOKEN" \
|
||||
-H "X-Bot-Tag: scanner-alpha-prod" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"exchange":"deribit","instrument":"BTC-PERPETUAL","interval":"1h","start_date":"2026-05-01","end_date":"2026-05-29"}'
|
||||
|
||||
# Consenso multi-exchange: ometti "exchange", usa il simbolo canonico
|
||||
curl -X POST http://localhost:9000/mcp/tools/get_historical \
|
||||
-H "Authorization: Bearer $MAINNET_TOKEN" -H "X-Bot-Tag: scanner-alpha-prod" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"instrument":"BTC","interval":"1h","start_date":"2026-05-01","end_date":"2026-05-29"}'
|
||||
```
|
||||
|
||||
Per lo schema completo dei body di richiesta e risposta:
|
||||
@@ -326,24 +325,23 @@ Per lo schema completo dei body di richiesta e risposta:
|
||||
|
||||
---
|
||||
|
||||
## 14. Discovery strumenti & schemi candele
|
||||
## 13. Discovery strumenti & schemi candele
|
||||
|
||||
Schemi verificati sulle risposte live. Tutti i `get_historical` ritornano la
|
||||
chiave uniforme `candles: [{timestamp(ms), open, high, low, close, volume}]`.
|
||||
Schemi verificati sulle risposte live. `get_historical` ritorna la chiave
|
||||
uniforme `candles: [{timestamp(ms), open, high, low, close, volume}]`.
|
||||
|
||||
### Lista strumenti
|
||||
| Interfaccia | Tool | Request body | Risposta (campi principali) |
|
||||
|---|---|---|---|
|
||||
| `/mcp` (comune) | `get_instruments` | `{exchange?, currency:"BTC", kind:"future"}` | `instruments[]` uniforme: `exchange`, `symbol`, `asset_class`, `type`, `fees`, `history_start`, `tick_size`, `native` · `failed_sources[]` |
|
||||
| Deribit (`/mcp-deribit`) | `get_instruments` | `{currency:"BTC"\|…, kind:"future", offset:int, limit:100}` — **paginato** (`has_more`) | `instruments[]`: `name`, `expiry`, `option_type`, `tick_size`, `min_trade_amount`, `maker_commission`, `taker_commission`, `creation_timestamp` · meta: `total`, `offset`, `limit`, `has_more`, `testnet`, `data_timestamp` |
|
||||
| Hyperliquid (`/mcp-hyperliquid`) | `get_markets` | `{}` | lista `{asset, mark_price, funding_rate, open_interest, volume_24h, max_leverage}` |
|
||||
|
||||
### `get_historical` — body per exchange
|
||||
| Interfaccia | Body | Note risoluzione |
|
||||
### Tool dati comuni (`/mcp`)
|
||||
| Tool | Request body | Risposta (campi principali) |
|
||||
|---|---|---|
|
||||
| `/mcp` (comune) | `{exchange, instrument, interval, start_date, end_date}` | `1m`/`5m`/`15m`/`1h`/`4h`/`1d` · ritorna il singolo exchange |
|
||||
| Deribit | `{instrument, start_date:"YYYY-MM-DD", end_date, resolution}` | `1`/`5`/`15`/`60`/`1D` (paginazione interna, no cap) |
|
||||
| Hyperliquid | `{asset\|instrument, start_date, end_date, resolution, interval?, limit:50}` | `1m`/`5m`/`15m`/`1h`/`1d` · `limit` default **50** (alzare per finestre lunghe) |
|
||||
| `get_instruments` | `{exchange?, currency:"BTC", kind:"future"}` | `instruments[]` uniforme: `exchange`, `symbol`, `asset_class`, `type`, `fees`, `history_start`, `tick_size`, `native` · `failed_sources[]` |
|
||||
| `get_historical` | `{exchange?, instrument, interval, start_date, end_date, asset_class?}` | single: `{exchange, instrument, interval, candles, sources_used}` · consensus (no `exchange`): `+ div_pct`/`sources` per bar, `failed_sources` |
|
||||
| `get_indicators` | come `get_historical` + `indicators:["rsi","atr",…]` | `{exchange, instrument, interval, indicators:{…}, candles_used, sources_used}` |
|
||||
|
||||
`get_instruments` Deribit (sorgente grezza): `instruments[]` include `name`,
|
||||
`expiry`, `option_type`, `tick_size`, `min_trade_amount`, `maker_commission`,
|
||||
`taker_commission`, `creation_timestamp`; Hyperliquid `get_markets` (sorgente):
|
||||
`{asset, mark_price, funding_rate, open_interest, volume_24h, max_leverage}`.
|
||||
Ogni risposta `/tools/*` riceve inoltre `data_timestamp` dal middleware.
|
||||
|
||||
### Convenzione simboli Deribit
|
||||
- **BTC/ETH** → perpetui *inverse*: `BTC-PERPETUAL`, `ETH-PERPETUAL`
|
||||
|
||||
Reference in New Issue
Block a user