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.
package/js/src/bitget.js CHANGED
@@ -66,6 +66,8 @@ export default class bitget extends Exchange {
66
66
  'fetchCanceledAndClosedOrders': true,
67
67
  'fetchCanceledOrders': true,
68
68
  'fetchClosedOrders': true,
69
+ 'fetchConvertCurrencies': true,
70
+ 'fetchConvertQuote': true,
69
71
  'fetchCrossBorrowRate': true,
70
72
  'fetchCrossBorrowRates': false,
71
73
  'fetchCurrencies': true,
@@ -8445,6 +8447,143 @@ export default class bitget extends Exchange {
8445
8447
  'marginMode': marginType,
8446
8448
  };
8447
8449
  }
8450
+ async fetchConvertQuote(fromCode, toCode, amount = undefined, params = {}) {
8451
+ /**
8452
+ * @method
8453
+ * @name bitget#fetchConvertQuote
8454
+ * @description fetch a quote for converting from one currency to another
8455
+ * @see https://www.bitget.com/api-doc/common/convert/Get-Quoted-Price
8456
+ * @param {string} fromCode the currency that you want to sell and convert from
8457
+ * @param {string} toCode the currency that you want to buy and convert into
8458
+ * @param {float} [amount] how much you want to trade in units of the from currency
8459
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
8460
+ * @returns {object} a [conversion structure]{@link https://docs.ccxt.com/#/?id=conversion-structure}
8461
+ */
8462
+ await this.loadMarkets();
8463
+ const request = {
8464
+ 'fromCoin': fromCode.toUpperCase(),
8465
+ 'toCoin': toCode.toUpperCase(),
8466
+ 'fromCoinSize': this.numberToString(amount),
8467
+ };
8468
+ const response = await this.privateConvertGetV2ConvertQuotedPrice(this.extend(request, params));
8469
+ //
8470
+ // {
8471
+ // "code": "00000",
8472
+ // "msg": "success",
8473
+ // "requestTime": 1712121940158,
8474
+ // "data": {
8475
+ // "fromCoin": "USDT",
8476
+ // "fromCoinSize": "5",
8477
+ // "cnvtPrice": "0.9993007892377704",
8478
+ // "toCoin": "USDC",
8479
+ // "toCoinSize": "4.99650394",
8480
+ // "traceId": "1159288930228187140",
8481
+ // "fee": "0"
8482
+ // }
8483
+ // }
8484
+ //
8485
+ const data = this.safeDict(response, 'data', {});
8486
+ const fromCurrencyId = this.safeString(data, 'fromCoin', fromCode);
8487
+ const fromCurrency = this.currency(fromCurrencyId);
8488
+ const toCurrencyId = this.safeString(data, 'toCoin', toCode);
8489
+ const toCurrency = this.currency(toCurrencyId);
8490
+ return this.parseConversion(data, fromCurrency, toCurrency);
8491
+ }
8492
+ parseConversion(conversion, fromCurrency = undefined, toCurrency = undefined) {
8493
+ //
8494
+ // fetchConvertQuote
8495
+ //
8496
+ // {
8497
+ // "fromCoin": "USDT",
8498
+ // "fromCoinSize": "5",
8499
+ // "cnvtPrice": "0.9993007892377704",
8500
+ // "toCoin": "USDC",
8501
+ // "toCoinSize": "4.99650394",
8502
+ // "traceId": "1159288930228187140",
8503
+ // "fee": "0"
8504
+ // }
8505
+ //
8506
+ const timestamp = this.safeInteger(conversion, 'ts');
8507
+ const fromCoin = this.safeString(conversion, 'fromCoin');
8508
+ const fromCode = this.safeCurrencyCode(fromCoin, fromCurrency);
8509
+ const to = this.safeString(conversion, 'toCoin');
8510
+ const toCode = this.safeCurrencyCode(to, toCurrency);
8511
+ return {
8512
+ 'info': conversion,
8513
+ 'timestamp': timestamp,
8514
+ 'datetime': this.iso8601(timestamp),
8515
+ 'id': this.safeString(conversion, 'traceId'),
8516
+ 'fromCurrency': fromCode,
8517
+ 'fromAmount': this.safeNumber(conversion, 'fromCoinSize'),
8518
+ 'toCurrency': toCode,
8519
+ 'toAmount': this.safeNumber(conversion, 'toCoinSize'),
8520
+ 'price': this.safeNumber(conversion, 'cnvtPrice'),
8521
+ 'fee': this.safeNumber(conversion, 'fee'),
8522
+ };
8523
+ }
8524
+ async fetchConvertCurrencies(params = {}) {
8525
+ /**
8526
+ * @method
8527
+ * @name bitget#fetchConvertCurrencies
8528
+ * @description fetches all available currencies that can be converted
8529
+ * @see https://www.bitget.com/api-doc/common/convert/Get-Convert-Currencies
8530
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
8531
+ * @returns {object} an associative dictionary of currencies
8532
+ */
8533
+ await this.loadMarkets();
8534
+ const response = await this.privateConvertGetV2ConvertCurrencies(params);
8535
+ //
8536
+ // {
8537
+ // "code": "00000",
8538
+ // "msg": "success",
8539
+ // "requestTime": 1712121755897,
8540
+ // "data": [
8541
+ // {
8542
+ // "coin": "BTC",
8543
+ // "available": "0.00009850",
8544
+ // "maxAmount": "0.756266",
8545
+ // "minAmount": "0.00001"
8546
+ // },
8547
+ // ]
8548
+ // }
8549
+ //
8550
+ const result = {};
8551
+ const data = this.safeList(response, 'data', []);
8552
+ for (let i = 0; i < data.length; i++) {
8553
+ const entry = data[i];
8554
+ const id = this.safeString(entry, 'coin');
8555
+ const code = this.safeCurrencyCode(id);
8556
+ result[code] = {
8557
+ 'info': entry,
8558
+ 'id': id,
8559
+ 'code': code,
8560
+ 'networks': undefined,
8561
+ 'type': undefined,
8562
+ 'name': undefined,
8563
+ 'active': undefined,
8564
+ 'deposit': undefined,
8565
+ 'withdraw': this.safeNumber(entry, 'available'),
8566
+ 'fee': undefined,
8567
+ 'precision': undefined,
8568
+ 'limits': {
8569
+ 'amount': {
8570
+ 'min': this.safeNumber(entry, 'minAmount'),
8571
+ 'max': this.safeNumber(entry, 'maxAmount'),
8572
+ },
8573
+ 'withdraw': {
8574
+ 'min': undefined,
8575
+ 'max': undefined,
8576
+ },
8577
+ 'deposit': {
8578
+ 'min': undefined,
8579
+ 'max': undefined,
8580
+ },
8581
+ },
8582
+ 'created': undefined,
8583
+ };
8584
+ }
8585
+ return result;
8586
+ }
8448
8587
  handleErrors(code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
8449
8588
  if (!response) {
8450
8589
  return undefined; // fallback to default error handler
package/js/src/okx.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import Exchange from './abstract/okx.js';
2
- import type { TransferEntry, Int, OrderSide, OrderType, Trade, OHLCV, Order, FundingRateHistory, OrderRequest, FundingHistory, Str, Transaction, Ticker, OrderBook, Balances, Tickers, Market, Greeks, Strings, MarketInterface, Currency, Leverage, Num, Account, OptionChain, Option, MarginModification, TradingFeeInterface, Currencies } from './base/types.js';
2
+ import type { TransferEntry, Int, OrderSide, OrderType, Trade, OHLCV, Order, FundingRateHistory, OrderRequest, FundingHistory, Str, Transaction, Ticker, OrderBook, Balances, Tickers, Market, Greeks, Strings, MarketInterface, Currency, Leverage, Num, Account, OptionChain, Option, MarginModification, TradingFeeInterface, Currencies, Conversion } from './base/types.js';
3
3
  /**
4
4
  * @class okx
5
5
  * @augments Exchange
@@ -297,6 +297,9 @@ export default class okx extends Exchange {
297
297
  baseVolume: number;
298
298
  quoteVolume: any;
299
299
  };
300
+ fetchConvertQuote(fromCode: string, toCode: string, amount?: Num, params?: {}): Promise<Conversion>;
301
+ parseConversion(conversion: any, fromCurrency?: Currency, toCurrency?: Currency): Conversion;
302
+ fetchConvertCurrencies(params?: {}): Promise<Currencies>;
300
303
  handleErrors(httpCode: any, reason: any, url: any, method: any, headers: any, body: any, response: any, requestHeaders: any, requestBody: any): any;
301
304
  fetchMarginAdjustmentHistory(symbol?: Str, type?: Str, since?: Num, limit?: Num, params?: {}): Promise<MarginModification[]>;
302
305
  }
package/js/src/okx.js CHANGED
@@ -63,6 +63,8 @@ export default class okx extends Exchange {
63
63
  'fetchCanceledOrders': true,
64
64
  'fetchClosedOrder': undefined,
65
65
  'fetchClosedOrders': true,
66
+ 'fetchConvertCurrencies': true,
67
+ 'fetchConvertQuote': true,
66
68
  'fetchCrossBorrowRate': true,
67
69
  'fetchCrossBorrowRates': true,
68
70
  'fetchCurrencies': true,
@@ -7607,6 +7609,157 @@ export default class okx extends Exchange {
7607
7609
  'quoteVolume': undefined,
7608
7610
  };
7609
7611
  }
7612
+ async fetchConvertQuote(fromCode, toCode, amount = undefined, params = {}) {
7613
+ /**
7614
+ * @method
7615
+ * @name okx#fetchConvertQuote
7616
+ * @description fetch a quote for converting from one currency to another
7617
+ * @see https://www.okx.com/docs-v5/en/#funding-account-rest-api-estimate-quote
7618
+ * @param {string} fromCode the currency that you want to sell and convert from
7619
+ * @param {string} toCode the currency that you want to buy and convert into
7620
+ * @param {float} [amount] how much you want to trade in units of the from currency
7621
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
7622
+ * @returns {object} a [conversion structure]{@link https://docs.ccxt.com/#/?id=conversion-structure}
7623
+ */
7624
+ await this.loadMarkets();
7625
+ const request = {
7626
+ 'baseCcy': fromCode.toUpperCase(),
7627
+ 'quoteCcy': toCode.toUpperCase(),
7628
+ 'rfqSzCcy': fromCode.toUpperCase(),
7629
+ 'rfqSz': this.numberToString(amount),
7630
+ 'side': 'sell',
7631
+ };
7632
+ const response = await this.privatePostAssetConvertEstimateQuote(this.extend(request, params));
7633
+ //
7634
+ // {
7635
+ // "code": "0",
7636
+ // "data": [
7637
+ // {
7638
+ // "baseCcy": "ETH",
7639
+ // "baseSz": "0.01023052",
7640
+ // "clQReqId": "",
7641
+ // "cnvtPx": "2932.40104429",
7642
+ // "origRfqSz": "30",
7643
+ // "quoteCcy": "USDT",
7644
+ // "quoteId": "quoterETH-USDT16461885104612381",
7645
+ // "quoteSz": "30",
7646
+ // "quoteTime": "1646188510461",
7647
+ // "rfqSz": "30",
7648
+ // "rfqSzCcy": "USDT",
7649
+ // "side": "buy",
7650
+ // "ttlMs": "10000"
7651
+ // }
7652
+ // ],
7653
+ // "msg": ""
7654
+ // }
7655
+ //
7656
+ const data = this.safeList(response, 'data', []);
7657
+ const result = this.safeDict(data, 0, {});
7658
+ const fromCurrencyId = this.safeString(result, 'baseCcy', fromCode);
7659
+ const fromCurrency = this.currency(fromCurrencyId);
7660
+ const toCurrencyId = this.safeString(result, 'quoteCcy', toCode);
7661
+ const toCurrency = this.currency(toCurrencyId);
7662
+ return this.parseConversion(result, fromCurrency, toCurrency);
7663
+ }
7664
+ parseConversion(conversion, fromCurrency = undefined, toCurrency = undefined) {
7665
+ //
7666
+ // fetchConvertQuote
7667
+ //
7668
+ // {
7669
+ // "baseCcy": "ETH",
7670
+ // "baseSz": "0.01023052",
7671
+ // "clQReqId": "",
7672
+ // "cnvtPx": "2932.40104429",
7673
+ // "origRfqSz": "30",
7674
+ // "quoteCcy": "USDT",
7675
+ // "quoteId": "quoterETH-USDT16461885104612381",
7676
+ // "quoteSz": "30",
7677
+ // "quoteTime": "1646188510461",
7678
+ // "rfqSz": "30",
7679
+ // "rfqSzCcy": "USDT",
7680
+ // "side": "buy",
7681
+ // "ttlMs": "10000"
7682
+ // }
7683
+ //
7684
+ const timestamp = this.safeInteger(conversion, 'quoteTime');
7685
+ const fromCoin = this.safeString(conversion, 'baseCcy');
7686
+ const fromCode = this.safeCurrencyCode(fromCoin, fromCurrency);
7687
+ const to = this.safeString(conversion, 'quoteCcy');
7688
+ const toCode = this.safeCurrencyCode(to, toCurrency);
7689
+ return {
7690
+ 'info': conversion,
7691
+ 'timestamp': timestamp,
7692
+ 'datetime': this.iso8601(timestamp),
7693
+ 'id': this.safeString(conversion, 'clQReqId'),
7694
+ 'fromCurrency': fromCode,
7695
+ 'fromAmount': this.safeNumber(conversion, 'baseSz'),
7696
+ 'toCurrency': toCode,
7697
+ 'toAmount': this.safeNumber(conversion, 'quoteSz'),
7698
+ 'price': this.safeNumber(conversion, 'cnvtPx'),
7699
+ 'fee': undefined,
7700
+ };
7701
+ }
7702
+ async fetchConvertCurrencies(params = {}) {
7703
+ /**
7704
+ * @method
7705
+ * @name okx#fetchConvertCurrencies
7706
+ * @description fetches all available currencies that can be converted
7707
+ * @see https://www.okx.com/docs-v5/en/#funding-account-rest-api-get-convert-currencies
7708
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
7709
+ * @returns {object} an associative dictionary of currencies
7710
+ */
7711
+ await this.loadMarkets();
7712
+ const response = await this.privateGetAssetConvertCurrencies(params);
7713
+ //
7714
+ // {
7715
+ // "code": "0",
7716
+ // "data": [
7717
+ // {
7718
+ // "ccy": "BTC",
7719
+ // "max": "",
7720
+ // "min": ""
7721
+ // },
7722
+ // ],
7723
+ // "msg": ""
7724
+ // }
7725
+ //
7726
+ const result = {};
7727
+ const data = this.safeList(response, 'data', []);
7728
+ for (let i = 0; i < data.length; i++) {
7729
+ const entry = data[i];
7730
+ const id = this.safeString(entry, 'ccy');
7731
+ const code = this.safeCurrencyCode(id);
7732
+ result[code] = {
7733
+ 'info': entry,
7734
+ 'id': id,
7735
+ 'code': code,
7736
+ 'networks': undefined,
7737
+ 'type': undefined,
7738
+ 'name': undefined,
7739
+ 'active': undefined,
7740
+ 'deposit': undefined,
7741
+ 'withdraw': undefined,
7742
+ 'fee': undefined,
7743
+ 'precision': undefined,
7744
+ 'limits': {
7745
+ 'amount': {
7746
+ 'min': this.safeNumber(entry, 'min'),
7747
+ 'max': this.safeNumber(entry, 'max'),
7748
+ },
7749
+ 'withdraw': {
7750
+ 'min': undefined,
7751
+ 'max': undefined,
7752
+ },
7753
+ 'deposit': {
7754
+ 'min': undefined,
7755
+ 'max': undefined,
7756
+ },
7757
+ },
7758
+ 'created': undefined,
7759
+ };
7760
+ }
7761
+ return result;
7762
+ }
7610
7763
  handleErrors(httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) {
7611
7764
  if (!response) {
7612
7765
  return undefined; // fallback to default error handler
@@ -1,5 +1,5 @@
1
1
  import krakenRest from '../kraken.js';
2
- import type { Int, OrderSide, OrderType, Str, OrderBook, Order, Trade, Ticker, OHLCV, Num } from '../base/types.js';
2
+ import type { Int, Strings, OrderSide, OrderType, Str, OrderBook, Order, Trade, Ticker, Tickers, OHLCV, Num } from '../base/types.js';
3
3
  import Client from '../base/ws/Client.js';
4
4
  export default class kraken extends krakenRest {
5
5
  describe(): any;
@@ -17,8 +17,11 @@ export default class kraken extends krakenRest {
17
17
  requestId(): any;
18
18
  watchPublic(name: any, symbol: any, params?: {}): Promise<any>;
19
19
  watchTicker(symbol: string, params?: {}): Promise<Ticker>;
20
+ watchTickers(symbols?: Strings, params?: {}): Promise<Tickers>;
20
21
  watchTrades(symbol: string, since?: Int, limit?: Int, params?: {}): Promise<Trade[]>;
22
+ watchTradesForSymbols(symbols: string[], since?: Int, limit?: Int, params?: {}): Promise<Trade[]>;
21
23
  watchOrderBook(symbol: string, limit?: Int, params?: {}): Promise<OrderBook>;
24
+ watchOrderBookForSymbols(symbols: string[], limit?: Int, params?: {}): Promise<OrderBook>;
22
25
  watchOHLCV(symbol: string, timeframe?: string, since?: Int, limit?: Int, params?: {}): Promise<OHLCV[]>;
23
26
  loadMarkets(reload?: boolean, params?: {}): Promise<import("../base/types.js").Dictionary<import("../base/types.js").MarketInterface>>;
24
27
  watchHeartbeat(params?: {}): Promise<any>;
@@ -49,6 +52,8 @@ export default class kraken extends krakenRest {
49
52
  watchOrders(symbol?: Str, since?: Int, limit?: Int, params?: {}): Promise<Order[]>;
50
53
  handleOrders(client: Client, message: any, subscription?: any): void;
51
54
  parseWsOrder(order: any, market?: any): Order;
55
+ watchMultiHelper(unifiedName: string, channelName: string, symbols?: Strings, subscriptionArgs?: any, params?: {}): Promise<any>;
56
+ getMessageHash(unifiedElementName: string, subChannelName?: Str, symbol?: Str): string;
52
57
  handleSubscriptionStatus(client: Client, message: any): void;
53
58
  handleErrorMessage(client: Client, message: any): boolean;
54
59
  handleMessage(client: Client, message: any): void;
@@ -19,10 +19,12 @@ export default class kraken extends krakenRest {
19
19
  'watchMyTrades': true,
20
20
  'watchOHLCV': true,
21
21
  'watchOrderBook': true,
22
+ 'watchOrderBookForSymbols': true,
22
23
  'watchOrders': true,
23
24
  'watchTicker': true,
24
- 'watchTickers': false,
25
+ 'watchTickers': true,
25
26
  'watchTrades': true,
27
+ 'watchTradesForSymbols': true,
26
28
  'createOrderWs': true,
27
29
  'editOrderWs': true,
28
30
  'cancelOrderWs': true,
@@ -313,10 +315,9 @@ export default class kraken extends krakenRest {
313
315
  // ]
314
316
  //
315
317
  const wsName = message[3];
316
- const name = 'ticker';
317
- const messageHash = name + ':' + wsName;
318
318
  const market = this.safeValue(this.options['marketsByWsName'], wsName);
319
319
  const symbol = market['symbol'];
320
+ const messageHash = this.getMessageHash('ticker', undefined, symbol);
320
321
  const ticker = message[1];
321
322
  const vwap = this.safeString(ticker['p'], 0);
322
323
  let quoteVolume = undefined;
@@ -347,9 +348,6 @@ export default class kraken extends krakenRest {
347
348
  'quoteVolume': quoteVolume,
348
349
  'info': ticker,
349
350
  });
350
- // todo add support for multiple tickers (may be tricky)
351
- // kraken confirms multi-pair subscriptions separately one by one
352
- // trigger correct watchTickers calls upon receiving any of symbols
353
351
  this.tickers[symbol] = result;
354
352
  client.resolve(result, messageHash);
355
353
  }
@@ -367,9 +365,9 @@ export default class kraken extends krakenRest {
367
365
  //
368
366
  const wsName = this.safeString(message, 3);
369
367
  const name = this.safeString(message, 2);
370
- const messageHash = name + ':' + wsName;
371
368
  const market = this.safeValue(this.options['marketsByWsName'], wsName);
372
369
  const symbol = market['symbol'];
370
+ const messageHash = this.getMessageHash(name, undefined, symbol);
373
371
  let stored = this.safeValue(this.trades, symbol);
374
372
  if (stored === undefined) {
375
373
  const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
@@ -470,25 +468,61 @@ export default class kraken extends krakenRest {
470
468
  * @param {object} [params] extra parameters specific to the exchange API endpoint
471
469
  * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
472
470
  */
473
- return await this.watchPublic('ticker', symbol, params);
471
+ await this.loadMarkets();
472
+ symbol = this.symbol(symbol);
473
+ const tickers = await this.watchTickers([symbol], params);
474
+ return tickers[symbol];
475
+ }
476
+ async watchTickers(symbols = undefined, params = {}) {
477
+ /**
478
+ * @method
479
+ * @name kraken#watchTickers
480
+ * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
481
+ * @param {string} symbol unified symbol of the market to fetch the ticker for
482
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
483
+ * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
484
+ */
485
+ await this.loadMarkets();
486
+ symbols = this.marketSymbols(symbols, undefined, false);
487
+ const ticker = await this.watchMultiHelper('ticker', 'ticker', symbols, undefined, params);
488
+ if (this.newUpdates) {
489
+ const result = {};
490
+ result[ticker['symbol']] = ticker;
491
+ return result;
492
+ }
493
+ return this.filterByArray(this.tickers, 'symbol', symbols);
474
494
  }
475
495
  async watchTrades(symbol, since = undefined, limit = undefined, params = {}) {
476
496
  /**
477
497
  * @method
478
498
  * @name kraken#watchTrades
479
499
  * @description get the list of most recent trades for a particular symbol
500
+ * @see https://docs.kraken.com/websockets/#message-trade
480
501
  * @param {string} symbol unified symbol of the market to fetch trades for
481
502
  * @param {int} [since] timestamp in ms of the earliest trade to fetch
482
503
  * @param {int} [limit] the maximum amount of trades to fetch
483
504
  * @param {object} [params] extra parameters specific to the exchange API endpoint
484
505
  * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
485
506
  */
486
- await this.loadMarkets();
487
- symbol = this.symbol(symbol);
488
- const name = 'trade';
489
- const trades = await this.watchPublic(name, symbol, params);
507
+ return await this.watchTradesForSymbols([symbol], since, limit, params);
508
+ }
509
+ async watchTradesForSymbols(symbols, since = undefined, limit = undefined, params = {}) {
510
+ /**
511
+ * @method
512
+ * @name kraken#watchTradesForSymbols
513
+ * @see https://docs.kraken.com/websockets/#message-trade
514
+ * @description get the list of most recent trades for a list of symbols
515
+ * @param {string[]} symbols unified symbol of the market to fetch trades for
516
+ * @param {int} [since] timestamp in ms of the earliest trade to fetch
517
+ * @param {int} [limit] the maximum amount of trades to fetch
518
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
519
+ * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
520
+ */
521
+ const trades = await this.watchMultiHelper('trade', 'trade', symbols, undefined, params);
490
522
  if (this.newUpdates) {
491
- limit = trades.getLimit(symbol, limit);
523
+ const first = this.safeList(trades, 0);
524
+ const tradeSymbol = this.safeString(first, 'symbol');
525
+ limit = trades.getLimit(tradeSymbol, limit);
492
526
  }
493
527
  return this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
494
528
  }
@@ -497,15 +531,28 @@ export default class kraken extends krakenRest {
497
531
  * @method
498
532
  * @name kraken#watchOrderBook
499
533
  * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
534
+ * @see https://docs.kraken.com/websockets/#message-book
500
535
  * @param {string} symbol unified symbol of the market to fetch the order book for
501
536
  * @param {int} [limit] the maximum amount of order book entries to return
502
537
  * @param {object} [params] extra parameters specific to the exchange API endpoint
503
538
  * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
504
539
  */
505
- const name = 'book';
540
+ return await this.watchOrderBookForSymbols([symbol], limit, params);
541
+ }
542
+ async watchOrderBookForSymbols(symbols, limit = undefined, params = {}) {
543
+ /**
544
+ * @method
545
+ * @name kraken#watchOrderBookForSymbols
546
+ * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
547
+ * @see https://docs.kraken.com/websockets/#message-book
548
+ * @param {string[]} symbols unified array of symbols
549
+ * @param {int} [limit] the maximum amount of order book entries to return
550
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
551
+ * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
552
+ */
506
553
  const request = {};
507
554
  if (limit !== undefined) {
508
- if ((limit === 10) || (limit === 25) || (limit === 100) || (limit === 500) || (limit === 1000)) {
555
+ if (this.inArray(limit, [10, 25, 100, 500, 1000])) {
509
556
  request['subscription'] = {
510
557
  'depth': limit, // default 10, valid options 10, 25, 100, 500, 1000
511
558
  };
@@ -514,7 +561,7 @@ export default class kraken extends krakenRest {
514
561
  throw new NotSupported(this.id + ' watchOrderBook accepts limit values of 10, 25, 100, 500 and 1000 only');
515
562
  }
516
563
  }
517
- const orderbook = await this.watchPublic(name, symbol, this.extend(request, params));
564
+ const orderbook = await this.watchMultiHelper('orderbook', 'book', symbols, { 'limit': limit }, this.extend(request, params));
518
565
  return orderbook.limit();
519
566
  }
520
567
  async watchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
@@ -643,7 +690,7 @@ export default class kraken extends krakenRest {
643
690
  const market = this.safeValue(this.options['marketsByWsName'], wsName);
644
691
  const symbol = market['symbol'];
645
692
  let timestamp = undefined;
646
- const messageHash = 'book:' + wsName;
693
+ const messageHash = this.getMessageHash('orderbook', undefined, symbol);
647
694
  // if this is a snapshot
648
695
  if ('as' in message[1]) {
649
696
  // todo get depth from marketsByWsName
@@ -724,6 +771,7 @@ export default class kraken extends krakenRest {
724
771
  if (localChecksum !== c) {
725
772
  const error = new InvalidNonce(this.id + ' invalid checksum');
726
773
  client.reject(error, messageHash);
774
+ return;
727
775
  }
728
776
  }
729
777
  orderbook['symbol'] = symbol;
@@ -1251,6 +1299,48 @@ export default class kraken extends krakenRest {
1251
1299
  'trades': trades,
1252
1300
  });
1253
1301
  }
1302
+ async watchMultiHelper(unifiedName, channelName, symbols = undefined, subscriptionArgs = undefined, params = {}) {
1303
+ await this.loadMarkets();
1304
+ // symbols are required
1305
+ symbols = this.marketSymbols(symbols, undefined, false, true, false);
1306
+ const messageHashes = [];
1307
+ for (let i = 0; i < symbols.length; i++) {
1308
+ messageHashes.push(this.getMessageHash(unifiedName, undefined, this.symbol(symbols[i])));
1309
+ }
1310
+ // for WS subscriptions, we can't use .marketIds (symbols), instead a custom is field needed
1311
+ const markets = this.marketsForSymbols(symbols);
1312
+ const wsMarketIds = [];
1313
+ for (let i = 0; i < markets.length; i++) {
1314
+ const wsMarketId = this.safeString(markets[i]['info'], 'wsname');
1315
+ wsMarketIds.push(wsMarketId);
1316
+ }
1317
+ const request = {
1318
+ 'event': 'subscribe',
1319
+ 'reqid': this.requestId(),
1320
+ 'pair': wsMarketIds,
1321
+ 'subscription': {
1322
+ 'name': channelName,
1323
+ },
1324
+ };
1325
+ const url = this.urls['api']['ws']['public'];
1326
+ return await this.watchMultiple(url, messageHashes, this.extend(request, params), messageHashes, subscriptionArgs);
1327
+ }
1328
+ getMessageHash(unifiedElementName, subChannelName = undefined, symbol = undefined) {
1329
+ // unifiedElementName can be : orderbook, trade, ticker, bidask ...
1330
+ // subChannelName only applies to channel that needs specific variation (i.e. depth_50, depth_100..) to be selected
1331
+ const withSymbol = symbol !== undefined;
1332
+ let messageHash = unifiedElementName;
1333
+ if (!withSymbol) {
1334
+ messageHash += 's';
1335
+ }
1336
+ else {
1337
+ messageHash += '@' + symbol;
1338
+ }
1339
+ if (subChannelName !== undefined) {
1340
+ messageHash += '#' + subChannelName;
1341
+ }
1342
+ return messageHash;
1343
+ }
1254
1344
  handleSubscriptionStatus(client, message) {
1255
1345
  //
1256
1346
  // public
@@ -4,11 +4,14 @@ import Client from '../base/ws/Client.js';
4
4
  export default class krakenfutures extends krakenfuturesRest {
5
5
  describe(): any;
6
6
  authenticate(params?: {}): Promise<any>;
7
+ watchOrderBookForSymbols(symbols: string[], limit?: Int, params?: {}): Promise<OrderBook>;
7
8
  subscribePublic(name: string, symbols: string[], params?: {}): Promise<any>;
8
9
  subscribePrivate(name: string, messageHash: string, params?: {}): Promise<any>;
9
10
  watchTicker(symbol: string, params?: {}): Promise<Ticker>;
10
11
  watchTickers(symbols?: Strings, params?: {}): Promise<Tickers>;
12
+ watchBidsAsks(symbols?: Strings, params?: {}): Promise<Tickers>;
11
13
  watchTrades(symbol?: Str, since?: Int, limit?: Int, params?: {}): Promise<Trade[]>;
14
+ watchTradesForSymbols(symbols: string[], since?: Int, limit?: Int, params?: {}): Promise<Trade[]>;
12
15
  watchOrderBook(symbol: string, limit?: Int, params?: {}): Promise<OrderBook>;
13
16
  watchPositions(symbols?: Strings, since?: Int, limit?: Int, params?: {}): Promise<Position[]>;
14
17
  handlePositions(client: any, message: any): void;
@@ -16,19 +19,22 @@ export default class krakenfutures extends krakenfuturesRest {
16
19
  watchOrders(symbol?: Str, since?: Int, limit?: Int, params?: {}): Promise<Order[]>;
17
20
  watchMyTrades(symbol?: Str, since?: Int, limit?: Int, params?: {}): Promise<Trade[]>;
18
21
  watchBalance(params?: {}): Promise<Balances>;
19
- handleTrade(client: Client, message: any): any;
22
+ handleTrade(client: Client, message: any): void;
20
23
  parseWsTrade(trade: any, market?: any): Trade;
21
24
  parseWsOrderTrade(trade: any, market?: any): Trade;
22
25
  handleOrder(client: Client, message: any): any;
23
26
  handleOrderSnapshot(client: Client, message: any): void;
24
27
  parseWsOrder(order: any, market?: any): Order;
25
- handleTicker(client: Client, message: any): any;
28
+ handleTicker(client: Client, message: any): void;
29
+ handleBidAsk(client: Client, message: any): void;
26
30
  parseWsTicker(ticker: any, market?: any): Ticker;
27
31
  handleOrderBookSnapshot(client: Client, message: any): void;
28
32
  handleOrderBook(client: Client, message: any): void;
29
33
  handleBalance(client: Client, message: any): void;
30
34
  handleMyTrades(client: Client, message: any): void;
31
35
  parseWsMyTrade(trade: any, market?: any): Trade;
36
+ watchMultiHelper(unifiedName: string, channelName: string, symbols?: Strings, subscriptionArgs?: any, params?: {}): Promise<any>;
37
+ getMessageHash(unifiedElementName: string, subChannelName?: Str, symbol?: Str): string;
32
38
  handleErrorMessage(client: Client, message: any): void;
33
39
  handleMessage(client: any, message: any): void;
34
40
  handleAuthenticate(client: Client, message: any): any;