sfccxt
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges
1,145 lines (1,131 loc) • 215 kB
JavaScript
'use strict';
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { BadRequest, InvalidNonce, BadSymbol, InvalidOrder, InvalidAddress, ExchangeError, ArgumentsRequired, NotSupported, InsufficientFunds, PermissionDenied } = require ('./base/errors');
const { TICK_SIZE } = require ('./base/functions/number');
const Precise = require ('./base/Precise');
// ---------------------------------------------------------------------------
module.exports = class mexc3 extends Exchange {
describe () {
return this.deepExtend (super.describe (), {
'id': 'mexc3',
'name': 'MEXC Global',
'countries': [ 'SC' ], // Seychelles
'rateLimit': 50, // default rate limit is 20 times per second
'version': 'v3',
'has': {
'CORS': undefined,
'spot': undefined,
'margin': true,
'swap': undefined,
'future': undefined,
'option': undefined,
'addMargin': true,
'borrowMargin': true,
'cancelAllOrders': true,
'cancelOrder': true,
'cancelOrders': undefined,
'createDepositAddress': undefined,
'createLimitOrder': undefined,
'createMarketOrder': undefined,
'createOrder': true,
'createReduceOnlyOrder': true,
'deposit': undefined,
'editOrder': undefined,
'fetchAccounts': true,
'fetchBalance': true,
'fetchBidsAsks': true,
'fetchBorrowRate': undefined,
'fetchBorrowRateHistory': undefined,
'fetchBorrowRates': undefined,
'fetchBorrowRatesPerSymbol': undefined,
'fetchCanceledOrders': true,
'fetchClosedOrder': undefined,
'fetchClosedOrders': true,
'fetchCurrencies': true,
'fetchDeposit': undefined,
'fetchDepositAddress': true,
'fetchDepositAddresses': undefined,
'fetchDepositAddressesByNetwork': true,
'fetchDeposits': true,
'fetchDepositWithdrawFee': 'emulated',
'fetchDepositWithdrawFees': true,
'fetchFundingHistory': true,
'fetchFundingRate': true,
'fetchFundingRateHistory': true,
'fetchFundingRates': undefined,
'fetchIndexOHLCV': true,
'fetchL2OrderBook': true,
'fetchLedger': undefined,
'fetchLedgerEntry': undefined,
'fetchLeverageTiers': true,
'fetchMarginMode': false,
'fetchMarketLeverageTiers': undefined,
'fetchMarkets': true,
'fetchMarkOHLCV': true,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenOrder': undefined,
'fetchOpenOrders': true,
'fetchOrder': true,
'fetchOrderBook': true,
'fetchOrderBooks': undefined,
'fetchOrders': true,
'fetchOrderTrades': true,
'fetchPosition': true,
'fetchPositionMode': true,
'fetchPositions': true,
'fetchPositionsRisk': undefined,
'fetchPremiumIndexOHLCV': false,
'fetchStatus': true,
'fetchTicker': true,
'fetchTickers': true,
'fetchTime': true,
'fetchTrades': true,
'fetchTradingFee': undefined,
'fetchTradingFees': true,
'fetchTradingLimits': undefined,
'fetchTransactionFee': 'emulated',
'fetchTransactionFees': true,
'fetchTransactions': undefined,
'fetchTransfer': true,
'fetchTransfers': true,
'fetchWithdrawal': undefined,
'fetchWithdrawals': true,
'privateAPI': true,
'publicAPI': true,
'reduceMargin': true,
'repayMargin': true,
'setLeverage': true,
'setMarginMode': undefined,
'setPositionMode': true,
'signIn': undefined,
'transfer': undefined,
'withdraw': true,
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/137283979-8b2a818d-8633-461b-bfca-de89e8c446b2.jpg',
'api': {
'spot': {
'public': 'https://api.mexc.com',
'private': 'https://api.mexc.com',
},
'spot2': {
'public': 'https://www.mexc.com/open/api/v2',
'private': 'https://www.mexc.com/open/api/v2',
},
'contract': {
'public': 'https://contract.mexc.com/api/v1/contract',
'private': 'https://contract.mexc.com/api/v1/private',
},
},
'www': 'https://www.mexc.com/',
'doc': [
'https://mxcdevelop.github.io/apidocs/spot_v3_en/',
'https://mxcdevelop.github.io/APIDoc/', // v1 & v2 : soon to be deprecated
],
'fees': [
'https://www.mexc.com/fee',
],
'referral': 'https://m.mexc.com/auth/signup?inviteCode=1FQ1G',
},
'api': {
'spot': {
'public': {
'get': {
'ping': 1,
'time': 1,
'exchangeInfo': 1,
'depth': 1,
'trades': 1,
'historicalTrades': 1,
'aggTrades': 1,
'klines': 1,
'avgPrice': 1,
'ticker/24hr': 1,
'ticker/price': 1,
'ticker/bookTicker': 1,
'etf/info': 1,
},
},
'private': {
'get': {
'order': 1,
'openOrders': 1,
'allOrders': 1,
'account': 1,
'myTrades': 1,
'sub-account/list': 1,
'sub-account/apiKey': 1,
'capital/config/getall': 1,
'capital/deposit/hisrec': 1,
'capital/withdraw/history': 1,
'capital/deposit/address': 1,
'capital/transfer': 1,
'capital/sub-account/universalTransfer': 1,
'margin/loan': 1,
'margin/allOrders': 1,
'margin/myTrades': 1,
'margin/openOrders': 1,
'margin/maxTransferable': 1,
'margin/priceIndex': 1,
'margin/order': 1,
'margin/isolated/account': 1,
'margin/maxBorrowable': 1,
'margin/repay': 1,
'margin/isolated/pair': 1,
'margin/forceLiquidationRec': 1,
'margin/isolatedMarginData': 1,
'margin/isolatedMarginTier': 1,
'rebate/taxQuery': 1,
'rebate/detail': 1,
'rebate/detail/kickback': 1,
'rebate/referCode': 1,
'mxDeduct/enable': 1,
},
'post': {
'order': 1,
'order/test': 1,
'sub-account/virtualSubAccount': 1,
'sub-account/apiKey': 1,
'sub-account/futures': 1,
'sub-account/margin': 1,
'batchOrders': 1,
'capital/withdraw/apply': 1,
'capital/transfer': 1,
'capital/deposit/address': 1,
'capital/sub-account/universalTransfer': 1,
'margin/tradeMode': 1,
'margin/order': 1,
'margin/loan': 1,
'margin/repay': 1,
'mxDeduct/enable': 1,
},
'delete': {
'order': 1,
'openOrders': 1,
'sub-account/apiKey': 1,
'margin/order': 1,
'margin/openOrders': 1,
},
},
},
'contract': {
'public': {
'get': {
'ping': 2,
'detail': 2,
'support_currencies': 2, // TODO: should we implement 'fetchCurrencies' solely for swap? because spot doesnt have it atm
'depth/{symbol}': 2,
'depth_commits/{symbol}/{limit}': 2,
'index_price/{symbol}': 2,
'fair_price/{symbol}': 2,
'funding_rate/{symbol}': 2,
'kline/{symbol}': 2,
'kline/index_price/{symbol}': 2,
'kline/fair_price/{symbol}': 2,
'deals/{symbol}': 2,
'ticker': 2,
'risk_reverse': 2,
'risk_reverse/history': 2,
'funding_rate/history': 2,
},
},
'private': {
'get': {
'account/assets': 2,
'account/asset/{currency}': 2,
'account/transfer_record': 2,
'position/list/history_positions': 2,
'position/open_positions': 2,
'position/funding_records': 2,
'position/position_mode': 2,
'order/list/open_orders/{symbol}': 2,
'order/list/history_orders': 2,
'order/external/{symbol}/{external_oid}': 2,
'order/get/{order_id}': 2,
'order/batch_query': 8,
'order/deal_details/{order_id}': 2,
'order/list/order_deals': 2,
'planorder/list/orders': 2,
'stoporder/list/orders': 2,
'stoporder/order_details/{stop_order_id}': 2,
'account/risk_limit': 2, // TO_DO: gets max/min position size, allowed sides, leverage, maintenance margin, initial margin, etc...
'account/tiered_fee_rate': 2, // TO_DO: taker/maker fees for account
'position/leverage': 2,
},
'post': {
'position/change_margin': 2,
'position/change_leverage': 2,
'position/change_position_mode': 2,
'order/submit': 2,
'order/submit_batch': 40,
'order/cancel': 2,
'order/cancel_with_external': 2,
'order/cancel_all': 2,
'account/change_risk_level': 2,
'planorder/place': 2,
'planorder/cancel': 2,
'planorder/cancel_all': 2,
'stoporder/cancel': 2,
'stoporder/cancel_all': 2,
'stoporder/change_price': 2,
'stoporder/change_plan_price': 2,
},
},
},
'spot2': {
'public': {
'get': {
'market/symbols': 1,
'market/coin/list': 2,
'common/timestamp': 1,
'common/ping': 1,
'market/ticker': 1,
'market/depth': 1,
'market/deals': 1,
'market/kline': 1,
'market/api_default_symbols': 2,
},
},
'private': {
'get': {
'account/info': 1,
'order/open_orders': 1,
'order/list': 1,
'order/query': 1,
'order/deals': 1,
'order/deal_detail': 1,
'asset/deposit/address/list': 2,
'asset/deposit/list': 2,
'asset/address/list': 2,
'asset/withdraw/list': 2,
'asset/internal/transfer/record': 10,
'account/balance': 10,
'asset/internal/transfer/info': 10,
'market/api_symbols': 2,
},
'post': {
'order/place': 1,
'order/place_batch': 1,
'order/advanced/place_batch': 1,
'asset/withdraw': 2,
'asset/internal/transfer': 10,
},
'delete': {
'order/cancel': 1,
'order/cancel_by_symbol': 1,
'asset/withdraw': 2,
},
},
},
},
'precisionMode': TICK_SIZE,
'timeframes': {
'1m': '1m', // spot, swap
'3m': '3m', // spot
'5m': '5m', // spot, swap
'15m': '15m', // spot, swap
'30m': '30m', // spot, swap
'1h': '1h', // spot, swap
'2h': '2h', // spot
'4h': '4h', // spot, swap
'6h': '6h', // spot
'8h': '8h', // spot, swap
'12h': '12h', // spot
'1d': '1d', // spot, swap
'3d': '3d', // spot
'1w': '1w', // spot, swap
'1M': '1M', // spot, swap
},
'fees': {
'trading': {
'tierBased': false,
'percentage': true,
'maker': this.parseNumber ('0.002'), // maker / taker
'taker': this.parseNumber ('0.002'),
},
},
'options': {
'createMarketBuyOrderRequiresPrice': true,
'unavailableContracts': {
'BTC/USDT:USDT': true,
'LTC/USDT:USDT': true,
'ETH/USDT:USDT': true,
},
'fetchMarkets': {
'types': {
'spot': true,
'future': {
'linear': false,
'inverse': false,
},
'swap': {
'linear': true,
'inverse': false,
},
},
},
'timeframes': {
'spot': {
'1m': '1m',
'3m': '3m',
'5m': '5m',
'15m': '15m',
'30m': '30m',
'1h': '1h',
'2h': '2h',
'4h': '4h',
'6h': '6h',
'8h': '8h',
'12h': '12h',
'1d': '1d',
'3d': '3d',
'1w': '1w',
'1M': '1M',
},
'swap': {
'1m': 'Min1',
'5m': 'Min5',
'15m': 'Min15',
'30m': 'Min30',
'1h': 'Min60',
'4h': 'Hour4',
'8h': 'Hour8',
'1d': 'Day1',
'1w': 'Week1',
'1M': 'Month1',
},
},
'defaultType': 'spot', // spot, swap
'networks': {
'TRX': 'TRC20',
'ETH': 'ERC20',
'BEP20': 'BEP20(BSC)',
'BSC': 'BEP20(BSC)',
},
'networksById': {
'BEP20(BSC)': 'BSC',
},
'networkAliases': {
'BSC(BEP20)': 'BSC',
},
'recvWindow': 5 * 1000, // 5 sec, default
'maxTimeTillEnd': 90 * 86400 * 1000 - 1, // 90 days
'broker': 'CCXT',
},
'commonCurrencies': {
'BEYONDPROTOCOL': 'BEYOND',
'BIFI': 'BIFIF',
'BYN': 'BeyondFi',
'COFI': 'COFIX', // conflict with CoinFi
'DFI': 'DfiStarter',
'DFT': 'dFuture',
'DRK': 'DRK',
'EGC': 'Egoras Credit',
'FLUX1': 'FLUX', // switched places
'FLUX': 'FLUX1', // switched places
'FREE': 'FreeRossDAO', // conflict with FREE Coin
'GMT': 'GMT Token', // Conflict with GMT (STEPN)
'STEPN': 'GMT', // Conflict with GMT Token
'HERO': 'Step Hero', // conflict with Metahero
'MIMO': 'Mimosa',
'PROS': 'Pros.Finance', // conflict with Prosper
'SIN': 'Sin City Token',
'SOUL': 'Soul Swap',
},
'exceptions': {
'exact': {
// until mexc migrates fully to v3, it might be worth to note the version & market aside errors, not easily remove obsolete version's exceptions in future
'-1128': BadRequest,
'-2011': BadRequest,
'-1121': BadSymbol,
'10101': InsufficientFunds, // {"msg":"资金不足","code":10101}
'2009': InvalidOrder, // {"success":false,"code":2009,"message":"Position is not exists or closed."}
'2011': BadRequest,
'30004': InsufficientFunds,
'33333': BadRequest, // {"msg":"Not support transfer","code":33333}
'44444': BadRequest,
'1002': InvalidOrder,
'30019': BadRequest,
'30005': InvalidOrder,
'2003': InvalidOrder,
'2005': InsufficientFunds,
'600': BadRequest,
'70011': PermissionDenied, // {"code":70011,"msg":"Pair user ban trade apikey."}
'88004': InsufficientFunds, // {"msg":"超出最大可借,最大可借币为:18.09833211","code":88004}
'88009': ExchangeError, // v3 {"msg":"Loan record does not exist","code":88009}
'88013': InvalidOrder, // {"msg":"最小交易额不能小于:5USDT","code":88013}
'88015': InsufficientFunds, // {"msg":"持仓不足","code":88015}
'700003': InvalidNonce, // {"code":700003,"msg":"Timestamp for this request is outside of the recvWindow."}
},
'broad': {
'Order quantity error, please try to modify.': BadRequest, // code:2011
'Combination of optional parameters invalid': BadRequest, // code:-2011
'api market order is disabled': BadRequest, //
'Contract not allow place order!': InvalidOrder, // code:1002
'Oversold': InvalidOrder, // code:30005
'Insufficient position': InsufficientFunds, // code:30004
'Insufficient balance!': InsufficientFunds, // code:2005
'Bid price is great than max allow price': InvalidOrder, // code:2003
'Invalid symbol.': BadSymbol, // code:-1121
'Param error!': BadRequest, // code:600
},
},
});
}
async fetchStatus (params = {}) {
/**
* @method
* @name mexc3#fetchStatus
* @description the latest known information on the availability of the exchange API
* @param {object} params extra parameters specific to the mexc3 api endpoint
* @returns {object} a [status structure]{@link https://docs.ccxt.com/en/latest/manual.html#exchange-status-structure}
*/
const [ marketType, query ] = this.handleMarketTypeAndParams ('fetchStatus', undefined, params);
let response = undefined;
let status = undefined;
let updated = undefined;
if (marketType === 'spot') {
response = await this.spotPublicGetPing (query);
//
// {}
//
status = Object.keys (response).length ? this.json (response) : 'ok';
} else if (marketType === 'swap') {
response = await this.contractPublicGetPing (query);
//
// {"success":true,"code":"0","data":"1648124374985"}
//
status = this.safeValue (response, 'success') ? 'ok' : this.json (response);
updated = this.safeInteger (response, 'data');
}
return {
'status': status,
'updated': updated,
'url': undefined,
'eta': undefined,
'info': response,
};
}
async fetchTime (params = {}) {
/**
* @method
* @name mexc3#fetchTime
* @description fetches the current integer timestamp in milliseconds from the exchange server
* @param {object} params extra parameters specific to the mexc3 api endpoint
* @returns {int} the current integer timestamp in milliseconds from the exchange server
*/
const [ marketType, query ] = this.handleMarketTypeAndParams ('fetchTime', undefined, params);
let response = undefined;
if (marketType === 'spot') {
response = await this.spotPublicGetTime (query);
//
// {"serverTime": "1647519277579"}
//
return this.safeInteger (response, 'serverTime');
} else if (marketType === 'swap') {
response = await this.contractPublicGetPing (query);
//
// {"success":true,"code":"0","data":"1648124374985"}
//
return this.safeInteger (response, 'data');
}
}
async fetchCurrencies (params = {}) {
/**
* @method
* @name mexc3#fetchCurrencies
* @description fetches all available currencies on an exchange
* @see https://mxcdevelop.github.io/apidocs/spot_v3_en/#query-the-currency-information
* @param {object} params extra parameters specific to the mexc3 api endpoint
* @returns {object} an associative dictionary of currencies
*/
// this endpoint requires authentication
// while fetchCurrencies is a public API method by design
// therefore we check the keys here
// and fallback to generating the currencies from the markets
if (!this.checkRequiredCredentials (false)) {
return undefined;
}
const response = await this.spotPrivateGetCapitalConfigGetall (params);
//
// {
// coin: 'QANX',
// name: 'QANplatform',
// networkList: [
// {
// coin: 'QANX',
// depositDesc: null,
// depositEnable: true,
// minConfirm: '0',
// name: 'QANplatform',
// network: 'BEP20(BSC)',
// withdrawEnable: false,
// withdrawFee: '42.000000000000000000',
// withdrawIntegerMultiple: null,
// withdrawMax: '24000000.000000000000000000',
// withdrawMin: '20.000000000000000000',
// sameAddress: false,
// contract: '0xAAA7A10a8ee237ea61E8AC46C50A8Db8bCC1baaa'
// },
// {
// coin: 'QANX',
// depositDesc: null,
// depositEnable: true,
// minConfirm: '0',
// name: 'QANplatform',
// network: 'ERC20',
// withdrawEnable: true,
// withdrawFee: '2732.000000000000000000',
// withdrawIntegerMultiple: null,
// withdrawMax: '24000000.000000000000000000',
// withdrawMin: '240.000000000000000000',
// sameAddress: false,
// contract: '0xAAA7A10a8ee237ea61E8AC46C50A8Db8bCC1baaa'
// }
// ]
// }
//
const result = {};
for (let i = 0; i < response.length; i++) {
const currency = response[i];
const id = this.safeString (currency, 'coin');
const code = this.safeCurrencyCode (id);
const name = this.safeString (currency, 'name');
let currencyActive = false;
let currencyFee = undefined;
let currencyWithdrawMin = undefined;
let currencyWithdrawMax = undefined;
let depositEnabled = false;
let withdrawEnabled = false;
const networks = {};
const chains = this.safeValue (currency, 'networkList', []);
for (let j = 0; j < chains.length; j++) {
const chain = chains[j];
const networkId = this.safeString (chain, 'network');
const network = this.safeNetwork (networkId);
const isDepositEnabled = this.safeValue (chain, 'depositEnable', false);
const isWithdrawEnabled = this.safeValue (chain, 'withdrawEnable', false);
const active = (isDepositEnabled && isWithdrawEnabled);
currencyActive = active || currencyActive;
const withdrawMin = this.safeString (chain, 'withdrawMin');
const withdrawMax = this.safeString (chain, 'withdrawMax');
currencyWithdrawMin = (currencyWithdrawMin === undefined) ? withdrawMin : currencyWithdrawMin;
currencyWithdrawMax = (currencyWithdrawMax === undefined) ? withdrawMax : currencyWithdrawMax;
const fee = this.safeNumber (chain, 'withdrawFee');
currencyFee = (currencyFee === undefined) ? fee : currencyFee;
if (Precise.stringGt (currencyWithdrawMin, withdrawMin)) {
currencyWithdrawMin = withdrawMin;
}
if (Precise.stringLt (currencyWithdrawMax, withdrawMax)) {
currencyWithdrawMax = withdrawMax;
}
if (isDepositEnabled) {
depositEnabled = true;
}
if (isWithdrawEnabled) {
withdrawEnabled = true;
}
networks[network] = {
'info': chain,
'id': networkId,
'network': network,
'active': active,
'deposit': isDepositEnabled,
'withdraw': isWithdrawEnabled,
'fee': this.safeNumber (chain, 'fee'),
'precision': undefined,
'limits': {
'withdraw': {
'min': withdrawMin,
'max': withdrawMax,
},
},
};
}
const networkKeys = Object.keys (networks);
const networkKeysLength = networkKeys.length;
if ((networkKeysLength === 1) || ('NONE' in networks)) {
const defaultNetwork = this.safeValue2 (networks, 'NONE', networkKeysLength - 1);
if (defaultNetwork !== undefined) {
currencyFee = defaultNetwork['fee'];
}
}
result[code] = {
'info': currency,
'id': id,
'code': code,
'name': name,
'active': currencyActive,
'deposit': depositEnabled,
'withdraw': withdrawEnabled,
'fee': currencyFee,
'precision': undefined,
'limits': {
'amount': {
'min': undefined,
'max': undefined,
},
'withdraw': {
'min': currencyWithdrawMin,
'max': currencyWithdrawMax,
},
},
'networks': networks,
};
}
return result;
}
safeNetwork (networkId) {
if (networkId.indexOf ('BSC') >= 0) {
return 'BEP20';
}
const parts = networkId.split (' ');
networkId = parts.join ('');
networkId = networkId.replace ('-20', '20');
const networksById = {
'ETH': 'ETH',
'ERC20': 'ERC20',
'BEP20(BSC)': 'BEP20',
'TRX': 'TRC20',
};
return this.safeString (networksById, networkId, networkId);
}
async fetchMarkets (params = {}) {
/**
* @method
* @name mexc3#fetchMarkets
* @description retrieves data on all markets for mexc3
* @param {object} params extra parameters specific to the exchange api endpoint
* @returns {[object]} an array of objects representing market data
*/
const spotMarket = await this.fetchSpotMarkets (params);
const swapMarket = await this.fetchSwapMarkets (params);
return this.arrayConcat (spotMarket, swapMarket);
}
async fetchSpotMarkets (params = {}) {
const response = await this.spotPublicGetExchangeInfo (params);
//
// {
// "timezone": "CST",
// "serverTime": 1647521860402,
// "rateLimits": [],
// "exchangeFilters": [],
// "symbols": [
// {
// "symbol": "OGNUSDT",
// "status": "ENABLED",
// "baseAsset": "OGN",
// "baseAssetPrecision": "2",
// "quoteAsset": "USDT",
// "quoteAssetPrecision": "4",
// "orderTypes": [
// "LIMIT",
// "LIMIT_MAKER"
// ],
// "baseCommissionPrecision": "2",
// "quoteCommissionPrecision": "4",
// "quoteOrderQtyMarketAllowed": false,
// "isSpotTradingAllowed": true,
// "isMarginTradingAllowed": true,
// "permissions": [
// "SPOT",
// "MARGIN"
// ],
// "filters": [],
// "baseSizePrecision": "0.01", // this turned out to be a minimum base amount for order
// "maxQuoteAmount": "5000000",
// "makerCommission": "0.002",
// "takerCommission": "0.002"
// "quoteAmountPrecision": "5", // this turned out to be a minimum cost amount for order
// "quotePrecision": "4", // deprecated in favor of 'quoteAssetPrecision' ( https://dev.binance.vision/t/what-is-the-difference-between-quoteprecision-and-quoteassetprecision/4333 )
// // note, "icebergAllowed" & "ocoAllowed" fields were recently removed
// },
// ]
// }
//
// Notes:
// - 'quoteAssetPrecision' & 'baseAssetPrecision' are not currency's real blockchain precision (to view currency's actual individual precision, refer to fetchCurrencies() method).
//
const data = this.safeValue (response, 'symbols', []);
const result = [];
for (let i = 0; i < data.length; i++) {
const market = data[i];
const id = this.safeString (market, 'symbol');
const baseId = this.safeString (market, 'baseAsset');
const quoteId = this.safeString (market, 'quoteAsset');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const status = this.safeString (market, 'status');
const isSpotTradingAllowed = this.safeValue (market, 'isSpotTradingAllowed');
let active = false;
if ((status === 'ENABLED') && (isSpotTradingAllowed)) {
active = true;
}
const isMarginTradingAllowed = this.safeValue (market, 'isMarginTradingAllowed');
const makerCommission = this.safeNumber (market, 'makerCommission');
const takerCommission = this.safeNumber (market, 'takerCommission');
const maxQuoteAmount = this.safeNumber (market, 'maxQuoteAmount');
result.push ({
'id': id,
'symbol': base + '/' + quote,
'base': base,
'quote': quote,
'settle': undefined,
'baseId': baseId,
'quoteId': quoteId,
'settleId': undefined,
'type': 'spot',
'spot': true,
'margin': isMarginTradingAllowed,
'swap': false,
'future': false,
'option': false,
'active': active,
'contract': false,
'linear': undefined,
'inverse': undefined,
'taker': takerCommission,
'maker': makerCommission,
'contractSize': undefined,
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'precision': {
'amount': this.parseNumber (this.parsePrecision (this.safeString (market, 'baseAssetPrecision'))),
'price': this.parseNumber (this.parsePrecision (this.safeString (market, 'quoteAssetPrecision'))),
},
'limits': {
'leverage': {
'min': undefined,
'max': undefined,
},
'amount': {
'min': this.safeNumber (market, 'baseSizePrecision'),
'max': undefined,
},
'price': {
'min': undefined,
'max': undefined,
},
'cost': {
'min': this.safeNumber (market, 'quoteAmountPrecision'),
'max': maxQuoteAmount,
},
},
'info': market,
});
}
return result;
}
async fetchSwapMarkets (params = {}) {
const response = await this.contractPublicGetDetail (params);
//
// {
// "success":true,
// "code":0,
// "data":[
// {
// "symbol":"BTC_USDT",
// "displayName":"BTC_USDT永续",
// "displayNameEn":"BTC_USDT SWAP",
// "positionOpenType":3,
// "baseCoin":"BTC",
// "quoteCoin":"USDT",
// "settleCoin":"USDT",
// "contractSize":0.0001,
// "minLeverage":1,
// "maxLeverage":125,
// "priceScale":2, // seems useless atm, as it's just how UI shows the price, i.e. 29583.50 for BTC/USDT:USDT, while price ticksize is 0.5
// "volScale":0, // probably: contract amount precision
// "amountScale":4, // probably: quote currency precision
// "priceUnit":0.5, // price tick size
// "volUnit":1, // probably: contract tick size
// "minVol":1,
// "maxVol":1000000,
// "bidLimitPriceRate":0.1,
// "askLimitPriceRate":0.1,
// "takerFeeRate":0.0006,
// "makerFeeRate":0.0002,
// "maintenanceMarginRate":0.004,
// "initialMarginRate":0.008,
// "riskBaseVol":10000,
// "riskIncrVol":200000,
// "riskIncrMmr":0.004,
// "riskIncrImr":0.004,
// "riskLevelLimit":5,
// "priceCoefficientVariation":0.1,
// "indexOrigin":["BINANCE","GATEIO","HUOBI","MXC"],
// "state":0, // 0 enabled, 1 delivery, 2 completed, 3 offline, 4 pause
// "isNew":false,
// "isHot":true,
// "isHidden":false
// },
// ]
// }
//
const data = this.safeValue (response, 'data', []);
const result = [];
for (let i = 0; i < data.length; i++) {
const market = data[i];
const id = this.safeString (market, 'symbol');
const baseId = this.safeString (market, 'baseCoin');
const quoteId = this.safeString (market, 'quoteCoin');
const settleId = this.safeString (market, 'settleCoin');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const settle = this.safeCurrencyCode (settleId);
const state = this.safeString (market, 'state');
result.push ({
'id': id,
'symbol': base + '/' + quote + ':' + settle,
'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': (state === '0'),
'contract': true,
'linear': true,
'inverse': false,
'taker': this.safeNumber (market, 'takerFeeRate'),
'maker': this.safeNumber (market, 'makerFeeRate'),
'contractSize': this.safeNumber (market, 'contractSize'),
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'precision': {
'amount': this.safeNumber (market, 'volUnit'),
'price': this.safeNumber (market, 'priceUnit'),
},
'limits': {
'leverage': {
'min': this.safeNumber (market, 'minLeverage'),
'max': this.safeNumber (market, 'maxLeverage'),
},
'amount': {
'min': this.safeNumber (market, 'minVol'),
'max': this.safeNumber (market, 'maxVol'),
},
'price': {
'min': undefined,
'max': undefined,
},
'cost': {
'min': undefined,
'max': undefined,
},
},
'info': market,
});
}
return result;
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
/**
* @method
* @name mexc3#fetchOrderBook
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @param {string} 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 {object} params extra parameters specific to the mexc3 api endpoint
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/en/latest/manual.html#order-book-structure} indexed by market symbols
*/
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'symbol': market['id'],
};
if (limit !== undefined) {
request['limit'] = limit;
}
let orderbook = undefined;
if (market['spot']) {
const response = await this.spotPublicGetDepth (this.extend (request, params));
//
// {
// "lastUpdateId": "744267132",
// "bids": [
// ["40838.50","0.387864"],
// ["40837.95","0.008400"],
// ],
// "asks": [
// ["40838.61","6.544908"],
// ["40838.88","0.498000"],
// ]
// }
//
orderbook = this.parseOrderBook (response, symbol);
orderbook['nonce'] = this.safeInteger (response, 'lastUpdateId');
} else if (market['swap']) {
const response = await this.contractPublicGetDepthSymbol (this.extend (request, params));
//
// {
// "success":true,
// "code":0,
// "data":{
// "asks":[
// [3445.72,48379,1],
// [3445.75,34994,1],
// ],
// "bids":[
// [3445.55,44081,1],
// [3445.51,24857,1],
// ],
// "version":2827730444,
// "timestamp":1634117846232
// }
// }
//
const data = this.safeValue (response, 'data');
const timestamp = this.safeInteger (data, 'timestamp');
orderbook = this.parseOrderBook (data, symbol, timestamp);
orderbook['nonce'] = this.safeInteger (data, 'version');
}
return orderbook;
}
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name mexc3#fetchTrades
* @description get the list of most recent trades for a particular symbol
* @param {string} 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 {object} params extra parameters specific to the mexc3 api endpoint
* @returns {[object]} 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 = {
'symbol': market['id'],
};
if (limit !== undefined) {
request['limit'] = limit;
}
// if (since !== undefined) {
// request['startTime'] = since; bug in api, waiting for fix
// }
let trades = undefined;
if (market['spot']) {
let method = this.safeString (this.options, 'fetchTradesMethod', 'spotPublicGetAggTrades');
method = this.safeString (params, 'method', method); // AggTrades, HistoricalTrades, Trades
trades = await this[method] (this.extend (request, params));
//
// /trades, /historicalTrades
//
// [
// {
// "id": null,
// "price": "40798.94",
// "qty": "0.000508",
// "quoteQty": "20.72586152",
// "time": "1647546934374",
// "isBuyerMaker": true,
// "isBestMatch": true
// },
// ]
//
// /aggrTrades
//
// [
// {
// "a": null,
// "f": null,
// "l": null,
// "p": "40679",
// "q": "0.001309",
// "T": 1647551328000,
// "m": true,
// "M": true
// },
// ]
//
} else if (market['swap']) {
const response = await this.contractPublicGetDealsSymbol (this.extend (request, params));
//
// {
// "success": true,
// "code": 0,
// "data": [
// {
// "p": 31199,
// "v": 18,
// "T": 1,
// "O": 3,
// "M": 2,
// "t": 1609831235985
// },
// ]
// }
//
trades = this.safeValue (response, 'data');
}
return this.parseTrades (trades, market, since, limit);
}
parseTrade (trade, market = undefined) {
let id = undefined;
let timestamp = undefined;
let orderId = undefined;
let symbol = undefined;
let fee = undefined;
const type = undefined;
let side = undefined;
let takerOrMaker = undefined;
let priceString = undefined;
let amountString = undefined;
let costString = undefined;
// if swap
if ('v' in trade) {
//
// swap: fetchTrades
//
// {
// "p": 31199,
// "v": 18,
// "T": 1,
// "O": 3,
// "M": 2,
// "t": 1609831235985
// }
//
timestamp = this.safeInteger (trade, 't');
market = this.safeMarket (undefined, market);
symbol = market['symbol'];
priceString = this.safeString (trade, 'p');
amountString = this.safeString (trade, 'v');
side = this.parseOrderSide (this.safeString (trade, 'T'));
takerOrMaker = 'taker';
} else {
//
// spot: fetchTrades (for aggTrades)
//
// {
// "a": null,
// "f": null,
// "l": null,
// "p": "40679",
// "q": "0.001309",
// "T": 1647551328000,
// "m": true,
// "M": true
// }
//
// spot: fetchMyTrades, fetchOrderTrades
//
// {
// "symbol": "BTCUSDT",
// "id": "133948532984922113",
// "orderId": "133948532531949568",
// "orderListId": "-1",
//