ccxt
Version:
1,119 lines (1,117 loc) • 60.8 kB
JavaScript
// ----------------------------------------------------------------------------
// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
// EDIT THE CORRESPONDENT .ts FILE INSTEAD
// ---------------------------------------------------------------------------
import Exchange from './abstract/mudrex.js';
import { ArgumentsRequired, AuthenticationError, BadRequest, BadSymbol, ExchangeError, InsufficientFunds, OrderNotFound, RateLimitExceeded, NullResponse } from './base/errors.js';
import { Precise } from './base/Precise.js';
// ---------------------------------------------------------------------------
/**
* @class mudrex
* @augments Exchange
*/
export default class mudrex extends Exchange {
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': BadRequest,
},
'broad': {
'Invalid trade currency': BadRequest,
'Params error': BadRequest,
'invalid trigger type': BadRequest,
'invalid order type': BadRequest,
'order price out of permissible range': BadRequest,
'quantity not a multiple of the quantity step': BadRequest,
'leverage out of permissible range': BadRequest,
'insufficient balance': InsufficientFunds,
'asset not found': BadSymbol,
'leverage not found': OrderNotFound,
'order not found': OrderNotFound,
'Rate limit exceeded': 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 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 = this.safeList(response, 'errors', []);
const first = this.safeDict(errors, 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 AuthenticationError(msg);
}
if (code === 429 || low.indexOf('rate') >= 0) {
throw new RateLimitExceeded(msg);
}
if (low.indexOf('insufficient') >= 0) {
throw new InsufficientFunds(msg);
}
if (code === 400) {
throw new BadRequest(msg);
}
throw new 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 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 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 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 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 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.stringMul(Precise.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.sa