hyperquant 0.7__py3-none-any.whl → 0.9__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 +74 -1
- hyperquant/broker/bitget.py +245 -35
- hyperquant/broker/coinup.py +556 -0
- hyperquant/broker/coinw.py +487 -0
- hyperquant/broker/lbank.py +312 -5
- hyperquant/broker/models/bitget.py +84 -8
- hyperquant/broker/models/coinup.py +290 -0
- hyperquant/broker/models/coinw.py +724 -0
- hyperquant/broker/models/lbank.py +16 -6
- hyperquant/broker/ws.py +53 -4
- {hyperquant-0.7.dist-info → hyperquant-0.9.dist-info}/METADATA +4 -2
- {hyperquant-0.7.dist-info → hyperquant-0.9.dist-info}/RECORD +13 -9
- {hyperquant-0.7.dist-info → hyperquant-0.9.dist-info}/WHEEL +0 -0
hyperquant/broker/lbank.py
CHANGED
|
@@ -136,6 +136,162 @@ class Lbank:
|
|
|
136
136
|
|
|
137
137
|
await self.store.initialize(*requests)
|
|
138
138
|
|
|
139
|
+
async def query_trade(
|
|
140
|
+
self,
|
|
141
|
+
order_id: str | None = None,
|
|
142
|
+
*,
|
|
143
|
+
product_group: str = "SwapU",
|
|
144
|
+
page_index: int = 1,
|
|
145
|
+
page_size: int = 20,
|
|
146
|
+
) -> list[dict[str, Any]]:
|
|
147
|
+
"""Fetch trade executions linked to a given OrderSysID.
|
|
148
|
+
|
|
149
|
+
Example response payload::
|
|
150
|
+
|
|
151
|
+
[
|
|
152
|
+
{
|
|
153
|
+
"TradeUnitID": "e1b03fb1-6849-464f-a",
|
|
154
|
+
"ProductGroup": "SwapU",
|
|
155
|
+
"CloseProfit": 0,
|
|
156
|
+
"BusinessNo": 1001770339345505,
|
|
157
|
+
"TradeID": "1000162046503720",
|
|
158
|
+
"PositionID": "1000632926272299",
|
|
159
|
+
"DeriveSource": "0",
|
|
160
|
+
"OrderID": "",
|
|
161
|
+
"Direction": "0",
|
|
162
|
+
"InstrumentID": "SOLUSDT",
|
|
163
|
+
"OffsetFlag": "0",
|
|
164
|
+
"Remark": "def",
|
|
165
|
+
"DdlnTime": "0",
|
|
166
|
+
"UseMargin": 0.054213,
|
|
167
|
+
"Currency": "USDT",
|
|
168
|
+
"Turnover": 5.4213,
|
|
169
|
+
"SettlementGroup": "SwapU",
|
|
170
|
+
"Leverage": 100,
|
|
171
|
+
"OrderSysID": "1000632948114584",
|
|
172
|
+
"ExchangeID": "Exchange",
|
|
173
|
+
"AccountID": "e1b03fb1-6849-464f-a986-94b9a6e625e6",
|
|
174
|
+
"TradeTime": 1760161085,
|
|
175
|
+
"Fee": 0.00325278,
|
|
176
|
+
"OrderPrice": 180.89,
|
|
177
|
+
"InsertTime": 1760161085,
|
|
178
|
+
"MemberID": "e1b03fb1-6849-464f-a986-94b9a6e625e6",
|
|
179
|
+
"MatchRole": "1",
|
|
180
|
+
"ClearCurrency": "USDT",
|
|
181
|
+
"Price": 180.71,
|
|
182
|
+
"Volume": 0.03,
|
|
183
|
+
"OpenPrice": 182.94,
|
|
184
|
+
"MasterAccountID": "",
|
|
185
|
+
"PriceCurrency": "USDT",
|
|
186
|
+
"FeeCurrency": "USDT"
|
|
187
|
+
}
|
|
188
|
+
]
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
if not order_id:
|
|
192
|
+
raise ValueError("order_id is required to query order executions")
|
|
193
|
+
|
|
194
|
+
params = {
|
|
195
|
+
"ProductGroup": product_group,
|
|
196
|
+
"OrderSysID": order_id,
|
|
197
|
+
"pageIndex": page_index,
|
|
198
|
+
"pageSize": page_size,
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
res = await self.client.get(
|
|
202
|
+
f"{self.front_api}/cfd/query/v1.0/Trade",
|
|
203
|
+
params=params,
|
|
204
|
+
headers=self._rest_headers,
|
|
205
|
+
)
|
|
206
|
+
data = await res.json()
|
|
207
|
+
payload = self._ensure_ok("query_trade", data)
|
|
208
|
+
|
|
209
|
+
if isinstance(payload, dict):
|
|
210
|
+
rows = payload.get("data")
|
|
211
|
+
if isinstance(rows, list):
|
|
212
|
+
return rows
|
|
213
|
+
elif isinstance(payload, list): # pragma: no cover - defensive fallback
|
|
214
|
+
return payload
|
|
215
|
+
|
|
216
|
+
return []
|
|
217
|
+
|
|
218
|
+
async def query_order(
|
|
219
|
+
self,
|
|
220
|
+
order_id: str | None = None,
|
|
221
|
+
*,
|
|
222
|
+
product_group: str = "SwapU",
|
|
223
|
+
page_index: int = 1,
|
|
224
|
+
page_size: int = 20,
|
|
225
|
+
) -> dict[str, Any]:
|
|
226
|
+
"""
|
|
227
|
+
返回值示例:
|
|
228
|
+
|
|
229
|
+
.. code:: json
|
|
230
|
+
|
|
231
|
+
{
|
|
232
|
+
"order_id": "1000632478428573",
|
|
233
|
+
"instrument_id": "SOLUSDT",
|
|
234
|
+
"position_id": "1000632478428573",
|
|
235
|
+
"direction": "0",
|
|
236
|
+
"offset_flag": "0",
|
|
237
|
+
"trade_time": 1760123456,
|
|
238
|
+
"avg_price": 182.5,
|
|
239
|
+
"volume": 0.03,
|
|
240
|
+
"turnover": 5.475,
|
|
241
|
+
"fee": 0.003285,
|
|
242
|
+
"trade_count": 1
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
如果没有订单成交返回
|
|
246
|
+
{
|
|
247
|
+
"order_id": "1000632478428573",
|
|
248
|
+
"trade_count": 0
|
|
249
|
+
}
|
|
250
|
+
"""
|
|
251
|
+
|
|
252
|
+
if not order_id:
|
|
253
|
+
raise ValueError("order_id is required to query order statistics")
|
|
254
|
+
|
|
255
|
+
trades = await self.query_trade(
|
|
256
|
+
order_id,
|
|
257
|
+
product_group=product_group,
|
|
258
|
+
page_index=page_index,
|
|
259
|
+
page_size=page_size,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
if not trades:
|
|
263
|
+
return {
|
|
264
|
+
"order_id": order_id,
|
|
265
|
+
"trade_count": 0,
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
def _to_float(value: Any) -> float:
|
|
269
|
+
try:
|
|
270
|
+
return float(value)
|
|
271
|
+
except (TypeError, ValueError):
|
|
272
|
+
return 0.0
|
|
273
|
+
|
|
274
|
+
total_volume = sum(_to_float(trade.get("Volume")) for trade in trades)
|
|
275
|
+
total_turnover = sum(_to_float(trade.get("Turnover")) for trade in trades)
|
|
276
|
+
total_fee = sum(_to_float(trade.get("Fee")) for trade in trades)
|
|
277
|
+
|
|
278
|
+
avg_price = total_turnover / total_volume if total_volume else None
|
|
279
|
+
last_trade = trades[-1]
|
|
280
|
+
|
|
281
|
+
return {
|
|
282
|
+
"order_id": order_id,
|
|
283
|
+
"instrument_id": last_trade.get("InstrumentID"),
|
|
284
|
+
"position_id": last_trade.get("PositionID"),
|
|
285
|
+
"direction": last_trade.get("Direction"),
|
|
286
|
+
"offset_flag": last_trade.get("OffsetFlag"),
|
|
287
|
+
"trade_time": last_trade.get("TradeTime"),
|
|
288
|
+
"avg_price": avg_price,
|
|
289
|
+
"volume": total_volume,
|
|
290
|
+
"turnover": total_turnover,
|
|
291
|
+
"fee": total_fee,
|
|
292
|
+
"trade_count": len(trades),
|
|
293
|
+
}
|
|
294
|
+
|
|
139
295
|
def _resolve_instrument(self) -> str | None:
|
|
140
296
|
detail_entries = self.store.detail.find()
|
|
141
297
|
if detail_entries:
|
|
@@ -212,7 +368,71 @@ class Lbank:
|
|
|
212
368
|
order_proportion: str = "0.0000",
|
|
213
369
|
client_order_id: str | None = None,
|
|
214
370
|
) -> dict[str, Any]:
|
|
215
|
-
"""Create an order using documented REST parameters.
|
|
371
|
+
"""Create an order using documented REST parameters.
|
|
372
|
+
|
|
373
|
+
返回示例:
|
|
374
|
+
|
|
375
|
+
.. code:: json
|
|
376
|
+
|
|
377
|
+
{
|
|
378
|
+
"offsetFlag": "5",
|
|
379
|
+
"orderType": "1",
|
|
380
|
+
"reserveMode": "0",
|
|
381
|
+
"fee": "0.0066042",
|
|
382
|
+
"frozenFee": "0",
|
|
383
|
+
"ddlnTime": "0",
|
|
384
|
+
"userID": "lbank_exchange_user",
|
|
385
|
+
"masterAccountID": "",
|
|
386
|
+
"exchangeID": "Exchange",
|
|
387
|
+
"accountID": "e1b03fb1-6849-464f-a986-94b9a6e625e6",
|
|
388
|
+
"orderSysID": "1000633129818889",
|
|
389
|
+
"volumeRemain": "0",
|
|
390
|
+
"price": "183.36",
|
|
391
|
+
"businessValue": "1760183423813",
|
|
392
|
+
"frozenMargin": "0",
|
|
393
|
+
"instrumentID": "SOLUSDT",
|
|
394
|
+
"posiDirection": "2",
|
|
395
|
+
"volumeMode": "1",
|
|
396
|
+
"volume": "0.06",
|
|
397
|
+
"insertTime": "1760183423",
|
|
398
|
+
"copyMemberID": "",
|
|
399
|
+
"position": "0.06",
|
|
400
|
+
"tradePrice": "183.45",
|
|
401
|
+
"leverage": "100",
|
|
402
|
+
"businessResult": "",
|
|
403
|
+
"availableUse": "0",
|
|
404
|
+
"orderStatus": "1",
|
|
405
|
+
"openPrice": "182.94",
|
|
406
|
+
"frozenMoney": "0",
|
|
407
|
+
"remark": "def",
|
|
408
|
+
"reserveUse": "0",
|
|
409
|
+
"sessionNo": "41",
|
|
410
|
+
"isCrossMargin": "1",
|
|
411
|
+
"closeProfit": "0.0306",
|
|
412
|
+
"businessNo": "1001770756852986", # 订单有成交会并入仓位 businessNo
|
|
413
|
+
"relatedOrderSysID": "",
|
|
414
|
+
"positionID": "1000632926272299",
|
|
415
|
+
"mockResp": false,
|
|
416
|
+
"deriveSource": "0",
|
|
417
|
+
"copyOrderID": "",
|
|
418
|
+
"currency": "USDT",
|
|
419
|
+
"turnover": "11.007",
|
|
420
|
+
"frontNo": "-68",
|
|
421
|
+
"direction": "1",
|
|
422
|
+
"orderPriceType": "4",
|
|
423
|
+
"volumeCancled": "0",
|
|
424
|
+
"updateTime": "1760183423",
|
|
425
|
+
"localID": "1000633129818889",
|
|
426
|
+
"volumeTraded": "0.06",
|
|
427
|
+
"appid": "WEB",
|
|
428
|
+
"tradeUnitID": "e1b03fb1-6849-464f-a",
|
|
429
|
+
"businessType": "P",
|
|
430
|
+
"memberID": "e1b03fb1-6849-464f-a986-94b9a6e625e6",
|
|
431
|
+
"timeCondition": "0",
|
|
432
|
+
"copyProfit": "0"
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
"""
|
|
216
436
|
|
|
217
437
|
direction_code = self._normalize_direction(direction)
|
|
218
438
|
offset_code = self._normalize_offset(offset_flag)
|
|
@@ -244,9 +464,7 @@ class Lbank:
|
|
|
244
464
|
# logger.warning("Price is ignored for market orders")
|
|
245
465
|
pass
|
|
246
466
|
|
|
247
|
-
|
|
248
|
-
# payload["LocalID"] = client_order_id
|
|
249
|
-
print(payload)
|
|
467
|
+
|
|
250
468
|
res = await self.client.post(
|
|
251
469
|
f"{self.front_api}/cfd/cff/v1/SendOrderInsert",
|
|
252
470
|
json=payload,
|
|
@@ -277,6 +495,95 @@ class Lbank:
|
|
|
277
495
|
if not isinstance(data, dict) or data.get("code") != 200:
|
|
278
496
|
raise RuntimeError(f"{operation} failed: {data}")
|
|
279
497
|
return data.get("data") or {}
|
|
498
|
+
|
|
499
|
+
# https://uuapi.rerrkvifj.com/cfd/agg/v1/sendQryAll
|
|
500
|
+
# {
|
|
501
|
+
# "productGroup": "SwapU",
|
|
502
|
+
# "instrumentID": "BTCUSDT",
|
|
503
|
+
# "asset": "USDT"
|
|
504
|
+
# }
|
|
505
|
+
|
|
506
|
+
async def query_all(self, symbol:str):
|
|
507
|
+
"""查询资产信息
|
|
508
|
+
|
|
509
|
+
.. code:: json
|
|
510
|
+
|
|
511
|
+
{
|
|
512
|
+
"fundingRateTimestamp": 28800,
|
|
513
|
+
"isMarketAcount": 0,
|
|
514
|
+
"longMaxVolume": 10000000000000000,
|
|
515
|
+
"role": 2,
|
|
516
|
+
"openingTime": 1609545600000,
|
|
517
|
+
"isCrossMargin": 1,
|
|
518
|
+
"longLeverage": 25,
|
|
519
|
+
"shortLastVolume": 10000000000000000,
|
|
520
|
+
"longLastVolume": 10000000000000000,
|
|
521
|
+
"onTime": 1609459200000,
|
|
522
|
+
"shortMaintenanceMarginRate": "0.0025",
|
|
523
|
+
"state": 3,
|
|
524
|
+
"markedPrice": "111031.3",
|
|
525
|
+
"assetBalance": {
|
|
526
|
+
"reserveAvailable": "0.0",
|
|
527
|
+
"balance": "22.79163408",
|
|
528
|
+
"frozenMargin": "0.0",
|
|
529
|
+
"reserveMode": "0",
|
|
530
|
+
"totalCloseProfit": "-15.982736",
|
|
531
|
+
"available": "22.79163408",
|
|
532
|
+
"crossMargin": "0.0",
|
|
533
|
+
"reserve": "0.0",
|
|
534
|
+
"frozenFee": "0.0",
|
|
535
|
+
"marginAble": "0.0",
|
|
536
|
+
"realAvailable": "22.79163408"
|
|
537
|
+
},
|
|
538
|
+
"longMaxLeverage": 200,
|
|
539
|
+
"shortMaintenanceMarginQuickAmount": "0",
|
|
540
|
+
"shortLastAmount": "10798590",
|
|
541
|
+
"unrealProfitCalType": "2",
|
|
542
|
+
"longLastAmount": "10798590",
|
|
543
|
+
"shortMaxVolume": 10000000000000000,
|
|
544
|
+
"shortLeverage": 25,
|
|
545
|
+
"calMarkedPrice": "111031.3",
|
|
546
|
+
"longMaintenanceMarginRate": "0.0025",
|
|
547
|
+
"wsToken": "fa1d5e0ad94ede6efab6ced66ea5367cfe68c81173863424dc6e8d846d7e723b",
|
|
548
|
+
"shortMaxLeverage": 200,
|
|
549
|
+
"nextFundingRateTimestamp": 1760976000000,
|
|
550
|
+
"longMaintenanceMarginQuickAmount": "0",
|
|
551
|
+
"forbidTrade": false,
|
|
552
|
+
"defaultPositionType": "2",
|
|
553
|
+
"lastPrice": "111027.9",
|
|
554
|
+
"fundingRate": "0.00003598"
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
"""
|
|
558
|
+
|
|
559
|
+
payload = {
|
|
560
|
+
"productGroup": "SwapU",
|
|
561
|
+
"instrumentID": symbol,
|
|
562
|
+
"asset": "USDT"
|
|
563
|
+
}
|
|
564
|
+
res = await self.client.post(
|
|
565
|
+
f"{self.front_api}/cfd/agg/v1/sendQryAll",
|
|
566
|
+
json=payload,
|
|
567
|
+
headers=self._rest_headers,
|
|
568
|
+
)
|
|
569
|
+
data = await res.json()
|
|
570
|
+
return self._ensure_ok("query_all", data)
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
async def set_position_mode(self, mode: Literal["hedge", "oneway"] = "oneway") -> dict[str, Any]:
|
|
574
|
+
"""设置持仓模式到单向持仓或对冲持仓"""
|
|
575
|
+
|
|
576
|
+
mode_code = "2" if mode == "oneway" else "1"
|
|
577
|
+
payload = {
|
|
578
|
+
"PositionType": mode_code,
|
|
579
|
+
}
|
|
580
|
+
res = await self.client.post(
|
|
581
|
+
f"{self.front_api}/cfd/action/v1.0/SendMemberAction",
|
|
582
|
+
json=payload,
|
|
583
|
+
headers=self._rest_headers,
|
|
584
|
+
)
|
|
585
|
+
data = await res.json()
|
|
586
|
+
return self._ensure_ok("set_position_mode", data)
|
|
280
587
|
|
|
281
588
|
@staticmethod
|
|
282
589
|
def _normalize_direction(direction: str) -> str:
|
|
@@ -351,4 +658,4 @@ class Lbank:
|
|
|
351
658
|
batch = send_jsons[i:i+5]
|
|
352
659
|
await asyncio.gather(*(sub(send_json) for send_json in batch))
|
|
353
660
|
if i + 5 < len(send_jsons):
|
|
354
|
-
await asyncio.sleep(0.
|
|
661
|
+
await asyncio.sleep(0.3)
|
|
@@ -16,13 +16,18 @@ class Detail(DataStore):
|
|
|
16
16
|
_KEYS = ["symbol"]
|
|
17
17
|
|
|
18
18
|
def _transform(self, entry: dict[str, Any]) -> dict[str, Any] | None:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
step_size = place_to_step(
|
|
23
|
-
tick_size = place_to_step(
|
|
24
|
-
|
|
25
|
-
entry[
|
|
19
|
+
step_place = entry.get("volume_place", 1)
|
|
20
|
+
tick_place = entry.get("price_place", 1)
|
|
21
|
+
|
|
22
|
+
step_size = place_to_step(step_place)
|
|
23
|
+
tick_size = place_to_step(tick_place)
|
|
24
|
+
|
|
25
|
+
entry["stepSize"] = step_size
|
|
26
|
+
entry["tickSize"] = tick_size
|
|
27
|
+
# expose snake_case aliases for downstream callers keeping legacy naming
|
|
28
|
+
entry["step_size"] = step_size
|
|
29
|
+
entry["tick_size"] = tick_size
|
|
30
|
+
|
|
26
31
|
return entry
|
|
27
32
|
|
|
28
33
|
def _onresponse(self, data: list[dict[str, Any]] | dict[str, Any] | None) -> None:
|
|
@@ -280,4 +285,75 @@ class BitgetDataStore(BitgetV2DataStore):
|
|
|
280
285
|
}
|
|
281
286
|
]
|
|
282
287
|
"""
|
|
283
|
-
return self._get("book")
|
|
288
|
+
return self._get("book")
|
|
289
|
+
|
|
290
|
+
@property
|
|
291
|
+
def account(self) -> DataStore:
|
|
292
|
+
"""
|
|
293
|
+
_KEYS = ["instType", "marginCoin"]
|
|
294
|
+
|
|
295
|
+
Data Structure:
|
|
296
|
+
|
|
297
|
+
.. code:: json
|
|
298
|
+
|
|
299
|
+
[
|
|
300
|
+
{
|
|
301
|
+
"marginCoin": "USDT",
|
|
302
|
+
"frozen": "0.00000000",
|
|
303
|
+
"available": "13.98545761",
|
|
304
|
+
"maxOpenPosAvailable": "13.98545761",
|
|
305
|
+
"maxTransferOut": "13.98545761",
|
|
306
|
+
"equity": "13.98545761",
|
|
307
|
+
"usdtEquity": "13.985457617660",
|
|
308
|
+
"crossedRiskRate": "0",
|
|
309
|
+
"unrealizedPL": "0.000000000000",
|
|
310
|
+
"unionTotalMargin": "100",
|
|
311
|
+
"unionAvailable": "20",
|
|
312
|
+
"unionMm": "15",
|
|
313
|
+
"assetMode": "union"
|
|
314
|
+
}
|
|
315
|
+
]
|
|
316
|
+
"""
|
|
317
|
+
return self._get("account")
|
|
318
|
+
|
|
319
|
+
@property
|
|
320
|
+
def position(self) -> DataStore:
|
|
321
|
+
"""
|
|
322
|
+
_KEYS = ["instType", "instId", "posId"]
|
|
323
|
+
|
|
324
|
+
Data Structure:
|
|
325
|
+
|
|
326
|
+
.. code:: json
|
|
327
|
+
|
|
328
|
+
[
|
|
329
|
+
{
|
|
330
|
+
"posId": "1",
|
|
331
|
+
"instId": "ETHUSDT",
|
|
332
|
+
"marginCoin": "USDT",
|
|
333
|
+
"marginSize": "9.5",
|
|
334
|
+
"marginMode": "crossed",
|
|
335
|
+
"holdSide": "short",
|
|
336
|
+
"posMode": "hedge_mode",
|
|
337
|
+
"total": "0.1",
|
|
338
|
+
"available": "0.1",
|
|
339
|
+
"frozen": "0",
|
|
340
|
+
"openPriceAvg": "1900",
|
|
341
|
+
"leverage": 20,
|
|
342
|
+
"achievedProfits": "0",
|
|
343
|
+
"unrealizedPL": "0",
|
|
344
|
+
"unrealizedPLR": "0",
|
|
345
|
+
"liquidationPrice": "5788.108475905242",
|
|
346
|
+
"keepMarginRate": "0.005",
|
|
347
|
+
"marginRate": "0.004416374196",
|
|
348
|
+
"cTime": "1695649246169",
|
|
349
|
+
"breakEvenPrice": "24778.97",
|
|
350
|
+
"totalFee": "1.45",
|
|
351
|
+
"deductedFee": "0.388",
|
|
352
|
+
"markPrice": "2500",
|
|
353
|
+
"assetMode": "union",
|
|
354
|
+
"uTime": "1695711602568",
|
|
355
|
+
"autoMargin": "off"
|
|
356
|
+
}
|
|
357
|
+
]
|
|
358
|
+
"""
|
|
359
|
+
return self._get("position")
|