ccxt 4.4.21 → 4.4.22

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 (52) hide show
  1. package/README.md +4 -4
  2. package/dist/ccxt.browser.min.js +4 -4
  3. package/dist/cjs/ccxt.js +1 -1
  4. package/dist/cjs/src/alpaca.js +1 -0
  5. package/dist/cjs/src/base/Exchange.js +21 -0
  6. package/dist/cjs/src/bigone.js +3 -0
  7. package/dist/cjs/src/binance.js +103 -0
  8. package/dist/cjs/src/bitflyer.js +57 -0
  9. package/dist/cjs/src/bitget.js +77 -0
  10. package/dist/cjs/src/bybit.js +78 -0
  11. package/dist/cjs/src/cex.js +1307 -1385
  12. package/dist/cjs/src/cryptocom.js +1 -1
  13. package/dist/cjs/src/gate.js +103 -3
  14. package/dist/cjs/src/htx.js +1 -7
  15. package/dist/cjs/src/hyperliquid.js +10 -8
  16. package/dist/cjs/src/kucoin.js +27 -59
  17. package/dist/cjs/src/okx.js +74 -0
  18. package/js/ccxt.d.ts +3 -3
  19. package/js/ccxt.js +1 -1
  20. package/js/src/abstract/bitflyer.d.ts +1 -0
  21. package/js/src/abstract/bitget.d.ts +3 -0
  22. package/js/src/abstract/cex.d.ts +28 -29
  23. package/js/src/abstract/gate.d.ts +5 -0
  24. package/js/src/abstract/gateio.d.ts +5 -0
  25. package/js/src/abstract/kucoin.d.ts +1 -0
  26. package/js/src/abstract/kucoinfutures.d.ts +1 -0
  27. package/js/src/abstract/okx.d.ts +1 -0
  28. package/js/src/alpaca.js +1 -0
  29. package/js/src/base/Exchange.d.ts +8 -2
  30. package/js/src/base/Exchange.js +21 -0
  31. package/js/src/base/types.d.ts +8 -0
  32. package/js/src/bigone.js +3 -0
  33. package/js/src/binance.d.ts +3 -1
  34. package/js/src/binance.js +103 -0
  35. package/js/src/bitflyer.d.ts +3 -1
  36. package/js/src/bitflyer.js +57 -0
  37. package/js/src/bitget.d.ts +3 -1
  38. package/js/src/bitget.js +77 -0
  39. package/js/src/bybit.d.ts +3 -1
  40. package/js/src/bybit.js +78 -0
  41. package/js/src/cex.d.ts +34 -20
  42. package/js/src/cex.js +1308 -1386
  43. package/js/src/cryptocom.js +1 -1
  44. package/js/src/gate.d.ts +2 -0
  45. package/js/src/gate.js +103 -3
  46. package/js/src/htx.js +1 -7
  47. package/js/src/hyperliquid.js +10 -8
  48. package/js/src/kucoin.d.ts +0 -1
  49. package/js/src/kucoin.js +27 -59
  50. package/js/src/okx.d.ts +3 -1
  51. package/js/src/okx.js +74 -0
  52. package/package.json +1 -1
@@ -18,7 +18,7 @@ class cex extends cex$1 {
18
18
  'id': 'cex',
19
19
  'name': 'CEX.IO',
20
20
  'countries': ['GB', 'EU', 'CY', 'RU'],
21
- 'rateLimit': 1500,
21
+ 'rateLimit': 1667,
22
22
  'pro': true,
23
23
  'has': {
24
24
  'CORS': undefined,
@@ -27,152 +27,78 @@ class cex extends cex$1 {
27
27
  'swap': false,
28
28
  'future': false,
29
29
  'option': false,
30
- 'addMargin': false,
31
30
  'cancelAllOrders': true,
32
31
  'cancelOrder': true,
33
- 'cancelOrders': false,
34
- 'createDepositAddress': false,
35
- 'createMarketBuyOrderWithCost': true,
36
- 'createMarketOrderWithCost': false,
37
- 'createMarketSellOrderWithCost': false,
38
32
  'createOrder': true,
39
- 'createStopLimitOrder': false,
40
- 'createStopMarketOrder': false,
41
- 'createStopOrder': false,
42
- 'editOrder': true,
33
+ 'fetchAccounts': true,
43
34
  'fetchBalance': true,
44
35
  'fetchClosedOrders': true,
45
36
  'fetchCurrencies': true,
46
- 'fetchDeposit': false,
47
37
  'fetchDepositAddress': true,
48
- 'fetchDepositAddresses': false,
49
- 'fetchDepositAddressesByNetwork': false,
50
- 'fetchDeposits': false,
51
- 'fetchDepositsWithdrawals': false,
52
- 'fetchFundingHistory': false,
53
- 'fetchFundingRate': false,
54
- 'fetchFundingRateHistory': false,
55
- 'fetchFundingRates': false,
56
- 'fetchIndexOHLCV': false,
57
- 'fetchMarginMode': false,
38
+ 'fetchDepositsWithdrawals': true,
39
+ 'fetchLedger': true,
58
40
  'fetchMarkets': true,
59
- 'fetchMarkOHLCV': false,
60
41
  'fetchOHLCV': true,
61
- 'fetchOpenInterestHistory': false,
62
42
  'fetchOpenOrders': true,
63
- 'fetchOrder': true,
64
43
  'fetchOrderBook': true,
65
- 'fetchOrders': true,
66
- 'fetchPosition': false,
67
- 'fetchPositionHistory': false,
68
- 'fetchPositionMode': false,
69
- 'fetchPositions': false,
70
- 'fetchPositionsForSymbol': false,
71
- 'fetchPositionsHistory': false,
72
- 'fetchPositionsRisk': false,
73
- 'fetchPremiumIndexOHLCV': false,
74
44
  'fetchTicker': true,
75
45
  'fetchTickers': true,
46
+ 'fetchTime': true,
76
47
  'fetchTrades': true,
77
- 'fetchTradingFee': false,
78
48
  'fetchTradingFees': true,
79
- 'fetchTransactions': false,
80
- 'fetchTransfer': false,
81
- 'fetchTransfers': false,
82
- 'fetchWithdrawal': false,
83
- 'fetchWithdrawals': false,
84
- 'fetchWithdrawalWhitelist': false,
85
- 'reduceMargin': false,
86
- 'setLeverage': false,
87
- 'setMargin': false,
88
- 'setMarginMode': false,
89
- 'transfer': false,
90
- 'withdraw': false,
91
- },
92
- 'timeframes': {
93
- '1m': '1m',
94
- '1h': '1h',
95
- '1d': '1d',
49
+ 'transfer': true,
96
50
  },
97
51
  'urls': {
98
52
  'logo': 'https://user-images.githubusercontent.com/1294454/27766442-8ddc33b0-5ed8-11e7-8b98-f786aef0f3c9.jpg',
99
53
  'api': {
100
- 'rest': 'https://cex.io/api',
54
+ 'public': 'https://trade.cex.io/api/spot/rest-public',
55
+ 'private': 'https://trade.cex.io/api/spot/rest',
101
56
  },
102
57
  'www': 'https://cex.io',
103
- 'doc': 'https://cex.io/cex-api',
58
+ 'doc': 'https://trade.cex.io/docs/',
104
59
  'fees': [
105
60
  'https://cex.io/fee-schedule',
106
61
  'https://cex.io/limits-commissions',
107
62
  ],
108
63
  'referral': 'https://cex.io/r/0/up105393824/0/',
109
64
  },
110
- 'requiredCredentials': {
111
- 'apiKey': true,
112
- 'secret': true,
113
- 'uid': true,
114
- },
115
65
  'api': {
116
66
  'public': {
117
- 'get': [
118
- 'currency_profile',
119
- 'currency_limits/',
120
- 'last_price/{pair}/',
121
- 'last_prices/{currencies}/',
122
- 'ohlcv/hd/{yyyymmdd}/{pair}',
123
- 'order_book/{pair}/',
124
- 'ticker/{pair}/',
125
- 'tickers/{currencies}/',
126
- 'trade_history/{pair}/',
127
- ],
128
- 'post': [
129
- 'convert/{pair}',
130
- 'price_stats/{pair}',
131
- ],
67
+ 'get': {},
68
+ 'post': {
69
+ 'get_server_time': 1,
70
+ 'get_pairs_info': 1,
71
+ 'get_currencies_info': 1,
72
+ 'get_processing_info': 10,
73
+ 'get_ticker': 1,
74
+ 'get_trade_history': 1,
75
+ 'get_order_book': 1,
76
+ 'get_candles': 1,
77
+ },
132
78
  },
133
79
  'private': {
134
- 'post': [
135
- 'active_orders_status/',
136
- 'archived_orders/{pair}/',
137
- 'balance/',
138
- 'cancel_order/',
139
- 'cancel_orders/{pair}/',
140
- 'cancel_replace_order/{pair}/',
141
- 'close_position/{pair}/',
142
- 'get_address/',
143
- 'get_crypto_address',
144
- 'get_myfee/',
145
- 'get_order/',
146
- 'get_order_tx/',
147
- 'open_orders/{pair}/',
148
- 'open_orders/',
149
- 'open_position/{pair}/',
150
- 'open_positions/{pair}/',
151
- 'place_order/{pair}/',
152
- 'raw_tx_history',
153
- ],
154
- },
155
- },
156
- 'fees': {
157
- 'trading': {
158
- 'maker': this.parseNumber('0.0016'),
159
- 'taker': this.parseNumber('0.0025'),
160
- },
161
- 'funding': {
162
- 'withdraw': {},
163
- 'deposit': {
164
- // 'USD': amount => amount * 0.035 + 0.25,
165
- // 'EUR': amount => amount * 0.035 + 0.24,
166
- // 'RUB': amount => amount * 0.05 + 15.57,
167
- // 'GBP': amount => amount * 0.035 + 0.2,
168
- 'BTC': 0.0,
169
- 'ETH': 0.0,
170
- 'BCH': 0.0,
171
- 'DASH': 0.0,
172
- 'BTG': 0.0,
173
- 'ZEC': 0.0,
174
- 'XRP': 0.0,
175
- 'XLM': 0.0,
80
+ 'get': {},
81
+ 'post': {
82
+ 'get_my_current_fee': 5,
83
+ 'get_fee_strategy': 1,
84
+ 'get_my_volume': 5,
85
+ 'do_create_account': 1,
86
+ 'get_my_account_status_v3': 5,
87
+ 'get_my_wallet_balance': 5,
88
+ 'get_my_orders': 5,
89
+ 'do_my_new_order': 1,
90
+ 'do_cancel_my_order': 1,
91
+ 'do_cancel_all_orders': 5,
92
+ 'get_order_book': 1,
93
+ 'get_candles': 1,
94
+ 'get_trade_history': 1,
95
+ 'get_my_transaction_history': 1,
96
+ 'get_my_funding_history': 5,
97
+ 'do_my_internal_transfer': 1,
98
+ 'get_processing_info': 10,
99
+ 'get_deposit_address': 5,
100
+ 'do_deposit_funds_from_wallet': 1,
101
+ 'do_withdrawal_funds_to_wallet': 1,
176
102
  },
177
103
  },
178
104
  },
@@ -180,1520 +106,1516 @@ class cex extends cex$1 {
180
106
  'exceptions': {
181
107
  'exact': {},
182
108
  'broad': {
109
+ 'You have negative balance on following accounts': errors.InsufficientFunds,
110
+ 'Mandatory parameter side should be one of BUY,SELL': errors.BadRequest,
111
+ 'API orders from Main account are not allowed': errors.BadRequest,
112
+ 'check failed': errors.BadRequest,
183
113
  'Insufficient funds': errors.InsufficientFunds,
184
- 'Nonce must be incremented': errors.InvalidNonce,
185
- 'Invalid Order': errors.InvalidOrder,
186
- 'Order not found': errors.OrderNotFound,
187
- 'limit exceeded': errors.RateLimitExceeded,
188
- 'Invalid API key': errors.AuthenticationError,
189
- 'There was an error while placing your order': errors.InvalidOrder,
190
- 'Sorry, too many clients already': errors.DDoSProtection,
191
- 'Invalid Symbols Pair': errors.BadSymbol,
192
- 'Wrong currency pair': errors.BadSymbol, // {"error":"There was an error while placing your order: Wrong currency pair.","safe":true}
114
+ 'Get deposit address for main account is not allowed': errors.PermissionDenied,
115
+ 'Market Trigger orders are not allowed': errors.BadRequest, // for some reason, triggerPrice does not work for market orders
193
116
  },
194
117
  },
118
+ 'timeframes': {
119
+ '1m': '1m',
120
+ '5m': '5m',
121
+ '15m': '15m',
122
+ '30m': '30m',
123
+ '1h': '1h',
124
+ '2h': '2h',
125
+ '4h': '4h',
126
+ '1d': '1d',
127
+ },
195
128
  'options': {
196
- 'fetchOHLCVWarning': true,
197
- 'createMarketBuyOrderRequiresPrice': true,
198
- 'order': {
199
- 'status': {
200
- 'c': 'canceled',
201
- 'd': 'closed',
202
- 'cd': 'canceled',
203
- 'a': 'open',
204
- },
205
- },
206
- 'defaultNetwork': 'ERC20',
207
- 'defaultNetworks': {
208
- 'USDT': 'TRC20',
209
- },
210
129
  'networks': {
211
- 'ERC20': 'Ethereum',
212
- 'BTC': 'BTC',
213
- 'BEP20': 'Binance Smart Chain',
214
- 'TRC20': 'Tron',
130
+ 'BTC': 'bitcoin',
131
+ 'ERC20': 'ERC20',
132
+ 'BSC20': 'binancesmartchain',
133
+ 'DOGE': 'dogecoin',
134
+ 'ALGO': 'algorand',
135
+ 'XLM': 'stellar',
136
+ 'ATOM': 'cosmos',
137
+ 'LTC': 'litecoin',
138
+ 'XRP': 'ripple',
139
+ 'FTM': 'fantom',
140
+ 'MINA': 'mina',
141
+ 'THETA': 'theta',
142
+ 'XTZ': 'tezos',
143
+ 'TIA': 'celestia',
144
+ 'CRONOS': 'cronos',
145
+ 'MATIC': 'polygon',
146
+ 'TON': 'ton',
147
+ 'TRC20': 'tron',
148
+ 'SOLANA': 'solana',
149
+ 'SGB': 'songbird',
150
+ 'DYDX': 'dydx',
151
+ 'DASH': 'dash',
152
+ 'ZIL': 'zilliqa',
153
+ 'EOS': 'eos',
154
+ 'AVALANCHEC': 'avalanche',
155
+ 'ETHPOW': 'ethereumpow',
156
+ 'NEAR': 'near',
157
+ 'ARB': 'arbitrum',
158
+ 'DOT': 'polkadot',
159
+ 'OPT': 'optimism',
160
+ 'INJ': 'injective',
161
+ 'ADA': 'cardano',
162
+ 'ONT': 'ontology',
163
+ 'ICP': 'icp',
164
+ 'KAVA': 'kava',
165
+ 'KSM': 'kusama',
166
+ 'SEI': 'sei',
167
+ // 'OSM': 'osmosis',
168
+ 'NEO': 'neo',
169
+ 'NEO3': 'neo3',
170
+ // 'TERRAOLD': 'terra', // tbd
171
+ // 'TERRA': 'terra2', // tbd
172
+ // 'EVER': 'everscale', // tbd
173
+ 'XDC': 'xdc',
215
174
  },
216
175
  },
217
176
  });
218
177
  }
219
- async fetchCurrenciesFromCache(params = {}) {
220
- // this method is now redundant
221
- // currencies are now fetched before markets
222
- const options = this.safeValue(this.options, 'fetchCurrencies', {});
223
- const timestamp = this.safeInteger(options, 'timestamp');
224
- const expires = this.safeInteger(options, 'expires', 1000);
225
- const now = this.milliseconds();
226
- if ((timestamp === undefined) || ((now - timestamp) > expires)) {
227
- const response = await this.publicGetCurrencyProfile(params);
228
- this.options['fetchCurrencies'] = this.extend(options, {
229
- 'response': response,
230
- 'timestamp': now,
231
- });
232
- }
233
- return this.safeValue(this.options['fetchCurrencies'], 'response');
234
- }
235
178
  async fetchCurrencies(params = {}) {
236
179
  /**
237
180
  * @method
238
181
  * @name cex#fetchCurrencies
239
182
  * @description fetches all available currencies on an exchange
240
- * @param {object} [params] extra parameters specific to the exchange API endpoint
241
- * @returns {object} an associative dictionary of currencies
183
+ * @see https://trade.cex.io/docs/#rest-public-api-calls-currencies-info
184
+ * @param {dict} [params] extra parameters specific to the exchange API endpoint
185
+ * @returns {dict} an associative dictionary of currencies
242
186
  */
243
- const response = await this.fetchCurrenciesFromCache(params);
244
- this.options['currencies'] = {
245
- 'timestamp': this.milliseconds(),
246
- 'response': response,
247
- };
187
+ const promises = [];
188
+ promises.push(this.publicPostGetCurrenciesInfo(params));
248
189
  //
249
- // {
250
- // "e":"currency_profile",
251
- // "ok":"ok",
252
- // "data":{
253
- // "symbols":[
254
- // {
255
- // "code":"GHS",
256
- // "contract":true,
257
- // "commodity":true,
258
- // "fiat":false,
259
- // "description":"CEX.IO doesn't provide cloud mining services anymore.",
260
- // "precision":8,
261
- // "scale":0,
262
- // "minimumCurrencyAmount":"0.00000001",
263
- // "minimalWithdrawalAmount":-1
264
- // },
265
- // {
266
- // "code":"BTC",
267
- // "contract":false,
268
- // "commodity":false,
269
- // "fiat":false,
270
- // "description":"",
271
- // "precision":8,
272
- // "scale":0,
273
- // "minimumCurrencyAmount":"0.00000001",
274
- // "minimalWithdrawalAmount":0.002
275
- // },
276
- // {
277
- // "code":"ETH",
278
- // "contract":false,
279
- // "commodity":false,
280
- // "fiat":false,
281
- // "description":"",
282
- // "precision":8,
283
- // "scale":2,
284
- // "minimumCurrencyAmount":"0.00000100",
285
- // "minimalWithdrawalAmount":0.01
286
- // }
287
- // ],
288
- // "pairs":[
289
- // {
290
- // "symbol1":"BTC",
291
- // "symbol2":"USD",
292
- // "pricePrecision":1,
293
- // "priceScale":"/1000000",
294
- // "minLotSize":0.002,
295
- // "minLotSizeS2":20
296
- // },
297
- // {
298
- // "symbol1":"ETH",
299
- // "symbol2":"USD",
300
- // "pricePrecision":2,
301
- // "priceScale":"/10000",
302
- // "minLotSize":0.1,
303
- // "minLotSizeS2":20
304
- // }
305
- // ]
306
- // }
307
- // }
190
+ // {
191
+ // "ok": "ok",
192
+ // "data": [
193
+ // {
194
+ // "currency": "ZAP",
195
+ // "fiat": false,
196
+ // "precision": "8",
197
+ // "walletPrecision": "6",
198
+ // "walletDeposit": true,
199
+ // "walletWithdrawal": true
200
+ // },
201
+ // ...
308
202
  //
309
- const data = this.safeValue(response, 'data', []);
310
- const currencies = this.safeValue(data, 'symbols', []);
311
- const result = {};
312
- for (let i = 0; i < currencies.length; i++) {
313
- const currency = currencies[i];
314
- const id = this.safeString(currency, 'code');
315
- const code = this.safeCurrencyCode(id);
316
- const active = true;
317
- result[code] = {
318
- 'id': id,
319
- 'code': code,
320
- 'name': id,
321
- 'active': active,
322
- 'deposit': undefined,
323
- 'withdraw': undefined,
324
- 'precision': this.parseNumber(this.parsePrecision(this.safeString(currency, 'precision'))),
325
- 'fee': undefined,
203
+ promises.push(this.publicPostGetProcessingInfo(params));
204
+ //
205
+ // {
206
+ // "ok": "ok",
207
+ // "data": {
208
+ // "ADA": {
209
+ // "name": "Cardano",
210
+ // "blockchains": {
211
+ // "cardano": {
212
+ // "type": "coin",
213
+ // "deposit": "enabled",
214
+ // "minDeposit": "1",
215
+ // "withdrawal": "enabled",
216
+ // "minWithdrawal": "5",
217
+ // "withdrawalFee": "1",
218
+ // "withdrawalFeePercent": "0",
219
+ // "depositConfirmations": "15"
220
+ // }
221
+ // }
222
+ // },
223
+ // ...
224
+ //
225
+ const responses = await Promise.all(promises);
226
+ const dataCurrencies = this.safeList(responses[0], 'data', []);
227
+ const dataNetworks = this.safeDict(responses[1], 'data', {});
228
+ const currenciesIndexed = this.indexBy(dataCurrencies, 'currency');
229
+ const data = this.deepExtend(currenciesIndexed, dataNetworks);
230
+ return this.parseCurrencies(this.toArray(data));
231
+ }
232
+ parseCurrency(rawCurrency) {
233
+ const id = this.safeString(rawCurrency, 'currency');
234
+ const code = this.safeCurrencyCode(id);
235
+ const type = this.safeBool(rawCurrency, 'fiat') ? 'fiat' : 'crypto';
236
+ const currencyDepositEnabled = this.safeBool(rawCurrency, 'walletDeposit');
237
+ const currencyWithdrawEnabled = this.safeBool(rawCurrency, 'walletWithdrawal');
238
+ const currencyPrecision = this.parseNumber(this.parsePrecision(this.safeString(rawCurrency, 'precision')));
239
+ const networks = {};
240
+ const rawNetworks = this.safeDict(rawCurrency, 'blockchains', {});
241
+ const keys = Object.keys(rawNetworks);
242
+ for (let j = 0; j < keys.length; j++) {
243
+ const networkId = keys[j];
244
+ const rawNetwork = rawNetworks[networkId];
245
+ const networkCode = this.networkIdToCode(networkId);
246
+ const deposit = this.safeString(rawNetwork, 'deposit') === 'enabled';
247
+ const withdraw = this.safeString(rawNetwork, 'withdrawal') === 'enabled';
248
+ networks[networkCode] = {
249
+ 'id': networkId,
250
+ 'network': networkCode,
251
+ 'margin': undefined,
252
+ 'deposit': deposit,
253
+ 'withdraw': withdraw,
254
+ 'fee': this.safeNumber(rawNetwork, 'withdrawalFee'),
255
+ 'precision': currencyPrecision,
326
256
  'limits': {
327
- 'amount': {
328
- 'min': this.safeNumber(currency, 'minimumCurrencyAmount'),
257
+ 'deposit': {
258
+ 'min': this.safeNumber(rawNetwork, 'minDeposit'),
329
259
  'max': undefined,
330
260
  },
331
261
  'withdraw': {
332
- 'min': this.safeNumber(currency, 'minimalWithdrawalAmount'),
262
+ 'min': this.safeNumber(rawNetwork, 'minWithdrawal'),
333
263
  'max': undefined,
334
264
  },
335
265
  },
336
- 'info': currency,
266
+ 'info': rawNetwork,
337
267
  };
338
268
  }
339
- return result;
269
+ return this.safeCurrencyStructure({
270
+ 'id': id,
271
+ 'code': code,
272
+ 'name': undefined,
273
+ 'type': type,
274
+ 'active': undefined,
275
+ 'deposit': currencyDepositEnabled,
276
+ 'withdraw': currencyWithdrawEnabled,
277
+ 'fee': undefined,
278
+ 'precision': currencyPrecision,
279
+ 'limits': {
280
+ 'amount': {
281
+ 'min': undefined,
282
+ 'max': undefined,
283
+ },
284
+ 'withdraw': {
285
+ 'min': undefined,
286
+ 'max': undefined,
287
+ },
288
+ },
289
+ 'networks': networks,
290
+ 'info': rawCurrency,
291
+ });
340
292
  }
341
293
  async fetchMarkets(params = {}) {
342
294
  /**
343
295
  * @method
344
296
  * @name cex#fetchMarkets
345
- * @description retrieves data on all markets for cex
297
+ * @description retrieves data on all markets for ace
298
+ * @see https://trade.cex.io/docs/#rest-public-api-calls-pairs-info
346
299
  * @param {object} [params] extra parameters specific to the exchange API endpoint
347
300
  * @returns {object[]} an array of objects representing market data
348
301
  */
349
- const currenciesResponse = await this.fetchCurrenciesFromCache(params);
350
- const currenciesData = this.safeValue(currenciesResponse, 'data', {});
351
- const currencies = this.safeValue(currenciesData, 'symbols', []);
352
- const currenciesById = this.indexBy(currencies, 'code');
353
- const pairs = this.safeValue(currenciesData, 'pairs', []);
354
- const response = await this.publicGetCurrencyLimits(params);
302
+ const response = await this.publicPostGetPairsInfo(params);
355
303
  //
356
- // {
357
- // "e":"currency_limits",
358
- // "ok":"ok",
359
- // "data": {
360
- // "pairs":[
361
- // {
362
- // "symbol1":"BTC",
363
- // "symbol2":"USD",
364
- // "minLotSize":0.002,
365
- // "minLotSizeS2":20,
366
- // "maxLotSize":30,
367
- // "minPrice":"1500",
368
- // "maxPrice":"35000"
369
- // },
370
- // {
371
- // "symbol1":"BCH",
372
- // "symbol2":"EUR",
373
- // "minLotSize":0.1,
374
- // "minLotSizeS2":20,
375
- // "maxLotSize":null,
376
- // "minPrice":"25",
377
- // "maxPrice":"8192"
378
- // }
379
- // ]
380
- // }
381
- // }
304
+ // {
305
+ // "ok": "ok",
306
+ // "data": [
307
+ // {
308
+ // "base": "AI",
309
+ // "quote": "USD",
310
+ // "baseMin": "30",
311
+ // "baseMax": "2516000",
312
+ // "baseLotSize": "0.000001",
313
+ // "quoteMin": "10",
314
+ // "quoteMax": "1000000",
315
+ // "quoteLotSize": "0.01000000",
316
+ // "basePrecision": "6",
317
+ // "quotePrecision": "8",
318
+ // "pricePrecision": "4",
319
+ // "minPrice": "0.0377",
320
+ // "maxPrice": "19.5000"
321
+ // },
322
+ // ...
382
323
  //
383
- const result = [];
384
- const markets = this.safeValue(response['data'], 'pairs');
385
- for (let i = 0; i < markets.length; i++) {
386
- const market = markets[i];
387
- const baseId = this.safeString(market, 'symbol1');
388
- const quoteId = this.safeString(market, 'symbol2');
389
- const base = this.safeCurrencyCode(baseId);
390
- const quote = this.safeCurrencyCode(quoteId);
391
- const baseCurrency = this.safeValue(currenciesById, baseId, {});
392
- const quoteCurrency = this.safeValue(currenciesById, quoteId, {});
393
- let pricePrecisionString = this.safeString(quoteCurrency, 'precision', '8');
394
- for (let j = 0; j < pairs.length; j++) {
395
- const pair = pairs[j];
396
- if ((pair['symbol1'] === baseId) && (pair['symbol2'] === quoteId)) {
397
- // we might need to account for `priceScale` here
398
- pricePrecisionString = this.safeString(pair, 'pricePrecision', pricePrecisionString);
399
- }
400
- }
401
- const baseCurrencyPrecision = this.safeString(baseCurrency, 'precision', '8');
402
- const baseCurrencyScale = this.safeString(baseCurrency, 'scale', '0');
403
- const amountPrecisionString = Precise["default"].stringSub(baseCurrencyPrecision, baseCurrencyScale);
404
- result.push({
405
- 'id': baseId + '/' + quoteId,
406
- 'symbol': base + '/' + quote,
407
- 'base': base,
408
- 'quote': quote,
409
- 'settle': undefined,
410
- 'baseId': baseId,
411
- 'quoteId': quoteId,
412
- 'settleId': undefined,
413
- 'type': 'spot',
414
- 'spot': true,
415
- 'margin': undefined,
416
- 'swap': false,
417
- 'future': false,
418
- 'option': false,
419
- 'active': undefined,
420
- 'contract': false,
421
- 'linear': undefined,
422
- 'inverse': undefined,
423
- 'contractSize': undefined,
424
- 'expiry': undefined,
425
- 'expiryDatetime': undefined,
426
- 'strike': undefined,
427
- 'optionType': undefined,
428
- 'precision': {
429
- 'amount': this.parseNumber(this.parsePrecision(amountPrecisionString)),
430
- 'price': this.parseNumber(this.parsePrecision(pricePrecisionString)),
324
+ const data = this.safeList(response, 'data', []);
325
+ return this.parseMarkets(data);
326
+ }
327
+ parseMarket(market) {
328
+ const baseId = this.safeString(market, 'base');
329
+ const base = this.safeCurrencyCode(baseId);
330
+ const quoteId = this.safeString(market, 'quote');
331
+ const quote = this.safeCurrencyCode(quoteId);
332
+ const id = base + '-' + quote; // not actual id, but for this exchange we can use this abbreviation, because e.g. tickers have hyphen in between
333
+ const symbol = base + '/' + quote;
334
+ return this.safeMarketStructure({
335
+ 'id': id,
336
+ 'symbol': symbol,
337
+ 'base': base,
338
+ 'baseId': baseId,
339
+ 'quote': quote,
340
+ 'quoteId': quoteId,
341
+ 'settle': undefined,
342
+ 'settleId': undefined,
343
+ 'type': 'spot',
344
+ 'spot': true,
345
+ 'margin': false,
346
+ 'swap': false,
347
+ 'future': false,
348
+ 'option': false,
349
+ 'contract': false,
350
+ 'linear': undefined,
351
+ 'inverse': undefined,
352
+ 'contractSize': undefined,
353
+ 'expiry': undefined,
354
+ 'expiryDatetime': undefined,
355
+ 'strike': undefined,
356
+ 'optionType': undefined,
357
+ 'limits': {
358
+ 'amount': {
359
+ 'min': this.safeNumber(market, 'baseMin'),
360
+ 'max': this.safeNumber(market, 'baseMax'),
431
361
  },
432
- 'limits': {
433
- 'leverage': {
434
- 'min': undefined,
435
- 'max': undefined,
436
- },
437
- 'amount': {
438
- 'min': this.safeNumber(market, 'minLotSize'),
439
- 'max': this.safeNumber(market, 'maxLotSize'),
440
- },
441
- 'price': {
442
- 'min': this.safeNumber(market, 'minPrice'),
443
- 'max': this.safeNumber(market, 'maxPrice'),
444
- },
445
- 'cost': {
446
- 'min': this.safeNumber(market, 'minLotSizeS2'),
447
- 'max': undefined,
448
- },
362
+ 'price': {
363
+ 'min': this.safeNumber(market, 'minPrice'),
364
+ 'max': this.safeNumber(market, 'maxPrice'),
449
365
  },
450
- 'created': undefined,
451
- 'info': market,
452
- });
453
- }
454
- return result;
455
- }
456
- parseBalance(response) {
457
- const result = { 'info': response };
458
- const ommited = ['username', 'timestamp'];
459
- const balances = this.omit(response, ommited);
460
- const currencyIds = Object.keys(balances);
461
- for (let i = 0; i < currencyIds.length; i++) {
462
- const currencyId = currencyIds[i];
463
- const balance = this.safeValue(balances, currencyId, {});
464
- const account = this.account();
465
- account['free'] = this.safeString(balance, 'available');
466
- // https://github.com/ccxt/ccxt/issues/5484
467
- account['used'] = this.safeString(balance, 'orders', '0');
468
- const code = this.safeCurrencyCode(currencyId);
469
- result[code] = account;
470
- }
471
- return this.safeBalance(result);
366
+ 'cost': {
367
+ 'min': this.safeNumber(market, 'quoteMin'),
368
+ 'max': this.safeNumber(market, 'quoteMax'),
369
+ },
370
+ 'leverage': {
371
+ 'min': undefined,
372
+ 'max': undefined,
373
+ },
374
+ },
375
+ 'precision': {
376
+ 'amount': this.safeString(market, 'baseLotSize'),
377
+ 'price': this.parseNumber(this.parsePrecision(this.safeString(market, 'pricePrecision'))),
378
+ // 'cost': this.parseNumber (this.parsePrecision (this.safeString (market, 'quoteLotSize'))), // buggy, doesn't reflect their documentation
379
+ 'base': this.parseNumber(this.parsePrecision(this.safeString(market, 'basePrecision'))),
380
+ 'quote': this.parseNumber(this.parsePrecision(this.safeString(market, 'quotePrecision'))),
381
+ },
382
+ 'active': undefined,
383
+ 'created': undefined,
384
+ 'info': market,
385
+ });
472
386
  }
473
- async fetchBalance(params = {}) {
387
+ async fetchTime(params = {}) {
474
388
  /**
475
389
  * @method
476
- * @name cex#fetchBalance
477
- * @see https://docs.cex.io/#account-balance
478
- * @description query for balance and get the amount of funds available for trading or funds locked in orders
390
+ * @name cex#fetchTime
391
+ * @description fetches the current integer timestamp in milliseconds from the exchange server
479
392
  * @param {object} [params] extra parameters specific to the exchange API endpoint
480
- * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
393
+ * @returns {int} the current integer timestamp in milliseconds from the exchange server
481
394
  */
482
- await this.loadMarkets();
483
- const response = await this.privatePostBalance(params);
484
- return this.parseBalance(response);
395
+ const response = await this.publicPostGetServerTime(params);
396
+ //
397
+ // {
398
+ // "ok": "ok",
399
+ // "data": {
400
+ // "timestamp": "1728472063472",
401
+ // "ISODate": "2024-10-09T11:07:43.472Z"
402
+ // }
403
+ // }
404
+ //
405
+ const data = this.safeDict(response, 'data');
406
+ const timestamp = this.safeInteger(data, 'timestamp');
407
+ return timestamp;
485
408
  }
486
- async fetchOrderBook(symbol, limit = undefined, params = {}) {
409
+ async fetchTicker(symbol, params = {}) {
487
410
  /**
488
411
  * @method
489
- * @name cex#fetchOrderBook
490
- * @see https://docs.cex.io/#orderbook
491
- * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
492
- * @param {string} symbol unified symbol of the market to fetch the order book for
493
- * @param {int} [limit] the maximum amount of order book entries to return
412
+ * @name cex#fetchTicker
413
+ * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
414
+ * @see https://trade.cex.io/docs/#rest-public-api-calls-ticker
415
+ * @param {string[]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
494
416
  * @param {object} [params] extra parameters specific to the exchange API endpoint
495
- * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
417
+ * @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
496
418
  */
497
419
  await this.loadMarkets();
498
- const market = this.market(symbol);
499
- const request = {
500
- 'pair': market['id'],
501
- };
502
- if (limit !== undefined) {
503
- request['depth'] = limit;
504
- }
505
- const response = await this.publicGetOrderBookPair(this.extend(request, params));
506
- const timestamp = this.safeTimestamp(response, 'timestamp');
507
- return this.parseOrderBook(response, market['symbol'], timestamp);
508
- }
509
- parseOHLCV(ohlcv, market = undefined) {
510
- //
511
- // [
512
- // 1591403940,
513
- // 0.024972,
514
- // 0.024972,
515
- // 0.024969,
516
- // 0.024969,
517
- // 0.49999900
518
- // ]
519
- //
520
- return [
521
- this.safeTimestamp(ohlcv, 0),
522
- this.safeNumber(ohlcv, 1),
523
- this.safeNumber(ohlcv, 2),
524
- this.safeNumber(ohlcv, 3),
525
- this.safeNumber(ohlcv, 4),
526
- this.safeNumber(ohlcv, 5),
527
- ];
420
+ const response = await this.fetchTickers([symbol], params);
421
+ return this.safeDict(response, symbol, {});
528
422
  }
529
- async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
423
+ async fetchTickers(symbols = undefined, params = {}) {
530
424
  /**
531
425
  * @method
532
- * @name cex#fetchOHLCV
533
- * @see https://docs.cex.io/#historical-ohlcv-chart
534
- * @description fetches 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
426
+ * @name cex#fetchTickers
427
+ * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
428
+ * @see https://trade.cex.io/docs/#rest-public-api-calls-ticker
429
+ * @param {string[]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
539
430
  * @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
431
+ * @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
541
432
  */
542
433
  await this.loadMarkets();
543
- const market = this.market(symbol);
544
- if (since === undefined) {
545
- since = this.milliseconds() - 86400000; // yesterday
546
- }
547
- else {
548
- if (this.options['fetchOHLCVWarning']) {
549
- throw new errors.ExchangeError(this.id + " fetchOHLCV warning: CEX can return historical candles for a certain date only, this might produce an empty or null reply. Set exchange.options['fetchOHLCVWarning'] = false or add ({ 'options': { 'fetchOHLCVWarning': false }}) to constructor params to suppress this warning message.");
550
- }
551
- }
552
- const request = {
553
- 'pair': market['id'],
554
- 'yyyymmdd': this.yyyymmdd(since, ''),
555
- };
556
- try {
557
- const response = await this.publicGetOhlcvHdYyyymmddPair(this.extend(request, params));
558
- //
559
- // {
560
- // "time":20200606,
561
- // "data1m":"[[1591403940,0.024972,0.024972,0.024969,0.024969,0.49999900]]",
562
- // }
563
- //
564
- const key = 'data' + this.safeString(this.timeframes, timeframe, timeframe);
565
- const data = this.safeString(response, key);
566
- const ohlcvs = JSON.parse(data);
567
- return this.parseOHLCVs(ohlcvs, market, timeframe, since, limit);
568
- }
569
- catch (e) {
570
- if (e instanceof errors.NullResponse) {
571
- return [];
572
- }
434
+ const request = {};
435
+ if (symbols !== undefined) {
436
+ request['pairs'] = this.marketIds(symbols);
573
437
  }
574
- return undefined;
438
+ const response = await this.publicPostGetTicker(this.extend(request, params));
439
+ //
440
+ // {
441
+ // "ok": "ok",
442
+ // "data": {
443
+ // "AI-USD": {
444
+ // "bestBid": "0.3917",
445
+ // "bestAsk": "0.3949",
446
+ // "bestBidChange": "0.0035",
447
+ // "bestBidChangePercentage": "0.90",
448
+ // "bestAskChange": "0.0038",
449
+ // "bestAskChangePercentage": "0.97",
450
+ // "low": "0.3787",
451
+ // "high": "0.3925",
452
+ // "volume30d": "2945.722277",
453
+ // "lastTradeDateISO": "2024-10-11T06:18:42.077Z",
454
+ // "volume": "120.736000",
455
+ // "quoteVolume": "46.65654070",
456
+ // "lastTradeVolume": "67.914000",
457
+ // "volumeUSD": "46.65",
458
+ // "last": "0.3949",
459
+ // "lastTradePrice": "0.3925",
460
+ // "priceChange": "0.0038",
461
+ // "priceChangePercentage": "0.97"
462
+ // },
463
+ // ...
464
+ //
465
+ const data = this.safeDict(response, 'data', {});
466
+ return this.parseTickers(data, symbols);
575
467
  }
576
468
  parseTicker(ticker, market = undefined) {
577
- const timestamp = this.safeTimestamp(ticker, 'timestamp');
578
- const volume = this.safeString(ticker, 'volume');
579
- const high = this.safeString(ticker, 'high');
580
- const low = this.safeString(ticker, 'low');
581
- const bid = this.safeString(ticker, 'bid');
582
- const ask = this.safeString(ticker, 'ask');
583
- const last = this.safeString(ticker, 'last');
584
- const symbol = this.safeSymbol(undefined, market);
469
+ const marketId = this.safeString(ticker, 'id');
470
+ const symbol = this.safeSymbol(marketId, market);
585
471
  return this.safeTicker({
586
472
  'symbol': symbol,
587
- 'timestamp': timestamp,
588
- 'datetime': this.iso8601(timestamp),
589
- 'high': high,
590
- 'low': low,
591
- 'bid': bid,
473
+ 'timestamp': undefined,
474
+ 'datetime': undefined,
475
+ 'high': this.safeNumber(ticker, 'high'),
476
+ 'low': this.safeNumber(ticker, 'low'),
477
+ 'bid': this.safeNumber(ticker, 'bestBid'),
592
478
  'bidVolume': undefined,
593
- 'ask': ask,
479
+ 'ask': this.safeNumber(ticker, 'bestAsk'),
594
480
  'askVolume': undefined,
595
481
  'vwap': undefined,
596
482
  'open': undefined,
597
- 'close': last,
598
- 'last': last,
483
+ 'close': this.safeString(ticker, 'lastTradePrice'),
599
484
  'previousClose': undefined,
600
- 'change': undefined,
601
- 'percentage': undefined,
485
+ 'change': this.safeNumber(ticker, 'priceChange'),
486
+ 'percentage': this.safeNumber(ticker, 'priceChangePercentage'),
602
487
  'average': undefined,
603
- 'baseVolume': volume,
604
- 'quoteVolume': undefined,
488
+ 'baseVolume': this.safeString(ticker, 'volume'),
489
+ 'quoteVolume': this.safeString(ticker, 'quoteVolume'),
605
490
  'info': ticker,
606
491
  }, market);
607
492
  }
608
- async fetchTickers(symbols = undefined, params = {}) {
609
- /**
610
- * @method
611
- * @name cex#fetchTickers
612
- * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
613
- * @param {string[]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
614
- * @param {object} [params] extra parameters specific to the exchange API endpoint
615
- * @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
616
- */
617
- await this.loadMarkets();
618
- symbols = this.marketSymbols(symbols);
619
- const currencies = Object.keys(this.currencies);
620
- const request = {
621
- 'currencies': currencies.join('/'),
622
- };
623
- const response = await this.publicGetTickersCurrencies(this.extend(request, params));
624
- const tickers = this.safeValue(response, 'data', []);
625
- const result = {};
626
- for (let t = 0; t < tickers.length; t++) {
627
- const ticker = tickers[t];
628
- const marketId = this.safeString(ticker, 'pair');
629
- const market = this.safeMarket(marketId, undefined, ':');
630
- const symbol = market['symbol'];
631
- result[symbol] = this.parseTicker(ticker, market);
632
- }
633
- return this.filterByArrayTickers(result, 'symbol', symbols);
634
- }
635
- async fetchTicker(symbol, params = {}) {
493
+ async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) {
636
494
  /**
637
495
  * @method
638
- * @name cex#fetchTicker
639
- * @see https://docs.cex.io/#ticker
640
- * @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
641
- * @param {string} symbol unified symbol of the market to fetch the ticker for
496
+ * @name cex#fetchTrades
497
+ * @description get the list of most recent trades for a particular symbol
498
+ * @see https://trade.cex.io/docs/#rest-public-api-calls-trade-history
499
+ * @param {string} symbol unified symbol of the market to fetch trades for
500
+ * @param {int} [since] timestamp in ms of the earliest trade to fetch
501
+ * @param {int} [limit] the maximum amount of trades to fetch
642
502
  * @param {object} [params] extra parameters specific to the exchange API endpoint
643
- * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
503
+ * @param {int} [params.until] timestamp in ms of the latest entry
504
+ * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
644
505
  */
645
506
  await this.loadMarkets();
646
507
  const market = this.market(symbol);
647
508
  const request = {
648
509
  'pair': market['id'],
649
510
  };
650
- const ticker = await this.publicGetTickerPair(this.extend(request, params));
651
- return this.parseTicker(ticker, market);
511
+ if (since !== undefined) {
512
+ request['fromDateISO'] = this.iso8601(since);
513
+ }
514
+ let until = undefined;
515
+ [until, params] = this.handleParamInteger2(params, 'until', 'till');
516
+ if (until !== undefined) {
517
+ request['toDateISO'] = this.iso8601(until);
518
+ }
519
+ if (limit !== undefined) {
520
+ request['pageSize'] = Math.min(limit, 10000); // has a bug, still returns more trades
521
+ }
522
+ const response = await this.publicPostGetTradeHistory(this.extend(request, params));
523
+ //
524
+ // {
525
+ // "ok": "ok",
526
+ // "data": {
527
+ // "pageSize": "10",
528
+ // "trades": [
529
+ // {
530
+ // "tradeId": "1728630559823-0",
531
+ // "dateISO": "2024-10-11T07:09:19.823Z",
532
+ // "side": "SELL",
533
+ // "price": "60879.5",
534
+ // "amount": "0.00165962"
535
+ // },
536
+ // ... followed by older trades
537
+ //
538
+ const data = this.safeDict(response, 'data', {});
539
+ const trades = this.safeList(data, 'trades', []);
540
+ return this.parseTrades(trades, market, since, limit);
652
541
  }
653
542
  parseTrade(trade, market = undefined) {
654
543
  //
655
- // fetchTrades (public)
544
+ // public fetchTrades
656
545
  //
657
- // {
658
- // "type": "sell",
659
- // "date": "1638401878",
660
- // "amount": "0.401000",
661
- // "price": "249",
662
- // "tid": "11922"
663
- // }
546
+ // {
547
+ // "tradeId": "1728630559823-0",
548
+ // "dateISO": "2024-10-11T07:09:19.823Z",
549
+ // "side": "SELL",
550
+ // "price": "60879.5",
551
+ // "amount": "0.00165962"
552
+ // },
664
553
  //
665
- const timestamp = this.safeTimestamp(trade, 'date');
666
- const id = this.safeString(trade, 'tid');
667
- const type = undefined;
668
- const side = this.safeString(trade, 'type');
669
- const priceString = this.safeString(trade, 'price');
670
- const amountString = this.safeString(trade, 'amount');
554
+ const dateStr = this.safeString(trade, 'dateISO');
555
+ const timestamp = this.parse8601(dateStr);
671
556
  market = this.safeMarket(undefined, market);
672
557
  return this.safeTrade({
673
558
  'info': trade,
674
- 'id': id,
675
559
  'timestamp': timestamp,
676
560
  'datetime': this.iso8601(timestamp),
677
561
  'symbol': market['symbol'],
678
- 'type': type,
679
- 'side': side,
562
+ 'id': this.safeString(trade, 'tradeId'),
680
563
  'order': undefined,
564
+ 'type': undefined,
681
565
  'takerOrMaker': undefined,
682
- 'price': priceString,
683
- 'amount': amountString,
566
+ 'side': this.safeStringLower(trade, 'side'),
567
+ 'price': this.safeString(trade, 'price'),
568
+ 'amount': this.safeString(trade, 'amount'),
684
569
  'cost': undefined,
685
570
  'fee': undefined,
686
571
  }, market);
687
572
  }
688
- async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) {
573
+ async fetchOrderBook(symbol, limit = undefined, params = {}) {
689
574
  /**
690
575
  * @method
691
- * @name cex#fetchTrades
692
- * @see https://docs.cex.io/#trade-history
693
- * @description get the list of most recent trades for a particular symbol
694
- * @param {string} symbol unified symbol of the market to fetch trades for
695
- * @param {int} [since] timestamp in ms of the earliest trade to fetch
696
- * @param {int} [limit] the maximum amount of trades to fetch
576
+ * @name cex#fetchOrderBook
577
+ * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
578
+ * @see https://trade.cex.io/docs/#rest-public-api-calls-order-book
579
+ * @param {string} symbol unified symbol of the market to fetch the order book for
580
+ * @param {int} [limit] the maximum amount of order book entries to return
697
581
  * @param {object} [params] extra parameters specific to the exchange API endpoint
698
- * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
582
+ * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
583
+ */
584
+ await this.loadMarkets();
585
+ const market = this.market(symbol);
586
+ const request = {
587
+ 'pair': market['id'],
588
+ };
589
+ const response = await this.publicPostGetOrderBook(this.extend(request, params));
590
+ //
591
+ // {
592
+ // "ok": "ok",
593
+ // "data": {
594
+ // "timestamp": "1728636922648",
595
+ // "currency1": "BTC",
596
+ // "currency2": "USDT",
597
+ // "bids": [
598
+ // [
599
+ // "60694.1",
600
+ // "13.12849761"
601
+ // ],
602
+ // [
603
+ // "60694.0",
604
+ // "0.71829244"
605
+ // ],
606
+ // ...
607
+ //
608
+ const orderBook = this.safeDict(response, 'data', {});
609
+ const timestamp = this.safeInteger(orderBook, 'timestamp');
610
+ return this.parseOrderBook(orderBook, market['symbol'], timestamp);
611
+ }
612
+ async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
613
+ /**
614
+ * @method
615
+ * @name cex#fetchOHLCV
616
+ * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
617
+ * @see https://trade.cex.io/docs/#rest-public-api-calls-candles
618
+ * @param {string} symbol unified symbol of the market to fetch OHLCV data for
619
+ * @param {string} timeframe the length of time each candle represents
620
+ * @param {int} [since] timestamp in ms of the earliest candle to fetch
621
+ * @param {int} [limit] the maximum amount of candles to fetch
622
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
623
+ * @param {int} [params.until] timestamp in ms of the latest entry
624
+ * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
699
625
  */
626
+ let dataType = undefined;
627
+ [dataType, params] = this.handleOptionAndParams(params, 'fetchOHLCV', 'dataType');
628
+ if (dataType === undefined) {
629
+ throw new errors.ArgumentsRequired(this.id + ' fetchOHLCV requires a parameter "dataType" to be either "bestBid" or "bestAsk"');
630
+ }
700
631
  await this.loadMarkets();
701
632
  const market = this.market(symbol);
702
633
  const request = {
703
634
  'pair': market['id'],
635
+ 'resolution': this.timeframes[timeframe],
636
+ 'dataType': dataType,
704
637
  };
705
- const response = await this.publicGetTradeHistoryPair(this.extend(request, params));
706
- return this.parseTrades(response, market, since, limit);
638
+ if (since !== undefined) {
639
+ request['fromISO'] = this.iso8601(since);
640
+ }
641
+ let until = undefined;
642
+ [until, params] = this.handleParamInteger2(params, 'until', 'till');
643
+ if (until !== undefined) {
644
+ request['toISO'] = this.iso8601(until);
645
+ }
646
+ else if (since === undefined) {
647
+ // exchange still requires that we provide one of them
648
+ request['toISO'] = this.iso8601(this.milliseconds());
649
+ }
650
+ if (since !== undefined && until !== undefined && limit !== undefined) {
651
+ throw new errors.ArgumentsRequired(this.id + ' fetchOHLCV does not support fetching candles with both a limit and since/until');
652
+ }
653
+ else if ((since !== undefined || until !== undefined) && limit === undefined) {
654
+ throw new errors.ArgumentsRequired(this.id + ' fetchOHLCV requires a limit parameter when fetching candles with since or until');
655
+ }
656
+ if (limit !== undefined) {
657
+ request['limit'] = limit;
658
+ }
659
+ const response = await this.publicPostGetCandles(this.extend(request, params));
660
+ //
661
+ // {
662
+ // "ok": "ok",
663
+ // "data": [
664
+ // {
665
+ // "timestamp": "1728643320000",
666
+ // "open": "61061",
667
+ // "high": "61095.1",
668
+ // "low": "61048.5",
669
+ // "close": "61087.8",
670
+ // "volume": "0",
671
+ // "resolution": "1m",
672
+ // "isClosed": true,
673
+ // "timestampISO": "2024-10-11T10:42:00.000Z"
674
+ // },
675
+ // ...
676
+ //
677
+ const data = this.safeList(response, 'data', []);
678
+ return this.parseOHLCVs(data, market, timeframe, since, limit);
679
+ }
680
+ parseOHLCV(ohlcv, market = undefined) {
681
+ return [
682
+ this.safeInteger(ohlcv, 'timestamp'),
683
+ this.safeNumber(ohlcv, 'open'),
684
+ this.safeNumber(ohlcv, 'high'),
685
+ this.safeNumber(ohlcv, 'low'),
686
+ this.safeNumber(ohlcv, 'close'),
687
+ this.safeNumber(ohlcv, 'volume'),
688
+ ];
707
689
  }
708
690
  async fetchTradingFees(params = {}) {
709
691
  /**
710
692
  * @method
711
693
  * @name cex#fetchTradingFees
712
- * @see https://docs.cex.io/#get-my-fee
713
694
  * @description fetch the trading fees for multiple markets
695
+ * @see https://trade.cex.io/docs/#rest-public-api-calls-candles
714
696
  * @param {object} [params] extra parameters specific to the exchange API endpoint
715
697
  * @returns {object} a dictionary of [fee structures]{@link https://docs.ccxt.com/#/?id=fee-structure} indexed by market symbols
716
698
  */
717
699
  await this.loadMarkets();
718
- const response = await this.privatePostGetMyfee(params);
700
+ const response = await this.privatePostGetMyCurrentFee(params);
719
701
  //
720
- // {
721
- // "e": "get_myfee",
722
- // "ok": "ok",
723
- // "data": {
724
- // 'BTC:USD': { buy: '0.25', sell: '0.25', buyMaker: '0.15', sellMaker: "0.15" },
725
- // 'ETH:USD': { buy: '0.25', sell: '0.25', buyMaker: '0.15', sellMaker: "0.15" },
726
- // ..
727
- // }
728
- // }
702
+ // {
703
+ // "ok": "ok",
704
+ // "data": {
705
+ // "tradingFee": {
706
+ // "AI-USD": {
707
+ // "percent": "0.25"
708
+ // },
709
+ // ...
729
710
  //
730
- const data = this.safeValue(response, 'data', {});
711
+ const data = this.safeDict(response, 'data', {});
712
+ const fees = this.safeDict(data, 'tradingFee', {});
713
+ return this.parseTradingFees(fees, true);
714
+ }
715
+ parseTradingFees(response, useKeyAsId = false) {
731
716
  const result = {};
717
+ const keys = Object.keys(response);
718
+ for (let i = 0; i < keys.length; i++) {
719
+ const key = keys[i];
720
+ let market = undefined;
721
+ if (useKeyAsId) {
722
+ market = this.safeMarket(key);
723
+ }
724
+ const parsed = this.parseTradingFee(response[key], market);
725
+ result[parsed['symbol']] = parsed;
726
+ }
732
727
  for (let i = 0; i < this.symbols.length; i++) {
733
728
  const symbol = this.symbols[i];
734
- const market = this.market(symbol);
735
- const fee = this.safeValue(data, market['id'], {});
736
- const makerString = this.safeString(fee, 'buyMaker');
737
- const takerString = this.safeString(fee, 'buy');
738
- const maker = this.parseNumber(Precise["default"].stringDiv(makerString, '100'));
739
- const taker = this.parseNumber(Precise["default"].stringDiv(takerString, '100'));
740
- result[symbol] = {
741
- 'info': fee,
742
- 'symbol': symbol,
743
- 'maker': maker,
744
- 'taker': taker,
745
- 'percentage': true,
746
- };
729
+ if (!(symbol in result)) {
730
+ const market = this.market(symbol);
731
+ result[symbol] = this.parseTradingFee(response, market);
732
+ }
747
733
  }
748
734
  return result;
749
735
  }
750
- async createOrder(symbol, type, side, amount, price = undefined, params = {}) {
736
+ parseTradingFee(fee, market = undefined) {
737
+ return {
738
+ 'info': fee,
739
+ 'symbol': this.safeString(market, 'symbol'),
740
+ 'maker': this.safeNumber(fee, 'percent'),
741
+ 'taker': this.safeNumber(fee, 'percent'),
742
+ 'percentage': undefined,
743
+ 'tierBased': undefined,
744
+ };
745
+ }
746
+ async fetchAccounts(params = {}) {
747
+ await this.loadMarkets();
748
+ const response = await this.privatePostGetMyAccountStatusV3(params);
749
+ //
750
+ // {
751
+ // "ok": "ok",
752
+ // "data": {
753
+ // "convertedCurrency": "USD",
754
+ // "balancesPerAccounts": {
755
+ // "": {
756
+ // "AI": {
757
+ // "balance": "0.000000",
758
+ // "balanceOnHold": "0.000000"
759
+ // },
760
+ // "USDT": {
761
+ // "balance": "0.00000000",
762
+ // "balanceOnHold": "0.00000000"
763
+ // }
764
+ // }
765
+ // }
766
+ // }
767
+ // }
768
+ //
769
+ const data = this.safeDict(response, 'data', {});
770
+ const balances = this.safeDict(data, 'balancesPerAccounts', {});
771
+ const arrays = this.toArray(balances);
772
+ return this.parseAccounts(arrays, params);
773
+ }
774
+ parseAccount(account) {
775
+ return {
776
+ 'id': undefined,
777
+ 'type': undefined,
778
+ 'code': undefined,
779
+ 'info': account,
780
+ };
781
+ }
782
+ async fetchBalance(params = {}) {
751
783
  /**
752
784
  * @method
753
- * @name cex#createOrder
754
- * @see https://docs.cex.io/#place-order
755
- * @description create a trade order
756
- * @see https://cex.io/rest-api#place-order
757
- * @param {string} symbol unified symbol of the market to create an order in
758
- * @param {string} type 'market' or 'limit'
759
- * @param {string} side 'buy' or 'sell'
760
- * @param {float} amount how much of currency you want to trade in units of base currency
761
- * @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
785
+ * @name cex#fetchBalance
786
+ * @description query for balance and get the amount of funds available for trading or funds locked in orders
787
+ * @see https://trade.cex.io/docs/#rest-private-api-calls-account-status-v3
762
788
  * @param {object} [params] extra parameters specific to the exchange API endpoint
763
- * @param {float} [params.cost] the quote quantity that can be used as an alternative for the amount for market buy orders
764
- * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
789
+ * @param {object} [params.method] 'privatePostGetMyWalletBalance' or 'privatePostGetMyAccountStatusV3'
790
+ * @param {object} [params.account] in case 'privatePostGetMyAccountStatusV3' is chosen, this can specify the account name (default is empty string)
791
+ * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
765
792
  */
766
- await this.loadMarkets();
767
- const market = this.market(symbol);
768
- const request = {
769
- 'pair': market['id'],
770
- 'type': side,
771
- };
772
- // for market buy it requires the amount of quote currency to spend
773
- if ((type === 'market') && (side === 'buy')) {
774
- let quoteAmount = undefined;
775
- let createMarketBuyOrderRequiresPrice = true;
776
- [createMarketBuyOrderRequiresPrice, params] = this.handleOptionAndParams(params, 'createOrder', 'createMarketBuyOrderRequiresPrice', true);
777
- const cost = this.safeString(params, 'cost');
778
- params = this.omit(params, 'cost');
779
- if (cost !== undefined) {
780
- quoteAmount = this.costToPrecision(symbol, cost);
781
- }
782
- else if (createMarketBuyOrderRequiresPrice) {
783
- if (price === undefined) {
784
- throw new errors.InvalidOrder(this.id + ' createOrder() requires the price argument for market buy orders to calculate the total cost to spend (amount * price), alternatively set the createMarketBuyOrderRequiresPrice option or param to false and pass the cost to spend in the amount argument');
785
- }
786
- else {
787
- const amountString = this.numberToString(amount);
788
- const priceString = this.numberToString(price);
789
- const costRequest = Precise["default"].stringMul(amountString, priceString);
790
- quoteAmount = this.costToPrecision(symbol, costRequest);
791
- }
792
- }
793
- else {
794
- quoteAmount = this.costToPrecision(symbol, amount);
795
- }
796
- request['amount'] = quoteAmount;
793
+ let accountName = undefined;
794
+ [accountName, params] = this.handleParamString(params, 'account', ''); // default is empty string
795
+ let method = undefined;
796
+ [method, params] = this.handleParamString(params, 'method', 'privatePostGetMyWalletBalance');
797
+ let accountBalance = undefined;
798
+ if (method === 'privatePostGetMyAccountStatusV3') {
799
+ const response = await this.privatePostGetMyAccountStatusV3(params);
800
+ //
801
+ // {
802
+ // "ok": "ok",
803
+ // "data": {
804
+ // "convertedCurrency": "USD",
805
+ // "balancesPerAccounts": {
806
+ // "": {
807
+ // "AI": {
808
+ // "balance": "0.000000",
809
+ // "balanceOnHold": "0.000000"
810
+ // },
811
+ // ....
812
+ //
813
+ const data = this.safeDict(response, 'data', {});
814
+ const balances = this.safeDict(data, 'balancesPerAccounts', {});
815
+ accountBalance = this.safeDict(balances, accountName, {});
797
816
  }
798
817
  else {
799
- request['amount'] = this.amountToPrecision(symbol, amount);
818
+ const response = await this.privatePostGetMyWalletBalance(params);
819
+ //
820
+ // {
821
+ // "ok": "ok",
822
+ // "data": {
823
+ // "AI": {
824
+ // "balance": "25.606429"
825
+ // },
826
+ // "USDT": {
827
+ // "balance": "7.935449"
828
+ // },
829
+ // ...
830
+ //
831
+ accountBalance = this.safeDict(response, 'data', {});
800
832
  }
801
- if (type === 'limit') {
802
- request['price'] = this.numberToString(price);
833
+ return this.parseBalance(accountBalance);
834
+ }
835
+ parseBalance(response) {
836
+ const result = {
837
+ 'info': response,
838
+ };
839
+ const keys = Object.keys(response);
840
+ for (let i = 0; i < keys.length; i++) {
841
+ const key = keys[i];
842
+ const balance = this.safeDict(response, key, {});
843
+ const code = this.safeCurrencyCode(key);
844
+ const account = {
845
+ 'used': this.safeString(balance, 'balanceOnHold'),
846
+ 'free': this.safeString(balance, 'balance'),
847
+ };
848
+ result[code] = account;
803
849
  }
804
- else {
805
- request['order_type'] = type;
850
+ return this.safeBalance(result);
851
+ }
852
+ async fetchOrdersByStatus(status, symbol = undefined, since = undefined, limit = undefined, params = {}) {
853
+ /**
854
+ * @method
855
+ * @name cex#fetchOrders
856
+ * @description fetches information on multiple orders made by the user
857
+ * @see https://trade.cex.io/docs/#rest-private-api-calls-orders
858
+ * @param {string} symbol unified market symbol of the market orders were made in
859
+ * @param {int} [since] the earliest time in ms to fetch orders for
860
+ * @param {int} [limit] the maximum number of order structures to retrieve
861
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
862
+ * @param {int} [params.until] timestamp in ms of the latest entry
863
+ * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
864
+ */
865
+ await this.loadMarkets();
866
+ const request = {};
867
+ const isClosedOrders = (status === 'closed');
868
+ if (isClosedOrders) {
869
+ request['archived'] = true;
806
870
  }
807
- const response = await this.privatePostPlaceOrderPair(this.extend(request, params));
871
+ let market = undefined;
872
+ if (symbol !== undefined) {
873
+ market = this.market(symbol);
874
+ request['pair'] = market['id'];
875
+ }
876
+ if (limit !== undefined) {
877
+ request['pageSize'] = limit;
878
+ }
879
+ if (since !== undefined) {
880
+ request['serverCreateTimestampFrom'] = since;
881
+ }
882
+ else if (isClosedOrders) {
883
+ // exchange requires a `since` parameter for closed orders, so set default to allowed 365
884
+ request['serverCreateTimestampFrom'] = this.milliseconds() - 364 * 24 * 60 * 60 * 1000;
885
+ }
886
+ let until = undefined;
887
+ [until, params] = this.handleParamInteger2(params, 'until', 'till');
888
+ if (until !== undefined) {
889
+ request['serverCreateTimestampTo'] = until;
890
+ }
891
+ const response = await this.privatePostGetMyOrders(this.extend(request, params));
808
892
  //
809
- // {
810
- // "id": "12978363524",
811
- // "time": 1586610022259,
812
- // "type": "buy",
813
- // "price": "0.033934",
814
- // "amount": "0.10722802",
815
- // "pending": "0.10722802",
816
- // "complete": false
817
- // }
893
+ // if called without `pair`
818
894
  //
819
- const placedAmount = this.safeString(response, 'amount');
820
- const remaining = this.safeString(response, 'pending');
821
- const timestamp = this.safeValue(response, 'time');
822
- const complete = this.safeValue(response, 'complete');
823
- const status = complete ? 'closed' : 'open';
824
- let filled = undefined;
825
- if ((placedAmount !== undefined) && (remaining !== undefined)) {
826
- filled = Precise["default"].stringMax(Precise["default"].stringSub(placedAmount, remaining), '0');
895
+ // {
896
+ // "ok": "ok",
897
+ // "data": [
898
+ // {
899
+ // "orderId": "1313003",
900
+ // "clientOrderId": "037F0AFEB93A",
901
+ // "clientId": "up421412345",
902
+ // "accountId": null,
903
+ // "status": "FILLED",
904
+ // "statusIsFinal": true,
905
+ // "currency1": "AI",
906
+ // "currency2": "USDT",
907
+ // "side": "BUY",
908
+ // "orderType": "Market",
909
+ // "timeInForce": "IOC",
910
+ // "comment": null,
911
+ // "rejectCode": null,
912
+ // "rejectReason": null,
913
+ // "initialOnHoldAmountCcy1": null,
914
+ // "initialOnHoldAmountCcy2": "10.23456700",
915
+ // "executedAmountCcy1": "25.606429",
916
+ // "executedAmountCcy2": "10.20904439",
917
+ // "requestedAmountCcy1": null,
918
+ // "requestedAmountCcy2": "10.20904439",
919
+ // "originalAmountCcy2": "10.23456700",
920
+ // "feeAmount": "0.02552261",
921
+ // "feeCurrency": "USDT",
922
+ // "price": null,
923
+ // "averagePrice": "0.3986",
924
+ // "clientCreateTimestamp": "1728474625320",
925
+ // "serverCreateTimestamp": "1728474624956",
926
+ // "lastUpdateTimestamp": "1728474628015",
927
+ // "expireTime": null,
928
+ // "effectiveTime": null
929
+ // },
930
+ // ...
931
+ //
932
+ const data = this.safeValue(response, 'data', []);
933
+ return this.parseOrders(data, market, since, limit);
934
+ }
935
+ async fetchClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
936
+ /**
937
+ * @method
938
+ * @name cex#fetchClosedOrders
939
+ * @description fetches information on multiple canceled orders made by the user
940
+ * @param {string} symbol unified market symbol of the market orders were made in
941
+ * @param {int} [since] timestamp in ms of the earliest order, default is undefined
942
+ * @param {int} [limit] max number of orders to return, default is undefined
943
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
944
+ * @returns {object} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
945
+ */
946
+ return await this.fetchOrdersByStatus('closed', symbol, since, limit, params);
947
+ }
948
+ async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
949
+ /**
950
+ * @method
951
+ * @name cex#fetchOpenOrders
952
+ * @description fetches information on multiple canceled orders made by the user
953
+ * @param {string} symbol unified market symbol of the market orders were made in
954
+ * @param {int} [since] timestamp in ms of the earliest order, default is undefined
955
+ * @param {int} [limit] max number of orders to return, default is undefined
956
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
957
+ * @returns {object} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
958
+ */
959
+ return await this.fetchOrdersByStatus('open', symbol, since, limit, params);
960
+ }
961
+ parseOrderStatus(status) {
962
+ const statuses = {
963
+ 'FILLED': 'closed',
964
+ 'CANCELLED': 'canceled',
965
+ };
966
+ return this.safeString(statuses, status, undefined);
967
+ }
968
+ parseOrder(order, market = undefined) {
969
+ //
970
+ // "orderId": "1313003",
971
+ // "clientOrderId": "037F0AFEB93A",
972
+ // "clientId": "up421412345",
973
+ // "accountId": null,
974
+ // "status": "FILLED",
975
+ // "statusIsFinal": true,
976
+ // "currency1": "AI",
977
+ // "currency2": "USDT",
978
+ // "side": "BUY",
979
+ // "orderType": "Market",
980
+ // "timeInForce": "IOC",
981
+ // "comment": null,
982
+ // "rejectCode": null,
983
+ // "rejectReason": null,
984
+ // "initialOnHoldAmountCcy1": null,
985
+ // "initialOnHoldAmountCcy2": "10.23456700",
986
+ // "executedAmountCcy1": "25.606429",
987
+ // "executedAmountCcy2": "10.20904439",
988
+ // "requestedAmountCcy1": null,
989
+ // "requestedAmountCcy2": "10.20904439",
990
+ // "originalAmountCcy2": "10.23456700",
991
+ // "feeAmount": "0.02552261",
992
+ // "feeCurrency": "USDT",
993
+ // "price": null,
994
+ // "averagePrice": "0.3986",
995
+ // "clientCreateTimestamp": "1728474625320",
996
+ // "serverCreateTimestamp": "1728474624956",
997
+ // "lastUpdateTimestamp": "1728474628015",
998
+ // "expireTime": null,
999
+ // "effectiveTime": null
1000
+ //
1001
+ const currency1 = this.safeString(order, 'currency1');
1002
+ const currency2 = this.safeString(order, 'currency2');
1003
+ let marketId = undefined;
1004
+ if (currency1 !== undefined && currency2 !== undefined) {
1005
+ marketId = currency1 + '-' + currency2;
827
1006
  }
1007
+ market = this.safeMarket(marketId, market);
1008
+ const symbol = market['symbol'];
1009
+ const status = this.parseOrderStatus(this.safeString(order, 'status'));
1010
+ const fee = {};
1011
+ const feeAmount = this.safeNumber(order, 'feeAmount');
1012
+ if (feeAmount !== undefined) {
1013
+ const currencyId = this.safeString(order, 'feeCurrency');
1014
+ const feeCode = this.safeCurrencyCode(currencyId);
1015
+ fee['currency'] = feeCode;
1016
+ fee['fee'] = feeAmount;
1017
+ }
1018
+ const timestamp = this.safeInteger(order, 'serverCreateTimestamp');
1019
+ const requestedBase = this.safeNumber(order, 'requestedAmountCcy1');
1020
+ const executedBase = this.safeNumber(order, 'executedAmountCcy1');
1021
+ // const requestedQuote = this.safeNumber (order, 'requestedAmountCcy2');
1022
+ const executedQuote = this.safeNumber(order, 'executedAmountCcy2');
828
1023
  return this.safeOrder({
829
- 'id': this.safeString(response, 'id'),
830
- 'info': response,
831
- 'clientOrderId': undefined,
1024
+ 'id': this.safeString(order, 'orderId'),
1025
+ 'clientOrderId': this.safeString(order, 'clientOrderId'),
832
1026
  'timestamp': timestamp,
833
1027
  'datetime': this.iso8601(timestamp),
1028
+ 'lastUpdateTimestamp': this.safeInteger(order, 'lastUpdateTimestamp'),
834
1029
  'lastTradeTimestamp': undefined,
835
- 'type': type,
836
- 'side': this.safeString(response, 'type'),
837
- 'symbol': market['symbol'],
1030
+ 'symbol': symbol,
1031
+ 'type': this.safeStringLower(order, 'orderType'),
1032
+ 'timeInForce': this.safeString(order, 'timeInForce'),
1033
+ 'postOnly': undefined,
1034
+ 'side': this.safeStringLower(order, 'side'),
1035
+ 'price': this.safeNumber(order, 'price'),
1036
+ 'stopPrice': this.safeNumber(order, 'stopPrice'),
1037
+ 'amount': requestedBase,
1038
+ 'cost': executedQuote,
1039
+ 'average': this.safeNumber(order, 'averagePrice'),
1040
+ 'filled': executedBase,
1041
+ 'remaining': undefined,
838
1042
  'status': status,
839
- 'price': this.safeString(response, 'price'),
840
- 'amount': placedAmount,
841
- 'cost': undefined,
842
- 'average': undefined,
843
- 'remaining': remaining,
844
- 'filled': filled,
845
- 'fee': undefined,
1043
+ 'fee': fee,
846
1044
  'trades': undefined,
847
- });
1045
+ 'info': order,
1046
+ }, market);
1047
+ }
1048
+ async createOrder(symbol, type, side, amount, price = undefined, params = {}) {
1049
+ /**
1050
+ * @method
1051
+ * @name cex#createOrder
1052
+ * @description create a trade order
1053
+ * @see https://trade.cex.io/docs/#rest-private-api-calls-new-order
1054
+ * @param {string} symbol unified symbol of the market to create an order in
1055
+ * @param {string} type 'market' or 'limit'
1056
+ * @param {string} side 'buy' or 'sell'
1057
+ * @param {float} amount how much of currency you want to trade in units of base currency
1058
+ * @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
1059
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1060
+ * @param {string} [params.accountId] account-id to use (default is empty string)
1061
+ * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
1062
+ */
1063
+ let accountId = undefined;
1064
+ [accountId, params] = this.handleOptionAndParams(params, 'createOrder', 'accountId');
1065
+ if (accountId === undefined) {
1066
+ throw new errors.ArgumentsRequired(this.id + ' createOrder() : API trading is now allowed from main account, set params["accountId"] or .options["createOrder"]["accountId"] to the name of your sub-account');
1067
+ }
1068
+ await this.loadMarkets();
1069
+ const market = this.market(symbol);
1070
+ const request = {
1071
+ 'clientOrderId': this.uuid(),
1072
+ 'currency1': market['baseId'],
1073
+ 'currency2': market['quoteId'],
1074
+ 'accountId': accountId,
1075
+ 'orderType': this.capitalize(type.toLowerCase()),
1076
+ 'side': side.toUpperCase(),
1077
+ 'timestamp': this.milliseconds(),
1078
+ 'amountCcy1': this.amountToPrecision(symbol, amount),
1079
+ };
1080
+ let timeInForce = undefined;
1081
+ [timeInForce, params] = this.handleOptionAndParams(params, 'createOrder', 'timeInForce', 'GTC');
1082
+ if (type === 'limit') {
1083
+ request['price'] = this.priceToPrecision(symbol, price);
1084
+ request['timeInForce'] = timeInForce;
1085
+ }
1086
+ let triggerPrice = undefined;
1087
+ [triggerPrice, params] = this.handleParamString(params, 'triggerPrice');
1088
+ if (triggerPrice !== undefined) {
1089
+ request['type'] = 'Stop Limit';
1090
+ request['stopPrice'] = triggerPrice;
1091
+ }
1092
+ const response = await this.privatePostDoMyNewOrder(this.extend(request, params));
1093
+ //
1094
+ // on success
1095
+ //
1096
+ // {
1097
+ // "ok": "ok",
1098
+ // "data": {
1099
+ // "messageType": "executionReport",
1100
+ // "clientId": "up132245425",
1101
+ // "orderId": "1318485",
1102
+ // "clientOrderId": "b5b6cd40-154c-4c1c-bd51-4a442f3d50b9",
1103
+ // "accountId": "sub1",
1104
+ // "status": "FILLED",
1105
+ // "currency1": "LTC",
1106
+ // "currency2": "USDT",
1107
+ // "side": "BUY",
1108
+ // "executedAmountCcy1": "0.23000000",
1109
+ // "executedAmountCcy2": "15.09030000",
1110
+ // "requestedAmountCcy1": "0.23000000",
1111
+ // "requestedAmountCcy2": null,
1112
+ // "orderType": "Market",
1113
+ // "timeInForce": null,
1114
+ // "comment": null,
1115
+ // "executionType": "Trade",
1116
+ // "executionId": "1726747124624_101_41116",
1117
+ // "transactTime": "2024-10-15T15:08:12.794Z",
1118
+ // "expireTime": null,
1119
+ // "effectiveTime": null,
1120
+ // "averagePrice": "65.61",
1121
+ // "lastQuantity": "0.23000000",
1122
+ // "lastAmountCcy1": "0.23000000",
1123
+ // "lastAmountCcy2": "15.09030000",
1124
+ // "lastPrice": "65.61",
1125
+ // "feeAmount": "0.03772575",
1126
+ // "feeCurrency": "USDT",
1127
+ // "clientCreateTimestamp": "1729004892014",
1128
+ // "serverCreateTimestamp": "1729004891628",
1129
+ // "lastUpdateTimestamp": "1729004892786"
1130
+ // }
1131
+ // }
1132
+ //
1133
+ // on failure, there are extra fields
1134
+ //
1135
+ // "status": "REJECTED",
1136
+ // "requestedAmountCcy1": null,
1137
+ // "orderRejectReason": "{\\" code \\ ":405,\\" reason \\ ":\\" Either AmountCcy1(OrderQty)or AmountCcy2(CashOrderQty)should be specified for market order not both \\ "}",
1138
+ // "rejectCode": 405,
1139
+ // "rejectReason": "Either AmountCcy1 (OrderQty) or AmountCcy2 (CashOrderQty) should be specified for market order not both",
1140
+ //
1141
+ const data = this.safeDict(response, 'data');
1142
+ return this.parseOrder(data, market);
848
1143
  }
849
1144
  async cancelOrder(id, symbol = undefined, params = {}) {
850
1145
  /**
851
1146
  * @method
852
1147
  * @name cex#cancelOrder
853
- * @see https://docs.cex.io/#cancel-order
854
1148
  * @description cancels an open order
1149
+ * @see https://trade.cex.io/docs/#rest-private-api-calls-cancel-order
855
1150
  * @param {string} id order id
856
- * @param {string} symbol not used by cex cancelOrder ()
1151
+ * @param {string} symbol unified symbol of the market the order was made in
857
1152
  * @param {object} [params] extra parameters specific to the exchange API endpoint
858
1153
  * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
859
1154
  */
860
1155
  await this.loadMarkets();
861
1156
  const request = {
862
- 'id': id,
1157
+ 'orderId': parseInt(id),
1158
+ 'cancelRequestId': 'c_' + (this.milliseconds()).toString(),
1159
+ 'timestamp': this.milliseconds(),
863
1160
  };
864
- const response = await this.privatePostCancelOrder(this.extend(request, params));
865
- // 'true'
866
- return this.extend(this.parseOrder({}), { 'info': response, 'type': undefined, 'id': id, 'status': 'canceled' });
1161
+ const response = await this.privatePostDoCancelMyOrder(this.extend(request, params));
1162
+ //
1163
+ // {"ok":"ok","data":{}}
1164
+ //
1165
+ const data = this.safeDict(response, 'data', {});
1166
+ return this.parseOrder(data);
867
1167
  }
868
1168
  async cancelAllOrders(symbol = undefined, params = {}) {
869
1169
  /**
870
1170
  * @method
871
1171
  * @name cex#cancelAllOrders
872
- * @see https://docs.cex.io/#cancel-all-orders-for-given-pair
873
1172
  * @description cancel all open orders in a market
874
- * @param {string} symbol unified market symbol of the market to cancel orders in
875
- * @param {object} [params] extra parameters specific to the cex api endpoint
876
- * @param {string} [params.marginMode] 'cross' or 'isolated', for spot margin trading
1173
+ * @see https://trade.cex.io/docs/#rest-private-api-calls-cancel-all-orders
1174
+ * @param {string} symbol alpaca cancelAllOrders cannot setting symbol, it will cancel all open orders
1175
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
877
1176
  * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
878
1177
  */
879
- if (symbol === undefined) {
880
- throw new errors.ArgumentsRequired(this.id + ' cancelAllOrders requires a symbol.');
881
- }
882
1178
  await this.loadMarkets();
883
- const market = this.market(symbol);
884
- const request = {
885
- 'pair': market['id'],
886
- };
887
- const orders = await this.privatePostCancelOrdersPair(this.extend(request, params));
1179
+ const response = await this.privatePostDoCancelAllOrders(params);
888
1180
  //
889
- // {
890
- // "e":"cancel_orders",
891
- // "ok":"ok",
892
- // "data":[
893
- // ]
894
- // }
1181
+ // {
1182
+ // "ok": "ok",
1183
+ // "data": {
1184
+ // "clientOrderIds": [
1185
+ // "3AF77B67109F"
1186
+ // ]
1187
+ // }
1188
+ // }
895
1189
  //
896
- return orders;
897
- }
898
- parseOrder(order, market = undefined) {
899
- // Depending on the call, 'time' can be a unix int, unix string or ISO string
900
- // Yes, really
901
- let timestamp = this.safeValue(order, 'time');
902
- if (typeof timestamp === 'string' && timestamp.indexOf('T') >= 0) {
903
- // ISO8601 string
904
- timestamp = this.parse8601(timestamp);
1190
+ const data = this.safeDict(response, 'data', {});
1191
+ const ids = this.safeList(data, 'clientOrderIds', []);
1192
+ const orders = [];
1193
+ for (let i = 0; i < ids.length; i++) {
1194
+ const id = ids[i];
1195
+ orders.push({ 'clientOrderId': id });
905
1196
  }
906
- else if (timestamp !== undefined) {
907
- // either integer or string integer
908
- timestamp = parseInt(timestamp);
909
- }
910
- let symbol = undefined;
911
- const baseId = this.safeString(order, 'symbol1');
912
- const quoteId = this.safeString(order, 'symbol2');
913
- if (market === undefined && baseId !== undefined && quoteId !== undefined) {
914
- const base = this.safeCurrencyCode(baseId);
915
- const quote = this.safeCurrencyCode(quoteId);
916
- if ((base !== undefined) && (quote !== undefined)) {
917
- symbol = base + '/' + quote;
918
- }
919
- if (symbol in this.markets) {
920
- market = this.market(symbol);
921
- }
1197
+ return this.parseOrders(orders);
1198
+ }
1199
+ async fetchLedger(code = undefined, since = undefined, limit = undefined, params = {}) {
1200
+ /**
1201
+ * @method
1202
+ * @name cex#fetchLedger
1203
+ * @description fetch the history of changes, actions done by the user or operations that altered the balance of the user
1204
+ * @see https://trade.cex.io/docs/#rest-private-api-calls-transaction-history
1205
+ * @param {string} [code] unified currency code
1206
+ * @param {int} [since] timestamp in ms of the earliest ledger entry
1207
+ * @param {int} [limit] max number of ledger entries to return
1208
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1209
+ * @param {int} [params.until] timestamp in ms of the latest ledger entry
1210
+ * @returns {object} a [ledger structure]{@link https://docs.ccxt.com/#/?id=ledger-structure}
1211
+ */
1212
+ await this.loadMarkets();
1213
+ let currency = undefined;
1214
+ const request = {};
1215
+ if (code !== undefined) {
1216
+ currency = this.currency(code);
1217
+ request['currency'] = currency['id'];
922
1218
  }
923
- const status = this.parseOrderStatus(this.safeString(order, 'status'));
924
- const price = this.safeString(order, 'price');
925
- let amount = this.omitZero(this.safeString(order, 'amount'));
926
- // sell orders can have a negative amount
927
- // https://github.com/ccxt/ccxt/issues/5338
928
- if (amount !== undefined) {
929
- amount = Precise["default"].stringAbs(amount);
1219
+ if (since !== undefined) {
1220
+ request['dateFrom'] = since;
930
1221
  }
931
- else if (market !== undefined) {
932
- const amountKey = 'a:' + market['base'] + 'cds:';
933
- amount = Precise["default"].stringAbs(this.safeString(order, amountKey));
1222
+ if (limit !== undefined) {
1223
+ request['pageSize'] = limit;
934
1224
  }
935
- const remaining = this.safeString2(order, 'pending', 'remains');
936
- const filled = Precise["default"].stringSub(amount, remaining);
937
- let fee = undefined;
938
- let cost = undefined;
939
- if (market !== undefined) {
940
- symbol = market['symbol'];
941
- const taCost = this.safeString(order, 'ta:' + market['quote']);
942
- const ttaCost = this.safeString(order, 'tta:' + market['quote']);
943
- cost = Precise["default"].stringAdd(taCost, ttaCost);
944
- const baseFee = 'fa:' + market['base'];
945
- const baseTakerFee = 'tfa:' + market['base'];
946
- const quoteFee = 'fa:' + market['quote'];
947
- const quoteTakerFee = 'tfa:' + market['quote'];
948
- let feeRate = this.safeString(order, 'tradingFeeMaker');
949
- if (!feeRate) {
950
- feeRate = this.safeString(order, 'tradingFeeTaker', feeRate);
951
- }
952
- if (feeRate) {
953
- feeRate = Precise["default"].stringDiv(feeRate, '100'); // convert to mathematically-correct percentage coefficients: 1.0 = 100%
954
- }
955
- if ((baseFee in order) || (baseTakerFee in order)) {
956
- const baseFeeCost = this.safeNumber2(order, baseFee, baseTakerFee);
957
- fee = {
958
- 'currency': market['base'],
959
- 'rate': this.parseNumber(feeRate),
960
- 'cost': baseFeeCost,
961
- };
962
- }
963
- else if ((quoteFee in order) || (quoteTakerFee in order)) {
964
- const quoteFeeCost = this.safeNumber2(order, quoteFee, quoteTakerFee);
965
- fee = {
966
- 'currency': market['quote'],
967
- 'rate': this.parseNumber(feeRate),
968
- 'cost': quoteFeeCost,
969
- };
970
- }
1225
+ let until = undefined;
1226
+ [until, params] = this.handleParamInteger2(params, 'until', 'till');
1227
+ if (until !== undefined) {
1228
+ request['dateTo'] = until;
971
1229
  }
972
- if (!cost) {
973
- cost = Precise["default"].stringMul(price, filled);
1230
+ const response = await this.privatePostGetMyTransactionHistory(this.extend(request, params));
1231
+ //
1232
+ // {
1233
+ // "ok": "ok",
1234
+ // "data": [
1235
+ // {
1236
+ // "transactionId": "30367722",
1237
+ // "timestamp": "2024-10-14T14:08:49.987Z",
1238
+ // "accountId": "",
1239
+ // "type": "withdraw",
1240
+ // "amount": "-12.39060600",
1241
+ // "details": "Withdraw fundingId=1235039 clientId=up421412345 walletTxId=76337154166",
1242
+ // "currency": "USDT"
1243
+ // },
1244
+ // ...
1245
+ //
1246
+ const data = this.safeList(response, 'data', []);
1247
+ return this.parseLedger(data, currency, since, limit);
1248
+ }
1249
+ parseLedgerEntry(item, currency = undefined) {
1250
+ let amount = this.safeString(item, 'amount');
1251
+ let direction = undefined;
1252
+ if (Precise["default"].stringLe(amount, '0')) {
1253
+ direction = 'out';
1254
+ amount = Precise["default"].stringMul('-1', amount);
974
1255
  }
975
- const side = this.safeString(order, 'type');
976
- let trades = undefined;
977
- const orderId = this.safeString(order, 'id');
978
- if ('vtx' in order) {
979
- trades = [];
980
- for (let i = 0; i < order['vtx'].length; i++) {
981
- const item = order['vtx'][i];
982
- const tradeSide = this.safeString(item, 'type');
983
- if (tradeSide === 'cancel') {
984
- // looks like this might represent the cancelled part of an order
985
- // { "id": "4426729543",
986
- // "type": "cancel",
987
- // "time": "2017-09-22T00:24:30.476Z",
988
- // "user": "up106404164",
989
- // "c": "user:up106404164:a:BCH",
990
- // "d": "order:4426728375:a:BCH",
991
- // "a": "0.09935956",
992
- // "amount": "0.09935956",
993
- // "balance": "0.42580261",
994
- // "symbol": "BCH",
995
- // "order": "4426728375",
996
- // "buy": null,
997
- // "sell": null,
998
- // "pair": null,
999
- // "pos": null,
1000
- // "cs": "0.42580261",
1001
- // "ds": 0 }
1002
- continue;
1003
- }
1004
- const tradePrice = this.safeString(item, 'price');
1005
- if (tradePrice === undefined) {
1006
- // this represents the order
1007
- // {
1008
- // "a": "0.47000000",
1009
- // "c": "user:up106404164:a:EUR",
1010
- // "d": "order:6065499239:a:EUR",
1011
- // "cs": "1432.93",
1012
- // "ds": "476.72",
1013
- // "id": "6065499249",
1014
- // "buy": null,
1015
- // "pos": null,
1016
- // "pair": null,
1017
- // "sell": null,
1018
- // "time": "2018-04-22T13:07:22.152Z",
1019
- // "type": "buy",
1020
- // "user": "up106404164",
1021
- // "order": "6065499239",
1022
- // "amount": "-715.97000000",
1023
- // "symbol": "EUR",
1024
- // "balance": "1432.93000000" }
1025
- continue;
1026
- }
1027
- // todo: deal with these
1028
- if (tradeSide === 'costsNothing') {
1029
- continue;
1030
- }
1031
- // --
1032
- // if (side !== tradeSide)
1033
- // throw new Error (JSON.stringify (order, null, 2));
1034
- // if (orderId !== item['order'])
1035
- // throw new Error (JSON.stringify (order, null, 2));
1036
- // --
1037
- // partial buy trade
1038
- // {
1039
- // "a": "0.01589885",
1040
- // "c": "user:up106404164:a:BTC",
1041
- // "d": "order:6065499239:a:BTC",
1042
- // "cs": "0.36300000",
1043
- // "ds": 0,
1044
- // "id": "6067991213",
1045
- // "buy": "6065499239",
1046
- // "pos": null,
1047
- // "pair": null,
1048
- // "sell": "6067991206",
1049
- // "time": "2018-04-22T23:09:11.773Z",
1050
- // "type": "buy",
1051
- // "user": "up106404164",
1052
- // "order": "6065499239",
1053
- // "price": 7146.5,
1054
- // "amount": "0.01589885",
1055
- // "symbol": "BTC",
1056
- // "balance": "0.36300000",
1057
- // "symbol2": "EUR",
1058
- // "fee_amount": "0.19" }
1059
- // --
1060
- // trade with zero amount, but non-zero fee
1061
- // {
1062
- // "a": "0.00000000",
1063
- // "c": "user:up106404164:a:EUR",
1064
- // "d": "order:5840654423:a:EUR",
1065
- // "cs": 559744,
1066
- // "ds": 0,
1067
- // "id": "5840654429",
1068
- // "buy": "5807238573",
1069
- // "pos": null,
1070
- // "pair": null,
1071
- // "sell": "5840654423",
1072
- // "time": "2018-03-15T03:20:14.010Z",
1073
- // "type": "sell",
1074
- // "user": "up106404164",
1075
- // "order": "5840654423",
1076
- // "price": 730,
1077
- // "amount": "0.00000000",
1078
- // "symbol": "EUR",
1079
- // "balance": "5597.44000000",
1080
- // "symbol2": "BCH",
1081
- // "fee_amount": "0.01" }
1082
- // --
1083
- // trade which should have an amount of exactly 0.002BTC
1084
- // {
1085
- // "a": "16.70000000",
1086
- // "c": "user:up106404164:a:GBP",
1087
- // "d": "order:9927386681:a:GBP",
1088
- // "cs": "86.90",
1089
- // "ds": 0,
1090
- // "id": "9927401610",
1091
- // "buy": "9927401601",
1092
- // "pos": null,
1093
- // "pair": null,
1094
- // "sell": "9927386681",
1095
- // "time": "2019-08-21T15:25:37.777Z",
1096
- // "type": "sell",
1097
- // "user": "up106404164",
1098
- // "order": "9927386681",
1099
- // "price": 8365,
1100
- // "amount": "16.70000000",
1101
- // "office": "UK",
1102
- // "symbol": "GBP",
1103
- // "balance": "86.90000000",
1104
- // "symbol2": "BTC",
1105
- // "fee_amount": "0.03"
1106
- // }
1107
- const tradeTimestamp = this.parse8601(this.safeString(item, 'time'));
1108
- const tradeAmount = this.safeString(item, 'amount');
1109
- const feeCost = this.safeString(item, 'fee_amount');
1110
- let absTradeAmount = Precise["default"].stringAbs(tradeAmount);
1111
- let tradeCost = undefined;
1112
- if (tradeSide === 'sell') {
1113
- tradeCost = absTradeAmount;
1114
- absTradeAmount = Precise["default"].stringDiv(Precise["default"].stringAdd(feeCost, tradeCost), tradePrice);
1115
- }
1116
- else {
1117
- tradeCost = Precise["default"].stringMul(absTradeAmount, tradePrice);
1118
- }
1119
- trades.push({
1120
- 'id': this.safeString(item, 'id'),
1121
- 'timestamp': tradeTimestamp,
1122
- 'datetime': this.iso8601(tradeTimestamp),
1123
- 'order': orderId,
1124
- 'symbol': symbol,
1125
- 'price': this.parseNumber(tradePrice),
1126
- 'amount': this.parseNumber(absTradeAmount),
1127
- 'cost': this.parseNumber(tradeCost),
1128
- 'side': tradeSide,
1129
- 'fee': {
1130
- 'cost': this.parseNumber(feeCost),
1131
- 'currency': market['quote'],
1132
- },
1133
- 'info': item,
1134
- 'type': undefined,
1135
- 'takerOrMaker': undefined,
1136
- });
1137
- }
1256
+ else {
1257
+ direction = 'in';
1138
1258
  }
1139
- return this.safeOrder({
1140
- 'info': order,
1141
- 'id': orderId,
1142
- 'clientOrderId': undefined,
1143
- 'datetime': this.iso8601(timestamp),
1259
+ const currencyId = this.safeString(item, 'currency');
1260
+ currency = this.safeCurrency(currencyId, currency);
1261
+ const code = this.safeCurrencyCode(currencyId, currency);
1262
+ const timestampString = this.safeString(item, 'timestamp');
1263
+ const timestamp = this.parse8601(timestampString);
1264
+ const type = this.safeString(item, 'type');
1265
+ return this.safeLedgerEntry({
1266
+ 'info': item,
1267
+ 'id': this.safeString(item, 'transactionId'),
1268
+ 'direction': direction,
1269
+ 'account': this.safeString(item, 'accountId', ''),
1270
+ 'referenceAccount': undefined,
1271
+ 'referenceId': undefined,
1272
+ 'type': this.parseLedgerEntryType(type),
1273
+ 'currency': code,
1274
+ 'amount': this.parseNumber(amount),
1144
1275
  'timestamp': timestamp,
1145
- 'lastTradeTimestamp': undefined,
1146
- 'status': status,
1147
- 'symbol': symbol,
1148
- 'type': (price === undefined) ? 'market' : 'limit',
1149
- 'timeInForce': undefined,
1150
- 'postOnly': undefined,
1151
- 'side': side,
1152
- 'price': price,
1153
- 'stopPrice': undefined,
1154
- 'triggerPrice': undefined,
1155
- 'cost': cost,
1156
- 'amount': amount,
1157
- 'filled': filled,
1158
- 'remaining': remaining,
1159
- 'trades': trades,
1160
- 'fee': fee,
1161
- 'average': undefined,
1162
- });
1276
+ 'datetime': this.iso8601(timestamp),
1277
+ 'before': undefined,
1278
+ 'after': undefined,
1279
+ 'status': undefined,
1280
+ 'fee': undefined,
1281
+ }, currency);
1163
1282
  }
1164
- async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
1283
+ parseLedgerEntryType(type) {
1284
+ const ledgerType = {
1285
+ 'deposit': 'deposit',
1286
+ 'withdraw': 'withdrawal',
1287
+ 'commission': 'fee',
1288
+ };
1289
+ return this.safeString(ledgerType, type, type);
1290
+ }
1291
+ async fetchDepositsWithdrawals(code = undefined, since = undefined, limit = undefined, params = {}) {
1165
1292
  /**
1166
1293
  * @method
1167
- * @name cex#fetchOpenOrders
1168
- * @see https://docs.cex.io/#open-orders
1169
- * @description fetch all unfilled currently open orders
1170
- * @param {string} symbol unified market symbol
1171
- * @param {int} [since] the earliest time in ms to fetch open orders for
1172
- * @param {int} [limit] the maximum number of open orders structures to retrieve
1294
+ * @name cex#fetchDepositsWithdrawals
1295
+ * @description fetch history of deposits and withdrawals
1296
+ * @see https://trade.cex.io/docs/#rest-private-api-calls-funding-history
1297
+ * @param {string} [code] unified currency code for the currency of the deposit/withdrawals, default is undefined
1298
+ * @param {int} [since] timestamp in ms of the earliest deposit/withdrawal, default is undefined
1299
+ * @param {int} [limit] max number of deposit/withdrawals to return, default is undefined
1173
1300
  * @param {object} [params] extra parameters specific to the exchange API endpoint
1174
- * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
1301
+ * @returns {object} a list of [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure}
1175
1302
  */
1176
1303
  await this.loadMarkets();
1177
1304
  const request = {};
1178
- let market = undefined;
1179
- let orders = undefined;
1180
- if (symbol !== undefined) {
1181
- market = this.market(symbol);
1182
- request['pair'] = market['id'];
1183
- orders = await this.privatePostOpenOrdersPair(this.extend(request, params));
1305
+ let currency = undefined;
1306
+ if (code !== undefined) {
1307
+ currency = this.currency(code);
1184
1308
  }
1185
- else {
1186
- orders = await this.privatePostOpenOrders(this.extend(request, params));
1309
+ if (since !== undefined) {
1310
+ request['dateFrom'] = since;
1311
+ }
1312
+ if (limit !== undefined) {
1313
+ request['pageSize'] = limit;
1187
1314
  }
1188
- for (let i = 0; i < orders.length; i++) {
1189
- orders[i] = this.extend(orders[i], { 'status': 'open' });
1315
+ let until = undefined;
1316
+ [until, params] = this.handleParamInteger2(params, 'until', 'till');
1317
+ if (until !== undefined) {
1318
+ request['dateTo'] = until;
1190
1319
  }
1191
- return this.parseOrders(orders, market, since, limit);
1320
+ const response = await this.privatePostGetMyFundingHistory(this.extend(request, params));
1321
+ //
1322
+ // {
1323
+ // "ok": "ok",
1324
+ // "data": [
1325
+ // {
1326
+ // "clientId": "up421412345",
1327
+ // "accountId": "",
1328
+ // "currency": "USDT",
1329
+ // "direction": "withdraw",
1330
+ // "amount": "12.39060600",
1331
+ // "commissionAmount": "0.00000000",
1332
+ // "status": "approved",
1333
+ // "updatedAt": "2024-10-14T14:08:50.013Z",
1334
+ // "txId": "30367718",
1335
+ // "details": {}
1336
+ // },
1337
+ // ...
1338
+ //
1339
+ const data = this.safeList(response, 'data', []);
1340
+ return this.parseTransactions(data, currency, since, limit);
1192
1341
  }
1193
- async fetchClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
1342
+ parseTransaction(transaction, currency = undefined) {
1343
+ const currencyId = this.safeString(transaction, 'currency');
1344
+ const direction = this.safeString(transaction, 'direction');
1345
+ const type = (direction === 'withdraw') ? 'withdrawal' : 'deposit';
1346
+ const code = this.safeCurrencyCode(currencyId, currency);
1347
+ const updatedAt = this.safeString(transaction, 'updatedAt');
1348
+ const timestamp = this.parse8601(updatedAt);
1349
+ return {
1350
+ 'info': transaction,
1351
+ 'id': this.safeString(transaction, 'txId'),
1352
+ 'txid': undefined,
1353
+ 'type': type,
1354
+ 'currency': code,
1355
+ 'network': undefined,
1356
+ 'amount': this.safeNumber(transaction, 'amount'),
1357
+ 'status': this.parseTransactionStatus(this.safeString(transaction, 'status')),
1358
+ 'timestamp': timestamp,
1359
+ 'datetime': this.iso8601(timestamp),
1360
+ 'address': undefined,
1361
+ 'addressFrom': undefined,
1362
+ 'addressTo': undefined,
1363
+ 'tag': undefined,
1364
+ 'tagFrom': undefined,
1365
+ 'tagTo': undefined,
1366
+ 'updated': undefined,
1367
+ 'comment': undefined,
1368
+ 'fee': {
1369
+ 'currency': code,
1370
+ 'cost': this.safeNumber(transaction, 'commissionAmount'),
1371
+ },
1372
+ 'internal': undefined,
1373
+ };
1374
+ }
1375
+ parseTransactionStatus(status) {
1376
+ const statuses = {
1377
+ 'rejected': 'rejected',
1378
+ 'pending': 'pending',
1379
+ 'approved': 'ok',
1380
+ };
1381
+ return this.safeString(statuses, status, status);
1382
+ }
1383
+ async transfer(code, amount, fromAccount, toAccount, params = {}) {
1194
1384
  /**
1195
1385
  * @method
1196
- * @name cex#fetchClosedOrders
1197
- * @see https://docs.cex.io/#archived-orders
1198
- * @description fetches information on multiple closed orders made by the user
1199
- * @param {string} symbol unified market symbol of the market orders were made in
1200
- * @param {int} [since] the earliest time in ms to fetch orders for
1201
- * @param {int} [limit] the maximum number of order structures to retrieve
1386
+ * @name cex#transfer
1387
+ * @description transfer currency internally between wallets on the same account
1388
+ * @see https://trade.cex.io/docs/#rest-private-api-calls-internal-transfer
1389
+ * @param {string} code unified currency code
1390
+ * @param {float} amount amount to transfer
1391
+ * @param {string} fromAccount 'SPOT', 'FUND', or 'CONTRACT'
1392
+ * @param {string} toAccount 'SPOT', 'FUND', or 'CONTRACT'
1202
1393
  * @param {object} [params] extra parameters specific to the exchange API endpoint
1203
- * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
1394
+ * @returns {object} a [transfer structure]{@link https://docs.ccxt.com/#/?id=transfer-structure}
1204
1395
  */
1205
- if (symbol === undefined) {
1206
- throw new errors.ArgumentsRequired(this.id + ' fetchClosedOrders() requires a symbol argument');
1396
+ let transfer = undefined;
1397
+ if (toAccount !== '' && fromAccount !== '') {
1398
+ transfer = await this.transferBetweenSubAccounts(code, amount, fromAccount, toAccount, params);
1207
1399
  }
1208
- await this.loadMarkets();
1209
- const market = this.market(symbol);
1210
- const request = { 'pair': market['id'] };
1211
- const response = await this.privatePostArchivedOrdersPair(this.extend(request, params));
1212
- return this.parseOrders(response, market, since, limit);
1400
+ else {
1401
+ transfer = await this.transferBetweenMainAndSubAccount(code, amount, fromAccount, toAccount, params);
1402
+ }
1403
+ const fillResponseFromRequest = this.handleOption('transfer', 'fillResponseFromRequest', true);
1404
+ if (fillResponseFromRequest) {
1405
+ transfer['fromAccount'] = fromAccount;
1406
+ transfer['toAccount'] = toAccount;
1407
+ }
1408
+ return transfer;
1213
1409
  }
1214
- async fetchOrder(id, symbol = undefined, params = {}) {
1215
- /**
1216
- * @method
1217
- * @name cex#fetchOrder
1218
- * @see https://docs.cex.io/?python#get-order-details
1219
- * @description fetches information on an order made by the user
1220
- * @param {string} id the order id
1221
- * @param {string} symbol not used by cex fetchOrder
1222
- * @param {object} [params] extra parameters specific to the exchange API endpoint
1223
- * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
1224
- */
1410
+ async transferBetweenMainAndSubAccount(code, amount, fromAccount, toAccount, params = {}) {
1225
1411
  await this.loadMarkets();
1412
+ const currency = this.currency(code);
1413
+ const fromMain = (fromAccount === '');
1414
+ const targetAccount = fromMain ? toAccount : fromAccount;
1415
+ const guid = this.safeString(params, 'guid', this.uuid());
1226
1416
  const request = {
1227
- 'id': id.toString(),
1417
+ 'currency': currency['id'],
1418
+ 'amount': this.currencyToPrecision(code, amount),
1419
+ 'accountId': targetAccount,
1420
+ 'clientTxId': guid,
1228
1421
  };
1229
- const response = await this.privatePostGetOrderTx(this.extend(request, params));
1230
- const data = this.safeValue(response, 'data', {});
1422
+ let response = undefined;
1423
+ if (fromMain) {
1424
+ response = await this.privatePostDoDepositFundsFromWallet(this.extend(request, params));
1425
+ }
1426
+ else {
1427
+ response = await this.privatePostDoWithdrawalFundsToWallet(this.extend(request, params));
1428
+ }
1429
+ // both endpoints return the same structure, the only difference is that
1430
+ // the "accountId" is filled with the "subAccount"
1231
1431
  //
1232
1432
  // {
1233
- // "id": "5442731603",
1234
- // "type": "sell",
1235
- // "time": 1516132358071,
1236
- // "lastTxTime": 1516132378452,
1237
- // "lastTx": "5442734452",
1238
- // "pos": null,
1239
- // "user": "up106404164",
1240
- // "status": "d",
1241
- // "symbol1": "ETH",
1242
- // "symbol2": "EUR",
1243
- // "amount": "0.50000000",
1244
- // "kind": "api",
1245
- // "price": "923.3386",
1246
- // "tfacf": "1",
1247
- // "fa:EUR": "0.55",
1248
- // "ta:EUR": "369.77",
1249
- // "remains": "0.00000000",
1250
- // "tfa:EUR": "0.22",
1251
- // "tta:EUR": "91.95",
1252
- // "a:ETH:cds": "0.50000000",
1253
- // "a:EUR:cds": "461.72",
1254
- // "f:EUR:cds": "0.77",
1255
- // "tradingFeeMaker": "0.15",
1256
- // "tradingFeeTaker": "0.23",
1257
- // "tradingFeeStrategy": "userVolumeAmount",
1258
- // "tradingFeeUserVolumeAmount": "2896912572",
1259
- // "orderId": "5442731603",
1260
- // "next": false,
1261
- // "vtx": [
1262
- // {
1263
- // "id": "5442734452",
1264
- // "type": "sell",
1265
- // "time": "2018-01-16T19:52:58.452Z",
1266
- // "user": "up106404164",
1267
- // "c": "user:up106404164:a:EUR",
1268
- // "d": "order:5442731603:a:EUR",
1269
- // "a": "104.53000000",
1270
- // "amount": "104.53000000",
1271
- // "balance": "932.71000000",
1272
- // "symbol": "EUR",
1273
- // "order": "5442731603",
1274
- // "buy": "5442734443",
1275
- // "sell": "5442731603",
1276
- // "pair": null,
1277
- // "pos": null,
1278
- // "office": null,
1279
- // "cs": "932.71",
1280
- // "ds": 0,
1281
- // "price": 923.3386,
1282
- // "symbol2": "ETH",
1283
- // "fee_amount": "0.16"
1284
- // },
1285
- // {
1286
- // "id": "5442731609",
1287
- // "type": "sell",
1288
- // "time": "2018-01-16T19:52:38.071Z",
1289
- // "user": "up106404164",
1290
- // "c": "user:up106404164:a:EUR",
1291
- // "d": "order:5442731603:a:EUR",
1292
- // "a": "91.73000000",
1293
- // "amount": "91.73000000",
1294
- // "balance": "563.49000000",
1295
- // "symbol": "EUR",
1296
- // "order": "5442731603",
1297
- // "buy": "5442618127",
1298
- // "sell": "5442731603",
1299
- // "pair": null,
1300
- // "pos": null,
1301
- // "office": null,
1302
- // "cs": "563.49",
1303
- // "ds": 0,
1304
- // "price": 924.0092,
1305
- // "symbol2": "ETH",
1306
- // "fee_amount": "0.22"
1307
- // },
1308
- // {
1309
- // "id": "5442731604",
1310
- // "type": "sell",
1311
- // "time": "2018-01-16T19:52:38.071Z",
1312
- // "user": "up106404164",
1313
- // "c": "order:5442731603:a:ETH",
1314
- // "d": "user:up106404164:a:ETH",
1315
- // "a": "0.50000000",
1316
- // "amount": "-0.50000000",
1317
- // "balance": "15.80995000",
1318
- // "symbol": "ETH",
1319
- // "order": "5442731603",
1320
- // "buy": null,
1321
- // "sell": null,
1322
- // "pair": null,
1323
- // "pos": null,
1324
- // "office": null,
1325
- // "cs": "0.50000000",
1326
- // "ds": "15.80995000"
1327
- // }
1328
- // ]
1433
+ // "ok": "ok",
1434
+ // "data": {
1435
+ // "accountId": "sub1",
1436
+ // "clientTxId": "27ba8284-67cf-4386-9ec7-80b3871abd45",
1437
+ // "currency": "USDT",
1438
+ // "status": "approved"
1439
+ // }
1329
1440
  // }
1330
1441
  //
1331
- return this.parseOrder(data);
1442
+ const data = this.safeDict(response, 'data', {});
1443
+ return this.parseTransfer(data, currency);
1332
1444
  }
1333
- async fetchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
1334
- /**
1335
- * @method
1336
- * @name cex#fetchOrders
1337
- * @see https://docs.cex.io/#archived-orders
1338
- * @description fetches information on multiple orders made by the user
1339
- * @param {string} symbol unified market symbol of the market orders were made in
1340
- * @param {int} [since] the earliest time in ms to fetch orders for
1341
- * @param {int} [limit] the maximum number of order structures to retrieve
1342
- * @param {object} [params] extra parameters specific to the exchange API endpoint
1343
- * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
1344
- */
1445
+ async transferBetweenSubAccounts(code, amount, fromAccount, toAccount, params = {}) {
1345
1446
  await this.loadMarkets();
1346
- const market = this.market(symbol);
1447
+ const currency = this.currency(code);
1347
1448
  const request = {
1348
- 'limit': limit,
1349
- 'pair': market['id'],
1350
- 'dateFrom': since,
1449
+ 'currency': currency['id'],
1450
+ 'amount': this.currencyToPrecision(code, amount),
1451
+ 'fromAccountId': fromAccount,
1452
+ 'toAccountId': toAccount,
1351
1453
  };
1352
- const response = await this.privatePostArchivedOrdersPair(this.extend(request, params));
1353
- const results = [];
1354
- for (let i = 0; i < response.length; i++) {
1355
- // cancelled (unfilled):
1356
- // { "id": "4005785516",
1357
- // "type": "sell",
1358
- // "time": "2017-07-18T19:08:34.223Z",
1359
- // "lastTxTime": "2017-07-18T19:08:34.396Z",
1360
- // "lastTx": "4005785522",
1361
- // "pos": null,
1362
- // "status": "c",
1363
- // "symbol1": "ETH",
1364
- // "symbol2": "GBP",
1365
- // "amount": "0.20000000",
1366
- // "price": "200.5625",
1367
- // "remains": "0.20000000",
1368
- // 'a:ETH:cds': "0.20000000",
1369
- // "tradingFeeMaker": "0",
1370
- // "tradingFeeTaker": "0.16",
1371
- // "tradingFeeUserVolumeAmount": "10155061217",
1372
- // "orderId": "4005785516" }
1373
- // --
1374
- // cancelled (partially filled buy):
1375
- // { "id": "4084911657",
1376
- // "type": "buy",
1377
- // "time": "2017-08-05T03:18:39.596Z",
1378
- // "lastTxTime": "2019-03-19T17:37:46.404Z",
1379
- // "lastTx": "8459265833",
1380
- // "pos": null,
1381
- // "status": "cd",
1382
- // "symbol1": "BTC",
1383
- // "symbol2": "GBP",
1384
- // "amount": "0.05000000",
1385
- // "price": "2241.4692",
1386
- // "tfacf": "1",
1387
- // "remains": "0.03910535",
1388
- // 'tfa:GBP': "0.04",
1389
- // 'tta:GBP': "24.39",
1390
- // 'a:BTC:cds': "0.01089465",
1391
- // 'a:GBP:cds': "112.26",
1392
- // 'f:GBP:cds': "0.04",
1393
- // "tradingFeeMaker": "0",
1394
- // "tradingFeeTaker": "0.16",
1395
- // "tradingFeeUserVolumeAmount": "13336396963",
1396
- // "orderId": "4084911657" }
1397
- // --
1398
- // cancelled (partially filled sell):
1399
- // { "id": "4426728375",
1400
- // "type": "sell",
1401
- // "time": "2017-09-22T00:24:20.126Z",
1402
- // "lastTxTime": "2017-09-22T00:24:30.476Z",
1403
- // "lastTx": "4426729543",
1404
- // "pos": null,
1405
- // "status": "cd",
1406
- // "symbol1": "BCH",
1407
- // "symbol2": "BTC",
1408
- // "amount": "0.10000000",
1409
- // "price": "0.11757182",
1410
- // "tfacf": "1",
1411
- // "remains": "0.09935956",
1412
- // 'tfa:BTC': "0.00000014",
1413
- // 'tta:BTC': "0.00007537",
1414
- // 'a:BCH:cds': "0.10000000",
1415
- // 'a:BTC:cds': "0.00007537",
1416
- // 'f:BTC:cds': "0.00000014",
1417
- // "tradingFeeMaker": "0",
1418
- // "tradingFeeTaker": "0.18",
1419
- // "tradingFeeUserVolumeAmount": "3466715450",
1420
- // "orderId": "4426728375" }
1421
- // --
1422
- // filled:
1423
- // { "id": "5342275378",
1424
- // "type": "sell",
1425
- // "time": "2018-01-04T00:28:12.992Z",
1426
- // "lastTxTime": "2018-01-04T00:28:12.992Z",
1427
- // "lastTx": "5342275393",
1428
- // "pos": null,
1429
- // "status": "d",
1430
- // "symbol1": "BCH",
1431
- // "symbol2": "BTC",
1432
- // "amount": "0.10000000",
1433
- // "kind": "api",
1434
- // "price": "0.17",
1435
- // "remains": "0.00000000",
1436
- // 'tfa:BTC': "0.00003902",
1437
- // 'tta:BTC': "0.01699999",
1438
- // 'a:BCH:cds': "0.10000000",
1439
- // 'a:BTC:cds': "0.01699999",
1440
- // 'f:BTC:cds': "0.00003902",
1441
- // "tradingFeeMaker": "0.15",
1442
- // "tradingFeeTaker": "0.23",
1443
- // "tradingFeeUserVolumeAmount": "1525951128",
1444
- // "orderId": "5342275378" }
1445
- // --
1446
- // market order (buy):
1447
- // { "id": "6281946200",
1448
- // "pos": null,
1449
- // "time": "2018-05-23T11:55:43.467Z",
1450
- // "type": "buy",
1451
- // "amount": "0.00000000",
1452
- // "lastTx": "6281946210",
1453
- // "status": "d",
1454
- // "amount2": "20.00",
1455
- // "orderId": "6281946200",
1456
- // "remains": "0.00000000",
1457
- // "symbol1": "ETH",
1458
- // "symbol2": "EUR",
1459
- // "tfa:EUR": "0.05",
1460
- // "tta:EUR": "19.94",
1461
- // "a:ETH:cds": "0.03764100",
1462
- // "a:EUR:cds": "20.00",
1463
- // "f:EUR:cds": "0.05",
1464
- // "lastTxTime": "2018-05-23T11:55:43.467Z",
1465
- // "tradingFeeTaker": "0.25",
1466
- // "tradingFeeUserVolumeAmount": "55998097" }
1467
- // --
1468
- // market order (sell):
1469
- // { "id": "6282200948",
1470
- // "pos": null,
1471
- // "time": "2018-05-23T12:42:58.315Z",
1472
- // "type": "sell",
1473
- // "amount": "-0.05000000",
1474
- // "lastTx": "6282200958",
1475
- // "status": "d",
1476
- // "orderId": "6282200948",
1477
- // "remains": "0.00000000",
1478
- // "symbol1": "ETH",
1479
- // "symbol2": "EUR",
1480
- // "tfa:EUR": "0.07",
1481
- // "tta:EUR": "26.49",
1482
- // "a:ETH:cds": "0.05000000",
1483
- // "a:EUR:cds": "26.49",
1484
- // "f:EUR:cds": "0.07",
1485
- // "lastTxTime": "2018-05-23T12:42:58.315Z",
1486
- // "tradingFeeTaker": "0.25",
1487
- // "tradingFeeUserVolumeAmount": "56294576" }
1488
- const order = response[i];
1489
- const status = this.parseOrderStatus(this.safeString(order, 'status'));
1490
- const baseId = this.safeString(order, 'symbol1');
1491
- const quoteId = this.safeString(order, 'symbol2');
1492
- const base = this.safeCurrencyCode(baseId);
1493
- const quote = this.safeCurrencyCode(quoteId);
1494
- const symbolInner = base + '/' + quote;
1495
- const side = this.safeString(order, 'type');
1496
- const baseAmount = this.safeNumber(order, 'a:' + baseId + ':cds');
1497
- const quoteAmount = this.safeNumber(order, 'a:' + quoteId + ':cds');
1498
- const fee = this.safeNumber(order, 'f:' + quoteId + ':cds');
1499
- const amount = this.safeString(order, 'amount');
1500
- const price = this.safeString(order, 'price');
1501
- const remaining = this.safeString(order, 'remains');
1502
- const filled = Precise["default"].stringSub(amount, remaining);
1503
- let orderAmount = undefined;
1504
- let cost = undefined;
1505
- let average = undefined;
1506
- let type = undefined;
1507
- if (!price) {
1508
- type = 'market';
1509
- orderAmount = baseAmount;
1510
- cost = quoteAmount;
1511
- average = Precise["default"].stringDiv(orderAmount, cost);
1512
- }
1513
- else {
1514
- const ta = this.safeString(order, 'ta:' + quoteId, '0');
1515
- const tta = this.safeString(order, 'tta:' + quoteId, '0');
1516
- const fa = this.safeString(order, 'fa:' + quoteId, '0');
1517
- const tfa = this.safeString(order, 'tfa:' + quoteId, '0');
1518
- if (side === 'sell') {
1519
- cost = Precise["default"].stringAdd(Precise["default"].stringAdd(ta, tta), Precise["default"].stringAdd(fa, tfa));
1520
- }
1521
- else {
1522
- cost = Precise["default"].stringSub(Precise["default"].stringAdd(ta, tta), Precise["default"].stringAdd(fa, tfa));
1523
- }
1524
- type = 'limit';
1525
- orderAmount = amount;
1526
- average = Precise["default"].stringDiv(cost, filled);
1527
- }
1528
- const time = this.safeString(order, 'time');
1529
- const lastTxTime = this.safeString(order, 'lastTxTime');
1530
- const timestamp = this.parse8601(time);
1531
- const safeOrder = this.safeOrder({
1532
- 'info': order,
1533
- 'id': this.safeString(order, 'id'),
1534
- 'timestamp': timestamp,
1535
- 'datetime': this.iso8601(timestamp),
1536
- 'lastUpdated': this.parse8601(lastTxTime),
1537
- 'status': status,
1538
- 'symbol': symbolInner,
1539
- 'side': side,
1540
- 'price': price,
1541
- 'amount': orderAmount,
1542
- 'average': average,
1543
- 'type': type,
1544
- 'filled': filled,
1545
- 'cost': cost,
1546
- 'remaining': remaining,
1547
- 'fee': {
1548
- 'cost': fee,
1549
- 'currency': quote,
1550
- },
1551
- });
1552
- results.push(safeOrder);
1553
- }
1554
- return results;
1555
- }
1556
- parseOrderStatus(status) {
1557
- return this.safeString(this.options['order']['status'], status, status);
1454
+ const response = await this.privatePostDoMyInternalTransfer(this.extend(request, params));
1455
+ //
1456
+ // {
1457
+ // "ok": "ok",
1458
+ // "data": {
1459
+ // "transactionId": "30225415"
1460
+ // }
1461
+ // }
1462
+ //
1463
+ const data = this.safeDict(response, 'data', {});
1464
+ return this.parseTransfer(data, currency);
1558
1465
  }
1559
- async editOrder(id, symbol, type, side, amount = undefined, price = undefined, params = {}) {
1560
- /**
1561
- * @method
1562
- * @name cex#editOrderWs
1563
- * @description edit a trade order
1564
- * @see https://docs.cex.io/#cancel-replace-order
1565
- * @param {string} id order id
1566
- * @param {string} symbol unified symbol of the market to create an order in
1567
- * @param {string} type 'market' or 'limit'
1568
- * @param {string} side 'buy' or 'sell'
1569
- * @param {float} amount how much of the currency you want to trade in units of the base currency
1570
- * @param {float|undefined} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
1571
- * @param {object} [params] extra parameters specific to the cex api endpoint
1572
- * @returns {object} an [order structure]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure}
1573
- */
1574
- if (amount === undefined) {
1575
- throw new errors.ArgumentsRequired(this.id + ' editOrder() requires a amount argument');
1576
- }
1577
- if (price === undefined) {
1578
- throw new errors.ArgumentsRequired(this.id + ' editOrder() requires a price argument');
1579
- }
1580
- await this.loadMarkets();
1581
- const market = this.market(symbol);
1582
- // see: https://cex.io/rest-api#/definitions/CancelReplaceOrderRequest
1583
- const request = {
1584
- 'pair': market['id'],
1585
- 'type': side,
1586
- 'amount': amount,
1587
- 'price': price,
1588
- 'order_id': id,
1466
+ parseTransfer(transfer, currency = undefined) {
1467
+ //
1468
+ // transferBetweenSubAccounts
1469
+ //
1470
+ // {
1471
+ // "ok": "ok",
1472
+ // "data": {
1473
+ // "transactionId": "30225415"
1474
+ // }
1475
+ // }
1476
+ //
1477
+ // transfer between main/sub
1478
+ //
1479
+ // {
1480
+ // "ok": "ok",
1481
+ // "data": {
1482
+ // "accountId": "sub1",
1483
+ // "clientTxId": "27ba8284-67cf-4386-9ec7-80b3871abd45",
1484
+ // "currency": "USDT",
1485
+ // "status": "approved"
1486
+ // }
1487
+ // }
1488
+ //
1489
+ const currencyId = this.safeString(transfer, 'currency');
1490
+ const currencyCode = this.safeCurrencyCode(currencyId, currency);
1491
+ return {
1492
+ 'info': transfer,
1493
+ 'id': this.safeString2(transfer, 'transactionId', 'clientTxId'),
1494
+ 'timestamp': undefined,
1495
+ 'datetime': undefined,
1496
+ 'currency': currencyCode,
1497
+ 'amount': undefined,
1498
+ 'fromAccount': undefined,
1499
+ 'toAccount': undefined,
1500
+ 'status': this.parseTransactionStatus(this.safeString(transfer, 'status')),
1589
1501
  };
1590
- const response = await this.privatePostCancelReplaceOrderPair(this.extend(request, params));
1591
- return this.parseOrder(response, market);
1592
1502
  }
1593
1503
  async fetchDepositAddress(code, params = {}) {
1594
1504
  /**
1595
1505
  * @method
1596
1506
  * @name cex#fetchDepositAddress
1597
- * @see https://docs.cex.io/#get-crypto-address
1598
1507
  * @description fetch the deposit address for a currency associated with this account
1508
+ * @see https://trade.cex.io/docs/#rest-private-api-calls-deposit-address
1599
1509
  * @param {string} code unified currency code
1600
1510
  * @param {object} [params] extra parameters specific to the exchange API endpoint
1511
+ * @param {string} [params.accountId] account-id (default to empty string) to refer to (at this moment, only sub-accounts allowed by exchange)
1601
1512
  * @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure}
1602
1513
  */
1514
+ let accountId = undefined;
1515
+ [accountId, params] = this.handleOptionAndParams(params, 'createOrder', 'accountId');
1516
+ if (accountId === undefined) {
1517
+ throw new errors.ArgumentsRequired(this.id + ' fetchDepositAddress() : main account is not allowed to fetch deposit address from api, set params["accountId"] or .options["createOrder"]["accountId"] to the name of your sub-account');
1518
+ }
1603
1519
  await this.loadMarkets();
1520
+ let networkCode = undefined;
1521
+ [networkCode, params] = this.handleNetworkCodeAndParams(params);
1604
1522
  const currency = this.currency(code);
1605
1523
  const request = {
1524
+ 'accountId': accountId,
1606
1525
  'currency': currency['id'],
1526
+ 'blockchain': this.networkCodeToId(networkCode),
1607
1527
  };
1608
- const [networkCode, query] = this.handleNetworkCodeAndParams(params);
1609
- // atm, cex doesn't support network in the request
1610
- const response = await this.privatePostGetCryptoAddress(this.extend(request, query));
1528
+ const response = await this.privatePostGetDepositAddress(this.extend(request, params));
1611
1529
  //
1612
1530
  // {
1613
- // "e": "get_crypto_address",
1614
- // "ok": "ok",
1615
- // "data": {
1616
- // "name": "BTC",
1617
- // "addresses": [
1618
- // {
1619
- // "blockchain": "Bitcoin",
1620
- // "address": "2BvKwe1UwrdTjq2nzhscFYXwqCjCaaHCeq"
1531
+ // "ok": "ok",
1532
+ // "data": {
1533
+ // "address": "TCr..................1AE",
1534
+ // "accountId": "sub1",
1535
+ // "currency": "USDT",
1536
+ // "blockchain": "tron"
1537
+ // }
1538
+ // }
1621
1539
  //
1622
- // // for others coins (i.e. XRP, XLM) other keys are present:
1623
- // // "destination": "rF1sdh25BJX3qFwneeTBwaq3zPEWYcwjp2",
1624
- // // "destinationTag": "7519113655",
1625
- // // "memo": "XLM-memo12345",
1626
- // }
1627
- // ]
1628
- // }
1629
- // }
1630
- //
1631
- const data = this.safeValue(response, 'data', {});
1632
- const addresses = this.safeValue(data, 'addresses', []);
1633
- const chainsIndexedById = this.indexBy(addresses, 'blockchain');
1634
- const selectedNetworkId = this.selectNetworkIdFromRawNetworks(code, networkCode, chainsIndexedById);
1635
- const addressObject = this.safeValue(chainsIndexedById, selectedNetworkId, {});
1636
- const address = this.safeString2(addressObject, 'address', 'destination');
1540
+ const data = this.safeDict(response, 'data', {});
1541
+ return this.parseDepositAddress(data, currency);
1542
+ }
1543
+ parseDepositAddress(depositAddress, currency = undefined) {
1544
+ const address = this.safeString(depositAddress, 'address');
1545
+ const currencyId = this.safeString(depositAddress, 'currency');
1546
+ currency = this.safeCurrency(currencyId, currency);
1637
1547
  this.checkAddress(address);
1638
1548
  return {
1639
- 'info': data,
1640
- 'currency': code,
1641
- 'network': this.networkIdToCode(selectedNetworkId),
1549
+ 'info': depositAddress,
1550
+ 'currency': currency['code'],
1551
+ 'network': this.networkIdToCode(this.safeString(depositAddress, 'blockchain')),
1642
1552
  'address': address,
1643
- 'tag': this.safeString2(addressObject, 'destinationTag', 'memo'),
1553
+ 'tag': undefined,
1644
1554
  };
1645
1555
  }
1646
- nonce() {
1647
- return this.milliseconds();
1648
- }
1649
1556
  sign(path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
1650
- let url = this.urls['api']['rest'] + '/' + this.implodeParams(path, params);
1557
+ let url = this.urls['api'][api] + '/' + this.implodeParams(path, params);
1651
1558
  const query = this.omit(params, this.extractParams(path));
1652
1559
  if (api === 'public') {
1653
- if (Object.keys(query).length) {
1654
- url += '?' + this.urlencode(query);
1560
+ if (method === 'GET') {
1561
+ if (Object.keys(query).length) {
1562
+ url += '?' + this.urlencode(query);
1563
+ }
1564
+ }
1565
+ else {
1566
+ body = this.json(query);
1567
+ headers = {
1568
+ 'Content-Type': 'application/json',
1569
+ };
1655
1570
  }
1656
1571
  }
1657
1572
  else {
1658
1573
  this.checkRequiredCredentials();
1659
- const nonce = this.nonce().toString();
1660
- const auth = nonce + this.uid + this.apiKey;
1661
- const signature = this.hmac(this.encode(auth), this.encode(this.secret), sha256.sha256);
1662
- body = this.json(this.extend({
1663
- 'key': this.apiKey,
1664
- 'signature': signature.toUpperCase(),
1665
- 'nonce': nonce,
1666
- }, query));
1574
+ const seconds = this.seconds().toString();
1575
+ body = this.json(query);
1576
+ const auth = path + seconds + body;
1577
+ const signature = this.hmac(this.encode(auth), this.encode(this.secret), sha256.sha256, 'base64');
1667
1578
  headers = {
1668
1579
  'Content-Type': 'application/json',
1580
+ 'X-AGGR-KEY': this.apiKey,
1581
+ 'X-AGGR-TIMESTAMP': seconds,
1582
+ 'X-AGGR-SIGNATURE': signature,
1669
1583
  };
1670
1584
  }
1671
1585
  return { 'url': url, 'method': method, 'body': body, 'headers': headers };
1672
1586
  }
1673
1587
  handleErrors(code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
1674
- if (Array.isArray(response)) {
1675
- return response; // public endpoints may return []-arrays
1676
- }
1677
- if (body === 'true') {
1678
- return undefined;
1679
- }
1588
+ // in some cases, like from createOrder, exchange returns nested escaped JSON string:
1589
+ // {"ok":"ok","data":{"messageType":"executionReport", "orderRejectReason":"{\"code\":405}"} }
1590
+ // and because of `.parseJson` bug, we need extra fix
1680
1591
  if (response === undefined) {
1681
- throw new errors.NullResponse(this.id + ' returned ' + this.json(response));
1682
- }
1683
- if ('e' in response) {
1684
- if ('ok' in response) {
1685
- if (response['ok'] === 'ok') {
1686
- return undefined;
1687
- }
1592
+ if (body === undefined) {
1593
+ throw new errors.NullResponse(this.id + ' returned empty response');
1594
+ }
1595
+ else if (body[0] === '{') {
1596
+ const fixed = this.fixStringifiedJsonMembers(body);
1597
+ response = this.parseJson(fixed);
1598
+ }
1599
+ else {
1600
+ throw new errors.NullResponse(this.id + ' returned unparsed response: ' + body);
1688
1601
  }
1689
1602
  }
1690
- if ('error' in response) {
1691
- const message = this.safeString(response, 'error');
1603
+ const error = this.safeString(response, 'error');
1604
+ if (error !== undefined) {
1692
1605
  const feedback = this.id + ' ' + body;
1693
- this.throwExactlyMatchedException(this.exceptions['exact'], message, feedback);
1694
- this.throwBroadlyMatchedException(this.exceptions['broad'], message, feedback);
1606
+ this.throwExactlyMatchedException(this.exceptions['exact'], error, feedback);
1607
+ this.throwBroadlyMatchedException(this.exceptions['broad'], error, feedback);
1695
1608
  throw new errors.ExchangeError(feedback);
1696
1609
  }
1610
+ // check errors in order-engine (the responses are not standard, so we parse here)
1611
+ if (url.indexOf('do_my_new_order') >= 0) {
1612
+ const data = this.safeDict(response, 'data', {});
1613
+ const rejectReason = this.safeString(data, 'rejectReason');
1614
+ if (rejectReason !== undefined) {
1615
+ this.throwBroadlyMatchedException(this.exceptions['broad'], rejectReason, rejectReason);
1616
+ throw new errors.ExchangeError(this.id + ' createOrder() ' + rejectReason);
1617
+ }
1618
+ }
1697
1619
  return undefined;
1698
1620
  }
1699
1621
  }