ccxt 4.5.44 → 4.5.45

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.
Files changed (81) hide show
  1. package/README.md +9 -12
  2. package/dist/ccxt.browser.min.js +3 -3
  3. package/dist/cjs/ccxt.js +1 -12
  4. package/dist/cjs/src/abstract/kucoinfutures.js +1 -1
  5. package/dist/cjs/src/base/Exchange.js +36 -3
  6. package/dist/cjs/src/base/functions/encode.js +2 -2
  7. package/dist/cjs/src/base/functions/generic.js +8 -2
  8. package/dist/cjs/src/bitrue.js +1 -1
  9. package/dist/cjs/src/bitteam.js +1 -1
  10. package/dist/cjs/src/btcbox.js +1 -1
  11. package/dist/cjs/src/cex.js +1 -0
  12. package/dist/cjs/src/gate.js +227 -168
  13. package/dist/cjs/src/grvt.js +3 -2
  14. package/dist/cjs/src/hyperliquid.js +16 -5
  15. package/dist/cjs/src/kraken.js +2 -2
  16. package/dist/cjs/src/krakenfutures.js +1 -5
  17. package/dist/cjs/src/kucoin.js +4729 -970
  18. package/dist/cjs/src/kucoinfutures.js +14 -3434
  19. package/dist/cjs/src/lbank.js +1 -1
  20. package/dist/cjs/src/poloniex.js +1 -1
  21. package/dist/cjs/src/pro/gate.js +37 -1
  22. package/dist/cjs/src/pro/kucoin.js +819 -178
  23. package/dist/cjs/src/pro/kucoinfutures.js +95 -1261
  24. package/dist/cjs/src/pro/mexc.js +10 -5
  25. package/dist/cjs/src/pro/okx.js +84 -39
  26. package/js/ccxt.d.ts +2 -14
  27. package/js/ccxt.js +2 -10
  28. package/js/src/abstract/kucoin.d.ts +46 -3
  29. package/js/src/abstract/kucoinfutures.d.ts +27 -12
  30. package/js/src/base/Exchange.d.ts +12 -1
  31. package/js/src/base/Exchange.js +36 -3
  32. package/js/src/base/functions/encode.js +2 -2
  33. package/js/src/base/functions/generic.js +9 -3
  34. package/js/src/bitrue.js +1 -1
  35. package/js/src/bitteam.js +1 -1
  36. package/js/src/btcbox.js +1 -1
  37. package/js/src/cex.js +2 -1
  38. package/js/src/gate.d.ts +125 -119
  39. package/js/src/gate.js +227 -168
  40. package/js/src/grvt.js +3 -2
  41. package/js/src/hyperliquid.d.ts +3 -1
  42. package/js/src/hyperliquid.js +16 -5
  43. package/js/src/kraken.js +2 -2
  44. package/js/src/krakenfutures.js +1 -5
  45. package/js/src/kucoin.d.ts +696 -100
  46. package/js/src/kucoin.js +4730 -971
  47. package/js/src/kucoinfutures.d.ts +4 -522
  48. package/js/src/kucoinfutures.js +14 -3434
  49. package/js/src/lbank.js +1 -1
  50. package/js/src/poloniex.js +1 -1
  51. package/js/src/pro/gate.d.ts +30 -1
  52. package/js/src/pro/gate.js +37 -1
  53. package/js/src/pro/kucoin.d.ts +70 -30
  54. package/js/src/pro/kucoin.js +821 -180
  55. package/js/src/pro/kucoinfutures.d.ts +17 -195
  56. package/js/src/pro/kucoinfutures.js +96 -1262
  57. package/js/src/pro/mexc.js +10 -5
  58. package/js/src/pro/okx.d.ts +1 -0
  59. package/js/src/pro/okx.js +84 -39
  60. package/package.json +1 -1
  61. package/dist/cjs/src/abstract/alp.js +0 -11
  62. package/dist/cjs/src/abstract/defx.js +0 -11
  63. package/dist/cjs/src/abstract/timex.js +0 -11
  64. package/dist/cjs/src/alp.js +0 -1059
  65. package/dist/cjs/src/defx.js +0 -2142
  66. package/dist/cjs/src/pro/defx.js +0 -866
  67. package/dist/cjs/src/timex.js +0 -1793
  68. package/js/src/abstract/alp.d.ts +0 -21
  69. package/js/src/abstract/alp.js +0 -5
  70. package/js/src/abstract/defx.d.ts +0 -72
  71. package/js/src/abstract/defx.js +0 -5
  72. package/js/src/abstract/timex.d.ts +0 -65
  73. package/js/src/abstract/timex.js +0 -5
  74. package/js/src/alp.d.ts +0 -209
  75. package/js/src/alp.js +0 -1052
  76. package/js/src/defx.d.ts +0 -348
  77. package/js/src/defx.js +0 -2135
  78. package/js/src/pro/defx.d.ts +0 -236
  79. package/js/src/pro/defx.js +0 -859
  80. package/js/src/timex.d.ts +0 -247
  81. package/js/src/timex.js +0 -1786
@@ -4,6 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var kucoin$1 = require('../kucoin.js');
6
6
  var errors = require('../base/errors.js');
7
+ var Precise = require('../base/Precise.js');
7
8
  var Cache = require('../base/ws/Cache.js');
8
9
 
9
10
  // ---------------------------------------------------------------------------
@@ -23,6 +24,8 @@ class kucoin extends kucoin$1["default"] {
23
24
  'watchBidsAsks': true,
24
25
  'watchOrderBook': true,
25
26
  'watchOrders': true,
27
+ 'watchPosition': true,
28
+ 'watchPositions': false,
26
29
  'watchMyTrades': true,
27
30
  'watchTickers': true,
28
31
  'watchTicker': true,
@@ -40,15 +43,24 @@ class kucoin extends kucoin$1["default"] {
40
43
  'options': {
41
44
  'tradesLimit': 1000,
42
45
  'watchTicker': {
43
- 'name': 'market/snapshot', // market/ticker
46
+ 'spotMethod': '/market/snapshot', // '/market/ticker'
44
47
  },
45
48
  'watchOrderBook': {
46
49
  'snapshotDelay': 5,
47
50
  'snapshotMaxRetries': 3,
48
- 'method': '/market/level2', // '/spotMarket/level2Depth5' or '/spotMarket/level2Depth50'
51
+ 'spotMethod': '/market/level2',
52
+ 'contractMethod': '/contractMarket/level2', // '/contractMarket/level2Depth5' or '/contractMarket/level2Depth20'
49
53
  },
50
54
  'watchMyTrades': {
51
- 'method': '/spotMarket/tradeOrders', // or '/spot/tradeFills'
55
+ 'spotMethod': '/spotMarket/tradeOrders', // or '/spot/tradeFills'
56
+ },
57
+ 'watchBalance': {
58
+ 'fetchBalanceSnapshot': true,
59
+ 'awaitBalanceSnapshot': true, // whether to wait for the balance snapshot before providing updates
60
+ },
61
+ 'watchPosition': {
62
+ 'fetchPositionSnapshot': true,
63
+ 'awaitPositionSnapshot': true, // whether to wait for the position snapshot before providing updates
52
64
  },
53
65
  },
54
66
  'streaming': {
@@ -59,9 +71,12 @@ class kucoin extends kucoin$1["default"] {
59
71
  },
60
72
  });
61
73
  }
62
- async negotiate(privateChannel, params = {}) {
63
- const connectId = privateChannel ? 'private' : 'public';
64
- const urls = this.safeValue(this.options, 'urls', {});
74
+ async negotiate(privateChannel, isFuturesMethod = false, params = {}) {
75
+ let connectId = privateChannel ? 'private' : 'public';
76
+ if (isFuturesMethod) {
77
+ connectId += 'Futures';
78
+ }
79
+ const urls = this.safeDict(this.options, 'urls', {});
65
80
  let future = this.safeValue(urls, connectId);
66
81
  if (future !== undefined) {
67
82
  return await future;
@@ -69,16 +84,15 @@ class kucoin extends kucoin$1["default"] {
69
84
  // we store an awaitable to the url
70
85
  // so that multiple calls don't asynchronously
71
86
  // fetch different urls and overwrite each other
72
- urls[connectId] = this.spawn(this.negotiateHelper, privateChannel, params);
87
+ urls[connectId] = this.spawn(this.negotiateHelper, privateChannel, connectId, params);
73
88
  this.options['urls'] = urls;
74
89
  future = urls[connectId];
75
90
  return await future;
76
91
  }
77
- async negotiateHelper(privateChannel, params = {}) {
92
+ async negotiateHelper(privateChannel, connectId, params = {}) {
78
93
  let response = undefined;
79
- const connectId = privateChannel ? 'private' : 'public';
80
94
  try {
81
- if (privateChannel) {
95
+ if (connectId === 'private') {
82
96
  response = await this.privatePostBulletPrivate(params);
83
97
  //
84
98
  // {
@@ -98,12 +112,18 @@ class kucoin extends kucoin$1["default"] {
98
112
  // }
99
113
  //
100
114
  }
101
- else {
115
+ else if (connectId === 'public') {
102
116
  response = await this.publicPostBulletPublic(params);
103
117
  }
104
- const data = this.safeValue(response, 'data', {});
105
- const instanceServers = this.safeValue(data, 'instanceServers', []);
106
- const firstInstanceServer = this.safeValue(instanceServers, 0);
118
+ else if (connectId === 'privateFutures') {
119
+ response = await this.futuresPrivatePostBulletPrivate(params);
120
+ }
121
+ else {
122
+ response = await this.futuresPublicPostBulletPublic(params);
123
+ }
124
+ const data = this.safeDict(response, 'data', {});
125
+ const instanceServers = this.safeList(data, 'instanceServers', []);
126
+ const firstInstanceServer = this.safeDict(instanceServers, 0);
107
127
  const pingInterval = this.safeInteger(firstInstanceServer, 'pingInterval');
108
128
  const endpoint = this.safeString(firstInstanceServer, 'endpoint');
109
129
  const token = this.safeString(data, 'token');
@@ -191,7 +211,8 @@ class kucoin extends kucoin$1["default"] {
191
211
  * @method
192
212
  * @name kucoin#watchTicker
193
213
  * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
194
- * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/market-snapshot
214
+ * @see https://www.kucoin.com/docs-new/3470063w0
215
+ * @see https://www.kucoin.com/docs-new/3470081w0
195
216
  * @param {string} symbol unified symbol of the market to fetch the ticker for
196
217
  * @param {object} [params] extra parameters specific to the exchange API endpoint
197
218
  * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/?id=ticker-structure}
@@ -200,17 +221,25 @@ class kucoin extends kucoin$1["default"] {
200
221
  await this.loadMarkets();
201
222
  const market = this.market(symbol);
202
223
  symbol = market['symbol'];
203
- const url = await this.negotiate(false);
204
- const [method, query] = this.handleOptionAndParams(params, 'watchTicker', 'method', '/market/snapshot');
224
+ const isFuturesMethod = market['contract'];
225
+ const url = await this.negotiate(false, isFuturesMethod);
226
+ let method = '/market/snapshot';
227
+ if (isFuturesMethod) {
228
+ method = '/contractMarket/ticker';
229
+ }
230
+ else {
231
+ [method, params] = this.handleOptionAndParams(params, 'watchTicker', 'spotMethod', method);
232
+ }
205
233
  const topic = method + ':' + market['id'];
206
234
  const messageHash = 'ticker:' + symbol;
207
- return await this.subscribe(url, messageHash, topic, query);
235
+ return await this.subscribe(url, messageHash, topic, params);
208
236
  }
209
237
  /**
210
238
  * @method
211
239
  * @name kucoin#unWatchTicker
212
240
  * @description unWatches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
213
- * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/market-snapshot
241
+ * @see https://www.kucoin.com/docs-new/3470063w0
242
+ * @see https://www.kucoin.com/docs-new/3470081w0
214
243
  * @param {string} symbol unified symbol of the market to fetch the ticker for
215
244
  * @param {object} [params] extra parameters specific to the exchange API endpoint
216
245
  * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/?id=ticker-structure}
@@ -219,16 +248,25 @@ class kucoin extends kucoin$1["default"] {
219
248
  await this.loadMarkets();
220
249
  const market = this.market(symbol);
221
250
  symbol = market['symbol'];
222
- const url = await this.negotiate(false);
223
- let method = undefined;
224
- [method, params] = this.handleOptionAndParams(params, 'watchTicker', 'method', '/market/snapshot');
251
+ const isFuturesMethod = market['contract'];
252
+ const url = await this.negotiate(false, isFuturesMethod);
253
+ let method = '/market/snapshot';
254
+ if (isFuturesMethod) {
255
+ method = '/contractMarket/ticker';
256
+ }
257
+ else {
258
+ [method, params] = this.handleOptionAndParams(params, 'watchTicker', 'spotMethod', method);
259
+ }
225
260
  const topic = method + ':' + market['id'];
226
261
  const messageHash = 'unsubscribe:ticker:' + symbol;
227
262
  const subMessageHash = 'ticker:' + symbol;
228
263
  const subscription = {
229
- 'messageHashes': [messageHash],
230
- 'subMessageHashes': [subMessageHash],
231
- 'topic': 'trades',
264
+ // we have to add the topic to the messageHashes and subMessageHashes
265
+ // because handleSubscriptionStatus needs them to remove the subscription from the client
266
+ // without them subscription would never be removed and re-subscribe would fail because of duplicate subscriptionHash
267
+ 'messageHashes': [messageHash, topic],
268
+ 'subMessageHashes': [subMessageHash, topic],
269
+ 'topic': 'ticker',
232
270
  'unsubscribe': true,
233
271
  'symbols': [symbol],
234
272
  };
@@ -237,19 +275,33 @@ class kucoin extends kucoin$1["default"] {
237
275
  /**
238
276
  * @method
239
277
  * @name kucoin#watchTickers
240
- * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/ticker
278
+ * @see https://www.kucoin.com/docs-new/3470063w0
279
+ * @see https://www.kucoin.com/docs-new/3470064w0
280
+ * @see https://www.kucoin.com/docs-new/3470081w0
241
281
  * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
242
282
  * @param {string[]} symbols unified symbol of the market to fetch the ticker for
243
283
  * @param {object} [params] extra parameters specific to the exchange API endpoint
244
- * @param {string} [params.method] either '/market/snapshot' or '/market/ticker' default is '/market/ticker'
284
+ * @param {string} [params.method] *spot markets only* either '/market/snapshot' or '/market/ticker' default is '/market/ticker'
245
285
  * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/?id=ticker-structure}
246
286
  */
247
287
  async watchTickers(symbols = undefined, params = {}) {
248
288
  await this.loadMarkets();
249
- symbols = this.marketSymbols(symbols);
289
+ symbols = this.marketSymbols(symbols, undefined, true, true);
290
+ const firstMarket = this.getMarketFromSymbols(symbols);
291
+ let marketType = undefined;
292
+ [marketType, params] = this.handleMarketTypeAndParams('watchTickers', firstMarket, params);
293
+ const isFuturesMethod = (marketType !== 'spot') && (marketType !== 'margin');
294
+ if (isFuturesMethod && symbols === undefined) {
295
+ throw new errors.ArgumentsRequired(this.id + ' watchTickers() requires a list of symbols for ' + marketType + ' markets');
296
+ }
250
297
  const messageHash = 'tickers';
251
- let method = undefined;
252
- [method, params] = this.handleOptionAndParams(params, 'watchTickers', 'method', '/market/ticker');
298
+ let method = '/market/ticker';
299
+ if (isFuturesMethod) {
300
+ method = '/contractMarket/ticker';
301
+ }
302
+ else {
303
+ [method, params] = this.handleOptionAndParams2(params, 'watchTickers', 'method', 'spotMethod', method);
304
+ }
253
305
  const messageHashes = [];
254
306
  const topics = [];
255
307
  if (symbols !== undefined) {
@@ -260,7 +312,7 @@ class kucoin extends kucoin$1["default"] {
260
312
  topics.push(method + ':' + market['id']);
261
313
  }
262
314
  }
263
- const url = await this.negotiate(false);
315
+ const url = await this.negotiate(false, isFuturesMethod);
264
316
  let tickers = undefined;
265
317
  if (symbols === undefined) {
266
318
  const allTopic = method + ':all';
@@ -336,43 +388,106 @@ class kucoin extends kucoin$1["default"] {
336
388
  // }
337
389
  // }
338
390
  //
391
+ // futures
392
+ // {
393
+ // "subject": "ticker",
394
+ // "topic": "/contractMarket/ticker:XBTUSDM",
395
+ // "data": {
396
+ // "symbol": "XBTUSDM", //Market of the symbol
397
+ // "sequence": 45, //Sequence number which is used to judge the continuity of the pushed messages
398
+ // "side": "sell", //Transaction side of the last traded taker order
399
+ // "price": "3600.0", //Filled price
400
+ // "size": 16, //Filled quantity
401
+ // "tradeId": "5c9dcf4170744d6f5a3d32fb", //Order ID
402
+ // "bestBidSize": 795, //Best bid size
403
+ // "bestBidPrice": "3200.0", //Best bid
404
+ // "bestAskPrice": "3600.0", //Best ask size
405
+ // "bestAskSize": 284, //Best ask
406
+ // "ts": 1553846081210004941 //Filled time - nanosecond
407
+ // }
408
+ // }
409
+ //
339
410
  const topic = this.safeString(message, 'topic');
340
- let market = undefined;
341
- if (topic !== undefined) {
342
- const parts = topic.split(':');
343
- const first = this.safeString(parts, 1);
344
- let marketId = undefined;
345
- if (first === 'all') {
346
- marketId = this.safeString(message, 'subject');
347
- }
348
- else {
349
- marketId = first;
411
+ if (topic.indexOf('contractMarket') < 0) {
412
+ let market = undefined;
413
+ if (topic !== undefined) {
414
+ const parts = topic.split(':');
415
+ const first = this.safeString(parts, 1);
416
+ let marketId = undefined;
417
+ if (first === 'all') {
418
+ marketId = this.safeString(message, 'subject');
419
+ }
420
+ else {
421
+ marketId = first;
422
+ }
423
+ market = this.safeMarket(marketId, market, '-');
350
424
  }
351
- market = this.safeMarket(marketId, market, '-');
425
+ const data = this.safeDict(message, 'data', {});
426
+ const rawTicker = this.safeDict(data, 'data', data);
427
+ const ticker = this.parseTicker(rawTicker, market);
428
+ const symbol = ticker['symbol'];
429
+ this.tickers[symbol] = ticker;
430
+ const messageHash = 'ticker:' + symbol;
431
+ client.resolve(ticker, messageHash);
432
+ // watchTickers
433
+ const allTickers = {};
434
+ allTickers[symbol] = ticker;
435
+ client.resolve(allTickers, 'tickers');
352
436
  }
353
- const data = this.safeValue(message, 'data', {});
354
- const rawTicker = this.safeValue(data, 'data', data);
355
- const ticker = this.parseTicker(rawTicker, market);
356
- const symbol = ticker['symbol'];
357
- this.tickers[symbol] = ticker;
358
- const messageHash = 'ticker:' + symbol;
437
+ else {
438
+ this.handleContractTicker(client, message);
439
+ }
440
+ }
441
+ handleContractTicker(client, message) {
442
+ //
443
+ // ticker (v1)
444
+ //
445
+ // {
446
+ // "subject": "ticker",
447
+ // "topic": "/contractMarket/ticker:XBTUSDM",
448
+ // "data": {
449
+ // "symbol": "XBTUSDM", //Market of the symbol
450
+ // "sequence": 45, //Sequence number which is used to judge the continuity of the pushed messages
451
+ // "side": "sell", //Transaction side of the last traded taker order
452
+ // "price": "3600.0", //Filled price
453
+ // "size": 16, //Filled quantity
454
+ // "tradeId": "5c9dcf4170744d6f5a3d32fb", //Order ID
455
+ // "bestBidSize": 795, //Best bid size
456
+ // "bestBidPrice": "3200.0", //Best bid
457
+ // "bestAskPrice": "3600.0", //Best ask size
458
+ // "bestAskSize": 284, //Best ask
459
+ // "ts": 1553846081210004941 //Filled time - nanosecond
460
+ // }
461
+ // }
462
+ //
463
+ const data = this.safeDict(message, 'data', {});
464
+ const marketId = this.safeString(data, 'symbol');
465
+ const market = this.safeMarket(marketId, undefined, '-');
466
+ const ticker = this.parseTicker(data, market);
467
+ this.tickers[market['symbol']] = ticker;
468
+ const messageHash = 'ticker:' + market['symbol'];
359
469
  client.resolve(ticker, messageHash);
360
- // watchTickers
361
- const allTickers = {};
362
- allTickers[symbol] = ticker;
363
- client.resolve(allTickers, 'tickers');
364
470
  }
365
471
  /**
366
472
  * @method
367
473
  * @name kucoin#watchBidsAsks
368
- * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level1-bbo-market-data
474
+ * @see https://www.kucoin.com/docs-new/3470067w0
475
+ * @see https://www.kucoin.com/docs-new/3470080w0
369
476
  * @description watches best bid & ask for symbols
370
477
  * @param {string[]} symbols unified symbol of the market to fetch the ticker for
371
478
  * @param {object} [params] extra parameters specific to the exchange API endpoint
372
479
  * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/?id=ticker-structure}
373
480
  */
374
481
  async watchBidsAsks(symbols = undefined, params = {}) {
375
- const ticker = await this.watchMultiHelper('watchBidsAsks', '/spotMarket/level1:', symbols, params);
482
+ await this.loadMarkets();
483
+ symbols = this.marketSymbols(symbols, undefined, false, true, false);
484
+ const firstMarket = this.getMarketFromSymbols(symbols);
485
+ const isFuturesMethod = firstMarket['contract'];
486
+ let channelName = '/spotMarket/level1:';
487
+ if (isFuturesMethod) {
488
+ channelName = '/contractMarket/tickerV2:';
489
+ }
490
+ const ticker = await this.watchMultiHelper('watchBidsAsks', channelName, symbols, params);
376
491
  if (this.newUpdates) {
377
492
  const tickers = {};
378
493
  tickers[ticker['symbol']] = ticker;
@@ -421,6 +536,20 @@ class kucoin extends kucoin$1["default"] {
421
536
  // subject: 'level1'
422
537
  // }
423
538
  //
539
+ // futures
540
+ // {
541
+ // "subject": "tickerV2",
542
+ // "topic": "/contractMarket/tickerV2:XBTUSDM",
543
+ // "data": {
544
+ // "symbol": "XBTUSDM", //Market of the symbol
545
+ // "bestBidSize": 795, // Best bid size
546
+ // "bestBidPrice": 3200.0, // Best bid
547
+ // "bestAskPrice": 3600.0, // Best ask
548
+ // "bestAskSize": 284, // Best ask size
549
+ // "ts": 1553846081210004941 // Filled time - nanosecond
550
+ // }
551
+ // }
552
+ //
424
553
  const parsedTicker = this.parseWsBidAsk(message);
425
554
  const symbol = parsedTicker['symbol'];
426
555
  this.bidsasks[symbol] = parsedTicker;
@@ -429,30 +558,51 @@ class kucoin extends kucoin$1["default"] {
429
558
  }
430
559
  parseWsBidAsk(ticker, market = undefined) {
431
560
  const topic = this.safeString(ticker, 'topic');
432
- const parts = topic.split(':');
433
- const marketId = parts[1];
434
- market = this.safeMarket(marketId, market);
435
- const symbol = this.safeString(market, 'symbol');
436
- const data = this.safeDict(ticker, 'data', {});
437
- const ask = this.safeList(data, 'asks', []);
438
- const bid = this.safeList(data, 'bids', []);
439
- const timestamp = this.safeInteger(data, 'timestamp');
440
- return this.safeTicker({
441
- 'symbol': symbol,
442
- 'timestamp': timestamp,
443
- 'datetime': this.iso8601(timestamp),
444
- 'ask': this.safeNumber(ask, 0),
445
- 'askVolume': this.safeNumber(ask, 1),
446
- 'bid': this.safeNumber(bid, 0),
447
- 'bidVolume': this.safeNumber(bid, 1),
448
- 'info': ticker,
449
- }, market);
561
+ if (topic.indexOf('contractMarket') < 0) {
562
+ const parts = topic.split(':');
563
+ const marketId = parts[1];
564
+ market = this.safeMarket(marketId, market);
565
+ const symbol = this.safeString(market, 'symbol');
566
+ const data = this.safeDict(ticker, 'data', {});
567
+ const ask = this.safeList(data, 'asks', []);
568
+ const bid = this.safeList(data, 'bids', []);
569
+ const timestamp = this.safeInteger(data, 'timestamp');
570
+ return this.safeTicker({
571
+ 'symbol': symbol,
572
+ 'timestamp': timestamp,
573
+ 'datetime': this.iso8601(timestamp),
574
+ 'ask': this.safeNumber(ask, 0),
575
+ 'askVolume': this.safeNumber(ask, 1),
576
+ 'bid': this.safeNumber(bid, 0),
577
+ 'bidVolume': this.safeNumber(bid, 1),
578
+ 'info': ticker,
579
+ }, market);
580
+ }
581
+ else {
582
+ // futures
583
+ const data = this.safeDict(ticker, 'data', {});
584
+ const marketId = this.safeString(data, 'symbol');
585
+ market = this.safeMarket(marketId, market);
586
+ const symbol = this.safeString(market, 'symbol');
587
+ const timestamp = this.safeIntegerProduct(data, 'ts', 0.000001);
588
+ return this.safeTicker({
589
+ 'symbol': symbol,
590
+ 'timestamp': timestamp,
591
+ 'datetime': this.iso8601(timestamp),
592
+ 'ask': this.safeNumber(data, 'bestAskPrice'),
593
+ 'askVolume': this.safeNumber(data, 'bestAskSize'),
594
+ 'bid': this.safeNumber(data, 'bestBidPrice'),
595
+ 'bidVolume': this.safeNumber(data, 'bestBidSize'),
596
+ 'info': ticker,
597
+ }, market);
598
+ }
450
599
  }
451
600
  /**
452
601
  * @method
453
602
  * @name kucoin#watchOHLCV
454
603
  * @description watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
455
- * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/klines
604
+ * @see https://www.kucoin.com/docs-new/3470071w0
605
+ * @see https://www.kucoin.com/docs-new/3470086w0
456
606
  * @param {string} symbol unified symbol of the market to fetch OHLCV data for
457
607
  * @param {string} timeframe the length of time each candle represents
458
608
  * @param {int} [since] timestamp in ms of the earliest candle to fetch
@@ -462,11 +612,16 @@ class kucoin extends kucoin$1["default"] {
462
612
  */
463
613
  async watchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
464
614
  await this.loadMarkets();
465
- const url = await this.negotiate(false);
466
615
  const market = this.market(symbol);
467
616
  symbol = market['symbol'];
617
+ const isFuturesMethod = market['contract'];
618
+ const url = await this.negotiate(false, isFuturesMethod);
468
619
  const period = this.safeString(this.timeframes, timeframe, timeframe);
469
- const topic = '/market/candles:' + market['id'] + '_' + period;
620
+ let channelName = '/market/candles:';
621
+ if (isFuturesMethod) {
622
+ channelName = '/contractMarket/limitCandle:';
623
+ }
624
+ const topic = channelName + market['id'] + '_' + period;
470
625
  const messageHash = 'candles:' + symbol + ':' + timeframe;
471
626
  const ohlcv = await this.subscribe(url, messageHash, topic, params);
472
627
  if (this.newUpdates) {
@@ -478,7 +633,8 @@ class kucoin extends kucoin$1["default"] {
478
633
  * @method
479
634
  * @name kucoin#unWatchOHLCV
480
635
  * @description unWatches historical candlestick data containing the open, high, low, and close price, and the volume of a market
481
- * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/klines
636
+ * @see https://www.kucoin.com/docs-new/3470071w0
637
+ * @see https://www.kucoin.com/docs-new/3470086w0
482
638
  * @param {string} symbol unified symbol of the market to fetch OHLCV data for
483
639
  * @param {string} timeframe the length of time each candle represents
484
640
  * @param {object} [params] extra parameters specific to the exchange API endpoint
@@ -486,19 +642,29 @@ class kucoin extends kucoin$1["default"] {
486
642
  */
487
643
  async unWatchOHLCV(symbol, timeframe = '1m', params = {}) {
488
644
  await this.loadMarkets();
489
- const url = await this.negotiate(false);
490
645
  const market = this.market(symbol);
491
646
  symbol = market['symbol'];
647
+ const isFuturesMethod = market['contract'];
648
+ const url = await this.negotiate(false, isFuturesMethod);
649
+ let channelName = '/market/candles:';
650
+ if (isFuturesMethod) {
651
+ channelName = '/contractMarket/limitCandle:';
652
+ }
492
653
  const period = this.safeString(this.timeframes, timeframe, timeframe);
493
- const topic = '/market/candles:' + market['id'] + '_' + period;
654
+ const topic = channelName + market['id'] + '_' + period;
494
655
  const messageHash = 'unsubscribe:candles:' + symbol + ':' + timeframe;
495
656
  const subMessageHash = 'candles:' + symbol + ':' + timeframe;
657
+ const symbolAndTimeframe = [symbol, timeframe];
496
658
  const subscription = {
497
- 'messageHashes': [messageHash],
498
- 'subMessageHashes': [subMessageHash],
659
+ // we have to add the topic to the messageHashes and subMessageHashes
660
+ // because handleSubscriptionStatus needs them to remove the subscription from the client
661
+ // without them subscription would never be removed and re-subscribe would fail because of duplicate subscriptionHash
662
+ 'messageHashes': [messageHash, topic],
663
+ 'subMessageHashes': [subMessageHash, topic],
499
664
  'topic': 'ohlcv',
500
665
  'unsubscribe': true,
501
666
  'symbols': [symbol],
667
+ 'symbolsAndTimeframes': [symbolAndTimeframe],
502
668
  };
503
669
  return await this.unSubscribe(url, messageHash, topic, messageHash, params, subscription);
504
670
  }
@@ -523,9 +689,29 @@ class kucoin extends kucoin$1["default"] {
523
689
  // "type": "message"
524
690
  // }
525
691
  //
526
- const data = this.safeValue(message, 'data', {});
692
+ // futures
693
+ // {
694
+ // "topic":"/contractMarket/limitCandle:LTCUSDTM_1min",
695
+ // "type":"message",
696
+ // "data":{
697
+ // "symbol":"LTCUSDTM",
698
+ // "candles":[
699
+ // "1715470980",
700
+ // "81.38",
701
+ // "81.38",
702
+ // "81.38",
703
+ // "81.38",
704
+ // "61.0", - Note value 5 is incorrect and will be fixed in subsequent versions of kucoin
705
+ // "61"
706
+ // ],
707
+ // "time":1715470994801
708
+ // },
709
+ // "subject":"candle.stick"
710
+ // }
711
+ //
712
+ const data = this.safeDict(message, 'data', {});
527
713
  const marketId = this.safeString(data, 'symbol');
528
- const candles = this.safeValue(data, 'candles', []);
714
+ const candles = this.safeList(data, 'candles', []);
529
715
  const topic = this.safeString(message, 'topic');
530
716
  const parts = topic.split('_');
531
717
  const interval = this.safeString(parts, 1);
@@ -541,15 +727,25 @@ class kucoin extends kucoin$1["default"] {
541
727
  stored = new Cache.ArrayCacheByTimestamp(limit);
542
728
  this.ohlcvs[symbol][timeframe] = stored;
543
729
  }
544
- const ohlcv = this.parseOHLCV(candles, market);
545
- stored.append(ohlcv);
730
+ const isContractMarket = (topic.indexOf('contractMarket') >= 0);
731
+ const baseVolumeIndex = isContractMarket ? 6 : 5; // Note value 5 is incorrect and will be fixed in subsequent versions of kucoin
732
+ const parsed = [
733
+ this.safeTimestamp(candles, 0),
734
+ this.safeNumber(candles, 1),
735
+ this.safeNumber(candles, 3),
736
+ this.safeNumber(candles, 4),
737
+ this.safeNumber(candles, 2),
738
+ this.safeNumber(candles, baseVolumeIndex),
739
+ ];
740
+ stored.append(parsed);
546
741
  client.resolve(stored, messageHash);
547
742
  }
548
743
  /**
549
744
  * @method
550
745
  * @name kucoin#watchTrades
551
746
  * @description get the list of most recent trades for a particular symbol
552
- * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/match-execution-data
747
+ * @see https://www.kucoin.com/docs-new/3470072w0
748
+ * @see https://www.kucoin.com/docs-new/3470084w0
553
749
  * @param {string} symbol unified symbol of the market to fetch trades for
554
750
  * @param {int} [since] timestamp in ms of the earliest trade to fetch
555
751
  * @param {int} [limit] the maximum amount of trades to fetch
@@ -563,7 +759,8 @@ class kucoin extends kucoin$1["default"] {
563
759
  * @method
564
760
  * @name kucoin#watchTradesForSymbols
565
761
  * @description get the list of most recent trades for a particular symbol
566
- * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/match-execution-data
762
+ * @see https://www.kucoin.com/docs-new/3470072w0
763
+ * @see https://www.kucoin.com/docs-new/3470084w0
567
764
  * @param {string[]} symbols
568
765
  * @param {int} [since] timestamp in ms of the earliest trade to fetch
569
766
  * @param {int} [limit] the maximum amount of trades to fetch
@@ -576,17 +773,23 @@ class kucoin extends kucoin$1["default"] {
576
773
  throw new errors.ArgumentsRequired(this.id + ' watchTradesForSymbols() requires a non-empty array of symbols');
577
774
  }
578
775
  await this.loadMarkets();
579
- symbols = this.marketSymbols(symbols);
776
+ symbols = this.marketSymbols(symbols, undefined, false, true);
777
+ const firstMarket = this.getMarketFromSymbols(symbols);
778
+ const isFuturesMethod = firstMarket['contract'];
580
779
  const marketIds = this.marketIds(symbols);
581
- const url = await this.negotiate(false);
780
+ const url = await this.negotiate(false, isFuturesMethod);
582
781
  const messageHashes = [];
583
782
  const subscriptionHashes = [];
584
- const topic = '/market/match:' + marketIds.join(',');
783
+ let channelName = '/market/match:';
784
+ if (isFuturesMethod) {
785
+ channelName = '/contractMarket/execution:';
786
+ }
787
+ const topic = channelName + marketIds.join(',');
585
788
  for (let i = 0; i < symbols.length; i++) {
586
789
  const symbol = symbols[i];
587
790
  messageHashes.push('trades:' + symbol);
588
791
  const marketId = marketIds[i];
589
- subscriptionHashes.push('/market/match:' + marketId);
792
+ subscriptionHashes.push(channelName + marketId);
590
793
  }
591
794
  const trades = await this.subscribeMultiple(url, messageHashes, topic, subscriptionHashes, params);
592
795
  if (this.newUpdates) {
@@ -600,24 +803,36 @@ class kucoin extends kucoin$1["default"] {
600
803
  * @method
601
804
  * @name kucoin#unWatchTradesForSymbols
602
805
  * @description unWatches trades stream
603
- * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/match-execution-data
806
+ * @see https://www.kucoin.com/docs-new/3470072w0
807
+ * @see https://www.kucoin.com/docs-new/3470084w0
604
808
  * @param {string} symbols
605
809
  * @param {object} [params] extra parameters specific to the exchange API endpoint
606
810
  * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/?id=public-trades}
607
811
  */
608
812
  async unWatchTradesForSymbols(symbols, params = {}) {
609
813
  await this.loadMarkets();
610
- symbols = this.marketSymbols(symbols, undefined, false);
814
+ symbols = this.marketSymbols(symbols, undefined, false, true);
611
815
  const marketIds = this.marketIds(symbols);
612
- const url = await this.negotiate(false);
816
+ const firstMarket = this.getMarketFromSymbols(symbols);
817
+ const isFuturesMethod = firstMarket['contract'];
818
+ const url = await this.negotiate(false, isFuturesMethod);
613
819
  const messageHashes = [];
614
820
  const subscriptionHashes = [];
615
- const topic = '/market/match:' + marketIds.join(',');
821
+ let channelName = '/market/match:';
822
+ if (isFuturesMethod) {
823
+ channelName = '/contractMarket/execution:';
824
+ }
825
+ const topic = channelName + marketIds.join(',');
616
826
  for (let i = 0; i < symbols.length; i++) {
617
827
  const symbol = symbols[i];
618
828
  messageHashes.push('unsubscribe:trades:' + symbol);
619
829
  subscriptionHashes.push('trades:' + symbol);
620
830
  }
831
+ // we have to add the topic to the messageHashes and subMessageHashes
832
+ // because handleSubscriptionStatus needs them to remove the subscription from the client
833
+ // without them subscription would never be removed and re-subscribe would fail because of duplicate subscriptionHash
834
+ messageHashes.push(topic);
835
+ subscriptionHashes.push(topic);
621
836
  const subscription = {
622
837
  'messageHashes': messageHashes,
623
838
  'subMessageHashes': subscriptionHashes,
@@ -631,7 +846,8 @@ class kucoin extends kucoin$1["default"] {
631
846
  * @method
632
847
  * @name kucoin#unWatchTrades
633
848
  * @description unWatches trades stream
634
- * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/match-execution-data
849
+ * @see https://www.kucoin.com/docs-new/3470072w0
850
+ * @see https://www.kucoin.com/docs-new/3470084w0
635
851
  * @param {string} symbol unified symbol of the market to fetch trades for
636
852
  * @param {object} [params] extra parameters specific to the exchange API endpoint
637
853
  * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/?id=public-trades}
@@ -659,18 +875,20 @@ class kucoin extends kucoin$1["default"] {
659
875
  // "type": "message"
660
876
  // }
661
877
  //
662
- const data = this.safeValue(message, 'data', {});
663
- const trade = this.parseTrade(data);
878
+ const data = this.safeDict(message, 'data', {});
879
+ const marketId = this.safeString(data, 'symbol');
880
+ const market = this.safeMarket(marketId);
881
+ const trade = this.parseTrade(data, market);
664
882
  const symbol = trade['symbol'];
665
883
  const messageHash = 'trades:' + symbol;
666
- let trades = this.safeValue(this.trades, symbol);
667
- if (trades === undefined) {
884
+ if (!(symbol in this.trades)) {
668
885
  const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
669
- trades = new Cache.ArrayCache(limit);
670
- this.trades[symbol] = trades;
886
+ const stored = new Cache.ArrayCache(limit);
887
+ this.trades[symbol] = stored;
671
888
  }
672
- trades.append(trade);
673
- client.resolve(trades, messageHash);
889
+ const cache = this.trades[symbol];
890
+ cache.append(trade);
891
+ client.resolve(cache, messageHash);
674
892
  }
675
893
  /**
676
894
  * @method
@@ -723,15 +941,16 @@ class kucoin extends kucoin$1["default"] {
723
941
  /**
724
942
  * @method
725
943
  * @name kucoin#watchOrderBookForSymbols
726
- * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level1-bbo-market-data
727
- * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level2-market-data
728
- * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level2-5-best-ask-bid-orders
729
- * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level2-50-best-ask-bid-orders
944
+ * @see https://www.kucoin.com/docs-new/3470069w0 // spot level 5
945
+ * @see https://www.kucoin.com/docs-new/3470070w0 // spot level 50
946
+ * @see https://www.kucoin.com/docs-new/3470068w0 // spot incremental
947
+ * @see https://www.kucoin.com/docs-new/3470083w0 // futures level 5
948
+ * @see https://www.kucoin.com/docs-new/3470097w0 // futures level 50
949
+ * @see https://www.kucoin.com/docs-new/3470082w0 // futures incremental
730
950
  * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
731
951
  * @param {string[]} symbols unified array of symbols
732
952
  * @param {int} [limit] the maximum amount of order book entries to return
733
953
  * @param {object} [params] extra parameters specific to the exchange API endpoint
734
- * @param {string} [params.method] either '/market/level2' or '/spotMarket/level2Depth5' or '/spotMarket/level2Depth50' default is '/market/level2'
735
954
  * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/?id=order-book-structure} indexed by market symbols
736
955
  */
737
956
  async watchOrderBookForSymbols(symbols, limit = undefined, params = {}) {
@@ -747,11 +966,19 @@ class kucoin extends kucoin$1["default"] {
747
966
  await this.loadMarkets();
748
967
  symbols = this.marketSymbols(symbols);
749
968
  const marketIds = this.marketIds(symbols);
750
- const url = await this.negotiate(false);
751
- let method = undefined;
752
- [method, params] = this.handleOptionAndParams(params, 'watchOrderBook', 'method', '/market/level2');
753
- if ((limit === 5) || (limit === 50)) {
754
- method = '/spotMarket/level2Depth' + limit.toString();
969
+ const firstMarket = this.getMarketFromSymbols(symbols);
970
+ const isFuturesMethod = firstMarket['contract'];
971
+ const url = await this.negotiate(false, isFuturesMethod);
972
+ let method = isFuturesMethod ? '/contractMarket/level2' : '/market/level2';
973
+ const optionName = isFuturesMethod ? 'contractMethod' : 'spotMethod';
974
+ [method, params] = this.handleOptionAndParams2(params, 'watchOrderBook', optionName, 'method', method);
975
+ if (method.indexOf('Depth') === -1) {
976
+ if ((limit === 5) || (limit === 50)) {
977
+ if (!isFuturesMethod) {
978
+ method = '/spotMarket/level2';
979
+ }
980
+ method += 'Depth' + limit.toString();
981
+ }
755
982
  }
756
983
  const topic = method + ':' + marketIds.join(',');
757
984
  const messageHashes = [];
@@ -763,7 +990,7 @@ class kucoin extends kucoin$1["default"] {
763
990
  subscriptionHashes.push(method + ':' + marketId);
764
991
  }
765
992
  let subscription = {};
766
- if (method === '/market/level2') { // other streams return the entire orderbook, so we don't need to fetch the snapshot through REST
993
+ if ((method === '/market/level2') || (method === '/contractMarket/level2')) { // other streams return the entire orderbook, so we don't need to fetch the snapshot through REST
767
994
  subscription = {
768
995
  'method': this.handleOrderBookSubscription,
769
996
  'symbols': symbols,
@@ -776,27 +1003,37 @@ class kucoin extends kucoin$1["default"] {
776
1003
  /**
777
1004
  * @method
778
1005
  * @name kucoin#unWatchOrderBookForSymbols
779
- * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level1-bbo-market-data
780
- * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level2-market-data
781
- * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level2-5-best-ask-bid-orders
782
- * @see https://www.kucoin.com/docs/websocket/spot-trading/public-channels/level2-50-best-ask-bid-orders
1006
+ * @see https://www.kucoin.com/docs-new/3470069w0 // spot level 5
1007
+ * @see https://www.kucoin.com/docs-new/3470070w0 // spot level 50
1008
+ * @see https://www.kucoin.com/docs-new/3470068w0 // spot incremental
1009
+ * @see https://www.kucoin.com/docs-new/3470083w0 // futures level 5
1010
+ * @see https://www.kucoin.com/docs-new/3470097w0 // futures level 50
1011
+ * @see https://www.kucoin.com/docs-new/3470082w0 // futures incremental
783
1012
  * @description unWatches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
784
1013
  * @param {string[]} symbols unified array of symbols
785
1014
  * @param {object} [params] extra parameters specific to the exchange API endpoint
786
- * @param {string} [params.method] either '/market/level2' or '/spotMarket/level2Depth5' or '/spotMarket/level2Depth50' default is '/market/level2'
1015
+ * @param {string} [params.method] either '/market/level2' or '/spotMarket/level2Depth5' or '/spotMarket/level2Depth50' or '/contractMarket/level2' or '/contractMarket/level2Depth5' or '/contractMarket/level2Depth50' default is '/market/level2' for spot and '/contractMarket/level2' for futures
787
1016
  * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/?id=order-book-structure} indexed by market symbols
788
1017
  */
789
1018
  async unWatchOrderBookForSymbols(symbols, params = {}) {
790
1019
  const limit = this.safeInteger(params, 'limit');
791
1020
  params = this.omit(params, 'limit');
792
1021
  await this.loadMarkets();
793
- symbols = this.marketSymbols(symbols, undefined, false);
1022
+ symbols = this.marketSymbols(symbols, undefined, false, true);
794
1023
  const marketIds = this.marketIds(symbols);
795
- const url = await this.negotiate(false);
796
- let method = undefined;
797
- [method, params] = this.handleOptionAndParams(params, 'watchOrderBook', 'method', '/market/level2');
798
- if ((limit === 5) || (limit === 50)) {
799
- method = '/spotMarket/level2Depth' + limit.toString();
1024
+ const firstMarket = this.getMarketFromSymbols(symbols);
1025
+ const isFuturesMethod = firstMarket['contract'];
1026
+ const url = await this.negotiate(false, isFuturesMethod);
1027
+ let method = isFuturesMethod ? '/contractMarket/level2' : '/market/level2';
1028
+ const optionName = isFuturesMethod ? 'contractMethod' : 'spotMethod';
1029
+ [method, params] = this.handleOptionAndParams2(params, 'watchOrderBook', optionName, 'method', method);
1030
+ if (method.indexOf('Depth') === -1) {
1031
+ if ((limit === 5) || (limit === 50)) {
1032
+ if (!isFuturesMethod) {
1033
+ method = '/spotMarket/level2';
1034
+ }
1035
+ method += 'Depth' + limit.toString();
1036
+ }
800
1037
  }
801
1038
  const topic = method + ':' + marketIds.join(',');
802
1039
  const messageHashes = [];
@@ -806,6 +1043,11 @@ class kucoin extends kucoin$1["default"] {
806
1043
  messageHashes.push('unsubscribe:orderbook:' + symbol);
807
1044
  subscriptionHashes.push('orderbook:' + symbol);
808
1045
  }
1046
+ // we have to add the topic to the messageHashes and subMessageHashes
1047
+ // because handleSubscriptionStatus needs them to remove the subscription from the client
1048
+ // without them subscription would never be removed and re-subscribe would fail because of duplicate subscriptionHash
1049
+ messageHashes.push(topic);
1050
+ subscriptionHashes.push(topic);
809
1051
  const subscription = {
810
1052
  'messageHashes': messageHashes,
811
1053
  'symbols': symbols,
@@ -856,8 +1098,7 @@ class kucoin extends kucoin$1["default"] {
856
1098
  // "subject": "level2"
857
1099
  // }
858
1100
  //
859
- const data = this.safeValue(message, 'data');
860
- const subject = this.safeString(message, 'subject');
1101
+ const data = this.safeDict(message, 'data');
861
1102
  const topic = this.safeString(message, 'topic');
862
1103
  const topicParts = topic.split(':');
863
1104
  const topicSymbol = this.safeString(topicParts, 1);
@@ -866,7 +1107,7 @@ class kucoin extends kucoin$1["default"] {
866
1107
  const symbol = this.safeSymbol(marketId, undefined, '-');
867
1108
  const messageHash = 'orderbook:' + symbol;
868
1109
  // let orderbook = this.safeDict (this.orderbooks, symbol);
869
- if (subject === 'level2') {
1110
+ if (topic.indexOf('Depth') >= 0) {
870
1111
  if (!(symbol in this.orderbooks)) {
871
1112
  this.orderbooks[symbol] = this.orderBook();
872
1113
  }
@@ -912,14 +1153,14 @@ class kucoin extends kucoin$1["default"] {
912
1153
  getCacheIndex(orderbook, cache) {
913
1154
  const firstDelta = this.safeValue(cache, 0);
914
1155
  const nonce = this.safeInteger(orderbook, 'nonce');
915
- const firstDeltaStart = this.safeInteger(firstDelta, 'sequenceStart');
1156
+ const firstDeltaStart = this.safeInteger2(firstDelta, 'sequenceStart', 'sequence');
916
1157
  if (nonce < firstDeltaStart - 1) {
917
1158
  return -1;
918
1159
  }
919
1160
  for (let i = 0; i < cache.length; i++) {
920
1161
  const delta = cache[i];
921
- const deltaStart = this.safeInteger(delta, 'sequenceStart');
922
- const deltaEnd = this.safeInteger(delta, 'sequenceEnd');
1162
+ const deltaStart = this.safeInteger2(delta, 'sequenceStart', 'sequence');
1163
+ const deltaEnd = this.safeInteger2(delta, 'sequenceEnd', 'timestamp'); // todo check
923
1164
  if ((nonce >= deltaStart - 1) && (nonce < deltaEnd)) {
924
1165
  return i;
925
1166
  }
@@ -928,16 +1169,34 @@ class kucoin extends kucoin$1["default"] {
928
1169
  }
929
1170
  handleDelta(orderbook, delta) {
930
1171
  const timestamp = this.safeInteger2(delta, 'time', 'timestamp');
931
- orderbook['nonce'] = this.safeInteger(delta, 'sequenceEnd', timestamp);
1172
+ orderbook['nonce'] = this.safeInteger2(delta, 'sequenceEnd', 'sequence', timestamp);
932
1173
  orderbook['timestamp'] = timestamp;
933
1174
  orderbook['datetime'] = this.iso8601(timestamp);
934
- const changes = this.safeValue(delta, 'changes', delta);
935
- const bids = this.safeValue(changes, 'bids', []);
936
- const asks = this.safeValue(changes, 'asks', []);
1175
+ const change = this.safeString(delta, 'change');
937
1176
  const storedBids = orderbook['bids'];
938
1177
  const storedAsks = orderbook['asks'];
939
- this.handleBidAsks(storedBids, bids);
940
- this.handleBidAsks(storedAsks, asks);
1178
+ if (change !== undefined) {
1179
+ // handling futures orderbook update
1180
+ const splitChange = change.split(',');
1181
+ const price = this.safeNumber(splitChange, 0);
1182
+ const side = this.safeString(splitChange, 1);
1183
+ const quantity = this.safeNumber(splitChange, 2);
1184
+ const type = (side === 'buy') ? 'bids' : 'asks';
1185
+ const value = [price, quantity];
1186
+ if (type === 'bids') {
1187
+ storedBids.storeArray(value);
1188
+ }
1189
+ else {
1190
+ storedAsks.storeArray(value);
1191
+ }
1192
+ }
1193
+ else {
1194
+ const changes = this.safeDict(delta, 'changes', delta);
1195
+ const bids = this.safeList(changes, 'bids', []);
1196
+ const asks = this.safeList(changes, 'asks', []);
1197
+ this.handleBidAsks(storedBids, bids);
1198
+ this.handleBidAsks(storedAsks, asks);
1199
+ }
941
1200
  }
942
1201
  handleBidAsks(bookSide, bidAsks) {
943
1202
  for (let i = 0; i < bidAsks.length; i++) {
@@ -947,7 +1206,7 @@ class kucoin extends kucoin$1["default"] {
947
1206
  }
948
1207
  handleOrderBookSubscription(client, message, subscription) {
949
1208
  const limit = this.safeInteger(subscription, 'limit');
950
- const symbols = this.safeValue(subscription, 'symbols');
1209
+ const symbols = this.safeList(subscription, 'symbols');
951
1210
  if (symbols === undefined) {
952
1211
  const symbol = this.safeString(subscription, 'symbol');
953
1212
  this.orderbooks[symbol] = this.orderBook({}, limit);
@@ -1010,36 +1269,63 @@ class kucoin extends kucoin$1["default"] {
1010
1269
  * @method
1011
1270
  * @name kucoin#watchOrders
1012
1271
  * @description watches information on multiple orders made by the user
1013
- * @see https://www.kucoin.com/docs/websocket/spot-trading/private-channels/private-order-change
1014
- * @see https://www.kucoin.com/docs/websocket/spot-trading/private-channels/stop-order-event
1272
+ * @see https://www.kucoin.com/docs-new/3470074w0 // spot regular orders
1273
+ * @see https://www.kucoin.com/docs-new/3470139w0 // spot trigger orders
1274
+ * @see https://www.kucoin.com/docs-new/3470090w0 // contract regular orders
1275
+ * @see https://www.kucoin.com/docs-new/3470091w0 // contract trigger orders
1015
1276
  * @param {string} symbol unified market symbol of the market orders were made in
1016
1277
  * @param {int} [since] the earliest time in ms to fetch orders for
1017
1278
  * @param {int} [limit] the maximum number of order structures to retrieve
1018
1279
  * @param {object} [params] extra parameters specific to the exchange API endpoint
1019
1280
  * @param {boolean} [params.trigger] trigger orders are watched if true
1281
+ * @param {string} [params.type] 'spot' or 'swap' (default is 'spot' if symbol is not provided)
1020
1282
  * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/?id=order-structure}
1021
1283
  */
1022
1284
  async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
1023
1285
  await this.loadMarkets();
1024
- const trigger = this.safeValue2(params, 'stop', 'trigger');
1286
+ const trigger = this.safeBool2(params, 'stop', 'trigger');
1025
1287
  params = this.omit(params, ['stop', 'trigger']);
1026
- const url = await this.negotiate(true);
1027
- const topic = trigger ? '/spotMarket/advancedOrders' : '/spotMarket/tradeOrders';
1028
- const request = {
1029
- 'privateChannel': true,
1030
- };
1288
+ let market = undefined;
1031
1289
  let messageHash = 'orders';
1032
1290
  if (symbol !== undefined) {
1033
- const market = this.market(symbol);
1291
+ market = this.market(symbol);
1034
1292
  symbol = market['symbol'];
1035
1293
  messageHash = messageHash + ':' + symbol;
1036
1294
  }
1295
+ let marketType = undefined;
1296
+ [marketType, params] = this.handleMarketTypeAndParams('watchOrders', market, params);
1297
+ const isFuturesMethod = ((marketType !== 'spot') && (marketType !== 'margin'));
1298
+ const url = await this.negotiate(true, isFuturesMethod);
1299
+ let topic = trigger ? '/spotMarket/advancedOrders' : '/spotMarket/tradeOrders';
1300
+ if (isFuturesMethod) {
1301
+ topic = trigger ? '/contractMarket/advancedOrders' : '/contractMarket/tradeOrders';
1302
+ }
1303
+ if (symbol === undefined) {
1304
+ const suffix = this.getOrdersMessageHashSuffix(topic);
1305
+ messageHash += suffix;
1306
+ }
1307
+ const request = {
1308
+ 'privateChannel': true,
1309
+ };
1037
1310
  const orders = await this.subscribe(url, messageHash, topic, this.extend(request, params));
1038
1311
  if (this.newUpdates) {
1039
1312
  limit = orders.getLimit(symbol, limit);
1040
1313
  }
1041
1314
  return this.filterBySymbolSinceLimit(orders, symbol, since, limit, true);
1042
1315
  }
1316
+ getOrdersMessageHashSuffix(topic) {
1317
+ let suffix = '-spot';
1318
+ if (topic === '/spotMarket/advancedOrders') {
1319
+ suffix += '-trigger';
1320
+ }
1321
+ else if (topic === '/contractMarket/tradeOrders') {
1322
+ suffix = '-contract';
1323
+ }
1324
+ else if (topic === '/contractMarket/advancedOrders') {
1325
+ suffix = '-contract-trigger';
1326
+ }
1327
+ return suffix;
1328
+ }
1043
1329
  parseWsOrderStatus(status) {
1044
1330
  const statuses = {
1045
1331
  'open': 'open',
@@ -1090,13 +1376,42 @@ class kucoin extends kucoin$1["default"] {
1090
1376
  // "type": "triggered"
1091
1377
  // }
1092
1378
  //
1379
+ // futures
1380
+ // {
1381
+ // "symbol": "ETHUSDTM",
1382
+ // "orderType": "market",
1383
+ // "side": "buy",
1384
+ // "canceledSize": "0",
1385
+ // "orderId": "416204113500479490",
1386
+ // "positionSide": "LONG",
1387
+ // "liquidity": "taker",
1388
+ // "marginMode": "ISOLATED",
1389
+ // "type": "match",
1390
+ // "feeType": "takerFee",
1391
+ // "orderTime": "1772043995356345762",
1392
+ // "size": "1",
1393
+ // "filledSize": "1",
1394
+ // "price": "0",
1395
+ // "matchPrice": "2068.55",
1396
+ // "matchSize": "1",
1397
+ // "remainSize": "0",
1398
+ // "tradeId": "1815302608109",
1399
+ // "clientOid": "9f7a2be0-effe-45bd-bdc8-1614715a583a",
1400
+ // "tradeType": "trade",
1401
+ // "status": "match",
1402
+ // "ts": 1772043995362000000
1403
+ // }
1404
+ //
1093
1405
  const rawType = this.safeString(order, 'type');
1094
1406
  let status = this.parseWsOrderStatus(rawType);
1095
- const timestamp = this.safeInteger2(order, 'orderTime', 'createdAt');
1407
+ let timestamp = this.safeInteger2(order, 'orderTime', 'createdAt');
1096
1408
  const marketId = this.safeString(order, 'symbol');
1097
1409
  market = this.safeMarket(marketId, market);
1410
+ if (market['contract']) {
1411
+ timestamp = this.safeIntegerProduct(order, 'orderTime', 0.000001);
1412
+ }
1098
1413
  const triggerPrice = this.safeString(order, 'stopPrice');
1099
- const triggerSuccess = this.safeValue(order, 'triggerSuccess');
1414
+ const triggerSuccess = this.safeBool(order, 'triggerSuccess');
1100
1415
  const triggerFail = (triggerSuccess !== true) && (triggerSuccess !== undefined); // TODO: updated to triggerSuccess === False once transpiler transpiles it correctly
1101
1416
  if ((status === 'triggered') && triggerFail) {
1102
1417
  status = 'canceled';
@@ -1147,8 +1462,7 @@ class kucoin extends kucoin$1["default"] {
1147
1462
  // "type": "open"
1148
1463
  // }
1149
1464
  //
1150
- const messageHash = 'orders';
1151
- const data = this.safeValue(message, 'data');
1465
+ const data = this.safeDict(message, 'data');
1152
1466
  const tradeId = this.safeString(data, 'tradeId');
1153
1467
  if (tradeId !== undefined) {
1154
1468
  this.handleMyTrade(client, message);
@@ -1156,7 +1470,7 @@ class kucoin extends kucoin$1["default"] {
1156
1470
  const parsed = this.parseWsOrder(data);
1157
1471
  const symbol = this.safeString(parsed, 'symbol');
1158
1472
  const orderId = this.safeString(parsed, 'id');
1159
- const triggerPrice = this.safeValue(parsed, 'triggerPrice');
1473
+ const triggerPrice = this.safeString(parsed, 'triggerPrice');
1160
1474
  const isTriggerOrder = (triggerPrice !== undefined);
1161
1475
  if (this.orders === undefined) {
1162
1476
  const limit = this.safeInteger(this.options, 'ordersLimit', 1000);
@@ -1173,42 +1487,63 @@ class kucoin extends kucoin$1["default"] {
1173
1487
  }
1174
1488
  }
1175
1489
  cachedOrders.append(parsed);
1176
- client.resolve(cachedOrders, messageHash);
1490
+ const messageHash = 'orders';
1491
+ const topic = this.safeString(message, 'topic');
1492
+ const suffix = this.getOrdersMessageHashSuffix(topic);
1493
+ const typeSpecificMessageHash = messageHash + suffix;
1494
+ client.resolve(cachedOrders, typeSpecificMessageHash);
1177
1495
  const symbolSpecificMessageHash = messageHash + ':' + symbol;
1178
1496
  client.resolve(cachedOrders, symbolSpecificMessageHash);
1179
1497
  }
1180
1498
  /**
1181
1499
  * @method
1182
1500
  * @name kucoin#watchMyTrades
1183
- * @description watches information on multiple trades made by the user
1184
- * @see https://www.kucoin.com/docs/websocket/spot-trading/private-channels/private-order-change
1501
+ * @description watches information on multiple trades made by the user on spot
1502
+ * @see https://www.kucoin.com/docs-new/3470074w0
1503
+ * @see https://www.kucoin.com/docs-new/3470090w0
1185
1504
  * @param {string} symbol unified market symbol of the market trades were made in
1186
1505
  * @param {int} [since] the earliest time in ms to fetch trades for
1187
1506
  * @param {int} [limit] the maximum number of trade structures to retrieve
1188
1507
  * @param {object} [params] extra parameters specific to the exchange API endpoint
1189
- * @param {string} [params.method] '/spotMarket/tradeOrders' or '/spot/tradeFills' default is '/spotMarket/tradeOrders'
1508
+ * @param {string} [params.method] '/spotMarket/tradeOrders' or '/spot/tradeFills' or '/contractMarket/tradeOrders', default is '/spotMarket/tradeOrders'
1190
1509
  * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/?id=trade-structure}
1191
1510
  */
1192
1511
  async watchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
1193
1512
  await this.loadMarkets();
1194
- const url = await this.negotiate(true);
1195
- let topic = undefined;
1196
- [topic, params] = this.handleOptionAndParams(params, 'watchMyTrades', 'method', '/spotMarket/tradeOrders');
1197
- const request = {
1198
- 'privateChannel': true,
1199
- };
1200
1513
  let messageHash = 'myTrades';
1514
+ let market = undefined;
1201
1515
  if (symbol !== undefined) {
1202
- const market = this.market(symbol);
1516
+ market = this.market(symbol);
1203
1517
  symbol = market['symbol'];
1204
1518
  messageHash = messageHash + ':' + market['symbol'];
1205
1519
  }
1520
+ let marketType = undefined;
1521
+ [marketType, params] = this.handleMarketTypeAndParams('watchMyTrades', market, params);
1522
+ const isFuturesMethod = ((marketType !== 'spot') && (marketType !== 'margin'));
1523
+ const url = await this.negotiate(true, isFuturesMethod);
1524
+ let topic = isFuturesMethod ? '/contractMarket/tradeOrders' : '/spotMarket/tradeOrders';
1525
+ const optionName = isFuturesMethod ? 'contractMethod' : 'spotMethod';
1526
+ [topic, params] = this.handleOptionAndParams2(params, 'watchMyTrades', optionName, 'method', topic);
1527
+ const request = {
1528
+ 'privateChannel': true,
1529
+ };
1530
+ if (symbol === undefined) {
1531
+ const suffix = this.getMyTradesMessageHashSuffix(topic);
1532
+ messageHash += suffix;
1533
+ }
1206
1534
  const trades = await this.subscribe(url, messageHash, topic, this.extend(request, params));
1207
1535
  if (this.newUpdates) {
1208
1536
  limit = trades.getLimit(symbol, limit);
1209
1537
  }
1210
1538
  return this.filterBySymbolSinceLimit(trades, symbol, since, limit, true);
1211
1539
  }
1540
+ getMyTradesMessageHashSuffix(topic) {
1541
+ let suffix = '-spot';
1542
+ if (topic.indexOf('contractMarket') >= 0) {
1543
+ suffix = '-contract';
1544
+ }
1545
+ return suffix;
1546
+ }
1212
1547
  handleMyTrade(client, message) {
1213
1548
  //
1214
1549
  // {
@@ -1247,7 +1582,10 @@ class kucoin extends kucoin$1["default"] {
1247
1582
  const myTrades = this.myTrades;
1248
1583
  myTrades.append(parsed);
1249
1584
  const messageHash = 'myTrades';
1250
- client.resolve(this.myTrades, messageHash);
1585
+ const topic = this.safeString(message, 'topic');
1586
+ const suffix = this.getMyTradesMessageHashSuffix(topic);
1587
+ const typeSpecificMessageHash = messageHash + suffix;
1588
+ client.resolve(this.myTrades, typeSpecificMessageHash);
1251
1589
  const symbolSpecificMessageHash = messageHash + ':' + parsed['symbol'];
1252
1590
  client.resolve(this.myTrades, symbolSpecificMessageHash);
1253
1591
  }
@@ -1334,19 +1672,74 @@ class kucoin extends kucoin$1["default"] {
1334
1672
  * @method
1335
1673
  * @name kucoin#watchBalance
1336
1674
  * @description watch balance and get the amount of funds available for trading or funds locked in orders
1337
- * @see https://www.kucoin.com/docs/websocket/spot-trading/private-channels/account-balance-change
1675
+ * @see https://www.kucoin.com/docs-new/3470075w0 // spot balance
1676
+ * @see https://www.kucoin.com/docs-new/3470092w0 // contract balance
1338
1677
  * @param {object} [params] extra parameters specific to the exchange API endpoint
1678
+ * @param {string} [params.type] 'spot' or 'swap' (default is 'spot')
1339
1679
  * @returns {object} a [balance structure]{@link https://docs.ccxt.com/?id=balance-structure}
1340
1680
  */
1341
1681
  async watchBalance(params = {}) {
1342
1682
  await this.loadMarkets();
1343
- const url = await this.negotiate(true);
1344
- const topic = '/account/balance';
1683
+ const defaultType = this.safeString(this.options, 'defaultType', 'spot');
1684
+ const type = this.safeString(params, 'type', defaultType);
1685
+ params = this.omit(params, 'type');
1686
+ const accountsByType = this.safeDict(this.options, 'accountsByType', {});
1687
+ const uniformType = this.safeString(accountsByType, type, type);
1688
+ const isFuturesMethod = (uniformType === 'contract');
1689
+ const url = await this.negotiate(true, isFuturesMethod);
1690
+ const client = this.client(url);
1691
+ this.setBalanceCache(client, uniformType);
1692
+ const options = this.safeDict(this.options, 'watchBalance');
1693
+ const fetchBalanceSnapshot = this.safeBool(options, 'fetchBalanceSnapshot', false);
1694
+ const awaitBalanceSnapshot = this.safeBool(options, 'awaitBalanceSnapshot', true);
1695
+ if (fetchBalanceSnapshot && awaitBalanceSnapshot) {
1696
+ await client.future(uniformType + ':fetchBalanceSnapshot');
1697
+ }
1698
+ const messageHash = uniformType + ':balance';
1699
+ const requestId = this.requestId().toString();
1700
+ const subscriptionHash = isFuturesMethod ? '/contractAccount/wallet' : '/account/balance';
1345
1701
  const request = {
1702
+ 'id': requestId,
1703
+ 'type': 'subscribe',
1704
+ 'topic': subscriptionHash,
1705
+ 'response': true,
1346
1706
  'privateChannel': true,
1347
1707
  };
1348
- const messageHash = 'balance';
1349
- return await this.subscribe(url, messageHash, topic, this.extend(request, params));
1708
+ const message = this.extend(request, params);
1709
+ if (!(subscriptionHash in client.subscriptions)) {
1710
+ client.subscriptions[requestId] = subscriptionHash;
1711
+ }
1712
+ return await this.watch(url, messageHash, message, type);
1713
+ }
1714
+ setBalanceCache(client, type) {
1715
+ if ((type in client.subscriptions) && (type in this.balance)) {
1716
+ return;
1717
+ }
1718
+ const options = this.safeDict(this.options, 'watchBalance');
1719
+ const fetchBalanceSnapshot = this.safeBool(options, 'fetchBalanceSnapshot', false);
1720
+ if (fetchBalanceSnapshot) {
1721
+ const messageHash = type + ':fetchBalanceSnapshot';
1722
+ if (!(messageHash in client.futures)) {
1723
+ client.future(messageHash);
1724
+ this.spawn(this.loadBalanceSnapshot, client, messageHash, type);
1725
+ }
1726
+ }
1727
+ else {
1728
+ this.balance[type] = {};
1729
+ }
1730
+ }
1731
+ async loadBalanceSnapshot(client, messageHash, type) {
1732
+ const params = {
1733
+ 'type': type,
1734
+ };
1735
+ const response = await this.fetchBalance(params);
1736
+ this.balance[type] = this.extend(response, this.safeValue(this.balance, type, {}));
1737
+ // don't remove the future from the .futures cache
1738
+ if (messageHash in client.futures) {
1739
+ const future = client.futures[messageHash];
1740
+ future.resolve();
1741
+ client.resolve(this.balance[type], type + ':balance');
1742
+ }
1350
1743
  }
1351
1744
  handleBalance(client, message) {
1352
1745
  //
@@ -1372,8 +1765,51 @@ class kucoin extends kucoin$1["default"] {
1372
1765
  // "total":"89"
1373
1766
  // }
1374
1767
  //
1375
- const data = this.safeValue(message, 'data', {});
1376
- const messageHash = 'balance';
1768
+ // futures
1769
+ // {
1770
+ // "id": "6375553193027a0001f6566f",
1771
+ // "type": "message",
1772
+ // "topic": "/contractAccount/wallet",
1773
+ // "userId": "613a896885d8660006151f01",
1774
+ // "channelType": "private",
1775
+ // "subject": "availableBalance.change",
1776
+ // "data": {
1777
+ // "currency": "USDT",
1778
+ // "holdBalance": "0.0000000000",
1779
+ // "availableBalance": "14.0350281903",
1780
+ // "timestamp": "1668633905657"
1781
+ // }
1782
+ // }
1783
+ //
1784
+ // {
1785
+ // "topic": "/contractAccount/wallet",
1786
+ // "type": "message",
1787
+ // "subject": "walletBalance.change",
1788
+ // "id": "699f586d4416a80001df3804",
1789
+ // "userId": "64f99aced178640001306e6e",
1790
+ // "channelType": "private",
1791
+ // "data": {
1792
+ // "crossPosMargin": "0",
1793
+ // "isolatedOrderMargin": "0",
1794
+ // "holdBalance": "0",
1795
+ // "equity": "49.50050236",
1796
+ // "version": "2874",
1797
+ // "availableBalance": "28.67180236",
1798
+ // "isolatedPosMargin": "20.7308",
1799
+ // "maxWithdrawAmount": "28.67180236",
1800
+ // "walletBalance": "49.40260236",
1801
+ // "isolatedFundingFeeMargin": "0",
1802
+ // "crossUnPnl": "0",
1803
+ // "totalCrossMargin": "28.67180236",
1804
+ // "currency": "USDT",
1805
+ // "isolatedUnPnl": "0.0979",
1806
+ // "availableMargin": "28.67180236",
1807
+ // "crossOrderMargin": "0",
1808
+ // "timestamp": "1772050541214"
1809
+ // }
1810
+ // }
1811
+ //
1812
+ const data = this.safeDict(message, 'data', {});
1377
1813
  const currencyId = this.safeString(data, 'currency');
1378
1814
  const relationEvent = this.safeString(data, 'relationEvent');
1379
1815
  let requestAccountType = undefined;
@@ -1381,27 +1817,210 @@ class kucoin extends kucoin$1["default"] {
1381
1817
  const relationEventParts = relationEvent.split('.');
1382
1818
  requestAccountType = this.safeString(relationEventParts, 0);
1383
1819
  }
1384
- const selectedType = this.safeString2(this.options, 'watchBalance', 'defaultType', 'trade'); // trade, main, margin or other
1385
- const accountsByType = this.safeValue(this.options, 'accountsByType');
1820
+ const topic = this.safeString(message, 'topic');
1821
+ if (topic === '/contractAccount/wallet') {
1822
+ requestAccountType = 'contract';
1823
+ }
1824
+ const accountsByType = this.safeDict(this.options, 'accountsByType');
1386
1825
  const uniformType = this.safeString(accountsByType, requestAccountType, 'trade');
1387
1826
  if (!(uniformType in this.balance)) {
1388
1827
  this.balance[uniformType] = {};
1389
1828
  }
1390
1829
  this.balance[uniformType]['info'] = data;
1391
- const timestamp = this.safeInteger(data, 'time');
1830
+ const timestamp = this.safeInteger2(data, 'time', 'timestamp');
1392
1831
  this.balance[uniformType]['timestamp'] = timestamp;
1393
1832
  this.balance[uniformType]['datetime'] = this.iso8601(timestamp);
1394
1833
  const code = this.safeCurrencyCode(currencyId);
1395
1834
  const account = this.account();
1396
- account['free'] = this.safeString(data, 'available');
1397
- account['used'] = this.safeString(data, 'hold');
1835
+ let used = this.safeString2(data, 'hold', 'holdBalance');
1836
+ const isolatedPosMargin = this.omitZero(this.safeString(data, 'isolatedPosMargin'));
1837
+ if (isolatedPosMargin !== undefined) {
1838
+ used = Precise["default"].stringAdd(used, isolatedPosMargin);
1839
+ }
1840
+ account['free'] = this.safeString2(data, 'available', 'availableBalance');
1841
+ account['used'] = used;
1398
1842
  account['total'] = this.safeString(data, 'total');
1399
1843
  this.balance[uniformType][code] = account;
1400
1844
  this.balance[uniformType] = this.safeBalance(this.balance[uniformType]);
1401
- if (uniformType === selectedType) {
1402
- client.resolve(this.balance[uniformType], messageHash);
1845
+ const messageHash = uniformType + ':balance';
1846
+ client.resolve(this.balance[uniformType], messageHash);
1847
+ }
1848
+ /**
1849
+ * @method
1850
+ * @name kucoin#watchPosition
1851
+ * @description watch open positions for a specific symbol
1852
+ * @see https://www.kucoin.com/docs-new/3470093w0
1853
+ * @param {string|undefined} symbol unified market symbol
1854
+ * @param {object} params extra parameters specific to the exchange API endpoint
1855
+ * @returns {object} a [position structure]{@link https://docs.ccxt.com/en/latest/manual.html#position-structure}
1856
+ */
1857
+ async watchPosition(symbol = undefined, params = {}) {
1858
+ if (symbol === undefined) {
1859
+ throw new errors.ArgumentsRequired(this.id + ' watchPosition() requires a symbol argument');
1860
+ }
1861
+ await this.loadMarkets();
1862
+ const url = await this.negotiate(true);
1863
+ const market = this.market(symbol);
1864
+ const topic = '/contract/position:' + market['id'];
1865
+ const request = {
1866
+ 'privateChannel': true,
1867
+ };
1868
+ const messageHash = 'position:' + market['symbol'];
1869
+ const client = this.client(url);
1870
+ this.setPositionCache(client, symbol);
1871
+ const fetchPositionSnapshot = this.handleOption('watchPosition', 'fetchPositionSnapshot', true);
1872
+ const awaitPositionSnapshot = this.handleOption('watchPosition', 'awaitPositionSnapshot', true);
1873
+ const currentPosition = this.getCurrentPosition(symbol);
1874
+ if (fetchPositionSnapshot && awaitPositionSnapshot && currentPosition === undefined) {
1875
+ const snapshot = await client.future('fetchPositionSnapshot:' + symbol);
1876
+ return snapshot;
1877
+ }
1878
+ return await this.subscribe(url, messageHash, topic, this.extend(request, params));
1879
+ }
1880
+ getCurrentPosition(symbol) {
1881
+ if (this.positions === undefined) {
1882
+ return undefined;
1883
+ }
1884
+ const cache = this.positions.hashmap;
1885
+ const symbolCache = this.safeValue(cache, symbol, {});
1886
+ const values = Object.values(symbolCache);
1887
+ return this.safeValue(values, 0);
1888
+ }
1889
+ setPositionCache(client, symbol) {
1890
+ const fetchPositionSnapshot = this.handleOption('watchPosition', 'fetchPositionSnapshot', false);
1891
+ if (fetchPositionSnapshot) {
1892
+ const messageHash = 'fetchPositionSnapshot:' + symbol;
1893
+ if (!(messageHash in client.futures)) {
1894
+ client.future(messageHash);
1895
+ this.spawn(this.loadPositionSnapshot, client, messageHash, symbol);
1896
+ }
1403
1897
  }
1404
1898
  }
1899
+ async loadPositionSnapshot(client, messageHash, symbol) {
1900
+ const position = await this.fetchPosition(symbol);
1901
+ this.positions = new Cache.ArrayCacheBySymbolById();
1902
+ const cache = this.positions;
1903
+ cache.append(position);
1904
+ // don't remove the future from the .futures cache
1905
+ if (messageHash in client.futures) {
1906
+ const future = client.futures[messageHash];
1907
+ future.resolve(cache);
1908
+ client.resolve(position, 'position:' + symbol);
1909
+ }
1910
+ }
1911
+ handlePosition(client, message) {
1912
+ //
1913
+ // Position Changes Caused Operations
1914
+ // {
1915
+ // "type": "message",
1916
+ // "userId": "5c32d69203aa676ce4b543c7", // Deprecated, will detele later
1917
+ // "channelType": "private",
1918
+ // "topic": "/contract/position:XBTUSDM",
1919
+ // "subject": "position.change",
1920
+ // "data": {
1921
+ // "realisedGrossPnl": 0E-8, //Accumulated realised profit and loss
1922
+ // "symbol": "XBTUSDM", //Symbol
1923
+ // "crossMode": false, //Cross mode or not
1924
+ // "liquidationPrice": 1000000.0, //Liquidation price
1925
+ // "posLoss": 0E-8, //Manually added margin amount
1926
+ // "avgEntryPrice": 7508.22, //Average entry price
1927
+ // "unrealisedPnl": -0.00014735, //Unrealised profit and loss
1928
+ // "markPrice": 7947.83, //Mark price
1929
+ // "posMargin": 0.00266779, //Position margin
1930
+ // "autoDeposit": false, //Auto deposit margin or not
1931
+ // "riskLimit": 100000, //Risk limit
1932
+ // "unrealisedCost": 0.00266375, //Unrealised value
1933
+ // "posComm": 0.00000392, //Bankruptcy cost
1934
+ // "posMaint": 0.00001724, //Maintenance margin
1935
+ // "posCost": 0.00266375, //Position value
1936
+ // "maintMarginReq": 0.005, //Maintenance margin rate
1937
+ // "bankruptPrice": 1000000.0, //Bankruptcy price
1938
+ // "realisedCost": 0.00000271, //Currently accumulated realised position value
1939
+ // "markValue": 0.00251640, //Mark value
1940
+ // "posInit": 0.00266375, //Position margin
1941
+ // "realisedPnl": -0.00000253, //Realised profit and losts
1942
+ // "maintMargin": 0.00252044, //Position margin
1943
+ // "realLeverage": 1.06, //Leverage of the order
1944
+ // "changeReason": "positionChange", //changeReason:marginChange、positionChange、liquidation、autoAppendMarginStatusChange、adl
1945
+ // "currentCost": 0.00266375, //Current position value
1946
+ // "openingTimestamp": 1558433191000, //Open time
1947
+ // "currentQty": -20, //Current position
1948
+ // "delevPercentage": 0.52, //ADL ranking percentile
1949
+ // "currentComm": 0.00000271, //Current commission
1950
+ // "realisedGrossCost": 0E-8, //Accumulated reliased gross profit value
1951
+ // "isOpen": true, //Opened position or not
1952
+ // "posCross": 1.2E-7, //Manually added margin
1953
+ // "currentTimestamp": 1558506060394, //Current timestamp
1954
+ // "unrealisedRoePcnt": -0.0553, //Rate of return on investment
1955
+ // "unrealisedPnlPcnt": -0.0553, //Position profit and loss ratio
1956
+ // "settleCurrency": "XBT" //Currency used to clear and settle the trades
1957
+ // }
1958
+ // }
1959
+ // Position Changes Caused by Mark Price
1960
+ // {
1961
+ // "userId": "5cd3f1a7b7ebc19ae9558591", // Deprecated, will detele later
1962
+ // "topic": "/contract/position:XBTUSDM",
1963
+ // "subject": "position.change",
1964
+ // "data": {
1965
+ // "markPrice": 7947.83, //Mark price
1966
+ // "markValue": 0.00251640, //Mark value
1967
+ // "maintMargin": 0.00252044, //Position margin
1968
+ // "realLeverage": 10.06, //Leverage of the order
1969
+ // "unrealisedPnl": -0.00014735, //Unrealised profit and lost
1970
+ // "unrealisedRoePcnt": -0.0553, //Rate of return on investment
1971
+ // "unrealisedPnlPcnt": -0.0553, //Position profit and loss ratio
1972
+ // "delevPercentage": 0.52, //ADL ranking percentile
1973
+ // "currentTimestamp": 1558087175068, //Current timestamp
1974
+ // "settleCurrency": "XBT" //Currency used to clear and settle the trades
1975
+ // }
1976
+ // }
1977
+ // Funding Settlement
1978
+ // {
1979
+ // "userId": "xbc453tg732eba53a88ggyt8c", // Deprecated, will detele later
1980
+ // "topic": "/contract/position:XBTUSDM",
1981
+ // "subject": "position.settlement",
1982
+ // "data": {
1983
+ // "fundingTime": 1551770400000, //Funding time
1984
+ // "qty": 100, //Position siz
1985
+ // "markPrice": 3610.85, //Settlement price
1986
+ // "fundingRate": -0.002966, //Funding rate
1987
+ // "fundingFee": -296, //Funding fees
1988
+ // "ts": 1547697294838004923, //Current time (nanosecond)
1989
+ // "settleCurrency": "XBT" //Currency used to clear and settle the trades
1990
+ // }
1991
+ // }
1992
+ // Adjustmet result of risk limit level
1993
+ // {
1994
+ // "userId": "xbc453tg732eba53a88ggyt8c",
1995
+ // "topic": "/contract/position:ADAUSDTM",
1996
+ // "subject": "position.adjustRiskLimit",
1997
+ // "data": {
1998
+ // "success": true, // Successful or not
1999
+ // "riskLimitLevel": 1, // Current risk limit level
2000
+ // "msg": "" // Failure reason
2001
+ // }
2002
+ // }
2003
+ //
2004
+ const topic = this.safeString(message, 'topic', '');
2005
+ const parts = topic.split(':');
2006
+ const marketId = this.safeString(parts, 1);
2007
+ const symbol = this.safeSymbol(marketId, undefined, '');
2008
+ const cache = this.positions;
2009
+ const currentPosition = this.getCurrentPosition(symbol);
2010
+ const messageHash = 'position:' + symbol;
2011
+ const data = this.safeDict(message, 'data', {});
2012
+ const newPosition = this.parsePosition(data);
2013
+ const keys = Object.keys(newPosition);
2014
+ for (let i = 0; i < keys.length; i++) {
2015
+ const key = keys[i];
2016
+ if (newPosition[key] === undefined) {
2017
+ delete newPosition[key];
2018
+ }
2019
+ }
2020
+ const position = this.extend(currentPosition, newPosition);
2021
+ cache.append(position);
2022
+ client.resolve(position, messageHash);
2023
+ }
1405
2024
  handleSubject(client, message) {
1406
2025
  //
1407
2026
  // {
@@ -1437,6 +2056,18 @@ class kucoin extends kucoin$1["default"] {
1437
2056
  'orderChange': this.handleOrder,
1438
2057
  'stopOrder': this.handleOrder,
1439
2058
  '/spot/tradeFills': this.handleMyTrade,
2059
+ // futures messages
2060
+ 'ticker': this.handleTicker,
2061
+ 'tickerV2': this.handleBidAsk,
2062
+ 'candle.stick': this.handleOHLCV,
2063
+ 'match': this.handleTrade,
2064
+ 'orderUpdated': this.handleOrder,
2065
+ 'symbolOrderChange': this.handleOrder,
2066
+ 'availableBalance.change': this.handleBalance,
2067
+ 'walletBalance.change': this.handleBalance,
2068
+ 'position.change': this.handlePosition,
2069
+ 'position.settlement': this.handlePosition,
2070
+ 'position.adjustRiskLimit': this.handlePosition,
1440
2071
  };
1441
2072
  const method = this.safeValue(methods, subject);
1442
2073
  if (method !== undefined) {
@@ -1492,6 +2123,16 @@ class kucoin extends kucoin$1["default"] {
1492
2123
  method.call(this, client, message);
1493
2124
  }
1494
2125
  }
2126
+ getMessageHash(elementName, symbol = undefined) {
2127
+ // method from kucoinfutures
2128
+ // elementName can be 'ticker', 'bidask', ...
2129
+ if (symbol !== undefined) {
2130
+ return elementName + ':' + symbol;
2131
+ }
2132
+ else {
2133
+ return elementName + 's@all';
2134
+ }
2135
+ }
1495
2136
  }
1496
2137
 
1497
2138
  exports["default"] = kucoin;