from __future__ import annotations from cerbero_mcp.exchanges.cross.consensus import merge_candles def _c(ts, o, h, l, c, v): return {"timestamp": ts, "open": o, "high": h, "low": l, "close": c, "volume": v} def test_empty_input(): assert merge_candles({}) == [] def test_single_source_passthrough(): out = merge_candles({"bybit": [_c(1, 100, 110, 90, 105, 5)]}) assert len(out) == 1 assert out[0]["timestamp"] == 1 assert out[0]["close"] == 105 assert out[0]["sources"] == 1 assert out[0]["div_pct"] == 0.0 def test_three_sources_identical_no_divergence(): src = { "bybit": [_c(1, 100, 110, 90, 105, 5)], "hyperliquid": [_c(1, 100, 110, 90, 105, 3)], "deribit": [_c(1, 100, 110, 90, 105, 7)], } out = merge_candles(src) assert len(out) == 1 assert out[0]["close"] == 105.0 assert out[0]["sources"] == 3 assert out[0]["div_pct"] == 0.0 # volume is mean across sources assert abs(out[0]["volume"] - 5.0) < 1e-9 def test_three_sources_divergent_close(): src = { "bybit": [_c(1, 100, 110, 90, 100, 1)], "hyperliquid": [_c(1, 100, 110, 90, 110, 1)], "deribit": [_c(1, 100, 110, 90, 105, 1)], } out = merge_candles(src) # median of [100, 110, 105] = 105 assert out[0]["close"] == 105.0 # div_pct = (110 - 100) / 105 ≈ 0.0952 assert abs(out[0]["div_pct"] - 10 / 105) < 1e-6 assert out[0]["sources"] == 3 def test_misaligned_timestamps(): src = { "bybit": [_c(1, 100, 110, 90, 105, 1), _c(2, 100, 110, 90, 105, 1)], "hyperliquid": [_c(2, 100, 110, 90, 105, 1), _c(3, 100, 110, 90, 105, 1)], } out = merge_candles(src) timestamps = [c["timestamp"] for c in out] sources_by_ts = {c["timestamp"]: c["sources"] for c in out} assert timestamps == [1, 2, 3] assert sources_by_ts == {1: 1, 2: 2, 3: 1} def test_two_sources_even_median(): src = { "bybit": [_c(1, 100, 110, 90, 100, 1)], "hyperliquid": [_c(1, 100, 110, 90, 110, 1)], } out = merge_candles(src) # even median = mean of two = 105 assert out[0]["close"] == 105.0 def test_empty_source_ignored(): src = { "bybit": [_c(1, 100, 110, 90, 105, 1)], "hyperliquid": [], } out = merge_candles(src) assert len(out) == 1 assert out[0]["sources"] == 1 def test_output_sorted_by_timestamp(): src = { "bybit": [_c(3, 100, 110, 90, 105, 1), _c(1, 100, 110, 90, 105, 1), _c(2, 100, 110, 90, 105, 1)], } out = merge_candles(src) assert [c["timestamp"] for c in out] == [1, 2, 3]