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.

@@ -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
- # if client_order_id:
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.1)
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
- step_size = entry.get('volume_place', 1)
21
- tick_size = entry.get('price_place', 1)
22
- step_size = place_to_step(step_size)
23
- tick_size = place_to_step(tick_size)
24
- entry['tickSize'] = tick_size
25
- entry['stepSize'] = step_size
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")