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.
Files changed (105) hide show
  1. walrasquant/__init__.py +7 -0
  2. walrasquant/aggregation.py +449 -0
  3. walrasquant/backends/__init__.py +5 -0
  4. walrasquant/backends/db.py +109 -0
  5. walrasquant/backends/db_memory.py +61 -0
  6. walrasquant/backends/db_postgresql.py +321 -0
  7. walrasquant/backends/db_sqlite.py +310 -0
  8. walrasquant/base/__init__.py +24 -0
  9. walrasquant/base/api_client.py +46 -0
  10. walrasquant/base/connector.py +863 -0
  11. walrasquant/base/ems.py +794 -0
  12. walrasquant/base/exchange.py +213 -0
  13. walrasquant/base/oms.py +428 -0
  14. walrasquant/base/retry.py +220 -0
  15. walrasquant/base/sms.py +545 -0
  16. walrasquant/base/ws_client.py +408 -0
  17. walrasquant/config.py +284 -0
  18. walrasquant/constants.py +413 -0
  19. walrasquant/core/__init__.py +0 -0
  20. walrasquant/core/cache.py +688 -0
  21. walrasquant/core/clock.py +59 -0
  22. walrasquant/core/connection.py +41 -0
  23. walrasquant/core/entity.py +504 -0
  24. walrasquant/core/nautilius_core.py +103 -0
  25. walrasquant/core/registry.py +41 -0
  26. walrasquant/engine.py +745 -0
  27. walrasquant/error.py +34 -0
  28. walrasquant/exchange/__init__.py +13 -0
  29. walrasquant/exchange/base_factory.py +172 -0
  30. walrasquant/exchange/binance/__init__.py +30 -0
  31. walrasquant/exchange/binance/connector.py +1093 -0
  32. walrasquant/exchange/binance/constants.py +934 -0
  33. walrasquant/exchange/binance/ems.py +140 -0
  34. walrasquant/exchange/binance/error.py +48 -0
  35. walrasquant/exchange/binance/exchange.py +144 -0
  36. walrasquant/exchange/binance/factory.py +115 -0
  37. walrasquant/exchange/binance/oms.py +1807 -0
  38. walrasquant/exchange/binance/rest_api.py +1653 -0
  39. walrasquant/exchange/binance/schema.py +1063 -0
  40. walrasquant/exchange/binance/websockets.py +389 -0
  41. walrasquant/exchange/bitget/__init__.py +28 -0
  42. walrasquant/exchange/bitget/connector.py +578 -0
  43. walrasquant/exchange/bitget/constants.py +392 -0
  44. walrasquant/exchange/bitget/ems.py +202 -0
  45. walrasquant/exchange/bitget/error.py +36 -0
  46. walrasquant/exchange/bitget/exchange.py +128 -0
  47. walrasquant/exchange/bitget/factory.py +135 -0
  48. walrasquant/exchange/bitget/oms.py +1619 -0
  49. walrasquant/exchange/bitget/rest_api.py +610 -0
  50. walrasquant/exchange/bitget/schema.py +885 -0
  51. walrasquant/exchange/bitget/websockets.py +753 -0
  52. walrasquant/exchange/bybit/__init__.py +32 -0
  53. walrasquant/exchange/bybit/connector.py +819 -0
  54. walrasquant/exchange/bybit/constants.py +479 -0
  55. walrasquant/exchange/bybit/ems.py +93 -0
  56. walrasquant/exchange/bybit/error.py +36 -0
  57. walrasquant/exchange/bybit/exchange.py +108 -0
  58. walrasquant/exchange/bybit/factory.py +128 -0
  59. walrasquant/exchange/bybit/oms.py +1195 -0
  60. walrasquant/exchange/bybit/rest_api.py +570 -0
  61. walrasquant/exchange/bybit/schema.py +867 -0
  62. walrasquant/exchange/bybit/websockets.py +307 -0
  63. walrasquant/exchange/hyperliquid/__init__.py +28 -0
  64. walrasquant/exchange/hyperliquid/connector.py +370 -0
  65. walrasquant/exchange/hyperliquid/constants.py +371 -0
  66. walrasquant/exchange/hyperliquid/ems.py +156 -0
  67. walrasquant/exchange/hyperliquid/error.py +48 -0
  68. walrasquant/exchange/hyperliquid/exchange.py +120 -0
  69. walrasquant/exchange/hyperliquid/factory.py +135 -0
  70. walrasquant/exchange/hyperliquid/oms.py +1081 -0
  71. walrasquant/exchange/hyperliquid/rest_api.py +348 -0
  72. walrasquant/exchange/hyperliquid/schema.py +583 -0
  73. walrasquant/exchange/hyperliquid/websockets.py +592 -0
  74. walrasquant/exchange/okx/__init__.py +25 -0
  75. walrasquant/exchange/okx/connector.py +931 -0
  76. walrasquant/exchange/okx/constants.py +518 -0
  77. walrasquant/exchange/okx/ems.py +144 -0
  78. walrasquant/exchange/okx/error.py +66 -0
  79. walrasquant/exchange/okx/exchange.py +102 -0
  80. walrasquant/exchange/okx/factory.py +138 -0
  81. walrasquant/exchange/okx/oms.py +1199 -0
  82. walrasquant/exchange/okx/rest_api.py +799 -0
  83. walrasquant/exchange/okx/schema.py +1449 -0
  84. walrasquant/exchange/okx/websockets.py +420 -0
  85. walrasquant/exchange/registry.py +201 -0
  86. walrasquant/execution/__init__.py +24 -0
  87. walrasquant/execution/algorithm.py +968 -0
  88. walrasquant/execution/algorithms/__init__.py +3 -0
  89. walrasquant/execution/algorithms/twap.py +392 -0
  90. walrasquant/execution/config.py +34 -0
  91. walrasquant/execution/constants.py +27 -0
  92. walrasquant/execution/schema.py +62 -0
  93. walrasquant/indicator.py +382 -0
  94. walrasquant/push.py +77 -0
  95. walrasquant/schema.py +755 -0
  96. walrasquant/strategy.py +1805 -0
  97. walrasquant/tools/__init__.py +0 -0
  98. walrasquant/tools/pm2_wrapper.py +1016 -0
  99. walrasquant/web/__init__.py +26 -0
  100. walrasquant/web/app.py +157 -0
  101. walrasquant/web/server.py +92 -0
  102. walrasquant_lib-0.4.20.dist-info/METADATA +162 -0
  103. walrasquant_lib-0.4.20.dist-info/RECORD +105 -0
  104. walrasquant_lib-0.4.20.dist-info/WHEEL +4 -0
  105. 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