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,578 @@
1
+ import msgspec
2
+
3
+ # import asyncio
4
+ from typing import Dict, List
5
+ from walrasquant.base import PublicConnector, PrivateConnector
6
+ from walrasquant.config import OrderQueryConfig
7
+ from walrasquant.core.nautilius_core import MessageBus, LiveClock
8
+ from walrasquant.core.entity import TaskManager
9
+ from walrasquant.core.cache import AsyncCache
10
+ from walrasquant.core.registry import OrderRegistry
11
+ from walrasquant.schema import (
12
+ BookL1,
13
+ Trade,
14
+ Kline,
15
+ # BookL2,
16
+ # BookOrderData,
17
+ # FundingRate,
18
+ # IndexPrice,
19
+ # MarkPrice,
20
+ KlineList,
21
+ Ticker,
22
+ )
23
+ from walrasquant.constants import (
24
+ KlineInterval,
25
+ BookLevel,
26
+ )
27
+ from walrasquant.exchange.bitget.schema import (
28
+ BitgetMarket,
29
+ BitgetWsUtaGeneralMsg,
30
+ BitgetWsUtaArgMsg,
31
+ BitgetBooks1WsMsg,
32
+ BitgetWsTradeWsMsg,
33
+ BitgetWsCandleWsMsg,
34
+ )
35
+ from walrasquant.exchange.bitget.rest_api import BitgetApiClient
36
+ from walrasquant.exchange.bitget.websockets import BitgetWSClient
37
+ from walrasquant.exchange.bitget.oms import BitgetOrderManagementSystem
38
+ from walrasquant.exchange.bitget.constants import (
39
+ BitgetAccountType,
40
+ BitgetKlineInterval,
41
+ BitgetUtaInstType,
42
+ BitgetEnumParser,
43
+ BitgetInstType,
44
+ )
45
+ from walrasquant.exchange.bitget.exchange import BitgetExchangeManager
46
+
47
+
48
+ class BitgetPublicConnector(PublicConnector):
49
+ _uta_inst_type_map = {
50
+ BitgetUtaInstType.SPOT: "spot",
51
+ BitgetUtaInstType.USDC_FUTURES: "linear",
52
+ BitgetUtaInstType.USDT_FUTURES: "linear",
53
+ BitgetUtaInstType.COIN_FUTURES: "inverse",
54
+ }
55
+ _inst_type_map = {
56
+ BitgetInstType.SPOT: "spot",
57
+ BitgetInstType.USDC_FUTURES: "linear",
58
+ BitgetInstType.USDT_FUTURES: "linear",
59
+ BitgetInstType.COIN_FUTURES: "inverse",
60
+ }
61
+ _api_client: BitgetApiClient
62
+ _ws_client: BitgetWSClient
63
+ _account_type: BitgetAccountType
64
+ _market: Dict[str, BitgetMarket]
65
+
66
+ def __init__(
67
+ self,
68
+ account_type: BitgetAccountType,
69
+ exchange: BitgetExchangeManager,
70
+ msgbus: MessageBus,
71
+ clock: LiveClock,
72
+ task_manager: TaskManager,
73
+ custom_url: str | None = None,
74
+ enable_rate_limit: bool = True,
75
+ max_subscriptions_per_client: int | None = None,
76
+ max_clients: int | None = None,
77
+ ):
78
+ if not account_type.is_uta:
79
+ raise ValueError(
80
+ "Public connector only supports UTA account type."
81
+ "Please set `account_type` to `BitgetAccountType.UTA` or `BitgetAccountType.UTA_DEMO`."
82
+ )
83
+
84
+ super().__init__(
85
+ account_type=account_type,
86
+ market=exchange.market,
87
+ market_id=exchange.market_id,
88
+ exchange_id=exchange.exchange_id,
89
+ ws_client=BitgetWSClient(
90
+ account_type=account_type,
91
+ handler=self._ws_msg_handler,
92
+ clock=clock,
93
+ task_manager=task_manager,
94
+ custom_url=custom_url,
95
+ max_subscriptions_per_client=max_subscriptions_per_client,
96
+ max_clients=max_clients,
97
+ ),
98
+ clock=clock,
99
+ msgbus=msgbus,
100
+ api_client=BitgetApiClient(
101
+ clock=clock,
102
+ testnet=account_type.is_testnet,
103
+ enable_rate_limit=enable_rate_limit,
104
+ ),
105
+ task_manager=task_manager,
106
+ )
107
+ self._testnet = account_type.is_testnet
108
+ self._ws_msg_general_decoder = msgspec.json.Decoder(BitgetWsUtaGeneralMsg)
109
+ self._ws_books1_decoder = msgspec.json.Decoder(BitgetBooks1WsMsg)
110
+ self._ws_trade_decoder = msgspec.json.Decoder(BitgetWsTradeWsMsg)
111
+ self._ws_candle_decoder = msgspec.json.Decoder(BitgetWsCandleWsMsg)
112
+
113
+ def _get_inst_type(self, market: BitgetMarket):
114
+ if market.spot:
115
+ return "spot"
116
+ elif market.linear:
117
+ return "usdt-futures" if market.quote == "USDT" else "usdc-futures"
118
+ elif market.inverse:
119
+ return "coin-futures"
120
+
121
+ def _uta_inst_type_suffix(self, inst_type: BitgetUtaInstType):
122
+ return self._uta_inst_type_map[inst_type]
123
+
124
+ def _inst_type_suffix(self, inst_type: BitgetInstType):
125
+ return self._inst_type_map[inst_type]
126
+
127
+ async def request_klines(
128
+ self,
129
+ symbol: str,
130
+ interval: KlineInterval,
131
+ limit: int | None = None,
132
+ start_time: int | None = None,
133
+ end_time: int | None = None,
134
+ ) -> KlineList:
135
+ """Request klines"""
136
+ raise NotImplementedError
137
+
138
+ async def request_ticker(
139
+ self,
140
+ symbol: str,
141
+ ) -> Ticker:
142
+ """Request 24hr ticker data"""
143
+ market = self._market.get(symbol)
144
+ if not market:
145
+ raise ValueError(f"Symbol is not available: {symbol}")
146
+
147
+ if market.spot:
148
+ category = "SPOT"
149
+ elif market.linear:
150
+ category = "USDT-FUTURES" if market.quote == "USDT" else "USDC-FUTURES"
151
+ elif market.inverse:
152
+ category = "COIN-FUTURES"
153
+
154
+ res = await self._api_client.get_api_v3_market_tickers(
155
+ category=category,
156
+ symbol=symbol,
157
+ )
158
+
159
+ ticker_response = res.data[0]
160
+
161
+ ticker = Ticker(
162
+ exchange=self._exchange_id,
163
+ symbol=symbol,
164
+ last_price=float(ticker_response.lastPrice),
165
+ timestamp=self._clock.timestamp_ms(),
166
+ volume=float(ticker_response.volume24h),
167
+ volumeCcy=float(ticker_response.turnover24h),
168
+ )
169
+
170
+ return ticker
171
+
172
+ async def request_all_tickers(
173
+ self,
174
+ ) -> Dict[str, Ticker]:
175
+ """Request 24hr ticker data for multiple symbols"""
176
+ all_tickers: Dict[str, Ticker] = {}
177
+ for category in ["SPOT", "USDT-FUTURES", "COIN-FUTURES", "USDC-FUTURES"]:
178
+ res = await self._api_client.get_api_v3_market_tickers(
179
+ category=category,
180
+ )
181
+
182
+ for ticker_res in res.data:
183
+ sym_id = ticker_res.symbol
184
+ symbol = self._market_id.get(
185
+ f"{sym_id}_{self._inst_type_suffix(ticker_res.category)}"
186
+ )
187
+
188
+ if not symbol:
189
+ continue
190
+
191
+ all_tickers[symbol] = Ticker(
192
+ exchange=self._exchange_id,
193
+ symbol=symbol,
194
+ last_price=float(ticker_res.lastPrice),
195
+ timestamp=self._clock.timestamp_ms(),
196
+ volume=float(ticker_res.volume24h),
197
+ volumeCcy=float(ticker_res.turnover24h),
198
+ )
199
+ return all_tickers
200
+
201
+ async def request_index_klines(
202
+ self,
203
+ symbol: str,
204
+ interval: KlineInterval,
205
+ limit: int | None = None,
206
+ start_time: int | None = None,
207
+ end_time: int | None = None,
208
+ ) -> KlineList:
209
+ """Request index klines"""
210
+ raise NotImplementedError
211
+
212
+ def _ws_msg_handler(self, raw: bytes):
213
+ """Handle incoming WebSocket messages"""
214
+ # Process the message based on its type
215
+ # if raw == b"pong":
216
+ # self._ws_client._transport.notify_user_specific_pong_received()
217
+ # self._log.debug(f"Pong received: `{raw.decode()}`")
218
+ # return
219
+
220
+ try:
221
+ msg = self._ws_msg_general_decoder.decode(raw)
222
+ if msg.is_event_data:
223
+ self._handle_event_data(msg)
224
+ elif msg.arg is not None:
225
+ if msg.arg.topic == "books1":
226
+ self._handle_books1_data(raw, msg.arg)
227
+ elif msg.arg.topic == "publicTrade":
228
+ self._handle_trade_data(raw, msg.arg)
229
+ elif msg.arg.topic == "kline":
230
+ self._handle_candle_data(raw, msg.arg)
231
+
232
+ # print(f"Received WebSocket message: {raw}")
233
+ except msgspec.DecodeError as e:
234
+ self._log.error(f"Error decoding message: {str(raw)} {e}")
235
+
236
+ def _handle_event_data(self, msg: BitgetWsUtaGeneralMsg):
237
+ if msg.event == "subscribe" and msg.arg is not None:
238
+ arg = msg.arg
239
+ self._log.debug(f"Subscribed to {arg.message}")
240
+ elif msg.event == "error":
241
+ code = msg.code
242
+ error_msg = msg.msg
243
+ self._log.error(f"Subscribed error code={code} {error_msg}")
244
+
245
+ def _handle_candle_data(self, raw: bytes, arg: BitgetWsUtaArgMsg):
246
+ msg = self._ws_candle_decoder.decode(raw)
247
+ self._log.debug(f"Received kline data: {str(msg)}")
248
+ sym_id = f"{arg.symbol}_{self._uta_inst_type_suffix(arg.instType)}"
249
+ symbol = self._market_id[sym_id]
250
+ interval = BitgetEnumParser.parse_kline_interval(
251
+ BitgetKlineInterval(arg.interval)
252
+ )
253
+ for data in msg.data:
254
+ kline = Kline(
255
+ exchange=self._exchange_id,
256
+ symbol=symbol,
257
+ open=float(data.open),
258
+ high=float(data.high),
259
+ low=float(data.low),
260
+ close=float(data.close),
261
+ volume=float(data.volume),
262
+ quote_volume=float(data.turnover),
263
+ start=int(data.start),
264
+ timestamp=self._clock.timestamp_ms(),
265
+ interval=interval,
266
+ confirm=False, # NOTE: need to handle confirm yourself
267
+ )
268
+ self._msgbus.publish(topic="kline", msg=kline)
269
+ # self._log.debug(f"Kline update: {str(kline)}")
270
+
271
+ def _handle_trade_data(self, raw: bytes, arg: BitgetWsUtaArgMsg):
272
+ msg = self._ws_trade_decoder.decode(raw)
273
+ sym_id = f"{arg.symbol}_{self._uta_inst_type_suffix(arg.instType)}"
274
+ for data in msg.data:
275
+ trade = Trade(
276
+ exchange=self._exchange_id,
277
+ symbol=self._market_id[sym_id],
278
+ price=float(data.p),
279
+ size=float(data.v),
280
+ timestamp=int(data.T),
281
+ side=BitgetEnumParser.parse_order_side(data.S),
282
+ )
283
+ self._msgbus.publish(topic="trade", msg=trade)
284
+ # self._log.debug(f"Trade update: {str(trade)}")
285
+
286
+ def _handle_books1_data(self, raw: bytes, arg: BitgetWsUtaArgMsg):
287
+ msg = self._ws_books1_decoder.decode(raw)
288
+ sym_id = f"{arg.symbol}_{self._uta_inst_type_suffix(arg.instType)}"
289
+ symbol = self._market_id[sym_id]
290
+ for data in msg.data:
291
+ bids = data.b[0]
292
+ asks = data.a[0]
293
+ bookl1 = BookL1(
294
+ exchange=self._exchange_id,
295
+ symbol=symbol,
296
+ bid=float(bids.px),
297
+ bid_size=float(bids.sz),
298
+ ask=float(asks.px),
299
+ ask_size=float(asks.sz),
300
+ timestamp=int(data.ts),
301
+ )
302
+ self._msgbus.publish(topic="bookl1", msg=bookl1)
303
+ # self._log.debug(f"BookL1 update: {str(bookl1)}")
304
+
305
+ def subscribe_bookl1(self, symbol: str | List[str]):
306
+ symbol = symbol if isinstance(symbol, list) else [symbol]
307
+ symbols_by_inst_type = {}
308
+
309
+ for sym in symbol:
310
+ market = self._market.get(sym)
311
+ if not market:
312
+ raise ValueError(f"Symbol {sym} not found in market data.")
313
+ inst_type = self._get_inst_type(market)
314
+ if inst_type not in symbols_by_inst_type:
315
+ symbols_by_inst_type[inst_type] = []
316
+ symbols_by_inst_type[inst_type].append(market.id)
317
+
318
+ for inst_type, symbols in symbols_by_inst_type.items():
319
+ self._ws_client.subscribe_depth_v3(symbols, inst_type, "books1")
320
+
321
+ def unsubscribe_bookl1(self, symbol: str | List[str]):
322
+ symbol = symbol if isinstance(symbol, list) else [symbol]
323
+ symbols_by_inst_type = {}
324
+
325
+ for sym in symbol:
326
+ market = self._market.get(sym)
327
+ if not market:
328
+ raise ValueError(f"Symbol {sym} not found in market data.")
329
+ inst_type = self._get_inst_type(market)
330
+ if inst_type not in symbols_by_inst_type:
331
+ symbols_by_inst_type[inst_type] = []
332
+ symbols_by_inst_type[inst_type].append(market.id)
333
+
334
+ for inst_type, symbols in symbols_by_inst_type.items():
335
+ self._ws_client.unsubscribe_depth_v3(symbols, inst_type, "books1")
336
+
337
+ def subscribe_trade(self, symbol):
338
+ symbol = symbol if isinstance(symbol, list) else [symbol]
339
+ symbols_by_inst_type = {}
340
+
341
+ for sym in symbol:
342
+ market = self._market.get(sym)
343
+ if not market:
344
+ raise ValueError(f"Symbol {sym} not found in market data.")
345
+ inst_type = self._get_inst_type(market)
346
+ if inst_type not in symbols_by_inst_type:
347
+ symbols_by_inst_type[inst_type] = []
348
+ symbols_by_inst_type[inst_type].append(market.id)
349
+
350
+ for inst_type, symbols in symbols_by_inst_type.items():
351
+ self._ws_client.subscribe_trades_v3(symbols, inst_type)
352
+
353
+ def unsubscribe_trade(self, symbol):
354
+ symbol = symbol if isinstance(symbol, list) else [symbol]
355
+ symbols_by_inst_type = {}
356
+
357
+ for sym in symbol:
358
+ market = self._market.get(sym)
359
+ if not market:
360
+ raise ValueError(f"Symbol {sym} not found in market data.")
361
+ inst_type = self._get_inst_type(market)
362
+ if inst_type not in symbols_by_inst_type:
363
+ symbols_by_inst_type[inst_type] = []
364
+ symbols_by_inst_type[inst_type].append(market.id)
365
+
366
+ for inst_type, symbols in symbols_by_inst_type.items():
367
+ self._ws_client.unsubscribe_trades_v3(symbols, inst_type)
368
+
369
+ def subscribe_kline(self, symbol: str | List[str], interval: KlineInterval):
370
+ """Subscribe to the kline data"""
371
+ symbol = symbol if isinstance(symbol, list) else [symbol]
372
+ bitget_interval = BitgetEnumParser.to_bitget_kline_interval(interval)
373
+ symbols_by_inst_type = {}
374
+
375
+ for sym in symbol:
376
+ market = self._market.get(sym)
377
+ if not market:
378
+ raise ValueError(f"Symbol {sym} not found in market data.")
379
+ inst_type = self._get_inst_type(market)
380
+ if inst_type not in symbols_by_inst_type:
381
+ symbols_by_inst_type[inst_type] = []
382
+ symbols_by_inst_type[inst_type].append(market.id)
383
+
384
+ for inst_type, symbols in symbols_by_inst_type.items():
385
+ self._ws_client.subscribe_candlestick_v3(
386
+ symbols, inst_type, bitget_interval
387
+ )
388
+
389
+ def unsubscribe_kline(self, symbol: str | List[str], interval: KlineInterval):
390
+ """Unsubscribe from the kline data"""
391
+ symbol = symbol if isinstance(symbol, list) else [symbol]
392
+ bitget_interval = BitgetEnumParser.to_bitget_kline_interval(interval)
393
+ symbols_by_inst_type = {}
394
+
395
+ for sym in symbol:
396
+ market = self._market.get(sym)
397
+ if not market:
398
+ raise ValueError(f"Symbol {sym} not found in market data.")
399
+ inst_type = self._get_inst_type(market)
400
+ if inst_type not in symbols_by_inst_type:
401
+ symbols_by_inst_type[inst_type] = []
402
+ symbols_by_inst_type[inst_type].append(market.id)
403
+
404
+ for inst_type, symbols in symbols_by_inst_type.items():
405
+ self._ws_client.unsubscribe_candlestick_v3(
406
+ symbols, inst_type, bitget_interval
407
+ )
408
+
409
+ def subscribe_bookl2(self, symbol: str | List[str], level: BookLevel):
410
+ """Subscribe to the bookl2 data"""
411
+ raise NotImplementedError
412
+
413
+ def unsubscribe_bookl2(self, symbol: str | List[str], level: BookLevel):
414
+ """Unsubscribe from the bookl2 data"""
415
+ raise NotImplementedError
416
+
417
+ def subscribe_funding_rate(self, symbol: str | List[str]):
418
+ """Subscribe to the funding rate data"""
419
+ raise NotImplementedError
420
+
421
+ def unsubscribe_funding_rate(self, symbol: str | List[str]):
422
+ """Unsubscribe from the funding rate data"""
423
+ raise NotImplementedError
424
+
425
+ def subscribe_index_price(self, symbol: str | List[str]):
426
+ """Subscribe to the index price data"""
427
+ raise NotImplementedError
428
+
429
+ def unsubscribe_index_price(self, symbol: str | List[str]):
430
+ """Unsubscribe from the index price data"""
431
+ raise NotImplementedError
432
+
433
+ def subscribe_mark_price(self, symbol: str | List[str]):
434
+ """Subscribe to the mark price data"""
435
+ raise NotImplementedError
436
+
437
+ def unsubscribe_mark_price(self, symbol: str | List[str]):
438
+ """Unsubscribe from the mark price data"""
439
+ raise NotImplementedError
440
+
441
+
442
+ class BitgetPrivateConnector(PrivateConnector):
443
+ _account_type: BitgetAccountType
444
+ _market: Dict[str, BitgetMarket]
445
+ _api_client: BitgetApiClient
446
+ _oms: BitgetOrderManagementSystem
447
+
448
+ def __init__(
449
+ self,
450
+ account_type: BitgetAccountType,
451
+ exchange: BitgetExchangeManager,
452
+ cache: AsyncCache,
453
+ registry: OrderRegistry,
454
+ clock: LiveClock,
455
+ msgbus: MessageBus,
456
+ task_manager: TaskManager,
457
+ order_query_config: OrderQueryConfig,
458
+ enable_rate_limit: bool = True,
459
+ max_subscriptions_per_client: int | None = None,
460
+ max_clients: int | None = None,
461
+ **kwargs,
462
+ ):
463
+ if not exchange.api_key or not exchange.secret or not exchange.passphrase:
464
+ raise ValueError(
465
+ "ApiKey, Secret and Passphrase must be provided for private connector."
466
+ )
467
+
468
+ max_slippage = kwargs.pop("max_slippage", 0.02)
469
+
470
+ api_client = BitgetApiClient(
471
+ clock=clock,
472
+ api_key=exchange.api_key,
473
+ secret=exchange.secret,
474
+ passphrase=exchange.passphrase,
475
+ testnet=account_type.is_testnet,
476
+ enable_rate_limit=enable_rate_limit,
477
+ **kwargs,
478
+ )
479
+
480
+ oms = BitgetOrderManagementSystem(
481
+ account_type=account_type,
482
+ api_key=exchange.api_key,
483
+ secret=exchange.secret,
484
+ passphrase=exchange.passphrase,
485
+ market=exchange.market,
486
+ market_id=exchange.market_id,
487
+ registry=registry,
488
+ cache=cache,
489
+ api_client=api_client,
490
+ exchange_id=exchange.exchange_id,
491
+ clock=clock,
492
+ msgbus=msgbus,
493
+ task_manager=task_manager,
494
+ max_slippage=max_slippage,
495
+ enable_rate_limit=enable_rate_limit,
496
+ max_subscriptions_per_client=max_subscriptions_per_client,
497
+ max_clients=max_clients,
498
+ order_query_config=order_query_config,
499
+ )
500
+
501
+ super().__init__(
502
+ account_type=account_type,
503
+ market=exchange.market,
504
+ api_client=api_client,
505
+ task_manager=task_manager,
506
+ oms=oms,
507
+ )
508
+
509
+ async def connect(self):
510
+ if self._account_type.is_uta:
511
+ self._oms._ws_client.subscribe_v3_order()
512
+ self._oms._ws_client.subscribe_v3_position()
513
+ self._oms._ws_client.subscribe_v3_account()
514
+ elif self._account_type.is_future:
515
+ self._oms._ws_client.subscribe_orders(
516
+ inst_types=["USDT-FUTURES", "USDC-FUTURES", "COIN-FUTURES"]
517
+ )
518
+ self._oms._ws_client.subscribe_positions(
519
+ inst_types=["USDT-FUTURES", "USDC-FUTURES", "COIN-FUTURES"]
520
+ )
521
+ self._oms._ws_client.subscribe_account(
522
+ inst_types=["USDT-FUTURES", "USDC-FUTURES", "COIN-FUTURES"]
523
+ )
524
+ elif self._account_type.is_spot:
525
+ self._oms._ws_client.subscribe_orders(inst_types=["SPOT"])
526
+ self._oms._ws_client.subscribe_account(inst_types=["SPOT"])
527
+ await self._oms._ws_client.connect()
528
+ await self._oms._ws_api_client.connect()
529
+
530
+
531
+ # async def main():
532
+ # from walrasquant.constants import settings
533
+
534
+ # API_KEY = settings.BITGET.DEMO1.API_KEY
535
+ # SECRET = settings.BITGET.DEMO1.SECRET
536
+ # PASSPHRASE = settings.BITGET.DEMO1.PASSPHRASE
537
+ # exchange = BitgetExchangeManager(
538
+ # config={
539
+ # "apiKey": API_KEY,
540
+ # "secret": SECRET,
541
+ # "password": PASSPHRASE,
542
+ # }
543
+ # )
544
+ # logguard, msgbus, clock = setup_nexus_core("test-001", level_stdout="DEBUG")
545
+ # task_manager = TaskManager(
546
+ # loop=asyncio.get_event_loop(),
547
+ # )
548
+ # private_connector = BitgetPrivateConnector(
549
+ # exchange=exchange,
550
+ # account_type=BitgetAccountType.UTA_DEMO,
551
+ # cache=AsyncCache(
552
+ # strategy_id="bitget_test",
553
+ # user_id="test_user",
554
+ # msgbus=msgbus,
555
+ # clock=clock,
556
+ # task_manager=task_manager,
557
+ # ),
558
+ # msgbus=msgbus,
559
+ # clock=clock,
560
+ # task_manager=task_manager,
561
+ # enable_rate_limit=True,
562
+ # )
563
+ # await private_connector.connect()
564
+
565
+ # public_connector = BitgetPublicConnector(
566
+ # account_type=BitgetAccountType.UTA,
567
+ # exchange=exchange,
568
+ # msgbus=msgbus,
569
+ # clock=clock,
570
+ # task_manager=task_manager,
571
+ # )
572
+ # await public_connector.subscribe_kline("BTCUSDT-PERP.BITGET", interval=KlineInterval.MINUTE_1)
573
+
574
+ # await task_manager.wait()
575
+
576
+
577
+ # if __name__ == "__main__":
578
+ # asyncio.run(main())