feat(V2): /mcp-cross/tools/get_historical with cross-exchange consensus
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>
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
"""Pure consensus aggregation: merge per-source OHLCV candles by timestamp.
|
||||
|
||||
The output is a single time-series with the median OHLC across sources,
|
||||
mean volume, the contributing source count, and a divergence % computed
|
||||
on the close range. div_pct gives a quick quality signal: 0 means full
|
||||
agreement, > X% means at least one source is suspect.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections import defaultdict
|
||||
from statistics import median
|
||||
from typing import Any
|
||||
|
||||
|
||||
def merge_candles(by_source: dict[str, list[dict[str, Any]]]) -> list[dict[str, Any]]:
|
||||
grouped: dict[int, list[dict[str, Any]]] = defaultdict(list)
|
||||
for candles in by_source.values():
|
||||
for c in candles:
|
||||
grouped[int(c["timestamp"])].append(c)
|
||||
|
||||
out: list[dict[str, Any]] = []
|
||||
for ts in sorted(grouped):
|
||||
rows = grouped[ts]
|
||||
closes = [float(r["close"]) for r in rows]
|
||||
med_close = float(median(closes))
|
||||
div_pct = (max(closes) - min(closes)) / med_close if med_close else 0.0
|
||||
out.append({
|
||||
"timestamp": ts,
|
||||
"open": float(median(float(r["open"]) for r in rows)),
|
||||
"high": float(median(float(r["high"]) for r in rows)),
|
||||
"low": float(median(float(r["low"]) for r in rows)),
|
||||
"close": med_close,
|
||||
"volume": sum(float(r["volume"]) for r in rows) / len(rows),
|
||||
"sources": len(rows),
|
||||
"div_pct": div_pct,
|
||||
})
|
||||
return out
|
||||
Reference in New Issue
Block a user