Files
lz_db 0fab423a18 add
2025-11-16 12:31:03 +08:00

6419 lines
275 KiB
Python

# -*- 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.async_support.base.exchange import Exchange
from ccxt.abstract.bingx import ImplicitAPI
import asyncio
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',
},
},
},
})
async 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 = await self.swapV2PublicGetServerTime(params)
#
# {
# "code": 0,
# "msg": "",
# "data": {
# "serverTime": 1675319535362
# }
# }
#
data = self.safe_dict(response, 'data')
return self.safe_integer(data, 'serverTime')
async 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 = await 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
async def fetch_spot_markets(self, params) -> List[Market]:
response = await 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)
async def fetch_swap_markets(self, params):
response = await 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)
async def fetch_inverse_swap_markets(self, params):
response = await 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,
})
async 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 = await asyncio.gather(*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)
async 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
"""
await self.load_markets()
paginate = False
paginate, params = self.handle_option_and_params(params, 'fetchOHLCV', 'paginate', False)
if paginate:
return await 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 = await self.spotV1PublicGetMarketKline(self.extend(request, params))
else:
if market['inverse']:
response = await self.cswapV1PublicGetMarketKlines(self.extend(request, params))
else:
price = self.safe_string(params, 'price')
params = self.omit(params, 'price')
if price == 'mark':
response = await self.swapV1PublicGetMarketMarkPriceKlines(self.extend(request, params))
else:
response = await 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'),
]
async 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 <https://docs.ccxt.com/#/?id=public-trades>`
"""
await 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 = await self.spotV1PublicGetMarketTrades(self.extend(request, params))
else:
response = await 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)
async 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 <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
"""
await 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 = await self.spotV1PublicGetMarketDepth(self.extend(request, params))
else:
if market['inverse']:
response = await self.cswapV1PublicGetMarketDepth(self.extend(request, params))
else:
response = await 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
async 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 <https://docs.ccxt.com/#/?id=funding-rate-structure>`
"""
await self.load_markets()
market = self.market(symbol)
request: dict = {
'symbol': market['id'],
}
response = None
if market['inverse']:
response = await self.cswapV1PublicGetMarketPremiumIndex(self.extend(request, params))
else:
response = await 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)
async 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 <https://docs.ccxt.com/#/?id=funding-rate-structure>`
"""
await self.load_markets()
symbols = self.market_symbols(symbols, 'swap', True)
response = await 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,
}
async 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 <https://docs.ccxt.com/#/?id=funding-rate-history-structure>` 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 <https://docs.ccxt.com/#/?id=funding-rate-history-structure>`
"""
if symbol is None:
raise ArgumentsRequired(self.id + ' fetchFundingRateHistory() requires a symbol argument')
await self.load_markets()
paginate = False
paginate, params = self.handle_option_and_params(params, 'fetchFundingRateHistory', 'paginate')
if paginate:
return await 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 = await 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),
}
async 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:
"""
await self.load_markets()
market = self.market(symbol)
request: dict = {
'symbol': market['id'],
}
response = None
if market['inverse']:
response = await self.cswapV1PublicGetMarketOpenInterest(self.extend(request, params))
else:
response = await 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)
async 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 <https://docs.ccxt.com/#/?id=ticker-structure>`
"""
await self.load_markets()
market = self.market(symbol)
request: dict = {
'symbol': market['id'],
}
response = None
if market['spot']:
response = await self.spotV1PublicGetTicker24hr(self.extend(request, params))
else:
if market['inverse']:
response = await self.cswapV1PublicGetMarketTicker(self.extend(request, params))
else:
response = await 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)
async 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 <https://docs.ccxt.com/#/?id=ticker-structure>`
"""
await 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 = await self.spotV1PublicGetTicker24hr(params)
else:
if subType == 'inverse':
response = await self.cswapV1PublicGetMarketTicker(params)
else:
response = await 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)
async 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 <https://docs.ccxt.com/#/?id=ticker-structure>`
"""
await 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 = await 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 = await 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)
async 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 <https://docs.ccxt.com/#/?id=ticker-structure>`
"""
await 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 = await self.cswapV1PublicGetMarketPremiumIndex(params)
else:
response = await 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)
async 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 <https://docs.ccxt.com/#/?id=balance-structure>`
"""
await 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 = await 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 = await self.fundV1PrivateGetAccountBalance(marketTypeQuery)
# {
# code: '0',
# timestamp: '1754906016631',
# data: {
# assets: [
# {
# asset: 'USDT',
# free: '44.37692200000000237300',
# locked: '0.00000000000000000000'
# }
# ]
# }
# }
elif marketType == 'spot':
response = await self.spotV1PrivateGetAccountBalance(marketTypeQuery)
#
# {
# "code": 0,
# "msg": "",
# "debugMsg": "",
# "data": {
# "balances": [
# {
# "asset": "USDT",
# "free": "45.733046995800514",
# "locked": "0"
# },
# ]
# }
# }
#
else:
if subType == 'inverse':
response = await 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 = await 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)
async 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 <https://docs.ccxt.com/#/?id=position-structure>`
"""
await 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 = await 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)
async 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 <https://docs.ccxt.com/#/?id=position-structure>`
"""
await 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 = await 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 = await 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 = await 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)
async 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 <https://docs.ccxt.com/#/?id=position-structure>`
"""
await 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 = await 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 = await 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,
})
async 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 <https://docs.ccxt.com/#/?id=order-structure>`
"""
params['quoteOrderQty'] = cost
return await self.create_order(symbol, 'market', side, cost, None, params)
async 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 <https://docs.ccxt.com/#/?id=order-structure>`
"""
params['quoteOrderQty'] = cost
return await self.create_order(symbol, 'market', 'buy', cost, None, params)
async 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 <https://docs.ccxt.com/#/?id=order-structure>`
"""
params['quoteOrderQty'] = cost
return await 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)
async 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 <https://docs.ccxt.com/#/?id=order-structure>`
"""
await 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 = await self.swapV2PrivatePostTradeOrderTest(request)
elif market['inverse']:
response = await self.cswapV1PrivatePostTradeOrder(request)
elif type == 'twap':
response = await self.swapV1PrivatePostTwapOrder(request)
else:
response = await self.swapV2PrivatePostTradeOrder(request)
else:
response = await 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)
async 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 <https://docs.ccxt.com/#/?id=order-structure>`
"""
await 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 = await self.swapV2PrivatePostTradeBatchOrders(request)
else:
sync = self.safe_bool(params, 'sync', False)
if sync:
request['sync'] = True
request['data'] = self.json(ordersRequests)
response = await 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)
async 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 <https://docs.ccxt.com/#/?id=order-structure>`
"""
await 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 = await 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 = await self.spotV1PrivatePostTradeCancel(self.extend(request, params))
else:
if subType == 'inverse':
response = await self.cswapV1PrivateDeleteTradeCancelOrder(self.extend(request, params))
else:
response = await 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)
async 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 <https://docs.ccxt.com/#/?id=order-structure>`
"""
if symbol is None:
raise ArgumentsRequired(self.id + ' cancelAllOrders() requires a symbol argument')
await self.load_markets()
market = self.market(symbol)
request: dict = {
'symbol': market['id'],
}
response = None
if market['spot']:
response = await 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 = await 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 = await 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)
async 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 <https://docs.ccxt.com/#/?id=order-structure>`
"""
if symbol is None:
raise ArgumentsRequired(self.id + ' cancelOrders() requires a symbol argument')
await 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 = await 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 = await 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)
async 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
"""
await 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 = await self.spotV1PrivatePostTradeCancelAllAfter(self.extend(request, params))
elif type == 'swap':
response = await 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
async 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 <https://docs.ccxt.com/#/?id=order-structure>`
"""
await 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 = await 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 = await 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 = await 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 = await 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)
async 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 <https://docs.ccxt.com/#/?id=order-structure>`
"""
await 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 = await 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)
async 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 <https://docs.ccxt.com/#/?id=order-structure>`
"""
await 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 = await self.spotV1PrivateGetTradeOpenOrders(self.extend(request, params))
else:
isTwapOrder = self.safe_bool(params, 'twap', False)
params = self.omit(params, 'twap')
if isTwapOrder:
response = await self.swapV1PrivateGetTwapOpenOrders(self.extend(request, params))
elif subType == 'inverse':
response = await self.cswapV1PrivateGetTradeOpenOrders(self.extend(request, params))
else:
response = await 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)
async 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 <https://docs.ccxt.com/#/?id=order-structure>`
"""
await self.load_markets()
orders = await self.fetch_canceled_and_closed_orders(symbol, since, limit, params)
return self.filter_by(orders, 'status', 'closed')
async 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 <https://docs.ccxt.com/#/?id=order-structure>`
"""
await self.load_markets()
orders = await self.fetch_canceled_and_closed_orders(symbol, since, limit, params)
return self.filter_by(orders, 'status', 'canceled')
async 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 <https://docs.ccxt.com/#/?id=order-structure>`
"""
await 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 = await self.contractV1PrivateGetAllOrders(self.extend(request, params))
elif type == 'spot':
if limit is not None:
request['pageSize'] = limit
response = await 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 = await 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 = await 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 = await 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)
async 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 <https://docs.ccxt.com/#/?id=transfer-structure>`
"""
await 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 = await 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,
}
async 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 <https://docs.ccxt.com/#/?id=transfer-structure>`
"""
await 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 await 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 = await 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)
async 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 <https://docs.ccxt.com/#/?id=address-structure>`, indexed by the network
"""
await 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 = await 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')
async 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 <https://docs.ccxt.com/#/?id=address-structure>`
"""
network = self.safe_string(params, 'network')
params = self.omit(params, ['network'])
addressStructures = await 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,
}
async 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 <https://docs.ccxt.com/#/?id=transaction-structure>`
"""
await 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 = await 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)
async 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 <https://docs.ccxt.com/#/?id=transaction-structure>`
"""
await 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 = await 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)
async 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')
await 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 await self.cswapV1PrivatePostTradeMarginType(self.extend(request, params))
else:
return await self.swapV2PrivatePostTradeMarginType(self.extend(request, params))
async def add_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
request: dict = {
'type': 1,
}
return await self.set_margin(symbol, amount, self.extend(request, params))
async def reduce_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
request: dict = {
'type': 2,
}
return await self.set_margin(symbol, amount, self.extend(request, params))
async 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 <https://docs.ccxt.com/#/?id=add-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)')
await self.load_markets()
market = self.market(symbol)
request: dict = {
'symbol': market['id'],
'amount': self.amount_to_precision(market['symbol'], amount),
'type': type,
}
response = await 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,
}
async 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 <https://docs.ccxt.com/#/?id=leverage-structure>`
"""
await self.load_markets()
market = self.market(symbol)
request: dict = {
'symbol': market['id'],
}
response = None
if market['inverse']:
response = await 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 = await 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'),
}
async 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'])
await self.load_markets()
market = self.market(symbol)
request: dict = {
'symbol': market['id'],
'side': side,
'leverage': leverage,
}
if market['inverse']:
return await 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 await 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"
# }
# }
#
async 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 <https://docs.ccxt.com/#/?id=trade-structure>`
"""
if symbol is None:
raise ArgumentsRequired(self.id + ' fetchMyTrades() requires a symbol argument')
await 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 = await 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 = await 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 = await 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
async 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 <https://docs.ccxt.com/#/?id=fee-structure>`
"""
await self.load_markets()
response = await 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
async 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 <https://docs.ccxt.com/#/?id=transaction-structure>`
"""
tag, params = self.handle_withdraw_tag_and_params(tag, params)
self.check_address(address)
await 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 = await 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
async 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 <https://docs.ccxt.com/#/?id=liquidation-structure>`
"""
await 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 = await 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 = await 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),
})
async 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 <https://docs.ccxt.com/#/?id=order-structure>`
"""
await self.load_markets()
market = self.market(symbol)
positionId = self.safe_string(params, 'positionId')
request: dict = {}
response = None
if positionId is not None:
response = await 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 = await self.cswapV1PrivatePostTradeCloseAllPositions(self.extend(request, params))
#
# {
# "code": 0,
# "msg": "",
# "timestamp": 1720771601428,
# "data": {
# "success": ["1811673520637231104"],
# "failed": null
# }
# }
#
else:
response = await 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)
async 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 <https://docs.ccxt.com/#/?id=position-structure>`
"""
await 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 = await self.cswapV1PrivatePostTradeCloseAllPositions(self.extend(request, params))
#
# {
# "code": 0,
# "msg": "",
# "timestamp": 1720771601428,
# "data": {
# "success": ["1811673520637231104"],
# "failed": null
# }
# }
#
else:
response = await 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
async 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 = await 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'),
}
async 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 await self.swapV1PrivatePostPositionSideDual(self.extend(request, params))
async 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 <https://docs.ccxt.com/#/?id=order-structure>`
"""
await 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 = await 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 = await 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)
async 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 <https://docs.ccxt.com/#/?id=margin-mode-structure>`
"""
await 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 = await self.cswapV1PrivateGetTradeMarginType(self.extend(request, params))
#
# {
# "code": 0,
# "msg": "",
# "timestamp": 1721966069132,
# "data": {
# "symbol": "SOL-USD",
# "marginType": "CROSSED"
# }
# }
#
else:
response = await 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,
}
async 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 <https://docs.ccxt.com/#/?id=fee-structure>`
"""
await 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 = await self.spotV1PrivateGetUserCommissionRate(self.extend(request, params))
#
# {
# "code": 0,
# "msg": "",
# "debugMsg": "",
# "data": {
# "takerCommissionRate": 0.001,
# "makerCommissionRate": 0.001
# }
# }
#
commission = data
else:
if market['inverse']:
response = await self.cswapV1PrivateGetUserCommissionRate(params)
#
# {
# "code": 0,
# "msg": "",
# "timestamp": 1721365261438,
# "data": {
# "takerCommissionRate": "0.0005",
# "makerCommissionRate": "0.0002"
# }
# }
#
commission = data
else:
response = await 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