walrasquant-lib 0.4.20__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.
- walrasquant/__init__.py +7 -0
- walrasquant/aggregation.py +449 -0
- walrasquant/backends/__init__.py +5 -0
- walrasquant/backends/db.py +109 -0
- walrasquant/backends/db_memory.py +61 -0
- walrasquant/backends/db_postgresql.py +321 -0
- walrasquant/backends/db_sqlite.py +310 -0
- walrasquant/base/__init__.py +24 -0
- walrasquant/base/api_client.py +46 -0
- walrasquant/base/connector.py +863 -0
- walrasquant/base/ems.py +794 -0
- walrasquant/base/exchange.py +213 -0
- walrasquant/base/oms.py +428 -0
- walrasquant/base/retry.py +220 -0
- walrasquant/base/sms.py +545 -0
- walrasquant/base/ws_client.py +408 -0
- walrasquant/config.py +284 -0
- walrasquant/constants.py +413 -0
- walrasquant/core/__init__.py +0 -0
- walrasquant/core/cache.py +688 -0
- walrasquant/core/clock.py +59 -0
- walrasquant/core/connection.py +41 -0
- walrasquant/core/entity.py +504 -0
- walrasquant/core/nautilius_core.py +103 -0
- walrasquant/core/registry.py +41 -0
- walrasquant/engine.py +745 -0
- walrasquant/error.py +34 -0
- walrasquant/exchange/__init__.py +13 -0
- walrasquant/exchange/base_factory.py +172 -0
- walrasquant/exchange/binance/__init__.py +30 -0
- walrasquant/exchange/binance/connector.py +1093 -0
- walrasquant/exchange/binance/constants.py +934 -0
- walrasquant/exchange/binance/ems.py +140 -0
- walrasquant/exchange/binance/error.py +48 -0
- walrasquant/exchange/binance/exchange.py +144 -0
- walrasquant/exchange/binance/factory.py +115 -0
- walrasquant/exchange/binance/oms.py +1807 -0
- walrasquant/exchange/binance/rest_api.py +1653 -0
- walrasquant/exchange/binance/schema.py +1063 -0
- walrasquant/exchange/binance/websockets.py +389 -0
- walrasquant/exchange/bitget/__init__.py +28 -0
- walrasquant/exchange/bitget/connector.py +578 -0
- walrasquant/exchange/bitget/constants.py +392 -0
- walrasquant/exchange/bitget/ems.py +202 -0
- walrasquant/exchange/bitget/error.py +36 -0
- walrasquant/exchange/bitget/exchange.py +128 -0
- walrasquant/exchange/bitget/factory.py +135 -0
- walrasquant/exchange/bitget/oms.py +1619 -0
- walrasquant/exchange/bitget/rest_api.py +610 -0
- walrasquant/exchange/bitget/schema.py +885 -0
- walrasquant/exchange/bitget/websockets.py +753 -0
- walrasquant/exchange/bybit/__init__.py +32 -0
- walrasquant/exchange/bybit/connector.py +819 -0
- walrasquant/exchange/bybit/constants.py +479 -0
- walrasquant/exchange/bybit/ems.py +93 -0
- walrasquant/exchange/bybit/error.py +36 -0
- walrasquant/exchange/bybit/exchange.py +108 -0
- walrasquant/exchange/bybit/factory.py +128 -0
- walrasquant/exchange/bybit/oms.py +1195 -0
- walrasquant/exchange/bybit/rest_api.py +570 -0
- walrasquant/exchange/bybit/schema.py +867 -0
- walrasquant/exchange/bybit/websockets.py +307 -0
- walrasquant/exchange/hyperliquid/__init__.py +28 -0
- walrasquant/exchange/hyperliquid/connector.py +370 -0
- walrasquant/exchange/hyperliquid/constants.py +371 -0
- walrasquant/exchange/hyperliquid/ems.py +156 -0
- walrasquant/exchange/hyperliquid/error.py +48 -0
- walrasquant/exchange/hyperliquid/exchange.py +120 -0
- walrasquant/exchange/hyperliquid/factory.py +135 -0
- walrasquant/exchange/hyperliquid/oms.py +1081 -0
- walrasquant/exchange/hyperliquid/rest_api.py +348 -0
- walrasquant/exchange/hyperliquid/schema.py +583 -0
- walrasquant/exchange/hyperliquid/websockets.py +592 -0
- walrasquant/exchange/okx/__init__.py +25 -0
- walrasquant/exchange/okx/connector.py +931 -0
- walrasquant/exchange/okx/constants.py +518 -0
- walrasquant/exchange/okx/ems.py +144 -0
- walrasquant/exchange/okx/error.py +66 -0
- walrasquant/exchange/okx/exchange.py +102 -0
- walrasquant/exchange/okx/factory.py +138 -0
- walrasquant/exchange/okx/oms.py +1199 -0
- walrasquant/exchange/okx/rest_api.py +799 -0
- walrasquant/exchange/okx/schema.py +1449 -0
- walrasquant/exchange/okx/websockets.py +420 -0
- walrasquant/exchange/registry.py +201 -0
- walrasquant/execution/__init__.py +24 -0
- walrasquant/execution/algorithm.py +968 -0
- walrasquant/execution/algorithms/__init__.py +3 -0
- walrasquant/execution/algorithms/twap.py +392 -0
- walrasquant/execution/config.py +34 -0
- walrasquant/execution/constants.py +27 -0
- walrasquant/execution/schema.py +62 -0
- walrasquant/indicator.py +382 -0
- walrasquant/push.py +77 -0
- walrasquant/schema.py +755 -0
- walrasquant/strategy.py +1805 -0
- walrasquant/tools/__init__.py +0 -0
- walrasquant/tools/pm2_wrapper.py +1016 -0
- walrasquant/web/__init__.py +26 -0
- walrasquant/web/app.py +157 -0
- walrasquant/web/server.py +92 -0
- walrasquant_lib-0.4.20.dist-info/METADATA +162 -0
- walrasquant_lib-0.4.20.dist-info/RECORD +105 -0
- walrasquant_lib-0.4.20.dist-info/WHEEL +4 -0
- walrasquant_lib-0.4.20.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,867 @@
|
|
|
1
|
+
import msgspec
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
from typing import Final
|
|
4
|
+
from typing import Dict, Any, Generic, TypeVar, List
|
|
5
|
+
from walrasquant.schema import BaseMarket, Balance, BookOrderData
|
|
6
|
+
from walrasquant.exchange.bybit.constants import (
|
|
7
|
+
BybitProductType,
|
|
8
|
+
BybitOrderSide,
|
|
9
|
+
BybitOrderType,
|
|
10
|
+
BybitTimeInForce,
|
|
11
|
+
BybitOrderStatus,
|
|
12
|
+
BybitTriggerType,
|
|
13
|
+
BybitStopOrderType,
|
|
14
|
+
BybitTriggerDirection,
|
|
15
|
+
BybitPositionIdx,
|
|
16
|
+
BybitPositionSide,
|
|
17
|
+
BybitOpType,
|
|
18
|
+
BybitKlineInterval,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
BYBIT_PONG: Final[str] = "pong"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class BybitKlineResponseArray(msgspec.Struct, array_like=True):
|
|
26
|
+
startTime: str
|
|
27
|
+
openPrice: str
|
|
28
|
+
highPrice: str
|
|
29
|
+
lowPrice: str
|
|
30
|
+
closePrice: str
|
|
31
|
+
volume: str
|
|
32
|
+
turnover: str
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class BybitKlineResponseResult(msgspec.Struct):
|
|
36
|
+
symbol: str
|
|
37
|
+
category: BybitProductType
|
|
38
|
+
list: list[BybitKlineResponseArray]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class BybitKlineResponse(msgspec.Struct):
|
|
42
|
+
retCode: int
|
|
43
|
+
retMsg: str
|
|
44
|
+
result: BybitKlineResponseResult
|
|
45
|
+
time: int
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class BybitIndexKlineResponseArray(msgspec.Struct, array_like=True):
|
|
49
|
+
startTime: str
|
|
50
|
+
openPrice: str
|
|
51
|
+
highPrice: str
|
|
52
|
+
lowPrice: str
|
|
53
|
+
closePrice: str
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class BybitIndexKlineResponseResult(msgspec.Struct):
|
|
57
|
+
symbol: str
|
|
58
|
+
category: BybitProductType
|
|
59
|
+
list: list[BybitIndexKlineResponseArray]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class BybitIndexKlineResponse(msgspec.Struct):
|
|
63
|
+
retCode: int
|
|
64
|
+
retMsg: str
|
|
65
|
+
result: BybitIndexKlineResponseResult
|
|
66
|
+
time: int
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class BybitWsKline(msgspec.Struct):
|
|
70
|
+
start: int
|
|
71
|
+
end: int
|
|
72
|
+
interval: BybitKlineInterval
|
|
73
|
+
open: str
|
|
74
|
+
close: str
|
|
75
|
+
high: str
|
|
76
|
+
low: str
|
|
77
|
+
volume: str
|
|
78
|
+
turnover: str
|
|
79
|
+
confirm: bool
|
|
80
|
+
timestamp: int
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class BybitWsKlineMsg(msgspec.Struct):
|
|
84
|
+
# Topic name
|
|
85
|
+
topic: str
|
|
86
|
+
ts: int
|
|
87
|
+
type: str
|
|
88
|
+
data: list[BybitWsKline]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class BybitOrder(msgspec.Struct, omit_defaults=True, kw_only=True):
|
|
92
|
+
orderId: str
|
|
93
|
+
orderLinkId: str
|
|
94
|
+
blockTradeId: str | None = None
|
|
95
|
+
symbol: str
|
|
96
|
+
price: str
|
|
97
|
+
qty: str
|
|
98
|
+
side: BybitOrderSide
|
|
99
|
+
isLeverage: str
|
|
100
|
+
positionIdx: int
|
|
101
|
+
orderStatus: BybitOrderStatus
|
|
102
|
+
cancelType: str
|
|
103
|
+
rejectReason: str
|
|
104
|
+
avgPrice: str | None = None
|
|
105
|
+
leavesQty: str
|
|
106
|
+
leavesValue: str
|
|
107
|
+
cumExecQty: str
|
|
108
|
+
cumExecValue: str
|
|
109
|
+
cumExecFee: str
|
|
110
|
+
timeInForce: BybitTimeInForce
|
|
111
|
+
orderType: BybitOrderType
|
|
112
|
+
stopOrderType: BybitStopOrderType
|
|
113
|
+
orderIv: str
|
|
114
|
+
triggerPrice: str
|
|
115
|
+
takeProfit: str
|
|
116
|
+
stopLoss: str
|
|
117
|
+
tpTriggerBy: str
|
|
118
|
+
slTriggerBy: str
|
|
119
|
+
triggerDirection: BybitTriggerDirection = BybitTriggerDirection.NONE
|
|
120
|
+
triggerBy: BybitTriggerType
|
|
121
|
+
lastPriceOnCreated: str
|
|
122
|
+
reduceOnly: bool
|
|
123
|
+
closeOnTrigger: bool
|
|
124
|
+
smpType: str
|
|
125
|
+
smpGroup: int
|
|
126
|
+
smpOrderId: str
|
|
127
|
+
tpslMode: str | None = None
|
|
128
|
+
tpLimitPrice: str
|
|
129
|
+
slLimitPrice: str
|
|
130
|
+
placeType: str
|
|
131
|
+
createdTime: str
|
|
132
|
+
updatedTime: str
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class BybitOrderResult(msgspec.Struct):
|
|
136
|
+
orderId: str
|
|
137
|
+
orderLinkId: str
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class BybitOrderResponse(msgspec.Struct):
|
|
141
|
+
retCode: int
|
|
142
|
+
retMsg: str
|
|
143
|
+
result: BybitOrderResult
|
|
144
|
+
time: int
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class BybitBatchOrderResult(msgspec.Struct):
|
|
148
|
+
category: str
|
|
149
|
+
symbol: str
|
|
150
|
+
orderId: str
|
|
151
|
+
orderLinkId: str
|
|
152
|
+
createAt: str
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class BybitBatchOrderExtInfo(msgspec.Struct):
|
|
156
|
+
code: int
|
|
157
|
+
msg: str
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class BybitPositionStruct(msgspec.Struct, kw_only=True):
|
|
161
|
+
positionIdx: BybitPositionIdx
|
|
162
|
+
riskId: int
|
|
163
|
+
riskLimitValue: str
|
|
164
|
+
symbol: str
|
|
165
|
+
side: BybitPositionSide
|
|
166
|
+
size: str
|
|
167
|
+
avgPrice: str
|
|
168
|
+
positionValue: str
|
|
169
|
+
tradeMode: int
|
|
170
|
+
positionStatus: str
|
|
171
|
+
autoAddMargin: int
|
|
172
|
+
adlRankIndicator: int
|
|
173
|
+
leverage: str
|
|
174
|
+
positionBalance: str
|
|
175
|
+
markPrice: str
|
|
176
|
+
liqPrice: str
|
|
177
|
+
bustPrice: str
|
|
178
|
+
positionMM: str
|
|
179
|
+
positionIM: str
|
|
180
|
+
takeProfit: str
|
|
181
|
+
stopLoss: str
|
|
182
|
+
trailingStop: str
|
|
183
|
+
unrealisedPnl: str
|
|
184
|
+
cumRealisedPnl: str
|
|
185
|
+
createdTime: str
|
|
186
|
+
updatedTime: str
|
|
187
|
+
tpslMode: str | None = None
|
|
188
|
+
delta: str | None = None
|
|
189
|
+
gamma: str | None = None
|
|
190
|
+
vega: str | None = None
|
|
191
|
+
theta: str | None = None
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
T = TypeVar("T")
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class BybitListResult(Generic[T], msgspec.Struct):
|
|
198
|
+
list: list[T]
|
|
199
|
+
nextPageCursor: str | None = None
|
|
200
|
+
category: BybitProductType | None = None
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class BybitPositionResponse(msgspec.Struct):
|
|
204
|
+
retCode: int
|
|
205
|
+
retMsg: str
|
|
206
|
+
result: BybitListResult[BybitPositionStruct]
|
|
207
|
+
time: int
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class BybitOrderHistoryResponse(msgspec.Struct):
|
|
211
|
+
retCode: int
|
|
212
|
+
retMsg: str
|
|
213
|
+
result: BybitListResult[BybitOrder]
|
|
214
|
+
time: int
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class BybitOpenOrdersResponse(msgspec.Struct):
|
|
218
|
+
retCode: int
|
|
219
|
+
retMsg: str
|
|
220
|
+
result: BybitListResult[BybitOrder]
|
|
221
|
+
time: int
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class BybitResponse(msgspec.Struct, frozen=True):
|
|
225
|
+
retCode: int
|
|
226
|
+
retMsg: str
|
|
227
|
+
result: Dict[str, Any]
|
|
228
|
+
time: int
|
|
229
|
+
retExtInfo: Dict[str, Any] | None = None
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
class BybitWsApiGeneralMsg(msgspec.Struct):
|
|
233
|
+
retCode: int
|
|
234
|
+
op: BybitOpType
|
|
235
|
+
retMsg: str
|
|
236
|
+
|
|
237
|
+
@property
|
|
238
|
+
def is_success(self):
|
|
239
|
+
return self.retCode == 0
|
|
240
|
+
|
|
241
|
+
@property
|
|
242
|
+
def error_msg(self):
|
|
243
|
+
return f"code={self.retCode}, msg={self.retMsg}"
|
|
244
|
+
|
|
245
|
+
@property
|
|
246
|
+
def is_auth(self):
|
|
247
|
+
return self.op == BybitOpType.AUTH
|
|
248
|
+
|
|
249
|
+
@property
|
|
250
|
+
def is_ping(self):
|
|
251
|
+
return self.op == BybitOpType.PING
|
|
252
|
+
|
|
253
|
+
@property
|
|
254
|
+
def is_pong(self):
|
|
255
|
+
return self.op == BybitOpType.PONG
|
|
256
|
+
|
|
257
|
+
@property
|
|
258
|
+
def is_order_create(self):
|
|
259
|
+
return self.op == BybitOpType.ORDER_CREATE
|
|
260
|
+
|
|
261
|
+
@property
|
|
262
|
+
def is_order_amend(self):
|
|
263
|
+
return self.op == BybitOpType.ORDER_AMEND
|
|
264
|
+
|
|
265
|
+
@property
|
|
266
|
+
def is_order_cancel(self):
|
|
267
|
+
return self.op == BybitOpType.ORDER_CANCEL
|
|
268
|
+
|
|
269
|
+
@property
|
|
270
|
+
def is_order_create_batch(self):
|
|
271
|
+
return self.op == BybitOpType.ORDER_CREATE_BATCH
|
|
272
|
+
|
|
273
|
+
@property
|
|
274
|
+
def is_order_amend_batch(self):
|
|
275
|
+
return self.op == BybitOpType.ORDER_AMEND_BATCH
|
|
276
|
+
|
|
277
|
+
@property
|
|
278
|
+
def is_order_cancel_batch(self):
|
|
279
|
+
return self.op == BybitOpType.ORDER_CANCEL_BATCH
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
class BybitWsApiOrderMsgData(msgspec.Struct):
|
|
283
|
+
orderId: str | None = None
|
|
284
|
+
orderLinkId: str | None = None
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
class BybitWsApiOrderMsg(msgspec.Struct):
|
|
288
|
+
reqId: str
|
|
289
|
+
retCode: int
|
|
290
|
+
retMsg: str
|
|
291
|
+
data: BybitWsApiOrderMsgData | None = None
|
|
292
|
+
|
|
293
|
+
@property
|
|
294
|
+
def oid(self):
|
|
295
|
+
return self.reqId[1:].rsplit(".", 1)[0] # strip prefix and reqId nonce
|
|
296
|
+
|
|
297
|
+
@property
|
|
298
|
+
def is_success(self):
|
|
299
|
+
return self.retCode == 0
|
|
300
|
+
|
|
301
|
+
@property
|
|
302
|
+
def error_msg(self):
|
|
303
|
+
return f"code={self.retCode}, msg={self.retMsg}"
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
class BybitWsMessageGeneral(msgspec.Struct):
|
|
307
|
+
success: bool | None = None
|
|
308
|
+
conn_id: str = ""
|
|
309
|
+
op: str = ""
|
|
310
|
+
topic: str = ""
|
|
311
|
+
ret_msg: str = ""
|
|
312
|
+
args: list[str] = []
|
|
313
|
+
|
|
314
|
+
@property
|
|
315
|
+
def is_pong(self):
|
|
316
|
+
# NOTE: for private ws, pong message has 'ret_msg' is None, the 'op' is 'pong'
|
|
317
|
+
return self.ret_msg == BYBIT_PONG or self.op == BYBIT_PONG
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
class BybitWsOrderbookDepth(msgspec.Struct):
|
|
321
|
+
# symbol
|
|
322
|
+
s: str
|
|
323
|
+
# bids
|
|
324
|
+
b: list[list[str]]
|
|
325
|
+
# asks
|
|
326
|
+
a: list[list[str]]
|
|
327
|
+
# Update ID. Is a sequence. Occasionally, you'll receive "u"=1, which is a
|
|
328
|
+
# snapshot data due to the restart of the service.
|
|
329
|
+
u: int
|
|
330
|
+
# Cross sequence
|
|
331
|
+
seq: int
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
class BybitWsOrderbookDepthMsg(msgspec.Struct):
|
|
335
|
+
topic: str
|
|
336
|
+
type: str
|
|
337
|
+
ts: int
|
|
338
|
+
data: BybitWsOrderbookDepth
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
class BybitOrderBook(msgspec.Struct):
|
|
342
|
+
bids: Dict[
|
|
343
|
+
float, float
|
|
344
|
+
] = {} # key: price, value: size when sorted return (price, size)
|
|
345
|
+
asks: Dict[float, float] = {}
|
|
346
|
+
|
|
347
|
+
def parse_orderbook_depth(self, msg: BybitWsOrderbookDepthMsg, levels: int = 1):
|
|
348
|
+
if msg.type == "snapshot":
|
|
349
|
+
self._handle_snapshot(msg.data)
|
|
350
|
+
elif msg.type == "delta":
|
|
351
|
+
self._handle_delta(msg.data)
|
|
352
|
+
return self._get_orderbook(levels)
|
|
353
|
+
|
|
354
|
+
def _handle_snapshot(self, data: BybitWsOrderbookDepth) -> None:
|
|
355
|
+
if data.b:
|
|
356
|
+
self.bids.clear()
|
|
357
|
+
if data.a:
|
|
358
|
+
self.asks.clear()
|
|
359
|
+
|
|
360
|
+
for price, size in data.b:
|
|
361
|
+
self.bids[float(price)] = float(size)
|
|
362
|
+
|
|
363
|
+
for price, size in data.a:
|
|
364
|
+
self.asks[float(price)] = float(size)
|
|
365
|
+
|
|
366
|
+
def _handle_delta(self, data: BybitWsOrderbookDepth) -> None:
|
|
367
|
+
for price, size in data.b:
|
|
368
|
+
if float(size) == 0:
|
|
369
|
+
self.bids.pop(float(price))
|
|
370
|
+
else:
|
|
371
|
+
self.bids[float(price)] = float(size)
|
|
372
|
+
|
|
373
|
+
for price, size in data.a:
|
|
374
|
+
if float(size) == 0:
|
|
375
|
+
self.asks.pop(float(price))
|
|
376
|
+
else:
|
|
377
|
+
self.asks[float(price)] = float(size)
|
|
378
|
+
|
|
379
|
+
def _get_orderbook(self, levels: int):
|
|
380
|
+
bids = sorted(self.bids.items(), reverse=True)[:levels] # bids descending
|
|
381
|
+
asks = sorted(self.asks.items())[:levels] # asks ascending
|
|
382
|
+
|
|
383
|
+
bids = [BookOrderData(price=price, size=size) for price, size in bids]
|
|
384
|
+
asks = [BookOrderData(price=price, size=size) for price, size in asks]
|
|
385
|
+
|
|
386
|
+
return {
|
|
387
|
+
"bids": bids,
|
|
388
|
+
"asks": asks,
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
class BybitWsTrade(msgspec.Struct):
|
|
393
|
+
# The timestamp (ms) that the order is filled
|
|
394
|
+
T: int
|
|
395
|
+
# Symbol name
|
|
396
|
+
s: str
|
|
397
|
+
# Side of taker. Buy,Sell
|
|
398
|
+
S: BybitOrderSide
|
|
399
|
+
# Trade size
|
|
400
|
+
v: str
|
|
401
|
+
# Trade price
|
|
402
|
+
p: str
|
|
403
|
+
# Trade id
|
|
404
|
+
i: str
|
|
405
|
+
# Whether is a block trade or not
|
|
406
|
+
BT: bool
|
|
407
|
+
# Direction of price change
|
|
408
|
+
L: str | None = None
|
|
409
|
+
# Message id unique to options
|
|
410
|
+
id: str | None = None
|
|
411
|
+
# Mark price, unique field for option
|
|
412
|
+
mP: str | None = None
|
|
413
|
+
# Index price, unique field for option
|
|
414
|
+
iP: str | None = None
|
|
415
|
+
# Mark iv, unique field for option
|
|
416
|
+
mIv: str | None = None
|
|
417
|
+
# iv, unique field for option
|
|
418
|
+
iv: str | None = None
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
class BybitWsTradeMsg(msgspec.Struct):
|
|
422
|
+
topic: str
|
|
423
|
+
type: str
|
|
424
|
+
ts: int
|
|
425
|
+
data: list[BybitWsTrade]
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
class BybitWsOrder(msgspec.Struct, kw_only=True):
|
|
429
|
+
category: BybitProductType
|
|
430
|
+
symbol: str
|
|
431
|
+
orderId: str
|
|
432
|
+
side: BybitOrderSide
|
|
433
|
+
orderType: BybitOrderType
|
|
434
|
+
cancelType: str
|
|
435
|
+
price: str
|
|
436
|
+
qty: str
|
|
437
|
+
orderIv: str
|
|
438
|
+
timeInForce: BybitTimeInForce
|
|
439
|
+
orderStatus: BybitOrderStatus
|
|
440
|
+
orderLinkId: str
|
|
441
|
+
lastPriceOnCreated: str
|
|
442
|
+
reduceOnly: bool
|
|
443
|
+
leavesQty: str
|
|
444
|
+
leavesValue: str
|
|
445
|
+
cumExecQty: str
|
|
446
|
+
cumExecValue: str
|
|
447
|
+
avgPrice: str
|
|
448
|
+
blockTradeId: str
|
|
449
|
+
positionIdx: BybitPositionIdx
|
|
450
|
+
cumExecFee: str
|
|
451
|
+
createdTime: str
|
|
452
|
+
updatedTime: str
|
|
453
|
+
rejectReason: str
|
|
454
|
+
triggerPrice: str
|
|
455
|
+
takeProfit: str
|
|
456
|
+
stopLoss: str
|
|
457
|
+
tpTriggerBy: str
|
|
458
|
+
slTriggerBy: str
|
|
459
|
+
tpLimitPrice: str
|
|
460
|
+
slLimitPrice: str
|
|
461
|
+
closeOnTrigger: bool
|
|
462
|
+
placeType: str
|
|
463
|
+
smpType: str
|
|
464
|
+
smpGroup: int
|
|
465
|
+
smpOrderId: str
|
|
466
|
+
feeCurrency: str | None = None
|
|
467
|
+
triggerBy: BybitTriggerType
|
|
468
|
+
stopOrderType: BybitStopOrderType
|
|
469
|
+
triggerDirection: BybitTriggerDirection = BybitTriggerDirection.NONE
|
|
470
|
+
tpslMode: str | None = None
|
|
471
|
+
createType: str | None = None
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
class BybitWsOrderMsg(msgspec.Struct):
|
|
475
|
+
topic: str
|
|
476
|
+
id: str
|
|
477
|
+
creationTime: int
|
|
478
|
+
data: list[BybitWsOrder]
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
class BybitLotSizeFilter(msgspec.Struct):
|
|
482
|
+
basePrecision: str | None = None
|
|
483
|
+
quotePrecision: str | None = None
|
|
484
|
+
minOrderQty: str | None = None
|
|
485
|
+
maxOrderQty: str | None = None
|
|
486
|
+
minOrderAmt: str | None = None
|
|
487
|
+
maxOrderAmt: str | None = None
|
|
488
|
+
qtyStep: str | None = None
|
|
489
|
+
postOnlyMaxOrderQty: str | None = None
|
|
490
|
+
maxMktOrderQty: str | None = None
|
|
491
|
+
minNotionalValue: str | None = None
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
class BybitPriceFilter(msgspec.Struct):
|
|
495
|
+
minPrice: str | None = None
|
|
496
|
+
maxPrice: str | None = None
|
|
497
|
+
tickSize: str | None = None
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
class BybitRiskParameters(msgspec.Struct):
|
|
501
|
+
limitParameter: str | None = None
|
|
502
|
+
marketParameter: str | None = None
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
class BybitLeverageFilter(msgspec.Struct):
|
|
506
|
+
minLeverage: str | None = None
|
|
507
|
+
maxLeverage: str | None = None
|
|
508
|
+
leverageStep: str | None = None
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
class BybitMarketInfo(msgspec.Struct):
|
|
512
|
+
symbol: str
|
|
513
|
+
baseCoin: str
|
|
514
|
+
quoteCoin: str
|
|
515
|
+
innovation: str | None = None
|
|
516
|
+
status: str | None = None
|
|
517
|
+
marginTrading: str | None = None
|
|
518
|
+
lotSizeFilter: BybitLotSizeFilter | None = None
|
|
519
|
+
priceFilter: BybitPriceFilter | None = None
|
|
520
|
+
riskParameters: BybitRiskParameters | None = None
|
|
521
|
+
settleCoin: str | None = None
|
|
522
|
+
optionsType: str | None = None
|
|
523
|
+
launchTime: str | None = None
|
|
524
|
+
deliveryTime: str | None = None
|
|
525
|
+
deliveryFeeRate: str | None = None
|
|
526
|
+
contractType: str | None = None
|
|
527
|
+
priceScale: str | None = None
|
|
528
|
+
leverageFilter: BybitLeverageFilter | None = None
|
|
529
|
+
unifiedMarginTrade: bool | None = None
|
|
530
|
+
fundingInterval: str | int | None = None
|
|
531
|
+
copyTrading: str | None = None
|
|
532
|
+
upperFundingRate: str | None = None
|
|
533
|
+
lowerFundingRate: str | None = None
|
|
534
|
+
isPreListing: bool | None = None
|
|
535
|
+
preListingInfo: dict | None = None
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
class BybitMarket(BaseMarket):
|
|
539
|
+
info: BybitMarketInfo
|
|
540
|
+
feeSide: str
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
class BybitCoinBalance(msgspec.Struct):
|
|
544
|
+
availableToBorrow: str
|
|
545
|
+
bonus: str
|
|
546
|
+
accruedInterest: str
|
|
547
|
+
availableToWithdraw: str
|
|
548
|
+
totalOrderIM: str
|
|
549
|
+
equity: str
|
|
550
|
+
usdValue: str
|
|
551
|
+
borrowAmount: str
|
|
552
|
+
# Sum of maintenance margin for all positions.
|
|
553
|
+
totalPositionMM: str
|
|
554
|
+
# Sum of initial margin of all positions + Pre-occupied liquidation fee.
|
|
555
|
+
totalPositionIM: str
|
|
556
|
+
walletBalance: str
|
|
557
|
+
# Unrealised P&L
|
|
558
|
+
unrealisedPnl: str
|
|
559
|
+
# Cumulative Realised P&L
|
|
560
|
+
cumRealisedPnl: str
|
|
561
|
+
locked: str
|
|
562
|
+
# Whether it can be used as a margin collateral currency (platform)
|
|
563
|
+
collateralSwitch: bool
|
|
564
|
+
# Whether the collateral is turned on by the user
|
|
565
|
+
marginCollateral: bool
|
|
566
|
+
coin: str
|
|
567
|
+
|
|
568
|
+
def parse_to_balance(self) -> Balance:
|
|
569
|
+
locked = Decimal(self.locked)
|
|
570
|
+
free = Decimal(self.walletBalance) - locked
|
|
571
|
+
return Balance(
|
|
572
|
+
asset=self.coin,
|
|
573
|
+
locked=locked,
|
|
574
|
+
free=free,
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
class BybitWalletBalance(msgspec.Struct):
|
|
579
|
+
totalEquity: str
|
|
580
|
+
accountIMRate: str
|
|
581
|
+
totalMarginBalance: str
|
|
582
|
+
totalInitialMargin: str
|
|
583
|
+
accountType: str
|
|
584
|
+
totalAvailableBalance: str
|
|
585
|
+
accountMMRate: str
|
|
586
|
+
totalPerpUPL: str
|
|
587
|
+
totalWalletBalance: str
|
|
588
|
+
accountLTV: str
|
|
589
|
+
totalMaintenanceMargin: str
|
|
590
|
+
coin: list[BybitCoinBalance]
|
|
591
|
+
|
|
592
|
+
def parse_to_balances(self) -> list[Balance]:
|
|
593
|
+
return [coin.parse_to_balance() for coin in self.coin]
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
class BybitWalletBalanceResponse(msgspec.Struct):
|
|
597
|
+
retCode: int
|
|
598
|
+
retMsg: str
|
|
599
|
+
result: BybitListResult[BybitWalletBalance]
|
|
600
|
+
time: int
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
class BybitWsAccountWalletCoin(msgspec.Struct):
|
|
604
|
+
coin: str
|
|
605
|
+
equity: str
|
|
606
|
+
usdValue: str
|
|
607
|
+
walletBalance: str
|
|
608
|
+
availableToWithdraw: str
|
|
609
|
+
availableToBorrow: str
|
|
610
|
+
borrowAmount: str
|
|
611
|
+
accruedInterest: str
|
|
612
|
+
totalOrderIM: str
|
|
613
|
+
totalPositionIM: str
|
|
614
|
+
totalPositionMM: str
|
|
615
|
+
unrealisedPnl: str
|
|
616
|
+
cumRealisedPnl: str
|
|
617
|
+
bonus: str
|
|
618
|
+
collateralSwitch: bool
|
|
619
|
+
marginCollateral: bool
|
|
620
|
+
locked: str
|
|
621
|
+
spotHedgingQty: str
|
|
622
|
+
|
|
623
|
+
def parse_to_balance(self) -> Balance:
|
|
624
|
+
total = Decimal(self.walletBalance)
|
|
625
|
+
locked = Decimal(self.locked) # TODO: Locked only valid for Spot
|
|
626
|
+
free = total - locked
|
|
627
|
+
return Balance(
|
|
628
|
+
asset=self.coin,
|
|
629
|
+
locked=locked,
|
|
630
|
+
free=free,
|
|
631
|
+
)
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
class BybitWsAccountWallet(msgspec.Struct):
|
|
635
|
+
accountIMRate: str
|
|
636
|
+
accountMMRate: str
|
|
637
|
+
totalEquity: str
|
|
638
|
+
totalWalletBalance: str
|
|
639
|
+
totalMarginBalance: str
|
|
640
|
+
totalAvailableBalance: str
|
|
641
|
+
totalPerpUPL: str
|
|
642
|
+
totalInitialMargin: str
|
|
643
|
+
totalMaintenanceMargin: str
|
|
644
|
+
coin: List[BybitWsAccountWalletCoin]
|
|
645
|
+
accountLTV: str
|
|
646
|
+
accountType: str
|
|
647
|
+
|
|
648
|
+
def parse_to_balances(self) -> list[Balance]:
|
|
649
|
+
return [coin.parse_to_balance() for coin in self.coin]
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
class BybitWsAccountWalletMsg(msgspec.Struct):
|
|
653
|
+
topic: str
|
|
654
|
+
id: str
|
|
655
|
+
creationTime: int
|
|
656
|
+
data: List[BybitWsAccountWallet]
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
class BybitWsPosition(msgspec.Struct, kw_only=True):
|
|
660
|
+
category: BybitProductType
|
|
661
|
+
symbol: str
|
|
662
|
+
side: BybitPositionSide
|
|
663
|
+
size: str
|
|
664
|
+
positionIdx: int
|
|
665
|
+
tradeMode: int
|
|
666
|
+
positionValue: str
|
|
667
|
+
riskId: int
|
|
668
|
+
riskLimitValue: str
|
|
669
|
+
entryPrice: str
|
|
670
|
+
markPrice: str
|
|
671
|
+
leverage: str
|
|
672
|
+
positionBalance: str
|
|
673
|
+
autoAddMargin: int
|
|
674
|
+
positionIM: str
|
|
675
|
+
positionMM: str
|
|
676
|
+
liqPrice: str
|
|
677
|
+
bustPrice: str
|
|
678
|
+
tpslMode: str
|
|
679
|
+
takeProfit: str
|
|
680
|
+
stopLoss: str
|
|
681
|
+
trailingStop: str
|
|
682
|
+
unrealisedPnl: str
|
|
683
|
+
curRealisedPnl: str
|
|
684
|
+
sessionAvgPrice: str
|
|
685
|
+
delta: str | None = None
|
|
686
|
+
gamma: str | None = None
|
|
687
|
+
vega: str | None = None
|
|
688
|
+
theta: str | None = None
|
|
689
|
+
cumRealisedPnl: str
|
|
690
|
+
positionStatus: str
|
|
691
|
+
adlRankIndicator: int
|
|
692
|
+
isReduceOnly: bool
|
|
693
|
+
mmrSysUpdatedTime: str
|
|
694
|
+
leverageSysUpdatedTime: str
|
|
695
|
+
createdTime: str
|
|
696
|
+
updatedTime: str
|
|
697
|
+
seq: int
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
class BybitWsPositionMsg(msgspec.Struct):
|
|
701
|
+
topic: str
|
|
702
|
+
id: str
|
|
703
|
+
creationTime: int
|
|
704
|
+
data: List[BybitWsPosition]
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
class BybitWsTickerMsg(msgspec.Struct):
|
|
708
|
+
topic: str
|
|
709
|
+
type: str
|
|
710
|
+
ts: int
|
|
711
|
+
data: "BybitWsTicker"
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
class BybitWsTicker(msgspec.Struct, kw_only=True):
|
|
715
|
+
symbol: str
|
|
716
|
+
tickDirection: str | None = None
|
|
717
|
+
price24hPcnt: str | None = None
|
|
718
|
+
lastPrice: str | None = None
|
|
719
|
+
prevPrice24h: str | None = None
|
|
720
|
+
highPrice24h: str | None = None
|
|
721
|
+
lowPrice24h: str | None = None
|
|
722
|
+
prevPrice1h: str | None = None
|
|
723
|
+
markPrice: str | None = None
|
|
724
|
+
indexPrice: str | None = None
|
|
725
|
+
openInterest: str | None = None
|
|
726
|
+
openInterestValue: str | None = None
|
|
727
|
+
turnover24h: str | None = None
|
|
728
|
+
volume24h: str | None = None
|
|
729
|
+
nextFundingTime: str | None = None
|
|
730
|
+
fundingRate: str | None = None
|
|
731
|
+
bid1Price: str | None = None
|
|
732
|
+
bid1Size: str | None = None
|
|
733
|
+
ask1Price: str | None = None
|
|
734
|
+
ask1Size: str | None = None
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
class BybitTicker(msgspec.Struct):
|
|
738
|
+
symbol: str | None = None
|
|
739
|
+
markPrice: str | None = None
|
|
740
|
+
indexPrice: str | None = None
|
|
741
|
+
nextFundingTime: str | None = None
|
|
742
|
+
fundingRate: str | None = None
|
|
743
|
+
|
|
744
|
+
def parse_ticker(self, msg: BybitWsTickerMsg):
|
|
745
|
+
if msg.type == "snapshot":
|
|
746
|
+
self.handle_snapshot(msg.data)
|
|
747
|
+
elif msg.type == "delta":
|
|
748
|
+
self.handle_delta(msg.data)
|
|
749
|
+
return self
|
|
750
|
+
|
|
751
|
+
def handle_snapshot(self, data: "BybitWsTicker"):
|
|
752
|
+
self.symbol = data.symbol
|
|
753
|
+
self.markPrice = data.markPrice
|
|
754
|
+
self.indexPrice = data.indexPrice
|
|
755
|
+
self.nextFundingTime = data.nextFundingTime
|
|
756
|
+
self.fundingRate = data.fundingRate
|
|
757
|
+
|
|
758
|
+
def handle_delta(self, data: "BybitWsTicker"):
|
|
759
|
+
# For delta updates, only update fields that aren't None
|
|
760
|
+
if data.markPrice is not None:
|
|
761
|
+
self.markPrice = data.markPrice
|
|
762
|
+
if data.indexPrice is not None:
|
|
763
|
+
self.indexPrice = data.indexPrice
|
|
764
|
+
if data.nextFundingTime is not None:
|
|
765
|
+
self.nextFundingTime = data.nextFundingTime
|
|
766
|
+
if data.fundingRate is not None:
|
|
767
|
+
self.fundingRate = data.fundingRate
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
class BybitBatchOrderResponse(msgspec.Struct):
|
|
771
|
+
retCode: int
|
|
772
|
+
retMsg: str
|
|
773
|
+
result: BybitListResult[BybitBatchOrderResult]
|
|
774
|
+
retExtInfo: BybitListResult[BybitBatchOrderExtInfo]
|
|
775
|
+
time: int
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
class BybitBatchCancelOrderResult(msgspec.Struct):
|
|
779
|
+
category: str
|
|
780
|
+
symbol: str
|
|
781
|
+
orderId: str
|
|
782
|
+
orderLinkId: str
|
|
783
|
+
|
|
784
|
+
|
|
785
|
+
class BybitBatchCancelOrderResponse(msgspec.Struct):
|
|
786
|
+
retCode: int
|
|
787
|
+
retMsg: str
|
|
788
|
+
result: BybitListResult[BybitBatchCancelOrderResult]
|
|
789
|
+
retExtInfo: BybitListResult[BybitBatchOrderExtInfo]
|
|
790
|
+
time: int
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
################################################################################
|
|
794
|
+
# GET /v5/market/tickers
|
|
795
|
+
################################################################################
|
|
796
|
+
|
|
797
|
+
|
|
798
|
+
class BybitTickerData(msgspec.Struct):
|
|
799
|
+
"""
|
|
800
|
+
Ticker data structure for Bybit market tickers.
|
|
801
|
+
Supports all product types: spot, linear, inverse, option.
|
|
802
|
+
"""
|
|
803
|
+
|
|
804
|
+
symbol: str # Symbol name
|
|
805
|
+
lastPrice: str # Last price
|
|
806
|
+
bid1Price: str # Best bid price
|
|
807
|
+
bid1Size: str # Best bid size
|
|
808
|
+
ask1Price: str # Best ask price
|
|
809
|
+
ask1Size: str # Best ask size
|
|
810
|
+
prevPrice24h: str # Market price 24 hours ago
|
|
811
|
+
price24hPcnt: str # Percentage change of market price relative to 24h
|
|
812
|
+
highPrice24h: str # The highest price in the last 24 hours
|
|
813
|
+
lowPrice24h: str # The lowest price in the last 24 hours
|
|
814
|
+
turnover24h: str # Turnover for 24h
|
|
815
|
+
volume24h: str # Volume for 24h
|
|
816
|
+
|
|
817
|
+
# Optional fields for different product types
|
|
818
|
+
indexPrice: str | None = None # Index price
|
|
819
|
+
markPrice: str | None = None # Mark price
|
|
820
|
+
prevPrice1h: str | None = None # Market price an hour ago
|
|
821
|
+
openInterest: str | None = None # Open interest size
|
|
822
|
+
openInterestValue: str | None = None # Open interest value
|
|
823
|
+
fundingRate: str | None = None # Funding rate
|
|
824
|
+
nextFundingTime: str | None = None # Next funding time (ms)
|
|
825
|
+
predictedDeliveryPrice: str | None = None # Predicted delivery price
|
|
826
|
+
basisRate: str | None = None # Basis rate
|
|
827
|
+
basis: str | None = None # Basis
|
|
828
|
+
deliveryFeeRate: str | None = None # Delivery fee rate
|
|
829
|
+
deliveryTime: str | None = None # Delivery timestamp (ms)
|
|
830
|
+
preOpenPrice: str | None = None # Estimated pre-market contract open price
|
|
831
|
+
preQty: str | None = None # Estimated pre-market contract open qty
|
|
832
|
+
curPreListingPhase: str | None = None # Current pre-market contract phase
|
|
833
|
+
usdIndexPrice: str | None = None # USD index price (for spot)
|
|
834
|
+
|
|
835
|
+
# Option-specific fields
|
|
836
|
+
bid1Iv: str | None = None # Best bid iv (for options)
|
|
837
|
+
ask1Iv: str | None = None # Best ask iv (for options)
|
|
838
|
+
markIv: str | None = None # Mark price iv (for options)
|
|
839
|
+
underlyingPrice: str | None = None # Underlying price (for options)
|
|
840
|
+
totalVolume: str | None = None # Total volume
|
|
841
|
+
totalTurnover: str | None = None # Total turnover
|
|
842
|
+
delta: str | None = None # Delta (for options)
|
|
843
|
+
gamma: str | None = None # Gamma (for options)
|
|
844
|
+
vega: str | None = None # Vega (for options)
|
|
845
|
+
theta: str | None = None # Theta (for options)
|
|
846
|
+
change24h: str | None = None # Change in 24h
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
class BybitTickersResult(msgspec.Struct):
|
|
850
|
+
"""
|
|
851
|
+
Result structure for Bybit tickers response.
|
|
852
|
+
"""
|
|
853
|
+
|
|
854
|
+
category: str # Product type
|
|
855
|
+
list: list[BybitTickerData] # List of ticker data
|
|
856
|
+
|
|
857
|
+
|
|
858
|
+
class BybitTickersResponse(msgspec.Struct, kw_only=True):
|
|
859
|
+
"""
|
|
860
|
+
Response structure for GET /v5/market/tickers.
|
|
861
|
+
"""
|
|
862
|
+
|
|
863
|
+
retCode: int # Return code
|
|
864
|
+
retMsg: str # Return message
|
|
865
|
+
result: BybitTickersResult # Result data
|
|
866
|
+
retExtInfo: dict[str, Any] | None = None # Extended info
|
|
867
|
+
time: int # Response timestamp
|