hyperquant 0.93__py3-none-any.whl → 0.95__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.
- hyperquant/broker/auth.py +55 -0
- hyperquant/broker/bitmart.py +495 -0
- hyperquant/broker/lighter.py +470 -0
- hyperquant/broker/models/bitmart.py +609 -0
- hyperquant/broker/models/lighter.py +508 -0
- {hyperquant-0.93.dist-info → hyperquant-0.95.dist-info}/METADATA +2 -1
- {hyperquant-0.93.dist-info → hyperquant-0.95.dist-info}/RECORD +8 -4
- {hyperquant-0.93.dist-info → hyperquant-0.95.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import time
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Iterable, Literal, Sequence
|
|
6
|
+
|
|
7
|
+
from pybotters.store import DataStore, DataStoreCollection
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from pybotters.typedefs import Item
|
|
11
|
+
from pybotters.ws import ClientWebSocketResponse
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _maybe_to_dict(payload: Any) -> Any:
|
|
15
|
+
"""Convert pydantic models to dict, keeping plain dict/list untouched."""
|
|
16
|
+
if payload is None:
|
|
17
|
+
return None
|
|
18
|
+
if hasattr(payload, "to_dict"):
|
|
19
|
+
return payload.to_dict()
|
|
20
|
+
if hasattr(payload, "model_dump"):
|
|
21
|
+
return payload.model_dump()
|
|
22
|
+
return payload
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Book(DataStore):
|
|
26
|
+
"""Order book snapshots sourced from Lighter websocket feeds."""
|
|
27
|
+
|
|
28
|
+
_KEYS = ["s", "S", "p"]
|
|
29
|
+
|
|
30
|
+
def _init(self) -> None:
|
|
31
|
+
self.limit: int | None = None
|
|
32
|
+
self.id_to_symbol: dict[str, str] = {} # broker设置
|
|
33
|
+
self._last_update: float = 0.0
|
|
34
|
+
self._state: dict[str, dict[str, dict[float, float]]] = {}
|
|
35
|
+
self._visible: dict[str, dict[str, dict[float, dict[str, Any]]]] = {}
|
|
36
|
+
|
|
37
|
+
@staticmethod
|
|
38
|
+
def _market_id_from_channel(channel: str | None) -> str | None:
|
|
39
|
+
if not channel:
|
|
40
|
+
return None
|
|
41
|
+
if ":" in channel:
|
|
42
|
+
return channel.split(":", 1)[1]
|
|
43
|
+
if "/" in channel:
|
|
44
|
+
return channel.split("/", 1)[1]
|
|
45
|
+
return channel
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@staticmethod
|
|
49
|
+
def _make_entry(symbol: str, side: Literal["a", "b"], price: float, size: float) -> dict[str, Any]:
|
|
50
|
+
return {
|
|
51
|
+
"s": symbol,
|
|
52
|
+
"S": side,
|
|
53
|
+
"p": f"{price}",
|
|
54
|
+
"q": f"{abs(size)}",
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
def _on_message(self, msg: dict[str, Any]) -> None:
|
|
58
|
+
msg_type = msg.get("type")
|
|
59
|
+
if msg_type not in {"subscribed/order_book", "update/order_book"}:
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
market_id = self._market_id_from_channel(msg.get("channel"))
|
|
63
|
+
if market_id is None:
|
|
64
|
+
return
|
|
65
|
+
|
|
66
|
+
order_book = msg.get("order_book")
|
|
67
|
+
if not isinstance(order_book, dict):
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
state = self._state.setdefault(market_id, {"ask": {}, "bid": {}})
|
|
71
|
+
visible = self._visible.setdefault(market_id, {"a": {}, "b": {}})
|
|
72
|
+
|
|
73
|
+
symbol = self.id_to_symbol.get(market_id)
|
|
74
|
+
if symbol is None:
|
|
75
|
+
symbol = market_id
|
|
76
|
+
|
|
77
|
+
for side_name, updates_data in (("ask", order_book.get("asks")), ("bid", order_book.get("bids"))):
|
|
78
|
+
side_state = state[side_name]
|
|
79
|
+
if not updates_data:
|
|
80
|
+
continue
|
|
81
|
+
for level in updates_data:
|
|
82
|
+
price = level.get("price")
|
|
83
|
+
size = level.get("size") or level.get("remaining_base_amount") or level.get("base_amount")
|
|
84
|
+
if price is None or size is None:
|
|
85
|
+
continue
|
|
86
|
+
try:
|
|
87
|
+
price_val = float(price)
|
|
88
|
+
size_val = float(size)
|
|
89
|
+
except (TypeError, ValueError):
|
|
90
|
+
continue
|
|
91
|
+
if size_val <= 0:
|
|
92
|
+
side_state.pop(price_val, None)
|
|
93
|
+
else:
|
|
94
|
+
side_state[price_val] = size_val
|
|
95
|
+
|
|
96
|
+
def build_visible(side_name: Literal["ask", "bid"]) -> dict[float, dict[str, Any]]:
|
|
97
|
+
side_state = state[side_name]
|
|
98
|
+
reverse = side_name == "bid"
|
|
99
|
+
sorted_levels = sorted(side_state.items(), reverse=reverse)
|
|
100
|
+
if self.limit is not None:
|
|
101
|
+
sorted_levels = sorted_levels[: self.limit]
|
|
102
|
+
entry_side = "a" if side_name == "ask" else "b"
|
|
103
|
+
return {
|
|
104
|
+
price: self._make_entry(symbol, entry_side, price, size)
|
|
105
|
+
for price, size in sorted_levels
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
new_visible = {
|
|
109
|
+
"a": build_visible("ask"),
|
|
110
|
+
"b": build_visible("bid"),
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
removals: list[dict[str, Any]] = []
|
|
114
|
+
updates: list[dict[str, Any]] = []
|
|
115
|
+
|
|
116
|
+
for side_key in ("a", "b"):
|
|
117
|
+
prev_side = visible[side_key]
|
|
118
|
+
next_side = new_visible[side_key]
|
|
119
|
+
|
|
120
|
+
for price, entry in prev_side.items():
|
|
121
|
+
if price not in next_side:
|
|
122
|
+
removals.append({k: entry[k] for k in self._KEYS})
|
|
123
|
+
|
|
124
|
+
for price, entry in next_side.items():
|
|
125
|
+
prev_entry = prev_side.get(price)
|
|
126
|
+
if prev_entry is None or prev_entry.get("q") != entry.get("q"):
|
|
127
|
+
updates.append(entry)
|
|
128
|
+
|
|
129
|
+
if removals:
|
|
130
|
+
self._delete(removals)
|
|
131
|
+
if updates:
|
|
132
|
+
self._update(updates)
|
|
133
|
+
|
|
134
|
+
self._visible[market_id] = new_visible
|
|
135
|
+
self._last_update = time.time()
|
|
136
|
+
|
|
137
|
+
def sorted(self, query: Item | None = None, limit: int | None = None) -> dict[str, list[Item]]:
|
|
138
|
+
return self._sorted(
|
|
139
|
+
item_key="S",
|
|
140
|
+
item_asc_key="a",
|
|
141
|
+
item_desc_key="b",
|
|
142
|
+
sort_key="p",
|
|
143
|
+
query=query,
|
|
144
|
+
limit=limit,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def last_update(self) -> float:
|
|
149
|
+
return self._last_update
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class Detail(DataStore):
|
|
155
|
+
"""Market metadata."""
|
|
156
|
+
|
|
157
|
+
_KEYS = ["symbol"]
|
|
158
|
+
|
|
159
|
+
def _onresponse(self, data: Any) -> None:
|
|
160
|
+
payload = _maybe_to_dict(data) or {}
|
|
161
|
+
order_books = payload.get("order_books") or payload.get("order_book_details") or []
|
|
162
|
+
|
|
163
|
+
if isinstance(order_books, dict):
|
|
164
|
+
order_books = list(order_books.values())
|
|
165
|
+
|
|
166
|
+
normalized: list[dict[str, Any]] = []
|
|
167
|
+
for entry in order_books or []:
|
|
168
|
+
if not isinstance(entry, dict):
|
|
169
|
+
continue
|
|
170
|
+
market_id = entry.get("market_id") or entry.get("id")
|
|
171
|
+
symbol = entry.get("symbol")
|
|
172
|
+
if market_id is None and symbol is None:
|
|
173
|
+
continue
|
|
174
|
+
record = dict(entry)
|
|
175
|
+
if market_id is None and symbol is not None:
|
|
176
|
+
record["market_id"] = symbol
|
|
177
|
+
normalized.append(record)
|
|
178
|
+
|
|
179
|
+
self._clear()
|
|
180
|
+
if normalized:
|
|
181
|
+
self._insert(normalized)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class Orders(DataStore):
|
|
185
|
+
"""Active orders."""
|
|
186
|
+
|
|
187
|
+
_KEYS = ["order_id"]
|
|
188
|
+
|
|
189
|
+
def _init(self) -> None:
|
|
190
|
+
self.id_to_symbol: dict[str, str] = {} # broker设置
|
|
191
|
+
|
|
192
|
+
@staticmethod
|
|
193
|
+
def _normalize(entry: dict[str, Any]) -> dict[str, Any] | None:
|
|
194
|
+
order_id = entry.get("order_id") or entry.get("orderId")
|
|
195
|
+
if order_id is None:
|
|
196
|
+
return None
|
|
197
|
+
normalized = dict(entry)
|
|
198
|
+
normalized["order_id"] = str(order_id)
|
|
199
|
+
return normalized
|
|
200
|
+
|
|
201
|
+
def _onresponse(self, data: Any) -> None:
|
|
202
|
+
payload = _maybe_to_dict(data) or {}
|
|
203
|
+
orders = payload.get("orders") or []
|
|
204
|
+
items: list[dict[str, Any]] = []
|
|
205
|
+
for entry in orders:
|
|
206
|
+
if not isinstance(entry, dict):
|
|
207
|
+
continue
|
|
208
|
+
normalized = self._normalize(entry)
|
|
209
|
+
if self.id_to_symbol:
|
|
210
|
+
market_id = entry.get("market_index")
|
|
211
|
+
if market_id is not None:
|
|
212
|
+
symbol = self.id_to_symbol.get(str(market_id))
|
|
213
|
+
if symbol is not None and normalized is not None:
|
|
214
|
+
normalized["symbol"] = symbol
|
|
215
|
+
|
|
216
|
+
if normalized:
|
|
217
|
+
items.append(normalized)
|
|
218
|
+
|
|
219
|
+
self._clear()
|
|
220
|
+
if items:
|
|
221
|
+
self._insert(items)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
class Accounts(DataStore):
|
|
228
|
+
"""Account level balances and metadata."""
|
|
229
|
+
|
|
230
|
+
_KEYS = ["account_index"]
|
|
231
|
+
|
|
232
|
+
def _normalize_account(self, entry: dict[str, Any]) -> dict[str, Any] | None:
|
|
233
|
+
account_index = entry.get("account_index") or entry.get("index")
|
|
234
|
+
if account_index is None:
|
|
235
|
+
return None
|
|
236
|
+
normalized = dict(entry)
|
|
237
|
+
normalized["account_index"] = account_index
|
|
238
|
+
normalized.pop("positions", None)
|
|
239
|
+
return normalized
|
|
240
|
+
|
|
241
|
+
def _on_accounts(self, accounts: Iterable[dict[str, Any]]) -> None:
|
|
242
|
+
normalized: list[dict[str, Any]] = []
|
|
243
|
+
for account in accounts:
|
|
244
|
+
if not isinstance(account, dict):
|
|
245
|
+
continue
|
|
246
|
+
record = self._normalize_account(account)
|
|
247
|
+
if record:
|
|
248
|
+
normalized.append(record)
|
|
249
|
+
if not normalized:
|
|
250
|
+
return
|
|
251
|
+
keys = [{"account_index": record["account_index"]} for record in normalized]
|
|
252
|
+
self._delete(keys)
|
|
253
|
+
self._insert(normalized)
|
|
254
|
+
|
|
255
|
+
def _onresponse(self, data: Any) -> None:
|
|
256
|
+
payload = _maybe_to_dict(data) or {}
|
|
257
|
+
accounts = payload.get("accounts") or []
|
|
258
|
+
self._on_accounts(accounts)
|
|
259
|
+
|
|
260
|
+
def _on_message(self, msg: dict[str, Any]) -> None:
|
|
261
|
+
account = msg.get("account")
|
|
262
|
+
if not isinstance(account, dict):
|
|
263
|
+
return
|
|
264
|
+
self._on_accounts([account])
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
class Positions(DataStore):
|
|
268
|
+
"""Per-market positions grouped by account."""
|
|
269
|
+
|
|
270
|
+
_KEYS = ["account_index", "market_id"]
|
|
271
|
+
|
|
272
|
+
def _init(self) -> None:
|
|
273
|
+
self.id_to_symbol: dict[str, str] = {} # broker设置
|
|
274
|
+
|
|
275
|
+
@staticmethod
|
|
276
|
+
def _normalize(
|
|
277
|
+
account_index: Any,
|
|
278
|
+
position: dict[str, Any],
|
|
279
|
+
) -> dict[str, Any] | None:
|
|
280
|
+
market_id = position.get("market_id")
|
|
281
|
+
if account_index is None or market_id is None:
|
|
282
|
+
return None
|
|
283
|
+
normalized = dict(position)
|
|
284
|
+
normalized["account_index"] = account_index
|
|
285
|
+
normalized["market_id"] = market_id
|
|
286
|
+
return normalized
|
|
287
|
+
|
|
288
|
+
def _update_positions(
|
|
289
|
+
self,
|
|
290
|
+
account_index: Any,
|
|
291
|
+
positions: Sequence[dict[str, Any]] | None,
|
|
292
|
+
) -> None:
|
|
293
|
+
if positions is None:
|
|
294
|
+
return
|
|
295
|
+
items: list[dict[str, Any]] = []
|
|
296
|
+
for position in positions:
|
|
297
|
+
if not isinstance(position, dict):
|
|
298
|
+
continue
|
|
299
|
+
record = self._normalize(account_index, position)
|
|
300
|
+
if record:
|
|
301
|
+
items.append(record)
|
|
302
|
+
if not items:
|
|
303
|
+
return
|
|
304
|
+
keys = [{"account_index": item["account_index"], "market_id": item["market_id"]} for item in items]
|
|
305
|
+
self._delete(keys)
|
|
306
|
+
self._insert(items)
|
|
307
|
+
|
|
308
|
+
def _onresponse(self, data: Any) -> None:
|
|
309
|
+
payload = _maybe_to_dict(data) or {}
|
|
310
|
+
accounts = payload.get("accounts") or []
|
|
311
|
+
for account in accounts:
|
|
312
|
+
if not isinstance(account, dict):
|
|
313
|
+
continue
|
|
314
|
+
account_index = account.get("account_index") or account.get("index")
|
|
315
|
+
positions = account.get("positions")
|
|
316
|
+
self._update_positions(account_index, positions)
|
|
317
|
+
|
|
318
|
+
def _on_message(self, msg: dict[str, Any]) -> None:
|
|
319
|
+
account = msg.get("account")
|
|
320
|
+
if not isinstance(account, dict):
|
|
321
|
+
return
|
|
322
|
+
account_index = account.get("account_index") or account.get("index")
|
|
323
|
+
positions = account.get("positions")
|
|
324
|
+
self._update_positions(account_index, positions)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
class LighterDataStore(DataStoreCollection):
|
|
328
|
+
"""Data store collection for the Lighter exchange."""
|
|
329
|
+
|
|
330
|
+
def _init(self) -> None:
|
|
331
|
+
self._create("book", datastore_class=Book)
|
|
332
|
+
self._create("detail", datastore_class=Detail)
|
|
333
|
+
self._create("orders", datastore_class=Orders)
|
|
334
|
+
self._create("accounts", datastore_class=Accounts)
|
|
335
|
+
self._create("positions", datastore_class=Positions)
|
|
336
|
+
|
|
337
|
+
def set_id_to_symbol(self, id_to_symbol: dict[str, str]) -> None:
|
|
338
|
+
self.id_to_symbol = id_to_symbol
|
|
339
|
+
self.book.id_to_symbol = self.id_to_symbol
|
|
340
|
+
self.orders.id_to_symbol = self.id_to_symbol
|
|
341
|
+
self.positions.id_to_symbol = self.id_to_symbol
|
|
342
|
+
|
|
343
|
+
def onmessage(self, msg: Item, ws: ClientWebSocketResponse | None = None) -> None:
|
|
344
|
+
|
|
345
|
+
msg_type = msg.get("type")
|
|
346
|
+
if msg_type == "ping":
|
|
347
|
+
asyncio.create_task(ws.send_json({"type": "pong"}))
|
|
348
|
+
return
|
|
349
|
+
|
|
350
|
+
if isinstance(msg, dict):
|
|
351
|
+
msg_type = msg.get("type")
|
|
352
|
+
if msg_type in {"subscribed/order_book", "update/order_book"}:
|
|
353
|
+
self.book._on_message(msg)
|
|
354
|
+
elif msg_type in {"subscribed/account_all", "update/account_all"}:
|
|
355
|
+
self.accounts._on_message(msg)
|
|
356
|
+
self.positions._on_message(msg)
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
@property
|
|
360
|
+
def book(self) -> Book:
|
|
361
|
+
"""
|
|
362
|
+
Lighter 深度快照。
|
|
363
|
+
|
|
364
|
+
.. code:: json
|
|
365
|
+
|
|
366
|
+
{
|
|
367
|
+
"s": "BTC-USD",
|
|
368
|
+
"S": "a", # \"a\"=ask / \"b\"=bid
|
|
369
|
+
"p": "50250.5",
|
|
370
|
+
"q": "0.37"
|
|
371
|
+
}
|
|
372
|
+
"""
|
|
373
|
+
return self._get("book")
|
|
374
|
+
|
|
375
|
+
@property
|
|
376
|
+
def detail(self) -> Detail:
|
|
377
|
+
"""
|
|
378
|
+
`lighter.models.OrderBookDetail` 元数据。
|
|
379
|
+
|
|
380
|
+
.. code:: json
|
|
381
|
+
|
|
382
|
+
[
|
|
383
|
+
{
|
|
384
|
+
"symbol": "DOLO",
|
|
385
|
+
"market_id": 75,
|
|
386
|
+
"status": "active",
|
|
387
|
+
"taker_fee": "0.0000",
|
|
388
|
+
"maker_fee": "0.0000",
|
|
389
|
+
"liquidation_fee": "1.0000",
|
|
390
|
+
"min_base_amount": "30.0",
|
|
391
|
+
"min_quote_amount": "10.000000",
|
|
392
|
+
"supported_size_decimals": 1,
|
|
393
|
+
"supported_price_decimals": 5,
|
|
394
|
+
"supported_quote_decimals": 6,
|
|
395
|
+
"order_quote_limit": ""
|
|
396
|
+
}
|
|
397
|
+
]
|
|
398
|
+
"""
|
|
399
|
+
return self._get("detail")
|
|
400
|
+
|
|
401
|
+
@property
|
|
402
|
+
def orders(self) -> Orders:
|
|
403
|
+
"""
|
|
404
|
+
活动订单(`lighter.models.Order`)。
|
|
405
|
+
|
|
406
|
+
.. code:: json
|
|
407
|
+
|
|
408
|
+
{
|
|
409
|
+
"order_index": 21673573193817727,
|
|
410
|
+
"client_order_index": 0,
|
|
411
|
+
"order_id": "21673573193817727",
|
|
412
|
+
"client_order_id": "0",
|
|
413
|
+
"market_index": 75,
|
|
414
|
+
"symbol": "DOLO",
|
|
415
|
+
"owner_account_index": 311464,
|
|
416
|
+
"initial_base_amount": "146.7",
|
|
417
|
+
"price": "0.07500",
|
|
418
|
+
"nonce": 281474963807871,
|
|
419
|
+
"remaining_base_amount": "146.7",
|
|
420
|
+
"is_ask": false,
|
|
421
|
+
"base_size": 1467,
|
|
422
|
+
"base_price": 7500,
|
|
423
|
+
"filled_base_amount": "0.0",
|
|
424
|
+
"filled_quote_amount": "0.000000",
|
|
425
|
+
"side": "",
|
|
426
|
+
"type": "limit",
|
|
427
|
+
"time_in_force": "good-till-time",
|
|
428
|
+
"reduce_only": false,
|
|
429
|
+
"trigger_price": "0.00000",
|
|
430
|
+
"order_expiry": 1764082202799,
|
|
431
|
+
"status": "open",
|
|
432
|
+
"trigger_status": "na",
|
|
433
|
+
"trigger_time": 0,
|
|
434
|
+
"parent_order_index": 0,
|
|
435
|
+
"parent_order_id": "0",
|
|
436
|
+
"to_trigger_order_id_0": "0",
|
|
437
|
+
"to_trigger_order_id_1": "0",
|
|
438
|
+
"to_cancel_order_id_0": "0",
|
|
439
|
+
"block_height": 75734444,
|
|
440
|
+
"timestamp": 1761663003,
|
|
441
|
+
"created_at": 1761663003,
|
|
442
|
+
"updated_at": 1761663003
|
|
443
|
+
}
|
|
444
|
+
"""
|
|
445
|
+
return self._get("orders")
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
@property
|
|
449
|
+
def accounts(self) -> Accounts:
|
|
450
|
+
"""
|
|
451
|
+
账户概览(`lighter.models.DetailedAccount`)。
|
|
452
|
+
|
|
453
|
+
.. code:: json
|
|
454
|
+
[
|
|
455
|
+
{
|
|
456
|
+
"code": 0,
|
|
457
|
+
"account_type": 0,
|
|
458
|
+
"index": 311464,
|
|
459
|
+
"l1_address": "0x5B3f0AdDfaf4c1d8729e266b22093545EFaE6c0e",
|
|
460
|
+
"cancel_all_time": 0,
|
|
461
|
+
"total_order_count": 1,
|
|
462
|
+
"total_isolated_order_count": 0,
|
|
463
|
+
"pending_order_count": 0,
|
|
464
|
+
"available_balance": "30.000000",
|
|
465
|
+
"status": 0,
|
|
466
|
+
"collateral": "30.000000",
|
|
467
|
+
"account_index": 311464,
|
|
468
|
+
"name": "",
|
|
469
|
+
"description": "",
|
|
470
|
+
"can_invite": false,
|
|
471
|
+
"referral_points_percentage": "",
|
|
472
|
+
"total_asset_value": "30",
|
|
473
|
+
"cross_asset_value": "30",
|
|
474
|
+
"shares": []
|
|
475
|
+
}
|
|
476
|
+
]
|
|
477
|
+
"""
|
|
478
|
+
return self._get("accounts")
|
|
479
|
+
|
|
480
|
+
@property
|
|
481
|
+
def positions(self) -> Positions:
|
|
482
|
+
"""
|
|
483
|
+
账户持仓(`lighter.models.AccountPosition`)。
|
|
484
|
+
|
|
485
|
+
.. code:: json
|
|
486
|
+
|
|
487
|
+
[
|
|
488
|
+
{
|
|
489
|
+
"market_id": 75,
|
|
490
|
+
"symbol": "DOLO",
|
|
491
|
+
"initial_margin_fraction": "33.33",
|
|
492
|
+
"open_order_count": 1,
|
|
493
|
+
"pending_order_count": 0,
|
|
494
|
+
"position_tied_order_count": 0,
|
|
495
|
+
"sign": 1,
|
|
496
|
+
"position": "129.8",
|
|
497
|
+
"avg_entry_price": "0.08476",
|
|
498
|
+
"position_value": "10.969398",
|
|
499
|
+
"unrealized_pnl": "-0.032450",
|
|
500
|
+
"realized_pnl": "0.000000",
|
|
501
|
+
"liquidation_price": "0",
|
|
502
|
+
"margin_mode": 0,
|
|
503
|
+
"allocated_margin": "0.000000",
|
|
504
|
+
"account_index": 311464
|
|
505
|
+
}
|
|
506
|
+
]
|
|
507
|
+
"""
|
|
508
|
+
return self._get("positions")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hyperquant
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.95
|
|
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
|
|
@@ -18,6 +18,7 @@ Requires-Dist: colorama>=0.4.6
|
|
|
18
18
|
Requires-Dist: cryptography>=44.0.2
|
|
19
19
|
Requires-Dist: curl-cffi>=0.13.0
|
|
20
20
|
Requires-Dist: duckdb>=1.2.2
|
|
21
|
+
Requires-Dist: lighter-sdk
|
|
21
22
|
Requires-Dist: numpy>=1.21.0
|
|
22
23
|
Requires-Dist: pandas>=2.2.3
|
|
23
24
|
Requires-Dist: pybotters>=1.9.1
|
|
@@ -4,13 +4,15 @@ hyperquant/db.py,sha256=i2TjkCbmH4Uxo7UTDvOYBfy973gLcGexdzuT_YcSeIE,6678
|
|
|
4
4
|
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
|
-
hyperquant/broker/auth.py,sha256=
|
|
7
|
+
hyperquant/broker/auth.py,sha256=C8B5-x8Qcaeafm4ZwPCVFR7GRURmHC3CE4_vdg00Qgw,12139
|
|
8
8
|
hyperquant/broker/bitget.py,sha256=X_S0LKZ7FZAEb6oEMr1vdGP1fondzK74BhmNTpRDSEA,9488
|
|
9
|
+
hyperquant/broker/bitmart.py,sha256=3lAkSy8J2p-M-663cUVFuRU-XtgNEwWAa4SKgeG6Vb8,16798
|
|
9
10
|
hyperquant/broker/coinup.py,sha256=eOr8BTRXiTb5tCU2FDmvBdXXgqiwVmCbP5pdeA1ORJ8,20390
|
|
10
11
|
hyperquant/broker/coinw.py,sha256=SnJU0vASh77rfcpMGWaIfTblQSjQk3vjlW_4juYdbcs,17214
|
|
11
12
|
hyperquant/broker/edgex.py,sha256=TqUO2KRPLN_UaxvtLL6HnA9dAQXC1sGxOfqTHd6W5k8,18378
|
|
12
13
|
hyperquant/broker/hyperliquid.py,sha256=7MxbI9OyIBcImDelPJu-8Nd53WXjxPB5TwE6gsjHbto,23252
|
|
13
14
|
hyperquant/broker/lbank.py,sha256=98M5wmSoeHwbBYMA3rh25zqLb6fQKVaEmwqALF5nOvY,22181
|
|
15
|
+
hyperquant/broker/lighter.py,sha256=lW4LgEJHqbiUZPu2BE1aKz_JBIW94KTKnCkD9vuHt-Q,16695
|
|
14
16
|
hyperquant/broker/ourbit.py,sha256=NUcDSIttf-HGWzoW1uBTrGLPHlkuemMjYCm91MigTno,18228
|
|
15
17
|
hyperquant/broker/ws.py,sha256=NS71Do-62mtVrGcyNE-AtHJkDecsSxdz_KU1yNBr_BQ,4079
|
|
16
18
|
hyperquant/broker/lib/edgex_sign.py,sha256=lLUCmY8HHRLfLKyGrlTJYaBlSHPsIMWg3EZnQJKcmyk,95785
|
|
@@ -18,16 +20,18 @@ hyperquant/broker/lib/hpstore.py,sha256=LnLK2zmnwVvhEbLzYI-jz_SfYpO1Dv2u2cJaRAb8
|
|
|
18
20
|
hyperquant/broker/lib/hyper_types.py,sha256=HqjjzjUekldjEeVn6hxiWA8nevAViC2xHADOzDz9qyw,991
|
|
19
21
|
hyperquant/broker/lib/util.py,sha256=iMU1qF0CHj5zzlIMEQGwjz-qtEVosEe7slXOCuB7Rcw,566
|
|
20
22
|
hyperquant/broker/models/bitget.py,sha256=0RwDY75KrJb-c-oYoMxbqxWfsILe-n_Npojz4UFUq7c,11389
|
|
23
|
+
hyperquant/broker/models/bitmart.py,sha256=Utu67H0D1wzppKeHqZmsPLeJ0sao2-yR_iGwWP5p0wU,20800
|
|
21
24
|
hyperquant/broker/models/coinup.py,sha256=X_ngB2_sgTOdfAZqTyeWvCN03j-0_inZ6ugZKW6hR7k,11173
|
|
22
25
|
hyperquant/broker/models/coinw.py,sha256=LvLMVP7i-qkkTK1ubw8eBkMK2RQmFoKPxdKqmC4IToY,22157
|
|
23
26
|
hyperquant/broker/models/edgex.py,sha256=vPAkceal44cjTYKQ_0BoNAskOpmkno_Yo1KxgMLPc6Y,33954
|
|
24
27
|
hyperquant/broker/models/hyperliquid.py,sha256=c4r5739ibZfnk69RxPjQl902AVuUOwT8RNvKsMtwXBY,9459
|
|
25
28
|
hyperquant/broker/models/lbank.py,sha256=vHkNKxIMzpoC_EwcZnEOPOupizF92yGWi9GKxvYYFUQ,19181
|
|
29
|
+
hyperquant/broker/models/lighter.py,sha256=RpqyMPrXbs4_OY9WSDep4T8pDhxDGaFQ8vdVmLZnfBg,16732
|
|
26
30
|
hyperquant/broker/models/ourbit.py,sha256=xMcbuCEXd3XOpPBq0RYF2zpTFNnxPtuNJZCexMZVZ1k,41965
|
|
27
31
|
hyperquant/datavison/_util.py,sha256=92qk4vO856RqycO0YqEIHJlEg-W9XKapDVqAMxe6rbw,533
|
|
28
32
|
hyperquant/datavison/binance.py,sha256=3yNKTqvt_vUQcxzeX4ocMsI5k6Q6gLZrvgXxAEad6Kc,5001
|
|
29
33
|
hyperquant/datavison/coinglass.py,sha256=PEjdjISP9QUKD_xzXNzhJ9WFDTlkBrRQlVL-5pxD5mo,10482
|
|
30
34
|
hyperquant/datavison/okx.py,sha256=yg8WrdQ7wgWHNAInIgsWPM47N3Wkfr253169IPAycAY,6898
|
|
31
|
-
hyperquant-0.
|
|
32
|
-
hyperquant-0.
|
|
33
|
-
hyperquant-0.
|
|
35
|
+
hyperquant-0.95.dist-info/METADATA,sha256=VCnz-XozwQFVbD3iQCz_Xg1kmKFOSqLCqyQwmeIq_iU,4409
|
|
36
|
+
hyperquant-0.95.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
37
|
+
hyperquant-0.95.dist-info/RECORD,,
|
|
File without changes
|