hyperquant 0.69__py3-none-any.whl → 0.72__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,291 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from typing import Any, Literal
5
+
6
+ import pybotters
7
+
8
+ from .models.bitget import BitgetDataStore
9
+ from .lib.util import fmt_value
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class Bitget:
15
+ """Bitget public/privileged client (REST + WS).
16
+
17
+ 默认只支持单向持仓(One-way mode)。
18
+ """
19
+
20
+ def __init__(
21
+ self,
22
+ client: pybotters.Client,
23
+ *,
24
+ rest_api: str | None = None,
25
+ ws_url: str | None = None,
26
+ ) -> None:
27
+ self.client = client
28
+ self.store = BitgetDataStore()
29
+
30
+ self.rest_api = rest_api or "https://api.bitget.com"
31
+ self.ws_url = ws_url or "wss://ws.bitget.com/v2/ws/public"
32
+ self.ws_url_private = ws_url or "wss://ws.bitget.com/v2/ws/private"
33
+
34
+ self._ws_app = None
35
+
36
+ async def __aenter__(self) -> "Bitget":
37
+ await self.update("detail")
38
+ return self
39
+
40
+ async def __aexit__(self, exc_type, exc, tb) -> None: # pragma: no cover - symmetry
41
+ pass
42
+
43
+ async def sub_personal(self) -> None:
44
+ sub_msg = {
45
+ "op": "subscribe",
46
+ "args": [
47
+ {"instType": "USDT-FUTURES", "channel": "orders", "instId": "default"},
48
+ {
49
+ "instType": "USDT-FUTURES",
50
+ "channel": "positions",
51
+ "instId": "default",
52
+ },
53
+ {"instType": "USDT-FUTURES", "channel": "account", "coin": "default"},
54
+ ],
55
+ }
56
+ self.client.ws_connect(
57
+ self.ws_url_private,
58
+ send_json=sub_msg,
59
+ hdlr_json=self.store.onmessage,
60
+ )
61
+
62
+ async def update(
63
+ self,
64
+ update_type: Literal["detail", "ticker", "all"] = "all",
65
+ ) -> None:
66
+ """Refresh cached REST resources."""
67
+
68
+ requests: list[Any] = []
69
+
70
+ if update_type in {"detail", "all"}:
71
+ requests.append(
72
+ self.client.get(
73
+ f"{self.rest_api}/api/v2/mix/market/contracts",
74
+ params={"productType": "usdt-futures"},
75
+ )
76
+ )
77
+
78
+ if update_type in {"ticker", "all"}:
79
+ requests.append(
80
+ self.client.get(
81
+ f"{self.rest_api}/api/v2/mix/market/tickers",
82
+ params={"productType": "usdt-futures"},
83
+ )
84
+ )
85
+
86
+ if not requests:
87
+ raise ValueError(f"update_type err: {update_type}")
88
+
89
+ await self.store.initialize(*requests)
90
+
91
+ async def place_order(
92
+ self,
93
+ symbol: str,
94
+ *,
95
+ direction: Literal["buy", "sell", "long", "short", "0", "1"],
96
+ volume: float,
97
+ price: float | None = None,
98
+ order_type: Literal[
99
+ "market",
100
+ "limit_gtc",
101
+ "limit_ioc",
102
+ "limit_fok",
103
+ "limit_post_only",
104
+ "limit",
105
+ ] = "market",
106
+ margin_mode: Literal["isolated", "crossed"] = "crossed",
107
+ product_type: str = "USDT-FUTURES",
108
+ margin_coin: str = "USDT",
109
+ reduce_only: bool | None = None,
110
+ offset_flag: Literal["open", "close", "0", "1"] | None = None,
111
+ client_order_id: str | None = None,
112
+ extra_params: dict[str, Any] | None = None,
113
+ ) -> dict[str, Any]:
114
+ """Create an order via ``POST /api/v2/mix/order/place-order``.
115
+
116
+ Parameters mirror the Bitget V2 contract REST API but are normalized to
117
+ match the broker interface (``direction``, ``order_type`` etc.).
118
+ """
119
+
120
+ side = self._normalize_direction(direction)
121
+ order_type_code, force_code = self._resolve_order_type(order_type)
122
+
123
+ if reduce_only is None:
124
+ reduce_only = self._normalize_offset(offset_flag)
125
+
126
+ detail = self._get_detail_entry(symbol)
127
+ volume_str = self._format_with_step(
128
+ volume, detail.get("step_size") or detail.get("stepSize")
129
+ )
130
+
131
+ payload: dict[str, Any] = {
132
+ "symbol": symbol,
133
+ "productType": product_type,
134
+ "marginMode": margin_mode,
135
+ "marginCoin": margin_coin,
136
+ "side": side,
137
+ "size": volume_str,
138
+ "orderType": order_type_code,
139
+ }
140
+
141
+ if force_code:
142
+ payload["force"] = force_code
143
+
144
+ if order_type_code == "limit":
145
+ if price is None:
146
+ raise ValueError("price is required for Bitget limit orders")
147
+ payload["price"] = self._format_with_step(
148
+ price,
149
+ detail.get("tick_size") or detail.get("tickSize"),
150
+ )
151
+ elif price is not None:
152
+ logger.debug("Price %.8f ignored for market order", price)
153
+
154
+ if reduce_only is True:
155
+ payload["reduceOnly"] = "YES"
156
+ elif reduce_only is False:
157
+ payload["reduceOnly"] = "NO"
158
+
159
+ if client_order_id:
160
+ payload["clientOid"] = client_order_id
161
+
162
+ if extra_params:
163
+ payload.update(extra_params)
164
+
165
+ res = await self.client.post(
166
+ f"{self.rest_api}/api/v2/mix/order/place-order",
167
+ data=payload,
168
+ )
169
+ data = await res.json()
170
+ return self._ensure_ok("place_order", data)
171
+
172
+ async def cancel_order(
173
+ self,
174
+ order_sys_id: str,
175
+ *,
176
+ symbol: str,
177
+ margin_mode: Literal["isolated", "crossed"],
178
+ product_type: str = "USDT-FUTURES",
179
+ margin_coin: str = "USDT",
180
+ client_order_id: str | None = None,
181
+ ) -> dict[str, Any]:
182
+ """Cancel an order via ``POST /api/v2/mix/order/cancel-order``."""
183
+
184
+ payload = {
185
+ "symbol": symbol,
186
+ "productType": product_type,
187
+ "marginMode": margin_mode,
188
+ "marginCoin": margin_coin,
189
+ }
190
+
191
+ if client_order_id:
192
+ payload["clientOid"] = client_order_id
193
+ else:
194
+ payload["orderId"] = order_sys_id
195
+
196
+ res = await self.client.post(
197
+ f"{self.rest_api}/api/v2/mix/order/cancel-order",
198
+ json=payload,
199
+ )
200
+ data = await res.json()
201
+ return self._ensure_ok("cancel_order", data)
202
+
203
+ async def sub_orderbook(self, symbols: list[str], channel: str = "books1") -> None:
204
+ """Subscribe to Bitget order-book snapshots/updates."""
205
+
206
+ submsg = {"op": "subscribe", "args": []}
207
+ for symbol in symbols:
208
+ submsg["args"].append(
209
+ {"instType": "SPOT", "channel": channel, "instId": symbol}
210
+ )
211
+
212
+ self.client.ws_connect(
213
+ self.ws_url,
214
+ send_json=submsg,
215
+ hdlr_json=self.store.onmessage,
216
+ )
217
+
218
+ def _get_detail_entry(self, symbol: str) -> dict[str, Any]:
219
+ detail = self.store.detail.get({"symbol": symbol})
220
+ if not detail:
221
+ raise ValueError(
222
+ f"Unknown Bitget instrument: {symbol}. Call update('detail') first or provide valid symbol."
223
+ )
224
+ return detail
225
+
226
+ @staticmethod
227
+ def _format_with_step(value: float, step: Any) -> str:
228
+ if step in (None, 0, "0"):
229
+ return str(value)
230
+ try:
231
+ step_float = float(step)
232
+ except (TypeError, ValueError): # pragma: no cover - defensive guard
233
+ return str(value)
234
+ if step_float <= 0:
235
+ return str(value)
236
+ return fmt_value(value, step_float)
237
+
238
+ @staticmethod
239
+ def _normalize_direction(direction: str) -> str:
240
+ mapping = {
241
+ "buy": "buy",
242
+ "long": "buy",
243
+ "0": "buy",
244
+ "sell": "sell",
245
+ "short": "sell",
246
+ "1": "sell",
247
+ }
248
+ key = str(direction).lower()
249
+ try:
250
+ return mapping[key]
251
+ except KeyError as exc: # pragma: no cover - guard
252
+ raise ValueError(f"Unsupported direction: {direction}") from exc
253
+
254
+ @staticmethod
255
+ def _normalize_offset(
256
+ offset: Literal["open", "close", "0", "1"] | None,
257
+ ) -> bool | None:
258
+ if offset is None:
259
+ return None
260
+ mapping = {
261
+ "open": False,
262
+ "0": False,
263
+ "close": True,
264
+ "1": True,
265
+ }
266
+ key = str(offset).lower()
267
+ if key in mapping:
268
+ return mapping[key]
269
+ raise ValueError(f"Unsupported offset_flag: {offset}")
270
+
271
+ @staticmethod
272
+ def _resolve_order_type(order_type: str) -> tuple[str, str | None]:
273
+ mapping = {
274
+ "market": ("market", None),
275
+ "limit": ("limit", "gtc"),
276
+ "limit_gtc": ("limit", "gtc"),
277
+ "limit_ioc": ("limit", "ioc"),
278
+ "limit_fok": ("limit", "fok"),
279
+ "limit_post_only": ("limit", "post_only"),
280
+ }
281
+ key = str(order_type).lower()
282
+ try:
283
+ return mapping[key]
284
+ except KeyError as exc: # pragma: no cover - guard
285
+ raise ValueError(f"Unsupported order_type: {order_type}") from exc
286
+
287
+ @staticmethod
288
+ def _ensure_ok(operation: str, data: Any) -> dict[str, Any]:
289
+ if not isinstance(data, dict) or data.get("code") != "00000":
290
+ raise RuntimeError(f"{operation} failed: {data}")
291
+ return data.get("data") or {}
@@ -7,3 +7,16 @@ def fmt_value(price: float, tick: float) -> str:
7
7
  return str(
8
8
  (price_dec / tick_dec).quantize(Decimal("1"), rounding=ROUND_HALF_UP) * tick_dec
9
9
  )
10
+
11
+
12
+ def place_to_step(place: int) -> float:
13
+ """
14
+ 把 pricePlace / volumePlace 转换成 tick_size / lot_size
15
+
16
+ Args:
17
+ place (int): 小数位数,例如 pricePlace=1, volumePlace=2
18
+
19
+ Returns:
20
+ float: 步长 (step),例如 0.1, 0.01
21
+ """
22
+ return 10 ** (-place)
@@ -0,0 +1,288 @@
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")
@@ -184,7 +184,8 @@ class Orders(DataStore):
184
184
  "side": direction,
185
185
  "offset": offset_flag,
186
186
  "order_type": order_kind,
187
- "price": entry.get("Price"),
187
+ "price": entry.get('TradePrice') or entry.get("Price"),
188
+ "order_price": entry.get("OrderPrice") or entry.get('Price'),
188
189
  "quantity": entry.get("Volume"),
189
190
  "filled": entry.get("VolumeTraded"),
190
191
  "remaining": entry.get("VolumeRemain"),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperquant
3
- Version: 0.69
3
+ Version: 0.72
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
@@ -5,6 +5,7 @@ hyperquant/draw.py,sha256=up_lQ3pHeVLoNOyh9vPjgNwjD0M-6_IetSGviQUgjhY,54624
5
5
  hyperquant/logkit.py,sha256=nUo7nx5eONvK39GOhWwS41zNRL756P2J7-5xGzwXnTY,8462
6
6
  hyperquant/notikit.py,sha256=x5yAZ_tAvLQRXcRbcg-VabCaN45LUhvlTZnUqkIqfAA,3596
7
7
  hyperquant/broker/auth.py,sha256=Wst7mTBuUS2BQ5hZd0a8FNNs5Uc01ac9WzJpseTuyAY,7673
8
+ hyperquant/broker/bitget.py,sha256=n0McsEUStwtQaLaIxyJoGlougn2tmd4p7SQfA8Gumiw,9174
8
9
  hyperquant/broker/edgex.py,sha256=TqUO2KRPLN_UaxvtLL6HnA9dAQXC1sGxOfqTHd6W5k8,18378
9
10
  hyperquant/broker/hyperliquid.py,sha256=7MxbI9OyIBcImDelPJu-8Nd53WXjxPB5TwE6gsjHbto,23252
10
11
  hyperquant/broker/lbank.py,sha256=dZUbi0a_Vhkp4pJ1V11X6nEM7I4HhQIVRgpSMeGcAMU,11681
@@ -13,15 +14,16 @@ hyperquant/broker/ws.py,sha256=9Zu5JSLj-ylYEVmFmRwvZDDnVYKwb37cLHfZzA0AZGc,2200
13
14
  hyperquant/broker/lib/edgex_sign.py,sha256=lLUCmY8HHRLfLKyGrlTJYaBlSHPsIMWg3EZnQJKcmyk,95785
14
15
  hyperquant/broker/lib/hpstore.py,sha256=LnLK2zmnwVvhEbLzYI-jz_SfYpO1Dv2u2cJaRAb84D8,8296
15
16
  hyperquant/broker/lib/hyper_types.py,sha256=HqjjzjUekldjEeVn6hxiWA8nevAViC2xHADOzDz9qyw,991
16
- hyperquant/broker/lib/util.py,sha256=u02kGb-7LMCi32UNLeKoPaZBZ2LBEjx72KRkaKX0yQg,275
17
+ hyperquant/broker/lib/util.py,sha256=iMU1qF0CHj5zzlIMEQGwjz-qtEVosEe7slXOCuB7Rcw,566
18
+ hyperquant/broker/models/bitget.py,sha256=P-dADalyIxYbSNLqXTSu23h7JMS_0FfOH899imVp1aA,9119
17
19
  hyperquant/broker/models/edgex.py,sha256=vPAkceal44cjTYKQ_0BoNAskOpmkno_Yo1KxgMLPc6Y,33954
18
20
  hyperquant/broker/models/hyperliquid.py,sha256=c4r5739ibZfnk69RxPjQl902AVuUOwT8RNvKsMtwXBY,9459
19
- hyperquant/broker/models/lbank.py,sha256=ZCD1dOUMyWPT8lKDj6C6LcHEof2d0JN384McURzLA-s,18868
21
+ hyperquant/broker/models/lbank.py,sha256=_ztqhYGDqEHpmSITfYp_cpEJiJrhfJ9dwZ52K_PhE2A,18969
20
22
  hyperquant/broker/models/ourbit.py,sha256=xMcbuCEXd3XOpPBq0RYF2zpTFNnxPtuNJZCexMZVZ1k,41965
21
23
  hyperquant/datavison/_util.py,sha256=92qk4vO856RqycO0YqEIHJlEg-W9XKapDVqAMxe6rbw,533
22
24
  hyperquant/datavison/binance.py,sha256=3yNKTqvt_vUQcxzeX4ocMsI5k6Q6gLZrvgXxAEad6Kc,5001
23
25
  hyperquant/datavison/coinglass.py,sha256=PEjdjISP9QUKD_xzXNzhJ9WFDTlkBrRQlVL-5pxD5mo,10482
24
26
  hyperquant/datavison/okx.py,sha256=yg8WrdQ7wgWHNAInIgsWPM47N3Wkfr253169IPAycAY,6898
25
- hyperquant-0.69.dist-info/METADATA,sha256=c6WCydNN5Q8suoZeKx54-RHdT3VipbbIfayFeyC0ir4,4317
26
- hyperquant-0.69.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
27
- hyperquant-0.69.dist-info/RECORD,,
27
+ hyperquant-0.72.dist-info/METADATA,sha256=YCqf-2q7ieiCqAHbUfOhrAPrqnFMzz6ZvAs5f4_PTAU,4317
28
+ hyperquant-0.72.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
29
+ hyperquant-0.72.dist-info/RECORD,,