hyperquant 0.34__py3-none-any.whl → 0.36__py3-none-any.whl
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/broker/auth.py +5 -1
- hyperquant/broker/models/ourbit.py +133 -87
- hyperquant/broker/ourbit.py +10 -3
- hyperquant/broker/ws.py +27 -2
- {hyperquant-0.34.dist-info → hyperquant-0.36.dist-info}/METADATA +1 -1
- {hyperquant-0.34.dist-info → hyperquant-0.36.dist-info}/RECORD +7 -7
- {hyperquant-0.34.dist-info → hyperquant-0.36.dist-info}/WHEEL +0 -0
hyperquant/broker/auth.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import time
|
2
2
|
import hashlib
|
3
3
|
from typing import Any
|
4
|
+
from aiohttp import ClientWebSocketResponse
|
4
5
|
from multidict import CIMultiDict
|
5
6
|
from yarl import URL
|
6
7
|
import pybotters
|
@@ -45,7 +46,10 @@ class Auth:
|
|
45
46
|
|
46
47
|
# 更新 kwargs.body,保证发出去的与签名一致
|
47
48
|
kwargs.update({"data": raw_body_for_sign})
|
48
|
-
|
49
|
+
|
49
50
|
return args
|
51
|
+
|
50
52
|
|
51
53
|
pybotters.auth.Hosts.items['futures.ourbit.com'] = pybotters.auth.Item("ourbit", Auth.ourbit)
|
54
|
+
|
55
|
+
|
@@ -137,6 +137,21 @@ class Ticker(DataStore):
|
|
137
137
|
class Orders(DataStore):
|
138
138
|
_KEYS = ["order_id"]
|
139
139
|
|
140
|
+
def _fmt(self, order:dict):
|
141
|
+
return {
|
142
|
+
"order_id": order.get("orderId"),
|
143
|
+
"symbol": order.get("symbol"),
|
144
|
+
"px": order.get("price"),
|
145
|
+
"vol": order.get("vol"),
|
146
|
+
"lev": order.get("leverage"),
|
147
|
+
"side": "buy" if order.get("side") == 1 else "sell",
|
148
|
+
"deal_vol": order.get("dealVol"),
|
149
|
+
"deal_avg_px": order.get("dealAvgPrice"),
|
150
|
+
"create_ts": order.get("createTime"),
|
151
|
+
"update_ts": order.get("updateTime"),
|
152
|
+
"state": "open"
|
153
|
+
}
|
154
|
+
|
140
155
|
# {'success': True, 'code': 0, 'data': [{'orderId': '219108574599630976', 'symbol': 'SOL_USDT', 'positionId': 0, 'price': 190, 'priceStr': '190', 'vol': 1, 'leverage': 20, 'side': 1, 'category': 1, 'orderType': 1, 'dealAvgPrice': 0, 'dealAvgPriceStr': '0', 'dealVol': 0, 'orderMargin': 0.09652, 'takerFee': 0, 'makerFee': 0, 'profit': 0, 'feeCurrency': 'USDT', 'openType': 1, 'state': 2, 'externalOid': '_m_2228b23a75204e1982b301e44d439cbb', 'errorCode': 0, 'usedMargin': 0, 'createTime': 1756277955008, 'updateTime': 1756277955037, 'positionMode': 1, 'version': 1, 'showCancelReason': 0, 'showProfitRateShare': 0, 'voucher': False}]}
|
141
156
|
def _onresponse(self, data: dict[str, Any]):
|
142
157
|
orders = data.get("data", [])
|
@@ -144,25 +159,33 @@ class Orders(DataStore):
|
|
144
159
|
data_to_insert: list[Item] = []
|
145
160
|
for order in orders:
|
146
161
|
order: dict[str, Any] = order
|
147
|
-
|
148
|
-
data_to_insert.append(
|
149
|
-
{
|
150
|
-
"order_id": order.get("orderId"),
|
151
|
-
"symbol": order.get("symbol"),
|
152
|
-
"px": order.get("priceStr"),
|
153
|
-
"vol": order.get("vol"),
|
154
|
-
"lev": order.get("leverage"),
|
155
|
-
"side": "buy" if order.get("side") == 1 else "sell",
|
156
|
-
"deal_vol": order.get("dealVol"),
|
157
|
-
"deal_avg_px": order.get("dealAvgPriceStr"),
|
158
|
-
"create_ts": order.get("createTime"),
|
159
|
-
"update_ts": order.get("updateTime"),
|
160
|
-
}
|
161
|
-
)
|
162
|
+
data_to_insert.append(self._fmt(order))
|
162
163
|
|
163
164
|
self._clear()
|
164
165
|
self._update(data_to_insert)
|
165
|
-
|
166
|
+
|
167
|
+
def _on_message(self, msg: dict[str, Any]) -> None:
|
168
|
+
data:dict = msg.get("data", {})
|
169
|
+
if msg.get('channel') == 'push.personal.order':
|
170
|
+
state = data.get("state")
|
171
|
+
if state == 2:
|
172
|
+
order = self._fmt(data)
|
173
|
+
order["state"] = "open"
|
174
|
+
self._insert([order])
|
175
|
+
elif state == 3:
|
176
|
+
order = self._fmt(data)
|
177
|
+
order["state"] = "filled"
|
178
|
+
self._update([order])
|
179
|
+
self._find_and_delete({
|
180
|
+
"order_id": order.get("order_id")
|
181
|
+
})
|
182
|
+
elif state == 4:
|
183
|
+
order = self._fmt(data)
|
184
|
+
order["state"] = "canceled"
|
185
|
+
self._update([order])
|
186
|
+
self._find_and_delete({
|
187
|
+
"order_id": order.get("order_id")
|
188
|
+
})
|
166
189
|
|
167
190
|
class Detail(DataStore):
|
168
191
|
_KEYS = ["symbol"]
|
@@ -193,15 +216,9 @@ class Detail(DataStore):
|
|
193
216
|
class Position(DataStore):
|
194
217
|
_KEYS = ["position_id"]
|
195
218
|
# {"success":true,"code":0,"data":[{"positionId":5355366,"symbol":"SOL_USDT","positionType":1,"openType":1,"state":1,"holdVol":1,"frozenVol":0,"closeVol":0,"holdAvgPrice":203.44,"holdAvgPriceFullyScale":"203.44","openAvgPrice":203.44,"openAvgPriceFullyScale":"203.44","closeAvgPrice":0,"liquidatePrice":194.07,"oim":0.10253376,"im":0.10253376,"holdFee":0,"realised":-0.0008,"leverage":20,"marginRatio":0.0998,"createTime":1756275984696,"updateTime":1756275984696,"autoAddIm":false,"version":1,"profitRatio":0,"newOpenAvgPrice":203.44,"newCloseAvgPrice":0,"closeProfitLoss":0,"fee":0.00081376}]}
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
data_to_insert: list[Item] = []
|
200
|
-
for position in positions:
|
201
|
-
position: dict[str, Any] = position
|
202
|
-
|
203
|
-
data_to_insert.append(
|
204
|
-
{
|
219
|
+
|
220
|
+
def _fmt(self, position:dict):
|
221
|
+
return {
|
205
222
|
"position_id": position.get("positionId"),
|
206
223
|
"symbol": position.get("symbol"),
|
207
224
|
"side": "short" if position.get("positionType") == 2 else "long",
|
@@ -222,7 +239,17 @@ class Position(DataStore):
|
|
222
239
|
"margin_ratio": position.get("marginRatio"),
|
223
240
|
"create_ts": position.get("createTime"),
|
224
241
|
"update_ts": position.get("updateTime"),
|
225
|
-
|
242
|
+
}
|
243
|
+
|
244
|
+
def _onresponse(self, data: dict[str, Any]):
|
245
|
+
positions = data.get("data", [])
|
246
|
+
if positions:
|
247
|
+
data_to_insert: list[Item] = []
|
248
|
+
for position in positions:
|
249
|
+
position: dict[str, Any] = position
|
250
|
+
|
251
|
+
data_to_insert.append(
|
252
|
+
self._fmt(position)
|
226
253
|
)
|
227
254
|
|
228
255
|
self._clear()
|
@@ -230,31 +257,43 @@ class Position(DataStore):
|
|
230
257
|
else:
|
231
258
|
self._clear()
|
232
259
|
|
260
|
+
def _on_message(self, msg: dict[str, Any]) -> None:
|
261
|
+
data:dict = msg.get("data", {})
|
262
|
+
state = data.get("state")
|
263
|
+
position_id = data.get("positionId")
|
264
|
+
if state == 3:
|
265
|
+
self._find_and_delete({"position_id": position_id})
|
266
|
+
return
|
267
|
+
|
268
|
+
self._update([self._fmt(data)])
|
269
|
+
|
233
270
|
class Balance(DataStore):
|
234
271
|
_KEYS = ["currency"]
|
235
272
|
|
273
|
+
def _fmt(self, balance: dict) -> dict:
|
274
|
+
return {
|
275
|
+
"available_balance": balance.get("availableBalance"),
|
276
|
+
"bonus": balance.get("bonus"),
|
277
|
+
"currency": balance.get("currency"),
|
278
|
+
"frozen_balance": balance.get("frozenBalance"),
|
279
|
+
"last_bonus": balance.get("lastBonus"),
|
280
|
+
"position_margin": balance.get("positionMargin"),
|
281
|
+
"wallet_balance": balance.get("walletBalance"),
|
282
|
+
}
|
283
|
+
|
236
284
|
def _onresponse(self, data: dict[str, Any]):
|
237
285
|
balances = data.get("data", [])
|
238
286
|
if balances:
|
239
287
|
data_to_insert: list[Item] = []
|
240
288
|
for balance in balances:
|
241
289
|
balance: dict[str, Any] = balance
|
242
|
-
data_to_insert.append(
|
243
|
-
"currency": balance.get("currency"),
|
244
|
-
"position_margin": balance.get("positionMargin"),
|
245
|
-
"available_balance": balance.get("availableBalance"),
|
246
|
-
"cash_balance": balance.get("cashBalance"),
|
247
|
-
"frozen_balance": balance.get("frozenBalance"),
|
248
|
-
"equity": balance.get("equity"),
|
249
|
-
"unrealized": balance.get("unrealized"),
|
250
|
-
"bonus": balance.get("bonus"),
|
251
|
-
"last_bonus": balance.get("lastBonus"),
|
252
|
-
"wallet_balance": balance.get("walletBalance"),
|
253
|
-
"voucher": balance.get("voucher"),
|
254
|
-
"voucher_using": balance.get("voucherUsing"),
|
255
|
-
})
|
290
|
+
data_to_insert.append(self._fmt(balance))
|
256
291
|
self._clear()
|
257
292
|
self._insert(data_to_insert)
|
293
|
+
|
294
|
+
def _on_message(self, msg: dict[str, Any]) -> None:
|
295
|
+
data: dict = msg.get("data", {})
|
296
|
+
self._update([self._fmt(data)])
|
258
297
|
|
259
298
|
class OurbitSwapDataStore(DataStoreCollection):
|
260
299
|
"""
|
@@ -318,6 +357,12 @@ class OurbitSwapDataStore(DataStoreCollection):
|
|
318
357
|
self.book._on_message(msg)
|
319
358
|
if channel == "push.tickers":
|
320
359
|
self.ticker._on_message(msg)
|
360
|
+
if channel == "push.personal.position":
|
361
|
+
self.position._on_message(msg)
|
362
|
+
if channel == "push.personal.order":
|
363
|
+
self.orders._on_message(msg)
|
364
|
+
if channel == "push.personal.asset":
|
365
|
+
self.balance._on_message(msg)
|
321
366
|
else:
|
322
367
|
logger.debug(f"未知的channel: {channel}")
|
323
368
|
|
@@ -436,7 +481,7 @@ class OurbitSwapDataStore(DataStoreCollection):
|
|
436
481
|
"side": "buy",
|
437
482
|
"price": "110152.5",
|
438
483
|
"size": "0.1",
|
439
|
-
"
|
484
|
+
"state": "open", // ("open", "closed", "canceled")
|
440
485
|
"create_ts": 1625247600000,
|
441
486
|
"update_ts": 1625247600000
|
442
487
|
}
|
@@ -448,57 +493,58 @@ class OurbitSwapDataStore(DataStoreCollection):
|
|
448
493
|
def position(self) -> Position:
|
449
494
|
"""
|
450
495
|
持仓数据
|
451
|
-
|
452
496
|
Data structure:
|
453
|
-
.. code:: python
|
454
|
-
[
|
455
|
-
{
|
456
|
-
"position_id": "123456",
|
457
|
-
"symbol": "BTC_USDT",
|
458
|
-
"side": "long",
|
459
|
-
"open_type": "limit",
|
460
|
-
"state": "open",
|
461
|
-
"hold_vol": "0.1",
|
462
|
-
"frozen_vol": "0.0",
|
463
|
-
"close_vol": "0.0",
|
464
|
-
"hold_avg_price": "110152.5",
|
465
|
-
"open_avg_price": "110152.5",
|
466
|
-
"close_avg_price": "0.0",
|
467
|
-
"liquidate_price": "100000.0",
|
468
|
-
"oim": "0.0",
|
469
|
-
"im": "0.0",
|
470
|
-
"hold_fee": "0.0",
|
471
|
-
"realised": "0.0",
|
472
|
-
"leverage": "10",
|
473
|
-
"margin_ratio": "0.1",
|
474
|
-
"create_ts": 1625247600000,
|
475
|
-
"update_ts": 1625247600000
|
476
|
-
}
|
477
|
-
]
|
478
|
-
"""
|
479
|
-
return self._get("position", Position)
|
480
497
|
|
481
|
-
|
482
|
-
|
483
|
-
"""账户余额数据
|
484
|
-
|
485
|
-
Data structure:
|
486
|
-
.. code:: python
|
498
|
+
.. code:: json
|
499
|
+
|
487
500
|
[
|
488
501
|
{
|
489
|
-
"
|
490
|
-
"
|
491
|
-
"
|
492
|
-
"
|
493
|
-
"
|
494
|
-
"
|
495
|
-
"
|
496
|
-
"
|
497
|
-
"
|
498
|
-
"
|
499
|
-
"
|
500
|
-
"
|
502
|
+
"position_id": "123456",
|
503
|
+
"symbol": "BTC_USDT",
|
504
|
+
"side": "long",
|
505
|
+
"open_type": "limit",
|
506
|
+
"state": "open",
|
507
|
+
"hold_vol": "0.1",
|
508
|
+
"frozen_vol": "0.0",
|
509
|
+
"close_vol": "0.0",
|
510
|
+
"hold_avg_price": "110152.5",
|
511
|
+
"open_avg_price": "110152.5",
|
512
|
+
"close_avg_price": "0.0",
|
513
|
+
"liquidate_price": "100000.0",
|
514
|
+
"oim": "0.0",
|
515
|
+
"im": "0.0",
|
516
|
+
"hold_fee": "0.0",
|
517
|
+
"realised": "0.0",
|
518
|
+
"leverage": "10",
|
519
|
+
"margin_ratio": "0.1",
|
520
|
+
"create_ts": 1625247600000,
|
521
|
+
"update_ts": 1625247600000
|
501
522
|
}
|
502
523
|
]
|
503
524
|
"""
|
525
|
+
return self._get("position", Position)
|
526
|
+
|
527
|
+
@property
|
528
|
+
def balance(self) -> Balance:
|
529
|
+
@property
|
530
|
+
def balance(self) -> Balance:
|
531
|
+
"""账户余额数据
|
532
|
+
|
533
|
+
Data structure:
|
534
|
+
|
535
|
+
.. code:: python
|
536
|
+
|
537
|
+
[
|
538
|
+
{
|
539
|
+
"currency": "USDT", # 币种
|
540
|
+
"position_margin": 0.3052, # 持仓保证金
|
541
|
+
"available_balance": 19.7284, # 可用余额
|
542
|
+
"frozen_balance": 0, # 冻结余额
|
543
|
+
"bonus": 0, # 奖励
|
544
|
+
"last_bonus": 0, # 最后奖励
|
545
|
+
"wallet_balance": 20.0337 # 钱包余额
|
546
|
+
}
|
547
|
+
]
|
548
|
+
"""
|
549
|
+
return self._get("balance", Balance)
|
504
550
|
return self._get("balance", Balance)
|
hyperquant/broker/ourbit.py
CHANGED
@@ -87,6 +87,13 @@ class OurbitSwap:
|
|
87
87
|
hdlr_json=self.store.onmessage
|
88
88
|
)
|
89
89
|
|
90
|
+
async def sub_personal(self):
|
91
|
+
self.client.ws_connect(
|
92
|
+
self.ws_url,
|
93
|
+
send_json={ "method": "sub.personal.user.preference"},
|
94
|
+
hdlr_json=self.store.onmessage
|
95
|
+
)
|
96
|
+
|
90
97
|
def ret_content(self, res: pybotters.FetchResult):
|
91
98
|
match res.data:
|
92
99
|
case {"success": True}:
|
@@ -113,7 +120,7 @@ class OurbitSwap:
|
|
113
120
|
position_id: Optional[int] = None,
|
114
121
|
):
|
115
122
|
"""
|
116
|
-
size
|
123
|
+
size为合约张数, openType 1 为逐仓, 2为全仓
|
117
124
|
|
118
125
|
.. code ::
|
119
126
|
{
|
@@ -134,12 +141,12 @@ class OurbitSwap:
|
|
134
141
|
price = self.fmt_price(symbol, price)
|
135
142
|
|
136
143
|
print(f'下单价格 {price}')
|
137
|
-
leverage =
|
144
|
+
leverage = min(max_lev, leverage)
|
138
145
|
|
139
146
|
data = {
|
140
147
|
"symbol": symbol,
|
141
148
|
"side": 1 if side == "buy" else 3,
|
142
|
-
"openType":
|
149
|
+
"openType": 2,
|
143
150
|
"type": "5",
|
144
151
|
"vol": size,
|
145
152
|
"leverage": leverage,
|
hyperquant/broker/ws.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import asyncio
|
2
2
|
import pybotters
|
3
|
-
|
3
|
+
from pybotters.ws import ClientWebSocketResponse, logger
|
4
4
|
|
5
5
|
class Heartbeat:
|
6
6
|
@staticmethod
|
@@ -9,4 +9,29 @@ class Heartbeat:
|
|
9
9
|
await ws.send_str('{"method":"ping"}')
|
10
10
|
await asyncio.sleep(10.0)
|
11
11
|
|
12
|
-
pybotters.ws.HeartbeatHosts.items['futures.ourbit.com'] = Heartbeat.ourbit
|
12
|
+
pybotters.ws.HeartbeatHosts.items['futures.ourbit.com'] = Heartbeat.ourbit
|
13
|
+
|
14
|
+
class WssAuth:
|
15
|
+
@staticmethod
|
16
|
+
async def ourbit(ws: ClientWebSocketResponse):
|
17
|
+
key: str = ws._response._session.__dict__["_apis"][
|
18
|
+
pybotters.ws.AuthHosts.items[ws._response.url.host].name
|
19
|
+
][0]
|
20
|
+
await ws.send_json(
|
21
|
+
{
|
22
|
+
"method": "login",
|
23
|
+
"param": {
|
24
|
+
"token": key
|
25
|
+
}
|
26
|
+
}
|
27
|
+
)
|
28
|
+
async for msg in ws:
|
29
|
+
# {"channel":"rs.login","data":"success","ts":1756470267848}
|
30
|
+
data = msg.json()
|
31
|
+
if data.get("channel") == "rs.login":
|
32
|
+
if data.get("data") == "success":
|
33
|
+
break
|
34
|
+
else:
|
35
|
+
logger.warning(f"WebSocket login failed: {data}")
|
36
|
+
|
37
|
+
pybotters.ws.AuthHosts.items['futures.ourbit.com'] = pybotters.auth.Item("ourbit", WssAuth.ourbit)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: hyperquant
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.36
|
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
|
@@ -4,18 +4,18 @@ hyperquant/db.py,sha256=i2TjkCbmH4Uxo7UTDvOYBfy973gLcGexdzuT_YcSeIE,6678
|
|
4
4
|
hyperquant/draw.py,sha256=up_lQ3pHeVLoNOyh9vPjgNwjD0M-6_IetSGviQUgjhY,54624
|
5
5
|
hyperquant/logkit.py,sha256=WALpXpIA3Ywr5DxKKK3k5EKubZ2h-ISGfc5dUReQUBQ,7795
|
6
6
|
hyperquant/notikit.py,sha256=x5yAZ_tAvLQRXcRbcg-VabCaN45LUhvlTZnUqkIqfAA,3596
|
7
|
-
hyperquant/broker/auth.py,sha256=
|
7
|
+
hyperquant/broker/auth.py,sha256=F9rNP4I8tJTk3IKwA7Z-DG8DMiCZRMQYZPTE9Gigf-Y,1635
|
8
8
|
hyperquant/broker/hyperliquid.py,sha256=7MxbI9OyIBcImDelPJu-8Nd53WXjxPB5TwE6gsjHbto,23252
|
9
|
-
hyperquant/broker/ourbit.py,sha256=
|
10
|
-
hyperquant/broker/ws.py,sha256=
|
9
|
+
hyperquant/broker/ourbit.py,sha256=FcWIilfr4k-As9UcT3aYbeUWCqZIYahF6Ruc7Qf3tT4,9813
|
10
|
+
hyperquant/broker/ws.py,sha256=03-5okpoJincYGvE7vWgu6mdrEchLmsa-ZrGxmsJJ4Q,1239
|
11
11
|
hyperquant/broker/lib/hpstore.py,sha256=LnLK2zmnwVvhEbLzYI-jz_SfYpO1Dv2u2cJaRAb84D8,8296
|
12
12
|
hyperquant/broker/lib/hyper_types.py,sha256=HqjjzjUekldjEeVn6hxiWA8nevAViC2xHADOzDz9qyw,991
|
13
13
|
hyperquant/broker/models/hyperliquid.py,sha256=c4r5739ibZfnk69RxPjQl902AVuUOwT8RNvKsMtwXBY,9459
|
14
|
-
hyperquant/broker/models/ourbit.py,sha256=
|
14
|
+
hyperquant/broker/models/ourbit.py,sha256=oP_iJRXlYR-itjrO9wa4nxlkmMxExVMDSZWDfff24Ek,19846
|
15
15
|
hyperquant/datavison/_util.py,sha256=92qk4vO856RqycO0YqEIHJlEg-W9XKapDVqAMxe6rbw,533
|
16
16
|
hyperquant/datavison/binance.py,sha256=3yNKTqvt_vUQcxzeX4ocMsI5k6Q6gLZrvgXxAEad6Kc,5001
|
17
17
|
hyperquant/datavison/coinglass.py,sha256=PEjdjISP9QUKD_xzXNzhJ9WFDTlkBrRQlVL-5pxD5mo,10482
|
18
18
|
hyperquant/datavison/okx.py,sha256=yg8WrdQ7wgWHNAInIgsWPM47N3Wkfr253169IPAycAY,6898
|
19
|
-
hyperquant-0.
|
20
|
-
hyperquant-0.
|
21
|
-
hyperquant-0.
|
19
|
+
hyperquant-0.36.dist-info/METADATA,sha256=x504f5mx-Xud9fmgA9U9cxf-GjC17mCB8NTppxsVR30,4317
|
20
|
+
hyperquant-0.36.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
21
|
+
hyperquant-0.36.dist-info/RECORD,,
|
File without changes
|