hyperquant 1.21__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.
- hyperquant/broker/auth.py +101 -1
- hyperquant/broker/bitmart.py +170 -80
- hyperquant/broker/lighter.py +134 -2
- hyperquant/broker/models/bitmart.py +14 -25
- hyperquant/broker/models/lighter.py +301 -0
- hyperquant/broker/ws.py +7 -0
- {hyperquant-1.21.dist-info → hyperquant-1.24.dist-info}/METADATA +2 -4
- {hyperquant-1.21.dist-info → hyperquant-1.24.dist-info}/RECORD +9 -11
- hyperquant/broker/coinup.py +0 -591
- hyperquant/broker/models/coinup.py +0 -334
- {hyperquant-1.21.dist-info → hyperquant-1.24.dist-info}/WHEEL +0 -0
|
@@ -98,29 +98,11 @@ class Book(DataStore):
|
|
|
98
98
|
self._last_update = ms_t
|
|
99
99
|
|
|
100
100
|
def _on_message_api(self, msg: dict[str, Any]) -> None:
|
|
101
|
-
# {
|
|
102
|
-
# "data": {
|
|
103
|
-
# "symbol": "BTCUSDT",
|
|
104
|
-
# "asks": [
|
|
105
|
-
# {
|
|
106
|
-
# "price": "70294.4",
|
|
107
|
-
# "vol": "455"
|
|
108
|
-
# }
|
|
109
|
-
# ],
|
|
110
|
-
# "bids": [
|
|
111
|
-
# {
|
|
112
|
-
# "price": "70293.9",
|
|
113
|
-
# "vol": "1856"
|
|
114
|
-
# }
|
|
115
|
-
# ],
|
|
116
|
-
# "ms_t": 1730399750402
|
|
117
|
-
# },
|
|
118
|
-
# "group": "futures/depthAll20:BTCUSDT@200ms"
|
|
119
|
-
# }
|
|
120
101
|
data = msg.get("data")
|
|
121
102
|
if not isinstance(data, dict):
|
|
122
103
|
return
|
|
123
|
-
symbol
|
|
104
|
+
# Some callers embed symbol at top-level; prefer msg["symbol"] when present
|
|
105
|
+
symbol = msg.get("symbol") or data.get("symbol")
|
|
124
106
|
asks = data.get("asks") or []
|
|
125
107
|
bids = data.get("bids") or []
|
|
126
108
|
if self.limit:
|
|
@@ -128,12 +110,19 @@ class Book(DataStore):
|
|
|
128
110
|
bids = bids[: self.limit]
|
|
129
111
|
|
|
130
112
|
self._find_and_delete({'s': symbol})
|
|
113
|
+
# OpenAPI order book arrays are typically [price, size, timestamp]
|
|
114
|
+
def _normalize_level(level: Any) -> tuple[str, str]:
|
|
115
|
+
if isinstance(level, dict):
|
|
116
|
+
return str(level.get("price", "0")), str(level.get("vol", "0"))
|
|
117
|
+
if isinstance(level, (list, tuple)) and len(level) >= 2:
|
|
118
|
+
return str(level[0]), str(level[1])
|
|
119
|
+
return "0", "0"
|
|
120
|
+
|
|
131
121
|
self._update([
|
|
132
122
|
self._make_entry(
|
|
133
123
|
symbol,
|
|
134
124
|
"a",
|
|
135
|
-
entry
|
|
136
|
-
entry.get("vol", '0'),
|
|
125
|
+
*_normalize_level(entry),
|
|
137
126
|
)
|
|
138
127
|
for entry in asks
|
|
139
128
|
])
|
|
@@ -141,11 +130,11 @@ class Book(DataStore):
|
|
|
141
130
|
self._make_entry(
|
|
142
131
|
symbol,
|
|
143
132
|
"b",
|
|
144
|
-
entry
|
|
145
|
-
entry.get("vol", '0'),
|
|
133
|
+
*_normalize_level(entry),
|
|
146
134
|
)
|
|
147
135
|
for entry in bids
|
|
148
136
|
])
|
|
137
|
+
|
|
149
138
|
|
|
150
139
|
def sorted(self, query: Item | None = None, limit: int | None = None) -> dict[str, list[Item]]:
|
|
151
140
|
return self._sorted(
|
|
@@ -325,7 +314,7 @@ class BitmartDataStore(DataStoreCollection):
|
|
|
325
314
|
self._create("positions", datastore_class=Positions)
|
|
326
315
|
self._create("balances", datastore_class=Balances)
|
|
327
316
|
self._create("ticker", datastore_class=Ticker)
|
|
328
|
-
|
|
317
|
+
|
|
329
318
|
def onmessage(self, msg: Item, ws: ClientWebSocketResponse | None = None) -> None:
|
|
330
319
|
if isinstance(msg, dict):
|
|
331
320
|
group = msg.get("group")
|
|
@@ -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.
|
|
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,14 +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
|
|
20
|
+
Requires-Dist: lighter-sdk>=0.1.4
|
|
22
21
|
Requires-Dist: numpy>=1.21.0
|
|
23
22
|
Requires-Dist: pandas>=2.2.3
|
|
24
23
|
Requires-Dist: pybotters>=1.9.1
|
|
25
24
|
Requires-Dist: pyecharts>=2.0.8
|
|
26
|
-
Requires-Dist: rnet==3.0.0rc10
|
|
27
25
|
Description-Content-Type: text/markdown
|
|
28
26
|
|
|
29
27
|
# minquant
|
|
@@ -4,34 +4,32 @@ hyperquant/db.py,sha256=i2TjkCbmH4Uxo7UTDvOYBfy973gLcGexdzuT_YcSeIE,6678
|
|
|
4
4
|
hyperquant/draw.py,sha256=up_lQ3pHeVLoNOyh9vPjgNwjD0M-6_IetSGviQUgjhY,54624
|
|
5
5
|
hyperquant/logkit.py,sha256=nUo7nx5eONvK39GOhWwS41zNRL756P2J7-5xGzwXnTY,8462
|
|
6
6
|
hyperquant/notikit.py,sha256=x5yAZ_tAvLQRXcRbcg-VabCaN45LUhvlTZnUqkIqfAA,3596
|
|
7
|
-
hyperquant/broker/auth.py,sha256=
|
|
7
|
+
hyperquant/broker/auth.py,sha256=3Ws_2iaF2NTCVIbkLzL_0ae3y7f4vgj9zmozvGaUXnk,16029
|
|
8
8
|
hyperquant/broker/bitget.py,sha256=X_S0LKZ7FZAEb6oEMr1vdGP1fondzK74BhmNTpRDSEA,9488
|
|
9
|
-
hyperquant/broker/bitmart.py,sha256=
|
|
10
|
-
hyperquant/broker/coinup.py,sha256=eOr8BTRXiTb5tCU2FDmvBdXXgqiwVmCbP5pdeA1ORJ8,20390
|
|
9
|
+
hyperquant/broker/bitmart.py,sha256=7j_8TU3Dxjj5HCNX7CbSO3nPZcQH1t31A9UOv5tTbg0,25974
|
|
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=mgkdHjXXmOAhVlU8uZJ28fcPqzHM0fnjJXXYMTD4wJs,21918
|
|
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
|
-
hyperquant/broker/models/bitmart.py,sha256=
|
|
24
|
-
hyperquant/broker/models/coinup.py,sha256=X_ngB2_sgTOdfAZqTyeWvCN03j-0_inZ6ugZKW6hR7k,11173
|
|
22
|
+
hyperquant/broker/models/bitmart.py,sha256=O9RnU-XBeR9SzicG15jzuzK5oy2kMrRJAyZSqC8DXUw,21938
|
|
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=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.
|
|
36
|
-
hyperquant-1.
|
|
37
|
-
hyperquant-1.
|
|
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,,
|