修复 6h/8h/12h K 线间隔异常与 Gate 无原生 6h 问题

- 校验 K 线中位间隔,异常时从 1h 聚合

- 分页按实际 K 线步进 since;补充聚合单元测试

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-02 15:29:25 +08:00
parent abcda762d8
commit eebc50cffa
2 changed files with 236 additions and 34 deletions
+65 -3
View File
@@ -3,17 +3,24 @@ from __future__ import annotations
import unittest
from hub_ohlcv_lib import fetch_ohlcv_for_hub
from hub_ohlcv_lib import (
aggregate_ohlcv_bars,
bars_spacing_matches_timeframe,
fetch_ohlcv_for_hub,
)
class _FakeExchange:
def __init__(self, pages):
def __init__(self, pages, *, timeframes=None):
self.pages = list(pages)
self.calls = []
self.markets = {}
self.timeframes = timeframes if timeframes is not None else {}
def fetch_ohlcv(self, symbol, timeframe=None, since=None, limit=None):
self.calls.append({"symbol": symbol, "since": since, "limit": limit})
self.calls.append(
{"symbol": symbol, "since": since, "limit": limit, "timeframe": timeframe}
)
if not self.pages:
return []
page = self.pages.pop(0)
@@ -125,6 +132,61 @@ class TestHubOhlcvLib(unittest.TestCase):
self.assertGreaterEqual(len(ex.calls), 3)
self.assertAlmostEqual(out["bars"][-1]["close"], 3.05)
def test_aggregate_6h_from_1h_when_exchange_lacks_native(self):
"""Gate 等无 6h 时应从 1h 聚合。"""
from hub_ohlcv_lib import TIMEFRAME_MS
h1 = TIMEFRAME_MS["1h"]
h6 = TIMEFRAME_MS["6h"]
base = 1_700_000_000_000
base = (base // h6) * h6
one_h = [
[base + i * h1, 100.0 + i, 101.0 + i, 99.0 + i, 100.5 + i, 10.0]
for i in range(24)
]
ex = _FakeExchange(
[one_h],
timeframes={"1h": "1h", "4h": "4h", "8h": "8h"},
)
out = fetch_ohlcv_for_hub(
symbol="BTC/USDT",
timeframe="6h",
since_ms=base,
limit=4,
normalize_symbol_input=lambda s: str(s).strip().upper(),
normalize_exchange_symbol=lambda s: f"{s}:USDT" if ":" not in s else s,
ensure_markets_loaded=lambda: None,
exchange=ex,
)
self.assertTrue(out.get("ok"))
bars = out.get("bars") or []
self.assertEqual(len(bars), 4)
self.assertTrue(bars_spacing_matches_timeframe(bars, "6h"))
self.assertEqual(ex.calls[0]["timeframe"], "1h")
def test_aggregate_ohlcv_bars_buckets(self):
from hub_ohlcv_lib import TIMEFRAME_MS
h1 = TIMEFRAME_MS["1h"]
h6 = TIMEFRAME_MS["6h"]
base = (1_700_000_000_000 // h6) * h6
src = [
{
"open_time_ms": base + i * h1,
"open": 1.0,
"high": 2.0,
"low": 0.5,
"close": 1.5,
"volume": 1.0,
}
for i in range(6)
]
out = aggregate_ohlcv_bars(src, "6h")
self.assertEqual(len(out), 1)
self.assertEqual(out[0]["volume"], 6.0)
self.assertEqual(out[0]["high"], 2.0)
self.assertEqual(out[0]["low"], 0.5)
if __name__ == "__main__":
unittest.main()