ccxt 3.1.13__py2.py3-none-any.whl → 3.1.15__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 -1
- ccxt/async_support/__init__.py +1 -1
- ccxt/async_support/base/exchange.py +30 -13
- ccxt/async_support/huobi.py +4 -1
- ccxt/async_support/okx.py +1 -0
- ccxt/async_support/poloniex.py +1 -1
- ccxt/base/exchange.py +30 -13
- ccxt/huobi.py +4 -1
- ccxt/okx.py +1 -0
- ccxt/poloniex.py +1 -1
- ccxt/pro/__init__.py +3 -1
- ccxt/pro/alpaca.py +3 -3
- ccxt/pro/ascendex.py +2 -2
- ccxt/pro/binance.py +7 -5
- ccxt/pro/bitfinex.py +1 -1
- ccxt/pro/bitfinex2.py +3 -3
- ccxt/pro/bitget.py +3 -3
- ccxt/pro/bitmart.py +2 -2
- ccxt/pro/bitmex.py +3 -3
- ccxt/pro/bitopro.py +1 -1
- ccxt/pro/bitpanda.py +1 -1
- ccxt/pro/bitstamp.py +2 -2
- ccxt/pro/bittrex.py +3 -3
- ccxt/pro/bitvavo.py +3 -3
- ccxt/pro/blockchaincom.py +2 -2
- ccxt/pro/btcex.py +3 -3
- ccxt/pro/bybit.py +3 -3
- ccxt/pro/cex.py +2 -2
- ccxt/pro/coinbasepro.py +3 -3
- ccxt/pro/coinex.py +18 -12
- ccxt/pro/cryptocom.py +3 -3
- ccxt/pro/currencycom.py +2 -2
- ccxt/pro/deribit.py +3 -3
- ccxt/pro/exmo.py +2 -2
- ccxt/pro/gate.py +4 -4
- ccxt/pro/gemini.py +2 -2
- ccxt/pro/hitbtc.py +2 -2
- ccxt/pro/hollaex.py +2 -2
- ccxt/pro/huobi.py +4 -4
- ccxt/pro/huobijp.py +2 -2
- ccxt/pro/idex.py +3 -3
- ccxt/pro/independentreserve.py +1 -1
- ccxt/pro/kraken.py +2 -2
- ccxt/pro/krakenfutures.py +4 -4
- ccxt/pro/kucoin.py +3 -3
- ccxt/pro/kucoinfutures.py +1 -1
- ccxt/pro/luno.py +1 -1
- ccxt/pro/mexc.py +3 -3
- ccxt/pro/ndax.py +2 -2
- ccxt/pro/okcoin.py +3 -3
- ccxt/pro/okx.py +2 -2
- ccxt/pro/phemex.py +3 -3
- ccxt/pro/poloniex.py +973 -0
- ccxt/pro/poloniexfutures.py +1 -1
- ccxt/pro/probit.py +2 -2
- ccxt/pro/upbit.py +1 -1
- ccxt/pro/wazirx.py +3 -3
- ccxt/pro/whitebit.py +4 -4
- ccxt/pro/woo.py +2 -2
- ccxt/test/base/test_shared_methods.py +13 -13
- {ccxt-3.1.13.dist-info → ccxt-3.1.15.dist-info}/METADATA +5 -5
- {ccxt-3.1.13.dist-info → ccxt-3.1.15.dist-info}/RECORD +64 -63
- {ccxt-3.1.13.dist-info → ccxt-3.1.15.dist-info}/WHEEL +0 -0
- {ccxt-3.1.13.dist-info → ccxt-3.1.15.dist-info}/top_level.txt +0 -0
ccxt/pro/poloniex.py
ADDED
@@ -0,0 +1,973 @@
|
|
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
|
+
import hashlib
|
9
|
+
from ccxt.async_support.base.ws.client import Client
|
10
|
+
from typing import Optional
|
11
|
+
from typing import List
|
12
|
+
from ccxt.base.errors import ExchangeError
|
13
|
+
from ccxt.base.errors import BadRequest
|
14
|
+
from ccxt.base.errors import AuthenticationError
|
15
|
+
from ccxt.base.precise import Precise
|
16
|
+
|
17
|
+
|
18
|
+
class poloniex(ccxt.async_support.poloniex):
|
19
|
+
|
20
|
+
def describe(self):
|
21
|
+
return self.deep_extend(super(poloniex, self).describe(), {
|
22
|
+
'has': {
|
23
|
+
'ws': True,
|
24
|
+
'watchOHLCV': True,
|
25
|
+
'watchOrderBook': True,
|
26
|
+
'watchTicker': True,
|
27
|
+
'watchTickers': True,
|
28
|
+
'watchTrades': True,
|
29
|
+
'watchBalance': True,
|
30
|
+
'watchStatus': False,
|
31
|
+
'watchOrders': True,
|
32
|
+
'watchMyTrades': True,
|
33
|
+
},
|
34
|
+
'urls': {
|
35
|
+
'api': {
|
36
|
+
'ws': {
|
37
|
+
'public': 'wss://ws.poloniex.com/ws/public',
|
38
|
+
'private': 'wss://ws.poloniex.com/ws/private',
|
39
|
+
},
|
40
|
+
},
|
41
|
+
},
|
42
|
+
'options': {
|
43
|
+
'tradesLimit': 1000,
|
44
|
+
'ordersLimit': 1000,
|
45
|
+
'OHLCVLimit': 1000,
|
46
|
+
'watchOrderBook': {
|
47
|
+
'name': 'book_lv2', # can also be 'book'
|
48
|
+
},
|
49
|
+
'connectionsLimit': 2000, # 2000 public, 2000 private, 4000 total, only for subscribe events, unsubscribe not restricted
|
50
|
+
'requestsLimit': 500, # per second, only for subscribe events, unsubscribe not restricted
|
51
|
+
'timeframes': {
|
52
|
+
'1m': 'candles_minute_1',
|
53
|
+
'5m': 'candles_minute_5',
|
54
|
+
'10m': 'candles_minute_10',
|
55
|
+
'15m': 'candles_minute_15',
|
56
|
+
'30m': 'candles_minute_30',
|
57
|
+
'1h': 'candles_hour_1',
|
58
|
+
'2h': 'candles_hour_2',
|
59
|
+
'4h': 'candles_hour_4',
|
60
|
+
'6h': 'candles_hour_6',
|
61
|
+
'12h': 'candles_hour_12',
|
62
|
+
'1d': 'candles_day_1',
|
63
|
+
'3d': 'candles_day_3',
|
64
|
+
'1w': 'candles_week_1',
|
65
|
+
'1M': 'candles_month_1',
|
66
|
+
},
|
67
|
+
},
|
68
|
+
'streaming': {
|
69
|
+
'keepAlive': 15000,
|
70
|
+
'ping': self.ping,
|
71
|
+
},
|
72
|
+
})
|
73
|
+
|
74
|
+
async def authenticate(self, params={}):
|
75
|
+
"""
|
76
|
+
* @ignore
|
77
|
+
authenticates the user to access private web socket channels
|
78
|
+
see https://docs.poloniex.com/#authenticated-channels-market-data-authentication
|
79
|
+
:returns dict: response from exchange
|
80
|
+
"""
|
81
|
+
self.check_required_credentials()
|
82
|
+
timestamp = self.number_to_string(self.milliseconds())
|
83
|
+
url = self.urls['api']['ws']['private']
|
84
|
+
messageHash = 'authenticated'
|
85
|
+
client = self.client(url)
|
86
|
+
future = self.safe_value(client.subscriptions, messageHash)
|
87
|
+
if future is None:
|
88
|
+
accessPath = '/ws'
|
89
|
+
requestString = 'GET\n' + accessPath + '\nsignTimestamp=' + timestamp
|
90
|
+
signature = self.hmac(self.encode(requestString), self.encode(self.secret), hashlib.sha256, 'base64')
|
91
|
+
request = {
|
92
|
+
'event': 'subscribe',
|
93
|
+
'channel': ['auth'],
|
94
|
+
'params': {
|
95
|
+
'key': self.apiKey,
|
96
|
+
'signTimestamp': timestamp,
|
97
|
+
'signature': signature,
|
98
|
+
'signatureMethod': 'HmacSHA256', # optional
|
99
|
+
'signatureVersion': '2', # optional
|
100
|
+
},
|
101
|
+
}
|
102
|
+
message = self.extend(request, params)
|
103
|
+
future = await self.watch(url, messageHash, message)
|
104
|
+
#
|
105
|
+
# {
|
106
|
+
# "data": {
|
107
|
+
# "success": True,
|
108
|
+
# "ts": 1645597033915
|
109
|
+
# },
|
110
|
+
# "channel": "auth"
|
111
|
+
# }
|
112
|
+
#
|
113
|
+
# # Failure to return results
|
114
|
+
#
|
115
|
+
# {
|
116
|
+
# "data": {
|
117
|
+
# "success": False,
|
118
|
+
# "message": "Authentication failed!",
|
119
|
+
# "ts": 1646276295075
|
120
|
+
# },
|
121
|
+
# "channel": "auth"
|
122
|
+
# }
|
123
|
+
#
|
124
|
+
client.subscriptions[messageHash] = future
|
125
|
+
return future
|
126
|
+
|
127
|
+
async def subscribe(self, name: str, messageHash: str, isPrivate: bool, symbols: Optional[List[str]] = None, params={}):
|
128
|
+
"""
|
129
|
+
* @ignore
|
130
|
+
Connects to a websocket channel
|
131
|
+
:param str name: name of the channel
|
132
|
+
:param boolean isPrivate: True for the authenticated url, False for the public url
|
133
|
+
:param [str]|None symbols: CCXT market symbols
|
134
|
+
:param dict params: extra parameters specific to the poloniex api
|
135
|
+
:returns dict: data from the websocket stream
|
136
|
+
"""
|
137
|
+
publicOrPrivate = 'private' if isPrivate else 'public'
|
138
|
+
url = self.urls['api']['ws'][publicOrPrivate]
|
139
|
+
subscribe = {
|
140
|
+
'event': 'subscribe',
|
141
|
+
'channel': [
|
142
|
+
name,
|
143
|
+
],
|
144
|
+
}
|
145
|
+
marketIds = []
|
146
|
+
if symbols is not None:
|
147
|
+
if len(symbols) == 1:
|
148
|
+
symbol = symbols[0]
|
149
|
+
marketId = self.market_id(symbol)
|
150
|
+
marketIds.append(marketId)
|
151
|
+
messageHash = messageHash + ':' + symbol
|
152
|
+
else:
|
153
|
+
for i in range(0, len(symbols)):
|
154
|
+
symbol = symbols[i]
|
155
|
+
marketIds.append(self.market_id(symbol))
|
156
|
+
else:
|
157
|
+
marketIds.append('all')
|
158
|
+
if name != 'balances':
|
159
|
+
subscribe['symbols'] = marketIds
|
160
|
+
request = self.extend(subscribe, params)
|
161
|
+
return await self.watch(url, messageHash, request, name)
|
162
|
+
|
163
|
+
async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Optional[int] = None, limit: Optional[int] = None, params={}):
|
164
|
+
"""
|
165
|
+
watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
166
|
+
see https://docs.poloniex.com/#public-channels-market-data-candlesticks
|
167
|
+
:param str symbol: unified symbol of the market to fetch OHLCV data for
|
168
|
+
:param str timeframe: the length of time each candle represents
|
169
|
+
:param int|None since: timestamp in ms of the earliest candle to fetch
|
170
|
+
:param int|None limit: the maximum amount of candles to fetch
|
171
|
+
:param dict params: extra parameters specific to the poloniex api endpoint
|
172
|
+
:returns: [[int]] A list of candles ordered, open, high, low, close, volume
|
173
|
+
"""
|
174
|
+
await self.load_markets()
|
175
|
+
timeframes = self.safe_value(self.options, 'timeframes', {})
|
176
|
+
channel = self.safe_string(timeframes, timeframe, timeframe)
|
177
|
+
if channel is None:
|
178
|
+
raise BadRequest(self.id + ' watchOHLCV cannot take a timeframe of ' + timeframe)
|
179
|
+
ohlcv = await self.subscribe(channel, channel, False, [symbol], params)
|
180
|
+
if self.newUpdates:
|
181
|
+
limit = ohlcv.getLimit(symbol, limit)
|
182
|
+
return self.filter_by_since_limit(ohlcv, since, limit, 0, True)
|
183
|
+
|
184
|
+
async def watch_ticker(self, symbol: str, params={}):
|
185
|
+
"""
|
186
|
+
watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
|
187
|
+
see https://docs.poloniex.com/#public-channels-market-data-ticker
|
188
|
+
:param str symbol: unified symbol of the market to fetch the ticker for
|
189
|
+
:param dict params: extra parameters specific to the poloniex api endpoint
|
190
|
+
:returns dict: a `ticker structure <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
|
191
|
+
"""
|
192
|
+
await self.load_markets()
|
193
|
+
name = 'ticker'
|
194
|
+
return await self.subscribe(name, name, False, [symbol], params)
|
195
|
+
|
196
|
+
async def watch_tickers(self, symbols=None, params={}):
|
197
|
+
"""
|
198
|
+
watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
|
199
|
+
see https://docs.poloniex.com/#public-channels-market-data-ticker
|
200
|
+
:param str symbol: unified symbol of the market to fetch the ticker for
|
201
|
+
:param dict params: extra parameters specific to the poloniex api endpoint
|
202
|
+
:returns dict: a `ticker structure <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
|
203
|
+
"""
|
204
|
+
await self.load_markets()
|
205
|
+
name = 'ticker'
|
206
|
+
return await self.subscribe(name, name, False, symbols, params)
|
207
|
+
|
208
|
+
async def watch_trades(self, symbol: str, since: Optional[int] = None, limit: Optional[int] = None, params={}):
|
209
|
+
"""
|
210
|
+
get the list of most recent trades for a particular symbol
|
211
|
+
see https://docs.poloniex.com/#public-channels-market-data-trades
|
212
|
+
:param str symbol: unified symbol of the market to fetch trades for
|
213
|
+
:param int|None since: timestamp in ms of the earliest trade to fetch
|
214
|
+
:param int|None limit: the maximum amount of trades to fetch
|
215
|
+
:param dict params: extra parameters specific to the poloniex api endpoint
|
216
|
+
:returns [dict]: a list of `trade structures <https://docs.ccxt.com/en/latest/manual.html?#public-trades>`
|
217
|
+
"""
|
218
|
+
await self.load_markets()
|
219
|
+
symbol = self.symbol(symbol)
|
220
|
+
name = 'trades'
|
221
|
+
trades = await self.subscribe(name, name, False, [symbol], params)
|
222
|
+
if self.newUpdates:
|
223
|
+
limit = trades.getLimit(symbol, limit)
|
224
|
+
return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
|
225
|
+
|
226
|
+
async def watch_order_book(self, symbol: str, limit: Optional[int] = None, params={}):
|
227
|
+
"""
|
228
|
+
watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
|
229
|
+
see https://docs.poloniex.com/#public-channels-market-data-book-level-2
|
230
|
+
:param str symbol: unified symbol of the market to fetch the order book for
|
231
|
+
:param int|None limit: not used by poloniex watchOrderBook
|
232
|
+
:param dict params: extra parameters specific to the poloniex api endpoint
|
233
|
+
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/en/latest/manual.html#order-book-structure>` indexed by market symbols
|
234
|
+
"""
|
235
|
+
await self.load_markets()
|
236
|
+
watchOrderBookOptions = self.safe_value(self.options, 'watchOrderBook')
|
237
|
+
name = self.safe_string(watchOrderBookOptions, 'name', 'book_lv2')
|
238
|
+
name, params = self.handle_option_and_params(params, 'method', 'name', name)
|
239
|
+
orderbook = await self.subscribe(name, name, False, [symbol], params)
|
240
|
+
return orderbook.limit()
|
241
|
+
|
242
|
+
async def watch_orders(self, symbol: Optional[str] = None, since: Optional[int] = None, limit: Optional[int] = None, params={}):
|
243
|
+
"""
|
244
|
+
watches information on multiple orders made by the user
|
245
|
+
see https://docs.poloniex.com/#authenticated-channels-market-data-orders
|
246
|
+
:param str|None symbol: unified market symbol of the market orders were made in
|
247
|
+
:param int|None since: not used by poloniex watchOrders
|
248
|
+
:param int|None limit: not used by poloniex watchOrders
|
249
|
+
:param dict params: extra parameters specific to the poloniex api endpoint
|
250
|
+
:returns [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
|
251
|
+
"""
|
252
|
+
await self.load_markets()
|
253
|
+
name = 'orders'
|
254
|
+
await self.authenticate()
|
255
|
+
if symbol is not None:
|
256
|
+
symbol = self.symbol(symbol)
|
257
|
+
symbols = None if (symbol is None) else [symbol]
|
258
|
+
orders = await self.subscribe(name, name, True, symbols, params)
|
259
|
+
if self.newUpdates:
|
260
|
+
limit = orders.getLimit(symbol, limit)
|
261
|
+
return self.filter_by_since_limit(orders, since, limit, 'timestamp', True)
|
262
|
+
|
263
|
+
async def watch_my_trades(self, symbol: Optional[str] = None, since: Optional[int] = None, limit: Optional[int] = None, params={}):
|
264
|
+
"""
|
265
|
+
watches information on multiple trades made by the user using orders stream
|
266
|
+
see https://docs.poloniex.com/#authenticated-channels-market-data-orders
|
267
|
+
:param str|None symbol: unified market symbol of the market orders were made in
|
268
|
+
:param int|None since: not used by poloniex watchMyTrades
|
269
|
+
:param int|None limit: not used by poloniex watchMyTrades
|
270
|
+
:param dict params: extra parameters specific to the poloniex strean
|
271
|
+
:returns [dict]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
|
272
|
+
"""
|
273
|
+
await self.load_markets()
|
274
|
+
name = 'orders'
|
275
|
+
messageHash = 'myTrades'
|
276
|
+
await self.authenticate()
|
277
|
+
if symbol is not None:
|
278
|
+
symbol = self.symbol(symbol)
|
279
|
+
symbols = None if (symbol is None) else [symbol]
|
280
|
+
trades = await self.subscribe(name, messageHash, True, symbols, params)
|
281
|
+
if self.newUpdates:
|
282
|
+
limit = trades.getLimit(symbol, limit)
|
283
|
+
return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
|
284
|
+
|
285
|
+
async def watch_balance(self, params={}):
|
286
|
+
"""
|
287
|
+
watches information on multiple orders made by the user
|
288
|
+
see https://docs.poloniex.com/#authenticated-channels-market-data-balances
|
289
|
+
:param str|None symbol: not used by poloniex watchBalance
|
290
|
+
:param int|None since: not used by poloniex watchBalance
|
291
|
+
:param int|None limit: not used by poloniex watchBalance
|
292
|
+
:param dict params: extra parameters specific to the poloniex api endpoint
|
293
|
+
:returns [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
|
294
|
+
"""
|
295
|
+
await self.load_markets()
|
296
|
+
name = 'balances'
|
297
|
+
await self.authenticate()
|
298
|
+
return await self.subscribe(name, name, True, None, params)
|
299
|
+
|
300
|
+
def parse_ws_ohlcv(self, ohlcv, market=None):
|
301
|
+
#
|
302
|
+
# {
|
303
|
+
# symbol: 'BTC_USDT',
|
304
|
+
# amount: '840.7240416',
|
305
|
+
# high: '24832.35',
|
306
|
+
# quantity: '0.033856',
|
307
|
+
# tradeCount: 1,
|
308
|
+
# low: '24832.35',
|
309
|
+
# closeTime: 1676942519999,
|
310
|
+
# startTime: 1676942460000,
|
311
|
+
# close: '24832.35',
|
312
|
+
# open: '24832.35',
|
313
|
+
# ts: 1676942492072
|
314
|
+
# }
|
315
|
+
#
|
316
|
+
return [
|
317
|
+
self.safe_integer(ohlcv, 'startTime'),
|
318
|
+
self.safe_number(ohlcv, 'open'),
|
319
|
+
self.safe_number(ohlcv, 'high'),
|
320
|
+
self.safe_number(ohlcv, 'low'),
|
321
|
+
self.safe_number(ohlcv, 'close'),
|
322
|
+
self.safe_number(ohlcv, 'quantity'),
|
323
|
+
]
|
324
|
+
|
325
|
+
def handle_ohlcv(self, client: Client, message):
|
326
|
+
#
|
327
|
+
# {
|
328
|
+
# channel: 'candles_minute_1',
|
329
|
+
# data: [
|
330
|
+
# {
|
331
|
+
# symbol: 'BTC_USDT',
|
332
|
+
# amount: '840.7240416',
|
333
|
+
# high: '24832.35',
|
334
|
+
# quantity: '0.033856',
|
335
|
+
# tradeCount: 1,
|
336
|
+
# low: '24832.35',
|
337
|
+
# closeTime: 1676942519999,
|
338
|
+
# startTime: 1676942460000,
|
339
|
+
# close: '24832.35',
|
340
|
+
# open: '24832.35',
|
341
|
+
# ts: 1676942492072
|
342
|
+
# }
|
343
|
+
# ]
|
344
|
+
# }
|
345
|
+
#
|
346
|
+
data = self.safe_value(message, 'data')
|
347
|
+
data = self.safe_value(data, 0)
|
348
|
+
channel = self.safe_string(message, 'channel')
|
349
|
+
marketId = self.safe_string(data, 'symbol')
|
350
|
+
symbol = self.safe_symbol(marketId)
|
351
|
+
market = self.safe_market(symbol)
|
352
|
+
timeframe = self.find_timeframe(channel)
|
353
|
+
messageHash = channel + ':' + symbol
|
354
|
+
parsed = self.parse_ws_ohlcv(data, market)
|
355
|
+
self.ohlcvs[symbol] = self.safe_value(self.ohlcvs, symbol, {})
|
356
|
+
stored = self.safe_value(self.ohlcvs[symbol], timeframe)
|
357
|
+
if symbol is not None:
|
358
|
+
if stored is None:
|
359
|
+
limit = self.safe_integer(self.options, 'OHLCVLimit', 1000)
|
360
|
+
stored = ArrayCacheByTimestamp(limit)
|
361
|
+
self.ohlcvs[symbol][timeframe] = stored
|
362
|
+
stored.append(parsed)
|
363
|
+
client.resolve(stored, messageHash)
|
364
|
+
return message
|
365
|
+
|
366
|
+
def handle_trade(self, client: Client, message):
|
367
|
+
#
|
368
|
+
# {
|
369
|
+
# channel: 'trades',
|
370
|
+
# data: [
|
371
|
+
# {
|
372
|
+
# symbol: 'BTC_USDT',
|
373
|
+
# amount: '13.41634893',
|
374
|
+
# quantity: '0.000537',
|
375
|
+
# takerSide: 'buy',
|
376
|
+
# createTime: 1676950548834,
|
377
|
+
# price: '24983.89',
|
378
|
+
# id: '62486976',
|
379
|
+
# ts: 1676950548839
|
380
|
+
# }
|
381
|
+
# ]
|
382
|
+
# }
|
383
|
+
#
|
384
|
+
data = self.safe_value(message, 'data', [])
|
385
|
+
for i in range(0, len(data)):
|
386
|
+
item = data[i]
|
387
|
+
marketId = self.safe_string(item, 'symbol')
|
388
|
+
if marketId is not None:
|
389
|
+
trade = self.parse_ws_trade(item)
|
390
|
+
symbol = trade['symbol']
|
391
|
+
type = 'trades'
|
392
|
+
messageHash = type + ':' + symbol
|
393
|
+
tradesArray = self.safe_value(self.trades, symbol)
|
394
|
+
if tradesArray is None:
|
395
|
+
tradesLimit = self.safe_integer(self.options, 'tradesLimit', 1000)
|
396
|
+
tradesArray = ArrayCache(tradesLimit)
|
397
|
+
self.trades[symbol] = tradesArray
|
398
|
+
tradesArray.append(trade)
|
399
|
+
client.resolve(tradesArray, messageHash)
|
400
|
+
return message
|
401
|
+
|
402
|
+
def parse_ws_trade(self, trade, market=None):
|
403
|
+
#
|
404
|
+
# handleTrade
|
405
|
+
#
|
406
|
+
# {
|
407
|
+
# symbol: 'BTC_USDT',
|
408
|
+
# amount: '13.41634893',
|
409
|
+
# quantity: '0.000537',
|
410
|
+
# takerSide: 'buy',
|
411
|
+
# createTime: 1676950548834,
|
412
|
+
# price: '24983.89',
|
413
|
+
# id: '62486976',
|
414
|
+
# ts: 1676950548839
|
415
|
+
# }
|
416
|
+
#
|
417
|
+
# private trade
|
418
|
+
# {
|
419
|
+
# "orderId":"186250258089635840",
|
420
|
+
# "tradeId":"62036513",
|
421
|
+
# "clientOrderId":"",
|
422
|
+
# "accountType":"SPOT",
|
423
|
+
# "eventType":"trade",
|
424
|
+
# "symbol":"ADA_USDT",
|
425
|
+
# "side":"SELL",
|
426
|
+
# "type":"MARKET",
|
427
|
+
# "price":"0",
|
428
|
+
# "quantity":"3",
|
429
|
+
# "state":"FILLED",
|
430
|
+
# "createTime":1685371921891,
|
431
|
+
# "tradeTime":1685371921908,
|
432
|
+
# "tradePrice":"0.37694",
|
433
|
+
# "tradeQty":"3",
|
434
|
+
# "feeCurrency":"USDT",
|
435
|
+
# "tradeFee":"0.00226164",
|
436
|
+
# "tradeAmount":"1.13082",
|
437
|
+
# "filledQuantity":"3",
|
438
|
+
# "filledAmount":"1.13082",
|
439
|
+
# "ts":1685371921945,
|
440
|
+
# "source":"WEB",
|
441
|
+
# "orderAmount":"0",
|
442
|
+
# "matchRole":"TAKER"
|
443
|
+
# }
|
444
|
+
#
|
445
|
+
marketId = self.safe_string(trade, 'symbol')
|
446
|
+
market = self.safe_market(marketId, market)
|
447
|
+
timestamp = self.safe_integer(trade, 'createTime')
|
448
|
+
takerMaker = self.safe_string_lower_2(trade, 'matchRole', 'taker')
|
449
|
+
return self.safe_trade({
|
450
|
+
'info': trade,
|
451
|
+
'id': self.safe_string_2(trade, 'id', 'tradeId'),
|
452
|
+
'symbol': self.safe_string(market, 'symbol'),
|
453
|
+
'timestamp': timestamp,
|
454
|
+
'datetime': self.iso8601(timestamp),
|
455
|
+
'order': self.safe_string(trade, 'orderId'),
|
456
|
+
'type': self.safe_string_lower(trade, 'type'),
|
457
|
+
'side': self.safe_string_lower_2(trade, 'takerSide', 'side'),
|
458
|
+
'takerOrMaker': takerMaker,
|
459
|
+
'price': self.omit_zero(self.safe_number_2(trade, 'tradePrice', 'price')),
|
460
|
+
'amount': self.omit_zero(self.safe_number_2(trade, 'filledQuantity', 'quantity')),
|
461
|
+
'cost': self.safe_string_2(trade, 'amount', 'filledAmount'),
|
462
|
+
'fee': {
|
463
|
+
'rate': None,
|
464
|
+
'cost': self.safe_string(trade, 'tradeFee'),
|
465
|
+
'currency': self.safe_string(trade, 'feeCurrency'),
|
466
|
+
},
|
467
|
+
}, market)
|
468
|
+
|
469
|
+
def parse_status(self, status):
|
470
|
+
statuses = {
|
471
|
+
'NEW': 'open',
|
472
|
+
'PARTIALLY_FILLED': 'open',
|
473
|
+
'FILLED': 'closed',
|
474
|
+
'PENDING_CANCEL': 'open',
|
475
|
+
'PARTIALLY_CANCELED': 'open',
|
476
|
+
'CANCELED': 'canceled',
|
477
|
+
# FAILED
|
478
|
+
}
|
479
|
+
return self.safe_string(statuses, status, status)
|
480
|
+
|
481
|
+
def parse_ws_order_trade(self, trade, market=None):
|
482
|
+
#
|
483
|
+
# {
|
484
|
+
# "symbol": "BTC_USDT",
|
485
|
+
# "type": "LIMIT",
|
486
|
+
# "quantity": "1",
|
487
|
+
# "orderId": "32471407854219264",
|
488
|
+
# "tradeFee": "0",
|
489
|
+
# "clientOrderId": "",
|
490
|
+
# "accountType": "SPOT",
|
491
|
+
# "feeCurrency": "",
|
492
|
+
# "eventType": "place",
|
493
|
+
# "source": "API",
|
494
|
+
# "side": "BUY",
|
495
|
+
# "filledQuantity": "0",
|
496
|
+
# "filledAmount": "0",
|
497
|
+
# "matchRole": "MAKER",
|
498
|
+
# "state": "NEW",
|
499
|
+
# "tradeTime": 0,
|
500
|
+
# "tradeAmount": "0",
|
501
|
+
# "orderAmount": "0",
|
502
|
+
# "createTime": 1648708186922,
|
503
|
+
# "price": "47112.1",
|
504
|
+
# "tradeQty": "0",
|
505
|
+
# "tradePrice": "0",
|
506
|
+
# "tradeId": "0",
|
507
|
+
# "ts": 1648708187469
|
508
|
+
# }
|
509
|
+
#
|
510
|
+
timestamp = self.safe_integer(trade, 'tradeTime')
|
511
|
+
marketId = self.safe_string(trade, 'symbol')
|
512
|
+
return self.safe_trade({
|
513
|
+
'info': trade,
|
514
|
+
'id': self.safe_string(trade, 'tradeId'),
|
515
|
+
'symbol': self.safe_symbol(marketId, market),
|
516
|
+
'timestamp': timestamp,
|
517
|
+
'datetime': self.iso8601(timestamp),
|
518
|
+
'order': self.safe_string(trade, 'orderId'),
|
519
|
+
'type': self.safe_string_lower(trade, 'type'),
|
520
|
+
'side': self.safe_string(trade, 'side'),
|
521
|
+
'takerOrMaker': self.safe_string_lower(trade, 'matchRole'),
|
522
|
+
'price': self.safe_string(trade, 'price'),
|
523
|
+
'amount': self.safe_string(trade, 'tradeAmount'),
|
524
|
+
'cost': None,
|
525
|
+
'fee': {
|
526
|
+
'rate': None,
|
527
|
+
'cost': self.safe_string(trade, 'tradeFee'),
|
528
|
+
'currency': self.safe_string(trade, 'feeCurrency'),
|
529
|
+
},
|
530
|
+
}, market)
|
531
|
+
|
532
|
+
def handle_order(self, client: Client, message):
|
533
|
+
#
|
534
|
+
# Order is created
|
535
|
+
#
|
536
|
+
# {
|
537
|
+
# channel: 'orders',
|
538
|
+
# data: [
|
539
|
+
# {
|
540
|
+
# "symbol": "BTC_USDT",
|
541
|
+
# "type": "LIMIT",
|
542
|
+
# "quantity": "1",
|
543
|
+
# "orderId": "32471407854219264",
|
544
|
+
# "tradeFee": "0",
|
545
|
+
# "clientOrderId": "",
|
546
|
+
# "accountType": "SPOT",
|
547
|
+
# "feeCurrency": "",
|
548
|
+
# "eventType": "place",
|
549
|
+
# "source": "API",
|
550
|
+
# "side": "BUY",
|
551
|
+
# "filledQuantity": "0",
|
552
|
+
# "filledAmount": "0",
|
553
|
+
# "matchRole": "MAKER",
|
554
|
+
# "state": "NEW",
|
555
|
+
# "tradeTime": 0,
|
556
|
+
# "tradeAmount": "0",
|
557
|
+
# "orderAmount": "0",
|
558
|
+
# "createTime": 1648708186922,
|
559
|
+
# "price": "47112.1",
|
560
|
+
# "tradeQty": "0",
|
561
|
+
# "tradePrice": "0",
|
562
|
+
# "tradeId": "0",
|
563
|
+
# "ts": 1648708187469
|
564
|
+
# }
|
565
|
+
# ]
|
566
|
+
# }
|
567
|
+
#
|
568
|
+
data = self.safe_value(message, 'data', [])
|
569
|
+
orders = self.orders
|
570
|
+
if orders is None:
|
571
|
+
limit = self.safe_integer(self.options, 'ordersLimit')
|
572
|
+
orders = ArrayCacheBySymbolById(limit)
|
573
|
+
self.orders = orders
|
574
|
+
marketIds = []
|
575
|
+
for i in range(0, len(data)):
|
576
|
+
order = self.safe_value(data, i)
|
577
|
+
marketId = self.safe_string(order, 'symbol')
|
578
|
+
eventType = self.safe_string(order, 'eventType')
|
579
|
+
if marketId is not None:
|
580
|
+
symbol = self.safe_symbol(marketId)
|
581
|
+
orderId = self.safe_string(order, 'orderId')
|
582
|
+
clientOrderId = self.safe_string(order, 'clientOrderId')
|
583
|
+
if eventType == 'place' or eventType == 'canceled':
|
584
|
+
parsed = self.parse_ws_order(order)
|
585
|
+
orders.append(parsed)
|
586
|
+
else:
|
587
|
+
previousOrders = self.safe_value(orders.hashmap, symbol, {})
|
588
|
+
previousOrder = self.safe_value_2(previousOrders, orderId, clientOrderId)
|
589
|
+
trade = self.parse_ws_trade(order)
|
590
|
+
self.handle_my_trades(client, trade)
|
591
|
+
if previousOrder['trades'] is None:
|
592
|
+
previousOrder['trades'] = []
|
593
|
+
previousOrder['trades'].append(trade)
|
594
|
+
previousOrder['lastTradeTimestamp'] = trade['timestamp']
|
595
|
+
totalCost = '0'
|
596
|
+
totalAmount = '0'
|
597
|
+
previousOrderTrades = previousOrder['trades']
|
598
|
+
for i in range(0, len(previousOrderTrades)):
|
599
|
+
previousOrderTrade = previousOrderTrades[i]
|
600
|
+
cost = self.number_to_string(previousOrderTrade['cost'])
|
601
|
+
amount = self.number_to_string(previousOrderTrade['amount'])
|
602
|
+
totalCost = Precise.string_add(totalCost, cost)
|
603
|
+
totalAmount = Precise.string_add(totalAmount, amount)
|
604
|
+
if Precise.string_gt(totalAmount, '0'):
|
605
|
+
previousOrder['average'] = self.parse_number(Precise.string_div(totalCost, totalAmount))
|
606
|
+
previousOrder['cost'] = self.parse_number(totalCost)
|
607
|
+
if previousOrder['filled'] is not None:
|
608
|
+
tradeAmount = self.number_to_string(trade['amount'])
|
609
|
+
previousOrderFilled = self.number_to_string(previousOrder['filled'])
|
610
|
+
previousOrderFilled = Precise.string_add(previousOrderFilled, tradeAmount)
|
611
|
+
previousOrder['filled'] = previousOrderFilled
|
612
|
+
if previousOrder['amount'] is not None:
|
613
|
+
previousOrderAmount = self.number_to_string(previousOrder['amount'])
|
614
|
+
previousOrder['remaining'] = self.parse_number(Precise.string_sub(previousOrderAmount, previousOrderFilled))
|
615
|
+
if previousOrder['fee'] is None:
|
616
|
+
previousOrder['fee'] = {
|
617
|
+
'rate': None,
|
618
|
+
'cost': 0,
|
619
|
+
'currency': trade['fee']['currency'],
|
620
|
+
}
|
621
|
+
if (previousOrder['fee']['cost'] is not None) and (trade['fee']['cost'] is not None):
|
622
|
+
stringOrderCost = self.number_to_string(previousOrder['fee']['cost'])
|
623
|
+
stringTradeCost = self.number_to_string(trade['fee']['cost'])
|
624
|
+
previousOrder['fee']['cost'] = Precise.string_add(stringOrderCost, stringTradeCost)
|
625
|
+
rawState = self.safe_string(order, 'state')
|
626
|
+
state = self.parse_status(rawState)
|
627
|
+
previousOrder['status'] = state
|
628
|
+
# update the newUpdates count
|
629
|
+
orders.append(previousOrder)
|
630
|
+
marketIds.append(marketId)
|
631
|
+
for i in range(0, len(marketIds)):
|
632
|
+
marketId = marketIds[i]
|
633
|
+
market = self.market(marketId)
|
634
|
+
symbol = market['symbol']
|
635
|
+
messageHash = 'orders:' + symbol
|
636
|
+
client.resolve(orders[symbol], messageHash)
|
637
|
+
client.resolve(orders, 'orders')
|
638
|
+
return message
|
639
|
+
|
640
|
+
def parse_ws_order(self, order, market=None):
|
641
|
+
#
|
642
|
+
# {
|
643
|
+
# "symbol": "BTC_USDT",
|
644
|
+
# "type": "LIMIT",
|
645
|
+
# "quantity": "1",
|
646
|
+
# "orderId": "32471407854219264",
|
647
|
+
# "tradeFee": "0",
|
648
|
+
# "clientOrderId": "",
|
649
|
+
# "accountType": "SPOT",
|
650
|
+
# "feeCurrency": "",
|
651
|
+
# "eventType": "place",
|
652
|
+
# "source": "API",
|
653
|
+
# "side": "BUY",
|
654
|
+
# "filledQuantity": "0",
|
655
|
+
# "filledAmount": "0",
|
656
|
+
# "matchRole": "MAKER",
|
657
|
+
# "state": "NEW",
|
658
|
+
# "tradeTime": 0,
|
659
|
+
# "tradeAmount": "0",
|
660
|
+
# "orderAmount": "0",
|
661
|
+
# "createTime": 1648708186922,
|
662
|
+
# "price": "47112.1",
|
663
|
+
# "tradeQty": "0",
|
664
|
+
# "tradePrice": "0",
|
665
|
+
# "tradeId": "0",
|
666
|
+
# "ts": 1648708187469
|
667
|
+
# }
|
668
|
+
#
|
669
|
+
id = self.safe_string(order, 'orderId')
|
670
|
+
clientOrderId = self.safe_string(order, 'clientOrderId')
|
671
|
+
marketId = self.safe_string(order, 'symbol')
|
672
|
+
timestamp = self.safe_string(order, 'ts')
|
673
|
+
filledAmount = self.safe_string(order, 'filledAmount')
|
674
|
+
status = self.safe_string(order, 'state')
|
675
|
+
trades = None
|
676
|
+
if not Precise.string_eq(filledAmount, '0'):
|
677
|
+
trades = []
|
678
|
+
trade = self.parse_ws_order_trade(order)
|
679
|
+
trades.append(trade)
|
680
|
+
return self.safe_order({
|
681
|
+
'info': order,
|
682
|
+
'symbol': self.safe_symbol(marketId, market),
|
683
|
+
'id': id,
|
684
|
+
'clientOrderId': clientOrderId,
|
685
|
+
'timestamp': timestamp,
|
686
|
+
'datetime': self.iso8601(timestamp),
|
687
|
+
'lastTradeTimestamp': None,
|
688
|
+
'type': self.safe_string(order, 'type'),
|
689
|
+
'timeInForce': None,
|
690
|
+
'postOnly': None,
|
691
|
+
'side': self.safe_string(order, 'side'),
|
692
|
+
'price': self.safe_string(order, 'price'),
|
693
|
+
'stopPrice': None,
|
694
|
+
'triggerPrice': None,
|
695
|
+
'amount': self.safe_string(order, 'quantity'),
|
696
|
+
'cost': None,
|
697
|
+
'average': None,
|
698
|
+
'filled': filledAmount,
|
699
|
+
'remaining': self.safe_string(order, 'remaining_size'),
|
700
|
+
'status': self.parse_status(status),
|
701
|
+
'fee': {
|
702
|
+
'rate': None,
|
703
|
+
'cost': self.safe_string(order, 'tradeFee'),
|
704
|
+
'currency': self.safe_string(order, 'feeCurrency'),
|
705
|
+
},
|
706
|
+
'trades': trades,
|
707
|
+
})
|
708
|
+
|
709
|
+
def handle_ticker(self, client: Client, message):
|
710
|
+
#
|
711
|
+
# {
|
712
|
+
# channel: 'ticker',
|
713
|
+
# data: [
|
714
|
+
# {
|
715
|
+
# symbol: 'BTC_USDT',
|
716
|
+
# startTime: 1677280800000,
|
717
|
+
# open: '23154.32',
|
718
|
+
# high: '23212.21',
|
719
|
+
# low: '22761.01',
|
720
|
+
# close: '23148.86',
|
721
|
+
# quantity: '105.179566',
|
722
|
+
# amount: '2423161.17436702',
|
723
|
+
# tradeCount: 17582,
|
724
|
+
# dailyChange: '-0.0002',
|
725
|
+
# markPrice: '23151.09',
|
726
|
+
# closeTime: 1677367197924,
|
727
|
+
# ts: 1677367251090
|
728
|
+
# }
|
729
|
+
# ]
|
730
|
+
# }
|
731
|
+
#
|
732
|
+
data = self.safe_value(message, 'data', [])
|
733
|
+
for i in range(0, len(data)):
|
734
|
+
item = data[i]
|
735
|
+
marketId = self.safe_string(item, 'symbol')
|
736
|
+
if marketId is not None:
|
737
|
+
ticker = self.parse_ticker(item)
|
738
|
+
symbol = ticker['symbol']
|
739
|
+
self.tickers[symbol] = ticker
|
740
|
+
messageHash = 'ticker:' + symbol
|
741
|
+
client.resolve(ticker, messageHash)
|
742
|
+
client.resolve(self.tickers, 'ticker')
|
743
|
+
return message
|
744
|
+
|
745
|
+
def handle_order_book(self, client: Client, message):
|
746
|
+
#
|
747
|
+
# snapshot
|
748
|
+
#
|
749
|
+
# {
|
750
|
+
# channel: 'book_lv2',
|
751
|
+
# data: [
|
752
|
+
# {
|
753
|
+
# symbol: 'BTC_USDT',
|
754
|
+
# createTime: 1677368876253,
|
755
|
+
# "asks": [
|
756
|
+
# ["5.65", "0.02"],
|
757
|
+
# ...
|
758
|
+
# ],
|
759
|
+
# "bids": [
|
760
|
+
# ["6.16", "0.6"],
|
761
|
+
# ...
|
762
|
+
# ],
|
763
|
+
# lastId: 164148724,
|
764
|
+
# id: 164148725,
|
765
|
+
# ts: 1677368876316
|
766
|
+
# }
|
767
|
+
# ],
|
768
|
+
# action: 'snapshot'
|
769
|
+
# }
|
770
|
+
#
|
771
|
+
# update
|
772
|
+
#
|
773
|
+
# {
|
774
|
+
# channel: 'book_lv2',
|
775
|
+
# data: [
|
776
|
+
# {
|
777
|
+
# symbol: 'BTC_USDT',
|
778
|
+
# createTime: 1677368876882,
|
779
|
+
# "asks": [
|
780
|
+
# ["6.35", "3"]
|
781
|
+
# ],
|
782
|
+
# "bids": [
|
783
|
+
# ["5.65", "0.02"]
|
784
|
+
# ],
|
785
|
+
# lastId: 164148725,
|
786
|
+
# id: 164148726,
|
787
|
+
# ts: 1677368876890
|
788
|
+
# }
|
789
|
+
# ],
|
790
|
+
# action: 'update'
|
791
|
+
# }
|
792
|
+
#
|
793
|
+
data = self.safe_value(message, 'data', [])
|
794
|
+
type = self.safe_string(message, 'action')
|
795
|
+
snapshot = type == 'snapshot'
|
796
|
+
update = type == 'update'
|
797
|
+
for i in range(0, len(data)):
|
798
|
+
item = data[i]
|
799
|
+
marketId = self.safe_string(item, 'symbol')
|
800
|
+
market = self.safe_market(marketId)
|
801
|
+
symbol = market['symbol']
|
802
|
+
name = 'book_lv2'
|
803
|
+
messageHash = name + ':' + symbol
|
804
|
+
subscription = self.safe_value(client.subscriptions, messageHash, {})
|
805
|
+
limit = self.safe_integer(subscription, 'limit')
|
806
|
+
timestamp = self.safe_integer(item, 'ts')
|
807
|
+
asks = self.safe_value(item, 'asks')
|
808
|
+
bids = self.safe_value(item, 'bids')
|
809
|
+
if snapshot or update:
|
810
|
+
if snapshot:
|
811
|
+
self.orderbooks[symbol] = self.order_book({}, limit)
|
812
|
+
orderbook = self.orderbooks[symbol]
|
813
|
+
if bids is not None:
|
814
|
+
for i in range(0, len(bids)):
|
815
|
+
bid = self.safe_value(bids, i)
|
816
|
+
price = self.safe_number(bid, 0)
|
817
|
+
amount = self.safe_number(bid, 1)
|
818
|
+
orderbook['bids'].store(price, amount)
|
819
|
+
if asks is not None:
|
820
|
+
for i in range(0, len(asks)):
|
821
|
+
ask = self.safe_value(asks, i)
|
822
|
+
price = self.safe_number(ask, 0)
|
823
|
+
amount = self.safe_number(ask, 1)
|
824
|
+
orderbook['asks'].store(price, amount)
|
825
|
+
orderbook['symbol'] = symbol
|
826
|
+
orderbook['timestamp'] = timestamp
|
827
|
+
orderbook['datetime'] = self.iso8601(timestamp)
|
828
|
+
client.resolve(orderbook, messageHash)
|
829
|
+
|
830
|
+
def handle_balance(self, client: Client, message):
|
831
|
+
#
|
832
|
+
# {
|
833
|
+
# "channel": "balances",
|
834
|
+
# "data": [
|
835
|
+
# {
|
836
|
+
# "changeTime": 1657312008411,
|
837
|
+
# "accountId": "1234",
|
838
|
+
# "accountType": "SPOT",
|
839
|
+
# "eventType": "place_order",
|
840
|
+
# "available": "9999999983.668",
|
841
|
+
# "currency": "BTC",
|
842
|
+
# "id": 60018450912695040,
|
843
|
+
# "userId": 12345,
|
844
|
+
# "hold": "16.332",
|
845
|
+
# "ts": 1657312008443
|
846
|
+
# }
|
847
|
+
# ]
|
848
|
+
# }
|
849
|
+
#
|
850
|
+
data = self.safe_value(message, 'data', [])
|
851
|
+
messageHash = 'balances'
|
852
|
+
self.balance = self.parse_ws_balance(data)
|
853
|
+
client.resolve(self.balance, messageHash)
|
854
|
+
|
855
|
+
def parse_ws_balance(self, response):
|
856
|
+
#
|
857
|
+
# [
|
858
|
+
# {
|
859
|
+
# "changeTime": 1657312008411,
|
860
|
+
# "accountId": "1234",
|
861
|
+
# "accountType": "SPOT",
|
862
|
+
# "eventType": "place_order",
|
863
|
+
# "available": "9999999983.668",
|
864
|
+
# "currency": "BTC",
|
865
|
+
# "id": 60018450912695040,
|
866
|
+
# "userId": 12345,
|
867
|
+
# "hold": "16.332",
|
868
|
+
# "ts": 1657312008443
|
869
|
+
# }
|
870
|
+
# ]
|
871
|
+
#
|
872
|
+
firstBalance = self.safe_value(response, 0, {})
|
873
|
+
timestamp = self.safe_integer(firstBalance, 'ts')
|
874
|
+
result = {
|
875
|
+
'info': response,
|
876
|
+
'timestamp': timestamp,
|
877
|
+
'datetime': self.iso8601(timestamp),
|
878
|
+
}
|
879
|
+
for i in range(0, len(response)):
|
880
|
+
balance = self.safe_value(response, i)
|
881
|
+
currencyId = self.safe_string(balance, 'currency')
|
882
|
+
code = self.safe_currency_code(currencyId)
|
883
|
+
newAccount = self.account()
|
884
|
+
newAccount['free'] = self.safe_string(balance, 'available')
|
885
|
+
newAccount['used'] = self.safe_string(balance, 'hold')
|
886
|
+
result[code] = newAccount
|
887
|
+
return self.safe_balance(result)
|
888
|
+
|
889
|
+
def handle_my_trades(self, client: Client, parsedTrade):
|
890
|
+
# emulated using the orders' stream
|
891
|
+
messageHash = 'myTrades'
|
892
|
+
symbol = parsedTrade['symbol']
|
893
|
+
if self.myTrades is None:
|
894
|
+
limit = self.safe_integer(self.options, 'tradesLimit', 1000)
|
895
|
+
self.myTrades = ArrayCacheBySymbolById(limit)
|
896
|
+
trades = self.myTrades
|
897
|
+
trades.append(parsedTrade)
|
898
|
+
client.resolve(trades, messageHash)
|
899
|
+
symbolMessageHash = messageHash + ':' + symbol
|
900
|
+
client.resolve(trades, symbolMessageHash)
|
901
|
+
|
902
|
+
def handle_message(self, client: Client, message):
|
903
|
+
if self.handle_error_message(client, message):
|
904
|
+
return
|
905
|
+
type = self.safe_string(message, 'channel')
|
906
|
+
event = self.safe_string(message, 'event')
|
907
|
+
if event == 'pong':
|
908
|
+
client.lastPong = self.milliseconds()
|
909
|
+
methods = {
|
910
|
+
'candles_minute_1': self.handle_ohlcv,
|
911
|
+
'candles_minute_5': self.handle_ohlcv,
|
912
|
+
'candles_minute_10': self.handle_ohlcv,
|
913
|
+
'candles_minute_15': self.handle_ohlcv,
|
914
|
+
'candles_minute_30': self.handle_ohlcv,
|
915
|
+
'candles_hour_1': self.handle_ohlcv,
|
916
|
+
'candles_hour_2': self.handle_ohlcv,
|
917
|
+
'candles_hour_4': self.handle_ohlcv,
|
918
|
+
'candles_hour_6': self.handle_ohlcv,
|
919
|
+
'candles_hour_12': self.handle_ohlcv,
|
920
|
+
'candles_day_1': self.handle_ohlcv,
|
921
|
+
'candles_day_3': self.handle_ohlcv,
|
922
|
+
'candles_week_1': self.handle_ohlcv,
|
923
|
+
'candles_month_1': self.handle_ohlcv,
|
924
|
+
'book': self.handle_order_book,
|
925
|
+
'book_lv2': self.handle_order_book,
|
926
|
+
'ticker': self.handle_ticker,
|
927
|
+
'trades': self.handle_trade,
|
928
|
+
'orders': self.handle_order,
|
929
|
+
'balances': self.handle_balance,
|
930
|
+
}
|
931
|
+
method = self.safe_value(methods, type)
|
932
|
+
if type == 'auth':
|
933
|
+
self.handle_authenticate(client, message)
|
934
|
+
else:
|
935
|
+
data = self.safe_value(message, 'data', [])
|
936
|
+
dataLength = len(data)
|
937
|
+
if dataLength > 0:
|
938
|
+
return method(client, message)
|
939
|
+
|
940
|
+
def handle_error_message(self, client: Client, message):
|
941
|
+
#
|
942
|
+
# {message: 'Invalid channel value ["ordersss"]', event: 'error'}
|
943
|
+
event = self.safe_string(message, 'event')
|
944
|
+
if event == 'error':
|
945
|
+
error = self.safe_string(message, 'message')
|
946
|
+
raise ExchangeError(self.id + ' error: ' + self.json(error))
|
947
|
+
return False
|
948
|
+
|
949
|
+
def handle_authenticate(self, client: Client, message):
|
950
|
+
#
|
951
|
+
# {
|
952
|
+
# success: True,
|
953
|
+
# ret_msg: '',
|
954
|
+
# op: 'auth',
|
955
|
+
# conn_id: 'ce3dpomvha7dha97tvp0-2xh'
|
956
|
+
# }
|
957
|
+
#
|
958
|
+
data = self.safe_value(message, 'data')
|
959
|
+
success = self.safe_value(data, 'success')
|
960
|
+
messageHash = 'authenticated'
|
961
|
+
if success:
|
962
|
+
client.resolve(message, messageHash)
|
963
|
+
else:
|
964
|
+
error = AuthenticationError(self.id + ' ' + self.json(message))
|
965
|
+
client.reject(error, messageHash)
|
966
|
+
if messageHash in client.subscriptions:
|
967
|
+
del client.subscriptions[messageHash]
|
968
|
+
return message
|
969
|
+
|
970
|
+
def ping(self, client):
|
971
|
+
return {
|
972
|
+
'event': 'ping',
|
973
|
+
}
|