UNPKG

sfccxt

Version:

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

1,316 lines (1,280 loc) 47.9 kB
'use strict'; // --------------------------------------------------------------------------- const mexcRest = require ('../mexc.js'); const { AuthenticationError, BadSymbol, BadRequest, NotSupported } = require ('../base/errors'); const { ArrayCache, ArrayCacheBySymbolById, ArrayCacheByTimestamp } = require ('./base/Cache'); // --------------------------------------------------------------------------- module.exports = class mexc extends mexcRest { describe () { return this.deepExtend (super.describe (), { 'has': { 'ws': true, 'watchBalance': true, 'watchMyTrades': true, 'watchOHLCV': true, 'watchOrderBook': true, 'watchOrders': true, 'watchTicker': true, 'watchTickers': false, // for now 'watchTrades': true, }, 'urls': { 'api': { 'ws': { 'spot': 'wss://wbs.mexc.com/raw/ws', 'swap': 'wss://contract.mexc.com/ws', }, }, }, 'options': { 'timeframes': { '1m': 'Min1', '5m': 'Min5', '15m': 'Min15', '30m': 'Min30', '1h': 'Min60', '4h': 'Hour4', '8h': 'Hour8', '1d': 'Day1', '1w': 'Week1', '1M': 'Month1', }, }, 'streaming': { 'ping': this.ping, 'keepAlive': 10000, }, 'exceptions': { 'ws': { 'exact': { 'signature validation failed': AuthenticationError, // { channel: 'sub.personal', msg: 'signature validation failed'} }, 'broad': { 'Contract not exists': BadSymbol, // { channel: 'rs.error', data: 'Contract not exists', ts: 1651509181535} }, }, }, }); } async watchTicker (symbol, params = {}) { /** * @method * @name mexc#watchTicker * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market * @param {string} symbol unified symbol of the market to fetch the ticker for * @param {object} params extra parameters specific to the mexc api endpoint * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/en/latest/manual.html#ticker-structure} */ await this.loadMarkets (); const market = this.market (symbol); symbol = market['symbol']; const channel = 'sub.ticker'; const messageHash = 'ticker' + ':' + symbol; const requestParams = { 'symbol': market['id'], }; if (market['type'] === 'spot') { throw new NotSupported (this.id + ' watchTicker does not support spot markets'); } else { return await this.watchSwapPublic (messageHash, channel, requestParams, params); } } handleTicker (client, message) { // // { // channel: 'push.ticker', // data: { // amount24: 491939387.90105, // ask1: 39530.5, // bid1: 39530, // contractId: 10, // fairPrice: 39533.4, // fundingRate: 0.00015, // high24Price: 40310.5, // holdVol: 187680157, // indexPrice: 39538.5, // lastPrice: 39530, // lower24Price: 38633, // maxBidPrice: 43492, // minAskPrice: 35584.5, // riseFallRate: 0.0138, // riseFallValue: 539.5, // symbol: 'BTC_USDT', // timestamp: 1651160401009, // volume24: 125171687 // }, // symbol: 'BTC_USDT', // ts: 1651160401009 // } // const data = this.safeValue (message, 'data', {}); const marketId = this.safeString (message, 'symbol'); const market = this.safeMarket (marketId); const symbol = market['symbol']; const ticker = this.parseTicker (data, market); this.tickers[symbol] = ticker; const messageHash = 'ticker:' + symbol; client.resolve (ticker, messageHash); return message; } async watchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { /** * @method * @name mexc#watchOHLCV * @description watches historical candlestick data containing the open, high, low, and close price, and the volume of a market * @param {string} symbol unified symbol of the market to fetch OHLCV data for * @param {string} timeframe the length of time each candle represents * @param {int|undefined} since timestamp in ms of the earliest candle to fetch * @param {int|undefined} limit the maximum amount of candles to fetch * @param {object} params extra parameters specific to the mexc 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 requestParams = {}; symbol = market['symbol']; const type = market['type']; const timeframes = this.safeValue (this.options, 'timeframes', {}); const timeframeValue = this.safeString (timeframes, timeframe); const channel = 'sub.kline'; const messageHash = 'kline' + ':' + timeframeValue + ':' + symbol; requestParams['symbol'] = market['id']; requestParams['interval'] = timeframeValue; if (since !== undefined) { requestParams['start'] = since; } let ohlcv = undefined; if (type === 'spot') { ohlcv = await this.watchSpotPublic (messageHash, channel, requestParams, params); } else { ohlcv = await this.watchSwapPublic (messageHash, channel, requestParams, params); } if (this.newUpdates) { limit = ohlcv.getLimit (symbol, limit); } return this.filterBySinceLimit (ohlcv, since, limit, 0, true); } handleOHLCV (client, message) { // spot // { // symbol: 'BTC_USDT', // data: { // symbol: 'BTC_USDT', // interval: 'Min1', // t: 1651230720, // o: 38870.18, // c: 38867.55, // h: 38873.19, // l: 38867.05, // v: 71031.87886502, // q: 1.827357, // e: 38867.05, // rh: 38873.19, // rl: 38867.05 // }, // channel: 'push.kline', // symbol_display: 'BTC_USDT' // } // // swap // // { // channel: 'push.kline', // data: { // a: 325653.3287, // c: 38839, // h: 38909.5, // interval: 'Min1', // l: 38833, // o: 38901.5, // q: 83808, // rc: 38839, // rh: 38909.5, // rl: 38833, // ro: 38909.5, // symbol: 'BTC_USDT', // t: 1651230660 // }, // symbol: 'BTC_USDT', // ts: 1651230713067 // } // const marketId = this.safeString (message, 'symbol'); const market = this.safeMarket (marketId); const symbol = market['symbol']; const data = this.safeValue (message, 'data', {}); const interval = this.safeString (data, 'interval'); const messageHash = 'kline' + ':' + interval + ':' + symbol; const timeframes = this.safeValue (this.options, 'timeframes', {}); const timeframe = this.findTimeframe (interval, timeframes); const parsed = this.parseWsOHLCV (data, market); this.ohlcvs[symbol] = this.safeValue (this.ohlcvs, symbol, {}); let stored = this.safeValue (this.ohlcvs[symbol], timeframe); if (stored === undefined) { const limit = this.safeInteger (this.options, 'OHLCVLimit', 1000); stored = new ArrayCacheByTimestamp (limit); this.ohlcvs[symbol][timeframe] = stored; } stored.append (parsed); client.resolve (stored, messageHash); return message; } parseWsOHLCV (ohlcv, market = undefined) { // // spot // { // symbol: 'BTC_USDT', // interval: 'Min1', // t: 1651230720, // o: 38870.18, // c: 38867.55, // h: 38873.19, // l: 38867.05, // v: 71031.87886502, // q: 1.827357, // e: 38867.05, // rh: 38873.19, // rl: 38867.05 // } // // swap // // { // a: 325653.3287, // c: 38839, // h: 38909.5, // interval: 'Min1', // l: 38833, // o: 38901.5, // q: 83808, // rc: 38839, // rh: 38909.5, // rl: 38833, // ro: 38909.5, // symbol: 'BTC_USDT', // t: 1651230660 // } // return [ this.safeIntegerProduct (ohlcv, 't', 1000), this.safeNumber (ohlcv, 'o'), this.safeNumber (ohlcv, 'h'), this.safeNumber (ohlcv, 'l'), this.safeNumber (ohlcv, 'c'), this.safeNumber2 (ohlcv, 'v', 'q'), ]; } async watchOrderBook (symbol, limit = undefined, params = {}) { /** * @method * @name mexc#watchOrderBook * @description watches 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 mexc 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); symbol = market['symbol']; const messageHash = 'orderbook' + ':' + symbol; const requestParams = { 'symbol': market['id'], }; if (limit !== undefined) { if (limit !== 5 && limit !== 10 && limit !== 20) { throw new BadRequest (this.id + ' watchOrderBook limit parameter cannot be different from 5, 10 or 20'); } } else { limit = 20; } let orderbook = undefined; if (market['type'] === 'swap') { const channel = 'sub.depth'; requestParams['compress'] = true; requestParams['limit'] = limit; orderbook = await this.watchSwapPublic (messageHash, channel, requestParams, params); } else { const channel = 'sub.limit.depth'; requestParams['depth'] = limit; orderbook = await this.watchSpotPublic (messageHash, channel, requestParams, params); } return orderbook.limit (); } handleOrderBook (client, message) { // // swap // { // "channel":"push.depth", // "data":{ // "asks":[ // [ // 39146.5, // 11264, // 1 // ] // ], // "bids":[ // [ // 39144, // 35460, // 1 // ] // ], // "end":4895965272, // "begin":4895965271 // }, // "symbol":"BTC_USDT", // "ts":1651239652372 // } // // spot // { // "channel":"push.limit.depth", // "symbol":"BTC_USDT", // "data":{ // "asks":[ // [ // "38694.68", // "2.250996" // ], // ], // "bids":[ // [ // "38694.65", // "0.783084" // ], // ] // }, // "depth":5, // "version":"1170951528" // } // const marketId = this.safeString (message, 'symbol'); const market = this.safeMarket (marketId); const symbol = market['symbol']; const data = this.safeValue (message, 'data'); const timestamp = this.safeInteger (message, 'ts'); const snapshot = this.parseOrderBook (data, symbol, timestamp); let nonce = this.safeNumber (data, 'end'); if (nonce === undefined) { nonce = this.safeNumber (message, 'version'); } snapshot['nonce'] = nonce; let orderbook = this.safeValue (this.orderbooks, symbol); if (orderbook === undefined) { orderbook = this.orderBook (snapshot); this.orderbooks[symbol] = orderbook; } else { // spot channels always return entire snapshots // whereas swap channels return incremental updates // after the first message if (market['type'] === 'spot') { orderbook.reset (snapshot); } else { this.handleOrderBookMessage (client, message, orderbook); } } const messageHash = 'orderbook' + ':' + symbol; client.resolve (orderbook, messageHash); } handleOrderBookMessage (client, message, orderbook) { // // { // "channel":"push.depth", // "data":{ // "asks":[ // [ // 39146.5, // 11264, // 1 // ] // ], // "bids":[ // [ // 39144, // 35460, // 1 // ] // ], // "end":4895965272, // "begin":4895965271 // }, // "symbol":"BTC_USDT", // "ts":1651239652372 // const data = this.safeValue (message, 'data', {}); const nonce = this.safeNumber (data, 'end'); const asks = this.safeValue (data, 'asks', []); const bids = this.safeValue (data, 'bids', []); this.handleDeltas (orderbook['asks'], asks); this.handleDeltas (orderbook['bids'], bids); const timestamp = this.safeInteger (message, 'ts'); const marketId = this.safeString (message, 'symbol'); const symbol = this.safeSymbol (marketId); orderbook['nonce'] = nonce; orderbook['symbol'] = symbol; orderbook['timestamp'] = timestamp; orderbook['datetime'] = this.iso8601 (timestamp); return orderbook; } handleDelta (bookside, delta) { // // [ // 39146.5, // 11264, // 1 // ] // const price = this.safeFloat (delta, 0); const amount = this.safeFloat (delta, 1); bookside.store (price, amount); } handleDeltas (bookside, deltas) { for (let i = 0; i < deltas.length; i++) { this.handleDelta (bookside, deltas[i]); } } async watchTrades (symbol, since = undefined, limit = undefined, params = {}) { /** * @method * @name mexc#watchTrades * @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 mexc 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); symbol = market['symbol']; const channel = 'sub.deal'; const messageHash = 'trades' + ':' + symbol; const requestParams = { 'symbol': market['id'], }; let trades = undefined; if (market['type'] === 'spot') { trades = await this.watchSpotPublic (messageHash, channel, requestParams, params); } else { trades = await this.watchSwapPublic (messageHash, channel, requestParams, params); } if (this.newUpdates) { limit = trades.getLimit (symbol, limit); } return this.filterBySinceLimit (trades, since, limit, 'timestamp', true); } handleTrades (client, message) { // // swap trades // { // "channel":"push.deal", // "data":{ // "M":1, // "O":1, // "T":1, // "p":6866.5, // "t":1587442049632, // "v":2096 // }, // "symbol":"BTC_USDT", // "ts":1587442022003 // } // // spot trades // // { // "symbol":"BTC_USDT", // "data":{ // "deals":[ // { // "t":1651227552839, // "p":"39190.01", // "q":"0.001357", // "T":2 // } // ] // }, // "channel":"push.deal" // } // const marketId = this.safeString (message, 'symbol'); const market = this.safeMarket (marketId); const symbol = market['symbol']; let stored = this.safeValue (this.trades, symbol); if (stored === undefined) { const limit = this.safeInteger (this.options, 'tradesLimit', 1000); stored = new ArrayCache (limit); this.trades[symbol] = stored; } const data = this.safeValue (message, 'data', {}); let trades = undefined; if ('deals' in data) { trades = this.safeValue (data, 'deals', []); } else { trades = [ data ]; } for (let j = 0; j < trades.length; j++) { const parsedTrade = this.parseWsTrade (trades[j], market); stored.append (parsedTrade); } const messageHash = 'trades' + ':' + symbol; client.resolve (stored, messageHash); } async watchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { /** * @method * @name mexc#watchMyTrades * @description watches information on multiple trades made by the user * @param {string} 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 {object} params extra parameters specific to the mexc api endpoint * @returns {[object]} a list of [order structures]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure */ await this.loadMarkets (); let messageHash = 'trade'; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); symbol = market['symbol']; messageHash += ':' + market['symbol']; } let type = undefined; [ type, params ] = this.handleMarketTypeAndParams ('watchMyTrades', market, params); let trades = undefined; if (type === 'spot') { throw new NotSupported (this.id + ' watchMyTrades does not support spot markets'); } else { trades = await this.watchSwapPrivate (messageHash, params); } if (this.newUpdates) { limit = trades.getLimit (symbol, limit); } return this.filterBySymbolSinceLimit (trades, symbol, since, limit, true); } handleMyTrade (client, message, subscription = undefined) { // // swap trade // { // channel: 'push.personal.order.deal', // data: { // category: 1, // fee: 0.00060288, // feeCurrency: 'USDT', // id: '311655369', // isSelf: false, // orderId: '276461245253669888', // positionMode: 1, // price: 100.48, // profit: 0.0003, // side: 4, // symbol: 'LTC_USDT', // taker: true, // timestamp: 1651583897276, // vol: 1 // }, // ts: 1651583897291 // } // const data = this.safeValue (message, 'data', {}); const marketId = this.safeString (data, 'symbol'); const market = this.safeMarket (marketId); const parsed = this.parseWsTrade (data, market); if (this.myTrades === undefined) { const limit = this.safeInteger (this.options, 'tradesLimit', 1000); this.myTrades = new ArrayCacheBySymbolById (limit); } const trades = this.myTrades; trades.append (parsed); let channel = 'trade'; // non-symbol specific client.resolve (trades, channel); channel += ':' + market['symbol']; client.resolve (trades, channel); } parseWsTrade (trade, market = undefined) { // // public spot // { // "t":1651227552839, // "p":"39190.01", // "q":"0.001357", // "T":2 // } // // public swap // // { // "M":1, // "O":1, // "T":1, // "p":6866.5, // "t":1587442049632, // "v":2096 // } // // private swap // { // category: 1, // fee: 0.00060288,2 // feeCurrency: 'USDT', // id: '311655369', // isSelf: false, // orderId: '276461245253669888', // positionMode: 1, // price: 100.48, // profit: 0.0003, // side: 4, // symbol: 'LTC_USDT', // taker: true, // timestamp: 1651583897276, // vol: 1 // } // const timestamp = this.safeInteger2 (trade, 'timestamp', 't'); const marketId = this.safeString (trade, 'symbol'); market = this.safeMarket (marketId, market, '_'); const symbol = market['symbol']; const priceString = this.safeString2 (trade, 'price', 'p'); let amountString = this.safeString2 (trade, 'vol', 'q'); if (amountString === undefined) { amountString = this.safeString (trade, 'v'); } let rawSide = this.safeString (trade, 'T'); let side = undefined; if (rawSide === undefined) { rawSide = this.safeString (trade, 'side'); side = this.parseSwapSide (rawSide); } else { side = (rawSide === '1') ? 'buy' : 'sell'; } let id = this.safeString (trade, 'id'); if (id === undefined) { id = timestamp.toString () + '-' + market['id'] + '-' + amountString; } const feeCostString = this.safeString (trade, 'fee'); let fee = undefined; if (feeCostString !== undefined) { const feeCurrencyId = this.safeString (trade, 'feeCurrency'); const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId); fee = { 'cost': feeCostString, 'currency': feeCurrencyCode, }; } const orderId = this.safeString (trade, 'orderId'); const isTaker = this.safeValue (trade, 'taker', true); const takerOrMaker = isTaker ? 'taker' : 'maker'; return this.safeTrade ({ 'info': trade, 'id': id, 'order': orderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': undefined, 'side': side, 'takerOrMaker': takerOrMaker, 'price': priceString, 'amount': amountString, 'cost': undefined, 'fee': fee, }, market); } async watchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { /** * @method * @name mexc#watchOrders * @description watches information on multiple orders made by the user * @param {string|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 {object} params extra parameters specific to the mexc api endpoint * @returns {[object]} a list of [order structures]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure} */ await this.loadMarkets (); let messageHash = 'order'; let market = undefined; if (symbol !== undefined) { market = this.market (symbol); symbol = market['symbol']; messageHash += ':' + market['symbol']; } let type = undefined; [ type, params ] = this.handleMarketTypeAndParams ('watchOrders', market, params); let orders = undefined; if (type === 'spot') { orders = await this.watchSpotPrivate (messageHash, params); } else { orders = await this.watchSwapPrivate (messageHash, params); } if (this.newUpdates) { limit = orders.getLimit (symbol, limit); } return this.filterBySymbolSinceLimit (orders, symbol, since, limit, true); } handleOrder (client, message, subscription = undefined) { // // spot order // { // symbol: 'LTC_USDT', // data: { // price: 100.25, // quantity: 0.0498, // amount: 4.99245, // remainAmount: 0.01245, // remainQuantity: 0, // remainQ: 0, // remainA: 0, // id: '0b1bf3a33916499f8d1a711a7d5a6fc4', // status: 2, // tradeType: 1, // orderType: 3, // createTime: 1651499416000, // isTaker: 1, // symbolDisplay: 'LTC_USDT', // clientOrderId: '' // }, // channel: 'push.personal.order', // eventTime: 1651499416639, // symbol_display: 'LTC_USDT' // } // // spot trigger // // { // symbol: 'LTC_USDT', // data: { // id: '048dddc31b9a451084b8db8b561a0e33', // market: 'USDT', // currency: 'LTC', // triggerType: 'LE', // triggerPrice: 80, // tradeType: 'BUY', // orderType: 100, // price: 70, // quantity: 0.0857, // state: 'NEW', // createTime: 1651578450223, // currencyDisplay: 'LTC' // }, // channel: 'push.personal.trigger.order', // symbol_display: 'LTC_USDT' // } // // swap order // { // channel: 'push.personal.order', // data: { // category: 1, // createTime: 1651500368131, // dealAvgPrice: 0, // dealVol: 0, // errorCode: 0, // externalOid: '_m_4a78c91ca8be4c4580d94e637b1f70d1', // feeCurrency: 'USDT', // leverage: 1, // makerFee: 0, // openType: 2, // orderId: '276110898672819715', // orderMargin: 0.5006, // orderType: 1, // positionId: 0, // positionMode: 1, // price: 50, // profit: 0, // remainVol: 1, // side: 1, // state: 2, // symbol: 'LTC_USDT', // takerFee: 0, // updateTime: 1651500368142, // usedMargin: 0, // version: 1, // vol: 1 // }, // ts: 1651500368149 // } // const data = this.safeValue (message, 'data', {}); let marketId = this.safeString (message, 'symbol'); if (marketId === undefined) { marketId = this.safeString (data, 'symbol'); } const market = this.safeMarket (marketId); const parsed = this.parseWSOrder (data, market); if (this.orders === undefined) { const limit = this.safeInteger (this.options, 'ordersLimit', 1000); this.orders = new ArrayCacheBySymbolById (limit); } const orders = this.orders; orders.append (parsed); let channel = 'order'; // non-symbol specific client.resolve (orders, channel); channel += ':' + market['symbol']; client.resolve (orders, channel); } parseWSOrder (order, market = undefined) { // // spot order // { // price: 100.25, // quantity: 0.0498, // amount: 4.99245, // remainAmount: 0.01245, // remainQuantity: 0, // remainQ: 0, // remainA: 0, // id: '0b1bf3a33916499f8d1a711a7d5a6fc4', // status: 2, // tradeType: 1, // 1 = buy, 2 = sell // orderType: 3, // 1 = limit, 3 = market, 100 = 'limit // createTime: 1651499416000, // isTaker: 1, // symbolDisplay: 'LTC_USDT', // clientOrderId: '' // } // // spot trigger order // { // id: '048dddc31b9a451084b8db8b561a0e33', // market: 'USDT', // currency: 'LTC', // triggerType: 'LE', // triggerPrice: 80, // tradeType: 'BUY', // orderType: 100, // price: 70, // quantity: 0.0857, // state: 'NEW', // createTime: 1651578450223, // currencyDisplay: 'LTC' // } // // swap order // { // category: 1, // createTime: 1651500368131, // dealAvgPrice: 0, // dealVol: 0, // errorCode: 0, // externalOid: '_m_4a78c91ca8be4c4580d94e637b1f70d1', // feeCurrency: 'USDT', // leverage: 1, // makerFee: 0, // openType: 2, // orderId: '276110898672819715', // orderMargin: 0.5006, // orderType: 1, // 5 = market, 1 = limit, // positionId: 0, // positionMode: 1, // price: 50, // profit: 0, // remainVol: 1, // side: 1, // state: 2, // symbol: 'LTC_USDT', // takerFee: 0, // updateTime: 1651500368142, // usedMargin: 0, // version: 1, // vol: 1 // } // const id = this.safeString2 (order, 'orderId', 'id'); const state = this.safeString2 (order, 'state', 'status'); const timestamp = this.safeInteger (order, 'createTime'); const price = this.safeString (order, 'price'); const amount = this.safeString2 (order, 'quantity', 'vol'); const remaining = this.safeString (order, 'remainQuantity'); const filled = this.safeString (order, 'dealVol'); const cost = this.safeString (order, 'amount'); const avgPrice = this.safeString (order, 'dealAvgPrice'); const marketId = this.safeString2 (order, 'symbol', 'symbolDisplay'); const symbol = this.safeSymbol (marketId, market, '_'); const sideCheck = this.safeString (order, 'side'); let side = this.parseSwapSide (sideCheck); if (side === undefined) { const tradeType = this.safeStringLower (order, 'tradeType'); if ((tradeType === 'ask') || (tradeType === '2')) { side = 'sell'; } else if ((tradeType === 'bid') || (tradeType === '1')) { side = 'buy'; } else { side = tradeType; } } const status = this.parseWsOrderStatus (state, market); let clientOrderId = this.safeString2 (order, 'client_order_id', 'orderId'); if (clientOrderId === '') { clientOrderId = undefined; } const rawType = this.safeString (order, 'orderType'); const isMarket = (rawType === '3') || (rawType === '5'); const type = isMarket ? 'market' : 'limit'; return this.safeOrder ({ 'id': id, 'clientOrderId': clientOrderId, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': this.safeInteger (order, 'updateTime'), 'status': status, 'symbol': symbol, 'type': type, 'timeInForce': undefined, 'side': side, 'price': price, 'stopPrice': undefined, 'average': avgPrice, 'amount': amount, 'cost': cost, 'filled': filled, 'remaining': remaining, 'fee': undefined, 'trades': undefined, 'info': order, }, market); } parseSwapSide (side) { const sides = { '1': 'open long', '2': 'close short', '3': 'open short', '4': 'close long', }; return this.safeString (sides, side); } parseWsOrderStatus (status, market = undefined) { let statuses = {}; if (market['type'] === 'spot') { statuses = { // spot limit/market '1': 'open', '2': 'closed', '3': 'open', '4': 'canceled', '5': 'open', // spot trigger only 'NEW': 'open', 'FILLED': 'closed', 'PARTIALLY_FILLED': 'open', 'CANCELED': 'canceled', 'PARTIALLY_CANCELED': 'canceled', }; } else { statuses = { '2': 'open', '3': 'closed', '4': 'canceled', }; } return this.safeString (statuses, status, status); } async watchBalance (params = {}) { /** * @method * @name mexc#watchBalance * @description query for balance and get the amount of funds available for trading or funds locked in orders * @param {object} params extra parameters specific to the mexc api endpoint * @returns {object} a [balance structure]{@link https://docs.ccxt.com/en/latest/manual.html?#balance-structure} */ await this.loadMarkets (); const messageHash = 'balance'; let type = undefined; [ type, params ] = this.handleMarketTypeAndParams ('watchBalance', undefined, params); if (type === 'spot') { throw new NotSupported (this.id + ' watchBalance does not support spot markets'); } else { return this.watchSwapPrivate (messageHash, params); } } handleBalance (client, message) { // // swap balance // // { // channel: 'push.personal.asset', // data: { // availableBalance: 49.2076809226, // bonus: 0, // currency: 'USDT', // frozenBalance: 0.5006, // positionMargin: 0 // }, // ts: 1651501676430 // } // const data = this.safeValue (message, 'data'); const currencyId = this.safeString (data, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['free'] = this.safeString (data, 'availableBalance'); account['used'] = this.safeString (data, 'frozenBalance'); this.balance[code] = account; this.balance = this.safeBalance (this.balance); const messageHash = 'balance'; client.resolve (this.balance, messageHash); } async watchSwapPublic (messageHash, channel, requestParams, params = {}) { const url = this.urls['api']['ws']['swap']; const request = { 'method': channel, 'param': requestParams, }; const message = this.extend (request, params); return await this.watch (url, messageHash, message, messageHash); } async watchSpotPublic (messageHash, channel, requestParams, params = {}) { const url = this.urls['api']['ws']['spot']; const request = { 'op': channel, }; const extendedRequest = this.extend (request, requestParams); const message = this.extend (extendedRequest, params); return await this.watch (url, messageHash, message, messageHash); } async watchSpotPrivate (messageHash, params = {}) { this.checkRequiredCredentials (); const channel = 'sub.personal'; const url = this.urls['api']['ws']['spot']; const timestamp = this.milliseconds ().toString (); const request = { 'op': channel, 'api_key': this.apiKey, 'req_time': timestamp, }; const sortedParams = this.keysort (request); sortedParams['api_secret'] = this.secret; const encodedParams = this.urlencode (sortedParams); const hash = this.hash (this.encode (encodedParams), 'md5'); request['sign'] = hash; const extendedRequest = this.extend (request, params); return await this.watch (url, messageHash, extendedRequest, channel); } async watchSwapPrivate (messageHash, params = {}) { this.checkRequiredCredentials (); const channel = 'login'; const url = this.urls['api']['ws']['swap']; const timestamp = this.milliseconds ().toString (); const payload = this.apiKey + timestamp; const signature = this.hmac (this.encode (payload), this.encode (this.secret), 'sha256'); const request = { 'method': channel, 'param': { 'apiKey': this.apiKey, 'signature': signature, 'reqTime': timestamp, }, }; const extendedRequest = this.extend (request, params); const message = this.extend (extendedRequest, params); return await this.watch (url, messageHash, message, channel); } handleErrorMessage (client, message) { // // { channel: 'sub.personal', msg: 'signature validation failed' } // // { // channel: 'rs.error', // data: 'Contract not exists', // ts: 1651509181535 // } // const channel = this.safeString (message, 'channel'); try { const feedback = this.id + ' ' + this.json (message); if (channel.indexOf ('error') >= 0) { const data = this.safeValue (message, 'data'); if (typeof data === 'string') { this.throwExactlyMatchedException (this.exceptions['ws']['exact'], data, feedback); this.throwBroadlyMatchedException (this.exceptions['ws']['broad'], data, feedback); } } if (channel === 'sub.personal') { const msg = this.safeString (message, 'msg'); this.throwExactlyMatchedException (this.exceptions['ws']['exact'], msg, feedback); } } catch (e) { if (e instanceof AuthenticationError) { return false; } } return message; } handleAuthenticate (client, message) { // // { channel: 'rs.login', data: 'success', ts: 1651486643082 } // // { channel: 'sub.personal', msg: 'OK' } // return message; } handleMessage (client, message) { // // spot pong // // "ping" // // swap pong // { channel: 'pong', data: 1651570941402, ts: 1651570941402 } // // auth spot // // { channel: 'sub.personal', msg: 'OK' } // // auth swap // // { channel: 'rs.login', data: 'success', ts: 1651486643082 } // // subscription // // { channel: 'rs.sub.depth', data: 'success', ts: 1651239594401 } // // swap ohlcv // { // "channel":"push.kline", // "data":{ // "a":233.740269343644737245, // "c":6885, // "h":6910.5, // "interval":"Min60", // "l":6885, // "o":6894.5, // "q":1611754, // "symbol":"BTC_USDT", // "t":1587448800 // }, // "symbol":"BTC_USDT", // "ts":1587442022003 // } // // swap ticker // { // channel: 'push.ticker', // data: { // amount24: 491939387.90105, // ask1: 39530.5, // bid1: 39530, // contractId: 10, // fairPrice: 39533.4, // fundingRate: 0.00015, // high24Price: 40310.5, // holdVol: 187680157, // indexPrice: 39538.5, // lastPrice: 39530, // lower24Price: 38633, // maxBidPrice: 43492, // minAskPrice: 35584.5, // riseFallRate: 0.0138, // riseFallValue: 539.5, // symbol: 'BTC_USDT', // timestamp: 1651160401009, // volume24: 125171687 // }, // symbol: 'BTC_USDT', // ts: 1651160401009 // } // // swap trades // { // "channel":"push.deal", // "data":{ // "M":1, // "O":1, // "T":1, // "p":6866.5, // "t":1587442049632, // "v":2096 // }, // "symbol":"BTC_USDT", // "ts":1587442022003 // } // // spot trades // // { // "symbol":"BTC_USDT", // "data":{ // "deals":[ // { // "t":1651227552839, // "p":"39190.01", // "q":"0.001357", // "T":2 // } // ] // }, // "channel":"push.deal" // } // // spot order // { // symbol: 'LTC_USDT', // data: { // price: 100.25, // quantity: 0.0498, // amount: 4.99245, // remainAmount: 0.01245, // remainQuantity: 0, // remainQ: 0, // remainA: 0, // id: '0b1bf3a33916499f8d1a711a7d5a6fc4', // status: 2, // tradeType: 1, // orderType: 3, // createTime: 1651499416000, // isTaker: 1, // symbolDisplay: 'LTC_USDT', // clientOrderId: '' // }, // channel: 'push.personal.order', // eventTime: 1651499416639, // symbol_display: 'LTC_USDT' // } // if (!this.handleErrorMessage (client, message)) { return; } if (message === 'pong') { this.handlePong (client, message); return; } const channel = this.safeString (message, 'channel'); const methods = { 'pong': this.handlePong, 'rs.login': this.handleAuthenticate, 'push.deal': this.handleTrades, 'orderbook': this.handleOrderBook, 'push.kline': this.handleOHLCV, 'push.ticker': this.handleTicker, 'push.depth': this.handleOrderBook, 'push.limit.depth': this.handleOrderBook, 'push.personal.order': this.handleOrder, 'push.personal.trigger.order': this.handleOrder, 'push.personal.plan.order': this.handleOrder, 'push.personal.order.deal': this.handleMyTrade, 'push.personal.asset': this.handleBalance, }; const method = this.safeValue (methods, channel); if (method !== undefined) { method.call (this, client, message); } } ping (client) { const type = this.safeString (this.options, 'defaultType', 'spot'); if (type === 'spot') { return 'ping'; } return { 'method': 'ping' }; } handlePong (client, message) { // // { channel: 'pong', data: 1651570941402, ts: 1651570941402 } // client.lastPong = this.milliseconds (); return message; } };