hyperquant 0.45__tar.gz → 0.47__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 (30) hide show
  1. {hyperquant-0.45 → hyperquant-0.47}/PKG-INFO +1 -1
  2. {hyperquant-0.45 → hyperquant-0.47}/pyproject.toml +1 -1
  3. {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/broker/models/ourbit.py +64 -36
  4. {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/broker/ourbit.py +13 -9
  5. {hyperquant-0.45 → hyperquant-0.47}/tmp.py +16 -12
  6. {hyperquant-0.45 → hyperquant-0.47}/uv.lock +1 -1
  7. {hyperquant-0.45 → hyperquant-0.47}/.gitignore +0 -0
  8. {hyperquant-0.45 → hyperquant-0.47}/.python-version +0 -0
  9. {hyperquant-0.45 → hyperquant-0.47}/README.md +0 -0
  10. {hyperquant-0.45 → hyperquant-0.47}/data/logs/notikit.log +0 -0
  11. {hyperquant-0.45 → hyperquant-0.47}/pub.sh +0 -0
  12. {hyperquant-0.45 → hyperquant-0.47}/requirements-dev.lock +0 -0
  13. {hyperquant-0.45 → hyperquant-0.47}/requirements.lock +0 -0
  14. {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/__init__.py +0 -0
  15. {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/broker/auth.py +0 -0
  16. {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/broker/hyperliquid.py +0 -0
  17. {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/broker/lib/hpstore.py +0 -0
  18. {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/broker/lib/hyper_types.py +0 -0
  19. {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/broker/models/hyperliquid.py +0 -0
  20. {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/broker/ws.py +0 -0
  21. {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/core.py +0 -0
  22. {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/datavison/_util.py +0 -0
  23. {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/datavison/binance.py +0 -0
  24. {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/datavison/coinglass.py +0 -0
  25. {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/datavison/okx.py +0 -0
  26. {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/db.py +0 -0
  27. {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/draw.py +0 -0
  28. {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/logkit.py +0 -0
  29. {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/notikit.py +0 -0
  30. {hyperquant-0.45 → hyperquant-0.47}/test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperquant
3
- Version: 0.45
3
+ Version: 0.47
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.45"
3
+ version = "0.47"
4
4
  description = "A minimal yet hyper-efficient backtesting framework for quantitative trading"
5
5
  authors = [
6
6
  { name = "MissinA", email = "1421329142@qq.com" }
@@ -670,7 +670,10 @@ class SpotOrders(DataStore):
670
670
  self._insert([item])
671
671
 
672
672
  elif state == 3 or state == 2:
673
- item["state"] = "filled"
673
+ if state == 3:
674
+ item["state"] = "partially_filled"
675
+ if state == 2:
676
+ item["state"] = "filled"
674
677
 
675
678
  # 如果这三个字段存在追加
676
679
  if d.get("singleDealId") and d.get("singleDealPrice") and d.get("singleDealQuantity"):
@@ -697,8 +700,9 @@ class SpotBook(DataStore):
697
700
  # super().__init__()
698
701
  self._time: int | None = None
699
702
  self.limit = 1
700
- self.loss = True
701
- self.version = None
703
+ self.loss = {} # 改为字典,按symbol跟踪
704
+ self.versions = {}
705
+ self.cache = []
702
706
 
703
707
  def _onresponse(self, data: dict[str, Any]):
704
708
  data = data.get("data")
@@ -706,18 +710,22 @@ class SpotBook(DataStore):
706
710
  book_data = data.get("data")
707
711
  asks = book_data.get("asks", [])
708
712
  bids = book_data.get("bids", [])
709
- version = int(data.get("version", 0))
713
+ version = int(data.get("version", None))
714
+
710
715
 
711
716
  # 保存当前快照版本
712
- self.version = version
713
-
714
- # 应用缓存的增量(只保留连续的部分)
715
- items: list = self.find({"s": symbol})
717
+ self.versions[symbol] = version
718
+
719
+ # # 应用缓存的增量(只保留连续的部分)
720
+ # items: list = self.find({"s": symbol})
721
+ # items.sort(key=lambda x: x.get("fv", 0)) # 按 fromVersion 排序
722
+ # self._find_and_delete({"s": symbol})
723
+
724
+ # 处理缓存
725
+ items = [item for item in self.cache if item.get("s") == symbol]
716
726
  items.sort(key=lambda x: x.get("fv", 0)) # 按 fromVersion 排序
727
+ self.cache = [item for item in self.cache if item.get("s") != symbol]
717
728
 
718
- # 清空旧数据,插入快照
719
- self._find_and_delete({"s": symbol})
720
-
721
729
  for side, S in ((asks, "a"), (bids, "b")):
722
730
  for item in side:
723
731
  self._insert([{"s": symbol, "S": S, "p": item["p"], "q": item["q"]}])
@@ -725,57 +733,77 @@ class SpotBook(DataStore):
725
733
  if items:
726
734
  min_version = min(item.get("fv", 0) for item in items)
727
735
  max_version = max(item.get("tv", 0) for item in items)
728
- self.version = max_version
736
+ # self.version = max_version
737
+ self.versions[symbol] = max_version
738
+
739
+ # if max_version == 0:
740
+ # print('vvv---')
741
+ # print(items)
729
742
 
730
- if not (min_version <= self.version <= max_version):
731
- self.loss = True
743
+ if not (min_version <= self.versions[symbol] <= max_version):
744
+ self.loss[symbol] = True
732
745
  logger.warning(f"SpotBook: Snapshot version {self.version} out of range ({min_version}, {max_version}) for symbol={symbol} (丢补丁)")
733
746
  return
734
-
735
- self.loss = False
747
+
748
+ # 处理过往msg内容
749
+ self.loss[symbol] = False
736
750
  for item in items:
737
751
  fv, tv = item.get("fv", 0), item.get("tv", 0)
738
- if self.version <= tv and self.version >= fv:
739
- self._insert([{ "s": symbol, "S": item["S"], "p": item["p"], "q": item["q"]}])
752
+ if self.versions[symbol] <= tv and self.versions[symbol] >= fv:
753
+ if float(item["q"]) == 0.0:
754
+ self._find_and_delete({"s": symbol, "S": item["S"], "p": item["p"]})
755
+ else:
756
+ self._insert([{ "s": symbol, "S": item["S"], "p": item["p"], "q": item["q"]}])
757
+
758
+ sort_data = self.sorted({'s': symbol}, self.limit)
759
+ asks = sort_data.get('a', [])
760
+ bids = sort_data.get('b', [])
761
+ self._find_and_delete({'s': symbol})
762
+ self._update(asks + bids)
763
+
740
764
  else:
741
- self.loss = False
765
+ self.loss[symbol] = False
742
766
 
743
767
 
744
768
  def _on_message(self, msg: dict[str, Any]) -> None:
745
769
 
746
- ts = time.time() * 1000 # 预留时间戳(如需记录可用)
770
+ # ts = time.time() * 1000 # 预留时间戳(如需记录可用)
747
771
  data = msg.get("d", {}) or {}
748
772
  symbol = msg.get("s")
749
773
  fv = int(data.get("fromVersion"))
750
774
  tv = int(data.get("toVersion"))
775
+ if fv == 0 or tv == 0:
776
+ # print(f'发现fv或tv为0, msg:\n {msg}')
777
+ return
778
+
751
779
  asks: list = data.get("asks", []) or []
752
780
  bids: list = data.get("bids", []) or []
753
781
 
782
+ now_version = self.versions.get(symbol, None)
783
+
754
784
  # 以下几张情况都会被认为正常
755
785
  check_con = (
756
- self.version is None or
757
- fv <= self.version <= tv or
758
- self.version + 1 == fv
786
+ now_version is None or
787
+ fv <= now_version <= tv or
788
+ now_version + 1 == fv
759
789
  )
760
790
 
761
791
  if not check_con:
762
- logger.warning(f"(丢补丁) version:{self.version} fv:{fv} tv:{tv} msg:\n {msg}")
763
- # self.loss = True # 暂时不这样做
764
- return
765
-
766
- self.version = tv
792
+ logger.warning(f"(丢补丁) version:{now_version} fv:{fv} tv:{tv} ")
793
+ self.loss[symbol] = True # 暂时不这样做
794
+
795
+
767
796
 
768
- if self.loss:
797
+ if self.loss.get(symbol, True):
769
798
  for item in asks:
770
- self._insert(
771
- [{"s": symbol, "S": "a", "p": item["p"], "q": item["q"], "fv": fv, "tv": tv}]
772
- )
799
+ self.cache.append({"s": symbol, "S": "a", "p": item["p"], "q": item["q"], "fv": fv, "tv": tv})
773
800
  for item in bids:
774
- self._insert(
775
- [{"s": symbol, "S": "b", "p": item["p"], "q": item["q"], "fv": fv, "tv": tv}]
776
- )
801
+ self.cache.append({"s": symbol, "S": "b", "p": item["p"], "q": item["q"], "fv": fv, "tv": tv})
777
802
  return
778
803
 
804
+ self.versions[symbol] = tv
805
+
806
+
779
807
  to_delete, to_update = [], []
780
808
  for side, S in ((asks, "a"), (bids, "b")):
781
809
  for item in side:
@@ -793,7 +821,7 @@ class SpotBook(DataStore):
793
821
  self._find_and_delete({'s': symbol})
794
822
  self._update(asks + bids)
795
823
 
796
- print(f'处理耗时: {time.time()*1000 - ts:.2f} ms')
824
+ # print(f'处理耗时: {time.time()*1000 - ts:.2f} ms')
797
825
 
798
826
 
799
827
 
@@ -413,7 +413,7 @@ class OurbitSpot:
413
413
  )
414
414
  await wsapp._event.wait()
415
415
 
416
- # await asyncio.sleep(2) # 等待ws连接稳定
416
+ # await asyncio.sleep(1) # 等待ws连接稳定
417
417
 
418
418
  # 并发获取每个交易对的初始深度数据
419
419
  tasks = [
@@ -428,14 +428,18 @@ class OurbitSpot:
428
428
  for idx, response in enumerate(responses):
429
429
  symbol = symbols[idx]
430
430
  self.store.book._onresponse(response.data)
431
- # Check if initialization failed (loss is False), retry if so
432
- retry_count = 0
433
- while self.store.book.loss is True:
434
- logger.warning(f"Orderbook initialization failed for {symbol}, refetching depth snapshot (retry {retry_count+1})")
435
- await asyncio.sleep(0.1)
436
- response_retry = await self.client.fetch('GET', f"{self.api_url}/api/platform/spot/market/depth?symbol={symbol}")
437
- self.store.book._onresponse(response_retry.data)
438
- retry_count += 1
431
+
432
+ async def check_loss():
433
+ await asyncio.sleep(1)
434
+ while True:
435
+ loss = self.store.book.loss
436
+ for symbol, is_loss in loss.items():
437
+ if is_loss:
438
+ resp = await self.client.fetch('GET', f"{self.api_url}/api/platform/spot/market/depth?symbol={symbol}")
439
+ self.store.book._onresponse(resp.data)
440
+ await asyncio.sleep(1)
441
+
442
+ asyncio.create_task(check_loss())
439
443
 
440
444
 
441
445
  async def place_order(
@@ -90,16 +90,16 @@ async def test_watch_order():
90
90
  async def watch_orders(ob_spot: OurbitSpot):
91
91
  with ob_spot.store.orders.watch() as stream:
92
92
  async for change in stream:
93
-
94
- data = change.data
95
- if change.operation == 'delete':
96
- state = data['state']
97
- if state == 'filled':
98
- price = float(data['avg_price'])
99
- quantity = float(data['deal_quantity'])
100
- ts = time.time() * 1000
101
- symbol = data['symbol']
102
- print(price, quantity, ts, symbol, '@@@@')
93
+ print(change.data)
94
+ # data = change.data
95
+ # if change.operation == 'delete':
96
+ # state = data['state']
97
+ # if state == 'filled':
98
+ # price = float(data['avg_price'])
99
+ # quantity = float(data['deal_quantity'])
100
+ # ts = time.time() * 1000
101
+ # symbol = data['symbol']
102
+ # print(price, quantity, ts, symbol, '@@@@')
103
103
 
104
104
  async with pybotters.Client(apis={
105
105
  "ourbit": [
@@ -146,11 +146,15 @@ async def test_orderbook():
146
146
  }) as client:
147
147
  ob_spot = OurbitSpot(client)
148
148
  await ob_spot.__aenter__()
149
- await ob_spot.sub_orderbook(['XRP_USDT'])
149
+ await ob_spot.update('ticker')
150
+ # symbols = [d['symbol'] for d in ob_spot.store.ticker.find()][:30]
151
+ symbols = ['ETC_USDT']
152
+
153
+ await ob_spot.sub_orderbook(symbols)
150
154
  while True:
151
155
  await ob_spot.store.book.wait()
152
156
  print(ob_spot.store.book.find())
153
157
 
154
158
 
155
159
  if __name__ == "__main__":
156
- asyncio.run(test_orderbook())
160
+ asyncio.run(test_watch_order())
@@ -530,7 +530,7 @@ wheels = [
530
530
 
531
531
  [[package]]
532
532
  name = "hyperquant"
533
- version = "0.44"
533
+ version = "0.46"
534
534
  source = { editable = "." }
535
535
  dependencies = [
536
536
  { name = "aiohttp" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes