ccxt 4.3.57__py2.py3-none-any.whl → 4.3.59__py2.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 (91) hide show
  1. ccxt/__init__.py +5 -5
  2. ccxt/abstract/bitmart.py +1 -0
  3. ccxt/abstract/btcbox.py +1 -0
  4. ccxt/abstract/kucoin.py +1 -0
  5. ccxt/abstract/kucoinfutures.py +1 -0
  6. ccxt/abstract/upbit.py +3 -0
  7. ccxt/abstract/xt.py +1 -0
  8. ccxt/alpaca.py +5 -1
  9. ccxt/async_support/__init__.py +5 -5
  10. ccxt/async_support/alpaca.py +5 -1
  11. ccxt/async_support/base/exchange.py +1 -1
  12. ccxt/async_support/base/ws/client.py +1 -0
  13. ccxt/async_support/bigone.py +19 -1
  14. ccxt/async_support/binance.py +1 -1
  15. ccxt/async_support/bingx.py +116 -26
  16. ccxt/async_support/bitget.py +1 -1
  17. ccxt/async_support/bitmart.py +1 -0
  18. ccxt/async_support/btcbox.py +145 -8
  19. ccxt/async_support/bybit.py +2 -2
  20. ccxt/async_support/cex.py +1 -1
  21. ccxt/async_support/coinmate.py +28 -32
  22. ccxt/async_support/coinone.py +1 -1
  23. ccxt/async_support/coinsph.py +1 -1
  24. ccxt/async_support/deribit.py +15 -1
  25. ccxt/async_support/digifinex.py +30 -7
  26. ccxt/async_support/gate.py +22 -18
  27. ccxt/async_support/htx.py +10 -8
  28. ccxt/async_support/hyperliquid.py +106 -2
  29. ccxt/async_support/kraken.py +1 -1
  30. ccxt/async_support/kucoin.py +3 -0
  31. ccxt/async_support/latoken.py +5 -1
  32. ccxt/async_support/mexc.py +11 -11
  33. ccxt/async_support/novadax.py +1 -1
  34. ccxt/async_support/okcoin.py +1 -1
  35. ccxt/async_support/okx.py +3 -1
  36. ccxt/async_support/phemex.py +1 -1
  37. ccxt/async_support/probit.py +1 -1
  38. ccxt/async_support/tokocrypto.py +1 -1
  39. ccxt/async_support/upbit.py +139 -45
  40. ccxt/async_support/woo.py +2 -1
  41. ccxt/async_support/xt.py +70 -7
  42. ccxt/base/errors.py +23 -23
  43. ccxt/base/exchange.py +9 -9
  44. ccxt/bigone.py +19 -1
  45. ccxt/binance.py +1 -1
  46. ccxt/bingx.py +116 -26
  47. ccxt/bitget.py +1 -1
  48. ccxt/bitmart.py +1 -0
  49. ccxt/btcbox.py +145 -8
  50. ccxt/bybit.py +2 -2
  51. ccxt/cex.py +1 -1
  52. ccxt/coinmate.py +28 -32
  53. ccxt/coinone.py +1 -1
  54. ccxt/coinsph.py +1 -1
  55. ccxt/deribit.py +15 -1
  56. ccxt/digifinex.py +30 -7
  57. ccxt/gate.py +22 -18
  58. ccxt/htx.py +10 -8
  59. ccxt/hyperliquid.py +106 -2
  60. ccxt/kraken.py +1 -1
  61. ccxt/kucoin.py +3 -0
  62. ccxt/latoken.py +5 -1
  63. ccxt/mexc.py +11 -11
  64. ccxt/novadax.py +1 -1
  65. ccxt/okcoin.py +1 -1
  66. ccxt/okx.py +3 -1
  67. ccxt/phemex.py +1 -1
  68. ccxt/pro/__init__.py +3 -1
  69. ccxt/pro/binance.py +11 -13
  70. ccxt/pro/bingx.py +11 -8
  71. ccxt/pro/bitmart.py +2 -2
  72. ccxt/pro/bitopro.py +1 -1
  73. ccxt/pro/coincheck.py +1 -1
  74. ccxt/pro/coinone.py +1 -1
  75. ccxt/pro/htx.py +1 -1
  76. ccxt/pro/hyperliquid.py +1 -1
  77. ccxt/pro/okx.py +3 -3
  78. ccxt/pro/xt.py +1043 -0
  79. ccxt/probit.py +1 -1
  80. ccxt/test/tests_async.py +2 -2
  81. ccxt/test/tests_helpers.py +1 -1
  82. ccxt/test/tests_sync.py +2 -2
  83. ccxt/tokocrypto.py +1 -1
  84. ccxt/upbit.py +139 -45
  85. ccxt/woo.py +2 -1
  86. ccxt/xt.py +70 -7
  87. {ccxt-4.3.57.dist-info → ccxt-4.3.59.dist-info}/METADATA +5 -5
  88. {ccxt-4.3.57.dist-info → ccxt-4.3.59.dist-info}/RECORD +91 -90
  89. {ccxt-4.3.57.dist-info → ccxt-4.3.59.dist-info}/LICENSE.txt +0 -0
  90. {ccxt-4.3.57.dist-info → ccxt-4.3.59.dist-info}/WHEEL +0 -0
  91. {ccxt-4.3.57.dist-info → ccxt-4.3.59.dist-info}/top_level.txt +0 -0
ccxt/pro/xt.py ADDED
@@ -0,0 +1,1043 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
4
+ # https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
5
+
6
+ import ccxt.async_support
7
+ from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById, ArrayCacheByTimestamp
8
+ from ccxt.base.types import Balances, Int, Market, Order, OrderBook, Str, Strings, Ticker, Tickers, Trade
9
+ from ccxt.async_support.base.ws.client import Client
10
+ from typing import List
11
+
12
+
13
+ class xt(ccxt.async_support.xt):
14
+
15
+ def describe(self):
16
+ return self.deep_extend(super(xt, self).describe(), {
17
+ 'has': {
18
+ 'ws': True,
19
+ 'watchOHLCV': True,
20
+ 'watchOrderBook': True,
21
+ 'watchTicker': True,
22
+ 'watchTickers': True,
23
+ 'watchTrades': True,
24
+ 'watchBalance': True,
25
+ 'watchOrders': True,
26
+ 'watchMyTrades': True,
27
+ 'watchPositions': None, # TODO https://doc.xt.com/#futures_user_websocket_v2position
28
+ },
29
+ 'urls': {
30
+ 'api': {
31
+ 'ws': {
32
+ 'spot': 'wss://stream.xt.com',
33
+ 'contract': 'wss://fstream.xt.com/ws',
34
+ },
35
+ },
36
+ },
37
+ 'options': {
38
+ 'tradesLimit': 1000,
39
+ 'ordersLimit': 1000,
40
+ 'OHLCVLimit': 1000,
41
+ 'watchTicker': {
42
+ 'method': 'ticker', # agg_ticker(contract only)
43
+ },
44
+ 'watchTickers': {
45
+ 'method': 'tickers', # agg_tickers(contract only)
46
+ },
47
+ },
48
+ 'streaming': {
49
+ 'keepAlive': 20000,
50
+ 'ping': self.ping,
51
+ },
52
+ 'token': None,
53
+ })
54
+
55
+ async def get_listen_key(self, isContract: bool):
56
+ """
57
+ * @ignore
58
+ required for private endpoints
59
+ :param str isContract: True for contract trades
60
+ :see: https://doc.xt.com/#websocket_privategetToken
61
+ :see: https://doc.xt.com/#futures_user_websocket_v2base
62
+ :returns str: listen key / access token
63
+ """
64
+ self.check_required_credentials()
65
+ tradeType = 'contract' if isContract else 'spot'
66
+ url = self.urls['api']['ws'][tradeType]
67
+ if not isContract:
68
+ url = url + '/private'
69
+ client = self.client(url)
70
+ token = self.safe_dict(client.subscriptions, 'token')
71
+ if token is None:
72
+ if isContract:
73
+ response = await self.privateLinearGetFutureUserV1UserListenKey()
74
+ #
75
+ # {
76
+ # returnCode: '0',
77
+ # msgInfo: 'success',
78
+ # error: null,
79
+ # result: '3BC1D71D6CF96DA3458FC35B05B633351684511731128'
80
+ # }
81
+ #
82
+ client.subscriptions['token'] = self.safe_string(response, 'result')
83
+ else:
84
+ response = await self.privateSpotPostWsToken()
85
+ #
86
+ # {
87
+ # "rc": 0,
88
+ # "mc": "SUCCESS",
89
+ # "ma": [],
90
+ # "result": {
91
+ # "token": "eyJhbqGciOiJSUzI1NiJ9.eyJhY2NvdW50SWQiOiIyMTQ2Mjg1MzIyNTU5Iiwic3ViIjoibGh4dDRfMDAwMUBzbmFwbWFpbC5jYyIsInNjb3BlIjoiYXV0aCIsImlzcyI6Inh0LmNvbSIsImxhc3RBdXRoVGltZSI6MTY2MzgxMzY5MDk1NSwic2lnblR5cGUiOiJBSyIsInVzZXJOYW1lIjoibGh4dDRfMDAwMUBzbmFwbWFpbC5jYyIsImV4cCI6MTY2NjQwNTY5MCwiZGV2aWNlIjoidW5rbm93biIsInVzZXJJZCI6MjE0NjI4NTMyMjU1OX0.h3zJlJBQrK2x1HvUxsKivnn6PlSrSDXXXJ7WqHAYSrN2CG5XPTKc4zKnTVoYFbg6fTS0u1fT8wH7wXqcLWXX71vm0YuP8PCvdPAkUIq4-HyzltbPr5uDYd0UByx0FPQtq1exvsQGe7evXQuDXx3SEJXxEqUbq_DNlXPTq_JyScI",
92
+ # "refreshToken": "eyJhbGciOiqJSUzI1NiJ9.eyJhY2NvdW50SWQiOiIyMTQ2Mjg1MzIyNTU5Iiwic3ViIjoibGh4dDRfMDAwMUBzbmFwbWFpbC5jYyIsInNjb3BlIjoicmVmcmVzaCIsImlzcyI6Inh0LmNvbSIsImxhc3RBdXRoVGltZSI6MTY2MzgxMzY5MDk1NSwic2lnblR5cGUiOiJBSyIsInVzZXJOYW1lIjoibGh4dDRfMDAwMUBzbmFwbWFpbC5jYyIsImV4cCI6MTY2NjQwNTY5MCwiZGV2aWNlIjoidW5rbm93biIsInVzZXJJZCI6MjE0NjI4NTMyMjU1OX0.Fs3YVm5YrEOzzYOSQYETSmt9iwxUHBovh2u73liv1hLUec683WGfktA_s28gMk4NCpZKFeQWFii623FvdfNoteXR0v1yZ2519uNvNndtuZICDdv3BQ4wzW1wIHZa1skxFfqvsDnGdXpjqu9UFSbtHwxprxeYfnxChNk4ssei430"
93
+ # }
94
+ # }
95
+ #
96
+ result = self.safe_dict(response, 'result')
97
+ client.subscriptions['token'] = self.safe_string(result, 'accessToken')
98
+ return client.subscriptions['token']
99
+
100
+ def get_cache_index(self, orderbook, cache):
101
+ # return the first index of the cache that can be applied to the orderbook or -1 if not possible
102
+ nonce = self.safe_integer(orderbook, 'nonce')
103
+ firstDelta = self.safe_value(cache, 0)
104
+ firstDeltaNonce = self.safe_integer_2(firstDelta, 'i', 'u')
105
+ if nonce < firstDeltaNonce - 1:
106
+ return -1
107
+ for i in range(0, len(cache)):
108
+ delta = cache[i]
109
+ deltaNonce = self.safe_integer_2(delta, 'i', 'u')
110
+ if deltaNonce >= nonce:
111
+ return i
112
+ return len(cache)
113
+
114
+ def handle_delta(self, orderbook, delta):
115
+ orderbook['nonce'] = self.safe_integer_2(delta, 'i', 'u')
116
+ obAsks = self.safe_list(delta, 'a', [])
117
+ obBids = self.safe_list(delta, 'b', [])
118
+ bids = orderbook['bids']
119
+ asks = orderbook['asks']
120
+ for i in range(0, len(obBids)):
121
+ bid = obBids[i]
122
+ price = self.safe_number(bid, 0)
123
+ quantity = self.safe_number(bid, 1)
124
+ bids.store(price, quantity)
125
+ for i in range(0, len(obAsks)):
126
+ ask = obAsks[i]
127
+ price = self.safe_number(ask, 0)
128
+ quantity = self.safe_number(ask, 1)
129
+ asks.store(price, quantity)
130
+ # self.handleBidAsks(storedBids, bids)
131
+ # self.handleBidAsks(storedAsks, asks)
132
+
133
+ async def subscribe(self, name: str, access: str, methodName: str, market: Market = None, symbols: List[str] = None, params={}):
134
+ """
135
+ * @ignore
136
+ Connects to a websocket channel
137
+ :see: https://doc.xt.com/#websocket_privaterequestFormat
138
+ :see: https://doc.xt.com/#futures_market_websocket_v2base
139
+ :param str name: name of the channel
140
+ :param str access: public or private
141
+ :param str methodName: the name of the CCXT class method
142
+ :param dict [market]: CCXT market
143
+ :param str[] [symbols]: unified market symbols
144
+ :param dict params: extra parameters specific to the xt api
145
+ :returns dict: data from the websocket stream
146
+ """
147
+ privateAccess = access == 'private'
148
+ type = None
149
+ type, params = self.handle_market_type_and_params(methodName, market, params)
150
+ isContract = (type != 'spot')
151
+ subscribe = {
152
+ 'method': 'SUBSCRIBE' if isContract else 'subscribe',
153
+ 'id': self.number_to_string(self.milliseconds()) + name, # call back ID
154
+ }
155
+ if privateAccess:
156
+ if not isContract:
157
+ subscribe['params'] = [name]
158
+ subscribe['listenKey'] = await self.get_listen_key(isContract)
159
+ else:
160
+ listenKey = await self.get_listen_key(isContract)
161
+ param = name + '@' + listenKey
162
+ subscribe['params'] = [param]
163
+ else:
164
+ subscribe['params'] = [name]
165
+ tradeType = 'contract' if isContract else 'spot'
166
+ messageHash = name + '::' + tradeType
167
+ if symbols is not None:
168
+ messageHash = messageHash + '::' + ','.join(symbols)
169
+ request = self.extend(subscribe, params)
170
+ tail = access
171
+ if isContract:
172
+ tail = 'user' if privateAccess else 'market'
173
+ url = self.urls['api']['ws'][tradeType] + '/' + tail
174
+ return await self.watch(url, messageHash, request, messageHash)
175
+
176
+ async def watch_ticker(self, symbol: str, params={}) -> Ticker:
177
+ """
178
+ watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
179
+ :see: https://doc.xt.com/#websocket_publictickerRealTime
180
+ :see: https://doc.xt.com/#futures_market_websocket_v2tickerRealTime
181
+ :see: https://doc.xt.com/#futures_market_websocket_v2aggTickerRealTime
182
+ :param str symbol: unified symbol of the market to fetch the ticker for
183
+ :param dict params: extra parameters specific to the xt api endpoint
184
+ :param str [params.method]: 'agg_ticker'(contract only) or 'ticker', default = 'ticker' - the endpoint that will be streamed
185
+ :returns dict: a `ticker structure <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
186
+ """
187
+ await self.load_markets()
188
+ market = self.market(symbol)
189
+ options = self.safe_dict(self.options, 'watchTicker')
190
+ defaultMethod = self.safe_string(options, 'method', 'ticker')
191
+ method = self.safe_string(params, 'method', defaultMethod)
192
+ name = method + '@' + market['id']
193
+ return await self.subscribe(name, 'public', 'watchTicker', market, None, params)
194
+
195
+ async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
196
+ """
197
+ watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
198
+ :see: https://doc.xt.com/#websocket_publicallTicker
199
+ :see: https://doc.xt.com/#futures_market_websocket_v2allTicker
200
+ :see: https://doc.xt.com/#futures_market_websocket_v2allAggTicker
201
+ :param str [symbols]: unified market symbols
202
+ :param dict params: extra parameters specific to the xt api endpoint
203
+ :param str [params.method]: 'agg_tickers'(contract only) or 'tickers', default = 'tickers' - the endpoint that will be streamed
204
+ :returns dict: a `ticker structure <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
205
+ """
206
+ await self.load_markets()
207
+ options = self.safe_dict(self.options, 'watchTickers')
208
+ defaultMethod = self.safe_string(options, 'method', 'tickers')
209
+ name = self.safe_string(params, 'method', defaultMethod)
210
+ market = None
211
+ if symbols is not None:
212
+ market = self.market(symbols[0])
213
+ tickers = await self.subscribe(name, 'public', 'watchTickers', market, symbols, params)
214
+ if self.newUpdates:
215
+ return tickers
216
+ return self.filter_by_array(self.tickers, 'symbol', symbols)
217
+
218
+ async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
219
+ """
220
+ watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
221
+ :see: https://doc.xt.com/#websocket_publicsymbolKline
222
+ :see: https://doc.xt.com/#futures_market_websocket_v2symbolKline
223
+ :param str symbol: unified symbol of the market to fetch OHLCV data for
224
+ :param str timeframe: 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, or 1M
225
+ :param int [since]: not used by xt watchOHLCV
226
+ :param int [limit]: not used by xt watchOHLCV
227
+ :param dict params: extra parameters specific to the xt api endpoint
228
+ :returns int[][]: A list of candles ordered, open, high, low, close, volume
229
+ """
230
+ await self.load_markets()
231
+ market = self.market(symbol)
232
+ name = 'kline@' + market['id'] + ',' + timeframe
233
+ return await self.subscribe(name, 'public', 'watchOHLCV', market, None, params)
234
+
235
+ async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
236
+ """
237
+ get the list of most recent trades for a particular symbol
238
+ :see: https://doc.xt.com/#websocket_publicdealRecord
239
+ :see: https://doc.xt.com/#futures_market_websocket_v2dealRecord
240
+ :param str symbol: unified symbol of the market to fetch trades for
241
+ :param int [since]: timestamp in ms of the earliest trade to fetch
242
+ :param int [limit]: the maximum amount of trades to fetch
243
+ :param dict params: extra parameters specific to the xt api endpoint
244
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/en/latest/manual.html?#public-trades>`
245
+ """
246
+ await self.load_markets()
247
+ market = self.market(symbol)
248
+ name = 'trade@' + market['id']
249
+ trades = await self.subscribe(name, 'public', 'watchTrades', market, None, params)
250
+ if self.newUpdates:
251
+ limit = trades.getLimit(symbol, limit)
252
+ return self.filter_by_since_limit(trades, since, limit, 'timestamp')
253
+
254
+ async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
255
+ """
256
+ watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
257
+ :see: https://doc.xt.com/#websocket_publiclimitDepth
258
+ :see: https://doc.xt.com/#websocket_publicincreDepth
259
+ :see: https://doc.xt.com/#futures_market_websocket_v2limitDepth
260
+ :see: https://doc.xt.com/#futures_market_websocket_v2increDepth
261
+ :param str symbol: unified symbol of the market to fetch the order book for
262
+ :param int [limit]: not used by xt watchOrderBook
263
+ :param dict params: extra parameters specific to the xt api endpoint
264
+ :param int [params.levels]: 5, 10, 20, or 50
265
+ :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/en/latest/manual.html#order-book-structure>` indexed by market symbols
266
+ """
267
+ await self.load_markets()
268
+ market = self.market(symbol)
269
+ levels = self.safe_string(params, 'levels')
270
+ params = self.omit(params, 'levels')
271
+ name = 'depth_update@' + market['id']
272
+ if levels is not None:
273
+ name = 'depth@' + market['id'] + ',' + levels
274
+ orderbook = await self.subscribe(name, 'public', 'watchOrderBook', market, None, params)
275
+ return orderbook.limit()
276
+
277
+ async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
278
+ """
279
+ watches information on multiple orders made by the user
280
+ :see: https://doc.xt.com/#websocket_privateorderChange
281
+ :see: https://doc.xt.com/#futures_user_websocket_v2order
282
+ :param str [symbol]: unified market symbol
283
+ :param int [since]: not used by xt watchOrders
284
+ :param int [limit]: the maximum number of orders to return
285
+ :param dict params: extra parameters specific to the xt api endpoint
286
+ :returns dict[]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
287
+ """
288
+ await self.load_markets()
289
+ name = 'order'
290
+ market = None
291
+ if symbol is not None:
292
+ market = self.market(symbol)
293
+ orders = await self.subscribe(name, 'private', 'watchOrders', market, None, params)
294
+ if self.newUpdates:
295
+ limit = orders.getLimit(symbol, limit)
296
+ return self.filter_by_since_limit(orders, since, limit, 'timestamp')
297
+
298
+ async def watch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
299
+ """
300
+ watches information on multiple trades made by the user
301
+ :see: https://doc.xt.com/#websocket_privateorderDeal
302
+ :see: https://doc.xt.com/#futures_user_websocket_v2trade
303
+ :param str symbol: unified market symbol of the market orders were made in
304
+ :param int [since]: the earliest time in ms to fetch orders for
305
+ :param int [limit]: the maximum number of orde structures to retrieve
306
+ :param dict params: extra parameters specific to the kucoin api endpoint
307
+ :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
308
+ """
309
+ await self.load_markets()
310
+ name = 'trade'
311
+ market = None
312
+ if symbol is not None:
313
+ market = self.market(symbol)
314
+ trades = await self.subscribe(name, 'private', 'watchMyTrades', market, None, params)
315
+ if self.newUpdates:
316
+ limit = trades.getLimit(symbol, limit)
317
+ return self.filter_by_since_limit(trades, since, limit, 'timestamp')
318
+
319
+ async def watch_balance(self, params={}) -> Balances:
320
+ """
321
+ watches information on multiple orders made by the user
322
+ :see: https://doc.xt.com/#websocket_privatebalanceChange
323
+ :see: https://doc.xt.com/#futures_user_websocket_v2balance
324
+ :param dict params: extra parameters specific to the xt api endpoint
325
+ :returns dict[]: a list of `balance structures <https://docs.ccxt.com/#/?id=balance-structure>`
326
+ """
327
+ await self.load_markets()
328
+ name = 'balance'
329
+ return await self.subscribe(name, 'private', 'watchBalance', None, None, params)
330
+
331
+ def handle_ticker(self, client: Client, message: dict):
332
+ #
333
+ # spot
334
+ #
335
+ # {
336
+ # topic: 'ticker',
337
+ # event: 'ticker@btc_usdt',
338
+ # data: {
339
+ # s: 'btc_usdt', # symbol
340
+ # t: 1683501935877, # time(Last transaction time)
341
+ # cv: '-82.67', # priceChangeValue(24 hour price change)
342
+ # cr: '-0.0028', # priceChangeRate 24-hour price change(percentage)
343
+ # o: '28823.87', # open price
344
+ # c: '28741.20', # close price
345
+ # h: '29137.64', # highest price
346
+ # l: '28660.93', # lowest price
347
+ # q: '6372.601573', # quantity
348
+ # v: '184086075.2772391' # volume
349
+ # }
350
+ # }
351
+ #
352
+ # contract
353
+ #
354
+ # {
355
+ # "topic": "ticker",
356
+ # "event": "ticker@btc_usdt",
357
+ # "data": {
358
+ # "s": "btc_index", # trading pair
359
+ # "o": "49000", # opening price
360
+ # "c": "50000", # closing price
361
+ # "h": "0.1", # highest price
362
+ # "l": "0.1", # lowest price
363
+ # "a": "0.1", # volume
364
+ # "v": "0.1", # turnover
365
+ # "ch": "0.21", # quote change
366
+ # "t": 123124124 # timestamp
367
+ # }
368
+ # }
369
+ #
370
+ # agg_ticker(contract)
371
+ #
372
+ # {
373
+ # "topic": "agg_ticker",
374
+ # "event": "agg_ticker@btc_usdt",
375
+ # "data": {
376
+ # "s": "btc_index", # trading pair
377
+ # "o": "49000", # opening price
378
+ # "c": "50000", # closing price
379
+ # "h": "0.1", # highest price
380
+ # "l": "0.1", # lowest price
381
+ # "a": "0.1", # volume
382
+ # "v": "0.1", # turnover
383
+ # "ch": "0.21", # quote change
384
+ # "i": "0.21" , # index price
385
+ # "m": "0.21", # mark price
386
+ # "bp": "0.21", # bid price
387
+ # "ap": "0.21" , # ask price
388
+ # "t": 123124124 # timestamp
389
+ # }
390
+ # }
391
+ #
392
+ data = self.safe_dict(message, 'data')
393
+ marketId = self.safe_string(data, 's')
394
+ if marketId is not None:
395
+ cv = self.safe_string(data, 'cv')
396
+ isSpot = cv is not None
397
+ ticker = self.parse_ticker(data)
398
+ symbol = ticker['symbol']
399
+ self.tickers[symbol] = ticker
400
+ event = self.safe_string(message, 'event')
401
+ messageHashTail = 'spot' if isSpot else 'contract'
402
+ messageHash = event + '::' + messageHashTail
403
+ client.resolve(ticker, messageHash)
404
+ return message
405
+
406
+ def handle_tickers(self, client: Client, message: dict):
407
+ #
408
+ # spot
409
+ #
410
+ # {
411
+ # topic: 'tickers',
412
+ # event: 'tickers',
413
+ # data: [
414
+ # {
415
+ # s: 'elon_usdt',
416
+ # t: 1683502958381,
417
+ # cv: '-0.0000000125',
418
+ # cr: '-0.0495',
419
+ # o: '0.0000002522',
420
+ # c: '0.0000002397',
421
+ # h: '0.0000002690',
422
+ # l: '0.0000002371',
423
+ # q: '3803783034.0000000000',
424
+ # v: '955.3260820022'
425
+ # },
426
+ # ...
427
+ # ]
428
+ # }
429
+ #
430
+ # contract
431
+ #
432
+ # {
433
+ # "topic": "tickers",
434
+ # "event": "tickers",
435
+ # "data": [
436
+ # {
437
+ # "s": "btc_index", # trading pair
438
+ # "o": "49000", # opening price
439
+ # "c": "50000", # closing price
440
+ # "h": "0.1", # highest price
441
+ # "l": "0.1", # lowest price
442
+ # "a": "0.1", # volume
443
+ # "v": "0.1", # turnover
444
+ # "ch": "0.21", # quote change
445
+ # "t": 123124124 # timestamp
446
+ # }
447
+ # ]
448
+ # }
449
+ #
450
+ # agg_ticker(contract)
451
+ #
452
+ # {
453
+ # "topic": "agg_tickers",
454
+ # "event": "agg_tickers",
455
+ # "data": [
456
+ # {
457
+ # "s": "btc_index", # trading pair
458
+ # "o": "49000", # opening price
459
+ # "c": "50000", # closing price
460
+ # "h": "0.1", # highest price
461
+ # "l": "0.1", # lowest price
462
+ # "a": "0.1", # volume
463
+ # "v": "0.1", # turnover
464
+ # "ch": "0.21", # quote change
465
+ # "i": "0.21" , # index price
466
+ # "m": "0.21", # mark price
467
+ # "bp": "0.21", # bid price
468
+ # "ap": "0.21" , # ask price
469
+ # "t": 123124124 # timestamp
470
+ # }
471
+ # ]
472
+ # }
473
+ #
474
+ data = self.safe_list(message, 'data', [])
475
+ firstTicker = self.safe_dict(data, 0)
476
+ spotTest = self.safe_string_2(firstTicker, 'cv', 'aq')
477
+ tradeType = 'spot' if (spotTest is not None) else 'contract'
478
+ newTickers = []
479
+ for i in range(0, len(data)):
480
+ tickerData = data[i]
481
+ ticker = self.parse_ticker(tickerData)
482
+ symbol = ticker['symbol']
483
+ self.tickers[symbol] = ticker
484
+ newTickers.append(ticker)
485
+ messageHashStart = self.safe_string(message, 'topic') + '::' + tradeType
486
+ messageHashes = self.find_message_hashes(client, messageHashStart + '::')
487
+ for i in range(0, len(messageHashes)):
488
+ messageHash = messageHashes[i]
489
+ parts = messageHash.split('::')
490
+ symbolsString = parts[2]
491
+ symbols = symbolsString.split(',')
492
+ tickers = self.filter_by_array(newTickers, 'symbol', symbols)
493
+ tickersSymbols = list(tickers.keys())
494
+ numTickers = len(tickersSymbols)
495
+ if numTickers > 0:
496
+ client.resolve(tickers, messageHash)
497
+ client.resolve(self.tickers, messageHashStart)
498
+ return message
499
+
500
+ def handle_ohlcv(self, client: Client, message: dict):
501
+ #
502
+ # spot
503
+ #
504
+ # {
505
+ # "topic": "kline",
506
+ # "event": "kline@btc_usdt,5m",
507
+ # "data": {
508
+ # "s": "btc_usdt", # symbol
509
+ # "t": 1656043200000, # time
510
+ # "i": "5m", # interval
511
+ # "o": "44000", # open price
512
+ # "c": "50000", # close price
513
+ # "h": "52000", # highest price
514
+ # "l": "36000", # lowest price
515
+ # "q": "34.2", # qty(quantity)
516
+ # "v": "230000" # volume
517
+ # }
518
+ # }
519
+ #
520
+ # contract
521
+ #
522
+ # {
523
+ # "topic": "kline",
524
+ # "event": "kline@btc_usdt,5m",
525
+ # "data": {
526
+ # "s": "btc_index", # trading pair
527
+ # "o": "49000", # opening price
528
+ # "c": "50000", # closing price
529
+ # "h": "0.1", # highest price
530
+ # "l": "0.1", # lowest price
531
+ # "a": "0.1", # volume
532
+ # "v": "0.1", # turnover
533
+ # "ch": "0.21", # quote change
534
+ # "t": 123124124 # timestamp
535
+ # }
536
+ # }
537
+ #
538
+ data = self.safe_dict(message, 'data', {})
539
+ marketId = self.safe_string(data, 's')
540
+ if marketId is not None:
541
+ timeframe = self.safe_string(data, 'i')
542
+ tradeType = 'spot' if ('q' in data) else 'contract'
543
+ market = self.safe_market(marketId, None, None, tradeType)
544
+ symbol = market['symbol']
545
+ parsed = self.parse_ohlcv(data, market)
546
+ self.ohlcvs[symbol] = self.safe_dict(self.ohlcvs, symbol, {})
547
+ stored = self.safe_value(self.ohlcvs[symbol], timeframe)
548
+ if stored is None:
549
+ limit = self.safe_integer(self.options, 'OHLCVLimit', 1000)
550
+ stored = ArrayCacheByTimestamp(limit)
551
+ self.ohlcvs[symbol][timeframe] = stored
552
+ stored.append(parsed)
553
+ event = self.safe_string(message, 'event')
554
+ messageHash = event + '::' + tradeType
555
+ client.resolve(stored, messageHash)
556
+ return message
557
+
558
+ def handle_trade(self, client: Client, message: dict):
559
+ #
560
+ # spot
561
+ #
562
+ # {
563
+ # topic: 'trade',
564
+ # event: 'trade@btc_usdt',
565
+ # data: {
566
+ # s: 'btc_usdt',
567
+ # i: '228825383103928709',
568
+ # t: 1684258222702,
569
+ # p: '27003.65',
570
+ # q: '0.000796',
571
+ # b: True
572
+ # }
573
+ # }
574
+ #
575
+ # contract
576
+ #
577
+ # {
578
+ # "topic": "trade",
579
+ # "event": "trade@btc_usdt",
580
+ # "data": {
581
+ # "s": "btc_index", # trading pair
582
+ # "p": "50000", # price
583
+ # "a": "0.1" # Quantity
584
+ # "m": "BID" # Deal side BID:Buy ASK:Sell
585
+ # "t": 123124124 # timestamp
586
+ # }
587
+ # }
588
+ #
589
+ data = self.safe_dict(message, 'data')
590
+ marketId = self.safe_string_lower(data, 's')
591
+ if marketId is not None:
592
+ trade = self.parse_trade(data)
593
+ i = self.safe_string(data, 'i')
594
+ tradeType = 'spot' if (i is not None) else 'contract'
595
+ market = self.safe_market(marketId, None, None, tradeType)
596
+ symbol = market['symbol']
597
+ event = self.safe_string(message, 'event')
598
+ tradesArray = self.safe_value(self.trades, symbol)
599
+ if tradesArray is None:
600
+ tradesLimit = self.safe_integer(self.options, 'tradesLimit', 1000)
601
+ tradesArray = ArrayCache(tradesLimit)
602
+ self.trades[symbol] = tradesArray
603
+ tradesArray.append(trade)
604
+ messageHash = event + '::' + tradeType
605
+ client.resolve(tradesArray, messageHash)
606
+ return message
607
+
608
+ def handle_order_book(self, client: Client, message: dict):
609
+ #
610
+ # spot
611
+ #
612
+ # {
613
+ # "topic": "depth",
614
+ # "event": "depth@btc_usdt,20",
615
+ # "data": {
616
+ # "s": "btc_usdt", # symbol
617
+ # "fi": 1681433733351, # firstUpdateId = previous lastUpdateId + 1
618
+ # "i": 1681433733371, # updateId
619
+ # "a": [ # asks(sell order)
620
+ # [ # [0]price, [1]quantity
621
+ # "34000", # price
622
+ # "1.2" # quantity
623
+ # ],
624
+ # [
625
+ # "34001",
626
+ # "2.3"
627
+ # ]
628
+ # ],
629
+ # "b": [ # bids(buy order)
630
+ # [
631
+ # "32000",
632
+ # "0.2"
633
+ # ],
634
+ # [
635
+ # "31000",
636
+ # "0.5"
637
+ # ]
638
+ # ]
639
+ # }
640
+ # }
641
+ #
642
+ # contract
643
+ #
644
+ # {
645
+ # "topic": "depth",
646
+ # "event": "depth@btc_usdt,20",
647
+ # "data": {
648
+ # s: "btc_usdt",
649
+ # pu: "548111455664",
650
+ # fu: "548111455665",
651
+ # u: "548111455667",
652
+ # a: [
653
+ # [
654
+ # "26841.5",
655
+ # "50210",
656
+ # ],
657
+ # ],
658
+ # b: [
659
+ # [
660
+ # "26841",
661
+ # "67075",
662
+ # ],
663
+ # ],
664
+ # t: 1684530667083,
665
+ # }
666
+ # }
667
+ #
668
+ data = self.safe_dict(message, 'data')
669
+ marketId = self.safe_string(data, 's')
670
+ if marketId is not None:
671
+ event = self.safe_string(message, 'event')
672
+ splitEvent = event.split(',')
673
+ event = self.safe_string(splitEvent, 0)
674
+ tradeType = 'contract' if ('fu' in data) else 'spot'
675
+ market = self.safe_market(marketId, None, None, tradeType)
676
+ symbol = market['symbol']
677
+ obAsks = self.safe_list(data, 'a')
678
+ obBids = self.safe_list(data, 'b')
679
+ messageHash = event + '::' + tradeType
680
+ if not (symbol in self.orderbooks):
681
+ subscription = self.safe_dict(client.subscriptions, messageHash, {})
682
+ limit = self.safe_integer(subscription, 'limit')
683
+ self.orderbooks[symbol] = self.order_book({}, limit)
684
+ orderbook = self.orderbooks[symbol]
685
+ nonce = self.safe_integer(orderbook, 'nonce')
686
+ if nonce is None:
687
+ cacheLength = len(orderbook.cache)
688
+ snapshotDelay = self.handle_option('watchOrderBook', 'snapshotDelay', 25)
689
+ if cacheLength == snapshotDelay:
690
+ self.spawn(self.load_order_book, client, messageHash, symbol)
691
+ orderbook.cache.append(data)
692
+ return
693
+ if obAsks is not None:
694
+ asks = orderbook['asks']
695
+ for i in range(0, len(obAsks)):
696
+ ask = obAsks[i]
697
+ price = self.safe_number(ask, 0)
698
+ quantity = self.safe_number(ask, 1)
699
+ asks.store(price, quantity)
700
+ if obBids is not None:
701
+ bids = orderbook['bids']
702
+ for i in range(0, len(obBids)):
703
+ bid = obBids[i]
704
+ price = self.safe_number(bid, 0)
705
+ quantity = self.safe_number(bid, 1)
706
+ bids.store(price, quantity)
707
+ timestamp = self.safe_integer(data, 't')
708
+ orderbook['nonce'] = self.safe_integer_2(data, 'i', 'u')
709
+ orderbook['timestamp'] = timestamp
710
+ orderbook['datetime'] = self.iso8601(timestamp)
711
+ orderbook['symbol'] = symbol
712
+ client.resolve(orderbook, messageHash)
713
+
714
+ def parse_ws_order_trade(self, trade: dict, market: Market = None):
715
+ #
716
+ # {
717
+ # "s": "btc_usdt", # symbol
718
+ # "t": 1656043204763, # time happened time
719
+ # "i": "6216559590087220004", # orderId,
720
+ # "ci": "test123", # clientOrderId
721
+ # "st": "PARTIALLY_FILLED", # state
722
+ # "sd": "BUY", # side BUY/SELL
723
+ # "eq": "2", # executedQty executed quantity
724
+ # "ap": "30000", # avg price
725
+ # "f": "0.002" # fee
726
+ # }
727
+ #
728
+ # contract
729
+ #
730
+ # {
731
+ # "symbol": "btc_usdt", # Trading pair
732
+ # "orderId": "1234", # Order Id
733
+ # "origQty": "34244", # Original Quantity
734
+ # "avgPrice": "123", # Quantity
735
+ # "price": "1111", # Average price
736
+ # "executedQty": "34244", # Volume(Cont)
737
+ # "orderSide": "BUY", # BUY, SELL
738
+ # "positionSide": "LONG", # LONG, SHORT
739
+ # "marginFrozen": "123", # Occupied margin
740
+ # "sourceType": "default", # DEFAULT:normal order,ENTRUST:plan commission,PROFIR:Take Profit and Stop Loss
741
+ # "sourceId" : "1231231", # Triggering conditions ID
742
+ # "state": "", # state:NEW:New order(unfilled);PARTIALLY_FILLED:Partial deal;PARTIALLY_CANCELED:Partial revocation;FILLED:Filled;CANCELED:Cancled;REJECTED:Order failed;EXPIRED:Expired
743
+ # "createTime": 1731231231, # CreateTime
744
+ # "clientOrderId": "204788317630342726"
745
+ # }
746
+ #
747
+ marketId = self.safe_string(trade, 's')
748
+ tradeType = 'contract' if ('symbol' in trade) else 'spot'
749
+ market = self.safe_market(marketId, market, None, tradeType)
750
+ timestamp = self.safe_string(trade, 't')
751
+ return self.safe_trade({
752
+ 'info': trade,
753
+ 'id': None,
754
+ 'timestamp': timestamp,
755
+ 'datetime': self.iso8601(timestamp),
756
+ 'symbol': market['symbol'],
757
+ 'order': self.safe_string(trade, 'i', 'orderId'),
758
+ 'type': self.parse_order_status(self.safe_string(trade, 'st', 'state')),
759
+ 'side': self.safe_string_lower(trade, 'sd', 'orderSide'),
760
+ 'takerOrMaker': None,
761
+ 'price': self.safe_number(trade, 'price'),
762
+ 'amount': self.safe_string(trade, 'origQty'),
763
+ 'cost': None,
764
+ 'fee': {
765
+ 'currency': None,
766
+ 'cost': self.safe_number(trade, 'f'),
767
+ 'rate': None,
768
+ },
769
+ }, market)
770
+
771
+ def parse_ws_order(self, order: dict, market: Market = None):
772
+ #
773
+ # spot
774
+ #
775
+ # {
776
+ # "s": "btc_usdt", # symbol
777
+ # "bc": "btc", # base currency
778
+ # "qc": "usdt", # quotation currency
779
+ # "t": 1656043204763, # happened time
780
+ # "ct": 1656043204663, # create time
781
+ # "i": "6216559590087220004", # order id,
782
+ # "ci": "test123", # client order id
783
+ # "st": "PARTIALLY_FILLED", # state NEW/PARTIALLY_FILLED/FILLED/CANCELED/REJECTED/EXPIRED
784
+ # "sd": "BUY", # side BUY/SELL
785
+ # "tp": "LIMIT", # type LIMIT/MARKET
786
+ # "oq": "4" # original quantity
787
+ # "oqq": 48000, # original quotation quantity
788
+ # "eq": "2", # executed quantity
789
+ # "lq": "2", # remaining quantity
790
+ # "p": "4000", # price
791
+ # "ap": "30000", # avg price
792
+ # "f":"0.002" # fee
793
+ # }
794
+ #
795
+ # contract
796
+ #
797
+ # {
798
+ # "symbol": "btc_usdt", # Trading pair
799
+ # "orderId": "1234", # Order Id
800
+ # "origQty": "34244", # Original Quantity
801
+ # "avgPrice": "123", # Quantity
802
+ # "price": "1111", # Average price
803
+ # "executedQty": "34244", # Volume(Cont)
804
+ # "orderSide": "BUY", # BUY, SELL
805
+ # "positionSide": "LONG", # LONG, SHORT
806
+ # "marginFrozen": "123", # Occupied margin
807
+ # "sourceType": "default", # DEFAULT:normal order,ENTRUST:plan commission,PROFIR:Take Profit and Stop Loss
808
+ # "sourceId" : "1231231", # Triggering conditions ID
809
+ # "state": "", # state:NEW:New order(unfilled);PARTIALLY_FILLED:Partial deal;PARTIALLY_CANCELED:Partial revocation;FILLED:Filled;CANCELED:Cancled;REJECTED:Order failed;EXPIRED:Expired
810
+ # "createTime": 1731231231, # CreateTime
811
+ # "clientOrderId": "204788317630342726"
812
+ # }
813
+ #
814
+ marketId = self.safe_string_2(order, 's', 'symbol')
815
+ tradeType = 'contract' if ('symbol' in order) else 'spot'
816
+ market = self.safe_market(marketId, market, None, tradeType)
817
+ timestamp = self.safe_integer_2(order, 'ct', 'createTime')
818
+ return self.safe_order({
819
+ 'info': order,
820
+ 'id': self.safe_string_2(order, 'i', 'orderId'),
821
+ 'clientOrderId': self.safe_string_2(order, 'ci', 'clientOrderId'),
822
+ 'timestamp': timestamp,
823
+ 'datetime': self.iso8601(timestamp),
824
+ 'lastTradeTimestamp': None,
825
+ 'symbol': market['symbol'],
826
+ 'type': market['type'],
827
+ 'timeInForce': None,
828
+ 'postOnly': None,
829
+ 'side': self.safe_string_lower_2(order, 'sd', 'orderSide'),
830
+ 'price': self.safe_number_2(order, 'p', 'price'),
831
+ 'stopPrice': None,
832
+ 'stopLoss': None,
833
+ 'takeProfit': None,
834
+ 'amount': self.safe_string_2(order, 'oq', 'origQty'),
835
+ 'filled': self.safe_string_2(order, 'eq', 'executedQty'),
836
+ 'remaining': self.safe_string(order, 'lq'),
837
+ 'cost': None,
838
+ 'average': self.safe_string_2(order, 'ap', 'avgPrice'),
839
+ 'status': self.parse_order_status(self.safe_string(order, 'st', 'state')),
840
+ 'fee': {
841
+ 'currency': None,
842
+ 'cost': self.safe_number(order, 'f'),
843
+ },
844
+ 'trades': None,
845
+ }, market)
846
+
847
+ def handle_order(self, client: Client, message: dict):
848
+ #
849
+ # spot
850
+ #
851
+ # {
852
+ # "topic": "order",
853
+ # "event": "order",
854
+ # "data": {
855
+ # "s": "btc_usdt", # symbol
856
+ # "t": 1656043204763, # time happened time
857
+ # "i": "6216559590087220004", # orderId,
858
+ # "ci": "test123", # clientOrderId
859
+ # "st": "PARTIALLY_FILLED", # state
860
+ # "sd": "BUY", # side BUY/SELL
861
+ # "eq": "2", # executedQty executed quantity
862
+ # "ap": "30000", # avg price
863
+ # "f": "0.002" # fee
864
+ # }
865
+ # }
866
+ #
867
+ # contract
868
+ #
869
+ # {
870
+ # "topic": "order",
871
+ # "event": "order@123456",
872
+ # "data": {
873
+ # "symbol": "btc_usdt", # Trading pair
874
+ # "orderId": "1234", # Order Id
875
+ # "origQty": "34244", # Original Quantity
876
+ # "avgPrice": "123", # Quantity
877
+ # "price": "1111", # Average price
878
+ # "executedQty": "34244", # Volume(Cont)
879
+ # "orderSide": "BUY", # BUY, SELL
880
+ # "positionSide": "LONG", # LONG, SHORT
881
+ # "marginFrozen": "123", # Occupied margin
882
+ # "sourceType": "default", # DEFAULT:normal order,ENTRUST:plan commission,PROFIR:Take Profit and Stop Loss
883
+ # "sourceId" : "1231231", # Triggering conditions ID
884
+ # "state": "", # state:NEW:New order(unfilled);PARTIALLY_FILLED:Partial deal;PARTIALLY_CANCELED:Partial revocation;FILLED:Filled;CANCELED:Cancled;REJECTED:Order failed;EXPIRED:Expired
885
+ # "createTime": 1731231231, # CreateTime
886
+ # "clientOrderId": "204788317630342726"
887
+ # }
888
+ # }
889
+ #
890
+ orders = self.orders
891
+ if orders is None:
892
+ limit = self.safe_integer(self.options, 'ordersLimit')
893
+ orders = ArrayCacheBySymbolById(limit)
894
+ self.orders = orders
895
+ order = self.safe_dict(message, 'data', {})
896
+ marketId = self.safe_string_2(order, 's', 'symbol')
897
+ if marketId is not None:
898
+ tradeType = 'contract' if ('symbol' in order) else 'spot'
899
+ market = self.safe_market(marketId, None, None, tradeType)
900
+ parsed = self.parse_ws_order(order, market)
901
+ orders.append(parsed)
902
+ client.resolve(orders, 'order::' + tradeType)
903
+ return message
904
+
905
+ def handle_balance(self, client: Client, message: dict):
906
+ #
907
+ # spot
908
+ #
909
+ # {
910
+ # topic: 'balance',
911
+ # event: 'balance',
912
+ # data: {
913
+ # a: 3513677381884,
914
+ # t: 1684250056775,
915
+ # c: 'usdt',
916
+ # b: '7.71000000',
917
+ # f: '0.00000000',
918
+ # z: 'SPOT'
919
+ # }
920
+ # }
921
+ #
922
+ # contract
923
+ #
924
+ # {
925
+ # "topic": "balance",
926
+ # "event": "balance@123456",
927
+ # "data": {
928
+ # "coin": "usdt",
929
+ # "underlyingType": 1, # 1:Coin-M,2:USDT-M
930
+ # "walletBalance": "123", # Balance
931
+ # "openOrderMarginFrozen": "123", # Frozen order
932
+ # "isolatedMargin": "213", # Isolated Margin
933
+ # "crossedMargin": "0" # Crossed Margin
934
+ # "availableBalance": '2.256114450000000000',
935
+ # "coupon": '0',
936
+ # "bonus": '0'
937
+ # }
938
+ # }
939
+ #
940
+ data = self.safe_dict(message, 'data', {})
941
+ currencyId = self.safe_string_2(data, 'c', 'coin')
942
+ code = self.safe_currency_code(currencyId)
943
+ account = self.account()
944
+ account['free'] = self.safe_string(data, 'availableBalance')
945
+ account['used'] = self.safe_string(data, 'f')
946
+ account['total'] = self.safe_string_2(data, 'b', 'walletBalance')
947
+ self.balance[code] = account
948
+ self.balance = self.safe_balance(self.balance)
949
+ tradeType = 'contract' if ('coin' in data) else 'spot'
950
+ client.resolve(self.balance, 'balance::' + tradeType)
951
+
952
+ def handle_my_trades(self, client: Client, message: dict):
953
+ #
954
+ # spot
955
+ #
956
+ # {
957
+ # "topic": "trade",
958
+ # "event": "trade",
959
+ # "data": {
960
+ # "s": "btc_usdt", # symbol
961
+ # "t": 1656043204763, # time
962
+ # "i": "6316559590087251233", # tradeId
963
+ # "oi": "6216559590087220004", # orderId
964
+ # "p": "30000", # trade price
965
+ # "q": "3", # qty quantity
966
+ # "v": "90000" # volume trade amount
967
+ # }
968
+ # }
969
+ #
970
+ # contract
971
+ #
972
+ # {
973
+ # "topic": "trade",
974
+ # "event": "trade@123456",
975
+ # "data": {
976
+ # "symbol": 'btc_usdt',
977
+ # "orderSide": 'SELL',
978
+ # "positionSide": 'LONG',
979
+ # "orderId": '231485367663419328',
980
+ # "price": '27152.7',
981
+ # "quantity": '33',
982
+ # "marginUnfrozen": '2.85318000',
983
+ # "timestamp": 1684892412565
984
+ # }
985
+ # }
986
+ #
987
+ data = self.safe_dict(message, 'data', {})
988
+ stored = self.myTrades
989
+ if stored is None:
990
+ limit = self.safe_integer(self.options, 'tradesLimit', 1000)
991
+ stored = ArrayCacheBySymbolById(limit)
992
+ self.myTrades = stored
993
+ parsedTrade = self.parse_trade(data)
994
+ market = self.market(parsedTrade['symbol'])
995
+ stored.append(parsedTrade)
996
+ tradeType = 'contract' if market['contract'] else 'spot'
997
+ client.resolve(stored, 'trade::' + tradeType)
998
+
999
+ def handle_message(self, client: Client, message):
1000
+ event = self.safe_string(message, 'event')
1001
+ if event == 'pong':
1002
+ client.onPong()
1003
+ elif event is not None:
1004
+ topic = self.safe_string(message, 'topic')
1005
+ methods = {
1006
+ 'kline': self.handle_ohlcv,
1007
+ 'depth': self.handle_order_book,
1008
+ 'depth_update': self.handle_order_book,
1009
+ 'ticker': self.handle_ticker,
1010
+ 'agg_ticker': self.handle_ticker,
1011
+ 'tickers': self.handle_tickers,
1012
+ 'agg_tickers': self.handle_tickers,
1013
+ 'balance': self.handle_balance,
1014
+ 'order': self.handle_order,
1015
+ }
1016
+ method = self.safe_value(methods, topic)
1017
+ if topic == 'trade':
1018
+ data = self.safe_dict(message, 'data')
1019
+ if ('oi' in data) or ('orderId' in data):
1020
+ method = self.handle_my_trades
1021
+ else:
1022
+ method = self.handle_trade
1023
+ if method is not None:
1024
+ method(client, message)
1025
+
1026
+ def ping(self, client: Client):
1027
+ client.lastPong = self.milliseconds()
1028
+ return 'ping'
1029
+
1030
+ def handle_error_message(self, client: Client, message: dict):
1031
+ #
1032
+ # {
1033
+ # "id": "123",
1034
+ # "code": 401,
1035
+ # "msg": "token expire"
1036
+ # }
1037
+ #
1038
+ msg = self.safe_string(message, 'msg')
1039
+ if (msg == 'invalid_listen_key') or (msg == 'token expire'):
1040
+ client.subscriptions['token'] = None
1041
+ self.get_listen_key(True)
1042
+ return
1043
+ client.reject(message)