hyperquant 0.53__tar.gz → 0.55__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.53 → hyperquant-0.55}/PKG-INFO +1 -1
- hyperquant-0.55/apis.json +5 -0
- {hyperquant-0.53 → hyperquant-0.55}/pyproject.toml +1 -1
- {hyperquant-0.53 → hyperquant-0.55}/src/hyperquant/broker/models/ourbit.py +5 -5
- {hyperquant-0.53 → hyperquant-0.55}/src/hyperquant/broker/ourbit.py +11 -4
- {hyperquant-0.53 → hyperquant-0.55}/tests/test_order_sync.py +1 -26
- hyperquant-0.55/tests/test_ourbit.py +183 -0
- {hyperquant-0.53 → hyperquant-0.55}/uv.lock +1 -1
- hyperquant-0.53/apis.json +0 -5
- hyperquant-0.53/tests/test_ourbit.py +0 -76
- {hyperquant-0.53 → hyperquant-0.55}/.gitignore +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/.python-version +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/README.md +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/backtest_tmp.py +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/data/alpine_smoke.log +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/data/logs/notikit.log +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/data/logs/test_order_sync.log +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/deals.json +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/pnl_chart.html +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/pub.sh +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/requirements-dev.lock +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/requirements.lock +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/src/hyperquant/__init__.py +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/src/hyperquant/broker/auth.py +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/src/hyperquant/broker/hyperliquid.py +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/src/hyperquant/broker/lib/hpstore.py +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/src/hyperquant/broker/lib/hyper_types.py +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/src/hyperquant/broker/models/hyperliquid.py +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/src/hyperquant/broker/ws.py +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/src/hyperquant/core.py +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/src/hyperquant/datavison/_util.py +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/src/hyperquant/datavison/binance.py +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/src/hyperquant/datavison/coinglass.py +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/src/hyperquant/datavison/okx.py +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/src/hyperquant/db.py +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/src/hyperquant/draw.py +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/src/hyperquant/logkit.py +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/src/hyperquant/notikit.py +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/tests/test.py +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/tests/test_draw.py +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/tests/test_lbank.py +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/tests/test_store.py +0 -0
- {hyperquant-0.53 → hyperquant-0.55}/tests/tmp.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: hyperquant
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.55
|
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
|
@@ -55,7 +55,7 @@ class Book(DataStore):
|
|
55
55
|
self._find_and_delete({"s": symbol})
|
56
56
|
|
57
57
|
# 处理买卖盘数据
|
58
|
-
for side_id, levels in (("
|
58
|
+
for side_id, levels in (("b", bids), ("a", asks)):
|
59
59
|
for i, level in enumerate(levels):
|
60
60
|
# level格式: [price, size, count]
|
61
61
|
if len(level) >= 3:
|
@@ -185,7 +185,7 @@ class Orders(DataStore):
|
|
185
185
|
})
|
186
186
|
else:
|
187
187
|
order = self._fmt(data)
|
188
|
-
order["state"] = "
|
188
|
+
order["state"] = f"unknown_{state}"
|
189
189
|
self._update([order])
|
190
190
|
self._find_and_delete({
|
191
191
|
"order_id": order.get("order_id")
|
@@ -420,7 +420,7 @@ class OurbitSwapDataStore(DataStoreCollection):
|
|
420
420
|
|
421
421
|
Keys: ("s", "S", "p")
|
422
422
|
- s: 交易对符号,如 "BTC_USDT"
|
423
|
-
- S: 买卖方向,"
|
423
|
+
- S: 买卖方向,"a" 表示卖单(ask),"b" 表示买单(bid)
|
424
424
|
- p: 价格
|
425
425
|
|
426
426
|
Data structure:
|
@@ -430,7 +430,7 @@ class OurbitSwapDataStore(DataStoreCollection):
|
|
430
430
|
[
|
431
431
|
{
|
432
432
|
"s": "BTC_USDT", # 交易对符号
|
433
|
-
"S": "
|
433
|
+
"S": "a", # 卖单方向(ask)
|
434
434
|
"p": "110152.5", # 价格
|
435
435
|
"q": "53539", # 数量
|
436
436
|
"ct": 1, # 该价格的订单数量
|
@@ -438,7 +438,7 @@ class OurbitSwapDataStore(DataStoreCollection):
|
|
438
438
|
},
|
439
439
|
{
|
440
440
|
"s": "BTC_USDT", # 交易对符号
|
441
|
-
"S": "
|
441
|
+
"S": "b", # 买单方向(bid)
|
442
442
|
"p": "110152.4", # 价格
|
443
443
|
"q": "76311", # 数量
|
444
444
|
"ct": 1, # 该价格的订单数量
|
@@ -83,11 +83,12 @@ class OurbitSwap:
|
|
83
83
|
)
|
84
84
|
|
85
85
|
async def sub_personal(self):
|
86
|
-
self.client.ws_connect(
|
86
|
+
wsapp = self.client.ws_connect(
|
87
87
|
self.ws_url,
|
88
88
|
send_json={"method": "sub.personal.user.preference"},
|
89
89
|
hdlr_json=self.store.onmessage,
|
90
90
|
)
|
91
|
+
await wsapp._event.wait()
|
91
92
|
|
92
93
|
def ret_content(self, res: pybotters.FetchResult):
|
93
94
|
match res.data:
|
@@ -115,6 +116,7 @@ class OurbitSwap:
|
|
115
116
|
usdt_amount: Optional[float] = None,
|
116
117
|
leverage: Optional[int] = 20,
|
117
118
|
position_id: Optional[int] = None,
|
119
|
+
quantity: float = None, # 兼容参数,不使用
|
118
120
|
):
|
119
121
|
"""
|
120
122
|
size为合约张数, openType 1 为逐仓, 2为全仓
|
@@ -167,12 +169,17 @@ class OurbitSwap:
|
|
167
169
|
if position_id is None:
|
168
170
|
raise ValueError("position_id is required for closing position")
|
169
171
|
data["positionId"] = position_id
|
170
|
-
|
171
|
-
# print(time.time(), '下单')
|
172
|
+
|
172
173
|
res = await self.client.fetch(
|
173
174
|
"POST", f"{self.api_url}/api/v1/private/order/create", data=data
|
174
175
|
)
|
175
|
-
|
176
|
+
# 'orderId' =
|
177
|
+
# '226474723700166962'
|
178
|
+
# 'ts' =
|
179
|
+
# 1758034181833
|
180
|
+
ret_c = self.ret_content(res)
|
181
|
+
# 只返回 orderId
|
182
|
+
return ret_c["orderId"]
|
176
183
|
|
177
184
|
async def place_tpsl(
|
178
185
|
self,
|
@@ -60,7 +60,7 @@ async def order_sync(
|
|
60
60
|
return {"order_id": oid, "symbol": symbol, "state": "timeout"}
|
61
61
|
|
62
62
|
|
63
|
-
async def
|
63
|
+
async def test_order_sync():
|
64
64
|
async with pybotters.Client(
|
65
65
|
apis={
|
66
66
|
"ourbit": [
|
@@ -78,30 +78,5 @@ async def main():
|
|
78
78
|
result = await order_sync( ob, symbol="SOL_USDT", side="buy", order_type="market", usdt_amount=8, price=200, window_sec=2)
|
79
79
|
print(result)
|
80
80
|
|
81
|
-
|
82
|
-
# while True:
|
83
|
-
# asks = ob.store.book.find({'s': 'SOL_USDT', 'S': 'a'})
|
84
|
-
# bids = ob.store.book.find({'s': 'SOL_USDT', 'S': 'b'})
|
85
|
-
# if asks and bids:
|
86
|
-
# best_ask = float(asks[0]['p'])
|
87
|
-
# best_bid = float(bids[0]['p'])
|
88
|
-
|
89
|
-
# result = await order_sync( ob, symbol="SOL_USDT", side="buy", order_type="limit", quantity=0.04, price=best_bid, window_sec=1)
|
90
|
-
# logger.info(result)
|
91
|
-
|
92
|
-
# if result['state'] == 'filled':
|
93
|
-
# logger.ok(f"买入成功: {result}")
|
94
|
-
# elif result['state'] == 'canceled':
|
95
|
-
# if result.get('avg_price'):
|
96
|
-
# logger.ok(f"部分成交,已撤单: {result}")
|
97
|
-
# else:
|
98
|
-
# logger.error(f"买入失败: {result}")
|
99
|
-
|
100
|
-
# await asyncio.sleep(1)
|
101
|
-
|
102
|
-
|
103
|
-
|
104
81
|
|
105
|
-
if __name__ == "__main__":
|
106
|
-
asyncio.run(main())
|
107
82
|
|
@@ -0,0 +1,183 @@
|
|
1
|
+
import pybotters
|
2
|
+
|
3
|
+
from hyperquant.broker.ourbit import OurbitSpot
|
4
|
+
from hyperquant.broker.ourbit import OurbitSwap
|
5
|
+
|
6
|
+
async def download_orders():
|
7
|
+
async with pybotters.Client(
|
8
|
+
apis={
|
9
|
+
"ourbit": [
|
10
|
+
"WEB3bf088f8b2f2fae07592fe1a6240e2d798100a9cb2a91f8fda1056b6865ab3ee"
|
11
|
+
]
|
12
|
+
}
|
13
|
+
) as client:
|
14
|
+
# 时间区间 (毫秒)
|
15
|
+
start_time = 1757254540000 # 起始
|
16
|
+
end_time = 1757433599999 # 结束
|
17
|
+
|
18
|
+
page_size = 100 # 接口最大 100
|
19
|
+
page_num = 1
|
20
|
+
all_results = []
|
21
|
+
|
22
|
+
while True:
|
23
|
+
url = (
|
24
|
+
"https://www.ourbit.com/api/platform/spot/deal/deals"
|
25
|
+
f"?endTime={end_time}&pageNum={page_num}&pageSize={page_size}&startTime={start_time}"
|
26
|
+
)
|
27
|
+
res = await client.fetch("GET", url)
|
28
|
+
result_list = res.data["data"]["resultList"]
|
29
|
+
got = len(result_list)
|
30
|
+
print(f"page {page_num} -> {got} items")
|
31
|
+
all_results.extend(result_list)
|
32
|
+
|
33
|
+
if got < page_size: # 最后一页
|
34
|
+
break
|
35
|
+
page_num += 1
|
36
|
+
|
37
|
+
print(f"total collected: {len(all_results)}")
|
38
|
+
|
39
|
+
# 写入汇总数据
|
40
|
+
import json
|
41
|
+
with open("deals.json", "w") as f:
|
42
|
+
json.dump(
|
43
|
+
{
|
44
|
+
"data": {
|
45
|
+
"resultList": all_results,
|
46
|
+
"total": len(all_results),
|
47
|
+
"pageSize": page_size,
|
48
|
+
"pagesFetched": page_num
|
49
|
+
}
|
50
|
+
},
|
51
|
+
f,
|
52
|
+
indent=2
|
53
|
+
)
|
54
|
+
print("Saved to deals.json")
|
55
|
+
|
56
|
+
async def test_detail():
|
57
|
+
async with pybotters.Client() as client:
|
58
|
+
ob = OurbitSpot(client)
|
59
|
+
await ob.__aenter__()
|
60
|
+
print(ob.store.detail.get({
|
61
|
+
'name': 'OPEN'
|
62
|
+
}))
|
63
|
+
|
64
|
+
async def test_ourbit_wrap():
|
65
|
+
async with pybotters.Client(
|
66
|
+
apis={
|
67
|
+
"ourbit": [
|
68
|
+
"WEBa07743126a6cc69a896a501404ae8357e4116059ebc5bde75020881dc53a24ba"
|
69
|
+
]
|
70
|
+
}
|
71
|
+
) as client:
|
72
|
+
ob = OurbitSpot(client)
|
73
|
+
await ob.__aenter__()
|
74
|
+
await ob.update('balance')
|
75
|
+
print(ob.store.balance.find())
|
76
|
+
|
77
|
+
|
78
|
+
import asyncio
|
79
|
+
import time
|
80
|
+
from hyperquant.broker.ourbit import OurbitSpot
|
81
|
+
import pybotters
|
82
|
+
from hyperquant.logkit import get_logger
|
83
|
+
|
84
|
+
logger = get_logger('test_order_sync', './data/logs/test_order_sync.log', show_time=True)
|
85
|
+
|
86
|
+
|
87
|
+
# 等待指定 oid 的最终 delete,超时抛 TimeoutError
|
88
|
+
async def wait_delete(stream: pybotters.StoreStream, oid: str, seconds: float):
|
89
|
+
async with asyncio.timeout(seconds):
|
90
|
+
while True:
|
91
|
+
change = await stream.__anext__()
|
92
|
+
# print(change.operation, change.data)
|
93
|
+
if change.operation == "delete" and change.data.get("order_id") == oid:
|
94
|
+
return change.data # 内含 state / avg_price / deal_quantity 等累计字段
|
95
|
+
|
96
|
+
|
97
|
+
async def order_sync(
|
98
|
+
ob: OurbitSpot| OurbitSwap,
|
99
|
+
symbol: str = "SOL_USDT",
|
100
|
+
side: str = "buy",
|
101
|
+
order_type: str = "market", # "market" / "limit"
|
102
|
+
usdt_amount: float | None = None, # 市价可填
|
103
|
+
quantity: float | None = None, # 市价可填
|
104
|
+
price: float | None = None, # 限价必填
|
105
|
+
window_sec: float = 2.0, # 主等待窗口(限价可设为 5.0)
|
106
|
+
grace_sec: float = 2, # 撤单后宽限
|
107
|
+
):
|
108
|
+
with ob.store.orders.watch() as stream:
|
109
|
+
# 下单(只保留最简两种入参形态)
|
110
|
+
try:
|
111
|
+
if isinstance(ob, OurbitSwap):
|
112
|
+
oid = await ob.place_order(
|
113
|
+
symbol,
|
114
|
+
side,
|
115
|
+
order_type=order_type,
|
116
|
+
usdt_amount=usdt_amount,
|
117
|
+
quantity=quantity,
|
118
|
+
price=price
|
119
|
+
)
|
120
|
+
except Exception as e:
|
121
|
+
return {"symbol": symbol, "state": "error", "error": str(e)}
|
122
|
+
|
123
|
+
# 步骤1:主窗口内等待这单的最终 delete
|
124
|
+
try:
|
125
|
+
return await wait_delete(stream, oid, window_sec)
|
126
|
+
except TimeoutError:
|
127
|
+
# 步骤2:到点撤单(市价通常用不到;限价才有意义)
|
128
|
+
for i in range(3):
|
129
|
+
try:
|
130
|
+
await ob.cancel_order(oid)
|
131
|
+
break
|
132
|
+
except Exception:
|
133
|
+
pass
|
134
|
+
await asyncio.sleep(0.1)
|
135
|
+
# 固定宽限内再等“迟到”的最终 delete
|
136
|
+
try:
|
137
|
+
return await wait_delete(stream, oid, grace_sec)
|
138
|
+
except TimeoutError:
|
139
|
+
return {"order_id": oid, "symbol": symbol, "state": "timeout"}
|
140
|
+
|
141
|
+
|
142
|
+
async def test_order_sync_spot():
|
143
|
+
async with pybotters.Client(
|
144
|
+
apis={
|
145
|
+
"ourbit": [
|
146
|
+
"WEB3bf088f8b2f2fae07592fe1a6240e2d798100a9cb2a91f8fda1056b6865ab3ee"
|
147
|
+
]
|
148
|
+
}
|
149
|
+
) as client:
|
150
|
+
ob = OurbitSpot(client)
|
151
|
+
await ob.__aenter__()
|
152
|
+
await ob.sub_personal() # 私有频道
|
153
|
+
ob.store.book.limit = 3
|
154
|
+
await ob.sub_orderbook(["SOL_USDT"]) # 订单簿频道
|
155
|
+
# # 示例:市价
|
156
|
+
# now= time.time()
|
157
|
+
result = await order_sync( ob, symbol="SOL_USDT", side="buy", order_type="market", usdt_amount=8, price=200, window_sec=2)
|
158
|
+
print(result)
|
159
|
+
|
160
|
+
|
161
|
+
async def test_order_sync_swap():
|
162
|
+
async with pybotters.Client(
|
163
|
+
apis={
|
164
|
+
"ourbit": [
|
165
|
+
"WEBb401428e69af1815808e470be0a4f4e8a70a5c5cc0b0df0a33220f689167c629"
|
166
|
+
]
|
167
|
+
}
|
168
|
+
) as client:
|
169
|
+
ob = OurbitSwap(client)
|
170
|
+
await ob.__aenter__()
|
171
|
+
await ob.sub_personal() # 私有频道
|
172
|
+
|
173
|
+
# await ob.sub_orderbook(["SOL_USDT"]) # 订单簿频道
|
174
|
+
# # 示例:市价
|
175
|
+
# now= time.time()
|
176
|
+
result = await order_sync( ob, symbol="SOL_USDT", side="buy", order_type="limit_IOC", usdt_amount=8, price=200, window_sec=3)
|
177
|
+
print(result)
|
178
|
+
|
179
|
+
|
180
|
+
|
181
|
+
if __name__ == "__main__":
|
182
|
+
import asyncio
|
183
|
+
asyncio.run(test_order_sync_swap())
|
hyperquant-0.53/apis.json
DELETED
@@ -1,76 +0,0 @@
|
|
1
|
-
import pybotters
|
2
|
-
|
3
|
-
from hyperquant.broker.ourbit import OurbitSpot
|
4
|
-
from hyperquant.broker.ourbit import OurbitSpot
|
5
|
-
|
6
|
-
async def download_orders():
|
7
|
-
async with pybotters.Client(
|
8
|
-
apis={
|
9
|
-
"ourbit": [
|
10
|
-
"WEB3bf088f8b2f2fae07592fe1a6240e2d798100a9cb2a91f8fda1056b6865ab3ee"
|
11
|
-
]
|
12
|
-
}
|
13
|
-
) as client:
|
14
|
-
# 时间区间 (毫秒)
|
15
|
-
start_time = 1757254540000 # 起始
|
16
|
-
end_time = 1757433599999 # 结束
|
17
|
-
|
18
|
-
page_size = 100 # 接口最大 100
|
19
|
-
page_num = 1
|
20
|
-
all_results = []
|
21
|
-
|
22
|
-
while True:
|
23
|
-
url = (
|
24
|
-
"https://www.ourbit.com/api/platform/spot/deal/deals"
|
25
|
-
f"?endTime={end_time}&pageNum={page_num}&pageSize={page_size}&startTime={start_time}"
|
26
|
-
)
|
27
|
-
res = await client.fetch("GET", url)
|
28
|
-
result_list = res.data["data"]["resultList"]
|
29
|
-
got = len(result_list)
|
30
|
-
print(f"page {page_num} -> {got} items")
|
31
|
-
all_results.extend(result_list)
|
32
|
-
|
33
|
-
if got < page_size: # 最后一页
|
34
|
-
break
|
35
|
-
page_num += 1
|
36
|
-
|
37
|
-
print(f"total collected: {len(all_results)}")
|
38
|
-
|
39
|
-
# 写入汇总数据
|
40
|
-
import json
|
41
|
-
with open("deals.json", "w") as f:
|
42
|
-
json.dump(
|
43
|
-
{
|
44
|
-
"data": {
|
45
|
-
"resultList": all_results,
|
46
|
-
"total": len(all_results),
|
47
|
-
"pageSize": page_size,
|
48
|
-
"pagesFetched": page_num
|
49
|
-
}
|
50
|
-
},
|
51
|
-
f,
|
52
|
-
indent=2
|
53
|
-
)
|
54
|
-
print("Saved to deals.json")
|
55
|
-
|
56
|
-
async def test_detail():
|
57
|
-
async with pybotters.Client() as client:
|
58
|
-
ob = OurbitSpot(client)
|
59
|
-
await ob.__aenter__()
|
60
|
-
print(ob.store.detail.get({
|
61
|
-
'name': 'OPEN'
|
62
|
-
}))
|
63
|
-
|
64
|
-
async def test_query_orders():
|
65
|
-
async with pybotters.Client(
|
66
|
-
apis={
|
67
|
-
"ourbit": [
|
68
|
-
"WEB3bf088f8b2f2fae07592fe1a6240e2d798100a9cb2a91f8fda1056b6865ab3ee"
|
69
|
-
]
|
70
|
-
}
|
71
|
-
) as client:
|
72
|
-
ob = OurbitSpot(client)
|
73
|
-
|
74
|
-
if __name__ == "__main__":
|
75
|
-
import asyncio
|
76
|
-
asyncio.run(test_detail())
|
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
|
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
|