UNPKG

sfccxt

Version:

A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges

1,145 lines (1,131 loc) 215 kB
'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", //