# -*- coding: utf-8 -*- # 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 from ccxt.base.exchange import Exchange from ccxt.abstract.bingx import ImplicitAPI import hashlib import numbers from ccxt.base.types import Any, Balances, Currencies, Currency, DepositAddress, Int, Leverage, MarginMode, MarginModification, Market, Num, Order, OrderBook, OrderRequest, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, FundingRate, FundingRates, Trade, TradingFeeInterface, Transaction, TransferEntry from typing import List from ccxt.base.errors import ExchangeError from ccxt.base.errors import AuthenticationError from ccxt.base.errors import PermissionDenied from ccxt.base.errors import AccountSuspended from ccxt.base.errors import ArgumentsRequired from ccxt.base.errors import BadRequest from ccxt.base.errors import BadSymbol from ccxt.base.errors import InsufficientFunds from ccxt.base.errors import InvalidOrder from ccxt.base.errors import OrderNotFound from ccxt.base.errors import NotSupported from ccxt.base.errors import OperationFailed from ccxt.base.errors import DDoSProtection from ccxt.base.decimal_to_precision import TICK_SIZE from ccxt.base.precise import Precise class bingx(Exchange, ImplicitAPI): def describe(self) -> Any: return self.deep_extend(super(bingx, self).describe(), { 'id': 'bingx', 'name': 'BingX', 'countries': ['US'], # North America, Canada, the EU, Hong Kong and Taiwan 'rateLimit': 100, 'version': 'v1', 'certified': True, 'pro': True, 'has': { 'CORS': None, 'spot': True, 'margin': False, 'swap': True, 'future': False, 'option': False, 'addMargin': True, 'borrowCrossMargin': False, 'borrowIsolatedMargin': False, 'borrowMargin': False, '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, 'fetchAllGreeks': False, 'fetchBalance': True, 'fetchBorrowInterest': False, 'fetchBorrowRate': False, 'fetchBorrowRateHistories': False, 'fetchBorrowRateHistory': False, 'fetchBorrowRates': False, 'fetchBorrowRatesPerSymbol': False, 'fetchCanceledOrders': True, 'fetchClosedOrders': True, 'fetchCrossBorrowRate': False, 'fetchCrossBorrowRates': False, 'fetchCurrencies': True, 'fetchDepositAddress': True, 'fetchDepositAddresses': False, 'fetchDepositAddressesByNetwork': True, 'fetchDeposits': True, 'fetchDepositWithdrawFee': 'emulated', 'fetchDepositWithdrawFees': True, 'fetchFundingRate': True, 'fetchFundingRateHistory': True, 'fetchFundingRates': True, 'fetchGreeks': False, 'fetchIsolatedBorrowRate': False, 'fetchIsolatedBorrowRates': False, '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, 'fetchOption': False, 'fetchOptionChain': False, '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, 'fetchVolatilityHistory': False, 'fetchWithdrawals': True, 'reduceMargin': True, 'repayCrossMargin': False, 'repayIsolatedMargin': False, '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': { 'fund': 'https://open-api.{hostname}/openApi', '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', 'api': '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': self.parse_number('0.001'), 'taker': self.parse_number('0.001'), }, 'swap': { 'feeSide': 'quote', 'maker': self.parse_number('0.0002'), 'taker': self.parse_number('0.0005'), }, }, 'requiredCredentials': { 'apiKey': True, 'secret': True, }, 'api': { 'fund': { 'v1': { 'private': { 'get': { 'account/balance': 1, }, }, }, }, '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, 'oco/orderList': 5, 'oco/openOrderList': 5, 'oco/historyOrderList': 5, }, 'post': { 'trade/order': 2, 'trade/cancel': 2, 'trade/batchOrders': 5, 'trade/order/cancelReplace': 5, 'trade/cancelOrders': 5, 'trade/cancelOpenOrders': 5, 'trade/cancelAllAfter': 5, 'oco/order': 5, 'oco/cancel': 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, 'tradingRules': 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, 'trade/reverse': 5, 'trade/autoAddMargin': 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, }, }, 'private': { 'get': { 'user/balance': 2, }, }, }, }, '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, # post method in doc '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, 'asset/transferRecord': 5, 'capital/deposit/hisrec': 1, 'capital/withdraw/history': 1, }, 'post': { 'post/asset/transfer': 1, }, }, }, 'asset': { 'v1': { 'private': { 'post': { 'transfer': 5, }, }, 'public': { 'get': { 'transfer/supportCoins': 5, }, }, }, }, }, 'agent': { 'v1': { 'private': { 'get': { 'account/inviteAccountList': 5, 'reward/commissionDataList': 5, 'account/inviteRelationCheck': 5, 'asset/depositDetailList': 5, 'reward/third/commissionDataList': 5, 'asset/partnerData': 5, 'commissionDataList/referralCode': 5, 'account/superiorCheck': 5, }, }, }, }, }, '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, # {"code":100410,"msg":"The current system is busy, please try again later"} '100421': BadSymbol, # {"code":100421,"msg":"This pair is currently restricted from API trading","debugMsg":""} '100440': ExchangeError, '100500': OperationFailed, # {"code":100500,"msg":"The current system is busy, please try again later","debugMsg":""} '100503': ExchangeError, '80001': BadRequest, '80012': InsufficientFunds, # {"code":80012,"msg":"{\"Code\":101253,\"Msg\":\"margin is not enough\"}} '80014': BadRequest, '80016': OrderNotFound, '80017': OrderNotFound, '100414': AccountSuspended, # {"code":100414,"msg":"Code: 100414, Msg: risk control check fail,code(1)","debugMsg":""} '100419': PermissionDenied, # {"code":100419,"msg":"IP does not match IP whitelist","success":false,"timestamp":1705274099347} '100437': BadRequest, # {"code":100437,"msg":"The withdrawal amount is lower than the minimum limit, please re-enter.","timestamp":1689258588845} '101204': InsufficientFunds, # {"code":101204,"msg":"","data":{}} '110425': InvalidOrder, # {"code":110425,"msg":"Please ensure that the minimum nominal value of the order placed must be greater than 2u","data":{}} 'Insufficient assets': InsufficientFunds, # {"transferErrorMsg":"Insufficient assets"} 'illegal transferType': BadRequest, # {"transferErrorMsg":"illegal transferType"} }, 'broad': {}, }, 'commonCurrencies': { 'SNOW': 'Snowman', # Snowman vs SnowSwap conflict 'OMNI': 'OmniCat', 'NAP': '$NAP', # NAP on SOL = SNAP 'TRUMP': 'TRUMPMAGA', 'TRUMPSOL': 'TRUMP', }, 'options': { 'defaultType': 'spot', 'accountsByType': { 'funding': 'fund', 'spot': 'spot', 'future': 'stdFutures', 'swap': 'USDTMPerp', 'linear': 'USDTMPerp', 'inverse': 'coinMPerp', }, 'accountsById': { 'fund': 'funding', 'spot': 'spot', 'stdFutures': 'future', 'USDTMPerp': 'linear', 'coinMPerp': 'inverse', }, 'recvWindow': 5 * 1000, # 5 sec 'broker': 'CCXT', 'defaultNetworks': { 'ETH': 'ETH', 'USDT': 'ERC20', 'USDC': 'ERC20', 'BTC': 'BTC', 'LTC': 'LTC', }, 'networks': { 'ARBITRUM': 'ARB', 'MATIC': 'POLYGON', 'ZKSYNC': 'ZKSYNCERA', 'AVAXC': 'AVAX-C', 'HBAR': 'HEDERA', }, }, '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, # 512 days for 'allFillOrders', 1000 days for 'fillOrders' 'daysBack': 30, # 30 for 'allFillOrders', 7 for 'fillHistory' 'untilDays': 30, # 30 for 'allFillOrders', 7 for 'fillHistory' 'symbolRequired': True, }, 'fetchOrder': { 'marginMode': False, 'trigger': False, 'trailing': False, 'symbolRequired': True, }, 'fetchOpenOrders': { 'marginMode': False, 'limit': None, 'trigger': False, 'trailing': False, 'symbolRequired': False, }, 'fetchOrders': { 'marginMode': False, 'limit': 1000, 'daysBack': 20000, # since epoch 'untilDays': 7, 'trigger': False, 'trailing': False, 'symbolRequired': True, }, 'fetchClosedOrders': { 'marginMode': False, 'limit': 1000, 'daysBack': None, 'daysBackCanceled': None, 'untilDays': 7, 'trigger': False, 'trailing': False, 'symbolRequired': True, }, 'fetchOHLCV': { 'limit': 1440, }, }, 'defaultForInverse': { 'extends': 'defaultForLinear', 'fetchMyTrades': { 'limit': 1000, 'daysBack': None, 'untilDays': None, }, 'fetchOrders': None, }, # 'spot': { 'extends': 'defaultForLinear', 'fetchCurrencies': { 'private': True, }, 'createOrder': { 'triggerPriceType': None, 'attachedStopLossTakeProfit': None, 'trailing': False, }, 'fetchMyTrades': { 'limit': 1000, 'daysBack': 1, 'untilDays': 1, }, 'fetchOrders': None, 'fetchClosedOrders': { 'limit': 100, 'untilDays': None, }, }, 'swap': { 'linear': { 'extends': 'defaultForLinear', }, 'inverse': { 'extends': 'defaultForInverse', }, }, 'defaultForFuture': { 'extends': 'defaultForLinear', 'fetchOrders': None, }, 'future': { 'linear': { 'extends': 'defaultForFuture', }, 'inverse': { 'extends': 'defaultForFuture', }, }, }, }) def fetch_time(self, params={}) -> Int: """ fetches the current integer timestamp in milliseconds from the bingx server https://bingx-api.github.io/docs/#/swapV2/base-info.html#Get%20Server%20Time :param dict [params]: extra parameters specific to the exchange API endpoint :returns int: the current integer timestamp in milliseconds from the bingx server """ response = self.swapV2PublicGetServerTime(params) # # { # "code": 0, # "msg": "", # "data": { # "serverTime": 1675319535362 # } # } # data = self.safe_dict(response, 'data') return self.safe_integer(data, 'serverTime') def fetch_currencies(self, params={}) -> Currencies: """ fetches all available currencies on an exchange https://bingx-api.github.io/docs/#/common/account-api.html#All%20Coins :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: an associative dictionary of currencies """ if not self.check_required_credentials(False): return {} isSandbox = self.safe_bool(self.options, 'sandboxMode', False) if isSandbox: return {} response = self.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" # } # ] # } # ] # } # data = self.safe_list(response, 'data', []) result: dict = {} for i in range(0, len(data)): entry = data[i] currencyId = self.safe_string(entry, 'coin') code = self.safe_currency_code(currencyId) name = self.safe_string(entry, 'name') networkList = self.safe_list(entry, 'networkList') networks: dict = {} for j in range(0, len(networkList)): rawNetwork = networkList[j] network = self.safe_string(rawNetwork, 'network') networkCode = self.network_id_to_code(network) limits: dict = { 'withdraw': { 'min': self.safe_number(rawNetwork, 'withdrawMin'), 'max': self.safe_number(rawNetwork, 'withdrawMax'), }, 'deposit': { 'min': self.safe_number(rawNetwork, 'depositMin'), 'max': None, }, } precision = self.parse_number(self.parse_precision(self.safe_string(rawNetwork, 'withdrawPrecision'))) networks[networkCode] = { 'info': rawNetwork, 'id': network, 'network': networkCode, 'fee': self.safe_number(rawNetwork, 'withdrawFee'), 'active': None, 'deposit': self.safe_bool(rawNetwork, 'depositEnable'), 'withdraw': self.safe_bool(rawNetwork, 'withdrawEnable'), 'precision': precision, 'limits': limits, } if not (code in result): # the exchange could return the same currency with different networks result[code] = { 'info': entry, 'code': code, 'id': currencyId, 'precision': None, 'name': name, 'active': None, 'deposit': None, 'withdraw': None, 'networks': networks, 'fee': None, 'limits': None, 'type': 'crypto', # only cryptos now } else: existing = result[code] existingNetworks = self.safe_dict(existing, 'networks', {}) newNetworkCodes = list(networks.keys()) for j in range(0, len(newNetworkCodes)): newNetworkCode = newNetworkCodes[j] if not (newNetworkCode in existingNetworks): existingNetworks[newNetworkCode] = networks[newNetworkCode] result[code]['networks'] = existingNetworks codes = list(result.keys()) for i in range(0, len(codes)): code = codes[i] currency = result[code] result[code] = self.safe_currency_structure(currency) return result def fetch_spot_markets(self, params) -> List[Market]: response = self.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 # }, # ... # ] # } # } # data = self.safe_dict(response, 'data') markets = self.safe_list(data, 'symbols', []) return self.parse_markets(markets) def fetch_swap_markets(self, params): response = self.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" # }, # ... # ] # } # markets = self.safe_list(response, 'data', []) return self.parse_markets(markets) def fetch_inverse_swap_markets(self, params): response = self.cswapV1PublicGetMarketContracts(params) # # { # "code": 0, # "msg": "", # "timestamp": 1720074487610, # "data": [ # { # "symbol": "BNB-USD", # "pricePrecision": 2, # "minTickSize": "10", # "minTradeValue": "10", # "minQty": "1.00000000", # "status": 1, # "timeOnline": 1713175200000 # }, # ] # } # markets = self.safe_list(response, 'data', []) return self.parse_markets(markets) def parse_market(self, market: dict) -> Market: id = self.safe_string(market, 'symbol') symbolParts = id.split('-') baseId = symbolParts[0] quoteId = symbolParts[1] base = self.safe_currency_code(baseId) quote = self.safe_currency_code(quoteId) currency = self.safe_string(market, 'currency') checkIsInverse = False checkIsLinear = True minTickSize = self.safe_number(market, 'minTickSize') if minTickSize is not None: # inverse swap market currency = baseId checkIsInverse = True checkIsLinear = False settle = self.safe_currency_code(currency) pricePrecision = self.safe_number(market, 'tickSize') if pricePrecision is None: pricePrecision = self.parse_number(self.parse_precision(self.safe_string(market, 'pricePrecision'))) quantityPrecision = self.safe_number(market, 'stepSize') if quantityPrecision is None: quantityPrecision = self.parse_number(self.parse_precision(self.safe_string(market, 'quantityPrecision'))) type = 'swap' if (settle is not None) else 'spot' spot = type == 'spot' swap = type == 'swap' symbol = base + '/' + quote if settle is not None: symbol += ':' + settle fees = self.safe_dict(self.fees, type, {}) contractSize = self.parse_number('1') if (swap) else None isActive = False if (self.safe_string(market, 'apiStateOpen') == 'true') and (self.safe_string(market, 'apiStateClose') == 'true'): isActive = True # swap active elif self.safe_bool(market, 'apiStateSell') and self.safe_bool(market, 'apiStateBuy') and (self.safe_string(market, 'status') == '1'): isActive = True # spot active isInverse = None if (spot) else checkIsInverse isLinear = None if (spot) else checkIsLinear minAmount = None if not spot: minAmount = self.safe_number_2(market, 'minQty', 'tradeMinQuantity') timeOnline = self.safe_integer(market, 'timeOnline') if timeOnline == 0: timeOnline = None return self.safe_market_structure({ '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': self.safe_number(fees, 'taker'), 'maker': self.safe_number(fees, 'maker'), 'feeSide': self.safe_string(fees, 'feeSide'), 'contractSize': contractSize, 'expiry': None, 'expiryDatetime': None, 'strike': None, 'optionType': None, 'precision': { 'amount': quantityPrecision, 'price': pricePrecision, }, 'limits': { 'leverage': { 'min': None, 'max': None, }, 'amount': { 'min': minAmount, 'max': None, }, 'price': { 'min': minTickSize, 'max': None, }, 'cost': { 'min': self.safe_number_n(market, ['minNotional', 'tradeMinUSDT', 'minTradeValue']), 'max': self.safe_number(market, 'maxNotional'), }, }, 'created': timeOnline, 'info': market, }) def fetch_markets(self, params={}) -> List[Market]: """ retrieves data on all markets for bingx https://bingx-api.github.io/docs/#/spot/market-api.html#Query%20Symbols https://bingx-api.github.io/docs/#/swapV2/market-api.html#Contract%20Information https://bingx-api.github.io/docs/#/en-us/cswap/market-api.html#Contract%20Information :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict[]: an array of objects representing market data """ requests = [self.fetch_swap_markets(params)] isSandbox = self.safe_bool(self.options, 'sandboxMode', False) if not isSandbox: requests.append(self.fetch_inverse_swap_markets(params)) requests.append(self.fetch_spot_markets(params)) # sandbox is swap only promises = requests linearSwapMarkets = self.safe_list(promises, 0, []) inverseSwapMarkets = self.safe_list(promises, 1, []) spotMarkets = self.safe_list(promises, 2, []) swapMarkets = self.array_concat(linearSwapMarkets, inverseSwapMarkets) return self.array_concat(spotMarkets, swapMarkets) def fetch_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}) -> List[list]: """ fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market https://bingx-api.github.io/docs/#/swapV2/market-api.html#K-Line%20Data https://bingx-api.github.io/docs/#/spot/market-api.html#Candlestick%20chart%20data https://bingx-api.github.io/docs/#/swapV2/market-api.html#%20K-Line%20Data https://bingx-api.github.io/docs/#/en-us/swapV2/market-api.html#Mark%20Price%20Kline/Candlestick%20Data https://bingx-api.github.io/docs/#/en-us/cswap/market-api.html#Get%20K-line%20Data :param str symbol: unified symbol of the market to fetch OHLCV data for :param str 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 dict [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 self 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, open, high, low, close, volume """ self.load_markets() paginate = False paginate, params = self.handle_option_and_params(params, 'fetchOHLCV', 'paginate', False) if paginate: return self.fetch_paginated_call_deterministic('fetchOHLCV', symbol, since, limit, timeframe, params, 1440) market = self.market(symbol) request: dict = { 'symbol': market['id'], } request['interval'] = self.safe_string(self.timeframes, timeframe, timeframe) if since is not None: request['startTime'] = max(since - 1, 0) if limit is not None: request['limit'] = limit until = self.safe_integer_2(params, 'until', 'endTime') if until is not None: params = self.omit(params, ['until']) request['endTime'] = until response = None if market['spot']: response = self.spotV1PublicGetMarketKline(self.extend(request, params)) else: if market['inverse']: response = self.cswapV1PublicGetMarketKlines(self.extend(request, params)) else: price = self.safe_string(params, 'price') params = self.omit(params, 'price') if price == 'mark': response = self.swapV1PublicGetMarketMarkPriceKlines(self.extend(request, params)) else: response = self.swapV3PublicGetQuoteKlines(self.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 # } # ] # } # ohlcvs = self.safe_value(response, 'data', []) if not isinstance(ohlcvs, list): ohlcvs = [ohlcvs] return self.parse_ohlcvs(ohlcvs, market, timeframe, since, limit) def parse_ohlcv(self, ohlcv, market: Market = None) -> list: # # { # "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 isinstance(ohlcv, list): return [ self.safe_integer(ohlcv, 0), self.safe_number(ohlcv, 1), self.safe_number(ohlcv, 2), self.safe_number(ohlcv, 3), self.safe_number(ohlcv, 4), self.safe_number(ohlcv, 5), ] return [ self.safe_integer_2(ohlcv, 'time', 'closeTime'), self.safe_number(ohlcv, 'open'), self.safe_number(ohlcv, 'high'), self.safe_number(ohlcv, 'low'), self.safe_number(ohlcv, 'close'), self.safe_number(ohlcv, 'volume'), ] def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]: """ get the list of most recent trades for a particular symbol https://bingx-api.github.io/docs/#/spot/market-api.html#Query%20transaction%20records https://bingx-api.github.io/docs/#/swapV2/market-api.html#The%20latest%20Trade%20of%20a%20Trading%20Pair :param str 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 dict [params]: extra parameters specific to the exchange API endpoint :returns dict[]: a list of `trade structures ` """ self.load_markets() market = self.market(symbol) request: dict = { 'symbol': market['id'], } if limit is not None: request['limit'] = min(limit, 100) # avoid API exception "limit should less than 100" response = None marketType = None marketType, params = self.handle_market_type_and_params('fetchTrades', market, params) if marketType == 'spot': response = self.spotV1PublicGetMarketTrades(self.extend(request, params)) else: response = self.swapV2PublicGetQuoteTrades(self.extend(request, params)) # # spot # # { # "code": 0, # "data": [ # { # "id": 43148253, # "price": 25714.71, # "qty": 1.674571, # "time": 1655085975589, # "buyerMaker": False # } # ] # } # # swap # # { # "code":0, # "msg":"", # "data":[ # { # "time": 1672025549368, # "isBuyerMaker": True, # "price": "16885.0", # "qty": "3.3002", # "quoteQty": "55723.87" # }, # ... # ] # } # trades = self.safe_list(response, 'data', []) return self.parse_trades(trades, market, since, limit) def parse_trade(self, trade: dict, market: Market = None) -> Trade: # # spot fetchTrades # # { # "id": 43148253, # "price": 25714.71, # "qty": 1.674571, # "time": 1655085975589, # "buyerMaker": False # } # # spot fetchMyTrades # # { # "symbol": "LTC-USDT", # "id": 36237072, # "orderId": 1674069326895775744, # "price": "85.891", # "qty": "0.0582", # "quoteQty": "4.9988562000000005", # "commission": -0.00005820000000000001, # "commissionAsset": "LTC", # "time": 1687964205000, # "isBuyer": True, # "isMaker": False # } # # swap fetchTrades # # { # "time": 1672025549368, # "isBuyerMaker": True, # "price": "16885.0", # "qty": "3.3002", # "quoteQty": "55723.87" # } # # swap fetchMyTrades # # { # "volume": "0.1", # "price": "106.75", # "amount": "10.6750", # "commission": "-0.0053", # "currency": "USDT", # "orderId": "1676213270274379776", # "liquidatedPrice": "0.00", # "liquidatedMarginRatio": "0.00", # "filledTime": "2023-07-04T20:56:01.000+0800" # } # # ws spot # # { # "E": 1690214529432, # "T": 1690214529386, # "e": "trade", # "m": True, # "p": "29110.19", # "q": "0.1868", # "s": "BTC-USDT", # "t": "57903921" # } # # ws linear swap # # { # "q": "0.0421", # "p": "29023.5", # "T": 1690221401344, # "m": False, # "s": "BTC-USDT" # } # # ws inverse swap # # { # "e": "trade", # "E": 1722920589665, # "s": "BTC-USD", # "t": "39125001", # "p": "55360.0", # "q": "1", # "T": 1722920589582, # "m": False # } # # inverse swap fetchMyTrades # # { # "orderId": "1817441228670648320", # "symbol": "SOL-USD", # "type": "MARKET", # "side": "BUY", # "positionSide": "LONG", # "tradeId": "97244554", # "volume": "2", # "tradePrice": "182.652", # "amount": "20.00000000", # "realizedPnl": "0.00000000", # "commission": "-0.00005475", # "currency": "SOL", # "buyer": True, # "maker": False, # "tradeTime": 1722146730000 # } # time = self.safe_integer_n(trade, ['time', 'filledTm', 'T', 'tradeTime']) datetimeId = self.safe_string(trade, 'filledTm') if datetimeId is not None: time = self.parse8601(datetimeId) if time == 0: time = None cost = self.safe_string(trade, 'quoteQty') # type = 'spot' if (cost is None) else 'swap'; self is not reliable currencyId = self.safe_string_n(trade, ['currency', 'N', 'commissionAsset']) currencyCode = self.safe_currency_code(currencyId) m = self.safe_bool(trade, 'm') marketId = self.safe_string_2(trade, 's', 'symbol') isBuyerMaker = self.safe_bool_n(trade, ['buyerMaker', 'isBuyerMaker', 'maker']) takeOrMaker = None if (isBuyerMaker is not None) or (m is not None): takeOrMaker = 'maker' if (isBuyerMaker or m) else 'taker' side = self.safe_string_lower_2(trade, 'side', 'S') if side is None: if (isBuyerMaker is not None) or (m is not None): side = 'sell' if (isBuyerMaker or m) else 'buy' takeOrMaker = 'taker' isBuyer = self.safe_bool(trade, 'isBuyer') if isBuyer is not None: side = 'buy' if isBuyer else 'sell' isMaker = self.safe_bool(trade, 'isMaker') if isMaker is not None: takeOrMaker = 'maker' if isMaker else 'taker' amount = self.safe_string_n(trade, ['qty', 'amount', 'q']) if (market is not None) and market['swap'] and ('volume' in trade): # private trade returns num of contracts instead of base currency(as the order-related methods do) contractSize = self.safe_string(market['info'], 'tradeMinQuantity') volume = self.safe_string(trade, 'volume') amount = Precise.string_mul(volume, contractSize) return self.safe_trade({ 'id': self.safe_string_n(trade, ['id', 't']), 'info': trade, 'timestamp': time, 'datetime': self.iso8601(time), 'symbol': self.safe_symbol(marketId, market, '-'), 'order': self.safe_string_2(trade, 'orderId', 'i'), 'type': self.safe_string_lower(trade, 'o'), 'side': self.parse_order_side(side), 'takerOrMaker': takeOrMaker, 'price': self.safe_string_n(trade, ['price', 'p', 'tradePrice']), 'amount': amount, 'cost': cost, 'fee': { 'cost': self.parse_number(Precise.string_abs(self.safe_string_2(trade, 'commission', 'n'))), 'currency': currencyCode, }, }, market) def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook: """ fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data https://bingx-api.github.io/docs/#/spot/market-api.html#Query%20depth%20information https://bingx-api.github.io/docs/#/swapV2/market-api.html#Get%20Market%20Depth https://bingx-api.github.io/docs/#/en-us/cswap/market-api.html#Query%20Depth%20Data :param str symbol: unified symbol of the market to fetch the order book for :param int [limit]: the maximum amount of order book entries to return :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: A dictionary of `order book structures ` indexed by market symbols """ self.load_markets() market = self.market(symbol) request: dict = { 'symbol': market['id'], } if limit is not None: request['limit'] = limit response = None marketType = None marketType, params = self.handle_market_type_and_params('fetchOrderBook', market, params) if marketType == 'spot': response = self.spotV1PublicGetMarketDepth(self.extend(request, params)) else: if market['inverse']: response = self.cswapV1PublicGetMarketDepth(self.extend(request, params)) else: response = self.swapV2PublicGetQuoteDepth(self.extend(request, params)) # # spot # # { # "code":0, # "timestamp":1743240504535, # "data":{ # "bids":[ # ["83775.39","1.981875"], # ["83775.38","0.001076"], # ["83775.34","0.254716"], # ], # "asks":[ # ["83985.40","0.000013"], # ["83980.00","0.000011"], # ["83975.70","0.000061000000000000005"], # ], # "ts":1743240504535, # "lastUpdateId":13565639906 # } # } # # # linear swap # # { # "code":0, # "msg":"", # "data":{ # "T":1743240836255, # "bids":[ # ["83760.7","7.0861"], # ["83760.6","0.0044"], # ["83757.7","1.9526"], # ], # "asks":[ # ["83784.3","8.3531"], # ["83782.8","23.7289"], # ["83780.1","18.0617"], # ], # "bidsCoin":[ # ["83760.7","0.0007"], # ["83760.6","0.0000"], # ["83757.7","0.0002"], # ], # "asksCoin":[ # ["83784.3","0.0008"], # ["83782.8","0.0024"], # ["83780.1","0.0018"], # ] # } # } # # inverse swap # # { # "code":0, # "msg":"", # "timestamp":1743240979146, # "data":{ # "T":1743240978691, # "bids":[ # ["83611.4","241.0"], # ["83611.3","1.0"], # ["83602.9","666.0"], # ], # "asks":[ # ["83645.0","4253.0"], # ["83640.5","3188.0"], # ["83636.0","5540.0"], # ] # } # } # orderbook = self.safe_dict(response, 'data', {}) nonce = self.safe_integer(orderbook, 'lastUpdateId') timestamp = self.safe_integer_2(orderbook, 'T', 'ts') result = self.parse_order_book(orderbook, market['symbol'], timestamp, 'bids', 'asks', 0, 1) result['nonce'] = nonce return result def fetch_funding_rate(self, symbol: str, params={}) -> FundingRate: """ fetch the current funding rate https://bingx-api.github.io/docs/#/swapV2/market-api.html#Current%20Funding%20Rate https://bingx-api.github.io/docs/#/en-us/cswap/market-api.html#Price%20&%20Current%20Funding%20Rate :param str symbol: unified market symbol :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: a `funding rate structure ` """ self.load_markets() market = self.market(symbol) request: dict = { 'symbol': market['id'], } response = None if market['inverse']: response = self.cswapV1PublicGetMarketPremiumIndex(self.extend(request, params)) else: response = self.swapV2PublicGetQuotePremiumIndex(self.extend(request, params)) # # { # "code":0, # "msg":"", # "data":[ # { # "symbol": "BTC-USDT", # "markPrice": "16884.5", # "indexPrice": "16886.9", # "lastFundingRate": "0.0001", # "nextFundingTime": 1672041600000 # }, # ... # ] # } # data = self.safe_dict(response, 'data') return self.parse_funding_rate(data, market) def fetch_funding_rates(self, symbols: Strings = None, params={}) -> FundingRates: """ fetch the current funding rate for multiple symbols https://bingx-api.github.io/docs/#/swapV2/market-api.html#Current%20Funding%20Rate :param str[] [symbols]: list of unified market symbols :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict[]: a list of `funding rate structures ` """ self.load_markets() symbols = self.market_symbols(symbols, 'swap', True) response = self.swapV2PublicGetQuotePremiumIndex(self.extend(params)) data = self.safe_list(response, 'data', []) return self.parse_funding_rates(data, symbols) def parse_funding_rate(self, contract, market: Market = None) -> FundingRate: # # { # "symbol": "BTC-USDT", # "markPrice": "16884.5", # "indexPrice": "16886.9", # "lastFundingRate": "0.0001", # "nextFundingTime": 1672041600000 # } # marketId = self.safe_string(contract, 'symbol') nextFundingTimestamp = self.safe_integer(contract, 'nextFundingTime') return { 'info': contract, 'symbol': self.safe_symbol(marketId, market, '-', 'swap'), 'markPrice': self.safe_number(contract, 'markPrice'), 'indexPrice': self.safe_number(contract, 'indexPrice'), 'interestRate': None, 'estimatedSettlePrice': None, 'timestamp': None, 'datetime': None, 'fundingRate': self.safe_number(contract, 'lastFundingRate'), 'fundingTimestamp': None, 'fundingDatetime': None, 'nextFundingRate': None, 'nextFundingTimestamp': nextFundingTimestamp, 'nextFundingDatetime': self.iso8601(nextFundingTimestamp), 'previousFundingRate': None, 'previousFundingTimestamp': None, 'previousFundingDatetime': None, 'interval': None, } def fetch_funding_rate_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}): """ fetches historical funding rate prices https://bingx-api.github.io/docs/#/swapV2/market-api.html#Funding%20Rate%20History :param str symbol: unified symbol of the market to fetch the funding rate history for :param int [since]: timestamp in ms of the earliest funding rate to fetch :param int [limit]: the maximum amount of `funding rate structures ` to fetch :param dict [params]: extra parameters specific to the exchange API endpoint :param int [params.until]: timestamp in ms of the latest funding rate to fetch :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params) :returns dict[]: a list of `funding rate structures ` """ if symbol is None: raise ArgumentsRequired(self.id + ' fetchFundingRateHistory() requires a symbol argument') self.load_markets() paginate = False paginate, params = self.handle_option_and_params(params, 'fetchFundingRateHistory', 'paginate') if paginate: return self.fetch_paginated_call_deterministic('fetchFundingRateHistory', symbol, since, limit, '8h', params) market = self.market(symbol) request: dict = { 'symbol': market['id'], } if since is not None: request['startTime'] = since if limit is not None: request['limit'] = limit until = self.safe_integer_2(params, 'until', 'startTime') if until is not None: params = self.omit(params, ['until']) request['startTime'] = until response = self.swapV2PublicGetQuoteFundingRate(self.extend(request, params)) # # { # "code":0, # "msg":"", # "data":[ # { # "symbol": "BTC-USDT", # "fundingRate": "0.0001", # "fundingTime": 1585684800000 # }, # ... # ] # } # data = self.safe_list(response, 'data', []) return self.parse_funding_rate_histories(data, market, since, limit) def parse_funding_rate_history(self, contract, market: Market = None): # # { # "symbol": "BTC-USDT", # "fundingRate": "0.0001", # "fundingTime": 1585684800000 # } # timestamp = self.safe_integer(contract, 'fundingTime') return { 'info': contract, 'symbol': self.safe_symbol(self.safe_string(contract, 'symbol'), market, '-', 'swap'), 'fundingRate': self.safe_number(contract, 'fundingRate'), 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), } def fetch_open_interest(self, symbol: str, params={}): """ retrieves the open interest of a trading pair https://bingx-api.github.io/docs/#/swapV2/market-api.html#Get%20Swap%20Open%20Positions https://bingx-api.github.io/docs/#/en-us/cswap/market-api.html#Get%20Swap%20Open%20Positions :param str symbol: unified CCXT market symbol :param dict [params]: exchange specific parameters :returns dict} an open interest structure{@link https://docs.ccxt.com/#/?id=open-interest-structure: """ self.load_markets() market = self.market(symbol) request: dict = { 'symbol': market['id'], } response = None if market['inverse']: response = self.cswapV1PublicGetMarketOpenInterest(self.extend(request, params)) else: response = self.swapV2PublicGetQuoteOpenInterest(self.extend(request, params)) # # linear swap # # { # "code": 0, # "msg": "", # "data": { # "openInterest": "3289641547.10", # "symbol": "BTC-USDT", # "time": 1672026617364 # } # } # # inverse swap # # { # "code": 0, # "msg": "", # "timestamp": 1720328247986, # "data": [ # { # "symbol": "BTC-USD", # "openInterest": "749.1160", # "timestamp": 1720310400000 # } # ] # } # result: dict = {} if market['inverse']: data = self.safe_list(response, 'data', []) result = self.safe_dict(data, 0, {}) else: result = self.safe_dict(response, 'data', {}) return self.parse_open_interest(result, market) def parse_open_interest(self, interest, market: Market = None): # # linear swap # # { # "openInterest": "3289641547.10", # "symbol": "BTC-USDT", # "time": 1672026617364 # } # # inverse swap # # { # "symbol": "BTC-USD", # "openInterest": "749.1160", # "timestamp": 1720310400000 # } # timestamp = self.safe_integer_2(interest, 'time', 'timestamp') id = self.safe_string(interest, 'symbol') symbol = self.safe_symbol(id, market, '-', 'swap') openInterest = self.safe_number(interest, 'openInterest') return self.safe_open_interest({ 'symbol': symbol, 'baseVolume': None, 'quoteVolume': None, # deprecated 'openInterestAmount': None, 'openInterestValue': openInterest, 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), 'info': interest, }, market) def fetch_ticker(self, symbol: str, params={}) -> Ticker: """ fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market https://bingx-api.github.io/docs/#/en-us/swapV2/market-api.html#Get%20Ticker https://bingx-api.github.io/docs/#/en-us/spot/market-api.html#24-hour%20price%20changes https://bingx-api.github.io/docs/#/en-us/cswap/market-api.html#Query%2024-Hour%20Price%20Change :param str symbol: unified symbol of the market to fetch the ticker for :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: a `ticker structure ` """ self.load_markets() market = self.market(symbol) request: dict = { 'symbol': market['id'], } response = None if market['spot']: response = self.spotV1PublicGetTicker24hr(self.extend(request, params)) else: if market['inverse']: response = self.cswapV1PublicGetMarketTicker(self.extend(request, params)) else: response = self.swapV2PublicGetQuoteTicker(self.extend(request, params)) # # spot and swap # # { # "code": 0, # "msg": "", # "timestamp": 1720647285296, # "data": [ # { # "symbol": "SOL-USD", # "priceChange": "-2.418", # "priceChangePercent": "-1.6900%", # "lastPrice": "140.574", # "lastQty": "1", # "highPrice": "146.190", # "lowPrice": "138.586", # "volume": "1464648.00", # "quoteVolume": "102928.12", # "openPrice": "142.994", # "closeTime": "1720647284976", # "bidPrice": "140.573", # "bidQty": "372", # "askPrice": "140.577", # "askQty": "58" # } # ] # } # data = self.safe_list(response, 'data') if data is not None: first = self.safe_dict(data, 0, {}) return self.parse_ticker(first, market) dataDict = self.safe_dict(response, 'data', {}) return self.parse_ticker(dataDict, market) def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers: """ fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market https://bingx-api.github.io/docs/#/en-us/swapV2/market-api.html#Get%20Ticker https://bingx-api.github.io/docs/#/en-us/spot/market-api.html#24-hour%20price%20changes https://bingx-api.github.io/docs/#/en-us/cswap/market-api.html#Query%2024-Hour%20Price%20Change :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: a dictionary of `ticker structures ` """ self.load_markets() market = None if symbols is not None: symbols = self.market_symbols(symbols) firstSymbol = self.safe_string(symbols, 0) if firstSymbol is not None: market = self.market(firstSymbol) type = None type, params = self.handle_market_type_and_params('fetchTickers', market, params) subType = None subType, params = self.handle_sub_type_and_params('fetchTickers', market, params) response = None if type == 'spot': response = self.spotV1PublicGetTicker24hr(params) else: if subType == 'inverse': response = self.cswapV1PublicGetMarketTicker(params) else: response = self.swapV2PublicGetQuoteTicker(params) # # spot and swap # # { # "code": 0, # "msg": "", # "timestamp": 1720647285296, # "data": [ # { # "symbol": "SOL-USD", # "priceChange": "-2.418", # "priceChangePercent": "-1.6900%", # "lastPrice": "140.574", # "lastQty": "1", # "highPrice": "146.190", # "lowPrice": "138.586", # "volume": "1464648.00", # "quoteVolume": "102928.12", # "openPrice": "142.994", # "closeTime": "1720647284976", # "bidPrice": "140.573", # "bidQty": "372", # "askPrice": "140.577", # "askQty": "58" # }, # ... # ] # } # tickers = self.safe_list(response, 'data') return self.parse_tickers(tickers, symbols) def fetch_mark_price(self, symbol: str, params={}) -> Ticker: """ fetches mark prices for the market https://bingx-api.github.io/docs/#/en-us/swapV2/market-api.html#Mark%20Price%20and%20Funding%20Rate :param str symbol: unified symbol of the market to fetch the ticker for :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: a dictionary of `ticker structures ` """ self.load_markets() market = self.market(symbol) subType = None subType, params = self.handle_sub_type_and_params('fetchMarkPrice', market, params, 'linear') request = { 'symbol': market['id'], } response = None if subType == 'inverse': response = self.cswapV1PublicGetMarketPremiumIndex(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "timestamp": 1728577213289, # "data": [ # { # "symbol": "ETH-USD", # "lastFundingRate": "0.0001", # "markPrice": "2402.68", # "indexPrice": "2404.92", # "nextFundingTime": 1728604800000 # } # ] # } # else: response = self.swapV2PublicGetQuotePremiumIndex(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "data": { # "symbol": "ETH-USDT", # "markPrice": "2408.40", # "indexPrice": "2409.62", # "lastFundingRate": "0.00009900", # "nextFundingTime": 1728604800000 # } # } # if isinstance(response['data'], list): return self.parse_ticker(self.safe_dict(response['data'], 0, {}), market) return self.parse_ticker(response['data'], market) def fetch_mark_prices(self, symbols: Strings = None, params={}) -> Tickers: """ fetches mark prices for multiple markets https://bingx-api.github.io/docs/#/en-us/swapV2/market-api.html#Mark%20Price%20and%20Funding%20Rate :param str[] [symbols]: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: a dictionary of `ticker structures ` """ self.load_markets() market = None if symbols is not None: symbols = self.market_symbols(symbols) firstSymbol = self.safe_string(symbols, 0) if firstSymbol is not None: market = self.market(firstSymbol) subType = None subType, params = self.handle_sub_type_and_params('fetchMarkPrices', market, params, 'linear') response = None if subType == 'inverse': response = self.cswapV1PublicGetMarketPremiumIndex(params) else: response = self.swapV2PublicGetQuotePremiumIndex(params) # # spot and swap # # { # "code": 0, # "msg": "", # "timestamp": 1720647285296, # "data": [ # { # "symbol": "SOL-USD", # "priceChange": "-2.418", # "priceChangePercent": "-1.6900%", # "lastPrice": "140.574", # "lastQty": "1", # "highPrice": "146.190", # "lowPrice": "138.586", # "volume": "1464648.00", # "quoteVolume": "102928.12", # "openPrice": "142.994", # "closeTime": "1720647284976", # "bidPrice": "140.573", # "bidQty": "372", # "askPrice": "140.577", # "askQty": "58" # }, # ... # ] # } # tickers = self.safe_list(response, 'data') return self.parse_tickers(tickers, symbols) def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker: # # mark price # { # "symbol": "string", # "lastFundingRate": "string", # "markPrice": "string", # "indexPrice": "string", # "nextFundingTime": "int64" # } # # spot # { # "symbol": "BTC-USDT", # "openPrice": "26032.08", # "highPrice": "26178.86", # "lowPrice": "25968.18", # "lastPrice": "26113.60", # "volume": "1161.79", # "quoteVolume": "30288466.44", # "openTime": "1693081020762", # "closeTime": "1693167420762", # added 2023-11-10: # "bidPrice": 16726.0, # "bidQty": 0.05, # "askPrice": 16726.0, # "askQty": 0.05, # } # swap # # { # "symbol": "BTC-USDT", # "priceChange": "52.5", # "priceChangePercent": "0.31%", # they started to add the percent sign in value # "lastPrice": "16880.5", # "lastQty": "2.2238", # only present in swap! # "highPrice": "16897.5", # "lowPrice": "16726.0", # "volume": "245870.1692", # "quoteVolume": "4151395117.73", # "openPrice": "16832.0", # "openTime": 1672026667803, # "closeTime": 1672026648425, # added 2023-11-10: # "bidPrice": 16726.0, # "bidQty": 0.05, # "askPrice": 16726.0, # "askQty": 0.05, # } # marketId = self.safe_string(ticker, 'symbol') lastQty = self.safe_string(ticker, 'lastQty') # in spot markets, lastQty is not present # it's(bad, but) the only way we can check the tickers origin type = 'spot' if (lastQty is None) else 'swap' market = self.safe_market(marketId, market, None, type) symbol = market['symbol'] open = self.safe_string(ticker, 'openPrice') high = self.safe_string(ticker, 'highPrice') low = self.safe_string(ticker, 'lowPrice') close = self.safe_string(ticker, 'lastPrice') quoteVolume = self.safe_string(ticker, 'quoteVolume') baseVolume = self.safe_string(ticker, 'volume') percentage = self.safe_string(ticker, 'priceChangePercent') if percentage is not None: percentage = percentage.replace('%', '') change = self.safe_string(ticker, 'priceChange') ts = self.safe_integer(ticker, 'closeTime') if ts == 0: ts = None datetime = self.iso8601(ts) bid = self.safe_string(ticker, 'bidPrice') bidVolume = self.safe_string(ticker, 'bidQty') ask = self.safe_string(ticker, 'askPrice') askVolume = self.safe_string(ticker, 'askQty') return self.safe_ticker({ 'symbol': symbol, 'timestamp': ts, 'datetime': datetime, 'high': high, 'low': low, 'bid': bid, 'bidVolume': bidVolume, 'ask': ask, 'askVolume': askVolume, 'vwap': None, 'open': open, 'close': close, 'last': None, 'previousClose': None, 'change': change, 'percentage': percentage, 'average': None, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'markPrice': self.safe_string(ticker, 'markPrice'), 'indexPrice': self.safe_string(ticker, 'indexPrice'), 'info': ticker, }, market) def fetch_balance(self, params={}) -> Balances: """ query for balance and get the amount of funds available for trading or funds locked in orders https://bingx-api.github.io/docs/#/spot/trade-api.html#Query%20Assets https://bingx-api.github.io/docs/#/en-us/swapV2/account-api.html#Query%20account%20data https://bingx-api.github.io/docs/#/standard/contract-interface.html#Query%20standard%20contract%20balance https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Query%20Account%20Assets :param dict [params]: extra parameters specific to the exchange API endpoint :param boolean [params.standard]: whether to fetch standard contract balances :param str [params.type]: the type of balance to fetch(spot, swap, funding) default is `spot` :returns dict: a `balance structure ` """ self.load_markets() response = None standard = None standard, params = self.handle_option_and_params(params, 'fetchBalance', 'standard', False) subType = None subType, params = self.handle_sub_type_and_params('fetchBalance', None, params) marketType, marketTypeQuery = self.handle_market_type_and_params('fetchBalance', None, params) if standard: response = self.contractV1PrivateGetBalance(marketTypeQuery) # # { # "code": 0, # "timestamp": 1721192833454, # "data": [ # { # "asset": "USDT", # "balance": "4.72644300000000000000", # "crossWalletBalance": "4.72644300000000000000", # "crossUnPnl": "0", # "availableBalance": "4.72644300000000000000", # "maxWithdrawAmount": "4.72644300000000000000", # "marginAvailable": False, # "updateTime": 1721192833443 # }, # ] # } # elif (marketType == 'funding') or (marketType == 'fund'): response = self.fundV1PrivateGetAccountBalance(marketTypeQuery) # { # code: '0', # timestamp: '1754906016631', # data: { # assets: [ # { # asset: 'USDT', # free: '44.37692200000000237300', # locked: '0.00000000000000000000' # } # ] # } # } elif marketType == 'spot': response = self.spotV1PrivateGetAccountBalance(marketTypeQuery) # # { # "code": 0, # "msg": "", # "debugMsg": "", # "data": { # "balances": [ # { # "asset": "USDT", # "free": "45.733046995800514", # "locked": "0" # }, # ] # } # } # else: if subType == 'inverse': response = self.cswapV1PrivateGetUserBalance(marketTypeQuery) # # { # "code": 0, # "msg": "", # "timestamp": 1721191833813, # "data": [ # { # "asset": "SOL", # "balance": "0.35707951", # "equity": "0.35791051", # "unrealizedProfit": "0.00083099", # "availableMargin": "0.35160653", # "usedMargin": "0.00630397", # "freezedMargin": "0", # "shortUid": "12851936" # } # ] # } # else: response = self.swapV3PrivateGetUserBalance(marketTypeQuery) # # { # "code": 0, # "msg": "", # "data": [ # { # "userId": "116***295", # "asset": "USDT", # "balance": "194.8212", # "equity": "196.7431", # "unrealizedProfit": "1.9219", # "realisedProfit": "-109.2504", # "availableMargin": "193.7609", # "usedMargin": "1.0602", # "freezedMargin": "0.0000", # "shortUid": "12851936" # } # ] # } return self.parse_balance(response) def parse_balance(self, response) -> Balances: # # standard # # { # "code": 0, # "timestamp": 1721192833454, # "data": [ # { # "asset": "USDT", # "balance": "4.72644300000000000000", # "crossWalletBalance": "4.72644300000000000000", # "crossUnPnl": "0", # "availableBalance": "4.72644300000000000000", # "maxWithdrawAmount": "4.72644300000000000000", # "marginAvailable": False, # "updateTime": 1721192833443 # }, # ] # } # # spot # # { # "code": 0, # "msg": "", # "debugMsg": "", # "data": { # "balances": [ # { # "asset": "USDT", # "free": "45.733046995800514", # "locked": "0" # }, # ] # } # } # # inverse swap # # { # "code": 0, # "msg": "", # "timestamp": 1721191833813, # "data": [ # { # "asset": "SOL", # "balance": "0.35707951", # "equity": "0.35791051", # "unrealizedProfit": "0.00083099", # "availableMargin": "0.35160653", # "usedMargin": "0.00630397", # "freezedMargin": "0", # "shortUid": "12851936" # } # ] # } # # linear swap # # { # "code": 0, # "msg": "", # "data": [ # { # "userId": "116***295", # "asset": "USDT", # "balance": "194.8212", # "equity": "196.7431", # "unrealizedProfit": "1.9219", # "realisedProfit": "-109.2504", # "availableMargin": "193.7609", # "usedMargin": "1.0602", # "freezedMargin": "0.0000", # "shortUid": "12851936" # } # ] # } # result: dict = {'info': response} contractBalances = self.safe_list(response, 'data') firstContractBalances = self.safe_dict(contractBalances, 0) isContract = firstContractBalances is not None spotData = self.safe_dict(response, 'data', {}) spotBalances = self.safe_list_2(spotData, 'balances', 'assets', []) if isContract: for i in range(0, len(contractBalances)): balance = contractBalances[i] currencyId = self.safe_string(balance, 'asset') if currencyId is None: # linear v3 returns empty asset break code = self.safe_currency_code(currencyId) account = self.account() account['free'] = self.safe_string_2(balance, 'availableMargin', 'availableBalance') account['used'] = self.safe_string(balance, 'usedMargin') account['total'] = self.safe_string(balance, 'maxWithdrawAmount') result[code] = account else: for i in range(0, len(spotBalances)): balance = spotBalances[i] currencyId = self.safe_string(balance, 'asset') code = self.safe_currency_code(currencyId) account = self.account() account['free'] = self.safe_string(balance, 'free') account['used'] = self.safe_string(balance, 'locked') result[code] = account return self.safe_balance(result) def fetch_position_history(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Position]: """ fetches historical positions https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20Position%20History :param str symbol: unified contract symbol :param int [since]: the earliest time in ms to fetch positions for :param int [limit]: the maximum amount of records to fetch :param dict [params]: extra parameters specific to the exchange api endpoint :param int [params.until]: the latest time in ms to fetch positions for :returns dict[]: a list of `position structures ` """ self.load_markets() market = self.market(symbol) request: dict = { 'symbol': market['id'], } if limit is not None: request['pageSize'] = limit if since is not None: request['startTs'] = since request, params = self.handle_until_option('endTs', request, params) response = None if market['linear']: response = self.swapV1PrivateGetTradePositionHistory(self.extend(request, params)) else: raise NotSupported(self.id + ' fetchPositionHistory() is not supported for inverse swap positions') # # { # "code": 0, # "msg": "", # "data": { # "positionHistory": [ # { # "positionId": "1861675561156571136", # "symbol": "LTC-USDT", # "isolated": False, # "positionSide": "LONG", # "openTime": 1732693017000, # "updateTime": 1733310292000, # "avgPrice": "95.18", # "avgClosePrice": "129.48", # "realisedProfit": "102.89", # "netProfit": "99.63", # "positionAmt": "30.0", # "closePositionAmt": "30.0", # "leverage": 6, # "closeAllPositions": True, # "positionCommission": "-0.33699650000000003", # "totalFunding": "-2.921461693902908" # }, # ] # } # } # data = self.safe_dict(response, 'data', {}) records = self.safe_list(data, 'positionHistory', []) positions = self.parse_positions(records) return self.filter_by_symbol_since_limit(positions, symbol, since, limit) def fetch_positions(self, symbols: Strings = None, params={}) -> List[Position]: """ fetch all open positions https://bingx-api.github.io/docs/#/en-us/swapV2/account-api.html#Query%20position%20data https://bingx-api.github.io/docs/#/en-us/standard/contract-interface.html#position https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Query%20warehouse :param str[]|None symbols: list of unified market symbols :param dict [params]: extra parameters specific to the exchange API endpoint :param boolean [params.standard]: whether to fetch standard contract positions :returns dict[]: a list of `position structures ` """ self.load_markets() symbols = self.market_symbols(symbols) standard = None standard, params = self.handle_option_and_params(params, 'fetchPositions', 'standard', False) response = None if standard: response = self.contractV1PrivateGetAllPosition(params) else: market = None if symbols is not None: symbols = self.market_symbols(symbols) firstSymbol = self.safe_string(symbols, 0) if firstSymbol is not None: market = self.market(firstSymbol) subType = None subType, params = self.handle_sub_type_and_params('fetchPositions', market, params) if subType == 'inverse': response = self.cswapV1PrivateGetUserPositions(params) # # { # "code": 0, # "msg": "", # "timestamp": 0, # "data": [ # { # "symbol": "SOL-USD", # "positionId": "1813080351385337856", # "positionSide": "LONG", # "isolated": False, # "positionAmt": "1", # "availableAmt": "1", # "unrealizedProfit": "-0.00009074", # "initialMargin": "0.00630398", # "liquidationPrice": 23.968303426677032, # "avgPrice": "158.63", # "leverage": 10, # "markPrice": "158.402", # "riskRate": "0.00123783", # "maxMarginReduction": "0", # "updateTime": 1721107015848 # } # ] # } # else: response = self.swapV2PrivateGetUserPositions(params) # # { # "code": 0, # "msg": "", # "data": [ # { # "positionId": "1792480725958881280", # "symbol": "LTC-USDT", # "currency": "USDT", # "positionAmt": "0.1", # "availableAmt": "0.1", # "positionSide": "LONG", # "isolated": False, # "avgPrice": "83.53", # "initialMargin": "1.3922", # "margin": "0.3528", # "leverage": 6, # "unrealizedProfit": "-1.0393", # "realisedProfit": "-0.2119", # "liquidationPrice": 0, # "pnlRatio": "-0.7465", # "maxMarginReduction": "0.0000", # "riskRate": "0.0008", # "markPrice": "73.14", # "positionValue": "7.3136", # "onlyOnePosition": True, # "updateTime": 1721088016688 # } # ] # } # positions = self.safe_list(response, 'data', []) return self.parse_positions(positions, symbols) def fetch_position(self, symbol: str, params={}): """ fetch data on a single open contract trade position https://bingx-api.github.io/docs/#/en-us/swapV2/account-api.html#Query%20position%20data https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Query%20warehouse :param str symbol: unified market symbol of the market the position is held in :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: a `position structure ` """ self.load_markets() market = self.market(symbol) if not market['swap']: raise BadRequest(self.id + ' fetchPosition() supports swap markets only') request: dict = { 'symbol': market['id'], } response = None if market['inverse']: response = self.cswapV1PrivateGetUserPositions(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "timestamp": 0, # "data": [ # { # "symbol": "SOL-USD", # "positionId": "1813080351385337856", # "positionSide": "LONG", # "isolated": False, # "positionAmt": "1", # "availableAmt": "1", # "unrealizedProfit": "-0.00009074", # "initialMargin": "0.00630398", # "liquidationPrice": 23.968303426677032, # "avgPrice": "158.63", # "leverage": 10, # "markPrice": "158.402", # "riskRate": "0.00123783", # "maxMarginReduction": "0", # "updateTime": 1721107015848 # } # ] # } # else: response = self.swapV2PrivateGetUserPositions(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "data": [ # { # "positionId": "1792480725958881280", # "symbol": "LTC-USDT", # "currency": "USDT", # "positionAmt": "0.1", # "availableAmt": "0.1", # "positionSide": "LONG", # "isolated": False, # "avgPrice": "83.53", # "initialMargin": "1.3922", # "margin": "0.3528", # "leverage": 6, # "unrealizedProfit": "-1.0393", # "realisedProfit": "-0.2119", # "liquidationPrice": 0, # "pnlRatio": "-0.7465", # "maxMarginReduction": "0.0000", # "riskRate": "0.0008", # "markPrice": "73.14", # "positionValue": "7.3136", # "onlyOnePosition": True, # "updateTime": 1721088016688 # } # ] # } # data = self.safe_list(response, 'data', []) first = self.safe_dict(data, 0, {}) return self.parse_position(first, market) def parse_position(self, position: dict, market: Market = None): # # inverse swap # # { # "symbol": "SOL-USD", # "positionId": "1813080351385337856", # "positionSide": "LONG", # "isolated": False, # "positionAmt": "1", # "availableAmt": "1", # "unrealizedProfit": "-0.00009074", # "initialMargin": "0.00630398", # "liquidationPrice": 23.968303426677032, # "avgPrice": "158.63", # "leverage": 10, # "markPrice": "158.402", # "riskRate": "0.00123783", # "maxMarginReduction": "0", # "updateTime": 1721107015848 # } # # linear swap # # { # "positionId": "1792480725958881280", # "symbol": "LTC-USDT", # "currency": "USDT", # "positionAmt": "0.1", # "availableAmt": "0.1", # "positionSide": "LONG", # "isolated": False, # "avgPrice": "83.53", # "initialMargin": "1.3922", # "margin": "0.3528", # "leverage": 6, # "unrealizedProfit": "-1.0393", # "realisedProfit": "-0.2119", # "liquidationPrice": 0, # "pnlRatio": "-0.7465", # "maxMarginReduction": "0.0000", # "riskRate": "0.0008", # "markPrice": "73.14", # "positionValue": "7.3136", # "onlyOnePosition": True, # "updateTime": 1721088016688 # } # # standard position # # { # "currentPrice": "82.91", # "symbol": "LTC/USDT", # "initialMargin": "5.00000000000000000000", # "unrealizedProfit": "-0.26464500", # "leverage": "20.000000000", # "isolated": True, # "entryPrice": "83.13", # "positionSide": "LONG", # "positionAmt": "1.20365912", # } # # linear swap fetchPositionHistory # # { # "positionId": "1861675561156571136", # "symbol": "LTC-USDT", # "isolated": False, # "positionSide": "LONG", # "openTime": 1732693017000, # "updateTime": 1733310292000, # "avgPrice": "95.18", # "avgClosePrice": "129.48", # "realisedProfit": "102.89", # "netProfit": "99.63", # "positionAmt": "30.0", # "closePositionAmt": "30.0", # "leverage": 6, # "closeAllPositions": True, # "positionCommission": "-0.33699650000000003", # "totalFunding": "-2.921461693902908" # } # marketId = self.safe_string(position, 'symbol', '') marketId = marketId.replace('/', '-') # standard return different format isolated = self.safe_bool(position, 'isolated') marginMode = None if isolated is not None: marginMode = 'isolated' if isolated else 'cross' timestamp = self.safe_integer(position, 'openTime') return self.safe_position({ 'info': position, 'id': self.safe_string(position, 'positionId'), 'symbol': self.safe_symbol(marketId, market, '-', 'swap'), 'notional': self.safe_number(position, 'positionValue'), 'marginMode': marginMode, 'liquidationPrice': None, 'entryPrice': self.safe_number_2(position, 'avgPrice', 'entryPrice'), 'unrealizedPnl': self.safe_number(position, 'unrealizedProfit'), 'realizedPnl': self.safe_number(position, 'realisedProfit'), 'percentage': None, 'contracts': self.safe_number(position, 'positionAmt'), 'contractSize': None, 'markPrice': self.safe_number(position, 'markPrice'), 'lastPrice': None, 'side': self.safe_string_lower(position, 'positionSide'), 'hedged': None, 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), 'lastUpdateTimestamp': self.safe_integer(position, 'updateTime'), 'maintenanceMargin': None, 'maintenanceMarginPercentage': None, 'collateral': None, 'initialMargin': self.safe_number(position, 'initialMargin'), 'initialMarginPercentage': None, 'leverage': self.safe_number(position, 'leverage'), 'marginRatio': None, 'stopLossPrice': None, 'takeProfitPrice': None, }) def create_market_order_with_cost(self, symbol: str, side: OrderSide, cost: float, params={}): """ create a market order by providing the symbol, side and cost :param str symbol: unified symbol of the market to create an order in :param str side: 'buy' or 'sell' :param float cost: how much you want to trade in units of the quote currency :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: an `order structure ` """ params['quoteOrderQty'] = cost return self.create_order(symbol, 'market', side, cost, None, params) def create_market_buy_order_with_cost(self, symbol: str, cost: float, params={}): """ create a market buy order by providing the symbol and cost :param str symbol: unified symbol of the market to create an order in :param float cost: how much you want to trade in units of the quote currency :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: an `order structure ` """ params['quoteOrderQty'] = cost return self.create_order(symbol, 'market', 'buy', cost, None, params) def create_market_sell_order_with_cost(self, symbol: str, cost: float, params={}): """ create a market sell order by providing the symbol and cost :param str symbol: unified symbol of the market to create an order in :param float cost: how much you want to trade in units of the quote currency :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: an `order structure ` """ params['quoteOrderQty'] = cost return self.create_order(symbol, 'market', 'sell', cost, None, params) def create_order_request(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}): """ @ignore helper function to build request :param str symbol: unified symbol of the market to create an order in :param str type: 'market' or 'limit' :param str side: 'buy' or 'sell' :param float amount: how much 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, ignored in market orders :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: request to be sent to the exchange """ market = self.market(symbol) postOnly = None marketType = None marketType, params = self.handle_market_type_and_params('createOrder', market, params) type = type.upper() request: dict = { 'symbol': market['id'], 'type': type, 'side': side.upper(), } isMarketOrder = type == 'MARKET' isSpot = marketType == 'spot' isTwapOrder = type == 'TWAP' if isTwapOrder and isSpot: raise BadSymbol(self.id + ' createOrder() twap order supports swap contracts only') stopLossPrice = self.safe_string(params, 'stopLossPrice') takeProfitPrice = self.safe_string(params, 'takeProfitPrice') triggerPrice = self.safe_string_2(params, 'stopPrice', 'triggerPrice') isTriggerOrder = triggerPrice is not None isStopLossPriceOrder = stopLossPrice is not None isTakeProfitPriceOrder = takeProfitPrice is not None exchangeClientOrderId = 'newClientOrderId' if isSpot else 'clientOrderID' clientOrderId = self.safe_string_2(params, exchangeClientOrderId, 'clientOrderId') if clientOrderId is not None: request[exchangeClientOrderId] = clientOrderId timeInForce = self.safe_string_upper(params, 'timeInForce') postOnly, params = self.handle_post_only(isMarketOrder, timeInForce == 'PostOnly', params) if postOnly or (timeInForce == 'PostOnly'): request['timeInForce'] = 'PostOnly' elif timeInForce == 'IOC': request['timeInForce'] = 'IOC' elif timeInForce == 'GTC': request['timeInForce'] = 'GTC' if isSpot: cost = self.safe_string_2(params, 'cost', 'quoteOrderQty') params = self.omit(params, 'cost') if cost is not None: request['quoteOrderQty'] = self.parse_to_numeric(self.cost_to_precision(symbol, cost)) else: if isMarketOrder and (price is not None): # keep the legacy behavior, to avoid breaking the old spot-market-buying code calculatedCost = Precise.string_mul(self.number_to_string(amount), self.number_to_string(price)) request['quoteOrderQty'] = self.parse_to_numeric(calculatedCost) else: request['quantity'] = self.parse_to_numeric(self.amount_to_precision(symbol, amount)) if not isMarketOrder: request['price'] = self.parse_to_numeric(self.price_to_precision(symbol, price)) if triggerPrice is not None: if isMarketOrder and self.safe_string(request, 'quoteOrderQty') is None: raise ArgumentsRequired(self.id + ' createOrder() requires the cost parameter(or the amount + price) for placing spot market-buy trigger orders') request['stopPrice'] = self.price_to_precision(symbol, triggerPrice) if type == 'LIMIT': request['type'] = 'TRIGGER_LIMIT' elif type == 'MARKET': request['type'] = 'TRIGGER_MARKET' elif (stopLossPrice is not None) or (takeProfitPrice is not None): stopTakePrice = stopLossPrice if (stopLossPrice is not None) else takeProfitPrice if type == 'LIMIT': request['type'] = 'TAKE_STOP_LIMIT' elif type == 'MARKET': request['type'] = 'TAKE_STOP_MARKET' request['stopPrice'] = self.parse_to_numeric(self.price_to_precision(symbol, stopTakePrice)) else: if isTwapOrder: twapRequest: dict = { 'symbol': request['symbol'], 'side': request['side'], 'positionSide': 'LONG' if (side == 'buy') else 'SHORT', 'triggerPrice': self.parse_to_numeric(self.price_to_precision(symbol, triggerPrice)), 'totalAmount': self.parse_to_numeric(self.amount_to_precision(symbol, amount)), } # { # "symbol": "LTC-USDT", # "side": "BUY", # "positionSide": "LONG", # "priceType": "constant", # "priceVariance": "10", # "triggerPrice": "120", # "interval": 8, # "amountPerOrder": "0.5", # "totalAmount": "1" # } return self.extend(twapRequest, params) if timeInForce == 'FOK': request['timeInForce'] = 'FOK' trailingAmount = self.safe_string(params, 'trailingAmount') trailingPercent = self.safe_string_2(params, 'trailingPercent', 'priceRate') trailingType = self.safe_string(params, 'trailingType', 'TRAILING_STOP_MARKET') isTrailingAmountOrder = trailingAmount is not None isTrailingPercentOrder = trailingPercent is not None isTrailing = isTrailingAmountOrder or isTrailingPercentOrder stopLoss = self.safe_value(params, 'stopLoss') takeProfit = self.safe_value(params, 'takeProfit') isStopLoss = stopLoss is not None isTakeProfit = takeProfit is not None if ((type == 'LIMIT') or (type == 'TRIGGER_LIMIT') or (type == 'STOP') or (type == 'TAKE_PROFIT')) and not isTrailing: request['price'] = self.parse_to_numeric(self.price_to_precision(symbol, price)) reduceOnly = self.safe_bool(params, 'reduceOnly', False) if isTriggerOrder: request['stopPrice'] = self.parse_to_numeric(self.price_to_precision(symbol, triggerPrice)) if isMarketOrder or (type == 'TRIGGER_MARKET'): request['type'] = 'TRIGGER_MARKET' elif (type == 'LIMIT') or (type == 'TRIGGER_LIMIT'): request['type'] = 'TRIGGER_LIMIT' elif isStopLossPriceOrder or isTakeProfitPriceOrder: # This can be used to set the stop loss and take profit, but the position needs to be opened first reduceOnly = True if isStopLossPriceOrder: request['stopPrice'] = self.parse_to_numeric(self.price_to_precision(symbol, stopLossPrice)) if isMarketOrder or (type == 'STOP_MARKET'): request['type'] = 'STOP_MARKET' elif (type == 'LIMIT') or (type == 'STOP'): request['type'] = 'STOP' elif isTakeProfitPriceOrder: request['stopPrice'] = self.parse_to_numeric(self.price_to_precision(symbol, takeProfitPrice)) if isMarketOrder or (type == 'TAKE_PROFIT_MARKET'): request['type'] = 'TAKE_PROFIT_MARKET' elif (type == 'LIMIT') or (type == 'TAKE_PROFIT'): request['type'] = 'TAKE_PROFIT' elif isTrailing: request['type'] = trailingType if isTrailingAmountOrder: request['price'] = self.parse_to_numeric(trailingAmount) elif isTrailingPercentOrder: requestTrailingPercent = Precise.string_div(trailingPercent, '100') request['priceRate'] = self.parse_to_numeric(requestTrailingPercent) if isStopLoss or isTakeProfit: stringifiedAmount = self.number_to_string(amount) if isStopLoss: slTriggerPrice = self.safe_string_2(stopLoss, 'triggerPrice', 'stopPrice', stopLoss) slWorkingType = self.safe_string(stopLoss, 'workingType', 'MARK_PRICE') slType = self.safe_string(stopLoss, 'type', 'STOP_MARKET') slRequest: dict = { 'stopPrice': self.parse_to_numeric(self.price_to_precision(symbol, slTriggerPrice)), 'workingType': slWorkingType, 'type': slType, } slPrice = self.safe_string(stopLoss, 'price') if slPrice is not None: slRequest['price'] = self.parse_to_numeric(self.price_to_precision(symbol, slPrice)) slQuantity = self.safe_string(stopLoss, 'quantity', stringifiedAmount) slRequest['quantity'] = self.parse_to_numeric(self.amount_to_precision(symbol, slQuantity)) request['stopLoss'] = self.json(slRequest) if isTakeProfit: tkTriggerPrice = self.safe_string_2(takeProfit, 'triggerPrice', 'stopPrice', takeProfit) tkWorkingType = self.safe_string(takeProfit, 'workingType', 'MARK_PRICE') tpType = self.safe_string(takeProfit, 'type', 'TAKE_PROFIT_MARKET') tpRequest: dict = { 'stopPrice': self.parse_to_numeric(self.price_to_precision(symbol, tkTriggerPrice)), 'workingType': tkWorkingType, 'type': tpType, } slPrice = self.safe_string(takeProfit, 'price') if slPrice is not None: tpRequest['price'] = self.parse_to_numeric(self.price_to_precision(symbol, slPrice)) tkQuantity = self.safe_string(takeProfit, 'quantity', stringifiedAmount) tpRequest['quantity'] = self.parse_to_numeric(self.amount_to_precision(symbol, tkQuantity)) request['takeProfit'] = self.json(tpRequest) positionSide = None hedged = self.safe_bool(params, 'hedged', False) if hedged: params = self.omit(params, 'reduceOnly') if reduceOnly: positionSide = 'SHORT' if (side == 'buy') else 'LONG' else: positionSide = 'LONG' if (side == 'buy') else 'SHORT' else: positionSide = 'BOTH' request['positionSide'] = positionSide amountReq = amount if not market['inverse']: amountReq = self.parse_to_numeric(self.amount_to_precision(symbol, amount)) request['quantity'] = amountReq # precision not available for inverse contracts params = self.omit(params, ['hedged', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice', 'trailingAmount', 'trailingPercent', 'trailingType', 'takeProfit', 'stopLoss', 'clientOrderId']) return self.extend(request, params) def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}): """ create a trade order https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Trade%20order https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Create%20an%20Order https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Trade%20order https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Place%20TWAP%20Order :param str symbol: unified symbol of the market to create an order in :param str type: 'market' or 'limit' :param str side: 'buy' or 'sell' :param float amount: how much 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, ignored in market orders :param dict [params]: extra parameters specific to the exchange API endpoint :param str [params.clientOrderId]: a unique id for the order :param bool [params.postOnly]: True to place a post only order :param str [params.timeInForce]: spot supports 'PO', 'GTC' and 'IOC', swap supports 'PO', 'GTC', 'IOC' and 'FOK' :param bool [params.reduceOnly]: *swap only* True or False whether the order is reduce only :param float [params.triggerPrice]: triggerPrice at which the attached take profit / stop loss order will be triggered :param float [params.stopLossPrice]: stop loss trigger price :param float [params.takeProfitPrice]: take profit trigger price :param float [params.cost]: the quote quantity that can be used alternative for the amount :param float [params.trailingAmount]: *swap only* the quote amount to trail away from the current market price :param float [params.trailingPercent]: *swap only* the percent to trail away from the current market price :param dict [params.takeProfit]: *takeProfit object in params* containing the triggerPrice at which the attached take profit order will be triggered :param float [params.takeProfit.triggerPrice]: take profit trigger price :param dict [params.stopLoss]: *stopLoss object in params* containing the triggerPrice at which the attached stop loss order will be triggered :param float [params.stopLoss.triggerPrice]: stop loss trigger price :param boolean [params.test]: *swap only* whether to use the test endpoint or not, default is False :param str [params.positionSide]: *contracts only* "BOTH" for one way mode, "LONG" for buy side of hedged mode, "SHORT" for sell side of hedged mode :param boolean [params.hedged]: *swap only* whether the order is in hedged mode or one way mode :returns dict: an `order structure ` """ self.load_markets() market = self.market(symbol) test = self.safe_bool(params, 'test', False) params = self.omit(params, 'test') request = self.create_order_request(symbol, type, side, amount, price, params) response = None if market['swap']: if test: response = self.swapV2PrivatePostTradeOrderTest(request) elif market['inverse']: response = self.cswapV1PrivatePostTradeOrder(request) elif type == 'twap': response = self.swapV1PrivatePostTwapOrder(request) else: response = self.swapV2PrivatePostTradeOrder(request) else: response = self.spotV1PrivatePostTradeOrder(request) # # spot # # { # "code": 0, # "msg": "", # "data": { # "symbol": "XRP-USDT", # "orderId": 1514090846268424192, # "transactTime": 1649822362855, # "price": "0.5", # "origQty": "10", # "executedQty": "0", # "cummulativeQuoteQty": "0", # "status": "PENDING", # "type": "LIMIT", # "side": "BUY" # } # } # # linear swap # # { # "code": 0, # "msg": "", # "data": { # "order": { # "symbol": "BTC-USDT", # "orderId": 1709036527545438208, # "side": "BUY", # "positionSide": "LONG", # "type": "TRIGGER_LIMIT", # "clientOrderID": "", # "workingType": "" # } # } # } # # inverse swap # # { # "orderId": 1809841379603398656, # "symbol": "SOL-USD", # "positionSide": "LONG", # "side": "BUY", # "type": "LIMIT", # "price": 100, # "quantity": 1, # "stopPrice": 0, # "workingType": "", # "timeInForce": "" # } # # twap order # # { # "code": 0, # "msg": "", # "timestamp": 1732693774386, # "data": { # "mainOrderId": "4633860139993029715" # } # } # if isinstance(response, str): # broken api engine : order-ids are too long numbers(i.e. 1742930526912864656) # and json.loadscan not handle them in JS, so we have to use .parseJson # however, when order has an attached SL/TP, their value types need extra parsing response = self.fix_stringified_json_members(response) response = self.parse_json(response) data = self.safe_dict(response, 'data', {}) result: dict = {} if market['swap']: if market['inverse']: result = response else: result = self.safe_dict(data, 'order', data) else: result = data return self.parse_order(result, market) def create_orders(self, orders: List[OrderRequest], params={}): """ create a list of trade orders https://bingx-api.github.io/docs/#/spot/trade-api.html#Batch%20Placing%20Orders https://bingx-api.github.io/docs/#/swapV2/trade-api.html#Bulk%20order :param Array orders: list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params :param dict [params]: extra parameters specific to the exchange API endpoint :param boolean [params.sync]: *spot only* if True, multiple orders are ordered serially and all orders do not require the same symbol/side/type :returns dict: an `order structure ` """ self.load_markets() ordersRequests = [] marketIds = [] for i in range(0, len(orders)): rawOrder = orders[i] marketId = self.safe_string(rawOrder, 'symbol') type = self.safe_string(rawOrder, 'type') marketIds.append(marketId) side = self.safe_string(rawOrder, 'side') amount = self.safe_number(rawOrder, 'amount') price = self.safe_number(rawOrder, 'price') orderParams = self.safe_dict(rawOrder, 'params', {}) orderRequest = self.create_order_request(marketId, type, side, amount, price, orderParams) ordersRequests.append(orderRequest) symbols = self.market_symbols(marketIds, None, False, True, True) symbolsLength = len(symbols) market = self.market(symbols[0]) request: dict = {} response = None if market['swap']: if symbolsLength > 5: raise InvalidOrder(self.id + ' createOrders() can not create more than 5 orders at once for swap markets') request['batchOrders'] = self.json(ordersRequests) response = self.swapV2PrivatePostTradeBatchOrders(request) else: sync = self.safe_bool(params, 'sync', False) if sync: request['sync'] = True request['data'] = self.json(ordersRequests) response = self.spotV1PrivatePostTradeBatchOrders(request) # # spot # # { # "code": 0, # "msg": "", # "debugMsg": "", # "data": { # "orders": [ # { # "symbol": "BTC-USDT", # "orderId": 1720661389564968960, # "transactTime": 1699072618272, # "price": "25000", # "origQty": "0.0002", # "executedQty": "0", # "cummulativeQuoteQty": "0", # "status": "PENDING", # "type": "LIMIT", # "side": "BUY" # }, # ] # } # } # # swap # # { # "code": 0, # "msg": "", # "data": { # "orders": [ # { # "symbol": "BTC-USDT", # "orderId": 1720657081994006528, # "side": "BUY", # "positionSide": "LONG", # "type": "LIMIT", # "clientOrderID": "", # "workingType": "" # }, # ] # } # } # if isinstance(response, str): # broken api engine : order-ids are too long numbers(i.e. 1742930526912864656) # and json.loadscan not handle them in JS, so we have to use .parseJson # however, when order has an attached SL/TP, their value types need extra parsing response = self.fix_stringified_json_members(response) response = self.parse_json(response) data = self.safe_dict(response, 'data', {}) result = self.safe_list(data, 'orders', []) return self.parse_orders(result, market) def parse_order_side(self, side): sides: dict = { 'BUY': 'buy', 'SELL': 'sell', 'SHORT': 'sell', 'LONG': 'buy', 'ask': 'sell', 'bid': 'buy', } return self.safe_string(sides, side, side) def parse_order_type(self, type: Str): types: dict = { 'trigger_market': 'market', 'trigger_limit': 'limit', 'stop_limit': 'limit', 'stop_market': 'market', 'take_profit_market': 'market', 'stop': 'limit', } return self.safe_string(types, type, type) def parse_order(self, order: dict, market: Market = None) -> Order: # # spot # createOrder, createOrders, cancelOrder # # { # "symbol": "XRP-USDT", # "orderId": 1514090846268424192, # "transactTime": 1649822362855, # "price": "0.5", # "origQty": "10", # "executedQty": "0", # "cummulativeQuoteQty": "0", # "status": "PENDING", # "type": "LIMIT", # "side": "BUY" # } # # fetchOrder # # { # "symbol": "ETH-USDT", # "orderId": "1660602123001266176", # "price": "1700", # "origQty": "0.003", # "executedQty": "0", # "cummulativeQuoteQty": "0", # "status": "PENDING", # "type": "LIMIT", # "side": "BUY", # "time": "1684753373276", # "updateTime": "1684753373276", # "origQuoteOrderQty": "0", # "fee": "0", # "feeAsset": "ETH" # } # # fetchOpenOrders, fetchClosedOrders # # { # "symbol": "XRP-USDT", # "orderId": 1514073325788200960, # "price": "0.5", # "StopPrice": "0", # "origQty": "20", # "executedQty": "10", # "cummulativeQuoteQty": "5", # "status": "PENDING", # "type": "LIMIT", # "side": "BUY", # "time": 1649818185647, # "updateTime": 1649818185647, # "origQuoteOrderQty": "0" # "fee": "-0.01" # } # # # linear swap # createOrder, createOrders # # { # "symbol": "BTC-USDT", # "orderId": 1590973236294713344, # "side": "BUY", # "positionSide": "LONG", # "type": "LIMIT" # } # # inverse swap createOrder # # { # "orderId": 1809841379603398656, # "symbol": "SOL-USD", # "positionSide": "LONG", # "side": "BUY", # "type": "LIMIT", # "price": 100, # "quantity": 1, # "stopPrice": 0, # "workingType": "", # "timeInForce": "" # } # # fetchOrder, fetchOpenOrders, fetchClosedOrders # # { # "symbol": "BTC-USDT", # "orderId": 1709036527545438208, # "side": "BUY", # "positionSide": "LONG", # "type": "TRIGGER_LIMIT", # "origQty": "0.0010", # "price": "22000.0", # "executedQty": "0.0000", # "avgPrice": "0.0", # "cumQuote": "", # "stopPrice": "23000.0", # "profit": "", # "commission": "", # "status": "NEW", # "time": 1696301035187, # "updateTime": 1696301035187, # "clientOrderId": "", # "leverage": "", # "takeProfit": "", # "stopLoss": "", # "advanceAttr": 0, # "positionID": 0, # "takeProfitEntrustPrice": 0, # "stopLossEntrustPrice": 0, # "orderType": "", # "workingType": "MARK_PRICE" # } # with tp and sl # { # orderId: 1741440894764281900, # symbol: 'LTC-USDT', # positionSide: 'LONG', # side: 'BUY', # type: 'MARKET', # price: 0, # quantity: 1, # stopPrice: 0, # workingType: 'MARK_PRICE', # clientOrderID: '', # timeInForce: 'GTC', # priceRate: 0, # stopLoss: '{"stopPrice":50,"workingType":"MARK_PRICE","type":"STOP_MARKET","quantity":1}', # takeProfit: '{"stopPrice":150,"workingType":"MARK_PRICE","type":"TAKE_PROFIT_MARKET","quantity":1}', # reduceOnly: False # } # # editOrder(swap) # # { # cancelResult: 'true', # cancelMsg: '', # cancelResponse: { # cancelClientOrderId: '', # cancelOrderId: '1755336244265705472', # symbol: 'SOL-USDT', # orderId: '1755336244265705472', # side: 'SELL', # positionSide: 'SHORT', # type: 'LIMIT', # origQty: '1', # price: '100.000', # executedQty: '0', # avgPrice: '0.000', # cumQuote: '0', # stopPrice: '', # profit: '0.0000', # commission: '0.000000', # status: 'PENDING', # time: '1707339747860', # updateTime: '1707339747860', # clientOrderId: '', # leverage: '20X', # workingType: 'MARK_PRICE', # onlyOnePosition: False, # reduceOnly: False # }, # replaceResult: 'true', # replaceMsg: '', # newOrderResponse: { # orderId: '1755338440612995072', # symbol: 'SOL-USDT', # positionSide: 'SHORT', # side: 'SELL', # type: 'LIMIT', # price: '99', # quantity: '2', # stopPrice: '0', # workingType: 'MARK_PRICE', # clientOrderID: '', # timeInForce: 'GTC', # priceRate: '0', # stopLoss: '', # takeProfit: '', # reduceOnly: False # } # } # # editOrder(spot) # # { # cancelResult: {code: '0', msg: '', result: True}, # openResult: {code: '0', msg: '', result: True}, # orderOpenResponse: { # symbol: 'SOL-USDT', # orderId: '1755334007697866752', # transactTime: '1707339214620', # price: '99', # stopPrice: '0', # origQty: '0.2', # executedQty: '0', # cummulativeQuoteQty: '0', # status: 'PENDING', # type: 'LIMIT', # side: 'SELL', # clientOrderID: '' # }, # orderCancelResponse: { # symbol: 'SOL-USDT', # orderId: '1755117055251480576', # price: '100', # stopPrice: '0', # origQty: '0.2', # executedQty: '0', # cummulativeQuoteQty: '0', # status: 'CANCELED', # type: 'LIMIT', # side: 'SELL' # } # } # # stop loss order # # { # "symbol": "ETH-USDT", # "orderId": "1792461744476422144", # "price": "2775.65", # "StopPrice": "2778.42", # "origQty": "0.032359", # "executedQty": "0", # "cummulativeQuoteQty": "0", # "status": "NEW", # "type": "TAKE_STOP_LIMIT", # "side": "SELL", # "time": "1716191156868", # "updateTime": "1716191156868", # "origQuoteOrderQty": "0", # "fee": "0", # "feeAsset": "USDT", # "clientOrderID": "" # } # # inverse swap cancelAllOrders, cancelOrder, fetchOrder, fetchOpenOrders, fetchClosedOrders, fetchCanceledOrders # # { # "symbol": "SOL-USD", # "orderId": "1809845251327672320", # "side": "BUY", # "positionSide": "LONG", # "type": "LIMIT", # "quantity": 1, # "origQty": "0", # "price": "90", # "executedQty": "0", # "avgPrice": "0", # "cumQuote": "0", # "stopPrice": "", # "profit": "0.0000", # "commission": "0.000000", # "status": "CANCELLED", # "time": 1720335707872, # "updateTime": 1720335707912, # "clientOrderId": "", # "leverage": "", # "takeProfit": { # "type": "", # "quantity": 0, # "stopPrice": 0, # "price": 0, # "workingType": "", # "stopGuaranteed": "" # }, # "stopLoss": { # "type": "", # "quantity": 0, # "stopPrice": 0, # "price": 0, # "workingType": "", # "stopGuaranteed": "" # }, # "advanceAttr": 0, # "positionID": 0, # "takeProfitEntrustPrice": 0, # "stopLossEntrustPrice": 0, # "orderType": "", # "workingType": "" # } # info = order newOrder = self.safe_dict_2(order, 'newOrderResponse', 'orderOpenResponse') if newOrder is not None: order = newOrder positionSide = self.safe_string_2(order, 'positionSide', 'ps') marketType = 'spot' if (positionSide is None) else 'swap' marketId = self.safe_string_2(order, 'symbol', 's') if market is None: market = self.safe_market(marketId, None, None, marketType) side = self.safe_string_lower_2(order, 'side', 'S') timestamp = self.safe_integer_n(order, ['time', 'transactTime', 'E', 'createdTime']) lastTradeTimestamp = self.safe_integer_2(order, 'updateTime', 'T') statusId = self.safe_string_upper_n(order, ['status', 'X', 'orderStatus']) feeCurrencyCode = self.safe_string_2(order, 'feeAsset', 'N') feeCost = self.safe_string_n(order, ['fee', 'commission', 'n']) if (feeCurrencyCode is None): if market['spot']: if side == 'buy': feeCurrencyCode = market['base'] else: feeCurrencyCode = market['quote'] else: feeCurrencyCode = market['quote'] stopLoss = self.safe_value(order, 'stopLoss') stopLossPrice = None if (stopLoss is not None) and (stopLoss != ''): stopLossPrice = self.omit_zero(self.safe_string(stopLoss, 'stopLoss')) if (stopLoss is not None) and ((not isinstance(stopLoss, numbers.Real))) and (stopLoss != ''): # stopLoss: '{"stopPrice":50,"workingType":"MARK_PRICE","type":"STOP_MARKET","quantity":1}', if isinstance(stopLoss, str): stopLoss = self.parse_json(stopLoss) stopLossPrice = self.omit_zero(self.safe_string(stopLoss, 'stopPrice')) takeProfit = self.safe_value(order, 'takeProfit') takeProfitPrice = None if takeProfit is not None and (takeProfit != ''): takeProfitPrice = self.omit_zero(self.safe_string(takeProfit, 'takeProfit')) if (takeProfit is not None) and ((not isinstance(takeProfit, numbers.Real))) and (takeProfit != ''): # takeProfit: '{"stopPrice":150,"workingType":"MARK_PRICE","type":"TAKE_PROFIT_MARKET","quantity":1}', if isinstance(takeProfit, str): takeProfit = self.parse_json(takeProfit) takeProfitPrice = self.omit_zero(self.safe_string(takeProfit, 'stopPrice')) rawType = self.safe_string_lower_2(order, 'type', 'o') stopPrice = self.omit_zero(self.safe_string_2(order, 'StopPrice', 'stopPrice')) triggerPrice = stopPrice if stopPrice is not None: if (rawType.find('stop') > -1) and (stopLossPrice is None): stopLossPrice = stopPrice triggerPrice = None if (rawType.find('take') > -1) and (takeProfitPrice is None): takeProfitPrice = stopPrice triggerPrice = None return self.safe_order({ 'info': info, 'id': self.safe_string_n(order, ['orderId', 'i', 'mainOrderId']), 'clientOrderId': self.safe_string_n(order, ['clientOrderID', 'clientOrderId', 'origClientOrderId', 'c']), 'symbol': self.safe_symbol(marketId, market, '-', marketType), 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), 'lastTradeTimestamp': lastTradeTimestamp, 'lastUpdateTimestamp': self.safe_integer(order, 'updateTime'), 'type': self.parse_order_type(rawType), 'timeInForce': self.safe_string(order, 'timeInForce'), 'postOnly': None, 'side': self.parse_order_side(side), 'price': self.safe_string_2(order, 'price', 'p'), 'triggerPrice': triggerPrice, 'stopLossPrice': stopLossPrice, 'takeProfitPrice': takeProfitPrice, 'average': self.safe_string_2(order, 'avgPrice', 'ap'), 'cost': self.safe_string(order, 'cummulativeQuoteQty'), 'amount': self.safe_string_n(order, ['origQty', 'q', 'quantity', 'totalAmount']), 'filled': self.safe_string_2(order, 'executedQty', 'z'), 'remaining': None, 'status': self.parse_order_status(statusId), 'fee': { 'currency': feeCurrencyCode, 'cost': Precise.string_abs(feeCost), }, 'trades': None, 'reduceOnly': self.safe_bool_2(order, 'reduceOnly', 'ro'), }, market) def parse_order_status(self, status: Str): statuses: dict = { 'NEW': 'open', 'PENDING': 'open', 'PARTIALLY_FILLED': 'open', 'RUNNING': 'open', 'FILLED': 'closed', 'CANCELED': 'canceled', 'CANCELLED': 'canceled', 'FAILED': 'canceled', } return self.safe_string(statuses, status, status) def cancel_order(self, id: str, symbol: Str = None, params={}): """ cancels an open order https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Cancel%20Order https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Cancel%20Order https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Cancel%20an%20Order https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Cancel%20TWAP%20Order :param str id: order id :param str symbol: unified symbol of the market the order was made in :param dict [params]: extra parameters specific to the exchange API endpoint :param str [params.clientOrderId]: a unique id for the order :returns dict: an `order structure ` """ self.load_markets() isTwapOrder = self.safe_bool(params, 'twap', False) params = self.omit(params, 'twap') response = None market = None if isTwapOrder: twapRequest: dict = { 'mainOrderId': id, } response = self.swapV1PrivatePostTwapCancelOrder(self.extend(twapRequest, params)) # # { # "code": 0, # "msg": "", # "timestamp": 1702731661854, # "data": { # "symbol": "BNB-USDT", # "side": "BUY", # "positionSide": "LONG", # "priceType": "constant", # "priceVariance": "2000", # "triggerPrice": "68000", # "interval": 8, # "amountPerOrder": "0.111", # "totalAmount": "0.511", # "orderStatus": "Running", # "executedQty": "0.1", # "duration": 800, # "maxDuration": 9000, # "createdTime": 1702731661854, # "updateTime": 1702731661854 # } # } # else: if symbol is None: raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument') market = self.market(symbol) request: dict = { 'symbol': market['id'], } clientOrderId = self.safe_string_2(params, 'clientOrderId', 'clientOrderID') params = self.omit(params, ['clientOrderId']) if clientOrderId is not None: request['clientOrderID'] = clientOrderId else: request['orderId'] = id type = None subType = None type, params = self.handle_market_type_and_params('cancelOrder', market, params) subType, params = self.handle_sub_type_and_params('cancelOrder', market, params) if type == 'spot': response = self.spotV1PrivatePostTradeCancel(self.extend(request, params)) else: if subType == 'inverse': response = self.cswapV1PrivateDeleteTradeCancelOrder(self.extend(request, params)) else: response = self.swapV2PrivateDeleteTradeOrder(self.extend(request, params)) # # spot # # { # "code": 0, # "msg": "", # "data": { # "symbol": "XRP-USDT", # "orderId": 1514090846268424192, # "price": "0.5", # "origQty": "10", # "executedQty": "0", # "cummulativeQuoteQty": "0", # "status": "CANCELED", # "type": "LIMIT", # "side": "BUY" # } # } # # inverse swap # # { # "code": 0, # "msg": "", # "data": { # "order": { # "symbol": "SOL-USD", # "orderId": "1816002957423951872", # "side": "BUY", # "positionSide": "Long", # "type": "Pending", # "quantity": 0, # "origQty": "0", # "price": "150", # "executedQty": "0", # "avgPrice": "0", # "cumQuote": "0", # "stopPrice": "", # "profit": "0.0000", # "commission": "0.000000", # "status": "CANCELLED", # "time": 1721803819410, # "updateTime": 1721803819427, # "clientOrderId": "", # "leverage": "", # "takeProfit": { # "type": "", # "quantity": 0, # "stopPrice": 0, # "price": 0, # "workingType": "", # "stopGuaranteed": "" # }, # "stopLoss": { # "type": "", # "quantity": 0, # "stopPrice": 0, # "price": 0, # "workingType": "", # "stopGuaranteed": "" # }, # "advanceAttr": 0, # "positionID": 0, # "takeProfitEntrustPrice": 0, # "stopLossEntrustPrice": 0, # "orderType": "", # "workingType": "" # } # } # } # # linear swap # # { # "code": 0, # "msg": "", # "data": { # "order": { # "symbol": "LINK-USDT", # "orderId": 1597783850786750464, # "side": "BUY", # "positionSide": "LONG", # "type": "TRIGGER_MARKET", # "origQty": "5.0", # "price": "5.0000", # "executedQty": "0.0", # "avgPrice": "0.0000", # "cumQuote": "0", # "stopPrice": "5.0000", # "profit": "", # "commission": "", # "status": "CANCELLED", # "time": 1669776330000, # "updateTime": 1669776330000 # } # } # } # data = self.safe_dict(response, 'data', {}) order = self.safe_dict(data, 'order', data) return self.parse_order(order, market) def cancel_all_orders(self, symbol: Str = None, params={}): """ cancel all open orders https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Cancel%20orders%20by%20symbol https://bingx-api.github.io/docs/#/swapV2/trade-api.html#Cancel%20All%20Orders https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Cancel%20all%20orders :param str [symbol]: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict[]: a list of `order structures ` """ if symbol is None: raise ArgumentsRequired(self.id + ' cancelAllOrders() requires a symbol argument') self.load_markets() market = self.market(symbol) request: dict = { 'symbol': market['id'], } response = None if market['spot']: response = self.spotV1PrivatePostTradeCancelOpenOrders(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "debugMsg": "", # "data": { # "orders": [{ # "symbol": "ADA-USDT", # "orderId": 1740659971369992192, # "transactTime": 1703840651730, # "price": 5, # "stopPrice": 0, # "origQty": 10, # "executedQty": 0, # "cummulativeQuoteQty": 0, # "status": "CANCELED", # "type": "LIMIT", # "side": "SELL" # }] # } # } # elif market['swap']: if market['inverse']: response = self.cswapV1PrivateDeleteTradeAllOpenOrders(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "timestamp": 1720501468364, # "data": { # "success": [ # { # "symbol": "SOL-USD", # "orderId": "1809845251327672320", # "side": "BUY", # "positionSide": "LONG", # "type": "LIMIT", # "quantity": 1, # "origQty": "0", # "price": "90", # "executedQty": "0", # "avgPrice": "0", # "cumQuote": "0", # "stopPrice": "", # "profit": "0.0000", # "commission": "0.000000", # "status": "CANCELLED", # "time": 1720335707872, # "updateTime": 1720335707912, # "clientOrderId": "", # "leverage": "", # "takeProfit": { # "type": "", # "quantity": 0, # "stopPrice": 0, # "price": 0, # "workingType": "", # "stopGuaranteed": "" # }, # "stopLoss": { # "type": "", # "quantity": 0, # "stopPrice": 0, # "price": 0, # "workingType": "", # "stopGuaranteed": "" # }, # "advanceAttr": 0, # "positionID": 0, # "takeProfitEntrustPrice": 0, # "stopLossEntrustPrice": 0, # "orderType": "", # "workingType": "" # } # ], # "failed": null # } # } # else: response = self.swapV2PrivateDeleteTradeAllOpenOrders(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "data": { # "success": [ # { # "symbol": "LINK-USDT", # "orderId": 1597783835095859200, # "side": "BUY", # "positionSide": "LONG", # "type": "TRIGGER_LIMIT", # "origQty": "5.0", # "price": "9.0000", # "executedQty": "0.0", # "avgPrice": "0.0000", # "cumQuote": "0", # "stopPrice": "9.5000", # "profit": "", # "commission": "", # "status": "NEW", # "time": 1669776326000, # "updateTime": 1669776326000 # } # ], # "failed": null # } # } # else: raise BadRequest(self.id + ' cancelAllOrders is only supported for spot and swap markets.') data = self.safe_dict(response, 'data', {}) orders = self.safe_list_2(data, 'success', 'orders', []) return self.parse_orders(orders) def cancel_orders(self, ids: List[str], symbol: Str = None, params={}): """ cancel multiple orders https://bingx-api.github.io/docs/#/swapV2/trade-api.html#Cancel%20a%20Batch%20of%20Orders https://bingx-api.github.io/docs/#/spot/trade-api.html#Cancel%20a%20Batch%20of%20Orders :param str[] ids: order ids :param str symbol: unified market symbol, default is None :param dict [params]: extra parameters specific to the exchange API endpoint :param str[] [params.clientOrderIds]: client order ids :returns dict: an list of `order structures ` """ if symbol is None: raise ArgumentsRequired(self.id + ' cancelOrders() requires a symbol argument') self.load_markets() market = self.market(symbol) request: dict = { 'symbol': market['id'], } clientOrderIds = self.safe_value(params, 'clientOrderIds') params = self.omit(params, 'clientOrderIds') idsToParse = ids areClientOrderIds = (clientOrderIds is not None) if areClientOrderIds: idsToParse = clientOrderIds parsedIds = [] for i in range(0, len(idsToParse)): id = idsToParse[i] stringId = str(id) parsedIds.append(stringId) response = None if market['spot']: spotReqKey = 'clientOrderIDs' if areClientOrderIds else 'orderIds' request[spotReqKey] = ','.join(parsedIds) response = self.spotV1PrivatePostTradeCancelOrders(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "debugMsg": "", # "data": { # "orders": [ # { # "symbol": "SOL-USDT", # "orderId": 1795970045910614016, # "transactTime": 1717027601111, # "price": "180.25", # "stopPrice": "0", # "origQty": "0.03", # "executedQty": "0", # "cummulativeQuoteQty": "0", # "status": "CANCELED", # "type": "LIMIT", # "side": "SELL", # "clientOrderID": "" # }, # ... # ] # } # } # else: if areClientOrderIds: request['clientOrderIDList'] = self.json(parsedIds) else: request['orderIdList'] = parsedIds response = self.swapV2PrivateDeleteTradeBatchOrders(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "data": { # "success": [ # { # "symbol": "LINK-USDT", # "orderId": 1597783850786750464, # "side": "BUY", # "positionSide": "LONG", # "type": "TRIGGER_MARKET", # "origQty": "5.0", # "price": "5.5710", # "executedQty": "0.0", # "avgPrice": "0.0000", # "cumQuote": "0", # "stopPrice": "5.0000", # "profit": "0.0000", # "commission": "0.000000", # "status": "CANCELLED", # "time": 1669776330000, # "updateTime": 1672370837000 # } # ], # "failed": null # } # } # data = self.safe_dict(response, 'data', {}) success = self.safe_list_2(data, 'success', 'orders', []) return self.parse_orders(success) def cancel_all_orders_after(self, timeout: Int, params={}): """ dead man's switch, cancel all orders after the given timeout https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Cancel%20all%20orders%20in%20countdown https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Cancel%20all%20orders%20in%20countdown :param number timeout: time in milliseconds, 0 represents cancel the timer :param dict [params]: extra parameters specific to the exchange API endpoint :param str [params.type]: spot or swap market :returns dict: the api result """ self.load_markets() isActive = (timeout > 0) request: dict = { 'type': 'ACTIVATE' if (isActive) else 'CLOSE', 'timeOut': (self.parse_to_int(timeout / 1000)) if (isActive) else 0, } response = None type = None type, params = self.handle_market_type_and_params('cancelAllOrdersAfter', None, params) if type == 'spot': response = self.spotV1PrivatePostTradeCancelAllAfter(self.extend(request, params)) elif type == 'swap': response = self.swapV2PrivatePostTradeCancelAllAfter(self.extend(request, params)) else: raise NotSupported(self.id + ' cancelAllOrdersAfter() is not supported for ' + type + ' markets') # # { # code: '0', # msg: '', # data: { # triggerTime: '1712645434', # status: 'ACTIVATED', # note: 'All your perpetual pending orders will be closed automatically at 2024-04-09 06:50:34 UTC(+0),before that you can cancel the timer, or self.extend triggerTime time by self request' # } # } # return response def fetch_order(self, id: str, symbol: Str = None, params={}): """ fetches information on an order made by the user https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Query%20Order%20details https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20Order%20details https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Query%20Order https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#TWAP%20Order%20Details :param str id: the order id :param str symbol: unified symbol of the market the order was made in :param dict [params]: extra parameters specific to the exchange API endpoint :param boolean [params.twap]: if fetching twap order :returns dict: an `order structure ` """ self.load_markets() isTwapOrder = self.safe_bool(params, 'twap', False) params = self.omit(params, 'twap') response = None market = None if isTwapOrder: twapRequest: dict = { 'mainOrderId': id, } response = self.swapV1PrivateGetTwapOrderDetail(self.extend(twapRequest, params)) # # { # "code": 0, # "msg": "success cancel order", # "timestamp": 1732760856617, # "data": { # "symbol": "LTC-USDT", # "mainOrderId": "5596903086063901779", # "side": "BUY", # "positionSide": "LONG", # "priceType": "constant", # "priceVariance": "10.00", # "triggerPrice": "120.00", # "interval": 8, # "amountPerOrder": "0.5", # "totalAmount": "1.0", # "orderStatus": "Filled", # "executedQty": "1.0", # "duration": 16, # "maxDuration": 86400, # "createdTime": 1732693017000, # "updateTime": 1732693033000 # } # } # else: if symbol is None: raise ArgumentsRequired(self.id + ' fetchOrder() requires a symbol argument') market = self.market(symbol) request: dict = { 'symbol': market['id'], 'orderId': id, } type = None subType = None type, params = self.handle_market_type_and_params('fetchOrder', market, params) subType, params = self.handle_sub_type_and_params('fetchOrder', market, params) if type == 'spot': response = self.spotV1PrivateGetTradeQuery(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "data": { # "symbol": "XRP-USDT", # "orderId": 1514087361158316032, # "price": "0.5", # "origQty": "10", # "executedQty": "0", # "cummulativeQuoteQty": "0", # "status": "CANCELED", # "type": "LIMIT", # "side": "BUY", # "time": 1649821532000, # "updateTime": 1649821543000, # "origQuoteOrderQty": "0", # "fee": "0", # "feeAsset": "XRP" # } # } # else: if subType == 'inverse': response = self.cswapV1PrivateGetTradeOrderDetail(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "data": { # "order": { # "symbol": "SOL-USD", # "orderId": "1816342420721254400", # "side": "BUY", # "positionSide": "Long", # "type": "LIMIT", # "quantity": 1, # "origQty": "", # "price": "150", # "executedQty": "0", # "avgPrice": "0.000", # "cumQuote": "", # "stopPrice": "", # "profit": "0.0000", # "commission": "0.0000", # "status": "Pending", # "time": 1721884753767, # "updateTime": 1721884753786, # "clientOrderId": "", # "leverage": "", # "takeProfit": { # "type": "TAKE_PROFIT", # "quantity": 0, # "stopPrice": 0, # "price": 0, # "workingType": "MARK_PRICE", # "stopGuaranteed": "" # }, # "stopLoss": { # "type": "STOP", # "quantity": 0, # "stopPrice": 0, # "price": 0, # "workingType": "MARK_PRICE", # "stopGuaranteed": "" # }, # "advanceAttr": 0, # "positionID": 0, # "takeProfitEntrustPrice": 0, # "stopLossEntrustPrice": 0, # "orderType": "", # "workingType": "MARK_PRICE" # } # } # } # else: response = self.swapV2PrivateGetTradeOrder(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "data": { # "order": { # "symbol": "BTC-USDT", # "orderId": 1597597642269917184, # "side": "SELL", # "positionSide": "LONG", # "type": "TAKE_PROFIT_MARKET", # "origQty": "1.0000", # "price": "0.0", # "executedQty": "0.0000", # "avgPrice": "0.0", # "cumQuote": "", # "stopPrice": "16494.0", # "profit": "", # "commission": "", # "status": "FILLED", # "time": 1669731935000, # "updateTime": 1669752524000 # } # } # } # data = self.safe_dict(response, 'data', {}) order = self.safe_dict(data, 'order', data) return self.parse_order(order, market) def fetch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]: """ fetches information on multiple orders made by the user https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#All%20Orders https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20Order%20history(returns less fields than above) :param str 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 dict [params]: extra parameters specific to the exchange API endpoint :param int [params.until]: the latest time in ms to fetch entries for :param int [params.orderId]: Only return subsequent orders, and return the latest order by default :returns Order[]: a list of `order structures ` """ self.load_markets() request: dict = {} market = None if symbol is not None: market = self.market(symbol) request['symbol'] = market['id'] type = None type, params = self.handle_market_type_and_params('fetchOrders', market, params) if type != 'swap': raise NotSupported(self.id + ' fetchOrders() is only supported for swap markets') if limit is not None: request['limit'] = limit if since is not None: request['startTime'] = since request, params = self.handle_until_option('endTime', request, params) response = self.swapV1PrivateGetTradeFullOrder(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "data": { # "orders": [ # { # "symbol": "PYTH-USDT", # "orderId": 1736007506620112100, # "side": "SELL", # "positionSide": "SHORT", # "type": "LIMIT", # "origQty": "33", # "price": "0.3916", # "executedQty": "33", # "avgPrice": "0.3916", # "cumQuote": "13", # "stopPrice": "", # "profit": "0.0000", # "commission": "-0.002585", # "status": "FILLED", # "time": 1702731418000, # "updateTime": 1702731470000, # "clientOrderId": "", # "leverage": "15X", # "takeProfit": { # "type": "TAKE_PROFIT", # "quantity": 0, # "stopPrice": 0, # "price": 0, # "workingType": "" # }, # "stopLoss": { # "type": "STOP", # "quantity": 0, # "stopPrice": 0, # "price": 0, # "workingType": "" # }, # "advanceAttr": 0, # "positionID": 0, # "takeProfitEntrustPrice": 0, # "stopLossEntrustPrice": 0, # "orderType": "", # "workingType": "MARK_PRICE", # "stopGuaranteed": False, # "triggerOrderId": 1736012449498123500 # } # ] # } # } # data = self.safe_dict(response, 'data', {}) orders = self.safe_list(data, 'orders', []) return self.parse_orders(orders, market, since, limit) def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]: """ fetch all unfilled currently open orders https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Current%20Open%20Orders https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Current%20All%20Open%20Orders https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Query%20all%20current%20pending%20orders https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20TWAP%20Entrusted%20Order :param str 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 dict [params]: extra parameters specific to the exchange API endpoint :param boolean [params.twap]: if fetching twap open orders :returns dict[]: a list of `order structures ` """ self.load_markets() market = None request: dict = {} if symbol is not None: market = self.market(symbol) request['symbol'] = market['id'] type = None subType = None response = None type, params = self.handle_market_type_and_params('fetchOpenOrders', market, params) subType, params = self.handle_sub_type_and_params('fetchOpenOrders', market, params) if type == 'spot': response = self.spotV1PrivateGetTradeOpenOrders(self.extend(request, params)) else: isTwapOrder = self.safe_bool(params, 'twap', False) params = self.omit(params, 'twap') if isTwapOrder: response = self.swapV1PrivateGetTwapOpenOrders(self.extend(request, params)) elif subType == 'inverse': response = self.cswapV1PrivateGetTradeOpenOrders(self.extend(request, params)) else: response = self.swapV2PrivateGetTradeOpenOrders(self.extend(request, params)) # # spot # # { # "code": 0, # "msg": "", # "data": { # "orders": [ # { # "symbol": "XRP-USDT", # "orderId": 1514073325788200960, # "price": "0.5", # "origQty": "20", # "executedQty": "0", # "cummulativeQuoteQty": "0", # "status": "PENDING", # "type": "LIMIT", # "side": "BUY", # "time": 1649818185647, # "updateTime": 1649818185647, # "origQuoteOrderQty": "0" # } # ] # } # } # # inverse swap # # { # "code": 0, # "msg": "", # "data": { # "orders": [ # { # "symbol": "SOL-USD", # "orderId": "1816013900044320768", # "side": "BUY", # "positionSide": "Long", # "type": "LIMIT", # "quantity": 1, # "origQty": "", # "price": "150", # "executedQty": "0", # "avgPrice": "0.000", # "cumQuote": "", # "stopPrice": "", # "profit": "0.0000", # "commission": "0.0000", # "status": "Pending", # "time": 1721806428334, # "updateTime": 1721806428352, # "clientOrderId": "", # "leverage": "", # "takeProfit": { # "type": "TAKE_PROFIT", # "quantity": 0, # "stopPrice": 0, # "price": 0, # "workingType": "MARK_PRICE", # "stopGuaranteed": "" # }, # "stopLoss": { # "type": "STOP", # "quantity": 0, # "stopPrice": 0, # "price": 0, # "workingType": "MARK_PRICE", # "stopGuaranteed": "" # }, # "advanceAttr": 0, # "positionID": 0, # "takeProfitEntrustPrice": 0, # "stopLossEntrustPrice": 0, # "orderType": "", # "workingType": "MARK_PRICE" # } # ] # } # } # # linear swap # # { # "code": 0, # "msg": "", # "data": { # "orders": [ # { # "symbol": "LINK-USDT", # "orderId": 1585839271162413056, # "side": "BUY", # "positionSide": "LONG", # "type": "TRIGGER_MARKET", # "origQty": "5.0", # "price": "9", # "executedQty": "0.0", # "avgPrice": "0", # "cumQuote": "0", # "stopPrice": "5", # "profit": "0.0000", # "commission": "0.000000", # "status": "CANCELLED", # "time": 1667631605000, # "updateTime": 1667631605000 # }, # ] # } # } # # twap # # { # "code": 0, # "msg": "", # "timestamp": 1702731661854, # "data": { # "list": [ # { # "symbol": "BNB-USDT", # "side": "BUY", # "positionSide": "LONG", # "priceType": "constant", # "priceVariance": "2000", # "triggerPrice": "68000", # "interval": 8, # "amountPerOrder": "0.111", # "totalAmount": "0.511", # "orderStatus": "Running", # "executedQty": "0.1", # "duration": 800, # "maxDuration": 9000, # "createdTime": 1702731661854, # "updateTime": 1702731661854 # } # ], # "total": 1 # } # } # data = self.safe_dict(response, 'data', {}) orders = self.safe_list_2(data, 'orders', 'list', []) return self.parse_orders(orders, market, since, limit) def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]: """ fetches information on multiple closed orders made by the user https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Query%20Order%20history https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20Order%20history https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#User's%20History%20Orders https://bingx-api.github.io/docs/#/standard/contract-interface.html#Historical%20order :param str symbol: unified market symbol of the closed orders :param int [since]: timestamp in ms of the earliest order :param int [limit]: the max number of closed orders to return :param dict [params]: extra parameters specific to the exchange API endpoint :param int [params.until]: the latest time in ms to fetch orders for :param boolean [params.standard]: whether to fetch standard contract orders :returns Order[]: a list of `order structures ` """ self.load_markets() orders = self.fetch_canceled_and_closed_orders(symbol, since, limit, params) return self.filter_by(orders, 'status', 'closed') def fetch_canceled_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}): """ fetches information on multiple canceled orders made by the user https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Query%20Order%20history https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20Order%20history https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#User's%20History%20Orders https://bingx-api.github.io/docs/#/standard/contract-interface.html#Historical%20order :param str symbol: unified market symbol of the canceled orders :param int [since]: timestamp in ms of the earliest order :param int [limit]: the max number of canceled orders to return :param dict [params]: extra parameters specific to the exchange API endpoint :param int [params.until]: the latest time in ms to fetch orders for :param boolean [params.standard]: whether to fetch standard contract orders :returns dict: a list of `order structures ` """ self.load_markets() orders = self.fetch_canceled_and_closed_orders(symbol, since, limit, params) return self.filter_by(orders, 'status', 'canceled') def fetch_canceled_and_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}): """ fetches information on multiple closed orders made by the user https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Query%20Order%20history https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20Order%20history https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#User's%20History%20Orders https://bingx-api.github.io/docs/#/standard/contract-interface.html#Historical%20order https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20TWAP%20Historical%20Orders :param str [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 dict [params]: extra parameters specific to the exchange API endpoint :param int [params.until]: the latest time in ms to fetch orders for :param boolean [params.standard]: whether to fetch standard contract orders :param boolean [params.twap]: if fetching twap orders :returns dict[]: a list of `order structures ` """ self.load_markets() market = None request: dict = {} if symbol is not None: market = self.market(symbol) request['symbol'] = market['id'] type = None subType = None standard = None response = None type, params = self.handle_market_type_and_params('fetchClosedOrders', market, params) subType, params = self.handle_sub_type_and_params('fetchClosedOrders', market, params) standard, params = self.handle_option_and_params(params, 'fetchClosedOrders', 'standard', False) if standard: response = self.contractV1PrivateGetAllOrders(self.extend(request, params)) elif type == 'spot': if limit is not None: request['pageSize'] = limit response = self.spotV1PrivateGetTradeHistoryOrders(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "data": { # "orders": [ # { # "symbol": "XRP-USDT", # "orderId": 1514073325788200960, # "price": "0.5", # "origQty": "20", # "executedQty": "0", # "cummulativeQuoteQty": "0", # "status": "PENDING", # "type": "LIMIT", # "side": "BUY", # "time": 1649818185647, # "updateTime": 1649818185647, # "origQuoteOrderQty": "0" # } # ] # } # } # else: isTwapOrder = self.safe_bool(params, 'twap', False) params = self.omit(params, 'twap') if isTwapOrder: request['pageIndex'] = 1 request['pageSize'] = 100 if (limit is None) else limit request['startTime'] = 1 if (since is None) else since until = self.safe_integer(params, 'until', self.milliseconds()) params = self.omit(params, 'until') request['endTime'] = until response = self.swapV1PrivateGetTwapHistoryOrders(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "timestamp": 1702731661854, # "data": { # "list": [ # { # "symbol": "BNB-USDT", # "side": "BUY", # "positionSide": "LONG", # "priceType": "constant", # "priceVariance": "2000", # "triggerPrice": "68000", # "interval": 8, # "amountPerOrder": "0.111", # "totalAmount": "0.511", # "orderStatus": "Running", # "executedQty": "0.1", # "duration": 800, # "maxDuration": 9000, # "createdTime": 1702731661854, # "updateTime": 1702731661854 # } # ], # "total": 1 # } # } # elif subType == 'inverse': response = self.cswapV1PrivateGetTradeOrderHistory(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "data": { # "orders": [ # { # "symbol": "SOL-USD", # "orderId": "1816002957423951872", # "side": "BUY", # "positionSide": "LONG", # "type": "LIMIT", # "quantity": 1, # "origQty": "10.00000000", # "price": "150.000", # "executedQty": "0.00000000", # "avgPrice": "0.000", # "cumQuote": "", # "stopPrice": "0.000", # "profit": "0.0000", # "commission": "0.000000", # "status": "Filled", # "time": 1721803819000, # "updateTime": 1721803856000, # "clientOrderId": "", # "leverage": "", # "takeProfit": { # "type": "", # "quantity": 0, # "stopPrice": 0, # "price": 0, # "workingType": "", # "stopGuaranteed": "" # }, # "stopLoss": { # "type": "", # "quantity": 0, # "stopPrice": 0, # "price": 0, # "workingType": "", # "stopGuaranteed": "" # }, # "advanceAttr": 0, # "positionID": 0, # "takeProfitEntrustPrice": 0, # "stopLossEntrustPrice": 0, # "orderType": "", # "workingType": "MARK_PRICE" # }, # ] # } # } # else: response = self.swapV2PrivateGetTradeAllOrders(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "data": { # "orders": [ # { # "symbol": "LINK-USDT", # "orderId": 1585839271162413056, # "side": "BUY", # "positionSide": "LONG", # "type": "TRIGGER_MARKET", # "origQty": "5.0", # "price": "9", # "executedQty": "0.0", # "avgPrice": "0", # "cumQuote": "0", # "stopPrice": "5", # "profit": "0.0000", # "commission": "0.000000", # "status": "CANCELLED", # "time": 1667631605000, # "updateTime": 1667631605000 # }, # ] # } # } # data = self.safe_dict(response, 'data', {}) orders = self.safe_list_2(data, 'orders', 'list', []) return self.parse_orders(orders, market, since, limit) def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry: """ transfer currency internally between wallets on the same account https://bingx-api.github.io/docs/#/en-us/common/account-api.html#Asset%20Transfer%20New :param str code: unified currency code :param float amount: amount to transfer :param str fromAccount: account to transfer from(spot, swap, futures, or funding) :param str toAccount: account to transfer to(spot, swap(linear or inverse), future, or funding) :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: a `transfer structure ` """ self.load_markets() currency = self.currency(code) accountsByType = self.safe_dict(self.options, 'accountsByType', {}) subType = None subType, params = self.handle_sub_type_and_params('transfer', None, params) fromId = self.safe_string(accountsByType, fromAccount, fromAccount) toId = self.safe_string(accountsByType, toAccount, toAccount) if fromId == 'swap': if subType == 'inverse': fromId = 'coinMPerp' else: fromId = 'USDTMPerp' if toId == 'swap': if subType == 'inverse': toId = 'coinMPerp' else: toId = 'USDTMPerp' request: dict = { 'fromAccount': fromId, 'toAccount': toId, 'asset': currency['id'], 'amount': self.currency_to_precision(code, amount), } response = self.apiAssetV1PrivatePostTransfer(self.extend(request, params)) # # { # "tranId": 1933130865269936128, # "transferId": "1051450703949464903736" # } # return { 'info': response, 'id': self.safe_string(response, 'transferId'), 'timestamp': None, 'datetime': None, 'currency': code, 'amount': amount, 'fromAccount': fromAccount, 'toAccount': toAccount, 'status': None, } def fetch_transfers(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[TransferEntry]: """ fetch a history of internal transfers made on an account https://bingx-api.github.io/docs/#/en-us/common/account-api.html#Asset%20transfer%20records%20new :param str [code]: unified currency code of the currency transferred :param int [since]: the earliest time in ms to fetch transfers for :param int [limit]: the maximum number of transfers structures to retrieve(default 10, max 100) :param dict [params]: extra parameters specific to the exchange API endpoint :param str params.fromAccount:(mandatory) transfer from(spot, swap(linear or inverse), future, or funding) :param str params.toAccount:(mandatory) transfer to(spot, swap(linear or inverse), future, or funding) :param boolean [params.paginate]: whether to paginate the results(default False) :returns dict[]: a list of `transfer structures ` """ self.load_markets() request: dict = {} currency = None if code is not None: currency = self.currency(code) accountsByType = self.safe_dict(self.options, 'accountsByType', {}) fromAccount = self.safe_string(params, 'fromAccount') toAccount = self.safe_string(params, 'toAccount') fromId = self.safe_string(accountsByType, fromAccount, fromAccount) toId = self.safe_string(accountsByType, toAccount, toAccount) if fromId is None or toId is None: raise ExchangeError(self.id + ' fromAccount & toAccount parameters are required') if fromAccount is not None: request['fromAccount'] = fromId if toAccount is not None: request['toAccount'] = toId params = self.omit(params, ['fromAccount', 'toAccount']) maxLimit = 100 paginate = False paginate, params = self.handle_option_and_params(params, 'fetchTransfers', 'paginate', False) if paginate: return self.fetch_paginated_call_dynamic('fetchTransfers', None, since, limit, params, maxLimit) if since is not None: request['startTime'] = since if limit is not None: request['pageSize'] = limit request, params = self.handle_until_option('endTime', request, params) response = self.apiV3PrivateGetAssetTransferRecord(self.extend(request, params)) # # { # "total": 2, # "rows": [ # { # "asset": "LTC", # "amount": "0.05000000000000000000", # "status": "CONFIRMED", # "transferId": "1051461075661819338791", # "timestamp": 1752202092000, # "fromAccount": "spot", # "toAccount": "USDTMPerp" # } # ] # } # rows = self.safe_list(response, 'rows', []) return self.parse_transfers(rows, currency, since, limit) def parse_transfer(self, transfer: dict, currency: Currency = None) -> TransferEntry: tranId = self.safe_string(transfer, 'transferId') timestamp = self.safe_integer(transfer, 'timestamp') currencyId = self.safe_string(transfer, 'asset') currencyCode = self.safe_currency_code(currencyId, currency) status = self.safe_string(transfer, 'status') accountsById = self.safe_dict(self.options, 'accountsById', {}) fromId = self.safe_string(transfer, 'fromAccount') toId = self.safe_string(transfer, 'toAccount') fromAccount = self.safe_string(accountsById, fromId, fromId) toAccount = self.safe_string(accountsById, toId, toId) return { 'info': transfer, 'id': tranId, 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), 'currency': currencyCode, 'amount': self.safe_number(transfer, 'amount'), 'fromAccount': fromAccount, 'toAccount': toAccount, 'status': self.parse_transfer_status(status), } def parse_transfer_status(self, status: Str) -> str: statuses: dict = { 'CONFIRMED': 'ok', } return self.safe_string(statuses, status, status) def fetch_deposit_addresses_by_network(self, code: str, params={}) -> List[DepositAddress]: """ fetch the deposit addresses for a currency associated with self account https://bingx-api.github.io/docs/#/en-us/common/wallet-api.html#Query%20Main%20Account%20Deposit%20Address :param str code: unified currency code :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: a dictionary `address structures `, indexed by the network """ self.load_markets() currency = self.currency(code) defaultRecvWindow = self.safe_integer(self.options, 'recvWindow') recvWindow = self.safe_integer(self.parse_params, 'recvWindow', defaultRecvWindow) request: dict = { 'coin': currency['id'], 'offset': 0, 'limit': 1000, 'recvWindow': recvWindow, } response = self.walletsV1PrivateGetCapitalDepositAddress(self.extend(request, params)) # # { # "code": "0", # "timestamp": "1695200226859", # "data": { # "data": [ # { # "coinId": "799", # "coin": "USDT", # "network": "BEP20", # "address": "6a7eda2817462dabb6493277a2cfe0f5c3f2550b", # "tag": '' # } # ], # "total": "1" # } # } # data = self.safe_list(self.safe_dict(response, 'data'), 'data') parsed = self.parse_deposit_addresses(data, [currency['code']], False) return self.index_by(parsed, 'network') def fetch_deposit_address(self, code: str, params={}) -> DepositAddress: """ fetch the deposit address for a currency associated with self account https://bingx-api.github.io/docs/#/en-us/common/wallet-api.html#Query%20Main%20Account%20Deposit%20Address :param str code: unified currency code :param dict [params]: extra parameters specific to the exchange API endpoint :param str [params.network]: The chain of currency. This only apply for multi-chain currency, and there is no need for single chain currency :returns dict: an `address structure ` """ network = self.safe_string(params, 'network') params = self.omit(params, ['network']) addressStructures = self.fetch_deposit_addresses_by_network(code, params) if network is not None: return self.safe_dict(addressStructures, network) else: options = self.safe_dict(self.options, 'defaultNetworks') defaultNetworkForCurrency = self.safe_string(options, code) if defaultNetworkForCurrency is not None: return self.safe_dict(addressStructures, defaultNetworkForCurrency) else: keys = list(addressStructures.keys()) key = self.safe_string(keys, 0) return self.safe_dict(addressStructures, key) def parse_deposit_address(self, depositAddress, currency: Currency = None) -> DepositAddress: # # { # "coinId":"4", # "coin":"USDT", # "network":"OMNI", # "address":"1HXyx8HVQRY7Nhqz63nwnRB7SpS9xQPzLN", # "addressWithPrefix":"1HXyx8HVQRY7Nhqz63nwnRB7SpS9xQPzLN" # } # tag = self.safe_string(depositAddress, 'tag') currencyId = self.safe_string(depositAddress, 'coin') currency = self.safe_currency(currencyId, currency) code = currency['code'] address = self.safe_string(depositAddress, 'addressWithPrefix') networkdId = self.safe_string(depositAddress, 'network') networkCode = self.network_id_to_code(networkdId, code) self.check_address(address) return { 'info': depositAddress, 'currency': code, 'network': networkCode, 'address': address, 'tag': tag, } def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]: """ fetch all deposits made to an account https://bingx-api.github.io/docs/#/spot/account-api.html#Deposit%20History(supporting%20network) :param str [code]: unified currency code :param int [since]: the earliest time in ms to fetch deposits for :param int [limit]: the maximum number of deposits structures to retrieve :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict[]: a list of `transaction structures ` """ self.load_markets() request: dict = { } currency = None if code is not None: currency = self.currency(code) request['coin'] = currency['id'] if since is not None: request['startTime'] = since if limit is not None: request['limit'] = limit # default 1000 response = self.spotV3PrivateGetCapitalDepositHisrec(self.extend(request, params)) # # [ # { # "amount":"0.00999800", # "coin":"PAXG", # "network":"ETH", # "status":1, # "address":"0x788cabe9236ce061e5a892e1a59395a81fc8d62c", # "addressTag":"", # "txId":"0xaad4654a3234aa6118af9b4b335f5ae81c360b2394721c019b5d1e75328b09f3", # "insertTime":1599621997000, # "transferType":0, # "unlockConfirm":"12/12", # confirm times for unlocking # "confirmTimes":"12/12" # }, # ] # return self.parse_transactions(response, currency, since, limit) def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]: """ fetch all withdrawals made from an account https://bingx-api.github.io/docs/#/spot/account-api.html#Withdraw%20History%20(supporting%20network) :param str [code]: unified currency code :param int [since]: the earliest time in ms to fetch withdrawals for :param int [limit]: the maximum number of withdrawals structures to retrieve :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict[]: a list of `transaction structures ` """ self.load_markets() request: dict = { } currency = None if code is not None: currency = self.currency(code) request['coin'] = currency['id'] if since is not None: request['startTime'] = since if limit is not None: request['limit'] = limit # default 1000 response = self.spotV3PrivateGetCapitalWithdrawHistory(self.extend(request, params)) # # [ # { # "address": "0x94df8b352de7f46f64b01d3666bf6e936e44ce60", # "amount": "8.91000000", # "applyTime": "2019-10-12 11:12:02", # "coin": "USDT", # "id": "b6ae22b3aa844210a7041aee7589627c", # "withdrawOrderId": "WITHDRAWtest123", # "network": "ETH", # "transferType": 0 # "status": 6, # "transactionFee": "0.004", # "confirmNo":3, # "info": "The address is not valid. Please confirm with the recipient", # "txId": "0xb5ef8c13b968a406cc62a93a8bd80f9e9a906ef1b3fcf20a2e48573c17659268" # }, # ] # return self.parse_transactions(response, currency, since, limit) def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction: # # fetchDeposits # # { # "amount":"0.00999800", # "coin":"PAXG", # "network":"ETH", # "status":1, # "address":"0x788cabe9236ce061e5a892e1a59395a81fc8d62c", # "addressTag":"", # "txId":"0xaad4654a3234aa6118af9b4b335f5ae81c360b2394721c019b5d1e75328b09f3", # "insertTime":1599621997000, # "transferType":0, # "unlockConfirm":"12/12", # confirm times for unlocking # "confirmTimes":"12/12" # } # # fetchWithdrawals # # { # "address": "0x94df8b352de7f46f64b01d3666bf6e936e44ce60", # "amount": "8.91000000", # "applyTime": "2019-10-12 11:12:02", # "coin": "USDT", # "id": "b6ae22b3aa844210a7041aee7589627c", # "withdrawOrderId": "WITHDRAWtest123", # "network": "ETH", # "transferType": 0 # "status": 6, # "transactionFee": "0.004", # "confirmNo":3, # "info": "The address is not valid. Please confirm with the recipient", # "txId": "0xb5ef8c13b968a406cc62a93a8bd80f9e9a906ef1b3fcf20a2e48573c17659268" # } # # withdraw # # { # "code":0, # "timestamp":1705274263621, # "data":{ # "id":"1264246141278773252" # } # } # # parse withdraw-type output first... # data = self.safe_value(transaction, 'data') dataId = None if (data is None) else self.safe_string(data, 'id') id = self.safe_string(transaction, 'id', dataId) address = self.safe_string(transaction, 'address') tag = self.safe_string(transaction, 'addressTag') timestamp = self.safe_integer_2(transaction, 'insertTime', 'timestamp') datetime = self.iso8601(timestamp) if timestamp is None: datetime = self.safe_string(transaction, 'applyTime') timestamp = self.parse8601(datetime) network = self.safe_string(transaction, 'network') currencyId = self.safe_string(transaction, 'coin') code = self.safe_currency_code(currencyId, currency) if (code is not None) and (code != network) and code.find(network) >= 0: if network is not None: code = code.replace(network, '') rawType = self.safe_string(transaction, 'transferType') type = 'deposit' if (rawType == '0') else 'withdrawal' return { 'info': transaction, 'id': id, 'txid': self.safe_string(transaction, 'txId'), 'type': type, 'currency': code, 'network': self.network_id_to_code(network), 'amount': self.safe_number(transaction, 'amount'), 'status': self.parse_transaction_status(self.safe_string(transaction, 'status')), 'timestamp': timestamp, 'datetime': datetime, 'address': address, 'addressFrom': None, 'addressTo': address, 'tag': tag, 'tagFrom': tag, 'tagTo': None, 'updated': None, 'comment': self.safe_string(transaction, 'info'), 'fee': { 'currency': code, 'cost': self.safe_number(transaction, 'transactionFee'), 'rate': None, }, 'internal': None, } def parse_transaction_status(self, status: str): statuses: dict = { '0': 'pending', '1': 'ok', '10': 'pending', '20': 'rejected', '30': 'ok', '40': 'rejected', '50': 'ok', '60': 'pending', '70': 'rejected', '2': 'pending', '3': 'rejected', '4': 'pending', '5': 'rejected', '6': 'ok', } return self.safe_string(statuses, status, status) def set_margin_mode(self, marginMode: str, symbol: Str = None, params={}): """ set margin mode to 'cross' or 'isolated' https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Change%20Margin%20Type https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Set%20Margin%20Type :param str marginMode: 'cross' or 'isolated' :param str symbol: unified market symbol :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: response from the exchange """ if symbol is None: raise ArgumentsRequired(self.id + ' setMarginMode() requires a symbol argument') self.load_markets() market = self.market(symbol) if market['type'] != 'swap': raise BadSymbol(self.id + ' setMarginMode() supports swap contracts only') marginMode = marginMode.upper() if marginMode == 'CROSS': marginMode = 'CROSSED' if marginMode != 'ISOLATED' and marginMode != 'CROSSED': raise BadRequest(self.id + ' setMarginMode() marginMode argument should be isolated or cross') request: dict = { 'symbol': market['id'], 'marginType': marginMode, } subType = None subType, params = self.handle_sub_type_and_params('setMarginMode', market, params) if subType == 'inverse': return self.cswapV1PrivatePostTradeMarginType(self.extend(request, params)) else: return self.swapV2PrivatePostTradeMarginType(self.extend(request, params)) def add_margin(self, symbol: str, amount: float, params={}) -> MarginModification: request: dict = { 'type': 1, } return self.set_margin(symbol, amount, self.extend(request, params)) def reduce_margin(self, symbol: str, amount: float, params={}) -> MarginModification: request: dict = { 'type': 2, } return self.set_margin(symbol, amount, self.extend(request, params)) def set_margin(self, symbol: str, amount: float, params={}) -> MarginModification: """ Either adds or reduces margin in an isolated position in order to set the margin to a specific value https://bingx-api.github.io/docs/#/swapV2/trade-api.html#Adjust%20isolated%20margin :param str symbol: unified market symbol of the market to set margin in :param float amount: the amount to set the margin to :param dict [params]: parameters specific to the bingx api endpoint :returns dict: A `margin structure ` """ type = self.safe_integer(params, 'type') # 1 increase margin 2 decrease margin if type is None: raise ArgumentsRequired(self.id + ' setMargin() requires a type parameter either 1(increase margin) or 2(decrease margin)') if not self.in_array(type, [1, 2]): raise ArgumentsRequired(self.id + ' setMargin() requires a type parameter either 1(increase margin) or 2(decrease margin)') self.load_markets() market = self.market(symbol) request: dict = { 'symbol': market['id'], 'amount': self.amount_to_precision(market['symbol'], amount), 'type': type, } response = self.swapV2PrivatePostTradePositionMargin(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "amount": 1, # "type": 1 # } # return self.parse_margin_modification(response, market) def parse_margin_modification(self, data: dict, market: Market = None) -> MarginModification: # # { # "code": 0, # "msg": "", # "amount": 1, # "type": 1 # } # type = self.safe_string(data, 'type') return { 'info': data, 'symbol': self.safe_string(market, 'symbol'), 'type': 'add' if (type == '1') else 'reduce', 'marginMode': 'isolated', 'amount': self.safe_number(data, 'amount'), 'total': self.safe_number(data, 'margin'), 'code': self.safe_string(market, 'settle'), 'status': None, 'timestamp': None, 'datetime': None, } def fetch_leverage(self, symbol: str, params={}) -> Leverage: """ fetch the set leverage for a market https://bingx-api.github.io/docs/#/swapV2/trade-api.html#Query%20Leverage https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Query%20Leverage :param str symbol: unified market symbol :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: a `leverage structure ` """ self.load_markets() market = self.market(symbol) request: dict = { 'symbol': market['id'], } response = None if market['inverse']: response = self.cswapV1PrivateGetTradeLeverage(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "timestamp": 1720683803391, # "data": { # "symbol": "SOL-USD", # "longLeverage": 5, # "shortLeverage": 5, # "maxLongLeverage": 50, # "maxShortLeverage": 50, # "availableLongVol": "4000000", # "availableShortVol": "4000000" # } # } # else: response = self.swapV2PrivateGetTradeLeverage(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "data": { # "longLeverage": 5, # "shortLeverage": 5, # "maxLongLeverage": 125, # "maxShortLeverage": 125, # "availableLongVol": "0.0000", # "availableShortVol": "0.0000", # "availableLongVal": "0.0", # "availableShortVal": "0.0", # "maxPositionLongVal": "0.0", # "maxPositionShortVal": "0.0" # } # } # data = self.safe_dict(response, 'data', {}) return self.parse_leverage(data, market) def parse_leverage(self, leverage: dict, market: Market = None) -> Leverage: # # linear swap # # { # "longLeverage": 5, # "shortLeverage": 5, # "maxLongLeverage": 125, # "maxShortLeverage": 125, # "availableLongVol": "0.0000", # "availableShortVol": "0.0000", # "availableLongVal": "0.0", # "availableShortVal": "0.0", # "maxPositionLongVal": "0.0", # "maxPositionShortVal": "0.0" # } # # inverse swap # # { # "symbol": "SOL-USD", # "longLeverage": 5, # "shortLeverage": 5, # "maxLongLeverage": 50, # "maxShortLeverage": 50, # "availableLongVol": "4000000", # "availableShortVol": "4000000" # } # marketId = self.safe_string(leverage, 'symbol') return { 'info': leverage, 'symbol': self.safe_symbol(marketId, market), 'marginMode': None, 'longLeverage': self.safe_integer(leverage, 'longLeverage'), 'shortLeverage': self.safe_integer(leverage, 'shortLeverage'), } def set_leverage(self, leverage: int, symbol: Str = None, params={}): """ set the level of leverage for a market https://bingx-api.github.io/docs/#/swapV2/trade-api.html#Switch%20Leverage https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Modify%20Leverage :param float leverage: the rate of leverage :param str symbol: unified market symbol :param dict [params]: extra parameters specific to the exchange API endpoint :param str [params.side]: hedged: ['long' or 'short']. one way: ['both'] :returns dict: response from the exchange """ if symbol is None: raise ArgumentsRequired(self.id + ' setLeverage() requires a symbol argument') side = self.safe_string_upper(params, 'side') self.check_required_argument('setLeverage', side, 'side', ['LONG', 'SHORT', 'BOTH']) self.load_markets() market = self.market(symbol) request: dict = { 'symbol': market['id'], 'side': side, 'leverage': leverage, } if market['inverse']: return self.cswapV1PrivatePostTradeLeverage(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "timestamp": 1720725058059, # "data": { # "symbol": "SOL-USD", # "longLeverage": 10, # "shortLeverage": 5, # "maxLongLeverage": 50, # "maxShortLeverage": 50, # "availableLongVol": "4000000", # "availableShortVol": "4000000" # } # } # else: return self.swapV2PrivatePostTradeLeverage(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "data": { # "leverage": 10, # "symbol": "BTC-USDT", # "availableLongVol": "0.0000", # "availableShortVol": "0.0000", # "availableLongVal": "0.0", # "availableShortVal": "0.0", # "maxPositionLongVal": "0.0", # "maxPositionShortVal": "0.0" # } # } # def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}): """ fetch all trades made by the user https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Query%20transaction%20details https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20historical%20transaction%20orders https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20historical%20transaction%20details https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Query%20Order%20Trade%20Detail :param str [symbol]: unified market symbol :param int [since]: the earliest time in ms to fetch trades for :param int [limit]: the maximum number of trades structures to retrieve :param dict [params]: extra parameters specific to the exchange API endpoint :param int [params.until]: timestamp in ms for the ending date filter, default is None :param str params['trandingUnit']: COIN(directly represent assets such and ETH) or CONT(represents the number of contract sheets) :param str params['orderId']: the order id required for inverse swap :returns dict[]: a list of `trade structures ` """ if symbol is None: raise ArgumentsRequired(self.id + ' fetchMyTrades() requires a symbol argument') self.load_markets() market = self.market(symbol) request: dict = {} fills = None response = None subType = None subType, params = self.handle_sub_type_and_params('fetchMyTrades', market, params) if subType == 'inverse': orderId = self.safe_string(params, 'orderId') if orderId is None: raise ArgumentsRequired(self.id + ' fetchMyTrades() requires an orderId argument for inverse swap trades') response = self.cswapV1PrivateGetTradeAllFillOrders(self.extend(request, params)) fills = self.safe_list(response, 'data', []) # # { # "code": 0, # "msg": "", # "timestamp": 1722147756019, # "data": [ # { # "orderId": "1817441228670648320", # "symbol": "SOL-USD", # "type": "MARKET", # "side": "BUY", # "positionSide": "LONG", # "tradeId": "97244554", # "volume": "2", # "tradePrice": "182.652", # "amount": "20.00000000", # "realizedPnl": "0.00000000", # "commission": "-0.00005475", # "currency": "SOL", # "buyer": True, # "maker": False, # "tradeTime": 1722146730000 # } # ] # } # else: request['symbol'] = market['id'] now = self.milliseconds() if since is not None: startTimeReq = 'startTime' if market['spot'] else 'startTs' request[startTimeReq] = since elif market['swap']: request['startTs'] = now - 30 * 24 * 60 * 60 * 1000 # 30 days for swap until = self.safe_integer(params, 'until') params = self.omit(params, 'until') if until is not None: endTimeReq = 'endTime' if market['spot'] else 'endTs' request[endTimeReq] = until elif market['swap']: request['endTs'] = now if market['spot']: if limit is not None: request['limit'] = limit # default 500, maximum 1000 response = self.spotV1PrivateGetTradeMyTrades(self.extend(request, params)) data = self.safe_dict(response, 'data', {}) fills = self.safe_list(data, 'fills', []) # # { # "code": 0, # "msg": "", # "debugMsg": "", # "data": { # "fills": [ # { # "symbol": "LTC-USDT", # "id": 36237072, # "orderId": 1674069326895775744, # "price": "85.891", # "qty": "0.0582", # "quoteQty": "4.9988562000000005", # "commission": -0.00005820000000000001, # "commissionAsset": "LTC", # "time": 1687964205000, # "isBuyer": True, # "isMaker": False # } # ] # } # } # else: tradingUnit = self.safe_string_upper(params, 'tradingUnit', 'CONT') params = self.omit(params, 'tradingUnit') request['tradingUnit'] = tradingUnit response = self.swapV2PrivateGetTradeAllFillOrders(self.extend(request, params)) data = self.safe_dict(response, 'data', {}) fills = self.safe_list(data, 'fill_orders', []) # # { # "code": "0", # "msg": '', # "data": {fill_orders: [ # { # "volume": "0.1", # "price": "106.75", # "amount": "10.6750", # "commission": "-0.0053", # "currency": "USDT", # "orderId": "1676213270274379776", # "liquidatedPrice": "0.00", # "liquidatedMarginRatio": "0.00", # "filledTime": "2023-07-04T20:56:01.000+0800" # } # ] # } # } # return self.parse_trades(fills, market, since, limit, params) def parse_deposit_withdraw_fee(self, fee, currency: Currency = None): # # currencie structure # networks = self.safe_dict(fee, 'networks', {}) networkCodes = list(networks.keys()) networksLength = len(networkCodes) result: dict = { 'info': networks, 'withdraw': { 'fee': None, 'percentage': None, }, 'deposit': { 'fee': None, 'percentage': None, }, 'networks': {}, } if networksLength != 0: for i in range(0, networksLength): networkCode = networkCodes[i] network = networks[networkCode] result['networks'][networkCode] = { 'deposit': {'fee': None, 'percentage': None}, 'withdraw': {'fee': self.safe_number(network, 'fee'), 'percentage': False}, } if networksLength == 1: result['withdraw']['fee'] = self.safe_number(network, 'withdrawFee') result['withdraw']['percentage'] = False return result def fetch_deposit_withdraw_fees(self, codes: Strings = None, params={}): """ fetch deposit and withdraw fees https://bingx-api.github.io/docs/#/common/account-api.html#All%20Coins'%20Information :param str[]|None codes: list of unified currency codes :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: a list of `fee structures ` """ self.load_markets() response = self.fetch_currencies(params) depositWithdrawFees: dict = {} responseCodes = list(response.keys()) for i in range(0, len(responseCodes)): code = responseCodes[i] if (codes is None) or (self.in_array(code, codes)): entry = response[code] depositWithdrawFees[code] = self.parse_deposit_withdraw_fee(entry) return depositWithdrawFees def withdraw(self, code: str, amount: float, address: str, tag: Str = None, params={}) -> Transaction: """ make a withdrawal https://bingx-api.github.io/docs/#/en-us/spot/wallet-api.html#Withdraw :param str code: unified currency code :param float amount: the amount to withdraw :param str address: the address to withdraw to :param str [tag]: :param dict [params]: extra parameters specific to the exchange API endpoint :param int [params.walletType]: 1 fund(funding) account, 2 standard account, 3 perpetual account, 15 spot account :returns dict: a `transaction structure ` """ tag, params = self.handle_withdraw_tag_and_params(tag, params) self.check_address(address) self.load_markets() currency = self.currency(code) defaultWalletType = 15 # spot walletType = None walletType, params = self.handle_option_and_params_2(params, 'withdraw', 'type', 'walletType', defaultWalletType) walletTypes = { 'funding': 1, 'fund': 1, 'standard': 2, 'perpetual': 3, 'spot': 15, } walletType = self.safe_integer(walletTypes, walletType, defaultWalletType) request: dict = { 'coin': currency['id'], 'address': address, 'amount': self.currency_to_precision(code, amount), 'walletType': walletType, } network = self.safe_string_upper(params, 'network') if network is not None: request['network'] = self.network_code_to_id(network) if tag is not None: request['addressTag'] = tag params = self.omit(params, ['walletType', 'network']) response = self.walletsV1PrivatePostCapitalWithdrawApply(self.extend(request, params)) data = self.safe_value(response, 'data') # { # "code":0, # "timestamp":1689258953651, # "data":{ # "id":"1197073063359000577" # } # } return self.parse_transaction(data) def parse_params(self, params): # sortedParams = self.keysort(params) rawKeys = list(params.keys()) keys = self.sort(rawKeys) for i in range(0, len(keys)): key = keys[i] value = params[key] if isinstance(value, list): arrStr = '[' for j in range(0, len(value)): arrayElement = value[j] if j > 0: arrStr += ',' arrStr += str(arrayElement) arrStr += ']' params[key] = arrStr return params def fetch_my_liquidations(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}): """ retrieves the users liquidated positions https://bingx-api.github.io/docs/#/swapV2/trade-api.html#User's%20Force%20Orders https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Query%20force%20orders :param str [symbol]: unified CCXT market symbol :param int [since]: the earliest time in ms to fetch liquidations for :param int [limit]: the maximum number of liquidation structures to retrieve :param dict [params]: exchange specific parameters for the bingx api endpoint :param int [params.until]: timestamp in ms of the latest liquidation :returns dict: an array of `liquidation structures ` """ self.load_markets() request: dict = { 'autoCloseType': 'LIQUIDATION', } request, params = self.handle_until_option('endTime', request, params) market = None if symbol is not None: market = self.market(symbol) request['symbol'] = market['id'] if since is not None: request['startTime'] = since if limit is not None: request['limit'] = limit subType = None subType, params = self.handle_sub_type_and_params('fetchMyLiquidations', market, params) response = None liquidations = None if subType == 'inverse': response = self.cswapV1PrivateGetTradeForceOrders(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "timestamp": 1721280071678, # "data": [ # { # "orderId": "string", # "symbol": "string", # "type": "string", # "side": "string", # "positionSide": "string", # "price": "string", # "quantity": "float64", # "stopPrice": "string", # "workingType": "string", # "status": "string", # "time": "int64", # "avgPrice": "string", # "executedQty": "string", # "profit": "string", # "commission": "string", # "updateTime": "string" # } # ] # } # liquidations = self.safe_list(response, 'data', []) else: response = self.swapV2PrivateGetTradeForceOrders(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "data": { # "orders": [ # { # "time": "int64", # "symbol": "string", # "side": "string", # "type": "string", # "positionSide": "string", # "cumQuote": "string", # "status": "string", # "stopPrice": "string", # "price": "string", # "origQty": "string", # "avgPrice": "string", # "executedQty": "string", # "orderId": "int64", # "profit": "string", # "commission": "string", # "workingType": "string", # "updateTime": "int64" # }, # ] # } # } # data = self.safe_dict(response, 'data', {}) liquidations = self.safe_list(data, 'orders', []) return self.parse_liquidations(liquidations, market, since, limit) def parse_liquidation(self, liquidation, market: Market = None): # # { # "time": "int64", # "symbol": "string", # "side": "string", # "type": "string", # "positionSide": "string", # "cumQuote": "string", # "status": "string", # "stopPrice": "string", # "price": "string", # "origQty": "string", # "avgPrice": "string", # "executedQty": "string", # "orderId": "int64", # "profit": "string", # "commission": "string", # "workingType": "string", # "updateTime": "int64" # } # marketId = self.safe_string(liquidation, 'symbol') timestamp = self.safe_integer(liquidation, 'time') contractsString = self.safe_string(liquidation, 'executedQty') contractSizeString = self.safe_string(market, 'contractSize') priceString = self.safe_string(liquidation, 'avgPrice') baseValueString = Precise.string_mul(contractsString, contractSizeString) quoteValueString = Precise.string_mul(baseValueString, priceString) return self.safe_liquidation({ 'info': liquidation, 'symbol': self.safe_symbol(marketId, market), 'contracts': self.parse_number(contractsString), 'contractSize': self.parse_number(contractSizeString), 'price': self.parse_number(priceString), 'baseValue': self.parse_number(baseValueString), 'quoteValue': self.parse_number(quoteValueString), 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), }) def close_position(self, symbol: str, side: OrderSide = None, params={}) -> Order: """ closes open positions for a market https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#One-Click%20Close%20All%20Positions https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Close%20all%20positions%20in%20bulk :param str symbol: Unified CCXT market symbol :param str [side]: not used by bingx :param dict [params]: extra parameters specific to the bingx api endpoint :param str|None [params.positionId]: the id of the position you would like to close :returns dict: an `order structure ` """ self.load_markets() market = self.market(symbol) positionId = self.safe_string(params, 'positionId') request: dict = {} response = None if positionId is not None: response = self.swapV1PrivatePostTradeClosePosition(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "timestamp": 1710992264190, # "data": { # "orderId": 1770656007907930112, # "positionId": "1751667128353910784", # "symbol": "LTC-USDT", # "side": "Ask", # "type": "MARKET", # "positionSide": "Long", # "origQty": "0.2" # } # } # else: request['symbol'] = market['id'] if market['inverse']: response = self.cswapV1PrivatePostTradeCloseAllPositions(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "timestamp": 1720771601428, # "data": { # "success": ["1811673520637231104"], # "failed": null # } # } # else: response = self.swapV2PrivatePostTradeCloseAllPositions(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "data": { # "success": [ # 1727686766700486656, # ], # "failed": null # } # } # data = self.safe_dict(response, 'data') return self.parse_order(data, market) def close_all_positions(self, params={}) -> List[Position]: """ closes open positions for a market https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#One-Click%20Close%20All%20Positions https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Close%20all%20positions%20in%20bulk :param dict [params]: extra parameters specific to the bingx api endpoint :param str [params.recvWindow]: request valid time window value :returns dict[]: `a list of position structures ` """ self.load_markets() defaultRecvWindow = self.safe_integer(self.options, 'recvWindow') recvWindow = self.safe_integer(self.parse_params, 'recvWindow', defaultRecvWindow) marketType = None marketType, params = self.handle_market_type_and_params('closeAllPositions', None, params) subType = None subType, params = self.handle_sub_type_and_params('closeAllPositions', None, params) if marketType == 'margin': raise BadRequest(self.id + ' closePositions() cannot be used for ' + marketType + ' markets') request: dict = { 'recvWindow': recvWindow, } response = None if subType == 'inverse': response = self.cswapV1PrivatePostTradeCloseAllPositions(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "timestamp": 1720771601428, # "data": { # "success": ["1811673520637231104"], # "failed": null # } # } # else: response = self.swapV2PrivatePostTradeCloseAllPositions(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "data": { # "success": [ # 1727686766700486656, # 1727686767048613888 # ], # "failed": null # } # } # data = self.safe_dict(response, 'data', {}) success = self.safe_list(data, 'success', []) positions = [] for i in range(0, len(success)): position = self.parse_position({'positionId': success[i]}) positions.append(position) return positions def fetch_position_mode(self, symbol: Str = None, params={}): """ fetchs the position mode, hedged or one way, hedged for binance is set identically for all linear markets or all inverse markets https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Get%20Position%20Mode :param str symbol: unified symbol of the market to fetch the order book for :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: an object detailing whether the market is in hedged or one-way mode """ response = self.swapV1PrivateGetPositionSideDual(params) # # { # "code": "0", # "msg": "", # "timeStamp": "1709002057516", # "data": { # "dualSidePosition": "false" # } # } # data = self.safe_dict(response, 'data', {}) dualSidePosition = self.safe_string(data, 'dualSidePosition') return { 'info': response, 'hedged': (dualSidePosition == 'true'), } def set_position_mode(self, hedged: bool, symbol: Str = None, params={}): """ set hedged to True or False for a market https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Set%20Position%20Mode :param bool hedged: set to True to use dualSidePosition :param str symbol: not used by bingx setPositionMode() :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: response from the exchange """ dualSidePosition = None if hedged: dualSidePosition = 'true' else: dualSidePosition = 'false' request: dict = { 'dualSidePosition': dualSidePosition, } # # { # code: '0', # msg: '', # timeStamp: '1703327432734', # data: {dualSidePosition: 'false'} # } # return self.swapV1PrivatePostPositionSideDual(self.extend(request, params)) def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}) -> Order: """ cancels an order and places a new order https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Cancel%20order%20and%20place%20a%20new%20order # spot https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Cancel%20an%20order%20and%20then%20Place%20a%20new%20order # swap :param str id: order id :param str symbol: unified symbol of the market to create an order in :param str type: 'market' or 'limit' :param str 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, ignored in market orders :param dict [params]: extra parameters specific to the exchange API endpoint :param str [params.triggerPrice]: Trigger price used for TAKE_STOP_LIMIT, TAKE_STOP_MARKET, TRIGGER_LIMIT, TRIGGER_MARKET order types. :param dict [params.takeProfit]: *takeProfit object in params* containing the triggerPrice at which the attached take profit order will be triggered :param float [params.takeProfit.triggerPrice]: take profit trigger price :param dict [params.stopLoss]: *stopLoss object in params* containing the triggerPrice at which the attached stop loss order will be triggered :param float [params.stopLoss.triggerPrice]: stop loss trigger price EXCHANGE SPECIFIC PARAMETERS :param str [params.cancelClientOrderID]: the user-defined id of the order to be canceled, 1-40 characters, different orders cannot use the same clientOrderID, only supports a query range of 2 hours :param str [params.cancelRestrictions]: cancel orders with specified status, NEW: New order, PENDING: Pending order, PARTIALLY_FILLED: Partially filled :param str [params.cancelReplaceMode]: STOP_ON_FAILURE - if the cancel order fails, it will not continue to place a new order, ALLOW_FAILURE - regardless of whether the cancel order succeeds or fails, it will continue to place a new order :param float [params.quoteOrderQty]: order amount :param str [params.newClientOrderId]: custom order id consisting of letters, numbers, and _, 1-40 characters, different orders cannot use the same newClientOrderId. :param str [params.positionSide]: *contract only* position direction, required for single position, for both long and short positions only LONG or SHORT can be chosen, defaults to LONG if empty :param str [params.reduceOnly]: *contract only* True or False, default=false for single position mode. self parameter is not accepted for both long and short positions mode :param float [params.priceRate]: *contract only* for type TRAILING_STOP_Market or TRAILING_TP_SL, Max = 1 :param str [params.workingType]: *contract only* StopPrice trigger price types, MARK_PRICE(default), CONTRACT_PRICE, or INDEX_PRICE :returns dict: an `order structure ` """ self.load_markets() market = self.market(symbol) request = self.create_order_request(symbol, type, side, amount, price, params) request['cancelOrderId'] = id request['cancelReplaceMode'] = 'STOP_ON_FAILURE' response = None if market['swap']: response = self.swapV1PrivatePostTradeCancelReplace(self.extend(request, params)) # # { # code: '0', # msg: '', # data: { # cancelResult: 'true', # cancelMsg: '', # cancelResponse: { # cancelClientOrderId: '', # cancelOrderId: '1755336244265705472', # symbol: 'SOL-USDT', # orderId: '1755336244265705472', # side: 'SELL', # positionSide: 'SHORT', # type: 'LIMIT', # origQty: '1', # price: '100.000', # executedQty: '0', # avgPrice: '0.000', # cumQuote: '0', # stopPrice: '', # profit: '0.0000', # commission: '0.000000', # status: 'PENDING', # time: '1707339747860', # updateTime: '1707339747860', # clientOrderId: '', # leverage: '20X', # workingType: 'MARK_PRICE', # onlyOnePosition: False, # reduceOnly: False # }, # replaceResult: 'true', # replaceMsg: '', # newOrderResponse: { # orderId: '1755338440612995072', # symbol: 'SOL-USDT', # positionSide: 'SHORT', # side: 'SELL', # type: 'LIMIT', # price: '99', # quantity: '2', # stopPrice: '0', # workingType: 'MARK_PRICE', # clientOrderID: '', # timeInForce: 'GTC', # priceRate: '0', # stopLoss: '', # takeProfit: '', # reduceOnly: False # } # } # } # else: response = self.spotV1PrivatePostTradeOrderCancelReplace(self.extend(request, params)) # # { # code: '0', # msg: '', # debugMsg: '', # data: { # cancelResult: {code: '0', msg: '', result: True}, # openResult: {code: '0', msg: '', result: True}, # orderOpenResponse: { # symbol: 'SOL-USDT', # orderId: '1755334007697866752', # transactTime: '1707339214620', # price: '99', # stopPrice: '0', # origQty: '0.2', # executedQty: '0', # cummulativeQuoteQty: '0', # status: 'PENDING', # type: 'LIMIT', # side: 'SELL', # clientOrderID: '' # }, # orderCancelResponse: { # symbol: 'SOL-USDT', # orderId: '1755117055251480576', # price: '100', # stopPrice: '0', # origQty: '0.2', # executedQty: '0', # cummulativeQuoteQty: '0', # status: 'CANCELED', # type: 'LIMIT', # side: 'SELL' # } # } # } # data = self.safe_dict(response, 'data') return self.parse_order(data, market) def fetch_margin_mode(self, symbol: str, params={}) -> MarginMode: """ fetches the margin mode of the trading pair https://bingx-api.github.io/docs/#/en-us/swapV2/trade-api.html#Query%20Margin%20Type https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Query%20Margin%20Type :param str symbol: unified symbol of the market to fetch the margin mode for :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: a `margin mode structure ` """ self.load_markets() market = self.market(symbol) request: dict = { 'symbol': market['id'], } subType = None response = None subType, params = self.handle_sub_type_and_params('fetchMarginMode', market, params) if subType == 'inverse': response = self.cswapV1PrivateGetTradeMarginType(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "timestamp": 1721966069132, # "data": { # "symbol": "SOL-USD", # "marginType": "CROSSED" # } # } # else: response = self.swapV2PrivateGetTradeMarginType(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "data": { # "marginType": "CROSSED" # } # } # data = self.safe_dict(response, 'data', {}) return self.parse_margin_mode(data, market) def parse_margin_mode(self, marginMode: dict, market=None) -> MarginMode: marketId = self.safe_string(marginMode, 'symbol') marginType = self.safe_string_lower(marginMode, 'marginType') marginType = 'cross' if (marginType == 'crossed') else marginType return { 'info': marginMode, 'symbol': self.safe_symbol(marketId, market, '-', 'swap'), 'marginMode': marginType, } def fetch_trading_fee(self, symbol: str, params={}) -> TradingFeeInterface: """ fetch the trading fees for a market https://bingx-api.github.io/docs/#/en-us/spot/trade-api.html#Query%20Trading%20Commission%20Rate https://bingx-api.github.io/docs/#/en-us/swapV2/account-api.html#Query%20Trading%20Commission%20Rate https://bingx-api.github.io/docs/#/en-us/cswap/trade-api.html#Query%20Trade%20Commission%20Rate :param str symbol: unified market symbol :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: a `fee structure ` """ self.load_markets() market = self.market(symbol) request: dict = { 'symbol': market['id'], } response = None commission: dict = {} data = self.safe_dict(response, 'data', {}) if market['spot']: response = self.spotV1PrivateGetUserCommissionRate(self.extend(request, params)) # # { # "code": 0, # "msg": "", # "debugMsg": "", # "data": { # "takerCommissionRate": 0.001, # "makerCommissionRate": 0.001 # } # } # commission = data else: if market['inverse']: response = self.cswapV1PrivateGetUserCommissionRate(params) # # { # "code": 0, # "msg": "", # "timestamp": 1721365261438, # "data": { # "takerCommissionRate": "0.0005", # "makerCommissionRate": "0.0002" # } # } # commission = data else: response = self.swapV2PrivateGetUserCommissionRate(params) # # { # "code": 0, # "msg": "", # "data": { # "commission": { # "takerCommissionRate": 0.0005, # "makerCommissionRate": 0.0002 # } # } # } # commission = self.safe_dict(data, 'commission', {}) return self.parse_trading_fee(commission, market) def parse_trading_fee(self, fee: dict, market: Market = None) -> TradingFeeInterface: # # { # "takerCommissionRate": 0.001, # "makerCommissionRate": 0.001 # } # symbol = market['symbol'] if (market is not None) else None return { 'info': fee, 'symbol': symbol, 'maker': self.safe_number(fee, 'makerCommissionRate'), 'taker': self.safe_number(fee, 'takerCommissionRate'), 'percentage': False, 'tierBased': False, } def custom_encode(self, params): # sortedParams = self.keysort(params) rawKeys = list(params.keys()) keys = self.sort(rawKeys) adjustedValue = None result = None for i in range(0, len(keys)): key = keys[i] value = params[key] if isinstance(value, list): arrStr = None for j in range(0, len(value)): arrayElement = value[j] isString = (isinstance(arrayElement, str)) if isString: if j > 0: arrStr += ',' + '"' + str(arrayElement) + '"' else: arrStr = '"' + str(arrayElement) + '"' else: if j > 0: arrStr += ',' + str(arrayElement) else: arrStr = str(arrayElement) adjustedValue = '[' + arrStr + ']' value = adjustedValue if i == 0: result = key + '=' + value else: result += '&' + key + '=' + value return result def sign(self, path, section='public', method='GET', params={}, headers=None, body=None): type = section[0] version = section[1] access = section[2] isSandbox = self.safe_bool(self.options, 'sandboxMode', False) if isSandbox and (type != 'swap'): raise NotSupported(self.id + ' does not have a testnet/sandbox URL for ' + type + ' endpoints') url = self.implode_hostname(self.urls['api'][type]) path = self.implode_params(path, params) versionIsTransfer = (version == 'transfer') versionIsAsset = (version == 'asset') if versionIsTransfer or versionIsAsset: if versionIsTransfer: type = 'account/transfer' else: type = 'api/asset' version = section[2] access = section[3] if path != 'account/apiPermissions': if type == 'spot' and version == 'v3': url += '/api' else: url += '/' + type url += '/' + version + '/' + path params = self.omit(params, self.extract_params(path)) params['timestamp'] = self.nonce() params = self.keysort(params) if access == 'public': if params: url += '?' + self.urlencode(params) elif access == 'private': self.check_required_credentials() isJsonContentType = (((type == 'subAccount') or (type == 'account/transfer')) and (method == 'POST')) parsedParams = None encodeRequest = None if isJsonContentType: encodeRequest = self.custom_encode(params) else: parsedParams = self.parse_params(params) encodeRequest = self.rawencode(parsedParams, True) signature = self.hmac(self.encode(encodeRequest), self.encode(self.secret), hashlib.sha256) headers = { 'X-BX-APIKEY': self.apiKey, 'X-SOURCE-KEY': self.safe_string(self.options, 'broker', 'CCXT'), } if isJsonContentType: headers['Content-Type'] = 'application/json' params['signature'] = signature body = self.json(params) else: query = self.urlencode(parsedParams, True) url += '?' + query + '&' + 'signature=' + signature return {'url': url, 'method': method, 'body': body, 'headers': headers} def nonce(self): return self.milliseconds() def set_sandbox_mode(self, enable: bool): super(bingx, self).set_sandbox_mode(enable) self.options['sandboxMode'] = enable def handle_errors(self, httpCode: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody): if response is None: return None # fallback to default error handler # # { # "code": 80014, # "msg": "Invalid parameters, err:Key: 'GetTickerRequest.Symbol' Error:Field validation for "Symbol" failed on the "len=0|endswith=-USDT" tag", # "data": { # } # } # code = self.safe_string(response, 'code') message = self.safe_string(response, 'msg') transferErrorMsg = self.safe_string(response, 'transferErrorMsg') # handling with errors from transfer endpoint if (transferErrorMsg is not None) or (code is not None and code != '0'): if transferErrorMsg is not None: message = transferErrorMsg feedback = self.id + ' ' + body self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback) self.throw_exactly_matched_exception(self.exceptions['exact'], code, feedback) self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback) raise ExchangeError(feedback) # unknown message return None