hyperquant 0.84__tar.gz → 0.86__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. {hyperquant-0.84 → hyperquant-0.86}/PKG-INFO +1 -1
  2. {hyperquant-0.84 → hyperquant-0.86}/pyproject.toml +1 -1
  3. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/broker/coinw.py +47 -7
  4. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/broker/models/coinw.py +7 -0
  5. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/broker/ws.py +1 -1
  6. {hyperquant-0.84 → hyperquant-0.86}/tests/test_coinw.py +22 -7
  7. {hyperquant-0.84 → hyperquant-0.86}/uv.lock +1 -1
  8. {hyperquant-0.84 → hyperquant-0.86}/.gitignore +0 -0
  9. {hyperquant-0.84 → hyperquant-0.86}/.python-version +0 -0
  10. {hyperquant-0.84 → hyperquant-0.86}/README.md +0 -0
  11. {hyperquant-0.84 → hyperquant-0.86}/apis.json +0 -0
  12. {hyperquant-0.84 → hyperquant-0.86}/data/alpine_smoke.log +0 -0
  13. {hyperquant-0.84 → hyperquant-0.86}/data/logs/notikit.log +0 -0
  14. {hyperquant-0.84 → hyperquant-0.86}/data/logs/test_order_sync.log +0 -0
  15. {hyperquant-0.84 → hyperquant-0.86}/data/records_swap.csv +0 -0
  16. {hyperquant-0.84 → hyperquant-0.86}/data/records_swapc.csv +0 -0
  17. {hyperquant-0.84 → hyperquant-0.86}/doc/lbank.md +0 -0
  18. {hyperquant-0.84 → hyperquant-0.86}/pub.sh +0 -0
  19. {hyperquant-0.84 → hyperquant-0.86}/requirements-dev.lock +0 -0
  20. {hyperquant-0.84 → hyperquant-0.86}/requirements.lock +0 -0
  21. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/__init__.py +0 -0
  22. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/broker/auth.py +0 -0
  23. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/broker/bitget.py +0 -0
  24. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/broker/edgex.py +0 -0
  25. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/broker/hyperliquid.py +0 -0
  26. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/broker/lbank.py +0 -0
  27. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/broker/lib/edgex_sign.py +0 -0
  28. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/broker/lib/hpstore.py +0 -0
  29. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/broker/lib/hyper_types.py +0 -0
  30. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/broker/lib/util.py +0 -0
  31. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/broker/models/bitget.py +0 -0
  32. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/broker/models/edgex.py +0 -0
  33. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/broker/models/hyperliquid.py +0 -0
  34. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/broker/models/lbank.py +0 -0
  35. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/broker/models/ourbit.py +0 -0
  36. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/broker/ourbit.py +0 -0
  37. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/core.py +0 -0
  38. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/datavison/_util.py +0 -0
  39. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/datavison/binance.py +0 -0
  40. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/datavison/coinglass.py +0 -0
  41. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/datavison/okx.py +0 -0
  42. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/db.py +0 -0
  43. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/draw.py +0 -0
  44. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/logkit.py +0 -0
  45. {hyperquant-0.84 → hyperquant-0.86}/src/hyperquant/notikit.py +0 -0
  46. {hyperquant-0.84 → hyperquant-0.86}/tests/test_bitget.py +0 -0
  47. {hyperquant-0.84 → hyperquant-0.86}/tests/test_draw.py +0 -0
  48. {hyperquant-0.84 → hyperquant-0.86}/tests/test_edgex.py +0 -0
  49. {hyperquant-0.84 → hyperquant-0.86}/tests/test_lbank.py +0 -0
  50. {hyperquant-0.84 → hyperquant-0.86}/tests/test_ourbit.py +0 -0
  51. {hyperquant-0.84 → hyperquant-0.86}/tests/tmp.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperquant
3
- Version: 0.84
3
+ Version: 0.86
4
4
  Summary: A minimal yet hyper-efficient backtesting framework for quantitative trading
5
5
  Project-URL: Homepage, https://github.com/yourusername/hyperquant
6
6
  Project-URL: Issues, https://github.com/yourusername/hyperquant/issues
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "hyperquant"
3
- version = "0.84"
3
+ version = "0.86"
4
4
  description = "A minimal yet hyper-efficient backtesting framework for quantitative trading"
5
5
  authors = [
6
6
  { name = "MissinA", email = "1421329142@qq.com" }
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import asyncio
4
4
  import logging
5
+ import time
5
6
  from typing import Any, Literal, Sequence
6
7
 
7
8
  import pybotters
@@ -305,8 +306,9 @@ class Coinw:
305
306
  self,
306
307
  pair_codes: Sequence[str] | str,
307
308
  *,
308
- depth_limit: int | None = None,
309
+ depth_limit: int | None = 1,
309
310
  biz: str = "futures",
311
+ stale_timeout: float = 5,
310
312
  ) -> pybotters.ws.WebSocketApp:
311
313
  """订阅 ``type=depth`` 订单簿数据,批量控制发送频率。"""
312
314
 
@@ -332,12 +334,50 @@ class Coinw:
332
334
  await ws_app._event.wait()
333
335
 
334
336
  chunk_size = 10
335
- for idx in range(0, len(subscriptions), chunk_size):
336
- batch = subscriptions[idx : idx + chunk_size]
337
- for msg in batch:
338
- await ws_app.current_ws.send_json(msg)
339
- if idx + chunk_size < len(subscriptions):
340
- await asyncio.sleep(2.05)
337
+
338
+ async def send_subs(target: pybotters.ws.WebSocketApp) -> None:
339
+ for idx in range(0, len(subscriptions), chunk_size):
340
+ batch = subscriptions[idx : idx + chunk_size]
341
+ for msg in batch:
342
+ await target.current_ws.send_json(msg)
343
+ if idx + chunk_size < len(subscriptions):
344
+ await asyncio.sleep(2.05)
345
+
346
+ await send_subs(ws_app)
347
+
348
+ ws_ref: dict[str, pybotters.ws.WebSocketApp] = {"app": ws_app}
349
+
350
+ async def monitor() -> None:
351
+ while True:
352
+ await asyncio.sleep(stale_timeout)
353
+ last_update = self.store.book.last_update
354
+ if not last_update:
355
+ continue
356
+ if time.time() - last_update < stale_timeout:
357
+ continue
358
+ logger.warning(
359
+ "CoinW order book idle for %.1f seconds, reconnecting.", stale_timeout
360
+ )
361
+ try:
362
+ current = ws_ref["app"]
363
+ if current.current_ws and not current.current_ws.closed:
364
+ await current.current_ws.close()
365
+ except Exception:
366
+ logger.exception("Error closing stale CoinW orderbook websocket")
367
+
368
+ try:
369
+ new_ws = self.client.ws_connect(
370
+ self.ws_url_public,
371
+ hdlr_json=self.store.onmessage,
372
+ headers=self._ws_headers,
373
+ )
374
+ await new_ws._event.wait()
375
+ await send_subs(new_ws)
376
+ ws_ref["app"] = new_ws
377
+ except Exception:
378
+ logger.exception("Failed to reconnect CoinW orderbook websocket")
379
+
380
+ asyncio.create_task(monitor())
341
381
 
342
382
  return ws_app
343
383
 
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
+ import time
4
5
  from typing import TYPE_CHECKING, Any, Awaitable
5
6
 
6
7
  import aiohttp
@@ -35,6 +36,7 @@ class Book(DataStore):
35
36
 
36
37
  def _init(self) -> None:
37
38
  self.limit: int | None = None
39
+ self._last_update: float = 0.0
38
40
 
39
41
  def _on_message(self, msg: dict[str, Any]) -> None:
40
42
  data = msg.get("data")
@@ -80,6 +82,7 @@ class Book(DataStore):
80
82
 
81
83
  self._find_and_delete({"s": str(symbol)})
82
84
  self._insert(entries)
85
+ self._last_update = time.time()
83
86
 
84
87
  def sorted(
85
88
  self, query: Item | None = None, limit: int | None = None
@@ -93,6 +96,10 @@ class Book(DataStore):
93
96
  limit=limit,
94
97
  )
95
98
 
99
+ @property
100
+ def last_update(self) -> float:
101
+ return self._last_update
102
+
96
103
 
97
104
  class Detail(DataStore):
98
105
  """CoinW 合约信息数据存储。
@@ -36,7 +36,7 @@ class Heartbeat:
36
36
  async def coinw(ws: ClientWebSocketResponse):
37
37
  while not ws.closed:
38
38
  await ws.send_json({"event": "ping"})
39
- await asyncio.sleep(10.0)
39
+ await asyncio.sleep(3.0)
40
40
 
41
41
  pybotters.ws.HeartbeatHosts.items['futures.ourbit.com'] = Heartbeat.ourbit
42
42
  pybotters.ws.HeartbeatHosts.items['www.ourbit.com'] = Heartbeat.ourbit_spot
@@ -37,15 +37,30 @@ async def test_sub_orderbook(
37
37
  interval: float = 1.0,
38
38
  ) -> None:
39
39
  """Subscribe CoinW order book and print snapshots."""
40
+ start = time.time()
41
+ print(f"订阅开始: {start:.3f}")
42
+
43
+ symbols = ['NMR_USDT', 'TRB_USDT', 'MELANIA_USDT', 'INJ_USDT', 'LTC_USDT', 'AUCTION_USDT']
44
+
45
+ # 去掉USDT
46
+ symbols = [s[:-5] if s.endswith('_USDT') else s for s in symbols]
47
+
40
48
  async with pybotters.Client() as client:
41
49
  async with Coinw(client) as cw:
42
50
  await cw.sub_orderbook(symbols, depth_limit=depth)
43
- while True:
44
- for sym in symbols:
45
- levels = cw.store.book.find({"s": sym})
46
- if levels:
47
- print(sym, levels[: depth * 2])
48
- await asyncio.sleep(interval)
51
+ with cw.store.book.watch() as watcher:
52
+ while True:
53
+ try:
54
+ change = await asyncio.wait_for(watcher.__anext__(), timeout=10.0)
55
+ except asyncio.TimeoutError:
56
+ print("超过10秒未收到新数据,退出订阅。")
57
+ break
58
+ else:
59
+ print(change.data)
60
+
61
+ end = time.time()
62
+ print(f"订阅结束: {end:.3f}, 耗时: {(end - start):.3f} 秒")
63
+
49
64
 
50
65
  async def test_place_cancel() -> None:
51
66
  """Demonstrate place/cancel flow (requires credentials and tradable environment)."""
@@ -209,4 +224,4 @@ async def test_order_sync_polling() -> None:
209
224
 
210
225
 
211
226
  if __name__ == "__main__":
212
- asyncio.run(test_update())
227
+ asyncio.run(test_sub_orderbook())
@@ -662,7 +662,7 @@ wheels = [
662
662
 
663
663
  [[package]]
664
664
  name = "hyperquant"
665
- version = "0.83"
665
+ version = "0.85"
666
666
  source = { editable = "." }
667
667
  dependencies = [
668
668
  { name = "aiohttp" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes