84ac9134db
Co-authored-by: Cursor <cursoragent@cursor.com>
99 lines
3.1 KiB
Python
99 lines
3.1 KiB
Python
"""hub_ohlcv_lib:分页拉取(Gate 等单次不足 chunk 时仍继续)。"""
|
|
from __future__ import annotations
|
|
|
|
import unittest
|
|
|
|
from hub_ohlcv_lib import fetch_ohlcv_for_hub
|
|
|
|
|
|
class _FakeExchange:
|
|
def __init__(self, pages):
|
|
self.pages = list(pages)
|
|
self.calls = []
|
|
self.markets = {}
|
|
|
|
def fetch_ohlcv(self, symbol, timeframe=None, since=None, limit=None):
|
|
self.calls.append({"symbol": symbol, "since": since, "limit": limit})
|
|
if not self.pages:
|
|
return []
|
|
page = self.pages.pop(0)
|
|
return [b for b in page if b[0] >= since] if since else page
|
|
|
|
|
|
class TestHubOhlcvLib(unittest.TestCase):
|
|
def test_price_tick_from_decimal_precision(self):
|
|
class _Ex:
|
|
markets = {"BTC/USDT:USDT": {"precision": {"price": 2}, "info": {}, "limits": {}}}
|
|
|
|
def load_markets(self):
|
|
return self.markets
|
|
|
|
def market(self, sym):
|
|
return self.markets[sym]
|
|
|
|
def price_to_precision(self, sym, price):
|
|
return "12345.67"
|
|
|
|
tick = __import__("hub_ohlcv_lib", fromlist=["price_tick_from_market"]).price_tick_from_market(
|
|
_Ex(), "BTC/USDT:USDT"
|
|
)
|
|
self.assertAlmostEqual(tick, 0.01)
|
|
|
|
def test_price_tick_from_info_tick_size(self):
|
|
class _Ex:
|
|
markets = {
|
|
"INJ/USDT:USDT": {
|
|
"precision": {"price": 4},
|
|
"info": {"tickSize": "0.001"},
|
|
"limits": {},
|
|
}
|
|
}
|
|
|
|
def load_markets(self):
|
|
return self.markets
|
|
|
|
def market(self, sym):
|
|
return self.markets[sym]
|
|
|
|
def price_to_precision(self, sym, price):
|
|
return "7.123"
|
|
|
|
from hub_ohlcv_lib import price_tick_from_market
|
|
|
|
tick = price_tick_from_market(_Ex(), "INJ/USDT:USDT")
|
|
self.assertAlmostEqual(tick, 0.001)
|
|
|
|
def test_pagination_continues_when_page_smaller_than_chunk(self):
|
|
"""Gate 等常返回 299 根/次,不应误判为已到末尾。"""
|
|
base = 1_700_000_000_000
|
|
step = 4 * 60 * 60 * 1000
|
|
page1 = [
|
|
[base + i * step, 1.0, 1.1, 0.9, 1.05, 100.0] for i in range(299)
|
|
]
|
|
page2 = [
|
|
[base + (299 + i) * step, 2.0, 2.1, 1.9, 2.05, 200.0] for i in range(299)
|
|
]
|
|
page3 = [
|
|
[base + (598 + i) * step, 3.0, 3.1, 2.9, 3.05, 300.0] for i in range(50)
|
|
]
|
|
ex = _FakeExchange([page1, page2, page3])
|
|
|
|
out = fetch_ohlcv_for_hub(
|
|
symbol="INJ/USDT",
|
|
timeframe="4h",
|
|
since_ms=base,
|
|
limit=600,
|
|
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"))
|
|
self.assertEqual(len(out.get("bars") or []), 600)
|
|
self.assertGreaterEqual(len(ex.calls), 3)
|
|
self.assertAlmostEqual(out["bars"][-1]["close"], 3.05)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|