ccxt 4.3.89 → 4.3.91

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 (69) hide show
  1. package/README.md +3 -3
  2. package/dist/ccxt.browser.min.js +3 -3
  3. package/dist/cjs/ccxt.js +1 -1
  4. package/dist/cjs/src/alpaca.js +2 -2
  5. package/dist/cjs/src/ascendex.js +95 -97
  6. package/dist/cjs/src/binance.js +1 -1
  7. package/dist/cjs/src/bingx.js +31 -17
  8. package/dist/cjs/src/bitfinex2.js +21 -22
  9. package/dist/cjs/src/bitget.js +2 -2
  10. package/dist/cjs/src/bitmart.js +6 -9
  11. package/dist/cjs/src/coinbaseinternational.js +2 -1
  12. package/dist/cjs/src/coinex.js +1 -17
  13. package/dist/cjs/src/hitbtc.js +2 -0
  14. package/dist/cjs/src/htx.js +49 -49
  15. package/dist/cjs/src/huobijp.js +0 -9
  16. package/dist/cjs/src/kucoin.js +59 -21
  17. package/dist/cjs/src/kucoinfutures.js +26 -2
  18. package/dist/cjs/src/latoken.js +1 -0
  19. package/dist/cjs/src/okx.js +1 -8
  20. package/dist/cjs/src/pro/binance.js +323 -0
  21. package/dist/cjs/src/pro/bingx.js +263 -91
  22. package/dist/cjs/src/pro/bithumb.js +5 -1
  23. package/dist/cjs/src/pro/bybit.js +1 -1
  24. package/dist/cjs/src/pro/coinex.js +994 -679
  25. package/dist/cjs/src/pro/lbank.js +2 -3
  26. package/dist/cjs/src/pro/okx.js +159 -3
  27. package/dist/cjs/src/whitebit.js +5 -3
  28. package/dist/cjs/src/woo.js +1 -1
  29. package/js/ccxt.d.ts +1 -1
  30. package/js/ccxt.js +1 -1
  31. package/js/src/abstract/kucoin.d.ts +1 -0
  32. package/js/src/abstract/kucoinfutures.d.ts +1 -0
  33. package/js/src/alpaca.js +2 -2
  34. package/js/src/ascendex.js +95 -97
  35. package/js/src/binance.js +1 -1
  36. package/js/src/bingx.js +31 -17
  37. package/js/src/bitfinex2.d.ts +0 -1
  38. package/js/src/bitfinex2.js +21 -22
  39. package/js/src/bitget.js +2 -2
  40. package/js/src/bitmart.d.ts +0 -1
  41. package/js/src/bitmart.js +6 -9
  42. package/js/src/coinbaseinternational.js +2 -1
  43. package/js/src/coinex.d.ts +0 -2
  44. package/js/src/coinex.js +1 -17
  45. package/js/src/hitbtc.js +2 -0
  46. package/js/src/htx.js +49 -49
  47. package/js/src/huobijp.d.ts +0 -1
  48. package/js/src/huobijp.js +0 -9
  49. package/js/src/kucoin.d.ts +3 -1
  50. package/js/src/kucoin.js +59 -21
  51. package/js/src/kucoinfutures.d.ts +1 -0
  52. package/js/src/kucoinfutures.js +26 -2
  53. package/js/src/latoken.js +1 -0
  54. package/js/src/okx.d.ts +0 -1
  55. package/js/src/okx.js +1 -8
  56. package/js/src/pro/binance.d.ts +9 -1
  57. package/js/src/pro/binance.js +327 -1
  58. package/js/src/pro/bingx.d.ts +2 -2
  59. package/js/src/pro/bingx.js +263 -91
  60. package/js/src/pro/bithumb.js +5 -1
  61. package/js/src/pro/bybit.js +1 -1
  62. package/js/src/pro/coinex.d.ts +12 -6
  63. package/js/src/pro/coinex.js +996 -681
  64. package/js/src/pro/lbank.js +2 -3
  65. package/js/src/pro/okx.d.ts +7 -0
  66. package/js/src/pro/okx.js +162 -4
  67. package/js/src/whitebit.js +5 -3
  68. package/js/src/woo.js +1 -1
  69. package/package.json +1 -1
@@ -5,12 +5,10 @@
5
5
  // EDIT THE CORRESPONDENT .ts FILE INSTEAD
6
6
 
7
7
  // ---------------------------------------------------------------------------
8
- import { Precise } from '../base/Precise.js';
9
8
  import coinexRest from '../coinex.js';
10
- import { AuthenticationError, BadRequest, ExchangeNotAvailable, NotSupported, RequestTimeout, ExchangeError } from '../base/errors.js';
11
- import { ArrayCache, ArrayCacheByTimestamp, ArrayCacheBySymbolById } from '../base/ws/Cache.js';
9
+ import { AuthenticationError, BadRequest, RateLimitExceeded, NotSupported, RequestTimeout, ExchangeError, ExchangeNotAvailable } from '../base/errors.js';
10
+ import { ArrayCache, ArrayCacheBySymbolById } from '../base/ws/Cache.js';
12
11
  import { sha256 } from '../static_dependencies/noble-hashes/sha256.js';
13
- import { md5 } from '../static_dependencies/noble-hashes/md5.js';
14
12
  // ---------------------------------------------------------------------------
15
13
  export default class coinex extends coinexRest {
16
14
  describe() {
@@ -18,25 +16,30 @@ export default class coinex extends coinexRest {
18
16
  'has': {
19
17
  'ws': true,
20
18
  'watchBalance': true,
19
+ 'watchBidsAsks': true,
21
20
  'watchTicker': true,
22
21
  'watchTickers': true,
23
22
  'watchTrades': true,
24
- 'watchMyTrades': false,
23
+ 'watchTradesForSymbols': true,
24
+ 'watchMyTrades': true,
25
25
  'watchOrders': true,
26
26
  'watchOrderBook': true,
27
- 'watchOHLCV': true,
28
- 'fetchOHLCVWs': true,
27
+ 'watchOrderBookForSymbols': true,
28
+ 'watchOHLCV': false,
29
+ 'fetchOHLCVWs': false,
29
30
  },
30
31
  'urls': {
31
32
  'api': {
32
33
  'ws': {
33
- 'spot': 'wss://socket.coinex.com/',
34
- 'swap': 'wss://perpetual.coinex.com/',
34
+ 'spot': 'wss://socket.coinex.com/v2/spot/',
35
+ 'swap': 'wss://socket.coinex.com/v2/futures/',
35
36
  },
36
37
  },
37
38
  },
38
39
  'options': {
39
- 'watchOHLCVWarning': true,
40
+ 'ws': {
41
+ 'gunzip': true,
42
+ },
40
43
  'timeframes': {
41
44
  '1m': 60,
42
45
  '3m': 180,
@@ -56,20 +59,31 @@ export default class coinex extends coinexRest {
56
59
  'watchOrderBook': {
57
60
  'limits': [5, 10, 20, 50],
58
61
  'defaultLimit': 50,
59
- 'aggregations': ['10', '1', '0', '0.1', '0.01'],
62
+ 'aggregations': ['1000', '100', '10', '1', '0', '0.1', '0.01', '0.001', '0.0001', '0.00001', '0.000001', '0.0000001', '0.00000001', '0.000000001', '0.0000000001', '0.00000000001'],
60
63
  'defaultAggregation': '0',
61
64
  },
62
65
  },
63
66
  'streaming': {},
64
67
  'exceptions': {
65
- 'codes': {
66
- '1': BadRequest,
67
- '2': ExchangeError,
68
- '3': ExchangeNotAvailable,
69
- '4': NotSupported,
70
- '5': RequestTimeout,
71
- '6': AuthenticationError, // Permission denied
68
+ 'exact': {
69
+ '20001': BadRequest,
70
+ '20002': NotSupported,
71
+ '21001': AuthenticationError,
72
+ '21002': AuthenticationError,
73
+ '23001': RequestTimeout,
74
+ '23002': RateLimitExceeded,
75
+ '24001': ExchangeError,
76
+ '24002': ExchangeNotAvailable,
77
+ '30001': BadRequest,
78
+ '30002': NotSupported,
79
+ '31001': AuthenticationError,
80
+ '31002': AuthenticationError,
81
+ '33001': RequestTimeout,
82
+ '33002': RateLimitExceeded,
83
+ '34001': ExchangeError,
84
+ '34002': ExchangeNotAvailable, // Service unavailable temporarily
72
85
  },
86
+ 'broad': {},
73
87
  },
74
88
  });
75
89
  }
@@ -84,61 +98,66 @@ export default class coinex extends coinexRest {
84
98
  //
85
99
  // {
86
100
  // "method": "state.update",
87
- // "params": [{
88
- // "BTCUSDT": {
89
- // "last": "31577.89",
90
- // "open": "29318.36",
91
- // "close": "31577.89",
92
- // "high": "32222.19",
93
- // "low": "29317.21",
94
- // "volume": "630.43024965",
95
- // "sell_total": "13.66143951",
96
- // "buy_total": "2.76410939",
97
- // "period": 86400,
98
- // "deal": "19457487.84611409070000000000"
99
- // }
100
- // }]
101
+ // "data": {
102
+ // "state_list": [
103
+ // {
104
+ // "market": "LATUSDT",
105
+ // "last": "0.008157",
106
+ // "open": "0.008286",
107
+ // "close": "0.008157",
108
+ // "high": "0.008390",
109
+ // "low": "0.008106",
110
+ // "volume": "807714.49139758",
111
+ // "volume_sell": "286170.69645599",
112
+ // "volume_buy": "266161.23236408",
113
+ // "value": "6689.21644207",
114
+ // "period": 86400
115
+ // },
116
+ // ]
117
+ // },
118
+ // "id": null
101
119
  // }
102
120
  //
103
121
  // swap
104
122
  //
105
123
  // {
106
124
  // "method": "state.update",
107
- // "params": [{
108
- // "BTCUSDT": {
109
- // "period": 86400,
110
- // "funding_time": 422,
111
- // "position_amount": "285.6246",
112
- // "funding_rate_last": "-0.00097933",
113
- // "funding_rate_next": "0.00022519",
114
- // "funding_rate_predict": "0.00075190",
115
- // "insurance": "17474289.49925859030905338270",
116
- // "last": "31570.08",
117
- // "sign_price": "31568.09",
118
- // "index_price": "31561.85000000",
119
- // "open": "29296.11",
120
- // "close": "31570.08",
121
- // "high": "32463.40",
122
- // "low": "29296.11",
123
- // "volume": "8774.7318",
124
- // "deal": "270675177.827928219109030017258398",
125
- // "sell_total": "19.2230",
126
- // "buy_total": "25.7814"
127
- // }
128
- // }]
125
+ // "data": {
126
+ // "state_list": [
127
+ // {
128
+ // "market": "ETHUSD_SIGNPRICE",
129
+ // "last": "1892.29",
130
+ // "open": "1884.62",
131
+ // "close": "1892.29",
132
+ // "high": "1894.09",
133
+ // "low": "1863.72",
134
+ // "volume": "0",
135
+ // "value": "0",
136
+ // "volume_sell": "0",
137
+ // "volume_buy": "0",
138
+ // "open_interest_size": "0",
139
+ // "insurance_fund_size": "0",
140
+ // "latest_funding_rate": "0",
141
+ // "next_funding_rate": "0",
142
+ // "latest_funding_time": 0,
143
+ // "next_funding_time": 0,
144
+ // "period": 86400
145
+ // },
146
+ // ]
147
+ // ],
148
+ // "id": null
129
149
  // }
130
150
  //
131
151
  const defaultType = this.safeString(this.options, 'defaultType');
132
- const params = this.safeValue(message, 'params', []);
133
- const rawTickers = this.safeValue(params, 0, {});
134
- const keys = Object.keys(rawTickers);
152
+ const data = this.safeDict(message, 'data', {});
153
+ const rawTickers = this.safeList(data, 'state_list', []);
135
154
  const newTickers = [];
136
- for (let i = 0; i < keys.length; i++) {
137
- const marketId = keys[i];
138
- const rawTicker = rawTickers[marketId];
155
+ for (let i = 0; i < rawTickers.length; i++) {
156
+ const entry = rawTickers[i];
157
+ const marketId = this.safeString(entry, 'market');
139
158
  const symbol = this.safeSymbol(marketId, undefined, undefined, defaultType);
140
159
  const market = this.safeMarket(marketId, undefined, undefined, defaultType);
141
- const parsedTicker = this.parseWSTicker(rawTicker, market);
160
+ const parsedTicker = this.parseWSTicker(entry, market);
142
161
  this.tickers[symbol] = parsedTicker;
143
162
  newTickers.push(parsedTicker);
144
163
  }
@@ -162,52 +181,53 @@ export default class coinex extends coinexRest {
162
181
  // spot
163
182
  //
164
183
  // {
165
- // "last": "31577.89",
166
- // "open": "29318.36",
167
- // "close": "31577.89",
168
- // "high": "32222.19",
169
- // "low": "29317.21",
170
- // "volume": "630.43024965",
171
- // "sell_total": "13.66143951",
172
- // "buy_total": "2.76410939",
173
- // "period": 86400,
174
- // "deal": "19457487.84611409070000000000"
184
+ // "market": "LATUSDT",
185
+ // "last": "0.008157",
186
+ // "open": "0.008286",
187
+ // "close": "0.008157",
188
+ // "high": "0.008390",
189
+ // "low": "0.008106",
190
+ // "volume": "807714.49139758",
191
+ // "volume_sell": "286170.69645599",
192
+ // "volume_buy": "266161.23236408",
193
+ // "value": "6689.21644207",
194
+ // "period": 86400
175
195
  // }
176
196
  //
177
197
  // swap
178
198
  //
179
199
  // {
180
- // "period": 86400,
181
- // "funding_time": 422,
182
- // "position_amount": "285.6246",
183
- // "funding_rate_last": "-0.00097933",
184
- // "funding_rate_next": "0.00022519",
185
- // "funding_rate_predict": "0.00075190",
186
- // "insurance": "17474289.49925859030905338270",
187
- // "last": "31570.08",
188
- // "sign_price": "31568.09",
189
- // "index_price": "31561.85000000",
190
- // "open": "29296.11",
191
- // "close": "31570.08",
192
- // "high": "32463.40",
193
- // "low": "29296.11",
194
- // "volume": "8774.7318",
195
- // "deal": "270675177.827928219109030017258398",
196
- // "sell_total": "19.2230",
197
- // "buy_total": "25.7814"
200
+ // "market": "ETHUSD_SIGNPRICE",
201
+ // "last": "1892.29",
202
+ // "open": "1884.62",
203
+ // "close": "1892.29",
204
+ // "high": "1894.09",
205
+ // "low": "1863.72",
206
+ // "volume": "0",
207
+ // "value": "0",
208
+ // "volume_sell": "0",
209
+ // "volume_buy": "0",
210
+ // "open_interest_size": "0",
211
+ // "insurance_fund_size": "0",
212
+ // "latest_funding_rate": "0",
213
+ // "next_funding_rate": "0",
214
+ // "latest_funding_time": 0,
215
+ // "next_funding_time": 0,
216
+ // "period": 86400
198
217
  // }
199
218
  //
200
219
  const defaultType = this.safeString(this.options, 'defaultType');
220
+ const marketId = this.safeString(ticker, 'market');
201
221
  return this.safeTicker({
202
- 'symbol': this.safeSymbol(undefined, market, undefined, defaultType),
222
+ 'symbol': this.safeSymbol(marketId, market, undefined, defaultType),
203
223
  'timestamp': undefined,
204
224
  'datetime': undefined,
205
225
  'high': this.safeString(ticker, 'high'),
206
226
  'low': this.safeString(ticker, 'low'),
207
227
  'bid': undefined,
208
- 'bidVolume': this.safeString(ticker, 'buy_total'),
228
+ 'bidVolume': this.safeString(ticker, 'volume_buy'),
209
229
  'ask': undefined,
210
- 'askVolume': this.safeString(ticker, 'sell_total'),
230
+ 'askVolume': this.safeString(ticker, 'volume_sell'),
211
231
  'vwap': undefined,
212
232
  'open': this.safeString(ticker, 'open'),
213
233
  'close': this.safeString(ticker, 'close'),
@@ -217,7 +237,7 @@ export default class coinex extends coinexRest {
217
237
  'percentage': undefined,
218
238
  'average': undefined,
219
239
  'baseVolume': this.safeString(ticker, 'volume'),
220
- 'quoteVolume': this.safeString(ticker, 'deal'),
240
+ 'quoteVolume': this.safeString(ticker, 'value'),
221
241
  'info': ticker,
222
242
  }, market);
223
243
  }
@@ -226,78 +246,290 @@ export default class coinex extends coinexRest {
226
246
  * @method
227
247
  * @name coinex#watchBalance
228
248
  * @description watch balance and get the amount of funds available for trading or funds locked in orders
249
+ * @see https://docs.coinex.com/api/v2/assets/balance/ws/spot_balance
250
+ * @see https://docs.coinex.com/api/v2/assets/balance/ws/futures_balance
229
251
  * @param {object} [params] extra parameters specific to the exchange API endpoint
230
252
  * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
231
253
  */
232
254
  await this.loadMarkets();
233
- await this.authenticate(params);
234
- const messageHash = 'balance';
235
255
  let type = undefined;
236
- [type, params] = this.handleMarketTypeAndParams('watchBalance', undefined, params);
256
+ [type, params] = this.handleMarketTypeAndParams('watchBalance', undefined, params, 'spot');
257
+ await this.authenticate(type);
237
258
  const url = this.urls['api']['ws'][type];
238
- const currencies = Object.keys(this.currencies_by_id);
259
+ let currencies = Object.keys(this.currencies_by_id);
260
+ if (currencies === undefined) {
261
+ currencies = [];
262
+ }
263
+ let messageHash = 'balances';
264
+ if (type === 'spot') {
265
+ messageHash += ':spot';
266
+ }
267
+ else {
268
+ messageHash += ':swap';
269
+ }
239
270
  const subscribe = {
240
- 'method': 'asset.subscribe',
241
- 'params': currencies,
271
+ 'method': 'balance.subscribe',
272
+ 'params': { 'ccy_list': currencies },
242
273
  'id': this.requestId(),
243
274
  };
244
275
  const request = this.deepExtend(subscribe, params);
245
276
  return await this.watch(url, messageHash, request, messageHash);
246
277
  }
247
278
  handleBalance(client, message) {
279
+ //
280
+ // spot
248
281
  //
249
282
  // {
250
- // "method": "asset.update",
251
- // "params": [
252
- // {
253
- // "BTC": {
254
- // "available": "250",
255
- // "frozen": "10",
256
- // }
257
- // }
258
- // ],
283
+ // "method": "balance.update",
284
+ // "data": {
285
+ // "balance_list": [
286
+ // {
287
+ // "margin_market": "BTCUSDT",
288
+ // "ccy": "BTC",
289
+ // "available": "44.62207740",
290
+ // "frozen": "0.00000000",
291
+ // "updated_at": 1689152421692
292
+ // },
293
+ // ]
294
+ // },
259
295
  // "id": null
260
296
  // }
261
297
  //
262
- const params = this.safeValue(message, 'params', []);
263
- const first = this.safeValue(params, 0, {});
264
- this.balance['info'] = first;
265
- const currencies = Object.keys(first);
266
- for (let i = 0; i < currencies.length; i++) {
267
- const currencyId = currencies[i];
268
- const code = this.safeCurrencyCode(currencyId);
269
- const available = this.safeString(first[currencyId], 'available');
270
- const frozen = this.safeString(first[currencyId], 'frozen');
271
- const account = this.account();
272
- account['free'] = available;
273
- account['used'] = frozen;
298
+ // swap
299
+ //
300
+ // {
301
+ // "method": "balance.update",
302
+ // "data": {
303
+ // "balance_list": [
304
+ // {
305
+ // "ccy": "USDT",
306
+ // "available": "97.92470982756335000001",
307
+ // "frozen": "0.00000000000000000000",
308
+ // "margin": "0.61442700000000000000",
309
+ // "transferrable": "97.92470982756335000001",
310
+ // "unrealized_pnl": "-0.00807000000000000000",
311
+ // "equity": "97.92470982756335000001"
312
+ // },
313
+ // ]
314
+ // },
315
+ // "id": null
316
+ // }
317
+ //
318
+ if (this.balance === undefined) {
319
+ this.balance = {};
320
+ }
321
+ const data = this.safeDict(message, 'data', {});
322
+ const balances = this.safeList(data, 'balance_list', []);
323
+ const firstEntry = balances[0];
324
+ const updated = this.safeInteger(firstEntry, 'updated_at');
325
+ const unrealizedPnl = this.safeString(firstEntry, 'unrealized_pnl');
326
+ const isSpot = (updated !== undefined);
327
+ const isSwap = (unrealizedPnl !== undefined);
328
+ let info = undefined;
329
+ let account = undefined;
330
+ let rawBalances = [];
331
+ if (isSpot) {
332
+ account = 'spot';
333
+ for (let i = 0; i < balances.length; i++) {
334
+ rawBalances = this.arrayConcat(rawBalances, balances);
335
+ }
336
+ info = rawBalances;
337
+ }
338
+ if (isSwap) {
339
+ account = 'swap';
340
+ for (let i = 0; i < balances.length; i++) {
341
+ rawBalances = this.arrayConcat(rawBalances, balances);
342
+ }
343
+ info = rawBalances;
344
+ }
345
+ for (let i = 0; i < rawBalances.length; i++) {
346
+ const entry = rawBalances[i];
347
+ this.parseWsBalance(entry, account);
348
+ }
349
+ let messageHash = undefined;
350
+ if (account !== undefined) {
351
+ if (this.safeValue(this.balance, account) === undefined) {
352
+ this.balance[account] = {};
353
+ }
354
+ this.balance[account]['info'] = info;
355
+ this.balance[account] = this.safeBalance(this.balance[account]);
356
+ messageHash = 'balances:' + account;
357
+ client.resolve(this.balance[account], messageHash);
358
+ }
359
+ }
360
+ parseWsBalance(balance, accountType = undefined) {
361
+ //
362
+ // spot
363
+ //
364
+ // {
365
+ // "margin_market": "BTCUSDT",
366
+ // "ccy": "BTC",
367
+ // "available": "44.62207740",
368
+ // "frozen": "0.00000000",
369
+ // "updated_at": 1689152421692
370
+ // }
371
+ //
372
+ // swap
373
+ //
374
+ // {
375
+ // "ccy": "USDT",
376
+ // "available": "97.92470982756335000001",
377
+ // "frozen": "0.00000000000000000000",
378
+ // "margin": "0.61442700000000000000",
379
+ // "transferrable": "97.92470982756335000001",
380
+ // "unrealized_pnl": "-0.00807000000000000000",
381
+ // "equity": "97.92470982756335000001"
382
+ // }
383
+ //
384
+ const account = this.account();
385
+ const currencyId = this.safeString(balance, 'ccy');
386
+ const code = this.safeCurrencyCode(currencyId);
387
+ account['free'] = this.safeString(balance, 'available');
388
+ account['used'] = this.safeString(balance, 'frozen');
389
+ if (accountType !== undefined) {
390
+ if (this.safeValue(this.balance, accountType) === undefined) {
391
+ this.balance[accountType] = {};
392
+ }
393
+ this.balance[accountType][code] = account;
394
+ }
395
+ else {
274
396
  this.balance[code] = account;
275
- this.balance = this.safeBalance(this.balance);
276
397
  }
277
- const messageHash = 'balance';
278
- client.resolve(this.balance, messageHash);
398
+ }
399
+ async watchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
400
+ /**
401
+ * @method
402
+ * @name coinex#watchMyTrades
403
+ * @description watches information on multiple trades made by the user
404
+ * @see https://docs.coinex.com/api/v2/spot/deal/ws/user-deals
405
+ * @see https://docs.coinex.com/api/v2/futures/deal/ws/user-deals
406
+ * @param {string} [symbol] unified symbol of the market the trades were made in
407
+ * @param {int} [since] the earliest time in ms to watch trades
408
+ * @param {int} [limit] the maximum number of trade structures to retrieve
409
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
410
+ * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
411
+ */
412
+ await this.loadMarkets();
413
+ let market = undefined;
414
+ if (symbol !== undefined) {
415
+ market = this.market(symbol);
416
+ symbol = market['symbol'];
417
+ }
418
+ let type = undefined;
419
+ [type, params] = this.handleMarketTypeAndParams('watchMyTrades', market, params, 'spot');
420
+ await this.authenticate(type);
421
+ const url = this.urls['api']['ws'][type];
422
+ const subscribedSymbols = [];
423
+ let messageHash = 'myTrades';
424
+ if (market !== undefined) {
425
+ messageHash += ':' + symbol;
426
+ subscribedSymbols.push(market['id']);
427
+ }
428
+ else {
429
+ if (type === 'spot') {
430
+ messageHash += ':spot';
431
+ }
432
+ else {
433
+ messageHash += ':swap';
434
+ }
435
+ }
436
+ const message = {
437
+ 'method': 'user_deals.subscribe',
438
+ 'params': { 'market_list': subscribedSymbols },
439
+ 'id': this.requestId(),
440
+ };
441
+ const request = this.deepExtend(message, params);
442
+ const trades = await this.watch(url, messageHash, request, messageHash);
443
+ if (this.newUpdates) {
444
+ limit = trades.getLimit(symbol, limit);
445
+ }
446
+ return this.filterBySymbolSinceLimit(trades, symbol, since, limit, true);
447
+ }
448
+ handleMyTrades(client, message) {
449
+ //
450
+ // {
451
+ // "method": "user_deals.update",
452
+ // "data": {
453
+ // "deal_id": 3514376759,
454
+ // "created_at": 1689152421692,
455
+ // "market": "BTCUSDT",
456
+ // "side": "buy",
457
+ // "order_id": 8678890,
458
+ // "margin_market": "BTCUSDT",
459
+ // "price": "30718.42",
460
+ // "amount": "0.00000325",
461
+ // "role": "taker",
462
+ // "fee": "0.0299",
463
+ // "fee_ccy": "USDT"
464
+ // },
465
+ // "id": null
466
+ // }
467
+ //
468
+ const data = this.safeDict(message, 'data', {});
469
+ const marketId = this.safeString(data, 'market');
470
+ const isSpot = client.url.indexOf('spot') > -1;
471
+ const defaultType = isSpot ? 'spot' : 'swap';
472
+ const market = this.safeMarket(marketId, undefined, undefined, defaultType);
473
+ const symbol = market['symbol'];
474
+ const messageHash = 'myTrades:' + symbol;
475
+ const messageWithType = 'myTrades:' + market['type'];
476
+ let stored = this.safeValue(this.trades, symbol);
477
+ if (stored === undefined) {
478
+ const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
479
+ stored = new ArrayCache(limit);
480
+ this.trades[symbol] = stored;
481
+ }
482
+ const parsed = this.parseWsTrade(data, market);
483
+ stored.append(parsed);
484
+ this.trades[symbol] = stored;
485
+ client.resolve(this.trades[symbol], messageWithType);
486
+ client.resolve(this.trades[symbol], messageHash);
279
487
  }
280
488
  handleTrades(client, message) {
489
+ //
490
+ // spot
281
491
  //
282
492
  // {
283
493
  // "method": "deals.update",
284
- // "params": [
285
- // "BTCUSD",
286
- // [{
287
- // "type": "sell",
288
- // "time": 1496458040.059284,
289
- // "price ": "46444.74",
290
- // "id": 29433,
291
- // "amount": "0.00120000"
292
- // }]
293
- // ],
494
+ // "data": {
495
+ // "market": "BTCUSDT",
496
+ // "deal_list": [
497
+ // {
498
+ // "deal_id": 3514376759,
499
+ // "created_at": 1689152421692,
500
+ // "side": "buy",
501
+ // "price": "30718.42",
502
+ // "amount": "0.00000325"
503
+ // },
504
+ // ]
505
+ // },
294
506
  // "id": null
295
507
  // }
296
508
  //
297
- const params = this.safeValue(message, 'params', []);
298
- const marketId = this.safeString(params, 0);
299
- const trades = this.safeValue(params, 1, []);
300
- const defaultType = this.safeString(this.options, 'defaultType');
509
+ // swap
510
+ //
511
+ // {
512
+ // "method": "deals.update",
513
+ // "data": {
514
+ // "market": "BTCUSDT",
515
+ // "deal_list": [
516
+ // {
517
+ // "deal_id": 3514376759,
518
+ // "created_at": 1689152421692,
519
+ // "side": "buy",
520
+ // "price": "30718.42",
521
+ // "amount": "0.00000325"
522
+ // },
523
+ // ]
524
+ // },
525
+ // "id": null
526
+ // }
527
+ //
528
+ const data = this.safeDict(message, 'data', {});
529
+ const trades = this.safeList(data, 'deal_list', []);
530
+ const marketId = this.safeString(data, 'market');
531
+ const isSpot = client.url.indexOf('spot') > -1;
532
+ const defaultType = isSpot ? 'spot' : 'swap';
301
533
  const market = this.safeMarket(marketId, undefined, undefined, defaultType);
302
534
  const symbol = market['symbol'];
303
535
  const messageHash = 'trades:' + symbol;
@@ -316,133 +548,127 @@ export default class coinex extends coinexRest {
316
548
  client.resolve(this.trades[symbol], messageHash);
317
549
  }
318
550
  parseWsTrade(trade, market = undefined) {
551
+ //
552
+ // spot watchTrades
319
553
  //
320
554
  // {
321
- // "type": "sell",
322
- // "time": 1496458040.059284,
323
- // "price ": "46444.74",
324
- // "id": 29433,
325
- // "amount": "0.00120000"
555
+ // "deal_id": 3514376759,
556
+ // "created_at": 1689152421692,
557
+ // "side": "buy",
558
+ // "price": "30718.42",
559
+ // "amount": "0.00000325"
326
560
  // }
327
561
  //
328
- const timestamp = this.safeTimestamp(trade, 'time');
329
- const defaultType = this.safeString(this.options, 'defaultType');
562
+ // swap watchTrades
563
+ //
564
+ // {
565
+ // "deal_id": 3514376759,
566
+ // "created_at": 1689152421692,
567
+ // "side": "buy",
568
+ // "price": "30718.42",
569
+ // "amount": "0.00000325"
570
+ // }
571
+ //
572
+ // spot and swap watchMyTrades
573
+ //
574
+ // {
575
+ // "deal_id": 3514376759,
576
+ // "created_at": 1689152421692,
577
+ // "market": "BTCUSDT",
578
+ // "side": "buy",
579
+ // "order_id": 8678890,
580
+ // "margin_market": "BTCUSDT",
581
+ // "price": "30718.42",
582
+ // "amount": "0.00000325",
583
+ // "role": "taker",
584
+ // "fee": "0.0299",
585
+ // "fee_ccy": "USDT"
586
+ // }
587
+ //
588
+ const timestamp = this.safeInteger(trade, 'created_at');
589
+ const isSpot = ('margin_market' in trade);
590
+ const defaultType = isSpot ? 'spot' : 'swap';
591
+ const marketId = this.safeString(trade, 'market');
592
+ market = this.safeMarket(marketId, market, undefined, defaultType);
593
+ let fee = {};
594
+ const feeCost = this.omitZero(this.safeString(trade, 'fee'));
595
+ if (feeCost !== undefined) {
596
+ const feeCurrencyId = this.safeString(trade, 'fee_ccy', market['quote']);
597
+ fee = {
598
+ 'currency': this.safeCurrencyCode(feeCurrencyId),
599
+ 'cost': feeCost,
600
+ };
601
+ }
330
602
  return this.safeTrade({
331
- 'id': this.safeString(trade, 'id'),
603
+ 'id': this.safeString(trade, 'deal_id'),
332
604
  'info': trade,
333
605
  'timestamp': timestamp,
334
606
  'datetime': this.iso8601(timestamp),
335
- 'symbol': this.safeSymbol(undefined, market, undefined, defaultType),
336
- 'order': undefined,
607
+ 'symbol': this.safeSymbol(marketId, market, undefined, defaultType),
608
+ 'order': this.safeString(trade, 'order_id'),
337
609
  'type': undefined,
338
- 'side': this.safeString(trade, 'type'),
339
- 'takerOrMaker': undefined,
610
+ 'side': this.safeString(trade, 'side'),
611
+ 'takerOrMaker': this.safeString(trade, 'role'),
340
612
  'price': this.safeString(trade, 'price'),
341
613
  'amount': this.safeString(trade, 'amount'),
342
614
  'cost': undefined,
343
- 'fee': undefined,
615
+ 'fee': fee,
344
616
  }, market);
345
617
  }
346
- handleOHLCV(client, message) {
347
- //
348
- // spot
349
- // {
350
- // "error": null,
351
- // "result": [
352
- // [
353
- // 1673846940,
354
- // "21148.74",
355
- // "21148.38",
356
- // "21148.75",
357
- // "21138.66",
358
- // "1.57060173",
359
- // "33214.9138778914"
360
- // ],
361
- // ]
362
- // "id": 1,
363
- // }
364
- // swap
365
- // {
366
- // "method": "kline.update",
367
- // "params": [
368
- // [
369
- // 1654019640, // timestamp
370
- // "32061.99", // open
371
- // "32061.28", // close
372
- // "32061.99", // high
373
- // "32061.28", // low
374
- // "0.1285", // amount base
375
- // "4119.943736" // amount quote
376
- // ]
377
- // ],
378
- // "id": null
379
- // }
380
- //
381
- const candles = this.safeValue2(message, 'params', 'result', []);
382
- const messageHash = 'ohlcv';
383
- const id = this.safeString(message, 'id');
384
- const ohlcvs = this.parseOHLCVs(candles);
385
- if (id !== undefined) {
386
- // spot subscription response
387
- client.resolve(ohlcvs, messageHash);
388
- return;
389
- }
390
- const keys = Object.keys(this.ohlcvs);
391
- const keysLength = keys.length;
392
- if (keysLength === 0) {
393
- this.ohlcvs['unknown'] = {};
394
- const limit = this.safeInteger(this.options, 'OHLCVLimit', 1000);
395
- const stored = new ArrayCacheByTimestamp(limit);
396
- this.ohlcvs['unknown']['unknown'] = stored;
397
- }
398
- const ohlcv = this.ohlcvs['unknown']['unknown'];
399
- for (let i = 0; i < ohlcvs.length; i++) {
400
- const candle = ohlcvs[i];
401
- ohlcv.append(candle);
402
- }
403
- client.resolve(ohlcv, messageHash);
404
- }
405
618
  async watchTicker(symbol, params = {}) {
406
619
  /**
407
620
  * @method
408
621
  * @name coinex#watchTicker
409
- * @see https://viabtc.github.io/coinex_api_en_doc/spot/#docsspot004_websocket007_state_subscribe
410
622
  * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
623
+ * @see https://docs.coinex.com/api/v2/spot/market/ws/market
624
+ * @see https://docs.coinex.com/api/v2/futures/market/ws/market-state
411
625
  * @param {string} symbol unified symbol of the market to fetch the ticker for
412
626
  * @param {object} [params] extra parameters specific to the exchange API endpoint
413
627
  * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
414
628
  */
629
+ await this.loadMarkets();
630
+ const market = this.market(symbol);
415
631
  const tickers = await this.watchTickers([symbol], params);
416
- return this.safeValue(tickers, symbol);
632
+ return tickers[market['symbol']];
417
633
  }
418
634
  async watchTickers(symbols = undefined, params = {}) {
419
635
  /**
420
636
  * @method
421
637
  * @name coinex#watchTickers
422
- * @see https://viabtc.github.io/coinex_api_en_doc/spot/#docsspot004_websocket007_state_subscribe
423
638
  * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
639
+ * @see https://docs.coinex.com/api/v2/spot/market/ws/market
640
+ * @see https://docs.coinex.com/api/v2/futures/market/ws/market-state
424
641
  * @param {string[]} symbols unified symbol of the market to fetch the ticker for
425
642
  * @param {object} [params] extra parameters specific to the exchange API endpoint
426
643
  * @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
427
644
  */
428
645
  await this.loadMarkets();
429
- symbols = this.marketSymbols(symbols);
646
+ const marketIds = this.marketIds(symbols);
647
+ let market = undefined;
648
+ const messageHashes = [];
649
+ const symbolsDefined = (symbols !== undefined);
650
+ if (symbolsDefined) {
651
+ for (let i = 0; i < symbols.length; i++) {
652
+ const symbol = symbols[i];
653
+ market = this.market(symbol);
654
+ messageHashes.push('tickers::' + market['symbol']);
655
+ }
656
+ }
657
+ else {
658
+ messageHashes.push('tickers');
659
+ }
430
660
  let type = undefined;
431
- [type, params] = this.handleMarketTypeAndParams('watchTickers', undefined, params);
661
+ [type, params] = this.handleMarketTypeAndParams('watchTickers', market, params);
432
662
  const url = this.urls['api']['ws'][type];
433
- let messageHash = 'tickers';
434
- if (symbols !== undefined) {
435
- messageHash = 'tickers::' + symbols.join(',');
436
- }
663
+ const subscriptionHashes = ['all@ticker'];
437
664
  const subscribe = {
438
665
  'method': 'state.subscribe',
666
+ 'params': { 'market_list': marketIds },
439
667
  'id': this.requestId(),
440
- 'params': [],
441
668
  };
442
- const request = this.deepExtend(subscribe, params);
443
- const newTickers = await this.watch(url, messageHash, request, messageHash);
669
+ const result = await this.watchMultiple(url, messageHashes, this.deepExtend(subscribe, params), subscriptionHashes);
444
670
  if (this.newUpdates) {
445
- return newTickers;
671
+ return result;
446
672
  }
447
673
  return this.filterByArray(this.tickers, 'symbol', symbols);
448
674
  }
@@ -450,179 +676,139 @@ export default class coinex extends coinexRest {
450
676
  /**
451
677
  * @method
452
678
  * @name coinex#watchTrades
453
- * @see https://viabtc.github.io/coinex_api_en_doc/spot/#docsspot004_websocket012_deal_subcribe
454
- * @see https://viabtc.github.io/coinex_api_en_doc/futures/#docsfutures002_websocket019_deal_subcribe
455
679
  * @description get the list of most recent trades for a particular symbol
680
+ * @see https://docs.coinex.com/api/v2/spot/market/ws/market-deals
681
+ * @see https://docs.coinex.com/api/v2/futures/market/ws/market-deals
456
682
  * @param {string} symbol unified symbol of the market to fetch trades for
457
683
  * @param {int} [since] timestamp in ms of the earliest trade to fetch
458
684
  * @param {int} [limit] the maximum amount of trades to fetch
459
685
  * @param {object} [params] extra parameters specific to the exchange API endpoint
460
686
  * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
461
687
  */
688
+ params['callerMethodName'] = 'watchTrades';
689
+ return await this.watchTradesForSymbols([symbol], since, limit, params);
690
+ }
691
+ async watchTradesForSymbols(symbols, since = undefined, limit = undefined, params = {}) {
692
+ /**
693
+ * @method
694
+ * @name coinex#watchTradesForSymbols
695
+ * @description watch the most recent trades for a list of symbols
696
+ * @see https://docs.coinex.com/api/v2/spot/market/ws/market-deals
697
+ * @see https://docs.coinex.com/api/v2/futures/market/ws/market-deals
698
+ * @param {string[]} symbols unified symbols of the markets to fetch trades for
699
+ * @param {int} [since] timestamp in ms of the earliest trade to fetch
700
+ * @param {int} [limit] the maximum amount of trades to fetch
701
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
702
+ * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
703
+ */
462
704
  await this.loadMarkets();
463
- const market = this.market(symbol);
705
+ const subscribedSymbols = [];
706
+ const messageHashes = [];
707
+ let market = undefined;
708
+ let callerMethodName = undefined;
709
+ [callerMethodName, params] = this.handleParamString(params, 'callerMethodName', 'watchTradesForSymbols');
710
+ const symbolsDefined = (symbols !== undefined);
711
+ if (symbolsDefined) {
712
+ for (let i = 0; i < symbols.length; i++) {
713
+ const symbol = symbols[i];
714
+ market = this.market(symbol);
715
+ subscribedSymbols.push(market['id']);
716
+ messageHashes.push('trades:' + market['symbol']);
717
+ }
718
+ }
719
+ else {
720
+ messageHashes.push('trades');
721
+ }
464
722
  let type = undefined;
465
- [type, params] = this.handleMarketTypeAndParams('watchTrades', market, params);
723
+ [type, params] = this.handleMarketTypeAndParams(callerMethodName, market, params);
466
724
  const url = this.urls['api']['ws'][type];
467
- const messageHash = 'trades:' + symbol;
468
- const subscriptionHash = 'trades';
469
- const subscribedSymbols = this.safeValue(this.options, 'watchTradesSubscriptions', []);
470
- subscribedSymbols.push(market['id']);
471
- const message = {
725
+ const subscriptionHashes = ['trades'];
726
+ const subscribe = {
472
727
  'method': 'deals.subscribe',
473
- 'params': subscribedSymbols,
728
+ 'params': { 'market_list': subscribedSymbols },
474
729
  'id': this.requestId(),
475
730
  };
476
- this.options['watchTradesSubscriptions'] = subscribedSymbols;
477
- const request = this.deepExtend(message, params);
478
- const trades = await this.watch(url, messageHash, request, subscriptionHash);
731
+ const trades = await this.watchMultiple(url, messageHashes, this.deepExtend(subscribe, params), subscriptionHashes);
732
+ if (this.newUpdates) {
733
+ return trades;
734
+ }
479
735
  return this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
480
736
  }
481
- async watchOrderBook(symbol, limit = undefined, params = {}) {
737
+ async watchOrderBookForSymbols(symbols, limit = undefined, params = {}) {
482
738
  /**
483
739
  * @method
484
- * @name coinex#watchOrderBook
485
- * @see https://viabtc.github.io/coinex_api_en_doc/spot/#docsspot004_websocket017_depth_subscribe_multi
486
- * @see https://viabtc.github.io/coinex_api_en_doc/futures/#docsfutures002_websocket011_depth_subscribe_multi
740
+ * @name coinex#watchOrderBookForSymbols
487
741
  * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
488
- * @param {string} symbol unified symbol of the market to fetch the order book for
742
+ * @see https://docs.coinex.com/api/v2/spot/market/ws/market-depth
743
+ * @see https://docs.coinex.com/api/v2/futures/market/ws/market-depth
744
+ * @param {string[]} symbols unified array of symbols
489
745
  * @param {int} [limit] the maximum amount of order book entries to return
490
746
  * @param {object} [params] extra parameters specific to the exchange API endpoint
491
747
  * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
492
748
  */
493
749
  await this.loadMarkets();
494
- const market = this.market(symbol);
495
- symbol = market['symbol'];
750
+ const watchOrderBookSubscriptions = {};
751
+ const messageHashes = [];
752
+ let market = undefined;
496
753
  let type = undefined;
497
- [type, params] = this.handleMarketTypeAndParams('watchOrderBook', market, params);
498
- const url = this.urls['api']['ws'][type];
499
- const name = 'orderbook';
500
- const messageHash = name + ':' + symbol;
501
- const options = this.safeValue(this.options, 'watchOrderBook', {});
502
- const limits = this.safeValue(options, 'limits', []);
754
+ let callerMethodName = undefined;
755
+ [callerMethodName, params] = this.handleParamString(params, 'callerMethodName', 'watchOrderBookForSymbols');
756
+ [type, params] = this.handleMarketTypeAndParams(callerMethodName, undefined, params);
757
+ const options = this.safeDict(this.options, 'watchOrderBook', {});
758
+ const limits = this.safeList(options, 'limits', []);
503
759
  if (limit === undefined) {
504
- limit = this.safeValue(options, 'defaultLimit', 50);
760
+ limit = this.safeInteger(options, 'defaultLimit', 50);
505
761
  }
506
762
  if (!this.inArray(limit, limits)) {
507
- throw new NotSupported(this.id + ' watchOrderBook() limit must be one of ' + limits.join(', '));
763
+ throw new NotSupported(this.id + ' watchOrderBookForSymbols() limit must be one of ' + limits.join(', '));
508
764
  }
509
765
  const defaultAggregation = this.safeString(options, 'defaultAggregation', '0');
510
- const aggregations = this.safeValue(options, 'aggregations', []);
766
+ const aggregations = this.safeList(options, 'aggregations', []);
511
767
  const aggregation = this.safeString(params, 'aggregation', defaultAggregation);
512
768
  if (!this.inArray(aggregation, aggregations)) {
513
- throw new NotSupported(this.id + ' watchOrderBook() aggregation must be one of ' + aggregations.join(', '));
769
+ throw new NotSupported(this.id + ' watchOrderBookForSymbols() aggregation must be one of ' + aggregations.join(', '));
514
770
  }
515
771
  params = this.omit(params, 'aggregation');
516
- const watchOrderBookSubscriptions = this.safeValue(this.options, 'watchOrderBookSubscriptions', {});
517
- watchOrderBookSubscriptions[symbol] = [market['id'], limit, aggregation, true];
518
- const subscribe = {
519
- 'method': 'depth.subscribe_multi',
520
- 'id': this.requestId(),
521
- 'params': Object.values(watchOrderBookSubscriptions),
522
- };
523
- this.options['watchOrderBookSubscriptions'] = watchOrderBookSubscriptions;
524
- const subscriptionHash = this.hash(this.encode(this.json(watchOrderBookSubscriptions)), sha256);
525
- const request = this.deepExtend(subscribe, params);
526
- const orderbook = await this.watch(url, messageHash, request, subscriptionHash, request);
527
- return orderbook.limit();
528
- }
529
- async watchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
530
- /**
531
- * @method
532
- * @name coinex#watchOHLCV
533
- * @see https://viabtc.github.io/coinex_api_en_doc/futures/#docsfutures002_websocket023_kline_subscribe
534
- * @description watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
535
- * @param {string} symbol unified symbol of the market to fetch OHLCV data for
536
- * @param {string} timeframe the length of time each candle represents
537
- * @param {int} [since] timestamp in ms of the earliest candle to fetch
538
- * @param {int} [limit] the maximum amount of candles to fetch
539
- * @param {object} [params] extra parameters specific to the exchange API endpoint
540
- * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
541
- */
542
- await this.loadMarkets();
543
- const market = this.market(symbol);
544
- symbol = market['symbol'];
545
- let type = undefined;
546
- [type, params] = this.handleMarketTypeAndParams('watchOHLCV', market, params);
547
- if (type !== 'swap') {
548
- throw new NotSupported(this.id + ' watchOHLCV() is only supported for swap markets. Try using fetchOHLCV () instead');
772
+ const symbolsDefined = (symbols !== undefined);
773
+ if (symbolsDefined) {
774
+ for (let i = 0; i < symbols.length; i++) {
775
+ const symbol = symbols[i];
776
+ market = this.market(symbol);
777
+ messageHashes.push('orderbook:' + market['symbol']);
778
+ watchOrderBookSubscriptions[symbol] = [market['id'], limit, aggregation, true];
779
+ }
549
780
  }
550
- const url = this.urls['api']['ws'][type];
551
- const messageHash = 'ohlcv';
552
- const watchOHLCVWarning = this.safeBool(this.options, 'watchOHLCVWarning', true);
553
- const client = this.safeValue(this.clients, url, {});
554
- const clientSub = this.safeValue(client, 'subscriptions', {});
555
- const existingSubscription = this.safeValue(clientSub, messageHash);
556
- const subSymbol = this.safeString(existingSubscription, 'symbol');
557
- const subTimeframe = this.safeString(existingSubscription, 'timeframe');
558
- // due to nature of coinex response can only watch one symbol at a time
559
- if (watchOHLCVWarning && existingSubscription !== undefined && (subSymbol !== symbol || subTimeframe !== timeframe)) {
560
- throw new ExchangeError(this.id + ' watchOHLCV() can only watch one symbol and timeframe at a time. To supress this warning set watchOHLCVWarning to false in options');
781
+ else {
782
+ messageHashes.push('orderbook');
561
783
  }
562
- const timeframes = this.safeValue(this.options, 'timeframes', {});
784
+ const marketList = Object.values(watchOrderBookSubscriptions);
563
785
  const subscribe = {
564
- 'method': 'kline.subscribe',
786
+ 'method': 'depth.subscribe',
787
+ 'params': { 'market_list': marketList },
565
788
  'id': this.requestId(),
566
- 'params': [
567
- market['id'],
568
- this.safeInteger(timeframes, timeframe),
569
- ],
570
789
  };
571
- const subscription = {
572
- 'symbol': symbol,
573
- 'timeframe': timeframe,
574
- };
575
- const request = this.deepExtend(subscribe, params);
576
- const ohlcvs = await this.watch(url, messageHash, request, messageHash, subscription);
790
+ const subscriptionHashes = this.hash(this.encode(this.json(watchOrderBookSubscriptions)), sha256);
791
+ const url = this.urls['api']['ws'][type];
792
+ const orderbooks = await this.watchMultiple(url, messageHashes, this.deepExtend(subscribe, params), subscriptionHashes);
577
793
  if (this.newUpdates) {
578
- limit = ohlcvs.getLimit(symbol, limit);
794
+ return orderbooks;
579
795
  }
580
- return this.filterBySinceLimit(ohlcvs, since, limit, 0);
796
+ return orderbooks.limit();
581
797
  }
582
- async fetchOHLCVWs(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
798
+ async watchOrderBook(symbol, limit = undefined, params = {}) {
583
799
  /**
584
800
  * @method
585
- * @name coinex#fetchOHLCV
586
- * @see https://viabtc.github.io/coinex_api_en_doc/spot/#docsspot004_websocket005_kline_query
587
- * @description query historical candlestick data containing the open, high, low, and close price, and the volume of a market
588
- * @param {string} symbol unified symbol of the market to query OHLCV data for
589
- * @param {string} timeframe the length of time each candle represents
590
- * @param {int|undefined} since timestamp in ms of the earliest candle to fetch
591
- * @param {int|undefined} limit the maximum amount of candles to fetch
592
- * @param {object} params extra parameters specific to the exchange API endpoint
593
- * @param {int|undefined} params.end the end time for spot markets, this.seconds () is set as default
594
- * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
801
+ * @name coinex#watchOrderBook
802
+ * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
803
+ * @see https://docs.coinex.com/api/v2/spot/market/ws/market-depth
804
+ * @see https://docs.coinex.com/api/v2/futures/market/ws/market-depth
805
+ * @param {string} symbol unified symbol of the market to fetch the order book for
806
+ * @param {int} [limit] the maximum amount of order book entries to return
807
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
808
+ * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
595
809
  */
596
- await this.loadMarkets();
597
- const market = this.market(symbol);
598
- const [type, query] = this.handleMarketTypeAndParams('fetchOHLCV', market, params);
599
- const url = this.urls['api']['ws'][type];
600
- symbol = market['symbol'];
601
- const messageHash = 'ohlcv';
602
- const timeframes = this.safeValue(this.options, 'timeframes', {});
603
- timeframe = this.safeString(timeframes, timeframe, timeframe);
604
- if (since === undefined) {
605
- since = 1640995200; // January 1, 2022
606
- }
607
- const id = this.requestId();
608
- const subscribe = {
609
- 'method': 'kline.query',
610
- 'params': [
611
- market['id'],
612
- this.parseToInt(since / 1000),
613
- this.safeInteger(params, 'end', this.seconds()),
614
- this.parseToInt(timeframe),
615
- ],
616
- 'id': id,
617
- };
618
- const subscription = {
619
- 'id': id,
620
- 'future': messageHash,
621
- };
622
- const subscriptionHash = id;
623
- const request = this.deepExtend(subscribe, query);
624
- const ohlcvs = await this.watch(url, messageHash, request, subscriptionHash, subscription);
625
- return this.filterBySinceLimit(ohlcvs, since, limit, 0);
810
+ params['callerMethodName'] = 'watchOrderBook';
811
+ return await this.watchOrderBookForSymbols([symbol], limit, params);
626
812
  }
627
813
  handleDelta(bookside, delta) {
628
814
  const bidAsk = this.parseBidAsk(delta, 0, 1);
@@ -637,52 +823,54 @@ export default class coinex extends coinexRest {
637
823
  //
638
824
  // {
639
825
  // "method": "depth.update",
640
- // "params": [
641
- // false,
642
- // {
826
+ // "data": {
827
+ // "market": "BTCUSDT",
828
+ // "is_full": true,
829
+ // "depth": {
643
830
  // "asks": [
644
- // ["46350.52", "1.07871851"],
645
- // ...
831
+ // [
832
+ // "30740.00",
833
+ // "0.31763545"
834
+ // ],
646
835
  // ],
647
836
  // "bids": [
648
- // ["46349.61", "0.04000000"],
649
- // ...
837
+ // [
838
+ // "30736.00",
839
+ // "0.04857373"
840
+ // ],
650
841
  // ],
651
- // "last": "46349.93",
652
- // "time": 1639987469166,
653
- // "checksum": 1533284725
654
- // },
655
- // "BTCUSDT"
656
- // ],
842
+ // "last": "30746.28",
843
+ // "updated_at": 1689152421692,
844
+ // "checksum": 2578768879
845
+ // }
846
+ // },
657
847
  // "id": null
658
848
  // }
659
849
  //
660
- const isSwap = client.url.indexOf('perpetual') >= 0;
661
- const marketType = isSwap ? 'swap' : 'spot';
662
- const params = this.safeValue(message, 'params', []);
663
- const fullOrderBook = this.safeValue(params, 0);
664
- let orderbook = this.safeValue(params, 1);
665
- const marketId = this.safeString(params, 2);
666
- const market = this.safeMarket(marketId, undefined, undefined, marketType);
850
+ const defaultType = this.safeString(this.options, 'defaultType');
851
+ const data = this.safeDict(message, 'data', {});
852
+ const depth = this.safeDict(data, 'depth', {});
853
+ const marketId = this.safeString(data, 'market');
854
+ const market = this.safeMarket(marketId, undefined, undefined, defaultType);
667
855
  const symbol = market['symbol'];
668
856
  const name = 'orderbook';
669
857
  const messageHash = name + ':' + symbol;
670
- const timestamp = this.safeInteger(orderbook, 'time');
858
+ const timestamp = this.safeInteger(depth, 'updated_at');
671
859
  const currentOrderBook = this.safeValue(this.orderbooks, symbol);
860
+ const fullOrderBook = this.safeBool(data, 'is_full', false);
672
861
  if (fullOrderBook) {
673
- const snapshot = this.parseOrderBook(orderbook, symbol, timestamp);
862
+ const snapshot = this.parseOrderBook(depth, symbol, timestamp);
674
863
  if (currentOrderBook === undefined) {
675
- orderbook = this.orderBook(snapshot);
676
- this.orderbooks[symbol] = orderbook;
864
+ this.orderbooks[symbol] = this.orderBook(snapshot);
677
865
  }
678
866
  else {
679
- orderbook = this.orderbooks[symbol];
867
+ const orderbook = this.orderbooks[symbol];
680
868
  orderbook.reset(snapshot);
681
869
  }
682
870
  }
683
871
  else {
684
- const asks = this.safeValue(orderbook, 'asks', []);
685
- const bids = this.safeValue(orderbook, 'bids', []);
872
+ const asks = this.safeList(depth, 'asks', []);
873
+ const bids = this.safeList(depth, 'bids', []);
686
874
  this.handleDeltas(currentOrderBook['asks'], asks);
687
875
  this.handleDeltas(currentOrderBook['bids'], bids);
688
876
  currentOrderBook['nonce'] = timestamp;
@@ -694,26 +882,59 @@ export default class coinex extends coinexRest {
694
882
  client.resolve(this.orderbooks[symbol], messageHash);
695
883
  }
696
884
  async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
885
+ /**
886
+ * @method
887
+ * @name coinex#watchOrders
888
+ * @description watches information on multiple orders made by the user
889
+ * @see https://docs.coinex.com/api/v2/spot/order/ws/user-order
890
+ * @see https://docs.coinex.com/api/v2/futures/order/ws/user-order
891
+ * @param {string} symbol unified market symbol of the market orders were made in
892
+ * @param {int} [since] the earliest time in ms to fetch orders for
893
+ * @param {int} [limit] the maximum number of order structures to retrieve
894
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
895
+ * @param {bool} [params.trigger] if the orders to watch are trigger orders or not
896
+ * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
897
+ */
697
898
  await this.loadMarkets();
698
- await this.authenticate(params);
899
+ const stop = this.safeBool2(params, 'trigger', 'stop');
900
+ params = this.omit(params, ['trigger', 'stop']);
699
901
  let messageHash = 'orders';
700
902
  let market = undefined;
701
- const [type, query] = this.handleMarketTypeAndParams('watchOrders', market, params);
702
- const message = {
703
- 'method': 'order.subscribe',
704
- 'id': this.requestId(),
705
- };
903
+ let marketList = undefined;
706
904
  if (symbol !== undefined) {
707
905
  market = this.market(symbol);
708
906
  symbol = market['symbol'];
709
- message['params'] = [market['id']];
907
+ }
908
+ let type = undefined;
909
+ [type, params] = this.handleMarketTypeAndParams('watchOrders', market, params, 'spot');
910
+ await this.authenticate(type);
911
+ if (symbol !== undefined) {
912
+ marketList = [market['id']];
710
913
  messageHash += ':' + symbol;
711
914
  }
712
915
  else {
713
- message['params'] = [];
916
+ marketList = [];
917
+ if (type === 'spot') {
918
+ messageHash += ':spot';
919
+ }
920
+ else {
921
+ messageHash += ':swap';
922
+ }
923
+ }
924
+ let method = undefined;
925
+ if (stop) {
926
+ method = 'stop.subscribe';
927
+ }
928
+ else {
929
+ method = 'order.subscribe';
714
930
  }
931
+ const message = {
932
+ 'method': method,
933
+ 'params': { 'market_list': marketList },
934
+ 'id': this.requestId(),
935
+ };
715
936
  const url = this.urls['api']['ws'][type];
716
- const request = this.deepExtend(message, query);
937
+ const request = this.deepExtend(message, params);
717
938
  const orders = await this.watch(url, messageHash, request, messageHash, request);
718
939
  if (this.newUpdates) {
719
940
  limit = orders.getLimit(symbol, limit);
@@ -722,94 +943,126 @@ export default class coinex extends coinexRest {
722
943
  }
723
944
  handleOrders(client, message) {
724
945
  //
725
- // spot
946
+ // spot
726
947
  //
727
- // {
728
- // "method": "order.update",
729
- // "params": [
730
- // 1,
731
- // {
732
- // "id": 77782469357,
733
- // "type": 1,
734
- // "side": 2,
735
- // "user": 1849116,
736
- // "account": 0,
737
- // "option": 2,
738
- // "ctime": 1653961043.048967,
739
- // "mtime": 1653961043.048967,
740
- // "market": "BTCUSDT",
741
- // "source": "web",
742
- // "client_id": '',
743
- // "price": "1.00",
744
- // "amount": "1.00000000",
745
- // "taker_fee": "0.0020",
746
- // "maker_fee": "0.0020",
747
- // "left": "1.00000000",
748
- // "deal_stock": "0",
749
- // "deal_money": "0",
750
- // "money_fee": "0",
751
- // "stock_fee": "0",
752
- // "asset_fee": "0",
753
- // "fee_discount": "1",
754
- // "last_deal_amount": "0",
755
- // "last_deal_price": "0",
756
- // "last_deal_time": 0,
757
- // "last_deal_id": 0,
758
- // "last_role": 0,
759
- // "fee_asset": null,
760
- // "stop_id": 0
761
- // }
762
- // ],
763
- // "id": null
764
- // }
948
+ // {
949
+ // "method": "order.update",
950
+ // "data": {
951
+ // "event": "put",
952
+ // "order": {
953
+ // "order_id": 12750,
954
+ // "market": "BTCUSDT",
955
+ // "margin_market": "BTCUSDT",
956
+ // "type": "limit",
957
+ // "side": "buy",
958
+ // "price": "5999.00",
959
+ // "amount": "1.50000000",
960
+ // "unfill_amount": "1.50000000",
961
+ // "fill_value": "1.50000000",
962
+ // "taker_fee_rate": "0.0001",
963
+ // "maker_fee_rate": "0.0001",
964
+ // "base_ccy_fee": "0.0001",
965
+ // "quote_ccy_fee": "0.0001",
966
+ // "discount_ccy_fee": "0.0001",
967
+ // "last_fill_amount": "0",
968
+ // "last_fill_price": "0",
969
+ // "client_id": "buy1_1234",
970
+ // "created_at": 1689152421692,
971
+ // "updated_at": 1689152421692,
972
+ // }
973
+ // },
974
+ // "id": null
975
+ // }
765
976
  //
766
- // swap
977
+ // spot stop
978
+ //
979
+ // {
980
+ // "method": "stop.update",
981
+ // "data": {
982
+ // "event": 1,
983
+ // "stop": {
984
+ // "stop_id": 102067022299,
985
+ // "market": "BTCUSDT",
986
+ // "margin_market": "BTCUSDT",
987
+ // "type": "limit",
988
+ // "side": "buy",
989
+ // "price": "20000.00",
990
+ // "amount": "0.10000000",
991
+ // "trigger_price": "20000.00",
992
+ // "trigger_direction": "lower",
993
+ // "taker_fee_rate": "0.0016",
994
+ // "maker_fee_rate": "0.0016",
995
+ // "status": "active_success",
996
+ // "client_id": "",
997
+ // "created_at": 1689152996689,
998
+ // "updated_at": 1689152996689,
999
+ // }
1000
+ // },
1001
+ // "id": null
1002
+ // }
1003
+ //
1004
+ // swap
1005
+ //
1006
+ // {
1007
+ // "method": "order.update",
1008
+ // "data": {
1009
+ // "event": "put",
1010
+ // "order": {
1011
+ // "order_id": 98388656341,
1012
+ // "stop_id": 0,
1013
+ // "market": "BTCUSDT",
1014
+ // "side": "buy",
1015
+ // "type": "limit",
1016
+ // "amount": "0.0010",
1017
+ // "price": "50000.00",
1018
+ // "unfilled_amount": "0.0010",
1019
+ // "filled_amount": "0",
1020
+ // "filled_value": "0",
1021
+ // "fee": "0",
1022
+ // "fee_ccy": "USDT",
1023
+ // "taker_fee_rate": "0.00046",
1024
+ // "maker_fee_rate": "0.00000000000000000000",
1025
+ // "client_id": "",
1026
+ // "last_filled_amount": "0.0010",
1027
+ // "last_filled_price": "30721.35",
1028
+ // "created_at": 1689145715129,
1029
+ // "updated_at": 1689145715129
1030
+ // }
1031
+ // },
1032
+ // "id": null
1033
+ // }
1034
+ //
1035
+ // swap stop
1036
+ //
1037
+ // {
1038
+ // "method": "stop.update",
1039
+ // "data": {
1040
+ // "event": "put",
1041
+ // "stop": {
1042
+ // "stop_id": 98389557871,
1043
+ // "market": "BTCUSDT",
1044
+ // "side": "sell",
1045
+ // "type": "limit",
1046
+ // "price": "20000.00",
1047
+ // "amount": "0.0100",
1048
+ // "trigger_price": "20000.00",
1049
+ // "trigger_direction": "higer",
1050
+ // "trigger_price_type": "index_price",
1051
+ // "taker_fee_rate": "0.00046",
1052
+ // "maker_fee_rate": "0.00026",
1053
+ // "client_id": "",
1054
+ // "created_at": 1689146382674,
1055
+ // "updated_at": 1689146382674
1056
+ // }
1057
+ // },
1058
+ // "id": null
1059
+ // }
767
1060
  //
768
- // {
769
- // "method": "order.update",
770
- // "params": [
771
- // 1,
772
- // {
773
- // "order_id": 23423462821,
774
- // "position_id": 0,
775
- // "stop_id": 0,
776
- // "market": "BTCUSDT",
777
- // "type": 1,
778
- // "side": 2,
779
- // "target": 0,
780
- // "effect_type": 1,
781
- // "user_id": 1849116,
782
- // "create_time": 1653961509.25049,
783
- // "update_time": 1653961509.25049,
784
- // "source": "web",
785
- // "price": "1.00",
786
- // "amount": "1.0000",
787
- // "taker_fee": "0.00050",
788
- // "maker_fee": "0.00030",
789
- // "left": "1.0000",
790
- // "deal_stock": "0.00000000000000000000",
791
- // "deal_fee": "0.00000000000000000000",
792
- // "deal_profit": "0.00000000000000000000",
793
- // "last_deal_amount": "0.00000000000000000000",
794
- // "last_deal_price": "0.00000000000000000000",
795
- // "last_deal_time": 0,
796
- // "last_deal_id": 0,
797
- // "last_deal_type": 0,
798
- // "last_deal_role": 0,
799
- // "client_id": '',
800
- // "fee_asset": '',
801
- // "fee_discount": "0.00000000000000000000",
802
- // "deal_asset_fee": "0.00000000000000000000",
803
- // "leverage": "3",
804
- // "position_type": 2
805
- // }
806
- // ],
807
- // "id": null
808
- // }
809
- //
810
- const params = this.safeValue(message, 'params', []);
811
- const order = this.safeValue(params, 1, {});
1061
+ const data = this.safeDict(message, 'data', {});
1062
+ const order = this.safeDict2(data, 'order', 'stop', {});
812
1063
  const parsedOrder = this.parseWsOrder(order);
1064
+ const symbol = parsedOrder['symbol'];
1065
+ const market = this.market(symbol);
813
1066
  if (this.orders === undefined) {
814
1067
  const limit = this.safeInteger(this.options, 'ordersLimit', 1000);
815
1068
  this.orders = new ArrayCacheBySymbolById(limit);
@@ -817,138 +1070,110 @@ export default class coinex extends coinexRest {
817
1070
  const orders = this.orders;
818
1071
  orders.append(parsedOrder);
819
1072
  let messageHash = 'orders';
820
- client.resolve(this.orders, messageHash);
821
- messageHash += ':' + parsedOrder['symbol'];
1073
+ const messageWithType = messageHash + ':' + market['type'];
1074
+ client.resolve(this.orders, messageWithType);
1075
+ messageHash += ':' + symbol;
822
1076
  client.resolve(this.orders, messageHash);
823
1077
  }
824
1078
  parseWsOrder(order, market = undefined) {
825
1079
  //
826
- // spot
1080
+ // spot
827
1081
  //
828
- // {
829
- // "id": 77782469357,
830
- // "type": 1,
831
- // "side": 2,
832
- // "user": 1849116,
833
- // "account": 0,
834
- // "option": 2,
835
- // "ctime": 1653961043.048967,
836
- // "mtime": 1653961043.048967,
837
- // "market": "BTCUSDT",
838
- // "source": "web",
839
- // "client_id": '',
840
- // "price": "1.00",
841
- // "amount": "1.00000000",
842
- // "taker_fee": "0.0020",
843
- // "maker_fee": "0.0020",
844
- // "left": "1.00000000",
845
- // "deal_stock": "0",
846
- // "deal_money": "0",
847
- // "money_fee": "0",
848
- // "stock_fee": "0",
849
- // "asset_fee": "0",
850
- // "fee_discount": "1",
851
- // "last_deal_amount": "0",
852
- // "last_deal_price": "0",
853
- // "last_deal_time": 0,
854
- // "last_deal_id": 0,
855
- // "last_role": 0,
856
- // "fee_asset": null,
857
- // "stop_id": 0
858
- // }
1082
+ // {
1083
+ // "order_id": 12750,
1084
+ // "market": "BTCUSDT",
1085
+ // "margin_market": "BTCUSDT",
1086
+ // "type": "limit",
1087
+ // "side": "buy",
1088
+ // "price": "5999.00",
1089
+ // "amount": "1.50000000",
1090
+ // "unfill_amount": "1.50000000",
1091
+ // "fill_value": "1.50000000",
1092
+ // "taker_fee_rate": "0.0001",
1093
+ // "maker_fee_rate": "0.0001",
1094
+ // "base_ccy_fee": "0.0001",
1095
+ // "quote_ccy_fee": "0.0001",
1096
+ // "discount_ccy_fee": "0.0001",
1097
+ // "last_fill_amount": "0",
1098
+ // "last_fill_price": "0",
1099
+ // "client_id": "buy1_1234",
1100
+ // "created_at": 1689152421692,
1101
+ // "updated_at": 1689152421692,
1102
+ // }
859
1103
  //
860
- // swap
1104
+ // spot stop
861
1105
  //
862
- // {
863
- // "order_id": 23423462821,
864
- // "position_id": 0,
865
- // "stop_id": 0,
866
- // "market": "BTCUSDT",
867
- // "type": 1,
868
- // "side": 2,
869
- // "target": 0,
870
- // "effect_type": 1,
871
- // "user_id": 1849116,
872
- // "create_time": 1653961509.25049,
873
- // "update_time": 1653961509.25049,
874
- // "source": "web",
875
- // "price": "1.00",
876
- // "amount": "1.0000",
877
- // "taker_fee": "0.00050",
878
- // "maker_fee": "0.00030",
879
- // "left": "1.0000",
880
- // "deal_stock": "0.00000000000000000000",
881
- // "deal_fee": "0.00000000000000000000",
882
- // "deal_profit": "0.00000000000000000000",
883
- // "last_deal_amount": "0.00000000000000000000",
884
- // "last_deal_price": "0.00000000000000000000",
885
- // "last_deal_time": 0,
886
- // "last_deal_id": 0,
887
- // "last_deal_type": 0,
888
- // "last_deal_role": 0,
889
- // "client_id": '',
890
- // "fee_asset": '',
891
- // "fee_discount": "0.00000000000000000000",
892
- // "deal_asset_fee": "0.00000000000000000000",
893
- // "leverage": "3",
894
- // "position_type": 2
895
- // }
896
- //
897
- // order.update_stop
898
- //
899
- // {
900
- // "id": 78006745870,
901
- // "type": 1,
902
- // "side": 2,
903
- // "user": 1849116,
904
- // "account": 1,
905
- // "option": 70,
906
- // "direction": 1,
907
- // "ctime": 1654171725.131976,
908
- // "mtime": 1654171725.131976,
909
- // "market": "BTCUSDT",
910
- // "source": "web",
911
- // "client_id": '',
912
- // "stop_price": "1.00",
913
- // "price": "1.00",
914
- // "amount": "1.00000000",
915
- // "taker_fee": "0.0020",
916
- // "maker_fee": "0.0020",
917
- // "fee_discount": "1",
918
- // "fee_asset": null,
919
- // "status": 0
920
- // }
921
- //
922
- const timestamp = this.safeTimestamp2(order, 'update_time', 'mtime');
1106
+ // {
1107
+ // "stop_id": 102067022299,
1108
+ // "market": "BTCUSDT",
1109
+ // "margin_market": "BTCUSDT",
1110
+ // "type": "limit",
1111
+ // "side": "buy",
1112
+ // "price": "20000.00",
1113
+ // "amount": "0.10000000",
1114
+ // "trigger_price": "20000.00",
1115
+ // "trigger_direction": "lower",
1116
+ // "taker_fee_rate": "0.0016",
1117
+ // "maker_fee_rate": "0.0016",
1118
+ // "status": "active_success",
1119
+ // "client_id": "",
1120
+ // "created_at": 1689152996689,
1121
+ // "updated_at": 1689152996689,
1122
+ // }
1123
+ //
1124
+ // swap
1125
+ //
1126
+ // {
1127
+ // "order_id": 98388656341,
1128
+ // "stop_id": 0,
1129
+ // "market": "BTCUSDT",
1130
+ // "side": "buy",
1131
+ // "type": "limit",
1132
+ // "amount": "0.0010",
1133
+ // "price": "50000.00",
1134
+ // "unfilled_amount": "0.0010",
1135
+ // "filled_amount": "0",
1136
+ // "filled_value": "0",
1137
+ // "fee": "0",
1138
+ // "fee_ccy": "USDT",
1139
+ // "taker_fee_rate": "0.00046",
1140
+ // "maker_fee_rate": "0.00000000000000000000",
1141
+ // "client_id": "",
1142
+ // "last_filled_amount": "0.0010",
1143
+ // "last_filled_price": "30721.35",
1144
+ // "created_at": 1689145715129,
1145
+ // "updated_at": 1689145715129
1146
+ // }
1147
+ //
1148
+ // swap stop
1149
+ //
1150
+ // {
1151
+ // "stop_id": 98389557871,
1152
+ // "market": "BTCUSDT",
1153
+ // "side": "sell",
1154
+ // "type": "limit",
1155
+ // "price": "20000.00",
1156
+ // "amount": "0.0100",
1157
+ // "trigger_price": "20000.00",
1158
+ // "trigger_direction": "higer",
1159
+ // "trigger_price_type": "index_price",
1160
+ // "taker_fee_rate": "0.00046",
1161
+ // "maker_fee_rate": "0.00026",
1162
+ // "client_id": "",
1163
+ // "created_at": 1689146382674,
1164
+ // "updated_at": 1689146382674
1165
+ // }
1166
+ //
1167
+ const timestamp = this.safeInteger(order, 'created_at');
923
1168
  const marketId = this.safeString(order, 'market');
924
- const typeCode = this.safeString(order, 'type');
925
- const type = this.safeString({
926
- '1': 'limit',
927
- '2': 'market',
928
- }, typeCode);
929
- const sideCode = this.safeString(order, 'side');
930
- const side = this.safeString({
931
- '1': 'sell',
932
- '2': 'buy',
933
- }, sideCode);
934
- const remaining = this.safeString(order, 'left');
935
- const amount = this.safeString(order, 'amount');
936
1169
  const status = this.safeString(order, 'status');
937
- const defaultType = this.safeString(this.options, 'defaultType');
1170
+ const isSpot = ('margin_market' in order);
1171
+ const defaultType = isSpot ? 'spot' : 'swap';
938
1172
  market = this.safeMarket(marketId, market, undefined, defaultType);
939
- let cost = this.safeString(order, 'deal_money');
940
- let filled = this.safeString(order, 'deal_stock');
941
- let average = undefined;
942
- if (market['swap']) {
943
- const leverage = this.safeString(order, 'leverage');
944
- cost = Precise.stringDiv(filled, leverage);
945
- average = Precise.stringDiv(filled, amount);
946
- filled = undefined;
947
- }
948
1173
  let fee = undefined;
949
- const feeCost = this.omitZero(this.safeString(order, 'money_fee'));
1174
+ const feeCost = this.omitZero(this.safeString2(order, 'fee', 'quote_ccy_fee'));
950
1175
  if (feeCost !== undefined) {
951
- const feeCurrencyId = this.safeString(order, 'fee_asset', market['quote']);
1176
+ const feeCurrencyId = this.safeString(order, 'fee_ccy', market['quote']);
952
1177
  fee = {
953
1178
  'currency': this.safeCurrencyCode(feeCurrencyId),
954
1179
  'cost': feeCost,
@@ -956,24 +1181,24 @@ export default class coinex extends coinexRest {
956
1181
  }
957
1182
  return this.safeOrder({
958
1183
  'info': order,
959
- 'id': this.safeString2(order, 'order_id', 'id'),
1184
+ 'id': this.safeString2(order, 'order_id', 'stop_id'),
960
1185
  'clientOrderId': this.safeString(order, 'client_id'),
961
1186
  'datetime': this.iso8601(timestamp),
962
1187
  'timestamp': timestamp,
963
- 'lastTradeTimestamp': this.safeTimestamp(order, 'last_deal_time'),
1188
+ 'lastTradeTimestamp': this.safeInteger(order, 'updated_at'),
964
1189
  'symbol': market['symbol'],
965
- 'type': type,
1190
+ 'type': this.safeString(order, 'type'),
966
1191
  'timeInForce': undefined,
967
1192
  'postOnly': undefined,
968
- 'side': side,
1193
+ 'side': this.safeString(order, 'side'),
969
1194
  'price': this.safeString(order, 'price'),
970
- 'stopPrice': this.safeString(order, 'stop_price'),
971
- 'triggerPrice': this.safeString(order, 'stop_price'),
972
- 'amount': amount,
973
- 'filled': filled,
974
- 'remaining': remaining,
975
- 'cost': cost,
976
- 'average': average,
1195
+ 'stopPrice': this.safeString(order, 'trigger_price'),
1196
+ 'triggerPrice': this.safeString(order, 'trigger_price'),
1197
+ 'amount': this.safeString(order, 'amount'),
1198
+ 'filled': this.safeString2(order, 'filled_amount', 'fill_value'),
1199
+ 'remaining': this.safeString2(order, 'unfilled_amount', 'unfill_amount'),
1200
+ 'cost': undefined,
1201
+ 'average': undefined,
977
1202
  'status': this.parseWsOrderStatus(status),
978
1203
  'fee': fee,
979
1204
  'trades': undefined,
@@ -981,25 +1206,116 @@ export default class coinex extends coinexRest {
981
1206
  }
982
1207
  parseWsOrderStatus(status) {
983
1208
  const statuses = {
984
- '0': 'pending',
985
- '1': 'ok',
1209
+ 'active_success': 'open',
1210
+ 'active_fail': 'canceled',
1211
+ 'cancel': 'canceled',
986
1212
  };
987
1213
  return this.safeString(statuses, status, status);
988
1214
  }
1215
+ async watchBidsAsks(symbols = undefined, params = {}) {
1216
+ /**
1217
+ * @method
1218
+ * @name coinex#watchBidsAsks
1219
+ * @description watches best bid & ask for symbols
1220
+ * @see https://docs.coinex.com/api/v2/spot/market/ws/market-bbo
1221
+ * @see https://docs.coinex.com/api/v2/futures/market/ws/market-bbo
1222
+ * @param {string[]} [symbols] unified symbol of the market to fetch the ticker for
1223
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1224
+ * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
1225
+ */
1226
+ await this.loadMarkets();
1227
+ const marketIds = this.marketIds(symbols);
1228
+ const messageHashes = [];
1229
+ let market = undefined;
1230
+ const symbolsDefined = (symbols !== undefined);
1231
+ if (symbolsDefined) {
1232
+ for (let i = 0; i < symbols.length; i++) {
1233
+ const symbol = symbols[i];
1234
+ market = this.market(symbol);
1235
+ messageHashes.push('bidsasks:' + market['symbol']);
1236
+ }
1237
+ }
1238
+ else {
1239
+ messageHashes.push('bidsasks');
1240
+ }
1241
+ let type = undefined;
1242
+ [type, params] = this.handleMarketTypeAndParams('watchBidsAsks', market, params);
1243
+ const url = this.urls['api']['ws'][type];
1244
+ const subscriptionHashes = ['all@bidsasks'];
1245
+ const subscribe = {
1246
+ 'method': 'bbo.subscribe',
1247
+ 'params': { 'market_list': marketIds },
1248
+ 'id': this.requestId(),
1249
+ };
1250
+ const result = await this.watchMultiple(url, messageHashes, this.deepExtend(subscribe, params), subscriptionHashes);
1251
+ if (this.newUpdates) {
1252
+ return result;
1253
+ }
1254
+ return this.filterByArray(this.bidsasks, 'symbol', symbols);
1255
+ }
1256
+ handleBidAsk(client, message) {
1257
+ //
1258
+ // {
1259
+ // "method": "bbo.update",
1260
+ // "data": {
1261
+ // "market": "BTCUSDT",
1262
+ // "updated_at": 1656660154,
1263
+ // "best_bid_price": "20000",
1264
+ // "best_bid_size": "0.1",
1265
+ // "best_ask_price": "20001",
1266
+ // "best_ask_size": "0.15"
1267
+ // },
1268
+ // "id": null
1269
+ // }
1270
+ //
1271
+ const data = this.safeDict(message, 'data', {});
1272
+ const parsedTicker = this.parseWsBidAsk(data);
1273
+ const symbol = parsedTicker['symbol'];
1274
+ this.bidsasks[symbol] = parsedTicker;
1275
+ const messageHash = 'bidsasks:' + symbol;
1276
+ client.resolve(parsedTicker, messageHash);
1277
+ }
1278
+ parseWsBidAsk(ticker, market = undefined) {
1279
+ //
1280
+ // {
1281
+ // "market": "BTCUSDT",
1282
+ // "updated_at": 1656660154,
1283
+ // "best_bid_price": "20000",
1284
+ // "best_bid_size": "0.1",
1285
+ // "best_ask_price": "20001",
1286
+ // "best_ask_size": "0.15"
1287
+ // }
1288
+ //
1289
+ const defaultType = this.safeString(this.options, 'defaultType');
1290
+ const marketId = this.safeString(ticker, 'market');
1291
+ market = this.safeMarket(marketId, market, undefined, defaultType);
1292
+ const timestamp = this.safeTimestamp(ticker, 'updated_at');
1293
+ return this.safeTicker({
1294
+ 'symbol': this.safeSymbol(marketId, market, undefined, defaultType),
1295
+ 'timestamp': timestamp,
1296
+ 'datetime': this.iso8601(timestamp),
1297
+ 'ask': this.safeNumber(ticker, 'best_ask_price'),
1298
+ 'askVolume': this.safeNumber(ticker, 'best_ask_size'),
1299
+ 'bid': this.safeNumber(ticker, 'best_bid_price'),
1300
+ 'bidVolume': this.safeNumber(ticker, 'best_bid_size'),
1301
+ 'info': ticker,
1302
+ }, market);
1303
+ }
989
1304
  handleMessage(client, message) {
990
- const error = this.safeValue(message, 'error');
1305
+ const method = this.safeString(message, 'method');
1306
+ const error = this.safeString(message, 'message');
991
1307
  if (error !== undefined) {
992
- throw new ExchangeError(this.id + ' ' + this.json(error));
1308
+ this.handleErrors(undefined, undefined, client.url, method, undefined, this.json(error), message, undefined, undefined);
993
1309
  }
994
- const method = this.safeString(message, 'method');
995
1310
  const handlers = {
996
1311
  'state.update': this.handleTicker,
997
- 'asset.update': this.handleBalance,
1312
+ 'balance.update': this.handleBalance,
998
1313
  'deals.update': this.handleTrades,
1314
+ 'user_deals.update': this.handleMyTrades,
999
1315
  'depth.update': this.handleOrderBook,
1000
1316
  'order.update': this.handleOrders,
1001
- 'kline.update': this.handleOHLCV,
1002
- 'order.update_stop': this.handleOrders,
1317
+ 'stop.update': this.handleOrders,
1318
+ 'bbo.update': this.handleBidAsk,
1003
1319
  };
1004
1320
  const handler = this.safeValue(handlers, method);
1005
1321
  if (handler !== undefined) {
@@ -1008,35 +1324,65 @@ export default class coinex extends coinexRest {
1008
1324
  }
1009
1325
  this.handleSubscriptionStatus(client, message);
1010
1326
  }
1327
+ handleErrors(code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
1328
+ if (response === undefined) {
1329
+ return undefined;
1330
+ }
1331
+ //
1332
+ // { "id": 1, "code": 20001, "message": "invalid argument" }
1333
+ // { "id": 2, "code": 21001, "message": "require auth" }
1334
+ // { "id": 1, "code": 21002, "message": "Signature Incorrect" }
1335
+ //
1336
+ const message = this.safeStringLower(response, 'message');
1337
+ const isErrorMessage = (message !== undefined) && (message !== 'ok');
1338
+ const errorCode = this.safeString(response, 'code');
1339
+ const isErrorCode = (errorCode !== undefined) && (errorCode !== '0');
1340
+ if (isErrorCode || isErrorMessage) {
1341
+ const feedback = this.id + ' ' + body;
1342
+ this.throwExactlyMatchedException(this.exceptions['exact'], errorCode, feedback);
1343
+ this.throwBroadlyMatchedException(this.exceptions['broad'], message, feedback);
1344
+ throw new ExchangeError(feedback);
1345
+ }
1346
+ return undefined;
1347
+ }
1011
1348
  handleAuthenticationMessage(client, message) {
1349
+ //
1350
+ // success
1012
1351
  //
1013
1352
  // {
1014
- // "error": null,
1015
- // "result": {
1016
- // "status": "success"
1017
- // },
1018
- // "id": 1
1353
+ // "id": 1,
1354
+ // "code": 0,
1355
+ // "message": "OK"
1356
+ // }
1357
+ //
1358
+ // fail
1359
+ //
1360
+ // {
1361
+ // "id": 1,
1362
+ // "code": 21002,
1363
+ // "message": ""
1019
1364
  // }
1020
1365
  //
1021
- const messageHashSpot = 'authenticated:spot';
1022
- const messageHashSwap = 'authenticated:swap';
1023
- // client.resolve (message, messageHashSpot);
1024
- // client.resolve (message, messageHashSwap);
1025
- const spotFuture = this.safeValue(client.futures, messageHashSpot);
1026
- spotFuture.resolve(true);
1027
- const swapFutures = this.safeValue(client.futures, messageHashSwap);
1028
- swapFutures.resolve(true);
1029
- return message;
1366
+ const status = this.safeStringLower(message, 'message');
1367
+ const errorCode = this.safeString(message, 'code');
1368
+ const messageHash = 'authenticated';
1369
+ if ((status === 'ok') || (errorCode === '0')) {
1370
+ const future = this.safeValue(client.futures, messageHash);
1371
+ future.resolve(true);
1372
+ }
1373
+ else {
1374
+ const error = new AuthenticationError(this.json(message));
1375
+ client.reject(error, messageHash);
1376
+ if (messageHash in client.subscriptions) {
1377
+ delete client.subscriptions[messageHash];
1378
+ }
1379
+ }
1030
1380
  }
1031
1381
  handleSubscriptionStatus(client, message) {
1032
1382
  const id = this.safeInteger(message, 'id');
1033
1383
  const subscription = this.safeValue(client.subscriptions, id);
1034
1384
  if (subscription !== undefined) {
1035
1385
  const futureIndex = this.safeString(subscription, 'future');
1036
- if (futureIndex === 'ohlcv') {
1037
- this.handleOHLCV(client, message);
1038
- return;
1039
- }
1040
1386
  const future = this.safeValue(client.futures, futureIndex);
1041
1387
  if (future !== undefined) {
1042
1388
  future.resolve(true);
@@ -1044,65 +1390,34 @@ export default class coinex extends coinexRest {
1044
1390
  delete client.subscriptions[id];
1045
1391
  }
1046
1392
  }
1047
- async authenticate(params = {}) {
1048
- let type = undefined;
1049
- [type, params] = this.handleMarketTypeAndParams('authenticate', undefined, params);
1393
+ async authenticate(type) {
1050
1394
  const url = this.urls['api']['ws'][type];
1051
1395
  const client = this.client(url);
1052
1396
  const time = this.milliseconds();
1053
- const isSpot = (type === 'spot');
1054
- const spotMessageHash = 'authenticated:spot';
1055
- const swapMessageHash = 'authenticated:swap';
1056
- const messageHash = isSpot ? spotMessageHash : swapMessageHash;
1397
+ const timestamp = time.toString();
1398
+ const messageHash = 'authenticated';
1057
1399
  const future = client.future(messageHash);
1058
1400
  const authenticated = this.safeValue(client.subscriptions, messageHash);
1059
- if (type === 'spot') {
1060
- if (authenticated !== undefined) {
1061
- return await future;
1062
- }
1063
- const requestId = this.requestId();
1064
- const subscribe = {
1065
- 'id': requestId,
1066
- 'future': spotMessageHash,
1067
- };
1068
- const signData = 'access_id=' + this.apiKey + '&tonce=' + this.numberToString(time) + '&secret_key=' + this.secret;
1069
- const hash = this.hash(this.encode(signData), md5);
1070
- const request = {
1071
- 'method': 'server.sign',
1072
- 'params': [
1073
- this.apiKey,
1074
- hash.toUpperCase(),
1075
- time,
1076
- ],
1077
- 'id': requestId,
1078
- };
1079
- this.watch(url, messageHash, request, requestId, subscribe);
1080
- client.subscriptions[messageHash] = true;
1081
- return await future;
1082
- }
1083
- else {
1084
- if (authenticated !== undefined) {
1085
- return await future;
1086
- }
1087
- const requestId = this.requestId();
1088
- const subscribe = {
1089
- 'id': requestId,
1090
- 'future': swapMessageHash,
1091
- };
1092
- const signData = 'access_id=' + this.apiKey + '&timestamp=' + this.numberToString(time) + '&secret_key=' + this.secret;
1093
- const hash = this.hash(this.encode(signData), sha256, 'hex');
1094
- const request = {
1095
- 'method': 'server.sign',
1096
- 'params': [
1097
- this.apiKey,
1098
- hash.toLowerCase(),
1099
- time,
1100
- ],
1101
- 'id': requestId,
1102
- };
1103
- this.watch(url, messageHash, request, requestId, subscribe);
1104
- client.subscriptions[messageHash] = true;
1401
+ if (authenticated !== undefined) {
1105
1402
  return await future;
1106
1403
  }
1404
+ const requestId = this.requestId();
1405
+ const subscribe = {
1406
+ 'id': requestId,
1407
+ 'future': messageHash,
1408
+ };
1409
+ const hmac = this.hmac(this.encode(timestamp), this.encode(this.secret), sha256, 'hex');
1410
+ const request = {
1411
+ 'id': requestId,
1412
+ 'method': 'server.sign',
1413
+ 'params': {
1414
+ 'access_id': this.apiKey,
1415
+ 'signed_str': hmac.toLowerCase(),
1416
+ 'timestamp': time,
1417
+ },
1418
+ };
1419
+ this.watch(url, messageHash, request, requestId, subscribe);
1420
+ client.subscriptions[messageHash] = true;
1421
+ return await future;
1107
1422
  }
1108
1423
  }