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