hyperquant 0.3__py3-none-any.whl → 0.5__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.
@@ -1,6 +1,8 @@
1
+ import asyncio
1
2
  from typing import Literal, Optional
2
3
  import pybotters
3
- from .models.ourbit import OurbitSwapDataStore
4
+ from .models.ourbit import OurbitSwapDataStore, OurbitSpotDataStore
5
+ from decimal import Decimal, ROUND_HALF_UP
4
6
 
5
7
 
6
8
  class OurbitSwap:
@@ -24,7 +26,8 @@ class OurbitSwap:
24
26
  return self
25
27
 
26
28
  async def update(
27
- self, update_type: Literal["position", "orders", "balance", "ticker", "all"] = "all"
29
+ self,
30
+ update_type: Literal["position", "orders", "balance", "ticker", "all"] = "all",
28
31
  ):
29
32
  """由于交易所很多不支持ws推送,这里使用Rest"""
30
33
  all_urls = [
@@ -32,6 +35,7 @@ class OurbitSwap:
32
35
  f"{self.api_url}/api/v1/private/order/list/open_orders?page_size=200",
33
36
  f"{self.api_url}/api/v1/private/account/assets",
34
37
  f"{self.api_url}/api/v1/contract/ticker",
38
+ f"{self.api_url}/api/platform/spot/market/v2/symbols",
35
39
  ]
36
40
 
37
41
  url_map = {
@@ -53,16 +57,11 @@ class OurbitSwap:
53
57
  async def sub_tickers(self):
54
58
  self.client.ws_connect(
55
59
  self.ws_url,
56
- send_json={
57
- "method": "sub.tickers",
58
- "param": {
59
- "timezone": "UTC+8"
60
- }
61
- },
62
- hdlr_json=self.store.onmessage
60
+ send_json={"method": "sub.tickers", "param": {"timezone": "UTC+8"}},
61
+ hdlr_json=self.store.onmessage,
63
62
  )
64
63
 
65
- async def sub_order_book(self, symbols: str | list[str]):
64
+ async def sub_orderbook(self, symbols: str | list[str]):
66
65
  if isinstance(symbols, str):
67
66
  symbols = [symbols]
68
67
 
@@ -71,19 +70,23 @@ class OurbitSwap:
71
70
 
72
71
  for symbol in symbols:
73
72
  step = self.store.detail.find({"symbol": symbol})[0].get("tick_size")
74
-
75
- send_jsons.append({
76
- "method": "sub.depth.step",
77
- "param": {
78
- "symbol": symbol,
79
- "step": str(step)
73
+
74
+ send_jsons.append(
75
+ {
76
+ "method": "sub.depth.step",
77
+ "param": {"symbol": symbol, "step": str(step)},
80
78
  }
81
- })
79
+ )
82
80
 
83
81
  await self.client.ws_connect(
82
+ self.ws_url, send_json=send_jsons, hdlr_json=self.store.onmessage
83
+ )
84
+
85
+ async def sub_personal(self):
86
+ self.client.ws_connect(
84
87
  self.ws_url,
85
- send_json=send_jsons,
86
- hdlr_json=self.store.onmessage
88
+ send_json={"method": "sub.personal.user.preference"},
89
+ hdlr_json=self.store.onmessage,
87
90
  )
88
91
 
89
92
  def ret_content(self, res: pybotters.FetchResult):
@@ -92,12 +95,20 @@ class OurbitSwap:
92
95
  return res.data["data"]
93
96
  case _:
94
97
  raise Exception(f"Failed api {res.response.url}: {res.data}")
95
-
98
+
99
+ def fmt_price(self, symbol, price: float) -> float:
100
+ tick = self.store.detail.find({"symbol": symbol})[0].get("tick_size")
101
+ tick_dec = Decimal(str(tick))
102
+ price_dec = Decimal(str(price))
103
+ return float(
104
+ (price_dec / tick_dec).quantize(Decimal("1"), rounding=ROUND_HALF_UP)
105
+ * tick_dec
106
+ )
96
107
 
97
108
  async def place_order(
98
109
  self,
99
110
  symbol: str,
100
- side: Literal["buy", "sell", "close"],
111
+ side: Literal["buy", "sell", "close_buy", "close_sell"],
101
112
  size: float = None,
102
113
  price: float = None,
103
114
  order_type: Literal["market", "limit_GTC", "limit_IOC"] = "market",
@@ -105,23 +116,34 @@ class OurbitSwap:
105
116
  leverage: Optional[int] = 20,
106
117
  position_id: Optional[int] = None,
107
118
  ):
108
- """size为合约张数"""
119
+ """
120
+ size为合约张数, openType 1 为逐仓, 2为全仓
121
+
122
+ .. code ::
123
+ {
124
+ "orderId": "219602019841167810",
125
+ "ts": 1756395601543
126
+ }
127
+
128
+ """
109
129
  if (size is None) == (usdt_amount is None):
110
130
  raise ValueError("params err")
111
131
 
112
132
  max_lev = self.store.detail.find({"symbol": symbol})[0].get("max_lev")
113
-
133
+
114
134
  if usdt_amount is not None:
115
135
  cs = self.store.detail.find({"symbol": symbol})[0].get("contract_sz")
116
136
  size = max(int(usdt_amount / cs / price), 1)
117
-
118
137
 
119
- leverage = max(max_lev, leverage)
138
+ if price is not None:
139
+ price = self.fmt_price(symbol, price)
140
+
141
+ leverage = min(max_lev, leverage)
120
142
 
121
143
  data = {
122
144
  "symbol": symbol,
123
145
  "side": 1 if side == "buy" else 3,
124
- "openType": 1,
146
+ "openType": 2,
125
147
  "type": "5",
126
148
  "vol": size,
127
149
  "leverage": leverage,
@@ -136,17 +158,64 @@ class OurbitSwap:
136
158
  data["type"] = "1"
137
159
  data["price"] = str(price)
138
160
 
139
- if side == "close":
140
- data["side"] = 4
161
+ if "close" in side:
162
+ if side == "close_buy":
163
+ data["side"] = 2
164
+ elif side == "close_sell":
165
+ data["side"] = 4
166
+
141
167
  if position_id is None:
142
168
  raise ValueError("position_id is required for closing position")
143
169
  data["positionId"] = position_id
144
-
145
- res = await self.client.fetch(
170
+ # import time
171
+ # print(time.time(), '下单')
172
+ res = await self.client.fetch(
146
173
  "POST", f"{self.api_url}/api/v1/private/order/create", data=data
147
174
  )
148
175
  return self.ret_content(res)
149
176
 
177
+ async def place_tpsl(
178
+ self,
179
+ position_id: int,
180
+ take_profit: Optional[float] = None,
181
+ stop_loss: Optional[float] = None,
182
+ ):
183
+ """
184
+ position_id 持仓ID
185
+
186
+ .. code:: json
187
+
188
+ {
189
+ "success": true,
190
+ "code": 0,
191
+ "data": 2280508
192
+ }
193
+ """
194
+ if (take_profit is None) and (stop_loss is None):
195
+ raise ValueError("params err")
196
+
197
+ data = {
198
+ "positionId": position_id,
199
+ "profitTrend": "1",
200
+ "lossTrend": "1",
201
+ "profitLossVolType": "SAME",
202
+ "volType": 2,
203
+ "takeProfitReverse": 2,
204
+ "stopLossReverse": 2,
205
+ "priceProtect": "0",
206
+ }
207
+
208
+ if take_profit is not None:
209
+ data["takeProfitPrice"] = take_profit
210
+ if stop_loss is not None:
211
+ data["stopLossPrice"] = stop_loss
212
+
213
+ res = await self.client.fetch(
214
+ "POST", f"{self.api_url}/api/v1/private/stoporder/place", data=data
215
+ )
216
+
217
+ return self.ret_content(res)
218
+
150
219
  async def cancel_orders(self, order_ids: list[str]):
151
220
  res = await self.client.fetch(
152
221
  "POST",
@@ -230,5 +299,241 @@ class OurbitSwap:
230
299
  "GET",
231
300
  f"{self.api_url}/api/v1/private/order/deal_details/{order_id}",
232
301
  )
233
-
234
302
  return self.ret_content(res)
303
+
304
+
305
+ class OurbitSpot:
306
+
307
+ def __init__(self, client: pybotters.Client, personal_msg_cb: callable=None):
308
+ """
309
+ ✅ 完成:
310
+ 下单, 撤单, 查询资金, 查询持有订单, 查询历史订单
311
+
312
+ """
313
+ self.client = client
314
+ self.store = OurbitSpotDataStore()
315
+ self.api_url = "https://www.ourbit.com"
316
+ self.ws_url = "wss://www.ourbit.com/ws"
317
+ self.personal_msg_cb = personal_msg_cb
318
+
319
+ async def __aenter__(self) -> "OurbitSpot":
320
+ client = self.client
321
+ await self.store.initialize(
322
+ client.get(f"{self.api_url}/api/platform/spot/market/v2/symbols")
323
+ )
324
+ return self
325
+
326
+ async def update(
327
+ self, update_type: Literal["orders", "balance", "ticker", "book", "all"] = "all"
328
+ ):
329
+
330
+ all_urls = [
331
+ f"{self.api_url}/api/platform/spot/order/current/orders/v2?orderTypes=1%2C2%2C3%2C4%2C5%2C100&pageNum=1&pageSize=100&states=0%2C1%2C3",
332
+ f"{self.api_url}/api/assetbussiness/asset/spot/statistic",
333
+ f"{self.api_url}/api/platform/spot/market/v2/tickers",
334
+ ]
335
+
336
+ # orderTypes=1%2C2%2C3%2C4%2C5%2C100&pageNum=1&pageSize=100&states=0%2C1%2C3
337
+
338
+ url_map = {
339
+ "orders": [all_urls[0]],
340
+ "balance": [all_urls[1]],
341
+ "ticker": [all_urls[2]],
342
+ "all": all_urls,
343
+ }
344
+
345
+ try:
346
+ urls = url_map[update_type]
347
+ except KeyError:
348
+ raise ValueError(f"Unknown update type: {update_type}")
349
+
350
+ # 直接传协程进去,initialize 会自己 await
351
+ await self.store.initialize(*(self.client.get(url) for url in urls))
352
+
353
+ async def sub_personal(self):
354
+ """订阅个人频道"""
355
+ # https://www.ourbit.com/ucenter/api/ws_token
356
+ res = await self.client.fetch("GET", f"{self.api_url}/ucenter/api/ws_token")
357
+
358
+ token = res.data["data"].get("wsToken")
359
+
360
+ app = self.client.ws_connect(
361
+ f"{self.ws_url}?wsToken={token}&platform=web",
362
+ send_json={
363
+ "method": "SUBSCRIPTION",
364
+ "params": [
365
+ "spot@private.orders",
366
+ "spot@private.trigger.orders",
367
+ "spot@private.balances",
368
+ ],
369
+ "id": 1,
370
+ },
371
+ hdlr_json=(
372
+ self.store.onmessage
373
+ if self.personal_msg_cb is None
374
+ else [self.store.onmessage, self.personal_msg_cb]
375
+ ),
376
+ )
377
+
378
+ await app._event.wait()
379
+
380
+ async def sub_orderbook(self, symbols: str | list[str]):
381
+ """订阅订单簿深度数据
382
+
383
+ Args:
384
+ symbols: 交易对符号,可以是单个字符串或字符串列表
385
+ """
386
+ import logging
387
+
388
+ logger = logging.getLogger("OurbitSpot")
389
+
390
+ if isinstance(symbols, str):
391
+ symbols = [symbols]
392
+
393
+ # 构建订阅参数
394
+ subscription_params = []
395
+ for symbol in symbols:
396
+ subscription_params.append(f"spot@public.increase.aggre.depth@{symbol}")
397
+
398
+ # 一次sub20个,超过需要分开订阅
399
+ for i in range(0, len(subscription_params), 20):
400
+ wsapp = self.client.ws_connect(
401
+ "wss://www.ourbit.com/ws?platform=web",
402
+ send_json={
403
+ "method": "SUBSCRIPTION",
404
+ "params": subscription_params[i : i + 20],
405
+ "id": 2,
406
+ },
407
+ hdlr_json=self.store.onmessage,
408
+ )
409
+ await wsapp._event.wait()
410
+
411
+ # await asyncio.sleep(1) # 等待ws连接稳定
412
+
413
+ # 并发获取每个交易对的初始深度数据
414
+ tasks = [
415
+ self.client.fetch(
416
+ "GET", f"{self.api_url}/api/platform/spot/market/depth?symbol={symbol}"
417
+ )
418
+ for symbol in symbols
419
+ ]
420
+
421
+ # 等待所有请求完成
422
+ responses = await asyncio.gather(*tasks)
423
+
424
+ # 处理响应数据
425
+ for idx, response in enumerate(responses):
426
+ symbol = symbols[idx]
427
+ self.store.book._onresponse(response.data)
428
+
429
+ async def check_loss():
430
+ await asyncio.sleep(1)
431
+ while True:
432
+ loss = self.store.book.loss
433
+ for symbol, is_loss in loss.items():
434
+ if is_loss:
435
+ resp = await self.client.fetch(
436
+ "GET",
437
+ f"{self.api_url}/api/platform/spot/market/depth?symbol={symbol}",
438
+ )
439
+ self.store.book._onresponse(resp.data)
440
+ await asyncio.sleep(1)
441
+
442
+ asyncio.create_task(check_loss())
443
+
444
+ async def place_order(
445
+ self,
446
+ symbol: str,
447
+ side: Literal["buy", "sell"],
448
+ price: float = None,
449
+ quantity: float = None,
450
+ order_type: Literal["market", "limit"] = "limit",
451
+ usdt_amount: float = None,
452
+ ):
453
+ """现货下单
454
+
455
+ Args:
456
+ symbol: 交易对,如 "SOL_USDT"
457
+ side: 买卖方向 "buy" 或 "sell"
458
+ price: 价格,市价单可为None
459
+ quantity: 数量
460
+ order_type: 订单类型 "market" 或 "limit"
461
+ usdt_amount: USDT金额,如果指定则根据价格计算数量
462
+
463
+ Returns:
464
+ 订单响应数据
465
+ """
466
+ # 参数检查
467
+ if order_type == "limit" and price is None:
468
+ raise ValueError("Limit orders require a price")
469
+ if quantity is None and usdt_amount is None:
470
+ raise ValueError("Either quantity or usdt_amount must be specified")
471
+
472
+ # 解析交易对
473
+ parts = symbol.split("_")
474
+ if len(parts) != 2:
475
+ raise ValueError(f"Invalid symbol format: {symbol}")
476
+
477
+ currency, market = parts
478
+
479
+ # 获取交易对详情
480
+ detail = self.store.detail.get({"name": currency})
481
+ if not detail:
482
+ raise ValueError(f"Unknown currency: {currency}")
483
+
484
+ price_scale = detail.get("price_scale")
485
+ quantity_scale = detail.get("quantity_scale")
486
+
487
+ # 构建请求数据
488
+ data = {"currency": currency, "market": market, "tradeType": side.upper()}
489
+
490
+ # 处理市价单和限价单的不同参数
491
+ if order_type == "limit":
492
+ data["orderType"] = "LIMIT_ORDER"
493
+ data["price"] = str(
494
+ round(price, price_scale) if price_scale is not None else price
495
+ )
496
+
497
+ # 计算并设置数量
498
+ if quantity is None and usdt_amount is not None and price:
499
+ quantity = usdt_amount / price
500
+
501
+ if quantity_scale is not None:
502
+ quantity = round(quantity, quantity_scale)
503
+ data["quantity"] = str(quantity)
504
+
505
+ elif order_type == "market":
506
+ data["orderType"] = "MARKET_ORDER"
507
+
508
+ # 市价单可以使用数量或金额,但不能同时使用
509
+ if usdt_amount is not None:
510
+ data["amount"] = str(usdt_amount)
511
+ else:
512
+ if quantity_scale is not None:
513
+ quantity = round(quantity, quantity_scale)
514
+ data["quantity"] = str(quantity)
515
+
516
+ if price:
517
+ data["price"] = str(price)
518
+
519
+ # 确定API端点
520
+ url = f'{self.api_url}/api/platform/spot/{"v4/order/place" if order_type == "market" else "order/place"}'
521
+ # print(f"Placing {symbol}: {data}")
522
+ # 发送请求
523
+ res = await self.client.fetch("POST", url, json=data)
524
+
525
+ # 处理响应
526
+ if res.data.get("msg") == "success":
527
+ return res.data["data"]
528
+ raise Exception(f"Failed to place order: {res.data}")
529
+
530
+ async def cancel_orders(self, order_ids: list[str]):
531
+
532
+ for order_id in order_ids:
533
+ url = f"{self.api_url}/api/platform/spot/order/cancel/v2?orderId={order_id}"
534
+ await self.client.fetch("DELETE", url)
535
+
536
+ async def cancel_order(self, order_id: str):
537
+
538
+ url = f"{self.api_url}/api/platform/spot/order/cancel/v2?orderId={order_id}"
539
+ res = await self.client.fetch("DELETE", url)
hyperquant/broker/ws.py CHANGED
@@ -1,5 +1,8 @@
1
1
  import asyncio
2
2
  import pybotters
3
+ from pybotters.ws import ClientWebSocketResponse, logger
4
+ from pybotters.auth import Hosts
5
+ import yarl
3
6
 
4
7
 
5
8
  class Heartbeat:
@@ -8,5 +11,38 @@ class Heartbeat:
8
11
  while not ws.closed:
9
12
  await ws.send_str('{"method":"ping"}')
10
13
  await asyncio.sleep(10.0)
14
+
15
+ async def ourbit_spot(ws: pybotters.ws.ClientWebSocketResponse):
16
+ while not ws.closed:
17
+ await ws.send_str('{"method":"ping"}')
18
+ await asyncio.sleep(10.0)
19
+
20
+ pybotters.ws.HeartbeatHosts.items['futures.ourbit.com'] = Heartbeat.ourbit
21
+ pybotters.ws.HeartbeatHosts.items['www.ourbit.com'] = Heartbeat.ourbit_spot
22
+
23
+ class WssAuth:
24
+ @staticmethod
25
+ async def ourbit(ws: ClientWebSocketResponse):
26
+ key: str = ws._response._session.__dict__["_apis"][
27
+ pybotters.ws.AuthHosts.items[ws._response.url.host].name
28
+ ][0]
29
+ await ws.send_json(
30
+ {
31
+ "method": "login",
32
+ "param": {
33
+ "token": key
34
+ }
35
+ }
36
+ )
37
+ async for msg in ws:
38
+ # {"channel":"rs.login","data":"success","ts":1756470267848}
39
+ data = msg.json()
40
+ if data.get("channel") == "rs.login":
41
+ if data.get("data") == "success":
42
+ break
43
+ else:
44
+ logger.warning(f"WebSocket login failed: {data}")
45
+
46
+
11
47
 
12
- pybotters.ws.HeartbeatHosts.items['futures.ourbit.com'] = Heartbeat.ourbit
48
+ pybotters.ws.AuthHosts.items['futures.ourbit.com'] = pybotters.auth.Item("ourbit", WssAuth.ourbit)
hyperquant/core.py CHANGED
@@ -269,10 +269,10 @@ class ExchangeBase:
269
269
  )
270
270
 
271
271
  class Exchange(ExchangeBase):
272
- def __init__(self, trade_symbols, fee=0.0002, initial_balance=10000, recorded=False):
272
+ def __init__(self, trade_symbols:list=[], fee=0.0002, initial_balance=10000, recorded=False):
273
273
  super().__init__(initial_balance=initial_balance, recorded=recorded)
274
274
  self.fee = fee
275
- self.trade_symbols = trade_symbols
275
+ self.trade_symbols:list = trade_symbols
276
276
  self.id_gen = 0
277
277
  self.account['USDT'].update({
278
278
  'hold': 0,
@@ -280,8 +280,12 @@ class Exchange(ExchangeBase):
280
280
  'short': 0
281
281
  })
282
282
  for symbol in trade_symbols:
283
- self.account[symbol] = {'amount': 0, 'hold_price': 0, 'value': 0, 'price': 0,
284
- 'realised_profit': 0, 'unrealised_profit': 0, 'fee': 0}
283
+ self.account[symbol] = self._act_template
284
+
285
+ @property
286
+ def _act_template(self):
287
+ return {'amount': 0, 'hold_price': 0, 'value': 0, 'price': 0,
288
+ 'realised_profit': 0, 'unrealised_profit': 0, 'fee': 0}.copy()
285
289
 
286
290
  def Trade(self, symbol, direction, price, amount, **kwargs):
287
291
  if self.recorded and 'time' not in kwargs:
@@ -307,8 +311,7 @@ class Exchange(ExchangeBase):
307
311
 
308
312
  if symbol not in self.trade_symbols:
309
313
  self.trade_symbols.append(symbol)
310
- self.account[symbol] = {'amount': 0, 'hold_price': 0, 'value': 0, 'price': 0,
311
- 'realised_profit': 0, 'unrealised_profit': 0, 'fee': 0}
314
+ self.account[symbol] = self._act_template
312
315
 
313
316
  cover_amount = 0 if direction * self.account[symbol]['amount'] >= 0 else min(abs(self.account[symbol]['amount']), amount)
314
317
  open_amount = amount - cover_amount
@@ -343,6 +346,7 @@ class Exchange(ExchangeBase):
343
346
 
344
347
  if kwargs:
345
348
  self.opt.update(kwargs)
349
+ self.account[symbol].update(kwargs)
346
350
 
347
351
  # 记录账户总资产到 history
348
352
  if self.recorded:
hyperquant/logkit.py CHANGED
@@ -146,10 +146,25 @@ class MinConsoleHandler(logging.StreamHandler):
146
146
  else:
147
147
  super().emit(record)
148
148
 
149
+ # ====================================================================================================
150
+ # ** NullLogger 类,用于禁用日志 **
151
+ # ====================================================================================================
152
+ class NullLogger:
153
+ def debug(self, *a, **k): pass
154
+ def info(self, *a, **k): pass
155
+ def ok(self, *a, **k): pass
156
+ def warning(self, *a, **k): pass
157
+ def error(self, *a, **k): pass
158
+ def critical(self, *a, **k): pass
159
+ def exception(self, *a, **k): pass
160
+ def divider(self, *a, **k): pass
161
+
149
162
  # ====================================================================================================
150
163
  # ** 功能函数 **
151
164
  # ====================================================================================================
152
- def get_logger(name=None, file_path=None, show_time=False, use_color=True, timezone="Asia/Shanghai") -> Logger:
165
+ def get_logger(name=None, file_path=None, show_time=False, use_color=True, timezone="Asia/Shanghai", level: object = None, enable_console: bool = True, enabled: bool = True):
166
+ if not enabled:
167
+ return NullLogger()
153
168
  if name is None:
154
169
  name = '_'
155
170
  logger_instance = Logger(name, show_time, use_color, timezone) # 传递时区参数
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperquant
3
- Version: 0.3
3
+ Version: 0.5
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
@@ -1,21 +1,21 @@
1
1
  hyperquant/__init__.py,sha256=UpjiX4LS5jmrBc2kE8RiLR02eCfD8JDQrR1q8zkLNcQ,161
2
- hyperquant/core.py,sha256=7XrpuHvccWl9lNyVihqaptupqUMsG3xYmQr8eEDrwS4,20610
2
+ hyperquant/core.py,sha256=iEI8qTNpyesB_w67SrKXeGoB9JllovBeJKI0EZFYew4,20631
3
3
  hyperquant/db.py,sha256=i2TjkCbmH4Uxo7UTDvOYBfy973gLcGexdzuT_YcSeIE,6678
4
4
  hyperquant/draw.py,sha256=up_lQ3pHeVLoNOyh9vPjgNwjD0M-6_IetSGviQUgjhY,54624
5
- hyperquant/logkit.py,sha256=WALpXpIA3Ywr5DxKKK3k5EKubZ2h-ISGfc5dUReQUBQ,7795
5
+ hyperquant/logkit.py,sha256=nUo7nx5eONvK39GOhWwS41zNRL756P2J7-5xGzwXnTY,8462
6
6
  hyperquant/notikit.py,sha256=x5yAZ_tAvLQRXcRbcg-VabCaN45LUhvlTZnUqkIqfAA,3596
7
- hyperquant/broker/auth.py,sha256=hrdVuBTZwD3xSy5GI5JTFJhKiHhvIjZHA_7G5IUFznQ,1580
8
- hyperquant/broker/hyperliquid.py,sha256=R_PZn5D-OFAvqsFnom4kUWHqbKPltdjura0VVvQvc2A,23265
9
- hyperquant/broker/ourbit.py,sha256=2_Dbs-E_c-IZKMfFTy8yhkOUnoXq8G5XMkpPm1ZYQUI,7679
10
- hyperquant/broker/ws.py,sha256=98Djt5n5sHUJKVbQ8Ql1t-G-Wiwu__4MYcUr5P6SDL0,326
7
+ hyperquant/broker/auth.py,sha256=oA9Yw1I59-u0Tnoj2e4wUup5q8V5T2qpga5RKbiAiZI,2614
8
+ hyperquant/broker/hyperliquid.py,sha256=7MxbI9OyIBcImDelPJu-8Nd53WXjxPB5TwE6gsjHbto,23252
9
+ hyperquant/broker/ourbit.py,sha256=Fza_nhfoSCf1Ulm7UHOlt969Wm37bKja9P5xpN93XqY,17902
10
+ hyperquant/broker/ws.py,sha256=umRzxwCaZaRIgIq4YY-AuA0wCXFT0uOBmQbIXFY8CK0,1555
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=QuKxUYlJzRC4zr0lNiz3dpireConbsRyOtOvA0_VTVA,39978
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.3.dist-info/METADATA,sha256=Fn_RGo3ffo7nq4-GxrvQjgJWpNmKjmWlK6SNhgY7Yaw,4316
20
- hyperquant-0.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
21
- hyperquant-0.3.dist-info/RECORD,,
19
+ hyperquant-0.5.dist-info/METADATA,sha256=A-fC66EHBajjznMPB06z6oaMCWHEy3TTmUUXxK2CLcU,4316
20
+ hyperquant-0.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
21
+ hyperquant-0.5.dist-info/RECORD,,