hyperquant 0.6__py3-none-any.whl → 0.8__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.
@@ -0,0 +1,359 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ from typing import TYPE_CHECKING, Any, Awaitable
5
+ from aiohttp import ClientResponse
6
+ from pybotters import DataStore
7
+ from pybotters.models.bitget_v2 import BitgetV2DataStore
8
+ from ..lib.util import place_to_step
9
+
10
+ if TYPE_CHECKING:
11
+ from pybotters.typedefs import Item
12
+
13
+ class Detail(DataStore):
14
+ """Futures instrument metadata store obtained from the futures instrument endpoint."""
15
+
16
+ _KEYS = ["symbol"]
17
+
18
+ def _transform(self, entry: dict[str, Any]) -> dict[str, Any] | None:
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
+
31
+ return entry
32
+
33
+ def _onresponse(self, data: list[dict[str, Any]] | dict[str, Any] | None) -> None:
34
+ if not data:
35
+ self._clear()
36
+ return
37
+ entries = data
38
+ if isinstance(data, dict): # pragma: no cover - defensive guard
39
+ entries = data.get("data") or []
40
+ items: list[dict[str, Any]] = []
41
+ for entry in entries or []:
42
+ transformed = self._transform(entry)
43
+ if transformed:
44
+ items.append(transformed)
45
+ if not items:
46
+ self._clear()
47
+ return
48
+ self._clear()
49
+ self._insert(items)
50
+
51
+
52
+ class Book(DataStore):
53
+ _KEYS = ["t", "s", "S", "p"]
54
+
55
+ def _onmessage(self, msg: Item) -> None:
56
+ action = msg["action"]
57
+ inst_type = msg["arg"]["instType"]
58
+ inst_id = msg["arg"]["instId"]
59
+
60
+ data_to_insert = []
61
+ data_to_update = []
62
+ data_to_delete = []
63
+ for book in msg["data"]:
64
+ for side in ("asks", "bids"):
65
+ for row in book[side]:
66
+ converted_row = {
67
+ "t": inst_type,
68
+ "s": inst_id,
69
+ "S": side[0],
70
+ "p": row[0],
71
+ "q": row[1],
72
+ }
73
+ if action == "snapshot":
74
+ data_to_insert.append(converted_row)
75
+ elif converted_row["q"] != "0":
76
+ data_to_update.append(converted_row)
77
+ else:
78
+ data_to_delete.append(converted_row)
79
+
80
+ # Cleanup on reconnect
81
+ if action == "snapshot":
82
+ self._find_and_delete({"t": inst_type, "s": inst_id})
83
+
84
+ self._insert(data_to_insert)
85
+ self._update(data_to_update)
86
+ self._delete(data_to_delete)
87
+
88
+ def sorted(
89
+ self, query: Item | None = None, limit: int | None = None
90
+ ) -> dict[str, list[Item]]:
91
+ return self._sorted(
92
+ item_key="side",
93
+ item_asc_key="a",
94
+ item_desc_key="b",
95
+ sort_key="p",
96
+ query=query,
97
+ limit=limit,
98
+ )
99
+
100
+
101
+ class BitgetDataStore(BitgetV2DataStore):
102
+
103
+ def _init(self):
104
+ super()._init()
105
+ self._create('detail', datastore_class=Detail)
106
+ self._create("book", datastore_class=Book)
107
+
108
+ async def initialize(self, *aws: Awaitable[ClientResponse]) -> None:
109
+ for fut in asyncio.as_completed(aws):
110
+ res = await fut
111
+ data = await res.json()
112
+ if res.url.path == '/api/v2/mix/market/contracts':
113
+ self.detail._onresponse(data)
114
+ elif res.url.path == '/api/v2/mix/market/tickers':
115
+ self.ticker._clear()
116
+ tickers = data.get('data', [])
117
+ # 为每个ticker添加额外的字段
118
+ for ticker in tickers:
119
+ symbol = ticker.get('symbol')
120
+ ticker['instId'] = symbol
121
+ ticker['instType'] = 'futures'
122
+
123
+ self.ticker._update(tickers)
124
+
125
+ @property
126
+ def detail(self) -> Detail:
127
+ """
128
+ _key: symbol
129
+
130
+ Data Structure:
131
+
132
+ .. code:: json
133
+
134
+ [
135
+ {
136
+ "symbol": "BTCUSDT",
137
+ "baseCoin": "BTC",
138
+ "quoteCoin": "USDT",
139
+ "buyLimitPriceRatio": "0.9",
140
+ "sellLimitPriceRatio": "0.9",
141
+ "feeRateUpRatio": "0.1",
142
+ "makerFeeRate": "0.0004",
143
+ "takerFeeRate": "0.0006",
144
+ "openCostUpRatio": "0.1",
145
+ "supportMarginCoins": [
146
+ "USDT"
147
+ ],
148
+ "minTradeNum": "0.01",
149
+ "priceEndStep": "1",
150
+ "volumePlace": "2",
151
+ "stepSize": "0.01",
152
+ "tickSize": "0.1",
153
+ "pricePlace": "1",
154
+ "sizeMultiplier": "0.01",
155
+ "symbolType": "perpetual",
156
+ "minTradeUSDT": "5",
157
+ "maxSymbolOrderNum": "999999",
158
+ "maxProductOrderNum": "999999",
159
+ "maxPositionNum": "150",
160
+ "symbolStatus": "normal",
161
+ "offTime": "-1",
162
+ "limitOpenTime": "-1",
163
+ "deliveryTime": "",
164
+ "deliveryStartTime": "",
165
+ "launchTime": "",
166
+ "fundInterval": "8",
167
+ "minLever": "1",
168
+ "maxLever": "125",
169
+ "posLimit": "0.05",
170
+ "maintainTime": "1680165535278",
171
+ "maxMarketOrderQty": "220",
172
+ "maxOrderQty": "1200"
173
+ }]
174
+
175
+ """
176
+ return self._get('detail')
177
+
178
+ @property
179
+ def ticker(self) -> DataStore:
180
+ """
181
+ _KEYS = ["instType", "instId"]
182
+
183
+ Data Structure:
184
+
185
+ .. code:: json
186
+
187
+ [
188
+ {
189
+ "symbol": "BTCUSDT",
190
+ "lastPr": "111534.6",
191
+ "askPr": "111534.6",
192
+ "bidPr": "111534.5",
193
+ "bidSz": "23.7924",
194
+ "askSz": "8.1762",
195
+ "high24h": "112300",
196
+ "low24h": "109136.2",
197
+ "ts": "1759115725508",
198
+ "change24h": "0.01906",
199
+ "baseVolume": "35520.11438048",
200
+ "quoteVolume": "3932280581.066103549",
201
+ "usdtVolume": "3932280581.066103549",
202
+ "openUtc": "112100",
203
+ "changeUtc24h": "-0.00504",
204
+ "indexPrice": "111587.6090439271505504",
205
+ "fundingRate": "-0.000002",
206
+ "holdingAmount": "66775.1917",
207
+ "deliveryStartTime": null,
208
+ "deliveryTime": null,
209
+ "deliveryStatus": "",
210
+ "open24h": "109448.3",
211
+ "markPrice": "111537",
212
+ "instId": "BTCUSDT",
213
+ "instType": "futures"
214
+ }
215
+ ]
216
+ """
217
+ return self._get('ticker')
218
+
219
+ @property
220
+ def orders(self) -> DataStore:
221
+ """
222
+ _KEYS = ["instType", "instId", "orderId"]
223
+ .. code:: json
224
+
225
+ [
226
+ {
227
+ "instType": "futures",
228
+ "instId": "BTCUSDT",
229
+ "orderId": "1",
230
+ "clientOid": "1",
231
+ "size": "8.0000",
232
+ "newSize": "500.0000",
233
+ "notional": "8.000000",
234
+ "orderType": "market",
235
+ "force": "gtc",
236
+ "side": "buy",
237
+ "fillPrice": "26256.0",
238
+ "tradeId": "1",
239
+ "baseVolume": "0.0003",
240
+ "fillTime": "1695797773286",
241
+ "fillFee": "-0.00000018",
242
+ "fillFeeCoin": "BTC",
243
+ "tradeScope": "T",
244
+ "accBaseVolume": "0.0003",
245
+ "priceAvg": "26256.0",
246
+ "status": "partially_filled",
247
+ "cTime": "1695797773257",
248
+ "uTime": "1695797773326",
249
+ "stpMode": "cancel_taker",
250
+ "feeDetail": [
251
+ {
252
+ "feeCoin": "BTC",
253
+ "fee": "-0.00000018"
254
+ }
255
+ ],
256
+ "enterPointSource": "WEB"
257
+ }
258
+ ]
259
+ """
260
+ return self._get('orders')
261
+
262
+ @property
263
+ def book(self) -> DataStore:
264
+ """
265
+ _KEYS = ["t", "s", "S", "p"]
266
+
267
+ Data Structure:
268
+
269
+ .. code:: json
270
+
271
+ [
272
+ {
273
+ "t": "futures",
274
+ "s": "BTCUSDT",
275
+ "S": "a",
276
+ "p": "111534.6",
277
+ "q": "8.1762"
278
+ },
279
+ {
280
+ "t": "futures",
281
+ "s": "BTCUSDT",
282
+ "S": "b",
283
+ "p": "111534.5",
284
+ "q": "23.7924"
285
+ }
286
+ ]
287
+ """
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")