ccxt-xigua
Version:
4,438 lines • 189 kB
JavaScript
'use strict';
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, ArgumentsRequired, BadRequest, BadSymbol, RateLimitExceeded, InsufficientFunds, OrderNotFound, InvalidOrder, AuthenticationError, PermissionDenied, ExchangeNotAvailable, RequestTimeout } = require ('./base/errors');
const { TICK_SIZE } = require ('./base/functions/number');
const Precise = require ('./base/Precise');
// ---------------------------------------------------------------------------
module.exports = class coinex extends Exchange {
describe () {
return this.deepExtend (super.describe (), {
'id': 'coinex',
'name': 'CoinEx',
'version': 'v1',
'countries': [ 'CN' ],
// IP ratelimit is 400 requests per second
// rateLimit = 1000ms / 400 = 2.5
// 200 per 2 seconds => 100 per second => weight = 4
// 120 per 2 seconds => 60 per second => weight = 6.667
// 80 per 2 seconds => 40 per second => weight = 10
// 60 per 2 seconds => 30 per second => weight = 13.334
// 40 per 2 seconds => 20 per second => weight = 20
// 20 per 2 seconds => 10 per second => weight = 40
'rateLimit': 2.5,
'pro': true,
'has': {
'CORS': undefined,
'spot': true,
'margin': true,
'swap': true,
'future': false,
'option': false,
'addMargin': true,
'borrowMargin': true,
'cancelAllOrders': true,
'cancelOrder': true,
'createDepositAddress': true,
'createOrder': true,
'createReduceOnlyOrder': true,
'fetchBalance': true,
'fetchBorrowInterest': true,
'fetchBorrowRate': true,
'fetchBorrowRateHistories': false,
'fetchBorrowRateHistory': false,
'fetchBorrowRates': true,
'fetchClosedOrders': true,
'fetchCurrencies': true,
'fetchDepositAddress': true,
'fetchDepositAddressByNetwork': false,
'fetchDepositAddresses': false,
'fetchDeposits': true,
'fetchFundingHistory': true,
'fetchFundingRate': true,
'fetchFundingRateHistory': true,
'fetchFundingRates': false,
'fetchIndexOHLCV': false,
'fetchLeverage': false,
'fetchLeverageTiers': true,
'fetchMarketLeverageTiers': 'emulated',
'fetchMarkets': true,
'fetchMarkOHLCV': false,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenOrders': true,
'fetchOrder': true,
'fetchOrderBook': true,
'fetchPosition': true,
'fetchPositions': true,
'fetchPositionsRisk': false,
'fetchPremiumIndexOHLCV': false,
'fetchTicker': true,
'fetchTickers': true,
'fetchTime': true,
'fetchTrades': true,
'fetchTradingFee': true,
'fetchTradingFees': true,
'fetchTransactionFee:': false,
'fetchTransactoinFees': false,
'fetchTransfer': false,
'fetchTransfers': true,
'fetchWithdrawal': false,
'fetchWithdrawals': true,
'reduceMargin': true,
'repayMargin': true,
'setLeverage': true,
'setMarginMode': true,
'setPositionMode': false,
'transfer': true,
'withdraw': true,
},
'timeframes': {
'1m': '1min',
'3m': '3min',
'5m': '5min',
'15m': '15min',
'30m': '30min',
'1h': '1hour',
'2h': '2hour',
'4h': '4hour',
'6h': '6hour',
'12h': '12hour',
'1d': '1day',
'3d': '3day',
'1w': '1week',
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/51840849/87182089-1e05fa00-c2ec-11ea-8da9-cc73b45abbbc.jpg',
'api': {
'public': 'https://api.coinex.com',
'private': 'https://api.coinex.com',
'perpetualPublic': 'https://api.coinex.com/perpetual',
'perpetualPrivate': 'https://api.coinex.com/perpetual',
},
'www': 'https://www.coinex.com',
'doc': 'https://github.com/coinexcom/coinex_exchange_api/wiki',
'fees': 'https://www.coinex.com/fees',
'referral': 'https://www.coinex.com/register?refer_code=yw5fz',
},
'api': {
'public': {
'get': {
'amm/market': 1,
'common/currency/rate': 1,
'common/asset/config': 1,
'common/maintain/info': 1,
'common/temp-maintain/info': 1,
'margin/market': 1,
'market/info': 1,
'market/list': 1,
'market/ticker': 1,
'market/ticker/all': 1,
'market/depth': 1,
'market/deals': 1,
'market/kline': 1,
'market/detail': 1,
},
},
'private': {
'get': {
'account/amm/balance': 40,
'account/investment/balance': 40,
'account/balance/history': 40,
'account/market/fee': 40,
'balance/coin/deposit': 40,
'balance/coin/withdraw': 40,
'balance/info': 40,
'balance/deposit/address/{coin_type}': 40,
'contract/transfer/history': 40,
'credit/info': 40,
'credit/balance': 40,
'investment/transfer/history': 40,
'margin/account': 1,
'margin/config': 1,
'margin/loan/history': 40,
'margin/transfer/history': 40,
'order/deals': 40,
'order/finished': 40,
'order/pending': 4,
'order/status': 4,
'order/status/batch': 4,
'order/user/deals': 40,
'order/stop/finished': 40,
'order/stop/pending': 4,
'order/user/trade/fee': 1,
'order/market/trade/info': 1,
'sub_account/balance': 1,
'sub_account/transfer/history': 40,
'sub_account/auth/api/{user_auth_id}': 40,
},
'post': {
'balance/coin/withdraw': 40,
'contract/balance/transfer': 40,
'margin/flat': 40,
'margin/loan': 40,
'margin/transfer': 40,
'order/limit/batch': 13.334,
'order/ioc': 6.667,
'order/limit': 6.667,
'order/market': 6.667,
'order/modify': 6.667,
'order/stop/limit': 6.667,
'order/stop/market': 6.667,
'order/stop/modify': 6.667,
'sub_account/transfer': 40,
'sub_account/register': 1,
'sub_account/unfrozen': 40,
'sub_account/frozen': 40,
'sub_account/auth/api': 40,
},
'put': {
'balance/deposit/address/{coin_type}': 40,
'sub_account/auth/api/{user_auth_id}': 40,
'v1/account/settings': 40,
},
'delete': {
'balance/coin/withdraw': 40,
'order/pending/batch': 13.334,
'order/pending': 6.667,
'order/stop/pending': 13.334,
'order/stop/pending/{id}': 13.334,
'sub_account/auth/api/{user_auth_id}': 40,
},
},
'perpetualPublic': {
'get': {
'ping': 1,
'time': 1,
'market/list': 1,
'market/limit_config': 1,
'market/ticker': 1,
'market/ticker/all': 1,
'market/depth': 1,
'market/deals': 1,
'market/funding_history': 1,
'market/user_deals': 1,
'market/kline': 1,
},
},
'perpetualPrivate': {
'get': {
'asset/query': 40,
'order/pending': 4,
'order/finished': 40,
'order/stop_finished': 40,
'order/stop_pending': 4,
'order/status': 4,
'order/stop_status': 4,
'position/pending': 40,
'position/funding': 40,
},
'post': {
'market/adjust_leverage': 1,
'market/position_expect': 1,
'order/put_limit': 10,
'order/put_market': 10,
'order/put_stop_limit': 10,
'order/put_stop_market': 10,
'order/modify': 10,
'order/modify_stop': 10,
'order/cancel': 10,
'order/cancel_all': 20,
'order/cancel_batch': 20,
'order/cancel_stop': 10,
'order/cancel_stop_all': 20,
'order/close_limit': 10,
'order/close_market': 10,
'position/adjust_margin': 10,
'position/stop_loss': 10,
'position/take_profit': 10,
'position/market_close': 10,
},
},
},
'fees': {
'trading': {
'maker': 0.001,
'taker': 0.001,
},
'funding': {
'withdraw': {
'BCH': 0.0,
'BTC': 0.001,
'LTC': 0.001,
'ETH': 0.001,
'ZEC': 0.0001,
'DASH': 0.0001,
},
},
},
'limits': {
'amount': {
'min': 0.001,
'max': undefined,
},
},
'precision': {
'amount': this.parseNumber ('0.00000001'),
'price': this.parseNumber ('0.00000001'),
},
'options': {
'createMarketBuyOrderRequiresPrice': true,
'defaultType': 'spot', // spot, swap, margin
'defaultSubType': 'linear', // linear, inverse
'defaultMarginMode': 'isolated', // isolated, cross
'fetchDepositAddress': {
'fillResponseFromRequest': true,
},
'accountsById': {
'spot': '0',
},
},
'commonCurrencies': {
'ACM': 'Actinium',
},
'precisionMode': TICK_SIZE,
});
}
async fetchCurrencies (params = {}) {
const response = await this.publicGetCommonAssetConfig (params);
//
// {
// code: 0,
// data: {
// 'CET-CSC': {
// asset: 'CET',
// chain: 'CSC',
// withdrawal_precision: 8,
// can_deposit: true,
// can_withdraw: true,
// deposit_least_amount: '0.026',
// withdraw_least_amount: '20',
// withdraw_tx_fee: '0.026'
// },
// ...
// message: 'Success',
// }
//
const data = this.safeValue (response, 'data', []);
const coins = Object.keys (data);
const result = {};
for (let i = 0; i < coins.length; i++) {
const coin = coins[i];
const currency = data[coin];
const currencyId = this.safeString (currency, 'asset');
const networkId = this.safeString (currency, 'chain');
const code = this.safeCurrencyCode (currencyId);
if (this.safeValue (result, code) === undefined) {
result[code] = {
'id': currencyId,
'numericId': undefined,
'code': code,
'info': currency,
'name': undefined,
'active': true,
'deposit': this.safeValue (currency, 'can_deposit'),
'withdraw': this.safeValue (currency, 'can_withdraw'),
'fee': this.safeNumber (currency, 'withdraw_tx_fee'),
'precision': this.parseNumber (this.parsePrecision (this.safeString (currency, 'withdrawal_precision'))),
'limits': {
'amount': {
'min': undefined,
'max': undefined,
},
'deposit': {
'min': this.safeNumber (currency, 'deposit_least_amount'),
'max': undefined,
},
'withdraw': {
'min': this.safeNumber (currency, 'withdraw_least_amount'),
'max': undefined,
},
},
};
}
const networks = this.safeValue (result[code], 'networks', {});
const network = {
'info': currency,
'id': networkId,
'network': networkId,
'name': undefined,
'limits': {
'amount': {
'min': undefined,
'max': undefined,
},
'deposit': {
'min': this.safeNumber (currency, 'deposit_least_amount'),
'max': undefined,
},
'withdraw': {
'min': this.safeNumber (currency, 'withdraw_least_amount'),
'max': undefined,
},
},
'active': true,
'deposit': this.safeValue (currency, 'can_deposit'),
'withdraw': this.safeValue (currency, 'can_withdraw'),
'fee': this.safeNumber (currency, 'withdraw_tx_fee'),
'precision': this.parseNumber (this.parsePrecision (this.safeString (currency, 'withdrawal_precision'))),
};
networks[networkId] = network;
result[code]['networks'] = networks;
}
return result;
}
async fetchMarkets (params = {}) {
/**
* @method
* @name coinex#fetchMarkets
* @description retrieves data on all markets for coinex
* @param {dict} params extra parameters specific to the exchange api endpoint
* @returns {[dict]} an array of objects representing market data
*/
let result = [];
const [ type, query ] = this.handleMarketTypeAndParams ('fetchMarkets', undefined, params);
if (type === 'spot' || type === 'margin') {
result = await this.fetchSpotMarkets (query);
} else if (type === 'swap') {
result = await this.fetchContractMarkets (query);
} else {
throw new ExchangeError (this.id + " does not support the '" + type + "' market type, set exchange.options['defaultType'] to 'spot', 'margin' or 'swap'");
}
return result;
}
async fetchSpotMarkets (params) {
const response = await this.publicGetMarketInfo (params);
//
// {
// "code": 0,
// "data": {
// "WAVESBTC": {
// "name": "WAVESBTC",
// "min_amount": "1",
// "maker_fee_rate": "0.001",
// "taker_fee_rate": "0.001",
// "pricing_name": "BTC",
// "pricing_decimal": 8,
// "trading_name": "WAVES",
// "trading_decimal": 8
// }
// }
// }
//
const markets = this.safeValue (response, 'data', {});
const result = [];
const keys = Object.keys (markets);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const market = markets[key];
const id = this.safeString (market, 'name');
const tradingName = this.safeString (market, 'trading_name');
const baseId = tradingName;
const quoteId = this.safeString (market, 'pricing_name');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
let symbol = base + '/' + quote;
if (tradingName === id) {
symbol = id;
}
result.push ({
'id': id,
'symbol': symbol,
'base': base,
'quote': quote,
'settle': undefined,
'baseId': baseId,
'quoteId': quoteId,
'settleId': undefined,
'type': 'spot',
'spot': true,
'margin': undefined,
'swap': false,
'future': false,
'option': false,
'active': undefined,
'contract': false,
'linear': undefined,
'inverse': undefined,
'taker': this.safeNumber (market, 'taker_fee_rate'),
'maker': this.safeNumber (market, 'maker_fee_rate'),
'contractSize': undefined,
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'precision': {
'amount': this.parseNumber (this.parsePrecision (this.safeString (market, 'trading_decimal'))),
'price': this.parseNumber (this.parsePrecision (this.safeString (market, 'pricing_decimal'))),
},
'limits': {
'leverage': {
'min': undefined,
'max': undefined,
},
'amount': {
'min': this.safeNumber (market, 'min_amount'),
'max': undefined,
},
'price': {
'min': undefined,
'max': undefined,
},
'cost': {
'min': undefined,
'max': undefined,
},
},
'info': market,
});
}
return result;
}
async fetchContractMarkets (params) {
const response = await this.perpetualPublicGetMarketList (params);
//
// {
// "code": 0,
// "data": [
// {
// "name": "BTCUSD",
// "type": 2, // 1: USDT-M Contracts, 2: Coin-M Contracts
// "leverages": ["3", "5", "8", "10", "15", "20", "30", "50", "100"],
// "stock": "BTC",
// "money": "USD",
// "fee_prec": 5,
// "stock_prec": 8,
// "money_prec": 1,
// "amount_prec": 0,
// "amount_min": "10",
// "multiplier": "1",
// "tick_size": "0.1", // Min. Price Increment
// "available": true
// },
// ],
// "message": "OK"
// }
//
const markets = this.safeValue (response, 'data', []);
const result = [];
for (let i = 0; i < markets.length; i++) {
const entry = markets[i];
const fees = this.fees;
const leverages = this.safeValue (entry, 'leverages', []);
const subType = this.safeInteger (entry, 'type');
const linear = (subType === 1) ? true : false;
const inverse = (subType === 2) ? true : false;
const id = this.safeString (entry, 'name');
const baseId = this.safeString (entry, 'stock');
const quoteId = this.safeString (entry, 'money');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const settleId = (subType === 1) ? 'USDT' : baseId;
const settle = this.safeCurrencyCode (settleId);
const symbol = base + '/' + quote + ':' + settle;
result.push ({
'id': id,
'symbol': symbol,
'base': base,
'quote': quote,
'settle': settle,
'baseId': baseId,
'quoteId': quoteId,
'settleId': settleId,
'type': 'swap',
'spot': false,
'margin': false,
'swap': true,
'future': false,
'option': false,
'active': this.safeValue (entry, 'available'),
'contract': true,
'linear': linear,
'inverse': inverse,
'taker': fees['trading']['taker'],
'maker': fees['trading']['maker'],
'contractSize': undefined,
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'precision': {
'amount': this.parseNumber (this.parsePrecision (this.safeString (entry, 'stock_prec'))),
'price': this.parseNumber (this.parsePrecision (this.safeString (entry, 'money_prec'))),
},
'limits': {
'leverage': {
'min': this.safeString (leverages, 0),
'max': this.safeString (leverages, leverages.length - 1),
},
'amount': {
'min': this.safeString (entry, 'amount_min'),
'max': undefined,
},
'price': {
'min': undefined,
'max': undefined,
},
'cost': {
'min': undefined,
'max': undefined,
},
},
'info': entry,
});
}
return result;
}
parseTicker (ticker, market = undefined) {
//
// Spot fetchTicker, fetchTickers
//
// {
// "vol": "293.19415130",
// "low": "38200.00",
// "open": "39514.99",
// "high": "39530.00",
// "last": "38649.57",
// "buy": "38640.20",
// "buy_amount": "0.22800000",
// "sell": "38640.21",
// "sell_amount": "0.02828439"
// }
//
// Swap fetchTicker, fetchTickers
//
// {
// "vol": "7714.2175",
// "low": "38200.00",
// "open": "39569.23",
// "high": "39569.23",
// "last": "38681.37",
// "buy": "38681.36",
// "period": 86400,
// "funding_time": 462,
// "position_amount": "296.7552",
// "funding_rate_last": "0.00009395",
// "funding_rate_next": "0.00000649",
// "funding_rate_predict": "-0.00007176",
// "insurance": "16464465.09431942163278132918",
// "sign_price": "38681.93",
// "index_price": "38681.69500000",
// "sell_total": "16.6039",
// "buy_total": "19.8481",
// "buy_amount": "4.6315",
// "sell": "38681.37",
// "sell_amount": "11.4044"
// }
//
const timestamp = this.safeInteger (ticker, 'date');
const symbol = this.safeSymbol (undefined, market);
ticker = this.safeValue (ticker, 'ticker', {});
const last = this.safeString (ticker, 'last');
return this.safeTicker ({
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'high': this.safeString (ticker, 'high'),
'low': this.safeString (ticker, 'low'),
'bid': this.safeString (ticker, 'buy'),
'bidVolume': undefined,
'ask': this.safeString (ticker, 'sell'),
'askVolume': undefined,
'vwap': undefined,
'open': this.safeString (ticker, 'open'),
'close': last,
'last': last,
'previousClose': undefined,
'change': undefined,
'percentage': undefined,
'average': undefined,
'baseVolume': this.safeString2 (ticker, 'vol', 'volume'),
'quoteVolume': undefined,
'info': ticker,
}, market);
}
async fetchTicker (symbol, params = {}) {
/**
* @method
* @name coinex#fetchTicker
* @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
* @param {str} symbol unified symbol of the market to fetch the ticker for
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {dict} a [ticker structure]{@link https://docs.ccxt.com/en/latest/manual.html#ticker-structure}
*/
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'market': market['id'],
};
const method = market['swap'] ? 'perpetualPublicGetMarketTicker' : 'publicGetMarketTicker';
const response = await this[method] (this.extend (request, params));
//
// Spot
//
// {
// "code": 0,
// "data": {
// "date": 1651306913414,
// "ticker": {
// "vol": "293.19415130",
// "low": "38200.00",
// "open": "39514.99",
// "high": "39530.00",
// "last": "38649.57",
// "buy": "38640.20",
// "buy_amount": "0.22800000",
// "sell": "38640.21",
// "sell_amount": "0.02828439"
// }
// },
// "message": "OK"
// }
//
// Swap
//
// {
// "code": 0,
// "data": {
// "date": 1651306641500,
// "ticker": {
// "vol": "7714.2175",
// "low": "38200.00",
// "open": "39569.23",
// "high": "39569.23",
// "last": "38681.37",
// "buy": "38681.36",
// "period": 86400,
// "funding_time": 462,
// "position_amount": "296.7552",
// "funding_rate_last": "0.00009395",
// "funding_rate_next": "0.00000649",
// "funding_rate_predict": "-0.00007176",
// "insurance": "16464465.09431942163278132918",
// "sign_price": "38681.93",
// "index_price": "38681.69500000",
// "sell_total": "16.6039",
// "buy_total": "19.8481",
// "buy_amount": "4.6315",
// "sell": "38681.37",
// "sell_amount": "11.4044"
// }
// },
// "message": "OK"
// }
//
return this.parseTicker (response['data'], market);
}
async fetchTickers (symbols = undefined, params = {}) {
/**
* @method
* @name coinex#fetchTickers
* @description fetches price tickers for multiple markets, statistical calculations with the information calculated over the past 24 hours each market
* @param {[str]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {dict} an array of [ticker structures]{@link https://docs.ccxt.com/en/latest/manual.html#ticker-structure}
*/
await this.loadMarkets ();
const [ marketType, query ] = this.handleMarketTypeAndParams ('fetchTickers', undefined, params);
const method = (marketType === 'swap') ? 'perpetualPublicGetMarketTickerAll' : 'publicGetMarketTickerAll';
const response = await this[method] (query);
//
// Spot
//
// {
// "code": 0,
// "data": {
// "date": 1651519857284,
// "ticker": {
// "PSPUSDT": {
// "vol": "127131.55227034",
// "low": "0.0669",
// "open": "0.0688",
// "high": "0.0747",
// "last": "0.0685",
// "buy": "0.0676",
// "buy_amount": "702.70117866",
// "sell": "0.0690",
// "sell_amount": "686.76861562"
// },
// }
// },
// "message": "Ok"
// }
//
// Swap
//
// {
// "code": 0,
// "data": {
// "date": 1651520268644,
// "ticker": {
// "KAVAUSDT": {
// "vol": "834924",
// "low": "3.9418",
// "open": "4.1834",
// "high": "4.4328",
// "last": "4.0516",
// "buy": "4.0443",
// "period": 86400,
// "funding_time": 262,
// "position_amount": "16111",
// "funding_rate_last": "-0.00069514",
// "funding_rate_next": "-0.00061009",
// "funding_rate_predict": "-0.00055812",
// "insurance": "16532425.53026084124483989548",
// "sign_price": "4.0516",
// "index_price": "4.0530",
// "sell_total": "59446",
// "buy_total": "62423",
// "buy_amount": "959",
// "sell": "4.0466",
// "sell_amount": "141"
// },
// }
// },
// "message": "Ok"
// }
//
const data = this.safeValue (response, 'data');
const timestamp = this.safeInteger (data, 'date');
const tickers = this.safeValue (data, 'ticker', {});
const marketIds = Object.keys (tickers);
const result = {};
for (let i = 0; i < marketIds.length; i++) {
const marketId = marketIds[i];
const market = this.safeMarket (marketId);
const symbol = market['symbol'];
const ticker = this.parseTicker ({
'date': timestamp,
'ticker': tickers[marketId],
}, market);
ticker['symbol'] = symbol;
result[symbol] = ticker;
}
return this.filterByArray (result, 'symbol', symbols);
}
async fetchTime (params = {}) {
/**
* @method
* @name coinex#fetchTime
* @description fetches the current integer timestamp in milliseconds from the exchange server
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {int} the current integer timestamp in milliseconds from the exchange server
*/
const response = await this.perpetualPublicGetTime (params);
//
// {
// code: '0',
// data: '1653261274414',
// message: 'OK'
// }
//
return this.safeNumber (response, 'data');
}
async fetchOrderBook (symbol, limit = 20, params = {}) {
/**
* @method
* @name coinex#fetchOrderBook
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @param {str} symbol unified symbol of the market to fetch the order book for
* @param {int|undefined} limit the maximum amount of order book entries to return
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {dict} A dictionary of [order book structures]{@link https://docs.ccxt.com/en/latest/manual.html#order-book-structure} indexed by market symbols
*/
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrderBook() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
if (limit === undefined) {
limit = 20; // default
}
const request = {
'market': this.marketId (symbol),
'merge': '0',
'limit': limit.toString (),
};
const method = market['swap'] ? 'perpetualPublicGetMarketDepth' : 'publicGetMarketDepth';
const response = await this[method] (this.extend (request, params));
//
// Spot
//
// {
// "code": 0,
// "data": {
// "asks": [
// ["41056.33", "0.31727613"],
// ["41056.34", "1.05657294"],
// ["41056.35", "0.02346648"]
// ],
// "bids": [
// ["41050.61", "0.40618608"],
// ["41046.98", "0.13800000"],
// ["41046.56", "0.22579234"]
// ],
// "last": "41050.61",
// "time": 1650573220346
// },
// "message": "OK"
// }
//
// Swap
//
// {
// "code": 0,
// "data": {
// "asks": [
// ["40620.90", "0.0384"],
// ["40625.50", "0.0219"],
// ["40625.90", "0.3506"]
// ],
// "bids": [
// ["40620.89", "19.6861"],
// ["40620.80", "0.0012"],
// ["40619.87", "0.0365"]
// ],
// "last": "40620.89",
// "time": 1650587672406,
// "sign_price": "40619.32",
// "index_price": "40609.93"
// },
// "message": "OK"
// }
//
const result = this.safeValue (response, 'data', {});
const timestamp = this.safeInteger (result, 'time');
return this.parseOrderBook (result, symbol, timestamp);
}
parseTrade (trade, market = undefined) {
//
// Spot and Swap fetchTrades (public)
//
// {
// "id": 2611511379,
// "type": "buy",
// "price": "192.63",
// "amount": "0.02266931",
// "date": 1638990110,
// "date_ms": 1638990110518
// },
//
// Spot and Margin fetchMyTrades (private)
//
// {
// "id": 2611520950,
// "order_id": 63286573298,
// "account_id": 0,
// "create_time": 1638990636,
// "type": "sell",
// "role": "taker",
// "price": "192.29",
// "amount": "0.098",
// "fee": "0.03768884",
// "fee_asset": "USDT",
// "market": "AAVEUSDT",
// "deal_money": "18.84442"
// }
//
// Swap fetchMyTrades (private)
//
// {
// "amount": "0.0012",
// "deal_fee": "0.0237528",
// "deal_insurance": "0",
// "deal_margin": "15.8352",
// "deal_order_id": 17797031903,
// "deal_profit": "0",
// "deal_stock": "47.5056",
// "deal_type": 1,
// "deal_user_id": 2969195,
// "fee_asset": "",
// "fee_discount": "0",
// "fee_price": "0",
// "fee_rate": "0.0005",
// "fee_real_rate": "0.0005",
// "id": 379044296,
// "leverage": "3",
// "margin_amount": "15.8352",
// "market": "BTCUSDT",
// "open_price": "39588",
// "order_id": 17797092987,
// "position_amount": "0.0012",
// "position_id": 62052321,
// "position_type": 1,
// "price": "39588",
// "role": 2,
// "side": 2,
// "time": 1650675936.016103,
// "user_id": 3620173
// }
//
let timestamp = this.safeTimestamp2 (trade, 'create_time', 'time');
if (timestamp === undefined) {
timestamp = this.safeInteger (trade, 'date_ms');
}
const tradeId = this.safeString (trade, 'id');
const orderId = this.safeString (trade, 'order_id');
const priceString = this.safeString (trade, 'price');
const amountString = this.safeString (trade, 'amount');
const marketId = this.safeString (trade, 'market');
market = this.safeMarket (marketId, market);
const symbol = this.safeSymbol (marketId, market);
const costString = this.safeString (trade, 'deal_money');
let fee = undefined;
const feeCostString = this.safeString2 (trade, 'fee', 'deal_fee');
if (feeCostString !== undefined) {
const feeCurrencyId = this.safeString (trade, 'fee_asset');
const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId);
fee = {
'cost': feeCostString,
'currency': feeCurrencyCode,
};
}
let takerOrMaker = this.safeString (trade, 'role');
if (takerOrMaker === '1') {
takerOrMaker = 'maker';
} else if (takerOrMaker === '2') {
takerOrMaker = 'taker';
}
let side = undefined;
if (market['type'] === 'swap') {
side = this.safeInteger (trade, 'side');
if (side === 1) {
side = 'sell';
} else if (side === 2) {
side = 'buy';
}
if (side === undefined) {
side = this.safeString (trade, 'type');
}
} else {
side = this.safeString (trade, 'type');
}
return this.safeTrade ({
'info': trade,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'symbol': symbol,
'id': tradeId,
'order': orderId,
'type': undefined,
'side': side,
'takerOrMaker': takerOrMaker,
'price': priceString,
'amount': amountString,
'cost': costString,
'fee': fee,
}, market);
}
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name coinex#fetchTrades
* @description get the list of most recent trades for a particular symbol
* @param {str} symbol unified symbol of the market to fetch trades for
* @param {int|undefined} since timestamp in ms of the earliest trade to fetch
* @param {int|undefined} limit the maximum amount of trades to fetch
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {[dict]} a list of [trade structures]{@link https://docs.ccxt.com/en/latest/manual.html?#public-trades}
*/
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'market': market['id'],
// 'last_id': 0,
};
if (limit !== undefined) {
request['limit'] = limit;
}
const method = market['swap'] ? 'perpetualPublicGetMarketDeals' : 'publicGetMarketDeals';
const response = await this[method] (this.extend (request, params));
//
// Spot and Swap
//
// {
// "code": 0,
// "data": [
// {
// "id": 2611511379,
// "type": "buy",
// "price": "192.63",
// "amount": "0.02266931",
// "date": 1638990110,
// "date_ms": 1638990110518
// },
// ],
// "message": "OK"
// }
//
return this.parseTrades (response['data'], market, since, limit);
}
async fetchTradingFee (symbol, params = {}) {
/**
* @method
* @name coinex#fetchTradingFee
* @description fetch the trading fees for a market
* @param {str} symbol unified market symbol
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {dict} a [fee structure]{@link https://docs.ccxt.com/en/latest/manual.html#fee-structure}
*/
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'market': market['id'],
};
const response = await this.publicGetMarketDetail (this.extend (request, params));
//
// {
// "code": 0,
// "data": {
// "name": "BTCUSDC",
// "min_amount": "0.0005",
// "maker_fee_rate": "0.002",
// "taker_fee_rate": "0.002",
// "pricing_name": "USDC",
// "pricing_decimal": 2,
// "trading_name": "BTC",
// "trading_decimal": 8
// },
// "message": "OK"
// }
//
const data = this.safeValue (response, 'data', {});
return this.parseTradingFee (data);
}
async fetchTradingFees (params = {}) {
/**
* @method
* @name coinex#fetchTradingFees
* @description fetch the trading fees for multiple markets
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {dict} a dictionary of [fee structures]{@link https://docs.ccxt.com/en/latest/manual.html#fee-structure} indexed by market symbols
*/
await this.loadMarkets ();
const response = await this.publicGetMarketInfo (params);
//
// {
// "code": 0,
// "data": {
// "WAVESBTC": {
// "name": "WAVESBTC",
// "min_amount": "1",
// "maker_fee_rate": "0.001",
// "taker_fee_rate": "0.001",
// "pricing_name": "BTC",
// "pricing_decimal": 8,
// "trading_name": "WAVES",
// "trading_decimal": 8
// }
// ...
// }
// }
//
const data = this.safeValue (response, 'data', {});
const result = {};
for (let i = 0; i < this.symbols.length; i++) {
const symbol = this.symbols[i];
const market = this.market (symbol);
const fee = this.safeValue (data, market['id'], {});
result[symbol] = this.parseTradingFee (fee, market);
}
return result;
}
parseTradingFee (fee, market = undefined) {
const marketId = this.safeValue (fee, 'name');
const symbol = this.safeSymbol (marketId, market);
return {
'info': fee,
'symbol': symbol,
'maker': this.safeNumber (fee, 'maker_fee_rate'),
'taker': this.safeNumber (fee, 'taker_fee_rate'),
'percentage': true,
'tierBased': true,
};
}
parseOHLCV (ohlcv, market = undefined) {
//
// [
// 1591484400,
// "0.02505349",
// "0.02506988",
// "0.02507000",
// "0.02505304",
// "343.19716223",
// "8.6021323866383196",
// "ETHBTC"
// ]
//
return [
this.safeTimestamp (ohlcv, 0),
this.safeNumber (ohlcv, 1),
this.safeNumber (ohlcv, 3),
this.safeNumber (ohlcv, 4),
this.safeNumber (ohlcv, 2),
this.safeNumber (ohlcv, 5),
];
}
async fetchOHLCV (symbol, timeframe = '5m', since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name coinex#fetchOHLCV
* @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @param {str} symbol unified symbol of the market to fetch OHLCV data for
* @param {str} timeframe the length of time each candle represents
* @param {int|undefined} since timestamp in ms of the earliest candle to fetch
* @param {int|undefined} limit the maximum amount of candles to fetch
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {[[int]]} A list of candles ordered as timestamp, open, high, low, close, volume
*/
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'market': market['id'],
'type': this.timeframes[timeframe],
};
if (limit !== undefined) {
request['limit'] = limit;
}
const method = market['swap'] ? 'perpetualPublicGetMarketKline' : 'publicGetMarketKline';
const response = await this[method] (this.extend (request, params));
//
// Spot
//
// {
// "code": 0,
// "data": [
// [1591484400, "0.02505349", "0.02506988", "0.02507000", "0.02505304", "343.19716223", "8.6021323866383196", "ETHBTC"],
// [1591484700, "0.02506990", "0.02508109", "0.02508109", "0.02506979", "91.59841581", "2.2972047780447000", "ETHBTC"],
// [1591485000, "0.02508106", "0.02507996", "0.02508106", "0.02507500", "65.15307697", "1.6340597822306000", "ETHBTC"],
// ],
// "message": "OK"
// }
//
// Swap
//
// {
// "code": 0,
// "data": [
// [1650569400, "41524.64", "41489.31", "41564.61", "41480.58", "29.7060", "1233907.099562"],
// [1650569700, "41489.31", "41438.29", "41489.31", "41391.87", "42.4115", "1756154.189061"],
// [1650570000, "41438.29", "41482.21", "41485.05", "41427.31", "22.2892", "924000.317861"]
// ],
// "message": "OK"
// }
//
const data = this.safeValue (response, 'data', []);
return this.parseOHLCVs (data, market, timeframe, since, limit);
}
async fetchMarginBalance (params = {}) {
await this.loadMarkets ();
const symbol = this.safeString (params, 'symbol');
let marketId = this.safeString (params, 'market');
let market = undefined;
if (symbol !== undefined) {
market = this.market (symbol);
marketId = market['id'];
} else if (marketId === undefined) {
throw new ArgumentsRequired (this.id + ' fetchMarginBalance() fetching a margin account requires a market parameter or a symbol parameter');
}
params = this.omit (params, [ 'symbol', 'market' ]);
const request = {
'market': marketId,
};
const response = await this.privateGetMarginAccount (this.extend (request, params));
//
// {
// "code": 0,
// "data": {
// "account_id": 126,
// "leverage": 3,
// "market_type": "AAVEUSDT",
// "sell_asset_type": "AAVE",
// "buy_asset_type": "USDT",
// "balance": {
// "sell_type": "0.3", // borrowed
// "buy_type": "30"
// },
// "frozen": {
// "sell_type": "0",
// "buy_type": "0"
// },
// "loan": {
// "sell_type": "0.3", // loan
// "buy_type": "0"
// },
// "interest": {
// "sell_type": "0.0000125",
// "buy_type": "0"
// },
// "can_transfer": {
// "sell_type": "0.02500646",
// "buy_type": "4.28635738"
// },
// "warn_rate": "",
// "liquidation_price": ""
// },
// "message": "Success"
// }
//
const result = { 'info': response };
const data = this.safeValue (response, 'data', {});
const free = this.safeValue (data, 'can_transfer', {});
const total = this.safeValue (data, 'balance', {});
//
const sellAccount = this.account ();
const sellCurrencyId = this.safeString (data, 'sell_asset_type');
const sellCurrencyCode = this.safeCurrencyCode (sellCurrencyId);
sellAccount['free'] = this.safeString (free, 'sell_type');
sellAccount['total'] = this.safeString (total, 'sell_type');
result[sellCurrencyCode] = sellAccount;
//
const buyAccount = this.account ();
const buyCurrencyId = this.safeString (data, 'buy_asset_type');
const buyCurrencyCode = this.safeCurrencyCode (buyCurrencyId);
buyAccount['free'] = this.safeString (free, 'buy_type');
buyAccount['total'] = this.safeString (total, 'buy_type');
result[buyCurrencyCode] = buyAccount;
//
return this.safeBalance (result);
}
async fetchSpotBalance (params = {}) {
await this.loadMarkets ();
const response = await this.privateGetBalanceInfo (params);
//
// {
// "code": 0,
// "data": {
// "BCH": { # BCH account
// "available": "13.60109", # Available BCH
// "frozen": "0.00000" # Frozen BCH
// },
// "BTC": { # BTC account
// "available": "32590.16", # Available BTC
// "frozen": "7000.00" # Frozen BTC
// },
// "ETH": { # ETH account
// "available": "5.06000", # Available ETH
// "frozen": "0.00000" # Frozen ETH
// }
// },
// "message": "Ok"
// }
//
const result = { 'info': response };
const balances = this.safeValue (response, 'data', {});
const currencyIds = Object.keys (balances);
for (let i = 0; i < currencyIds.length; i++) {
const currencyId = currencyIds[i];
const code = this.safeCurrencyCode (currencyId);
const balance = this.safeValue (balances, currencyId, {});
const account = this.account ();
account['free'] = this.safeString (balance, 'available');
account['used'] = this.safeString (balance, 'frozen');
result[code] = account;
}
return this.safeBalance (result);
}
async fetchSwapBalance (params = {}) {
await this.loadMarkets ();
const response = await this.perpetualPrivateGetAssetQuery (params);
//
// {
// "code": 0,
// "data": {
// "USDT": {
// "available": "37.24817690383456000000",
// "balance_total": "37.24817690383456000000",
// "frozen": "0.00000000000000000000",
// "margin": "0.00000000000000000000",
// "profit_unreal": "0.00000000000000000000",
// "transfer": "37.24817690383456000000"
// }
// },
// "message": "OK"
// }
//
const result = { 'info': response };
const balances = this.safeValue (response, 'data', {});
const currencyIds = Object.keys (balances);
for (let i = 0; i < currencyIds.length; i++) {
const currencyId = currencyIds[i];
const code = this.safeCurrencyCode (currencyId);
const balance = this.safeValue (balances, currencyId, {});
const account = this.account ();
account['free'] = this.safeString (balance, 'available');
account['used'] = this.safeString (balance, 'frozen');
account['total'] = this.safeString (balance, 'balance_total');
result[code] = account;
}
return this.safeBalance (result);
}
async fetchBalance (params = {}) {
/**
* @method
* @name coinex#fetchBalance
* @description query for balance and get the amount of funds available for trading or funds locked in orders
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {dict} a [balance structure]{@link https://docs.ccxt.com/en/latest/manual.html?#balance-structure}
*/
const accountType = this.safeString (params, 'type', 'main');
params = this.omit (params, 'type');
if (accountType === 'margin') {
return await this.fetchMarginBalance (params);
} else if (accountType === 'swap') {
return await this.fetchSwapBalance (params);
} else {
return await this.fetchSpotBalance (params);
}
}
parseOrderStatus (status) {
const statuses = {
'not_deal': 'open',
'part_deal': 'open',
'done': 'closed',
'cancel': 'canceled',
};
return this.safeString (statuses, status, status);
}
parseOrder (order, market = undefined) {
//
// fetchOrder
//
// {
// "amount": "0.1",
// "asset_fee": "0.22736197736197736197",
// "avg_price": "196.85000000000000000000",
// "create_time": 1537270135,
// "deal_amount": "0.1",
// "deal_fee": "0",
// "deal_money": "19.685",
// "fee_asset": "CET",
// "fee_discount": "0.5",
// "id": 1788259447,
// "left": "0",
// "maker_fee_rate": "0",
// "market": "ETHUSDT",
// "order_type": "limit",
// "price": "170.00000000",
// "status": "done",
// "taker_fee_rate": "0.0005",
// "type": "sell",
// }
//
// Spot and Margin createOrder, cancelOrder, fetchOrder
//
// {
// "amount":"1.5",
// "asset_fee":"0",
// "avg_price":"0.14208538",
// "client_id":"",
// "create_time":1650993819,
// "deal_amount":"10.55703267",
// "deal_fee":"0.0029999999971787292",
// "deal_money":"1.4999999985893646",
// "fee_asset":null,
// "fee_discount":"1",
// "finished_time":null,
// "id":74556296907,
// "left":"0.0000000014106354",
// "maker_fee_rate":"0",
// "market":"DOGEUSDT",
// "money_fee":"0.0029999999971787292",
// "order_type":"market",
// "price":"0",
// "status":"done",
// "stock_fee":"0",
// "taker_fee_rate":"0.002",
// "type":"buy"
// }
//
// Swap createOrder, cancelOrder, fetchOrder
//
// {
// "amount": "0.0005",
// "client_id": "",
// "create_time": 1651004578.618224,
// "deal_asset_fee": "0.00000000000000000000",
// "deal_fee": "0.00000000000000000000",
// "deal_profit": "0.00000000000000000000",
// "deal_stock": "0.00000000000000000000",
// "effect_type": 1,
// "fee_asset": "",
// "fee_discount": "0.00000000000000000000",
// "last_deal_amount": "0.00000000000000000000",
// "last_deal_id": 0,
// "last_deal_price": "0.00000000000000000000",
// "last_deal_role": 0,
// "last_deal_time": 0,
// "last_deal_type": 0,
// "left": "0.0005",
// "leverage": "3",
// "maker_fee": "0.00030",
// "market": "BTCUSDT",
// "order_id": 18221659097,
// "position_id": 0,
// "position_type": 1,
// "price": "30000.00",
// "side": 2,
// "source": "api.v1",
// "stop_id": 0,
// "taker_fee": "0.00050",
// "target": 0,
// "type": 1,
// "update_time": 1651004578.618224,
// "user_id": 3620173
// }
//
// Stop order createOrder
//
// {"status":"success"}
//
// Swap Stop cancelOrder, fetchOrder
//
// {
// "amount": "0.0005",
// "client_id": "",
// "create_time": 1651034023.008771,
// "effect_type": 1,
// "fee_asset": "",
// "fee_discount": "0.00000000000000000000",
// "maker_fee": "0.00030",
// "market": "BTCUSDT",
// "order_id": 18256915101,
// "price": "31000.00",
// "side": 2,
// "source": "api.v1",
// "state": 1,
// "stop_price": "31500.00",
// "stop_type": 1,
// "taker_fee": "0.00050",
// "target": 0,
// "type": 1,
// "update_time": 1651034397.193624,
// "user_id": 3620173
// }
//
//
// Spot and Margin fetchOpenOrders, fetchClosedOrders
//
// {
// "account_id": 0,
// "amount": "0.0005",
// "asset_fee": "0",
// "avg_price": "0.00",
// "client_id": "",
// "create_time": 1651089247,
// "deal_amount": "0",
// "deal_fee": "0",
// "deal_money": "0",
// "fee_asset": null,
// "fee_discount": "1",
// "finished_time": 0,
// "id": 74660190839,
// "left": "0.0005",
// "maker_fee_rate": "0.002",
// "market": "BTCUSDT",
// "money_fee": "0",
// "order_type": "limit",
// "price": "31000",
// "status": "not_deal",
// "stock_fee": "0",
// "taker_fee_rate": "0.002",
// "type": "buy"
// }
//
// Swap fetchOpenOrders, fetchClosedOrders
//
// {
// "amount": "0.0005",
// "client_id": "",
// "create_time": 1651030414.088431,
// "deal_asset_fee": "0",
// "deal_fee": "0.00960069",
// "deal_profit": "0.009825",
// "deal_stock": "19.20138",
// "effect_type": 0,
// "fee_asset": "",
// "fee_discount": "0",
// "left": "0",
// "leverage": "3",
// "maker_fee": "0",
// "market": "BTCUSDT",
// "order_id": 18253447431,
// "position_id": 0,
// "position_type": 1,
// "price": "0",
// "side": 1,
// "source": "web",
// "stop_id": 0,
// "taker_fee": "0.0005",
// "target": 0,
// "type": 2,
// "update_time": 1651030414.08847,
// "user_id": 3620173
// }
//
// Spot and Margin Stop fetchOpenOrders, fetchClosedOrders
//
// {
// "account_id": 0,
// "amount": "155",
// "client_id": "",
// "create_time": 1651089182,
// "fee_asset": null,
// "fee_discount": "1",
// "maker_fee": "0.002",
// "market": "BTCUSDT",
// "order_id": 74660111965,
// "order_type": "market",
// "price": "0",
// "state": 0,
// "stop_price": "31500",
// "taker_fee": "0.002",
// "type": "buy"
// }
//
// Swap Stop fetchOpenOrders
//
// {
// "amount": "0.0005",
// "client_id": "",
// "create_time": 1651089147.321691,
// "effect_type": 1,
// "fee_asset": "",
// "fee_discount": "0.00000000000000000000",
// "maker_fee": "0.00030",
// "market": "BTCUSDT",
// "order_id": 18332143848,
// "price": "31000.00",
// "side": 2,
// "source": "api.v1",
// "state": 1,
// "stop_price": "31500.00",
// "stop_type": 1,
// "taker_fee": "0.00050",
// "target": 0,
// "type": 1,
// "update_time": 1651089147.321691,
// "user_id": 3620173
// }
//
const timestamp = this.safeTimestamp (order, 'create_time');
const priceString = this.safeString (order, 'price');
const costString = this.safeString (order, 'deal_money');
const amountString = this.safeString (order, 'amount');
const filledString = this.safeString (order, 'deal_amount');
const averageString = this.safeString (order, 'avg_price');
const remainingString = this.safeString (order, 'left');
const marketId = this.safeString (order, 'market');
market = this.safeMarket (marketId, market);
const feeCurrencyId = this.safeString (order, 'fee_asset');
let feeCurrency = this.safeCurrencyCode (feeCurrencyId);
if (feeCurrency === undefined) {
feeCurrency = market['quote'];
}
const status = this.parseOrderStatus (this.safeString (order, 'status'));
let side = this.safeInteger (order, 'side');
if (side === 1) {
side = 'sell';
} else if (side === 2) {
side = 'buy';
} else {
side = this.safeString (order, 'type');
}
let type = this.safeString (order, 'order_type');
if (type === undefined) {
type = this.safeInteger (order, 'type');
if (type === 1) {
type = 'limit';
} else if (type === 2) {
type = 'market';
}
}
return this.safeOrder ({
'id': this.safeString2 (order, 'id', 'order_id'),
'clientOrderId': undefined,
'datetime': this.iso8601 (timestamp),
'timestamp': timestamp,
'lastTradeTimestamp': this.safeTimestamp (order, 'update_time'),
'status': status,
'symbol': market['symbol'],
'type': type,
'timeInForce': undefined,
'postOnly': undefined,
'reduceOnly': undefined,
'side': side,
'price': priceString,
'stopPrice': this.safeString (order, 'stop_price'),
'cost': costString,
'average': averageString,
'amount': amountString,
'filled': filledString,
'remaining': remainingString,
'trades': undefined,
'fee': {
'currency': feeCurrency,
'cost': this.safeString (order, 'deal_fee'),
},
'info': order,
}, market);
}
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
/**
* @method
* @name coinex#createOrder
* @description create a trade order
* @param {str} symbol unified symbol of the market to create an order in
* @param {str} type 'market' or 'limit'
* @param {str} side 'buy' or 'sell'
* @param {float} amount how much of currency you want to trade in units of base currency
* @param {float|undefined} price the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
* @param {dict} params extra parameters specific to the coinex api endpoint
* @param {float} triggerPrice price at which to triger stop orders
* @param {float} stopPrice price at which to triger stop orders
* @param {float} stopLossPrice price at which to trigger stop-loss orders
* @param {float} takeProfitPrice price at which to trigger take-profit orders
* @param {str} params.timeInForce "GTC", "IOC", "FOK", "PO"
* @param {bool} params.postOnly
* @param {bool} params.reduceOnly
* @returns {dict} an [order structure]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure}
*/
await this.loadMarkets ();
const market = this.market (symbol);
const swap = market['swap'];
const stopPrice = this.safeString2 (params, 'stopPrice', 'triggerPrice');
const stopLossPrice = this.safeString (params, 'stopLossPrice');
const takeProfitPrice = this.safeString (params, 'takeProfitPrice');
const option = this.safeString (params, 'option');
const isMarketOrder = type === 'market';
const postOnly = this.isPostOnly (isMarketOrder, option === 'MAKER_ONLY', params);
const positionId = this.safeInteger2 (params, 'position_id', 'positionId'); // Required for closing swap positions
let timeInForce = this.safeString (params, 'timeInForce'); // Spot: IOC, FOK, PO, GTC, ... NORMAL (default), MAKER_ONLY
const reduceOnly = this.safeValue (params, 'reduceOnly');
if (reduceOnly !== undefined) {
if (market['type'] !== 'swap') {
throw new InvalidOrder (this.id + ' createOrder() does not support reduceOnly for ' + market['type'] + ' orders, reduceOnly orders are supported for swap markets only');
}
}
let method = undefined;
const request = {
'market': market['id'],
};
if (swap) {
if (stopLossPrice || takeProfitPrice) {
const stopType = this.safeInteger (params, 'stop_type'); // 1: triggered by the latest transaction, 2: mark price, 3: index price
if (stopType === undefined) {
request['stop_type'] = 1;
}
if (positionId === undefined) {
throw new ArgumentsRequired (this.id + ' createOrder() requires a position_id parameter for stop loss and take profit orders');
}
request['position_id'] = positionId;
if (stopLossPrice) {
method = 'perpetualPrivatePostPositionStopLoss';
request['stop_loss_price'] = stopLossPrice;
} else if (takeProfitPrice) {
method = 'perpetualPrivatePostPositionTakeProfit';
request['take_profit_price'] = takeProfitPrice;
}
} else {
method = 'perpetualPrivatePostOrderPut' + this.capitalize (type);
side = (side === 'buy') ? 2 : 1;
if (stopPrice !== undefined) {
const stopType = this.safeInteger (params, 'stop_type'); // 1: triggered by the latest transaction, 2: mark price, 3: index price
if (stopType === undefined) {
request['stop_type'] = 1;
}
request['stop_price'] = this.priceToPrecision (symbol, stopPrice);
request['stop_type'] = this.priceToPrecision (symbol, stopType);
request['amount'] = this.amountToPrecision (symbol, amount);
request['side'] = side;
if (type === 'limit') {
method = 'perpetualPrivatePostOrderPutStopLimit';
request['price'] = this.priceToPrecision (symbol, price);
} else if (type === 'market') {
method = 'perpetualPrivatePostOrderPutStopMarket';
}
request['amount'] = this.amountToPrecision (symbol, amount);
}
if ((type !== 'market') || (stopPrice !== undefined)) {
if (postOnly) {
request['option'] = 1;
} else if (timeInForce !== undefined) {
if (timeInForce === 'IOC') {
timeInForce = 2;
} else if (timeInForce === 'FOK') {
timeInForce = 3;
} else {
timeInForce = 1;
}
request['effect_type'] = timeInForce; // exchange takes 'IOC' and 'FOK'
}
}
if (type === 'limit' && stopPrice === undefined) {
if (reduceOnly) {
method = 'perpetualPrivatePostOrderCloseLimit';
request['position_id'] = positionId;
} else {
request['side'] = side;
}
request['price'] = this.priceToPrecision (symbol, price);
request['amount'] = this.amountToPrecision (symbol, amount);
} else if (type === 'market' && stopPrice === undefined) {
if (reduceOnly) {
method = 'perpetualPrivatePostOrderCloseMarket';
request['position_id'] = positionId;
} else {
request['side'] = side;
request['amount'] = this.amountToPrecision (symbol, amount);
}
}
}
} else {
method = 'privatePostOrder' + this.capitalize (type);
request['type'] = side;
if ((type === 'market') && (side === 'buy')) {
if (this.options['createMarketBuyOrderRequiresPrice']) {
if (price === undefined) {
throw new InvalidOrder (this.id + " createOrder() requires the price argument with market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false to supply the cost in the amount argument (the exchange-specific behaviour)");
} else {
const amountString = this.amountToPrecision (symbol, amount);
const priceString = this.priceToPrecision (symbol, price);
const costString = Precise.stringMul (amountString, priceString);
const costNumber = this.parseNumber (costString);
request['amount'] = this.costToPrecision (symbol, costNumber);
}
} else {
request['amount'] = this.costToPrecision (symbol, amount);
}
} else {
request['amount'] = this.amountToPrecision (symbol, amount);
}
if ((type === 'limit') || (type === 'ioc')) {
request['price'] = this.priceToPrecision (symbol, price);
}
if (stopPrice !== undefined) {
request['stop_price'] = this.priceToPrecision (symbol, stopPrice);
if (type === 'limit') {
method = 'privatePostOrderStopLimit';
} else if (type === 'market') {
method = 'privatePostOrderStopMarket';
}
}
if ((type !== 'market') || (stopPrice !== undefined)) {
// following options cannot be applied to vanilla market orders (but can be applied to stop-market orders)
if ((timeInForce !== undefined) || postOnly) {
if ((postOnly || (timeInForce !== 'IOC')) && ((type === 'limit') && (stopPrice !== undefined))) {
throw new InvalidOrder (this.id + ' createOrder() only supports the IOC option for stop-limit orders');
}
if (postOnly) {
request['option'] = 'MAKER_ONLY';
} else {
if (timeInForce !== undefined) {
request['option'] = timeInForce; // exchange takes 'IOC' and 'FOK'
}
}
}
}
}
const accountId = this.safeInteger (params, 'account_id');
const defaultType = this.safeString (this.options, 'defaultType');
if (defaultType === 'margin') {
if (accountId === undefined) {
throw new BadRequest (this.id + ' createOrder() requires an account_id parameter for margin orders');
}
request['account_id'] = accountId;
}
params = this.omit (params, [ 'reduceOnly', 'positionId', 'timeInForce', 'postOnly', 'stopPrice', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice' ]);
const response = await this[method] (this.extend (request, params));
//
// Spot and Margin
//
// {
// "code": 0,
// "data": {
// "amount": "0.0005",
// "asset_fee": "0",
// "avg_price": "0.00",
// "client_id": "",
// "create_time": 1650951627,
// "deal_amount": "0",
// "deal_fee": "0",
// "deal_money": "0",
// "fee_asset": null,
// "fee_discount": "1",
// "finished_time": null,
// "id": 74510932594,
// "left": "0.0005",
// "maker_fee_rate": "0.002",
// "market": "BTCUSDT",
// "money_fee": "0",
// "order_type": "limit",
// "price": "30000",
// "status": "not_deal",
// "stock_fee": "0",
// "taker_fee_rate": "0.002",
// "type": "buy"
// },
// "message": "Success"
// }
//
// Swap
//
// {
// "code": 0,
// "data": {
// "amount": "0.0005",
// "client_id": "",
// "create_time": 1651004578.618224,
// "deal_asset_fee": "0.00000000000000000000",
// "deal_fee": "0.00000000000000000000",
// "deal_profit": "0.00000000000000000000",
// "deal_stock": "0.00000000000000000000",
// "effect_type": 1,
// "fee_asset": "",
// "fee_discount": "0.00000000000000000000",
// "last_deal_amount": "0.00000000000000000000",
// "last_deal_id": 0,
// "last_deal_price": "0.00000000000000000000",
// "last_deal_role": 0,
// "last_deal_time": 0,
// "last_deal_type": 0,
// "left": "0.0005",
// "leverage": "3",
// "maker_fee": "0.00030",
// "market": "BTCUSDT",
// "order_id": 18221659097,
// "position_id": 0,
// "position_type": 1,
// "price": "30000.00",
// "side": 2,
// "source": "api.v1",
// "stop_id": 0,
// "taker_fee": "0.00050",
// "target": 0,
// "type": 1,
// "update_time": 1651004578.618224,
// "user_id": 3620173
// },
// "message": "OK"
// }
//
// Stop Order
//
// {"code":0,"data":{"status":"success"},"message":"OK"}
//
const data = this.safeValue (response, 'data');
return this.parseOrder (data, market);
}
async cancelOrder (id, symbol = undefined, params = {}) {
/**
* @method
* @name coinex#cancelOrder
* @description cancels an open order
* @param {str} id order id
* @param {str|undefined} symbol unified symbol of the market the order was made in
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {dict} An [order structure]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure}
*/
await this.loadMarkets ();
const market = this.market (symbol);
const stop = this.safeValue (params, 'stop');
const swap = market['swap'];
const request = {
'market': market['id'],
};
const idRequest = swap ? 'order_id' : 'id';
request[idRequest] = id;
let method = swap ? 'perpetualPrivatePostOrderCancel' : 'privateDeleteOrderPending';
if (stop) {
if (swap) {
method = 'perpetualPrivatePostOrderCancelStop';
} else {
method = 'privateDeleteOrderStopPendingId';
}
}
const accountId = this.safeInteger (params, 'account_id');
const defaultType = this.safeString (this.options, 'defaultType');
if (defaultType === 'margin') {
if (accountId === undefined) {
throw new BadRequest (this.id + ' cancelOrder() requires an account_id parameter for margin orders');
}
request['account_id'] = accountId;
}
const query = this.omit (params, [ 'stop', 'account_id' ]);
const response = await this[method] (this.extend (request, query));
//
// Spot and Margin
//
// {
// "code": 0,
// "data": {
// "amount": "0.0005",
// "asset_fee": "0",
// "avg_price": "0.00",
// "client_id": "",
// "create_time": 1650951627,
// "deal_amount": "0",
// "deal_fee": "0",
// "deal_money": "0",
// "fee_asset": null,
// "fee_discount": "1",
// "finished_time": null,
// "id": 74510932594,
// "left": "0.0005",
// "maker_fee_rate": "0.002",
// "market": "BTCUSDT",
// "money_fee": "0",
// "order_type": "limit",
// "price": "30000",
// "status": "not_deal",
// "stock_fee": "0",
// "taker_fee_rate": "0.002",
// "type": "buy"
// },
// "message": "Success"
// }
//
// Swap
//
// {
// "code": 0,
// "data": {
// "amount": "0.0005",
// "client_id": "",
// "create_time": 1651004578.618224,
// "deal_asset_fee": "0.00000000000000000000",
// "deal_fee": "0.00000000000000000000",
// "deal_profit": "0.00000000000000000000",
// "deal_stock": "0.00000000000000000000",
// "effect_type": 1,
// "fee_asset": "",
// "fee_discount": "0.00000000000000000000",
// "last_deal_amount": "0.00000000000000000000",
// "last_deal_id": 0,
// "last_deal_price": "0.00000000000000000000",
// "last_deal_role": 0,
// "last_deal_time": 0,
// "last_deal_type": 0,
// "left": "0.0005",
// "leverage": "3",
// "maker_fee": "0.00030",
// "market": "BTCUSDT",
// "order_id": 18221659097,
// "position_id": 0,
// "position_type": 1,
// "price": "30000.00",
// "side": 2,
// "source": "api.v1",
// "stop_id": 0,
// "taker_fee": "0.00050",
// "target": 0,
// "type": 1,
// "update_time": 1651004578.618224,
// "user_id": 3620173
// },
// "message": "OK"
// }
//
// Swap Stop
//
// {
// "code": 0,
// "data": {
// "amount": "0.0005",
// "client_id": "",
// "create_time": 1651034023.008771,
// "effect_type": 1,
// "fee_asset": "",
// "fee_discount": "0.00000000000000000000",
// "maker_fee": "0.00030",
// "market": "BTCUSDT",
// "order_id": 18256915101,
// "price": "31000.00",
// "side": 2,
// "source": "api.v1",
// "state": 1,
// "stop_price": "31500.00",
// "stop_type": 1,
// "taker_fee": "0.00050",
// "target": 0,
// "type": 1,
// "update_time": 1651034397.193624,
// "user_id": 3620173
// },
// "message":"OK"
// }
//
// Spot and Margin Stop
//
// {"code":0,"data":{},"message":"Success"}
//
const data = this.safeValue (response, 'data');
return this.parseOrder (data, market);
}
async cancelAllOrders (symbol = undefined, params = {}) {
/**
* @method
* @name coinex#cancelAllOrders
* @description cancel all open orders in a market
* @param {str} symbol unified market symbol of the market to cancel orders in
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {[dict]} a list of [order structures]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure}
*/
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' cancellAllOrders() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
const marketId = market['id'];
const accountId = this.safeInteger (params, 'account_id', 0);
const request = {
'market': marketId,
// 'account_id': accountId, // SPOT, main account ID: 0, margin account ID: See < Inquire Margin Account Market Info >, future account ID: See < Inquire Future Account Market Info >
// 'side': 0, // SWAP, 0: All, 1: Sell, 2: Buy
};
const swap = market['swap'];
const stop = this.safeValue (params, 'stop');
let method = undefined;
if (swap) {
method = 'perpetualPrivatePostOrderCancelAll';
if (stop) {
method = 'perpetualPrivatePostOrderCancelStopAll';
}
} else {
method = 'privateDeleteOrderPending';
if (stop) {
method = 'privateDeleteOrderStopPending';
}
request['account_id'] = accountId;
}
params = this.omit (params, [ 'stop', 'account_id' ]);
const response = await this[method] (this.extend (request, params));
//
// Spot and Margin
//
// {"code": 0, "data": null, "message": "Success"}
//
// Swap
//
// {"code": 0, "data": {"status":"success"}, "message": "OK"}
//
return response;
}
async fetchOrder (id, symbol = undefined, params = {}) {
/**
* @method
* @name coinex#fetchOrder
* @description fetches information on an order made by the user
* @param {str} symbol unified symbol of the market the order was made in
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {dict} An [order structure]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure}
*/
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrder() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
const swap = market['swap'];
const stop = this.safeValue (params, 'stop');
const request = {
'market': market['id'],
// 'id': id, // SPOT
// 'order_id': id, // SWAP
};
const idRequest = swap ? 'order_id' : 'id';
request[idRequest] = id;
let method = undefined;
if (swap) {
method = stop ? 'perpetualPrivateGetOrderStopStatus' : 'perpetualPrivateGetOrderStatus';
} else {
method = 'privateGetOrderStatus';
}
params = this.omit (params, 'stop');
const response = await this[method] (this.extend (request, params));
//
// Spot
//
// {
// "code": 0,
// "data": {
// "amount": "0.1",
// "asset_fee": "0.22736197736197736197",
// "avg_price": "196.85000000000000000000",
// "create_time": 1537270135,
// "deal_amount": "0.1",
// "deal_fee": "0",
// "deal_money": "19.685",
// "fee_asset": "CET",
// "fee_discount": "0.5",
// "id": 1788259447,
// "left": "0",
// "maker_fee_rate": "0",
// "market": "ETHUSDT",
// "order_type": "limit",
// "price": "170.00000000",
// "status": "done",
// "taker_fee_rate": "0.0005",
// "type": "sell",
// },
// "message": "Ok"
// }
//
// Swap
//
// {
// "code": 0,
// "data": {
// "amount": "0.0005",
// "client_id": "",
// "create_time": 1651004578.618224,
// "deal_asset_fee": "0.00000000000000000000",
// "deal_fee": "0.00000000000000000000",
// "deal_profit": "0.00000000000000000000",
// "deal_stock": "0.00000000000000000000",
// "effect_type": 1,
// "fee_asset": "",
// "fee_discount": "0.00000000000000000000",
// "last_deal_amount": "0.00000000000000000000",
// "last_deal_id": 0,
// "last_deal_price": "0.00000000000000000000",
// "last_deal_role": 0,
// "last_deal_time": 0,
// "last_deal_type": 0,
// "left": "0.0005",
// "leverage": "3",
// "maker_fee": "0.00030",
// "market": "BTCUSDT",
// "order_id": 18221659097,
// "position_id": 0,
// "position_type": 1,
// "price": "30000.00",
// "side": 2,
// "source": "api.v1",
// "stop_id": 0,
// "taker_fee": "0.00050",
// "target": 0,
// "type": 1,
// "update_time": 1651004578.618224,
// "user_id": 3620173
// },
// "message": "OK"
// }
//
// Swap Stop
//
// {
// "code": 0,
// "data": {
// "amount": "0.0005",
// "client_id": "",
// "create_time": 1651034023.008771,
// "effect_type": 1,
// "fee_asset": "",
// "fee_discount": "0.00000000000000000000",
// "maker_fee": "0.00030",
// "market": "BTCUSDT",
// "order_id": 18256915101,
// "price": "31000.00",
// "side": 2,
// "source": "api.v1",
// "state": 1,
// "stop_price": "31500.00",
// "stop_type": 1,
// "taker_fee": "0.00050",
// "target": 0,
// "type": 1,
// "update_time": 1651034397.193624,
// "user_id": 3620173
// },
// "message":"OK"
// }
//
const data = this.safeValue (response, 'data');
return this.parseOrder (data, market);
}
async fetchOrdersByStatus (status, symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
limit = (limit === undefined) ? 100 : limit;
const request = {
'limit': limit,
// 'page': 1, // SPOT
// 'offset': 0, // SWAP
// 'side': 0, // SWAP, 0: All, 1: Sell, 2: Buy
};
const stop = this.safeValue (params, 'stop');
const side = this.safeInteger (params, 'side');
params = this.omit (params, 'stop');
let market = undefined;
if (symbol !== undefined) {
market = this.market (symbol);
request['market'] = market['id'];
}
const [ marketType, query ] = this.handleMarketTypeAndParams ('fetchOrdersByStatus', market, params);
let method = undefined;
if (marketType === 'swap') {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrdersByStatus() requires a symbol argument for swap markets');
}
method = 'perpetualPrivateGetOrder' + this.capitalize (status);
if (stop) {
method = 'perpetualPrivateGetOrderStopPending';
}
if (side !== undefined) {
request['side'] = side;
} else {
request['side'] = 0;
}
request['offset'] = 0;
} else {
method = 'privateGetOrder' + this.capitalize (status);
if (stop) {
method = 'privateGetOrderStop' + this.capitalize (status);
}
request['page'] = 1;
}
const accountId = this.safeInteger (params, 'account_id');
const defaultType = this.safeString (this.options, 'defaultType');
if (defaultType === 'margin') {
if (accountId === undefined) {
throw new BadRequest (this.id + ' fetchOpenOrders() and fetchClosedOrders() require an account_id parameter for margin orders');
}
request['account_id'] = accountId;
}
params = this.omit (query, 'account_id');
const response = await this[method] (this.extend (request, params));
//
// Spot and Margin
//
// {
// "code": 0,
// "data": {
// "count": 1,
// "curr_page": 1,
// "data": [
// {
// "account_id": 0,
// "amount": "0.0005",
// "asset_fee": "0",
// "avg_price": "0.00",
// "client_id": "",
// "create_time": 1651089247,
// "deal_amount": "0",
// "deal_fee": "0",
// "deal_money": "0",
// "fee_asset": null,
// "fee_discount": "1",
// "finished_time": 0,
// "id": 74660190839,
// "left": "0.0005",
// "maker_fee_rate": "0.002",
// "market": "BTCUSDT",
// "money_fee": "0",
// "order_type": "limit",
// "price": "31000",
// "status": "not_deal",
// "stock_fee": "0",
// "taker_fee_rate": "0.002",
// "type": "buy"
// }
// ],
// "has_next": false,
// "total": 1
// },
// "message": "Success"
// }
//
// Swap
//
// {
// "code": 0,
// "data": {
// "limit": 100,
// "offset": 0,
// "records": [
// {
// "amount": "0.0005",
// "client_id": "",
// "create_time": 1651030414.088431,
// "deal_asset_fee": "0",
// "deal_fee": "0.00960069",
// "deal_profit": "0.009825",
// "deal_stock": "19.20138",
// "effect_type": 0,
// "fee_asset": "",
// "fee_discount": "0",
// "left": "0",
// "leverage": "3",
// "maker_fee": "0",
// "market": "BTCUSDT",
// "order_id": 18253447431,
// "position_id": 0,
// "position_type": 1,
// "price": "0",
// "side": 1,
// "source": "web",
// "stop_id": 0,
// "taker_fee": "0.0005",
// "target": 0,
// "type": 2,
// "update_time": 1651030414.08847,
// "user_id": 3620173
// },
// ]
// },
// "message": "OK"
// }
//
// Spot and Margin Stop
//
// {
// "code": 0,
// "data": {
// "count": 1,
// "curr_page": 1,
// "data": [
// {
// "account_id": 0,
// "amount": "155",
// "client_id": "",
// "create_time": 1651089182,
// "fee_asset": null,
// "fee_discount": "1",
// "maker_fee": "0.002",
// "market": "BTCUSDT",
// "order_id": 74660111965,
// "order_type": "market",
// "price": "0",
// "state": 0,
// "stop_price": "31500",
// "taker_fee": "0.002",
// "type": "buy"
// }
// ],
// "has_next": false,
// "total": 0
// },
// "message": "Success"
// }
//
// Swap Stop
//
// {
// "code": 0,
// "data": {
// "limit": 100,
// "offset": 0,
// "records": [
// {
// "amount": "0.0005",
// "client_id": "",
// "create_time": 1651089147.321691,
// "effect_type": 1,
// "fee_asset": "",
// "fee_discount": "0.00000000000000000000",
// "maker_fee": "0.00030",
// "market": "BTCUSDT",
// "order_id": 18332143848,
// "price": "31000.00",
// "side": 2,
// "source": "api.v1",
// "state": 1,
// "stop_price": "31500.00",
// "stop_type": 1,
// "taker_fee": "0.00050",
// "target": 0,
// "type": 1,
// "update_time": 1651089147.321691,
// "user_id": 3620173
// }
// ],
// "total": 1
// },
// "message": "OK"
// }
//
const tradeRequest = (marketType === 'swap') ? 'records' : 'data';
const data = this.safeValue (response, 'data');
const orders = this.safeValue (data, tradeRequest, []);
return this.parseOrders (orders, market, since, limit);
}
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name coinex#fetchOpenOrders
* @description fetch all unfilled currently open orders
* @param {str|undefined} symbol unified market symbol
* @param {int|undefined} since the earliest time in ms to fetch open orders for
* @param {int|undefined} limit the maximum number of open orders structures to retrieve
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {[dict]} a list of [order structures]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure}
*/
return await this.fetchOrdersByStatus ('pending', symbol, since, limit, params);
}
async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name coinex#fetchClosedOrders
* @description fetches information on multiple closed orders made by the user
* @param {str|undefined} symbol unified market symbol of the market orders were made in
* @param {int|undefined} since the earliest time in ms to fetch orders for
* @param {int|undefined} limit the maximum number of orde structures to retrieve
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {[dict]} a list of [order structures]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure
*/
return await this.fetchOrdersByStatus ('finished', symbol, since, limit, params);
}
async createDepositAddress (code, params = {}) {
/**
* @method
* @name coinex#createDepositAddress
* @description create a currency deposit address
* @param {str} code unified currency code of the currency for the deposit address
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {dict} an [address structure]{@link https://docs.ccxt.com/en/latest/manual.html#address-structure}
*/
await this.loadMarkets ();
const currency = this.currency (code);
const request = {
'coin_type': currency['id'],
};
if ('network' in params) {
const network = this.safeString (params, 'network');
params = this.omit (params, 'network');
request['smart_contract_name'] = network;
}
const response = await this.privatePutBalanceDepositAddressCoinType (this.extend (request, params));
//
// {
// code: 0,
// data: {
// coin_address: 'TV639dSpb9iGRtoFYkCp4AoaaDYKrK1pw5',
// is_bitcoin_cash: false
// },
// message: 'Success'
// }
const data = this.safeValue (response, 'data', {});
return this.parseDepositAddress (data, currency);
}
async fetchDepositAddress (code, params = {}) {
/**
* @method
* @name coinex#fetchDepositAddress
* @description fetch the deposit address for a currency associated with this account
* @param {str} code unified currency code
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {dict} an [address structure]{@link https://docs.ccxt.com/en/latest/manual.html#address-structure}
*/
await this.loadMarkets ();
const currency = this.currency (code);
const request = {
'coin_type': currency['id'],
};
const networks = this.safeValue (currency, 'networks', {});
const network = this.safeString (params, 'network');
params = this.omit (params, 'network');
const networksKeys = Object.keys (networks);
const numOfNetworks = networksKeys.length;
if (networks !== undefined && numOfNetworks > 1) {
if (network === undefined) {
throw new ArgumentsRequired (this.id + ' fetchDepositAddress() ' + code + ' requires a network parameter');
}
if (!(network in networks)) {
throw new ExchangeError (this.id + ' fetchDepositAddress() ' + network + ' network not supported for ' + code);
}
}
if (network !== undefined) {
request['smart_contract_name'] = network;
}
const response = await this.privateGetBalanceDepositAddressCoinType (this.extend (request, params));
//
// {
// code: 0,
// data: {
// coin_address: '1P1JqozxioQwaqPwgMAQdNDYNyaVSqgARq',
// is_bitcoin_cash: false
// },
// message: 'Success'
// }
//
const data = this.safeValue (response, 'data', {});
const depositAddress = this.parseDepositAddress (data, currency);
const options = this.safeValue (this.options, 'fetchDepositAddress', {});
const fillResponseFromRequest = this.safeValue (options, 'fillResponseFromRequest', true);
if (fillResponseFromRequest) {
depositAddress['network'] = this.safeNetworkCode (network, currency);
}
return depositAddress;
}
safeNetwork (networkId, currency = undefined) {
const networks = this.safeValue (currency, 'networks', {});
const networksCodes = Object.keys (networks);
if (networkId === undefined && networksCodes.length === 1) {
return networks[networksCodes[0]];
}
return {
'id': networkId,
'network': (networkId === undefined) ? undefined : networkId.toUpperCase (),
};
}
safeNetworkCode (networkId, currency = undefined) {
const network = this.safeNetwork (networkId, currency);
return network['network'];
}
parseDepositAddress (depositAddress, currency = undefined) {
//
// {
// coin_address: '1P1JqozxioQwaqPwgMAQdNDYNyaVSqgARq',
// is_bitcoin_cash: false
// }
//
const address = this.safeString (depositAddress, 'coin_address');
return {
'info': depositAddress,
'currency': this.safeCurrencyCode (undefined, currency),
'address': address,
'tag': undefined,
'network': undefined,
};
}
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name coinex#fetchMyTrades
* @description fetch all trades made by the user
* @param {str} symbol unified market symbol
* @param {int|undefined} since the earliest time in ms to fetch trades for
* @param {int|undefined} limit the maximum number of trades structures to retrieve
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {[dict]} a list of [trade structures]{@link https://docs.ccxt.com/en/latest/manual.html#trade-structure}
*/
await this.loadMarkets ();
let market = undefined;
if (limit === undefined) {
limit = 100;
}
const request = {
'limit': limit, // SPOT and SWAP
'offset': 0, // SWAP, means query from a certain record
// 'page': 1, // SPOT
// 'side': 2, // SWAP, 0 for no limit, 1 for sell, 2 for buy
// 'start_time': since, // SWAP
// 'end_time': 1524228297, // SWAP
};
if (symbol !== undefined) {
market = this.market (symbol);
request['market'] = market['id'];
}
let type = undefined;
[ type, params ] = this.handleMarketTypeAndParams ('fetchMyTrades', market, params);
if (type !== 'spot' && symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a symbol argument for non-spot markets');
}
const swap = (type === 'swap');
let method = undefined;
if (swap) {
method = 'perpetualPublicGetMarketUserDeals';
const side = this.safeInteger (params, 'side');
if (side === undefined) {
throw new ArgumentsRequired (this.id + ' fetchMyTrades() requires a side parameter for swap markets');
}
if (since !== undefined) {
request['start_time'] = since;
}
request['side'] = side;
params = this.omit (params, 'side');
} else {
method = 'privateGetOrderUserDeals';
request['page'] = 1;
}
const accountId = this.safeInteger (params, 'account_id');
const defaultType = this.safeString (this.options, 'defaultType');
if (defaultType === 'margin') {
if (accountId === undefined) {
throw new BadRequest (this.id + ' fetchMyTrades() requires an account_id parameter for margin trades');
}
request['account_id'] = accountId;
params = this.omit (params, 'account_id');
}
const response = await this[method] (this.extend (request, params));
//
// Spot and Margin
//
// {
// "code": 0,
// "data": {
// "data": [
// {
// "id": 2611520950,
// "order_id": 63286573298,
// "account_id": 0,
// "create_time": 1638990636,
// "type": "sell",
// "role": "taker",
// "price": "192.29",
// "amount": "0.098",
// "fee": "0.03768884",
// "fee_asset": "USDT",
// "market": "AAVEUSDT",
// "deal_money": "18.84442"
// },
// ],
// "curr_page": 1,
// "has_next": false,
// "count": 3
// },
// "message": "Success"
// }
//
// Swap
//
// {
// "code": 0,
// "data": {
// "limit": 100,
// "offset": 0,
// "records": [
// {
// "amount": "0.0012",
// "deal_fee": "0.0237528",
// "deal_insurance": "0",
// "deal_margin": "15.8352",
// "deal_order_id": 17797031903,
// "deal_profit": "0",
// "deal_stock": "47.5056",
// "deal_type": 1,
// "deal_user_id": 2969195,
// "fee_asset": "",
// "fee_discount": "0",
// "fee_price": "0",
// "fee_rate": "0.0005",
// "fee_real_rate": "0.0005",
// "id": 379044296,
// "leverage": "3",
// "margin_amount": "15.8352",
// "market": "BTCUSDT",
// "open_price": "39588",
// "order_id": 17797092987,
// "position_amount": "0.0012",
// "position_id": 62052321,
// "position_type": 1,
// "price": "39588",
// "role": 2,
// "side": 2,
// "time": 1650675936.016103,
// "user_id": 3620173
// }
// ]
// },
// "message": "OK"
// }
//
const tradeRequest = swap ? 'records' : 'data';
const data = this.safeValue (response, 'data');
const trades = this.safeValue (data, tradeRequest, []);
return this.parseTrades (trades, market, since, limit);
}
async fetchPositions (symbols = undefined, params = {}) {
/**
* @method
* @name coinex#fetchPositions
* @description fetch all open positions
* @param {[str]|undefined} symbols list of unified market symbols
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {[dict]} a list of [position structure]{@link https://docs.ccxt.com/en/latest/manual.html#position-structure}
*/
await this.loadMarkets ();
const request = {};
let market = undefined;
if (symbols !== undefined) {
let symbol = undefined;
if (Array.isArray (symbols)) {
const symbolsLength = symbols.length;
if (symbolsLength > 1) {
throw new BadRequest (this.id + ' fetchPositions() symbols argument cannot contain more than 1 symbol');
}
symbol = symbols[0];
} else {
symbol = symbols;
}
market = this.market (symbol);
request['market'] = market['id'];
}
const response = await this.perpetualPrivateGetPositionPending (this.extend (request, params));
//
// {
// "code": 0,
// "data": [
// {
// "adl_sort": 3396,
// "adl_sort_val": "0.00007786",
// "amount": "0.0005",
// "amount_max": "0.0005",
// "amount_max_margin": "6.42101333333333333333",
// "bkr_price": "25684.05333333333333346175",
// "bkr_price_imply": "0.00000000000000000000",
// "close_left": "0.0005",
// "create_time": 1651294226.110899,
// "deal_all": "19.26000000000000000000",
// "deal_asset_fee": "0.00000000000000000000",
// "fee_asset": "",
// "finish_type": 1,
// "first_price": "38526.08",
// "insurance": "0.00000000000000000000",
// "latest_price": "38526.08",
// "leverage": "3",
// "liq_amount": "0.00000000000000000000",
// "liq_order_price": "0",
// "liq_order_time": 0,
// "liq_price": "25876.68373333333333346175",
// "liq_price_imply": "0.00000000000000000000",
// "liq_profit": "0.00000000000000000000",
// "liq_time": 0,
// "mainten_margin": "0.005",
// "mainten_margin_amount": "0.09631520000000000000",
// "maker_fee": "0.00000000000000000000",
// "margin_amount": "6.42101333333333333333",
// "market": "BTCUSDT",
// "open_margin": "0.33333333333333333333",
// "open_margin_imply": "0.00000000000000000000",
// "open_price": "38526.08000000000000000000",
// "open_val": "19.26304000000000000000",
// "open_val_max": "19.26304000000000000000",
// "position_id": 65847227,
// "profit_clearing": "-0.00963152000000000000",
// "profit_real": "-0.00963152000000000000",
// "profit_unreal": "0.00",
// "side": 2,
// "stop_loss_price": "0.00000000000000000000",
// "stop_loss_type": 0,
// "sys": 0,
// "take_profit_price": "0.00000000000000000000",
// "take_profit_type": 0,
// "taker_fee": "0.00000000000000000000",
// "total": 4661,
// "type": 1,
// "update_time": 1651294226.111196,
// "user_id": 3620173
// },
// ],
// "message": "OK"
// }
//
const position = this.safeValue (response, 'data', []);
const result = [];
for (let i = 0; i < position.length; i++) {
result.push (this.parsePosition (position[i], market));
}
return this.filterByArray (result, 'symbol', symbols, false);
}
async fetchPosition (symbol, params = {}) {
/**
* @method
* @name coinex#fetchPosition
* @description fetch data on a single open contract trade position
* @param {str} symbol unified market symbol of the market the position is held in, default is undefined
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {dict} a [position structure]{@link https://docs.ccxt.com/en/latest/manual.html#position-structure}
*/
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'market': market['id'],
};
const response = await this.perpetualPrivateGetPositionPending (this.extend (request, params));
//
// {
// "code": 0,
// "data": [
// {
// "adl_sort": 3396,
// "adl_sort_val": "0.00007786",
// "amount": "0.0005",
// "amount_max": "0.0005",
// "amount_max_margin": "6.42101333333333333333",
// "bkr_price": "25684.05333333333333346175",
// "bkr_price_imply": "0.00000000000000000000",
// "close_left": "0.0005",
// "create_time": 1651294226.110899,
// "deal_all": "19.26000000000000000000",
// "deal_asset_fee": "0.00000000000000000000",
// "fee_asset": "",
// "finish_type": 1,
// "first_price": "38526.08",
// "insurance": "0.00000000000000000000",
// "latest_price": "38526.08",
// "leverage": "3",
// "liq_amount": "0.00000000000000000000",
// "liq_order_price": "0",
// "liq_order_time": 0,
// "liq_price": "25876.68373333333333346175",
// "liq_price_imply": "0.00000000000000000000",
// "liq_profit": "0.00000000000000000000",
// "liq_time": 0,
// "mainten_margin": "0.005",
// "mainten_margin_amount": "0.09631520000000000000",
// "maker_fee": "0.00000000000000000000",
// "margin_amount": "6.42101333333333333333",
// "market": "BTCUSDT",
// "open_margin": "0.33333333333333333333",
// "open_margin_imply": "0.00000000000000000000",
// "open_price": "38526.08000000000000000000",
// "open_val": "19.26304000000000000000",
// "open_val_max": "19.26304000000000000000",
// "position_id": 65847227,
// "profit_clearing": "-0.00963152000000000000",
// "profit_real": "-0.00963152000000000000",
// "profit_unreal": "0.00",
// "side": 2,
// "stop_loss_price": "0.00000000000000000000",
// "stop_loss_type": 0,
// "sys": 0,
// "take_profit_price": "0.00000000000000000000",
// "take_profit_type": 0,
// "taker_fee": "0.00000000000000000000",
// "total": 4661,
// "type": 1,
// "update_time": 1651294226.111196,
// "user_id": 3620173
// }
// ],
// "message": "OK"
// }
//
const data = this.safeValue (response, 'data', []);
return this.parsePosition (data[0], market);
}
parsePosition (position, market = undefined) {
//
// {
// "adl_sort": 3396,
// "adl_sort_val": "0.00007786",
// "amount": "0.0005",
// "amount_max": "0.0005",
// "amount_max_margin": "6.42101333333333333333",
// "bkr_price": "25684.05333333333333346175",
// "bkr_price_imply": "0.00000000000000000000",
// "close_left": "0.0005",
// "create_time": 1651294226.110899,
// "deal_all": "19.26000000000000000000",
// "deal_asset_fee": "0.00000000000000000000",
// "fee_asset": "",
// "finish_type": 1,
// "first_price": "38526.08",
// "insurance": "0.00000000000000000000",
// "latest_price": "38526.08",
// "leverage": "3",
// "liq_amount": "0.00000000000000000000",
// "liq_order_price": "0",
// "liq_order_time": 0,
// "liq_price": "25876.68373333333333346175",
// "liq_price_imply": "0.00000000000000000000",
// "liq_profit": "0.00000000000000000000",
// "liq_time": 0,
// "mainten_margin": "0.005",
// "mainten_margin_amount": "0.09631520000000000000",
// "maker_fee": "0.00000000000000000000",
// "margin_amount": "6.42101333333333333333",
// "market": "BTCUSDT",
// "open_margin": "0.33333333333333333333",
// "open_margin_imply": "0.00000000000000000000",
// "open_price": "38526.08000000000000000000",
// "open_val": "19.26304000000000000000",
// "open_val_max": "19.26304000000000000000",
// "position_id": 65847227,
// "profit_clearing": "-0.00963152000000000000",
// "profit_real": "-0.00963152000000000000",
// "profit_unreal": "0.00",
// "side": 2,
// "stop_loss_price": "0.00000000000000000000",
// "stop_loss_type": 0,
// "sys": 0,
// "take_profit_price": "0.00000000000000000000",
// "take_profit_type": 0,
// "taker_fee": "0.00000000000000000000",
// "total": 4661,
// "type": 1,
// "update_time": 1651294226.111196,
// "user_id": 3620173
// }
//
const marketId = this.safeString (position, 'market');
market = this.safeMarket (marketId, market);
const symbol = market['symbol'];
const positionId = this.safeInteger (position, 'position_id');
const marginModeInteger = this.safeInteger (position, 'type');
const marginMode = (marginModeInteger === 1) ? 'isolated' : 'cross';
const liquidationPrice = this.safeString (position, 'liq_price');
const entryPrice = this.safeString (position, 'open_price');
const unrealizedPnl = this.safeString (position, 'profit_unreal');
const contractSize = this.safeString (position, 'amount');
const sideInteger = this.safeInteger (position, 'side');
const side = (sideInteger === 1) ? 'short' : 'long';
const timestamp = this.safeTimestamp (position, 'update_time');
const maintenanceMargin = this.safeString (position, 'mainten_margin_amount');
const maintenanceMarginPercentage = this.safeString (position, 'mainten_margin');
const collateral = this.safeString (position, 'margin_amount');
const leverage = this.safeNumber (position, 'leverage');
return {
'info': position,
'id': positionId,
'symbol': symbol,
'notional': undefined,
'marginMode': marginMode,
'liquidationPrice': liquidationPrice,
'entryPrice': entryPrice,
'unrealizedPnl': unrealizedPnl,
'percentage': undefined,
'contracts': undefined,
'contractSize': contractSize,
'markPrice': undefined,
'side': side,
'hedged': undefined,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'maintenanceMargin': maintenanceMargin,
'maintenanceMarginPercentage': maintenanceMarginPercentage,
'collateral': collateral,
'initialMargin': undefined,
'initialMarginPercentage': undefined,
'leverage': leverage,
'marginRatio': undefined,
};
}
async setMarginMode (marginMode, symbol = undefined, params = {}) {
/**
* @method
* @name coinex#setMarginMode
* @description set margin mode to 'cross' or 'isolated'
* @param {str} marginMode 'cross' or 'isolated'
* @param {str} symbol unified market symbol
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {dict} response from the exchange
*/
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' setMarginMode() requires a symbol argument');
}
marginMode = marginMode.toLowerCase ();
if (marginMode !== 'isolated' && marginMode !== 'cross') {
throw new BadRequest (this.id + ' setMarginMode() marginMode argument should be isolated or cross');
}
await this.loadMarkets ();
const market = this.market (symbol);
if (market['type'] !== 'swap') {
throw new BadSymbol (this.id + ' setMarginMode() supports swap contracts only');
}
const defaultMarginMode = this.safeString2 (this.options, 'defaultMarginMode', marginMode);
let defaultPositionType = undefined;
if (defaultMarginMode === 'isolated') {
defaultPositionType = 1;
} else if (defaultMarginMode === 'cross') {
defaultPositionType = 2;
}
const leverage = this.safeInteger (params, 'leverage');
const maxLeverage = this.safeInteger (market['limits']['leverage'], 'max', 100);
const positionType = this.safeInteger (params, 'position_type', defaultPositionType);
if (leverage === undefined) {
throw new ArgumentsRequired (this.id + ' setMarginMode() requires a leverage parameter');
}
if (positionType === undefined) {
throw new ArgumentsRequired (this.id + ' setMarginMode() requires a position_type parameter that will transfer margin to the specified trading pair');
}
if ((leverage < 3) || (leverage > maxLeverage)) {
throw new BadRequest (this.id + ' setMarginMode() leverage should be between 3 and ' + maxLeverage.toString () + ' for ' + symbol);
}
const request = {
'market': market['id'],
'leverage': leverage.toString (),
'position_type': positionType, // 1: isolated, 2: cross
};
return await this.perpetualPrivatePostMarketAdjustLeverage (this.extend (request, params));
}
async setLeverage (leverage, symbol = undefined, params = {}) {
/**
* @method
* @name coinex#setLeverage
* @description set the level of leverage for a market
* @param {float} leverage the rate of leverage
* @param {str} symbol unified market symbol
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {dict} response from the exchange
*/
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' setLeverage() requires a symbol argument');
}
await this.loadMarkets ();
const defaultMarginMode = this.safeString2 (this.options, 'defaultMarginMode', 'marginMode');
let defaultPositionType = undefined;
if (defaultMarginMode === 'isolated') {
defaultPositionType = 1;
} else if (defaultMarginMode === 'cross') {
defaultPositionType = 2;
}
const positionType = this.safeInteger (params, 'position_type', defaultPositionType);
if (positionType === undefined) {
throw new ArgumentsRequired (this.id + ' setLeverage() requires a position_type parameter that will transfer margin to the specified trading pair');
}
const market = this.market (symbol);
const maxLeverage = this.safeInteger (market['limits']['leverage'], 'max', 100);
if (market['type'] !== 'swap') {
throw new BadSymbol (this.id + ' setLeverage() supports swap contracts only');
}
if ((leverage < 3) || (leverage > maxLeverage)) {
throw new BadRequest (this.id + ' setLeverage() leverage should be between 3 and ' + maxLeverage.toString () + ' for ' + symbol);
}
const request = {
'market': market['id'],
'leverage': leverage.toString (),
'position_type': positionType, // 1: isolated, 2: cross
};
return await this.perpetualPrivatePostMarketAdjustLeverage (this.extend (request, params));
}
async fetchLeverageTiers (symbols = undefined, params = {}) {
/**
* @method
* @name coinex#fetchLeverageTiers
* @description retrieve information on the maximum leverage, and maintenance margin for trades of varying trade sizes
* @param {[str]|undefined} symbols list of unified market symbols
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {dict} a dictionary of [leverage tiers structures]{@link https://docs.ccxt.com/en/latest/manual.html#leverage-tiers-structure}, indexed by market symbols
*/
await this.loadMarkets ();
const response = await this.perpetualPublicGetMarketLimitConfig (params);
//
// {
// "code": 0,
// "data": {
// "BTCUSD": [
// ["500001", "100", "0.005"],
// ["1000001", "50", "0.01"],
// ["2000001", "30", "0.015"],
// ["5000001", "20", "0.02"],
// ["10000001", "15", "0.025"],
// ["20000001", "10", "0.03"]
// ],
// ...
// },
// "message": "OK"
// }
//
const data = this.safeValue (response, 'data', {});
return this.parseLeverageTiers (data, symbols, undefined);
}
parseLeverageTiers (response, symbols = undefined, marketIdKey = undefined) {
//
// {
// "BTCUSD": [
// ["500001", "100", "0.005"],
// ["1000001", "50", "0.01"],
// ["2000001", "30", "0.015"],
// ["5000001", "20", "0.02"],
// ["10000001", "15", "0.025"],
// ["20000001", "10", "0.03"]
// ],
// ...
// }
//
const tiers = {};
const marketIds = Object.keys (response);
for (let i = 0; i < marketIds.length; i++) {
const marketId = marketIds[i];
const market = this.safeMarket (marketId);
const symbol = this.safeString (market, 'symbol');
let symbolsLength = 0;
if (symbols !== undefined) {
symbolsLength = symbols.length;
}
if (symbol !== undefined && (symbolsLength === 0 || this.inArray (symbols, symbol))) {
tiers[symbol] = this.parseMarketLeverageTiers (response[marketId], market);
}
}
return tiers;
}
parseMarketLeverageTiers (item, market = undefined) {
const tiers = [];
let minNotional = 0;
for (let j = 0; j < item.length; j++) {
const bracket = item[j];
const maxNotional = this.safeNumber (bracket, 0);
tiers.push ({
'tier': j + 1,
'currency': market['linear'] ? market['base'] : market['quote'],
'minNotional': minNotional,
'maxNotional': maxNotional,
'maintenanceMarginRate': this.safeNumber (bracket, 2),
'maxLeverage': this.safeInteger (bracket, 1),
'info': bracket,
});
minNotional = maxNotional;
}
return tiers;
}
async modifyMarginHelper (symbol, amount, addOrReduce, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'market': market['id'],
'amount': this.amountToPrecision (symbol, amount),
'type': addOrReduce,
};
const response = await this.perpetualPrivatePostPositionAdjustMargin (this.extend (request, params));
//
// {
// "code": 0,
// "data": {
// "adl_sort": 1,
// "adl_sort_val": "0.00004320",
// "amount": "0.0005",
// "amount_max": "0.0005",
// "amount_max_margin": "6.57352000000000000000",
// "bkr_price": "16294.08000000000000011090",
// "bkr_price_imply": "0.00000000000000000000",
// "close_left": "0.0005",
// "create_time": 1651202571.320778,
// "deal_all": "19.72000000000000000000",
// "deal_asset_fee": "0.00000000000000000000",
// "fee_asset": "",
// "finish_type": 1,
// "first_price": "39441.12",
// "insurance": "0.00000000000000000000",
// "latest_price": "39441.12",
// "leverage": "3",
// "liq_amount": "0.00000000000000000000",
// "liq_order_price": "0",
// "liq_order_time": 0,
// "liq_price": "16491.28560000000000011090",
// "liq_price_imply": "0.00000000000000000000",
// "liq_profit": "0.00000000000000000000",
// "liq_time": 0,
// "mainten_margin": "0.005",
// "mainten_margin_amount": "0.09860280000000000000",
// "maker_fee": "0.00000000000000000000",
// "margin_amount": "11.57352000000000000000",
// "market": "BTCUSDT",
// "open_margin": "0.58687582908396110455",
// "open_margin_imply": "0.00000000000000000000",
// "open_price": "39441.12000000000000000000",
// "open_val": "19.72056000000000000000",
// "open_val_max": "19.72056000000000000000",
// "position_id": 65171206,
// "profit_clearing": "-0.00986028000000000000",
// "profit_real": "-0.00986028000000000000",
// "profit_unreal": "0.00",
// "side": 2,
// "stop_loss_price": "0.00000000000000000000",
// "stop_loss_type": 0,
// "sys": 0,
// "take_profit_price": "0.00000000000000000000",
// "take_profit_type": 0,
// "taker_fee": "0.00000000000000000000",
// "total": 3464,
// "type": 1,
// "update_time": 1651202638.911212,
// "user_id": 3620173
// },
// "message":"OK"
// }
//
const status = this.safeString (response, 'message');
const type = (addOrReduce === 1) ? 'add' : 'reduce';
return this.extend (this.parseMarginModification (response, market), {
'amount': this.parseNumber (amount),
'type': type,
'status': status,
});
}
parseMarginModification (data, market = undefined) {
return {
'info': data,
'type': undefined,
'amount': undefined,
'code': market['quote'],
'symbol': this.safeSymbol (undefined, market),
'status': undefined,
};
}
async addMargin (symbol, amount, params = {}) {
/**
* @method
* @name coinex#addMargin
* @description add margin
* @param {str} symbol unified market symbol
* @param {float} amount amount of margin to add
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {dict} a [margin structure]{@link https://docs.ccxt.com/en/latest/manual.html#add-margin-structure}
*/
return await this.modifyMarginHelper (symbol, amount, 1, params);
}
async reduceMargin (symbol, amount, params = {}) {
/**
* @method
* @name coinex#reduceMargin
* @description remove margin from a position
* @param {str} symbol unified market symbol
* @param {float} amount the amount of margin to remove
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {dict} a [margin structure]{@link https://docs.ccxt.com/en/latest/manual.html#reduce-margin-structure}
*/
return await this.modifyMarginHelper (symbol, amount, 2, params);
}
async fetchFundingHistory (symbol = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name coinex#fetchFundingHistory
* @description fetch the history of funding payments paid and received on this account
* @param {str} symbol unified market symbol
* @param {int|undefined} since the earliest time in ms to fetch funding history for
* @param {int|undefined} limit the maximum number of funding history structures to retrieve
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {dict} a [funding history structure]{@link https://docs.ccxt.com/en/latest/manual.html#funding-history-structure}
*/
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchFundingHistory() requires a symbol argument');
}
limit = (limit === undefined) ? 100 : limit;
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'market': market['id'],
'limit': limit,
// 'offset': 0,
// 'end_time': 1638990636000,
// 'windowtime': 1638990636000,
};
if (since !== undefined) {
request['start_time'] = since;
}
const response = await this.perpetualPrivateGetPositionFunding (this.extend (request, params));
//
// {
// "code": 0,
// "data": {
// "limit": 100,
// "offset": 0,
// "records": [
// {
// "amount": "0.0012",
// "asset": "USDT",
// "funding": "-0.0095688273996",
// "funding_rate": "0.00020034",
// "market": "BTCUSDT",
// "position_id": 62052321,
// "price": "39802.45",
// "real_funding_rate": "0.00020034",
// "side": 2,
// "time": 1650729623.933885,
// "type": 1,
// "user_id": 3620173,
// "value": "47.76294"
// },
// ]
// },
// "message": "OK"
// }
//
const data = this.safeValue (response, 'data', {});
const resultList = this.safeValue (data, 'records', []);
const result = [];
for (let i = 0; i < resultList.length; i++) {
const entry = resultList[i];
const timestamp = this.safeTimestamp (entry, 'time');
const currencyId = this.safeString (entry, 'asset');
const code = this.safeCurrencyCode (currencyId);
result.push ({
'info': entry,
'symbol': symbol,
'code': code,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'id': this.safeNumber (entry, 'position_id'),
'amount': this.safeNumber (entry, 'funding'),
});
}
return result;
}
async fetchFundingRate (symbol, params = {}) {
/**
* @method
* @name coinex#fetchFundingRate
* @description fetch the current funding rate
* @param {str} symbol unified market symbol
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {dict} a [funding rate structure]{@link https://docs.ccxt.com/en/latest/manual.html#funding-rate-structure}
*/
await this.loadMarkets ();
const market = this.market (symbol);
if (!market['swap']) {
throw new BadSymbol (this.id + ' fetchFundingRate() supports swap contracts only');
}
const request = {
'market': market['id'],
};
const response = await this.perpetualPublicGetMarketTicker (this.extend (request, params));
//
// {
// "code": 0,
// "data":
// {
// "date": 1650678472474,
// "ticker": {
// "vol": "6090.9430",
// "low": "39180.30",
// "open": "40474.97",
// "high": "40798.01",
// "last": "39659.30",
// "buy": "39663.79",
// "period": 86400,
// "funding_time": 372,
// "position_amount": "270.1956",
// "funding_rate_last": "0.00022913",
// "funding_rate_next": "0.00013158",
// "funding_rate_predict": "0.00016552",
// "insurance": "16045554.83969682659674035672",
// "sign_price": "39652.48",
// "index_price": "39648.44250000",
// "sell_total": "22.3913",
// "buy_total": "19.4498",
// "buy_amount": "12.8942",
// "sell": "39663.80",
// "sell_amount": "0.9388"
// }
// },
// "message": "OK"
// }
//
const data = this.safeValue (response, 'data', {});
const ticker = this.safeValue (data, 'ticker', {});
return this.parseFundingRate (ticker, market);
}
parseFundingRate (contract, market = undefined) {
//
// fetchFundingRate
//
// {
// "vol": "6090.9430",
// "low": "39180.30",
// "open": "40474.97",
// "high": "40798.01",
// "last": "39659.30",
// "buy": "39663.79",
// "period": 86400,
// "funding_time": 372,
// "position_amount": "270.1956",
// "funding_rate_last": "0.00022913",
// "funding_rate_next": "0.00013158",
// "funding_rate_predict": "0.00016552",
// "insurance": "16045554.83969682659674035672",
// "sign_price": "39652.48",
// "index_price": "39648.44250000",
// "sell_total": "22.3913",
// "buy_total": "19.4498",
// "buy_amount": "12.8942",
// "sell": "39663.80",
// "sell_amount": "0.9388"
// }
//
return {
'info': contract,
'symbol': this.safeSymbol (undefined, market),
'markPrice': this.safeString (contract, 'sign_price'),
'indexPrice': this.safeString (contract, 'index_price'),
'interestRate': undefined,
'estimatedSettlePrice': undefined,
'timestamp': undefined,
'datetime': undefined,
'fundingRate': this.safeString (contract, 'funding_rate_next'),
'fundingTimestamp': undefined,
'fundingDatetime': undefined,
'nextFundingRate': this.safeString (contract, 'funding_rate_predict'),
'nextFundingTimestamp': undefined,
'nextFundingDatetime': undefined,
'previousFundingRate': this.safeString (contract, 'funding_rate_last'),
'previousFundingTimestamp': undefined,
'previousFundingDatetime': undefined,
};
}
async withdraw (code, amount, address, tag = undefined, params = {}) {
/**
* @method
* @name coinex#withdraw
* @description make a withdrawal
* @param {str} code unified currency code
* @param {float} amount the amount to withdraw
* @param {str} address the address to withdraw to
* @param {str|undefined} tag
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {dict} a [transaction structure]{@link https://docs.ccxt.com/en/latest/manual.html#transaction-structure}
*/
[ tag, params ] = this.handleWithdrawTagAndParams (tag, params);
this.checkAddress (address);
await this.loadMarkets ();
const currency = this.currency (code);
if (tag) {
address = address + ':' + tag;
}
const request = {
'coin_type': currency['id'],
'coin_address': address, // must be authorized, inter-user transfer by a registered mobile phone number or an email address is supported
'actual_amount': parseFloat (amount), // the actual amount without fees, https://www.coinex.com/fees
'transfer_method': 'onchain', // onchain, local
};
const response = await this.privatePostBalanceCoinWithdraw (this.extend (request, params));
//
// {
// "code": 0,
// "data": {
// "actual_amount": "1.00000000",
// "amount": "1.00000000",
// "coin_address": "1KAv3pazbTk2JnQ5xTo6fpKK7p1it2RzD4",
// "coin_type": "BCH",
// "coin_withdraw_id": 206,
// "confirmations": 0,
// "create_time": 1524228297,
// "status": "audit",
// "tx_fee": "0",
// "tx_id": ""
// },
// "message": "Ok"
// }
//
const transaction = this.safeValue (response, 'data', {});
return this.parseTransaction (transaction, currency);
}
parseTransactionStatus (status) {
const statuses = {
'audit': 'pending',
'pass': 'pending',
'processing': 'pending',
'confirming': 'pending',
'not_pass': 'failed',
'cancel': 'canceled',
'finish': 'ok',
'fail': 'failed',
};
return this.safeString (statuses, status, status);
}
async fetchFundingRateHistory (symbol = undefined, since = undefined, limit = 100, params = {}) {
/**
* @method
* @name coinex#fetchFundingRateHistory
* @description fetches historical funding rate prices
* @param {str|undefined} symbol unified symbol of the market to fetch the funding rate history for
* @param {int|undefined} since timestamp in ms of the earliest funding rate to fetch
* @param {int|undefined} limit the maximum amount of [funding rate structures]{@link https://docs.ccxt.com/en/latest/manual.html?#funding-rate-history-structure} to fetch
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {[dict]} a list of [funding rate structures]{@link https://docs.ccxt.com/en/latest/manual.html?#funding-rate-history-structure}
*/
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchFundingRateHistory() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'market': market['id'],
'limit': limit,
'offset': 0,
// 'end_time': 1638990636,
};
if (since !== undefined) {
request['start_time'] = since;
}
const response = await this.perpetualPublicGetMarketFundingHistory (this.extend (request, params));
//
// {
// "code": 0,
// "data": {
// "offset": 0,
// "limit": 3,
// "records": [
// {
// "time": 1650672021.6230309,
// "market": "BTCUSDT",
// "asset": "USDT",
// "funding_rate": "0.00022913",
// "funding_rate_real": "0.00022913"
// },
// ]
// },
// "message": "OK"
// }
//
const data = this.safeValue (response, 'data');
const result = this.safeValue (data, 'records', []);
const rates = [];
for (let i = 0; i < result.length; i++) {
const entry = result[i];
const marketId = this.safeString (entry, 'market');
const symbol = this.safeSymbol (marketId);
const timestamp = this.safeTimestamp (entry, 'time');
rates.push ({
'info': entry,
'symbol': symbol,
'fundingRate': this.safeString (entry, 'funding_rate'),
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
});
}
const sorted = this.sortBy (rates, 'timestamp');
return this.filterBySymbolSinceLimit (sorted, market['symbol'], since, limit);
}
parseTransaction (transaction, currency = undefined) {
//
// fetchDeposits
//
// {
// "actual_amount": "120.00000000",
// "actual_amount_display": "120",
// "add_explorer": "XXX",
// "amount": "120.00000000",
// "amount_display": "120",
// "coin_address": "XXXXXXXX",
// "coin_address_display": "XXXXXXXX",
// "coin_deposit_id": 1866,
// "coin_type": "USDT",
// "confirmations": 0,
// "create_time": 1539595701,
// "explorer": "",
// "remark": "",
// "status": "finish",
// "status_display": "finish",
// "transfer_method": "local",
// "tx_id": "",
// "tx_id_display": "XXXXXXXXXX"
// }
//
// fetchWithdrawals
//
// {
// "actual_amount": "0.10000000",
// "amount": "0.10000000",
// "coin_address": "15sr1VdyXQ6sVLqeJUJ1uPzLpmQtgUeBSB",
// "coin_type": "BCH",
// "coin_withdraw_id": 203,
// "confirmations": 11,
// "create_time": 1515806440,
// "status": "finish",
// "tx_fee": "0",
// "tx_id": "896371d0e23d64d1cac65a0b7c9e9093d835affb572fec89dd4547277fbdd2f6"
// }
//
const id = this.safeString2 (transaction, 'coin_withdraw_id', 'coin_deposit_id');
const address = this.safeString (transaction, 'coin_address');
let tag = this.safeString (transaction, 'remark'); // set but unused
if (tag !== undefined) {
if (tag.length < 1) {
tag = undefined;
}
}
let txid = this.safeValue (transaction, 'tx_id');
if (txid !== undefined) {
if (txid.length < 1) {
txid = undefined;
}
}
const currencyId = this.safeString (transaction, 'coin_type');
const code = this.safeCurrencyCode (currencyId, currency);
const timestamp = this.safeTimestamp (transaction, 'create_time');
const type = ('coin_withdraw_id' in transaction) ? 'withdraw' : 'deposit';
const status = this.parseTransactionStatus (this.safeString (transaction, 'status'));
let amount = this.safeNumber (transaction, 'amount');
let feeCost = this.safeNumber (transaction, 'tx_fee');
if (type === 'deposit') {
feeCost = 0;
}
const fee = {
'cost': feeCost,
'currency': code,
};
// https://github.com/ccxt/ccxt/issues/8321
if (amount !== undefined) {
amount = amount - feeCost;
}
return {
'info': transaction,
'id': id,
'txid': txid,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'network': undefined,
'address': address,
'addressTo': undefined,
'addressFrom': undefined,
'tag': tag,
'tagTo': undefined,
'tagFrom': undefined,
'type': type,
'amount': amount,
'currency': code,
'status': status,
'updated': undefined,
'fee': fee,
};
}
async transfer (code, amount, fromAccount, toAccount, params = {}) {
/**
* @method
* @name coinex#transfer
* @description transfer currency internally between wallets on the same account
* @param {str} code unified currency code
* @param {float} amount amount to transfer
* @param {str} fromAccount account to transfer from
* @param {str} toAccount account to transfer to
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {dict} a [transfer structure]{@link https://docs.ccxt.com/en/latest/manual.html#transfer-structure}
*/
await this.loadMarkets ();
const currency = this.currency (code);
const amountToPrecision = this.currencyToPrecision (code, amount);
const request = {
'amount': amountToPrecision,
'coin_type': currency['id'],
};
let method = 'privatePostContractBalanceTransfer';
if ((fromAccount === 'spot') && (toAccount === 'swap')) {
request['transfer_side'] = 'in'; // 'in' spot to swap, 'out' swap to spot
} else if ((fromAccount === 'swap') && (toAccount === 'spot')) {
request['transfer_side'] = 'out'; // 'in' spot to swap, 'out' swap to spot
} else {
const accountsById = this.safeValue (this.options, 'accountsById', {});
const fromId = this.safeString (accountsById, fromAccount, fromAccount);
const toId = this.safeString (accountsById, toAccount, toAccount);
// fromAccount and toAccount must be integers for margin transfers
// spot is 0, use fetchBalance() to find the margin account id
request['from_account'] = parseInt (fromId);
request['to_account'] = parseInt (toId);
method = 'privatePostMarginTransfer';
}
const response = await this[method] (this.extend (request, params));
//
// {"code": 0, "data": null, "message": "Success"}
//
return this.extend (this.parseTransfer (response, currency), {
'amount': this.parseNumber (amountToPrecision),
'fromAccount': fromAccount,
'toAccount': toAccount,
});
}
parseTransferStatus (status) {
const statuses = {
'0': 'ok',
'SUCCESS': 'ok',
};
return this.safeString (statuses, status, status);
}
parseTransfer (transfer, currency = undefined) {
//
// fetchTransfers Swap
//
// {
// "amount": "10",
// "asset": "USDT",
// "transfer_type": "transfer_out", // from swap to spot
// "created_at": 1651633422
// },
//
// fetchTransfers Margin
//
// {
// "id": 7580062,
// "updated_at": 1653684379,
// "user_id": 3620173,
// "from_account_id": 0,
// "to_account_id": 1,
// "asset": "BTC",
// "amount": "0.00160829",
// "balance": "0.00160829",
// "transfer_type": "IN",
// "status": "SUCCESS",
// "created_at": 1653684379
// },
//
const timestamp = this.safeTimestamp (transfer, 'created_at');
const transferType = this.safeString (transfer, 'transfer_type');
let fromAccount = undefined;
let toAccount = undefined;
if (transferType === 'transfer_out') {
fromAccount = 'swap';
toAccount = 'spot';
} else if (transferType === 'transfer_in') {
fromAccount = 'spot';
toAccount = 'swap';
} else if (transferType === 'IN') {
fromAccount = 'spot';
toAccount = 'margin';
} else if (transferType === 'OUT') {
fromAccount = 'margin';
toAccount = 'spot';
}
const currencyId = this.safeString (transfer, 'asset');
const currencyCode = this.safeCurrencyCode (currencyId, currency);
return {
'id': this.safeInteger (transfer, 'id'),
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'currency': currencyCode,
'amount': this.safeNumber (transfer, 'amount'),
'fromAccount': fromAccount,
'toAccount': toAccount,
'status': this.parseTransferStatus (this.safeString2 (transfer, 'code', 'status')),
};
}
async fetchTransfers (code = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name coinex#fetchTransfers
* @description fetch a history of internal transfers made on an account
* @param {str|undefined} code unified currency code of the currency transferred
* @param {int|undefined} since the earliest time in ms to fetch transfers for
* @param {int|undefined} limit the maximum number of transfers structures to retrieve
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {[dict]} a list of [transfer structures]{@link https://docs.ccxt.com/en/latest/manual.html#transfer-structure}
*/
await this.loadMarkets ();
let currency = undefined;
const request = {
'page': 1,
'limit': limit,
// 'asset': 'USDT',
// 'start_time': since,
// 'end_time': 1515806440,
// 'transfer_type': 'transfer_in', // transfer_in: from Spot to Swap Account, transfer_out: from Swap to Spot Account
};
const page = this.safeInteger (params, 'page');
if (page !== undefined) {
request['page'] = page;
}
if (code !== undefined) {
currency = this.safeCurrencyCode (code);
request['asset'] = currency['id'];
}
if (since !== undefined) {
request['start_time'] = since;
}
params = this.omit (params, 'page');
const defaultType = this.safeString (this.options, 'defaultType');
const method = (defaultType === 'margin') ? 'privateGetMarginTransferHistory' : 'privateGetContractTransferHistory';
const response = await this[method] (this.extend (request, params));
//
// Swap
//
// {
// "code": 0,
// "data": {
// "records": [
// {
// "amount": "10",
// "asset": "USDT",
// "transfer_type": "transfer_out",
// "created_at": 1651633422
// },
// ],
// "total": 5
// },
// "message": "Success"
// }
//
// Margin
//
// {
// "code": 0,
// "data": {
// "records": [
// {
// "id": 7580062,
// "updated_at": 1653684379,
// "user_id": 3620173,
// "from_account_id": 0,
// "to_account_id": 1,
// "asset": "BTC",
// "amount": "0.00160829",
// "balance": "0.00160829",
// "transfer_type": "IN",
// "status": "SUCCESS",
// "created_at": 1653684379
// }
// ],
// "total": 1
// },
// "message": "Success"
// }
//
const data = this.safeValue (response, 'data', {});
const transfers = this.safeValue (data, 'records', []);
return this.parseTransfers (transfers, currency, since, limit);
}
async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name coinex#fetchWithdrawals
* @description fetch all withdrawals made from an account
* @param {str} code unified currency code
* @param {int|undefined} since the earliest time in ms to fetch withdrawals for
* @param {int|undefined} limit the maximum number of withdrawals structures to retrieve
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {[dict]} a list of [transaction structures]{@link https://docs.ccxt.com/en/latest/manual.html#transaction-structure}
*/
if (code === undefined) {
throw new ArgumentsRequired (this.id + ' fetchWithdrawals() requires a currency code argument');
}
await this.loadMarkets ();
const currency = this.currency (code);
const request = {
'coin_type': currency['id'],
};
if (limit !== undefined) {
request['Limit'] = limit;
}
const response = await this.privateGetBalanceCoinWithdraw (this.extend (request, params));
//
// {
// "code": 0,
// "data": {
// "has_next": true,
// "curr_page": 1,
// "count": 10,
// "data": [
// {
// "coin_withdraw_id": 203,
// "create_time": 1513933541,
// "actual_amount": "0.00100000",
// "actual_amount_display": "***",
// "amount": "0.00100000",
// "amount_display": "******",
// "coin_address": "1GVVx5UBddLKrckTprNi4VhHSymeQ8tsLF",
// "app_coin_address_display": "**********",
// "coin_address_display": "****************",
// "add_explorer": "https://explorer.viawallet.com/btc/address/1GVVx5UBddLKrckTprNi4VhHSymeQ8tsLF",
// "coin_type": "BTC",
// "confirmations": 6,
// "explorer": "https://explorer.viawallet.com/btc/tx/1GVVx5UBddLKrckTprNi4VhHSymeQ8tsLF",
// "fee": "0",
// "remark": "",
// "smart_contract_name": "BTC",
// "status": "finish",
// "status_display": "finish",
// "transfer_method": "onchain",
// "tx_fee": "0",
// "tx_id": "896371d0e23d64d1cac65a0b7c9e9093d835affb572fec89dd4547277fbdd2f6"
// }, /* many more data points */
// ],
// "total": ***,
// "total_page":***
// },
// "message": "Success"
// }
//
let data = this.safeValue (response, 'data');
if (!Array.isArray (data)) {
data = this.safeValue (data, 'data', []);
}
return this.parseTransactions (data, currency, since, limit);
}
async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name coinex#fetchDeposits
* @description fetch all deposits made to an account
* @param {str} code unified currency code
* @param {int|undefined} since the earliest time in ms to fetch deposits for
* @param {int|undefined} limit the maximum number of deposits structures to retrieve
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {[dict]} a list of [transaction structures]{@link https://docs.ccxt.com/en/latest/manual.html#transaction-structure}
*/
if (code === undefined) {
throw new ArgumentsRequired (this.id + ' fetchDeposits() requires a currency code argument');
}
await this.loadMarkets ();
const currency = this.currency (code);
const request = {
'coin_type': currency['id'],
};
if (limit !== undefined) {
request['Limit'] = limit;
}
const response = await this.privateGetBalanceCoinDeposit (this.extend (request, params));
// {
// "code": 0,
// "data": [
// {
// "actual_amount": "4.65397682",
// "actual_amount_display": "4.65397682",
// "add_explorer": "https://etherscan.io/address/0x361XXXXXX",
// "amount": "4.65397682",
// "amount_display": "4.65397682",
// "coin_address": "0x36dabcdXXXXXX",
// "coin_address_display": "0x361X*****XXXXX",
// "coin_deposit_id": 966191,
// "coin_type": "ETH",
// "confirmations": 30,
// "create_time": 1531661445,
// "explorer": "https://etherscan.io/tx/0x361XXXXXX",
// "remark": "",
// "status": "finish",
// "status_display": "finish",
// "transfer_method": "onchain",
// "tx_id": "0x361XXXXXX",
// "tx_id_display": "0x361XXXXXX"
// }
// ],
// "message": "Ok"
// }
//
let data = this.safeValue (response, 'data');
if (!Array.isArray (data)) {
data = this.safeValue (data, 'data', []);
}
return this.parseTransactions (data, currency, since, limit);
}
parseBorrowRate (info, currency = undefined) {
//
// {
// "market": "BTCUSDT",
// "leverage": 10,
// "BTC": {
// "min_amount": "0.002",
// "max_amount": "200",
// "day_rate": "0.001"
// },
// "USDT": {
// "min_amount": "60",
// "max_amount": "5000000",
// "day_rate": "0.001"
// }
// },
//
const timestamp = this.milliseconds ();
const baseCurrencyData = this.safeValue (info, currency, {});
return {
'currency': this.safeCurrencyCode (currency),
'rate': this.safeNumber (baseCurrencyData, 'day_rate'),
'period': 86400000,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'info': info,
};
}
async fetchBorrowRate (code, params = {}) {
/**
* @method
* @name coinex#fetchBorrowRate
* @description fetch the rate of interest to borrow a currency for margin trading
* @param {str} code unified currency code
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {dict} a [borrow rate structure]{@link https://docs.ccxt.com/en/latest/manual.html#borrow-rate-structure}
*/
await this.loadMarkets ();
let market = undefined;
if (code in this.markets) {
market = this.market (code);
} else {
const defaultSettle = this.safeString (this.options, 'defaultSettle', 'USDT');
market = this.market (code + '/' + defaultSettle);
}
const request = {
'market': market['id'],
};
const response = await this.privateGetMarginConfig (this.extend (request, params));
//
// {
// "code": 0,
// "data": {
// "market": "BTCUSDT",
// "leverage": 10,
// "BTC": {
// "min_amount": "0.002",
// "max_amount": "200",
// "day_rate": "0.001"
// },
// "USDT": {
// "min_amount": "60",
// "max_amount": "5000000",
// "day_rate": "0.001"
// }
// },
// "message": "Success"
// }
//
const data = this.safeValue (response, 'data', {});
return this.parseBorrowRate (data, market['base']);
}
async fetchBorrowRates (params = {}) {
/**
* @method
* @name coinex#fetchBorrowRates
* @description fetch the borrow interest rates of all currencies
* @param {dict} params extra parameters specific to the coinex api endpoint
* @returns {dict} a list of [borrow rate structures]{@link https://docs.ccxt.com/en/latest/manual.html#borrow-rate-structure}
*/
await this.loadMarkets ();
const response = await this.privateGetMarginConfig (params);
//
// {
// "code": 0,
// "data": [
// {
// "market": "BTCUSDT",
// "leverage": 10,
// "BTC": {
// "min_amount": "0.002",
// "max_amount": "200",
// "day_rate": "0.001"
// },
// "USDT": {
// "min_amount": "60",
// "max_amount": "5000000",
// "day_rate": "0.001"
// }
// },
// ],
// "message": "Success"
// }
//
const timestamp = this.milliseconds ();
const data = this.safeValue (response, 'data', {});
const rates = [];
for (let i = 0; i < data.length; i++) {
const entry = data[i];
const symbol = this.safeString (entry, 'market');
const market = this.safeMarket (symbol);
const currencyData = this.safeValue (entry, market['base']);
rates.push ({
'currency': market['base'],
'rate': this.safeNumber (currencyData, 'day_rate'),
'period': 86400000,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'info': entry,
});
}
return rates;
}
async fetchBorrowInterest (code = undefined, symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {};
let market = undefined;
if (symbol !== undefined) {
market = this.market (symbol);
request['market'] = market['id'];
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.privateGetMarginLoanHistory (this.extend (request, params));
//
// {
// "code": 0,
// "data": {
// "page": 1,
// "limit": 10,
// "total": 1,
// "has_next": false,
// "curr_page": 1,
// "count": 1,
// "data": [
// {
// "loan_id": 2616357,
// "create_time": 1654214027,
// "market_type": "BTCUSDT",
// "coin_type": "BTC",
// "day_rate": "0.001",
// "loan_amount": "0.0144",
// "interest_amount": "0",
// "unflat_amount": "0",
// "expire_time": 1655078027,
// "is_renew": true,
// "status": "finish"
// }
// ],
// "total_page": 1
// },
// "message": "Success"
// }
//
const data = this.safeValue (response, 'data', {});
const rows = this.safeValue (data, 'data', []);
const interest = this.parseBorrowInterests (rows, market);
return this.filterByCurrencySinceLimit (interest, code, since, limit);
}
parseBorrowInterest (info, market = undefined) {
//
// {
// "loan_id": 2616357,
// "create_time": 1654214027,
// "market_type": "BTCUSDT",
// "coin_type": "BTC",
// "day_rate": "0.001",
// "loan_amount": "0.0144",
// "interest_amount": "0",
// "unflat_amount": "0",
// "expire_time": 1655078027,
// "is_renew": true,
// "status": "finish"
// }
//
const marketId = this.safeString (info, 'market_type');
market = this.safeMarket (marketId, market);
const symbol = this.safeString (market, 'symbol');
const timestamp = this.safeTimestamp (info, 'expire_time');
const unflatAmount = this.safeString (info, 'unflat_amount');
const loanAmount = this.safeString (info, 'loan_amount');
let interest = Precise.stringSub (unflatAmount, loanAmount);
if (unflatAmount === '0') {
interest = undefined;
}
return {
'account': undefined, // deprecated
'symbol': symbol,
'marginMode': 'isolated',
'marginType': undefined, // deprecated
'currency': this.safeCurrencyCode (this.safeString (info, 'coin_type')),
'interest': this.parseNumber (interest),
'interestRate': this.safeNumber (info, 'day_rate'),
'amountBorrowed': this.parseNumber (loanAmount),
'timestamp': timestamp, // expiry time
'datetime': this.iso8601 (timestamp),
'info': info,
};
}
async borrowMargin (code, amount, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' borrowMargin() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
const currency = this.currency (code);
const request = {
'market': market['id'],
'coin_type': currency['id'],
'amount': this.currencyToPrecision (code, amount),
};
const response = await this.privatePostMarginLoan (this.extend (request, params));
//
// {
// "code": 0,
// "data": {
// "loan_id": 1670
// },
// "message": "Success"
// }
//
const data = this.safeValue (response, 'data', {});
const transaction = this.parseMarginLoan (data, currency);
return this.extend (transaction, {
'amount': amount,
'symbol': symbol,
});
}
async repayMargin (code, amount, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' repayMargin() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
const currency = this.currency (code);
const request = {
'market': market['id'],
'coin_type': currency['id'],
'amount': this.currencyToPrecision (code, amount),
};
const loanId = this.safeInteger (params, 'loan_id');
if (loanId !== undefined) {
request['loan_id'] = loanId;
}
const response = await this.privatePostMarginFlat (this.extend (request, params));
//
// {
// "code": 0,
// "data": null,
// "message": "Success"
// }
//
const transaction = this.parseMarginLoan (response, currency);
return this.extend (transaction, {
'amount': amount,
'symbol': symbol,
});
}
parseMarginLoan (info, currency = undefined) {
//
// borrowMargin
//
// {
// "loan_id": 1670
// }
//
// repayMargin
//
// {
// "code": 0,
// "data": null,
// "message": "Success"
// }
//
return {
'id': this.safeInteger (info, 'loan_id'),
'currency': this.safeCurrencyCode (undefined, currency),
'amount': undefined,
'symbol': undefined,
'timestamp': undefined,
'datetime': undefined,
'info': info,
};
}
nonce () {
return this.milliseconds ();
}
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
path = this.implodeParams (path, params);
let url = this.urls['api'][api] + '/' + this.version + '/' + path;
let query = this.omit (params, this.extractParams (path));
const nonce = this.nonce ().toString ();
if (api === 'perpetualPrivate' || url === 'https://api.coinex.com/perpetual/v1/market/user_deals') {
this.checkRequiredCredentials ();
query = this.extend ({
'access_id': this.apiKey,
'timestamp': nonce,
}, query);
query = this.keysort (query);
const urlencoded = this.rawencode (query);
const signature = this.hash (this.encode (urlencoded + '&secret_key=' + this.secret), 'sha256');
headers = {
'Authorization': signature.toLowerCase (),
'AccessId': this.apiKey,
};
if ((method === 'GET') || (method === 'PUT')) {
url += '?' + urlencoded;
} else {
headers['Content-Type'] = 'application/x-www-form-urlencoded';
body = urlencoded;
}
} else if (api === 'public' || api === 'perpetualPublic') {
if (Object.keys (query).length) {
url += '?' + this.urlencode (query);
}
} else {
this.checkRequiredCredentials ();
query = this.extend ({
'access_id': this.apiKey,
'tonce': nonce,
}, query);
query = this.keysort (query);
const urlencoded = this.rawencode (query);
const signature = this.hash (this.encode (urlencoded + '&secret_key=' + this.secret));
headers = {
'Authorization': signature.toUpperCase (),
'Content-Type': 'application/json',
};
if ((method === 'GET') || (method === 'DELETE') || (method === 'PUT')) {
url += '?' + urlencoded;
} else {
body = this.json (query);
}
}
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
}
handleErrors (httpCode, reason, url, method, headers, body, response, requestHeaders, requestBody) {
if (response === undefined) {
return;
}
const code = this.safeString (response, 'code');
const data = this.safeValue (response, 'data');
const message = this.safeString (response, 'message');
if ((code !== '0') || ((message !== 'Success') && (message !== 'Succeeded') && (message !== 'Ok') && !data)) {
const responseCodes = {
// https://github.com/coinexcom/coinex_exchange_api/wiki/013error_code
'23': PermissionDenied, // IP Prohibited
'24': AuthenticationError,
'25': AuthenticationError,
'34': AuthenticationError, // Access id is expires
'35': ExchangeNotAvailable, // Service unavailable
'36': RequestTimeout, // Service timeout
'213': RateLimitExceeded, // Too many requests
'107': InsufficientFunds,
'600': OrderNotFound,
'601': InvalidOrder,
'602': InvalidOrder,
'606': InvalidOrder,
};
const ErrorClass = this.safeValue (responseCodes, code, ExchangeError);
throw new ErrorClass (response['message']);
}
}
};