hyperquant 1.22__py3-none-any.whl → 1.25__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.
- hyperquant/broker/lighter.py +222 -12
- hyperquant/broker/models/lighter.py +360 -0
- hyperquant/broker/ws.py +7 -0
- {hyperquant-1.22.dist-info → hyperquant-1.25.dist-info}/METADATA +1 -4
- {hyperquant-1.22.dist-info → hyperquant-1.25.dist-info}/RECORD +6 -8
- hyperquant/broker/coinup.py +0 -591
- hyperquant/broker/models/coinup.py +0 -334
- {hyperquant-1.22.dist-info → hyperquant-1.25.dist-info}/WHEEL +0 -0
|
@@ -220,6 +220,62 @@ class Orders(DataStore):
|
|
|
220
220
|
if items:
|
|
221
221
|
self._insert(items)
|
|
222
222
|
|
|
223
|
+
def _on_message(self, msg: dict[str, Any]) -> None:
|
|
224
|
+
"""Handle websocket incremental updates for orders.
|
|
225
|
+
|
|
226
|
+
For WS updates we should not clear-and-reinsert. Instead:
|
|
227
|
+
- For fully filled or cancelled orders => delete
|
|
228
|
+
- Otherwise => update/insert
|
|
229
|
+
"""
|
|
230
|
+
if not isinstance(msg, dict):
|
|
231
|
+
return
|
|
232
|
+
|
|
233
|
+
orders_obj = msg.get("orders")
|
|
234
|
+
if orders_obj is None:
|
|
235
|
+
account = msg.get("account")
|
|
236
|
+
if isinstance(account, dict):
|
|
237
|
+
orders_obj = account.get("orders")
|
|
238
|
+
if not orders_obj:
|
|
239
|
+
return
|
|
240
|
+
|
|
241
|
+
# Normalize orders to a flat list of dicts
|
|
242
|
+
if isinstance(orders_obj, dict):
|
|
243
|
+
raw_list: list[dict[str, Any]] = []
|
|
244
|
+
for _, lst in orders_obj.items():
|
|
245
|
+
if isinstance(lst, list):
|
|
246
|
+
raw_list.extend([o for o in lst if isinstance(o, dict)])
|
|
247
|
+
elif isinstance(orders_obj, list):
|
|
248
|
+
raw_list = [o for o in orders_obj if isinstance(o, dict)]
|
|
249
|
+
else:
|
|
250
|
+
return
|
|
251
|
+
|
|
252
|
+
def _is_terminal(order: dict[str, Any]) -> bool:
|
|
253
|
+
status = str(order.get("status", "")).lower()
|
|
254
|
+
if status in {"cancelled", "canceled", "executed", "filled", "closed", "done"}:
|
|
255
|
+
return True
|
|
256
|
+
rem = order.get("remaining_base_amount")
|
|
257
|
+
try:
|
|
258
|
+
return float(rem) <= 0 if rem is not None else False
|
|
259
|
+
except Exception:
|
|
260
|
+
return False
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
for entry in raw_list:
|
|
264
|
+
normalized = self._normalize(entry)
|
|
265
|
+
if normalized is None:
|
|
266
|
+
continue
|
|
267
|
+
# enrich with symbol if mapping is available
|
|
268
|
+
if self.id_to_symbol:
|
|
269
|
+
market_id = entry.get("market_index")
|
|
270
|
+
if market_id is not None:
|
|
271
|
+
symbol = self.id_to_symbol.get(str(market_id))
|
|
272
|
+
if symbol is not None:
|
|
273
|
+
normalized["symbol"] = symbol
|
|
274
|
+
|
|
275
|
+
self._update([normalized])
|
|
276
|
+
if _is_terminal(entry):
|
|
277
|
+
self._delete([normalized])
|
|
278
|
+
|
|
223
279
|
|
|
224
280
|
|
|
225
281
|
|
|
@@ -324,6 +380,282 @@ class Positions(DataStore):
|
|
|
324
380
|
self._update_positions(account_index, positions)
|
|
325
381
|
|
|
326
382
|
|
|
383
|
+
class Klines(DataStore):
|
|
384
|
+
"""Candlestick/Kline store keyed by (symbol, resolution, timestamp).
|
|
385
|
+
|
|
386
|
+
- Maintains a list of active resolutions in ``_res_list`` (populated by REST updates).
|
|
387
|
+
- Updates candles in real-time by aggregating trade websocket messages.
|
|
388
|
+
"""
|
|
389
|
+
|
|
390
|
+
_KEYS = ["symbol", "resolution", "timestamp"]
|
|
391
|
+
|
|
392
|
+
def _init(self) -> None:
|
|
393
|
+
self.id_to_symbol: dict[str, str] = {}
|
|
394
|
+
self._current_symbol: str | None = None
|
|
395
|
+
self._res_list: list[str] = []
|
|
396
|
+
# Track last processed trade_id to deduplicate snapshot trades after reconnect
|
|
397
|
+
self._last_trade_id_by_market: dict[str, int] = {}
|
|
398
|
+
self._last_trade_id_by_symbol: dict[str, int] = {}
|
|
399
|
+
|
|
400
|
+
@staticmethod
|
|
401
|
+
def _resolution_to_ms(resolution: str) -> int | None:
|
|
402
|
+
try:
|
|
403
|
+
res = resolution.strip().lower()
|
|
404
|
+
except Exception:
|
|
405
|
+
return None
|
|
406
|
+
# Common forms: 1m, 5m, 1h, 1d; also allow pure digits => seconds
|
|
407
|
+
unit = res[-1]
|
|
408
|
+
num_part = res[:-1] if unit in {"s", "m", "h", "d", "w"} else res
|
|
409
|
+
try:
|
|
410
|
+
n = int(num_part)
|
|
411
|
+
except Exception:
|
|
412
|
+
return None
|
|
413
|
+
if unit == "s":
|
|
414
|
+
return n * 1000
|
|
415
|
+
if unit == "m" or unit not in {"s", "h", "d", "w"}: # default minutes if no unit
|
|
416
|
+
return n * 60 * 1000
|
|
417
|
+
if unit == "h":
|
|
418
|
+
return n * 60 * 60 * 1000
|
|
419
|
+
if unit == "d":
|
|
420
|
+
return n * 24 * 60 * 60 * 1000
|
|
421
|
+
if unit == "w":
|
|
422
|
+
return n * 7 * 24 * 60 * 60 * 1000
|
|
423
|
+
return None
|
|
424
|
+
|
|
425
|
+
@staticmethod
|
|
426
|
+
def _market_id_from_channel(channel: str | None) -> str | None:
|
|
427
|
+
if not channel:
|
|
428
|
+
return None
|
|
429
|
+
if ":" in channel:
|
|
430
|
+
return channel.split(":", 1)[1]
|
|
431
|
+
if "/" in channel:
|
|
432
|
+
return channel.split("/", 1)[1]
|
|
433
|
+
return channel
|
|
434
|
+
|
|
435
|
+
def _compose_item(
|
|
436
|
+
self,
|
|
437
|
+
*,
|
|
438
|
+
symbol: str,
|
|
439
|
+
resolution: str,
|
|
440
|
+
ts: int,
|
|
441
|
+
price: float,
|
|
442
|
+
size: float,
|
|
443
|
+
last_trade_id: int | None,
|
|
444
|
+
open_price: float | None = None,
|
|
445
|
+
) -> dict[str, Any]:
|
|
446
|
+
return {
|
|
447
|
+
"symbol": symbol,
|
|
448
|
+
"resolution": resolution,
|
|
449
|
+
"timestamp": ts,
|
|
450
|
+
"open": price if open_price is None else float(open_price),
|
|
451
|
+
"high": price,
|
|
452
|
+
"low": price,
|
|
453
|
+
"close": price,
|
|
454
|
+
"volume0": abs(size),
|
|
455
|
+
"volume1": abs(size) * price,
|
|
456
|
+
"last_trade_id": last_trade_id or 0,
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
def _ensure_backfill(self, *, symbol: str, resolution: str, new_bucket_ts: int) -> None:
|
|
460
|
+
"""Backfill missing empty bars up to (but not including) new_bucket_ts.
|
|
461
|
+
|
|
462
|
+
Uses the last known close as O/H/L/C for synthetic bars and zero volume.
|
|
463
|
+
"""
|
|
464
|
+
step = self._resolution_to_ms(resolution)
|
|
465
|
+
if not step:
|
|
466
|
+
return
|
|
467
|
+
# find the last existing bar before new_bucket_ts
|
|
468
|
+
rows = self.find({"symbol": symbol, "resolution": resolution})
|
|
469
|
+
prev = None
|
|
470
|
+
prev_ts = None
|
|
471
|
+
for r in rows:
|
|
472
|
+
try:
|
|
473
|
+
ts = int(r.get("timestamp"))
|
|
474
|
+
except Exception:
|
|
475
|
+
continue
|
|
476
|
+
if ts < new_bucket_ts and (prev_ts is None or ts > prev_ts):
|
|
477
|
+
prev = r
|
|
478
|
+
prev_ts = ts
|
|
479
|
+
if prev is None or prev_ts is None:
|
|
480
|
+
return
|
|
481
|
+
expected = prev_ts + step
|
|
482
|
+
while expected < new_bucket_ts:
|
|
483
|
+
prev_close = float(prev.get("close"))
|
|
484
|
+
fill_item = {
|
|
485
|
+
"symbol": symbol,
|
|
486
|
+
"resolution": resolution,
|
|
487
|
+
"timestamp": expected,
|
|
488
|
+
"open": prev_close,
|
|
489
|
+
"high": prev_close,
|
|
490
|
+
"low": prev_close,
|
|
491
|
+
"close": prev_close,
|
|
492
|
+
"volume0": 0.0,
|
|
493
|
+
"volume1": 0.0,
|
|
494
|
+
"last_trade_id": int(prev.get("last_trade_id", 0)) if prev.get("last_trade_id") is not None else 0,
|
|
495
|
+
}
|
|
496
|
+
self._insert([fill_item])
|
|
497
|
+
prev = fill_item
|
|
498
|
+
expected += step
|
|
499
|
+
|
|
500
|
+
def _merge_trade(self, *, symbol: str, trade_ts_ms: int, price: float, size: float, last_trade_id: int | None) -> None:
|
|
501
|
+
# Iterate active resolutions
|
|
502
|
+
for res in list(self._res_list):
|
|
503
|
+
interval_ms = self._resolution_to_ms(res)
|
|
504
|
+
if not interval_ms:
|
|
505
|
+
continue
|
|
506
|
+
bucket_ts = (trade_ts_ms // interval_ms) * interval_ms
|
|
507
|
+
# Upsert logic
|
|
508
|
+
existing = self.get({"symbol": symbol, "resolution": res, "timestamp": bucket_ts})
|
|
509
|
+
if existing is None:
|
|
510
|
+
# backfill any missing empty bars before creating a new bucket
|
|
511
|
+
self._ensure_backfill(symbol=symbol, resolution=res, new_bucket_ts=bucket_ts)
|
|
512
|
+
# open should be previous bar's close if exists; if none, fall back to current price
|
|
513
|
+
prev = None
|
|
514
|
+
rows = self.find({"symbol": symbol, "resolution": res})
|
|
515
|
+
prev_ts = None
|
|
516
|
+
for r in rows:
|
|
517
|
+
try:
|
|
518
|
+
ts = int(r.get("timestamp"))
|
|
519
|
+
except Exception:
|
|
520
|
+
continue
|
|
521
|
+
if ts < bucket_ts and (prev_ts is None or ts > prev_ts):
|
|
522
|
+
prev = r
|
|
523
|
+
prev_ts = ts
|
|
524
|
+
open_px = float(prev.get("close")) if prev is not None else price
|
|
525
|
+
self._insert([
|
|
526
|
+
self._compose_item(
|
|
527
|
+
symbol=symbol,
|
|
528
|
+
resolution=res,
|
|
529
|
+
ts=bucket_ts,
|
|
530
|
+
price=price,
|
|
531
|
+
size=size,
|
|
532
|
+
last_trade_id=last_trade_id,
|
|
533
|
+
open_price=open_px,
|
|
534
|
+
)
|
|
535
|
+
])
|
|
536
|
+
continue
|
|
537
|
+
# merge into existing
|
|
538
|
+
updated = dict(existing)
|
|
539
|
+
o = float(updated.get("open", price))
|
|
540
|
+
h = float(updated.get("high", price))
|
|
541
|
+
l = float(updated.get("low", price))
|
|
542
|
+
c = float(updated.get("close", price))
|
|
543
|
+
v0 = float(updated.get("volume0", 0.0))
|
|
544
|
+
v1 = float(updated.get("volume1", 0.0))
|
|
545
|
+
p = float(price)
|
|
546
|
+
s = abs(float(size))
|
|
547
|
+
updated["open"] = o
|
|
548
|
+
updated["high"] = max(h, p)
|
|
549
|
+
updated["low"] = min(l, p)
|
|
550
|
+
updated["close"] = p
|
|
551
|
+
updated["volume0"] = v0 + s
|
|
552
|
+
updated["volume1"] = v1 + s * p
|
|
553
|
+
if last_trade_id is not None:
|
|
554
|
+
try:
|
|
555
|
+
updated["last_trade_id"] = max(int(last_trade_id), int(updated.get("last_trade_id", 0)))
|
|
556
|
+
except Exception:
|
|
557
|
+
updated["last_trade_id"] = int(last_trade_id)
|
|
558
|
+
self._update([updated])
|
|
559
|
+
|
|
560
|
+
def _onresponse(self, data: Any, *, symbol: str | None = None, resolution: str | None = None) -> None:
|
|
561
|
+
payload = _maybe_to_dict(data) or {}
|
|
562
|
+
candlesticks = payload.get("candlesticks") or []
|
|
563
|
+
res = payload.get("resolution") or resolution
|
|
564
|
+
if res not in self._res_list and res is not None:
|
|
565
|
+
self._res_list.append(res)
|
|
566
|
+
|
|
567
|
+
sym = symbol or self._current_symbol
|
|
568
|
+
|
|
569
|
+
# Sort incoming bars by timestamp to backfill in order
|
|
570
|
+
items: list[dict[str, Any]] = []
|
|
571
|
+
for c in sorted((candlesticks or []), key=lambda x: x.get("timestamp", 0)):
|
|
572
|
+
if not isinstance(c, dict):
|
|
573
|
+
continue
|
|
574
|
+
entry = dict(c)
|
|
575
|
+
if sym is not None:
|
|
576
|
+
entry["symbol"] = sym
|
|
577
|
+
if res is not None:
|
|
578
|
+
entry["resolution"] = res
|
|
579
|
+
items.append(entry)
|
|
580
|
+
|
|
581
|
+
# Insert or update per bar; backfill gaps before inserting new bars
|
|
582
|
+
for entry in items:
|
|
583
|
+
sym_i = entry.get("symbol")
|
|
584
|
+
res_i = entry.get("resolution")
|
|
585
|
+
ts_i = entry.get("timestamp")
|
|
586
|
+
if sym_i is None or res_i is None or ts_i is None:
|
|
587
|
+
continue
|
|
588
|
+
if self.get({"symbol": sym_i, "resolution": res_i, "timestamp": ts_i}) is None:
|
|
589
|
+
self._ensure_backfill(symbol=sym_i, resolution=res_i, new_bucket_ts=int(ts_i))
|
|
590
|
+
self._insert([entry])
|
|
591
|
+
else:
|
|
592
|
+
self._update([entry])
|
|
593
|
+
|
|
594
|
+
# Update last_trade_id baseline (by symbol) from REST bars if available
|
|
595
|
+
if sym is not None:
|
|
596
|
+
max_tid = 0
|
|
597
|
+
for e in items:
|
|
598
|
+
try:
|
|
599
|
+
tid = int(e.get("last_trade_id", 0))
|
|
600
|
+
except Exception:
|
|
601
|
+
tid = 0
|
|
602
|
+
if tid > max_tid:
|
|
603
|
+
max_tid = tid
|
|
604
|
+
if max_tid:
|
|
605
|
+
prev = self._last_trade_id_by_symbol.get(sym, 0)
|
|
606
|
+
if max_tid > prev:
|
|
607
|
+
self._last_trade_id_by_symbol[sym] = max_tid
|
|
608
|
+
|
|
609
|
+
def _on_message(self, msg: dict[str, Any]) -> None:
|
|
610
|
+
msg_type = msg.get("type")
|
|
611
|
+
if msg_type not in {"subscribed/trade", "update/trade"}:
|
|
612
|
+
return
|
|
613
|
+
market_id = self._market_id_from_channel(msg.get("channel"))
|
|
614
|
+
if market_id is None:
|
|
615
|
+
return
|
|
616
|
+
market_id_str = str(market_id)
|
|
617
|
+
symbol = self.id_to_symbol.get(market_id_str) or market_id_str
|
|
618
|
+
trades = msg.get("trades") or []
|
|
619
|
+
# Baseline last trade_id from market and symbol
|
|
620
|
+
base_last_tid = max(
|
|
621
|
+
self._last_trade_id_by_market.get(market_id_str, 0),
|
|
622
|
+
self._last_trade_id_by_symbol.get(symbol, 0),
|
|
623
|
+
)
|
|
624
|
+
# Process in ascending trade_id order for stability
|
|
625
|
+
try:
|
|
626
|
+
trades_sorted = sorted(trades, key=lambda x: int(x.get("trade_id", 0)))
|
|
627
|
+
except Exception:
|
|
628
|
+
trades_sorted = trades
|
|
629
|
+
|
|
630
|
+
last_tid = base_last_tid
|
|
631
|
+
for t in trades_sorted:
|
|
632
|
+
if not isinstance(t, dict):
|
|
633
|
+
continue
|
|
634
|
+
ts = t.get("timestamp")
|
|
635
|
+
price = t.get("price")
|
|
636
|
+
size = t.get("size")
|
|
637
|
+
trade_id = t.get("trade_id")
|
|
638
|
+
try:
|
|
639
|
+
ts = int(ts)
|
|
640
|
+
p = float(price)
|
|
641
|
+
s = float(size)
|
|
642
|
+
tid = int(trade_id) if trade_id is not None else 0
|
|
643
|
+
except Exception:
|
|
644
|
+
continue
|
|
645
|
+
# Skip stale or duplicate snapshot trades
|
|
646
|
+
if tid and last_tid and tid <= last_tid:
|
|
647
|
+
continue
|
|
648
|
+
self._merge_trade(symbol=symbol, trade_ts_ms=ts, price=p, size=s, last_trade_id=tid)
|
|
649
|
+
if tid > last_tid:
|
|
650
|
+
last_tid = tid
|
|
651
|
+
|
|
652
|
+
# Persist last processed trade_id for this market
|
|
653
|
+
if last_tid and last_tid > base_last_tid:
|
|
654
|
+
self._last_trade_id_by_market[market_id_str] = last_tid
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
|
|
327
659
|
class LighterDataStore(DataStoreCollection):
|
|
328
660
|
"""Data store collection for the Lighter exchange."""
|
|
329
661
|
|
|
@@ -333,12 +665,14 @@ class LighterDataStore(DataStoreCollection):
|
|
|
333
665
|
self._create("orders", datastore_class=Orders)
|
|
334
666
|
self._create("accounts", datastore_class=Accounts)
|
|
335
667
|
self._create("positions", datastore_class=Positions)
|
|
668
|
+
self._create("klines", datastore_class=Klines)
|
|
336
669
|
|
|
337
670
|
def set_id_to_symbol(self, id_to_symbol: dict[str, str]) -> None:
|
|
338
671
|
self.id_to_symbol = id_to_symbol
|
|
339
672
|
self.book.id_to_symbol = self.id_to_symbol
|
|
340
673
|
self.orders.id_to_symbol = self.id_to_symbol
|
|
341
674
|
self.positions.id_to_symbol = self.id_to_symbol
|
|
675
|
+
self.klines.id_to_symbol = self.id_to_symbol
|
|
342
676
|
|
|
343
677
|
def onmessage(self, msg: Item, ws: ClientWebSocketResponse | None = None) -> None:
|
|
344
678
|
|
|
@@ -354,6 +688,11 @@ class LighterDataStore(DataStoreCollection):
|
|
|
354
688
|
elif msg_type in {"subscribed/account_all", "update/account_all"}:
|
|
355
689
|
self.accounts._on_message(msg)
|
|
356
690
|
self.positions._on_message(msg)
|
|
691
|
+
self.orders._on_message(msg)
|
|
692
|
+
elif msg_type in {"subscribed/account_all_orders", "update/account_all_orders"}:
|
|
693
|
+
self.orders._on_message(msg)
|
|
694
|
+
elif msg_type in {"subscribed/trade", "update/trade"}:
|
|
695
|
+
self.klines._on_message(msg)
|
|
357
696
|
|
|
358
697
|
|
|
359
698
|
@property
|
|
@@ -506,3 +845,24 @@ class LighterDataStore(DataStoreCollection):
|
|
|
506
845
|
]
|
|
507
846
|
"""
|
|
508
847
|
return self._get("positions")
|
|
848
|
+
|
|
849
|
+
@property
|
|
850
|
+
def klines(self) -> "Klines":
|
|
851
|
+
"""
|
|
852
|
+
K线/蜡烛图数据(`lighter.models.Candlesticks` -> `lighter.models.Candlestick`)。
|
|
853
|
+
|
|
854
|
+
.. code:: json
|
|
855
|
+
|
|
856
|
+
{
|
|
857
|
+
"symbol": "BTC",
|
|
858
|
+
"timestamp": 1730612700000,
|
|
859
|
+
"open": 68970.5,
|
|
860
|
+
"high": 69012.3,
|
|
861
|
+
"low": 68890.0,
|
|
862
|
+
"close": 68995.1,
|
|
863
|
+
"volume0": 12.34,
|
|
864
|
+
"volume1": 850000.0,
|
|
865
|
+
"resolution": "1m"
|
|
866
|
+
}
|
|
867
|
+
"""
|
|
868
|
+
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.
|
|
3
|
+
Version: 1.25
|
|
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
20
|
Requires-Dist: lighter-sdk
|
|
22
|
-
Requires-Dist: numba>=0.62.1
|
|
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=
|
|
14
|
+
hyperquant/broker/lighter.py,sha256=YuMR6pCWhJjZ2tGMbYanATvbV6t1J22-pyHqO1GNr2g,25639
|
|
16
15
|
hyperquant/broker/ourbit.py,sha256=NUcDSIttf-HGWzoW1uBTrGLPHlkuemMjYCm91MigTno,18228
|
|
17
|
-
hyperquant/broker/ws.py,sha256=
|
|
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=
|
|
27
|
+
hyperquant/broker/models/lighter.py,sha256=I6hjM0of8NLtjSmI6OlbJlvpYDDswLn9yyQLz0I1Mes,30495
|
|
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.
|
|
36
|
-
hyperquant-1.
|
|
37
|
-
hyperquant-1.
|
|
33
|
+
hyperquant-1.25.dist-info/METADATA,sha256=oR0A52JvaP4b2W20fQgZpZaoVGaTjtACFMgyQhNB0W0,4345
|
|
34
|
+
hyperquant-1.25.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
35
|
+
hyperquant-1.25.dist-info/RECORD,,
|