ccxt 4.4.88__py2.py3-none-any.whl → 4.4.91__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 (101) hide show
  1. ccxt/__init__.py +1 -3
  2. ccxt/abstract/bitget.py +58 -0
  3. ccxt/abstract/bitrue.py +65 -65
  4. ccxt/abstract/cryptocom.py +2 -0
  5. ccxt/abstract/luno.py +1 -0
  6. ccxt/async_support/__init__.py +1 -3
  7. ccxt/async_support/base/exchange.py +6 -3
  8. ccxt/async_support/base/ws/client.py +173 -64
  9. ccxt/async_support/base/ws/future.py +23 -50
  10. ccxt/async_support/binance.py +2 -2
  11. ccxt/async_support/bingx.py +55 -29
  12. ccxt/async_support/bitget.py +469 -147
  13. ccxt/async_support/bitmex.py +2 -1
  14. ccxt/async_support/bitrue.py +72 -66
  15. ccxt/async_support/bitvavo.py +34 -0
  16. ccxt/async_support/btcalpha.py +35 -0
  17. ccxt/async_support/btcbox.py +35 -0
  18. ccxt/async_support/btcmarkets.py +35 -0
  19. ccxt/async_support/btcturk.py +35 -0
  20. ccxt/async_support/bybit.py +9 -3
  21. ccxt/async_support/cex.py +61 -0
  22. ccxt/async_support/coinbase.py +1 -3
  23. ccxt/async_support/cryptocom.py +66 -2
  24. ccxt/async_support/cryptomus.py +1 -1
  25. ccxt/async_support/delta.py +2 -2
  26. ccxt/async_support/digifinex.py +39 -99
  27. ccxt/async_support/exmo.py +14 -7
  28. ccxt/async_support/gate.py +14 -7
  29. ccxt/async_support/hashkey.py +15 -28
  30. ccxt/async_support/hollaex.py +27 -22
  31. ccxt/async_support/hyperliquid.py +104 -53
  32. ccxt/async_support/kraken.py +54 -50
  33. ccxt/async_support/luno.py +87 -1
  34. ccxt/async_support/mexc.py +1 -0
  35. ccxt/async_support/modetrade.py +2 -2
  36. ccxt/async_support/okx.py +2 -1
  37. ccxt/async_support/paradex.py +1 -1
  38. ccxt/async_support/phemex.py +16 -8
  39. ccxt/async_support/tradeogre.py +3 -3
  40. ccxt/async_support/xt.py +1 -1
  41. ccxt/base/exchange.py +20 -8
  42. ccxt/binance.py +2 -2
  43. ccxt/bingx.py +55 -29
  44. ccxt/bitget.py +469 -147
  45. ccxt/bitmex.py +2 -1
  46. ccxt/bitrue.py +72 -66
  47. ccxt/bitvavo.py +34 -0
  48. ccxt/btcalpha.py +35 -0
  49. ccxt/btcbox.py +35 -0
  50. ccxt/btcmarkets.py +35 -0
  51. ccxt/btcturk.py +35 -0
  52. ccxt/bybit.py +9 -3
  53. ccxt/cex.py +61 -0
  54. ccxt/coinbase.py +1 -3
  55. ccxt/cryptocom.py +66 -2
  56. ccxt/cryptomus.py +1 -1
  57. ccxt/delta.py +2 -2
  58. ccxt/digifinex.py +39 -99
  59. ccxt/exmo.py +13 -7
  60. ccxt/gate.py +14 -7
  61. ccxt/hashkey.py +15 -28
  62. ccxt/hollaex.py +27 -22
  63. ccxt/hyperliquid.py +104 -53
  64. ccxt/kraken.py +53 -50
  65. ccxt/luno.py +87 -1
  66. ccxt/mexc.py +1 -0
  67. ccxt/modetrade.py +2 -2
  68. ccxt/okx.py +2 -1
  69. ccxt/paradex.py +1 -1
  70. ccxt/phemex.py +16 -8
  71. ccxt/pro/__init__.py +1 -127
  72. ccxt/pro/bitstamp.py +1 -1
  73. ccxt/pro/bybit.py +6 -136
  74. ccxt/pro/coinbase.py +2 -0
  75. ccxt/pro/cryptocom.py +27 -0
  76. ccxt/pro/kraken.py +249 -267
  77. ccxt/pro/mexc.py +0 -1
  78. ccxt/tradeogre.py +3 -3
  79. ccxt/xt.py +1 -1
  80. {ccxt-4.4.88.dist-info → ccxt-4.4.91.dist-info}/METADATA +64 -23
  81. {ccxt-4.4.88.dist-info → ccxt-4.4.91.dist-info}/RECORD +84 -101
  82. ccxt/abstract/coinlist.py +0 -57
  83. ccxt/async_support/base/ws/aiohttp_client.py +0 -147
  84. ccxt/async_support/bitcoincom.py +0 -18
  85. ccxt/async_support/bitfinex1.py +0 -1711
  86. ccxt/async_support/bitpanda.py +0 -17
  87. ccxt/async_support/coinlist.py +0 -2542
  88. ccxt/async_support/poloniexfutures.py +0 -1875
  89. ccxt/bitcoincom.py +0 -18
  90. ccxt/bitfinex1.py +0 -1710
  91. ccxt/bitpanda.py +0 -17
  92. ccxt/coinlist.py +0 -2542
  93. ccxt/poloniexfutures.py +0 -1875
  94. ccxt/pro/bitcoincom.py +0 -35
  95. ccxt/pro/bitfinex1.py +0 -635
  96. ccxt/pro/bitpanda.py +0 -16
  97. ccxt/pro/poloniexfutures.py +0 -1004
  98. ccxt/pro/wazirx.py +0 -766
  99. {ccxt-4.4.88.dist-info → ccxt-4.4.91.dist-info}/LICENSE.txt +0 -0
  100. {ccxt-4.4.88.dist-info → ccxt-4.4.91.dist-info}/WHEEL +0 -0
  101. {ccxt-4.4.88.dist-info → ccxt-4.4.91.dist-info}/top_level.txt +0 -0
@@ -1,1004 +0,0 @@
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
8
- from ccxt.base.types import Any, Balances, Int, Order, OrderBook, Str, Ticker, Trade
9
- from ccxt.async_support.base.ws.client import Client
10
- from typing import List
11
- from ccxt.base.errors import AuthenticationError
12
- from ccxt.base.errors import BadRequest
13
- from ccxt.base.errors import ChecksumError
14
-
15
-
16
- class poloniexfutures(ccxt.async_support.poloniexfutures):
17
-
18
- def describe(self) -> Any:
19
- return self.deep_extend(super(poloniexfutures, self).describe(), {
20
- 'has': {
21
- 'ws': True,
22
- 'cancelAllOrdersWs': False,
23
- 'cancelOrdersWs': False,
24
- 'cancelOrderWs': False,
25
- 'createOrderWs': False,
26
- 'editOrderWs': False,
27
- 'fetchBalanceWs': False,
28
- 'fetchOpenOrdersWs': False,
29
- 'fetchOrderWs': False,
30
- 'fetchTradesWs': False,
31
- 'watchOHLCV': False,
32
- 'watchOrderBook': True,
33
- 'watchTicker': True,
34
- 'watchTickers': False,
35
- 'watchTrades': True,
36
- 'watchTradesForSymbols': False,
37
- 'watchBalance': True,
38
- 'watchOrders': True,
39
- 'watchMyTrades': False,
40
- 'watchPosition': None,
41
- 'watchPositions': False,
42
- },
43
- 'urls': {
44
- 'api': {
45
- 'ws': 'wss://futures-apiws.poloniex.com/endpoint',
46
- },
47
- },
48
- 'options': {
49
- 'tradesLimit': 1000,
50
- 'ordersLimit': 1000,
51
- 'watchTicker': {
52
- 'method': '/contractMarket/ticker', # can also be /contractMarket/snapshot
53
- },
54
- 'watchOrders': {
55
- 'method': '/contractMarket/tradeOrders', # can also be /contractMarket/advancedOrders
56
- },
57
- 'watchOrderBook': {
58
- 'method': '/contractMarket/level2', # can also be '/contractMarket/level3v2'
59
- 'snapshotDelay': 5,
60
- 'snapshotMaxRetries': 3,
61
- 'checksum': True,
62
- },
63
- 'streamLimit': 5, # called tunnels by poloniexfutures docs
64
- 'streamBySubscriptionsHash': {},
65
- 'streamIndex': -1,
66
- },
67
- 'streaming': {
68
- 'keepAlive': 30000,
69
- 'maxPingPongMisses': 2.0,
70
- },
71
- })
72
-
73
- async def negotiate(self, privateChannel, params={}):
74
- connectId = 'private' if privateChannel else 'public'
75
- urls = self.safe_value(self.options, 'urls', {})
76
- if connectId in urls:
77
- # return urls[connectId]
78
- storedFuture = urls[connectId]
79
- return await storedFuture
80
- # we store an awaitable to the url
81
- # so that multiple calls don't asynchronously
82
- # fetch different urls and overwrite each other
83
- urls[connectId] = self.spawn(self.negotiate_helper, privateChannel, params)
84
- self.options['urls'] = urls
85
- future = urls[connectId]
86
- return await future
87
-
88
- async def negotiate_helper(self, privateChannel, params={}):
89
- response = None
90
- connectId = 'private' if privateChannel else 'public'
91
- try:
92
- if privateChannel:
93
- response = await self.privatePostBulletPrivate(params)
94
- #
95
- # {
96
- # "code": "200000",
97
- # "data": {
98
- # "instanceServers": [
99
- # {
100
- # "pingInterval": 50000,
101
- # "endpoint": "wss://push-private.kucoin.com/endpoint",
102
- # "protocol": "websocket",
103
- # "encrypt": True,
104
- # "pingTimeout": 10000
105
- # }
106
- # ],
107
- # "token": "2neAiuYvAU61ZDXANAGAsiL4-iAExhsBXZxftpOeh_55i3Ysy2q2LEsEWU64mdzUOPusi34M_wGoSf7iNyEWJ1UQy47YbpY4zVdzilNP-Bj3iXzrjjGlWtiYB9J6i9GjsxUuhPw3BlrzazF6ghq4Lzf7scStOz3KkxjwpsOBCH4=.WNQmhZQeUKIkh97KYgU0Lg=="
108
- # }
109
- # }
110
- #
111
- else:
112
- response = await self.publicPostBulletPublic(params)
113
- data = self.safe_value(response, 'data', {})
114
- instanceServers = self.safe_value(data, 'instanceServers', [])
115
- firstInstanceServer = self.safe_value(instanceServers, 0)
116
- pingInterval = self.safe_integer(firstInstanceServer, 'pingInterval')
117
- endpoint = self.safe_string(firstInstanceServer, 'endpoint')
118
- token = self.safe_string(data, 'token')
119
- result = endpoint + '?' + self.urlencode({
120
- 'token': token,
121
- 'privateChannel': privateChannel,
122
- 'connectId': connectId,
123
- })
124
- client = self.client(result)
125
- client.keepAlive = pingInterval
126
- return result
127
- except Exception as e:
128
- future = self.safe_value(self.options['urls'], connectId)
129
- future.reject(e)
130
- del self.options['urls'][connectId]
131
- return None
132
-
133
- def request_id(self):
134
- requestId = self.sum(self.safe_integer(self.options, 'requestId', 0), 1)
135
- self.options['requestId'] = requestId
136
- return requestId
137
-
138
- async def subscribe(self, name: str, isPrivate: bool, symbol: Str = None, subscription=None, params={}):
139
- """
140
- @ignore
141
- Connects to a websocket channel
142
- :param str name: name of the channel and suscriptionHash
143
- :param bool isPrivate: True for the authenticated url, False for the public url
144
- :param str symbol: is required for all public channels, not required for private channels(except position)
145
- :param dict subscription: subscription parameters
146
- :param dict [params]: extra parameters specific to the poloniex api
147
- :returns dict: data from the websocket stream
148
- """
149
- url = await self.negotiate(isPrivate)
150
- if symbol is not None:
151
- market = self.market(symbol)
152
- marketId = market['id']
153
- name += ':' + marketId
154
- messageHash = name
155
- tunnelId = await self.stream(url, messageHash)
156
- requestId = self.request_id()
157
- subscribe: dict = {
158
- 'id': requestId,
159
- 'type': 'subscribe',
160
- 'topic': name, # Subscribed topic. Some topics support subscribe to the data of multiple trading pairs through ",".
161
- 'privateChannel': isPrivate, # Adopt the private channel or not. Set by default.
162
- 'response': True, # Whether the server needs to return the receipt information of self subscription or not. Set by default.
163
- 'tunnelId': tunnelId,
164
- }
165
- subscriptionRequest: dict = {
166
- 'id': requestId,
167
- }
168
- if subscription is None:
169
- subscription = subscriptionRequest
170
- else:
171
- subscription = self.extend(subscriptionRequest, subscription)
172
- request = self.extend(subscribe, params)
173
- return await self.watch(url, messageHash, request, name, subscriptionRequest)
174
-
175
- def on_close(self, client, error):
176
- self.options['streamBySubscriptionsHash'] = {}
177
- super(poloniexfutures, self).on_close(client, error)
178
-
179
- async def stream(self, url, subscriptionHash):
180
- streamBySubscriptionsHash = self.safe_value(self.options, 'streamBySubscriptionsHash', {})
181
- stream = self.safe_string(streamBySubscriptionsHash, subscriptionHash)
182
- if stream is None:
183
- streamIndex = self.safe_integer(self.options, 'streamIndex', -1)
184
- streamLimit = self.safe_value(self.options, 'streamLimit')
185
- streamIndex = streamIndex + 1
186
- normalizedIndex = streamIndex % streamLimit
187
- self.options['streamIndex'] = streamIndex
188
- streamIndexString = self.number_to_string(normalizedIndex)
189
- stream = 'stream-' + streamIndexString
190
- self.options['streamBySubscriptionsHash'][subscriptionHash] = stream
191
- messageHash = 'tunnel:' + stream
192
- request: dict = {
193
- 'id': messageHash,
194
- 'type': 'openTunnel',
195
- 'newTunnelId': stream,
196
- 'response': True,
197
- }
198
- subscription: dict = {
199
- 'id': messageHash,
200
- 'method': self.handle_new_stream,
201
- }
202
- await self.watch(url, messageHash, request, messageHash, subscription)
203
- return stream
204
-
205
- def handle_order_book_subscription(self, client: Client, message, subscription):
206
- symbol = self.safe_string(subscription, 'symbol')
207
- limit = self.safe_integer(subscription, 'limit')
208
- self.orderbooks[symbol] = self.order_book({}, limit)
209
-
210
- def handle_subscription_status(self, client: Client, message):
211
- #
212
- # {
213
- # "id": "1578090438322",
214
- # "type": "ack"
215
- # }
216
- #
217
- id = self.safe_string(message, 'id')
218
- subscriptionsById = self.index_by(client.subscriptions, 'id')
219
- subscription = self.safe_value(subscriptionsById, id, {})
220
- method = self.safe_value(subscription, 'method')
221
- if method is not None:
222
- method(client, message, subscription)
223
- return message
224
-
225
- def handle_new_stream(self, client: Client, message, subscription):
226
- #
227
- # {
228
- # "id": "1545910840805",
229
- # "type": "ack"
230
- # }
231
- #
232
- messageHash = self.safe_string(message, 'id')
233
- client.resolve(message, messageHash)
234
-
235
- async def watch_ticker(self, symbol: str, params={}) -> Ticker:
236
- """
237
- watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
238
-
239
- https://api-docs.poloniex.com/futures/websocket/public#get-real-time-symbol-ticker
240
-
241
- :param str symbol: unified symbol of the market to fetch the ticker for
242
- :param dict [params]: extra parameters specific to the exchange API endpoint
243
- :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
244
- """
245
- await self.load_markets()
246
- symbol = self.symbol(symbol)
247
- name = '/contractMarket/ticker'
248
- return await self.subscribe(name, False, symbol, None, params)
249
-
250
- async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
251
- """
252
- get the list of most recent trades for a particular symbol
253
-
254
- https://api-docs.poloniex.com/futures/websocket/public#full-matching-engine-datalevel-3
255
-
256
- :param str symbol: unified symbol of the market to fetch trades for
257
- :param int [since]: timestamp in ms of the earliest trade to fetch
258
- :param int [limit]: the maximum amount of trades to fetch
259
- :param dict [params]: extra parameters specific to the exchange API endpoint
260
- :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
261
- """
262
- await self.load_markets()
263
- options = self.safe_value(self.options, 'watchTrades')
264
- name = self.safe_string(options, 'method', '/contractMarket/execution') # can also be /contractMarket/snapshot
265
- name, params = self.handle_option_and_params(params, 'method', 'name', name)
266
- symbol = self.symbol(symbol)
267
- trades = await self.subscribe(name, False, symbol, None, params)
268
- if self.newUpdates:
269
- limit = trades.getLimit(symbol, limit)
270
- return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
271
-
272
- async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
273
- """
274
- watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
275
-
276
- https://api-docs.poloniex.com/futures/websocket/public#level-2-market-data
277
-
278
- :param str symbol: unified symbol of the market to fetch the order book for
279
- :param int [limit]: not used by poloniexfutures watchOrderBook
280
- :param dict [params]: extra parameters specific to the exchange API endpoint
281
- :param str [params.method]: the method to use. Defaults to /contractMarket/level2 can also be /contractMarket/level3v2 to receive the raw stream of orders
282
- :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
283
- """
284
- await self.load_markets()
285
- options = self.safe_value(self.options, 'watchOrderBook')
286
- name = self.safe_string(options, 'method', '/contractMarket/level2') # can also be /contractMarket/level2, /contractMarket/level2Depth5:{symbol}, /contractMarket/level2Depth50:{symbol}
287
- name, params = self.handle_option_and_params(params, 'method', 'name', name)
288
- if name == '/contractMarket/level2' and limit is not None:
289
- if limit != 5 and limit != 50:
290
- raise BadRequest(self.id + ' watchOrderBook limit argument must be none, 5 or 50 if using method /contractMarket/level2')
291
- name += 'Depth' + self.number_to_string(limit)
292
- subscription: dict = {
293
- 'symbol': symbol,
294
- 'limit': limit,
295
- 'method': self.handle_order_book_subscription,
296
- }
297
- orderbook = await self.subscribe(name, False, symbol, subscription, params)
298
- return orderbook.limit()
299
-
300
- async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
301
- """
302
- watches information on multiple orders made by the user
303
-
304
- https://api-docs.poloniex.com/futures/websocket/user-messages#private-messages
305
-
306
- :param str symbol: filter by unified market symbol of the market orders were made in
307
- :param int [since]: the earliest time in ms to fetch orders for
308
- :param int [limit]: the maximum number of order structures to retrieve
309
- :param dict [params]: extra parameters specific to the exchange API endpoint
310
- :param str [params.method]: the method to use will default to /contractMarket/tradeOrders. Set to /contractMarket/advancedOrders to watch stop orders
311
- :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
312
- """
313
- await self.load_markets()
314
- options = self.safe_value(self.options, 'watchOrders')
315
- name = self.safe_string(options, 'method', '/contractMarket/tradeOrders')
316
- orders = await self.subscribe(name, True, None, None, params)
317
- if self.newUpdates:
318
- limit = orders.getLimit(symbol, limit)
319
- orders = self.filter_by_symbol_since_limit(orders, symbol, since, limit)
320
- length = len(orders)
321
- if length == 0:
322
- return await self.watch_orders(symbol, since, limit, params)
323
- return orders
324
-
325
- async def watch_balance(self, params={}) -> Balances:
326
- """
327
- watch balance and get the amount of funds available for trading or funds locked in orders
328
-
329
- https://api-docs.poloniex.com/futures/websocket/user-messages#account-balance-events
330
-
331
- :param dict [params]: extra parameters specific to the exchange API endpoint
332
- :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
333
- """
334
- await self.load_markets()
335
- name = '/contractAccount/wallet'
336
- return await self.subscribe(name, True, None, None, params)
337
-
338
- def handle_trade(self, client: Client, message):
339
- #
340
- # {
341
- # "data": {
342
- # "makerUserId": "1410336",
343
- # "symbol": "BTCUSDTPERP",
344
- # "sequence": 267913,
345
- # "side": "buy",
346
- # "size": 2,
347
- # "price": 28409.5,
348
- # "takerOrderId": "6426f9f15782c8000776995f",
349
- # "makerOrderId": "6426f9f141406b0008df976e",
350
- # "takerUserId": "1410880",
351
- # "tradeId": "6426f9f1de029f0001e334dd",
352
- # "ts": 1680275953739092500,
353
- # },
354
- # "subject": "match",
355
- # "topic": "/contractMarket/execution:BTCUSDTPERP",
356
- # "type": "message",
357
- # }
358
- #
359
- data = self.safe_value(message, 'data', {})
360
- marketId = self.safe_string(data, 'symbol')
361
- if marketId is not None:
362
- trade = self.parse_ws_trade(data)
363
- symbol = trade['symbol']
364
- messageHash = '/contractMarket/execution:' + marketId
365
- stored = self.safe_value(self.trades, symbol)
366
- if stored is None:
367
- tradesLimit = self.safe_integer(self.options, 'tradesLimit', 1000)
368
- stored = ArrayCache(tradesLimit)
369
- self.trades[symbol] = stored
370
- stored.append(trade)
371
- client.resolve(stored, messageHash)
372
- return message
373
-
374
- def parse_ws_trade(self, trade, market=None):
375
- #
376
- # handleTrade
377
- #
378
- # {
379
- # "makerUserId": "1410880",
380
- # "symbol": "BTCUSDTPERP",
381
- # "sequence": 731390,
382
- # "side": "sell",
383
- # "size": 2,
384
- # "price": 29372.4,
385
- # "takerOrderId": "644ef0fdd64748000759218a",
386
- # "makerOrderId": "644ef0fd25f4a50007f12fc5",
387
- # "takerUserId": "1410880",
388
- # "tradeId": "644ef0fdde029f0001eec346",
389
- # "ts": 1682895101923194000
390
- # }
391
- #
392
- marketId = self.safe_string(trade, 'symbol')
393
- market = self.safe_market(marketId, market)
394
- timestamp = self.safe_integer_product(trade, 'ts', 0.000001)
395
- return self.safe_trade({
396
- 'info': trade,
397
- 'id': self.safe_string(trade, 'tradeId'),
398
- 'symbol': self.safe_string(market, 'symbol'),
399
- 'timestamp': timestamp,
400
- 'datetime': self.iso8601(timestamp),
401
- 'order': self.safe_string_2(trade, 'takerOrderId', 'makerOrderId'),
402
- 'type': None,
403
- 'side': self.safe_string(trade, 'side'),
404
- 'takerOrMaker': None,
405
- 'price': self.safe_string(trade, 'price'),
406
- 'amount': self.safe_string_2(trade, 'matchSize', 'size'),
407
- 'cost': None,
408
- 'fee': None,
409
- }, market)
410
-
411
- def parse_ws_order_trade(self, trade, market=None):
412
- #
413
- # {
414
- # "symbol": "BTC_USDT",
415
- # "type": "LIMIT",
416
- # "quantity": "1",
417
- # "orderId": "32471407854219264",
418
- # "tradeFee": "0",
419
- # "clientOrderId": "",
420
- # "accountType": "SPOT",
421
- # "feeCurrency": "",
422
- # "eventType": "place",
423
- # "source": "API",
424
- # "side": "BUY",
425
- # "filledQuantity": "0",
426
- # "filledAmount": "0",
427
- # "matchRole": "MAKER",
428
- # "state": "NEW",
429
- # "tradeTime": 0,
430
- # "tradeAmount": "0",
431
- # "orderAmount": "0",
432
- # "createTime": 1648708186922,
433
- # "price": "47112.1",
434
- # "tradeQty": "0",
435
- # "tradePrice": "0",
436
- # "tradeId": "0",
437
- # "ts": 1648708187469
438
- # }
439
- #
440
- timestamp = self.safe_integer(trade, 'tradeTime')
441
- marketId = self.safe_string(trade, 'symbol')
442
- return self.safe_trade({
443
- 'info': trade,
444
- 'id': self.safe_string(trade, 'tradeId'),
445
- 'symbol': self.safe_symbol(marketId, market),
446
- 'timestamp': timestamp,
447
- 'datetime': self.iso8601(timestamp),
448
- 'order': self.safe_string(trade, 'orderId'),
449
- 'type': self.safe_string_lower(trade, 'type'),
450
- 'side': self.safe_string(trade, 'side'),
451
- 'takerOrMaker': self.safe_string_lower(trade, 'matchRole'),
452
- 'price': self.safe_string(trade, 'price'),
453
- 'amount': self.safe_string(trade, 'tradeAmount'), # ? tradeQty?
454
- 'cost': None,
455
- 'fee': {
456
- 'rate': None,
457
- 'cost': self.safe_string(trade, 'tradeFee'),
458
- 'currency': self.safe_string(trade, 'feeCurrency'),
459
- },
460
- }, market)
461
-
462
- def handle_order(self, client: Client, message):
463
- #
464
- # {
465
- # "data": {
466
- # "symbol": "ADAUSDTPERP",
467
- # "orderType": "limit",
468
- # "side": "buy",
469
- # "canceledSize": "1",
470
- # "orderId": "642b4d4c0494cd0007c76813",
471
- # "type": "canceled",
472
- # "orderTime": "1680559436101909048",
473
- # "size": "1",
474
- # "filledSize": "0",
475
- # "marginType": 1,
476
- # "price": "0.25",
477
- # "remainSize": "0",
478
- # "clientOid": "112cbbf1-95a3-4917-957c-d3a87d81f853",
479
- # "status": "done",
480
- # "ts": 1680559677560686600
481
- # },
482
- # "subject": "orderChange",
483
- # "topic": "/contractMarket/tradeOrders",
484
- # "channelType": "private",
485
- # "type": "message",
486
- # "userId": "1139790"
487
- # }
488
- # stop order
489
- # {
490
- # "data": {
491
- # "orderType": "stop",
492
- # "symbol": "BTCUSDTPERP",
493
- # "side": "buy",
494
- # "stopPriceType": "TP",
495
- # "orderId": "64514fe1850d2100074378f6",
496
- # "type": "open",
497
- # "createdAt": 1683050465847,
498
- # "stopPrice": "29000",
499
- # "size": 2,
500
- # "stop": "up",
501
- # "marginType": 0,
502
- # "orderPrice": "28552.9",
503
- # "ts": 1683050465847597300
504
- # },
505
- # "subject": "stopOrder",
506
- # "topic": "/contractMarket/advancedOrders",
507
- # "channelType": "private",
508
- # "id": "64514fe1850d2100074378fa",
509
- # "type": "message",
510
- # "userId": "1160396"
511
- # }
512
- #
513
- data = self.safe_value(message, 'data', {})
514
- orders = self.orders
515
- if orders is None:
516
- limit = self.safe_integer(self.options, 'ordersLimit')
517
- orders = ArrayCacheBySymbolById(limit)
518
- self.orders = orders
519
- messageHash = '/contractMarket/tradeOrders'
520
- parsed = self.parse_ws_order(data)
521
- orders.append(parsed)
522
- client.resolve(orders, messageHash)
523
- return message
524
-
525
- def parse_order_status(self, status: str, type: str):
526
- """
527
- @ignore
528
- :param str status: "match", "open", "done"
529
- :param str type: "open", "match", "filled", "canceled", "update"
530
- :returns str:
531
- """
532
- types: dict = {
533
- 'canceled': 'canceled',
534
- 'cancel': 'canceled',
535
- 'filled': 'closed',
536
- }
537
- parsedStatus = self.safe_string(types, type)
538
- if parsedStatus is None:
539
- statuses: dict = {
540
- 'open': 'open',
541
- 'match': 'open',
542
- 'done': 'closed',
543
- }
544
- parsedStatus = self.safe_string(statuses, status, status)
545
- return parsedStatus
546
-
547
- def parse_ws_order(self, order, market=None):
548
- #
549
- # {
550
- # "symbol": "ADAUSDTPERP",
551
- # "orderType": "limit",
552
- # "side": "buy",
553
- # "canceledSize": "1",
554
- # "orderId": "642b4d4c0494cd0007c76813",
555
- # "type": "canceled",
556
- # "orderTime": "1680559436101909048",
557
- # "size": "1",
558
- # "filledSize": "0",
559
- # "marginType": 1,
560
- # "price": "0.25",
561
- # "remainSize": "0",
562
- # "clientOid": "112cbbf1-95a3-4917-957c-d3a87d81f853",
563
- # "status": "done",
564
- # "ts": 1680559677560686600
565
- # }
566
- # stop
567
- # {
568
- # "orderType": "stop",
569
- # "symbol": "BTCUSDTPERP",
570
- # "side": "buy",
571
- # "stopPriceType": "TP",
572
- # "orderId": "64514fe1850d2100074378f6",
573
- # "type": "open",
574
- # "createdAt": 1683050465847,
575
- # "stopPrice": "29000",
576
- # "size": 2,
577
- # "stop": "up",
578
- # "marginType": 0,
579
- # "orderPrice": "28552.9",
580
- # "ts": 1683050465847597300
581
- # }
582
- #
583
- id = self.safe_string(order, 'orderId')
584
- clientOrderId = self.safe_string(order, 'clientOid')
585
- marketId = self.safe_string(order, 'symbol')
586
- timestamp = self.safe_integer_product_2(order, 'orderTime', 'ts', 0.000001)
587
- status = self.safe_string(order, 'status')
588
- messageType = self.safe_string(order, 'type')
589
- return self.safe_order({
590
- 'info': order,
591
- 'symbol': self.safe_symbol(marketId, market),
592
- 'id': id,
593
- 'clientOrderId': clientOrderId,
594
- 'timestamp': timestamp,
595
- 'datetime': self.iso8601(timestamp),
596
- 'lastTradeTimestamp': None,
597
- 'type': self.safe_string(order, 'orderType'),
598
- 'timeInForce': None,
599
- 'postOnly': None,
600
- 'side': self.safe_string(order, 'side'),
601
- 'price': self.safe_string_2(order, 'price', 'orderPrice'),
602
- 'stopPrice': self.safe_string(order, 'stopPrice'),
603
- 'triggerPrice': None,
604
- 'amount': self.safe_string(order, 'size'),
605
- 'cost': None,
606
- 'average': None,
607
- 'filled': self.safe_string(order, 'filledSize'),
608
- 'remaining': self.safe_string(order, 'remainSize'),
609
- 'status': self.parse_order_status(status, messageType),
610
- 'fee': None,
611
- 'trades': None,
612
- })
613
-
614
- def handle_ticker(self, client: Client, message):
615
- #
616
- # {
617
- # "subject": "ticker",
618
- # "topic": "/contractMarket/ticker:BTCUSDTPERP",
619
- # "data": {
620
- # "symbol": "BTCUSDTPERP", # Market of the symbol
621
- # "sequence": 45, # Sequence number which is used to judge the continuity of the pushed messages
622
- # "side": "sell", # Transaction side of the last traded taker order
623
- # "price": 3600.00, # Filled price
624
- # "size": 16, # Filled quantity
625
- # "tradeId": "5c9dcf4170744d6f5a3d32fb", # Order ID
626
- # "bestBidSize": 795, # Best bid size
627
- # "bestBidPrice": 3200.00, # Best bid
628
- # "bestAskPrice": 3600.00, # Best ask size
629
- # "bestAskSize": 284, # Best ask
630
- # "ts": 1553846081210004941 # Filled time - nanosecond
631
- # },
632
- # "type": "message",
633
- # }
634
- #
635
- # {
636
- # "topic": "/contractMarket/snapshot:BTCUSDTPERP",
637
- # "subject": "snapshot.24h",
638
- # "data": {
639
- # "volume": 30449670, #24h Volume
640
- # "turnover": 845169919063, #24h Turnover
641
- # "lastPrice": 3551, #Last price
642
- # "priceChgPct": 0.0043, #24h Change
643
- # "ts": 1547697294838004923 #Snapshot time(nanosecond)
644
- # }
645
- # }
646
- #
647
- data = self.safe_value(message, 'data', {})
648
- messageHash = self.safe_string(message, 'topic')
649
- symbol = self.get_symbol_from_topic(messageHash)
650
- if symbol is not None:
651
- ticker = self.parse_ticker(data)
652
- self.tickers[symbol] = ticker
653
- client.resolve(ticker, messageHash)
654
- return message
655
-
656
- def handle_l3_order_book(self, client: Client, message):
657
- #
658
- # {
659
- # "data": {
660
- # "symbol": "BTCUSDTPERP",
661
- # "sequence": 1679593048010,
662
- # "orderId": "6426fec8586b9500089d64d8",
663
- # "clientOid": "14e6ee8e-8757-462c-84db-ed12c2b62f55",
664
- # "ts": 1680277192127513900
665
- # },
666
- # "subject": "received",
667
- # "topic": "/contractMarket/level3v2:BTCUSDTPERP",
668
- # "type": "message"
669
- # }
670
- #
671
- # {
672
- # "data": {
673
- # "symbol": "BTCUSDTPERP",
674
- # "sequence": 1679593047982,
675
- # "side": "sell",
676
- # "orderTime": "1680277191900131371",
677
- # "size": "1",
678
- # "orderId": "6426fec7d32b6e000790268b",
679
- # "price": "28376.4",
680
- # "ts": 1680277191939042300
681
- # },
682
- # "subject": "open",
683
- # "topic": "/contractMarket/level3v2:BTCUSDTPERP",
684
- # "type": "message"
685
- # }
686
- #
687
- # {
688
- # "data": {
689
- # "symbol": "BTCUSDTPERP",
690
- # "reason": "canceled", # or "filled"
691
- # "sequence": 1679593047983,
692
- # "orderId": "6426fec74026fa0008e7046f",
693
- # "ts": 1680277191949842000
694
- # },
695
- # "subject": "done",
696
- # "topic": "/contractMarket/level3v2:BTCUSDTPERP",
697
- # "type": "message"
698
- # }
699
- #
700
- messageHash = self.safe_string(message, 'topic')
701
- subject = self.safe_string(message, 'subject')
702
- if subject == 'received':
703
- return
704
- # At the time of writting self, there is no implementation to easily convert each order into the orderbook so raw messages are returned
705
- client.resolve(message, messageHash)
706
-
707
- def handle_level_2(self, client: Client, message):
708
- # {
709
- # "subject": "level2",
710
- # "topic": "/contractMarket/level2:BTCUSDTPERP",
711
- # "type": "message",
712
- # "data": {
713
- # "sequence": 18, # Sequence number which is used to judge the continuity of pushed messages
714
- # "change": "5000.0,sell,83" # Price, side, quantity
715
- # "timestamp": 1551770400000
716
- # }
717
- # }
718
- topic = self.safe_string(message, 'topic')
719
- isSnapshot = topic.find('Depth') >= 0
720
- if isSnapshot:
721
- self.hande_l2_snapshot(client, message)
722
- return
723
- self.handle_l2_order_book(client, message)
724
-
725
- def handle_l2_order_book(self, client: Client, message):
726
- #
727
- # {
728
- # "id": 1545910660740,
729
- # "type": "subscribe",
730
- # "topic": "/contractMarket/level2:BTCUSDTPERP",
731
- # "response": True
732
- # }
733
- #
734
- # {
735
- # "subject": "level2",
736
- # "topic": "/contractMarket/level2:BTCUSDTPERP",
737
- # "type": "message",
738
- # "data": {
739
- # "sequence": 18, # Sequence number which is used to judge the continuity of pushed messages
740
- # "change": "5000.0,sell,83" # Price, side, quantity
741
- # "timestamp": 1551770400000
742
- # }
743
- # }
744
- #
745
- data = self.safe_value(message, 'data', {})
746
- messageHash = self.safe_string(message, 'topic', '')
747
- symbol = self.get_symbol_from_topic(messageHash)
748
- orderBook = self.safe_value(self.orderbooks, symbol)
749
- if orderBook is None:
750
- self.orderbooks[symbol] = self.order_book({})
751
- orderBook = self.orderbooks[symbol]
752
- orderBook['symbol'] = symbol
753
- nonce = self.safe_integer(orderBook, 'nonce')
754
- if nonce is None:
755
- cacheLength = len(orderBook.cache)
756
- snapshotDelay = self.handle_option('watchOrderBook', 'snapshotDelay', 5)
757
- if cacheLength == snapshotDelay:
758
- limit = 0
759
- self.spawn(self.load_order_book, client, messageHash, symbol, limit, {})
760
- orderBook.cache.append(data)
761
- return
762
- try:
763
- self.handle_delta(orderBook, data)
764
- client.resolve(orderBook, messageHash)
765
- except Exception as e:
766
- del self.orderbooks[symbol]
767
- client.reject(e, messageHash)
768
-
769
- def hande_l2_snapshot(self, client: Client, message):
770
- #
771
- # {
772
- # "type": "message",
773
- # "topic": "/contractMarket/level2Depth5:BTCUSDTPERP",
774
- # "subject": "level2",
775
- # "data": {
776
- # "asks": [
777
- # ["9993", "3"],
778
- # ["9992", "3"],
779
- # ["9991", "47"],
780
- # ["9990", "32"],
781
- # ["9989", "8"]
782
- # ],
783
- # "bids": [
784
- # ["9988", "56"],
785
- # ["9987", "15"],
786
- # ["9986", "100"],
787
- # ["9985", "10"],
788
- # ["9984", "10"]
789
- # ],
790
- # "timestamp": 1682993050531,
791
- # }
792
- # }
793
- #
794
- data = self.safe_value(message, 'data', {})
795
- messageHash = self.safe_string(message, 'topic', '')
796
- symbol = self.get_symbol_from_topic(messageHash)
797
- timestamp = self.safe_integer(data, 'timestamp')
798
- snapshot = self.parse_order_book(data, symbol, timestamp, 'bids', 'asks')
799
- orderbook = self.order_book(snapshot)
800
- self.orderbooks[symbol] = orderbook
801
- client.resolve(orderbook, messageHash)
802
-
803
- def get_symbol_from_topic(self, topic: str):
804
- splitTopic = topic.split(':')
805
- marketId = self.safe_string(splitTopic, 1)
806
- return self.safe_symbol(marketId)
807
-
808
- def get_cache_index(self, orderbook, cache):
809
- firstDelta = self.safe_value(cache, 0)
810
- nonce = self.safe_integer(orderbook, 'nonce')
811
- firstDeltaSequence = self.safe_integer(firstDelta, 'sequence')
812
- if firstDeltaSequence > nonce + 1:
813
- return -1
814
- for i in range(0, len(cache)):
815
- delta = cache[i]
816
- sequence = self.safe_integer(delta, 'sequence')
817
- if nonce == sequence - 1:
818
- return i
819
- return len(cache)
820
-
821
- def handle_delta(self, orderbook, delta):
822
- #
823
- # {
824
- # sequence: 123677914,
825
- # lastSequence: 123677913,
826
- # change: '80.36,buy,4924',
827
- # changes: ['80.19,buy,0',"80.15,buy,10794"],
828
- # timestamp: 1715643483528
829
- # },
830
- #
831
- sequence = self.safe_integer(delta, 'sequence')
832
- lastSequence = self.safe_integer(delta, 'lastSequence')
833
- nonce = self.safe_integer(orderbook, 'nonce')
834
- if nonce > sequence:
835
- return
836
- if nonce != lastSequence:
837
- checksum = self.handle_option('watchOrderBook', 'checksum', True)
838
- if checksum:
839
- raise ChecksumError(self.id + ' ' + self.orderbook_checksum_message(''))
840
- changes = self.safe_list(delta, 'changes')
841
- for i in range(0, len(changes)):
842
- change = changes[i]
843
- splitChange = change.split(',')
844
- price = self.safe_number(splitChange, 0)
845
- side = self.safe_string(splitChange, 1)
846
- size = self.safe_number(splitChange, 2)
847
- orderBookSide = orderbook['bids'] if (side == 'buy') else orderbook['asks']
848
- orderBookSide.store(price, size)
849
- timestamp = self.safe_integer(delta, 'timestamp')
850
- orderbook['timestamp'] = timestamp
851
- orderbook['datetime'] = self.iso8601(timestamp)
852
- orderbook['nonce'] = sequence
853
-
854
- def handle_balance(self, client: Client, message):
855
- #
856
- # {
857
- # "data": {
858
- # "currency": "USDT",
859
- # "availableBalance": "4.0000000000",
860
- # "timestamp": "1680557568670"
861
- # },
862
- # "subject": "availableBalance.change",
863
- # "topic": "/contractAccount/wallet",
864
- # "channelType": "private",
865
- # "id": "642b4600cae86800074b5ab7",
866
- # "type": "message",
867
- # "userId": "1139790"
868
- # }
869
- #
870
- # {
871
- # "data": {
872
- # "currency": "USDT",
873
- # "orderMargin": "0.0000000000",
874
- # "timestamp": "1680558743307"
875
- # },
876
- # "subject": "orderMargin.change",
877
- # "topic": "/contractAccount/wallet",
878
- # "channelType": "private",
879
- # "id": "642b4a97b58e360007c3a237",
880
- # "type": "message",
881
- # "userId": "1139790"
882
- # }
883
- #
884
- data = self.safe_value(message, 'data', [])
885
- messageHash = '/contractAccount/wallet'
886
- currencyId = self.safe_string(data, 'currency')
887
- currency = self.currency(currencyId)
888
- code = currency['code']
889
- self.balance[code] = self.parse_ws_balance(data)
890
- client.resolve(self.balance[code], messageHash)
891
- return message
892
-
893
- def parse_ws_balance(self, response):
894
- #
895
- # {
896
- # "currency": "USDT",
897
- # "availableBalance": "4.0000000000",
898
- # "timestamp": "1680557568670"
899
- # }
900
- #
901
- # {
902
- # "currency": "USDT",
903
- # "orderMargin": "0.0000000000",
904
- # "timestamp": "1680558743307"
905
- # }
906
- #
907
- timestamp = self.safe_integer(response, 'timestamp')
908
- result: dict = {
909
- 'info': response,
910
- 'timestamp': timestamp,
911
- 'datetime': self.iso8601(timestamp),
912
- }
913
- currencyId = self.safe_string(response, 'currency')
914
- code = self.safe_currency_code(currencyId)
915
- newAccount = self.account()
916
- newAccount['free'] = self.safe_string(response, 'availableBalance')
917
- result[code] = newAccount
918
- return self.safe_balance(result)
919
-
920
- def handle_system_status(self, client: Client, message):
921
- #
922
- # {
923
- # "id": "1578090234088", # connectId
924
- # "type": "welcome",
925
- # }
926
- #
927
- return message
928
-
929
- def handle_subject(self, client: Client, message):
930
- subject = self.safe_string(message, 'subject')
931
- methods: dict = {
932
- 'auth': self.handle_authenticate,
933
- 'received': self.handle_l3_order_book,
934
- 'open': self.handle_l3_order_book,
935
- 'update': self.handle_l3_order_book,
936
- 'done': self.handle_l3_order_book,
937
- 'level2': self.handle_level_2,
938
- 'ticker': self.handle_ticker,
939
- 'snapshot.24h': self.handle_ticker,
940
- 'match': self.handle_trade,
941
- 'orderChange': self.handle_order,
942
- 'stopOrder': self.handle_order,
943
- 'availableBalance.change': self.handle_balance,
944
- 'orderMargin.change': self.handle_balance,
945
- }
946
- method = self.safe_value(methods, subject)
947
- if method is not None:
948
- method(client, message)
949
-
950
- def ping(self, client: Client):
951
- id = str(self.request_id())
952
- return {
953
- 'id': id,
954
- 'type': 'ping',
955
- }
956
-
957
- def handle_pong(self, client: Client, message):
958
- client.lastPong = self.milliseconds()
959
- return message
960
-
961
- def handle_error_message(self, client: Client, message):
962
- #
963
- # {
964
- # "code": 404,
965
- # "data": "tunnel stream-0 is not exist",
966
- # "id": "3",
967
- # "type": "error"
968
- # }
969
- #
970
- client.reject(message)
971
-
972
- def handle_message(self, client: Client, message):
973
- type = self.safe_string(message, 'type')
974
- methods: dict = {
975
- 'welcome': self.handle_system_status,
976
- 'ack': self.handle_subscription_status,
977
- 'message': self.handle_subject,
978
- 'pong': self.handle_pong,
979
- 'error': self.handle_error_message,
980
- }
981
- method = self.safe_value(methods, type)
982
- if method is not None:
983
- method(client, message)
984
-
985
- def handle_authenticate(self, client, message):
986
- #
987
- # {
988
- # "success": True,
989
- # "ret_msg": '',
990
- # "op": "auth",
991
- # "conn_id": "ce3dpomvha7dha97tvp0-2xh"
992
- # }
993
- #
994
- data = self.safe_value(message, 'data')
995
- success = self.safe_value(data, 'success')
996
- messageHash = 'authenticated'
997
- if success:
998
- client.resolve(message, messageHash)
999
- else:
1000
- error = AuthenticationError(self.id + ' ' + self.json(message))
1001
- client.reject(error, messageHash)
1002
- if messageHash in client.subscriptions:
1003
- del client.subscriptions[messageHash]
1004
- return message