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.
- ccxt/__init__.py +1 -3
- ccxt/abstract/bitget.py +58 -0
- ccxt/abstract/bitrue.py +65 -65
- ccxt/abstract/cryptocom.py +2 -0
- ccxt/abstract/luno.py +1 -0
- ccxt/async_support/__init__.py +1 -3
- ccxt/async_support/base/exchange.py +6 -3
- ccxt/async_support/base/ws/client.py +173 -64
- ccxt/async_support/base/ws/future.py +23 -50
- ccxt/async_support/binance.py +2 -2
- ccxt/async_support/bingx.py +55 -29
- ccxt/async_support/bitget.py +469 -147
- ccxt/async_support/bitmex.py +2 -1
- ccxt/async_support/bitrue.py +72 -66
- ccxt/async_support/bitvavo.py +34 -0
- ccxt/async_support/btcalpha.py +35 -0
- ccxt/async_support/btcbox.py +35 -0
- ccxt/async_support/btcmarkets.py +35 -0
- ccxt/async_support/btcturk.py +35 -0
- ccxt/async_support/bybit.py +9 -3
- ccxt/async_support/cex.py +61 -0
- ccxt/async_support/coinbase.py +1 -3
- ccxt/async_support/cryptocom.py +66 -2
- ccxt/async_support/cryptomus.py +1 -1
- ccxt/async_support/delta.py +2 -2
- ccxt/async_support/digifinex.py +39 -99
- ccxt/async_support/exmo.py +14 -7
- ccxt/async_support/gate.py +14 -7
- ccxt/async_support/hashkey.py +15 -28
- ccxt/async_support/hollaex.py +27 -22
- ccxt/async_support/hyperliquid.py +104 -53
- ccxt/async_support/kraken.py +54 -50
- ccxt/async_support/luno.py +87 -1
- ccxt/async_support/mexc.py +1 -0
- ccxt/async_support/modetrade.py +2 -2
- ccxt/async_support/okx.py +2 -1
- ccxt/async_support/paradex.py +1 -1
- ccxt/async_support/phemex.py +16 -8
- ccxt/async_support/tradeogre.py +3 -3
- ccxt/async_support/xt.py +1 -1
- ccxt/base/exchange.py +20 -8
- ccxt/binance.py +2 -2
- ccxt/bingx.py +55 -29
- ccxt/bitget.py +469 -147
- ccxt/bitmex.py +2 -1
- ccxt/bitrue.py +72 -66
- ccxt/bitvavo.py +34 -0
- ccxt/btcalpha.py +35 -0
- ccxt/btcbox.py +35 -0
- ccxt/btcmarkets.py +35 -0
- ccxt/btcturk.py +35 -0
- ccxt/bybit.py +9 -3
- ccxt/cex.py +61 -0
- ccxt/coinbase.py +1 -3
- ccxt/cryptocom.py +66 -2
- ccxt/cryptomus.py +1 -1
- ccxt/delta.py +2 -2
- ccxt/digifinex.py +39 -99
- ccxt/exmo.py +13 -7
- ccxt/gate.py +14 -7
- ccxt/hashkey.py +15 -28
- ccxt/hollaex.py +27 -22
- ccxt/hyperliquid.py +104 -53
- ccxt/kraken.py +53 -50
- ccxt/luno.py +87 -1
- ccxt/mexc.py +1 -0
- ccxt/modetrade.py +2 -2
- ccxt/okx.py +2 -1
- ccxt/paradex.py +1 -1
- ccxt/phemex.py +16 -8
- ccxt/pro/__init__.py +1 -127
- ccxt/pro/bitstamp.py +1 -1
- ccxt/pro/bybit.py +6 -136
- ccxt/pro/coinbase.py +2 -0
- ccxt/pro/cryptocom.py +27 -0
- ccxt/pro/kraken.py +249 -267
- ccxt/pro/mexc.py +0 -1
- ccxt/tradeogre.py +3 -3
- ccxt/xt.py +1 -1
- {ccxt-4.4.88.dist-info → ccxt-4.4.91.dist-info}/METADATA +64 -23
- {ccxt-4.4.88.dist-info → ccxt-4.4.91.dist-info}/RECORD +84 -101
- ccxt/abstract/coinlist.py +0 -57
- ccxt/async_support/base/ws/aiohttp_client.py +0 -147
- ccxt/async_support/bitcoincom.py +0 -18
- ccxt/async_support/bitfinex1.py +0 -1711
- ccxt/async_support/bitpanda.py +0 -17
- ccxt/async_support/coinlist.py +0 -2542
- ccxt/async_support/poloniexfutures.py +0 -1875
- ccxt/bitcoincom.py +0 -18
- ccxt/bitfinex1.py +0 -1710
- ccxt/bitpanda.py +0 -17
- ccxt/coinlist.py +0 -2542
- ccxt/poloniexfutures.py +0 -1875
- ccxt/pro/bitcoincom.py +0 -35
- ccxt/pro/bitfinex1.py +0 -635
- ccxt/pro/bitpanda.py +0 -16
- ccxt/pro/poloniexfutures.py +0 -1004
- ccxt/pro/wazirx.py +0 -766
- {ccxt-4.4.88.dist-info → ccxt-4.4.91.dist-info}/LICENSE.txt +0 -0
- {ccxt-4.4.88.dist-info → ccxt-4.4.91.dist-info}/WHEEL +0 -0
- {ccxt-4.4.88.dist-info → ccxt-4.4.91.dist-info}/top_level.txt +0 -0
ccxt/pro/poloniexfutures.py
DELETED
@@ -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
|