ccxt 4.1.7 → 4.1.9

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 (63) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/README.md +3 -3
  3. package/dist/ccxt.browser.js +1298 -202
  4. package/dist/ccxt.browser.min.js +7 -7
  5. package/dist/cjs/ccxt.js +3 -1
  6. package/dist/cjs/src/base/Exchange.js +58 -2
  7. package/dist/cjs/src/bingx.js +100 -39
  8. package/dist/cjs/src/bitbns.js +3 -3
  9. package/dist/cjs/src/bitget.js +26 -2
  10. package/dist/cjs/src/bitmart.js +2 -0
  11. package/dist/cjs/src/coinfalcon.js +3 -3
  12. package/dist/cjs/src/gate.js +9 -1
  13. package/dist/cjs/src/kucoin.js +2 -2
  14. package/dist/cjs/src/kucoinfutures.js +3 -3
  15. package/dist/cjs/src/mexc.js +46 -3
  16. package/dist/cjs/src/pro/bingx.js +891 -0
  17. package/dist/cjs/src/pro/exmo.js +1 -1
  18. package/js/ccxt.d.ts +6 -3
  19. package/js/ccxt.js +3 -1
  20. package/js/src/abstract/bingx.d.ts +1 -0
  21. package/js/src/abstract/bitmart.d.ts +1 -0
  22. package/js/src/abstract/mexc.d.ts +2 -0
  23. package/js/src/abstract/mexc3.d.ts +2 -0
  24. package/js/src/base/Exchange.d.ts +6 -3
  25. package/js/src/base/Exchange.js +58 -2
  26. package/js/src/base/types.d.ts +7 -0
  27. package/js/src/binance.d.ts +17 -17
  28. package/js/src/bingx.d.ts +4 -3
  29. package/js/src/bingx.js +100 -39
  30. package/js/src/bitbns.js +3 -3
  31. package/js/src/bitfinex2.d.ts +13 -13
  32. package/js/src/bitget.d.ts +15 -15
  33. package/js/src/bitget.js +26 -2
  34. package/js/src/bitmart.js +2 -0
  35. package/js/src/bitmex.d.ts +15 -15
  36. package/js/src/bybit.d.ts +23 -23
  37. package/js/src/coinbase.d.ts +16 -16
  38. package/js/src/coinbasepro.d.ts +12 -12
  39. package/js/src/coinex.d.ts +2 -2
  40. package/js/src/coinfalcon.js +3 -3
  41. package/js/src/cryptocom.d.ts +12 -12
  42. package/js/src/deribit.d.ts +2 -2
  43. package/js/src/digifinex.d.ts +2 -2
  44. package/js/src/gate.d.ts +10 -10
  45. package/js/src/gate.js +9 -1
  46. package/js/src/huobi.d.ts +16 -16
  47. package/js/src/kraken.d.ts +2 -2
  48. package/js/src/krakenfutures.d.ts +6 -6
  49. package/js/src/kucoin.d.ts +13 -13
  50. package/js/src/kucoin.js +2 -2
  51. package/js/src/kucoinfutures.d.ts +10 -10
  52. package/js/src/kucoinfutures.js +3 -3
  53. package/js/src/mexc.d.ts +3 -3
  54. package/js/src/mexc.js +46 -3
  55. package/js/src/okx.d.ts +13 -13
  56. package/js/src/phemex.d.ts +2 -2
  57. package/js/src/poloniex.d.ts +5 -5
  58. package/js/src/pro/bingx.d.ts +24 -0
  59. package/js/src/pro/bingx.js +892 -0
  60. package/js/src/pro/exmo.js +1 -1
  61. package/js/src/woo.d.ts +2 -2
  62. package/package.json +1 -1
  63. package/skip-tests.json +3 -0
@@ -0,0 +1,892 @@
1
+ // ----------------------------------------------------------------------------
2
+
3
+ // PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
4
+ // https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
5
+ // EDIT THE CORRESPONDENT .ts FILE INSTEAD
6
+
7
+ // ---------------------------------------------------------------------------
8
+ import bingxRest from '../bingx.js';
9
+ import { BadRequest } from '../base/errors.js';
10
+ import { ArrayCache, ArrayCacheByTimestamp, ArrayCacheBySymbolById } from '../base/ws/Cache.js';
11
+ // ---------------------------------------------------------------------------
12
+ export default class bingx extends bingxRest {
13
+ describe() {
14
+ return this.deepExtend(super.describe(), {
15
+ 'has': {
16
+ 'ws': true,
17
+ 'watchTrades': true,
18
+ 'watchOrderBook': true,
19
+ 'watchOHLCV': true,
20
+ 'watchOrders': true,
21
+ 'watchMyTrades': true,
22
+ 'watchTicker': false,
23
+ 'watchTickers': false,
24
+ 'watchBalance': true,
25
+ },
26
+ 'urls': {
27
+ 'api': {
28
+ 'ws': {
29
+ 'spot': 'wss://open-api-ws.bingx.com/market',
30
+ 'swap': 'wss://open-api-swap.bingx.com/swap-market',
31
+ },
32
+ },
33
+ },
34
+ 'options': {
35
+ 'ws': {
36
+ 'gunzip': true,
37
+ },
38
+ 'swap': {
39
+ 'timeframes': {
40
+ '1m': '1m',
41
+ '3m': '3m',
42
+ '5m': '5m',
43
+ '15m': '15m',
44
+ '30m': '30m',
45
+ '1h': '1h',
46
+ '2h': '2h',
47
+ '4h': '4h',
48
+ '6h': '6h',
49
+ '12h': '12h',
50
+ '1d': '1d',
51
+ '3d': '3d',
52
+ '1w': '1w',
53
+ '1M': '1M',
54
+ },
55
+ },
56
+ 'spot': {
57
+ 'timeframes': {
58
+ '1m': '1min',
59
+ '5m': '5min',
60
+ '15m': '15min',
61
+ '30m': '30min',
62
+ '1h': '60min',
63
+ '1d': '1day',
64
+ },
65
+ },
66
+ },
67
+ 'streaming': {
68
+ 'keepAlive': 1800000, // 30 minutes
69
+ },
70
+ });
71
+ }
72
+ async watchTrades(symbol, since = undefined, limit = undefined, params = {}) {
73
+ /**
74
+ * @method
75
+ * @name bingx#watchTrades
76
+ * @description watches information on multiple trades made in a market
77
+ * @see https://bingx-api.github.io/docs/#/spot/socket/market.html#Subscribe%20to%20tick-by-tick
78
+ * @see https://bingx-api.github.io/docs/#/swapV2/socket/market.html#Subscribe%20the%20Latest%20Trade%20Detail
79
+ * @param {string} symbol unified market symbol of the market orders were made in
80
+ * @param {int} [since] the earliest time in ms to fetch orders for
81
+ * @param {int} [limit] the maximum number of orde structures to retrieve
82
+ * @param {object} [params] extra parameters specific to the bingx api endpoint
83
+ * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure
84
+ */
85
+ await this.loadMarkets();
86
+ const market = this.market(symbol);
87
+ const [marketType, query] = this.handleMarketTypeAndParams('watchTrades', market, params);
88
+ const url = this.safeValue(this.urls['api']['ws'], marketType);
89
+ if (url === undefined) {
90
+ throw new BadRequest(this.id + ' watchTrades is not supported for ' + marketType + ' markets.');
91
+ }
92
+ const messageHash = market['id'] + '@trade';
93
+ const uuid = this.uuid();
94
+ const request = {
95
+ 'id': uuid,
96
+ 'dataType': messageHash,
97
+ };
98
+ if (marketType === 'swap') {
99
+ request['reqType'] = 'sub';
100
+ }
101
+ const trades = await this.watch(url, messageHash, this.extend(request, query), messageHash);
102
+ if (this.newUpdates) {
103
+ limit = trades.getLimit(symbol, limit);
104
+ }
105
+ return this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
106
+ }
107
+ handleTrades(client, message) {
108
+ //
109
+ // spot
110
+ // first snapshot
111
+ //
112
+ // {
113
+ // id: 'd83b78ce-98be-4dc2-b847-12fe471b5bc5',
114
+ // code: 0,
115
+ // msg: 'SUCCESS',
116
+ // timestamp: 1690214699854
117
+ // }
118
+ //
119
+ // subsequent updates
120
+ //
121
+ // {
122
+ // code: 0,
123
+ // data: {
124
+ // E: 1690214529432,
125
+ // T: 1690214529386,
126
+ // e: 'trade',
127
+ // m: true,
128
+ // p: '29110.19',
129
+ // q: '0.1868',
130
+ // s: 'BTC-USDT',
131
+ // t: '57903921'
132
+ // },
133
+ // dataType: 'BTC-USDT@trade',
134
+ // success: true
135
+ // }
136
+ //
137
+ //
138
+ // swap
139
+ // first snapshot
140
+ //
141
+ // {
142
+ // id: '2aed93b1-6e1e-4038-aeba-f5eeaec2ca48',
143
+ // code: 0,
144
+ // msg: '',
145
+ // dataType: '',
146
+ // data: null
147
+ // }
148
+ //
149
+ // subsequent updates
150
+ //
151
+ //
152
+ // {
153
+ // code: 0,
154
+ // dataType: 'BTC-USDT@trade',
155
+ // data: [
156
+ // {
157
+ // q: '0.0421',
158
+ // p: '29023.5',
159
+ // T: 1690221401344,
160
+ // m: false,
161
+ // s: 'BTC-USDT'
162
+ // },
163
+ // ...
164
+ // ]
165
+ // }
166
+ //
167
+ const data = this.safeValue(message, 'data', []);
168
+ const messageHash = this.safeString(message, 'dataType');
169
+ const marketId = messageHash.split('@')[0];
170
+ const marketType = client.url.indexOf('swap') >= 0 ? 'swap' : 'spot';
171
+ const market = this.safeMarket(marketId, undefined, undefined, marketType);
172
+ const symbol = market['symbol'];
173
+ let trades = undefined;
174
+ if (Array.isArray(data)) {
175
+ trades = this.parseTrades(data, market);
176
+ }
177
+ else {
178
+ trades = [this.parseTrade(data, market)];
179
+ }
180
+ let stored = this.safeValue(this.trades, symbol);
181
+ if (stored === undefined) {
182
+ const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
183
+ stored = new ArrayCache(limit);
184
+ this.trades[symbol] = stored;
185
+ }
186
+ for (let j = 0; j < trades.length; j++) {
187
+ stored.append(trades[j]);
188
+ }
189
+ client.resolve(stored, messageHash);
190
+ }
191
+ async watchOrderBook(symbol, limit = undefined, params = {}) {
192
+ /**
193
+ * @method
194
+ * @name bingx#watchOrderBook
195
+ * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
196
+ * @see https://bingx-api.github.io/docs/#/spot/socket/market.html#Subscribe%20Market%20Depth%20Data
197
+ * @see https://bingx-api.github.io/docs/#/swapV2/socket/market.html#Subscribe%20Market%20Depth%20Data
198
+ * @param {string} symbol unified symbol of the market to fetch the order book for
199
+ * @param {int} [limit] the maximum amount of order book entries to return
200
+ * @param {object} [params] extra parameters specific to the bingx api endpoint
201
+ * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
202
+ */
203
+ await this.loadMarkets();
204
+ const market = this.market(symbol);
205
+ const [marketType, query] = this.handleMarketTypeAndParams('watchOrderBook', market, params);
206
+ if (limit === undefined) {
207
+ limit = 100;
208
+ }
209
+ else {
210
+ if (marketType === 'swap') {
211
+ if ((limit !== 5) && (limit !== 10) && (limit !== 20) && (limit !== 50) && (limit !== 100)) {
212
+ throw new BadRequest(this.id + ' watchOrderBook() (swap) only supports limit 5, 10, 20, 50, and 100');
213
+ }
214
+ }
215
+ else if (marketType === 'spot') {
216
+ if ((limit !== 20) && (limit !== 100)) {
217
+ throw new BadRequest(this.id + ' watchOrderBook() (spot) only supports limit 20, and 100');
218
+ }
219
+ }
220
+ }
221
+ const url = this.safeValue(this.urls['api']['ws'], marketType);
222
+ if (url === undefined) {
223
+ throw new BadRequest(this.id + ' watchOrderBook is not supported for ' + marketType + ' markets.');
224
+ }
225
+ const messageHash = market['id'] + '@depth' + limit.toString();
226
+ const uuid = this.uuid();
227
+ const request = {
228
+ 'id': uuid,
229
+ 'dataType': messageHash,
230
+ };
231
+ if (marketType === 'swap') {
232
+ request['reqType'] = 'sub';
233
+ }
234
+ const orderbook = await this.watch(url, messageHash, this.deepExtend(request, query), messageHash);
235
+ return orderbook.limit();
236
+ }
237
+ handleDelta(bookside, delta) {
238
+ const price = this.safeFloat(delta, 0);
239
+ const amount = this.safeFloat(delta, 1);
240
+ bookside.store(price, amount);
241
+ }
242
+ handleOrderBook(client, message) {
243
+ //
244
+ // spot
245
+ //
246
+ //
247
+ // {
248
+ // code: 0,
249
+ // dataType: 'BTC-USDT@depth20',
250
+ // data: {
251
+ // bids: [
252
+ // [ '28852.9', '34.2621' ],
253
+ // ...
254
+ // ],
255
+ // asks: [
256
+ // [ '28864.9', '23.4079' ],
257
+ // ...
258
+ // ]
259
+ // },
260
+ // dataType: 'BTC-USDT@depth20',
261
+ // success: true
262
+ // }
263
+ //
264
+ // swap
265
+ //
266
+ //
267
+ // {
268
+ // code: 0,
269
+ // dataType: 'BTC-USDT@depth20',
270
+ // data: {
271
+ // bids: [
272
+ // [ '28852.9', '34.2621' ],
273
+ // ...
274
+ // ],
275
+ // asks: [
276
+ // [ '28864.9', '23.4079' ],
277
+ // ...
278
+ // ]
279
+ // }
280
+ // }
281
+ //
282
+ const data = this.safeValue(message, 'data', []);
283
+ const messageHash = this.safeString(message, 'dataType');
284
+ const marketId = messageHash.split('@')[0];
285
+ const marketType = client.url.indexOf('swap') >= 0 ? 'swap' : 'spot';
286
+ const market = this.safeMarket(marketId, undefined, undefined, marketType);
287
+ const symbol = market['symbol'];
288
+ let orderbook = this.safeValue(this.orderbooks, symbol);
289
+ if (orderbook === undefined) {
290
+ orderbook = this.orderBook();
291
+ }
292
+ const snapshot = this.parseOrderBook(data, symbol, undefined, 'bids', 'asks', 0, 1);
293
+ orderbook.reset(snapshot);
294
+ this.orderbooks[symbol] = orderbook;
295
+ client.resolve(orderbook, messageHash);
296
+ }
297
+ parseWsOHLCV(ohlcv, market = undefined) {
298
+ //
299
+ // {
300
+ // c: '28909.0',
301
+ // o: '28915.4',
302
+ // h: '28915.4',
303
+ // l: '28896.1',
304
+ // v: '27.6919',
305
+ // T: 1690907580000
306
+ // }
307
+ //
308
+ return [
309
+ this.safeInteger(ohlcv, 'T'),
310
+ this.safeNumber(ohlcv, 'o'),
311
+ this.safeNumber(ohlcv, 'h'),
312
+ this.safeNumber(ohlcv, 'l'),
313
+ this.safeNumber(ohlcv, 'c'),
314
+ this.safeNumber(ohlcv, 'v'),
315
+ ];
316
+ }
317
+ handleOHLCV(client, message) {
318
+ //
319
+ // spot
320
+ //
321
+ // {
322
+ // code: 0,
323
+ // data: {
324
+ // E: 1696687498608,
325
+ // K: {
326
+ // T: 1696687499999,
327
+ // c: '27917.829',
328
+ // h: '27918.427',
329
+ // i: '1min',
330
+ // l: '27917.7',
331
+ // n: 262,
332
+ // o: '27917.91',
333
+ // q: '25715.359197',
334
+ // s: 'BTC-USDT',
335
+ // t: 1696687440000,
336
+ // v: '0.921100'
337
+ // },
338
+ // e: 'kline',
339
+ // s: 'BTC-USDT'
340
+ // },
341
+ // dataType: 'BTC-USDT@kline_1min',
342
+ // success: true
343
+ // }
344
+ //
345
+ // swap
346
+ // {
347
+ // code: 0,
348
+ // dataType: 'BTC-USDT@kline_1m',
349
+ // s: 'BTC-USDT',
350
+ // data: [
351
+ // {
352
+ // c: '28909.0',
353
+ // o: '28915.4',
354
+ // h: '28915.4',
355
+ // l: '28896.1',
356
+ // v: '27.6919',
357
+ // T: 1690907580000
358
+ // }
359
+ // ]
360
+ // }
361
+ //
362
+ const data = this.safeValue(message, 'data', []);
363
+ let candles = undefined;
364
+ if (Array.isArray(data)) {
365
+ candles = data;
366
+ }
367
+ else {
368
+ candles = [this.safeValue(data, 'K', [])];
369
+ }
370
+ const messageHash = this.safeString(message, 'dataType');
371
+ const timeframeId = messageHash.split('_')[1];
372
+ const marketId = messageHash.split('@')[0];
373
+ const marketType = client.url.indexOf('swap') >= 0 ? 'swap' : 'spot';
374
+ const market = this.safeMarket(marketId, undefined, undefined, marketType);
375
+ const symbol = market['symbol'];
376
+ this.ohlcvs[symbol] = this.safeValue(this.ohlcvs, symbol, {});
377
+ let stored = this.safeValue(this.ohlcvs[symbol], timeframeId);
378
+ if (stored === undefined) {
379
+ const limit = this.safeInteger(this.options, 'OHLCVLimit', 1000);
380
+ stored = new ArrayCacheByTimestamp(limit);
381
+ this.ohlcvs[symbol][timeframeId] = stored;
382
+ }
383
+ for (let i = 0; i < candles.length; i++) {
384
+ const candle = candles[i];
385
+ const parsed = this.parseWsOHLCV(candle, market);
386
+ stored.append(parsed);
387
+ }
388
+ client.resolve(stored, messageHash);
389
+ }
390
+ async watchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
391
+ /**
392
+ * @method
393
+ * @name bingx#watchOHLCV
394
+ * @description watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
395
+ * @see https://bingx-api.github.io/docs/#/spot/socket/market.html#K%E7%BA%BF%20Streams
396
+ * @see https://bingx-api.github.io/docs/#/swapV2/socket/market.html#Subscribe%20K-Line%20Data
397
+ * @param {string} symbol unified symbol of the market to fetch OHLCV data for
398
+ * @param {string} timeframe the length of time each candle represents
399
+ * @param {int} [since] timestamp in ms of the earliest candle to fetch
400
+ * @param {int} [limit] the maximum amount of candles to fetch
401
+ * @param {object} [params] extra parameters specific to the bingx api endpoint
402
+ * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
403
+ */
404
+ const market = this.market(symbol);
405
+ const [marketType, query] = this.handleMarketTypeAndParams('watchOHLCV', market, params);
406
+ const url = this.safeValue(this.urls['api']['ws'], marketType);
407
+ if (url === undefined) {
408
+ throw new BadRequest(this.id + ' watchOHLCV is not supported for ' + marketType + ' markets.');
409
+ }
410
+ const options = this.safeValue(this.options, marketType, {});
411
+ const timeframes = this.safeValue(options, 'timeframes', {});
412
+ const interval = this.safeString(timeframes, timeframe, timeframe);
413
+ const messageHash = market['id'] + '@kline_' + interval;
414
+ const uuid = this.uuid();
415
+ const request = {
416
+ 'id': uuid,
417
+ 'dataType': messageHash,
418
+ };
419
+ if (marketType === 'swap') {
420
+ request['reqType'] = 'sub';
421
+ }
422
+ const ohlcv = await this.watch(url, messageHash, this.extend(request, query), messageHash);
423
+ if (this.newUpdates) {
424
+ limit = ohlcv.getLimit(symbol, limit);
425
+ }
426
+ return this.filterBySinceLimit(ohlcv, since, limit, 0, true);
427
+ }
428
+ async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
429
+ /**
430
+ * @method
431
+ * @name bingx#watchOrders
432
+ * @see https://bingx-api.github.io/docs/#/spot/socket/account.html#Subscription%20order%20update%20data
433
+ * @see https://bingx-api.github.io/docs/#/swapV2/socket/account.html#Account%20balance%20and%20position%20update%20push
434
+ * @description watches information on multiple orders made by the user
435
+ * @param {string} symbol unified market symbol of the market orders were made in
436
+ * @param {int} [since] the earliest time in ms to fetch orders for
437
+ * @param {int} [limit] the maximum number of orde structures to retrieve
438
+ * @param {object} [params] extra parameters specific to the bingx api endpoint
439
+ * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
440
+ */
441
+ await this.loadMarkets();
442
+ await this.authenticate();
443
+ let type = undefined;
444
+ let market = undefined;
445
+ if (symbol !== undefined) {
446
+ market = this.market(symbol);
447
+ symbol = market['symbol'];
448
+ }
449
+ [type, params] = this.handleMarketTypeAndParams('watchOrders', market, params);
450
+ const isSpot = (type === 'spot');
451
+ const spotHash = 'spot:private';
452
+ const swapHash = 'swap:private';
453
+ const subscriptionHash = isSpot ? spotHash : swapHash;
454
+ const spotMessageHash = 'spot:order';
455
+ const swapMessageHash = 'swap:order';
456
+ let messageHash = isSpot ? spotMessageHash : swapMessageHash;
457
+ if (market !== undefined) {
458
+ messageHash += ':' + symbol;
459
+ }
460
+ const url = this.urls['api']['ws'][type] + '?listenKey=' + this.options['listenKey'];
461
+ let request = undefined;
462
+ const uuid = this.uuid();
463
+ if (isSpot) {
464
+ request = {
465
+ 'id': uuid,
466
+ 'dataType': 'spot.executionReport',
467
+ };
468
+ }
469
+ const orders = await this.watch(url, messageHash, request, subscriptionHash);
470
+ if (this.newUpdates) {
471
+ limit = orders.getLimit(symbol, limit);
472
+ }
473
+ return this.filterBySymbolSinceLimit(orders, symbol, since, limit, true);
474
+ }
475
+ async watchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
476
+ /**
477
+ * @method
478
+ * @name bingx#watchMyTrades
479
+ * @see https://bingx-api.github.io/docs/#/spot/socket/account.html#Subscription%20order%20update%20data
480
+ * @see https://bingx-api.github.io/docs/#/swapV2/socket/account.html#Account%20balance%20and%20position%20update%20push
481
+ * @description watches information on multiple trades made by the user
482
+ * @param {string} symbol unified market symbol of the market trades were made in
483
+ * @param {int} [since] the earliest time in ms to trades orders for
484
+ * @param {int} [limit] the maximum number of trades structures to retrieve
485
+ * @param {object} [params] extra parameters specific to the bingx api endpoint
486
+ * @returns {object[]} a list of [trade structures]{@link https://github.com/ccxt/ccxt/wiki/Manual#trade-structure
487
+ */
488
+ await this.loadMarkets();
489
+ await this.authenticate();
490
+ let type = undefined;
491
+ let market = undefined;
492
+ if (symbol !== undefined) {
493
+ market = this.market(symbol);
494
+ symbol = market['symbol'];
495
+ }
496
+ [type, params] = this.handleMarketTypeAndParams('watchOrders', market, params);
497
+ const isSpot = (type === 'spot');
498
+ const spotSubHash = 'spot:private';
499
+ const swapSubHash = 'swap:private';
500
+ const subscriptionHash = isSpot ? spotSubHash : swapSubHash;
501
+ const spotMessageHash = 'spot:mytrades';
502
+ const swapMessageHash = 'swap:mytrades';
503
+ let messageHash = isSpot ? spotMessageHash : swapMessageHash;
504
+ if (market !== undefined) {
505
+ messageHash += ':' + symbol;
506
+ }
507
+ const url = this.urls['api']['ws'][type] + '?listenKey=' + this.options['listenKey'];
508
+ let request = undefined;
509
+ const uuid = this.uuid();
510
+ if (isSpot) {
511
+ request = {
512
+ 'id': uuid,
513
+ 'dataType': 'spot.executionReport',
514
+ };
515
+ }
516
+ const trades = await this.watch(url, messageHash, request, subscriptionHash);
517
+ if (this.newUpdates) {
518
+ limit = trades.getLimit(symbol, limit);
519
+ }
520
+ return this.filterBySymbolSinceLimit(trades, symbol, since, limit, true);
521
+ }
522
+ async watchBalance(params = {}) {
523
+ /**
524
+ * @method
525
+ * @name bingx#watchBalance
526
+ * @see https://bingx-api.github.io/docs/#/spot/socket/account.html#Subscription%20order%20update%20data
527
+ * @see https://bingx-api.github.io/docs/#/swapV2/socket/account.html#Account%20balance%20and%20position%20update%20push
528
+ * @description query for balance and get the amount of funds available for trading or funds locked in orders
529
+ * @param {object} [params] extra parameters specific to the bingx api endpoint
530
+ * @returns {object} a [balance structure]{@link https://docs.ccxt.com/en/latest/manual.html?#balance-structure}
531
+ */
532
+ await this.loadMarkets();
533
+ await this.authenticate();
534
+ let type = undefined;
535
+ [type, params] = this.handleMarketTypeAndParams('watchBalance', undefined, params);
536
+ const isSpot = (type === 'spot');
537
+ const spotSubHash = 'spot:balance';
538
+ const swapSubHash = 'swap:private';
539
+ const spotMessageHash = 'spot:balance';
540
+ const swapMessageHash = 'swap:balance';
541
+ const messageHash = isSpot ? spotMessageHash : swapMessageHash;
542
+ const subscriptionHash = isSpot ? spotSubHash : swapSubHash;
543
+ const url = this.urls['api']['ws'][type] + '?listenKey=' + this.options['listenKey'];
544
+ let request = undefined;
545
+ const uuid = this.uuid();
546
+ if (type === 'spot') {
547
+ request = {
548
+ 'id': uuid,
549
+ 'dataType': 'ACCOUNT_UPDATE',
550
+ };
551
+ }
552
+ return await this.watch(url, messageHash, request, subscriptionHash);
553
+ }
554
+ handleErrorMessage(client, message) {
555
+ //
556
+ // { code: 100400, msg: '', timestamp: 1696245808833 }
557
+ //
558
+ // {
559
+ // code: 100500,
560
+ // id: '9cd37d32-da98-440b-bd04-37e7dbcf51ad',
561
+ // msg: '',
562
+ // timestamp: 1696245842307
563
+ // }
564
+ const code = this.safeString(message, 'code');
565
+ try {
566
+ if (code !== undefined) {
567
+ const feedback = this.id + ' ' + this.json(message);
568
+ this.throwExactlyMatchedException(this.exceptions['exact'], code, feedback);
569
+ }
570
+ }
571
+ catch (e) {
572
+ client.reject(e);
573
+ }
574
+ return true;
575
+ }
576
+ async authenticate(params = {}) {
577
+ const time = this.milliseconds();
578
+ const listenKey = this.safeString(this.options, 'listenKey');
579
+ if (listenKey === undefined) {
580
+ const response = await this.userAuthPrivatePostUserDataStream();
581
+ this.options['listenKey'] = this.safeString(response, 'listenKey');
582
+ this.options['lastAuthenticatedTime'] = time;
583
+ return;
584
+ }
585
+ const lastAuthenticatedTime = this.safeInteger(this.options, 'lastAuthenticatedTime', 0);
586
+ const listenKeyRefreshRate = this.safeInteger(this.options, 'listenKeyRefreshRate', 3600000); // 1 hour
587
+ if (time - lastAuthenticatedTime > listenKeyRefreshRate) {
588
+ const response = await this.userAuthPrivatePostUserDataStream({ 'listenKey': listenKey }); // extend the expiry
589
+ this.options['listenKey'] = this.safeString(response, 'listenKey');
590
+ this.options['lastAuthenticatedTime'] = time;
591
+ }
592
+ }
593
+ async pong(client, message) {
594
+ //
595
+ // spot
596
+ // {
597
+ // ping: '5963ba3db76049b2870f9a686b2ebaac',
598
+ // time: '2023-10-02T18:51:55.089+0800'
599
+ // }
600
+ // swap
601
+ // Ping
602
+ //
603
+ if (message === 'Ping') {
604
+ await client.send('Pong');
605
+ }
606
+ else {
607
+ const ping = this.safeString(message, 'ping');
608
+ const time = this.safeString(message, 'time');
609
+ await client.send({
610
+ 'pong': ping,
611
+ 'time': time,
612
+ });
613
+ }
614
+ }
615
+ handleOrder(client, message) {
616
+ //
617
+ // {
618
+ // "code": 0,
619
+ // "dataType": "spot.executionReport",
620
+ // "data": {
621
+ // "e": "executionReport",
622
+ // "E": 1694680212947,
623
+ // "s": "LTC-USDT",
624
+ // "S": "BUY",
625
+ // "o": "LIMIT",
626
+ // "q": 0.1,
627
+ // "p": 50,
628
+ // "x": "NEW",
629
+ // "X": "PENDING",
630
+ // "i": 1702238305204043800,
631
+ // "l": 0,
632
+ // "z": 0,
633
+ // "L": 0,
634
+ // "n": 0,
635
+ // "N": "",
636
+ // "T": 0,
637
+ // "t": 0,
638
+ // "O": 1694680212676,
639
+ // "Z": 0,
640
+ // "Y": 0,
641
+ // "Q": 0,
642
+ // "m": false
643
+ // }
644
+ // }
645
+ //
646
+ // {
647
+ // code: 0,
648
+ // dataType: 'spot.executionReport',
649
+ // data: {
650
+ // e: 'executionReport',
651
+ // E: 1694681809302,
652
+ // s: 'LTC-USDT',
653
+ // S: 'BUY',
654
+ // o: 'MARKET',
655
+ // q: 0,
656
+ // p: 62.29,
657
+ // x: 'TRADE',
658
+ // X: 'FILLED',
659
+ // i: '1702245001712369664',
660
+ // l: 0.0802,
661
+ // z: 0.0802,
662
+ // L: 62.308,
663
+ // n: -0.0000802,
664
+ // N: 'LTC',
665
+ // T: 1694681809256,
666
+ // t: 38259147,
667
+ // O: 1694681809248,
668
+ // Z: 4.9971016,
669
+ // Y: 4.9971016,
670
+ // Q: 5,
671
+ // m: false
672
+ // }
673
+ // }
674
+ // swap
675
+ // {
676
+ // "e": "ORDER_TRADE_UPDATE",
677
+ // "E": 1696843635475,
678
+ // "o": {
679
+ // "s": "LTC-USDT",
680
+ // "c": "",
681
+ // "i": "1711312357852147712",
682
+ // "S": "BUY",
683
+ // "o": "MARKET",
684
+ // "q": "0.10000000",
685
+ // "p": "64.35010000",
686
+ // "ap": "64.36000000",
687
+ // "x": "TRADE",
688
+ // "X": "FILLED",
689
+ // "N": "USDT",
690
+ // "n": "-0.00321800",
691
+ // "T": 0,
692
+ // "wt": "MARK_PRICE",
693
+ // "ps": "LONG",
694
+ // "rp": "0.00000000",
695
+ // "z": "0.10000000"
696
+ // }
697
+ // }
698
+ //
699
+ const isSpot = ('dataType' in message);
700
+ const data = this.safeValue2(message, 'data', 'o', {});
701
+ if (this.orders === undefined) {
702
+ const limit = this.safeInteger(this.options, 'ordersLimit', 1000);
703
+ this.orders = new ArrayCacheBySymbolById(limit);
704
+ }
705
+ const stored = this.orders;
706
+ const parsedOrder = this.parseOrder(data);
707
+ stored.append(parsedOrder);
708
+ const symbol = parsedOrder['symbol'];
709
+ const spotHash = 'spot:order';
710
+ const swapHash = 'swap:order';
711
+ const messageHash = (isSpot) ? spotHash : swapHash;
712
+ client.resolve(stored, messageHash);
713
+ client.resolve(stored, messageHash + ':' + symbol);
714
+ }
715
+ handleMyTrades(client, message) {
716
+ //
717
+ //
718
+ // {
719
+ // code: 0,
720
+ // dataType: 'spot.executionReport',
721
+ // data: {
722
+ // e: 'executionReport',
723
+ // E: 1694681809302,
724
+ // s: 'LTC-USDT',
725
+ // S: 'BUY',
726
+ // o: 'MARKET',
727
+ // q: 0,
728
+ // p: 62.29,
729
+ // x: 'TRADE',
730
+ // X: 'FILLED',
731
+ // i: '1702245001712369664',
732
+ // l: 0.0802,
733
+ // z: 0.0802,
734
+ // L: 62.308,
735
+ // n: -0.0000802,
736
+ // N: 'LTC',
737
+ // T: 1694681809256,
738
+ // t: 38259147,
739
+ // O: 1694681809248,
740
+ // Z: 4.9971016,
741
+ // Y: 4.9971016,
742
+ // Q: 5,
743
+ // m: false
744
+ // }
745
+ // }
746
+ //
747
+ // swap
748
+ // {
749
+ // "e": "ORDER_TRADE_UPDATE",
750
+ // "E": 1696843635475,
751
+ // "o": {
752
+ // "s": "LTC-USDT",
753
+ // "c": "",
754
+ // "i": "1711312357852147712",
755
+ // "S": "BUY",
756
+ // "o": "MARKET",
757
+ // "q": "0.10000000",
758
+ // "p": "64.35010000",
759
+ // "ap": "64.36000000",
760
+ // "x": "TRADE",
761
+ // "X": "FILLED",
762
+ // "N": "USDT",
763
+ // "n": "-0.00321800",
764
+ // "T": 0,
765
+ // "wt": "MARK_PRICE",
766
+ // "ps": "LONG",
767
+ // "rp": "0.00000000",
768
+ // "z": "0.10000000"
769
+ // }
770
+ // }
771
+ //
772
+ const isSpot = ('dataType' in message);
773
+ const result = this.safeValue2(message, 'data', 'o', {});
774
+ let cachedTrades = this.myTrades;
775
+ if (cachedTrades === undefined) {
776
+ const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
777
+ cachedTrades = new ArrayCacheBySymbolById(limit);
778
+ this.myTrades = cachedTrades;
779
+ }
780
+ const parsed = this.parseTrade(result);
781
+ const symbol = parsed['symbol'];
782
+ const spotHash = 'spot:mytrades';
783
+ const swapHash = 'swap:mytrades';
784
+ const messageHash = isSpot ? spotHash : swapHash;
785
+ cachedTrades.append(parsed);
786
+ client.resolve(cachedTrades, messageHash);
787
+ client.resolve(cachedTrades, messageHash + ':' + symbol);
788
+ }
789
+ handleBalance(client, message) {
790
+ // spot
791
+ // {
792
+ // "e":"ACCOUNT_UPDATE",
793
+ // "E":1696242817000,
794
+ // "T":1696242817142,
795
+ // "a":{
796
+ // "B":[
797
+ // {
798
+ // "a":"USDT",
799
+ // "bc":"-1.00000000000000000000",
800
+ // "cw":"86.59497382000000050000",
801
+ // "wb":"86.59497382000000050000"
802
+ // }
803
+ // ],
804
+ // "m":"ASSET_TRANSFER"
805
+ // }
806
+ // }
807
+ // swap
808
+ // {
809
+ // "e":"ACCOUNT_UPDATE",
810
+ // "E":1696244249320,
811
+ // "a":{
812
+ // "m":"WITHDRAW",
813
+ // "B":[
814
+ // {
815
+ // "a":"USDT",
816
+ // "wb":"49.81083984",
817
+ // "cw":"49.81083984",
818
+ // "bc":"-1.00000000"
819
+ // }
820
+ // ],
821
+ // "P":[
822
+ // ]
823
+ // }
824
+ // }
825
+ //
826
+ const a = this.safeValue(message, 'a', {});
827
+ const data = this.safeValue(a, 'B', []);
828
+ const timestamp = this.safeInteger2(message, 'T', 'E');
829
+ const type = ('P' in a) ? 'swap' : 'spot';
830
+ if (!(type in this.balance)) {
831
+ this.balance[type] = {};
832
+ }
833
+ this.balance[type]['info'] = data;
834
+ this.balance[type]['timestamp'] = timestamp;
835
+ this.balance[type]['datetime'] = this.iso8601(timestamp);
836
+ for (let i = 0; i < data.length; i++) {
837
+ const balance = data[i];
838
+ const currencyId = this.safeString(balance, 'a');
839
+ const code = this.safeCurrencyCode(currencyId);
840
+ const account = (code in this.balance) ? this.balance[code] : this.account();
841
+ account['total'] = this.safeString(balance, 'wb');
842
+ this.balance[type][code] = account;
843
+ }
844
+ this.balance[type] = this.safeBalance(this.balance[type]);
845
+ client.resolve(this.balance[type], type + ':balance');
846
+ }
847
+ handleMessage(client, message) {
848
+ if (!this.handleErrorMessage(client, message)) {
849
+ return;
850
+ }
851
+ // public subscriptions
852
+ if ((message === 'Ping') || ('ping' in message)) {
853
+ this.spawn(this.pong, client, message);
854
+ return;
855
+ }
856
+ const dataType = this.safeString(message, 'dataType', '');
857
+ if (dataType.indexOf('@depth') >= 0) {
858
+ this.handleOrderBook(client, message);
859
+ return;
860
+ }
861
+ if (dataType.indexOf('@trade') >= 0) {
862
+ this.handleTrades(client, message);
863
+ return;
864
+ }
865
+ if (dataType.indexOf('@kline') >= 0) {
866
+ this.handleOHLCV(client, message);
867
+ return;
868
+ }
869
+ if (dataType.indexOf('executionReport') >= 0) {
870
+ const data = this.safeValue(message, 'data', {});
871
+ const type = this.safeString(data, 'x');
872
+ if (type === 'TRADE') {
873
+ this.handleMyTrades(client, message);
874
+ }
875
+ this.handleOrder(client, message);
876
+ return;
877
+ }
878
+ const e = this.safeString(message, 'e');
879
+ if (e === 'ACCOUNT_UPDATE') {
880
+ this.handleBalance(client, message);
881
+ }
882
+ if (e === 'ORDER_TRADE_UPDATE') {
883
+ this.handleOrder(client, message);
884
+ const data = this.safeValue(message, 'o', {});
885
+ const type = this.safeString(data, 'x');
886
+ const status = this.safeString(data, 'X');
887
+ if ((type === 'TRADE') && (status === 'FILLED')) {
888
+ this.handleMyTrades(client, message);
889
+ }
890
+ }
891
+ }
892
+ }