ccxt
Version:
1,160 lines (1,158 loc) • 78.7 kB
JavaScript
// ----------------------------------------------------------------------------
// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
// EDIT THE CORRESPONDENT .ts FILE INSTEAD
// ---------------------------------------------------------------------------
import Exchange from './abstract/cex.js';
import { ExchangeError, ArgumentsRequired, NullResponse, PermissionDenied, InsufficientFunds, BadRequest, AuthenticationError } from './base/errors.js';
import { Precise } from './base/Precise.js';
import { TICK_SIZE } from './base/functions/number.js';
import { sha256 } from './static_dependencies/noble-hashes/sha256.js';
// ---------------------------------------------------------------------------
/**
* @class cex
* @augments Exchange
*/
export default class cex extends Exchange {
describe() {
return this.deepExtend(super.describe(), {
'id': 'cex',
'name': 'CEX.IO',
'countries': ['GB', 'EU', 'CY', 'RU'],
'rateLimit': 300,
'pro': true,
'has': {
'CORS': undefined,
'spot': true,
'margin': false,
'swap': false,
'future': false,
'option': false,
'addMargin': false,
'borrowCrossMargin': false,
'borrowIsolatedMargin': false,
'borrowMargin': false,
'cancelAllOrders': true,
'cancelOrder': true,
'closeAllPositions': false,
'closePosition': false,
'createOrder': true,
'createOrderWithTakeProfitAndStopLoss': false,
'createOrderWithTakeProfitAndStopLossWs': false,
'createPostOnlyOrder': false,
'createReduceOnlyOrder': false,
'createStopOrder': true,
'createTriggerOrder': true,
'fetchAccounts': true,
'fetchBalance': true,
'fetchBorrowInterest': false,
'fetchBorrowRate': false,
'fetchBorrowRateHistories': false,
'fetchBorrowRateHistory': false,
'fetchBorrowRates': false,
'fetchBorrowRatesPerSymbol': false,
'fetchClosedOrder': true,
'fetchClosedOrders': true,
'fetchCrossBorrowRate': false,
'fetchCrossBorrowRates': false,
'fetchCurrencies': true,
'fetchDepositAddress': true,
'fetchDepositsWithdrawals': true,
'fetchFundingHistory': false,
'fetchFundingInterval': false,
'fetchFundingIntervals': false,
'fetchFundingRate': false,
'fetchFundingRateHistory': false,
'fetchFundingRates': false,
'fetchGreeks': false,
'fetchIndexOHLCV': false,
'fetchIsolatedBorrowRate': false,
'fetchIsolatedBorrowRates': false,
'fetchIsolatedPositions': false,
'fetchLedger': true,
'fetchLeverage': false,
'fetchLeverages': false,
'fetchLeverageTiers': false,
'fetchLiquidations': false,
'fetchLongShortRatio': false,
'fetchLongShortRatioHistory': false,
'fetchMarginAdjustmentHistory': false,
'fetchMarginMode': false,
'fetchMarginModes': false,
'fetchMarketLeverageTiers': false,
'fetchMarkets': true,
'fetchMarkOHLCV': false,
'fetchMarkPrices': false,
'fetchMyLiquidations': false,
'fetchMySettlementHistory': false,
'fetchOHLCV': true,
'fetchOpenInterest': false,
'fetchOpenInterestHistory': false,
'fetchOpenInterests': false,
'fetchOpenOrder': true,
'fetchOpenOrders': true,
'fetchOption': false,
'fetchOptionChain': false,
'fetchOrderBook': true,
'fetchPosition': false,
'fetchPositionHistory': false,
'fetchPositionMode': false,
'fetchPositions': false,
'fetchPositionsForSymbol': false,
'fetchPositionsHistory': false,
'fetchPositionsRisk': false,
'fetchPremiumIndexOHLCV': false,
'fetchSettlementHistory': false,
'fetchTicker': true,
'fetchTickers': true,
'fetchTime': true,
'fetchTrades': true,
'fetchTradingFees': true,
'fetchVolatilityHistory': false,
'reduceMargin': false,
'repayCrossMargin': false,
'repayIsolatedMargin': false,
'repayMargin': false,
'setLeverage': false,
'setMargin': false,
'setMarginMode': false,
'setPositionMode': false,
'transfer': true,
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/27766442-8ddc33b0-5ed8-11e7-8b98-f786aef0f3c9.jpg',
'api': {
'public': 'https://trade.cex.io/api/spot/rest-public',
'private': 'https://trade.cex.io/api/spot/rest',
},
'www': 'https://cex.io',
'doc': 'https://trade.cex.io/docs/',
'fees': [
'https://cex.io/fee-schedule',
'https://cex.io/limits-commissions',
],
'referral': 'https://cex.io/r/0/up105393824/0/',
},
'api': {
'public': {
'get': {},
'post': {
'get_server_time': 1,
'get_pairs_info': 1,
'get_currencies_info': 1,
'get_processing_info': 10,
'get_ticker': 1,
'get_trade_history': 1,
'get_order_book': 1,
'get_candles': 1,
},
},
'private': {
'get': {},
'post': {
'get_my_current_fee': 5,
'get_fee_strategy': 1,
'get_my_volume': 5,
'do_create_account': 1,
'get_my_account_status_v3': 5,
'get_my_wallet_balance': 5,
'get_my_orders': 5,
'do_my_new_order': 1,
'do_cancel_my_order': 1,
'do_cancel_all_orders': 5,
'get_order_book': 1,
'get_candles': 1,
'get_trade_history': 1,
'get_my_transaction_history': 1,
'get_my_funding_history': 5,
'do_my_internal_transfer': 1,
'get_processing_info': 10,
'get_deposit_address': 5,
'do_deposit_funds_from_wallet': 1,
'do_withdrawal_funds_to_wallet': 1,
},
},
},
'features': {
'spot': {
'sandbox': false,
'createOrder': {
'marginMode': false,
'triggerPrice': true,
'triggerPriceType': undefined,
'triggerDirection': false,
'stopLossPrice': false,
'takeProfitPrice': false,
'attachedStopLossTakeProfit': undefined,
'timeInForce': {
'IOC': true,
'FOK': true,
'PO': false,
'GTD': true,
},
'hedged': false,
'leverage': false,
'marketBuyRequiresPrice': false,
'marketBuyByCost': true,
'selfTradePrevention': false,
'trailing': false,
'iceberg': false,
},
'createOrders': undefined,
'fetchMyTrades': undefined,
'fetchOrder': undefined,
'fetchOpenOrders': {
'marginMode': false,
'limit': 1000,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchOrders': undefined,
'fetchClosedOrders': {
'marginMode': false,
'limit': 1000,
'daysBack': 100000,
'daysBackCanceled': 1,
'untilDays': 100000,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchOHLCV': {
'limit': 1000,
},
},
'swap': {
'linear': undefined,
'inverse': undefined,
},
'future': {
'linear': undefined,
'inverse': undefined,
},
},
'precisionMode': TICK_SIZE,
'exceptions': {
'exact': {},
'broad': {
'You have negative balance on following accounts': InsufficientFunds,
'Mandatory parameter side should be one of BUY,SELL': BadRequest,
'API orders from Main account are not allowed': BadRequest,
'check failed': BadRequest,
'Insufficient funds': InsufficientFunds,
'Get deposit address for main account is not allowed': PermissionDenied,
'Market Trigger orders are not allowed': BadRequest,
'key not passed or incorrect': AuthenticationError,
},
},
'timeframes': {
'1m': '1m',
'5m': '5m',
'15m': '15m',
'30m': '30m',
'1h': '1h',
'2h': '2h',
'4h': '4h',
'1d': '1d',
},
'options': {
'networks': {
'BTC': 'bitcoin',
'ERC20': 'ERC20',
'BSC20': 'binancesmartchain',
'DOGE': 'dogecoin',
'ALGO': 'algorand',
'XLM': 'stellar',
'ATOM': 'cosmos',
'LTC': 'litecoin',
'XRP': 'ripple',
'FTM': 'fantom',
'MINA': 'mina',
'THETA': 'theta',
'XTZ': 'tezos',
'TIA': 'celestia',
'CRONOS': 'cronos',
'MATIC': 'polygon',
'TON': 'ton',
'TRC20': 'tron',
'SOLANA': 'solana',
'SGB': 'songbird',
'DYDX': 'dydx',
'DASH': 'dash',
'ZIL': 'zilliqa',
'EOS': 'eos',
'AVALANCHEC': 'avalanche',
'ETHPOW': 'ethereumpow',
'NEAR': 'near',
'ARB': 'arbitrum',
'DOT': 'polkadot',
'OPT': 'optimism',
'INJ': 'injective',
'ADA': 'cardano',
'ONT': 'ontology',
'ICP': 'icp',
'KAVA': 'kava',
'KSM': 'kusama',
'SEI': 'sei',
// 'OSM': 'osmosis',
'NEO': 'neo',
'NEO3': 'neo3',
// 'TERRAOLD': 'terra', // tbd
// 'TERRA': 'terra2', // tbd
// 'EVER': 'everscale', // tbd
'XDC': 'xdc',
},
},
});
}
/**
* @method
* @name cex#fetchCurrencies
* @description fetches all available currencies on an exchange
* @see https://trade.cex.io/docs/#rest-public-api-calls-currencies-info
* @param {dict} [params] extra parameters specific to the exchange API endpoint
* @returns {dict} an associative dictionary of currencies
*/
async fetchCurrencies(params = {}) {
const promises = [];
promises.push(this.publicPostGetCurrenciesInfo(params));
//
// {
// "ok": "ok",
// "data": [
// {
// "currency": "ZAP",
// "fiat": false,
// "precision": "8",
// "walletPrecision": "6",
// "walletDeposit": true,
// "walletWithdrawal": true
// },
// ...
//
promises.push(this.publicPostGetProcessingInfo(params));
//
// {
// "ok": "ok",
// "data": {
// "ADA": {
// "name": "Cardano",
// "blockchains": {
// "cardano": {
// "type": "coin",
// "deposit": "enabled",
// "minDeposit": "1",
// "withdrawal": "enabled",
// "minWithdrawal": "5",
// "withdrawalFee": "1",
// "withdrawalFeePercent": "0",
// "depositConfirmations": "15"
// }
// }
// },
// ...
//
const responses = await Promise.all(promises);
const dataCurrencies = this.safeList(responses[0], 'data', []);
const dataNetworks = this.safeDict(responses[1], 'data', {});
const currenciesIndexed = this.indexBy(dataCurrencies, 'currency');
const data = this.deepExtend(currenciesIndexed, dataNetworks);
return this.parseCurrencies(this.toArray(data));
}
parseCurrency(rawCurrency) {
const id = this.safeString(rawCurrency, 'currency');
const code = this.safeCurrencyCode(id);
const type = this.safeBool(rawCurrency, 'fiat') ? 'fiat' : 'crypto';
const currencyPrecision = this.parseNumber(this.parsePrecision(this.safeString(rawCurrency, 'precision')));
const networks = {};
const rawNetworks = this.safeDict(rawCurrency, 'blockchains', {});
const keys = Object.keys(rawNetworks);
for (let j = 0; j < keys.length; j++) {
const networkId = keys[j];
const rawNetwork = rawNetworks[networkId];
const networkCode = this.networkIdToCode(networkId);
const deposit = this.safeString(rawNetwork, 'deposit') === 'enabled';
const withdraw = this.safeString(rawNetwork, 'withdrawal') === 'enabled';
networks[networkCode] = {
'id': networkId,
'network': networkCode,
'margin': undefined,
'deposit': deposit,
'withdraw': withdraw,
'active': undefined,
'fee': this.safeNumber(rawNetwork, 'withdrawalFee'),
'precision': currencyPrecision,
'limits': {
'deposit': {
'min': this.safeNumber(rawNetwork, 'minDeposit'),
'max': undefined,
},
'withdraw': {
'min': this.safeNumber(rawNetwork, 'minWithdrawal'),
'max': undefined,
},
},
'info': rawNetwork,
};
}
return this.safeCurrencyStructure({
'id': id,
'code': code,
'name': undefined,
'type': type,
'active': undefined,
'deposit': this.safeBool(rawCurrency, 'walletDeposit'),
'withdraw': this.safeBool(rawCurrency, 'walletWithdrawal'),
'fee': undefined,
'precision': currencyPrecision,
'limits': {
'amount': {
'min': undefined,
'max': undefined,
},
'withdraw': {
'min': undefined,
'max': undefined,
},
},
'networks': networks,
'info': rawCurrency,
});
}
/**
* @method
* @name cex#fetchMarkets
* @description retrieves data on all markets for ace
* @see https://trade.cex.io/docs/#rest-public-api-calls-pairs-info
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} an array of objects representing market data
*/
async fetchMarkets(params = {}) {
const response = await this.publicPostGetPairsInfo(params);
//
// {
// "ok": "ok",
// "data": [
// {
// "base": "AI",
// "quote": "USD",
// "baseMin": "30",
// "baseMax": "2516000",
// "baseLotSize": "0.000001",
// "quoteMin": "10",
// "quoteMax": "1000000",
// "quoteLotSize": "0.01000000",
// "basePrecision": "6",
// "quotePrecision": "8",
// "pricePrecision": "4",
// "minPrice": "0.0377",
// "maxPrice": "19.5000"
// },
// ...
//
const data = this.safeList(response, 'data', []);
return this.parseMarkets(data);
}
parseMarket(market) {
const baseId = this.safeString(market, 'base');
const base = this.safeCurrencyCode(baseId);
const quoteId = this.safeString(market, 'quote');
const quote = this.safeCurrencyCode(quoteId);
const id = base + '-' + quote; // not actual id, but for this exchange we can use this abbreviation, because e.g. tickers have hyphen in between
const symbol = base + '/' + quote;
return this.safeMarketStructure({
'id': id,
'symbol': symbol,
'base': base,
'baseId': baseId,
'quote': quote,
'quoteId': quoteId,
'settle': undefined,
'settleId': undefined,
'type': 'spot',
'spot': true,
'margin': false,
'swap': false,
'future': false,
'option': false,
'contract': false,
'linear': undefined,
'inverse': undefined,
'contractSize': undefined,
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'limits': {
'amount': {
'min': this.safeNumber(market, 'baseMin'),
'max': this.safeNumber(market, 'baseMax'),
},
'price': {
'min': this.safeNumber(market, 'minPrice'),
'max': this.safeNumber(market, 'maxPrice'),
},
'cost': {
'min': this.safeNumber(market, 'quoteMin'),
'max': this.safeNumber(market, 'quoteMax'),
},
'leverage': {
'min': undefined,
'max': undefined,
},
},
'precision': {
'amount': this.safeString(market, 'baseLotSize'),
'price': this.parseNumber(this.parsePrecision(this.safeString(market, 'pricePrecision'))),
// 'cost': this.parseNumber (this.parsePrecision (this.safeString (market, 'quoteLotSize'))), // buggy, doesn't reflect their documentation
'base': this.parseNumber(this.parsePrecision(this.safeString(market, 'basePrecision'))),
'quote': this.parseNumber(this.parsePrecision(this.safeString(market, 'quotePrecision'))),
},
'active': undefined,
'created': undefined,
'info': market,
});
}
/**
* @method
* @name cex#fetchTime
* @description fetches the current integer timestamp in milliseconds from the exchange server
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {int} the current integer timestamp in milliseconds from the exchange server
*/
async fetchTime(params = {}) {
const response = await this.publicPostGetServerTime(params);
//
// {
// "ok": "ok",
// "data": {
// "timestamp": "1728472063472",
// "ISODate": "2024-10-09T11:07:43.472Z"
// }
// }
//
const data = this.safeDict(response, 'data');
const timestamp = this.safeInteger(data, 'timestamp');
return timestamp;
}
/**
* @method
* @name cex#fetchTicker
* @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
* @see https://trade.cex.io/docs/#rest-public-api-calls-ticker
* @param {string} symbol
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
*/
async fetchTicker(symbol, params = {}) {
await this.loadMarkets();
const response = await this.fetchTickers([symbol], params);
return this.safeDict(response, symbol, {});
}
/**
* @method
* @name cex#fetchTickers
* @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
* @see https://trade.cex.io/docs/#rest-public-api-calls-ticker
* @param {string[]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
*/
async fetchTickers(symbols = undefined, params = {}) {
await this.loadMarkets();
const request = {};
if (symbols !== undefined) {
request['pairs'] = this.marketIds(symbols);
}
const response = await this.publicPostGetTicker(this.extend(request, params));
//
// {
// "ok": "ok",
// "data": {
// "AI-USD": {
// "bestBid": "0.3917",
// "bestAsk": "0.3949",
// "bestBidChange": "0.0035",
// "bestBidChangePercentage": "0.90",
// "bestAskChange": "0.0038",
// "bestAskChangePercentage": "0.97",
// "low": "0.3787",
// "high": "0.3925",
// "volume30d": "2945.722277",
// "lastTradeDateISO": "2024-10-11T06:18:42.077Z",
// "volume": "120.736000",
// "quoteVolume": "46.65654070",
// "lastTradeVolume": "67.914000",
// "volumeUSD": "46.65",
// "last": "0.3949",
// "lastTradePrice": "0.3925",
// "priceChange": "0.0038",
// "priceChangePercentage": "0.97"
// },
// ...
//
const data = this.safeDict(response, 'data', {});
return this.parseTickers(data, symbols);
}
parseTicker(ticker, market = undefined) {
const marketId = this.safeString(ticker, 'id');
const symbol = this.safeSymbol(marketId, market);
return this.safeTicker({
'symbol': symbol,
'timestamp': undefined,
'datetime': undefined,
'high': this.safeNumber(ticker, 'high'),
'low': this.safeNumber(ticker, 'low'),
'bid': this.safeNumber(ticker, 'bestBid'),
'bidVolume': undefined,
'ask': this.safeNumber(ticker, 'bestAsk'),
'askVolume': undefined,
'vwap': undefined,
'open': undefined,
'close': this.safeString(ticker, 'last'),
'previousClose': undefined,
'change': this.safeNumber(ticker, 'priceChange'),
'percentage': this.safeNumber(ticker, 'priceChangePercentage'),
'average': undefined,
'baseVolume': this.safeString(ticker, 'volume'),
'quoteVolume': this.safeString(ticker, 'quoteVolume'),
'info': ticker,
}, market);
}
/**
* @method
* @name cex#fetchTrades
* @description get the list of most recent trades for a particular symbol
* @see https://trade.cex.io/docs/#rest-public-api-calls-trade-history
* @param {string} symbol unified symbol of the market to fetch trades for
* @param {int} [since] timestamp in ms of the earliest trade to fetch
* @param {int} [limit] the maximum amount of trades to fetch
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.until] timestamp in ms of the latest entry
* @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
*/
async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'pair': market['id'],
};
if (since !== undefined) {
request['fromDateISO'] = this.iso8601(since);
}
let until = undefined;
[until, params] = this.handleParamInteger2(params, 'until', 'till');
if (until !== undefined) {
request['toDateISO'] = this.iso8601(until);
}
if (limit !== undefined) {
request['pageSize'] = Math.min(limit, 10000); // has a bug, still returns more trades
}
const response = await this.publicPostGetTradeHistory(this.extend(request, params));
//
// {
// "ok": "ok",
// "data": {
// "pageSize": "10",
// "trades": [
// {
// "tradeId": "1728630559823-0",
// "dateISO": "2024-10-11T07:09:19.823Z",
// "side": "SELL",
// "price": "60879.5",
// "amount": "0.00165962"
// },
// ... followed by older trades
//
const data = this.safeDict(response, 'data', {});
const trades = this.safeList(data, 'trades', []);
return this.parseTrades(trades, market, since, limit);
}
parseTrade(trade, market = undefined) {
//
// public fetchTrades
//
// {
// "tradeId": "1728630559823-0",
// "dateISO": "2024-10-11T07:09:19.823Z",
// "side": "SELL",
// "price": "60879.5",
// "amount": "0.00165962"
// },
//
const dateStr = this.safeString(trade, 'dateISO');
const timestamp = this.parse8601(dateStr);
market = this.safeMarket(undefined, market);
return this.safeTrade({
'info': trade,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'symbol': market['symbol'],
'id': this.safeString(trade, 'tradeId'),
'order': undefined,
'type': undefined,
'takerOrMaker': undefined,
'side': this.safeStringLower(trade, 'side'),
'price': this.safeString(trade, 'price'),
'amount': this.safeString(trade, 'amount'),
'cost': undefined,
'fee': undefined,
}, market);
}
/**
* @method
* @name cex#fetchOrderBook
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://trade.cex.io/docs/#rest-public-api-calls-order-book
* @param {string} symbol unified symbol of the market to fetch the order book for
* @param {int} [limit] the maximum amount of order book entries to return
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
*/
async fetchOrderBook(symbol, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'pair': market['id'],
};
const response = await this.publicPostGetOrderBook(this.extend(request, params));
//
// {
// "ok": "ok",
// "data": {
// "timestamp": "1728636922648",
// "currency1": "BTC",
// "currency2": "USDT",
// "bids": [
// [
// "60694.1",
// "13.12849761"
// ],
// [
// "60694.0",
// "0.71829244"
// ],
// ...
//
const orderBook = this.safeDict(response, 'data', {});
const timestamp = this.safeInteger(orderBook, 'timestamp');
return this.parseOrderBook(orderBook, market['symbol'], timestamp);
}
/**
* @method
* @name cex#fetchOHLCV
* @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @see https://trade.cex.io/docs/#rest-public-api-calls-candles
* @param {string} symbol unified symbol of the market to fetch OHLCV data for
* @param {string} timeframe the length of time each candle represents
* @param {int} [since] timestamp in ms of the earliest candle to fetch
* @param {int} [limit] the maximum amount of candles to fetch
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.until] timestamp in ms of the latest entry
* @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
*/
async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
let dataType = undefined;
[dataType, params] = this.handleOptionAndParams(params, 'fetchOHLCV', 'dataType');
if (dataType === undefined) {
throw new ArgumentsRequired(this.id + ' fetchOHLCV requires a parameter "dataType" to be either "bestBid" or "bestAsk"');
}
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'pair': market['id'],
'resolution': this.timeframes[timeframe],
'dataType': dataType,
};
if (since !== undefined) {
request['fromISO'] = this.iso8601(since);
}
let until = undefined;
[until, params] = this.handleParamInteger2(params, 'until', 'till');
if (until !== undefined) {
request['toISO'] = this.iso8601(until);
}
else if (since === undefined) {
// exchange still requires that we provide one of them
request['toISO'] = this.iso8601(this.milliseconds());
}
if (since !== undefined && until !== undefined && limit !== undefined) {
throw new ArgumentsRequired(this.id + ' fetchOHLCV does not support fetching candles with both a limit and since/until');
}
else if ((since !== undefined || until !== undefined) && limit === undefined) {
throw new ArgumentsRequired(this.id + ' fetchOHLCV requires a limit parameter when fetching candles with since or until');
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.publicPostGetCandles(this.extend(request, params));
//
// {
// "ok": "ok",
// "data": [
// {
// "timestamp": "1728643320000",
// "open": "61061",
// "high": "61095.1",
// "low": "61048.5",
// "close": "61087.8",
// "volume": "0",
// "resolution": "1m",
// "isClosed": true,
// "timestampISO": "2024-10-11T10:42:00.000Z"
// },
// ...
//
const data = this.safeList(response, 'data', []);
return this.parseOHLCVs(data, market, timeframe, since, limit);
}
parseOHLCV(ohlcv, market = undefined) {
return [
this.safeInteger(ohlcv, 'timestamp'),
this.safeNumber(ohlcv, 'open'),
this.safeNumber(ohlcv, 'high'),
this.safeNumber(ohlcv, 'low'),
this.safeNumber(ohlcv, 'close'),
this.safeNumber(ohlcv, 'volume'),
];
}
/**
* @method
* @name cex#fetchTradingFees
* @description fetch the trading fees for multiple markets
* @see https://trade.cex.io/docs/#rest-public-api-calls-candles
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a dictionary of [fee structures]{@link https://docs.ccxt.com/#/?id=fee-structure} indexed by market symbols
*/
async fetchTradingFees(params = {}) {
await this.loadMarkets();
const response = await this.privatePostGetMyCurrentFee(params);
//
// {
// "ok": "ok",
// "data": {
// "tradingFee": {
// "AI-USD": {
// "percent": "0.25"
// },
// ...
//
const data = this.safeDict(response, 'data', {});
const fees = this.safeDict(data, 'tradingFee', {});
return this.parseTradingFees(fees, true);
}
parseTradingFees(response, useKeyAsId = false) {
const result = {};
const keys = Object.keys(response);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
let market = undefined;
if (useKeyAsId) {
market = this.safeMarket(key);
}
const parsed = this.parseTradingFee(response[key], market);
result[parsed['symbol']] = parsed;
}
for (let i = 0; i < this.symbols.length; i++) {
const symbol = this.symbols[i];
if (!(symbol in result)) {
const market = this.market(symbol);
result[symbol] = this.parseTradingFee(response, market);
}
}
return result;
}
parseTradingFee(fee, market = undefined) {
return {
'info': fee,
'symbol': this.safeString(market, 'symbol'),
'maker': this.safeNumber(fee, 'percent'),
'taker': this.safeNumber(fee, 'percent'),
'percentage': undefined,
'tierBased': undefined,
};
}
async fetchAccounts(params = {}) {
await this.loadMarkets();
const response = await this.privatePostGetMyAccountStatusV3(params);
//
// {
// "ok": "ok",
// "data": {
// "convertedCurrency": "USD",
// "balancesPerAccounts": {
// "": {
// "AI": {
// "balance": "0.000000",
// "balanceOnHold": "0.000000"
// },
// "USDT": {
// "balance": "0.00000000",
// "balanceOnHold": "0.00000000"
// }
// }
// }
// }
// }
//
const data = this.safeDict(response, 'data', {});
const balances = this.safeDict(data, 'balancesPerAccounts', {});
const arrays = this.toArray(balances);
return this.parseAccounts(arrays, params);
}
parseAccount(account) {
return {
'id': undefined,
'type': undefined,
'code': undefined,
'info': account,
};
}
/**
* @method
* @name cex#fetchBalance
* @description query for balance and get the amount of funds available for trading or funds locked in orders
* @see https://trade.cex.io/docs/#rest-private-api-calls-account-status-v3
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {object} [params.method] 'privatePostGetMyWalletBalance' or 'privatePostGetMyAccountStatusV3'
* @param {object} [params.account] in case 'privatePostGetMyAccountStatusV3' is chosen, this can specify the account name (default is empty string)
* @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
*/
async fetchBalance(params = {}) {
let accountName = undefined;
[accountName, params] = this.handleParamString(params, 'account', ''); // default is empty string
let method = undefined;
[method, params] = this.handleParamString(params, 'method', 'privatePostGetMyWalletBalance');
let accountBalance = undefined;
if (method === 'privatePostGetMyAccountStatusV3') {
const response = await this.privatePostGetMyAccountStatusV3(params);
//
// {
// "ok": "ok",
// "data": {
// "convertedCurrency": "USD",
// "balancesPerAccounts": {
// "": {
// "AI": {
// "balance": "0.000000",
// "balanceOnHold": "0.000000"
// },
// ....
//
const data = this.safeDict(response, 'data', {});
const balances = this.safeDict(data, 'balancesPerAccounts', {});
accountBalance = this.safeDict(balances, accountName, {});
}
else {
const response = await this.privatePostGetMyWalletBalance(params);
//
// {
// "ok": "ok",
// "data": {
// "AI": {
// "balance": "25.606429"
// },
// "USDT": {
// "balance": "7.935449"
// },
// ...
//
accountBalance = this.safeDict(response, 'data', {});
}
return this.parseBalance(accountBalance);
}
parseBalance(response) {
const result = {
'info': response,
};
const keys = Object.keys(response);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const balance = this.safeDict(response, key, {});
const code = this.safeCurrencyCode(key);
const account = {
'used': this.safeString(balance, 'balanceOnHold'),
'total': this.safeString(balance, 'balance'),
};
result[code] = account;
}
return this.safeBalance(result);
}
/**
* @method
* @name cex#fetchOrders
* @description fetches information on multiple orders made by the user
* @see https://trade.cex.io/docs/#rest-private-api-calls-orders
* @param {string} status order status to fetch for
* @param {string} symbol unified market symbol of the market orders were made in
* @param {int} [since] the earliest time in ms to fetch orders for
* @param {int} [limit] the maximum number of order structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.until] timestamp in ms of the latest entry
* @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async fetchOrdersByStatus(status, symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const request = {};
const isClosedOrders = (status === 'closed');
if (isClosedOrders) {
request['archived'] = true;
}
let market = undefined;
if (symbol !== undefined) {
market = this.market(symbol);
request['pair'] = market['id'];
}
if (limit !== undefined) {
request['pageSize'] = limit;
}
if (since !== undefined) {
request['serverCreateTimestampFrom'] = since;
}
else if (isClosedOrders) {
// exchange requires a `since` parameter for closed orders, so set default to allowed 365
request['serverCreateTimestampFrom'] = this.milliseconds() - 364 * 24 * 60 * 60 * 1000;
}
let until = undefined;
[until, params] = this.handleParamInteger2(params, 'until', 'till');
if (until !== undefined) {
request['serverCreateTimestampTo'] = until;
}
const response = await this.privatePostGetMyOrders(this.extend(request, params));
//
// if called without `pair`
//
// {
// "ok": "ok",
// "data": [
// {
// "orderId": "1313003",
// "clientOrderId": "037F0AFEB93A",
// "clientId": "up421412345",
// "accountId": null,
// "status": "FILLED",
// "statusIsFinal": true,
// "currency1": "AI",
// "currency2": "USDT",
// "side": "BUY",
// "orderType": "Market",
// "timeInForce": "IOC",
// "comment": null,
// "rejectCode": null,
// "rejectReason": null,
// "initialOnHoldAmountCcy1": null,
// "initialOnHoldAmountCcy2": "10.23456700",
// "executedAmountCcy1": "25.606429",
// "executedAmountCcy2": "10.20904439",
// "requestedAmountCcy1": null,
// "requestedAmountCcy2": "10.20904439",
// "originalAmountCcy2": "10.23456700",
// "feeAmount": "0.02552261",
// "feeCurrency": "USDT",
// "price": null,
// "averagePrice": "0.3986",
// "clientCreateTimestamp": "1728474625320",
// "serverCreateTimestamp": "1728474624956",
// "lastUpdateTimestamp": "1728474628015",
// "expireTime": null,
// "effectiveTime": null
// },
// ...
//
const data = this.safeList(response, 'data', []);
return this.parseOrders(data, market, since, limit);
}
/**
* @method
* @name cex#fetchClosedOrders
* @see https://trade.cex.io/docs/#rest-private-api-calls-orders
* @description fetches information on multiple canceled orders made by the user
* @param {string} symbol unified market symbol of the market orders were made in
* @param {int} [since] timestamp in ms of the earliest order, default is undefined
* @param {int} [limit] max number of orders to return, default is undefined
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async fetchClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
return await this.fetchOrdersByStatus('closed', symbol, since, limit, params);
}
/**
* @method
* @name cex#fetchOpenOrders
* @see https://trade.cex.io/docs/#rest-private-api-calls-orders
* @description fetches information on multiple canceled orders made by the user
* @param {string} symbol unified market symbol of the market orders were made in
* @param {int} [since] timestamp in ms of the earliest order, default is undefined
* @param {int} [limit] max number of orders to return, default is undefined
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
return await this.fetchOrdersByStatus('open', symbol, since, limit, params);
}
/**
* @method
* @name cex#fetchOpenOrder
* @description fetches information on an open order made by the user
* @see https://trade.cex.io/docs/#rest-private-api-calls-orders
* @param {string} id order id
* @param {string} [symbol] unified symbol of the market the order was made in
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async fetchOpenOrder(id, symbol = undefined, params = {}) {
await this.loadMarkets();
const request = {
'orderId': parseInt(id),
};
const result = await this.fetchOpenOrders(symbol, undefined, undefined, this.extend(request, params));
return result[0];
}
/**
* @method
* @name cex#fetchClosedOrder
* @description fetches information on an closed order made by the user
* @see https://trade.cex.io/docs/#rest-private-api-calls-orders
* @param {string} id order id
* @param {string} [symbol] unified symbol of the market the order was made in
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async fetchClosedOrder(id, symbol = undefined, params = {}) {
await this.loadMarkets();
const request = {
'orderId': parseInt(id),
};
const result = await this.fetchClosedOrders(symbol, undefined, undefined, this.extend(request, params));
return result[0];
}
parseOrderStatus(status) {
const statuses = {
'PENDING_NEW': 'open',
'NEW': 'open',
'PARTIALLY_FILLED': 'open',
'FILLED': 'closed',
'EXPIRED': 'expired',
'REJECTED': 'rejected',
'PENDING_CANCEL': 'canceling',
'CANCELLED': 'canceled',
};
return this.safeString(statuses, status, status);
}
parseOrder(order, market = undefined) {
//
// "orderId": "1313003",
// "clientOrderId": "037F0AFEB93A",
// "clientId": "up421412345",
// "accountId": null,
// "status": "FILLED",
// "statusIsFinal": true,
// "currency1": "AI",
// "currency2": "USDT",
// "side": "BUY",
// "orderType": "Market",
// "timeInForce": "IOC",
// "comment": null,
// "rejectCode": null,
//