UNPKG

ccxt

Version:

A JavaScript / TypeScript / Python / C# / PHP cryptocurrency trading library with support for 100+ exchanges

1,206 lines (1,204 loc) • 290 kB
// ---------------------------------------------------------------------------- // 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