ccxt 4.2.93 → 4.2.94

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.
@@ -16,10 +16,12 @@ class kraken extends kraken$1 {
16
16
  'watchMyTrades': true,
17
17
  'watchOHLCV': true,
18
18
  'watchOrderBook': true,
19
+ 'watchOrderBookForSymbols': true,
19
20
  'watchOrders': true,
20
21
  'watchTicker': true,
21
- 'watchTickers': false,
22
+ 'watchTickers': true,
22
23
  'watchTrades': true,
24
+ 'watchTradesForSymbols': true,
23
25
  'createOrderWs': true,
24
26
  'editOrderWs': true,
25
27
  'cancelOrderWs': true,
@@ -310,10 +312,9 @@ class kraken extends kraken$1 {
310
312
  // ]
311
313
  //
312
314
  const wsName = message[3];
313
- const name = 'ticker';
314
- const messageHash = name + ':' + wsName;
315
315
  const market = this.safeValue(this.options['marketsByWsName'], wsName);
316
316
  const symbol = market['symbol'];
317
+ const messageHash = this.getMessageHash('ticker', undefined, symbol);
317
318
  const ticker = message[1];
318
319
  const vwap = this.safeString(ticker['p'], 0);
319
320
  let quoteVolume = undefined;
@@ -344,9 +345,6 @@ class kraken extends kraken$1 {
344
345
  'quoteVolume': quoteVolume,
345
346
  'info': ticker,
346
347
  });
347
- // todo add support for multiple tickers (may be tricky)
348
- // kraken confirms multi-pair subscriptions separately one by one
349
- // trigger correct watchTickers calls upon receiving any of symbols
350
348
  this.tickers[symbol] = result;
351
349
  client.resolve(result, messageHash);
352
350
  }
@@ -364,9 +362,9 @@ class kraken extends kraken$1 {
364
362
  //
365
363
  const wsName = this.safeString(message, 3);
366
364
  const name = this.safeString(message, 2);
367
- const messageHash = name + ':' + wsName;
368
365
  const market = this.safeValue(this.options['marketsByWsName'], wsName);
369
366
  const symbol = market['symbol'];
367
+ const messageHash = this.getMessageHash(name, undefined, symbol);
370
368
  let stored = this.safeValue(this.trades, symbol);
371
369
  if (stored === undefined) {
372
370
  const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
@@ -467,25 +465,61 @@ class kraken extends kraken$1 {
467
465
  * @param {object} [params] extra parameters specific to the exchange API endpoint
468
466
  * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
469
467
  */
470
- return await this.watchPublic('ticker', symbol, params);
468
+ await this.loadMarkets();
469
+ symbol = this.symbol(symbol);
470
+ const tickers = await this.watchTickers([symbol], params);
471
+ return tickers[symbol];
472
+ }
473
+ async watchTickers(symbols = undefined, params = {}) {
474
+ /**
475
+ * @method
476
+ * @name kraken#watchTickers
477
+ * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
478
+ * @param {string} symbol unified symbol of the market to fetch the ticker for
479
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
480
+ * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
481
+ */
482
+ await this.loadMarkets();
483
+ symbols = this.marketSymbols(symbols, undefined, false);
484
+ const ticker = await this.watchMultiHelper('ticker', 'ticker', symbols, undefined, params);
485
+ if (this.newUpdates) {
486
+ const result = {};
487
+ result[ticker['symbol']] = ticker;
488
+ return result;
489
+ }
490
+ return this.filterByArray(this.tickers, 'symbol', symbols);
471
491
  }
472
492
  async watchTrades(symbol, since = undefined, limit = undefined, params = {}) {
473
493
  /**
474
494
  * @method
475
495
  * @name kraken#watchTrades
476
496
  * @description get the list of most recent trades for a particular symbol
497
+ * @see https://docs.kraken.com/websockets/#message-trade
477
498
  * @param {string} symbol unified symbol of the market to fetch trades for
478
499
  * @param {int} [since] timestamp in ms of the earliest trade to fetch
479
500
  * @param {int} [limit] the maximum amount of trades to fetch
480
501
  * @param {object} [params] extra parameters specific to the exchange API endpoint
481
502
  * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
482
503
  */
483
- await this.loadMarkets();
484
- symbol = this.symbol(symbol);
485
- const name = 'trade';
486
- const trades = await this.watchPublic(name, symbol, params);
504
+ return await this.watchTradesForSymbols([symbol], since, limit, params);
505
+ }
506
+ async watchTradesForSymbols(symbols, since = undefined, limit = undefined, params = {}) {
507
+ /**
508
+ * @method
509
+ * @name kraken#watchTradesForSymbols
510
+ * @see https://docs.kraken.com/websockets/#message-trade
511
+ * @description get the list of most recent trades for a list of symbols
512
+ * @param {string[]} symbols unified symbol of the market to fetch trades for
513
+ * @param {int} [since] timestamp in ms of the earliest trade to fetch
514
+ * @param {int} [limit] the maximum amount of trades to fetch
515
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
516
+ * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
517
+ */
518
+ const trades = await this.watchMultiHelper('trade', 'trade', symbols, undefined, params);
487
519
  if (this.newUpdates) {
488
- limit = trades.getLimit(symbol, limit);
520
+ const first = this.safeList(trades, 0);
521
+ const tradeSymbol = this.safeString(first, 'symbol');
522
+ limit = trades.getLimit(tradeSymbol, limit);
489
523
  }
490
524
  return this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
491
525
  }
@@ -494,15 +528,28 @@ class kraken extends kraken$1 {
494
528
  * @method
495
529
  * @name kraken#watchOrderBook
496
530
  * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
531
+ * @see https://docs.kraken.com/websockets/#message-book
497
532
  * @param {string} symbol unified symbol of the market to fetch the order book for
498
533
  * @param {int} [limit] the maximum amount of order book entries to return
499
534
  * @param {object} [params] extra parameters specific to the exchange API endpoint
500
535
  * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
501
536
  */
502
- const name = 'book';
537
+ return await this.watchOrderBookForSymbols([symbol], limit, params);
538
+ }
539
+ async watchOrderBookForSymbols(symbols, limit = undefined, params = {}) {
540
+ /**
541
+ * @method
542
+ * @name kraken#watchOrderBookForSymbols
543
+ * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
544
+ * @see https://docs.kraken.com/websockets/#message-book
545
+ * @param {string[]} symbols unified array of symbols
546
+ * @param {int} [limit] the maximum amount of order book entries to return
547
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
548
+ * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
549
+ */
503
550
  const request = {};
504
551
  if (limit !== undefined) {
505
- if ((limit === 10) || (limit === 25) || (limit === 100) || (limit === 500) || (limit === 1000)) {
552
+ if (this.inArray(limit, [10, 25, 100, 500, 1000])) {
506
553
  request['subscription'] = {
507
554
  'depth': limit, // default 10, valid options 10, 25, 100, 500, 1000
508
555
  };
@@ -511,7 +558,7 @@ class kraken extends kraken$1 {
511
558
  throw new errors.NotSupported(this.id + ' watchOrderBook accepts limit values of 10, 25, 100, 500 and 1000 only');
512
559
  }
513
560
  }
514
- const orderbook = await this.watchPublic(name, symbol, this.extend(request, params));
561
+ const orderbook = await this.watchMultiHelper('orderbook', 'book', symbols, { 'limit': limit }, this.extend(request, params));
515
562
  return orderbook.limit();
516
563
  }
517
564
  async watchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
@@ -640,7 +687,7 @@ class kraken extends kraken$1 {
640
687
  const market = this.safeValue(this.options['marketsByWsName'], wsName);
641
688
  const symbol = market['symbol'];
642
689
  let timestamp = undefined;
643
- const messageHash = 'book:' + wsName;
690
+ const messageHash = this.getMessageHash('orderbook', undefined, symbol);
644
691
  // if this is a snapshot
645
692
  if ('as' in message[1]) {
646
693
  // todo get depth from marketsByWsName
@@ -721,6 +768,7 @@ class kraken extends kraken$1 {
721
768
  if (localChecksum !== c) {
722
769
  const error = new errors.InvalidNonce(this.id + ' invalid checksum');
723
770
  client.reject(error, messageHash);
771
+ return;
724
772
  }
725
773
  }
726
774
  orderbook['symbol'] = symbol;
@@ -1248,6 +1296,48 @@ class kraken extends kraken$1 {
1248
1296
  'trades': trades,
1249
1297
  });
1250
1298
  }
1299
+ async watchMultiHelper(unifiedName, channelName, symbols = undefined, subscriptionArgs = undefined, params = {}) {
1300
+ await this.loadMarkets();
1301
+ // symbols are required
1302
+ symbols = this.marketSymbols(symbols, undefined, false, true, false);
1303
+ const messageHashes = [];
1304
+ for (let i = 0; i < symbols.length; i++) {
1305
+ messageHashes.push(this.getMessageHash(unifiedName, undefined, this.symbol(symbols[i])));
1306
+ }
1307
+ // for WS subscriptions, we can't use .marketIds (symbols), instead a custom is field needed
1308
+ const markets = this.marketsForSymbols(symbols);
1309
+ const wsMarketIds = [];
1310
+ for (let i = 0; i < markets.length; i++) {
1311
+ const wsMarketId = this.safeString(markets[i]['info'], 'wsname');
1312
+ wsMarketIds.push(wsMarketId);
1313
+ }
1314
+ const request = {
1315
+ 'event': 'subscribe',
1316
+ 'reqid': this.requestId(),
1317
+ 'pair': wsMarketIds,
1318
+ 'subscription': {
1319
+ 'name': channelName,
1320
+ },
1321
+ };
1322
+ const url = this.urls['api']['ws']['public'];
1323
+ return await this.watchMultiple(url, messageHashes, this.extend(request, params), messageHashes, subscriptionArgs);
1324
+ }
1325
+ getMessageHash(unifiedElementName, subChannelName = undefined, symbol = undefined) {
1326
+ // unifiedElementName can be : orderbook, trade, ticker, bidask ...
1327
+ // subChannelName only applies to channel that needs specific variation (i.e. depth_50, depth_100..) to be selected
1328
+ const withSymbol = symbol !== undefined;
1329
+ let messageHash = unifiedElementName;
1330
+ if (!withSymbol) {
1331
+ messageHash += 's';
1332
+ }
1333
+ else {
1334
+ messageHash += '@' + symbol;
1335
+ }
1336
+ if (subChannelName !== undefined) {
1337
+ messageHash += '#' + subChannelName;
1338
+ }
1339
+ return messageHash;
1340
+ }
1251
1341
  handleSubscriptionStatus(client, message) {
1252
1342
  //
1253
1343
  // public
@@ -25,9 +25,12 @@ class krakenfutures extends krakenfutures$1 {
25
25
  'fetchTradesWs': false,
26
26
  'watchOHLCV': false,
27
27
  'watchOrderBook': true,
28
+ 'watchOrderBookForSymbols': true,
28
29
  'watchTicker': true,
29
30
  'watchTickers': true,
31
+ 'watchBidsAsks': true,
30
32
  'watchTrades': true,
33
+ 'watchTradesForSymbols': true,
31
34
  'watchBalance': true,
32
35
  // 'watchStatus': true, // https://docs.futures.kraken.com/#websocket-api-public-feeds-heartbeat
33
36
  'watchOrders': true,
@@ -48,12 +51,6 @@ class krakenfutures extends krakenfutures$1 {
48
51
  'OHLCVLimit': 1000,
49
52
  'connectionLimit': 100,
50
53
  'requestLimit': 100,
51
- 'watchTicker': {
52
- 'method': 'ticker', // or ticker_lite
53
- },
54
- 'watchTickers': {
55
- 'method': 'ticker', // or ticker_lite
56
- },
57
54
  'fetchBalance': {
58
55
  'type': undefined,
59
56
  },
@@ -91,6 +88,20 @@ class krakenfutures extends krakenfutures$1 {
91
88
  }
92
89
  return future;
93
90
  }
91
+ async watchOrderBookForSymbols(symbols, limit = undefined, params = {}) {
92
+ /**
93
+ * @method
94
+ * @name krakenfutures#watchOrderBookForSymbols
95
+ * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
96
+ * @see https://docs.futures.kraken.com/#websocket-api-public-feeds-challenge
97
+ * @param {string[]} symbols unified array of symbols
98
+ * @param {int} [limit] the maximum amount of order book entries to return
99
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
100
+ * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
101
+ */
102
+ const orderbook = await this.watchMultiHelper('orderbook', 'book', symbols, { 'limit': limit }, params);
103
+ return orderbook.limit();
104
+ }
94
105
  async subscribePublic(name, symbols, params = {}) {
95
106
  /**
96
107
  * @ignore
@@ -158,34 +169,49 @@ class krakenfutures extends krakenfutures$1 {
158
169
  * @param {object} [params] extra parameters specific to the exchange API endpoint
159
170
  * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
160
171
  */
161
- const options = this.safeValue(this.options, 'watchTicker');
162
- const method = this.safeString(options, 'method', 'ticker'); // or ticker_lite
163
- const name = this.safeString(params, 'method', method);
164
- params = this.omit(params, ['method']);
165
- return await this.subscribePublic(name, [symbol], params);
172
+ await this.loadMarkets();
173
+ symbol = this.symbol(symbol);
174
+ const tickers = await this.watchTickers([symbol], params);
175
+ return tickers[symbol];
166
176
  }
167
177
  async watchTickers(symbols = undefined, params = {}) {
168
178
  /**
169
179
  * @method
170
- * @name krakenfutures#watchTicker
180
+ * @name krakenfutures#watchTickers
171
181
  * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
172
- * @see https://docs.futures.kraken.com/#websocket-api-public-feeds-ticker-lite
182
+ * @see https://docs.futures.kraken.com/#websocket-api-public-feeds-ticker
173
183
  * @param {string} symbol unified symbol of the market to fetch the ticker for
174
184
  * @param {object} [params] extra parameters specific to the exchange API endpoint
175
185
  * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
176
186
  */
177
- const method = this.safeString(this.options, 'watchTickerMethod', 'ticker'); // or ticker_lite
178
- const name = this.safeString2(params, 'method', 'watchTickerMethod', method);
179
- params = this.omit(params, ['watchTickerMethod', 'method']);
187
+ await this.loadMarkets();
180
188
  symbols = this.marketSymbols(symbols, undefined, false);
181
- const ticker = await this.subscribePublic(name, symbols, params);
189
+ const ticker = await this.watchMultiHelper('ticker', 'ticker', symbols, undefined, params);
182
190
  if (this.newUpdates) {
183
- const tickers = {};
184
- tickers[ticker['symbol']] = ticker;
185
- return tickers;
191
+ const result = {};
192
+ result[ticker['symbol']] = ticker;
193
+ return result;
186
194
  }
187
195
  return this.filterByArray(this.tickers, 'symbol', symbols);
188
196
  }
197
+ async watchBidsAsks(symbols = undefined, params = {}) {
198
+ /**
199
+ * @method
200
+ * @name krakenfutures#watchBidsAsks
201
+ * @see https://docs.futures.kraken.com/#websocket-api-public-feeds-ticker-lite
202
+ * @description watches best bid & ask for symbols
203
+ * @param {string[]} symbols unified symbol of the market to fetch the ticker for
204
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
205
+ * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
206
+ */
207
+ const ticker = await this.watchMultiHelper('bidask', 'ticker_lite', symbols, undefined, params);
208
+ if (this.newUpdates) {
209
+ const result = {};
210
+ result[ticker['symbol']] = ticker;
211
+ return result;
212
+ }
213
+ return this.filterByArray(this.bidsasks, 'symbol', symbols);
214
+ }
189
215
  async watchTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
190
216
  /**
191
217
  * @method
@@ -198,11 +224,25 @@ class krakenfutures extends krakenfutures$1 {
198
224
  * @param {object} [params] extra parameters specific to the exchange API endpoint
199
225
  * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
200
226
  */
201
- await this.loadMarkets();
202
- const name = 'trade';
203
- const trades = await this.subscribePublic(name, [symbol], params);
227
+ return await this.watchTradesForSymbols([symbol], since, limit, params);
228
+ }
229
+ async watchTradesForSymbols(symbols, since = undefined, limit = undefined, params = {}) {
230
+ /**
231
+ * @method
232
+ * @name krakenfutures#watchTradesForSymbols
233
+ * @see https://docs.futures.kraken.com/#websocket-api-public-feeds-trade
234
+ * @description get the list of most recent trades for a list of symbols
235
+ * @param {string[]} symbols unified symbol of the market to fetch trades for
236
+ * @param {int} [since] timestamp in ms of the earliest trade to fetch
237
+ * @param {int} [limit] the maximum amount of trades to fetch
238
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
239
+ * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
240
+ */
241
+ const trades = await this.watchMultiHelper('trade', 'trade', symbols, undefined, params);
204
242
  if (this.newUpdates) {
205
- limit = trades.getLimit(symbol, limit);
243
+ const first = this.safeList(trades, 0);
244
+ const tradeSymbol = this.safeString(first, 'symbol');
245
+ limit = trades.getLimit(tradeSymbol, limit);
206
246
  }
207
247
  return this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
208
248
  }
@@ -217,8 +257,7 @@ class krakenfutures extends krakenfutures$1 {
217
257
  * @param {object} [params] extra parameters specific to the exchange API endpoint
218
258
  * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
219
259
  */
220
- const orderbook = await this.subscribePublic('book', [symbol], params);
221
- return orderbook.limit();
260
+ return await this.watchOrderBookForSymbols([symbol], limit, params);
222
261
  }
223
262
  async watchPositions(symbols = undefined, since = undefined, limit = undefined, params = {}) {
224
263
  /**
@@ -466,7 +505,7 @@ class krakenfutures extends krakenfutures$1 {
466
505
  if (marketId !== undefined) {
467
506
  const market = this.market(marketId);
468
507
  const symbol = market['symbol'];
469
- const messageHash = 'trade:' + symbol;
508
+ const messageHash = this.getMessageHash('trade', undefined, symbol);
470
509
  if (this.safeList(this.trades, symbol) === undefined) {
471
510
  const tradesLimit = this.safeInteger(this.options, 'tradesLimit', 1000);
472
511
  this.trades[symbol] = new Cache.ArrayCache(tradesLimit);
@@ -488,7 +527,6 @@ class krakenfutures extends krakenfutures$1 {
488
527
  }
489
528
  client.resolve(tradesArray, messageHash);
490
529
  }
491
- return message;
492
530
  }
493
531
  parseWsTrade(trade, market = undefined) {
494
532
  //
@@ -914,7 +952,16 @@ class krakenfutures extends krakenfutures$1 {
914
952
  // "volumeQuote": 19628180
915
953
  // }
916
954
  //
917
- // ticker_lite
955
+ const marketId = this.safeString(message, 'product_id');
956
+ if (marketId !== undefined) {
957
+ const ticker = this.parseWsTicker(message);
958
+ const symbol = ticker['symbol'];
959
+ this.tickers[symbol] = ticker;
960
+ const messageHash = this.getMessageHash('ticker', undefined, symbol);
961
+ client.resolve(ticker, messageHash);
962
+ }
963
+ }
964
+ handleBidAsk(client, message) {
918
965
  //
919
966
  // {
920
967
  // "feed": "ticker_lite",
@@ -932,16 +979,13 @@ class krakenfutures extends krakenfutures$1 {
932
979
  // }
933
980
  //
934
981
  const marketId = this.safeString(message, 'product_id');
935
- const feed = this.safeString(message, 'feed');
936
982
  if (marketId !== undefined) {
937
983
  const ticker = this.parseWsTicker(message);
938
984
  const symbol = ticker['symbol'];
939
- this.tickers[symbol] = ticker;
940
- const messageHash = feed + ':' + symbol;
985
+ this.bidsasks[symbol] = ticker;
986
+ const messageHash = this.getMessageHash('bidask', undefined, symbol);
941
987
  client.resolve(ticker, messageHash);
942
988
  }
943
- client.resolve(this.tickers, feed);
944
- return message;
945
989
  }
946
990
  parseWsTicker(ticker, market = undefined) {
947
991
  //
@@ -1053,14 +1097,14 @@ class krakenfutures extends krakenfutures$1 {
1053
1097
  const marketId = this.safeString(message, 'product_id');
1054
1098
  const market = this.safeMarket(marketId);
1055
1099
  const symbol = market['symbol'];
1056
- const messageHash = 'book:' + symbol;
1057
- const subscription = this.safeValue(client.subscriptions, messageHash, {});
1100
+ const messageHash = this.getMessageHash('orderbook', undefined, symbol);
1101
+ const subscription = this.safeDict(client.subscriptions, messageHash, {});
1058
1102
  const limit = this.safeInteger(subscription, 'limit');
1059
1103
  const timestamp = this.safeInteger(message, 'timestamp');
1060
1104
  this.orderbooks[symbol] = this.orderBook({}, limit);
1061
1105
  const orderbook = this.orderbooks[symbol];
1062
- const bids = this.safeValue(message, 'bids');
1063
- const asks = this.safeValue(message, 'asks');
1106
+ const bids = this.safeList(message, 'bids');
1107
+ const asks = this.safeList(message, 'asks');
1064
1108
  for (let i = 0; i < bids.length; i++) {
1065
1109
  const bid = bids[i];
1066
1110
  const price = this.safeNumber(bid, 'price');
@@ -1095,7 +1139,7 @@ class krakenfutures extends krakenfutures$1 {
1095
1139
  const marketId = this.safeString(message, 'product_id');
1096
1140
  const market = this.safeMarket(marketId);
1097
1141
  const symbol = market['symbol'];
1098
- const messageHash = 'book:' + symbol;
1142
+ const messageHash = this.getMessageHash('orderbook', undefined, symbol);
1099
1143
  const orderbook = this.orderbooks[symbol];
1100
1144
  const side = this.safeString(message, 'side');
1101
1145
  const price = this.safeNumber(message, 'price');
@@ -1422,6 +1466,39 @@ class krakenfutures extends krakenfutures$1 {
1422
1466
  },
1423
1467
  });
1424
1468
  }
1469
+ async watchMultiHelper(unifiedName, channelName, symbols = undefined, subscriptionArgs = undefined, params = {}) {
1470
+ await this.loadMarkets();
1471
+ // symbols are required
1472
+ symbols = this.marketSymbols(symbols, undefined, false, true, false);
1473
+ const messageHashes = [];
1474
+ for (let i = 0; i < symbols.length; i++) {
1475
+ messageHashes.push(this.getMessageHash(unifiedName, undefined, this.symbol(symbols[i])));
1476
+ }
1477
+ const marketIds = this.marketIds(symbols);
1478
+ const request = {
1479
+ 'event': 'subscribe',
1480
+ 'feed': channelName,
1481
+ 'product_ids': marketIds,
1482
+ };
1483
+ const url = this.urls['api']['ws'];
1484
+ return await this.watchMultiple(url, messageHashes, this.extend(request, params), messageHashes, subscriptionArgs);
1485
+ }
1486
+ getMessageHash(unifiedElementName, subChannelName = undefined, symbol = undefined) {
1487
+ // unifiedElementName can be : orderbook, trade, ticker, bidask ...
1488
+ // subChannelName only applies to channel that needs specific variation (i.e. depth_50, depth_100..) to be selected
1489
+ const withSymbol = symbol !== undefined;
1490
+ let messageHash = unifiedElementName;
1491
+ if (!withSymbol) {
1492
+ messageHash += 's';
1493
+ }
1494
+ else {
1495
+ messageHash += ':' + symbol;
1496
+ }
1497
+ if (subChannelName !== undefined) {
1498
+ messageHash += '#' + subChannelName;
1499
+ }
1500
+ return messageHash;
1501
+ }
1425
1502
  handleErrorMessage(client, message) {
1426
1503
  //
1427
1504
  // {
@@ -1452,10 +1529,10 @@ class krakenfutures extends krakenfutures$1 {
1452
1529
  const feed = this.safeString(message, 'feed');
1453
1530
  const methods = {
1454
1531
  'ticker': this.handleTicker,
1532
+ 'ticker_lite': this.handleBidAsk,
1455
1533
  'trade': this.handleTrade,
1456
1534
  'trade_snapshot': this.handleTrade,
1457
1535
  // 'heartbeat': this.handleStatus,
1458
- 'ticker_lite': this.handleTicker,
1459
1536
  'book': this.handleOrderBook,
1460
1537
  'book_snapshot': this.handleOrderBookSnapshot,
1461
1538
  'open_orders_verbose': this.handleOrder,
@@ -173,22 +173,46 @@ class kucoin extends kucoin$1 {
173
173
  /**
174
174
  * @method
175
175
  * @name kucoin#watchTickers
176
+ * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/ticker
176
177
  * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
177
178
  * @param {string[]} symbols unified symbol of the market to fetch the ticker for
178
179
  * @param {object} [params] extra parameters specific to the exchange API endpoint
180
+ * @param {string} [params.method] either '/market/snapshot' or '/market/ticker' default is '/market/ticker'
179
181
  * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
180
182
  */
181
183
  await this.loadMarkets();
182
184
  symbols = this.marketSymbols(symbols);
183
- let messageHash = 'tickers';
185
+ const messageHash = 'tickers';
186
+ let method = undefined;
187
+ [method, params] = this.handleOptionAndParams(params, 'watchTickers', 'method', '/market/ticker');
188
+ const messageHashes = [];
189
+ const topics = [];
184
190
  if (symbols !== undefined) {
185
- messageHash = 'tickers::' + symbols.join(',');
191
+ for (let i = 0; i < symbols.length; i++) {
192
+ const symbol = symbols[i];
193
+ messageHashes.push('ticker:' + symbol);
194
+ const market = this.market(symbol);
195
+ topics.push(method + ':' + market['id']);
196
+ }
186
197
  }
187
198
  const url = await this.negotiate(false);
188
- const topic = '/market/ticker:all';
189
- const tickers = await this.subscribe(url, messageHash, topic, params);
190
- if (this.newUpdates) {
191
- return tickers;
199
+ let tickers = undefined;
200
+ if (symbols === undefined) {
201
+ const allTopic = method + ':all';
202
+ tickers = await this.subscribe(url, messageHash, allTopic, params);
203
+ if (this.newUpdates) {
204
+ return tickers;
205
+ }
206
+ }
207
+ else {
208
+ const marketIds = this.marketIds(symbols);
209
+ const symbolsTopic = method + ':' + marketIds.join(',');
210
+ tickers = await this.subscribeMultiple(url, messageHashes, symbolsTopic, topics, params);
211
+ if (this.newUpdates) {
212
+ const newDict = {};
213
+ newDict[tickers['symbol']] = tickers;
214
+ return newDict;
215
+ }
192
216
  }
193
217
  return this.filterByArray(this.tickers, 'symbol', symbols);
194
218
  }
@@ -272,19 +296,6 @@ class kucoin extends kucoin$1 {
272
296
  const allTickers = {};
273
297
  allTickers[symbol] = ticker;
274
298
  client.resolve(allTickers, 'tickers');
275
- const messageHashes = this.findMessageHashes(client, 'tickers::');
276
- for (let i = 0; i < messageHashes.length; i++) {
277
- const currentMessageHash = messageHashes[i];
278
- const parts = currentMessageHash.split('::');
279
- const symbolsString = parts[1];
280
- const symbols = symbolsString.split(',');
281
- const tickers = this.filterByArray(this.tickers, 'symbol', symbols);
282
- const tickersSymbols = Object.keys(tickers);
283
- const numTickers = tickersSymbols.length;
284
- if (numTickers > 0) {
285
- client.resolve(tickers, currentMessageHash);
286
- }
287
- }
288
299
  }
289
300
  async watchBidsAsks(symbols = undefined, params = {}) {
290
301
  /**