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