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