ccxt 4.4.21 → 4.4.23

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