ccxt 4.2.36 → 4.2.38

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/bybit.js CHANGED
@@ -3741,10 +3741,22 @@ export default class bybit extends Exchange {
3741
3741
  if (isStopLoss) {
3742
3742
  const slTriggerPrice = this.safeValue2(stopLoss, 'triggerPrice', 'stopPrice', stopLoss);
3743
3743
  request['stopLoss'] = this.priceToPrecision(symbol, slTriggerPrice);
3744
+ const slLimitPrice = this.safeValue(stopLoss, 'price');
3745
+ if (slLimitPrice !== undefined) {
3746
+ request['tpslMode'] = 'Partial';
3747
+ request['slOrderType'] = 'Limit';
3748
+ request['slLimitPrice'] = this.priceToPrecision(symbol, slLimitPrice);
3749
+ }
3744
3750
  }
3745
3751
  if (isTakeProfit) {
3746
3752
  const tpTriggerPrice = this.safeValue2(takeProfit, 'triggerPrice', 'stopPrice', takeProfit);
3747
3753
  request['takeProfit'] = this.priceToPrecision(symbol, tpTriggerPrice);
3754
+ const tpLimitPrice = this.safeValue(takeProfit, 'price');
3755
+ if (tpLimitPrice !== undefined) {
3756
+ request['tpslMode'] = 'Partial';
3757
+ request['tpOrderType'] = 'Limit';
3758
+ request['tpLimitPrice'] = this.priceToPrecision(symbol, tpLimitPrice);
3759
+ }
3748
3760
  }
3749
3761
  }
3750
3762
  if (market['spot']) {
@@ -6026,9 +6038,6 @@ export default class bybit extends Exchange {
6026
6038
  if (timestamp === undefined) {
6027
6039
  timestamp = this.safeIntegerN(position, ['updatedTime', 'updatedAt']);
6028
6040
  }
6029
- // default to cross of USDC margined positions
6030
- const tradeMode = this.safeInteger(position, 'tradeMode', 0);
6031
- const marginMode = tradeMode ? 'isolated' : 'cross';
6032
6041
  let collateralString = this.safeString(position, 'positionBalance');
6033
6042
  const entryPrice = this.omitZero(this.safeString2(position, 'entryPrice', 'avgPrice'));
6034
6043
  const liquidationPrice = this.omitZero(this.safeString(position, 'liqPrice'));
@@ -6092,7 +6101,7 @@ export default class bybit extends Exchange {
6092
6101
  'markPrice': this.safeNumber(position, 'markPrice'),
6093
6102
  'lastPrice': undefined,
6094
6103
  'collateral': this.parseNumber(collateralString),
6095
- 'marginMode': marginMode,
6104
+ 'marginMode': undefined,
6096
6105
  'side': side,
6097
6106
  'percentage': undefined,
6098
6107
  'stopLossPrice': this.safeNumber2(position, 'stop_loss', 'stopLoss'),
@@ -4,13 +4,18 @@ import Client from '../base/ws/Client.js';
4
4
  export default class gemini extends geminiRest {
5
5
  describe(): any;
6
6
  watchTrades(symbol: string, since?: Int, limit?: Int, params?: {}): Promise<Trade[]>;
7
+ watchTradesForSymbols(symbols: string[], since?: Int, limit?: Int, params?: {}): Promise<Trade[]>;
7
8
  parseWsTrade(trade: any, market?: any): Trade;
8
9
  handleTrade(client: Client, message: any): void;
9
10
  handleTrades(client: Client, message: any): void;
11
+ handleTradesForMultidata(client: Client, trades: any, timestamp: Int): void;
10
12
  watchOHLCV(symbol: string, timeframe?: string, since?: Int, limit?: Int, params?: {}): Promise<OHLCV[]>;
11
13
  handleOHLCV(client: Client, message: any): any;
12
14
  watchOrderBook(symbol: string, limit?: Int, params?: {}): Promise<OrderBook>;
13
15
  handleOrderBook(client: Client, message: any): void;
16
+ watchOrderBookForSymbols(symbols: string[], limit?: Int, params?: {}): Promise<OrderBook>;
17
+ helperForWatchMultipleConstruct(itemHashName: string, symbols: string[], params?: {}): Promise<any>;
18
+ handleOrderBookForMultidata(client: Client, rawOrderBookChanges: any, timestamp: Int, nonce: Int): void;
14
19
  handleL2Updates(client: Client, message: any): void;
15
20
  watchOrders(symbol?: Str, since?: Int, limit?: Int, params?: {}): Promise<Order[]>;
16
21
  handleHeartbeat(client: Client, message: any): any;
@@ -7,7 +7,7 @@
7
7
  // ---------------------------------------------------------------------------
8
8
  import geminiRest from '../gemini.js';
9
9
  import { ArrayCache, ArrayCacheBySymbolById, ArrayCacheByTimestamp } from '../base/ws/Cache.js';
10
- import { ExchangeError } from '../base/errors.js';
10
+ import { ExchangeError, NotSupported } from '../base/errors.js';
11
11
  import { sha384 } from '../static_dependencies/noble-hashes/sha512.js';
12
12
  // ---------------------------------------------------------------------------
13
13
  export default class gemini extends geminiRest {
@@ -19,9 +19,11 @@ export default class gemini extends geminiRest {
19
19
  'watchTicker': false,
20
20
  'watchTickers': false,
21
21
  'watchTrades': true,
22
+ 'watchTradesForSymbols': true,
22
23
  'watchMyTrades': false,
23
24
  'watchOrders': true,
24
25
  'watchOrderBook': true,
26
+ 'watchOrderBookForSymbols': true,
25
27
  'watchOHLCV': true,
26
28
  },
27
29
  'hostname': 'api.gemini.com',
@@ -70,7 +72,29 @@ export default class gemini extends geminiRest {
70
72
  }
71
73
  return this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
72
74
  }
75
+ async watchTradesForSymbols(symbols, since = undefined, limit = undefined, params = {}) {
76
+ /**
77
+ * @method
78
+ * @name gemini#watchTradesForSymbols
79
+ * @see https://docs.gemini.com/websocket-api/#multi-market-data
80
+ * @description get the list of most recent trades for a list of symbols
81
+ * @param {string[]} symbols unified symbol of the market to fetch trades for
82
+ * @param {int} [since] timestamp in ms of the earliest trade to fetch
83
+ * @param {int} [limit] the maximum amount of trades to fetch
84
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
85
+ * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
86
+ */
87
+ const trades = await this.helperForWatchMultipleConstruct('trades', symbols, params);
88
+ if (this.newUpdates) {
89
+ const first = this.safeList(trades, 0);
90
+ const tradeSymbol = this.safeString(first, 'symbol');
91
+ limit = trades.getLimit(tradeSymbol, limit);
92
+ }
93
+ return this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
94
+ }
73
95
  parseWsTrade(trade, market = undefined) {
96
+ //
97
+ // regular v2 trade
74
98
  //
75
99
  // {
76
100
  // "type": "trade",
@@ -82,11 +106,31 @@ export default class gemini extends geminiRest {
82
106
  // "side": "buy"
83
107
  // }
84
108
  //
109
+ // multi data trade
110
+ //
111
+ // {
112
+ // "type": "trade",
113
+ // "symbol": "ETHUSD",
114
+ // "tid": "1683002242170204", // this is not TS, but somewhat ID
115
+ // "price": "2299.24",
116
+ // "amount": "0.002662",
117
+ // "makerSide": "bid"
118
+ // }
119
+ //
85
120
  const timestamp = this.safeInteger(trade, 'timestamp');
86
- const id = this.safeString(trade, 'event_id');
121
+ const id = this.safeString2(trade, 'event_id', 'tid');
87
122
  const priceString = this.safeString(trade, 'price');
88
- const amountString = this.safeString(trade, 'quantity');
89
- const side = this.safeStringLower(trade, 'side');
123
+ const amountString = this.safeString2(trade, 'quantity', 'amount');
124
+ let side = this.safeStringLower(trade, 'side');
125
+ if (side === undefined) {
126
+ const marketSide = this.safeStringLower(trade, 'makerSide');
127
+ if (marketSide === 'bid') {
128
+ side = 'sell';
129
+ }
130
+ else if (marketSide === 'ask') {
131
+ side = 'buy';
132
+ }
133
+ }
90
134
  const marketId = this.safeStringLower(trade, 'symbol');
91
135
  const symbol = this.safeSymbol(marketId, market);
92
136
  return this.safeTrade({
@@ -186,6 +230,34 @@ export default class gemini extends geminiRest {
186
230
  client.resolve(stored, messageHash);
187
231
  }
188
232
  }
233
+ handleTradesForMultidata(client, trades, timestamp) {
234
+ if (trades !== undefined) {
235
+ const tradesLimit = this.safeInteger(this.options, 'tradesLimit', 1000);
236
+ const storesForSymbols = {};
237
+ for (let i = 0; i < trades.length; i++) {
238
+ const marketId = trades[i]['symbol'];
239
+ const market = this.safeMarket(marketId.toLowerCase());
240
+ const symbol = market['symbol'];
241
+ const trade = this.parseWsTrade(trades[i], market);
242
+ trade['timestamp'] = timestamp;
243
+ trade['datetime'] = this.iso8601(timestamp);
244
+ let stored = this.safeValue(this.trades, symbol);
245
+ if (stored === undefined) {
246
+ stored = new ArrayCache(tradesLimit);
247
+ this.trades[symbol] = stored;
248
+ }
249
+ stored.append(trade);
250
+ storesForSymbols[symbol] = stored;
251
+ }
252
+ const symbols = Object.keys(storesForSymbols);
253
+ for (let i = 0; i < symbols.length; i++) {
254
+ const symbol = symbols[i];
255
+ const stored = storesForSymbols[symbol];
256
+ const messageHash = 'trades:' + symbol;
257
+ client.resolve(stored, messageHash);
258
+ }
259
+ }
260
+ }
189
261
  async watchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
190
262
  /**
191
263
  * @method
@@ -331,6 +403,93 @@ export default class gemini extends geminiRest {
331
403
  this.orderbooks[symbol] = orderbook;
332
404
  client.resolve(orderbook, messageHash);
333
405
  }
406
+ async watchOrderBookForSymbols(symbols, limit = undefined, params = {}) {
407
+ /**
408
+ * @method
409
+ * @name gemini#watchOrderBookForSymbols
410
+ * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
411
+ * @see https://docs.gemini.com/websocket-api/#multi-market-data
412
+ * @param {string[]} symbols unified array of symbols
413
+ * @param {int} [limit] the maximum amount of order book entries to return
414
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
415
+ * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
416
+ */
417
+ const orderbook = await this.helperForWatchMultipleConstruct('orderbook', symbols, params);
418
+ return orderbook.limit();
419
+ }
420
+ async helperForWatchMultipleConstruct(itemHashName, symbols, params = {}) {
421
+ await this.loadMarkets();
422
+ symbols = this.marketSymbols(symbols, undefined, false, true, true);
423
+ const firstMarket = this.market(symbols[0]);
424
+ if (!firstMarket['spot'] && !firstMarket['linear']) {
425
+ throw new NotSupported(this.id + ' watchMultiple supports only spot or linear-swap symbols');
426
+ }
427
+ const messageHashes = [];
428
+ const marketIds = [];
429
+ for (let i = 0; i < symbols.length; i++) {
430
+ const symbol = symbols[i];
431
+ const messageHash = itemHashName + ':' + symbol;
432
+ messageHashes.push(messageHash);
433
+ const market = this.market(symbol);
434
+ marketIds.push(market['id']);
435
+ }
436
+ const queryStr = marketIds.join(',');
437
+ let url = this.urls['api']['ws'] + '/v1/multimarketdata?symbols=' + queryStr + '&heartbeat=true&';
438
+ if (itemHashName === 'orderbook') {
439
+ url += 'trades=false&bids=true&offers=true';
440
+ }
441
+ else if (itemHashName === 'trades') {
442
+ url += 'trades=true&bids=false&offers=false';
443
+ }
444
+ return await this.watchMultiple(url, messageHashes, undefined);
445
+ }
446
+ handleOrderBookForMultidata(client, rawOrderBookChanges, timestamp, nonce) {
447
+ //
448
+ // rawOrderBookChanges
449
+ //
450
+ // [
451
+ // {
452
+ // delta: "4105123935484.817624",
453
+ // price: "0.000000001",
454
+ // reason: "initial", // initial|cancel|place
455
+ // remaining: "4105123935484.817624",
456
+ // side: "bid", // bid|ask
457
+ // symbol: "SHIBUSD",
458
+ // type: "change", // seems always change
459
+ // },
460
+ // ...
461
+ //
462
+ const marketId = rawOrderBookChanges[0]['symbol'];
463
+ const market = this.safeMarket(marketId.toLowerCase());
464
+ const symbol = market['symbol'];
465
+ const messageHash = 'orderbook:' + symbol;
466
+ let orderbook = this.safeDict(this.orderbooks, symbol);
467
+ if (orderbook === undefined) {
468
+ orderbook = this.orderBook();
469
+ }
470
+ const bids = orderbook['bids'];
471
+ const asks = orderbook['asks'];
472
+ for (let i = 0; i < rawOrderBookChanges.length; i++) {
473
+ const entry = rawOrderBookChanges[i];
474
+ const price = this.safeNumber(entry, 'price');
475
+ const size = this.safeNumber(entry, 'remaining');
476
+ const rawSide = this.safeString(entry, 'side');
477
+ if (rawSide === 'bid') {
478
+ bids.store(price, size);
479
+ }
480
+ else {
481
+ asks.store(price, size);
482
+ }
483
+ }
484
+ orderbook['bids'] = bids;
485
+ orderbook['asks'] = asks;
486
+ orderbook['symbol'] = symbol;
487
+ orderbook['nonce'] = nonce;
488
+ orderbook['timestamp'] = timestamp;
489
+ orderbook['datetime'] = this.iso8601(timestamp);
490
+ this.orderbooks[symbol] = orderbook;
491
+ client.resolve(orderbook, messageHash);
492
+ }
334
493
  handleL2Updates(client, message) {
335
494
  //
336
495
  // {
@@ -411,6 +570,7 @@ export default class gemini extends geminiRest {
411
570
  // "socket_sequence": 7
412
571
  // }
413
572
  //
573
+ client.lastPong = this.milliseconds();
414
574
  return message;
415
575
  }
416
576
  handleSubscription(client, message) {
@@ -613,6 +773,33 @@ export default class gemini extends geminiRest {
613
773
  if (method !== undefined) {
614
774
  method.call(this, client, message);
615
775
  }
776
+ // handle multimarketdata
777
+ if (type === 'update') {
778
+ const ts = this.safeInteger(message, 'timestampms', this.milliseconds());
779
+ const eventId = this.safeInteger(message, 'eventId');
780
+ const events = this.safeList(message, 'events');
781
+ const orderBookItems = [];
782
+ const collectedEventsOfTrades = [];
783
+ for (let i = 0; i < events.length; i++) {
784
+ const event = events[i];
785
+ const eventType = this.safeString(event, 'type');
786
+ const isOrderBook = (eventType === 'change') && ('side' in event) && this.inArray(event['side'], ['ask', 'bid']);
787
+ if (isOrderBook) {
788
+ orderBookItems.push(event);
789
+ }
790
+ else if (eventType === 'trade') {
791
+ collectedEventsOfTrades.push(events[i]);
792
+ }
793
+ }
794
+ const lengthOb = orderBookItems.length;
795
+ if (lengthOb > 0) {
796
+ this.handleOrderBookForMultidata(client, orderBookItems, ts, eventId);
797
+ }
798
+ const lengthTrades = collectedEventsOfTrades.length;
799
+ if (lengthTrades > 0) {
800
+ this.handleTradesForMultidata(client, collectedEventsOfTrades, ts);
801
+ }
802
+ }
616
803
  }
617
804
  async authenticate(params = {}) {
618
805
  const url = this.safeString(params, 'url');
@@ -40,6 +40,7 @@ export default class kucoin extends kucoinRest {
40
40
  'watchOrderBook': {
41
41
  'snapshotDelay': 5,
42
42
  'snapshotMaxRetries': 3,
43
+ 'method': '/market/level2', // '/spotMarket/level2Depth5' or '/spotMarket/level2Depth50'
43
44
  },
44
45
  },
45
46
  'streaming': {
@@ -441,10 +442,15 @@ export default class kucoin extends kucoinRest {
441
442
  /**
442
443
  * @method
443
444
  * @name kucoin#watchOrderBook
445
+ * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level1-bbo-market-data
446
+ * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level2-market-data
447
+ * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level2-5-best-ask-bid-orders
448
+ * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level2-50-best-ask-bid-orders
444
449
  * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
445
450
  * @param {string} symbol unified symbol of the market to fetch the order book for
446
451
  * @param {int} [limit] the maximum amount of order book entries to return
447
452
  * @param {object} [params] extra parameters specific to the exchange API endpoint
453
+ * @param {string} [params.method] either '/market/level2' or '/spotMarket/level2Depth5' or '/spotMarket/level2Depth50' default is '/market/level2'
448
454
  * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
449
455
  */
450
456
  //
@@ -468,10 +474,15 @@ export default class kucoin extends kucoinRest {
468
474
  /**
469
475
  * @method
470
476
  * @name kucoin#watchOrderBookForSymbols
477
+ * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level1-bbo-market-data
478
+ * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level2-market-data
479
+ * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level2-5-best-ask-bid-orders
480
+ * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level2-50-best-ask-bid-orders
471
481
  * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
472
482
  * @param {string[]} symbols unified array of symbols
473
483
  * @param {int} [limit] the maximum amount of order book entries to return
474
484
  * @param {object} [params] extra parameters specific to the exchange API endpoint
485
+ * @param {string} [params.method] either '/market/level2' or '/spotMarket/level2Depth5' or '/spotMarket/level2Depth50' default is '/market/level2'
475
486
  * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
476
487
  */
477
488
  const symbolsLength = symbols.length;
@@ -479,28 +490,36 @@ export default class kucoin extends kucoinRest {
479
490
  throw new ArgumentsRequired(this.id + ' watchOrderBookForSymbols() requires a non-empty array of symbols');
480
491
  }
481
492
  if (limit !== undefined) {
482
- if ((limit !== 20) && (limit !== 100)) {
483
- throw new ExchangeError(this.id + " watchOrderBook 'limit' argument must be undefined, 20 or 100");
493
+ if ((limit !== 20) && (limit !== 100) && (limit !== 50) && (limit !== 5)) {
494
+ throw new ExchangeError(this.id + " watchOrderBook 'limit' argument must be undefined, 5, 20, 50 or 100");
484
495
  }
485
496
  }
486
497
  await this.loadMarkets();
487
498
  symbols = this.marketSymbols(symbols);
488
499
  const marketIds = this.marketIds(symbols);
489
500
  const url = await this.negotiate(false);
490
- const topic = '/market/level2:' + marketIds.join(',');
501
+ let method = undefined;
502
+ [method, params] = this.handleOptionAndParams(params, 'watchOrderBook', 'method', '/market/level2');
503
+ if ((limit === 5) || (limit === 50)) {
504
+ method = '/spotMarket/level2Depth' + limit.toString();
505
+ }
506
+ const topic = method + ':' + marketIds.join(',');
491
507
  const messageHashes = [];
492
508
  const subscriptionHashes = [];
493
509
  for (let i = 0; i < symbols.length; i++) {
494
510
  const symbol = symbols[i];
495
511
  messageHashes.push('orderbook:' + symbol);
496
512
  const marketId = marketIds[i];
497
- subscriptionHashes.push('/market/level2:' + marketId);
513
+ subscriptionHashes.push(method + ':' + marketId);
514
+ }
515
+ let subscription = {};
516
+ if (method === '/market/level2') { // other streams return the entire orderbook, so we don't need to fetch the snapshot through REST
517
+ subscription = {
518
+ 'method': this.handleOrderBookSubscription,
519
+ 'symbols': symbols,
520
+ 'limit': limit,
521
+ };
498
522
  }
499
- const subscription = {
500
- 'method': this.handleOrderBookSubscription,
501
- 'symbols': symbols,
502
- 'limit': limit,
503
- };
504
523
  const orderbook = await this.subscribeMultiple(url, messageHashes, topic, subscriptionHashes, params, subscription);
505
524
  return orderbook.limit();
506
525
  }
@@ -524,41 +543,74 @@ export default class kucoin extends kucoinRest {
524
543
  // }
525
544
  // }
526
545
  //
546
+ // {
547
+ // "topic": "/spotMarket/level2Depth5:BTC-USDT",
548
+ // "type": "message",
549
+ // "data": {
550
+ // "asks": [
551
+ // [
552
+ // "42815.6",
553
+ // "1.24016245"
554
+ // ]
555
+ // ],
556
+ // "bids": [
557
+ // [
558
+ // "42815.5",
559
+ // "0.08652716"
560
+ // ]
561
+ // ],
562
+ // "timestamp": 1707204474018
563
+ // },
564
+ // "subject": "level2"
565
+ // }
566
+ //
527
567
  const data = this.safeValue(message, 'data');
528
- const marketId = this.safeString(data, 'symbol');
568
+ const subject = this.safeString(message, 'subject');
569
+ const topic = this.safeString(message, 'topic');
570
+ const topicParts = topic.split(':');
571
+ const topicSymbol = this.safeString(topicParts, 1);
572
+ const topicChannel = this.safeString(topicParts, 0);
573
+ const marketId = this.safeString(data, 'symbol', topicSymbol);
529
574
  const symbol = this.safeSymbol(marketId, undefined, '-');
530
575
  const messageHash = 'orderbook:' + symbol;
531
- const storedOrderBook = this.orderbooks[symbol];
532
- const nonce = this.safeInteger(storedOrderBook, 'nonce');
533
- const deltaEnd = this.safeInteger(data, 'sequenceEnd');
534
- if (nonce === undefined) {
535
- const cacheLength = storedOrderBook.cache.length;
536
- const topic = this.safeString(message, 'topic');
537
- const topicParts = topic.split(':');
538
- const topicSymbol = this.safeString(topicParts, 1);
539
- const topicChannel = this.safeString(topicParts, 0);
540
- const subscriptions = Object.keys(client.subscriptions);
541
- let subscription = undefined;
542
- for (let i = 0; i < subscriptions.length; i++) {
543
- const key = subscriptions[i];
544
- if ((key.indexOf(topicSymbol) >= 0) && (key.indexOf(topicChannel) >= 0)) {
545
- subscription = client.subscriptions[key];
546
- break;
547
- }
576
+ let orderbook = this.safeDict(this.orderbooks, symbol);
577
+ if (subject === 'level2') {
578
+ if (orderbook === undefined) {
579
+ orderbook = this.orderBook();
548
580
  }
549
- const limit = this.safeInteger(subscription, 'limit');
550
- const snapshotDelay = this.handleOption('watchOrderBook', 'snapshotDelay', 5);
551
- if (cacheLength === snapshotDelay) {
552
- this.spawn(this.loadOrderBook, client, messageHash, symbol, limit, {});
581
+ else {
582
+ orderbook.reset();
553
583
  }
554
- storedOrderBook.cache.push(data);
555
- return;
584
+ orderbook['symbol'] = symbol;
556
585
  }
557
- else if (nonce >= deltaEnd) {
558
- return;
586
+ else {
587
+ const nonce = this.safeInteger(orderbook, 'nonce');
588
+ const deltaEnd = this.safeInteger2(data, 'sequenceEnd', 'timestamp');
589
+ if (nonce === undefined) {
590
+ const cacheLength = orderbook.cache.length;
591
+ const subscriptions = Object.keys(client.subscriptions);
592
+ let subscription = undefined;
593
+ for (let i = 0; i < subscriptions.length; i++) {
594
+ const key = subscriptions[i];
595
+ if ((key.indexOf(topicSymbol) >= 0) && (key.indexOf(topicChannel) >= 0)) {
596
+ subscription = client.subscriptions[key];
597
+ break;
598
+ }
599
+ }
600
+ const limit = this.safeInteger(subscription, 'limit');
601
+ const snapshotDelay = this.handleOption('watchOrderBook', 'snapshotDelay', 5);
602
+ if (cacheLength === snapshotDelay) {
603
+ this.spawn(this.loadOrderBook, client, messageHash, symbol, limit, {});
604
+ }
605
+ orderbook.cache.push(data);
606
+ return;
607
+ }
608
+ else if (nonce >= deltaEnd) {
609
+ return;
610
+ }
559
611
  }
560
- this.handleDelta(storedOrderBook, data);
561
- client.resolve(storedOrderBook, messageHash);
612
+ this.handleDelta(orderbook, data);
613
+ client.resolve(orderbook, messageHash);
562
614
  }
563
615
  getCacheIndex(orderbook, cache) {
564
616
  const firstDelta = this.safeValue(cache, 0);
@@ -578,11 +630,11 @@ export default class kucoin extends kucoinRest {
578
630
  return cache.length;
579
631
  }
580
632
  handleDelta(orderbook, delta) {
581
- orderbook['nonce'] = this.safeInteger(delta, 'sequenceEnd');
582
- const timestamp = this.safeInteger(delta, 'time');
633
+ const timestamp = this.safeInteger2(delta, 'time', 'timestamp');
634
+ orderbook['nonce'] = this.safeInteger(delta, 'sequenceEnd', timestamp);
583
635
  orderbook['timestamp'] = timestamp;
584
636
  orderbook['datetime'] = this.iso8601(timestamp);
585
- const changes = this.safeValue(delta, 'changes');
637
+ const changes = this.safeValue(delta, 'changes', delta);
586
638
  const bids = this.safeValue(changes, 'bids', []);
587
639
  const asks = this.safeValue(changes, 'asks', []);
588
640
  const storedBids = orderbook['bids'];
@@ -996,6 +1048,7 @@ export default class kucoin extends kucoinRest {
996
1048
  }
997
1049
  const subject = this.safeString(message, 'subject');
998
1050
  const methods = {
1051
+ 'level2': this.handleOrderBook,
999
1052
  'trade.l2update': this.handleOrderBook,
1000
1053
  'trade.ticker': this.handleTicker,
1001
1054
  'trade.snapshot': this.handleTicker,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccxt",
3
- "version": "4.2.36",
3
+ "version": "4.2.38",
4
4
  "description": "A JavaScript / TypeScript / Python / C# / PHP cryptocurrency trading library with support for 100+ exchanges",
5
5
  "unpkg": "dist/ccxt.browser.js",
6
6
  "type": "module",
@@ -144,7 +144,7 @@
144
144
  "as-table": "1.0.37",
145
145
  "asciichart": "^1.5.25",
146
146
  "assert": "^2.0.0",
147
- "ast-transpiler": "^0.0.41",
147
+ "ast-transpiler": "^0.0.43",
148
148
  "docsify": "^4.11.4",
149
149
  "eslint": "8.22.0",
150
150
  "eslint-config-airbnb-base": "15.0.0",