UNPKG

ccxt

Version:

A cryptocurrency trading API with more than 100 exchanges in JavaScript / TypeScript / Python / C# / PHP / Go

1,120 lines (1,117 loc) • 60.7 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var mudrex$1 = require('./abstract/mudrex.js'); var errors = require('./base/errors.js'); var Precise = require('./base/Precise.js'); // ---------------------------------------------------------------------------- // --------------------------------------------------------------------------- /** * @class mudrex * @augments Exchange */ class mudrex extends mudrex$1["default"] { describe() { return this.deepExtend(super.describe(), { 'id': 'mudrex', 'name': 'Mudrex', 'countries': ['IN'], 'rateLimit': 100, // 10 req/s default 'version': 'v1', 'pro': true, 'certified': false, 'dex': false, 'hostname': 'trade.mudrex.com', 'has': { 'CORS': undefined, 'spot': false, 'margin': false, 'swap': true, 'future': false, 'option': false, 'addMargin': true, 'cancelOrder': true, 'closePosition': true, 'createMarketOrder': true, 'createOrder': true, 'createOrderWithTakeProfitAndStopLoss': true, 'createReduceOnlyOrder': true, 'editOrder': true, 'fetchBalance': true, 'fetchClosedOrders': true, 'fetchFundingRate': false, 'fetchFundingRateHistory': false, 'fetchFundingRates': false, 'fetchIndexOHLCV': false, 'fetchLeverage': true, 'fetchMarkets': true, 'fetchMarkOHLCV': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenInterest': false, 'fetchOpenInterests': false, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBook': false, 'fetchOrders': true, 'fetchPositions': true, 'fetchPositionsHistory': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchTrades': false, 'reduceMargin': true, 'setLeverage': true, 'transfer': true, 'watchOHLCV': true, 'watchTicker': true, 'watchTickers': true, }, 'timeframes': { '1m': '1m', '3m': '3t', '5m': '5t', '10m': '10t', '15m': '15t', '30m': '30t', '1h': '1h', '4h': '4h', '6h': '6h', '12h': '12h', '1d': '1d', '1w': '1w', '1M': '1mth', }, 'urls': { 'logo': 'https://github.com/user-attachments/assets/72368864-84ed-43eb-8c75-d4fb77023b42', 'api': { 'public': 'https://trade.mudrex.com/fapi/v1', 'private': 'https://trade.mudrex.com/fapi/v1', 'market': 'https://trade.mudrex.com/fapi/v1', }, 'www': 'https://mudrex.com', 'doc': 'https://docs.trade.mudrex.com/docs', 'fees': 'https://docs.trade.mudrex.com', }, 'api': { 'market': { 'get': { 'price/kline': 1, 'price/mark-kline': 1, }, }, 'public': { 'get': {}, }, 'private': { 'get': { 'futures': 1, 'futures/{asset_id}': 1, 'wallet/funds': 5, 'futures/funds': 5, 'futures/orders': 1, 'futures/orders/history': 1, 'futures/orders/{order_id}': 1, 'futures/positions': 1, 'futures/positions/history': 1, 'futures/fee/history': 1, 'futures/{asset_id}/leverage': 2, 'futures/positions/{position_id}/liq-price': 1, }, 'post': { 'wallet/futures/transfer': 5, 'futures/transfers/inr': 5, 'futures/{asset_id}/order': 2, 'futures/positions/{position_id}/close': 2, 'futures/positions/{position_id}/close/partial': 2, 'futures/positions/{position_id}/reverse': 2, 'futures/positions/{position_id}/add-margin': 2, 'futures/positions/{position_id}/riskorder': 2, 'futures/{asset_id}/leverage': 2, }, 'patch': { 'futures/orders/{order_id}': 1, 'futures/positions/{position_id}/riskorder': 2, }, 'delete': { 'futures/orders/{order_id}': 2, }, }, }, 'requiredCredentials': { 'apiKey': false, 'secret': true, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'taker': this.parseNumber('0.00059'), 'maker': this.parseNumber('0.00023'), }, }, 'options': { 'defaultType': 'swap', 'broker': '42ce8902-8585-448c-a1e8-0371a6ca7ca8', }, 'exceptions': { 'exact': { '400 Invalid trade currency': errors.BadRequest, }, 'broad': { 'Invalid trade currency': errors.BadRequest, 'Params error': errors.BadRequest, 'invalid trigger type': errors.BadRequest, 'invalid order type': errors.BadRequest, 'order price out of permissible range': errors.BadRequest, 'quantity not a multiple of the quantity step': errors.BadRequest, 'leverage out of permissible range': errors.BadRequest, 'insufficient balance': errors.InsufficientFunds, 'asset not found': errors.BadSymbol, 'leverage not found': errors.OrderNotFound, 'order not found': errors.OrderNotFound, 'Rate limit exceeded': errors.RateLimitExceeded, }, }, }); } sign(path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { const apiUrls = this.safeDict(this.urls, 'api', {}); const base = this.safeString(apiUrls, api); if (base === undefined) { throw new errors.ExchangeError(this.id + ' unknown API namespace: ' + api); } let url = base + '/' + this.implodeParams(path, params); let query = this.omit(params, this.extractParams(path)); let requestHeaders = {}; if (headers !== undefined) { requestHeaders = this.extend({}, headers); } const brokerId = this.safeString(this.options, 'broker'); if (brokerId !== undefined) { requestHeaders['Partner-Id'] = brokerId; } const methodUpper = method.toUpperCase(); if (api === 'private') { this.checkRequiredCredentials(); requestHeaders['X-Authentication'] = this.secret; if (methodUpper === 'POST' || methodUpper === 'PATCH' || methodUpper === 'DELETE') { requestHeaders['Content-Type'] = 'application/json'; // is_symbol is a query-string flag even on write requests const isSymbol = this.safeString(query, 'is_symbol'); if (isSymbol !== undefined) { query = this.omit(query, 'is_symbol'); url += '?' + this.urlencode({ 'is_symbol': isSymbol }); } if ((methodUpper === 'DELETE') && this.isEmpty(query)) { return { 'url': url, 'method': methodUpper, 'body': undefined, 'headers': requestHeaders }; } const bodyStr = this.json(query); return { 'url': url, 'method': methodUpper, 'body': bodyStr, 'headers': requestHeaders }; } } if (Object.keys(query).length) { url += '?' + this.urlencode(query); } return { 'url': url, 'method': methodUpper, 'body': undefined, 'headers': requestHeaders }; } handleErrors(code, reason, url, method, headers, body, response, requestHeaders, requestBody) { if (response === undefined || typeof response !== 'object') { return undefined; } const success = this.safeBool(response, 'success', true); if (!success) { const errors$1 = this.safeList(response, 'errors', []); const first = this.safeDict(errors$1, 0, {}); const text = this.safeString(first, 'text', this.json(response)); const errCode = this.safeString(first, 'code'); this.throwExactlyMatchedException(this.exceptions['exact'], text, this.id + ' ' + text); this.throwExactlyMatchedException(this.exceptions['exact'], errCode, this.id + ' ' + text); this.throwBroadlyMatchedException(this.exceptions['broad'], text, this.id + ' ' + text); const msg = this.id + ' ' + text; const low = text.toLowerCase(); if (code === 401 || low.indexOf('auth') >= 0) { throw new errors.AuthenticationError(msg); } if (code === 429 || low.indexOf('rate') >= 0) { throw new errors.RateLimitExceeded(msg); } if (low.indexOf('insufficient') >= 0) { throw new errors.InsufficientFunds(msg); } if (code === 400) { throw new errors.BadRequest(msg); } throw new errors.ExchangeError(msg); } return undefined; } parseOHLCV(ohlcv, market = undefined) { // // [ 1782984660, 60681, 60797.6, 60671.8, 60693.3, 275.741 ] // [ timestampInSeconds, open, high, low, close, volume ] // return [ this.safeTimestamp(ohlcv, 0), this.safeNumber(ohlcv, 1), this.safeNumber(ohlcv, 2), this.safeNumber(ohlcv, 3), this.safeNumber(ohlcv, 4), this.safeNumber(ohlcv, 5), ]; } /** * @method * @name mudrex#fetchOHLCV * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market * @see https://docs.trade.mudrex.com/docs/historical-kline * @param {string} symbol unified symbol of the market to fetch OHLCV data for * @param {string} timeframe the length of time each candle represents * @param {int} [since] timestamp in ms of the earliest candle to fetch * @param {int} [limit] the maximum amount of candles to fetch * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {int} [params.until] timestamp in ms of the latest candle to fetch * @param {string} [params.price] "mark" to fetch mark price candles * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume */ async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { if (this.markets === undefined) { await this.loadMarkets(); } const market = this.market(symbol); const priceType = this.safeString(params, 'price'); params = this.omit(params, 'price'); // the endpoint expects the pair in "BASE/QUOTE" format (comma-separated for multiple) const assetPair = market['baseId'] + '/' + market['quoteId']; const request = { 'assets': assetPair, 'aggregation': this.safeString(this.timeframes, timeframe, timeframe), }; // the endpoint requires an explicit time window (in seconds) const duration = this.parseTimeframe(timeframe); let requestLimit = limit; if (requestLimit === undefined) { requestLimit = 500; } const now = this.seconds(); let startTime = undefined; if (since !== undefined) { startTime = this.parseToInt(since / 1000); } else { startTime = now - duration * requestLimit; } if (startTime === undefined) { throw new errors.ExchangeError(this.id + ' fetchOHLCV() missing startTime'); } let endTime = startTime + duration * requestLimit; const until = this.safeInteger(params, 'until'); if (until !== undefined) { params = this.omit(params, 'until'); endTime = this.parseToInt(until / 1000); } else if (endTime > now) { endTime = now; } request['start_time'] = startTime; request['end_time'] = endTime; let response = undefined; if (priceType === 'mark') { response = await this.marketGetPriceMarkKline(this.extend(request, params)); } else { response = await this.marketGetPriceKline(this.extend(request, params)); } // // { // "success": true, // "data": { // "asset_ticks": { // "btc/usdt": [ [ 1782984660, 60681, 60797.6, 60671.8, 60693.3, 275.741 ] ] // } // } // } // const data = this.safeDict(response, 'data', {}); const assetTicks = this.safeDict(data, 'asset_ticks', {}); const ohlcvs = this.safeList(assetTicks, assetPair.toLowerCase(), []); return this.parseOHLCVs(ohlcvs, market, timeframe, since, limit); } /** * @method * @name mudrex#fetchMarkOHLCV * @description fetches historical mark price candlestick data containing the open, high, low, and close price of a market * @see https://docs.trade.mudrex.com/docs * @param {string} symbol unified symbol of the market to fetch OHLCV data for * @param {string} timeframe the length of time each candle represents * @param {int} [since] timestamp in ms of the earliest candle to fetch * @param {int} [limit] the maximum amount of candles to fetch * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume */ async fetchMarkOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { return await this.fetchOHLCV(symbol, timeframe, since, limit, this.extend(params, { 'price': 'mark' })); } /** * @method * @name mudrex#fetchTicker * @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market * @see https://docs.trade.mudrex.com/docs * @param {string} symbol unified symbol of the market to fetch the ticker for * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a [ticker structure](https://docs.ccxt.com/#/?id=ticker-structure) */ async fetchTicker(symbol, params = {}) { if (this.markets === undefined) { await this.loadMarkets(); } const market = this.market(symbol); const request = { 'asset_id': market['id'], 'is_symbol': 1, }; const response = await this.privateGetFuturesAssetId(this.extend(request, params)); const data = this.safeDict(response, 'data', {}); return this.parseTicker(data, market); } /** * @method * @name mudrex#fetchTickers * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market * @see https://docs.trade.mudrex.com/docs * @param {string[]} [symbols] unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a dictionary of [ticker structures](https://docs.ccxt.com/#/?id=ticker-structure) */ async fetchTickers(symbols = undefined, params = {}) { if (this.markets === undefined) { await this.loadMarkets(); } const request = {}; const response = await this.privateGetFutures(this.extend(request, params)); const data = this.safeValue(response, 'data', []); const rows = Array.isArray(data) ? data : this.safeList(data, 'items', []); const resultTickers = {}; for (let i = 0; i < rows.length; i++) { const t = rows[i]; const sym = this.safeString(t, 'symbol'); if (sym === undefined) { continue; } const m = this.safeMarket(sym); const symbol = m['symbol']; if (symbols !== undefined && !this.inArray(symbol, symbols)) { continue; } resultTickers[symbol] = this.parseTicker(t, m); } return this.filterByArrayTickers(resultTickers, 'symbol', symbols); } parseTicker(ticker, market = undefined) { const ms = this.safeString(ticker, 'symbol'); market = this.safeMarket(ms, market); const symbol = market['symbol']; const ts = this.milliseconds(); const pct = this.safeNumber(ticker, 'change_perc'); return this.safeTicker({ 'symbol': symbol, 'timestamp': ts, 'datetime': this.iso8601(ts), 'high': undefined, 'low': undefined, 'bid': undefined, 'bidVolume': undefined, 'ask': undefined, 'askVolume': undefined, 'vwap': undefined, 'open': this.safeNumber(ticker, 'last_day_price'), 'close': this.safeNumber(ticker, 'price'), 'last': this.safeNumber(ticker, 'price'), 'previousClose': undefined, 'change': undefined, 'percentage': pct, 'average': undefined, 'baseVolume': undefined, 'quoteVolume': this.safeNumber(ticker, 'volume'), 'info': ticker, }, market); } /** * @method * @name mudrex#fetchMarkets * @description retrieves data on all markets for the exchange * @see https://docs.trade.mudrex.com/docs * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} an array of objects representing market data */ async fetchMarkets(params = {}) { const aggregated = []; let offset = 0; const pageLimit = 100; let paging = true; while (paging === true) { const q = this.extend({ 'limit': pageLimit, 'offset': offset }, params); const response = await this.privateGetFutures(q); const data = this.safeValue(response, 'data', []); let items = []; if (typeof data === 'object' && !Array.isArray(data)) { items = this.safeList(data, 'items', []); if (!items.length) { items = this.safeList(data, 'results', []); } if (!items.length && ('symbol' in data)) { items = [data]; } } else { items = this.toArray(data); } if (!items.length) { paging = false; break; } for (let i = 0; i < items.length; i++) { aggregated.push(items[i]); } if (items.length < pageLimit) { paging = false; } else { offset += pageLimit; } } const result = []; for (let i = 0; i < aggregated.length; i++) { result.push(this.parseMarket(aggregated[i])); } return result; } parseMarket(asset) { const ms = this.safeString(asset, 'symbol'); let base = ms; if (ms !== undefined && ms.endsWith('USDT')) { base = ms.slice(0, -4); } const quote = 'USDT'; const settle = 'USDT'; let symbol = undefined; if (base !== undefined) { symbol = base + '/' + quote + ':' + settle; } const priceStep = this.safeString(asset, 'price_step', '0.01'); const qtyStep = this.safeString(asset, 'quantity_step', '0.001'); return this.safeMarketStructure({ 'id': ms, 'lowercaseId': undefined, 'symbol': symbol, 'base': base, 'quote': quote, 'settle': settle, 'baseId': base, 'quoteId': 'USDT', 'settleId': 'USDT', 'type': 'swap', 'spot': false, 'margin': false, 'swap': true, 'future': false, 'option': false, 'active': true, 'contract': true, 'linear': true, 'inverse': false, 'taker': this.safeNumber(this.fees['trading'], 'taker'), 'maker': this.safeNumber(this.fees['trading'], 'maker'), 'contractSize': this.safeNumber(asset, 'contract_size', 1), 'expiry': undefined, 'expiryDatetime': undefined, 'strike': undefined, 'optionType': undefined, 'precision': { 'amount': this.parseNumber(qtyStep), 'price': this.parseNumber(priceStep), }, 'limits': { 'amount': { 'min': this.safeNumber(asset, 'min_contract'), 'max': this.safeNumber(asset, 'max_contract'), }, 'price': { 'min': this.safeNumber(asset, 'min_price'), 'max': this.safeNumber(asset, 'max_price'), }, 'cost': { 'min': this.safeNumber(asset, 'min_notional_value'), 'max': undefined, }, }, 'info': asset, 'created': undefined, }); } /** * @method * @name mudrex#fetchBalance * @description query for balance and get the amount of funds available for trading or funds locked in orders * @see https://docs.trade.mudrex.com/docs * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {string} [params.type] 'swap' (default) or 'spot' - which wallet balance to fetch * @param {string} [params.trade_currency] the settlement currency to query the balance for * @returns {object} a [balance structure](https://docs.ccxt.com/#/?id=balance-structure) */ async fetchBalance(params = {}) { if (this.markets === undefined) { await this.loadMarkets(); } let type = undefined; [type, params] = this.handleMarketTypeAndParams('fetchBalance', undefined, params, 'swap'); const requested = this.safeStringN(params, ['trade_currency', 'tradeCurrency', 'currency']); params = this.omit(params, ['trade_currency', 'tradeCurrency', 'currency']); const request = {}; let response = undefined; if (type === 'spot') { if (requested !== undefined) { request['currency'] = requested; } response = await this.privateGetWalletFunds(this.extend(request, params)); } else { if (requested !== undefined) { request['trade_currency'] = requested; } response = await this.privateGetFuturesFunds(this.extend(request, params)); } let currency = requested; if (currency === undefined) { currency = 'USDT'; } if (response === undefined) { throw new errors.NullResponse(this.id + ' fetchBalance() returned empty response'); } response['currency'] = currency; return this.parseBalance(response); } parseBalance(response) { const data = this.safeDict(response, 'data', {}); const currency = this.safeString(response, 'currency', 'USDT'); const timestamp = this.milliseconds(); const result = { 'info': response, 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), }; const account = this.account(); const futuresBalance = this.safeString(data, 'balance'); if (futuresBalance !== undefined) { // futures wallet: balance is the free/available margin, locked_amount is used, safeBalance derives total account['free'] = futuresBalance; account['used'] = this.safeString(data, 'locked_amount'); } else { // spot wallet: total is the total, withdrawable is free, safeBalance derives used account['total'] = this.safeString(data, 'total'); account['free'] = this.safeString(data, 'withdrawable'); } result[currency] = account; return this.safeBalance(result); } /** * @method * @name mudrex#fetchLeverage * @description fetch the set leverage for a market * @see https://docs.trade.mudrex.com/docs * @param {string} symbol unified market symbol * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a [leverage structure](https://docs.ccxt.com/#/?id=leverage-structure) */ async fetchLeverage(symbol, params = {}) { if (this.markets === undefined) { await this.loadMarkets(); } const market = this.market(symbol); const request = { 'asset_id': market['id'], 'is_symbol': 1, }; const response = await this.privateGetFuturesAssetIdLeverage(this.extend(request, params)); const data = this.safeDict(response, 'data', {}); return { 'info': response, 'symbol': symbol, 'marginMode': this.safeStringLower(data, 'margin_type'), 'longLeverage': this.safeNumber(data, 'leverage'), 'shortLeverage': this.safeNumber(data, 'leverage'), }; } /** * @method * @name mudrex#setLeverage * @description set the level of leverage for a market * @see https://docs.trade.mudrex.com/docs * @param {float} leverage the rate of leverage * @param {string} symbol unified market symbol * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {string} [params.marginType] 'ISOLATED' (default) or 'CROSSED' * @returns {object} response from the exchange */ async setLeverage(leverage, symbol = undefined, params = {}) { if (symbol === undefined) { throw new errors.ArgumentsRequired(this.id + ' setLeverage() requires a symbol'); } if (this.markets === undefined) { await this.loadMarkets(); } const market = this.market(symbol); const marginType = this.safeString(params, 'marginType', 'ISOLATED'); const request = { 'asset_id': market['id'], 'is_symbol': 1, 'margin_type': marginType, 'leverage': leverage, }; params = this.omit(params, ['marginType']); const response = await this.privatePostFuturesAssetIdLeverage(this.extend(request, params)); return response; } /** * @method * @name mudrex#createOrder * @description create a trade order * @see https://docs.trade.mudrex.com/docs * @param {string} symbol unified market symbol * @param {string} type 'market' or 'limit' * @param {string} side 'buy' or 'sell' * @param {float} amount how much you want to trade in units of the base currency * @param {float} [price] the price to fulfill the order, in units of the quote currency (also required for market orders on this exchange) * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {int} [params.leverage] leverage for the order, required if setLeverage() was not called beforehand * @param {bool} [params.reduceOnly] true if the order is reduce only * @param {object} [params.takeProfit] *takeProfit object in params* containing the trigger price of the take-profit order attached to this order * @param {float} [params.takeProfit.triggerPrice] take profit trigger price * @param {object} [params.stopLoss] *stopLoss object in params* containing the trigger price of the stop-loss order attached to this order * @param {float} [params.stopLoss.triggerPrice] stop loss trigger price * @param {float} [params.takeProfitPrice] the trigger price for a standalone take-profit order on an existing position (requires params.positionId) * @param {float} [params.stopLossPrice] the trigger price for a standalone stop-loss order on an existing position (requires params.positionId) * @param {string} [params.positionId] the id of the position the standalone stopLossPrice/takeProfitPrice order is attached to * @param {string} [params.trade_currency] the settlement currency for the order * @returns {object} an [order structure](https://docs.ccxt.com/#/?id=order-structure) */ async createOrder(symbol, type, side, amount, price = undefined, params = {}) { if (this.markets === undefined) { await this.loadMarkets(); } const market = this.market(symbol); // standalone stop-loss / take-profit orders (stopLossPrice/takeProfitPrice) are attached to // an existing position through the riskorder endpoint, so a positionId is required const stopLossPrice = this.safeString(params, 'stopLossPrice'); const takeProfitPrice = this.safeString(params, 'takeProfitPrice'); if ((stopLossPrice !== undefined) || (takeProfitPrice !== undefined)) { const positionId = this.safeString2(params, 'positionId', 'position_id'); if (positionId === undefined) { throw new errors.ArgumentsRequired(this.id + ' createOrder() requires a positionId parameter to place a stopLossPrice or takeProfitPrice order'); } params = this.omit(params, ['stopLossPrice', 'takeProfitPrice', 'positionId', 'position_id']); const riskRequest = { 'position_id': positionId, }; if (takeProfitPrice !== undefined) { riskRequest['is_takeprofit'] = true; riskRequest['takeprofit_price'] = this.priceToPrecision(symbol, takeProfitPrice); } if (stopLossPrice !== undefined) { riskRequest['is_stoploss'] = true; riskRequest['stoploss_price'] = this.priceToPrecision(symbol, stopLossPrice); } const riskResponse = await this.privatePostFuturesPositionsPositionIdRiskorder(this.extend(riskRequest, params)); const riskData = this.safeDict(riskResponse, 'data', riskResponse); return this.parseOrder(riskData, market); } const lev = this.safeInteger(params, 'leverage', 1); if ((type === 'market') && (price === undefined)) { throw new errors.ArgumentsRequired(this.id + ' createOrder() requires a price argument for market orders'); } const request = { 'asset_id': market['id'], 'is_symbol': 1, 'leverage': this.numberToString(lev), 'quantity': this.amountToPrecision(symbol, amount), 'order_price': this.priceToPrecision(symbol, price), 'order_type': (side === 'buy') ? 'LONG' : 'SHORT', 'trigger_type': (type === 'market') ? 'MARKET' : 'LIMIT', 'reduce_only': this.safeBool(params, 'reduceOnly', false), }; // mudrex only supports take-profit / stop-loss orders attached to the position-opening order const takeProfit = this.safeDict(params, 'takeProfit'); const stopLoss = this.safeDict(params, 'stopLoss'); if (takeProfit !== undefined) { request['is_takeprofit'] = true; request['takeprofit_price'] = this.priceToPrecision(symbol, this.safeStringN(takeProfit, ['triggerPrice', 'stopPrice', 'price'])); } if (stopLoss !== undefined) { request['is_stoploss'] = true; request['stoploss_price'] = this.priceToPrecision(symbol, this.safeStringN(stopLoss, ['triggerPrice', 'stopPrice', 'price'])); } params = this.omit(params, ['leverage', 'reduceOnly', 'takeProfit', 'stopLoss']); const response = await this.privatePostFuturesAssetIdOrder(this.extend(request, params)); const data = this.safeDict(response, 'data', response); // the create response omits the order/trigger type, so restore them from the request data['order_type'] = request['order_type']; data['trigger_type'] = request['trigger_type']; return this.parseOrder(data, market); } /** * @method * @name mudrex#editOrder * @description edit a trade order * @see https://docs.trade.mudrex.com/docs * @param {string} id order id * @param {string} symbol unified symbol of the market to edit an order in * @param {string} type 'market' or 'limit' * @param {string} side 'buy' or 'sell' * @param {float} [amount] how much of the currency you want to trade in units of the base currency * @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} an [order structure](https://docs.ccxt.com/#/?id=order-structure) */ async editOrder(id, symbol, type, side, amount = undefined, price = undefined, params = {}) { if (this.markets === undefined) { await this.loadMarkets(); } let market = undefined; if (symbol !== undefined) { market = this.market(symbol); } const request = { 'order_id': id, }; if (amount !== undefined) { request['quantity'] = this.amountToPrecision(symbol, amount); } if (price !== undefined) { request['order_price'] = this.priceToPrecision(symbol, price); } const response = await this.privatePatchFuturesOrdersOrderId(this.extend(request, params)); const data = this.safeDict(response, 'data', response); return this.parseOrder(data, market); } parseOrderStatus(status) { const statuses = { 'open': 'open', 'created': 'open', 'new': 'open', 'pending': 'open', 'partially_filled': 'open', 'filled': 'closed', 'completed': 'closed', 'cancelled': 'canceled', 'canceled': 'canceled', 'rejected': 'rejected', 'expired': 'expired', }; return this.safeString(statuses, status, status); } parseOrder(order, market = undefined) { const oms = this.safeString(order, 'symbol'); market = this.safeMarket(oms, market); const oid = this.safeString2(order, 'order_id', 'id'); const rawSide = this.safeStringUpper(order, 'order_type'); let side = undefined; if (rawSide === 'LONG') { side = 'buy'; } else if (rawSide === 'SHORT') { side = 'sell'; } const trig = this.safeStringUpper(order, 'trigger_type'); let typ = undefined; if (trig === 'MARKET') { typ = 'market'; } else if (trig === 'LIMIT') { typ = 'limit'; } let ts = this.parse8601(this.safeString(order, 'created_at')); if (ts === undefined) { ts = this.milliseconds(); } const status = this.parseOrderStatus(this.safeStringLower(order, 'status')); const sym = market['symbol']; return this.safeOrder({ 'info': order, 'id': oid, 'clientOrderId': undefined, 'timestamp': ts, 'datetime': this.iso8601(ts), 'lastTradeTimestamp': undefined, 'symbol': sym, 'type': typ, 'timeInForce': undefined, 'postOnly': undefined, 'side': side, 'price': this.safeNumber2(order, 'price', 'order_price'), 'stopPrice': undefined, 'triggerPrice': undefined, 'amount': this.safeNumber2(order, 'quantity', 'amount'), 'cost': undefined, 'average': undefined, 'filled': undefined, 'remaining': undefined, 'status': status, 'fee': undefined, 'trades': [], 'fees': [], 'lastUpdateTimestamp': undefined, 'reduceOnly': this.safeBool(order, 'reduce_only'), }, market); } /** * @method * @name mudrex#cancelOrder * @description cancels an open order * @see https://docs.trade.mudrex.com/docs * @param {string} id order id * @param {string} [symbol] unified symbol of the market the order was made in * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} An [order structure](https://docs.ccxt.com/#/?id=order-structure) */ async cancelOrder(id, symbol = undefined, params = {}) { if (this.markets === undefined) { await this.loadMarkets(); } let market = undefined; if (symbol !== undefined) { market = this.market(symbol); } const request = { 'order_id': id, }; const response = await this.privateDeleteFuturesOrdersOrderId(this.extend(request, params)); const data = this.safeDict(response, 'data', response); return this.parseOrder(data, market); } /** * @method * @name mudrex#fetchOrder * @description fetches information on an order made by the user * @see https://docs.trade.mudrex.com/docs * @param {string} id the order id * @param {string} [symbol] unified symbol of the market the order was made in * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} An [order structure](https://docs.ccxt.com/#/?id=order-structure) */ async fetchOrder(id, symbol = undefined, params = {}) { if (this.markets === undefined) { await this.loadMarkets(); } let market = undefined; if (symbol !== undefined) { market = this.market(symbol); } const request = { 'order_id': id, }; const response = await this.privateGetFuturesOrdersOrderId(this.extend(request, params)); const data = this.safeDict(response, 'data', response); return this.parseOrder(data, market); } /** * @method * @name mudrex#fetchOrdersByState * @ignore * @description fetches a list of orders filtered by their state * @param {string} state the state of the orders to fetch * @param {string} [symbol] unified market symbol * @param {int} [since] the earliest time in ms to fetch orders for * @param {int} [limit] the maximum number of order structures to retrieve * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {Order[]} a list of [order structures](https://docs.ccxt.com/#/?id=order-structure) */ async fetchOrdersByState(state, symbol = undefined, since = undefined, limit = undefined, params = {}) { if (this.markets === undefined) { await this.loadMarkets(); } const q = {}; if (limit !== undefined) { q['limit'] = limit; } const request = this.extend(q, params); let response = undefined; if (state === 'closed') { response = await this.privateGetFuturesOrdersHistory(request); } else { response = await this.privateGetFuturesOrders(request); } const data = this.safeValue(response, 'data', []); const rows = this.toArray(data); let market = undefined; if (symbol !== undefined) { market = this.market(symbol); } const orders = []; for (let i = 0; i < rows.length; i++) { orders.push(this.parseOrder(rows[i], market)); } return this.filterBySymbolSinceLimit(orders, symbol, since, limit); } /** * @method * @name mudrex#fetchOrders * @description fetches information on multiple orders made by the user * @see https://docs.trade.mudrex.com/docs * @param {string} [symbol] unified market symbol of the market orders were made in * @param {int} [since] the earliest time in ms to fetch orders for * @param {int} [limit] the maximum number of order structures to retrieve * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {Order[]} a list of [order structures](https://docs.ccxt.com/#/?id=order-structure) */ async fetchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByState('closed', symbol, since, limit, params); } /** * @method * @name mudrex#fetchOpenOrders * @description fetch all unfilled currently open orders * @see https://docs.trade.mudrex.com/docs * @param {string} [symbol] unified market symbol * @param {int} [since] the earliest time in ms to fetch open orders for * @param {int} [limit] the maximum number of open order structures to retrieve * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {Order[]} a list of [order structures](https://docs.ccxt.com/#/?id=order-structure) */ async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByState('open', symbol, since, limit, params); } /** * @method * @name mudrex#fetchClosedOrders * @description fetches information on multiple closed orders made by the user * @see https://docs.trade.mudrex.com/docs * @param {string} [symbol] unified market symbol of the market orders were made in * @param {int} [since] the earliest time in ms to fetch orders for * @param {int} [limit] the maximum number of order structures to retrieve * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {Order[]} a list of [order structures](https://docs.ccxt.com/#/?id=order-structure) */ async fetchClosedOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { return await this.fetchOrdersByState('closed', symbol, since, limit, params); } /** * @method * @name mudrex#fetchPositions * @description fetch all open positions * @see https://docs.trade.mudrex.com/docs * @param {string[]} [symbols] list of unified market symbols * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {string} [params.trade_currency] the settlement currency to query positions for * @returns {object[]} a list of [position structures](https://docs.ccxt.com/#/?id=position-structure) */ async fetchPositions(symbols = undefined, params = {}) { if (this.markets === undefined) { await this.loadMarkets(); } const q = {}; const response = await this.privateGetFuturesPositions(this.extend(q, params)); const data = this.safeValue(response, 'data', []); if (data === undefined) { return []; } const rows = this.toArray(data); const outPos = []; for (let i = 0; i < rows.length; i++) { const p = rows[i]; const symRaw = this.safeString(p, 'symbol'); const m = this.safeMarket(symRaw); const pos = this.parsePosition(p, m); outPos.push(pos); } return this.filterByArrayPositions(outPos, 'symbol', symbols, false); } /** * @method * @name mudrex#fetchPositionsHistory * @description fetches the history of closed positions * @see https://docs.trade.mudrex.com/docs/get-position-history * @param {string[]} [symbols] a list of unified market symbols * @param {int} [since] the earliest time in ms to fetch positions for * @param {int} [limit] the maximum number of position structures to retrieve * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {string} [params.trade_currency] the settlement currency to filter positions by * @returns {object[]} a list of [position structures](https://docs.ccxt.com/#/?id=position-structure) */ async fetchPositionsHistory(symbols = undefined, since = undefined, limit = undefined, params = {}) { if (this.markets === undefined) { await this.loadMarkets(); } symbols = this.marketSymbols(symbols); const request = {}; if (limit !== undefined) { request['limit'] = limit; } const response = await this.privateGetFuturesPositionsHistory(this.extend(request, params)); // // { // "success": true, // "data": [ // { // "id": "019f1ed6-...", // "position_type": "SHORT", // "status": "CLOSED", // "leverage": "3", // "entry_price": "1.3112", // "closed_price": "1.3395", // "quantity": "34", // "pnl": "-0.9622", // "created_at": "2026-07-01T10:18:57Z", // "updated_at": "2026-07-01T18:00:21Z", // "symbol": "CAKEUSDT", // "trade_currency": "USDT" // } // ] // } // const data = this.safeList(response, 'data', []); const positions = this.parsePositions(data, symbols); return this.filterBySinceLimit(positions, since, limit); } parsePosition(position, market = undefined) { market = this.safeMarket(undefined, market); const ms = this.safeString(position, 'symbol'); const symbol = this.safeSymbol(ms, market); // open positions use "order_type", closed positions (history) use "position_type" const rawSide = this.safeStringUpper2(position, 'order_type', 'position_type'); let side = undefined; if (rawSide === 'LONG') { side = 'long'; } else if (rawSide === 'SHORT') { side = 'short'; } let ts = this.parse8601(this.safeString(position, 'updated_at')); if (ts === undefined) { ts = this.parse8601(this.safeString(position, 'created_at')); } const quantityString = this.safeString(position, 'quantity'); const entryPriceString = this.safeString(position, 'entry_price'); const contractSizeString = this.safeString(market, 'contractSize', '1'); let notional = undefined; if ((quantityString !== undefined) && (entryPriceString !== undefined)) { notional = this.parseNumber(Precise["default"].stringMul(Precise["default"].stringMul(quantityString, entryPriceString), contractSizeString)); } const initialMargin = this.safeString(position, 'initial_margin'); return { 'info': position, 'id': this.safeString(position, 'id'), 'symbol': symbol, 'timestamp': ts, 'datetime': this.iso8601(ts), 'isolated': true, 'hedged': false, 'side': side, 'contracts': this.safeNumber(position, 'quantity'), 'contractSize': this.safeNumber(market, 'contractSize'), 'entryPrice': this.safeNumber(position, 'entry_price'), 'markPrice': undefined, 'lastPrice': this.safeNumber(position, 'closed_price'), // exit price for closed positions 'notional': notional, 'leverage': this.safeInteger(position, 'leverage'), 'collateral': this.parseNumber(initialMargin), 'initialMargin': this.parseNumber(initialMa