hyperquant 0.33__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,37 +239,61 @@ 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()
229
256
  self._insert(data_to_insert)
257
+ else:
258
+ self._clear()
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)])
230
269
 
231
270
  class Balance(DataStore):
232
271
  _KEYS = ["currency"]
233
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
+
234
284
  def _onresponse(self, data: dict[str, Any]):
235
285
  balances = data.get("data", [])
236
286
  if balances:
237
287
  data_to_insert: list[Item] = []
238
288
  for balance in balances:
239
289
  balance: dict[str, Any] = balance
240
- data_to_insert.append({
241
- "currency": balance.get("currency"),
242
- "position_margin": balance.get("positionMargin"),
243
- "available_balance": balance.get("availableBalance"),
244
- "cash_balance": balance.get("cashBalance"),
245
- "frozen_balance": balance.get("frozenBalance"),
246
- "equity": balance.get("equity"),
247
- "unrealized": balance.get("unrealized"),
248
- "bonus": balance.get("bonus"),
249
- "last_bonus": balance.get("lastBonus"),
250
- "wallet_balance": balance.get("walletBalance"),
251
- "voucher": balance.get("voucher"),
252
- "voucher_using": balance.get("voucherUsing"),
253
- })
290
+ data_to_insert.append(self._fmt(balance))
254
291
  self._clear()
255
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)])
256
297
 
257
298
  class OurbitSwapDataStore(DataStoreCollection):
258
299
  """
@@ -316,6 +357,12 @@ class OurbitSwapDataStore(DataStoreCollection):
316
357
  self.book._on_message(msg)
317
358
  if channel == "push.tickers":
318
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)
319
366
  else:
320
367
  logger.debug(f"未知的channel: {channel}")
321
368
 
@@ -434,7 +481,7 @@ class OurbitSwapDataStore(DataStoreCollection):
434
481
  "side": "buy",
435
482
  "price": "110152.5",
436
483
  "size": "0.1",
437
- "status": "open",
484
+ "state": "open", // ("open", "closed", "canceled")
438
485
  "create_ts": 1625247600000,
439
486
  "update_ts": 1625247600000
440
487
  }
@@ -446,57 +493,58 @@ class OurbitSwapDataStore(DataStoreCollection):
446
493
  def position(self) -> Position:
447
494
  """
448
495
  持仓数据
449
-
450
496
  Data structure:
451
- .. code:: python
452
- [
453
- {
454
- "position_id": "123456",
455
- "symbol": "BTC_USDT",
456
- "side": "long",
457
- "open_type": "limit",
458
- "state": "open",
459
- "hold_vol": "0.1",
460
- "frozen_vol": "0.0",
461
- "close_vol": "0.0",
462
- "hold_avg_price": "110152.5",
463
- "open_avg_price": "110152.5",
464
- "close_avg_price": "0.0",
465
- "liquidate_price": "100000.0",
466
- "oim": "0.0",
467
- "im": "0.0",
468
- "hold_fee": "0.0",
469
- "realised": "0.0",
470
- "leverage": "10",
471
- "margin_ratio": "0.1",
472
- "create_ts": 1625247600000,
473
- "update_ts": 1625247600000
474
- }
475
- ]
476
- """
477
- return self._get("position", Position)
478
497
 
479
- @property
480
- def balance(self) -> Balance:
481
- """账户余额数据
482
-
483
- Data structure:
484
- .. code:: python
498
+ .. code:: json
499
+
485
500
  [
486
501
  {
487
- "currency": "USDT", # 币种
488
- "position_margin": 0.3052, # 持仓保证金
489
- "available_balance": 19.7284, # 可用余额
490
- "cash_balance": 19.7284, # 现金余额
491
- "frozen_balance": 0, # 冻结余额
492
- "equity": 19.9442, # 权益
493
- "unrealized": -0.0895, # 未实现盈亏
494
- "bonus": 0, # 奖励
495
- "last_bonus": 0, # 最后奖励
496
- "wallet_balance": 20.0337, # 钱包余额
497
- "voucher": 0, # 代金券
498
- "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
499
522
  }
500
523
  ]
501
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)
502
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.33
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=zvjtx6fmOuOPAJ8jxD2RK9_mao2XuE2EXkxR74nmXKM,18525
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.33.dist-info/METADATA,sha256=TNKlHrMHpNgxFAsCIE3Oos_-skhADOqL7xMM6VrscjQ,4317
20
- hyperquant-0.33.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
21
- hyperquant-0.33.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,,