# -*- coding: utf-8 -*-

# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code

from ccxt.base.exchange import Exchange
import json
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 MarginModeAlreadySet
from ccxt.base.errors import BadResponse
from ccxt.base.errors import InsufficientFunds
from ccxt.base.errors import InvalidOrder
from ccxt.base.errors import OrderNotFound
from ccxt.base.errors import OrderImmediatelyFillable
from ccxt.base.errors import OrderNotFillable
from ccxt.base.errors import NotSupported
from ccxt.base.errors import DDoSProtection
from ccxt.base.errors import RateLimitExceeded
from ccxt.base.errors import ExchangeNotAvailable
from ccxt.base.errors import OnMaintenance
from ccxt.base.errors import InvalidNonce
from ccxt.base.errors import RequestTimeout
from ccxt.base.decimal_to_precision import TRUNCATE
from ccxt.base.decimal_to_precision import DECIMAL_PLACES
from ccxt.base.precise import Precise


class binance(Exchange):

    def describe(self):
        return self.deep_extend(super(binance, self).describe(), {
            'id': 'binance',
            'name': 'Binance',
            'countries': ['JP', 'MT'],  # Japan, Malta
            'rateLimit': 50,
            'certified': True,
            'pro': True,
            # new metainfo interface
            'has': {
                'CORS': None,
                'spot': True,
                'margin': True,
                'swap': True,
                'future': True,
                'option': None,
                'addMargin': True,
                'borrowMargin': True,
                'cancelAllOrders': True,
                'cancelOrder': True,
                'cancelOrders': None,
                'createDepositAddress': False,
                'createOrder': True,
                'createReduceOnlyOrder': True,
                'createStopLimitOrder': True,
                'createStopMarketOrder': False,
                'createStopOrder': True,
                'fetchAccounts': None,
                'fetchBalance': True,
                'fetchBidsAsks': True,
                'fetchBorrowInterest': True,
                'fetchBorrowRate': True,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': True,
                'fetchBorrowRates': False,
                'fetchBorrowRatesPerSymbol': False,
                'fetchCanceledOrders': False,
                'fetchClosedOrder': False,
                'fetchClosedOrders': 'emulated',
                'fetchCurrencies': True,
                'fetchDeposit': False,
                'fetchDepositAddress': True,
                'fetchDepositAddresses': False,
                'fetchDepositAddressesByNetwork': False,
                'fetchDeposits': True,
                'fetchFundingHistory': True,
                'fetchFundingRate': True,
                'fetchFundingRateHistory': True,
                'fetchFundingRates': True,
                'fetchIndexOHLCV': True,
                'fetchL3OrderBook': False,
                'fetchLedger': None,
                'fetchLeverage': False,
                'fetchLeverageTiers': True,
                'fetchMarketLeverageTiers': 'emulated',
                'fetchMarkets': True,
                'fetchMarkOHLCV': True,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenInterestHistory': True,
                'fetchOpenOrder': False,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrderBooks': False,
                'fetchOrders': True,
                'fetchOrderTrades': True,
                'fetchPosition': None,
                'fetchPositions': True,
                'fetchPositionsRisk': True,
                'fetchPremiumIndexOHLCV': False,
                'fetchStatus': True,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': True,
                'fetchTrades': True,
                'fetchTradingFee': True,
                'fetchTradingFees': True,
                'fetchTradingLimits': None,
                'fetchTransactionFee': None,
                'fetchTransactionFees': True,
                'fetchTransactions': False,
                'fetchTransfers': True,
                'fetchWithdrawal': False,
                'fetchWithdrawals': True,
                'fetchWithdrawalWhitelist': False,
                'reduceMargin': True,
                'repayMargin': True,
                'setLeverage': True,
                'setMargin': False,
                'setMarginMode': True,
                'setPositionMode': True,
                'signIn': False,
                'transfer': True,
                'withdraw': True,
            },
            'timeframes': {
                '1s': '1s',  # spot only for now
                '1m': '1m',
                '3m': '3m',
                '5m': '5m',
                '15m': '15m',
                '30m': '30m',
                '1h': '1h',
                '2h': '2h',
                '4h': '4h',
                '6h': '6h',
                '8h': '8h',
                '12h': '12h',
                '1d': '1d',
                '3d': '3d',
                '1w': '1w',
                '1M': '1M',
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/29604020-d5483cdc-87ee-11e7-94c7-d1a8d9169293.jpg',
                'test': {
                    'dapiPublic': 'https://testnet.binancefuture.com/dapi/v1',
                    'dapiPrivate': 'https://testnet.binancefuture.com/dapi/v1',
                    'eapiPublic': 'https://testnet.binanceops.com/eapi/v1',
                    'eapiPrivate': 'https://testnet.binanceops.com/eapi/v1',
                    'fapiPublic': 'https://testnet.binancefuture.com/fapi/v1',
                    'fapiPrivate': 'https://testnet.binancefuture.com/fapi/v1',
                    'fapiPrivateV2': 'https://testnet.binancefuture.com/fapi/v2',
                    'public': 'https://testnet.binance.vision/api/v3',
                    'private': 'https://testnet.binance.vision/api/v3',
                    'v1': 'https://testnet.binance.vision/api/v1',
                },
                'api': {
                    'wapi': 'https://api.binance.com/wapi/v3',
                    'sapi': 'https://api.binance.com/sapi/v1',
                    'sapiV3': 'https://api.binance.com/sapi/v3',
                    'dapiPublic': 'https://dapi.binance.com/dapi/v1',
                    'dapiPrivate': 'https://dapi.binance.com/dapi/v1',
                    'eapiPublic': 'https://eapi.binance.com/eapi/v1',
                    'eapiPrivate': 'https://eapi.binance.com/eapi/v1',
                    'dapiPrivateV2': 'https://dapi.binance.com/dapi/v2',
                    'dapiData': 'https://dapi.binance.com/futures/data',
                    'fapiPublic': 'https://fapi.binance.com/fapi/v1',
                    'fapiPrivate': 'https://fapi.binance.com/fapi/v1',
                    'fapiData': 'https://fapi.binance.com/futures/data',
                    'fapiPrivateV2': 'https://fapi.binance.com/fapi/v2',
                    'public': 'https://api.binance.com/api/v3',
                    'private': 'https://api.binance.com/api/v3',
                    'v1': 'https://api.binance.com/api/v1',
                },
                'www': 'https://www.binance.com',
                'referral': {
                    'url': 'https://accounts.binance.com/en/register?ref=D7YA7CLY',
                    'discount': 0.1,
                },
                'doc': [
                    'https://binance-docs.github.io/apidocs/spot/en',
                ],
                'api_management': 'https://www.binance.com/en/usercenter/settings/api-management',
                'fees': 'https://www.binance.com/en/fee/schedule',
            },
            'depth': 1,
            'api': {
                # the API structure below will need 3-layer apidefs
                'sapi': {
                    # IP(api) = 1200 per minute =>(rateLimit = 50)
                    # IP(sapi) request rate limit of 12 000 per minute
                    # 1 IP(sapi) => cost = 0.1
                    # 10 IP(sapi) => cost = 1
                    # UID(sapi) request rate limit of 180 000 per minute
                    # 1 UID(sapi) => cost = 1200 / 180 000 = 0.006667
                    'get': {
                        'system/status': 0.1,
                        # these endpoints require self.apiKey
                        'accountSnapshot': 240,  # Weight(IP): 2400 => cost = 0.1 * 2400 = 240
                        'margin/asset': 1,  # Weight(IP): 10 => cost = 0.1 * 10 = 1
                        'margin/pair': 1,
                        'margin/allAssets': 0.1,
                        'margin/allPairs': 0.1,
                        'margin/priceIndex': 1,
                        # these endpoints require self.apiKey + self.secret
                        'asset/assetDividend': 1,
                        'asset/dribblet': 0.1,
                        'asset/transfer': 0.1,
                        'asset/assetDetail': 0.1,
                        'asset/tradeFee': 0.1,
                        'margin/loan': 1,
                        'margin/repay': 1,
                        'margin/account': 1,
                        'margin/transfer': 0.1,
                        'margin/interestHistory': 0.1,
                        'margin/forceLiquidationRec': 0.1,
                        'margin/order': 1,
                        'margin/openOrders': 1,
                        'margin/allOrders': 20,  # Weight(IP): 200 => cost = 0.1 * 200 = 20
                        'margin/myTrades': 1,
                        'margin/maxBorrowable': 5,  # Weight(IP): 50 => cost = 0.1 * 50 = 5
                        'margin/maxTransferable': 5,
                        'margin/tradeCoeff': 1,
                        'margin/isolated/transfer': 0.1,
                        'margin/isolated/account': 1,
                        'margin/isolated/pair': 1,
                        'margin/isolated/allPairs': 1,
                        'margin/isolated/accountLimit': 0.1,
                        'margin/interestRateHistory': 0.1,
                        'margin/orderList': 1,
                        'margin/allOrderList': 20,  # Weight(IP): 200 => cost = 0.1 * 200 = 20
                        'margin/openOrderList': 1,
                        'margin/crossMarginData': {'cost': 0.1, 'noCoin': 0.5},
                        'margin/isolatedMarginData': {'cost': 0.1, 'noCoin': 1},
                        'margin/isolatedMarginTier': 0.1,
                        'margin/rateLimit/order': 2,
                        'margin/dribblet': 0.1,
                        'loan/income': 40,  # Weight(UID): 6000 => cost = 0.006667 * 6000 = 40
                        'loan/ongoing/orders': 40,  # Weight(IP): 400 => cost = 0.1 * 400 = 40
                        'loan/ltv/adjustment/history': 40,  # Weight(IP): 400 => cost = 0.1 * 400 = 40
                        'loan/borrow/history': 2.6667,  # Weight(UID): 400 => cost = 0.006667 * 400 ~= 2.6667
                        'loan/repay/history': 40,  # Weight(IP): 400 => cost = 0.1 * 400 = 40
                        'fiat/orders': 600.03,  # Weight(UID): 90000 => cost = 0.006667 * 90000 = 600.03
                        'fiat/payments': 0.1,
                        'futures/transfer': 1,
                        'futures/loan/borrow/history': 1,
                        'futures/loan/repay/history': 1,
                        'futures/loan/wallet': 1,
                        'futures/loan/adjustCollateral/history': 1,
                        'futures/loan/liquidationHistory': 1,
                        'rebate/taxQuery': 20.001,  # Weight(UID): 3000 => cost = 0.006667 * 3000 = 20.001
                        # https://binance-docs.github.io/apidocs/spot/en/#withdraw-sapi
                        'capital/config/getall': 1,  # get networks for withdrawing USDT ERC20 vs USDT Omni
                        'capital/deposit/address': 1,
                        'capital/deposit/hisrec': 0.1,
                        'capital/deposit/subAddress': 0.1,
                        'capital/deposit/subHisrec': 0.1,
                        'capital/withdraw/history': 0.1,
                        'convert/tradeFlow': 0.6667,  # Weight(UID): 100 => cost = 0.006667 * 100 = 0.6667
                        'account/status': 0.1,
                        'account/apiTradingStatus': 0.1,
                        'account/apiRestrictions/ipRestriction': 0.1,
                        'bnbBurn': 0.1,
                        # 'sub-account/assets': 1,(v3 endpoint)
                        'sub-account/futures/account': 1,
                        'sub-account/futures/accountSummary': 0.1,
                        'sub-account/futures/positionRisk': 1,
                        'sub-account/futures/internalTransfer': 0.1,
                        'sub-account/list': 0.1,
                        'sub-account/margin/account': 1,
                        'sub-account/margin/accountSummary': 1,
                        'sub-account/spotSummary': 0.1,
                        'sub-account/status': 1,
                        'sub-account/sub/transfer/history': 0.1,
                        'sub-account/transfer/subUserHistory': 0.1,
                        'sub-account/universalTransfer': 0.1,
                        'managed-subaccount/asset': 0.1,
                        'managed-subaccount/accountSnapshot': 240,
                        # lending endpoints
                        'lending/daily/product/list': 0.1,
                        'lending/daily/userLeftQuota': 0.1,
                        'lending/daily/userRedemptionQuota': 0.1,
                        'lending/daily/token/position': 0.1,
                        'lending/union/account': 0.1,
                        'lending/union/purchaseRecord': 0.1,
                        'lending/union/redemptionRecord': 0.1,
                        'lending/union/interestHistory': 0.1,
                        'lending/project/list': 0.1,
                        'lending/project/position/list': 0.1,
                        # mining endpoints
                        'mining/pub/algoList': 0.1,
                        'mining/pub/coinList': 0.1,
                        'mining/worker/detail': 0.5,  # Weight(IP): 5 => cost = 0.1 * 5 = 0.5
                        'mining/worker/list': 0.5,
                        'mining/payment/list': 0.5,
                        'mining/statistics/user/status': 0.5,
                        'mining/statistics/user/list': 0.5,
                        'mining/payment/uid': 0.5,
                        # liquid swap endpoints
                        'bswap/pools': 0.1,
                        'bswap/liquidity': {'cost': 0.1, 'noPoolId': 1},
                        'bswap/liquidityOps': 20.001,  # Weight(UID): 3000 => cost = 0.006667 * 3000 = 20.001
                        'bswap/quote': 1.00005,  # Weight(UID): 150 => cost = 0.006667 * 150 = 1.00005
                        'bswap/swap': 20.001,  # Weight(UID): 3000 => cost = 0.006667 * 3000 = 20.001
                        'bswap/poolConfigure': 1.00005,  # Weight(UID): 150 => cost = 0.006667 * 150 = 1.00005
                        'bswap/addLiquidityPreview': 1.00005,  # Weight(UID): 150 => cost = 0.006667 * 150 = 1.00005
                        'bswap/removeLiquidityPreview': 1.00005,  # Weight(UID): 150 => cost = 0.006667 * 150 = 1.00005
                        'bswap/unclaimedRewards': 6.667,  # Weight(UID): 1000 => cost = 0.006667 * 1000 = 6.667
                        'bswap/claimedHistory': 6.667,  # Weight(UID): 1000 => cost = 0.006667 * 1000 = 6.667
                        # leveraged token endpoints
                        'blvt/tokenInfo': 0.1,
                        'blvt/subscribe/record': 0.1,
                        'blvt/redeem/record': 0.1,
                        'blvt/userLimit': 0.1,
                        # broker api TODO(NOT IN DOCS)
                        'apiReferral/ifNewUser': 1,
                        'apiReferral/customization': 1,
                        'apiReferral/userCustomization': 1,
                        'apiReferral/rebate/recentRecord': 1,
                        'apiReferral/rebate/historicalRecord': 1,
                        'apiReferral/kickback/recentRecord': 1,
                        'apiReferral/kickback/historicalRecord': 1,
                        # brokerage API TODO https://binance-docs.github.io/Brokerage-API/General/ does not state ratelimits
                        'broker/subAccountApi': 1,
                        'broker/subAccount': 1,
                        'broker/subAccountApi/commission/futures': 1,
                        'broker/subAccountApi/commission/coinFutures': 1,
                        'broker/info': 1,
                        'broker/transfer': 1,
                        'broker/transfer/futures': 1,
                        'broker/rebate/recentRecord': 1,
                        'broker/rebate/historicalRecord': 1,
                        'broker/subAccount/bnbBurn/status': 1,
                        'broker/subAccount/depositHist': 1,
                        'broker/subAccount/spotSummary': 1,
                        'broker/subAccount/marginSummary': 1,
                        'broker/subAccount/futuresSummary': 1,
                        'broker/rebate/futures/recentRecord': 1,
                        'broker/subAccountApi/ipRestriction': 1,
                        'broker/universalTransfer': 1,
                        # v2 not supported yet
                        # GET /sapi/v2/broker/subAccount/futuresSummary
                        'account/apiRestrictions': 0.1,
                        # c2c / p2p
                        'c2c/orderMatch/listUserOrderHistory': 0.1,
                        # nft endpoints
                        'nft/history/transactions': 20.001,  # Weight(UID): 3000 => cost = 0.006667 * 3000 = 20.001
                        'nft/history/deposit': 20.001,
                        'nft/history/withdraw': 20.001,
                        'nft/user/getAsset': 20.001,
                        'pay/transactions': 20.001,  # Weight(UID): 3000 => cost = 0.006667 * 3000 = 20.001
                        'giftcard/verify': 0.1,
                        'giftcard/cryptography/rsa-public-key': 0.1,
                        'giftcard/buyCode/token-limit': 0.1,
                        'algo/futures/openOrders': 0.1,
                        'algo/futures/historicalOrders': 0.1,
                        'algo/futures/subOrders': 0.1,
                        'portfolio/account': 0.1,
                        'portfolio/collateralRate': 5,
                        'portfolio/pmLoan': 3.3335,
                        # staking
                        'staking/productList': 0.1,
                        'staking/position': 0.1,
                        'staking/stakingRecord': 0.1,
                        'staking/personalLeftQuota': 0.1,
                    },
                    'post': {
                        'asset/dust': 1,
                        'asset/dust-btc': 0.1,
                        'asset/transfer': 0.1,
                        'asset/get-funding-asset': 0.1,
                        'asset/convert-transfer': 0.033335,
                        'asset/convert-transfer/queryByPage': 0.033335,
                        'account/disableFastWithdrawSwitch': 0.1,
                        'account/enableFastWithdrawSwitch': 0.1,
                        # 'account/apiRestrictions/ipRestriction': 1, discontinued
                        # 'account/apiRestrictions/ipRestriction/ipList': 1, discontinued
                        'capital/withdraw/apply': 0.1,
                        'margin/transfer': 1,  # Weight(IP): 600 => cost = 0.1 * 600 = 60
                        'margin/loan': 20.001,  # Weight(UID): 3000 => cost = 0.006667 * 3000 = 20.001
                        'margin/repay': 20.001,
                        'margin/order': 0.040002,  # Weight(UID): 6 => cost = 0.006667 * 6 = 0.040002
                        'margin/order/oco': 0.040002,
                        # 'margin/isolated/create': 1, discontinued
                        'margin/isolated/transfer': 4.0002,  # Weight(UID): 600 => cost = 0.006667 * 600 = 4.0002
                        'margin/isolated/account': 2.0001,  # Weight(UID): 300 => cost = 0.006667 * 300 = 2.0001
                        'bnbBurn': 0.1,
                        'sub-account/virtualSubAccount': 0.1,
                        'sub-account/margin/transfer': 4.0002,  # Weight(UID): 600 => cost =  0.006667 * 600 = 4.0002
                        'sub-account/margin/enable': 0.1,
                        'sub-account/futures/enable': 0.1,
                        'sub-account/futures/transfer': 0.1,
                        'sub-account/futures/internalTransfer': 0.1,
                        'sub-account/transfer/subToSub': 0.1,
                        'sub-account/transfer/subToMaster': 0.1,
                        'sub-account/universalTransfer': 0.1,
                        'managed-subaccount/deposit': 0.1,
                        'managed-subaccount/withdraw': 0.1,
                        'userDataStream': 0.1,
                        'userDataStream/isolated': 0.1,
                        'futures/transfer': 0.1,
                        # lending
                        'lending/customizedFixed/purchase': 0.1,
                        'lending/daily/purchase': 0.1,
                        'lending/daily/redeem': 0.1,
                        # liquid swap endpoints
                        'bswap/liquidityAdd': 60,  # Weight(UID): 1000 + (Additional: 1 request every 3 seconds =  0.333 requests per second) => cost = ( 1000 / rateLimit ) / 0.333 = 60.0000006
                        'bswap/liquidityRemove': 60,  # Weight(UID): 1000 + (Additional: 1 request every three seconds)
                        'bswap/swap': 60,  # Weight(UID): 1000 + (Additional: 1 request every three seconds)
                        'bswap/claimRewards': 6.667,  # Weight(UID): 1000 => cost = 0.006667 * 1000 = 6.667
                        # leveraged token endpoints
                        'blvt/subscribe': 0.1,
                        'blvt/redeem': 0.1,
                        # brokerage API TODO: NO MENTION OF RATELIMITS IN BROKERAGE DOCS
                        'apiReferral/customization': 1,
                        'apiReferral/userCustomization': 1,
                        'apiReferral/rebate/historicalRecord': 1,
                        'apiReferral/kickback/historicalRecord': 1,
                        'broker/subAccount': 1,
                        'broker/subAccount/margin': 1,
                        'broker/subAccount/futures': 1,
                        'broker/subAccountApi': 1,
                        'broker/subAccountApi/permission': 1,
                        'broker/subAccountApi/commission': 1,
                        'broker/subAccountApi/commission/futures': 1,
                        'broker/subAccountApi/commission/coinFutures': 1,
                        'broker/transfer': 1,
                        'broker/transfer/futures': 1,
                        'broker/rebate/historicalRecord': 1,
                        'broker/subAccount/bnbBurn/spot': 1,
                        'broker/subAccount/bnbBurn/marginInterest': 1,
                        'broker/subAccount/blvt': 1,
                        'broker/subAccountApi/ipRestriction': 1,
                        'broker/subAccountApi/ipRestriction/ipList': 1,
                        'broker/universalTransfer': 1,
                        'broker/subAccountApi/permission/universalTransfer': 1,
                        'broker/subAccountApi/permission/vanillaOptions': 1,
                        #
                        'giftcard/createCode': 0.1,
                        'giftcard/redeemCode': 0.1,
                        'giftcard/buyCode': 0.1,
                        'algo/futures/newOrderVp': 20.001,
                        'algo/futures/newOrderTwap': 20.001,
                        # staking
                        'staking/purchase': 0.1,
                        'staking/redeem': 0.1,
                        'staking/setAutoStaking': 0.1,
                        'portfolio/repay': 20.001,
                        'loan/borrow': 40,  # Weight(UID): 6000 => cost = 0.006667 * 6000 = 40
                        'loan/repay': 40,  # Weight(UID): 6000 => cost = 0.006667 * 6000 = 40
                        'loan/adjust/ltv': 40,  # Weight(UID): 6000 => cost = 0.006667 * 6000 = 40
                    },
                    'put': {
                        'userDataStream': 0.1,
                        'userDataStream/isolated': 0.1,
                    },
                    'delete': {
                        # 'account/apiRestrictions/ipRestriction/ipList': 1, discontinued
                        'margin/openOrders': 0.1,
                        'margin/order': 0.0066667,  # Weight(UID): 1 => cost = 0.006667
                        'margin/orderList': 0.0066667,
                        'margin/isolated/account': 2.0001,  # Weight(UID): 300 => cost =  0.006667 * 300 = 2.0001
                        'userDataStream': 0.1,
                        'userDataStream/isolated': 0.1,
                        # brokerage API TODO NO MENTION OF RATELIMIT IN BROKERAGE DOCS
                        'broker/subAccountApi': 1,
                        'broker/subAccountApi/ipRestriction/ipList': 1,
                        'algo/futures/order': 0.1,
                    },
                },
                'sapiV3': {
                    'get': {
                        'sub-account/assets': 1,
                    },
                    'post': {
                        'asset/getUserAsset': 0.5,
                    },
                },
                # deprecated
                'wapi': {
                    'post': {
                        'withdraw': 1,
                        'sub-account/transfer': 1,
                    },
                    'get': {
                        'depositHistory': 1,
                        'withdrawHistory': 1,
                        'depositAddress': 1,
                        'accountStatus': 1,
                        'systemStatus': 1,
                        'apiTradingStatus': 1,
                        'userAssetDribbletLog': 1,
                        'tradeFee': 1,
                        'assetDetail': 1,
                        'sub-account/list': 1,
                        'sub-account/transfer/history': 1,
                        'sub-account/assets': 1,
                    },
                },
                'dapiPublic': {
                    'get': {
                        'ping': 1,
                        'time': 1,
                        'exchangeInfo': 1,
                        'depth': {'cost': 2, 'byLimit': [[50, 2], [100, 5], [500, 10], [1000, 20]]},
                        'trades': 5,
                        'historicalTrades': 20,
                        'aggTrades': 20,
                        'premiumIndex': 10,
                        'fundingRate': 1,
                        'klines': {'cost': 1, 'byLimit': [[99, 1], [499, 2], [1000, 5], [10000, 10]]},
                        'continuousKlines': {'cost': 1, 'byLimit': [[99, 1], [499, 2], [1000, 5], [10000, 10]]},
                        'indexPriceKlines': {'cost': 1, 'byLimit': [[99, 1], [499, 2], [1000, 5], [10000, 10]]},
                        'markPriceKlines': {'cost': 1, 'byLimit': [[99, 1], [499, 2], [1000, 5], [10000, 10]]},
                        'ticker/24hr': {'cost': 1, 'noSymbol': 40},
                        'ticker/price': {'cost': 1, 'noSymbol': 2},
                        'ticker/bookTicker': {'cost': 1, 'noSymbol': 2},
                        'openInterest': 1,
                        'pmExchangeInfo': 1,
                    },
                },
                'dapiData': {
                    'get': {
                        'openInterestHist': 1,
                        'topLongShortAccountRatio': 1,
                        'topLongShortPositionRatio': 1,
                        'globalLongShortAccountRatio': 1,
                        'takerBuySellVol': 1,
                        'basis': 1,
                    },
                },
                'dapiPrivate': {
                    'get': {
                        'positionSide/dual': 30,
                        'order': 1,
                        'openOrder': 1,
                        'openOrders': {'cost': 1, 'noSymbol': 5},
                        'allOrders': {'cost': 20, 'noSymbol': 40},
                        'balance': 1,
                        'account': 5,
                        'positionMargin/history': 1,
                        'positionRisk': 1,
                        'userTrades': {'cost': 20, 'noSymbol': 40},
                        'income': 20,
                        'leverageBracket': 1,
                        'forceOrders': {'cost': 20, 'noSymbol': 50},
                        'adlQuantile': 5,
                        'orderAmendment': 1,
                        'pmAccountInfo': 5,
                    },
                    'post': {
                        'positionSide/dual': 1,
                        'order': 4,
                        'batchOrders': 5,
                        'countdownCancelAll': 10,
                        'leverage': 1,
                        'marginType': 1,
                        'positionMargin': 1,
                        'listenKey': 1,
                    },
                    'put': {
                        'listenKey': 1,
                        'order': 1,
                        'batchOrders': 5,
                    },
                    'delete': {
                        'order': 1,
                        'allOpenOrders': 1,
                        'batchOrders': 5,
                        'listenKey': 1,
                    },
                },
                'dapiPrivateV2': {
                    'get': {
                        'leverageBracket': 1,
                    },
                },
                'fapiPublic': {
                    'get': {
                        'ping': 1,
                        'time': 1,
                        'exchangeInfo': 1,
                        'depth': {'cost': 2, 'byLimit': [[50, 2], [100, 5], [500, 10], [1000, 20]]},
                        'trades': 5,
                        'historicalTrades': 20,
                        'aggTrades': 20,
                        'klines': {'cost': 1, 'byLimit': [[99, 1], [499, 2], [1000, 5], [10000, 10]]},
                        'continuousKlines': {'cost': 1, 'byLimit': [[99, 1], [499, 2], [1000, 5], [10000, 10]]},
                        'markPriceKlines': {'cost': 1, 'byLimit': [[99, 1], [499, 2], [1000, 5], [10000, 10]]},
                        'indexPriceKlines': {'cost': 1, 'byLimit': [[99, 1], [499, 2], [1000, 5], [10000, 10]]},
                        'fundingRate': 1,
                        'premiumIndex': 1,
                        'ticker/24hr': {'cost': 1, 'noSymbol': 40},
                        'ticker/price': {'cost': 1, 'noSymbol': 2},
                        'ticker/bookTicker': {'cost': 1, 'noSymbol': 2},
                        'openInterest': 1,
                        'indexInfo': 1,
                        'apiTradingStatus': {'cost': 1, 'noSymbol': 10},
                        'lvtKlines': 1,
                        'pmExchangeInfo': 1,
                    },
                },
                'fapiData': {
                    'get': {
                        'openInterestHist': 1,
                        'topLongShortAccountRatio': 1,
                        'topLongShortPositionRatio': 1,
                        'globalLongShortAccountRatio': 1,
                        'takerlongshortRatio': 1,
                    },
                },
                'fapiPrivate': {
                    'get': {
                        'forceOrders': {'cost': 20, 'noSymbol': 50},
                        'allOrders': 5,
                        'openOrder': 1,
                        'openOrders': 1,
                        'order': 1,
                        'account': 5,
                        'balance': 5,
                        'leverageBracket': 1,
                        'positionMargin/history': 1,
                        'positionRisk': 5,
                        'positionSide/dual': 30,
                        'userTrades': 5,
                        'income': 30,
                        'commissionRate': 20,
                        'apiTradingStatus': 1,
                        'multiAssetsMargin': 30,
                        # broker endpoints
                        'apiReferral/ifNewUser': 1,
                        'apiReferral/customization': 1,
                        'apiReferral/userCustomization': 1,
                        'apiReferral/traderNum': 1,
                        'apiReferral/overview': 1,
                        'apiReferral/tradeVol': 1,
                        'apiReferral/rebateVol': 1,
                        'apiReferral/traderSummary': 1,
                        'adlQuantile': 5,
                        'pmAccountInfo': 5,
                    },
                    'post': {
                        'batchOrders': 5,
                        'positionSide/dual': 1,
                        'positionMargin': 1,
                        'marginType': 1,
                        'order': 4,
                        'leverage': 1,
                        'listenKey': 1,
                        'countdownCancelAll': 10,
                        'multiAssetsMargin': 1,
                        # broker endpoints
                        'apiReferral/customization': 1,
                        'apiReferral/userCustomization': 1,
                    },
                    'put': {
                        'listenKey': 1,
                    },
                    'delete': {
                        'batchOrders': 1,
                        'order': 1,
                        'allOpenOrders': 1,
                        'listenKey': 1,
                    },
                },
                'fapiPrivateV2': {
                    'get': {
                        'account': 1,
                        'balance': 1,
                        'positionRisk': 1,
                    },
                },
                'eapiPublic': {
                    'get': {
                        'ping': 1,
                        'time': 1,
                        'exchangeInfo': 1,
                        'index': 1,
                        'ticker': 5,
                        'mark': 5,
                        'depth': 1,
                        'klines': 1,
                        'trades': 5,
                        'historicalTrades': 20,
                        'exerciseHistory': 3,
                    },
                },
                'eapiPrivate': {
                    'get': {
                        'account': 3,
                        'position': 5,
                        'openOrders': {'cost': 1, 'noSymbol': 40},
                        'historyOrders': 3,
                        'userTrades': 5,
                        'exerciseRecord': 5,
                        'bill': 1,
                        'marginAccount': 3,
                        'mmp': 1,
                    },
                    'post': {
                        'transfer': 1,
                        'order': 1,
                        'batchOrders': 5,
                        'listenKey': 1,
                        'mmpSet': 1,
                        'mmpReset': 1,
                    },
                    'put': {
                        'listenKey': 1,
                    },
                    'delete': {
                        'order': 1,
                        'batchOrders': 1,
                        'allOpenOrders': 1,
                        'allOpenOrdersByUnderlying': 1,
                        'listenKey': 1,
                    },
                },
                'public': {
                    'get': {
                        'ping': 1,
                        'time': 1,
                        'depth': {'cost': 1, 'byLimit': [[100, 1], [500, 5], [1000, 10], [5000, 50]]},
                        'trades': 1,
                        'aggTrades': 1,
                        'historicalTrades': 5,
                        'klines': 1,
                        'ticker/24hr': {'cost': 1, 'noSymbol': 40},
                        'ticker/price': {'cost': 1, 'noSymbol': 2},
                        'ticker/bookTicker': {'cost': 1, 'noSymbol': 2},
                        'exchangeInfo': 10,
                    },
                    'put': {
                        'userDataStream': 1,
                    },
                    'post': {
                        'userDataStream': 1,
                    },
                    'delete': {
                        'userDataStream': 1,
                    },
                },
                'private': {
                    'get': {
                        'allOrderList': 10,  # oco
                        'openOrderList': 3,  # oco
                        'orderList': 2,  # oco
                        'order': 2,
                        'openOrders': {'cost': 3, 'noSymbol': 40},
                        'allOrders': 10,
                        'account': 10,
                        'myTrades': 10,
                        'rateLimit/order': 20,
                    },
                    'post': {
                        'order/oco': 1,
                        'order': 1,
                        'order/test': 1,
                    },
                    'delete': {
                        'openOrders': 1,  # added on 2020-04-25 for canceling all open orders per symbol
                        'orderList': 1,  # oco
                        'order': 1,
                    },
                },
            },
            'fees': {
                'trading': {
                    'feeSide': 'get',
                    'tierBased': False,
                    'percentage': True,
                    'taker': self.parse_number('0.001'),
                    'maker': self.parse_number('0.001'),
                },
                'future': {
                    'trading': {
                        'feeSide': 'quote',
                        'tierBased': True,
                        'percentage': True,
                        'taker': self.parse_number('0.000400'),
                        'maker': self.parse_number('0.000200'),
                        'tiers': {
                            'taker': [
                                [self.parse_number('0'), self.parse_number('0.000400')],
                                [self.parse_number('250'), self.parse_number('0.000400')],
                                [self.parse_number('2500'), self.parse_number('0.000350')],
                                [self.parse_number('7500'), self.parse_number('0.000320')],
                                [self.parse_number('22500'), self.parse_number('0.000300')],
                                [self.parse_number('50000'), self.parse_number('0.000270')],
                                [self.parse_number('100000'), self.parse_number('0.000250')],
                                [self.parse_number('200000'), self.parse_number('0.000220')],
                                [self.parse_number('400000'), self.parse_number('0.000200')],
                                [self.parse_number('750000'), self.parse_number('0.000170')],
                            ],
                            'maker': [
                                [self.parse_number('0'), self.parse_number('0.000200')],
                                [self.parse_number('250'), self.parse_number('0.000160')],
                                [self.parse_number('2500'), self.parse_number('0.000140')],
                                [self.parse_number('7500'), self.parse_number('0.000120')],
                                [self.parse_number('22500'), self.parse_number('0.000100')],
                                [self.parse_number('50000'), self.parse_number('0.000080')],
                                [self.parse_number('100000'), self.parse_number('0.000060')],
                                [self.parse_number('200000'), self.parse_number('0.000040')],
                                [self.parse_number('400000'), self.parse_number('0.000020')],
                                [self.parse_number('750000'), self.parse_number('0')],
                            ],
                        },
                    },
                },
                'delivery': {
                    'trading': {
                        'feeSide': 'base',
                        'tierBased': True,
                        'percentage': True,
                        'taker': self.parse_number('0.000500'),
                        'maker': self.parse_number('0.000100'),
                        'tiers': {
                            'taker': [
                                [self.parse_number('0'), self.parse_number('0.000500')],
                                [self.parse_number('250'), self.parse_number('0.000450')],
                                [self.parse_number('2500'), self.parse_number('0.000400')],
                                [self.parse_number('7500'), self.parse_number('0.000300')],
                                [self.parse_number('22500'), self.parse_number('0.000250')],
                                [self.parse_number('50000'), self.parse_number('0.000240')],
                                [self.parse_number('100000'), self.parse_number('0.000240')],
                                [self.parse_number('200000'), self.parse_number('0.000240')],
                                [self.parse_number('400000'), self.parse_number('0.000240')],
                                [self.parse_number('750000'), self.parse_number('0.000240')],
                            ],
                            'maker': [
                                [self.parse_number('0'), self.parse_number('0.000100')],
                                [self.parse_number('250'), self.parse_number('0.000080')],
                                [self.parse_number('2500'), self.parse_number('0.000050')],
                                [self.parse_number('7500'), self.parse_number('0.0000030')],
                                [self.parse_number('22500'), self.parse_number('0')],
                                [self.parse_number('50000'), self.parse_number('-0.000050')],
                                [self.parse_number('100000'), self.parse_number('-0.000060')],
                                [self.parse_number('200000'), self.parse_number('-0.000070')],
                                [self.parse_number('400000'), self.parse_number('-0.000080')],
                                [self.parse_number('750000'), self.parse_number('-0.000090')],
                            ],
                        },
                    },
                },
                'option': {},
            },
            'commonCurrencies': {
                'BCC': 'BCC',  # kept for backward-compatibility https://github.com/ccxt/ccxt/issues/4848
                'YOYO': 'YOYOW',
            },
            'precisionMode': DECIMAL_PLACES,
            # exchange-specific options
            'options': {
                'fetchCurrencies': True,  # self is a private call and it requires API keys
                # 'fetchTradesMethod': 'publicGetAggTrades',  # publicGetTrades, publicGetHistoricalTrades
                'defaultTimeInForce': 'GTC',  # 'GTC' = Good To Cancel(default), 'IOC' = Immediate Or Cancel
                'defaultType': 'spot',  # 'spot', 'future', 'margin', 'delivery'
                'hasAlreadyAuthenticatedSuccessfully': False,
                'warnOnFetchOpenOrdersWithoutSymbol': True,
                # not an error
                # https://github.com/ccxt/ccxt/issues/11268
                # https://github.com/ccxt/ccxt/pull/11624
                # POST https://fapi.binance.com/fapi/v1/marginType 400 Bad Request
                # binanceusdm
                'throwMarginModeAlreadySet': False,
                'fetchPositions': 'positionRisk',  # or 'account'
                'recvWindow': 10 * 1000,  # 10 sec
                'timeDifference': 0,  # the difference between system clock and Binance clock
                'adjustForTimeDifference': False,  # controls the adjustment logic upon instantiation
                'newOrderRespType': {
                    'market': 'FULL',  # 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills
                    'limit': 'FULL',  # we change it from 'ACK' by default to 'FULL'(returns immediately if limit is not hit)
                },
                'quoteOrderQty': True,  # whether market orders support amounts in quote currency
                'broker': {
                    'spot': 'x-R4BD3S82',
                    'margin': 'x-R4BD3S82',
                    'future': 'x-xcKtGhcu',
                    'delivery': 'x-xcKtGhcu',
                },
                'accountsByType': {
                    'main': 'MAIN',
                    'spot': 'MAIN',
                    'funding': 'FUNDING',
                    'margin': 'MARGIN',
                    'cross': 'MARGIN',
                    'future': 'UMFUTURE',
                    'delivery': 'CMFUTURE',
                },
                'accountsById': {
                    'MAIN': 'spot',
                    'FUNDING': 'funding',
                    'MARGIN': 'margin',
                    'UMFUTURE': 'future',
                    'CMFUTURE': 'delivery',
                },
                'networks': {
                    'ERC20': 'ETH',
                    'TRC20': 'TRX',
                    'BEP2': 'BNB',
                    'BEP20': 'BSC',
                    'OMNI': 'OMNI',
                    'EOS': 'EOS',
                    'SPL': 'SOL',
                },
                'reverseNetworks': {
                    'tronscan.org': 'TRC20',
                    'etherscan.io': 'ERC20',
                    'bscscan.com': 'BSC',
                    'explorer.binance.org': 'BEP2',
                    'bithomp.com': 'XRP',
                    'bloks.io': 'EOS',
                    'stellar.expert': 'XLM',
                    'blockchair.com/bitcoin': 'BTC',
                    'blockchair.com/bitcoin-cash': 'BCH',
                    'blockchair.com/ecash': 'XEC',
                    'explorer.litecoin.net': 'LTC',
                    'explorer.avax.network': 'AVAX',
                    'solscan.io': 'SOL',
                    'polkadot.subscan.io': 'DOT',
                    'dashboard.internetcomputer.org': 'ICP',
                    'explorer.chiliz.com': 'CHZ',
                    'cardanoscan.io': 'ADA',
                    'mainnet.theoan.com': 'AION',
                    'algoexplorer.io': 'ALGO',
                    'explorer.ambrosus.com': 'AMB',
                    'viewblock.io/zilliqa': 'ZIL',
                    'viewblock.io/arweave': 'AR',
                    'explorer.ark.io': 'ARK',
                    'atomscan.com': 'ATOM',
                    'www.mintscan.io': 'CTK',
                    'explorer.bitcoindiamond.org': 'BCD',
                    'btgexplorer.com': 'BTG',
                    'bts.ai': 'BTS',
                    'explorer.celo.org': 'CELO',
                    'explorer.nervos.org': 'CKB',
                    'cerebro.cortexlabs.ai': 'CTXC',
                    'chainz.cryptoid.info': 'VIA',
                    'explorer.dcrdata.org': 'DCR',
                    'digiexplorer.info': 'DGB',
                    'dock.subscan.io': 'DOCK',
                    'dogechain.info': 'DOGE',
                    'explorer.elrond.com': 'EGLD',
                    'blockscout.com': 'ETC',
                    'explore-fetchhub.fetch.ai': 'FET',
                    'filfox.info': 'FIL',
                    'fio.bloks.io': 'FIO',
                    'explorer.firo.org': 'FIRO',
                    'neoscan.io': 'NEO',
                    'ftmscan.com': 'FTM',
                    'explorer.gochain.io': 'GO',
                    'block.gxb.io': 'GXS',
                    'hash-hash.info': 'HBAR',
                    'www.hiveblockexplorer.com': 'HIVE',
                    'explorer.helium.com': 'HNT',
                    'tracker.icon.foundation': 'ICX',
                    'www.iostabc.com': 'IOST',
                    'explorer.iota.org': 'IOTA',
                    'iotexscan.io': 'IOTX',
                    'irishub.iobscan.io': 'IRIS',
                    'kava.mintscan.io': 'KAVA',
                    'scope.klaytn.com': 'KLAY',
                    'kmdexplorer.io': 'KMD',
                    'kusama.subscan.io': 'KSM',
                    'explorer.lto.network': 'LTO',
                    'polygonscan.com': 'POLYGON',
                    'explorer.ont.io': 'ONT',
                    'minaexplorer.com': 'MINA',
                    'nanolooker.com': 'NANO',
                    'explorer.nebulas.io': 'NAS',
                    'explorer.nbs.plus': 'NBS',
                    'explorer.nebl.io': 'NEBL',
                    'nulscan.io': 'NULS',
                    'nxscan.com': 'NXS',
                    'explorer.harmony.one': 'ONE',
                    'explorer.poa.network': 'POA',
                    'qtum.info': 'QTUM',
                    'explorer.rsk.co': 'RSK',
                    'www.oasisscan.com': 'ROSE',
                    'ravencoin.network': 'RVN',
                    'sc.tokenview.com': 'SC',
                    'secretnodes.com': 'SCRT',
                    'explorer.skycoin.com': 'SKY',
                    'steemscan.com': 'STEEM',
                    'explorer.stacks.co': 'STX',
                    'www.thetascan.io': 'THETA',
                    'scan.tomochain.com': 'TOMO',
                    'explore.vechain.org': 'VET',
                    'explorer.vite.net': 'VITE',
                    'www.wanscan.org': 'WAN',
                    'wavesexplorer.com': 'WAVES',
                    'wax.eosx.io': 'WAXP',
                    'waltonchain.pro': 'WTC',
                    'chain.nem.ninja': 'XEM',
                    'verge-blockchain.info': 'XVG',
                    'explorer.yoyow.org': 'YOYOW',
                    'explorer.zcha.in': 'ZEC',
                    'explorer.zensystem.io': 'ZEN',
                },
                'impliedNetworks': {
                    'ETH': {'ERC20': 'ETH'},
                    'TRX': {'TRC20': 'TRX'},
                },
                'legalMoney': {
                    'MXN': True,
                    'UGX': True,
                    'SEK': True,
                    'CHF': True,
                    'VND': True,
                    'AED': True,
                    'DKK': True,
                    'KZT': True,
                    'HUF': True,
                    'PEN': True,
                    'PHP': True,
                    'USD': True,
                    'TRY': True,
                    'EUR': True,
                    'NGN': True,
                    'PLN': True,
                    'BRL': True,
                    'ZAR': True,
                    'KES': True,
                    'ARS': True,
                    'RUB': True,
                    'AUD': True,
                    'NOK': True,
                    'CZK': True,
                    'GBP': True,
                    'UAH': True,
                    'GHS': True,
                    'HKD': True,
                    'CAD': True,
                    'INR': True,
                    'JPY': True,
                    'NZD': True,
                },
            },
            # https://binance-docs.github.io/apidocs/spot/en/#error-codes-2
            'exceptions': {
                'exact': {
                    'System is under maintenance.': OnMaintenance,  # {"code":1,"msg":"System is under maintenance."}
                    'System abnormality': ExchangeError,  # {"code":-1000,"msg":"System abnormality"}
                    'You are not authorized to execute self request.': PermissionDenied,  # {"msg":"You are not authorized to execute self request."}
                    'API key does not exist': AuthenticationError,
                    'Order would trigger immediately.': OrderImmediatelyFillable,
                    'Stop price would trigger immediately.': OrderImmediatelyFillable,  # {"code":-2010,"msg":"Stop price would trigger immediately."}
                    'Order would immediately match and take.': OrderImmediatelyFillable,  # {"code":-2010,"msg":"Order would immediately match and take."}
                    'Account has insufficient balance for requested action.': InsufficientFunds,
                    'Rest API trading is not enabled.': ExchangeNotAvailable,
                    "You don't have permission.": PermissionDenied,  # {"msg":"You don't have permission.","success":false}
                    'Market is closed.': ExchangeNotAvailable,  # {"code":-1013,"msg":"Market is closed."}
                    'Too many requests. Please try again later.': DDoSProtection,  # {"msg":"Too many requests. Please try again later.","success":false}
                    'This action disabled is on self account.': AccountSuspended,  # {"code":-2010,"msg":"This action disabled is on self account."}
                    'This type of sub-account exceeds the maximum number limit': BadRequest,  # {"code":-9000,"msg":"This type of sub-account exceeds the maximum number limit"}
                    'This symbol is not permitted for self account.': PermissionDenied,  # {"code":-2010,"msg":"This symbol is not permitted for self account."}
                    '-1000': ExchangeNotAvailable,  # {"code":-1000,"msg":"An unknown error occured while processing the request."}
                    '-1001': ExchangeNotAvailable,  # {"code":-1001,"msg":"'Internal error; unable to process your request. Please try again.'"}
                    '-1002': AuthenticationError,  # {"code":-1002,"msg":"'You are not authorized to execute self request.'"}
                    '-1003': RateLimitExceeded,  # {"code":-1003,"msg":"Too much request weight used, current limit is 1200 request weight per 1 MINUTE. Please use the websocket for live updates to avoid polling the API."}
                    '-1004': DDoSProtection,  # {"code":-1004,"msg":"Server is busy, please wait and try again"}
                    '-1005': PermissionDenied,  # {"code":-1005,"msg":"No such IP has been white listed"}
                    '-1006': BadResponse,  # {"code":-1006,"msg":"An unexpected response was received from the message bus. Execution status unknown."}
                    '-1007': RequestTimeout,  # {"code":-1007,"msg":"Timeout waiting for response from backend server. Send status unknown; execution status unknown."}
                    '-1010': BadResponse,  # {"code":-1010,"msg":"ERROR_MSG_RECEIVED."}
                    '-1011': PermissionDenied,  # {"code":-1011,"msg":"This IP cannot access self route."}
                    '-1013': InvalidOrder,  # {"code":-1013,"msg":"createOrder -> 'invalid quantity'/'invalid price'/MIN_NOTIONAL"}
                    '-1014': InvalidOrder,  # {"code":-1014,"msg":"Unsupported order combination."}
                    '-1015': RateLimitExceeded,  # {"code":-1015,"msg":"'Too many new orders; current limit is %s orders per %s.'"}
                    '-1016': ExchangeNotAvailable,  # {"code":-1016,"msg":"'This service is no longer available.',"}
                    '-1020': BadRequest,  # {"code":-1020,"msg":"'This operation is not supported.'"}
                    '-1021': InvalidNonce,  # {"code":-1021,"msg":"'your time is ahead of server'"}
                    '-1022': AuthenticationError,  # {"code":-1022,"msg":"Signature for self request is not valid."}
                    '-1023': BadRequest,  # {"code":-1023,"msg":"Start time is greater than end time."}
                    '-1099': AuthenticationError,  # {"code":-1099,"msg":"Not found, authenticated, or authorized"}
                    '-1100': BadRequest,  # {"code":-1100,"msg":"createOrder(symbol, 1, asdf) -> 'Illegal characters found in parameter 'price'"}
                    '-1101': BadRequest,  # {"code":-1101,"msg":"Too many parameters; expected %s and received %s."}
                    '-1102': BadRequest,  # {"code":-1102,"msg":"Param %s or %s must be sent, but both were empty"}
                    '-1103': BadRequest,  # {"code":-1103,"msg":"An unknown parameter was sent."}
                    '-1104': BadRequest,  # {"code":-1104,"msg":"Not all sent parameters were read, read 8 parameters but was sent 9"}
                    '-1105': BadRequest,  # {"code":-1105,"msg":"Parameter %s was empty."}
                    '-1106': BadRequest,  # {"code":-1106,"msg":"Parameter %s sent when not required."}
                    '-1108': BadRequest,  # {"code":-1108,"msg":"Invalid asset."}
                    '-1109': AuthenticationError,  # {"code":-1109,"msg":"Invalid account."}
                    '-1110': BadRequest,  # {"code":-1110,"msg":"Invalid symbolType."}
                    '-1111': BadRequest,  # {"code":-1111,"msg":"Precision is over the maximum defined for self asset."}
                    '-1112': InvalidOrder,  # {"code":-1112,"msg":"No orders on book for symbol."}
                    '-1113': BadRequest,  # {"code":-1113,"msg":"Withdrawal amount must be negative."}
                    '-1114': BadRequest,  # {"code":-1114,"msg":"TimeInForce parameter sent when not required."}
                    '-1115': BadRequest,  # {"code":-1115,"msg":"Invalid timeInForce."}
                    '-1116': BadRequest,  # {"code":-1116,"msg":"Invalid orderType."}
                    '-1117': BadRequest,  # {"code":-1117,"msg":"Invalid side."}
                    '-1118': BadRequest,  # {"code":-1118,"msg":"New client order ID was empty."}
                    '-1119': BadRequest,  # {"code":-1119,"msg":"Original client order ID was empty."}
                    '-1120': BadRequest,  # {"code":-1120,"msg":"Invalid interval."}
                    '-1121': BadSymbol,  # {"code":-1121,"msg":"Invalid symbol."}
                    '-1125': AuthenticationError,  # {"code":-1125,"msg":"This listenKey does not exist."}
                    '-1127': BadRequest,  # {"code":-1127,"msg":"More than %s hours between startTime and endTime."}
                    '-1128': BadRequest,  # {"code":-1128,"msg":"{"code":-1128,"msg":"Combination of optional parameters invalid."}"}
                    '-1130': BadRequest,  # {"code":-1130,"msg":"Data sent for paramter %s is not valid."}
                    '-1131': BadRequest,  # {"code":-1131,"msg":"recvWindow must be less than 60000"}
                    '-1136': BadRequest,  # {"code":-1136,"msg":"Invalid newOrderRespType"}
                    '-2008': AuthenticationError,  # {"code":-2008,"msg":"Invalid Api-Key ID."}
                    '-2010': ExchangeError,  # {"code":-2010,"msg":"generic error code for createOrder -> 'Account has insufficient balance for requested action.', {"code":-2010,"msg":"Rest API trading is not enabled."}, etc..."}
                    '-2011': OrderNotFound,  # {"code":-2011,"msg":"cancelOrder(1, 'BTC/USDT') -> 'UNKNOWN_ORDER'"}
                    '-2013': OrderNotFound,  # {"code":-2013,"msg":"fetchOrder(1, 'BTC/USDT') -> 'Order does not exist'"}
                    '-2014': AuthenticationError,  # {"code":-2014,"msg":"API-key format invalid."}
                    '-2015': AuthenticationError,  # {"code":-2015,"msg":"Invalid API-key, IP, or permissions for action."}
                    '-2016': BadRequest,  # {"code":-2016,"msg":"No trading window could be found for the symbol. Try ticker/24hrs instead."}
                    '-2018': InsufficientFunds,  # {"code":-2018,"msg":"Balance is insufficient"}
                    '-2019': InsufficientFunds,  # {"code":-2019,"msg":"Margin is insufficient."}
                    '-2020': OrderNotFillable,  # {"code":-2020,"msg":"Unable to fill."}
                    '-2021': OrderImmediatelyFillable,  # {"code":-2021,"msg":"Order would immediately trigger."}
                    '-2022': InvalidOrder,  # {"code":-2022,"msg":"ReduceOnly Order is rejected."}
                    '-2023': InsufficientFunds,  # {"code":-2023,"msg":"User in liquidation mode now."}
                    '-2024': InsufficientFunds,  # {"code":-2024,"msg":"Position is not sufficient."}
                    '-2025': InvalidOrder,  # {"code":-2025,"msg":"Reach max open order limit."}
                    '-2026': InvalidOrder,  # {"code":-2026,"msg":"This OrderType is not supported when reduceOnly."}
                    '-2027': InvalidOrder,  # {"code":-2027,"msg":"Exceeded the maximum allowable position at current leverage."}
                    '-2028': InsufficientFunds,  # {"code":-2028,"msg":"Leverage is smaller than permitted: insufficient margin balance"}
                    '-3000': ExchangeError,  # {"code":-3000,"msg":"Internal server error."}
                    '-3001': AuthenticationError,  # {"code":-3001,"msg":"Please enable 2FA first."}
                    '-3002': BadSymbol,  # {"code":-3002,"msg":"We don't have self asset."}
                    '-3003': BadRequest,  # {"code":-3003,"msg":"Margin account does not exist."}
                    '-3004': ExchangeError,  # {"code":-3004,"msg":"Trade not allowed."}
                    '-3005': InsufficientFunds,  # {"code":-3005,"msg":"Transferring out not allowed. Transfer out amount exceeds max amount."}
                    '-3006': InsufficientFunds,  # {"code":-3006,"msg":"Your borrow amount has exceed maximum borrow amount."}
                    '-3007': ExchangeError,  # {"code":-3007,"msg":"You have pending transaction, please try again later.."}
                    '-3008': InsufficientFunds,  # {"code":-3008,"msg":"Borrow not allowed. Your borrow amount has exceed maximum borrow amount."}
                    '-3009': BadRequest,  # {"code":-3009,"msg":"This asset are not allowed to transfer into margin account currently."}
                    '-3010': BadRequest,  # {"code":-3010,"msg":"Repay not allowed. Repay amount exceeds borrow amount."}
                    '-3011': BadRequest,  # {"code":-3011,"msg":"Your input date is invalid."}
                    '-3012': InsufficientFunds,  # {"code":-3012,"msg":"Borrow is banned for self asset."}
                    '-3013': BadRequest,  # {"code":-3013,"msg":"Borrow amount less than minimum borrow amount."}
                    '-3014': AccountSuspended,  # {"code":-3014,"msg":"Borrow is banned for self account."}
                    '-3015': BadRequest,  # {"code":-3015,"msg":"Repay amount exceeds borrow amount."}
                    '-3016': BadRequest,  # {"code":-3016,"msg":"Repay amount less than minimum repay amount."}
                    '-3017': ExchangeError,  # {"code":-3017,"msg":"This asset are not allowed to transfer into margin account currently."}
                    '-3018': AccountSuspended,  # {"code":-3018,"msg":"Transferring in has been banned for self account."}
                    '-3019': AccountSuspended,  # {"code":-3019,"msg":"Transferring out has been banned for self account."}
                    '-3020': InsufficientFunds,  # {"code":-3020,"msg":"Transfer out amount exceeds max amount."}
                    '-3021': BadRequest,  # {"code":-3021,"msg":"Margin account are not allowed to trade self trading pair."}
                    '-3022': AccountSuspended,  # {"code":-3022,"msg":"You account's trading is banned."}
                    '-3023': BadRequest,  # {"code":-3023,"msg":"You can't transfer out/place order under current margin level."}
                    '-3024': ExchangeError,  # {"code":-3024,"msg":"The unpaid debt is too small after self repayment."}
                    '-3025': BadRequest,  # {"code":-3025,"msg":"Your input date is invalid."}
                    '-3026': BadRequest,  # {"code":-3026,"msg":"Your input param is invalid."}
                    '-3027': BadSymbol,  # {"code":-3027,"msg":"Not a valid margin asset."}
                    '-3028': BadSymbol,  # {"code":-3028,"msg":"Not a valid margin pair."}
                    '-3029': ExchangeError,  # {"code":-3029,"msg":"Transfer failed."}
                    '-3036': AccountSuspended,  # {"code":-3036,"msg":"This account is not allowed to repay."}
                    '-3037': ExchangeError,  # {"code":-3037,"msg":"PNL is clearing. Wait a second."}
                    '-3038': BadRequest,  # {"code":-3038,"msg":"Listen key not found."}
                    '-3041': InsufficientFunds,  # {"code":-3041,"msg":"Balance is not enough"}
                    '-3042': BadRequest,  # {"code":-3042,"msg":"PriceIndex not available for self margin pair."}
                    '-3043': BadRequest,  # {"code":-3043,"msg":"Transferring in not allowed."}
                    '-3044': DDoSProtection,  # {"code":-3044,"msg":"System busy."}
                    '-3045': ExchangeError,  # {"code":-3045,"msg":"The system doesn't have enough asset now."}
                    '-3999': ExchangeError,  # {"code":-3999,"msg":"This function is only available for invited users."}
                    '-4001': BadRequest,  # {"code":-4001 ,"msg":"Invalid operation."}
                    '-4002': BadRequest,  # {"code":-4002 ,"msg":"Invalid get."}
                    '-4003': BadRequest,  # {"code":-4003 ,"msg":"Your input email is invalid."}
                    '-4004': AuthenticationError,  # {"code":-4004,"msg":"You don't login or auth."}
                    '-4005': RateLimitExceeded,  # {"code":-4005 ,"msg":"Too many new requests."}
                    '-4006': BadRequest,  # {"code":-4006 ,"msg":"Support main account only."}
                    '-4007': BadRequest,  # {"code":-4007 ,"msg":"Address validation is not passed."}
                    '-4008': BadRequest,  # {"code":-4008 ,"msg":"Address tag validation is not passed."}
                    '-4010': BadRequest,  # {"code":-4010 ,"msg":"White list mail has been confirmed."}  # [TODO] possible bug: it should probably be "has not been confirmed"
                    '-4011': BadRequest,  # {"code":-4011 ,"msg":"White list mail is invalid."}
                    '-4012': BadRequest,  # {"code":-4012 ,"msg":"White list is not opened."}
                    '-4013': AuthenticationError,  # {"code":-4013 ,"msg":"2FA is not opened."}
                    '-4014': PermissionDenied,  # {"code":-4014 ,"msg":"Withdraw is not allowed within 2 min login."}
                    '-4015': ExchangeError,  # {"code":-4015 ,"msg":"Withdraw is limited."}
                    '-4016': PermissionDenied,  # {"code":-4016 ,"msg":"Within 24 hours after password modification, withdrawal is prohibited."}
                    '-4017': PermissionDenied,  # {"code":-4017 ,"msg":"Within 24 hours after the release of 2FA, withdrawal is prohibited."}
                    '-4018': BadSymbol,  # {"code":-4018,"msg":"We don't have self asset."}
                    '-4019': BadSymbol,  # {"code":-4019,"msg":"Current asset is not open for withdrawal."}
                    '-4021': BadRequest,  # {"code":-4021,"msg":"Asset withdrawal must be an %s multiple of %s."}
                    '-4022': BadRequest,  # {"code":-4022,"msg":"Not less than the minimum pick-up quantity %s."}
                    '-4023': ExchangeError,  # {"code":-4023,"msg":"Within 24 hours, the withdrawal exceeds the maximum amount."}
                    '-4024': InsufficientFunds,  # {"code":-4024,"msg":"You don't have self asset."}
                    '-4025': InsufficientFunds,  # {"code":-4025,"msg":"The number of hold asset is less than zero."}
                    '-4026': InsufficientFunds,  # {"code":-4026,"msg":"You have insufficient balance."}
                    '-4027': ExchangeError,  # {"code":-4027,"msg":"Failed to obtain tranId."}
                    '-4028': BadRequest,  # {"code":-4028,"msg":"The amount of withdrawal must be greater than the Commission."}
                    '-4029': BadRequest,  # {"code":-4029,"msg":"The withdrawal record does not exist."}
                    '-4030': ExchangeError,  # {"code":-4030,"msg":"Confirmation of successful asset withdrawal. [TODO] possible bug in docs"}
                    '-4031': ExchangeError,  # {"code":-4031,"msg":"Cancellation failed."}
                    '-4032': ExchangeError,  # {"code":-4032,"msg":"Withdraw verification exception."}
                    '-4033': BadRequest,  # {"code":-4033,"msg":"Illegal address."}
                    '-4034': ExchangeError,  # {"code":-4034,"msg":"The address is suspected of fake."}
                    '-4035': PermissionDenied,  # {"code":-4035,"msg":"This address is not on the whitelist. Please join and try again."}
                    '-4036': BadRequest,  # {"code":-4036,"msg":"The new address needs to be withdrawn in {0} hours."}
                    '-4037': ExchangeError,  # {"code":-4037,"msg":"Re-sending Mail failed."}
                    '-4038': ExchangeError,  # {"code":-4038,"msg":"Please try again in 5 minutes."}
                    '-4039': BadRequest,  # {"code":-4039,"msg":"The user does not exist."}
                    '-4040': BadRequest,  # {"code":-4040,"msg":"This address not charged."}
                    '-4041': ExchangeError,  # {"code":-4041,"msg":"Please try again in one minute."}
                    '-4042': ExchangeError,  # {"code":-4042,"msg":"This asset cannot get deposit address again."}
                    '-4043': BadRequest,  # {"code":-4043,"msg":"More than 100 recharge addresses were used in 24 hours."}
                    '-4044': BadRequest,  # {"code":-4044,"msg":"This is a blacklist country."}
                    '-4045': ExchangeError,  # {"code":-4045,"msg":"Failure to acquire assets."}
                    '-4046': AuthenticationError,  # {"code":-4046,"msg":"Agreement not confirmed."}
                    '-4047': BadRequest,  # {"code":-4047,"msg":"Time interval must be within 0-90 days"}
                    '-5001': BadRequest,  # {"code":-5001,"msg":"Don't allow transfer to micro assets."}
                    '-5002': InsufficientFunds,  # {"code":-5002,"msg":"You have insufficient balance."}
                    '-5003': InsufficientFunds,  # {"code":-5003,"msg":"You don't have self asset."}
                    '-5004': BadRequest,  # {"code":-5004,"msg":"The residual balances of %s have exceeded 0.001BTC, Please re-choose."}
                    '-5005': InsufficientFunds,  # {"code":-5005,"msg":"The residual balances of %s is too low, Please re-choose."}
                    '-5006': BadRequest,  # {"code":-5006,"msg":"Only transfer once in 24 hours."}
                    '-5007': BadRequest,  # {"code":-5007,"msg":"Quantity must be greater than zero."}
                    '-5008': InsufficientFunds,  # {"code":-5008,"msg":"Insufficient amount of returnable assets."}
                    '-5009': BadRequest,  # {"code":-5009,"msg":"Product does not exist."}
                    '-5010': ExchangeError,  # {"code":-5010,"msg":"Asset transfer fail."}
                    '-5011': BadRequest,  # {"code":-5011,"msg":"future account not exists."}
                    '-5012': ExchangeError,  # {"code":-5012,"msg":"Asset transfer is in pending."}
                    '-5013': InsufficientFunds,  # {"code":-5013,"msg":"Asset transfer failed: insufficient balance""}  # undocumented
                    '-5021': BadRequest,  # {"code":-5021,"msg":"This parent sub have no relation"}
                    '-6001': BadRequest,  # {"code":-6001,"msg":"Daily product not exists."}
                    '-6003': BadRequest,  # {"code":-6003,"msg":"Product not exist or you don't have permission"}
                    '-6004': ExchangeError,  # {"code":-6004,"msg":"Product not in purchase status"}
                    '-6005': InvalidOrder,  # {"code":-6005,"msg":"Smaller than min purchase limit"}
                    '-6006': BadRequest,  # {"code":-6006,"msg":"Redeem amount error"}
                    '-6007': BadRequest,  # {"code":-6007,"msg":"Not in redeem time"}
                    '-6008': BadRequest,  # {"code":-6008,"msg":"Product not in redeem status"}
                    '-6009': RateLimitExceeded,  # {"code":-6009,"msg":"Request frequency too high"}
                    '-6011': BadRequest,  # {"code":-6011,"msg":"Exceeding the maximum num allowed to purchase per user"}
                    '-6012': InsufficientFunds,  # {"code":-6012,"msg":"Balance not enough"}
                    '-6013': ExchangeError,  # {"code":-6013,"msg":"Purchasing failed"}
                    '-6014': BadRequest,  # {"code":-6014,"msg":"Exceed up-limit allowed to purchased"}
                    '-6015': BadRequest,  # {"code":-6015,"msg":"Empty request body"}
                    '-6016': BadRequest,  # {"code":-6016,"msg":"Parameter err"}
                    '-6017': BadRequest,  # {"code":-6017,"msg":"Not in whitelist"}
                    '-6018': BadRequest,  # {"code":-6018,"msg":"Asset not enough"}
                    '-6019': AuthenticationError,  # {"code":-6019,"msg":"Need confirm"}
                    '-6020': BadRequest,  # {"code":-6020,"msg":"Project not exists"}
                    '-7001': BadRequest,  # {"code":-7001,"msg":"Date range is not supported."}
                    '-7002': BadRequest,  # {"code":-7002,"msg":"Data request type is not supported."}
                    '-9000': InsufficientFunds,  # {"code":-9000,"msg":"user have no avaliable amount"}"
                    '-10017': BadRequest,  # {"code":-10017,"msg":"Repay amount should not be larger than liability."}
                    '-11008': InsufficientFunds,  # {"code":-11008,"msg":"Exceeding the account's maximum borrowable limit."}  # undocumented
                    '-12014': RateLimitExceeded,  # {"code":-12014,"msg":"More than 1 request in 3 seconds"}
                    '-13000': BadRequest,  # {"code":-13000,"msg":"Redeption of the token is forbiden now"}
                    '-13001': BadRequest,  # {"code":-13001,"msg":"Exceeds individual 24h redemption limit of the token"}
                    '-13002': BadRequest,  # {"code":-13002,"msg":"Exceeds total 24h redemption limit of the token"}
                    '-13003': BadRequest,  # {"code":-13003,"msg":"Subscription of the token is forbiden now"}
                    '-13004': BadRequest,  # {"code":-13004,"msg":"Exceeds individual 24h subscription limit of the token"}
                    '-13005': BadRequest,  # {"code":-13005,"msg":"Exceeds total 24h subscription limit of the token"}
                    '-13006': InvalidOrder,  # {"code":-13006,"msg":"Subscription amount is too small"}
                    '-13007': AuthenticationError,  # {"code":-13007,"msg":"The Agreement is not signed"}
                    '-21001': BadRequest,  # {"code":-21001,"msg":"USER_IS_NOT_UNIACCOUNT"}
                    '-21002': BadRequest,  # {"code":-21002,"msg":"UNI_ACCOUNT_CANT_TRANSFER_FUTURE"}
                    '-21003': BadRequest,  # {"code":-21003,"msg":"NET_ASSET_MUST_LTE_RATIO"}
                    '100001003': BadRequest,  # {"code":100001003,"msg":"Verification failed"}  # undocumented
                },
                'broad': {
                    'has no operation privilege': PermissionDenied,
                    'MAX_POSITION': InvalidOrder,  # {"code":-2010,"msg":"Filter failure: MAX_POSITION"}
                },
            },
        })

    def cost_to_precision(self, symbol, cost):
        return self.decimal_to_precision(cost, TRUNCATE, self.markets[symbol]['precision']['quote'], self.precisionMode, self.paddingMode)

    def currency_to_precision(self, code, fee, networkCode=None):
        # info is available in currencies only if the user has configured his api keys
        if self.safe_value(self.currencies[code], 'precision') is not None:
            return self.decimal_to_precision(fee, TRUNCATE, self.currencies[code]['precision'], self.precisionMode, self.paddingMode)
        else:
            return self.number_to_string(fee)

    def nonce(self):
        return self.milliseconds() - self.options['timeDifference']

    def fetch_time(self, params={}):
        """
        fetches the current integer timestamp in milliseconds from the exchange server
        :param dict params: extra parameters specific to the binance api endpoint
        :returns int: the current integer timestamp in milliseconds from the exchange server
        """
        defaultType = self.safe_string_2(self.options, 'fetchTime', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        query = self.omit(params, 'type')
        method = 'publicGetTime'
        if type == 'future':
            method = 'fapiPublicGetTime'
        elif type == 'delivery':
            method = 'dapiPublicGetTime'
        response = getattr(self, method)(query)
        return self.safe_integer(response, 'serverTime')

    def fetch_currencies(self, params={}):
        """
        fetches all available currencies on an exchange
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: an associative dictionary of currencies
        """
        fetchCurrenciesEnabled = self.safe_value(self.options, 'fetchCurrencies')
        if not fetchCurrenciesEnabled:
            return None
        # self endpoint requires authentication
        # while fetchCurrencies is a public API method by design
        # therefore we check the keys here
        # and fallback to generating the currencies from the markets
        if not self.check_required_credentials(False):
            return None
        # sandbox/testnet does not support sapi endpoints
        apiBackup = self.safe_string(self.urls, 'apiBackup')
        if apiBackup is not None:
            return None
        response = self.sapiGetCapitalConfigGetall(params)
        result = {}
        for i in range(0, len(response)):
            #
            #     {
            #         coin: 'LINK',
            #         depositAllEnable: True,
            #         withdrawAllEnable: True,
            #         name: 'ChainLink',
            #         free: '0.06168',
            #         locked: '0',
            #         freeze: '0',
            #         withdrawing: '0',
            #         ipoing: '0',
            #         ipoable: '0',
            #         storage: '0',
            #         isLegalMoney: False,
            #         trading: True,
            #         networkList: [
            #             {
            #                 network: 'BNB',
            #                 coin: 'LINK',
            #                 withdrawIntegerMultiple: '0',
            #                 isDefault: False,
            #                 depositEnable: True,
            #                 withdrawEnable: True,
            #                 depositDesc: '',
            #                 withdrawDesc: '',
            #                 specialTips: 'Both a MEMO and an Address are required to successfully deposit your LINK BEP2 tokens to Binance.',
            #                 name: 'BEP2',
            #                 resetAddressStatus: False,
            #                 addressRegex: '^(bnb1)[0-9a-z]{38}$',
            #                 memoRegex: '^[0-9A-Za-z\\-_]{1,120}$',
            #                 withdrawFee: '0.002',
            #                 withdrawMin: '0.01',
            #                 withdrawMax: '9999999',
            #                 minConfirm: 1,
            #                 unLockConfirm: 0
            #             },
            #             {
            #                 network: 'BSC',
            #                 coin: 'LINK',
            #                 withdrawIntegerMultiple: '0.00000001',
            #                 isDefault: False,
            #                 depositEnable: True,
            #                 withdrawEnable: True,
            #                 depositDesc: '',
            #                 withdrawDesc: '',
            #                 specialTips: '',
            #                 name: 'BEP20(BSC)',
            #                 resetAddressStatus: False,
            #                 addressRegex: '^(0x)[0-9A-Fa-f]{40}$',
            #                 memoRegex: '',
            #                 withdrawFee: '0.005',
            #                 withdrawMin: '0.01',
            #                 withdrawMax: '9999999',
            #                 minConfirm: 15,
            #                 unLockConfirm: 0
            #             },
            #             {
            #                 network: 'ETH',
            #                 coin: 'LINK',
            #                 withdrawIntegerMultiple: '0.00000001',
            #                 isDefault: True,
            #                 depositEnable: True,
            #                 withdrawEnable: True,
            #                 depositDesc: '',
            #                 withdrawDesc: '',
            #                 name: 'ERC20',
            #                 resetAddressStatus: False,
            #                 addressRegex: '^(0x)[0-9A-Fa-f]{40}$',
            #                 memoRegex: '',
            #                 withdrawFee: '0.34',
            #                 withdrawMin: '0.68',
            #                 withdrawMax: '0',
            #                 minConfirm: 12,
            #                 unLockConfirm: 0
            #             }
            #         ]
            #     }
            #
            entry = response[i]
            id = self.safe_string(entry, 'coin')
            name = self.safe_string(entry, 'name')
            code = self.safe_currency_code(id)
            precision = None
            isWithdrawEnabled = True
            isDepositEnabled = True
            networkList = self.safe_value(entry, 'networkList', [])
            fees = {}
            fee = None
            for j in range(0, len(networkList)):
                networkItem = networkList[j]
                network = self.safe_string(networkItem, 'network')
                # name = self.safe_string(networkItem, 'name')
                withdrawFee = self.safe_number(networkItem, 'withdrawFee')
                depositEnable = self.safe_value(networkItem, 'depositEnable')
                withdrawEnable = self.safe_value(networkItem, 'withdrawEnable')
                isDepositEnabled = isDepositEnabled or depositEnable
                isWithdrawEnabled = isWithdrawEnabled or withdrawEnable
                fees[network] = withdrawFee
                isDefault = self.safe_value(networkItem, 'isDefault')
                if isDefault or (fee is None):
                    fee = withdrawFee
            trading = self.safe_value(entry, 'trading')
            active = (isWithdrawEnabled and isDepositEnabled and trading)
            result[code] = {
                'id': id,
                'name': name,
                'code': code,
                'precision': precision,
                'info': entry,
                'active': active,
                'deposit': isDepositEnabled,
                'withdraw': isWithdrawEnabled,
                'networks': networkList,
                'fee': fee,
                'fees': fees,
                'limits': self.limits,
            }
        return result

    def fetch_markets(self, params={}):
        """
        retrieves data on all markets for binance
        :param dict params: extra parameters specific to the exchange api endpoint
        :returns [dict]: an array of objects representing market data
        """
        defaultType = self.safe_string_2(self.options, 'fetchMarkets', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        query = self.omit(params, 'type')
        spot = (type == 'spot')
        margin = (type == 'margin')
        future = (type == 'future')
        delivery = (type == 'delivery')
        if (not spot) and (not margin) and (not future) and (not delivery):
            raise ExchangeError(self.id + " does not support '" + type + "' type, set exchange.options['defaultType'] to 'spot', 'margin', 'delivery' or 'future'")  # eslint-disable-line quotes
        method = 'publicGetExchangeInfo'
        if future:
            method = 'fapiPublicGetExchangeInfo'
        elif delivery:
            method = 'dapiPublicGetExchangeInfo'
        response = getattr(self, method)(query)
        #
        # spot / margin
        #
        #     {
        #         "timezone":"UTC",
        #         "serverTime":1575416692969,
        #         "rateLimits":[
        #             {"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200},
        #             {"rateLimitType":"ORDERS","interval":"SECOND","intervalNum":10,"limit":100},
        #             {"rateLimitType":"ORDERS","interval":"DAY","intervalNum":1,"limit":200000}
        #         ],
        #         "exchangeFilters":[],
        #         "symbols":[
        #             {
        #                 "symbol":"ETHBTC",
        #                 "status":"TRADING",
        #                 "baseAsset":"ETH",
        #                 "baseAssetPrecision":8,
        #                 "quoteAsset":"BTC",
        #                 "quotePrecision":8,
        #                 "baseCommissionPrecision":8,
        #                 "quoteCommissionPrecision":8,
        #                 "orderTypes":["LIMIT","LIMIT_MAKER","MARKET","STOP_LOSS_LIMIT","TAKE_PROFIT_LIMIT"],
        #                 "icebergAllowed":true,
        #                 "ocoAllowed":true,
        #                 "quoteOrderQtyMarketAllowed":true,
        #                 "allowTrailingStop":false,
        #                 "isSpotTradingAllowed":true,
        #                 "isMarginTradingAllowed":true,
        #                 "filters":[
        #                     {"filterType":"PRICE_FILTER","minPrice":"0.00000100","maxPrice":"100000.00000000","tickSize":"0.00000100"},
        #                     {"filterType":"PERCENT_PRICE","multiplierUp":"5","multiplierDown":"0.2","avgPriceMins":5},
        #                     {"filterType":"LOT_SIZE","minQty":"0.00100000","maxQty":"100000.00000000","stepSize":"0.00100000"},
        #                     {"filterType":"MIN_NOTIONAL","minNotional":"0.00010000","applyToMarket":true,"avgPriceMins":5},
        #                     {"filterType":"ICEBERG_PARTS","limit":10},
        #                     {"filterType":"MARKET_LOT_SIZE","minQty":"0.00000000","maxQty":"63100.00000000","stepSize":"0.00000000"},
        #                     {"filterType":"MAX_NUM_ORDERS","maxNumOrders":200},
        #                     {"filterType":"MAX_NUM_ALGO_ORDERS","maxNumAlgoOrders":5}
        #                 ],
        #                 "permissions":["SPOT","MARGIN"]}
        #             },
        #         ],
        #     }
        #
        # futures/usdt-margined(fapi)
        #
        #     {
        #         "timezone":"UTC",
        #         "serverTime":1575417244353,
        #         "rateLimits":[
        #             {"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":1200},
        #             {"rateLimitType":"ORDERS","interval":"MINUTE","intervalNum":1,"limit":1200}
        #         ],
        #         "exchangeFilters":[],
        #         "symbols":[
        #             {
        #                 "symbol":"BTCUSDT",
        #                 "status":"TRADING",
        #                 "maintMarginPercent":"2.5000",
        #                 "requiredMarginPercent":"5.0000",
        #                 "baseAsset":"BTC",
        #                 "quoteAsset":"USDT",
        #                 "pricePrecision":2,
        #                 "quantityPrecision":3,
        #                 "baseAssetPrecision":8,
        #                 "quotePrecision":8,
        #                 "filters":[
        #                     {"minPrice":"0.01","maxPrice":"100000","filterType":"PRICE_FILTER","tickSize":"0.01"},
        #                     {"stepSize":"0.001","filterType":"LOT_SIZE","maxQty":"1000","minQty":"0.001"},
        #                     {"stepSize":"0.001","filterType":"MARKET_LOT_SIZE","maxQty":"1000","minQty":"0.001"},
        #                     {"limit":200,"filterType":"MAX_NUM_ORDERS"},
        #                     {"multiplierDown":"0.8500","multiplierUp":"1.1500","multiplierDecimal":"4","filterType":"PERCENT_PRICE"}
        #                 ],
        #                 "orderTypes":["LIMIT","MARKET","STOP"],
        #                 "timeInForce":["GTC","IOC","FOK","GTX"]
        #             }
        #         ]
        #     }
        #
        # delivery/coin-margined(dapi)
        #
        #     {
        #         "timezone": "UTC",
        #         "serverTime": 1597667052958,
        #         "rateLimits": [
        #             {"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000},
        #             {"rateLimitType":"ORDERS","interval":"MINUTE","intervalNum":1,"limit":6000}
        #         ],
        #         "exchangeFilters": [],
        #         "symbols": [
        #             {
        #                 "symbol": "BTCUSD_200925",
        #                 "pair": "BTCUSD",
        #                 "contractType": "CURRENT_QUARTER",
        #                 "deliveryDate": 1601020800000,
        #                 "onboardDate": 1590739200000,
        #                 "contractStatus": "TRADING",
        #                 "contractSize": 100,
        #                 "marginAsset": "BTC",
        #                 "maintMarginPercent": "2.5000",
        #                 "requiredMarginPercent": "5.0000",
        #                 "baseAsset": "BTC",
        #                 "quoteAsset": "USD",
        #                 "pricePrecision": 1,
        #                 "quantityPrecision": 0,
        #                 "baseAssetPrecision": 8,
        #                 "quotePrecision": 8,
        #                 "equalQtyPrecision": 4,
        #                 "filters": [
        #                     {"minPrice":"0.1","maxPrice":"100000","filterType":"PRICE_FILTER","tickSize":"0.1"},
        #                     {"stepSize":"1","filterType":"LOT_SIZE","maxQty":"100000","minQty":"1"},
        #                     {"stepSize":"0","filterType":"MARKET_LOT_SIZE","maxQty":"100000","minQty":"1"},
        #                     {"limit":200,"filterType":"MAX_NUM_ORDERS"},
        #                     {"multiplierDown":"0.9500","multiplierUp":"1.0500","multiplierDecimal":"4","filterType":"PERCENT_PRICE"}
        #                 ],
        #                 "orderTypes": ["LIMIT","MARKET","STOP","STOP_MARKET","TAKE_PROFIT","TAKE_PROFIT_MARKET","TRAILING_STOP_MARKET"],
        #                 "timeInForce": ["GTC","IOC","FOK","GTX"]
        #             },
        #             {
        #                 "symbol": "BTCUSD_PERP",
        #                 "pair": "BTCUSD",
        #                 "contractType": "PERPETUAL",
        #                 "deliveryDate": 4133404800000,
        #                 "onboardDate": 1596006000000,
        #                 "contractStatus": "TRADING",
        #                 "contractSize": 100,
        #                 "marginAsset": "BTC",
        #                 "maintMarginPercent": "2.5000",
        #                 "requiredMarginPercent": "5.0000",
        #                 "baseAsset": "BTC",
        #                 "quoteAsset": "USD",
        #                 "pricePrecision": 1,
        #                 "quantityPrecision": 0,
        #                 "baseAssetPrecision": 8,
        #                 "quotePrecision": 8,
        #                 "equalQtyPrecision": 4,
        #                 "filters": [
        #                     {"minPrice":"0.1","maxPrice":"100000","filterType":"PRICE_FILTER","tickSize":"0.1"},
        #                     {"stepSize":"1","filterType":"LOT_SIZE","maxQty":"100000","minQty":"1"},
        #                     {"stepSize":"1","filterType":"MARKET_LOT_SIZE","maxQty":"100000","minQty":"1"},
        #                     {"limit":200,"filterType":"MAX_NUM_ORDERS"},
        #                     {"multiplierDown":"0.8500","multiplierUp":"1.1500","multiplierDecimal":"4","filterType":"PERCENT_PRICE"}
        #                 ],
        #                 "orderTypes": ["LIMIT","MARKET","STOP","STOP_MARKET","TAKE_PROFIT","TAKE_PROFIT_MARKET","TRAILING_STOP_MARKET"],
        #                 "timeInForce": ["GTC","IOC","FOK","GTX"]
        #             }
        #         ]
        #     }
        #
        if self.options['adjustForTimeDifference']:
            self.load_time_difference()
        markets = self.safe_value(response, 'symbols', [])
        result = []
        for i in range(0, len(markets)):
            market = markets[i]
            id = self.safe_string(market, 'symbol')
            lowercaseId = self.safe_string_lower(market, 'symbol')
            baseId = self.safe_string(market, 'baseAsset')
            quoteId = self.safe_string(market, 'quoteAsset')
            settleId = self.safe_string(market, 'marginAsset')
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            settle = self.safe_currency_code(settleId)
            contract = future or delivery
            contractType = self.safe_string(market, 'contractType')
            idSymbol = contract and (contractType != 'PERPETUAL')
            symbol = None
            expiry = None
            if idSymbol:
                symbol = id
                expiry = self.safe_integer(market, 'deliveryDate')
            else:
                symbol = base + '/' + quote
            filters = self.safe_value(market, 'filters', [])
            filtersByType = self.index_by(filters, 'filterType')
            status = self.safe_string_2(market, 'status', 'contractStatus')
            contractSize = None
            fees = self.fees
            linear = None
            inverse = None
            if contract:
                contractSize = self.safe_number(market, 'contractSize', self.parse_number('1'))
                fees = self.fees[type]
                linear = settle == quote
                inverse = settle == base
            active = (status == 'TRADING')
            if spot:
                permissions = self.safe_value(market, 'permissions', [])
                for j in range(0, len(permissions)):
                    if permissions[j] == 'TRD_GRP_003':
                        active = False
                        break
            isMarginTradingAllowed = self.safe_value(market, 'isMarginTradingAllowed', False)
            entry = {
                'id': id,
                'lowercaseId': lowercaseId,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'settle': settle,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': settleId,
                'type': type,
                'spot': spot,
                'margin': spot and isMarginTradingAllowed,
                'swap': future,
                'future': future,
                'delivery': delivery,
                'option': False,
                'active': active,
                'contract': contract,
                'linear': linear,
                'inverse': inverse,
                'taker': fees['trading']['taker'],
                'maker': fees['trading']['maker'],
                'contractSize': contractSize,
                'expiry': expiry,
                'expiryDatetime': self.iso8601(expiry),
                'strike': None,
                'optionType': None,
                'precision': {
                    'amount': self.safe_integer(market, 'quantityPrecision'),
                    'price': self.safe_integer(market, 'pricePrecision'),
                    'base': self.safe_integer(market, 'baseAssetPrecision'),
                    'quote': self.safe_integer(market, 'quotePrecision'),
                },
                'limits': {
                    'leverage': {
                        'min': None,
                        'max': None,
                    },
                    'amount': {
                        'min': None,
                        'max': None,
                    },
                    'price': {
                        'min': None,
                        'max': None,
                    },
                    'cost': {
                        'min': None,
                        'max': None,
                    },
                },
                'info': market,
            }
            if 'PRICE_FILTER' in filtersByType:
                filter = self.safe_value(filtersByType, 'PRICE_FILTER', {})
                # PRICE_FILTER reports zero values for maxPrice
                # since they updated filter types in November 2018
                # https://github.com/ccxt/ccxt/issues/4286
                # therefore limits['price']['max'] doesn't have any meaningful value except None
                entry['limits']['price'] = {
                    'min': self.safe_number(filter, 'minPrice'),
                    'max': self.safe_number(filter, 'maxPrice'),
                }
                entry['precision']['price'] = self.precision_from_string(filter['tickSize'])
            if 'LOT_SIZE' in filtersByType:
                filter = self.safe_value(filtersByType, 'LOT_SIZE', {})
                stepSize = self.safe_string(filter, 'stepSize')
                entry['precision']['amount'] = self.precision_from_string(stepSize)
                entry['limits']['amount'] = {
                    'min': self.safe_number(filter, 'minQty'),
                    'max': self.safe_number(filter, 'maxQty'),
                }
            if 'MARKET_LOT_SIZE' in filtersByType:
                filter = self.safe_value(filtersByType, 'MARKET_LOT_SIZE', {})
                entry['limits']['market'] = {
                    'min': self.safe_number(filter, 'minQty'),
                    'max': self.safe_number(filter, 'maxQty'),
                }
            if 'MIN_NOTIONAL' in filtersByType:
                filter = self.safe_value(filtersByType, 'MIN_NOTIONAL', {})
                entry['limits']['cost']['min'] = self.safe_number_2(filter, 'minNotional', 'notional')
            result.append(entry)
        return result

    def parse_balance_helper(self, entry):
        account = self.account()
        account['used'] = self.safe_string(entry, 'locked')
        account['free'] = self.safe_string(entry, 'free')
        account['total'] = self.safe_string(entry, 'totalAsset')
        return account

    def parse_balance(self, response, type=None, marginMode=None):
        result = {
            'info': response,
        }
        timestamp = None
        isolated = marginMode == 'isolated'
        if ((type == 'spot') or (type == 'margin') or (marginMode == 'cross')) and not isolated:
            timestamp = self.safe_integer(response, 'updateTime')
            balances = self.safe_value_2(response, 'balances', 'userAssets', [])
            for i in range(0, len(balances)):
                balance = balances[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
        elif isolated:
            assets = self.safe_value(response, 'assets')
            for i in range(0, len(assets)):
                asset = assets[i]
                marketId = self.safe_value(asset, 'symbol')
                symbol = self.safe_symbol(marketId)
                base = self.safe_value(asset, 'baseAsset', {})
                quote = self.safe_value(asset, 'quoteAsset', {})
                baseCode = self.safe_currency_code(self.safe_string(base, 'asset'))
                quoteCode = self.safe_currency_code(self.safe_string(quote, 'asset'))
                subResult = {}
                subResult[baseCode] = self.parse_balance_helper(base)
                subResult[quoteCode] = self.parse_balance_helper(quote)
                result[symbol] = self.safe_balance(subResult)
        elif type == 'savings':
            positionAmountVos = self.safe_value(response, 'positionAmountVos', [])
            for i in range(0, len(positionAmountVos)):
                entry = positionAmountVos[i]
                currencyId = self.safe_string(entry, 'asset')
                code = self.safe_currency_code(currencyId)
                account = self.account()
                usedAndTotal = self.safe_string(entry, 'amount')
                account['total'] = usedAndTotal
                account['used'] = usedAndTotal
                result[code] = account
        elif type == 'funding':
            for i in range(0, len(response)):
                entry = response[i]
                account = self.account()
                currencyId = self.safe_string(entry, 'asset')
                code = self.safe_currency_code(currencyId)
                account['free'] = self.safe_string(entry, 'free')
                frozen = self.safe_string(entry, 'freeze')
                withdrawing = self.safe_string(entry, 'withdrawing')
                locked = self.safe_string(entry, 'locked')
                account['used'] = Precise.string_add(frozen, Precise.string_add(locked, withdrawing))
                result[code] = account
        else:
            balances = response
            if not isinstance(response, list):
                balances = self.safe_value(response, 'assets', [])
            for i in range(0, len(balances)):
                balance = balances[i]
                currencyId = self.safe_string(balance, 'asset')
                code = self.safe_currency_code(currencyId)
                account = self.account()
                account['free'] = self.safe_string(balance, 'availableBalance')
                account['used'] = self.safe_string(balance, 'initialMargin')
                account['total'] = self.safe_string_2(balance, 'marginBalance', 'balance')
                result[code] = account
        result['timestamp'] = timestamp
        result['datetime'] = self.iso8601(timestamp)
        return result if isolated else self.safe_balance(result)

    def fetch_balance(self, params={}):
        """
        query for balance and get the amount of funds available for trading or funds locked in orders
        :param dict params: extra parameters specific to the binance api endpoint
        :param str|None params['type']: 'future', 'delivery', 'savings', 'funding', or 'spot'
        :param str|None params['marginMode']: 'cross' or 'isolated', for margin trading, uses self.options.defaultMarginMode if not passed, defaults to None/None/None
        :param [str]|None params['symbols']: unified market symbols, only used in isolated margin mode
        :returns dict: a `balance structure <https://docs.ccxt.com/en/latest/manual.html?#balance-structure>`
        """
        self.load_markets()
        defaultType = self.safe_string_2(self.options, 'fetchBalance', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        marginMode, query = self.handle_margin_mode_and_params('fetchBalance', params)
        method = 'privateGetAccount'
        request = {}
        if type == 'future':
            options = self.safe_value(self.options, type, {})
            fetchBalanceOptions = self.safe_value(options, 'fetchBalance', {})
            method = self.safe_string(fetchBalanceOptions, 'method', 'fapiPrivateV2GetAccount')
        elif type == 'delivery':
            options = self.safe_value(self.options, type, {})
            fetchBalanceOptions = self.safe_value(options, 'fetchBalance', {})
            method = self.safe_string(fetchBalanceOptions, 'method', 'dapiPrivateGetAccount')
        elif type == 'margin' or marginMode == 'cross':
            method = 'sapiGetMarginAccount'
        elif type == 'savings':
            method = 'sapiGetLendingUnionAccount'
        elif type == 'funding':
            method = 'sapiPostAssetGetFundingAsset'
        elif marginMode == 'isolated':
            method = 'sapiGetMarginIsolatedAccount'
            paramSymbols = self.safe_value(params, 'symbols')
            if paramSymbols is not None:
                symbols = ''
                if self.is_array(paramSymbols):
                    symbols = self.market_id(paramSymbols[0])
                    for i in range(1, len(paramSymbols)):
                        symbol = paramSymbols[i]
                        id = self.market_id(symbol)
                        symbols += ',' + id
                else:
                    symbols = paramSymbols
                request['symbols'] = symbols
        requestParams = self.omit(query, ['type', 'symbols'])
        response = getattr(self, method)(self.extend(request, requestParams))
        #
        # spot
        #
        #     {
        #         makerCommission: 10,
        #         takerCommission: 10,
        #         buyerCommission: 0,
        #         sellerCommission: 0,
        #         canTrade: True,
        #         canWithdraw: True,
        #         canDeposit: True,
        #         updateTime: 1575357359602,
        #         accountType: "MARGIN",
        #         balances: [
        #             {asset: "BTC", free: "0.00219821", locked: "0.00000000"  },
        #         ]
        #     }
        #
        # margin(cross)
        #
        #     {
        #         "borrowEnabled":true,
        #         "marginLevel":"999.00000000",
        #         "totalAssetOfBtc":"0.00000000",
        #         "totalLiabilityOfBtc":"0.00000000",
        #         "totalNetAssetOfBtc":"0.00000000",
        #         "tradeEnabled":true,
        #         "transferEnabled":true,
        #         "userAssets":[
        #             {"asset":"MATIC","borrowed":"0.00000000","free":"0.00000000","interest":"0.00000000","locked":"0.00000000","netAsset":"0.00000000"},
        #             {"asset":"VET","borrowed":"0.00000000","free":"0.00000000","interest":"0.00000000","locked":"0.00000000","netAsset":"0.00000000"},
        #             {"asset":"USDT","borrowed":"0.00000000","free":"0.00000000","interest":"0.00000000","locked":"0.00000000","netAsset":"0.00000000"}
        #         ],
        #     }
        #
        # margin(isolated)
        #
        #    {
        #        info: {
        #            assets: [
        #                {
        #                    baseAsset: {
        #                        asset: '1INCH',
        #                        borrowEnabled: True,
        #                        borrowed: '0',
        #                        free: '0',
        #                        interest: '0',
        #                        locked: '0',
        #                        netAsset: '0',
        #                        netAssetOfBtc: '0',
        #                        repayEnabled: True,
        #                        totalAsset: '0'
        #                    },
        #                    quoteAsset: {
        #                        asset: 'USDT',
        #                        borrowEnabled: True,
        #                        borrowed: '0',
        #                        free: '11',
        #                        interest: '0',
        #                        locked: '0',
        #                        netAsset: '11',
        #                        netAssetOfBtc: '0.00054615',
        #                        repayEnabled: True,
        #                        totalAsset: '11'
        #                    },
        #                    symbol: '1INCHUSDT',
        #                    isolatedCreated: True,
        #                    marginLevel: '999',
        #                    marginLevelStatus: 'EXCESSIVE',
        #                    marginRatio: '5',
        #                    indexPrice: '0.59184331',
        #                    liquidatePrice: '0',
        #                    liquidateRate: '0',
        #                    tradeEnabled: True,
        #                    enabled: True
        #                },
        #            ]
        #        }
        #    }
        #
        # futures(fapi)
        #
        #     fapiPrivateGetAccount
        #
        #     {
        #         "feeTier":0,
        #         "canTrade":true,
        #         "canDeposit":true,
        #         "canWithdraw":true,
        #         "updateTime":0,
        #         "totalInitialMargin":"0.00000000",
        #         "totalMaintMargin":"0.00000000",
        #         "totalWalletBalance":"4.54000000",
        #         "totalUnrealizedProfit":"0.00000000",
        #         "totalMarginBalance":"4.54000000",
        #         "totalPositionInitialMargin":"0.00000000",
        #         "totalOpenOrderInitialMargin":"0.00000000",
        #         "maxWithdrawAmount":"4.54000000",
        #         "assets":[
        #             {
        #                 "asset":"USDT",
        #                 "walletBalance":"4.54000000",
        #                 "unrealizedProfit":"0.00000000",
        #                 "marginBalance":"4.54000000",
        #                 "maintMargin":"0.00000000",
        #                 "initialMargin":"0.00000000",
        #                 "positionInitialMargin":"0.00000000",
        #                 "openOrderInitialMargin":"0.00000000",
        #                 "maxWithdrawAmount":"4.54000000"
        #             }
        #         ],
        #         "positions":[
        #             {
        #                 "symbol":"BTCUSDT",
        #                 "initialMargin":"0.00000",
        #                 "maintMargin":"0.00000",
        #                 "unrealizedProfit":"0.00000000",
        #                 "positionInitialMargin":"0.00000",
        #                 "openOrderInitialMargin":"0.00000"
        #             }
        #         ]
        #     }
        #
        #     fapiPrivateV2GetAccount
        #
        #     {
        #         "feeTier":0,
        #         "canTrade":true,
        #         "canDeposit":true,
        #         "canWithdraw":true,
        #         "updateTime":0,
        #         "totalInitialMargin":"0.00000000",
        #         "totalMaintMargin":"0.00000000",
        #         "totalWalletBalance":"0.00000000",
        #         "totalUnrealizedProfit":"0.00000000",
        #         "totalMarginBalance":"0.00000000",
        #         "totalPositionInitialMargin":"0.00000000",
        #         "totalOpenOrderInitialMargin":"0.00000000",
        #         "totalCrossWalletBalance":"0.00000000",
        #         "totalCrossUnPnl":"0.00000000",
        #         "availableBalance":"0.00000000",
        #         "maxWithdrawAmount":"0.00000000",
        #         "assets":[
        #             {
        #                 "asset":"BNB",
        #                 "walletBalance":"0.01000000",
        #                 "unrealizedProfit":"0.00000000",
        #                 "marginBalance":"0.01000000",
        #                 "maintMargin":"0.00000000",
        #                 "initialMargin":"0.00000000",
        #                 "positionInitialMargin":"0.00000000",
        #                 "openOrderInitialMargin":"0.00000000",
        #                 "maxWithdrawAmount":"0.01000000",
        #                 "crossWalletBalance":"0.01000000",
        #                 "crossUnPnl":"0.00000000",
        #                 "availableBalance":"0.01000000"
        #             }
        #         ],
        #         "positions":[
        #             {
        #                 "symbol":"BTCUSDT",
        #                 "initialMargin":"0",
        #                 "maintMargin":"0",
        #                 "unrealizedProfit":"0.00000000",
        #                 "positionInitialMargin":"0",
        #                 "openOrderInitialMargin":"0",
        #                 "leverage":"20",
        #                 "isolated":false,
        #                 "entryPrice":"0.00000",
        #                 "maxNotional":"5000000",
        #                 "positionSide":"BOTH"
        #             },
        #         ]
        #     }
        #
        #     fapiPrivateV2GetBalance
        #
        #     [
        #         {
        #             "accountAlias":"FzFzXquXXqoC",
        #             "asset":"BNB",
        #             "balance":"0.01000000",
        #             "crossWalletBalance":"0.01000000",
        #             "crossUnPnl":"0.00000000",
        #             "availableBalance":"0.01000000",
        #             "maxWithdrawAmount":"0.01000000"
        #         }
        #     ]
        #
        # savings
        #
        #     {
        #       "totalAmountInBTC": "0.3172",
        #       "totalAmountInUSDT": "10000",
        #       "totalFixedAmountInBTC": "0.3172",
        #       "totalFixedAmountInUSDT": "10000",
        #       "totalFlexibleInBTC": "0",
        #       "totalFlexibleInUSDT": "0",
        #       "positionAmountVos": [
        #         {
        #           "asset": "USDT",
        #           "amount": "10000",
        #           "amountInBTC": "0.3172",
        #           "amountInUSDT": "10000"
        #         },
        #         {
        #           "asset": "BUSD",
        #           "amount": "0",
        #           "amountInBTC": "0",
        #           "amountInUSDT": "0"
        #         }
        #       ]
        #     }
        #
        # binance pay
        #
        #     [
        #       {
        #         "asset": "BUSD",
        #         "free": "1129.83",
        #         "locked": "0",
        #         "freeze": "0",
        #         "withdrawing": "0"
        #       }
        #     ]
        #
        return self.parse_balance(response, type, marginMode)

    def fetch_order_book(self, symbol, limit=None, params={}):
        """
        fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
        :param str symbol: unified symbol of the market to fetch the order book for
        :param int|None limit: the maximum amount of order book entries to return
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/en/latest/manual.html#order-book-structure>` indexed by market symbols
        """
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        if limit is not None:
            request['limit'] = limit  # default 100, max 5000, see https://github.com/binance/binance-spot-api-docs/blob/master/rest-api.md#order-book
        method = 'publicGetDepth'
        if market['linear']:
            method = 'fapiPublicGetDepth'
        elif market['inverse']:
            method = 'dapiPublicGetDepth'
        response = getattr(self, method)(self.extend(request, params))
        #
        # future
        #
        #     {
        #         "lastUpdateId":333598053905,
        #         "E":1618631511986,
        #         "T":1618631511964,
        #         "bids":[
        #             ["2493.56","20.189"],
        #             ["2493.54","1.000"],
        #             ["2493.51","0.005"]
        #         ],
        #         "asks":[
        #             ["2493.57","0.877"],
        #             ["2493.62","0.063"],
        #             ["2493.71","12.054"],
        #         ]
        #     }
        timestamp = self.safe_integer(response, 'T')
        orderbook = self.parse_order_book(response, symbol, timestamp)
        orderbook['nonce'] = self.safe_integer(response, 'lastUpdateId')
        return orderbook

    def parse_ticker(self, ticker, market=None):
        #
        #     {
        #         symbol: 'ETHBTC',
        #         priceChange: '0.00068700',
        #         priceChangePercent: '2.075',
        #         weightedAvgPrice: '0.03342681',
        #         prevClosePrice: '0.03310300',
        #         lastPrice: '0.03378900',
        #         lastQty: '0.07700000',
        #         bidPrice: '0.03378900',
        #         bidQty: '7.16800000',
        #         askPrice: '0.03379000',
        #         askQty: '24.00000000',
        #         openPrice: '0.03310200',
        #         highPrice: '0.03388900',
        #         lowPrice: '0.03306900',
        #         volume: '205478.41000000',
        #         quoteVolume: '6868.48826294',
        #         openTime: 1601469986932,
        #         closeTime: 1601556386932,
        #         firstId: 196098772,
        #         lastId: 196186315,
        #         count: 87544
        #     }
        #
        # coinm
        #     {
        #         baseVolume: '214549.95171161',
        #         closeTime: '1621965286847',
        #         count: '1283779',
        #         firstId: '152560106',
        #         highPrice: '39938.3',
        #         lastId: '153843955',
        #         lastPrice: '37993.4',
        #         lastQty: '1',
        #         lowPrice: '36457.2',
        #         openPrice: '37783.4',
        #         openTime: '1621878840000',
        #         pair: 'BTCUSD',
        #         priceChange: '210.0',
        #         priceChangePercent: '0.556',
        #         symbol: 'BTCUSD_PERP',
        #         volume: '81990451',
        #         weightedAvgPrice: '38215.08713747'
        #     }
        #
        timestamp = self.safe_integer(ticker, 'closeTime')
        marketId = self.safe_string(ticker, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        last = self.safe_string(ticker, 'lastPrice')
        isCoinm = ('baseVolume' in ticker)
        baseVolume = None
        quoteVolume = None
        if isCoinm:
            baseVolume = self.safe_string(ticker, 'baseVolume')
            quoteVolume = self.safe_string(ticker, 'volume')
        else:
            baseVolume = self.safe_string(ticker, 'volume')
            quoteVolume = self.safe_string(ticker, 'quoteVolume')
        return self.safe_ticker({
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_string(ticker, 'highPrice'),
            'low': self.safe_string(ticker, 'lowPrice'),
            'bid': self.safe_string(ticker, 'bidPrice'),
            'bidVolume': self.safe_string(ticker, 'bidQty'),
            'ask': self.safe_string(ticker, 'askPrice'),
            'askVolume': self.safe_string(ticker, 'askQty'),
            'vwap': self.safe_string(ticker, 'weightedAvgPrice'),
            'open': self.safe_string(ticker, 'openPrice'),
            'close': last,
            'last': last,
            'previousClose': self.safe_string(ticker, 'prevClosePrice'),  # previous day close
            'change': self.safe_string(ticker, 'priceChange'),
            'percentage': self.safe_string(ticker, 'priceChangePercent'),
            'average': None,
            'baseVolume': baseVolume,
            'quoteVolume': quoteVolume,
            'info': ticker,
        }, market)

    def fetch_status(self, params={}):
        """
        the latest known information on the availability of the exchange API
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: a `status structure <https://docs.ccxt.com/en/latest/manual.html#exchange-status-structure>`
        """
        response = self.sapiGetSystemStatus(params)
        #
        #     {
        #         "status": 0,              # 0: normal，1：system maintenance
        #         "msg": "normal"           # "normal", "system_maintenance"
        #     }
        #
        statusRaw = self.safe_string(response, 'status')
        return {
            'status': self.safe_string({'0': 'ok', '1': 'maintenance'}, statusRaw, statusRaw),
            'updated': None,
            'eta': None,
            'url': None,
            'info': response,
        }

    def fetch_ticker(self, symbol, params={}):
        """
        fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
        :param str symbol: unified symbol of the market to fetch the ticker for
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: a `ticker structure <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        method = 'publicGetTicker24hr'
        if market['linear']:
            method = 'fapiPublicGetTicker24hr'
        elif market['inverse']:
            method = 'dapiPublicGetTicker24hr'
        response = getattr(self, method)(self.extend(request, params))
        if isinstance(response, list):
            firstTicker = self.safe_value(response, 0, {})
            return self.parse_ticker(firstTicker, market)
        return self.parse_ticker(response, market)

    def fetch_bids_asks(self, symbols=None, params={}):
        """
        fetches the bid and ask price and volume for multiple markets
        :param [str]|None symbols: unified symbols of the markets to fetch the bids and asks for, all markets are returned if not assigned
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: an array of `ticker structures <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
        """
        self.load_markets()
        defaultType = self.safe_string_2(self.options, 'fetchBidsAsks', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        query = self.omit(params, 'type')
        method = None
        if type == 'future':
            method = 'fapiPublicGetTickerBookTicker'
        elif type == 'delivery':
            method = 'dapiPublicGetTickerBookTicker'
        else:
            method = 'publicGetTickerBookTicker'
        response = getattr(self, method)(query)
        return self.parse_tickers(response, symbols)

    def fetch_tickers(self, symbols=None, params={}):
        """
        fetches price tickers for multiple markets, statistical calculations with the information calculated over the past 24 hours each market
        :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 binance api endpoint
        :returns dict: an array of `ticker structures <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
        """
        self.load_markets()
        defaultType = self.safe_string_2(self.options, 'fetchTickers', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        query = self.omit(params, 'type')
        defaultMethod = None
        if type == 'future':
            defaultMethod = 'fapiPublicGetTicker24hr'
        elif type == 'delivery':
            defaultMethod = 'dapiPublicGetTicker24hr'
        else:
            defaultMethod = 'publicGetTicker24hr'
        method = self.safe_string(self.options, 'fetchTickersMethod', defaultMethod)
        response = getattr(self, method)(query)
        return self.parse_tickers(response, symbols)

    def parse_ohlcv(self, ohlcv, market=None):
        # when api method = publicGetKlines or fapiPublicGetKlines or dapiPublicGetKlines
        #     [
        #         1591478520000,  # open time
        #         "0.02501300",  # open
        #         "0.02501800",  # high
        #         "0.02500000",  # low
        #         "0.02500000",  # close
        #         "22.19000000",  # volume
        #         1591478579999,  # close time
        #         "0.55490906",  # quote asset volume
        #         40,            # number of trades
        #         "10.92900000",  # taker buy base asset volume
        #         "0.27336462",  # taker buy quote asset volume
        #         "0"            # ignore
        #     ]
        #
        #  when api method = fapiPublicGetMarkPriceKlines or fapiPublicGetIndexPriceKlines
        #     [
        #         [
        #         1591256460000,          # Open time
        #         "9653.29201333",        # Open
        #         "9654.56401333",        # High
        #         "9653.07367333",        # Low
        #         "9653.07367333",        # Close(or latest price)
        #         "0",                    # Ignore
        #         1591256519999,          # Close time
        #         "0",                    # Ignore
        #         60,                     # Number of bisic data
        #         "0",                    # Ignore
        #         "0",                    # Ignore
        #         "0"                     # Ignore
        #         ]
        #     ]
        #
        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),
        ]

    def fetch_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
        """
        fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
        :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|None since: timestamp in ms of the earliest candle to fetch
        :param int|None limit: the maximum amount of candles to fetch
        :param dict params: extra parameters specific to the binance api endpoint
        :param str|None params['price']: "mark" or "index" for mark price and index price candles
        :param int|None params['until']: timestamp in ms of the latest candle to fetch
        :returns [[int]]: A list of candles ordered as timestamp, open, high, low, close, volume
        """
        self.load_markets()
        market = self.market(symbol)
        # binance docs say that the default limit 500, max 1500 for futures, max 1000 for spot markets
        # the reality is that the time range wider than 500 candles won't work right
        defaultLimit = 500
        maxLimit = 1500
        price = self.safe_string(params, 'price')
        until = self.safe_integer(params, 'until')
        params = self.omit(params, ['price', 'until'])
        limit = defaultLimit if (limit is None) else min(limit, maxLimit)
        request = {
            'interval': self.timeframes[timeframe],
            'limit': limit,
        }
        if price == 'index':
            request['pair'] = market['id']   # Index price takes self argument instead of symbol
        else:
            request['symbol'] = market['id']
        # duration = self.parse_timeframe(timeframe)
        if since is not None:
            request['startTime'] = since
            #
            # It didn't work before without the endTime
            # https://github.com/ccxt/ccxt/issues/8454
            #
            if market['inverse']:
                if since > 0:
                    duration = self.parse_timeframe(timeframe)
                    endTime = self.sum(since, limit * duration * 1000 - 1)
                    now = self.milliseconds()
                    request['endTime'] = min(now, endTime)
        if until is not None:
            request['endTime'] = until
        method = 'publicGetKlines'
        if price == 'mark':
            if market['inverse']:
                method = 'dapiPublicGetMarkPriceKlines'
            else:
                method = 'fapiPublicGetMarkPriceKlines'
        elif price == 'index':
            if market['inverse']:
                method = 'dapiPublicGetIndexPriceKlines'
            else:
                method = 'fapiPublicGetIndexPriceKlines'
        elif market['linear']:
            method = 'fapiPublicGetKlines'
        elif market['inverse']:
            method = 'dapiPublicGetKlines'
        response = getattr(self, method)(self.extend(request, params))
        #
        #     [
        #         [1591478520000,"0.02501300","0.02501800","0.02500000","0.02500000","22.19000000",1591478579999,"0.55490906",40,"10.92900000","0.27336462","0"],
        #         [1591478580000,"0.02499600","0.02500900","0.02499400","0.02500300","21.34700000",1591478639999,"0.53370468",24,"7.53800000","0.18850725","0"],
        #         [1591478640000,"0.02500800","0.02501100","0.02500300","0.02500800","154.14200000",1591478699999,"3.85405839",97,"5.32300000","0.13312641","0"],
        #     ]
        #
        return self.parse_ohlcvs(response, market, timeframe, since, limit)

    def parse_trade(self, trade, market=None):
        if 'isDustTrade' in trade:
            return self.parse_dust_trade(trade, market)
        #
        # aggregate trades
        # https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#compressedaggregate-trades-list
        #
        #     {
        #         "a": 26129,         # Aggregate tradeId
        #         "p": "0.01633102",  # Price
        #         "q": "4.70443515",  # Quantity
        #         "f": 27781,         # First tradeId
        #         "l": 27781,         # Last tradeId
        #         "T": 1498793709153,  # Timestamp
        #         "m": True,          # Was the buyer the maker?
        #         "M": True           # Was the trade the best price match?
        #     }
        #
        # recent public trades and old public trades
        # https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#recent-trades-list
        # https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#old-trade-lookup-market_data
        #
        #     {
        #         "id": 28457,
        #         "price": "4.00000100",
        #         "qty": "12.00000000",
        #         "time": 1499865549590,
        #         "isBuyerMaker": True,
        #         "isBestMatch": True
        #     }
        #
        # private trades
        # https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#account-trade-list-user_data
        #
        #     {
        #         "symbol": "BNBBTC",
        #         "id": 28457,
        #         "orderId": 100234,
        #         "price": "4.00000100",
        #         "qty": "12.00000000",
        #         "commission": "10.10000000",
        #         "commissionAsset": "BNB",
        #         "time": 1499865549590,
        #         "isBuyer": True,
        #         "isMaker": False,
        #         "isBestMatch": True
        #     }
        #
        # futures trades
        # https://binance-docs.github.io/apidocs/futures/en/#account-trade-list-user_data
        #
        #     {
        #       "accountId": 20,
        #       "buyer": False,
        #       "commission": "-0.07819010",
        #       "commissionAsset": "USDT",
        #       "counterPartyId": 653,
        #       "id": 698759,
        #       "maker": False,
        #       "orderId": 25851813,
        #       "price": "7819.01",
        #       "qty": "0.002",
        #       "quoteQty": "0.01563",
        #       "realizedPnl": "-0.91539999",
        #       "side": "SELL",
        #       "symbol": "BTCUSDT",
        #       "time": 1569514978020
        #     }
        #     {
        #       "symbol": "BTCUSDT",
        #       "id": 477128891,
        #       "orderId": 13809777875,
        #       "side": "SELL",
        #       "price": "38479.55",
        #       "qty": "0.001",
        #       "realizedPnl": "-0.00009534",
        #       "marginAsset": "USDT",
        #       "quoteQty": "38.47955",
        #       "commission": "-0.00076959",
        #       "commissionAsset": "USDT",
        #       "time": 1612733566708,
        #       "positionSide": "BOTH",
        #       "maker": True,
        #       "buyer": False
        #     }
        #
        # {respType: FULL}
        #
        #     {
        #       "price": "4000.00000000",
        #       "qty": "1.00000000",
        #       "commission": "4.00000000",
        #       "commissionAsset": "USDT",
        #       "tradeId": "1234",
        #     }
        #
        timestamp = self.safe_integer_2(trade, 'T', 'time')
        price = self.safe_string_2(trade, 'p', 'price')
        amount = self.safe_string_2(trade, 'q', 'qty')
        cost = self.safe_string_2(trade, 'quoteQty', 'baseQty')  # inverse futures
        marketId = self.safe_string(trade, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        id = self.safe_string_2(trade, 't', 'a')
        id = self.safe_string_2(trade, 'id', 'tradeId', id)
        side = None
        orderId = self.safe_string(trade, 'orderId')
        buyerMaker = self.safe_value_2(trade, 'm', 'isBuyerMaker')
        takerOrMaker = None
        if buyerMaker is not None:
            side = 'sell' if buyerMaker else 'buy'  # self is reversed intentionally
            takerOrMaker = 'taker'
        elif 'side' in trade:
            side = self.safe_string_lower(trade, 'side')
        else:
            if 'isBuyer' in trade:
                side = 'buy' if trade['isBuyer'] else 'sell'  # self is a True side
        fee = None
        if 'commission' in trade:
            fee = {
                'cost': self.safe_string(trade, 'commission'),
                'currency': self.safe_currency_code(self.safe_string(trade, 'commissionAsset')),
            }
        if 'isMaker' in trade:
            takerOrMaker = 'maker' if trade['isMaker'] else 'taker'
        if 'maker' in trade:
            takerOrMaker = 'maker' if trade['maker'] else 'taker'
        return self.safe_trade({
            'info': trade,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'id': id,
            'order': orderId,
            'type': None,
            'side': side,
            'takerOrMaker': takerOrMaker,
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': fee,
        }, market)

    def fetch_trades(self, symbol, since=None, limit=None, params={}):
        """
        get the list of most recent trades for a particular symbol
        :param str symbol: unified symbol of the market to fetch trades for
        :param int|None since: timestamp in ms of the earliest trade to fetch
        :param int|None limit: the maximum amount of trades to fetch
        :param dict params: extra parameters specific to the binance api endpoint
        :returns [dict]: a list of `trade structures <https://docs.ccxt.com/en/latest/manual.html?#public-trades>`
        """
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
            # 'fromId': 123,    # ID to get aggregate trades from INCLUSIVE.
            # 'startTime': 456,  # Timestamp in ms to get aggregate trades from INCLUSIVE.
            # 'endTime': 789,   # Timestamp in ms to get aggregate trades until INCLUSIVE.
            # 'limit': 500,     # default = 500, maximum = 1000
        }
        type, query = self.handle_market_type_and_params('fetchTrades', market, params)
        defaultMethod = None
        if type == 'future':
            if market['linear']:
                defaultMethod = 'fapiPublicGetAggTrades'
            elif market['inverse']:
                defaultMethod = 'dapiPublicGetAggTrades'
        elif type == 'delivery':
            defaultMethod = 'dapiPublicGetAggTrades'
        else:
            defaultMethod = 'publicGetAggTrades'
        method = self.safe_string(self.options, 'fetchTradesMethod', defaultMethod)
        if method == 'publicGetAggTrades':
            if type == 'future':
                if market['linear']:
                    method = 'fapiPublicGetAggTrades'
                elif market['inverse']:
                    method = 'dapiPublicGetAggTrades'
            elif type == 'delivery':
                method = 'dapiPublicGetAggTrades'
        elif method == 'publicGetHistoricalTrades':
            if type == 'future':
                if market['linear']:
                    method = 'fapiPublicGetHistoricalTrades'
                elif market['inverse']:
                    method = 'dapiPublicGetHistoricalTrades'
            elif type == 'delivery':
                method = 'dapiPublicGetHistoricalTrades'
        if since is not None:
            request['startTime'] = since
            # https://github.com/ccxt/ccxt/issues/6400
            # https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#compressedaggregate-trades-list
            request['endTime'] = self.sum(since, 3600000)
        if limit is not None:
            request['limit'] = limit  # default = 500, maximum = 1000
        #
        # Caveats:
        # - default limit(500) applies only if no other parameters set, trades up
        #   to the maximum limit may be returned to satisfy other parameters
        # - if both limit and time window is set and time window contains more
        #   trades than the limit then the last trades from the window are returned
        # - 'tradeId' accepted and returned by self method is "aggregate" trade id
        #   which is different from actual trade id
        # - setting both fromId and time window results in error
        response = getattr(self, method)(self.extend(request, query))
        #
        # aggregate trades
        #
        #     [
        #         {
        #             "a": 26129,         # Aggregate tradeId
        #             "p": "0.01633102",  # Price
        #             "q": "4.70443515",  # Quantity
        #             "f": 27781,         # First tradeId
        #             "l": 27781,         # Last tradeId
        #             "T": 1498793709153,  # Timestamp
        #             "m": True,          # Was the buyer the maker?
        #             "M": True           # Was the trade the best price match?
        #         }
        #     ]
        #
        # recent public trades and historical public trades
        #
        #     [
        #         {
        #             "id": 28457,
        #             "price": "4.00000100",
        #             "qty": "12.00000000",
        #             "time": 1499865549590,
        #             "isBuyerMaker": True,
        #             "isBestMatch": True
        #         }
        #     ]
        #
        return self.parse_trades(response, market, since, limit)

    def parse_order_status(self, status):
        statuses = {
            'NEW': 'open',
            'PARTIALLY_FILLED': 'open',
            'FILLED': 'closed',
            'CANCELED': 'canceled',
            'PENDING_CANCEL': 'canceling',  # currently unused
            'REJECTED': 'rejected',
            'EXPIRED': 'expired',
        }
        return self.safe_string(statuses, status, status)

    def parse_order(self, order, market=None):
        #
        # spot
        #
        #     {
        #         "symbol": "LTCBTC",
        #         "orderId": 1,
        #         "clientOrderId": "myOrder1",
        #         "price": "0.1",
        #         "origQty": "1.0",
        #         "executedQty": "0.0",
        #         "cummulativeQuoteQty": "0.0",
        #         "status": "NEW",
        #         "timeInForce": "GTC",
        #         "type": "LIMIT",
        #         "side": "BUY",
        #         "stopPrice": "0.0",
        #         "icebergQty": "0.0",
        #         "time": 1499827319559,
        #         "updateTime": 1499827319559,
        #         "isWorking": True
        #     }
        #
        # futures
        #
        #     {
        #         "symbol": "BTCUSDT",
        #         "orderId": 1,
        #         "clientOrderId": "myOrder1",
        #         "price": "0.1",
        #         "origQty": "1.0",
        #         "executedQty": "1.0",
        #         "cumQuote": "10.0",
        #         "status": "NEW",
        #         "timeInForce": "GTC",
        #         "type": "LIMIT",
        #         "side": "BUY",
        #         "stopPrice": "0.0",
        #         "updateTime": 1499827319559
        #     }
        #
        # createOrder with {"newOrderRespType": "FULL"}
        #
        #     {
        #       "symbol": "BTCUSDT",
        #       "orderId": 5403233939,
        #       "orderListId": -1,
        #       "clientOrderId": "x-R4BD3S825e669e75b6c14f69a2c43e",
        #       "transactTime": 1617151923742,
        #       "price": "0.00000000",
        #       "origQty": "0.00050000",
        #       "executedQty": "0.00050000",
        #       "cummulativeQuoteQty": "29.47081500",
        #       "status": "FILLED",
        #       "timeInForce": "GTC",
        #       "type": "MARKET",
        #       "side": "BUY",
        #       "fills": [
        #         {
        #           "price": "58941.63000000",
        #           "qty": "0.00050000",
        #           "commission": "0.00007050",
        #           "commissionAsset": "BNB",
        #           "tradeId": 737466631
        #         }
        #       ]
        #     }
        #
        # delivery
        #
        #     {
        #       "orderId": "18742727411",
        #       "symbol": "ETHUSD_PERP",
        #       "pair": "ETHUSD",
        #       "status": "FILLED",
        #       "clientOrderId": "x-xcKtGhcu3e2d1503fdd543b3b02419",
        #       "price": "0",
        #       "avgPrice": "4522.14",
        #       "origQty": "1",
        #       "executedQty": "1",
        #       "cumBase": "0.00221134",
        #       "timeInForce": "GTC",
        #       "type": "MARKET",
        #       "reduceOnly": False,
        #       "closePosition": False,
        #       "side": "SELL",
        #       "positionSide": "BOTH",
        #       "stopPrice": "0",
        #       "workingType": "CONTRACT_PRICE",
        #       "priceProtect": False,
        #       "origType": "MARKET",
        #       "time": "1636061952660",
        #       "updateTime": "1636061952660"
        #     }
        #
        status = self.parse_order_status(self.safe_string(order, 'status'))
        marketId = self.safe_string(order, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        filled = self.safe_string(order, 'executedQty', '0')
        timestamp = None
        lastTradeTimestamp = None
        if 'time' in order:
            timestamp = self.safe_integer(order, 'time')
        elif 'transactTime' in order:
            timestamp = self.safe_integer(order, 'transactTime')
        elif 'updateTime' in order:
            if status == 'open':
                if Precise.string_gt(filled, '0'):
                    lastTradeTimestamp = self.safe_integer(order, 'updateTime')
                else:
                    timestamp = self.safe_integer(order, 'updateTime')
        average = self.safe_string(order, 'avgPrice')
        price = self.safe_string(order, 'price')
        amount = self.safe_string(order, 'origQty')
        # - Spot/Margin market: cummulativeQuoteQty
        # - Futures market: cumQuote.
        #   Note self is not the actual cost, since Binance futures uses leverage to calculate margins.
        cost = self.safe_string_2(order, 'cummulativeQuoteQty', 'cumQuote')
        cost = self.safe_string(order, 'cumBase', cost)
        id = self.safe_string(order, 'orderId')
        type = self.safe_string_lower(order, 'type')
        side = self.safe_string_lower(order, 'side')
        fills = self.safe_value(order, 'fills', [])
        clientOrderId = self.safe_string(order, 'clientOrderId')
        timeInForce = self.safe_string(order, 'timeInForce')
        if timeInForce == 'GTX':
            # GTX means "Good Till Crossing" and is an equivalent way of saying Post Only
            timeInForce = 'PO'
        postOnly = (type == 'limit_maker') or (timeInForce == 'PO')
        if type == 'limit_maker':
            type = 'limit'
        stopPriceString = self.safe_string(order, 'stopPrice')
        stopPrice = self.parse_number(self.omit_zero(stopPriceString))
        return self.safe_order({
            'info': order,
            'id': id,
            'clientOrderId': clientOrderId,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': lastTradeTimestamp,
            'symbol': symbol,
            'type': type,
            'timeInForce': timeInForce,
            'postOnly': postOnly,
            'reduceOnly': self.safe_value(order, 'reduceOnly'),
            'side': side,
            'price': price,
            'stopPrice': stopPrice,
            'amount': amount,
            'cost': cost,
            'average': average,
            'filled': filled,
            'remaining': None,
            'status': status,
            'fee': None,
            'trades': fills,
        }, market)

    def create_order(self, symbol, type, side, amount, price=None, params={}):
        """
        create a trade order
        :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 currency you want to trade in units of base currency
        :param float|None price: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
        :param dict params: extra parameters specific to the binance api endpoint
        :param str|None params['marginMode']: 'cross' or 'isolated', for spot margin trading
        :returns dict: an `order structure <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        defaultType = self.safe_string_2(self.options, 'createOrder', 'defaultType', 'spot')
        marketType = self.safe_string(params, 'type', defaultType)
        clientOrderId = self.safe_string_2(params, 'newClientOrderId', 'clientOrderId')
        postOnly = self.safe_value(params, 'postOnly', False)
        reduceOnly = self.safe_value(params, 'reduceOnly')
        marginMode, query = self.handle_margin_mode_and_params('createOrder', params)
        if reduceOnly is not None:
            if (marketType != 'future') and (marketType != 'delivery'):
                raise InvalidOrder(self.id + ' createOrder() does not support reduceOnly for ' + marketType + ' orders, reduceOnly orders are supported for future and delivery markets only')
        method = 'privatePostOrder'
        if marketType == 'future':
            method = 'fapiPrivatePostOrder'
        elif marketType == 'delivery':
            method = 'dapiPrivatePostOrder'
        elif marketType == 'margin' or marginMode is not None:
            method = 'sapiPostMarginOrder'
        if market['spot'] or marketType == 'margin':
            # support for testing orders
            test = self.safe_value(query, 'test', False)
            if test:
                method += 'Test'
            # only supported for spot/margin api(all margin markets are spot markets)
            if postOnly:
                type = 'LIMIT_MAKER'
        initialUppercaseType = type.upper()
        uppercaseType = initialUppercaseType
        stopPrice = self.safe_number(query, 'stopPrice')
        if stopPrice is not None:
            if uppercaseType == 'MARKET':
                uppercaseType = 'STOP_MARKET' if market['contract'] else 'STOP_LOSS'
            elif uppercaseType == 'LIMIT':
                uppercaseType = 'STOP' if market['contract'] else 'STOP_LOSS_LIMIT'
        validOrderTypes = self.safe_value(market['info'], 'orderTypes')
        if not self.in_array(uppercaseType, validOrderTypes):
            if initialUppercaseType != uppercaseType:
                raise InvalidOrder(self.id + ' stopPrice parameter is not allowed for ' + symbol + ' ' + type + ' orders')
            else:
                raise InvalidOrder(self.id + ' ' + type + ' is not a valid order type for the ' + symbol + ' market')
        request = {
            'symbol': market['id'],
            'type': uppercaseType,
            'side': side.upper(),
        }
        if marginMode == 'isolated':
            request['isIsolated'] = True
        if clientOrderId is None:
            broker = self.safe_value(self.options, 'broker')
            if broker is not None:
                brokerId = self.safe_string(broker, marketType)
                if brokerId is not None:
                    request['newClientOrderId'] = brokerId + self.uuid22()
        else:
            request['newClientOrderId'] = clientOrderId
        if (marketType == 'spot') or (marketType == 'margin'):
            request['newOrderRespType'] = self.safe_value(self.options['newOrderRespType'], type, 'RESULT')  # 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills
        else:
            # delivery and future
            request['newOrderRespType'] = 'RESULT'  # "ACK", "RESULT", default "ACK"
        # additional required fields depending on the order type
        timeInForceIsRequired = False
        priceIsRequired = False
        stopPriceIsRequired = False
        quantityIsRequired = False
        #
        # spot/margin
        #
        #     LIMIT                timeInForce, quantity, price
        #     MARKET               quantity or quoteOrderQty
        #     STOP_LOSS            quantity, stopPrice
        #     STOP_LOSS_LIMIT      timeInForce, quantity, price, stopPrice
        #     TAKE_PROFIT          quantity, stopPrice
        #     TAKE_PROFIT_LIMIT    timeInForce, quantity, price, stopPrice
        #     LIMIT_MAKER          quantity, price
        #
        # futures
        #
        #     LIMIT                timeInForce, quantity, price
        #     MARKET               quantity
        #     STOP/TAKE_PROFIT     quantity, price, stopPrice
        #     STOP_MARKET          stopPrice
        #     TAKE_PROFIT_MARKET   stopPrice
        #     TRAILING_STOP_MARKET callbackRate
        #
        if uppercaseType == 'MARKET':
            if market['spot']:
                quoteOrderQty = self.safe_value(self.options, 'quoteOrderQty', True)
                if quoteOrderQty:
                    quoteOrderQty = self.safe_value_2(query, 'quoteOrderQty', 'cost')
                    precision = market['precision']['price']
                    if quoteOrderQty is not None:
                        request['quoteOrderQty'] = self.decimal_to_precision(quoteOrderQty, TRUNCATE, precision, self.precisionMode)
                    elif price is not None:
                        amountString = self.number_to_string(amount)
                        priceString = self.number_to_string(price)
                        quoteOrderQuantity = Precise.string_mul(amountString, priceString)
                        request['quoteOrderQty'] = self.decimal_to_precision(quoteOrderQuantity, TRUNCATE, precision, self.precisionMode)
                    else:
                        quantityIsRequired = True
                else:
                    quantityIsRequired = True
            else:
                quantityIsRequired = True
        elif uppercaseType == 'LIMIT':
            priceIsRequired = True
            timeInForceIsRequired = True
            quantityIsRequired = True
        elif (uppercaseType == 'STOP_LOSS') or (uppercaseType == 'TAKE_PROFIT'):
            stopPriceIsRequired = True
            quantityIsRequired = True
            if market['linear'] or market['inverse']:
                priceIsRequired = True
        elif (uppercaseType == 'STOP_LOSS_LIMIT') or (uppercaseType == 'TAKE_PROFIT_LIMIT'):
            quantityIsRequired = True
            stopPriceIsRequired = True
            priceIsRequired = True
            timeInForceIsRequired = True
        elif uppercaseType == 'LIMIT_MAKER':
            priceIsRequired = True
            quantityIsRequired = True
        elif uppercaseType == 'STOP':
            quantityIsRequired = True
            stopPriceIsRequired = True
            priceIsRequired = True
        elif (uppercaseType == 'STOP_MARKET') or (uppercaseType == 'TAKE_PROFIT_MARKET'):
            closePosition = self.safe_value(query, 'closePosition')
            if closePosition is None:
                quantityIsRequired = True
            stopPriceIsRequired = True
        elif uppercaseType == 'TRAILING_STOP_MARKET':
            quantityIsRequired = True
            callbackRate = self.safe_number(query, 'callbackRate')
            if callbackRate is None:
                raise InvalidOrder(self.id + ' createOrder() requires a callbackRate extra param for a ' + type + ' order')
        if quantityIsRequired:
            request['quantity'] = self.amount_to_precision(symbol, amount)
        if priceIsRequired:
            if price is None:
                raise InvalidOrder(self.id + ' createOrder() requires a price argument for a ' + type + ' order')
            request['price'] = self.price_to_precision(symbol, price)
        if timeInForceIsRequired:
            request['timeInForce'] = self.options['defaultTimeInForce']  # 'GTC' = Good To Cancel(default), 'IOC' = Immediate Or Cancel
        if stopPriceIsRequired:
            if stopPrice is None:
                raise InvalidOrder(self.id + ' createOrder() requires a stopPrice extra param for a ' + type + ' order')
            else:
                request['stopPrice'] = self.price_to_precision(symbol, stopPrice)
        requestParams = self.omit(params, ['quoteOrderQty', 'cost', 'stopPrice', 'test', 'type', 'newClientOrderId', 'clientOrderId', 'postOnly'])
        response = getattr(self, method)(self.extend(request, requestParams))
        return self.parse_order(response, market)

    def fetch_order(self, id, symbol=None, params={}):
        """
        fetches information on an order made by the user
        :param str symbol: unified symbol of the market the order was made in
        :param dict params: extra parameters specific to the binance api endpoint
        :param str|None params['marginMode']: 'cross' or 'isolated', for spot margin trading
        :returns dict: An `order structure <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrder() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        defaultType = self.safe_string_2(self.options, 'fetchOrder', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        marginMode, query = self.handle_margin_mode_and_params('fetchOrder', params)
        request = {
            'symbol': market['id'],
        }
        method = 'privateGetOrder'
        if type == 'future':
            method = 'fapiPrivateGetOrder'
        elif type == 'delivery':
            method = 'dapiPrivateGetOrder'
        elif type == 'margin' or marginMode is not None:
            method = 'sapiGetMarginOrder'
            if marginMode == 'isolated':
                request['isIsolated'] = True
        clientOrderId = self.safe_value_2(params, 'origClientOrderId', 'clientOrderId')
        if clientOrderId is not None:
            request['origClientOrderId'] = clientOrderId
        else:
            request['orderId'] = id
        requestParams = self.omit(query, ['type', 'clientOrderId', 'origClientOrderId'])
        response = getattr(self, method)(self.extend(request, requestParams))
        return self.parse_order(response, market)

    def fetch_orders(self, symbol=None, since=None, limit=None, params={}):
        """
        fetches information on multiple orders made by the user
        :param str symbol: unified market symbol of the market orders were made in
        :param int|None since: the earliest time in ms to fetch orders for
        :param int|None limit: the maximum number of  orde structures to retrieve
        :param dict params: extra parameters specific to the binance api endpoint
        :param str|None params['marginMode']: 'cross' or 'isolated', for spot margin trading
        :returns [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrders() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        defaultType = self.safe_string_2(self.options, 'fetchOrders', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        marginMode, query = self.handle_margin_mode_and_params('fetchOrders', params)
        request = {
            'symbol': market['id'],
        }
        method = 'privateGetAllOrders'
        if type == 'future':
            method = 'fapiPrivateGetAllOrders'
        elif type == 'delivery':
            method = 'dapiPrivateGetAllOrders'
        elif type == 'margin' or marginMode is not None:
            method = 'sapiGetMarginAllOrders'
            if marginMode == 'isolated':
                request['isIsolated'] = True
        if since is not None:
            request['startTime'] = since
        if limit is not None:
            request['limit'] = limit
        requestParams = self.omit(query, ['type'])
        response = getattr(self, method)(self.extend(request, requestParams))
        #
        #  spot
        #
        #     [
        #         {
        #             "symbol": "LTCBTC",
        #             "orderId": 1,
        #             "clientOrderId": "myOrder1",
        #             "price": "0.1",
        #             "origQty": "1.0",
        #             "executedQty": "0.0",
        #             "cummulativeQuoteQty": "0.0",
        #             "status": "NEW",
        #             "timeInForce": "GTC",
        #             "type": "LIMIT",
        #             "side": "BUY",
        #             "stopPrice": "0.0",
        #             "icebergQty": "0.0",
        #             "time": 1499827319559,
        #             "updateTime": 1499827319559,
        #             "isWorking": True
        #         }
        #     ]
        #
        #  futures
        #
        #     [
        #         {
        #             "symbol": "BTCUSDT",
        #             "orderId": 1,
        #             "clientOrderId": "myOrder1",
        #             "price": "0.1",
        #             "origQty": "1.0",
        #             "executedQty": "1.0",
        #             "cumQuote": "10.0",
        #             "status": "NEW",
        #             "timeInForce": "GTC",
        #             "type": "LIMIT",
        #             "side": "BUY",
        #             "stopPrice": "0.0",
        #             "updateTime": 1499827319559
        #         }
        #     ]
        #
        return self.parse_orders(response, market, since, limit)

    def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        """
        fetch all unfilled currently open orders
        :param str|None symbol: unified market symbol
        :param int|None since: the earliest time in ms to fetch open orders for
        :param int|None limit: the maximum number of  open orders structures to retrieve
        :param dict params: extra parameters specific to the binance api endpoint
        :param str|None params['marginMode']: 'cross' or 'isolated', for spot margin trading
        :returns [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        self.load_markets()
        market = None
        type = None
        request = {}
        marginMode, query = self.handle_margin_mode_and_params('fetchOpenOrders', params)
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
            defaultType = self.safe_string_2(self.options, 'fetchOpenOrders', 'defaultType', 'spot')
            marketType = market['type'] if ('type' in market) else defaultType
            type = self.safe_string(query, 'type', marketType)
        elif self.options['warnOnFetchOpenOrdersWithoutSymbol']:
            symbols = self.symbols
            numSymbols = len(symbols)
            fetchOpenOrdersRateLimit = int(numSymbols / 2)
            raise ExchangeError(self.id + ' fetchOpenOrders() WARNING: fetching open orders without specifying a symbol is rate-limited to one call per ' + str(fetchOpenOrdersRateLimit) + ' seconds. Do not call self method frequently to avoid ban. Set ' + self.id + '.options["warnOnFetchOpenOrdersWithoutSymbol"] = False to suppress self warning message.')
        else:
            defaultType = self.safe_string_2(self.options, 'fetchOpenOrders', 'defaultType', 'spot')
            type = self.safe_string(query, 'type', defaultType)
        requestParams = self.omit(query, 'type')
        method = 'privateGetOpenOrders'
        if type == 'future':
            method = 'fapiPrivateGetOpenOrders'
        elif type == 'delivery':
            method = 'dapiPrivateGetOpenOrders'
        elif type == 'margin' or marginMode is not None:
            method = 'sapiGetMarginOpenOrders'
            if marginMode == 'isolated':
                request['isIsolated'] = True
                if symbol is None:
                    raise ArgumentsRequired(self.id + ' fetchOpenOrders() requires a symbol argument for isolated markets')
        response = getattr(self, method)(self.extend(request, requestParams))
        return self.parse_orders(response, market, since, limit)

    def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
        """
        fetches information on multiple closed orders made by the user
        :param str symbol: unified market symbol of the market orders were made in
        :param int|None since: the earliest time in ms to fetch orders for
        :param int|None limit: the maximum number of  orde structures to retrieve
        :param dict params: extra parameters specific to the binance api endpoint
        :returns [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        orders = self.fetch_orders(symbol, since, limit, params)
        return self.filter_by(orders, 'status', 'closed')

    def cancel_order(self, id, symbol=None, params={}):
        """
        cancels an open order
        :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 binance api endpoint
        :returns dict: An `order structure <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        defaultType = self.safe_string_2(self.options, 'cancelOrder', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        # https://github.com/ccxt/ccxt/issues/6507
        origClientOrderId = self.safe_value_2(params, 'origClientOrderId', 'clientOrderId')
        marginMode, query = self.handle_margin_mode_and_params('cancelOrder', params)
        request = {
            'symbol': market['id'],
            # 'orderId': id,
            # 'origClientOrderId': id,
        }
        if origClientOrderId is None:
            request['orderId'] = id
        else:
            request['origClientOrderId'] = origClientOrderId
        method = 'privateDeleteOrder'
        if type == 'future':
            method = 'fapiPrivateDeleteOrder'
        elif type == 'delivery':
            method = 'dapiPrivateDeleteOrder'
        elif type == 'margin' or marginMode is not None:
            method = 'sapiDeleteMarginOrder'
            if marginMode == 'isolated':
                request['isIsolated'] = True
                if symbol is None:
                    raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument for isolated markets')
        requestParams = self.omit(query, ['type', 'origClientOrderId', 'clientOrderId'])
        response = getattr(self, method)(self.extend(request, requestParams))
        return self.parse_order(response, market)

    def cancel_all_orders(self, symbol=None, params={}):
        """
        cancel all open orders in a market
        :param str symbol: unified market symbol of the market to cancel orders in
        :param dict params: extra parameters specific to the binance api endpoint
        :param str|None params['marginMode']: 'cross' or 'isolated', for spot margin trading
        :returns [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelAllOrders() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        defaultType = self.safe_string_2(self.options, 'cancelAllOrders', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        params = self.omit(params, ['type'])
        marginMode, query = self.handle_margin_mode_and_params('cancelAllOrders', params)
        method = 'privateDeleteOpenOrders'
        if type == 'future':
            method = 'fapiPrivateDeleteAllOpenOrders'
        elif type == 'delivery':
            method = 'dapiPrivateDeleteAllOpenOrders'
        elif type == 'margin' or marginMode is not None:
            method = 'sapiDeleteMarginOpenOrders'
            if marginMode == 'isolated':
                request['isIsolated'] = True
        response = getattr(self, method)(self.extend(request, query))
        if isinstance(response, list):
            return self.parse_orders(response, market)
        else:
            return response

    def fetch_order_trades(self, id, symbol=None, since=None, limit=None, params={}):
        """
        fetch all the trades made from a single order
        :param str id: order id
        :param str symbol: unified market symbol
        :param int|None since: the earliest time in ms to fetch trades for
        :param int|None limit: the maximum number of trades to retrieve
        :param dict params: extra parameters specific to the binance api endpoint
        :returns [dict]: a list of `trade structures <https://docs.ccxt.com/en/latest/manual.html#trade-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrderTrades() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        type = self.safe_string(params, 'type', market['type'])
        params = self.omit(params, 'type')
        if type != 'spot':
            raise NotSupported(self.id + ' fetchOrderTrades() supports spot markets only')
        request = {
            'orderId': id,
        }
        return self.fetch_my_trades(symbol, since, limit, self.extend(request, params))

    def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
        """
        fetch all trades made by the user
        :param str symbol: unified market symbol
        :param int|None since: the earliest time in ms to fetch trades for
        :param int|None limit: the maximum number of trades structures to retrieve
        :param dict params: extra parameters specific to the binance api endpoint
        :returns [dict]: a list of `trade structures <https://docs.ccxt.com/en/latest/manual.html#trade-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchMyTrades() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        type = self.safe_string(params, 'type', market['type'])
        params = self.omit(params, 'type')
        method = None
        linear = (type == 'future')
        inverse = (type == 'delivery')
        marginMode = None
        marginMode, params = self.handle_margin_mode_and_params('fetchMyTrades', params)
        if type == 'spot' or type == 'margin':
            method = 'privateGetMyTrades'
            if (type == 'margin') or (marginMode is not None):
                method = 'sapiGetMarginMyTrades'
                if marginMode == 'isolated':
                    request['isIsolated'] = True
        elif linear:
            method = 'fapiPrivateGetUserTrades'
        elif inverse:
            method = 'dapiPrivateGetUserTrades'
        endTime = self.safe_integer_2(params, 'until', 'endTime')
        if since is not None:
            startTime = int(since)
            request['startTime'] = startTime
            # https://binance-docs.github.io/apidocs/futures/en/#account-trade-list-user_data
            # If startTime and endTime are both not sent, then the last 7 days' data will be returned.
            # The time between startTime and endTime cannot be longer than 7 days.
            # The parameter fromId cannot be sent with startTime or endTime.
            currentTimestamp = self.milliseconds()
            oneWeek = 7 * 24 * 60 * 60 * 1000
            if (currentTimestamp - startTime) >= oneWeek:
                if (endTime is None) and linear:
                    endTime = self.sum(startTime, oneWeek)
                    endTime = min(endTime, currentTimestamp)
        if endTime is not None:
            request['endTime'] = endTime
            params = self.omit(params, ['endTime', 'until'])
        if limit is not None:
            if type == 'future' or type == 'delivery':
                limit = min(limit, 1000)  # above 1000, returns error
            request['limit'] = limit
        response = getattr(self, method)(self.extend(request, params))
        #
        # spot trade
        #
        #     [
        #         {
        #             "symbol": "BNBBTC",
        #             "id": 28457,
        #             "orderId": 100234,
        #             "price": "4.00000100",
        #             "qty": "12.00000000",
        #             "commission": "10.10000000",
        #             "commissionAsset": "BNB",
        #             "time": 1499865549590,
        #             "isBuyer": True,
        #             "isMaker": False,
        #             "isBestMatch": True,
        #         }
        #     ]
        #
        # futures trade
        #
        #     [
        #         {
        #             "accountId": 20,
        #             "buyer": False,
        #             "commission": "-0.07819010",
        #             "commissionAsset": "USDT",
        #             "counterPartyId": 653,
        #             "id": 698759,
        #             "maker": False,
        #             "orderId": 25851813,
        #             "price": "7819.01",
        #             "qty": "0.002",
        #             "quoteQty": "0.01563",
        #             "realizedPnl": "-0.91539999",
        #             "side": "SELL",
        #             "symbol": "BTCUSDT",
        #             "time": 1569514978020
        #         }
        #     ]
        #
        return self.parse_trades(response, market, since, limit)

    def fetch_my_dust_trades(self, symbol=None, since=None, limit=None, params={}):
        """
        fetch all dust trades made by the user
        :param str|None symbol: not used by binance fetchMyDustTrades()
        :param int|None since: the earliest time in ms to fetch my dust trades for
        :param int|None limit: the maximum number of dust trades to retrieve
        :param dict params: extra parameters specific to the binance api endpoint
        :returns [dict]: a list of `trade structures <https://docs.ccxt.com/en/latest/manual.html#trade-structure>`
        """
        #
        # Binance provides an opportunity to trade insignificant(i.e. non-tradable and non-withdrawable)
        # token leftovers(of any asset) into `BNB` coin which in turn can be used to pay trading fees with it.
        # The corresponding trades history is called the `Dust Log` and can be requested via the following end-point:
        # https://github.com/binance-exchange/binance-official-api-docs/blob/master/wapi-api.md#dustlog-user_data
        #
        self.load_markets()
        request = {}
        if since is not None:
            request['startTime'] = since
            request['endTime'] = self.sum(since, 7776000000)
        response = self.sapiGetAssetDribblet(self.extend(request, params))
        #     {
        #       "total": "4",
        #       "userAssetDribblets": [
        #         {
        #           "operateTime": "1627575731000",
        #           "totalServiceChargeAmount": "0.00001453",
        #           "totalTransferedAmount": "0.00072693",
        #           "transId": "70899815863",
        #           "userAssetDribbletDetails": [
        #             {
        #               "fromAsset": "LTC",
        #               "amount": "0.000006",
        #               "transferedAmount": "0.00000267",
        #               "serviceChargeAmount": "0.00000005",
        #               "operateTime": "1627575731000",
        #               "transId": "70899815863"
        #             },
        #             {
        #               "fromAsset": "GBP",
        #               "amount": "0.15949157",
        #               "transferedAmount": "0.00072426",
        #               "serviceChargeAmount": "0.00001448",
        #               "operateTime": "1627575731000",
        #               "transId": "70899815863"
        #             }
        #           ]
        #         },
        #       ]
        #     }
        results = self.safe_value(response, 'userAssetDribblets', [])
        rows = self.safe_integer(response, 'total', 0)
        data = []
        for i in range(0, rows):
            logs = self.safe_value(results[i], 'userAssetDribbletDetails', [])
            for j in range(0, len(logs)):
                logs[j]['isDustTrade'] = True
                data.append(logs[j])
        trades = self.parse_trades(data, None, since, limit)
        return self.filter_by_since_limit(trades, since, limit)

    def parse_dust_trade(self, trade, market=None):
        #
        #     {
        #       "fromAsset": "USDT",
        #       "amount": "0.009669",
        #       "transferedAmount": "0.00002992",
        #       "serviceChargeAmount": "0.00000059",
        #       "operateTime": "1628076010000",
        #       "transId": "71416578712",
        #       "isDustTrade": True
        #     }
        #
        orderId = self.safe_string(trade, 'transId')
        timestamp = self.safe_integer(trade, 'operateTime')
        currencyId = self.safe_string(trade, 'fromAsset')
        tradedCurrency = self.safe_currency_code(currencyId)
        bnb = self.currency('BNB')
        earnedCurrency = bnb['code']
        applicantSymbol = earnedCurrency + '/' + tradedCurrency
        tradedCurrencyIsQuote = False
        if applicantSymbol in self.markets:
            tradedCurrencyIsQuote = True
        feeCostString = self.safe_string(trade, 'serviceChargeAmount')
        fee = {
            'currency': earnedCurrency,
            'cost': self.parse_number(feeCostString),
        }
        symbol = None
        amountString = None
        costString = None
        side = None
        if tradedCurrencyIsQuote:
            symbol = applicantSymbol
            amountString = self.safe_string(trade, 'transferedAmount')
            costString = self.safe_string(trade, 'amount')
            side = 'buy'
        else:
            symbol = tradedCurrency + '/' + earnedCurrency
            amountString = self.safe_string(trade, 'amount')
            costString = self.safe_string(trade, 'transferedAmount')
            side = 'sell'
        priceString = None
        if costString is not None:
            if amountString:
                priceString = Precise.string_div(costString, amountString)
        id = None
        amount = self.parse_number(amountString)
        price = self.parse_number(priceString)
        cost = self.parse_number(costString)
        type = None
        takerOrMaker = None
        return {
            'id': id,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'order': orderId,
            'type': type,
            'takerOrMaker': takerOrMaker,
            'side': side,
            'amount': amount,
            'price': price,
            'cost': cost,
            'fee': fee,
            'info': trade,
        }

    def fetch_deposits(self, code=None, since=None, limit=None, params={}):
        """
        fetch all deposits made to an account
        :param str|None code: unified currency code
        :param int|None since: the earliest time in ms to fetch deposits for
        :param int|None limit: the maximum number of deposits structures to retrieve
        :param dict params: extra parameters specific to the binance api endpoint
        :param int|None params['until']: the latest time in ms to fetch deposits for
        :returns [dict]: a list of `transaction structures <https://docs.ccxt.com/en/latest/manual.html#transaction-structure>`
        """
        self.load_markets()
        currency = None
        response = None
        request = {}
        legalMoney = self.safe_value(self.options, 'legalMoney', {})
        until = self.safe_integer(params, 'until')
        if code in legalMoney:
            if code is not None:
                currency = self.currency(code)
            request['transactionType'] = 0
            if since is not None:
                request['beginTime'] = since
            if until is not None:
                request['endTime'] = until
            raw = self.sapiGetFiatOrders(self.extend(request, params))
            response = self.safe_value(raw, 'data')
            #     {
            #       "code": "000000",
            #       "message": "success",
            #       "data": [
            #         {
            #           "orderNo": "25ced37075c1470ba8939d0df2316e23",
            #           "fiatCurrency": "EUR",
            #           "indicatedAmount": "15.00",
            #           "amount": "15.00",
            #           "totalFee": "0.00",
            #           "method": "card",
            #           "status": "Failed",
            #           "createTime": 1627501026000,
            #           "updateTime": 1627501027000
            #         }
            #       ],
            #       "total": 1,
            #       "success": True
            #     }
        else:
            if code is not None:
                currency = self.currency(code)
                request['coin'] = currency['id']
            if since is not None:
                request['startTime'] = since
                # max 3 months range https://github.com/ccxt/ccxt/issues/6495
                endTime = self.sum(since, 7776000000)
                if until is not None:
                    endTime = min(endTime, until)
                request['endTime'] = endTime
            if limit is not None:
                request['limit'] = limit
            response = self.sapiGetCapitalDepositHisrec(self.extend(request, params))
            #     [
            #       {
            #         "amount": "0.01844487",
            #         "coin": "BCH",
            #         "network": "BCH",
            #         "status": 1,
            #         "address": "1NYxAJhW2281HK1KtJeaENBqHeygA88FzR",
            #         "addressTag": "",
            #         "txId": "bafc5902504d6504a00b7d0306a41154cbf1d1b767ab70f3bc226327362588af",
            #         "insertTime": 1610784980000,
            #         "transferType": 0,
            #         "confirmTimes": "2/2"
            #       },
            #       {
            #         "amount": "4500",
            #         "coin": "USDT",
            #         "network": "BSC",
            #         "status": 1,
            #         "address": "0xc9c923c87347ca0f3451d6d308ce84f691b9f501",
            #         "addressTag": "",
            #         "txId": "Internal transfer 51376627901",
            #         "insertTime": 1618394381000,
            #         "transferType": 1,
            #         "confirmTimes": "1/15"
            #     }
            #   ]
        return self.parse_transactions(response, currency, since, limit)

    def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
        """
        fetch all withdrawals made from an account
        :param str|None code: unified currency code
        :param int|None since: the earliest time in ms to fetch withdrawals for
        :param int|None limit: the maximum number of withdrawals structures to retrieve
        :param dict params: extra parameters specific to the binance api endpoint
        :returns [dict]: a list of `transaction structures <https://docs.ccxt.com/en/latest/manual.html#transaction-structure>`
        """
        self.load_markets()
        legalMoney = self.safe_value(self.options, 'legalMoney', {})
        request = {}
        response = None
        currency = None
        if code in legalMoney:
            if code is not None:
                currency = self.currency(code)
            request['transactionType'] = 1
            if since is not None:
                request['beginTime'] = since
            raw = self.sapiGetFiatOrders(self.extend(request, params))
            response = self.safe_value(raw, 'data')
            #     {
            #       "code": "000000",
            #       "message": "success",
            #       "data": [
            #         {
            #           "orderNo": "CJW706452266115170304",
            #           "fiatCurrency": "GBP",
            #           "indicatedAmount": "10001.50",
            #           "amount": "100.00",
            #           "totalFee": "1.50",
            #           "method": "bank transfer",
            #           "status": "Successful",
            #           "createTime": 1620037745000,
            #           "updateTime": 1620038480000
            #         },
            #         {
            #           "orderNo": "CJW706287492781891584",
            #           "fiatCurrency": "GBP",
            #           "indicatedAmount": "10001.50",
            #           "amount": "100.00",
            #           "totalFee": "1.50",
            #           "method": "bank transfer",
            #           "status": "Successful",
            #           "createTime": 1619998460000,
            #           "updateTime": 1619998823000
            #         }
            #       ],
            #       "total": 39,
            #       "success": True
            #     }
        else:
            if code is not None:
                currency = self.currency(code)
                request['coin'] = currency['id']
            if since is not None:
                request['startTime'] = since
                # max 3 months range https://github.com/ccxt/ccxt/issues/6495
                request['endTime'] = self.sum(since, 7776000000)
            if limit is not None:
                request['limit'] = limit
            response = self.sapiGetCapitalWithdrawHistory(self.extend(request, params))
            #     [
            #       {
            #         "id": "69e53ad305124b96b43668ceab158a18",
            #         "amount": "28.75",
            #         "transactionFee": "0.25",
            #         "coin": "XRP",
            #         "status": 6,
            #         "address": "r3T75fuLjX51mmfb5Sk1kMNuhBgBPJsjza",
            #         "addressTag": "101286922",
            #         "txId": "19A5B24ED0B697E4F0E9CD09FCB007170A605BC93C9280B9E6379C5E6EF0F65A",
            #         "applyTime": "2021-04-15 12:09:16",
            #         "network": "XRP",
            #         "transferType": 0
            #       },
            #       {
            #         "id": "9a67628b16ba4988ae20d329333f16bc",
            #         "amount": "20",
            #         "transactionFee": "20",
            #         "coin": "USDT",
            #         "status": 6,
            #         "address": "0x0AB991497116f7F5532a4c2f4f7B1784488628e1",
            #         "txId": "0x77fbf2cf2c85b552f0fd31fd2e56dc95c08adae031d96f3717d8b17e1aea3e46",
            #         "applyTime": "2021-04-15 12:06:53",
            #         "network": "ETH",
            #         "transferType": 0
            #       },
            #       {
            #         "id": "a7cdc0afbfa44a48bd225c9ece958fe2",
            #         "amount": "51",
            #         "transactionFee": "1",
            #         "coin": "USDT",
            #         "status": 6,
            #         "address": "TYDmtuWL8bsyjvcauUTerpfYyVhFtBjqyo",
            #         "txId": "168a75112bce6ceb4823c66726ad47620ad332e69fe92d9cb8ceb76023f9a028",
            #         "applyTime": "2021-04-13 12:46:59",
            #         "network": "TRX",
            #         "transferType": 0
            #       }
            #     ]
        return self.parse_transactions(response, currency, since, limit)

    def parse_transaction_status_by_type(self, status, type=None):
        statusesByType = {
            'deposit': {
                '0': 'pending',
                '1': 'ok',
                # Fiat
                # Processing, Failed, Successful, Finished, Refunding, Refunded, Refund Failed, Order Partial credit Stopped
                'Processing': 'pending',
                'Failed': 'failed',
                'Successful': 'ok',
                'Refunding': 'canceled',
                'Refunded': 'canceled',
                'Refund Failed': 'failed',
            },
            'withdrawal': {
                '0': 'pending',  # Email Sent
                '1': 'canceled',  # Cancelled(different from 1 = ok in deposits)
                '2': 'pending',  # Awaiting Approval
                '3': 'failed',  # Rejected
                '4': 'pending',  # Processing
                '5': 'failed',  # Failure
                '6': 'ok',  # Completed
                # Fiat
                # Processing, Failed, Successful, Finished, Refunding, Refunded, Refund Failed, Order Partial credit Stopped
                'Processing': 'pending',
                'Failed': 'failed',
                'Successful': 'ok',
                'Refunding': 'canceled',
                'Refunded': 'canceled',
                'Refund Failed': 'failed',
            },
        }
        statuses = self.safe_value(statusesByType, type, {})
        return self.safe_string(statuses, status, status)

    def parse_transaction(self, transaction, currency=None):
        #
        # fetchDeposits
        #
        #     {
        #       "amount": "4500",
        #       "coin": "USDT",
        #       "network": "BSC",
        #       "status": 1,
        #       "address": "0xc9c923c87347ca0f3451d6d308ce84f691b9f501",
        #       "addressTag": "",
        #       "txId": "Internal transfer 51376627901",
        #       "insertTime": 1618394381000,
        #       "transferType": 1,
        #       "confirmTimes": "1/15"
        #     }
        #
        # fetchWithdrawals
        #
        #     {
        #       "id": "69e53ad305124b96b43668ceab158a18",
        #       "amount": "28.75",
        #       "transactionFee": "0.25",
        #       "coin": "XRP",
        #       "status": 6,
        #       "address": "r3T75fuLjX51mmfb5Sk1kMNuhBgBPJsjza",
        #       "addressTag": "101286922",
        #       "txId": "19A5B24ED0B697E4F0E9CD09FCB007170A605BC93C9280B9E6379C5E6EF0F65A",
        #       "applyTime": "2021-04-15 12:09:16",
        #       "network": "XRP",
        #       "transferType": 0
        #     }
        #
        # fiat transaction
        # withdraw
        #     {
        #       "orderNo": "CJW684897551397171200",
        #       "fiatCurrency": "GBP",
        #       "indicatedAmount": "29.99",
        #       "amount": "28.49",
        #       "totalFee": "1.50",
        #       "method": "bank transfer",
        #       "status": "Successful",
        #       "createTime": 1614898701000,
        #       "updateTime": 1614898820000
        #     }
        #
        # deposit
        #     {
        #       "orderNo": "25ced37075c1470ba8939d0df2316e23",
        #       "fiatCurrency": "EUR",
        #       "indicatedAmount": "15.00",
        #       "amount": "15.00",
        #       "totalFee": "0.00",
        #       "method": "card",
        #       "status": "Failed",
        #       "createTime": "1627501026000",
        #       "updateTime": "1627501027000"
        #     }
        #
        # withdraw
        #
        #    {id: '9a67628b16ba4988ae20d329333f16bc'}
        #
        id = self.safe_string_2(transaction, 'id', 'orderNo')
        address = self.safe_string(transaction, 'address')
        tag = self.safe_string(transaction, 'addressTag')  # set but unused
        if tag is not None:
            if len(tag) < 1:
                tag = None
        txid = self.safe_string(transaction, 'txId')
        if (txid is not None) and (txid.find('Internal transfer ') >= 0):
            txid = txid[18:]
        currencyId = self.safe_string_2(transaction, 'coin', 'fiatCurrency')
        code = self.safe_currency_code(currencyId, currency)
        timestamp = None
        insertTime = self.safe_integer_2(transaction, 'insertTime', 'createTime')
        applyTime = self.parse8601(self.safe_string(transaction, 'applyTime'))
        type = self.safe_string(transaction, 'type')
        if type is None:
            if (insertTime is not None) and (applyTime is None):
                type = 'deposit'
                timestamp = insertTime
            elif (insertTime is None) and (applyTime is not None):
                type = 'withdrawal'
                timestamp = applyTime
        status = self.parse_transaction_status_by_type(self.safe_string(transaction, 'status'), type)
        amount = self.safe_number(transaction, 'amount')
        feeCost = self.safe_number_2(transaction, 'transactionFee', 'totalFee')
        fee = None
        if feeCost is not None:
            fee = {'currency': code, 'cost': feeCost}
        updated = self.safe_integer_2(transaction, 'successTime', 'updateTime')
        internal = self.safe_integer(transaction, 'transferType')
        if internal is not None:
            internal = True if internal else False
        network = self.safe_string(transaction, 'network')
        return {
            'info': transaction,
            'id': id,
            'txid': txid,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'network': network,
            'address': address,
            'addressTo': address,
            'addressFrom': None,
            'tag': tag,
            'tagTo': tag,
            'tagFrom': None,
            'type': type,
            'amount': amount,
            'currency': code,
            'status': status,
            'updated': updated,
            'internal': internal,
            'fee': fee,
        }

    def parse_transfer_status(self, status):
        statuses = {
            'CONFIRMED': 'ok',
        }
        return self.safe_string(statuses, status, status)

    def parse_transfer(self, transfer, currency=None):
        #
        # transfer
        #
        #     {
        #         "tranId":13526853623
        #     }
        #
        # fetchTransfers
        #
        #     {
        #         timestamp: 1614640878000,
        #         asset: 'USDT',
        #         amount: '25',
        #         type: 'MAIN_UMFUTURE',
        #         status: 'CONFIRMED',
        #         tranId: 43000126248
        #     }
        #
        id = self.safe_string(transfer, 'tranId')
        currencyId = self.safe_string(transfer, 'asset')
        code = self.safe_currency_code(currencyId, currency)
        amount = self.safe_number(transfer, 'amount')
        type = self.safe_string(transfer, 'type')
        fromAccount = None
        toAccount = None
        accountsById = self.safe_value(self.options, 'accountsById', {})
        if type is not None:
            parts = type.split('_')
            fromAccount = self.safe_value(parts, 0)
            toAccount = self.safe_value(parts, 1)
            fromAccount = self.safe_string(accountsById, fromAccount, fromAccount)
            toAccount = self.safe_string(accountsById, toAccount, toAccount)
        timestamp = self.safe_integer(transfer, 'timestamp')
        status = self.parse_transfer_status(self.safe_string(transfer, 'status'))
        return {
            'info': transfer,
            'id': id,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'currency': code,
            'amount': amount,
            'fromAccount': fromAccount,
            'toAccount': toAccount,
            'status': status,
        }

    def parse_income(self, income, market=None):
        #
        #     {
        #       "symbol": "ETHUSDT",
        #       "incomeType": "FUNDING_FEE",
        #       "income": "0.00134317",
        #       "asset": "USDT",
        #       "time": "1621584000000",
        #       "info": "FUNDING_FEE",
        #       "tranId": "4480321991774044580",
        #       "tradeId": ""
        #     }
        #
        marketId = self.safe_string(income, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        amount = self.safe_number(income, 'income')
        currencyId = self.safe_string(income, 'asset')
        code = self.safe_currency_code(currencyId)
        id = self.safe_string(income, 'tranId')
        timestamp = self.safe_integer(income, 'time')
        return {
            'info': income,
            'symbol': symbol,
            'code': code,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'id': id,
            'amount': amount,
        }

    def parse_incomes(self, incomes, market=None, since=None, limit=None):
        result = []
        for i in range(0, len(incomes)):
            entry = incomes[i]
            parsed = self.parse_income(entry, market)
            result.append(parsed)
        sorted = self.sort_by(result, 'timestamp')
        return self.filter_by_since_limit(sorted, since, limit)

    def transfer(self, code, amount, fromAccount, toAccount, params={}):
        """
        transfer currency internally between wallets on the same account
        see https://binance-docs.github.io/apidocs/spot/en/#user-universal-transfer-user_data
        see https://binance-docs.github.io/apidocs/spot/en/#isolated-margin-account-transfer-margin
        :param str code: unified currency code
        :param float amount: amount to transfer
        :param str fromAccount: account to transfer from
        :param str toAccount: account to transfer to
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: a `transfer structure <https://docs.ccxt.com/en/latest/manual.html#transfer-structure>`
        """
        self.load_markets()
        currency = self.currency(code)
        request = {
            'asset': currency['id'],
            'amount': self.currency_to_precision(code, amount),
        }
        request['type'] = self.safe_string(params, 'type')
        method = 'sapiPostAssetTransfer'
        if request['type'] is None:
            symbol = self.safe_string(params, 'symbol')
            if symbol is not None:
                params = self.omit(params, 'symbol')
            fromId = self.convert_type_to_account(fromAccount).upper()
            toId = self.convert_type_to_account(toAccount).upper()
            if fromId == 'ISOLATED':
                if symbol is None:
                    raise ArgumentsRequired(self.id + ' transfer() requires params["symbol"] when fromAccount is ' + fromAccount)
                else:
                    fromId = self.market_id(symbol)
            if toId == 'ISOLATED':
                if symbol is None:
                    raise ArgumentsRequired(self.id + ' transfer() requires params["symbol"] when toAccount is ' + toAccount)
                else:
                    toId = self.market_id(symbol)
            fromIsolated = self.in_array(fromId, self.ids)
            toIsolated = self.in_array(toId, self.ids)
            if fromIsolated or toIsolated:  # Isolated margin transfer
                fromFuture = fromId == 'UMFUTURE' or fromId == 'CMFUTURE'
                toFuture = toId == 'UMFUTURE' or toId == 'CMFUTURE'
                fromSpot = fromId == 'MAIN'
                toSpot = toId == 'MAIN'
                funding = fromId == 'FUNDING' or toId == 'FUNDING'
                mining = fromId == 'MINING' or toId == 'MINING'
                prohibitedWithIsolated = fromFuture or toFuture or mining or funding
                if (fromIsolated or toIsolated) and prohibitedWithIsolated:
                    raise BadRequest(self.id + ' transfer() does not allow transfers between ' + fromAccount + ' and ' + toAccount)
                elif fromIsolated and toSpot:
                    method = 'sapiPostMarginIsolatedTransfer'
                    request['transFrom'] = 'ISOLATED_MARGIN'
                    request['transTo'] = 'SPOT'
                    request['symbol'] = fromId
                elif fromSpot and toIsolated:
                    method = 'sapiPostMarginIsolatedTransfer'
                    request['transFrom'] = 'SPOT'
                    request['transTo'] = 'ISOLATED_MARGIN'
                    request['symbol'] = toId
                else:
                    if self.in_array(fromId, self.ids):
                        request['fromSymbol'] = fromId
                        fromId = 'ISOLATEDMARGIN'
                    if self.in_array(toId, self.ids):
                        request['toSymbol'] = toId
                        toId = 'ISOLATEDMARGIN'
                    request['type'] = fromId + '_' + toId
            else:
                request['type'] = fromId + '_' + toId
        params = self.omit(params, 'type')
        response = getattr(self, method)(self.extend(request, params))
        #
        #     {
        #         "tranId":13526853623
        #     }
        #
        transfer = self.parse_transfer(response, currency)
        return self.extend(transfer, {
            'amount': amount,
            'currency': code,
            'fromAccount': fromAccount,
            'toAccount': toAccount,
        })

    def fetch_transfers(self, code=None, since=None, limit=None, params={}):
        """
        fetch a history of internal transfers made on an account
        :param str|None code: unified currency code of the currency transferred
        :param int|None since: the earliest time in ms to fetch transfers for
        :param int|None limit: the maximum number of  transfers structures to retrieve
        :param dict params: extra parameters specific to the binance api endpoint
        :returns [dict]: a list of `transfer structures <https://docs.ccxt.com/en/latest/manual.html#transfer-structure>`
        """
        self.load_markets()
        currency = None
        if code is not None:
            currency = self.currency(code)
        defaultType = self.safe_string_2(self.options, 'fetchTransfers', 'defaultType', 'spot')
        fromAccount = self.safe_string(params, 'fromAccount', defaultType)
        defaultTo = 'spot' if (fromAccount == 'future') else 'future'
        toAccount = self.safe_string(params, 'toAccount', defaultTo)
        type = self.safe_string(params, 'type')
        accountsByType = self.safe_value(self.options, 'accountsByType', {})
        fromId = self.safe_string(accountsByType, fromAccount)
        toId = self.safe_string(accountsByType, toAccount)
        if type is None:
            if fromId is None:
                keys = list(accountsByType.keys())
                raise ExchangeError(self.id + ' fromAccount parameter must be one of ' + ', '.join(keys))
            if toId is None:
                keys = list(accountsByType.keys())
                raise ExchangeError(self.id + ' toAccount parameter must be one of ' + ', '.join(keys))
            type = fromId + '_' + toId
        request = {
            'type': type,
        }
        if since is not None:
            request['startTime'] = since
        if limit is not None:
            request['size'] = limit
        response = self.sapiGetAssetTransfer(self.extend(request, params))
        #
        #     {
        #         total: 3,
        #         rows: [
        #             {
        #                 timestamp: 1614640878000,
        #                 asset: 'USDT',
        #                 amount: '25',
        #                 type: 'MAIN_UMFUTURE',
        #                 status: 'CONFIRMED',
        #                 tranId: 43000126248
        #             },
        #         ]
        #     }
        #
        rows = self.safe_value(response, 'rows', [])
        return self.parse_transfers(rows, currency, since, limit)

    def fetch_deposit_address(self, code, params={}):
        """
        fetch the deposit address for a currency associated with self account
        :param str code: unified currency code
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: an `address structure <https://docs.ccxt.com/en/latest/manual.html#address-structure>`
        """
        self.load_markets()
        currency = self.currency(code)
        request = {
            'coin': currency['id'],
            # 'network': 'ETH',  # 'BSC', 'XMR', you can get network and isDefault in networkList in the response of sapiGetCapitalConfigDetail
        }
        networks = self.safe_value(self.options, 'networks', {})
        network = self.safe_string_upper(params, 'network')  # self line allows the user to specify either ERC20 or ETH
        network = self.safe_string(networks, network, network)  # handle ERC20>ETH alias
        if network is not None:
            request['network'] = network
            params = self.omit(params, 'network')
        # has support for the 'network' parameter
        # https://binance-docs.github.io/apidocs/spot/en/#deposit-address-supporting-network-user_data
        response = self.sapiGetCapitalDepositAddress(self.extend(request, params))
        #
        #     {
        #         currency: 'XRP',
        #         address: 'rEb8TK3gBgk5auZkwc6sHnwrGVJH8DuaLh',
        #         tag: '108618262',
        #         info: {
        #             coin: 'XRP',
        #             address: 'rEb8TK3gBgk5auZkwc6sHnwrGVJH8DuaLh',
        #             tag: '108618262',
        #             url: 'https://bithomp.com/explorer/rEb8TK3gBgk5auZkwc6sHnwrGVJH8DuaLh'
        #         }
        #     }
        #
        address = self.safe_string(response, 'address')
        url = self.safe_string(response, 'url')
        impliedNetwork = None
        if url is not None:
            reverseNetworks = self.safe_value(self.options, 'reverseNetworks', {})
            parts = url.split('/')
            topLevel = self.safe_string(parts, 2)
            if (topLevel == 'blockchair.com') or (topLevel == 'viewblock.io'):
                subLevel = self.safe_string(parts, 3)
                if subLevel is not None:
                    topLevel = topLevel + '/' + subLevel
            impliedNetwork = self.safe_string(reverseNetworks, topLevel)
            impliedNetworks = self.safe_value(self.options, 'impliedNetworks', {
                'ETH': {'ERC20': 'ETH'},
                'TRX': {'TRC20': 'TRX'},
            })
            if code in impliedNetworks:
                conversion = self.safe_value(impliedNetworks, code, {})
                impliedNetwork = self.safe_string(conversion, impliedNetwork, impliedNetwork)
        tag = self.safe_string(response, 'tag', '')
        if len(tag) == 0:
            tag = None
        self.check_address(address)
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'network': impliedNetwork,
            'info': response,
        }

    def fetch_transaction_fees(self, codes=None, params={}):
        """
        fetch transaction fees
        :param [str]|None codes: not used by binance fetchTransactionFees()
        :param dict params: extra parameters specific to the binance api endpoint
        :returns [dict]: a list of `fee structures <https://docs.ccxt.com/en/latest/manual.html#fee-structure>`
        """
        self.load_markets()
        response = self.sapiGetCapitalConfigGetall(params)
        #
        #  [
        #     {
        #       coin: 'BAT',
        #       depositAllEnable: True,
        #       withdrawAllEnable: True,
        #       name: 'Basic Attention Token',
        #       free: '0',
        #       locked: '0',
        #       freeze: '0',
        #       withdrawing: '0',
        #       ipoing: '0',
        #       ipoable: '0',
        #       storage: '0',
        #       isLegalMoney: False,
        #       trading: True,
        #       networkList: [
        #         {
        #           network: 'BNB',
        #           coin: 'BAT',
        #           withdrawIntegerMultiple: '0.00000001',
        #           isDefault: False,
        #           depositEnable: True,
        #           withdrawEnable: True,
        #           depositDesc: '',
        #           withdrawDesc: '',
        #           specialTips: 'The name of self asset is Basic Attention Token(BAT). Both a MEMO and an Address are required to successfully deposit your BEP2 tokens to Binance.',
        #           name: 'BEP2',
        #           resetAddressStatus: False,
        #           addressRegex: '^(bnb1)[0-9a-z]{38}$',
        #           memoRegex: '^[0-9A-Za-z\\-_]{1,120}$',
        #           withdrawFee: '0.27',
        #           withdrawMin: '0.54',
        #           withdrawMax: '10000000000',
        #           minConfirm: '1',
        #           unLockConfirm: '0'
        #         },
        #         {
        #           network: 'BSC',
        #           coin: 'BAT',
        #           withdrawIntegerMultiple: '0.00000001',
        #           isDefault: False,
        #           depositEnable: True,
        #           withdrawEnable: True,
        #           depositDesc: '',
        #           withdrawDesc: '',
        #           specialTips: 'The name of self asset is Basic Attention Token. Please ensure you are depositing Basic Attention Token(BAT) tokens under the contract address ending in 9766e.',
        #           name: 'BEP20(BSC)',
        #           resetAddressStatus: False,
        #           addressRegex: '^(0x)[0-9A-Fa-f]{40}$',
        #           memoRegex: '',
        #           withdrawFee: '0.27',
        #           withdrawMin: '0.54',
        #           withdrawMax: '10000000000',
        #           minConfirm: '15',
        #           unLockConfirm: '0'
        #         },
        #         {
        #           network: 'ETH',
        #           coin: 'BAT',
        #           withdrawIntegerMultiple: '0.00000001',
        #           isDefault: True,
        #           depositEnable: True,
        #           withdrawEnable: True,
        #           depositDesc: '',
        #           withdrawDesc: '',
        #           specialTips: 'The name of self asset is Basic Attention Token. Please ensure you are depositing Basic Attention Token(BAT) tokens under the contract address ending in 887ef.',
        #           name: 'ERC20',
        #           resetAddressStatus: False,
        #           addressRegex: '^(0x)[0-9A-Fa-f]{40}$',
        #           memoRegex: '',
        #           withdrawFee: '27',
        #           withdrawMin: '54',
        #           withdrawMax: '10000000000',
        #           minConfirm: '12',
        #           unLockConfirm: '0'
        #         }
        #       ]
        #     }
        #  ]
        #
        withdrawFees = {}
        for i in range(0, len(response)):
            entry = response[i]
            currencyId = self.safe_string(entry, 'coin')
            code = self.safe_currency_code(currencyId)
            networkList = self.safe_value(entry, 'networkList', [])
            withdrawFees[code] = {}
            for j in range(0, len(networkList)):
                networkEntry = networkList[j]
                networkId = self.safe_string(networkEntry, 'network')
                networkCode = self.safe_currency_code(networkId)
                fee = self.safe_number(networkEntry, 'withdrawFee')
                withdrawFees[code][networkCode] = fee
        return {
            'withdraw': withdrawFees,
            'deposit': {},
            'info': response,
        }

    def withdraw(self, code, amount, address, tag=None, params={}):
        """
        make a withdrawal
        :param str code: unified currency code
        :param float amount: the amount to withdraw
        :param str address: the address to withdraw to
        :param str|None tag:
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: a `transaction structure <https://docs.ccxt.com/en/latest/manual.html#transaction-structure>`
        """
        tag, params = self.handle_withdraw_tag_and_params(tag, params)
        self.check_address(address)
        self.load_markets()
        currency = self.currency(code)
        request = {
            'coin': currency['id'],
            'address': address,
            'amount': amount,
            # https://binance-docs.github.io/apidocs/spot/en/#withdraw-sapi
            # issue sapiGetCapitalConfigGetall() to get networks for withdrawing USDT ERC20 vs USDT Omni
            # 'network': 'ETH',  # 'BTC', 'TRX', etc, optional
        }
        if tag is not None:
            request['addressTag'] = tag
        networks = self.safe_value(self.options, 'networks', {})
        network = self.safe_string_upper(params, 'network')  # self line allows the user to specify either ERC20 or ETH
        network = self.safe_string(networks, network, network)  # handle ERC20>ETH alias
        if network is not None:
            request['network'] = network
            params = self.omit(params, 'network')
        response = self.sapiPostCapitalWithdrawApply(self.extend(request, params))
        #     {id: '9a67628b16ba4988ae20d329333f16bc'}
        return self.parse_transaction(response, currency)

    def parse_trading_fee(self, fee, market=None):
        #
        #     {
        #         "symbol": "ADABNB",
        #         "makerCommission": 0.001,
        #         "takerCommission": 0.001
        #     }
        #
        marketId = self.safe_string(fee, 'symbol')
        symbol = self.safe_symbol(marketId)
        return {
            'info': fee,
            'symbol': symbol,
            'maker': self.safe_number(fee, 'makerCommission'),
            'taker': self.safe_number(fee, 'takerCommission'),
        }

    def fetch_trading_fee(self, symbol, params={}):
        """
        fetch the trading fees for a market
        :param str symbol: unified market symbol
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: a `fee structure <https://docs.ccxt.com/en/latest/manual.html#fee-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        response = self.sapiGetAssetTradeFee(self.extend(request, params))
        #
        #     [
        #       {
        #         "symbol": "BTCUSDT",
        #         "makerCommission": "0.001",
        #         "takerCommission": "0.001"
        #       }
        #     ]
        #
        first = self.safe_value(response, 0, {})
        return self.parse_trading_fee(first)

    def fetch_trading_fees(self, params={}):
        """
        fetch the trading fees for multiple markets
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: a dictionary of `fee structures <https://docs.ccxt.com/en/latest/manual.html#fee-structure>` indexed by market symbols
        """
        self.load_markets()
        method = None
        defaultType = self.safe_string_2(self.options, 'fetchTradingFees', 'defaultType', 'future')
        type = self.safe_string(params, 'type', defaultType)
        query = self.omit(params, 'type')
        if (type == 'spot') or (type == 'margin'):
            method = 'sapiGetAssetTradeFee'
        elif type == 'future':
            method = 'fapiPrivateGetAccount'
        elif type == 'delivery':
            method = 'dapiPrivateGetAccount'
        response = getattr(self, method)(query)
        #
        # sapi / spot
        #
        #    [
        #       {
        #         "symbol": "ZRXBNB",
        #         "makerCommission": "0.001",
        #         "takerCommission": "0.001"
        #       },
        #       {
        #         "symbol": "ZRXBTC",
        #         "makerCommission": "0.001",
        #         "takerCommission": "0.001"
        #       },
        #    ]
        #
        # fapi / future / linear
        #
        #     {
        #         "feeTier": 0,       # account commisssion tier
        #         "canTrade": True,   # if can trade
        #         "canDeposit": True,     # if can transfer in asset
        #         "canWithdraw": True,    # if can transfer out asset
        #         "updateTime": 0,
        #         "totalInitialMargin": "0.00000000",    # total initial margin required with current mark price(useless with isolated positions), only for USDT asset
        #         "totalMaintMargin": "0.00000000",     # total maintenance margin required, only for USDT asset
        #         "totalWalletBalance": "23.72469206",     # total wallet balance, only for USDT asset
        #         "totalUnrealizedProfit": "0.00000000",   # total unrealized profit, only for USDT asset
        #         "totalMarginBalance": "23.72469206",     # total margin balance, only for USDT asset
        #         "totalPositionInitialMargin": "0.00000000",    # initial margin required for positions with current mark price, only for USDT asset
        #         "totalOpenOrderInitialMargin": "0.00000000",   # initial margin required for open orders with current mark price, only for USDT asset
        #         "totalCrossWalletBalance": "23.72469206",      # crossed wallet balance, only for USDT asset
        #         "totalCrossUnPnl": "0.00000000",      # unrealized profit of crossed positions, only for USDT asset
        #         "availableBalance": "23.72469206",       # available balance, only for USDT asset
        #         "maxWithdrawAmount": "23.72469206"     # maximum amount for transfer out, only for USDT asset
        #         ...
        #     }
        #
        # dapi / delivery / inverse
        #
        #     {
        #         "canDeposit": True,
        #         "canTrade": True,
        #         "canWithdraw": True,
        #         "feeTier": 2,
        #         "updateTime": 0
        #     }
        #
        if (type == 'spot') or (type == 'margin'):
            #
            #    [
            #       {
            #         "symbol": "ZRXBNB",
            #         "makerCommission": "0.001",
            #         "takerCommission": "0.001"
            #       },
            #       {
            #         "symbol": "ZRXBTC",
            #         "makerCommission": "0.001",
            #         "takerCommission": "0.001"
            #       },
            #    ]
            #
            result = {}
            for i in range(0, len(response)):
                fee = self.parse_trading_fee(response[i])
                symbol = fee['symbol']
                result[symbol] = fee
            return result
        elif type == 'future':
            #
            #     {
            #         "feeTier": 0,       # account commisssion tier
            #         "canTrade": True,   # if can trade
            #         "canDeposit": True,     # if can transfer in asset
            #         "canWithdraw": True,    # if can transfer out asset
            #         "updateTime": 0,
            #         "totalInitialMargin": "0.00000000",    # total initial margin required with current mark price(useless with isolated positions), only for USDT asset
            #         "totalMaintMargin": "0.00000000",     # total maintenance margin required, only for USDT asset
            #         "totalWalletBalance": "23.72469206",     # total wallet balance, only for USDT asset
            #         "totalUnrealizedProfit": "0.00000000",   # total unrealized profit, only for USDT asset
            #         "totalMarginBalance": "23.72469206",     # total margin balance, only for USDT asset
            #         "totalPositionInitialMargin": "0.00000000",    # initial margin required for positions with current mark price, only for USDT asset
            #         "totalOpenOrderInitialMargin": "0.00000000",   # initial margin required for open orders with current mark price, only for USDT asset
            #         "totalCrossWalletBalance": "23.72469206",      # crossed wallet balance, only for USDT asset
            #         "totalCrossUnPnl": "0.00000000",      # unrealized profit of crossed positions, only for USDT asset
            #         "availableBalance": "23.72469206",       # available balance, only for USDT asset
            #         "maxWithdrawAmount": "23.72469206"     # maximum amount for transfer out, only for USDT asset
            #         ...
            #     }
            #
            symbols = list(self.markets.keys())
            result = {}
            feeTier = self.safe_integer(response, 'feeTier')
            feeTiers = self.fees[type]['trading']['tiers']
            maker = feeTiers['maker'][feeTier][1]
            taker = feeTiers['taker'][feeTier][1]
            for i in range(0, len(symbols)):
                symbol = symbols[i]
                result[symbol] = {
                    'info': {
                        'feeTier': feeTier,
                    },
                    'symbol': symbol,
                    'maker': maker,
                    'taker': taker,
                }
            return result
        elif type == 'delivery':
            #
            #     {
            #         "canDeposit": True,
            #         "canTrade": True,
            #         "canWithdraw": True,
            #         "feeTier": 2,
            #         "updateTime": 0
            #     }
            #
            symbols = list(self.markets.keys())
            result = {}
            feeTier = self.safe_integer(response, 'feeTier')
            feeTiers = self.fees[type]['trading']['tiers']
            maker = feeTiers['maker'][feeTier][1]
            taker = feeTiers['taker'][feeTier][1]
            for i in range(0, len(symbols)):
                symbol = symbols[i]
                result[symbol] = {
                    'info': {
                        'feeTier': feeTier,
                    },
                    'symbol': symbol,
                    'maker': maker,
                    'taker': taker,
                }
            return result

    def futures_transfer(self, code, amount, type, params={}):
        """
        transfer between futures account
        :param str code: unified currency code
        :param float amount: the amount to transfer
        :param str type: 1 - transfer from spot account to USDT-Ⓜ futures account, 2 - transfer from USDT-Ⓜ futures account to spot account, 3 - transfer from spot account to COIN-Ⓜ futures account, 4 - transfer from COIN-Ⓜ futures account to spot account
        :param dict params: extra parameters specific to the binance api endpoint
        :param float|None params.recvWindow:
        :returns dict: a `transfer structure <https://docs.ccxt.com/en/latest/manual.html#futures-transfer-structure>`
        """
        if (type < 1) or (type > 4):
            raise ArgumentsRequired(self.id + ' type must be between 1 and 4')
        self.load_markets()
        currency = self.currency(code)
        request = {
            'asset': currency['id'],
            'amount': amount,
            'type': type,
        }
        response = self.sapiPostFuturesTransfer(self.extend(request, params))
        #
        #   {
        #       "tranId": 100000001
        #   }
        #
        return self.parse_transfer(response, currency)

    def fetch_funding_rate(self, symbol, params={}):
        """
        fetch the current funding rate
        :param str symbol: unified market symbol
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: a `funding rate structure <https://docs.ccxt.com/en/latest/manual.html#funding-rate-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        method = None
        if market['linear']:
            method = 'fapiPublicGetPremiumIndex'
        elif market['inverse']:
            method = 'dapiPublicGetPremiumIndex'
        else:
            raise NotSupported(self.id + ' fetchFundingRate() supports linear and inverse contracts only')
        response = getattr(self, method)(self.extend(request, params))
        if market['inverse']:
            response = response[0]
        #
        #     {
        #         "symbol": "BTCUSDT",
        #         "markPrice": "45802.81129892",
        #         "indexPrice": "45745.47701915",
        #         "estimatedSettlePrice": "45133.91753671",
        #         "lastFundingRate": "0.00063521",
        #         "interestRate": "0.00010000",
        #         "nextFundingTime": "1621267200000",
        #         "time": "1621252344001"
        #     }
        #
        return self.parse_funding_rate(response, market)

    def fetch_funding_rate_history(self, symbol=None, since=None, limit=None, params={}):
        """
        fetches historical funding rate prices
        :param str|None symbol: unified symbol of the market to fetch the funding rate history for
        :param int|None since: timestamp in ms of the earliest funding rate to fetch
        :param int|None limit: the maximum amount of `funding rate structures <https://docs.ccxt.com/en/latest/manual.html?#funding-rate-history-structure>` to fetch
        :param dict params: extra parameters specific to the binance api endpoint
        :param int|None params['until']: timestamp in ms of the latest funding rate
        :returns [dict]: a list of `funding rate structures <https://docs.ccxt.com/en/latest/manual.html?#funding-rate-history-structure>`
        """
        self.load_markets()
        request = {}
        method = None
        defaultType = self.safe_string_2(self.options, 'fetchFundingRateHistory', 'defaultType', 'future')
        type = self.safe_string(params, 'type', defaultType)
        params = self.omit(params, 'type')
        if type == 'future':
            method = 'fapiPublicGetFundingRate'
        elif type == 'delivery':
            method = 'dapiPublicGetFundingRate'
        if symbol is not None:
            market = self.market(symbol)
            symbol = market['symbol']
            request['symbol'] = market['id']
            if market['linear']:
                method = 'fapiPublicGetFundingRate'
            elif market['inverse']:
                method = 'dapiPublicGetFundingRate'
        if method is None:
            raise NotSupported(self.id + ' fetchFundingRateHistory() is not supported for ' + type + ' markets')
        if since is not None:
            request['startTime'] = since
        until = self.safe_integer_2(params, 'until', 'till')  # unified in milliseconds
        endTime = self.safe_integer(params, 'endTime', until)  # exchange-specific in milliseconds
        params = self.omit(params, ['endTime', 'till', 'until'])
        if endTime is not None:
            request['endTime'] = endTime
        if limit is not None:
            request['limit'] = limit
        response = getattr(self, method)(self.extend(request, params))
        #
        #     {
        #         "symbol": "BTCUSDT",
        #         "fundingRate": "0.00063521",
        #         "fundingTime": "1621267200000",
        #     }
        #
        rates = []
        for i in range(0, len(response)):
            entry = response[i]
            timestamp = self.safe_integer(entry, 'fundingTime')
            rates.append({
                'info': entry,
                'symbol': self.safe_symbol(self.safe_string(entry, 'symbol')),
                'fundingRate': self.safe_number(entry, 'fundingRate'),
                'timestamp': timestamp,
                'datetime': self.iso8601(timestamp),
            })
        sorted = self.sort_by(rates, 'timestamp')
        return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)

    def fetch_funding_rates(self, symbols=None, params={}):
        """
        fetch the funding rate for multiple markets
        :param [str]|None symbols: list of unified market symbols
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: a dictionary of `funding rates structures <https://docs.ccxt.com/en/latest/manual.html#funding-rates-structure>`, indexe by market symbols
        """
        self.load_markets()
        symbols = self.market_symbols(symbols)
        method = None
        defaultType = self.safe_string_2(self.options, 'fetchFundingRates', 'defaultType', 'future')
        type = self.safe_string(params, 'type', defaultType)
        query = self.omit(params, 'type')
        if type == 'future':
            method = 'fapiPublicGetPremiumIndex'
        elif type == 'delivery':
            method = 'dapiPublicGetPremiumIndex'
        else:
            raise NotSupported(self.id + ' fetchFundingRates() supports linear and inverse contracts only')
        response = getattr(self, method)(query)
        result = []
        for i in range(0, len(response)):
            entry = response[i]
            parsed = self.parse_funding_rate(entry)
            result.append(parsed)
        return self.filter_by_array(result, 'symbol', symbols)

    def parse_funding_rate(self, contract, market=None):
        # ensure it matches with https://www.binance.com/en/futures/funding-history/0
        #
        #   {
        #     "symbol": "BTCUSDT",
        #     "markPrice": "45802.81129892",
        #     "indexPrice": "45745.47701915",
        #     "estimatedSettlePrice": "45133.91753671",
        #     "lastFundingRate": "0.00063521",
        #     "interestRate": "0.00010000",
        #     "nextFundingTime": "1621267200000",
        #     "time": "1621252344001"
        #  }
        #
        timestamp = self.safe_integer(contract, 'time')
        marketId = self.safe_string(contract, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        markPrice = self.safe_number(contract, 'markPrice')
        indexPrice = self.safe_number(contract, 'indexPrice')
        interestRate = self.safe_number(contract, 'interestRate')
        estimatedSettlePrice = self.safe_number(contract, 'estimatedSettlePrice')
        fundingRate = self.safe_number(contract, 'lastFundingRate')
        fundingTime = self.safe_integer(contract, 'nextFundingTime')
        return {
            'info': contract,
            'symbol': symbol,
            'markPrice': markPrice,
            'indexPrice': indexPrice,
            'interestRate': interestRate,
            'estimatedSettlePrice': estimatedSettlePrice,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'fundingRate': fundingRate,
            'fundingTimestamp': fundingTime,
            'fundingDatetime': self.iso8601(fundingTime),
            'nextFundingRate': None,
            'nextFundingTimestamp': None,
            'nextFundingDatetime': None,
            'previousFundingRate': None,
            'previousFundingTimestamp': None,
            'previousFundingDatetime': None,
        }

    def parse_account_positions(self, account):
        positions = self.safe_value(account, 'positions')
        assets = self.safe_value(account, 'assets', [])
        balances = {}
        for i in range(0, len(assets)):
            entry = assets[i]
            currencyId = self.safe_string(entry, 'asset')
            code = self.safe_currency_code(currencyId)
            crossWalletBalance = self.safe_string(entry, 'crossWalletBalance')
            crossUnPnl = self.safe_string(entry, 'crossUnPnl')
            balances[code] = {
                'crossMargin': Precise.string_add(crossWalletBalance, crossUnPnl),
                'crossWalletBalance': crossWalletBalance,
            }
        result = []
        for i in range(0, len(positions)):
            position = positions[i]
            marketId = self.safe_string(position, 'symbol')
            market = self.safe_market(marketId)
            code = market['quote'] if (self.options['defaultType'] == 'future') else market['base']
            # sometimes not all the codes are correctly returned...
            if code in balances:
                parsed = self.parse_account_position(self.extend(position, {
                    'crossMargin': balances[code]['crossMargin'],
                    'crossWalletBalance': balances[code]['crossWalletBalance'],
                }), market)
                result.append(parsed)
        return result

    def parse_account_position(self, position, market=None):
        #
        # usdm
        #    {
        #       "symbol": "BTCBUSD",
        #       "initialMargin": "0",
        #       "maintMargin": "0",
        #       "unrealizedProfit": "0.00000000",
        #       "positionInitialMargin": "0",
        #       "openOrderInitialMargin": "0",
        #       "leverage": "20",
        #       "isolated": False,
        #       "entryPrice": "0.0000",
        #       "maxNotional": "100000",
        #       "positionSide": "BOTH",
        #       "positionAmt": "0.000",
        #       "notional": "0",
        #       "isolatedWallet": "0",
        #       "updateTime": "0",
        #       "crossMargin": "100.93634809",
        #     }
        #
        # coinm
        #     {
        #       "symbol": "BTCUSD_210625",
        #       "initialMargin": "0.00024393",
        #       "maintMargin": "0.00002439",
        #       "unrealizedProfit": "-0.00000163",
        #       "positionInitialMargin": "0.00024393",
        #       "openOrderInitialMargin": "0",
        #       "leverage": "10",
        #       "isolated": False,
        #       "positionSide": "BOTH",
        #       "entryPrice": "41021.20000069",
        #       "maxQty": "100",
        #       "notionalValue": "0.00243939",
        #       "isolatedWallet": "0",
        #       "crossMargin": "0.314"
        #       "crossWalletBalance": "34",
        #     }
        #
        marketId = self.safe_string(position, 'symbol')
        market = self.safe_market(marketId, market)
        symbol = self.safe_string(market, 'symbol')
        leverageString = self.safe_string(position, 'leverage')
        leverage = int(leverageString)
        initialMarginString = self.safe_string(position, 'initialMargin')
        initialMargin = self.parse_number(initialMarginString)
        initialMarginPercentageString = Precise.string_div('1', leverageString, 8)
        rational = (1000 % leverage) == 0
        if not rational:
            initialMarginPercentageString = Precise.string_div(Precise.string_add(initialMarginPercentageString, '1e-8'), '1', 8)
        # as oppose to notionalValue
        usdm = ('notional' in position)
        maintenanceMarginString = self.safe_string(position, 'maintMargin')
        maintenanceMargin = self.parse_number(maintenanceMarginString)
        entryPriceString = self.safe_string(position, 'entryPrice')
        entryPrice = self.parse_number(entryPriceString)
        notionalString = self.safe_string_2(position, 'notional', 'notionalValue')
        notionalStringAbs = Precise.string_abs(notionalString)
        notional = self.parse_number(notionalStringAbs)
        contractsString = self.safe_string(position, 'positionAmt')
        contractsStringAbs = Precise.string_abs(contractsString)
        if contractsString is None:
            entryNotional = Precise.string_mul(Precise.string_mul(leverageString, initialMarginString), entryPriceString)
            contractSize = self.safe_string(market, 'contractSize')
            contractsString = Precise.string_div(entryNotional, contractSize)
            contractsStringAbs = Precise.string_div(Precise.string_add(contractsString, '0.5'), '1', 0)
        contracts = self.parse_number(contractsStringAbs)
        leverageBrackets = self.safe_value(self.options, 'leverageBrackets', {})
        leverageBracket = self.safe_value(leverageBrackets, symbol, [])
        maintenanceMarginPercentageString = None
        for i in range(0, len(leverageBracket)):
            bracket = leverageBracket[i]
            if Precise.string_lt(notionalStringAbs, bracket[0]):
                break
            maintenanceMarginPercentageString = bracket[1]
        maintenanceMarginPercentage = self.parse_number(maintenanceMarginPercentageString)
        unrealizedPnlString = self.safe_string(position, 'unrealizedProfit')
        unrealizedPnl = self.parse_number(unrealizedPnlString)
        timestamp = self.safe_integer(position, 'updateTime')
        if timestamp == 0:
            timestamp = None
        isolated = self.safe_value(position, 'isolated')
        marginMode = None
        collateralString = None
        walletBalance = None
        if isolated:
            marginMode = 'isolated'
            walletBalance = self.safe_string(position, 'isolatedWallet')
            collateralString = Precise.string_add(walletBalance, unrealizedPnlString)
        else:
            marginMode = 'cross'
            walletBalance = self.safe_string(position, 'crossWalletBalance')
            collateralString = self.safe_string(position, 'crossMargin')
        collateral = self.parse_number(collateralString)
        marginRatio = None
        side = None
        percentage = None
        liquidationPriceStringRaw = None
        liquidationPrice = None
        contractSize = self.safe_value(market, 'contractSize')
        contractSizeString = self.number_to_string(contractSize)
        if Precise.string_equals(notionalString, '0'):
            entryPrice = None
        else:
            side = 'short' if Precise.string_lt(notionalString, '0') else 'long'
            marginRatio = self.parse_number(Precise.string_div(Precise.string_add(Precise.string_div(maintenanceMarginString, collateralString), '5e-5'), '1', 4))
            percentage = self.parse_number(Precise.string_mul(Precise.string_div(unrealizedPnlString, initialMarginString, 4), '100'))
            if usdm:
                # calculate liquidation price
                #
                # liquidationPrice = (walletBalance / (contracts * (±1 + mmp))) + (±entryPrice / (±1 + mmp))
                #
                # mmp = maintenanceMarginPercentage
                # where ± is negative for long and positive for short
                # TODO: calculate liquidation price for coinm contracts
                onePlusMaintenanceMarginPercentageString = None
                entryPriceSignString = entryPriceString
                if side == 'short':
                    onePlusMaintenanceMarginPercentageString = Precise.string_add('1', maintenanceMarginPercentageString)
                else:
                    onePlusMaintenanceMarginPercentageString = Precise.string_add('-1', maintenanceMarginPercentageString)
                    entryPriceSignString = Precise.string_mul('-1', entryPriceSignString)
                leftSide = Precise.string_div(walletBalance, Precise.string_mul(contractsStringAbs, onePlusMaintenanceMarginPercentageString))
                rightSide = Precise.string_div(entryPriceSignString, onePlusMaintenanceMarginPercentageString)
                liquidationPriceStringRaw = Precise.string_add(leftSide, rightSide)
            else:
                # calculate liquidation price
                #
                # liquidationPrice = (contracts * contractSize(±1 - mmp)) / (±1/entryPrice * contracts * contractSize - walletBalance)
                #
                onePlusMaintenanceMarginPercentageString = None
                entryPriceSignString = entryPriceString
                if side == 'short':
                    onePlusMaintenanceMarginPercentageString = Precise.string_sub('1', maintenanceMarginPercentageString)
                else:
                    onePlusMaintenanceMarginPercentageString = Precise.string_sub('-1', maintenanceMarginPercentageString)
                    entryPriceSignString = Precise.string_mul('-1', entryPriceSignString)
                size = Precise.string_mul(contractsStringAbs, contractSizeString)
                leftSide = Precise.string_mul(size, onePlusMaintenanceMarginPercentageString)
                rightSide = Precise.string_sub(Precise.string_mul(Precise.string_div('1', entryPriceSignString), size), walletBalance)
                liquidationPriceStringRaw = Precise.string_div(leftSide, rightSide)
            pricePrecision = market['precision']['price']
            pricePrecisionPlusOne = pricePrecision + 1
            pricePrecisionPlusOneString = str(pricePrecisionPlusOne)
            # round half up
            rounder = Precise('5e-' + pricePrecisionPlusOneString)
            rounderString = str(rounder)
            liquidationPriceRoundedString = Precise.string_add(rounderString, liquidationPriceStringRaw)
            truncatedLiquidationPrice = Precise.string_div(liquidationPriceRoundedString, '1', pricePrecision)
            if truncatedLiquidationPrice[0] == '-':
                # user cannot be liquidated
                # since he has more collateral than the size of the position
                truncatedLiquidationPrice = None
            liquidationPrice = self.parse_number(truncatedLiquidationPrice)
        positionSide = self.safe_string(position, 'positionSide')
        hedged = positionSide != 'BOTH'
        return {
            'info': position,
            'id': None,
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'initialMargin': initialMargin,
            'initialMarginPercentage': self.parse_number(initialMarginPercentageString),
            'maintenanceMargin': maintenanceMargin,
            'maintenanceMarginPercentage': maintenanceMarginPercentage,
            'entryPrice': entryPrice,
            'notional': notional,
            'leverage': self.parse_number(leverageString),
            'unrealizedPnl': unrealizedPnl,
            'contracts': contracts,
            'contractSize': contractSize,
            'marginRatio': marginRatio,
            'liquidationPrice': liquidationPrice,
            'markPrice': None,
            'collateral': collateral,
            'marginMode': marginMode,
            'side': side,
            'hedged': hedged,
            'percentage': percentage,
        }

    def parse_position_risk(self, position, market=None):
        #
        # usdm
        #     {
        #       "symbol": "BTCUSDT",
        #       "positionAmt": "0.001",
        #       "entryPrice": "43578.07000",
        #       "markPrice": "43532.30000000",
        #       "unRealizedProfit": "-0.04577000",
        #       "liquidationPrice": "21841.24993976",
        #       "leverage": "2",
        #       "maxNotionalValue": "300000000",
        #       "marginType": "isolated",
        #       "isolatedMargin": "21.77841506",
        #       "isAutoAddMargin": "false",
        #       "positionSide": "BOTH",
        #       "notional": "43.53230000",
        #       "isolatedWallet": "21.82418506",
        #       "updateTime": "1621358023886"
        #     }
        #
        # coinm
        #     {
        #       "symbol": "BTCUSD_PERP",
        #       "positionAmt": "2",
        #       "entryPrice": "37643.10000021",
        #       "markPrice": "38103.05510455",
        #       "unRealizedProfit": "0.00006413",
        #       "liquidationPrice": "25119.97445760",
        #       "leverage": "2",
        #       "maxQty": "1500",
        #       "marginType": "isolated",
        #       "isolatedMargin": "0.00274471",
        #       "isAutoAddMargin": "false",
        #       "positionSide": "BOTH",
        #       "notionalValue": "0.00524892",
        #       "isolatedWallet": "0.00268058"
        #     }
        #
        marketId = self.safe_string(position, 'symbol')
        market = self.safe_market(marketId, market)
        symbol = self.safe_string(market, 'symbol')
        leverageBrackets = self.safe_value(self.options, 'leverageBrackets', {})
        leverageBracket = self.safe_value(leverageBrackets, symbol, [])
        notionalString = self.safe_string_2(position, 'notional', 'notionalValue')
        notionalStringAbs = Precise.string_abs(notionalString)
        maintenanceMarginPercentageString = None
        for i in range(0, len(leverageBracket)):
            bracket = leverageBracket[i]
            if Precise.string_lt(notionalStringAbs, bracket[0]):
                break
            maintenanceMarginPercentageString = bracket[1]
        notional = self.parse_number(notionalStringAbs)
        contractsAbs = Precise.string_abs(self.safe_string(position, 'positionAmt'))
        contracts = self.parse_number(contractsAbs)
        unrealizedPnlString = self.safe_string(position, 'unRealizedProfit')
        unrealizedPnl = self.parse_number(unrealizedPnlString)
        leverageString = self.safe_string(position, 'leverage')
        leverage = int(leverageString)
        liquidationPriceString = self.omit_zero(self.safe_string(position, 'liquidationPrice'))
        liquidationPrice = self.parse_number(liquidationPriceString)
        collateralString = None
        marginMode = self.safe_string(position, 'marginType')
        side = None
        if Precise.string_gt(notionalString, '0'):
            side = 'long'
        elif Precise.string_lt(notionalString, '0'):
            side = 'short'
        entryPriceString = self.safe_string(position, 'entryPrice')
        entryPrice = self.parse_number(entryPriceString)
        contractSize = self.safe_value(market, 'contractSize')
        contractSizeString = self.number_to_string(contractSize)
        # as oppose to notionalValue
        linear = ('notional' in position)
        if marginMode == 'cross':
            # calculate collateral
            precision = self.safe_value(market, 'precision', {})
            if linear:
                # walletBalance = (liquidationPrice * (±1 + mmp) ± entryPrice) * contracts
                onePlusMaintenanceMarginPercentageString = None
                entryPriceSignString = entryPriceString
                if side == 'short':
                    onePlusMaintenanceMarginPercentageString = Precise.string_add('1', maintenanceMarginPercentageString)
                    entryPriceSignString = Precise.string_mul('-1', entryPriceSignString)
                else:
                    onePlusMaintenanceMarginPercentageString = Precise.string_add('-1', maintenanceMarginPercentageString)
                inner = Precise.string_mul(liquidationPriceString, onePlusMaintenanceMarginPercentageString)
                leftSide = Precise.string_add(inner, entryPriceSignString)
                pricePrecision = self.safe_integer(precision, 'price')
                quotePrecision = self.safe_integer(precision, 'quote', pricePrecision)
                if quotePrecision is not None:
                    collateralString = Precise.string_div(Precise.string_mul(leftSide, contractsAbs), '1', quotePrecision)
            else:
                # walletBalance = (contracts * contractSize) * (±1/entryPrice - (±1 - mmp) / liquidationPrice)
                onePlusMaintenanceMarginPercentageString = None
                entryPriceSignString = entryPriceString
                if side == 'short':
                    onePlusMaintenanceMarginPercentageString = Precise.string_sub('1', maintenanceMarginPercentageString)
                else:
                    onePlusMaintenanceMarginPercentageString = Precise.string_sub('-1', maintenanceMarginPercentageString)
                    entryPriceSignString = Precise.string_mul('-1', entryPriceSignString)
                leftSide = Precise.string_mul(contractsAbs, contractSizeString)
                rightSide = Precise.string_sub(Precise.string_div('1', entryPriceSignString), Precise.string_div(onePlusMaintenanceMarginPercentageString, liquidationPriceString))
                basePrecision = self.safe_integer(precision, 'base')
                if basePrecision is not None:
                    collateralString = Precise.string_div(Precise.string_mul(leftSide, rightSide), '1', basePrecision)
        else:
            collateralString = self.safe_string(position, 'isolatedMargin')
        collateralString = '0' if (collateralString is None) else collateralString
        collateral = self.parse_number(collateralString)
        markPrice = self.parse_number(self.omit_zero(self.safe_string(position, 'markPrice')))
        timestamp = self.safe_integer(position, 'updateTime')
        if timestamp == 0:
            timestamp = None
        maintenanceMarginPercentage = self.parse_number(maintenanceMarginPercentageString)
        maintenanceMarginString = Precise.string_mul(maintenanceMarginPercentageString, notionalStringAbs)
        maintenanceMargin = self.parse_number(maintenanceMarginString)
        initialMarginPercentageString = Precise.string_div('1', leverageString, 8)
        rational = (1000 % leverage) == 0
        if not rational:
            initialMarginPercentageString = Precise.string_add(initialMarginPercentageString, '1e-8')
        initialMarginString = Precise.string_div(Precise.string_mul(notionalStringAbs, initialMarginPercentageString), '1', 8)
        initialMargin = self.parse_number(initialMarginString)
        marginRatio = None
        percentage = None
        if not Precise.string_equals(collateralString, '0'):
            marginRatio = self.parse_number(Precise.string_div(Precise.string_add(Precise.string_div(maintenanceMarginString, collateralString), '5e-5'), '1', 4))
            percentage = self.parse_number(Precise.string_mul(Precise.string_div(unrealizedPnlString, initialMarginString, 4), '100'))
        positionSide = self.safe_string(position, 'positionSide')
        hedged = positionSide != 'BOTH'
        return {
            'info': position,
            'id': None,
            'symbol': symbol,
            'contracts': contracts,
            'contractSize': contractSize,
            'unrealizedPnl': unrealizedPnl,
            'leverage': self.parse_number(leverageString),
            'liquidationPrice': liquidationPrice,
            'collateral': collateral,
            'notional': notional,
            'markPrice': markPrice,
            'entryPrice': entryPrice,
            'timestamp': timestamp,
            'initialMargin': initialMargin,
            'initialMarginPercentage': self.parse_number(initialMarginPercentageString),
            'maintenanceMargin': maintenanceMargin,
            'maintenanceMarginPercentage': maintenanceMarginPercentage,
            'marginRatio': marginRatio,
            'datetime': self.iso8601(timestamp),
            'marginMode': marginMode,
            'marginType': marginMode,  # deprecated
            'side': side,
            'hedged': hedged,
            'percentage': percentage,
        }

    def load_leverage_brackets(self, reload=False, params={}):
        self.load_markets()
        # by default cache the leverage bracket
        # it contains useful stuff like the maintenance margin and initial margin for positions
        leverageBrackets = self.safe_value(self.options, 'leverageBrackets')
        if (leverageBrackets is None) or (reload):
            method = None
            defaultType = self.safe_string(self.options, 'defaultType', 'future')
            type = self.safe_string(params, 'type', defaultType)
            query = self.omit(params, 'type')
            if type == 'future':
                method = 'fapiPrivateGetLeverageBracket'
            elif type == 'delivery':
                method = 'dapiPrivateV2GetLeverageBracket'
            else:
                raise NotSupported(self.id + ' loadLeverageBrackets() supports linear and inverse contracts only')
            response = getattr(self, method)(query)
            self.options['leverageBrackets'] = {}
            for i in range(0, len(response)):
                entry = response[i]
                marketId = self.safe_string(entry, 'symbol')
                symbol = self.safe_symbol(marketId)
                brackets = self.safe_value(entry, 'brackets', [])
                result = []
                for j in range(0, len(brackets)):
                    bracket = brackets[j]
                    floorValue = self.safe_string_2(bracket, 'notionalFloor', 'qtyFloor')
                    maintenanceMarginPercentage = self.safe_string(bracket, 'maintMarginRatio')
                    result.append([floorValue, maintenanceMarginPercentage])
                self.options['leverageBrackets'][symbol] = result
        return self.options['leverageBrackets']

    def fetch_leverage_tiers(self, symbols=None, params={}):
        """
        retrieve information on the maximum leverage, and maintenance margin for trades of varying trade sizes
        :param [str]|None symbols: list of unified market symbols
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: a dictionary of `leverage tiers structures <https://docs.ccxt.com/en/latest/manual.html#leverage-tiers-structure>`, indexed by market symbols
        """
        self.load_markets()
        type, query = self.handle_market_type_and_params('fetchLeverageTiers', None, params)
        method = None
        if type == 'future':
            method = 'fapiPrivateGetLeverageBracket'
        elif type == 'delivery':
            method = 'dapiPrivateV2GetLeverageBracket'
        else:
            raise NotSupported(self.id + ' fetchLeverageTiers() supports linear and inverse contracts only')
        response = getattr(self, method)(query)
        #
        # usdm
        #
        #    [
        #        {
        #            "symbol": "SUSHIUSDT",
        #            "brackets": [
        #                {
        #                    "bracket": 1,
        #                    "initialLeverage": 50,
        #                    "notionalCap": 50000,
        #                    "notionalFloor": 0,
        #                    "maintMarginRatio": 0.01,
        #                    "cum": 0.0
        #                },
        #                ...
        #            ]
        #        }
        #    ]
        #
        # coinm
        #
        #     [
        #         {
        #             "symbol":"XRPUSD_210326",
        #             "brackets":[
        #                 {
        #                     "bracket":1,
        #                     "initialLeverage":20,
        #                     "qtyCap":500000,
        #                     "qtyFloor":0,
        #                     "maintMarginRatio":0.0185,
        #                     "cum":0.0
        #                 }
        #             ]
        #         }
        #     ]
        #
        return self.parse_leverage_tiers(response, symbols, 'symbol')

    def parse_market_leverage_tiers(self, info, market):
        """
         * @ignore
        :param dict info: Exchange response for 1 market
        :param dict market: CCXT market
        """
        #
        #    {
        #        "symbol": "SUSHIUSDT",
        #        "brackets": [
        #            {
        #                "bracket": 1,
        #                "initialLeverage": 50,
        #                "notionalCap": 50000,
        #                "notionalFloor": 0,
        #                "maintMarginRatio": 0.01,
        #                "cum": 0.0
        #            },
        #            ...
        #        ]
        #    }
        #
        marketId = self.safe_string(info, 'symbol')
        market = self.safe_market(marketId, market)
        brackets = self.safe_value(info, 'brackets', [])
        tiers = []
        for j in range(0, len(brackets)):
            bracket = brackets[j]
            tiers.append({
                'tier': self.safe_number(bracket, 'bracket'),
                'currency': market['quote'],
                'minNotional': self.safe_number_2(bracket, 'notionalFloor', 'qtyFloor'),
                'maxNotional': self.safe_number_2(bracket, 'notionalCap', 'qtyCap'),
                'maintenanceMarginRate': self.safe_number(bracket, 'maintMarginRatio'),
                'maxLeverage': self.safe_number(bracket, 'initialLeverage'),
                'info': bracket,
            })
        return tiers

    def fetch_positions(self, symbols=None, params={}):
        """
        fetch all open positions
        :param [str]|None symbols: list of unified market symbols
        :param dict params: extra parameters specific to the binance api endpoint
        :returns [dict]: a list of `position structure <https://docs.ccxt.com/en/latest/manual.html#position-structure>`
        """
        defaultMethod = self.safe_string(self.options, 'fetchPositions', 'positionRisk')
        if defaultMethod == 'positionRisk':
            return self.fetch_positions_risk(symbols, params)
        elif defaultMethod == 'account':
            return self.fetch_account_positions(symbols, params)
        else:
            raise NotSupported(self.id + '.options["fetchPositions"] = "' + defaultMethod + '" is invalid, please choose between "account" and "positionRisk"')

    def fetch_account_positions(self, symbols=None, params={}):
        """
        fetch account positions
        :param [str]|None symbols: list of unified market symbols
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: data on account positions
        """
        if symbols is not None:
            if not isinstance(symbols, list):
                raise ArgumentsRequired(self.id + ' fetchPositions() requires an array argument for symbols')
        self.load_markets()
        self.load_leverage_brackets(False, params)
        method = None
        defaultType = self.safe_string(self.options, 'defaultType', 'future')
        type = self.safe_string(params, 'type', defaultType)
        query = self.omit(params, 'type')
        if type == 'future':
            method = 'fapiPrivateGetAccount'
        elif type == 'delivery':
            method = 'dapiPrivateGetAccount'
        else:
            raise NotSupported(self.id + ' fetchPositions() supports linear and inverse contracts only')
        account = getattr(self, method)(query)
        result = self.parse_account_positions(account)
        symbols = self.market_symbols(symbols)
        return self.filter_by_array(result, 'symbol', symbols, False)

    def fetch_positions_risk(self, symbols=None, params={}):
        """
        fetch positions risk
        :param [str]|None symbols: list of unified market symbols
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: data on the positions risk
        """
        if symbols is not None:
            if not isinstance(symbols, list):
                raise ArgumentsRequired(self.id + ' fetchPositionsRisk() requires an array argument for symbols')
        self.load_markets()
        self.load_leverage_brackets(False, params)
        request = {}
        method = None
        defaultType = 'future'
        defaultType = self.safe_string(self.options, 'defaultType', defaultType)
        type = self.safe_string(params, 'type', defaultType)
        params = self.omit(params, 'type')
        if (type == 'future') or (type == 'linear'):
            method = 'fapiPrivateGetPositionRisk'
            #  ### Response examples  ###
            #
            # For One-way position mode:
            #     [
            #         {
            #             "entryPrice": "0.00000",
            #             "marginType": "isolated",
            #             "isAutoAddMargin": "false",
            #             "isolatedMargin": "0.00000000",
            #             "leverage": "10",
            #             "liquidationPrice": "0",
            #             "markPrice": "6679.50671178",
            #             "maxNotionalValue": "20000000",
            #             "positionAmt": "0.000",
            #             "symbol": "BTCUSDT",
            #             "unRealizedProfit": "0.00000000",
            #             "positionSide": "BOTH",
            #             "updateTime": 0
            #        }
            #     ]
            #
            # For Hedge position mode:
            #     [
            #         {
            #             "entryPrice": "6563.66500",
            #             "marginType": "isolated",
            #             "isAutoAddMargin": "false",
            #             "isolatedMargin": "15517.54150468",
            #             "leverage": "10",
            #             "liquidationPrice": "5930.78",
            #             "markPrice": "6679.50671178",
            #             "maxNotionalValue": "20000000",
            #             "positionAmt": "20.000",
            #             "symbol": "BTCUSDT",
            #             "unRealizedProfit": "2316.83423560"
            #             "positionSide": "LONG",
            #             "updateTime": 1625474304765
            #         },
            #         {
            #             "entryPrice": "0.00000",
            #             "marginType": "isolated",
            #             "isAutoAddMargin": "false",
            #             "isolatedMargin": "5413.95799991",
            #             "leverage": "10",
            #             "liquidationPrice": "7189.95",
            #             "markPrice": "6679.50671178",
            #             "maxNotionalValue": "20000000",
            #             "positionAmt": "-10.000",
            #             "symbol": "BTCUSDT",
            #             "unRealizedProfit": "-1156.46711780",
            #             "positionSide": "SHORT",
            #             "updateTime": 0
            #         }
            #     ]
        elif (type == 'delivery') or (type == 'inverse'):
            method = 'dapiPrivateGetPositionRisk'
        else:
            raise NotSupported(self.id + ' fetchPositionsRisk() supports linear and inverse contracts only')
        response = getattr(self, method)(self.extend(request, params))
        result = []
        for i in range(0, len(response)):
            parsed = self.parse_position_risk(response[i])
            result.append(parsed)
        symbols = self.market_symbols(symbols)
        return self.filter_by_array(result, 'symbol', symbols, False)

    def fetch_funding_history(self, symbol=None, since=None, limit=None, params={}):
        """
        fetch the history of funding payments paid and received on self account
        :param str|None symbol: unified market symbol
        :param int|None since: the earliest time in ms to fetch funding history for
        :param int|None limit: the maximum number of funding history structures to retrieve
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: a `funding history structure <https://docs.ccxt.com/en/latest/manual.html#funding-history-structure>`
        """
        self.load_markets()
        market = None
        method = None
        defaultType = 'future'
        request = {
            'incomeType': 'FUNDING_FEE',  # "TRANSFER"，"WELCOME_BONUS", "REALIZED_PNL"，"FUNDING_FEE", "COMMISSION" and "INSURANCE_CLEAR"
        }
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
            if market['linear']:
                defaultType = 'future'
            elif market['inverse']:
                defaultType = 'delivery'
            else:
                raise NotSupported(self.id + ' fetchFundingHistory() supports linear and inverse contracts only')
        if since is not None:
            request['startTime'] = since
        if limit is not None:
            request['limit'] = limit
        defaultType = self.safe_string_2(self.options, 'fetchFundingHistory', 'defaultType', defaultType)
        type = self.safe_string(params, 'type', defaultType)
        params = self.omit(params, 'type')
        if (type == 'future') or (type == 'linear'):
            method = 'fapiPrivateGetIncome'
        elif (type == 'delivery') or (type == 'inverse'):
            method = 'dapiPrivateGetIncome'
        else:
            raise NotSupported(self.id + ' fetchFundingHistory() supports linear and inverse contracts only')
        response = getattr(self, method)(self.extend(request, params))
        return self.parse_incomes(response, market, since, limit)

    def set_leverage(self, leverage, symbol=None, params={}):
        """
        set the level of leverage for a market
        :param float leverage: the rate of leverage
        :param str symbol: unified market symbol
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: response from the exchange
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' setLeverage() requires a symbol argument')
        # WARNING: THIS WILL INCREASE LIQUIDATION PRICE FOR OPEN ISOLATED LONG POSITIONS
        # AND DECREASE LIQUIDATION PRICE FOR OPEN ISOLATED SHORT POSITIONS
        if (leverage < 1) or (leverage > 125):
            raise BadRequest(self.id + ' leverage should be between 1 and 125')
        self.load_markets()
        market = self.market(symbol)
        method = None
        if market['linear']:
            method = 'fapiPrivatePostLeverage'
        elif market['inverse']:
            method = 'dapiPrivatePostLeverage'
        else:
            raise NotSupported(self.id + ' setLeverage() supports linear and inverse contracts only')
        request = {
            'symbol': market['id'],
            'leverage': leverage,
        }
        return getattr(self, method)(self.extend(request, params))

    def set_margin_mode(self, marginMode, symbol=None, params={}):
        """
        set margin mode to 'cross' or 'isolated'
        :param str marginMode: 'cross' or 'isolated'
        :param str symbol: unified market symbol
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: response from the exchange
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' setMarginMode() requires a symbol argument')
        #
        # {"code": -4048 , "msg": "Margin type cannot be changed if there exists position."}
        #
        # or
        #
        # {"code": 200, "msg": "success"}
        #
        marginMode = marginMode.upper()
        if marginMode == 'CROSS':
            marginMode = 'CROSSED'
        if (marginMode != 'ISOLATED') and (marginMode != 'CROSSED'):
            raise BadRequest(self.id + ' marginMode must be either isolated or cross')
        self.load_markets()
        market = self.market(symbol)
        method = None
        if market['linear']:
            method = 'fapiPrivatePostMarginType'
        elif market['inverse']:
            method = 'dapiPrivatePostMarginType'
        else:
            raise NotSupported(self.id + ' setMarginMode() supports linear and inverse contracts only')
        request = {
            'symbol': market['id'],
            'marginType': marginMode,
        }
        response = None
        try:
            response = getattr(self, method)(self.extend(request, params))
        except Exception as e:
            # not an error
            # https://github.com/ccxt/ccxt/issues/11268
            # https://github.com/ccxt/ccxt/pull/11624
            # POST https://fapi.binance.com/fapi/v1/marginType 400 Bad Request
            # binanceusdm
            if isinstance(e, MarginModeAlreadySet):
                throwMarginModeAlreadySet = self.safe_value(self.options, 'throwMarginModeAlreadySet', False)
                if throwMarginModeAlreadySet:
                    raise e
                else:
                    response = {'code': -4046, 'msg': 'No need to change margin type.'}
        return response

    def set_position_mode(self, hedged, symbol=None, params={}):
        """
        set hedged to True or False for a market
        :param bool hedged: set to True to use dualSidePosition
        :param str|None symbol: not used by binance setPositionMode()
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: response from the exchange
        """
        defaultType = self.safe_string(self.options, 'defaultType', 'future')
        type = self.safe_string(params, 'type', defaultType)
        params = self.omit(params, ['type'])
        dualSidePosition = None
        if hedged:
            dualSidePosition = 'true'
        else:
            dualSidePosition = 'false'
        request = {
            'dualSidePosition': dualSidePosition,
        }
        method = None
        if type == 'delivery':
            method = 'dapiPrivatePostPositionSideDual'
        else:
            # default to future
            method = 'fapiPrivatePostPositionSideDual'
        #
        #     {
        #       "code": 200,
        #       "msg": "success"
        #     }
        #
        return getattr(self, method)(self.extend(request, params))

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        if not (api in self.urls['api']):
            raise NotSupported(self.id + ' does not have a testnet/sandbox URL for ' + api + ' endpoints')
        url = self.urls['api'][api]
        url += '/' + path
        if api == 'wapi':
            url += '.html'
        if path == 'historicalTrades':
            if self.apiKey:
                headers = {
                    'X-MBX-APIKEY': self.apiKey,
                }
            else:
                raise AuthenticationError(self.id + ' historicalTrades endpoint requires `apiKey` credential')
        userDataStream = (path == 'userDataStream') or (path == 'listenKey')
        if userDataStream:
            if self.apiKey:
                # v1 special case for userDataStream
                headers = {
                    'X-MBX-APIKEY': self.apiKey,
                    'Content-Type': 'application/x-www-form-urlencoded',
                }
                if method != 'GET':
                    body = self.urlencode(params)
            else:
                raise AuthenticationError(self.id + ' userDataStream endpoint requires `apiKey` credential')
        elif (api == 'private') or (api == 'eapiPrivate') or (api == 'sapi' and path != 'system/status') or (api == 'sapiV3') or (api == 'wapi' and path != 'systemStatus') or (api == 'dapiPrivate') or (api == 'dapiPrivateV2') or (api == 'fapiPrivate') or (api == 'fapiPrivateV2'):
            self.check_required_credentials()
            query = None
            defaultRecvWindow = self.safe_integer(self.options, 'recvWindow')
            extendedParams = self.extend({
                'timestamp': self.nonce(),
            }, params)
            if defaultRecvWindow is not None:
                extendedParams['recvWindow'] = defaultRecvWindow
            recvWindow = self.safe_integer(params, 'recvWindow')
            if recvWindow is not None:
                extendedParams['recvWindow'] = recvWindow
            if (api == 'sapi') and (path == 'asset/dust'):
                query = self.urlencode_with_array_repeat(extendedParams)
            elif (path == 'batchOrders') or (path.find('sub-account') >= 0) or (path == 'capital/withdraw/apply') or (path.find('staking') >= 0):
                query = self.rawencode(extendedParams)
            else:
                query = self.urlencode(extendedParams)
            signature = self.hmac(self.encode(query), self.encode(self.secret))
            query += '&' + 'signature=' + signature
            headers = {
                'X-MBX-APIKEY': self.apiKey,
            }
            if (method == 'GET') or (method == 'DELETE') or (api == 'wapi'):
                url += '?' + query
            else:
                body = query
                headers['Content-Type'] = 'application/x-www-form-urlencoded'
        else:
            if params:
                url += '?' + self.urlencode(params)
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
        if (code == 418) or (code == 429):
            raise DDoSProtection(self.id + ' ' + str(code) + ' ' + reason + ' ' + body)
        # error response in a form: {"code": -1013, "msg": "Invalid quantity."}
        # following block cointains legacy checks against message patterns in "msg" property
        # will switch "code" checks eventually, when we know all of them
        if code >= 400:
            if body.find('Price * QTY is zero or less') >= 0:
                raise InvalidOrder(self.id + ' order cost = amount * price is zero or less ' + body)
            if body.find('LOT_SIZE') >= 0:
                raise InvalidOrder(self.id + ' order amount should be evenly divisible by lot size ' + body)
            if body.find('PRICE_FILTER') >= 0:
                raise InvalidOrder(self.id + ' order price is invalid, i.e. exceeds allowed price precision, exceeds min price or max price limits or is invalid value in general, use self.price_to_precision(symbol, amount) ' + body)
        if response is None:
            return  # fallback to default error handler
        # check success value for wapi endpoints
        # response in format {'msg': 'The coin does not exist.', 'success': True/false}
        success = self.safe_value(response, 'success', True)
        if not success:
            message = self.safe_string(response, 'msg')
            parsedMessage = None
            if message is not None:
                try:
                    parsedMessage = json.loads(message)
                except Exception as e:
                    # do nothing
                    parsedMessage = None
                if parsedMessage is not None:
                    response = parsedMessage
        message = self.safe_string(response, 'msg')
        if message is not None:
            self.throw_exactly_matched_exception(self.exceptions['exact'], message, self.id + ' ' + message)
            self.throw_broadly_matched_exception(self.exceptions['broad'], message, self.id + ' ' + message)
        # checks against error codes
        error = self.safe_string(response, 'code')
        if error is not None:
            # https://github.com/ccxt/ccxt/issues/6501
            # https://github.com/ccxt/ccxt/issues/7742
            if (error == '200') or Precise.string_equals(error, '0'):
                return None
            # a workaround for {"code":-2015,"msg":"Invalid API-key, IP, or permissions for action."}
            # despite that their message is very confusing, it is raised by Binance
            # on a temporary ban, the API key is valid, but disabled for a while
            if (error == '-2015') and self.options['hasAlreadyAuthenticatedSuccessfully']:
                raise DDoSProtection(self.id + ' ' + body)
            feedback = self.id + ' ' + body
            if message == 'No need to change margin type.':
                # not an error
                # https://github.com/ccxt/ccxt/issues/11268
                # https://github.com/ccxt/ccxt/pull/11624
                # POST https://fapi.binance.com/fapi/v1/marginType 400 Bad Request
                # binanceusdm {"code":-4046,"msg":"No need to change margin type."}
                raise MarginModeAlreadySet(feedback)
            self.throw_exactly_matched_exception(self.exceptions['exact'], error, feedback)
            raise ExchangeError(feedback)
        if not success:
            raise ExchangeError(self.id + ' ' + body)

    def calculate_rate_limiter_cost(self, api, method, path, params, config={}, context={}):
        if ('noCoin' in config) and not ('coin' in params):
            return config['noCoin']
        elif ('noSymbol' in config) and not ('symbol' in params):
            return config['noSymbol']
        elif ('noPoolId' in config) and not ('poolId' in params):
            return config['noPoolId']
        elif ('byLimit' in config) and ('limit' in params):
            limit = params['limit']
            byLimit = config['byLimit']
            for i in range(0, len(byLimit)):
                entry = byLimit[i]
                if limit <= entry[0]:
                    return entry[1]
        return self.safe_value(config, 'cost', 1)

    def request(self, path, api='public', method='GET', params={}, headers=None, body=None, config={}, context={}):
        response = self.fetch2(path, api, method, params, headers, body, config, context)
        # a workaround for {"code":-2015,"msg":"Invalid API-key, IP, or permissions for action."}
        if (api == 'private') or (api == 'wapi'):
            self.options['hasAlreadyAuthenticatedSuccessfully'] = True
        return response

    def modify_margin_helper(self, symbol, amount, addOrReduce, params={}):
        # used to modify isolated positions
        defaultType = self.safe_string(self.options, 'defaultType', 'future')
        if defaultType == 'spot':
            defaultType = 'future'
        type = self.safe_string(params, 'type', defaultType)
        if (type == 'margin') or (type == 'spot'):
            raise NotSupported(self.id + ' add / reduce margin only supported with type future or delivery')
        self.load_markets()
        market = self.market(symbol)
        amount = self.amount_to_precision(symbol, amount)
        request = {
            'type': addOrReduce,
            'symbol': market['id'],
            'amount': amount,
        }
        method = None
        code = None
        if type == 'future':
            method = 'fapiPrivatePostPositionMargin'
            code = market['quote']
        else:
            method = 'dapiPrivatePostPositionMargin'
            code = market['base']
        response = getattr(self, method)(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "msg": "Successfully modify position margin.",
        #         "amount": 0.001,
        #         "type": 1
        #     }
        #
        return self.extend(self.parse_margin_modification(response, market), {
            'code': code,
        })

    def parse_margin_modification(self, data, market=None):
        rawType = self.safe_integer(data, 'type')
        resultType = 'add' if (rawType == 1) else 'reduce'
        resultAmount = self.safe_number(data, 'amount')
        errorCode = self.safe_string(data, 'code')
        status = 'ok' if (errorCode == '200') else 'failed'
        return {
            'info': data,
            'type': resultType,
            'amount': resultAmount,
            'code': None,
            'symbol': market['symbol'],
            'status': status,
        }

    def reduce_margin(self, symbol, amount, params={}):
        """
        remove margin from a position
        :param str symbol: unified market symbol
        :param float amount: the amount of margin to remove
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: a `margin structure <https://docs.ccxt.com/en/latest/manual.html#reduce-margin-structure>`
        """
        return self.modify_margin_helper(symbol, amount, 2, params)

    def add_margin(self, symbol, amount, params={}):
        """
        add margin
        :param str symbol: unified market symbol
        :param float amount: amount of margin to add
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: a `margin structure <https://docs.ccxt.com/en/latest/manual.html#add-margin-structure>`
        """
        return self.modify_margin_helper(symbol, amount, 1, params)

    def fetch_borrow_rate(self, code, params={}):
        """
        fetch the rate of interest to borrow a currency for margin trading
        :param str code: unified currency code
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: a `borrow rate structure <https://docs.ccxt.com/en/latest/manual.html#borrow-rate-structure>`
        """
        self.load_markets()
        currency = self.currency(code)
        request = {
            'asset': currency['id'],
            # 'vipLevel': self.safe_integer(params, 'vipLevel'),
        }
        response = self.sapiGetMarginInterestRateHistory(self.extend(request, params))
        #
        #     [
        #         {
        #             "asset": "USDT",
        #             "timestamp": 1638230400000,
        #             "dailyInterestRate": "0.0006",
        #             "vipLevel": 0
        #         },
        #     ]
        #
        rate = self.safe_value(response, 0)
        return self.parse_borrow_rate(rate)

    def fetch_borrow_rate_history(self, code, since=None, limit=None, params={}):
        """
        retrieves a history of a currencies borrow interest rate at specific time slots
        :param str code: unified currency code
        :param int|None since: timestamp for the earliest borrow rate
        :param int|None limit: the maximum number of `borrow rate structures <https://docs.ccxt.com/en/latest/manual.html#borrow-rate-structure>` to retrieve
        :param dict params: extra parameters specific to the exchange api endpoint
        :returns [dict]: an array of `borrow rate structures <https://docs.ccxt.com/en/latest/manual.html#borrow-rate-structure>`
        """
        self.load_markets()
        if limit is None:
            limit = 93
        elif limit > 93:
            # Binance API says the limit is 100, but "Illegal characters found in a parameter." is returned when limit is > 93
            raise BadRequest(self.id + ' fetchBorrowRateHistory() limit parameter cannot exceed 92')
        currency = self.currency(code)
        request = {
            'asset': currency['id'],
            'limit': limit,
        }
        if since is not None:
            request['startTime'] = since
            endTime = self.sum(since, limit * 86400000) - 1  # required when startTime is further than 93 days in the past
            now = self.milliseconds()
            request['endTime'] = min(endTime, now)  # cannot have an endTime later than current time
        response = self.sapiGetMarginInterestRateHistory(self.extend(request, params))
        #
        #     [
        #         {
        #             "asset": "USDT",
        #             "timestamp": 1638230400000,
        #             "dailyInterestRate": "0.0006",
        #             "vipLevel": 0
        #         },
        #     ]
        #
        return self.parse_borrow_rate_history(response)

    def parse_borrow_rate_history(self, response, code, since, limit):
        result = []
        for i in range(0, len(response)):
            item = response[i]
            borrowRate = self.parse_borrow_rate(item)
            result.append(borrowRate)
        sorted = self.sort_by(result, 'timestamp')
        return self.filter_by_currency_since_limit(sorted, code, since, limit)

    def parse_borrow_rate(self, info, currency=None):
        #
        #    {
        #        "asset": "USDT",
        #        "timestamp": 1638230400000,
        #        "dailyInterestRate": "0.0006",
        #        "vipLevel": 0
        #    }
        #
        timestamp = self.safe_number(info, 'timestamp')
        currency = self.safe_string(info, 'asset')
        return {
            'currency': self.safe_currency_code(currency),
            'rate': self.safe_number(info, 'dailyInterestRate'),
            'period': 86400000,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'info': info,
        }

    def create_gift_code(self, code, amount, params={}):
        """
        create gift code
        :param str code: gift code
        :param float amount: amount of currency for the gift
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: The gift code id, code, currency and amount
        """
        self.load_markets()
        currency = self.currency(code)
        # ensure you have enough token in your funding account before calling self code
        request = {
            'token': currency['id'],
            'amount': amount,
        }
        response = self.sapiPostGiftcardCreateCode(self.extend(request, params))
        #
        #     {
        #         code: '000000',
        #         message: 'success',
        #         data: {referenceNo: '0033002404219823', code: 'AP6EXTLKNHM6CEX7'},
        #         success: True
        #     }
        #
        data = self.safe_value(response, 'data')
        giftcardCode = self.safe_string(data, 'code')
        id = self.safe_string(data, 'referenceNo')
        return {
            'info': response,
            'id': id,
            'code': giftcardCode,
            'currency': code,
            'amount': amount,
        }

    def redeem_gift_code(self, giftcardCode, params={}):
        """
        redeem gift code
        :param str giftcardCode:
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: response from the exchange
        """
        request = {
            'code': giftcardCode,
        }
        response = self.sapiPostGiftcardRedeemCode(self.extend(request, params))
        #
        #     {
        #         code: '000000',
        #         message: 'success',
        #         data: {
        #             referenceNo: '0033002404219823',
        #             identityNo: '10316431732801474560'
        #         },
        #         success: True
        #     }
        #
        return response

    def verify_gift_code(self, id, params={}):
        """
        verify gift code
        :param str id: reference number id
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: response from the exchange
        """
        request = {
            'referenceNo': id,
        }
        response = self.sapiGetGiftcardVerify(self.extend(request, params))
        #
        #     {
        #         code: '000000',
        #         message: 'success',
        #         data: {valid: True},
        #         success: True
        #     }
        #
        return response

    def fetch_borrow_interest(self, code=None, symbol=None, since=None, limit=None, params={}):
        """
        fetch the interest owed by the user for borrowing currency for margin trading
        :param str|None code: unified currency code
        :param str|None symbol: unified market symbol when fetch interest in isolated markets
        :param int|None since: the earliest time in ms to fetch borrrow interest for
        :param int|None limit: the maximum number of structures to retrieve
        :param dict params: extra parameters specific to the binance api endpoint
        :returns [dict]: a list of `borrow interest structures <https://docs.ccxt.com/en/latest/manual.html#borrow-interest-structure>`
        """
        self.load_markets()
        request = {}
        market = None
        if code is not None:
            currency = self.currency(code)
            request['asset'] = currency['id']
        if since is not None:
            request['startTime'] = since
        if limit is not None:
            request['size'] = limit
        if symbol is not None:  # Isolated
            market = self.market(symbol)
            request['isolatedSymbol'] = market['id']
        response = self.sapiGetMarginInterestHistory(self.extend(request, params))
        #
        #     {
        #         "rows":[
        #             {
        #                 "isolatedSymbol": "BNBUSDT",  # isolated symbol, will not be returned for crossed margin
        #                 "asset": "BNB",
        #                 "interest": "0.02414667",
        #                 "interestAccuredTime": 1566813600000,
        #                 "interestRate": "0.01600000",
        #                 "principal": "36.22000000",
        #                 "type": "ON_BORROW"
        #             }
        #         ],
        #         "total": 1
        #     }
        #
        rows = self.safe_value(response, 'rows')
        interest = self.parse_borrow_interests(rows, market)
        return self.filter_by_currency_since_limit(interest, code, since, limit)

    def parse_borrow_interest(self, info, market):
        symbol = self.safe_string(info, 'isolatedSymbol')
        timestamp = self.safe_number(info, 'interestAccuredTime')
        marginMode = 'cross' if (symbol is None) else 'isolated'
        return {
            'account': 'cross' if (symbol is None) else symbol,
            'symbol': symbol,
            'marginMode': marginMode,
            'currency': self.safe_currency_code(self.safe_string(info, 'asset')),
            'interest': self.safe_number(info, 'interest'),
            'interestRate': self.safe_number(info, 'interestRate'),
            'amountBorrowed': self.safe_number(info, 'principal'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'info': info,
        }

    def repay_margin(self, code, amount, symbol=None, params={}):
        """
        repay borrowed margin and interest
        see https://binance-docs.github.io/apidocs/spot/en/#margin-account-repay-margin
        :param str code: unified currency code of the currency to repay
        :param float amount: the amount to repay
        :param str|None symbol: unified market symbol, required for isolated margin
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: a `margin loan structure <https://docs.ccxt.com/en/latest/manual.html#margin-loan-structure>`
        """
        self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
            symbol = market['symbol']
        currency = self.currency(code)
        request = {
            'asset': currency['id'],
            'amount': self.currency_to_precision(code, amount),
        }
        defaultMarginMode = self.safe_string_2(self.options, 'defaultMarginMode', 'marginMode', 'cross')
        marginMode = self.safe_string(params, 'marginMode', defaultMarginMode)  # cross or isolated
        if marginMode == 'isolated':
            if symbol is None:
                raise ArgumentsRequired(self.id + ' repayMargin() requires a symbol argument for isolated margin')
            request['isIsolated'] = 'TRUE'
            request['symbol'] = market['id']
        params = self.omit(params, 'marginMode')
        response = self.sapiPostMarginRepay(self.extend(request, params))
        #
        #     {
        #         "tranId": 108988250265,
        #         "clientTag":""
        #     }
        #
        transaction = self.parse_margin_loan(response, currency)
        return self.extend(transaction, {
            'amount': amount,
            'symbol': symbol,
        })

    def borrow_margin(self, code, amount, symbol=None, params={}):
        """
        create a loan to borrow margin
        see https://binance-docs.github.io/apidocs/spot/en/#margin-account-borrow-margin
        :param str code: unified currency code of the currency to borrow
        :param float amount: the amount to borrow
        :param str|None symbol: unified market symbol, required for isolated margin
        :param dict params: extra parameters specific to the binance api endpoint
        :returns dict: a `margin loan structure <https://docs.ccxt.com/en/latest/manual.html#margin-loan-structure>`
        """
        self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
            symbol = market['symbol']
        currency = self.currency(code)
        request = {
            'asset': currency['id'],
            'amount': self.currency_to_precision(code, amount),
        }
        defaultMarginMode = self.safe_string_2(self.options, 'defaultMarginMode', 'marginMode', 'cross')
        marginMode = self.safe_string(params, 'marginMode', defaultMarginMode)  # cross or isolated
        if marginMode == 'isolated':
            if symbol is None:
                raise ArgumentsRequired(self.id + ' borrowMargin() requires a symbol argument for isolated margin')
            request['isIsolated'] = 'TRUE'
            request['symbol'] = market['id']
        params = self.omit(params, 'marginMode')
        response = self.sapiPostMarginLoan(self.extend(request, params))
        #
        #     {
        #         "tranId": 108988250265,
        #         "clientTag":""
        #     }
        #
        transaction = self.parse_margin_loan(response, currency)
        return self.extend(transaction, {
            'amount': amount,
            'symbol': symbol,
        })

    def parse_margin_loan(self, info, currency=None):
        #
        #     {
        #         "tranId": 108988250265,
        #         "clientTag":""
        #     }
        #
        return {
            'id': self.safe_integer(info, 'tranId'),
            'currency': self.safe_currency_code(None, currency),
            'amount': None,
            'symbol': None,
            'timestamp': None,
            'datetime': None,
            'info': info,
        }

    def fetch_open_interest_history(self, symbol, timeframe='5m', since=None, limit=None, params={}):
        """
        Retrieves the open interest history of a currency
        :param str symbol: Unified CCXT market symbol
        :param str timeframe: "5m","15m","30m","1h","2h","4h","6h","12h", or "1d"
        :param int|None since: the time(ms) of the earliest record to retrieve as a unix timestamp
        :param int|None limit: default 30, max 500
        :param dict params: exchange specific parameters
        :param int|None params['until']: the time(ms) of the latest record to retrieve as a unix timestamp
        :returns dict: an array of `open interest history structure <https://docs.ccxt.com/en/latest/manual.html#interest-history-structure>`
        """
        if timeframe == '1m':
            raise BadRequest(self.id + 'fetchOpenInterestHistory cannot use the 1m timeframe')
        self.load_markets()
        market = self.market(symbol)
        request = {
            'period': self.timeframes[timeframe],
        }
        if limit is not None:
            request['limit'] = limit
        symbolKey = 'symbol' if market['linear'] else 'pair'
        request[symbolKey] = market['id']
        if market['delivery']:
            request['contractType'] = self.safe_string(params, 'contractType', 'CURRENT_QUARTER')
        if since is not None:
            request['startTime'] = since
        until = self.safe_integer_2(params, 'until', 'till')  # unified in milliseconds
        endTime = self.safe_integer(params, 'endTime', until)  # exchange-specific in milliseconds
        params = self.omit(params, ['endTime', 'until', 'till'])
        if endTime:
            request['endTime'] = endTime
        elif since:
            if limit is None:
                limit = 30  # Exchange default
            duration = self.parse_timeframe(timeframe)
            request['endTime'] = self.sum(since, duration * limit * 1000)
        method = 'fapiDataGetOpenInterestHist'
        if market['inverse']:
            method = 'dapiDataGetOpenInterestHist'
        response = getattr(self, method)(self.extend(request, params))
        #
        #  [
        #      {
        #          "symbol":"BTCUSDT",
        #          "sumOpenInterest":"75375.61700000",
        #          "sumOpenInterestValue":"3248828883.71251440",
        #          "timestamp":1642179900000
        #      },
        #      ...
        #  ]
        #
        return self.parse_open_interests(response, symbol, since, limit)

    def parse_open_interest(self, interest, market=None):
        timestamp = self.safe_integer(interest, 'timestamp')
        id = self.safe_string(interest, 'symbol')
        market = self.safe_market(id, market)
        amount = self.safe_number(interest, 'sumOpenInterest')
        value = self.safe_number(interest, 'sumOpenInterestValue')
        return {
            'symbol': self.safe_symbol(id),
            'baseVolume': amount,  # deprecated
            'quoteVolume': value,  # deprecated
            'openInterestAmount': amount,
            'openInterestValue': value,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'info': interest,
        }
