ccxt
Version:
1,206 lines (1,204 loc) • 290 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/bingx.js';
import { AuthenticationError, PermissionDenied, AccountSuspended, ExchangeError, InsufficientFunds, BadRequest, OrderNotFound, DDoSProtection, BadSymbol, ArgumentsRequired, NotSupported, OperationFailed, InvalidOrder } from './base/errors.js';
import { Precise } from './base/Precise.js';
import { sha256 } from './static_dependencies/noble-hashes/sha256.js';
import { TICK_SIZE } from './base/functions/number.js';
// ---------------------------------------------------------------------------
/**
* @class bingx
* @augments Exchange
*/
export default class bingx extends Exchange {
describe() {
return this.deepExtend(super.describe(), {
'id': 'bingx',
'name': 'BingX',
'countries': ['US'],
'rateLimit': 100,
'version': 'v1',
'certified': true,
'pro': true,
'has': {
'CORS': undefined,
'spot': true,
'margin': false,
'swap': true,
'future': false,
'option': false,
'addMargin': true,
'cancelAllOrders': true,
'cancelAllOrdersAfter': true,
'cancelOrder': true,
'cancelOrders': true,
'closeAllPositions': true,
'closePosition': true,
'createMarketBuyOrderWithCost': true,
'createMarketOrderWithCost': true,
'createMarketSellOrderWithCost': true,
'createOrder': true,
'createOrders': true,
'createOrderWithTakeProfitAndStopLoss': true,
'createReduceOnlyOrder': true,
'createStopLossOrder': true,
'createStopOrder': true,
'createTakeProfitOrder': true,
'createTrailingAmountOrder': true,
'createTrailingPercentOrder': true,
'createTriggerOrder': true,
'editOrder': true,
'fetchBalance': true,
'fetchCanceledOrders': true,
'fetchClosedOrders': true,
'fetchCurrencies': true,
'fetchDepositAddress': true,
'fetchDepositAddresses': false,
'fetchDepositAddressesByNetwork': true,
'fetchDeposits': true,
'fetchDepositWithdrawFee': 'emulated',
'fetchDepositWithdrawFees': true,
'fetchFundingRate': true,
'fetchFundingRateHistory': true,
'fetchFundingRates': true,
'fetchLeverage': true,
'fetchLiquidations': false,
'fetchMarginAdjustmentHistory': false,
'fetchMarginMode': true,
'fetchMarkets': true,
'fetchMarkOHLCV': true,
'fetchMarkPrice': true,
'fetchMarkPrices': true,
'fetchMyLiquidations': true,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenInterest': true,
'fetchOpenOrders': true,
'fetchOrder': true,
'fetchOrderBook': true,
'fetchOrders': true,
'fetchPosition': true,
'fetchPositionHistory': false,
'fetchPositionMode': true,
'fetchPositions': true,
'fetchPositionsHistory': true,
'fetchTicker': true,
'fetchTickers': true,
'fetchTime': true,
'fetchTrades': true,
'fetchTradingFee': true,
'fetchTransfers': true,
'fetchWithdrawals': true,
'reduceMargin': true,
'sandbox': true,
'setLeverage': true,
'setMargin': true,
'setMarginMode': true,
'setPositionMode': true,
'transfer': true,
},
'hostname': 'bingx.com',
'urls': {
'logo': 'https://github-production-user-asset-6210df.s3.amazonaws.com/1294454/253675376-6983b72e-4999-4549-b177-33b374c195e3.jpg',
'api': {
'spot': 'https://open-api.{hostname}/openApi',
'swap': 'https://open-api.{hostname}/openApi',
'contract': 'https://open-api.{hostname}/openApi',
'wallets': 'https://open-api.{hostname}/openApi',
'user': 'https://open-api.{hostname}/openApi',
'subAccount': 'https://open-api.{hostname}/openApi',
'account': 'https://open-api.{hostname}/openApi',
'copyTrading': 'https://open-api.{hostname}/openApi',
'cswap': 'https://open-api.{hostname}/openApi',
},
'test': {
'swap': 'https://open-api-vst.{hostname}/openApi', // only swap is really "test" but since the API keys are the same, we want to keep all the functionalities when the user enables the sandboxmode
},
'www': 'https://bingx.com/',
'doc': 'https://bingx-api.github.io/docs/',
'referral': 'https://bingx.com/invite/OHETOM',
},
'fees': {
'tierBased': true,
'spot': {
'feeSide': 'get',
'maker': this.parseNumber('0.001'),
'taker': this.parseNumber('0.001'),
},
'swap': {
'feeSide': 'quote',
'maker': this.parseNumber('0.0002'),
'taker': this.parseNumber('0.0005'),
},
},
'requiredCredentials': {
'apiKey': true,
'secret': true,
},
'api': {
'spot': {
'v1': {
'public': {
'get': {
'server/time': 1,
'common/symbols': 1,
'market/trades': 1,
'market/depth': 1,
'market/kline': 1,
'ticker/24hr': 1,
'ticker/price': 1,
'ticker/bookTicker': 1,
},
},
'private': {
'get': {
'trade/query': 1,
'trade/openOrders': 1,
'trade/historyOrders': 1,
'trade/myTrades': 2,
'user/commissionRate': 5,
'account/balance': 2,
},
'post': {
'trade/order': 2,
'trade/cancel': 2,
'trade/batchOrders': 5,
'trade/order/cancelReplace': 5,
'trade/cancelOrders': 5,
'trade/cancelOpenOrders': 5,
'trade/cancelAllAfter': 5,
},
},
},
'v2': {
'public': {
'get': {
'market/depth': 1,
'market/kline': 1,
},
},
},
'v3': {
'private': {
'get': {
'get/asset/transfer': 1,
'asset/transfer': 1,
'capital/deposit/hisrec': 1,
'capital/withdraw/history': 1,
},
'post': {
'post/asset/transfer': 5,
},
},
},
},
'swap': {
'v1': {
'public': {
'get': {
'ticker/price': 1,
'market/historicalTrades': 1,
'market/markPriceKlines': 1,
'trade/multiAssetsRules': 1,
},
},
'private': {
'get': {
'positionSide/dual': 5,
'trade/batchCancelReplace': 5,
'trade/fullOrder': 2,
'maintMarginRatio': 2,
'trade/positionHistory': 2,
'positionMargin/history': 2,
'twap/openOrders': 5,
'twap/historyOrders': 5,
'twap/orderDetail': 5,
'trade/assetMode': 5,
'user/marginAssets': 5,
},
'post': {
'trade/cancelReplace': 2,
'positionSide/dual': 5,
'trade/batchCancelReplace': 5,
'trade/closePosition': 2,
'trade/getVst': 5,
'twap/order': 5,
'twap/cancelOrder': 5,
'trade/assetMode': 5,
},
},
},
'v2': {
'public': {
'get': {
'server/time': 1,
'quote/contracts': 1,
'quote/price': 1,
'quote/depth': 1,
'quote/trades': 1,
'quote/premiumIndex': 1,
'quote/fundingRate': 1,
'quote/klines': 1,
'quote/openInterest': 1,
'quote/ticker': 1,
'quote/bookTicker': 1,
},
},
'private': {
'get': {
'user/balance': 2,
'user/positions': 2,
'user/income': 2,
'trade/openOrders': 2,
'trade/openOrder': 2,
'trade/order': 2,
'trade/marginType': 5,
'trade/leverage': 2,
'trade/forceOrders': 1,
'trade/allOrders': 2,
'trade/allFillOrders': 2,
'trade/fillHistory': 2,
'user/income/export': 2,
'user/commissionRate': 2,
'quote/bookTicker': 1,
},
'post': {
'trade/order': 2,
'trade/batchOrders': 2,
'trade/closeAllPositions': 2,
'trade/cancelAllAfter': 5,
'trade/marginType': 5,
'trade/leverage': 5,
'trade/positionMargin': 5,
'trade/order/test': 2,
},
'delete': {
'trade/order': 2,
'trade/batchOrders': 2,
'trade/allOpenOrders': 2,
},
},
},
'v3': {
'public': {
'get': {
'quote/klines': 1,
},
},
},
},
'cswap': {
'v1': {
'public': {
'get': {
'market/contracts': 1,
'market/premiumIndex': 1,
'market/openInterest': 1,
'market/klines': 1,
'market/depth': 1,
'market/ticker': 1,
},
},
'private': {
'get': {
'trade/leverage': 2,
'trade/forceOrders': 2,
'trade/allFillOrders': 2,
'trade/openOrders': 2,
'trade/orderDetail': 2,
'trade/orderHistory': 2,
'trade/marginType': 2,
'user/commissionRate': 2,
'user/positions': 2,
'user/balance': 2,
},
'post': {
'trade/order': 2,
'trade/leverage': 2,
'trade/allOpenOrders': 2,
'trade/closeAllPositions': 2,
'trade/marginType': 2,
'trade/positionMargin': 2,
},
'delete': {
'trade/allOpenOrders': 2,
'trade/cancelOrder': 2,
},
},
},
},
'contract': {
'v1': {
'private': {
'get': {
'allPosition': 2,
'allOrders': 2,
'balance': 2,
},
},
},
},
'wallets': {
'v1': {
'private': {
'get': {
'capital/config/getall': 5,
'capital/deposit/address': 5,
'capital/innerTransfer/records': 1,
'capital/subAccount/deposit/address': 5,
'capital/deposit/subHisrec': 2,
'capital/subAccount/innerTransfer/records': 1,
'capital/deposit/riskRecords': 5,
},
'post': {
'capital/withdraw/apply': 5,
'capital/innerTransfer/apply': 5,
'capital/subAccountInnerTransfer/apply': 2,
'capital/deposit/createSubAddress': 2,
},
},
},
},
'subAccount': {
'v1': {
'private': {
'get': {
'list': 10,
'assets': 2,
'allAccountBalance': 2,
},
'post': {
'create': 10,
'apiKey/create': 2,
'apiKey/edit': 2,
'apiKey/del': 2,
'updateStatus': 10,
},
},
},
},
'account': {
'v1': {
'private': {
'get': {
'uid': 1,
'apiKey/query': 2,
'account/apiPermissions': 5,
'allAccountBalance': 2,
},
'post': {
'innerTransfer/authorizeSubAccount': 1,
},
},
},
'transfer': {
'v1': {
'private': {
'get': {
'subAccount/asset/transferHistory': 1,
},
'post': {
'subAccount/transferAsset/supportCoins': 1,
'subAccount/transferAsset': 1,
},
},
},
},
},
'user': {
'auth': {
'private': {
'post': {
'userDataStream': 2,
},
'put': {
'userDataStream': 2,
},
'delete': {
'userDataStream': 2,
},
},
},
},
'copyTrading': {
'v1': {
'private': {
'get': {
'swap/trace/currentTrack': 2,
},
'post': {
'swap/trace/closeTrackOrder': 2,
'swap/trace/setTPSL': 2,
'spot/trader/sellOrder': 10,
},
},
},
},
'api': {
'v3': {
'private': {
'get': {
'asset/transfer': 1,
'capital/deposit/hisrec': 1,
'capital/withdraw/history': 1,
},
'post': {
'post/asset/transfer': 1,
},
},
},
},
},
'timeframes': {
'1m': '1m',
'3m': '3m',
'5m': '5m',
'15m': '15m',
'30m': '30m',
'1h': '1h',
'2h': '2h',
'4h': '4h',
'6h': '6h',
'12h': '12h',
'1d': '1d',
'3d': '3d',
'1w': '1w',
'1M': '1M',
},
'precisionMode': TICK_SIZE,
'exceptions': {
'exact': {
'400': BadRequest,
'401': AuthenticationError,
'403': PermissionDenied,
'404': BadRequest,
'429': DDoSProtection,
'418': PermissionDenied,
'500': ExchangeError,
'504': ExchangeError,
'100001': AuthenticationError,
'100412': AuthenticationError,
'100202': InsufficientFunds,
'100204': BadRequest,
'100400': BadRequest,
'100410': OperationFailed,
'100421': BadSymbol,
'100440': ExchangeError,
'100500': OperationFailed,
'100503': ExchangeError,
'80001': BadRequest,
'80012': InsufficientFunds,
'80014': BadRequest,
'80016': OrderNotFound,
'80017': OrderNotFound,
'100414': AccountSuspended,
'100419': PermissionDenied,
'100437': BadRequest,
'101204': InsufficientFunds,
'110425': InvalidOrder,
'Insufficient assets': InsufficientFunds,
'illegal transferType': BadRequest, // {"transferErrorMsg":"illegal transferType"}
},
'broad': {},
},
'commonCurrencies': {
'SNOW': 'Snowman',
'OMNI': 'OmniCat',
'NAP': '$NAP',
'TRUMP': 'TRUMPMAGA',
'TRUMPSOL': 'TRUMP',
},
'options': {
'defaultType': 'spot',
'accountsByType': {
'funding': 'FUND',
'spot': 'SPOT',
'swap': 'PFUTURES',
'future': 'SFUTURES',
},
'accountsById': {
'FUND': 'funding',
'SPOT': 'spot',
'PFUTURES': 'swap',
'SFUTURES': 'future',
},
'recvWindow': 5 * 1000,
'broker': 'CCXT',
'defaultNetworks': {
'ETH': 'ETH',
'USDT': 'ERC20',
'USDC': 'ERC20',
'BTC': 'BTC',
'LTC': 'LTC',
},
'networks': {
'ARB': 'ARBITRUM',
'MATIC': 'POLYGON',
},
},
'features': {
'defaultForLinear': {
'sandbox': true,
'createOrder': {
'marginMode': false,
'triggerPrice': true,
'triggerPriceType': {
'last': true,
'mark': true,
'index': true,
},
'triggerDirection': false,
'stopLossPrice': true,
'takeProfitPrice': true,
'attachedStopLossTakeProfit': {
'triggerPriceType': {
'last': true,
'mark': true,
'index': true,
},
'price': true,
},
'timeInForce': {
'IOC': true,
'FOK': true,
'PO': true,
'GTD': false,
},
'hedged': true,
'trailing': true,
'leverage': false,
'marketBuyRequiresPrice': false,
'marketBuyByCost': true,
'selfTradePrevention': false,
'iceberg': false,
},
'createOrders': {
'max': 5,
},
'fetchMyTrades': {
'marginMode': false,
'limit': 512,
'daysBack': 30,
'untilDays': 30,
'symbolRequired': true,
},
'fetchOrder': {
'marginMode': false,
'trigger': false,
'trailing': false,
'symbolRequired': true,
},
'fetchOpenOrders': {
'marginMode': false,
'limit': undefined,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchOrders': {
'marginMode': false,
'limit': 1000,
'daysBack': 20000,
'untilDays': 7,
'trigger': false,
'trailing': false,
'symbolRequired': true,
},
'fetchClosedOrders': {
'marginMode': false,
'limit': 1000,
'daysBack': undefined,
'daysBackCanceled': undefined,
'untilDays': 7,
'trigger': false,
'trailing': false,
'symbolRequired': true,
},
'fetchOHLCV': {
'limit': 1440,
},
},
'defaultForInverse': {
'extends': 'defaultForLinear',
'fetchMyTrades': {
'limit': 1000,
'daysBack': undefined,
'untilDays': undefined,
},
'fetchOrders': undefined,
},
//
'spot': {
'extends': 'defaultForLinear',
'createOrder': {
'triggerPriceType': undefined,
'attachedStopLossTakeProfit': undefined,
'trailing': false,
},
'fetchMyTrades': {
'limit': 1000,
'daysBack': 1,
'untilDays': 1,
},
'fetchOrders': undefined,
'fetchClosedOrders': {
'limit': 100,
'untilDays': undefined,
},
},
'swap': {
'linear': {
'extends': 'defaultForLinear',
},
'inverse': {
'extends': 'defaultForInverse',
},
},
'defaultForFuture': {
'extends': 'defaultForLinear',
'fetchOrders': undefined,
},
'future': {
'linear': {
'extends': 'defaultForFuture',
},
'inverse': {
'extends': 'defaultForFuture',
},
},
},
});
}
/**
* @method
* @name bingx#fetchTime
* @description fetches the current integer timestamp in milliseconds from the bingx server
* @see https://bingx-api.github.io/docs/#/swapV2/base-info.html#Get%20Server%20Time
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {int} the current integer timestamp in milliseconds from the bingx server
*/
async fetchTime(params = {}) {
const response = await this.swapV2PublicGetServerTime(params);
//
// {
// "code": 0,
// "msg": "",
// "data": {
// "serverTime": 1675319535362
// }
// }
//
const data = this.safeDict(response, 'data');
return this.safeInteger(data, 'serverTime');
}
/**
* @method
* @name bingx#fetchCurrencies
* @description fetches all available currencies on an exchange
* @see https://bingx-api.github.io/docs/#/common/account-api.html#All%20Coins
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an associative dictionary of currencies
*/
async fetchCurrencies(params = {}) {
if (!this.checkRequiredCredentials(false)) {
return undefined;
}
const isSandbox = this.safeBool(this.options, 'sandboxMode', false);
if (isSandbox) {
return undefined;
}
const response = await this.walletsV1PrivateGetCapitalConfigGetall(params);
//
// {
// "code": 0,
// "timestamp": 1702623271476,
// "data": [
// {
// "coin": "BTC",
// "name": "BTC",
// "networkList": [
// {
// "name": "BTC",
// "network": "BTC",
// "isDefault": true,
// "minConfirm": 2,
// "withdrawEnable": true,
// "depositEnable": true,
// "withdrawFee": "0.0006",
// "withdrawMax": "1.17522",
// "withdrawMin": "0.0005",
// "depositMin": "0.0002"
// },
// {
// "name": "BTC",
// "network": "BEP20",
// "isDefault": false,
// "minConfirm": 15,
// "withdrawEnable": true,
// "depositEnable": true,
// "withdrawFee": "0.0000066",
// "withdrawMax": "1.17522",
// "withdrawMin": "0.0000066",
// "depositMin": "0.0002"
// }
// ]
// }
// ]
// }
//
const data = this.safeList(response, 'data', []);
const result = {};
for (let i = 0; i < data.length; i++) {
const entry = data[i];
const currencyId = this.safeString(entry, 'coin');
const code = this.safeCurrencyCode(currencyId);
const name = this.safeString(entry, 'name');
const networkList = this.safeList(entry, 'networkList');
const networks = {};
for (let j = 0; j < networkList.length; j++) {
const rawNetwork = networkList[j];
const network = this.safeString(rawNetwork, 'network');
const networkCode = this.networkIdToCode(network);
const limits = {
'withdraw': {
'min': this.safeNumber(rawNetwork, 'withdrawMin'),
'max': this.safeNumber(rawNetwork, 'withdrawMax'),
},
};
const precision = this.parseNumber(this.parsePrecision(this.safeString(rawNetwork, 'withdrawPrecision')));
networks[networkCode] = {
'info': rawNetwork,
'id': network,
'network': networkCode,
'fee': this.safeNumber(rawNetwork, 'withdrawFee'),
'active': undefined,
'deposit': this.safeBool(rawNetwork, 'depositEnable'),
'withdraw': this.safeBool(rawNetwork, 'withdrawEnable'),
'precision': precision,
'limits': limits,
};
}
result[code] = this.safeCurrencyStructure({
'info': entry,
'code': code,
'id': currencyId,
'precision': undefined,
'name': name,
'active': undefined,
'deposit': undefined,
'withdraw': undefined,
'networks': networks,
'fee': undefined,
'limits': undefined,
'type': 'crypto', // only cryptos now
});
}
return result;
}
async fetchSpotMarkets(params) {
const response = await this.spotV1PublicGetCommonSymbols(params);
//
// {
// "code": 0,
// "msg": "",
// "debugMsg": "",
// "data": {
// "symbols": [
// {
// "symbol": "GEAR-USDT",
// "minQty": 735, // deprecated
// "maxQty": 2941177, // deprecated.
// "minNotional": 5,
// "maxNotional": 20000,
// "status": 1,
// "tickSize": 0.000001,
// "stepSize": 1,
// "apiStateSell": true,
// "apiStateBuy": true,
// "timeOnline": 0,
// "offTime": 0,
// "maintainTime": 0
// },
// ...
// ]
// }
// }
//
const data = this.safeDict(response, 'data');
const markets = this.safeList(data, 'symbols', []);
return this.parseMarkets(markets);
}
async fetchSwapMarkets(params) {
const response = await this.swapV2PublicGetQuoteContracts(params);
//
// {
// "code": 0,
// "msg": "",
// "data": [
// {
// "contractId": "100",
// "symbol": "BTC-USDT",
// "size": "0.0001",
// "quantityPrecision": "4",
// "pricePrecision": "1",
// "feeRate": "0.0005",
// "makerFeeRate": "0.0002",
// "takerFeeRate": "0.0005",
// "tradeMinLimit": "0",
// "tradeMinQuantity": "0.0001",
// "tradeMinUSDT": "2",
// "maxLongLeverage": "125",
// "maxShortLeverage": "125",
// "currency": "USDT",
// "asset": "BTC",
// "status": "1",
// "apiStateOpen": "true",
// "apiStateClose": "true",
// "ensureTrigger": true,
// "triggerFeeRate": "0.00020000"
// },
// ...
// ]
// }
//
const markets = this.safeList(response, 'data', []);
return this.parseMarkets(markets);
}
async fetchInverseSwapMarkets(params) {
const response = await this.cswapV1PublicGetMarketContracts(params);
//
// {
// "code": 0,
// "msg": "",
// "timestamp": 1720074487610,
// "data": [
// {
// "symbol": "BNB-USD",
// "pricePrecision": 2,
// "minTickSize": "10",
// "minTradeValue": "10",
// "minQty": "1.00000000",
// "status": 1,
// "timeOnline": 1713175200000
// },
// ]
// }
//
const markets = this.safeList(response, 'data', []);
return this.parseMarkets(markets);
}
parseMarket(market) {
const id = this.safeString(market, 'symbol');
const symbolParts = id.split('-');
const baseId = symbolParts[0];
const quoteId = symbolParts[1];
const base = this.safeCurrencyCode(baseId);
const quote = this.safeCurrencyCode(quoteId);
let currency = this.safeString(market, 'currency');
let checkIsInverse = false;
let checkIsLinear = true;
const minTickSize = this.safeNumber(market, 'minTickSize');
if (minTickSize !== undefined) {
// inverse swap market
currency = baseId;
checkIsInverse = true;
checkIsLinear = false;
}
const settle = this.safeCurrencyCode(currency);
let pricePrecision = this.safeNumber(market, 'tickSize');
if (pricePrecision === undefined) {
pricePrecision = this.parseNumber(this.parsePrecision(this.safeString(market, 'pricePrecision')));
}
let quantityPrecision = this.safeNumber(market, 'stepSize');
if (quantityPrecision === undefined) {
quantityPrecision = this.parseNumber(this.parsePrecision(this.safeString(market, 'quantityPrecision')));
}
const type = (settle !== undefined) ? 'swap' : 'spot';
const spot = type === 'spot';
const swap = type === 'swap';
let symbol = base + '/' + quote;
if (settle !== undefined) {
symbol += ':' + settle;
}
const fees = this.safeDict(this.fees, type, {});
const contractSize = (swap) ? this.parseNumber('1') : undefined;
let isActive = false;
if ((this.safeString(market, 'apiStateOpen') === 'true') && (this.safeString(market, 'apiStateClose') === 'true')) {
isActive = true; // swap active
}
else if (this.safeBool(market, 'apiStateSell') && this.safeBool(market, 'apiStateBuy') && (this.safeString(market, 'status') === '1')) {
isActive = true; // spot active
}
const isInverse = (spot) ? undefined : checkIsInverse;
const isLinear = (spot) ? undefined : checkIsLinear;
let minAmount = undefined;
if (!spot) {
minAmount = this.safeNumber2(market, 'minQty', 'tradeMinQuantity');
}
let timeOnline = this.safeInteger(market, 'timeOnline');
if (timeOnline === 0) {
timeOnline = undefined;
}
return this.safeMarketStructure({
'id': id,
'symbol': symbol,
'base': base,
'quote': quote,
'settle': settle,
'baseId': baseId,
'quoteId': quoteId,
'settleId': currency,
'type': type,
'spot': spot,
'margin': false,
'swap': swap,
'future': false,
'option': false,
'active': isActive,
'contract': swap,
'linear': isLinear,
'inverse': isInverse,
'taker': this.safeNumber(fees, 'taker'),
'maker': this.safeNumber(fees, 'maker'),
'feeSide': this.safeString(fees, 'feeSide'),
'contractSize': contractSize,
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'precision': {
'amount': quantityPrecision,
'price': pricePrecision,
},
'limits': {
'leverage': {
'min': undefined,
'max': undefined,
},
'amount': {
'min': minAmount,
'max': undefined,
},
'price': {
'min': minTickSize,
'max': undefined,
},
'cost': {
'min': this.safeNumberN(market, ['minNotional', 'tradeMinUSDT', 'minTradeValue']),
'max': this.safeNumber(market, 'maxNotional'),
},
},
'created': timeOnline,
'info': market,
});
}
/**
* @method
* @name bingx#fetchMarkets
* @description retrieves data on all markets for bingx
* @see https://bingx-api.github.io/docs/#/spot/market-api.html#Query%20Symbols
* @see https://bingx-api.github.io/docs/#/swapV2/market-api.html#Contract%20Information
* @see https://bingx-api.github.io/docs/#/en-us/cswap/market-api.html#Contract%20Information
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} an array of objects representing market data
*/
async fetchMarkets(params = {}) {
const requests = [this.fetchSwapMarkets(params)];
const isSandbox = this.safeBool(this.options, 'sandboxMode', false);
if (!isSandbox) {
requests.push(this.fetchInverseSwapMarkets(params));
requests.push(this.fetchSpotMarkets(params)); // sandbox is swap only
}
const promises = await Promise.all(requests);
const linearSwapMarkets = this.safeList(promises, 0, []);
const inverseSwapMarkets = this.safeList(promises, 1, []);
const spotMarkets = this.safeList(promises, 2, []);
const swapMarkets = this.arrayConcat(linearSwapMarkets, inverseSwapMarkets);
return this.arrayConcat(spotMarkets, swapMarkets);
}
/**
* @method
* @name bingx#fetchOHLCV
* @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @see https://bingx-api.github.io/docs/#/swapV2/market-api.html#K-Line%20Data
* @see https://bingx-api.github.io/docs/#/spot/market-api.html#Candlestick%20chart%20data
* @see https://bingx-api.github.io/docs/#/swapV2/market-api.html#%20K-Line%20Data
* @see https://bingx-api.github.io/docs/#/en-us/swapV2/market-api.html#Mark%20Price%20Kline/Candlestick%20Data
* @see https://bingx-api.github.io/docs/#/en-us/cswap/market-api.html#Get%20K-line%20Data
* @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 {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
* @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
*/
async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
let paginate = false;
[paginate, params] = this.handleOptionAndParams(params, 'fetchOHLCV', 'paginate', false);
if (paginate) {
return await this.fetchPaginatedCallDeterministic('fetchOHLCV', symbol, since, limit, timeframe, params, 1440);
}
const market = this.market(symbol);
const request = {
'symbol': market['id'],
};
request['interval'] = this.safeString(this.timeframes, timeframe, timeframe);
if (since !== undefined) {
request['startTime'] = Math.max(since - 1, 0);
}
if (limit !== undefined) {
request['limit'] = limit;
}
const until = this.safeInteger2(params, 'until', 'endTime');
if (until !== undefined) {
params = this.omit(params, ['until']);
request['endTime'] = until;
}
let response = undefined;
if (market['spot']) {
response = await this.spotV1PublicGetMarketKline(this.extend(request, params));
}
else {
if (market['inverse']) {
response = await this.cswapV1PublicGetMarketKlines(this.extend(request, params));
}
else {
const price = this.safeString(params, 'price');
params = this.omit(params, 'price');
if (price === 'mark') {
response = await this.swapV1PublicGetMarketMarkPriceKlines(this.extend(request, params));
}
else {
response = await this.swapV3PublicGetQuoteKlines(this.extend(request, params));
}
}
}
//
// {
// "code": 0,
// "msg": "",
// "data": [
// {
// "open": "19396.8",
// "close": "19394.4",
// "high": "19397.5",
// "low": "19385.7",
// "volume": "110.05",
// "time": 1666583700000
// },
// ...
// ]
// }
//
// fetchMarkOHLCV
//
// {
// "code": 0,
// "msg": "",
// "data": [
// {
// "open": "42191.7",
// "close": "42189.5",
// "high": "42196.5",
// "low": "42189.5",
// "volume": "0.00",
// "openTime": 1706508840000,
// "closeTime": 1706508840000
// }
// ]
// }
//
let ohlcvs = this.safeValue(response, 'data', []);
if (!Array.isArray(ohlcvs)) {
ohlcvs = [ohlcvs];
}
return this.parseOHLCVs(ohlcvs, market, timeframe, since, limit);
}
parseOHLCV(ohlcv, market = undefined) {
//
// {
// "open": "19394.4",
// "close": "19379.0",
// "high": "19394.4",
// "low": "19368.3",
// "volume": "167.44",
// "time": 1666584000000
// }
//
// fetchMarkOHLCV
//
// {
// "open": "42191.7",
// "close": "42189.5",
// "high": "42196.5",
// "low": "42189.5",
// "volume": "0.00",
// "openTime": 1706508840000,
// "closeTime": 1706508840000
// }
// spot
// [
// 1691402580000,
// 29093.61,
// 29093.93,
// 29087.73,
// 29093.24,
// 0.59,
// 1691402639999,
// 17221.07
// ]
//
if (Array.isArray(ohlcv)) {
return [
this.safeInteger(ohlcv, 0),
this.safeNumber(ohlcv, 1),
this.safeNumber(ohlcv, 2),
this.safeNumber(ohlcv, 3),
this.safeNumber(ohlcv, 4),
this.safeNumber(ohlcv, 5),
];
}
return [
this.safeInteger2(ohlcv, 'time', 'closeTime'),
this.safeNumber(ohlcv, 'open'),
this.safeNumber(ohlcv, 'high'),
this.safeNumber(ohlcv, 'low'),
this.safeNumber(ohlcv, 'close'),
this.safeNumber(ohlcv, 'volume'),
];
}
/**
* @method
* @name bingx#fetchTrades
* @description get the list of most recent trades for a particular symbol
* @see https://bingx-api.github.io/docs/#/spot/market-api.html#Query%20transaction%20records
* @see https://bingx-api.github.io/docs/#/swapV2/market-api.html#The%20latest%20Trade%20of%20a%20Trading%20Pair
* @param {string} symbol unified symbol of the market to fetch trades for
* @param {int} [since] timestamp in ms of the earliest trade to fetch
* @param {int} [limit] the maximum amount of trades to fetch
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
*/
async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'symbol': market['id'],
};
if (limit !== undefined) {
request['limit'] = Math.min(limit, 100); // avoid API exception "limit should less than 100"
}
let response = undefined;
let marketType = undefined;
[marketType, params] = this.handleMarketTypeAndParams('fetchTrades', market, params);
if (marketType === 'spot') {
response = await this.spotV1PublicGetMarketTrades(this.extend(request, params));
}
else {
response = await thi