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

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

from ccxt.async_support.base.exchange import Exchange
import math
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 InsufficientFunds
from ccxt.base.errors import InvalidOrder
from ccxt.base.errors import OrderNotFound
from ccxt.base.errors import OrderImmediatelyFillable
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 InvalidNonce
from ccxt.base.decimal_to_precision import TRUNCATE
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': 500,
            'certified': True,
            'pro': True,
            # new metainfo interface
            'has': {
                'cancelAllOrders': True,
                'cancelOrder': True,
                'CORS': False,
                'createOrder': True,
                'fetchCurrencies': True,
                'fetchBalance': True,
                'fetchBidsAsks': True,
                'fetchClosedOrders': 'emulated',
                'fetchDepositAddress': True,
                'fetchDeposits': True,
                'fetchFundingFees': True,
                'fetchMarkets': True,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrders': True,
                'fetchOrderBook': True,
                'fetchStatus': True,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': True,
                'fetchTrades': True,
                'fetchTradingFee': True,
                'fetchTradingFees': True,
                'fetchTransactions': False,
                'fetchWithdrawals': True,
                'withdraw': True,
                'transfer': True,
                'fetchTransfers': True,
            },
            'timeframes': {
                '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',
                    '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',
                    'v3': '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',
                    'dapiPublic': 'https://dapi.binance.com/dapi/v1',
                    'dapiPrivate': 'https://dapi.binance.com/dapi/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',
                    'v3': 'https://api.binance.com/api/v3',
                    'v1': 'https://api.binance.com/api/v1',
                },
                'www': 'https://www.binance.com',
                'referral': 'https://www.binance.com/?ref=10205187',
                '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',
            },
            'api': {
                # the API structure below will need 3-layer apidefs
                'sapi': {
                    'get': [
                        'accountSnapshot',
                        # these endpoints require self.apiKey
                        'margin/asset',
                        'margin/pair',
                        'margin/allAssets',
                        'margin/allPairs',
                        'margin/priceIndex',
                        # these endpoints require self.apiKey + self.secret
                        'asset/assetDividend',
                        'asset/transfer',
                        'asset/assetDetail',
                        'asset/tradeFee',
                        'margin/loan',
                        'margin/repay',
                        'margin/account',
                        'margin/transfer',
                        'margin/interestHistory',
                        'margin/forceLiquidationRec',
                        'margin/order',
                        'margin/openOrders',
                        'margin/allOrders',
                        'margin/myTrades',
                        'margin/maxBorrowable',
                        'margin/maxTransferable',
                        'margin/isolated/transfer',
                        'margin/isolated/account',
                        'margin/isolated/pair',
                        'margin/isolated/allPairs',
                        'fiatpayment/query/deposit/history',
                        'fiatpayment/query/withdraw/history',
                        'futures/transfer',
                        'futures/loan/borrow/history',
                        'futures/loan/repay/history',
                        'futures/loan/wallet',
                        'futures/loan/configs',
                        'futures/loan/calcAdjustLevel',
                        'futures/loan/calcMaxAdjustAmount',
                        'futures/loan/adjustCollateral/history',
                        'futures/loan/liquidationHistory',
                        # https://binance-docs.github.io/apidocs/spot/en/#withdraw-sapi
                        'capital/config/getall',  # get networks for withdrawing USDT ERC20 vs USDT Omni
                        'capital/deposit/address',
                        'capital/deposit/hisrec',
                        'capital/deposit/subAddress',
                        'capital/deposit/subHisrec',
                        'capital/withdraw/history',
                        'bnbBurn',
                        'sub-account/futures/account',
                        'sub-account/futures/accountSummary',
                        'sub-account/futures/positionRisk',
                        'sub-account/futures/internalTransfer',
                        'sub-account/margin/account',
                        'sub-account/margin/accountSummary',
                        'sub-account/spotSummary',
                        'sub-account/status',
                        'sub-account/transfer/subUserHistory',
                        'sub-account/universalTransfer',
                        # lending endpoints
                        'lending/daily/product/list',
                        'lending/daily/userLeftQuota',
                        'lending/daily/userRedemptionQuota',
                        'lending/daily/token/position',
                        'lending/union/account',
                        'lending/union/purchaseRecord',
                        'lending/union/redemptionRecord',
                        'lending/union/interestHistory',
                        'lending/project/list',
                        'lending/project/position/list',
                        # mining endpoints
                        'mining/pub/algoList',
                        'mining/pub/coinList',
                        'mining/worker/detail',
                        'mining/worker/list',
                        'mining/payment/list',
                        'mining/statistics/user/status',
                        'mining/statistics/user/list',
                        # liquid swap endpoints
                        'bswap/pools',
                        'bswap/liquidity',
                        'bswap/liquidityOps',
                        'bswap/quote',
                        'bswap/swap',
                        # leveraged token endpoints
                        'blvt/tokenInfo',
                        'blvt/subscribe/record',
                        'blvt/redeem/record',
                        'blvt/userLimit',
                        # broker api
                        'apiReferral/ifNewUser',
                        'apiReferral/customization',
                        'apiReferral/userCustomization',
                        'apiReferral/rebate/recentRecord',
                        'apiReferral/rebate/historicalRecord',
                        'apiReferral/kickback/recentRecord',
                        'apiReferral/kickback/historicalRecord',
                        # brokerage API
                        'broker/subAccountApi',
                        'broker/subAccount',
                        'broker/subAccountApi/commission/futures',
                        'broker/subAccountApi/commission/coinFutures',
                        'broker/info',
                        'broker/transfer',
                        'broker/transfer/futures',
                        'broker/rebate/recentRecord',
                        'broker/rebate/historicalRecord',
                        'broker/subAccount/bnbBurn/status',
                        'broker/subAccount/depositHist',
                        'broker/subAccount/spotSummary',
                        'broker/subAccount/marginSummary',
                        'broker/subAccount/futuresSummary',
                        'broker/rebate/futures/recentRecord',
                        'broker/subAccountApi/ipRestriction',
                        'broker/universalTransfer',
                        # v2 not supported yet
                        # GET /sapi/v2/broker/subAccount/futuresSummary
                    ],
                    'post': [
                        'asset/dust',
                        'asset/transfer',
                        'account/disableFastWithdrawSwitch',
                        'account/enableFastWithdrawSwitch',
                        'capital/withdraw/apply',
                        'margin/transfer',
                        'margin/loan',
                        'margin/repay',
                        'margin/order',
                        'margin/isolated/create',
                        'margin/isolated/transfer',
                        'bnbBurn',
                        'sub-account/margin/transfer',
                        'sub-account/margin/enable',
                        'sub-account/margin/enable',
                        'sub-account/futures/enable',
                        'sub-account/futures/transfer',
                        'sub-account/futures/internalTransfer',
                        'sub-account/transfer/subToSub',
                        'sub-account/transfer/subToMaster',
                        'sub-account/universalTransfer',
                        'userDataStream',
                        'userDataStream/isolated',
                        'futures/transfer',
                        'futures/loan/borrow',
                        'futures/loan/repay',
                        'futures/loan/adjustCollateral',
                        # lending
                        'lending/customizedFixed/purchase',
                        'lending/daily/purchase',
                        'lending/daily/redeem',
                        # liquid swap endpoints
                        'bswap/liquidityAdd',
                        'bswap/liquidityRemove',
                        'bswap/swap',
                        # leveraged token endpoints
                        'blvt/subscribe',
                        'blvt/redeem',
                        # brokerage API
                        'apiReferral/customization',
                        'apiReferral/userCustomization',
                        'apiReferral/rebate/historicalRecord',
                        'apiReferral/kickback/historicalRecord',
                        'broker/subAccount',
                        'broker/subAccount/margin',
                        'broker/subAccount/futures',
                        'broker/subAccountApi',
                        'broker/subAccountApi/permission',
                        'broker/subAccountApi/commission',
                        'broker/subAccountApi/commission/futures',
                        'broker/subAccountApi/commission/coinFutures',
                        'broker/transfer',
                        'broker/transfer/futures',
                        'broker/rebate/historicalRecord',
                        'broker/subAccount/bnbBurn/spot',
                        'broker/subAccount/bnbBurn/marginInterest',
                        'broker/subAccount/blvt',
                        'broker/subAccountApi/ipRestriction',
                        'broker/subAccountApi/ipRestriction/ipList',
                        'broker/universalTransfer',
                        'broker/subAccountApi/permission/universalTransfer',
                        'broker/subAccountApi/permission/vanillaOptions',
                    ],
                    'put': [
                        'userDataStream',
                        'userDataStream/isolated',
                    ],
                    'delete': [
                        'margin/openOrders',
                        'margin/order',
                        'userDataStream',
                        'userDataStream/isolated',
                        # brokerage API
                        'broker/subAccountApi',
                        'broker/subAccountApi/ipRestriction/ipList',
                    ],
                },
                # deprecated
                'wapi': {
                    'post': [
                        'withdraw',
                        'sub-account/transfer',
                    ],
                    'get': [
                        'depositHistory',
                        'withdrawHistory',
                        'depositAddress',
                        'accountStatus',
                        'systemStatus',
                        'apiTradingStatus',
                        'userAssetDribbletLog',
                        'tradeFee',
                        'assetDetail',
                        'sub-account/list',
                        'sub-account/transfer/history',
                        'sub-account/assets',
                    ],
                },
                'dapiPublic': {
                    'get': [
                        'ping',
                        'time',
                        'exchangeInfo',
                        'depth',
                        'trades',
                        'historicalTrades',
                        'aggTrades',
                        'premiumIndex',
                        'fundingRate',
                        'klines',
                        'continuousKlines',
                        'indexPriceKlines',
                        'markPriceKlines',
                        'ticker/24hr',
                        'ticker/price',
                        'ticker/bookTicker',
                        'allForceOrders',
                        'openInterest',
                    ],
                },
                'dapiData': {
                    'get': [
                        'openInterestHist',
                        'topLongShortAccountRatio',
                        'topLongShortPositionRatio',
                        'globalLongShortAccountRatio',
                        'takerBuySellVol',
                        'basis',
                    ],
                },
                'dapiPrivate': {
                    'get': [
                        'positionSide/dual',
                        'order',
                        'openOrder',
                        'openOrders',
                        'allOrders',
                        'balance',
                        'account',
                        'positionMargin/history',
                        'positionRisk',
                        'userTrades',
                        'income',
                        'leverageBracket',
                        'forceOrders',
                        'adlQuantile',
                    ],
                    'post': [
                        'positionSide/dual',
                        'order',
                        'batchOrders',
                        'countdownCancelAll',
                        'leverage',
                        'marginType',
                        'positionMargin',
                        'listenKey',
                    ],
                    'put': [
                        'listenKey',
                    ],
                    'delete': [
                        'order',
                        'allOpenOrders',
                        'batchOrders',
                        'listenKey',
                    ],
                },
                'dapiPrivateV2': {
                    'get': [
                        'leverageBracket',
                    ],
                },
                'fapiPublic': {
                    'get': [
                        'ping',
                        'time',
                        'exchangeInfo',
                        'depth',
                        'trades',
                        'historicalTrades',
                        'aggTrades',
                        'klines',
                        'continuousKlines',
                        'fundingRate',
                        'premiumIndex',
                        'ticker/24hr',
                        'ticker/price',
                        'ticker/bookTicker',
                        'allForceOrders',
                        'openInterest',
                        'indexInfo',
                    ],
                },
                'fapiData': {
                    'get': [
                        'openInterestHist',
                        'topLongShortAccountRatio',
                        'topLongShortPositionRatio',
                        'globalLongShortAccountRatio',
                        'takerlongshortRatio',
                    ],
                },
                'fapiPrivate': {
                    'get': [
                        'allForceOrders',
                        'allOrders',
                        'openOrder',
                        'openOrders',
                        'order',
                        'account',
                        'balance',
                        'leverageBracket',
                        'positionMargin/history',
                        'positionRisk',
                        'positionSide/dual',
                        'userTrades',
                        'income',
                        'commissionRate',
                        'apiTradingStatus',
                        # broker endpoints
                        'apiReferral/ifNewUser',
                        'apiReferral/customization',
                        'apiReferral/userCustomization',
                        'apiReferral/traderNum',
                        'apiReferral/overview',
                        'apiReferral/tradeVol',
                        'apiReferral/rebateVol',
                        'apiReferral/traderSummary',
                    ],
                    'post': [
                        'batchOrders',
                        'positionSide/dual',
                        'positionMargin',
                        'marginType',
                        'order',
                        'leverage',
                        'listenKey',
                        'countdownCancelAll',
                        # broker endpoints
                        'apiReferral/customization',
                        'apiReferral/userCustomization',
                    ],
                    'put': [
                        'listenKey',
                    ],
                    'delete': [
                        'batchOrders',
                        'order',
                        'allOpenOrders',
                        'listenKey',
                    ],
                },
                'fapiPrivateV2': {
                    'get': [
                        'account',
                        'balance',
                        'positionRisk',
                    ],
                },
                'v3': {
                    'get': [
                        'ticker/price',
                        'ticker/bookTicker',
                    ],
                },
                'public': {
                    'get': [
                        'ping',
                        'time',
                        'depth',
                        'trades',
                        'aggTrades',
                        'historicalTrades',
                        'klines',
                        'ticker/24hr',
                        'ticker/price',
                        'ticker/bookTicker',
                        'exchangeInfo',
                    ],
                    'put': ['userDataStream'],
                    'post': ['userDataStream'],
                    'delete': ['userDataStream'],
                },
                'private': {
                    'get': [
                        'allOrderList',  # oco
                        'openOrderList',  # oco
                        'orderList',  # oco
                        'order',
                        'openOrders',
                        'allOrders',
                        'account',
                        'myTrades',
                    ],
                    'post': [
                        'order/oco',
                        'order',
                        'order/test',
                    ],
                    'delete': [
                        'openOrders',  # added on 2020-04-25 for canceling all open orders per symbol
                        'orderList',  # oco
                        'order',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'feeSide': 'get',
                    'tierBased': False,
                    'percentage': True,
                    'taker': 0.001,
                    'maker': 0.001,
                },
            },
            'commonCurrencies': {
                'BCC': 'BCC',  # kept for backward-compatibility https://github.com/ccxt/ccxt/issues/4848
                'YOYO': 'YOYOW',
            },
            # exchange-specific options
            'options': {
                'fetchCurrencies': False,  # 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,
                'recvWindow': 5 * 1000,  # 5 sec, binance default
                'timeDifference': 0,  # the difference between system clock and Binance clock
                'adjustForTimeDifference': False,  # controls the adjustment logic upon instantiation
                'parseOrderToPrecision': False,  # force amounts and costs in parseOrder to precision
                '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',
                    'margin': 'MARGIN',
                    'future': 'UMFUTURE',
                    'delivery': 'CMFUTURE',
                    'mining': 'MINING',
                },
                'typesByAccount': {
                    'MAIN': 'spot',
                    'MARGIN': 'margin',
                    'UMFUTURE': 'future',
                    'CMFUTURE': 'delivery',
                    'MINING': 'mining',
                },
            },
            # https://binance-docs.github.io/apidocs/spot/en/#error-codes-2
            'exceptions': {
                '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.': DDoSProtection,  # {"msg":"Too many requests. Please try again later.","success":false}
                '-1000': ExchangeNotAvailable,  # {"code":-1000,"msg":"An unknown error occured while processing the request."}
                '-1001': ExchangeNotAvailable,  # 'Internal error; unable to process your request. Please try again.'
                '-1002': AuthenticationError,  # '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."}
                '-1013': InvalidOrder,  # createOrder -> 'invalid quantity'/'invalid price'/MIN_NOTIONAL
                '-1015': RateLimitExceeded,  # 'Too many new orders; current limit is %s orders per %s.'
                '-1016': ExchangeNotAvailable,  # 'This service is no longer available.',
                '-1020': BadRequest,  # 'This operation is not supported.'
                '-1021': InvalidNonce,  # 'your time is ahead of server'
                '-1022': AuthenticationError,  # {"code":-1022,"msg":"Signature for self request is not valid."}
                '-1100': BadRequest,  # createOrder(symbol, 1, asdf) -> 'Illegal characters found in parameter 'price'
                '-1101': BadRequest,  # Too many parameters; expected %s and received %s.
                '-1102': BadRequest,  # Param %s or %s must be sent, but both were empty
                '-1103': BadRequest,  # An unknown parameter was sent.
                '-1104': BadRequest,  # Not all sent parameters were read, read 8 parameters but was sent 9
                '-1105': BadRequest,  # Parameter %s was empty.
                '-1106': BadRequest,  # Parameter %s sent when not required.
                '-1111': BadRequest,  # Precision is over the maximum defined for self asset.
                '-1112': InvalidOrder,  # No orders on book for symbol.
                '-1114': BadRequest,  # TimeInForce parameter sent when not required.
                '-1115': BadRequest,  # Invalid timeInForce.
                '-1116': BadRequest,  # Invalid orderType.
                '-1117': BadRequest,  # Invalid side.
                '-1118': BadRequest,  # New client order ID was empty.
                '-1119': BadRequest,  # Original client order ID was empty.
                '-1120': BadRequest,  # Invalid interval.
                '-1121': BadSymbol,  # Invalid symbol.
                '-1125': AuthenticationError,  # This listenKey does not exist.
                '-1127': BadRequest,  # More than %s hours between startTime and endTime.
                '-1128': BadRequest,  # {"code":-1128,"msg":"Combination of optional parameters invalid."}
                '-1130': BadRequest,  # Data sent for paramter %s is not valid.
                '-1131': BadRequest,  # recvWindow must be less than 60000
                '-2010': ExchangeError,  # generic error code for createOrder -> 'Account has insufficient balance for requested action.', {"code":-2010,"msg":"Rest API trading is not enabled."}, etc...
                '-2011': OrderNotFound,  # cancelOrder(1, 'BTC/USDT') -> 'UNKNOWN_ORDER'
                '-2013': OrderNotFound,  # fetchOrder(1, 'BTC/USDT') -> 'Order does not exist'
                '-2014': AuthenticationError,  # {"code":-2014, "msg": "API-key format invalid."}
                '-2015': AuthenticationError,  # "Invalid API-key, IP, or permissions for action."
                '-2019': InsufficientFunds,  # {"code":-2019,"msg":"Margin is insufficient."}
                '-3005': InsufficientFunds,  # {"code":-3005,"msg":"Transferring out not allowed. Transfer out amount exceeds max amount."}
                '-3008': InsufficientFunds,  # {"code":-3008,"msg":"Borrow not allowed. Your borrow amount has exceed maximum borrow amount."}
                '-3010': ExchangeError,  # {"code":-3010,"msg":"Repay not allowed. Repay amount exceeds borrow amount."}
                '-3022': AccountSuspended,  # You account's trading is banned.
                '-4028': BadRequest,  # {"code":-4028,"msg":"Leverage 100 is not valid"}
                '-3020': InsufficientFunds,  # {"code":-3020,"msg":"Transfer out amount exceeds max amount."}
                '-3041': InsufficientFunds,  # {"code":-3041,"msg":"Balance is not enough"}
                '-5013': InsufficientFunds,  # Asset transfer failed: insufficient balance"
            },
        })

    def currency_to_precision(self, currency, fee):
        return self.number_to_string(fee)

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

    async def fetch_time(self, params={}):
        type = self.safe_string_2(self.options, 'fetchTime', 'defaultType', 'spot')
        method = 'publicGetTime'
        if type == 'future':
            method = 'fapiPublicGetTime'
        elif type == 'delivery':
            method = 'dapiPublicGetTime'
        response = await getattr(self, method)(params)
        return self.safe_integer(response, 'serverTime')

    async def load_time_difference(self, params={}):
        serverTime = await self.fetch_time(params)
        after = self.milliseconds()
        self.options['timeDifference'] = after - serverTime
        return self.options['timeDifference']

    async def fetch_currencies(self, params={}):
        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 = await 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,
                'fee': fee,
                'fees': fees,
                'limits': self.limits,
            }
        return result

    async def fetch_markets(self, params={}):
        defaultType = self.safe_string_2(self.options, 'fetchMarkets', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        query = self.omit(params, 'type')
        if (type != 'spot') and (type != 'future') and (type != 'margin') and (type != '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 type == 'future':
            method = 'fapiPublicGetExchangeInfo'
        elif type == 'delivery':
            method = 'dapiPublicGetExchangeInfo'
        response = await 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,
        #                 "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_ALGO_ORDERS","maxNumAlgoOrders":5}
        #                 ]
        #             },
        #         ],
        #     }
        #
        # 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']:
            await self.load_time_difference()
        markets = self.safe_value(response, 'symbols', [])
        result = []
        for i in range(0, len(markets)):
            market = markets[i]
            spot = (type == 'spot')
            future = (type == 'future')
            delivery = (type == 'delivery')
            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')
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            contractType = self.safe_string(market, 'contractType')
            idSymbol = (future or delivery) and (contractType != 'PERPETUAL')
            symbol = id if idSymbol else (base + '/' + quote)
            filters = self.safe_value(market, 'filters', [])
            filtersByType = self.index_by(filters, 'filterType')
            precision = {
                'base': self.safe_integer(market, 'baseAssetPrecision'),
                'quote': self.safe_integer(market, 'quotePrecision'),
                'amount': self.safe_integer(market, 'baseAssetPrecision'),
                'price': self.safe_integer(market, 'quotePrecision'),
            }
            status = self.safe_string_2(market, 'status', 'contractStatus')
            active = (status == 'TRADING')
            margin = self.safe_value(market, 'isMarginTradingAllowed', future or delivery)
            contractSize = self.safe_float(market, 'contractSize')
            entry = {
                'id': id,
                'lowercaseId': lowercaseId,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'baseId': baseId,
                'quoteId': quoteId,
                'info': market,
                'type': type,
                'spot': spot,
                'margin': margin,
                'future': future,
                'delivery': delivery,
                'active': active,
                'precision': precision,
                'contractSize': contractSize,
                'limits': {
                    'amount': {
                        'min': math.pow(10, -precision['amount']),
                        'max': None,
                    },
                    'price': {
                        'min': None,
                        'max': None,
                    },
                    'cost': {
                        'min': None,
                        'max': None,
                    },
                },
            }
            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

    async def fetch_balance(self, params={}):
        await self.load_markets()
        defaultType = self.safe_string_2(self.options, 'fetchBalance', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        method = 'privateGetAccount'
        if type == 'future':
            options = self.safe_value(self.options, 'future', {})
            fetchBalanceOptions = self.safe_value(options, 'fetchBalance', {})
            method = self.safe_string(fetchBalanceOptions, 'method', 'fapiPrivateV2GetAccount')
        elif type == 'delivery':
            options = self.safe_value(self.options, 'delivery', {})
            fetchBalanceOptions = self.safe_value(options, 'fetchBalance', {})
            method = self.safe_string(fetchBalanceOptions, 'method', 'dapiPrivateGetAccount')
        elif type == 'margin':
            method = 'sapiGetMarginAccount'
        query = self.omit(params, 'type')
        response = await getattr(self, method)(query)
        #
        # 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
        #
        #     {
        #         "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"}
        #         ],
        #     }
        #
        # 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"
        #         }
        #     ]
        #
        result = {
            'info': response,
        }
        timestamp = None
        if (type == 'spot') or (type == 'margin'):
            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
        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 self.parse_balance(result, False)

    async def fetch_order_book(self, symbol, limit=None, params={}):
        await 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-exchange/binance-official-api-docs/blob/master/rest-api.md#order-book
        method = 'publicGetDepth'
        if market['future']:
            method = 'fapiPublicGetDepth'
        elif market['delivery']:
            method = 'dapiPublicGetDepth'
        response = await 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"],["2493.37","0.280"],["2493.31","0.865"],["2493.30","0.514"],["2493.29","2.309"],["2493.25","1.500"],["2493.23","0.012"],["2493.22","7.240"],["2493.21","3.349"],["2493.20","2.030"],["2493.19","58.118"],["2493.18","174.836"],["2493.17","14.436"],["2493.12","2.000"],["2493.09","3.232"],["2493.08","2.010"],["2493.07","2.000"],["2493.06","2.000"],["2493.05","2.684"],["2493.04","2.000"],["2493.03","2.000"],["2493.02","5.000"],["2493.01","2.000"],["2493.00","1.035"],["2492.99","8.546"],["2492.98","4.012"],["2492.96","40.937"],["2492.95","40.595"],["2492.94","21.051"],["2492.92","4.012"],["2492.91","0.200"],["2492.85","2.000"],["2492.83","24.929"],["2492.81","50.000"],["2492.80","0.030"],["2492.76","0.264"],["2492.73","32.098"],["2492.71","32.664"],["2492.70","4.228"],["2492.65","1.230"],["2492.61","5.598"],["2492.60","34.786"],["2492.58","10.393"],["2492.54","4.543"],["2492.50","0.400"],["2492.49","0.600"],["2492.48","4.941"],["2492.45","1.207"],["2492.43","4.878"],["2492.40","4.762"],["2492.39","36.489"],["2492.37","3.000"],["2492.36","4.882"],["2492.33","28.117"],["2492.29","0.490"],["2492.28","76.365"],["2492.27","0.200"],["2492.23","3.804"],["2492.22","1.000"],["2492.19","20.011"],["2492.17","13.500"],["2492.16","4.058"],["2492.14","35.673"],["2492.13","1.915"],["2492.12","76.896"],["2492.10","8.050"],["2492.01","16.615"],["2492.00","10.335"],["2491.95","5.880"],["2491.93","10.000"],["2491.92","3.916"],["2491.90","0.795"],["2491.87","22.000"],["2491.85","1.260"],["2491.84","4.014"],["2491.83","6.668"],["2491.73","0.855"],["2491.72","7.572"],["2491.71","7.000"],["2491.68","3.916"],["2491.66","2.500"],["2491.64","4.945"],["2491.63","2.302"],["2491.62","4.012"],["2491.61","16.170"],["2491.60","0.793"],["2491.59","0.403"],["2491.57","17.445"],["2491.56","88.177"],["2491.53","10.000"],["2491.47","0.013"],["2491.45","0.157"],["2491.44","11.733"],["2491.39","3.593"],["2491.38","3.570"],["2491.36","28.077"],["2491.35","0.808"],["2491.30","0.065"],["2491.29","4.880"],["2491.27","22.000"],["2491.24","9.021"],["2491.23","68.393"],["2491.22","0.050"],["2491.21","1.316"],["2491.20","4.000"],["2491.19","0.108"],["2491.18","0.498"],["2491.17","5.000"],["2491.14","10.000"],["2491.13","0.383"],["2491.12","125.959"],["2491.10","0.870"],["2491.08","10.518"],["2491.05","54.743"],["2491.01","7.980"],["2490.96","3.916"],["2490.95","0.135"],["2490.91","0.140"],["2490.89","8.424"],["2490.88","5.930"],["2490.84","1.208"],["2490.83","2.005"],["2490.82","5.517"],["2490.81","73.707"],["2490.80","1.042"],["2490.79","9.626"],["2490.72","3.916"],["2490.70","0.148"],["2490.69","0.403"],["2490.68","0.012"],["2490.67","21.887"],["2490.66","0.008"],["2490.64","11.500"],["2490.61","0.005"],["2490.58","68.175"],["2490.55","0.218"],["2490.54","14.132"],["2490.53","5.157"],["2490.50","0.018"],["2490.49","9.216"],["2490.48","3.979"],["2490.47","1.884"],["2490.44","0.003"],["2490.36","14.132"],["2490.35","2.008"],["2490.34","0.200"],["2490.33","0.015"],["2490.30","0.065"],["2490.29","5.500"],["2490.28","24.203"],["2490.26","4.373"],["2490.25","0.026"],["2490.24","4.000"],["2490.23","177.628"],["2490.22","14.132"],["2490.21","0.181"],["2490.20","0.645"],["2490.19","9.024"],["2490.18","0.108"],["2490.17","0.085"],["2490.16","0.077"],["2490.14","0.275"],["2490.10","0.080"],["2490.07","0.015"],["2490.04","6.056"],["2490.00","6.796"],["2489.98","0.005"],["2489.97","0.258"],["2489.96","10.084"],["2489.95","1.202"],["2489.91","10.121"],["2489.90","10.084"],["2489.88","0.040"],["2489.87","0.004"],["2489.85","0.003"],["2489.76","3.916"],["2489.73","10.084"],["2489.71","0.272"],["2489.70","12.834"],["2489.67","0.403"],["2489.66","0.362"],["2489.64","0.738"],["2489.63","193.236"],["2489.62","14.152"],["2489.61","0.157"],["2489.59","4.011"],["2489.57","0.015"],["2489.55","0.046"],["2489.52","3.921"],["2489.51","0.005"],["2489.45","80.000"],["2489.44","0.649"],["2489.43","10.088"],["2489.39","0.009"],["2489.37","14.132"],["2489.35","72.262"],["2489.34","10.084"],["2489.33","14.136"],["2489.32","23.953"],["2489.30","0.065"],["2489.28","8.136"],["2489.24","8.022"],["2489.19","14.132"],["2489.18","0.085"],["2489.17","0.108"],["2489.14","10.084"],["2489.13","3.142"],["2489.12","77.827"],["2489.11","10.084"],["2489.10","0.080"],["2489.09","50.024"],["2489.04","3.916"],["2489.03","0.008"],["2489.01","10.084"],["2488.99","0.135"],["2488.98","0.187"],["2488.96","0.324"],["2488.92","0.064"],["2488.85","16.056"],["2488.83","14.132"],["2488.80","3.916"],["2488.79","10.084"],["2488.77","4.414"],["2488.76","0.005"],["2488.75","13.685"],["2488.73","0.020"],["2488.69","0.157"],["2488.60","80.000"],["2488.58","10.164"],["2488.57","0.004"],["2488.56","3.933"],["2488.54","3.311"],["2488.51","12.814"],["2488.50","80.099"],["2488.48","0.684"],["2488.44","0.024"],["2488.42","68.180"],["2488.39","4.412"],["2488.38","26.138"],["2488.34","44.134"],["2488.32","8.014"],["2488.30","0.065"],["2488.29","0.009"],["2488.27","4.513"],["2488.26","4.222"],["2488.25","80.000"],["2488.23","0.007"],["2488.22","0.281"],["2488.19","0.100"],["2488.18","80.100"],["2488.17","80.000"],["2488.16","8.197"],["2488.15","79.184"],["2488.13","0.025"],["2488.11","0.050"],["2488.10","0.080"],["2488.08","3.919"],["2488.04","40.103"],["2488.03","0.120"],["2488.02","0.008"],["2488.01","0.140"],["2488.00","0.406"],["2487.99","0.384"],["2487.98","0.060"],["2487.96","8.010"],["2487.94","0.246"],["2487.93","0.020"],["2487.91","0.136"],["2487.87","0.403"],["2487.84","17.910"],["2487.81","0.005"],["2487.80","0.073"],["2487.74","36.000"],["2487.73","3.225"],["2487.72","0.018"],["2487.71","0.319"],["2487.70","0.006"],["2487.66","0.003"],["2487.64","0.003"],["2487.63","0.008"],["2487.62","0.040"],["2487.60","3.916"],["2487.54","0.805"],["2487.52","0.022"],["2487.51","0.003"],["2487.50","0.051"],["2487.49","6.081"],["2487.47","80.015"],["2487.46","4.735"],["2487.45","30.000"],["2487.41","0.096"],["2487.40","0.078"],["2487.39","0.103"],["2487.37","2.279"],["2487.36","8.152"],["2487.35","2.145"],["2487.32","12.816"],["2487.31","10.023"],["2487.30","0.157"],["2487.27","0.005"],["2487.26","4.010"],["2487.25","0.008"],["2487.24","0.003"],["2487.23","0.014"],["2487.20","0.085"],["2487.17","0.011"],["2487.14","3.217"],["2487.12","3.916"],["2487.11","0.300"],["2487.10","0.088"],["2487.08","10.097"],["2487.07","1.467"],["2487.04","0.600"],["2487.01","18.363"],["2487.00","0.292"],["2486.99","0.014"],["2486.98","0.144"],["2486.97","0.443"],["2486.92","0.005"],["2486.91","0.016"],["2486.89","3.364"],["2486.88","4.166"],["2486.84","24.306"],["2486.83","0.181"],["2486.81","0.015"],["2486.80","0.082"],["2486.79","0.007"],["2486.76","0.011"],["2486.74","0.050"],["2486.73","0.782"],["2486.72","0.004"],["2486.69","0.003"],["2486.68","8.018"],["2486.66","10.004"],["2486.65","40.391"],["2486.64","3.916"],["2486.61","0.489"],["2486.60","0.196"],["2486.57","0.396"],["2486.55","4.015"],["2486.51","3.000"],["2486.50","0.003"],["2486.48","0.005"],["2486.47","0.010"],["2486.45","4.011"],["2486.44","0.602"],["2486.43","0.566"],["2486.42","3.140"],["2486.40","3.958"],["2486.39","0.003"],["2486.34","0.010"],["2486.31","6.281"],["2486.27","0.005"],["2486.26","0.004"],["2486.23","10.088"],["2486.22","0.015"],["2486.17","0.030"],["2486.16","3.916"],["2486.15","0.020"],["2486.13","13.130"],["2486.12","82.414"],["2486.11","0.244"],["2486.10","0.132"],["2486.08","0.720"],["2486.06","0.385"],["2486.01","0.004"],["2486.00","2.359"],["2485.99","154.159"],["2485.98","20.054"],["2485.96","1.000"],["2485.95","0.190"],["2485.92","4.463"],["2485.90","1.557"],["2485.87","0.402"],["2485.85","0.114"],["2485.81","0.900"],["2485.76","4.700"],["2485.75","0.300"],["2485.74","0.196"],["2485.73","4.010"],["2485.72","0.323"],["2485.70","0.263"],["2485.69","0.261"],["2485.68","3.688"],["2485.67","0.005"],["2485.64","1.216"],["2485.63","0.005"],["2485.62","0.015"],["2485.61","0.033"],["2485.60","0.004"],["2485.58","2.012"],["2485.56","0.020"],["2485.54","0.699"],["2485.52","0.003"],["2485.51","1.830"],["2485.48","5.964"],["2485.47","0.015"],["2485.44","7.251"],["2485.43","0.006"],["2485.42","0.644"],["2485.40","8.026"],["2485.38","0.489"],["2485.36","0.014"],["2485.35","0.005"],["2485.31","1.507"],["2485.30","2.107"],["2485.29","0.039"],["2485.28","0.642"],["2485.26","1.990"],["2485.25","4.996"],["2485.23","0.003"],["2485.22","0.277"],["2485.21","0.121"],["2485.20","3.952"],["2485.18","0.006"],["2485.17","0.043"],["2485.15","4.008"],["2485.14","4.434"],["2485.13","1.003"],["2485.05","0.204"],["2485.04","0.254"],["2485.02","5.000"],["2485.01","0.050"],["2485.00","80.821"],["2484.96","3.941"],["2484.95","10.023"],["2484.94","13.935"],["2484.92","0.059"],["2484.90","150.000"],["2484.89","0.004"],["2484.88","150.127"],["2484.87","0.004"],["2484.85","0.100"],["2484.83","0.006"],["2484.82","0.030"],["2484.81","1.246"],["2484.80","0.003"],["2484.79","0.045"],["2484.77","0.003"],["2484.74","0.036"],["2484.72","3.919"],["2484.70","0.134"],["2484.68","1.111"],["2484.66","76.955"],["2484.60","2.580"],["2484.59","31.432"],["2484.58","1.468"],["2484.55","1.153"],["2484.54","0.265"],["2484.53","20.024"],["2484.51","1.047"],["2484.50","0.818"],["2484.49","0.022"],["2484.48","3.887"],["2484.46","0.048"],["2484.45","0.224"],["2484.44","0.174"],["2484.43","223.079"],["2484.42","0.014"],["2484.41","1.115"],["2484.39","26.090"],["2484.38","0.066"],["2484.37","0.121"],["2484.34","0.255"],["2484.33","23.968"],["2484.29","0.085"],["2484.27","1.128"],["2484.26","1.456"],["2484.24","3.916"],["2484.23","28.126"],["2484.22","1.329"],["2484.19","2.015"],["2484.18","0.263"],["2484.15","15.489"],["2484.14","1.135"],["2484.13","0.572"],["2484.12","8.032"],["2484.11","0.021"],["2484.09","0.059"],["2484.08","0.038"],["2484.07","0.147"],["2484.05","24.156"],["2484.04","0.008"],["2484.01","1.184"],["2484.00","4.641"],["2483.99","0.006"],["2483.97","0.294"],["2483.96","0.424"],["2483.94","3.660"],["2483.93","2.067"],["2483.92","0.008"],["2483.89","0.141"],["2483.88","1.089"],
        #             ["2483.87","110.000"],["2483.85","4.018"],["2483.81","150.077"],["2483.80","0.003"],["2483.77","0.020"]
        #         ],
        #         "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
        #     }
        #
        timestamp = self.safe_integer(ticker, 'closeTime')
        marketId = self.safe_string(ticker, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        last = self.safe_number(ticker, 'lastPrice')
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_number(ticker, 'highPrice'),
            'low': self.safe_number(ticker, 'lowPrice'),
            'bid': self.safe_number(ticker, 'bidPrice'),
            'bidVolume': self.safe_number(ticker, 'bidQty'),
            'ask': self.safe_number(ticker, 'askPrice'),
            'askVolume': self.safe_number(ticker, 'askQty'),
            'vwap': self.safe_number(ticker, 'weightedAvgPrice'),
            'open': self.safe_number(ticker, 'openPrice'),
            'close': last,
            'last': last,
            'previousClose': self.safe_number(ticker, 'prevClosePrice'),  # previous day close
            'change': self.safe_number(ticker, 'priceChange'),
            'percentage': self.safe_number(ticker, 'priceChangePercent'),
            'average': None,
            'baseVolume': self.safe_number(ticker, 'volume'),
            'quoteVolume': self.safe_number(ticker, 'quoteVolume'),
            'info': ticker,
        }

    async def fetch_status(self, params={}):
        response = await self.wapiGetSystemStatus(params)
        status = self.safe_string(response, 'status')
        if status is not None:
            status = 'ok' if (status == '0') else 'maintenance'
            self.status = self.extend(self.status, {
                'status': status,
                'updated': self.milliseconds(),
            })
        return self.status

    async def fetch_ticker(self, symbol, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        method = 'publicGetTicker24hr'
        if market['future']:
            method = 'fapiPublicGetTicker24hr'
        elif market['delivery']:
            method = 'dapiPublicGetTicker24hr'
        response = await 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)

    async def fetch_bids_asks(self, symbols=None, params={}):
        await 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 = await getattr(self, method)(query)
        return self.parse_tickers(response, symbols)

    async def fetch_tickers(self, symbols=None, params={}):
        await 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 = await getattr(self, method)(query)
        return self.parse_tickers(response, symbols)

    def parse_ohlcv(self, ohlcv, market=None):
        #
        #     [
        #         1591478520000,
        #         "0.02501300",
        #         "0.02501800",
        #         "0.02500000",
        #         "0.02500000",
        #         "22.19000000",
        #         1591478579999,
        #         "0.55490906",
        #         40,
        #         "10.92900000",
        #         "0.27336462",
        #         "0"
        #     ]
        #
        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),
        ]

    async def fetch_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
        await 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
        limit = defaultLimit if (limit is None) else min(limit, maxLimit)
        request = {
            'symbol': market['id'],
            'interval': self.timeframes[timeframe],
            'limit': limit,
        }
        if since is not None:
            request['startTime'] = since
        method = 'publicGetKlines'
        if market['future']:
            method = 'fapiPublicGetKlines'
        elif market['delivery']:
            method = 'dapiPublicGetKlines'
        response = await 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
        #     }
        #
        timestamp = self.safe_integer_2(trade, 'T', 'time')
        priceString = self.safe_string_2(trade, 'p', 'price')
        amountString = self.safe_string_2(trade, 'q', 'qty')
        price = self.parse_number(priceString)
        amount = self.parse_number(amountString)
        cost = self.parse_number(Precise.string_mul(priceString, amountString))
        id = self.safe_string_2(trade, 't', 'a')
        id = self.safe_string(trade, 'id', id)
        side = None
        orderId = self.safe_string(trade, 'orderId')
        if 'm' in trade:
            side = 'sell' if trade['m'] else 'buy'  # self is reversed intentionally
        elif 'isBuyerMaker' in trade:
            side = 'sell' if trade['isBuyerMaker'] else 'buy'
        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_number(trade, 'commission'),
                'currency': self.safe_currency_code(self.safe_string(trade, 'commissionAsset')),
            }
        takerOrMaker = None
        if 'isMaker' in trade:
            takerOrMaker = 'maker' if trade['isMaker'] else 'taker'
        if 'maker' in trade:
            takerOrMaker = 'maker' if trade['maker'] else 'taker'
        marketId = self.safe_string(trade, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        return {
            '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,
        }

    async def fetch_trades(self, symbol, since=None, limit=None, params={}):
        await 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
        }
        defaultType = self.safe_string_2(self.options, 'fetchTrades', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        query = self.omit(params, 'type')
        defaultMethod = None
        if type == 'future':
            defaultMethod = 'fapiPublicGetAggTrades'
        elif type == 'delivery':
            defaultMethod = 'dapiPublicGetAggTrades'
        else:
            defaultMethod = 'publicGetAggTrades'
        method = self.safe_string(self.options, 'fetchTradesMethod', defaultMethod)
        if method == 'publicGetAggTrades':
            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 type == 'future':
                method = 'fapiPublicGetAggTrades'
            elif type == 'delivery':
                method = 'dapiPublicGetAggTrades'
        elif method == 'publicGetHistoricalTrades':
            if type == 'future':
                method = 'fapiPublicGetHistoricalTrades'
            elif type == 'delivery':
                method = 'dapiPublicGetHistoricalTrades'
        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 = await 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
        #         }
        #       ]
        #     }
        #
        status = self.parse_order_status(self.safe_string(order, 'status'))
        marketId = self.safe_string(order, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        timestamp = None
        if 'time' in order:
            timestamp = self.safe_integer(order, 'time')
        elif 'transactTime' in order:
            timestamp = self.safe_integer(order, 'transactTime')
        price = self.safe_number(order, 'price')
        amount = self.safe_number(order, 'origQty')
        filled = self.safe_number(order, 'executedQty')
        # - 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_number_2(order, 'cummulativeQuoteQty', 'cumQuote')
        id = self.safe_string(order, 'orderId')
        type = self.safe_string_lower(order, 'type')
        if type == 'limit_maker':
            type = 'limit'
        side = self.safe_string_lower(order, 'side')
        fills = self.safe_value(order, 'fills', [])
        trades = self.parse_trades(fills, market)
        clientOrderId = self.safe_string(order, 'clientOrderId')
        timeInForce = self.safe_string(order, 'timeInForce')
        postOnly = (type == 'limit_maker') or (timeInForce == 'GTX')
        stopPrice = self.safe_number(order, 'stopPrice')
        return self.safe_order({
            'info': order,
            'id': id,
            'clientOrderId': clientOrderId,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': None,
            'symbol': symbol,
            'type': type,
            'timeInForce': timeInForce,
            'postOnly': postOnly,
            'side': side,
            'price': price,
            'stopPrice': stopPrice,
            'amount': amount,
            'cost': cost,
            'average': None,
            'filled': filled,
            'remaining': None,
            'status': status,
            'fee': None,
            'trades': trades,
        })

    async def create_order(self, symbol, type, side, amount, price=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        defaultType = self.safe_string_2(self.options, 'createOrder', 'defaultType', market['type'])
        orderType = self.safe_string(params, 'type', defaultType)
        clientOrderId = self.safe_string_2(params, 'newClientOrderId', 'clientOrderId')
        params = self.omit(params, ['type', 'newClientOrderId', 'clientOrderId'])
        method = 'privatePostOrder'
        if orderType == 'future':
            method = 'fapiPrivatePostOrder'
        elif orderType == 'delivery':
            method = 'dapiPrivatePostOrder'
        elif orderType == 'margin':
            method = 'sapiPostMarginOrder'
        # the next 5 lines are added to support for testing orders
        if market['spot']:
            test = self.safe_value(params, 'test', False)
            if test:
                method += 'Test'
            params = self.omit(params, 'test')
        uppercaseType = type.upper()
        validOrderTypes = self.safe_value(market['info'], 'orderTypes')
        if not self.in_array(uppercaseType, validOrderTypes):
            raise InvalidOrder(self.id + ' ' + type + ' is not a valid order type in ' + market['type'] + ' market ' + symbol)
        request = {
            'symbol': market['id'],
            'type': uppercaseType,
            'side': side.upper(),
        }
        if clientOrderId is None:
            broker = self.safe_value(self.options, 'broker')
            if broker:
                brokerId = self.safe_string(broker, orderType)
                if brokerId is not None:
                    request['newClientOrderId'] = brokerId + self.uuid22()
        else:
            request['newClientOrderId'] = clientOrderId
        if (orderType == 'spot') or (orderType == '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':
            quoteOrderQty = self.safe_value(self.options, 'quoteOrderQty', False)
            if quoteOrderQty:
                quoteOrderQty = self.safe_number(params, 'quoteOrderQty')
                precision = market['precision']['price']
                if quoteOrderQty is not None:
                    request['quoteOrderQty'] = self.decimal_to_precision(quoteOrderQty, TRUNCATE, precision, self.precisionMode)
                    params = self.omit(params, 'quoteOrderQty')
                elif price is not None:
                    request['quoteOrderQty'] = self.decimal_to_precision(amount * price, TRUNCATE, precision, self.precisionMode)
                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['future'] or market['delivery']:
                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(params, 'closePosition')
            if closePosition is None:
                quantityIsRequired = True
            stopPriceIsRequired = True
        elif uppercaseType == 'TRAILING_STOP_MARKET':
            quantityIsRequired = True
            callbackRate = self.safe_number(params, '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:
            stopPrice = self.safe_number(params, 'stopPrice')
            if stopPrice is None:
                raise InvalidOrder(self.id + ' createOrder() requires a stopPrice extra param for a ' + type + ' order')
            else:
                params = self.omit(params, 'stopPrice')
                request['stopPrice'] = self.price_to_precision(symbol, stopPrice)
        response = await getattr(self, method)(self.extend(request, params))
        return self.parse_order(response, market)

    async def fetch_order(self, id, symbol=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrder() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        defaultType = self.safe_string_2(self.options, 'fetchOrder', 'defaultType', market['type'])
        type = self.safe_string(params, 'type', defaultType)
        method = 'privateGetOrder'
        if type == 'future':
            method = 'fapiPrivateGetOrder'
        elif type == 'delivery':
            method = 'dapiPrivateGetOrder'
        elif type == 'margin':
            method = 'sapiGetMarginOrder'
        request = {
            'symbol': market['id'],
        }
        clientOrderId = self.safe_value_2(params, 'origClientOrderId', 'clientOrderId')
        if clientOrderId is not None:
            request['origClientOrderId'] = clientOrderId
        else:
            request['orderId'] = id
        query = self.omit(params, ['type', 'clientOrderId', 'origClientOrderId'])
        response = await getattr(self, method)(self.extend(request, query))
        return self.parse_order(response, market)

    async def fetch_orders(self, symbol=None, since=None, limit=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrders() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        defaultType = self.safe_string_2(self.options, 'fetchOrders', 'defaultType', market['type'])
        type = self.safe_string(params, 'type', defaultType)
        method = 'privateGetAllOrders'
        if type == 'future':
            method = 'fapiPrivateGetAllOrders'
        elif type == 'delivery':
            method = 'dapiPrivateGetAllOrders'
        elif type == 'margin':
            method = 'sapiGetMarginAllOrders'
        request = {
            'symbol': market['id'],
        }
        if since is not None:
            request['startTime'] = since
        if limit is not None:
            request['limit'] = limit
        query = self.omit(params, 'type')
        response = await getattr(self, method)(self.extend(request, query))
        #
        #  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)

    async def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        await self.load_markets()
        market = None
        query = None
        type = None
        request = {}
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
            defaultType = self.safe_string_2(self.options, 'fetchOpenOrders', 'defaultType', market['type'])
            type = self.safe_string(params, 'type', defaultType)
            query = self.omit(params, 'type')
        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(params, 'type', defaultType)
            query = self.omit(params, 'type')
        method = 'privateGetOpenOrders'
        if type == 'future':
            method = 'fapiPrivateGetOpenOrders'
        elif type == 'delivery':
            method = 'dapiPrivateGetOpenOrders'
        elif type == 'margin':
            method = 'sapiGetMarginOpenOrders'
        response = await getattr(self, method)(self.extend(request, query))
        return self.parse_orders(response, market, since, limit)

    async def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
        orders = await self.fetch_orders(symbol, since, limit, params)
        return self.filter_by(orders, 'status', 'closed')

    async def cancel_order(self, id, symbol=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        defaultType = self.safe_string_2(self.options, 'fetchOpenOrders', 'defaultType', market['type'])
        type = self.safe_string(params, 'type', defaultType)
        # https://github.com/ccxt/ccxt/issues/6507
        origClientOrderId = self.safe_value_2(params, 'origClientOrderId', 'clientOrderId')
        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':
            method = 'sapiDeleteMarginOrder'
        query = self.omit(params, ['type', 'origClientOrderId', 'clientOrderId'])
        response = await getattr(self, method)(self.extend(request, query))
        return self.parse_order(response)

    async def cancel_all_orders(self, symbol=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelAllOrders() requires a symbol argument')
        await 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)
        query = self.omit(params, 'type')
        method = 'privateDeleteOpenOrders'
        if type == 'margin':
            method = 'sapiDeleteMarginOpenOrders'
        elif type == 'future':
            method = 'fapiPrivateDeleteAllOpenOrders'
        elif type == 'delivery':
            method = 'dapiPrivateDeleteAllOpenOrders'
        response = await getattr(self, method)(self.extend(request, query))
        if isinstance(response, list):
            return self.parse_orders(response, market)
        else:
            return response

    async def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchMyTrades() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        defaultType = self.safe_string_2(self.options, 'fetchMyTrades', 'defaultType')
        type = self.safe_string(params, 'type', defaultType)
        params = self.omit(params, 'type')
        method = None
        if type == 'spot':
            method = 'privateGetMyTrades'
        elif type == 'margin':
            method = 'sapiGetMarginMyTrades'
        elif type == 'future':
            method = 'fapiPrivateGetUserTrades'
        elif type == 'delivery':
            method = 'dapiPrivateGetUserTrades'
        request = {
            'symbol': market['id'],
        }
        if since is not None:
            request['startTime'] = since
        if limit is not None:
            request['limit'] = limit
        response = await 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)

    async def fetch_my_dust_trades(self, symbol=None, since=None, limit=None, params={}):
        #
        # 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
        #
        await self.load_markets()
        response = await self.wapiGetUserAssetDribbletLog(params)
        # {success:    True,
        #   results: {total:    1,
        #               rows: [{    transfered_total: "1.06468458",
        #                         service_charge_total: "0.02172826",
        #                                      tran_id: 2701371634,
        #                                         logs: [{             tranId:  2701371634,
        #                                                   serviceChargeAmount: "0.00012819",
        #                                                                   uid: "35103861",
        #                                                                amount: "0.8012",
        #                                                           operateTime: "2018-10-07 17:56:07",
        #                                                      transferedAmount: "0.00628141",
        #                                                             fromAsset: "ADA"                  }],
        #                                 operate_time: "2018-10-07 17:56:06"                                }]}}
        results = self.safe_value(response, 'results', {})
        rows = self.safe_value(results, 'rows', [])
        data = []
        for i in range(0, len(rows)):
            logs = rows[i]['logs']
            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):
        # {             tranId:  2701371634,
        #   serviceChargeAmount: "0.00012819",
        #                   uid: "35103861",
        #                amount: "0.8012",
        #           operateTime: "2018-10-07 17:56:07",
        #      transferedAmount: "0.00628141",
        #             fromAsset: "ADA"                  },
        orderId = self.safe_string(trade, 'tranId')
        timestamp = self.parse8601(self.safe_string(trade, 'operateTime'))
        tradedCurrency = self.safe_currency_code(self.safe_string(trade, 'fromAsset'))
        earnedCurrency = self.currency('BNB')['code']
        applicantSymbol = earnedCurrency + '/' + tradedCurrency
        tradedCurrencyIsQuote = False
        if applicantSymbol in self.markets:
            tradedCurrencyIsQuote = True
        #
        # Warning
        # Binance dust trade `fee` is already excluded from the `BNB` earning reported in the `Dust Log`.
        # So the parser should either set the `fee.cost` to `0` or add it on top of the earned
        # BNB `amount`(or `cost` depending on the trade `side`). The second of the above options
        # is much more illustrative and therefore preferable.
        #
        fee = {
            'currency': earnedCurrency,
            'cost': self.safe_number(trade, 'serviceChargeAmount'),
        }
        symbol = None
        amount = None
        cost = None
        side = None
        if tradedCurrencyIsQuote:
            symbol = applicantSymbol
            amount = self.sum(self.safe_number(trade, 'transferedAmount'), fee['cost'])
            cost = self.safe_number(trade, 'amount')
            side = 'buy'
        else:
            symbol = tradedCurrency + '/' + earnedCurrency
            amount = self.safe_number(trade, 'amount')
            cost = self.sum(self.safe_number(trade, 'transferedAmount'), fee['cost'])
            side = 'sell'
        price = None
        if cost is not None:
            if amount:
                price = cost / amount
        id = None
        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,
        }

    async def fetch_deposits(self, code=None, since=None, limit=None, params={}):
        await self.load_markets()
        currency = None
        request = {}
        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 = await 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)

    async def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
        await self.load_markets()
        currency = None
        request = {}
        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 = await 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',
            },
            '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
            },
        }
        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
        #     }
        #
        id = self.safe_string(transaction, 'id')
        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(transaction, 'coin')
        code = self.safe_currency_code(currencyId, currency)
        timestamp = None
        insertTime = self.safe_integer(transaction, 'insertTime')
        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(transaction, 'transactionFee')
        fee = None
        if feeCost is not None:
            fee = {'currency': code, 'cost': feeCost}
        updated = self.safe_integer(transaction, 'successTime')
        internal = self.safe_integer(transaction, 'transferType', False)
        internal = True if internal else False
        return {
            'info': transaction,
            'id': id,
            'txid': txid,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            '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
        typesByAccount = self.safe_value(self.options, 'typesByAccount', {})
        if type is not None:
            parts = type.split('_')
            fromAccount = self.safe_value(parts, 0)
            toAccount = self.safe_value(parts, 1)
            fromAccount = self.safe_string(typesByAccount, fromAccount, fromAccount)
            toAccount = self.safe_string(typesByAccount, 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)
        return self.filter_by_since_limit(result, since, limit, 'timestamp')

    async def transfer(self, code, amount, fromAccount, toAccount, params={}):
        await self.load_markets()
        currency = self.currency(code)
        type = self.safe_string(params, 'type')
        if type is None:
            accountsByType = self.safe_value(self.options, 'accountsByType', {})
            fromId = self.safe_string(accountsByType, fromAccount, fromAccount)
            toId = self.safe_string(accountsByType, toAccount, toAccount)
            if fromId is None:
                keys = list(accountsByType.keys())
                raise ExchangeError(self.id + ' fromAccount must be one of ' + ', '.join(keys))
            if toId is None:
                keys = list(accountsByType.keys())
                raise ExchangeError(self.id + ' toAccount must be one of ' + ', '.join(keys))
            type = fromId + '_' + toId
        request = {
            'asset': currency['id'],
            'amount': self.currency_to_precision(code, amount),
            'type': type,
        }
        response = await self.sapiPostAssetTransfer(self.extend(request, params))
        #
        #     {
        #         "tranId":13526853623
        #     }
        #
        transfer = self.parse_transfer(response, currency)
        return self.extend(transfer, {
            'amount': amount,
            'currency': code,
            'fromAccount': fromAccount,
            'toAccount': toAccount,
        })

    async def fetch_transfers(self, code=None, since=None, limit=None, params={}):
        await self.load_markets()
        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 = await 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)

    async def fetch_deposit_address(self, code, params={}):
        await 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
        }
        # has support for the 'network' parameter
        # https://binance-docs.github.io/apidocs/spot/en/#deposit-address-supporting-network-user_data
        response = await 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')
        tag = self.safe_string(response, 'tag')
        self.check_address(address)
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'info': response,
        }

    async def fetch_funding_fees(self, codes=None, params={}):
        response = await self.sapiGetAssetAssetDetail(params)
        #
        #     {
        #       "VRAB": {
        #         "withdrawFee": "100",
        #         "minWithdrawAmount": "200",
        #         "withdrawStatus": True,
        #         "depositStatus": True
        #       },
        #       "NZD": {
        #         "withdrawFee": "0",
        #         "minWithdrawAmount": "0",
        #         "withdrawStatus": False,
        #         "depositStatus": False
        #       },
        #       "AKRO": {
        #         "withdrawFee": "313",
        #         "minWithdrawAmount": "626",
        #         "withdrawStatus": True,
        #         "depositStatus": True
        #       },
        #     }
        #
        ids = list(response.keys())
        withdrawFees = {}
        for i in range(0, len(ids)):
            id = ids[i]
            code = self.safe_currency_code(id)
            withdrawFees[code] = self.safe_number(response[id], 'withdrawFee')
        return {
            'withdraw': withdrawFees,
            'deposit': {},
            'info': response,
        }

    async def withdraw(self, code, amount, address, tag=None, params={}):
        self.check_address(address)
        await 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
        response = await self.sapiPostCapitalWithdrawApply(self.extend(request, params))
        #     {id: '9a67628b16ba4988ae20d329333f16bc'}
        return {
            'info': response,
            'id': self.safe_string(response, 'id'),
        }

    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'),
        }

    async def fetch_trading_fee(self, symbol, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        response = await 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)

    async def fetch_trading_fees(self, params={}):
        await self.load_markets()
        response = await self.sapiGetAssetTradeFee(params)
        #
        #    [
        #       {
        #         "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

    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 == 'sapi') or (api == 'wapi' and path != 'systemStatus') or (api == 'dapiPrivate') or (api == 'dapiPrivateV2') or (api == 'fapiPrivate') or (api == 'fapiPrivateV2'):
            self.check_required_credentials()
            query = None
            recvWindow = self.safe_integer(self.options, 'recvWindow', 5000)
            if (api == 'sapi') and (path == 'asset/dust'):
                query = self.urlencode_with_array_repeat(self.extend({
                    'timestamp': self.nonce(),
                    'recvWindow': recvWindow,
                }, params))
            elif (path == 'batchOrders') or (path.find('sub-account') >= 0):
                query = self.rawencode(self.extend({
                    'timestamp': self.nonce(),
                    'recvWindow': recvWindow,
                }, params))
            else:
                query = self.urlencode(self.extend({
                    'timestamp': self.nonce(),
                    'recvWindow': recvWindow,
                }, params))
            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 float 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, 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 (error == '0'):
                return
            # 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 + ' temporary banned: ' + body)
            feedback = self.id + ' ' + body
            self.throw_exactly_matched_exception(self.exceptions, error, feedback)
            raise ExchangeError(feedback)
        if not success:
            raise ExchangeError(self.id + ' ' + body)

    async def request(self, path, api='public', method='GET', params={}, headers=None, body=None):
        response = await self.fetch2(path, api, method, params, headers, body)
        # 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

    async def futures_transfer(self, code, amount, type, params={}):
        if (type < 1) or (type > 4):
            raise ArgumentsRequired(self.id + ' type must be between 1 and 4')
        await self.load_markets()
        currency = self.currency(code)
        request = {
            'asset': currency['id'],
            'amount': amount,
            'type': type,
        }
        response = await self.sapiPostFuturesTransfer(self.extend(request, params))
        #
        #   {
        #       "tranId": 100000001
        #   }
        #
        return self.parse_transfer(response, currency)

    def parse_funding_rate(self, premiumIndex, 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(premiumIndex, 'time')
        marketId = self.safe_string(premiumIndex, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        markPrice = self.safe_number(premiumIndex, 'markPrice')
        indexPrice = self.safe_number(premiumIndex, 'indexPrice')
        interestRate = self.safe_number(premiumIndex, 'interestRate')
        # current funding rate
        fundingRate = self.safe_number(premiumIndex, 'lastFundingRate')
        nextFundingTime = self.safe_integer(premiumIndex, 'nextFundingTime')
        return {
            'info': premiumIndex,
            'symbol': symbol,
            'markPrice': markPrice,
            'indexPrice': indexPrice,
            'interestRate': interestRate,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'fundingRate': fundingRate,
            'nextFundingTimestamp': nextFundingTime,
            'nextFundingDatetime': self.iso8601(nextFundingTime),
        }

    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] = Precise.string_add(crossWalletBalance, crossUnPnl)
        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']
            parsed = self.parse_position(self.extend(position, {
                'crossMargin': balances[code],
            }), market)
            result.append(parsed)
        return result

    def parse_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"
        #     }
        #
        marketId = self.safe_string(position, 'symbol')
        market = self.safe_market(marketId, market)
        symbol = 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)
        maintenanceMarginString = self.safe_string(position, 'maintMargin')
        maintenanceMargin = self.parse_number(maintenanceMarginString)
        entryPriceString = self.safe_string(position, 'entryPrice')
        entryPriceFloat = float(entryPriceString)
        entryPrice = self.parse_number(entryPriceString)
        notionalString = self.safe_string_2(position, 'notional', 'notionalValue')
        notionalStringAbs = Precise.string_abs(notionalString)
        notionalFloat = float(notionalString)
        notionalFloatAbs = float(notionalStringAbs)
        notional = self.parse_number(Precise.string_abs(notionalString))
        contractsString = self.safe_string(position, 'positionAmt')
        if contractsString is None:
            contractsString = int(round(notionalFloat * entryPriceFloat / str(market['contractSize'])))
        contracts = self.parse_number(Precise.string_abs(contractsString))
        leverageBracket = self.options['leverageBrackets'][symbol]
        maintenanceMarginPercentageString = None
        for i in range(0, len(leverageBracket)):
            bracket = leverageBracket[i]
            if notionalFloatAbs < 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')
        marginType = None
        collateralString = None
        if isolated:
            marginType = 'isolated'
            walletBalance = self.safe_string(position, 'isolatedWallet')
            collateralString = Precise.string_add(walletBalance, unrealizedPnlString)
        else:
            marginType = 'cross'
            collateralString = self.safe_string(position, 'crossMargin')
        collateral = self.parse_number(collateralString)
        marginRatio = None
        side = None
        percentage = None
        if notionalFloat == 0.0:
            entryPrice = None
        else:
            side = 'short' if (notionalFloat < 0) else 'long'
            marginRatio = self.parse_number(Precise.string_div(maintenanceMarginString, collateralString, 4))
            percentage = self.parse_number(Precise.string_div(unrealizedPnlString, initialMarginString, 4))
        return {
            'info': position,
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'initialMargin': initialMargin,
            'initialMarginPercentage': self.parse_number(initialMarginPercentageString),
            'maintenanceMargin': maintenanceMargin,
            'maintenanceMarginPercentage': maintenanceMarginPercentage,
            'entryPrice': entryPrice,
            'notional': notional,
            'leverage': leverage,
            'unrealizedPnl': unrealizedPnl,
            'contracts': contracts,
            'marginRatio': marginRatio,
            'liquidationPrice': None,
            'markPrice': None,
            'collateral': collateral,
            'marginType': marginType,
            'side': side,
            '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 = market['symbol']
        leverageBracket = self.options['leverageBrackets'][symbol]
        notionalString = self.safe_string_2(position, 'notional', 'notionalValue')
        notionalStringAbs = Precise.string_abs(notionalString)
        notionalFloatAbs = float(notionalStringAbs)
        notionalFloat = float(notionalString)
        maintenanceMarginPercentageString = None
        for i in range(0, len(leverageBracket)):
            bracket = leverageBracket[i]
            if notionalFloatAbs < 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)
        liquidationPrice = self.safe_number(position, 'liquidationPrice')
        collateralString = self.safe_string(position, 'isolatedMargin')
        collateralFloat = float(collateralString)
        collateral = self.parse_number(collateralString)
        markPriceString = self.safe_string(position, 'markPrice')
        markPriceFloat = float(markPriceString)
        markPrice = None
        if markPriceFloat != 0.0:
            markPrice = self.parse_number(markPriceString)
        entryPrice = self.safe_number(position, 'entryPrice')
        timestamp = self.safe_integer(position, 'updateTime')
        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
        side = None
        percentage = None
        if collateralFloat == 0.0:
            collateral = None
        else:
            marginRatio = self.parse_number(Precise.string_div(maintenanceMarginString, collateralString, 4))
            side = 'short' if (notionalFloat < 0) else 'long'
            percentage = self.parse_number(Precise.string_div(unrealizedPnlString, initialMarginString, 4))
        marginType = self.safe_string(position, 'marginType')
        if marginType == 'cross':
            liquidationPrice = None
        return {
            'info': position,
            'symbol': symbol,
            'contracts': contracts,
            'unrealizedPnl': unrealizedPnl,
            'leverage': leverage,
            '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),
            'marginType': marginType,
            'side': side,
            'percentage': percentage,
        }
