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,753 @@
1
+ import hmac
2
+ import base64
3
+ import asyncio
4
+ import picows
5
+
6
+ from typing import Any, Callable, List, Dict
7
+
8
+ from walrasquant.base import WSClient
9
+ from walrasquant.core.entity import TaskManager
10
+ from walrasquant.core.nautilius_core import LiveClock
11
+ from walrasquant.exchange.bitget.constants import (
12
+ BitgetAccountType,
13
+ BitgetKlineInterval,
14
+ BitgetRateLimiter,
15
+ )
16
+
17
+
18
+ def user_pong_callback(self, frame: picows.WSFrame) -> bool:
19
+ return (
20
+ frame.msg_type == picows.WSMsgType.TEXT
21
+ and frame.get_payload_as_memoryview() == b"pong"
22
+ )
23
+
24
+
25
+ class BitgetWSClient(WSClient):
26
+ def __init__(
27
+ self,
28
+ account_type: BitgetAccountType,
29
+ handler: Callable[..., Any],
30
+ task_manager: TaskManager,
31
+ clock: LiveClock,
32
+ api_key: str | None = None,
33
+ secret: str | None = None,
34
+ passphrase: str | None = None,
35
+ custom_url: str | None = None,
36
+ max_subscriptions_per_client: int | None = None,
37
+ max_clients: int | None = None,
38
+ ):
39
+ self._api_key = api_key
40
+ self._secret = secret
41
+ self._passphrase = passphrase
42
+ self._account_type = account_type
43
+
44
+ if custom_url:
45
+ url = custom_url
46
+ elif self.is_private:
47
+ url = f"{account_type.stream_url}/private"
48
+ else:
49
+ url = f"{account_type.stream_url}/public"
50
+
51
+ super().__init__(
52
+ url,
53
+ handler=handler,
54
+ task_manager=task_manager,
55
+ clock=clock,
56
+ specific_ping_msg=b"ping",
57
+ auto_ping_strategy="ping_periodically",
58
+ ping_idle_timeout=30,
59
+ ping_reply_timeout=5,
60
+ user_pong_callback=user_pong_callback,
61
+ max_subscriptions_per_client=max_subscriptions_per_client,
62
+ max_clients=max_clients,
63
+ )
64
+
65
+ @property
66
+ def is_private(self):
67
+ return (
68
+ self._api_key is not None
69
+ or self._secret is not None
70
+ or self._passphrase is not None
71
+ )
72
+
73
+ def _get_auth_payload(self):
74
+ timestamp = self._clock.timestamp_ms()
75
+ message = f"{timestamp}GET/user/verify"
76
+
77
+ # Create HMAC-SHA256 signature
78
+ mac = hmac.new(
79
+ bytes(self._secret, encoding="utf8"), # type: ignore
80
+ bytes(message, encoding="utf-8"),
81
+ digestmod="sha256",
82
+ )
83
+
84
+ # Get the digest and encode with base64
85
+ signature = base64.b64encode(mac.digest()).decode("utf-8")
86
+
87
+ # Form the login payload
88
+ payload = {
89
+ "op": "login",
90
+ "args": [
91
+ {
92
+ "apiKey": self._api_key,
93
+ "passphrase": self._passphrase, # If required
94
+ "timestamp": timestamp,
95
+ "sign": signature,
96
+ }
97
+ ],
98
+ }
99
+
100
+ return payload
101
+
102
+ async def _auth(self, client_id: int | None = None):
103
+ self.send(self._get_auth_payload(), client_id=client_id)
104
+ await asyncio.sleep(5)
105
+
106
+ def _send_payload(
107
+ self,
108
+ params: List[Dict[str, Any]],
109
+ op: str = "subscribe",
110
+ chunk_size: int = 100,
111
+ client_id: int | None = None,
112
+ ):
113
+ # Split params into chunks of 100 if length exceeds 100
114
+ params_chunks = [
115
+ params[i : i + chunk_size] for i in range(0, len(params), chunk_size)
116
+ ]
117
+
118
+ for chunk in params_chunks:
119
+ payload = {"op": op, "args": chunk}
120
+ self.send(payload, client_id=client_id)
121
+
122
+ def _subscribe(self, params: List[Dict[str, Any]]):
123
+ assigned = self._register_subscriptions(params)
124
+ if not assigned:
125
+ return
126
+ for client_id, client_params in assigned.items():
127
+ for param in client_params:
128
+ formatted_param = ".".join(param.values())
129
+ self._log.debug(f"Subscribing to {formatted_param}...")
130
+ if self._is_client_connected(client_id):
131
+ self._send_payload(client_params, op="subscribe", client_id=client_id)
132
+
133
+ def _unsubscribe(self, params: List[Dict[str, Any]]):
134
+ removed = self._unregister_subscriptions(params)
135
+ if not removed:
136
+ return
137
+ for client_id, client_params in removed.items():
138
+ for param in client_params:
139
+ formatted_param = ".".join(param.values())
140
+ self._log.debug(f"Unsubscribing from {formatted_param}...")
141
+ self._send_payload(client_params, op="unsubscribe", client_id=client_id)
142
+
143
+ def subscribe_depth(self, symbols: List[str], inst_type: str, channel: str):
144
+ if channel not in ["books1", "books5", "books15"]:
145
+ raise ValueError(f"Invalid channel: {channel}")
146
+
147
+ params = [
148
+ {"instType": inst_type, "channel": channel, "instId": symbol}
149
+ for symbol in symbols
150
+ ]
151
+ self._subscribe(params)
152
+
153
+ def unsubscribe_depth(self, symbols: List[str], inst_type: str, channel: str):
154
+ if channel not in ["books1", "books5", "books15"]:
155
+ raise ValueError(f"Invalid channel: {channel}")
156
+
157
+ params = [
158
+ {"instType": inst_type, "channel": channel, "instId": symbol}
159
+ for symbol in symbols
160
+ ]
161
+ self._unsubscribe(params)
162
+
163
+ def subscribe_candlesticks(
164
+ self, symbols: List[str], inst_type: str, interval: BitgetKlineInterval
165
+ ):
166
+ params = [
167
+ {"instType": inst_type, "channel": interval.value, "instId": symbol}
168
+ for symbol in symbols
169
+ ]
170
+ self._subscribe(params)
171
+
172
+ def unsubscribe_candlesticks(
173
+ self, symbols: List[str], inst_type: str, interval: BitgetKlineInterval
174
+ ):
175
+ params = [
176
+ {"instType": inst_type, "channel": interval.value, "instId": symbol}
177
+ for symbol in symbols
178
+ ]
179
+ self._unsubscribe(params)
180
+
181
+ def subscribe_trade(self, symbols: List[str], inst_type: str):
182
+ params = [
183
+ {"instType": inst_type, "channel": "trade", "instId": symbol}
184
+ for symbol in symbols
185
+ ]
186
+ self._subscribe(params)
187
+
188
+ def unsubscribe_trade(self, symbols: List[str], inst_type: str):
189
+ params = [
190
+ {"instType": inst_type, "channel": "trade", "instId": symbol}
191
+ for symbol in symbols
192
+ ]
193
+ self._unsubscribe(params)
194
+
195
+ def subscribe_ticker(self, symbols: List[str], inst_type: str):
196
+ params = [
197
+ {"instType": inst_type, "channel": "ticker", "instId": symbol}
198
+ for symbol in symbols
199
+ ]
200
+ self._subscribe(params)
201
+
202
+ def unsubscribe_ticker(self, symbols: List[str], inst_type: str):
203
+ params = [
204
+ {"instType": inst_type, "channel": "ticker", "instId": symbol}
205
+ for symbol in symbols
206
+ ]
207
+ self._unsubscribe(params)
208
+
209
+ def subscribe_account(self, inst_types: List[str] | str):
210
+ if isinstance(inst_types, str):
211
+ inst_types = [inst_types]
212
+ params = [
213
+ {"instType": inst_type, "channel": "account", "coin": "default"}
214
+ for inst_type in inst_types
215
+ ]
216
+ self._subscribe(params)
217
+
218
+ def subscribe_positions(self, inst_types: List[str] | str):
219
+ if isinstance(inst_types, str):
220
+ inst_types = [inst_types]
221
+ params = [
222
+ {"instType": inst_type, "channel": "positions", "instId": "default"}
223
+ for inst_type in inst_types
224
+ ]
225
+ self._subscribe(params)
226
+
227
+ def subscribe_orders(self, inst_types: List[str] | str):
228
+ if isinstance(inst_types, str):
229
+ inst_types = [inst_types]
230
+ params = [
231
+ {"instType": inst_type, "channel": "orders", "instId": "default"}
232
+ for inst_type in inst_types
233
+ ]
234
+ self._subscribe(params)
235
+
236
+ ############ UTA subscribe ###########
237
+ ### Better Performance with v3 API ###
238
+
239
+ def subscribe_depth_v3(self, symbols: List[str], inst_type: str, topic: str):
240
+ if topic not in ["books1", "books5", "books15"]:
241
+ raise ValueError(f"Invalid channel: {topic}")
242
+
243
+ params = [
244
+ {"instType": inst_type, "topic": topic, "symbol": symbol}
245
+ for symbol in symbols
246
+ ]
247
+ self._subscribe(params)
248
+
249
+ def unsubscribe_depth_v3(self, symbols: List[str], inst_type: str, topic: str):
250
+ if topic not in ["books1", "books5", "books15"]:
251
+ raise ValueError(f"Invalid channel: {topic}")
252
+
253
+ params = [
254
+ {"instType": inst_type, "topic": topic, "symbol": symbol}
255
+ for symbol in symbols
256
+ ]
257
+ self._unsubscribe(params)
258
+
259
+ def subscribe_trades_v3(self, symbols: List[str], inst_type: str):
260
+ params = [
261
+ {"instType": inst_type, "topic": "publicTrade", "symbol": symbol}
262
+ for symbol in symbols
263
+ ]
264
+ self._subscribe(params)
265
+
266
+ def unsubscribe_trades_v3(self, symbols: List[str], inst_type: str):
267
+ params = [
268
+ {"instType": inst_type, "topic": "publicTrade", "symbol": symbol}
269
+ for symbol in symbols
270
+ ]
271
+ self._unsubscribe(params)
272
+
273
+ def subscribe_candlestick_v3(
274
+ self, symbols: List[str], inst_type: str, interval: BitgetKlineInterval
275
+ ):
276
+ params = [
277
+ {
278
+ "instType": inst_type,
279
+ "topic": "kline",
280
+ "symbol": symbol,
281
+ "interval": interval.value,
282
+ }
283
+ for symbol in symbols
284
+ ]
285
+ self._subscribe(params)
286
+
287
+ def unsubscribe_candlestick_v3(
288
+ self, symbols: List[str], inst_type: str, interval: BitgetKlineInterval
289
+ ):
290
+ params = [
291
+ {
292
+ "instType": inst_type,
293
+ "topic": "kline",
294
+ "symbol": symbol,
295
+ "interval": interval.value,
296
+ }
297
+ for symbol in symbols
298
+ ]
299
+ self._unsubscribe(params)
300
+
301
+ async def _resubscribe_for_client(
302
+ self, client_id: int, subscriptions: List[Dict[str, Any]]
303
+ ):
304
+ if not subscriptions:
305
+ return
306
+ if self.is_private:
307
+ await self._auth(client_id=client_id)
308
+ self._send_payload(subscriptions, client_id=client_id)
309
+
310
+ def subscribe_v3_order(self):
311
+ params = [{"instType": "UTA", "topic": "order"}]
312
+ self._subscribe(params)
313
+
314
+ def subscribe_v3_position(self):
315
+ params = [{"instType": "UTA", "topic": "position"}]
316
+ self._subscribe(params)
317
+
318
+ def subscribe_v3_account(self):
319
+ params = [{"instType": "UTA", "topic": "account"}]
320
+ self._subscribe(params)
321
+
322
+
323
+ class BitgetWSApiClient(WSClient):
324
+ def __init__(
325
+ self,
326
+ account_type: BitgetAccountType,
327
+ api_key: str,
328
+ secret: str,
329
+ passphrase: str,
330
+ handler: Callable[..., Any],
331
+ task_manager: TaskManager,
332
+ clock: LiveClock,
333
+ enable_rate_limit: bool,
334
+ ):
335
+ self._api_key = api_key
336
+ self._secret = secret
337
+ self._passphrase = passphrase
338
+ self._account_type = account_type
339
+
340
+ # Use V2 WebSocket API for trading
341
+ url = f"{account_type.stream_url}/private"
342
+
343
+ self._limiter = BitgetRateLimiter(
344
+ enable_rate_limit=enable_rate_limit,
345
+ )
346
+
347
+ super().__init__(
348
+ url,
349
+ handler=handler,
350
+ task_manager=task_manager,
351
+ clock=clock,
352
+ specific_ping_msg=b"ping",
353
+ auto_ping_strategy="ping_periodically",
354
+ ping_idle_timeout=30,
355
+ ping_reply_timeout=5,
356
+ user_pong_callback=user_pong_callback,
357
+ )
358
+
359
+ def _get_auth_payload(self):
360
+ timestamp = self._clock.timestamp_ms()
361
+ message = f"{timestamp}GET/user/verify"
362
+
363
+ # Create HMAC-SHA256 signature
364
+ mac = hmac.new(
365
+ bytes(self._secret, encoding="utf8"),
366
+ bytes(message, encoding="utf-8"),
367
+ digestmod="sha256",
368
+ )
369
+
370
+ # Get the digest and encode with base64
371
+ signature = base64.b64encode(mac.digest()).decode("utf-8")
372
+
373
+ # Form the login payload
374
+ payload = {
375
+ "op": "login",
376
+ "args": [
377
+ {
378
+ "apiKey": self._api_key,
379
+ "passphrase": self._passphrase,
380
+ "timestamp": timestamp,
381
+ "sign": signature,
382
+ }
383
+ ],
384
+ }
385
+
386
+ return payload
387
+
388
+ async def _auth(self, client_id: int | None = None):
389
+ self.send(self._get_auth_payload(), client_id=client_id)
390
+ await asyncio.sleep(5)
391
+
392
+ def _submit(
393
+ self, id: str, instType: str, instId: str, channel: str, params: Dict[str, Any]
394
+ ):
395
+ payload = {
396
+ "op": "trade",
397
+ "args": [
398
+ {
399
+ "id": id,
400
+ "instType": instType,
401
+ "instId": instId,
402
+ "channel": channel,
403
+ "params": params,
404
+ }
405
+ ],
406
+ }
407
+ self._log.debug(str(payload))
408
+ self.send(payload)
409
+
410
+ def _uta_submit(
411
+ self, id: str, topic: str, category: str, args: List[Dict[str, Any]]
412
+ ):
413
+ payload = {
414
+ "op": "trade",
415
+ "id": id,
416
+ "topic": topic,
417
+ "category": category,
418
+ "args": args,
419
+ }
420
+ self._log.debug(str(payload))
421
+ self.send(payload)
422
+
423
+ async def spot_place_order(
424
+ self,
425
+ id: str,
426
+ instId: str,
427
+ orderType: str,
428
+ side: str,
429
+ size: str,
430
+ force: str,
431
+ **kwargs,
432
+ ):
433
+ params = {
434
+ "orderType": orderType,
435
+ "side": side,
436
+ "size": size,
437
+ "force": force,
438
+ **kwargs,
439
+ }
440
+
441
+ await self._limiter.order_limit("/api/v2/spot/trade/place-order")
442
+ self._submit(
443
+ id=id, instType="SPOT", instId=instId, channel="place-order", params=params
444
+ )
445
+
446
+ async def spot_cancel_order(
447
+ self,
448
+ id: str,
449
+ instId: str,
450
+ clientOid: str,
451
+ ):
452
+ params = {
453
+ "clientOid": clientOid,
454
+ }
455
+
456
+ # await self._limiter.order_limit("/api/v2/spot/trade/cancel-order")
457
+ self._submit(
458
+ id=id, instType="SPOT", instId=instId, channel="cancel-order", params=params
459
+ )
460
+
461
+ async def future_place_order(
462
+ self,
463
+ id: str,
464
+ instId: str,
465
+ orderType: str,
466
+ side: str,
467
+ size: str,
468
+ force: str,
469
+ marginCoin: str,
470
+ marginMode: str,
471
+ instType: str,
472
+ **kwargs,
473
+ ):
474
+ params = {
475
+ "orderType": orderType,
476
+ "side": side,
477
+ "size": size,
478
+ "force": force,
479
+ "marginCoin": marginCoin,
480
+ "marginMode": marginMode,
481
+ **kwargs,
482
+ }
483
+
484
+ await self._limiter.order_limit("/api/v2/mix/order/place-order")
485
+ self._submit(
486
+ id=id,
487
+ instType=instType,
488
+ instId=instId,
489
+ channel="place-order",
490
+ params=params,
491
+ )
492
+
493
+ async def future_cancel_order(
494
+ self,
495
+ id: str,
496
+ instId: str,
497
+ clientOid: str,
498
+ instType: str,
499
+ ):
500
+ params = {
501
+ "clientOid": clientOid,
502
+ }
503
+
504
+ # await self._limiter.order_limit("/api/v2/mix/order/cancel-order")
505
+ self._submit(
506
+ id=id,
507
+ instType=instType,
508
+ instId=instId,
509
+ channel="cancel-order",
510
+ params=params,
511
+ )
512
+
513
+ async def uta_place_order(
514
+ self,
515
+ id: str,
516
+ category: str,
517
+ symbol: str,
518
+ orderType: str,
519
+ qty: str,
520
+ price: str,
521
+ side: str,
522
+ **kwargs,
523
+ ):
524
+ args = [
525
+ {
526
+ "orderType": orderType,
527
+ "price": price,
528
+ "qty": qty,
529
+ "side": side,
530
+ "symbol": symbol,
531
+ **kwargs,
532
+ }
533
+ ]
534
+
535
+ await self._limiter.order_limit("/api/v3/trade/place-order")
536
+ self._uta_submit(id=f"n{id}", topic="place-order", category=category, args=args)
537
+
538
+ async def uta_cancel_order(
539
+ self,
540
+ id: str,
541
+ clientOid: str,
542
+ ):
543
+ args = [
544
+ {
545
+ "clientOid": clientOid,
546
+ }
547
+ ]
548
+
549
+ # await self._limiter.order_limit("/api/v3/trade/cancel-order")
550
+ self._uta_submit(
551
+ id=f"c{id}",
552
+ topic="cancel-order",
553
+ category="", # No category specified in UTA cancel order
554
+ args=args,
555
+ )
556
+
557
+ async def _resubscribe_for_client(self, client_id: int, subscriptions: List[Any]):
558
+ await self._auth(client_id=client_id)
559
+
560
+
561
+ # async def main():
562
+ # from walrasquant.constants import settings
563
+ # from walrasquant.core.entity import TaskManager
564
+ # from walrasquant.core.nautilius_core import setup_nexus_core, UUID4
565
+
566
+ # API_KEY = settings.BITGET.DEMO1.API_KEY
567
+ # SECRET = settings.BITGET.DEMO1.SECRET
568
+ # PASSPHRASE = settings.BITGET.DEMO1.PASSPHRASE
569
+
570
+ # log_guard, _, clock = setup_nexus_core( # noqa
571
+ # trader_id="bnc-test",
572
+ # level_stdout="DEBUG",
573
+ # )
574
+
575
+ # task_manager = TaskManager(
576
+ # loop=asyncio.get_event_loop(),
577
+ # )
578
+
579
+ # ws_api_client = BitgetWSApiClient(
580
+ # account_type=BitgetAccountType.UTA_DEMO,
581
+ # api_key=API_KEY,
582
+ # secret=SECRET,
583
+ # passphrase=PASSPHRASE,
584
+ # handler=lambda msg: print(msg),
585
+ # task_manager=task_manager,
586
+ # clock=clock,
587
+ # enable_rate_limit=True,
588
+ # )
589
+
590
+ # await ws_api_client.connect()
591
+ # await ws_api_client.spot_place_order(
592
+ # id=UUID4().value,
593
+ # instId="BTCUSDT",
594
+ # orderType="limit",
595
+ # side="buy",
596
+ # size="0.001",
597
+ # price="116881",
598
+ # force="gtc",
599
+ # )
600
+
601
+ # await ws_api_client.spot_cancel_order(
602
+ # id="9ee1e7d2-99a1-4ff5-82f7-473f02ff38e7",
603
+ # instId="BTCUSDT",
604
+ # orderId="1344423929063153665",
605
+ # )
606
+
607
+ # await ws_api_client.uta_place_order(
608
+ # id=UUID4().value,
609
+ # category="spot", # SPOT MARGIN USDT-FUTURES COIN-FUTURES USDC-FUTURES
610
+ # symbol="BTCUSDT",
611
+ # orderType="limit",
612
+ # qty="0.001",
613
+ # price="110536",
614
+ # side="buy",
615
+ # )
616
+
617
+ # await ws_api_client.uta_cancel_order(
618
+ # id="3c14b801-ecc0-47f9-8126-56604d9f4c33",
619
+ # instId="BTCUSDT",
620
+ # orderId="1344515561982640128",
621
+ # )
622
+
623
+ # await task_manager.wait()
624
+
625
+ # {
626
+ # "event": "trade",
627
+ # "arg": [
628
+ # {
629
+ # "id": "30862609-4cb0-45cb-8562-992aaf232ee8",
630
+ # "instType": "SPOT",
631
+ # "channel": "place-order",
632
+ # "instId": "BTCUSDT",
633
+ # "params": {
634
+ # "orderId": "1344419561630867456",
635
+ # "clientOid": "0021c683-5e7e-416e-8dbb-dd34a0c3f97c",
636
+ # },
637
+ # }
638
+ # ],
639
+ # "code": 0,
640
+ # "msg": "Success",
641
+ # "ts": 1756260522296,
642
+ # }
643
+
644
+ # {
645
+ # "event": "error",
646
+ # "arg": [
647
+ # {
648
+ # "id": "1c6dbaae-4599-4371-9517-f800bc2f0dfb",
649
+ # "instType": "SPOT",
650
+ # "channel": "place-order",
651
+ # "instId": "BTCUSDT",
652
+ # "params": {
653
+ # "orderType": "limit",
654
+ # "side": "buy",
655
+ # "force": "gtc",
656
+ # "price": "80000",
657
+ # "size": "0.000001",
658
+ # },
659
+ # }
660
+ # ],
661
+ # "code": 43027,
662
+ # "msg": "The minimum order value 1 is not met",
663
+ # "ts": 1756260596285,
664
+ # }
665
+
666
+ # {
667
+ # "event": "trade",
668
+ # "arg": [
669
+ # {
670
+ # "id": "30862609-4cb0-45cb-8562-992aaf232ee8",
671
+ # "instType": "SPOT",
672
+ # "channel": "cancel-order",
673
+ # "instId": "BTCUSDT",
674
+ # "params": {"orderId": "1344419561630867456"},
675
+ # }
676
+ # ],
677
+ # "code": 0,
678
+ # "msg": "Success",
679
+ # "ts": 1756260769404,
680
+ # }
681
+
682
+ # {
683
+ # "event": "error",
684
+ # "arg": [
685
+ # {
686
+ # "id": "9ee1e7d2-99a1-4ff5-82f7-473f02ff38e7",
687
+ # "instType": "SPOT",
688
+ # "channel": "cancel-order",
689
+ # "instId": "BTCUSDT",
690
+ # "params": {"orderId": "1344423929063153665"},
691
+ # }
692
+ # ],
693
+ # "code": 43001,
694
+ # "msg": "The order does not exist",
695
+ # "ts": 1756261603661,
696
+ # }
697
+
698
+ ##### UTA ACCOUNT #####
699
+
700
+ # {
701
+ # "event": "error",
702
+ # "id": "bb63695d-b11f-4c6d-8166-460c612dc252",
703
+ # "code": "41101",
704
+ # "msg": "Param category=SPOT error",
705
+ # }
706
+
707
+ # {
708
+ # "event": "error",
709
+ # "id": "401822f1-4014-4bf0-af07-723fcbe391d4",
710
+ # "code": "25206",
711
+ # "msg": "BTC/USDT trading price cannot exceed 5%",
712
+ # }
713
+
714
+ # {
715
+ # "event": "trade",
716
+ # "id": "90ab565b-f863-4377-b275-08020b6c0536",
717
+ # "category": "spot",
718
+ # "topic": "place-order",
719
+ # "args": [
720
+ # {
721
+ # "symbol": "BTCUSDT",
722
+ # "orderId": "1344507887740108800",
723
+ # "clientOid": "1344507887740108801",
724
+ # "cTime": "1756281580862",
725
+ # }
726
+ # ],
727
+ # "code": "0",
728
+ # "msg": "Success",
729
+ # "ts": "1756281580865",
730
+ # }
731
+
732
+ # {
733
+ # "event": "error",
734
+ # "id": "90ab565b-f863-4377-b275-08020b6c0536",
735
+ # "code": "25204",
736
+ # "msg": "Order does not exist",
737
+ # }
738
+
739
+ # {
740
+ # "event": "trade",
741
+ # "id": "3c14b801-ecc0-47f9-8126-56604d9f4c33",
742
+ # "topic": "cancel-order",
743
+ # "args": [
744
+ # {"orderId": "1344515561982640128", "clientOid": "1344515561982640129"}
745
+ # ],
746
+ # "code": "0",
747
+ # "msg": "Success",
748
+ # "ts": "1756283452147",
749
+ # }
750
+
751
+
752
+ # if __name__ == "__main__":
753
+ # asyncio.run(main())