hyperquant 0.34__py3-none-any.whl → 0.35__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 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
- def _onresponse(self, data: dict[str, Any]):
197
- positions = data.get("data", [])
198
- if positions:
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
- "status": "open",
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
- @property
482
- def balance(self) -> Balance:
483
- """账户余额数据
484
-
485
- Data structure:
486
- .. code:: python
498
+ .. code:: json
499
+
487
500
  [
488
501
  {
489
- "currency": "USDT", # 币种
490
- "position_margin": 0.3052, # 持仓保证金
491
- "available_balance": 19.7284, # 可用余额
492
- "cash_balance": 19.7284, # 现金余额
493
- "frozen_balance": 0, # 冻结余额
494
- "equity": 19.9442, # 权益
495
- "unrealized": -0.0895, # 未实现盈亏
496
- "bonus": 0, # 奖励
497
- "last_bonus": 0, # 最后奖励
498
- "wallet_balance": 20.0337, # 钱包余额
499
- "voucher": 0, # 代金券
500
- "voucher_using": 0 # 使用中的代金券
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)
@@ -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}:
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.34
3
+ Version: 0.35
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=hrdVuBTZwD3xSy5GI5JTFJhKiHhvIjZHA_7G5IUFznQ,1580
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=ejaRAM3Q8Iho7ng3o7xx-j0mT3GjrTykfVZNMTg5lO4,9567
10
- hyperquant/broker/ws.py,sha256=98Djt5n5sHUJKVbQ8Ql1t-G-Wiwu__4MYcUr5P6SDL0,326
9
+ hyperquant/broker/ourbit.py,sha256=fbxuxnl5LcokUcfxeV3cgn58d3xnJZSrePS9pIPbXj0,9779
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=M37U3z9RrMuaFKLKZLi1i35R2-IYWCcrAaCMSsSoX9Y,18565
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.34.dist-info/METADATA,sha256=4qgi7gW9ck2jaF72_nb6a-J98V1RpRiMMmv-8BdX3N0,4317
20
- hyperquant-0.34.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
21
- hyperquant-0.34.dist-info/RECORD,,
19
+ hyperquant-0.35.dist-info/METADATA,sha256=-GcBn0cIun3j0WIdBrf10pF4vnpGXTPAET6360tGiQw,4317
20
+ hyperquant-0.35.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
21
+ hyperquant-0.35.dist-info/RECORD,,