hyperquant 1.1__py3-none-any.whl → 1.21__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/bitmart.py
CHANGED
|
@@ -30,6 +30,7 @@ class Bitmart:
|
|
|
30
30
|
self.private_api = "https://derivatives.bitmart.com"
|
|
31
31
|
self.forward_api = f'{self.private_api}/gw-api/contract-tiger/forward'
|
|
32
32
|
self.ws_url = ws_url or "wss://contract-ws-v2.bitmart.com/v1/ifcontract/realTime"
|
|
33
|
+
self.api_ws_url = "wss://openapi-ws-v2.bitmart.com/api?protocol=1.1"
|
|
33
34
|
|
|
34
35
|
self.account_index = account_index
|
|
35
36
|
self.apis = apis
|
|
@@ -281,6 +282,7 @@ class Bitmart:
|
|
|
281
282
|
*,
|
|
282
283
|
depth: str = "Depth",
|
|
283
284
|
depth_limit: int | None = None,
|
|
285
|
+
use_api_ws: bool = True,
|
|
284
286
|
) -> pybotters.ws.WebSocketApp:
|
|
285
287
|
"""Subscribe order book channel(s)."""
|
|
286
288
|
|
|
@@ -289,37 +291,55 @@ class Bitmart:
|
|
|
289
291
|
|
|
290
292
|
if not symbols:
|
|
291
293
|
raise ValueError("symbols must not be empty")
|
|
292
|
-
|
|
293
|
-
missing = [sym for sym in symbols if self.get_contract_id(sym) is None]
|
|
294
|
-
if missing:
|
|
295
|
-
await self.update("detail")
|
|
296
|
-
still_missing = [sym for sym in missing if self.get_contract_id(sym) is None]
|
|
297
|
-
if still_missing:
|
|
298
|
-
raise ValueError(f"Unknown symbols: {', '.join(still_missing)}")
|
|
299
|
-
|
|
300
294
|
if depth_limit is not None:
|
|
301
295
|
self.store.book.limit = depth_limit
|
|
296
|
+
if not use_api_ws:
|
|
297
|
+
missing = [sym for sym in symbols if self.get_contract_id(sym) is None]
|
|
298
|
+
if missing:
|
|
299
|
+
await self.update("detail")
|
|
300
|
+
still_missing = [sym for sym in missing if self.get_contract_id(sym) is None]
|
|
301
|
+
if still_missing:
|
|
302
|
+
raise ValueError(f"Unknown symbols: {', '.join(still_missing)}")
|
|
302
303
|
|
|
303
|
-
channels: list[str] = []
|
|
304
|
-
for symbol in symbols:
|
|
305
|
-
contract_id = self.get_contract_id(symbol)
|
|
306
|
-
if contract_id is None:
|
|
307
|
-
continue
|
|
308
|
-
self.store.book.id_to_symbol[str(contract_id)] = symbol
|
|
309
|
-
channels.append(f"{depth}:{contract_id}")
|
|
310
304
|
|
|
311
|
-
if not channels:
|
|
312
|
-
raise ValueError("No channels resolved for subscription")
|
|
313
305
|
|
|
314
|
-
|
|
315
|
-
|
|
306
|
+
channels: list[str] = []
|
|
307
|
+
for symbol in symbols:
|
|
308
|
+
contract_id = self.get_contract_id(symbol)
|
|
309
|
+
if contract_id is None:
|
|
310
|
+
continue
|
|
311
|
+
self.store.book.id_to_symbol[str(contract_id)] = symbol
|
|
312
|
+
channels.append(f"{depth}:{contract_id}")
|
|
313
|
+
|
|
314
|
+
if not channels:
|
|
315
|
+
raise ValueError("No channels resolved for subscription")
|
|
316
|
+
|
|
317
|
+
payload = {"action": "subscribe", "args": channels}
|
|
318
|
+
# print(payload)
|
|
319
|
+
|
|
320
|
+
ws_app = self.client.ws_connect(
|
|
321
|
+
self.api_ws_url,
|
|
322
|
+
send_json=payload,
|
|
323
|
+
hdlr_json=self.store.onmessage,
|
|
324
|
+
)
|
|
325
|
+
else:
|
|
326
|
+
channels: list[str] = []
|
|
327
|
+
for symbol in symbols:
|
|
328
|
+
channels.append(f"futures/depthAll5:{symbol}@100ms")
|
|
329
|
+
|
|
330
|
+
if not channels:
|
|
331
|
+
raise ValueError("No channels resolved for subscription")
|
|
332
|
+
|
|
333
|
+
payload = {"action": "subscribe", "args": channels}
|
|
334
|
+
# print(payload)
|
|
335
|
+
|
|
336
|
+
ws_app = self.client.ws_connect(
|
|
337
|
+
self.api_ws_url,
|
|
338
|
+
send_json=payload,
|
|
339
|
+
hdlr_json=self.store.onmessage,
|
|
340
|
+
autoping=False,
|
|
341
|
+
)
|
|
316
342
|
|
|
317
|
-
ws_app = self.client.ws_connect(
|
|
318
|
-
self.ws_url,
|
|
319
|
-
send_json=payload,
|
|
320
|
-
hdlr_json=self.store.onmessage,
|
|
321
|
-
autoping=False,
|
|
322
|
-
)
|
|
323
343
|
await ws_app._event.wait()
|
|
324
344
|
return ws_app
|
|
325
345
|
|
|
@@ -510,3 +530,101 @@ class Bitmart:
|
|
|
510
530
|
if resp.get("success") is False or resp.get("errno") not in (None, "OK"):
|
|
511
531
|
raise ValueError(f"Bitmart cancelOrders error: {resp}")
|
|
512
532
|
return resp
|
|
533
|
+
|
|
534
|
+
async def get_leverage(
|
|
535
|
+
self,
|
|
536
|
+
*,
|
|
537
|
+
symbol: str | None = None,
|
|
538
|
+
contract_id: int | str | None = None,
|
|
539
|
+
) -> dict[str, Any]:
|
|
540
|
+
"""
|
|
541
|
+
获取指定合约的杠杆信息(可通过 contract_id 或 symbol 查询)。
|
|
542
|
+
|
|
543
|
+
参数:
|
|
544
|
+
symbol (str | None): 合约符号,例如 "BTCUSDT"。如果未传入 contract_id,则会自动解析。
|
|
545
|
+
contract_id (int | str | None): 合约 ID,可直接指定。
|
|
546
|
+
|
|
547
|
+
返回:
|
|
548
|
+
dict[str, Any]: 杠杆信息字典,典型返回结构如下:
|
|
549
|
+
{
|
|
550
|
+
"contract_id": 1,
|
|
551
|
+
"leverage": 96, # 当前杠杆倍数
|
|
552
|
+
"open_type": 2, # 开仓类型 (1=全仓, 2=逐仓)
|
|
553
|
+
"max_leverage": {
|
|
554
|
+
"contract_id": 1,
|
|
555
|
+
"leverage": "200", # 最大可用杠杆倍数
|
|
556
|
+
"open_type": 0,
|
|
557
|
+
"imr": "0.005", # 初始保证金率
|
|
558
|
+
"mmr": "0.0025", # 维持保证金率
|
|
559
|
+
"value": "0"
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
异常:
|
|
564
|
+
ValueError: 当未提供 symbol 或 contract_id,或接口返回错误时抛出。
|
|
565
|
+
|
|
566
|
+
示例:
|
|
567
|
+
data = await bitmart.get_leverage(symbol="BTCUSDT")
|
|
568
|
+
print(data["leverage"]) # 输出当前杠杆倍数
|
|
569
|
+
"""
|
|
570
|
+
if contract_id is None:
|
|
571
|
+
if symbol is not None:
|
|
572
|
+
contract_id = self.get_contract_id(symbol)
|
|
573
|
+
if contract_id is None:
|
|
574
|
+
raise ValueError("Either contract_id or a valid symbol must be provided to get leverage info.")
|
|
575
|
+
res = await self.client.get(
|
|
576
|
+
f"{self.forward_api}/v1/ifcontract/getLeverage",
|
|
577
|
+
params={"contract_id": contract_id},
|
|
578
|
+
)
|
|
579
|
+
resp = await res.json()
|
|
580
|
+
if resp.get("success") is False or resp.get("errno") not in (None, "OK"):
|
|
581
|
+
raise ValueError(f"Bitmart getLeverage error: {resp}")
|
|
582
|
+
return resp.get("data")
|
|
583
|
+
|
|
584
|
+
async def bind_leverage(
|
|
585
|
+
self,
|
|
586
|
+
*,
|
|
587
|
+
symbol: str | None = None,
|
|
588
|
+
contract_id: int | str | None = None,
|
|
589
|
+
leverage: int | str,
|
|
590
|
+
open_type: Literal[1, 2] = 2,
|
|
591
|
+
) -> None:
|
|
592
|
+
"""
|
|
593
|
+
绑定(设置)指定合约的杠杆倍数。
|
|
594
|
+
|
|
595
|
+
参数:
|
|
596
|
+
symbol (str | None): 合约符号,例如 "BTCUSDT"。若未传入 contract_id,会自动解析。
|
|
597
|
+
contract_id (int | str | None): 合约 ID,可直接指定。
|
|
598
|
+
leverage (int | str): 要设置的杠杆倍数,如 20、50、100。
|
|
599
|
+
open_type (int): 开仓模式,1=全仓(Cross),2=逐仓(Isolated)。
|
|
600
|
+
|
|
601
|
+
返回:
|
|
602
|
+
None — 如果接口调用成功,不返回任何内容。
|
|
603
|
+
若失败则抛出 ValueError。
|
|
604
|
+
|
|
605
|
+
异常:
|
|
606
|
+
ValueError: 当未提供 symbol 或 contract_id,或接口返回错误时抛出。
|
|
607
|
+
|
|
608
|
+
示例:
|
|
609
|
+
await bitmart.bind_leverage(symbol="BTCUSDT", leverage=50, open_type=2)
|
|
610
|
+
"""
|
|
611
|
+
if contract_id is None:
|
|
612
|
+
if symbol is not None:
|
|
613
|
+
contract_id = self.get_contract_id(symbol)
|
|
614
|
+
if contract_id is None:
|
|
615
|
+
raise ValueError("Either contract_id or a valid symbol must be provided to bind leverage.")
|
|
616
|
+
|
|
617
|
+
payload = {
|
|
618
|
+
"contract_id": int(contract_id),
|
|
619
|
+
"leverage": leverage,
|
|
620
|
+
"open_type": open_type,
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
res = await self.client.post(
|
|
624
|
+
f"{self.forward_api}/v1/ifcontract/bindLeverage",
|
|
625
|
+
json=payload,
|
|
626
|
+
)
|
|
627
|
+
resp = await res.json()
|
|
628
|
+
if resp.get("success") is False or resp.get("errno") not in (None, "OK"):
|
|
629
|
+
raise ValueError(f"Bitmart bindLeverage error: {resp}")
|
|
630
|
+
return None
|
|
@@ -62,6 +62,7 @@ class Book(DataStore):
|
|
|
62
62
|
def _on_message(self, msg: dict[str, Any]) -> None:
|
|
63
63
|
data = msg.get("data")
|
|
64
64
|
group = msg.get("group")
|
|
65
|
+
ms_t = msg.get("ms_t")
|
|
65
66
|
if not isinstance(data, dict) or not isinstance(group, str):
|
|
66
67
|
return
|
|
67
68
|
|
|
@@ -94,7 +95,57 @@ class Book(DataStore):
|
|
|
94
95
|
for entry in depths
|
|
95
96
|
])
|
|
96
97
|
|
|
97
|
-
self._last_update =
|
|
98
|
+
self._last_update = ms_t
|
|
99
|
+
|
|
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
|
+
data = msg.get("data")
|
|
121
|
+
if not isinstance(data, dict):
|
|
122
|
+
return
|
|
123
|
+
symbol = data.get("symbol")
|
|
124
|
+
asks = data.get("asks") or []
|
|
125
|
+
bids = data.get("bids") or []
|
|
126
|
+
if self.limit:
|
|
127
|
+
asks = asks[: self.limit]
|
|
128
|
+
bids = bids[: self.limit]
|
|
129
|
+
|
|
130
|
+
self._find_and_delete({'s': symbol})
|
|
131
|
+
self._update([
|
|
132
|
+
self._make_entry(
|
|
133
|
+
symbol,
|
|
134
|
+
"a",
|
|
135
|
+
entry.get("price", '0'),
|
|
136
|
+
entry.get("vol", '0'),
|
|
137
|
+
)
|
|
138
|
+
for entry in asks
|
|
139
|
+
])
|
|
140
|
+
self._update([
|
|
141
|
+
self._make_entry(
|
|
142
|
+
symbol,
|
|
143
|
+
"b",
|
|
144
|
+
entry.get("price", '0'),
|
|
145
|
+
entry.get("vol", '0'),
|
|
146
|
+
)
|
|
147
|
+
for entry in bids
|
|
148
|
+
])
|
|
98
149
|
|
|
99
150
|
def sorted(self, query: Item | None = None, limit: int | None = None) -> dict[str, list[Item]]:
|
|
100
151
|
return self._sorted(
|
|
@@ -278,8 +329,12 @@ class BitmartDataStore(DataStoreCollection):
|
|
|
278
329
|
def onmessage(self, msg: Item, ws: ClientWebSocketResponse | None = None) -> None:
|
|
279
330
|
if isinstance(msg, dict):
|
|
280
331
|
group = msg.get("group")
|
|
281
|
-
|
|
282
|
-
|
|
332
|
+
|
|
333
|
+
if isinstance(group, str):
|
|
334
|
+
if group.startswith("futures/depth"):
|
|
335
|
+
self.book._on_message_api(msg)
|
|
336
|
+
if group.startswith("Depth"):
|
|
337
|
+
self.book._on_message(msg)
|
|
283
338
|
|
|
284
339
|
@property
|
|
285
340
|
def book(self) -> Book:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hyperquant
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.21
|
|
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
|
|
@@ -6,7 +6,7 @@ hyperquant/logkit.py,sha256=nUo7nx5eONvK39GOhWwS41zNRL756P2J7-5xGzwXnTY,8462
|
|
|
6
6
|
hyperquant/notikit.py,sha256=x5yAZ_tAvLQRXcRbcg-VabCaN45LUhvlTZnUqkIqfAA,3596
|
|
7
7
|
hyperquant/broker/auth.py,sha256=C8B5-x8Qcaeafm4ZwPCVFR7GRURmHC3CE4_vdg00Qgw,12139
|
|
8
8
|
hyperquant/broker/bitget.py,sha256=X_S0LKZ7FZAEb6oEMr1vdGP1fondzK74BhmNTpRDSEA,9488
|
|
9
|
-
hyperquant/broker/bitmart.py,sha256=
|
|
9
|
+
hyperquant/broker/bitmart.py,sha256=2Yd_Jzn0XYzl3rYN1rmNuwy7Wy74T3bBQ-ue7RvIbMU,22206
|
|
10
10
|
hyperquant/broker/coinup.py,sha256=eOr8BTRXiTb5tCU2FDmvBdXXgqiwVmCbP5pdeA1ORJ8,20390
|
|
11
11
|
hyperquant/broker/coinw.py,sha256=SnJU0vASh77rfcpMGWaIfTblQSjQk3vjlW_4juYdbcs,17214
|
|
12
12
|
hyperquant/broker/edgex.py,sha256=TqUO2KRPLN_UaxvtLL6HnA9dAQXC1sGxOfqTHd6W5k8,18378
|
|
@@ -20,7 +20,7 @@ hyperquant/broker/lib/hpstore.py,sha256=LnLK2zmnwVvhEbLzYI-jz_SfYpO1Dv2u2cJaRAb8
|
|
|
20
20
|
hyperquant/broker/lib/hyper_types.py,sha256=HqjjzjUekldjEeVn6hxiWA8nevAViC2xHADOzDz9qyw,991
|
|
21
21
|
hyperquant/broker/lib/util.py,sha256=iMU1qF0CHj5zzlIMEQGwjz-qtEVosEe7slXOCuB7Rcw,566
|
|
22
22
|
hyperquant/broker/models/bitget.py,sha256=0RwDY75KrJb-c-oYoMxbqxWfsILe-n_Npojz4UFUq7c,11389
|
|
23
|
-
hyperquant/broker/models/bitmart.py,sha256=
|
|
23
|
+
hyperquant/broker/models/bitmart.py,sha256=hPxSFLmsJif9wm4nTln5G_zCbsoRnM1BF9fnfciZIHo,22061
|
|
24
24
|
hyperquant/broker/models/coinup.py,sha256=X_ngB2_sgTOdfAZqTyeWvCN03j-0_inZ6ugZKW6hR7k,11173
|
|
25
25
|
hyperquant/broker/models/coinw.py,sha256=LvLMVP7i-qkkTK1ubw8eBkMK2RQmFoKPxdKqmC4IToY,22157
|
|
26
26
|
hyperquant/broker/models/edgex.py,sha256=vPAkceal44cjTYKQ_0BoNAskOpmkno_Yo1KxgMLPc6Y,33954
|
|
@@ -32,6 +32,6 @@ hyperquant/datavison/_util.py,sha256=92qk4vO856RqycO0YqEIHJlEg-W9XKapDVqAMxe6rbw
|
|
|
32
32
|
hyperquant/datavison/binance.py,sha256=3yNKTqvt_vUQcxzeX4ocMsI5k6Q6gLZrvgXxAEad6Kc,5001
|
|
33
33
|
hyperquant/datavison/coinglass.py,sha256=PEjdjISP9QUKD_xzXNzhJ9WFDTlkBrRQlVL-5pxD5mo,10482
|
|
34
34
|
hyperquant/datavison/okx.py,sha256=yg8WrdQ7wgWHNAInIgsWPM47N3Wkfr253169IPAycAY,6898
|
|
35
|
-
hyperquant-1.
|
|
36
|
-
hyperquant-1.
|
|
37
|
-
hyperquant-1.
|
|
35
|
+
hyperquant-1.21.dist-info/METADATA,sha256=1ZJFW9uGSQFzXQTBy74LLbaay4lUmc_pTOzSOugj_nU,4409
|
|
36
|
+
hyperquant-1.21.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
37
|
+
hyperquant-1.21.dist-info/RECORD,,
|
|
File without changes
|