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.
- {hyperquant-0.45 → hyperquant-0.47}/PKG-INFO +1 -1
- {hyperquant-0.45 → hyperquant-0.47}/pyproject.toml +1 -1
- {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/broker/models/ourbit.py +64 -36
- {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/broker/ourbit.py +13 -9
- {hyperquant-0.45 → hyperquant-0.47}/tmp.py +16 -12
- {hyperquant-0.45 → hyperquant-0.47}/uv.lock +1 -1
- {hyperquant-0.45 → hyperquant-0.47}/.gitignore +0 -0
- {hyperquant-0.45 → hyperquant-0.47}/.python-version +0 -0
- {hyperquant-0.45 → hyperquant-0.47}/README.md +0 -0
- {hyperquant-0.45 → hyperquant-0.47}/data/logs/notikit.log +0 -0
- {hyperquant-0.45 → hyperquant-0.47}/pub.sh +0 -0
- {hyperquant-0.45 → hyperquant-0.47}/requirements-dev.lock +0 -0
- {hyperquant-0.45 → hyperquant-0.47}/requirements.lock +0 -0
- {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/__init__.py +0 -0
- {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/broker/auth.py +0 -0
- {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/broker/hyperliquid.py +0 -0
- {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/broker/lib/hpstore.py +0 -0
- {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/broker/lib/hyper_types.py +0 -0
- {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/broker/models/hyperliquid.py +0 -0
- {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/broker/ws.py +0 -0
- {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/core.py +0 -0
- {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/datavison/_util.py +0 -0
- {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/datavison/binance.py +0 -0
- {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/datavison/coinglass.py +0 -0
- {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/datavison/okx.py +0 -0
- {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/db.py +0 -0
- {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/draw.py +0 -0
- {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/logkit.py +0 -0
- {hyperquant-0.45 → hyperquant-0.47}/src/hyperquant/notikit.py +0 -0
- {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.
|
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
|
@@ -670,7 +670,10 @@ class SpotOrders(DataStore):
|
|
670
670
|
self._insert([item])
|
671
671
|
|
672
672
|
elif state == 3 or state == 2:
|
673
|
-
|
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 =
|
701
|
-
self.
|
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",
|
713
|
+
version = int(data.get("version", None))
|
714
|
+
|
710
715
|
|
711
716
|
# 保存当前快照版本
|
712
|
-
self.
|
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.
|
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
|
-
|
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.
|
739
|
-
|
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
|
-
|
757
|
-
fv <=
|
758
|
-
|
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:{
|
763
|
-
|
764
|
-
|
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.
|
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.
|
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(
|
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
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
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
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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.
|
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(
|
160
|
+
asyncio.run(test_watch_order())
|
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
|
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
|
File without changes
|
File without changes
|