hyperquant 1.22__py3-none-any.whl → 1.24__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.

Potentially problematic release.


This version of hyperquant might be problematic. Click here for more details.

@@ -9,6 +9,7 @@ import pybotters
9
9
 
10
10
  from lighter.api.account_api import AccountApi
11
11
  from lighter.api.order_api import OrderApi
12
+ from lighter.api.candlestick_api import CandlestickApi
12
13
  from lighter.api_client import ApiClient
13
14
  from lighter.configuration import Configuration
14
15
  from lighter.signer_client import SignerClient
@@ -31,6 +32,7 @@ class Lighter:
31
32
  api_key_index: int = 3,
32
33
  api_client: ApiClient | None = None,
33
34
  order_api: OrderApi | None = None,
35
+ candlestick_api: CandlestickApi | None = None,
34
36
  account_api: AccountApi | None = None,
35
37
  ws_url: str | None = None,
36
38
  ) -> None:
@@ -46,6 +48,7 @@ class Lighter:
46
48
  self._owns_api_client = api_client is None
47
49
 
48
50
  self.order_api = order_api or OrderApi(self._api_client)
51
+ self.candlestick_api = candlestick_api or CandlestickApi(self._api_client)
49
52
  self.account_api = account_api or AccountApi(self._api_client)
50
53
  self.signer: SignerClient = None
51
54
 
@@ -150,7 +153,9 @@ class Lighter:
150
153
 
151
154
 
152
155
  if include_detail:
153
- tasks.append(("detail", self.order_api.order_books()))
156
+ # Use raw HTTP to avoid strict SDK model validation issues (e.g., status 'inactive').
157
+ url = f"{self.configuration.host.rstrip('/')}/api/v1/orderBooks"
158
+ tasks.append(("detail", self.client.get(url)))
154
159
 
155
160
  if include_orders:
156
161
  if account_index is None or symbol is None:
@@ -206,7 +211,12 @@ class Lighter:
206
211
  results: dict[str, Any] = {}
207
212
  for key, coroutine in tasks:
208
213
  try:
209
- results[key] = await coroutine
214
+ resp = await coroutine
215
+ if key == "detail":
216
+ # Parse JSON body for detail endpoint
217
+ results[key] = await resp.json()
218
+ else:
219
+ results[key] = resp
210
220
  except Exception:
211
221
  logger.exception("Lighter REST request %s failed", key)
212
222
  raise
@@ -300,6 +310,71 @@ class Lighter:
300
310
 
301
311
  return await self.sub_orderbook(market_ids=[], account_ids=account_ids)
302
312
 
313
+ async def sub_kline(
314
+ self,
315
+ symbols: Sequence[str] | str,
316
+ *,
317
+ resolutions: Sequence[str] | str,
318
+ ) -> pybotters.ws.WebSocketApp:
319
+ """Subscribe to trade streams and aggregate into klines in the store.
320
+
321
+ - symbols: list of symbols (e.g., ["BTC-USD"]) or a single symbol; may also be numeric market_ids.
322
+ - resolutions: list like ["1m", "5m"] or a single resolution; added to kline store for aggregation.
323
+ """
324
+
325
+ # Normalize inputs
326
+ symbol_list = [symbols] if isinstance(symbols, str) else list(symbols)
327
+ res_list = [resolutions] if isinstance(resolutions, str) else list(resolutions)
328
+
329
+ if not symbol_list:
330
+ raise ValueError("At least one symbol must be provided")
331
+ if not res_list:
332
+ raise ValueError("At least one resolution must be provided")
333
+
334
+ # Ensure market metadata for symbol->market_id resolution
335
+ needs_detail = any(self.get_contract_id(sym) is None and not str(sym).isdigit() for sym in symbol_list)
336
+ if needs_detail:
337
+ try:
338
+ await self.update("detail")
339
+ except Exception:
340
+ logger.exception("Failed to refresh Lighter market metadata for kline subscription")
341
+ raise
342
+
343
+ # Resolve market ids and populate id->symbol mapping for klines store
344
+ trade_market_ids: list[str] = []
345
+ for sym in symbol_list:
346
+ market_id = self.get_contract_id(sym)
347
+ if market_id is None:
348
+ if str(sym).isdigit():
349
+ market_id = str(sym)
350
+ symbol_for_map = str(sym)
351
+ else:
352
+ raise ValueError(f"Unknown symbol: {sym}")
353
+ else:
354
+ symbol_for_map = sym
355
+ market_id_str = str(market_id)
356
+ trade_market_ids.append(market_id_str)
357
+ # ensure klines store can resolve symbol from market id
358
+ self.store.klines.id_to_symbol[market_id_str] = symbol_for_map
359
+
360
+ # Register resolutions into kline store aggregation list
361
+ for r in res_list:
362
+ if r not in self.store.klines._res_list:
363
+ self.store.klines._res_list.append(r)
364
+
365
+ # Build subscribe payload for trade channels
366
+ channels = [f"trade/{mid}" for mid in trade_market_ids]
367
+ send_payload = [{"type": "subscribe", "channel": ch} for ch in channels]
368
+
369
+ ws_app = self.client.ws_connect(
370
+ self.ws_url,
371
+ send_json=send_payload,
372
+ hdlr_json=self.store.onmessage,
373
+ )
374
+
375
+ await ws_app._event.wait()
376
+ return ws_app
377
+
303
378
  async def place_order(
304
379
  self,
305
380
  symbol: str,
@@ -468,3 +543,60 @@ class Lighter:
468
543
  "request": request_payload,
469
544
  "response": response_payload,
470
545
  }
546
+
547
+ async def update_kline(
548
+ self,
549
+ symbol: str,
550
+ *,
551
+ resolution: str,
552
+ start_timestamp: int,
553
+ end_timestamp: int,
554
+ count_back: int,
555
+ set_timestamp_to_end: bool | None = None,
556
+ ) -> list[dict[str, Any]]:
557
+ """Fetch candlesticks and update the Kline store.
558
+
559
+ Parameters
560
+ - symbol: market symbol, e.g. "BTC-USD".
561
+ - resolution: e.g. "1m", "5m", "1h".
562
+ - start_timestamp: epoch milliseconds.
563
+ - end_timestamp: epoch milliseconds.
564
+ - count_back: number of bars to fetch.
565
+ - set_timestamp_to_end: if True, API sets last bar timestamp to the end.
566
+ """
567
+
568
+ market_id = self.get_contract_id(symbol)
569
+ if market_id is None:
570
+ # try to refresh metadata once
571
+ await self.update("detail")
572
+ market_id = self.get_contract_id(symbol)
573
+ if market_id is None:
574
+ raise ValueError(f"Unknown symbol: {symbol}")
575
+
576
+ resp = await self.candlestick_api.candlesticks(
577
+ market_id=int(market_id),
578
+ resolution=resolution,
579
+ start_timestamp=int(start_timestamp),
580
+ end_timestamp=int(end_timestamp),
581
+ count_back=int(count_back),
582
+ set_timestamp_to_end=bool(set_timestamp_to_end) if set_timestamp_to_end is not None else None,
583
+ )
584
+
585
+ # Update store
586
+ self.store.klines._onresponse(resp, symbol=symbol, resolution=resolution)
587
+
588
+ payload = _maybe_to_dict(resp) or {}
589
+ items = payload.get("candlesticks") or []
590
+ # attach symbol/resolution to return
591
+ out: list[dict[str, Any]] = []
592
+ for it in items:
593
+ if hasattr(it, "to_dict"):
594
+ d = it.to_dict()
595
+ elif hasattr(it, "model_dump"):
596
+ d = it.model_dump()
597
+ else:
598
+ d = dict(it) if isinstance(it, dict) else {"value": it}
599
+ d["symbol"] = symbol
600
+ d["resolution"] = resolution
601
+ out.append(d)
602
+ return out
@@ -324,6 +324,282 @@ class Positions(DataStore):
324
324
  self._update_positions(account_index, positions)
325
325
 
326
326
 
327
+ class Klines(DataStore):
328
+ """Candlestick/Kline store keyed by (symbol, resolution, timestamp).
329
+
330
+ - Maintains a list of active resolutions in ``_res_list`` (populated by REST updates).
331
+ - Updates candles in real-time by aggregating trade websocket messages.
332
+ """
333
+
334
+ _KEYS = ["symbol", "resolution", "timestamp"]
335
+
336
+ def _init(self) -> None:
337
+ self.id_to_symbol: dict[str, str] = {}
338
+ self._current_symbol: str | None = None
339
+ self._res_list: list[str] = []
340
+ # Track last processed trade_id to deduplicate snapshot trades after reconnect
341
+ self._last_trade_id_by_market: dict[str, int] = {}
342
+ self._last_trade_id_by_symbol: dict[str, int] = {}
343
+
344
+ @staticmethod
345
+ def _resolution_to_ms(resolution: str) -> int | None:
346
+ try:
347
+ res = resolution.strip().lower()
348
+ except Exception:
349
+ return None
350
+ # Common forms: 1m, 5m, 1h, 1d; also allow pure digits => seconds
351
+ unit = res[-1]
352
+ num_part = res[:-1] if unit in {"s", "m", "h", "d", "w"} else res
353
+ try:
354
+ n = int(num_part)
355
+ except Exception:
356
+ return None
357
+ if unit == "s":
358
+ return n * 1000
359
+ if unit == "m" or unit not in {"s", "h", "d", "w"}: # default minutes if no unit
360
+ return n * 60 * 1000
361
+ if unit == "h":
362
+ return n * 60 * 60 * 1000
363
+ if unit == "d":
364
+ return n * 24 * 60 * 60 * 1000
365
+ if unit == "w":
366
+ return n * 7 * 24 * 60 * 60 * 1000
367
+ return None
368
+
369
+ @staticmethod
370
+ def _market_id_from_channel(channel: str | None) -> str | None:
371
+ if not channel:
372
+ return None
373
+ if ":" in channel:
374
+ return channel.split(":", 1)[1]
375
+ if "/" in channel:
376
+ return channel.split("/", 1)[1]
377
+ return channel
378
+
379
+ def _compose_item(
380
+ self,
381
+ *,
382
+ symbol: str,
383
+ resolution: str,
384
+ ts: int,
385
+ price: float,
386
+ size: float,
387
+ last_trade_id: int | None,
388
+ open_price: float | None = None,
389
+ ) -> dict[str, Any]:
390
+ return {
391
+ "symbol": symbol,
392
+ "resolution": resolution,
393
+ "timestamp": ts,
394
+ "open": price if open_price is None else float(open_price),
395
+ "high": price,
396
+ "low": price,
397
+ "close": price,
398
+ "volume0": abs(size),
399
+ "volume1": abs(size) * price,
400
+ "last_trade_id": last_trade_id or 0,
401
+ }
402
+
403
+ def _ensure_backfill(self, *, symbol: str, resolution: str, new_bucket_ts: int) -> None:
404
+ """Backfill missing empty bars up to (but not including) new_bucket_ts.
405
+
406
+ Uses the last known close as O/H/L/C for synthetic bars and zero volume.
407
+ """
408
+ step = self._resolution_to_ms(resolution)
409
+ if not step:
410
+ return
411
+ # find the last existing bar before new_bucket_ts
412
+ rows = self.find({"symbol": symbol, "resolution": resolution})
413
+ prev = None
414
+ prev_ts = None
415
+ for r in rows:
416
+ try:
417
+ ts = int(r.get("timestamp"))
418
+ except Exception:
419
+ continue
420
+ if ts < new_bucket_ts and (prev_ts is None or ts > prev_ts):
421
+ prev = r
422
+ prev_ts = ts
423
+ if prev is None or prev_ts is None:
424
+ return
425
+ expected = prev_ts + step
426
+ while expected < new_bucket_ts:
427
+ prev_close = float(prev.get("close"))
428
+ fill_item = {
429
+ "symbol": symbol,
430
+ "resolution": resolution,
431
+ "timestamp": expected,
432
+ "open": prev_close,
433
+ "high": prev_close,
434
+ "low": prev_close,
435
+ "close": prev_close,
436
+ "volume0": 0.0,
437
+ "volume1": 0.0,
438
+ "last_trade_id": int(prev.get("last_trade_id", 0)) if prev.get("last_trade_id") is not None else 0,
439
+ }
440
+ self._insert([fill_item])
441
+ prev = fill_item
442
+ expected += step
443
+
444
+ def _merge_trade(self, *, symbol: str, trade_ts_ms: int, price: float, size: float, last_trade_id: int | None) -> None:
445
+ # Iterate active resolutions
446
+ for res in list(self._res_list):
447
+ interval_ms = self._resolution_to_ms(res)
448
+ if not interval_ms:
449
+ continue
450
+ bucket_ts = (trade_ts_ms // interval_ms) * interval_ms
451
+ # Upsert logic
452
+ existing = self.get({"symbol": symbol, "resolution": res, "timestamp": bucket_ts})
453
+ if existing is None:
454
+ # backfill any missing empty bars before creating a new bucket
455
+ self._ensure_backfill(symbol=symbol, resolution=res, new_bucket_ts=bucket_ts)
456
+ # open should be previous bar's close if exists; if none, fall back to current price
457
+ prev = None
458
+ rows = self.find({"symbol": symbol, "resolution": res})
459
+ prev_ts = None
460
+ for r in rows:
461
+ try:
462
+ ts = int(r.get("timestamp"))
463
+ except Exception:
464
+ continue
465
+ if ts < bucket_ts and (prev_ts is None or ts > prev_ts):
466
+ prev = r
467
+ prev_ts = ts
468
+ open_px = float(prev.get("close")) if prev is not None else price
469
+ self._insert([
470
+ self._compose_item(
471
+ symbol=symbol,
472
+ resolution=res,
473
+ ts=bucket_ts,
474
+ price=price,
475
+ size=size,
476
+ last_trade_id=last_trade_id,
477
+ open_price=open_px,
478
+ )
479
+ ])
480
+ continue
481
+ # merge into existing
482
+ updated = dict(existing)
483
+ o = float(updated.get("open", price))
484
+ h = float(updated.get("high", price))
485
+ l = float(updated.get("low", price))
486
+ c = float(updated.get("close", price))
487
+ v0 = float(updated.get("volume0", 0.0))
488
+ v1 = float(updated.get("volume1", 0.0))
489
+ p = float(price)
490
+ s = abs(float(size))
491
+ updated["open"] = o
492
+ updated["high"] = max(h, p)
493
+ updated["low"] = min(l, p)
494
+ updated["close"] = p
495
+ updated["volume0"] = v0 + s
496
+ updated["volume1"] = v1 + s * p
497
+ if last_trade_id is not None:
498
+ try:
499
+ updated["last_trade_id"] = max(int(last_trade_id), int(updated.get("last_trade_id", 0)))
500
+ except Exception:
501
+ updated["last_trade_id"] = int(last_trade_id)
502
+ self._update([updated])
503
+
504
+ def _onresponse(self, data: Any, *, symbol: str | None = None, resolution: str | None = None) -> None:
505
+ payload = _maybe_to_dict(data) or {}
506
+ candlesticks = payload.get("candlesticks") or []
507
+ res = payload.get("resolution") or resolution
508
+ if res not in self._res_list and res is not None:
509
+ self._res_list.append(res)
510
+
511
+ sym = symbol or self._current_symbol
512
+
513
+ # Sort incoming bars by timestamp to backfill in order
514
+ items: list[dict[str, Any]] = []
515
+ for c in sorted((candlesticks or []), key=lambda x: x.get("timestamp", 0)):
516
+ if not isinstance(c, dict):
517
+ continue
518
+ entry = dict(c)
519
+ if sym is not None:
520
+ entry["symbol"] = sym
521
+ if res is not None:
522
+ entry["resolution"] = res
523
+ items.append(entry)
524
+
525
+ # Insert or update per bar; backfill gaps before inserting new bars
526
+ for entry in items:
527
+ sym_i = entry.get("symbol")
528
+ res_i = entry.get("resolution")
529
+ ts_i = entry.get("timestamp")
530
+ if sym_i is None or res_i is None or ts_i is None:
531
+ continue
532
+ if self.get({"symbol": sym_i, "resolution": res_i, "timestamp": ts_i}) is None:
533
+ self._ensure_backfill(symbol=sym_i, resolution=res_i, new_bucket_ts=int(ts_i))
534
+ self._insert([entry])
535
+ else:
536
+ self._update([entry])
537
+
538
+ # Update last_trade_id baseline (by symbol) from REST bars if available
539
+ if sym is not None:
540
+ max_tid = 0
541
+ for e in items:
542
+ try:
543
+ tid = int(e.get("last_trade_id", 0))
544
+ except Exception:
545
+ tid = 0
546
+ if tid > max_tid:
547
+ max_tid = tid
548
+ if max_tid:
549
+ prev = self._last_trade_id_by_symbol.get(sym, 0)
550
+ if max_tid > prev:
551
+ self._last_trade_id_by_symbol[sym] = max_tid
552
+
553
+ def _on_message(self, msg: dict[str, Any]) -> None:
554
+ msg_type = msg.get("type")
555
+ if msg_type not in {"subscribed/trade", "update/trade"}:
556
+ return
557
+ market_id = self._market_id_from_channel(msg.get("channel"))
558
+ if market_id is None:
559
+ return
560
+ market_id_str = str(market_id)
561
+ symbol = self.id_to_symbol.get(market_id_str) or market_id_str
562
+ trades = msg.get("trades") or []
563
+ # Baseline last trade_id from market and symbol
564
+ base_last_tid = max(
565
+ self._last_trade_id_by_market.get(market_id_str, 0),
566
+ self._last_trade_id_by_symbol.get(symbol, 0),
567
+ )
568
+ # Process in ascending trade_id order for stability
569
+ try:
570
+ trades_sorted = sorted(trades, key=lambda x: int(x.get("trade_id", 0)))
571
+ except Exception:
572
+ trades_sorted = trades
573
+
574
+ last_tid = base_last_tid
575
+ for t in trades_sorted:
576
+ if not isinstance(t, dict):
577
+ continue
578
+ ts = t.get("timestamp")
579
+ price = t.get("price")
580
+ size = t.get("size")
581
+ trade_id = t.get("trade_id")
582
+ try:
583
+ ts = int(ts)
584
+ p = float(price)
585
+ s = float(size)
586
+ tid = int(trade_id) if trade_id is not None else 0
587
+ except Exception:
588
+ continue
589
+ # Skip stale or duplicate snapshot trades
590
+ if tid and last_tid and tid <= last_tid:
591
+ continue
592
+ self._merge_trade(symbol=symbol, trade_ts_ms=ts, price=p, size=s, last_trade_id=tid)
593
+ if tid > last_tid:
594
+ last_tid = tid
595
+
596
+ # Persist last processed trade_id for this market
597
+ if last_tid and last_tid > base_last_tid:
598
+ self._last_trade_id_by_market[market_id_str] = last_tid
599
+
600
+
601
+
602
+
327
603
  class LighterDataStore(DataStoreCollection):
328
604
  """Data store collection for the Lighter exchange."""
329
605
 
@@ -333,12 +609,14 @@ class LighterDataStore(DataStoreCollection):
333
609
  self._create("orders", datastore_class=Orders)
334
610
  self._create("accounts", datastore_class=Accounts)
335
611
  self._create("positions", datastore_class=Positions)
612
+ self._create("klines", datastore_class=Klines)
336
613
 
337
614
  def set_id_to_symbol(self, id_to_symbol: dict[str, str]) -> None:
338
615
  self.id_to_symbol = id_to_symbol
339
616
  self.book.id_to_symbol = self.id_to_symbol
340
617
  self.orders.id_to_symbol = self.id_to_symbol
341
618
  self.positions.id_to_symbol = self.id_to_symbol
619
+ self.klines.id_to_symbol = self.id_to_symbol
342
620
 
343
621
  def onmessage(self, msg: Item, ws: ClientWebSocketResponse | None = None) -> None:
344
622
 
@@ -354,6 +632,8 @@ class LighterDataStore(DataStoreCollection):
354
632
  elif msg_type in {"subscribed/account_all", "update/account_all"}:
355
633
  self.accounts._on_message(msg)
356
634
  self.positions._on_message(msg)
635
+ elif msg_type in {"subscribed/trade", "update/trade"}:
636
+ self.klines._on_message(msg)
357
637
 
358
638
 
359
639
  @property
@@ -506,3 +786,24 @@ class LighterDataStore(DataStoreCollection):
506
786
  ]
507
787
  """
508
788
  return self._get("positions")
789
+
790
+ @property
791
+ def klines(self) -> "Klines":
792
+ """
793
+ K线/蜡烛图数据(`lighter.models.Candlesticks` -> `lighter.models.Candlestick`)。
794
+
795
+ .. code:: json
796
+
797
+ {
798
+ "symbol": "BTC",
799
+ "timestamp": 1730612700000,
800
+ "open": 68970.5,
801
+ "high": 69012.3,
802
+ "low": 68890.0,
803
+ "close": 68995.1,
804
+ "volume0": 12.34,
805
+ "volume1": 850000.0,
806
+ "resolution": "1m"
807
+ }
808
+ """
809
+ return self._get("klines")
hyperquant/broker/ws.py CHANGED
@@ -37,12 +37,19 @@ class Heartbeat:
37
37
  while not ws.closed:
38
38
  await ws.send_json({"event": "ping"})
39
39
  await asyncio.sleep(3.0)
40
+
41
+ @staticmethod
42
+ async def lighter(ws: ClientWebSocketResponse):
43
+ while not ws.closed:
44
+ await ws.send_json({"type":"ping"})
45
+ await asyncio.sleep(3)
40
46
 
41
47
  pybotters.ws.HeartbeatHosts.items['futures.ourbit.com'] = Heartbeat.ourbit
42
48
  pybotters.ws.HeartbeatHosts.items['www.ourbit.com'] = Heartbeat.ourbit_spot
43
49
  pybotters.ws.HeartbeatHosts.items['quote.edgex.exchange'] = Heartbeat.edgex
44
50
  pybotters.ws.HeartbeatHosts.items['uuws.rerrkvifj.com'] = Heartbeat.lbank
45
51
  pybotters.ws.HeartbeatHosts.items['ws.futurescw.com'] = Heartbeat.coinw
52
+ pybotters.ws.HeartbeatHosts.items['mainnet.zklighter.elliot.ai'] = Heartbeat.lighter
46
53
 
47
54
  class WssAuth:
48
55
  @staticmethod
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperquant
3
- Version: 1.22
3
+ Version: 1.24
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
@@ -16,15 +16,12 @@ Requires-Python: >=3.11
16
16
  Requires-Dist: aiohttp>=3.10.4
17
17
  Requires-Dist: colorama>=0.4.6
18
18
  Requires-Dist: cryptography>=44.0.2
19
- Requires-Dist: curl-cffi>=0.13.0
20
19
  Requires-Dist: duckdb>=1.2.2
21
- Requires-Dist: lighter-sdk
22
- Requires-Dist: numba>=0.62.1
20
+ Requires-Dist: lighter-sdk>=0.1.4
23
21
  Requires-Dist: numpy>=1.21.0
24
22
  Requires-Dist: pandas>=2.2.3
25
23
  Requires-Dist: pybotters>=1.9.1
26
24
  Requires-Dist: pyecharts>=2.0.8
27
- Requires-Dist: rnet==3.0.0rc10
28
25
  Description-Content-Type: text/markdown
29
26
 
30
27
  # minquant
@@ -7,31 +7,29 @@ hyperquant/notikit.py,sha256=x5yAZ_tAvLQRXcRbcg-VabCaN45LUhvlTZnUqkIqfAA,3596
7
7
  hyperquant/broker/auth.py,sha256=3Ws_2iaF2NTCVIbkLzL_0ae3y7f4vgj9zmozvGaUXnk,16029
8
8
  hyperquant/broker/bitget.py,sha256=X_S0LKZ7FZAEb6oEMr1vdGP1fondzK74BhmNTpRDSEA,9488
9
9
  hyperquant/broker/bitmart.py,sha256=7j_8TU3Dxjj5HCNX7CbSO3nPZcQH1t31A9UOv5tTbg0,25974
10
- hyperquant/broker/coinup.py,sha256=eOr8BTRXiTb5tCU2FDmvBdXXgqiwVmCbP5pdeA1ORJ8,20390
11
10
  hyperquant/broker/coinw.py,sha256=SnJU0vASh77rfcpMGWaIfTblQSjQk3vjlW_4juYdbcs,17214
12
11
  hyperquant/broker/edgex.py,sha256=TqUO2KRPLN_UaxvtLL6HnA9dAQXC1sGxOfqTHd6W5k8,18378
13
12
  hyperquant/broker/hyperliquid.py,sha256=7MxbI9OyIBcImDelPJu-8Nd53WXjxPB5TwE6gsjHbto,23252
14
13
  hyperquant/broker/lbank.py,sha256=98M5wmSoeHwbBYMA3rh25zqLb6fQKVaEmwqALF5nOvY,22181
15
- hyperquant/broker/lighter.py,sha256=lW4LgEJHqbiUZPu2BE1aKz_JBIW94KTKnCkD9vuHt-Q,16695
14
+ hyperquant/broker/lighter.py,sha256=mgkdHjXXmOAhVlU8uZJ28fcPqzHM0fnjJXXYMTD4wJs,21918
16
15
  hyperquant/broker/ourbit.py,sha256=NUcDSIttf-HGWzoW1uBTrGLPHlkuemMjYCm91MigTno,18228
17
- hyperquant/broker/ws.py,sha256=NS71Do-62mtVrGcyNE-AtHJkDecsSxdz_KU1yNBr_BQ,4079
16
+ hyperquant/broker/ws.py,sha256=AzyFAHIDF4exxwm_IAEV6ihftwAlu19al8Vla4ygk-A,4354
18
17
  hyperquant/broker/lib/edgex_sign.py,sha256=lLUCmY8HHRLfLKyGrlTJYaBlSHPsIMWg3EZnQJKcmyk,95785
19
18
  hyperquant/broker/lib/hpstore.py,sha256=LnLK2zmnwVvhEbLzYI-jz_SfYpO1Dv2u2cJaRAb84D8,8296
20
19
  hyperquant/broker/lib/hyper_types.py,sha256=HqjjzjUekldjEeVn6hxiWA8nevAViC2xHADOzDz9qyw,991
21
20
  hyperquant/broker/lib/util.py,sha256=iMU1qF0CHj5zzlIMEQGwjz-qtEVosEe7slXOCuB7Rcw,566
22
21
  hyperquant/broker/models/bitget.py,sha256=0RwDY75KrJb-c-oYoMxbqxWfsILe-n_Npojz4UFUq7c,11389
23
22
  hyperquant/broker/models/bitmart.py,sha256=O9RnU-XBeR9SzicG15jzuzK5oy2kMrRJAyZSqC8DXUw,21938
24
- hyperquant/broker/models/coinup.py,sha256=X_ngB2_sgTOdfAZqTyeWvCN03j-0_inZ6ugZKW6hR7k,11173
25
23
  hyperquant/broker/models/coinw.py,sha256=LvLMVP7i-qkkTK1ubw8eBkMK2RQmFoKPxdKqmC4IToY,22157
26
24
  hyperquant/broker/models/edgex.py,sha256=vPAkceal44cjTYKQ_0BoNAskOpmkno_Yo1KxgMLPc6Y,33954
27
25
  hyperquant/broker/models/hyperliquid.py,sha256=c4r5739ibZfnk69RxPjQl902AVuUOwT8RNvKsMtwXBY,9459
28
26
  hyperquant/broker/models/lbank.py,sha256=vHkNKxIMzpoC_EwcZnEOPOupizF92yGWi9GKxvYYFUQ,19181
29
- hyperquant/broker/models/lighter.py,sha256=RpqyMPrXbs4_OY9WSDep4T8pDhxDGaFQ8vdVmLZnfBg,16732
27
+ hyperquant/broker/models/lighter.py,sha256=vpo1xm3S_FEV3GOlbN9_qEhrgj8qXM7jP8jstBP2S0Y,28217
30
28
  hyperquant/broker/models/ourbit.py,sha256=xMcbuCEXd3XOpPBq0RYF2zpTFNnxPtuNJZCexMZVZ1k,41965
31
29
  hyperquant/datavison/_util.py,sha256=92qk4vO856RqycO0YqEIHJlEg-W9XKapDVqAMxe6rbw,533
32
30
  hyperquant/datavison/binance.py,sha256=3yNKTqvt_vUQcxzeX4ocMsI5k6Q6gLZrvgXxAEad6Kc,5001
33
31
  hyperquant/datavison/coinglass.py,sha256=PEjdjISP9QUKD_xzXNzhJ9WFDTlkBrRQlVL-5pxD5mo,10482
34
32
  hyperquant/datavison/okx.py,sha256=yg8WrdQ7wgWHNAInIgsWPM47N3Wkfr253169IPAycAY,6898
35
- hyperquant-1.22.dist-info/METADATA,sha256=dH0APcdAjkH7pyeQiyPyTd3tl1E2tQVYxMd5YlbzjrE,4438
36
- hyperquant-1.22.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
37
- hyperquant-1.22.dist-info/RECORD,,
33
+ hyperquant-1.24.dist-info/METADATA,sha256=B2YHKuabMfGfMSx-OVQnCphVOPL5K94nuG5m5PENKAs,4352
34
+ hyperquant-1.24.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
35
+ hyperquant-1.24.dist-info/RECORD,,