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

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

from ccxt_zagrava.async_support.base.exchange import Exchange
from ccxt_zagrava.abstract.bitmex import ImplicitAPI
import hashlib
from ccxt_zagrava.base.types import Balances, Currencies, Currency, Int, Leverage, Leverages, Market, MarketType, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, Transaction
from typing import List
from ccxt_zagrava.base.errors import ExchangeError
from ccxt_zagrava.base.errors import AuthenticationError
from ccxt_zagrava.base.errors import PermissionDenied
from ccxt_zagrava.base.errors import ArgumentsRequired
from ccxt_zagrava.base.errors import BadRequest
from ccxt_zagrava.base.errors import BadSymbol
from ccxt_zagrava.base.errors import InsufficientFunds
from ccxt_zagrava.base.errors import InvalidOrder
from ccxt_zagrava.base.errors import OrderNotFound
from ccxt_zagrava.base.errors import DDoSProtection
from ccxt_zagrava.base.errors import ExchangeNotAvailable
from ccxt_zagrava.base.decimal_to_precision import TICK_SIZE
from ccxt_zagrava.base.precise import Precise


class bitmex(Exchange, ImplicitAPI):

    def describe(self):
        return self.deep_extend(super(bitmex, self).describe(), {
            'id': 'bitmex',
            'name': 'BitMEX',
            'countries': ['SC'],  # Seychelles
            'version': 'v1',
            'userAgent': None,
            # cheapest endpoints are 10 requests per second(trading)
            # 10 per second => rateLimit = 1000ms / 10 = 100ms
            # 120 per minute => 2 per second => weight = 5(authenticated)
            # 30 per minute => 0.5 per second => weight = 20(unauthenticated)
            'rateLimit': 100,
            'certified': True,
            'pro': True,
            'has': {
                'CORS': None,
                'spot': True,
                'margin': False,
                'swap': True,
                'future': True,
                'option': False,
                'addMargin': None,
                'cancelAllOrders': True,
                'cancelAllOrdersAfter': True,
                'cancelOrder': True,
                'cancelOrders': True,
                'closeAllPositions': False,
                'closePosition': True,
                'createOrder': True,
                'createReduceOnlyOrder': True,
                'createTrailingAmountOrder': True,
                'editOrder': True,
                'fetchBalance': True,
                'fetchClosedOrders': True,
                'fetchCurrencies': True,
                'fetchDepositAddress': True,
                'fetchDepositAddresses': False,
                'fetchDepositAddressesByNetwork': False,
                'fetchDepositsWithdrawals': 'emulated',
                'fetchDepositWithdrawFee': 'emulated',
                'fetchDepositWithdrawFees': True,
                'fetchFundingHistory': False,
                'fetchFundingRate': False,
                'fetchFundingRateHistory': True,
                'fetchFundingRates': True,
                'fetchIndexOHLCV': False,
                'fetchLedger': True,
                'fetchLeverage': 'emulated',
                'fetchLeverages': True,
                'fetchLeverageTiers': False,
                'fetchLiquidations': True,
                'fetchMarginAdjustmentHistory': False,
                'fetchMarketLeverageTiers': False,
                'fetchMarkets': True,
                'fetchMarkOHLCV': False,
                'fetchMyLiquidations': False,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrders': True,
                'fetchPosition': False,
                'fetchPositionHistory': False,
                'fetchPositions': True,
                'fetchPositionsHistory': False,
                'fetchPositionsRisk': False,
                'fetchPremiumIndexOHLCV': False,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTrades': True,
                'fetchTransactions': 'emulated',
                'fetchTransfer': False,
                'fetchTransfers': False,
                'reduceMargin': None,
                'sandbox': True,
                'setLeverage': True,
                'setMargin': None,
                'setMarginMode': True,
                'setPositionMode': False,
                'transfer': False,
                'withdraw': True,
            },
            'timeframes': {
                '1m': '1m',
                '5m': '5m',
                '1h': '1h',
                '1d': '1d',
            },
            'urls': {
                'test': {
                    'public': 'https://testnet.bitmex.com',
                    'private': 'https://testnet.bitmex.com',
                },
                'logo': 'https://github.com/ccxt_zagrava/ccxt_zagrava/assets/43336371/cea9cfe5-c57e-4b84-b2ac-77b960b04445',
                'api': {
                    'public': 'https://www.bitmex.com',
                    'private': 'https://www.bitmex.com',
                },
                'www': 'https://www.bitmex.com',
                'doc': [
                    'https://www.bitmex.com/app/apiOverview',
                    'https://github.com/BitMEX/api-connectors/tree/master/official-http',
                ],
                'fees': 'https://www.bitmex.com/app/fees',
                'referral': {
                    'url': 'https://www.bitmex.com/app/register/NZTR1q',
                    'discount': 0.1,
                },
            },
            'api': {
                'public': {
                    'get': {
                        'announcement': 5,
                        'announcement/urgent': 5,
                        'chat': 5,
                        'chat/channels': 5,
                        'chat/connected': 5,
                        'chat/pinned': 5,
                        'funding': 5,
                        'guild': 5,
                        'instrument': 5,
                        'instrument/active': 5,
                        'instrument/activeAndIndices': 5,
                        'instrument/activeIntervals': 5,
                        'instrument/compositeIndex': 5,
                        'instrument/indices': 5,
                        'instrument/usdVolume': 5,
                        'insurance': 5,
                        'leaderboard': 5,
                        'liquidation': 5,
                        'orderBook/L2': 5,
                        'porl/nonce': 5,
                        'quote': 5,
                        'quote/bucketed': 5,
                        'schema': 5,
                        'schema/websocketHelp': 5,
                        'settlement': 5,
                        'stats': 5,
                        'stats/history': 5,
                        'stats/historyUSD': 5,
                        'trade': 5,
                        'trade/bucketed': 5,
                        'wallet/assets': 5,
                        'wallet/networks': 5,
                    },
                },
                'private': {
                    'get': {
                        'address': 5,
                        'apiKey': 5,
                        'execution': 5,
                        'execution/tradeHistory': 5,
                        'globalNotification': 5,
                        'leaderboard/name': 5,
                        'order': 5,
                        'porl/snapshots': 5,
                        'position': 5,
                        'user': 5,
                        'user/affiliateStatus': 5,
                        'user/checkReferralCode': 5,
                        'user/commission': 5,
                        'user/csa': 5,
                        'user/depositAddress': 5,
                        'user/executionHistory': 5,
                        'user/getWalletTransferAccounts': 5,
                        'user/margin': 5,
                        'user/quoteFillRatio': 5,
                        'user/quoteValueRatio': 5,
                        'user/staking': 5,
                        'user/staking/instruments': 5,
                        'user/staking/tiers': 5,
                        'user/tradingVolume': 5,
                        'user/unstakingRequests': 5,
                        'user/wallet': 5,
                        'user/walletHistory': 5,
                        'user/walletSummary': 5,
                        'userAffiliates': 5,
                        'userEvent': 5,
                    },
                    'post': {
                        'address': 5,
                        'chat': 5,
                        'guild': 5,
                        'guild/archive': 5,
                        'guild/join': 5,
                        'guild/kick': 5,
                        'guild/leave': 5,
                        'guild/sharesTrades': 5,
                        'order': 1,
                        'order/cancelAllAfter': 5,
                        'order/closePosition': 5,
                        'position/isolate': 1,
                        'position/leverage': 1,
                        'position/riskLimit': 5,
                        'position/transferMargin': 1,
                        'user/addSubaccount': 5,
                        'user/cancelWithdrawal': 5,
                        'user/communicationToken': 5,
                        'user/confirmEmail': 5,
                        'user/confirmWithdrawal': 5,
                        'user/logout': 5,
                        'user/preferences': 5,
                        'user/requestWithdrawal': 5,
                        'user/unstakingRequests': 5,
                        'user/updateSubaccount': 5,
                        'user/walletTransfer': 5,
                    },
                    'put': {
                        'guild': 5,
                        'order': 1,
                    },
                    'delete': {
                        'order': 1,
                        'order/all': 1,
                        'user/unstakingRequests': 5,
                    },
                },
            },
            'exceptions': {
                'exact': {
                    'Invalid API Key.': AuthenticationError,
                    'This key is disabled.': PermissionDenied,
                    'Access Denied': PermissionDenied,
                    'Duplicate clOrdID': InvalidOrder,
                    'orderQty is invalid': InvalidOrder,
                    'Invalid price': InvalidOrder,
                    'Invalid stopPx for ordType': InvalidOrder,
                    'Account is restricted': PermissionDenied,  # {"error":{"message":"Account is restricted","name":"HTTPError"}}
                },
                'broad': {
                    'Signature not valid': AuthenticationError,
                    'overloaded': ExchangeNotAvailable,
                    'Account has insufficient Available Balance': InsufficientFunds,
                    'Service unavailable': ExchangeNotAvailable,  # {"error":{"message":"Service unavailable","name":"HTTPError"}}
                    'Server Error': ExchangeError,  # {"error":{"message":"Server Error","name":"HTTPError"}}
                    'Unable to cancel order due to existing state': InvalidOrder,
                    'We require all new traders to verify': PermissionDenied,  # {"message":"We require all new traders to verify their identity before their first deposit. Please visit bitmex.com/verify to complete the process.","name":"HTTPError"}
                },
            },
            'precisionMode': TICK_SIZE,
            'options': {
                # https://blog.bitmex.com/api_announcement/deprecation-of-api-nonce-header/
                # https://github.com/ccxt_zagrava/ccxt_zagrava/issues/4789
                'api-expires': 5,  # in seconds
                'fetchOHLCVOpenTimestamp': True,
                'oldPrecision': False,
                'networks': {
                    'BTC': 'btc',
                    'ERC20': 'eth',
                    'BEP20': 'bsc',
                    'TRC20': 'tron',
                    'AVAXC': 'avax',
                    'NEAR': 'near',
                    'XTZ': 'xtz',
                    'DOT': 'dot',
                    'SOL': 'sol',
                    'ADA': 'ada',
                },
            },
            'commonCurrencies': {
                'USDt': 'USDT',
                'XBt': 'BTC',
                'XBT': 'BTC',
                'Gwei': 'ETH',
                'GWEI': 'ETH',
                'LAMP': 'SOL',
                'LAMp': 'SOL',
            },
        })

    async def fetch_currencies(self, params={}) -> Currencies:
        """
        fetches all available currencies on an exchange
        :see: https://www.bitmex.com/api/explorer/#not /Wallet/Wallet_getAssetsConfig
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an associative dictionary of currencies
        """
        response = await self.publicGetWalletAssets(params)
        #
        #    {
        #        "XBt": {
        #            "asset": "XBT",
        #            "currency": "XBt",
        #            "majorCurrency": "XBT",
        #            "name": "Bitcoin",
        #            "currencyType": "Crypto",
        #            "scale": "8",
        #            # "mediumPrecision": "8",
        #            # "shorterPrecision": "4",
        #            # "symbol": "₿",
        #            # "weight": "1",
        #            # "tickLog": "0",
        #            "enabled": True,
        #            "isMarginCurrency": True,
        #            "minDepositAmount": "10000",
        #            "minWithdrawalAmount": "1000",
        #            "maxWithdrawalAmount": "100000000000000",
        #            "networks": [
        #                {
        #                    "asset": "btc",
        #                    "tokenAddress": "",
        #                    "depositEnabled": True,
        #                    "withdrawalEnabled": True,
        #                    "withdrawalFee": "20000",
        #                    "minFee": "20000",
        #                    "maxFee": "10000000"
        #                }
        #            ]
        #        },
        #     }
        #
        result: dict = {}
        for i in range(0, len(response)):
            currency = response[i]
            asset = self.safe_string(currency, 'asset')
            code = self.safe_currency_code(asset)
            id = self.safe_string(currency, 'currency')
            name = self.safe_string(currency, 'name')
            chains = self.safe_value(currency, 'networks', [])
            depositEnabled = False
            withdrawEnabled = False
            networks: dict = {}
            scale = self.safe_string(currency, 'scale')
            precisionString = self.parse_precision(scale)
            precision = self.parse_number(precisionString)
            for j in range(0, len(chains)):
                chain = chains[j]
                networkId = self.safe_string(chain, 'asset')
                network = self.network_id_to_code(networkId)
                withdrawalFeeRaw = self.safe_string(chain, 'withdrawalFee')
                withdrawalFee = self.parse_number(Precise.string_mul(withdrawalFeeRaw, precisionString))
                isDepositEnabled = self.safe_bool(chain, 'depositEnabled', False)
                isWithdrawEnabled = self.safe_bool(chain, 'withdrawalEnabled', False)
                active = (isDepositEnabled and isWithdrawEnabled)
                if isDepositEnabled:
                    depositEnabled = True
                if isWithdrawEnabled:
                    withdrawEnabled = True
                networks[network] = {
                    'info': chain,
                    'id': networkId,
                    'network': network,
                    'active': active,
                    'deposit': isDepositEnabled,
                    'withdraw': isWithdrawEnabled,
                    'fee': withdrawalFee,
                    'precision': None,
                    'limits': {
                        'withdraw': {
                            'min': None,
                            'max': None,
                        },
                        'deposit': {
                            'min': None,
                            'max': None,
                        },
                    },
                }
            currencyEnabled = self.safe_value(currency, 'enabled')
            currencyActive = currencyEnabled or (depositEnabled or withdrawEnabled)
            minWithdrawalString = self.safe_string(currency, 'minWithdrawalAmount')
            minWithdrawal = self.parse_number(Precise.string_mul(minWithdrawalString, precisionString))
            maxWithdrawalString = self.safe_string(currency, 'maxWithdrawalAmount')
            maxWithdrawal = self.parse_number(Precise.string_mul(maxWithdrawalString, precisionString))
            minDepositString = self.safe_string(currency, 'minDepositAmount')
            minDeposit = self.parse_number(Precise.string_mul(minDepositString, precisionString))
            result[code] = {
                'id': id,
                'code': code,
                'info': currency,
                'name': name,
                'active': currencyActive,
                'deposit': depositEnabled,
                'withdraw': withdrawEnabled,
                'fee': None,
                'precision': precision,
                'limits': {
                    'amount': {
                        'min': None,
                        'max': None,
                    },
                    'withdraw': {
                        'min': minWithdrawal,
                        'max': maxWithdrawal,
                    },
                    'deposit': {
                        'min': minDeposit,
                        'max': None,
                    },
                },
                'networks': networks,
            }
        return result

    def convert_from_real_amount(self, code, amount):
        currency = self.currency(code)
        precision = self.safe_string(currency, 'precision')
        amountString = self.number_to_string(amount)
        finalAmount = Precise.string_div(amountString, precision)
        return self.parse_number(finalAmount)

    def convert_to_real_amount(self, code: Str, amount: Str):
        if code is None:
            return amount
        elif amount is None:
            return None
        currency = self.currency(code)
        precision = self.safe_string(currency, 'precision')
        return Precise.string_mul(amount, precision)

    def amount_to_precision(self, symbol, amount):
        symbol = self.safe_symbol(symbol)
        market = self.market(symbol)
        oldPrecision = self.safe_value(self.options, 'oldPrecision')
        if market['spot'] and not oldPrecision:
            amount = self.convert_from_real_amount(market['base'], amount)
        return super(bitmex, self).amount_to_precision(symbol, amount)

    def convert_from_raw_quantity(self, symbol, rawQuantity, currencySide='base'):
        if self.safe_value(self.options, 'oldPrecision'):
            return self.parse_number(rawQuantity)
        symbol = self.safe_symbol(symbol)
        marketExists = self.in_array(symbol, self.symbols)
        if not marketExists:
            return self.parse_number(rawQuantity)
        market = self.market(symbol)
        if market['spot']:
            return self.parse_number(self.convert_to_real_amount(market[currencySide], rawQuantity))
        return self.parse_number(rawQuantity)

    def convert_from_raw_cost(self, symbol, rawQuantity):
        return self.convert_from_raw_quantity(symbol, rawQuantity, 'quote')

    async def fetch_markets(self, params={}) -> List[Market]:
        """
        retrieves data on all markets for bitmex
        :see: https://www.bitmex.com/api/explorer/#not /Instrument/Instrument_getActive
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: an array of objects representing market data
        """
        response = await self.publicGetInstrumentActive(params)
        #
        #  [
        #    {
        #        "symbol": "LTCUSDT",
        #        "rootSymbol": "LTC",
        #        "state": "Open",
        #        "typ": "FFWCSX",
        #        "listing": "2021-11-10T04:00:00.000Z",
        #        "front": "2021-11-10T04:00:00.000Z",
        #        "expiry": null,
        #        "settle": null,
        #        "listedSettle": null,
        #        "relistInterval": null,
        #        "inverseLeg": "",
        #        "sellLeg": "",
        #        "buyLeg": "",
        #        "optionStrikePcnt": null,
        #        "optionStrikeRound": null,
        #        "optionStrikePrice": null,
        #        "optionMultiplier": null,
        #        "positionCurrency": "LTC",  # can be empty for spot markets
        #        "underlying": "LTC",
        #        "quoteCurrency": "USDT",
        #        "underlyingSymbol": "LTCT=",  # can be empty for spot markets
        #        "reference": "BMEX",
        #        "referenceSymbol": ".BLTCT",  # can be empty for spot markets
        #        "calcInterval": null,
        #        "publishInterval": null,
        #        "publishTime": null,
        #        "maxOrderQty": 1000000000,
        #        "maxPrice": 1000000,
        #        "lotSize": 1000,
        #        "tickSize": 0.01,
        #        "multiplier": 100,
        #        "settlCurrency": "USDt",  # can be empty for spot markets
        #        "underlyingToPositionMultiplier": 10000,
        #        "underlyingToSettleMultiplier": null,
        #        "quoteToSettleMultiplier": 1000000,
        #        "isQuanto": False,
        #        "isInverse": False,
        #        "initMargin": 0.03,
        #        "maintMargin": 0.015,
        #        "riskLimit": 1000000000000,  # can be null for spot markets
        #        "riskStep": 1000000000000,  # can be null for spot markets
        #        "limit": null,
        #        "capped": False,
        #        "taxed": True,
        #        "deleverage": True,
        #        "makerFee": -0.0001,
        #        "takerFee": 0.0005,
        #        "settlementFee": 0,
        #        "insuranceFee": 0,
        #        "fundingBaseSymbol": ".LTCBON8H",  # can be empty for spot markets
        #        "fundingQuoteSymbol": ".USDTBON8H",  # can be empty for spot markets
        #        "fundingPremiumSymbol": ".LTCUSDTPI8H",  # can be empty for spot markets
        #        "fundingTimestamp": "2022-01-14T20:00:00.000Z",
        #        "fundingInterval": "2000-01-01T08:00:00.000Z",
        #        "fundingRate": 0.0001,
        #        "indicativeFundingRate": 0.0001,
        #        "rebalanceTimestamp": null,
        #        "rebalanceInterval": null,
        #        "openingTimestamp": "2022-01-14T17:00:00.000Z",
        #        "closingTimestamp": "2022-01-14T18:00:00.000Z",
        #        "sessionInterval": "2000-01-01T01:00:00.000Z",
        #        "prevClosePrice": 138.511,
        #        "limitDownPrice": null,
        #        "limitUpPrice": null,
        #        "bankruptLimitDownPrice": null,
        #        "bankruptLimitUpPrice": null,
        #        "prevTotalVolume": 12699024000,
        #        "totalVolume": 12702160000,
        #        "volume": 3136000,
        #        "volume24h": 114251000,
        #        "prevTotalTurnover": 232418052349000,
        #        "totalTurnover": 232463353260000,
        #        "turnover": 45300911000,
        #        "turnover24h": 1604331340000,
        #        "homeNotional24h": 11425.1,
        #        "foreignNotional24h": 1604331.3400000003,
        #        "prevPrice24h": 135.48,
        #        "vwap": 140.42165,
        #        "highPrice": 146.42,
        #        "lowPrice": 135.08,
        #        "lastPrice": 144.36,
        #        "lastPriceProtected": 144.36,
        #        "lastTickDirection": "MinusTick",
        #        "lastChangePcnt": 0.0655,
        #        "bidPrice": 143.75,
        #        "midPrice": 143.855,
        #        "askPrice": 143.96,
        #        "impactBidPrice": 143.75,
        #        "impactMidPrice": 143.855,
        #        "impactAskPrice": 143.96,
        #        "hasLiquidity": True,
        #        "openInterest": 38103000,
        #        "openValue": 547963053300,
        #        "fairMethod": "FundingRate",
        #        "fairBasisRate": 0.1095,
        #        "fairBasis": 0.004,
        #        "fairPrice": 143.811,
        #        "markMethod": "FairPrice",
        #        "markPrice": 143.811,
        #        "indicativeTaxRate": null,
        #        "indicativeSettlePrice": 143.807,
        #        "optionUnderlyingPrice": null,
        #        "settledPriceAdjustmentRate": null,
        #        "settledPrice": null,
        #        "timestamp": "2022-01-14T17:49:55.000Z"
        #    }
        #  ]
        #
        return self.parse_markets(response)

    def parse_market(self, market: dict) -> Market:
        id = self.safe_string(market, 'symbol')
        baseId = self.safe_string(market, 'underlying')
        quoteId = self.safe_string(market, 'quoteCurrency')
        settleId = self.safe_string(market, 'settlCurrency')
        settle = self.safe_currency_code(settleId)
        # 'positionCurrency' may be empty("", currently returns for ETHUSD)
        # so let's take the settlCurrency first and then adjust if needed
        typ = self.safe_string(market, 'typ')  # type definitions at: https://www.bitmex.com/api/explorer/#not /Instrument/Instrument_get
        type: MarketType
        swap = False
        spot = False
        future = False
        if typ == 'FFWCSX':
            type = 'swap'
            swap = True
        elif typ == 'IFXXXP':
            type = 'spot'
            spot = True
        elif typ == 'FFCCSX':
            type = 'future'
            future = True
        elif typ == 'FFICSX':
            # prediction markets(without any volume)
            quoteId = baseId
            baseId = self.safe_string(market, 'rootSymbol')
            type = 'future'
            future = True
        base = self.safe_currency_code(baseId)
        quote = self.safe_currency_code(quoteId)
        contract = swap or future
        contractSize = None
        isInverse = self.safe_value(market, 'isInverse')  # self is True when BASE and SETTLE are same, i.e. BTC/XXX:BTC
        isQuanto = self.safe_value(market, 'isQuanto')  # self is True when BASE and SETTLE are different, i.e. AXS/XXX:BTC
        linear = (not isInverse and not isQuanto) if contract else None
        status = self.safe_string(market, 'state')
        active = status != 'Unlisted'
        expiry = None
        expiryDatetime = None
        symbol = None
        if spot:
            symbol = base + '/' + quote
        elif contract:
            symbol = base + '/' + quote + ':' + settle
            if linear:
                multiplierString = self.safe_string_2(market, 'underlyingToPositionMultiplier', 'underlyingToSettleMultiplier')
                contractSize = self.parse_number(Precise.string_div('1', multiplierString))
            else:
                multiplierString = Precise.string_abs(self.safe_string(market, 'multiplier'))
                contractSize = self.parse_number(multiplierString)
            if future:
                expiryDatetime = self.safe_string(market, 'expiry')
                expiry = self.parse8601(expiryDatetime)
                symbol = symbol + '-' + self.yymmdd(expiry)
        else:
            # for index/exotic markets, default to id
            symbol = id
        positionId = self.safe_string_2(market, 'positionCurrency', 'underlying')
        position = self.safe_currency_code(positionId)
        positionIsQuote = (position == quote)
        maxOrderQty = self.safe_number(market, 'maxOrderQty')
        initMargin = self.safe_string(market, 'initMargin', '1')
        maxLeverage = self.parse_number(Precise.string_div('1', initMargin))
        return {
            'id': id,
            'symbol': symbol,
            'base': base,
            'quote': quote,
            'settle': settle,
            'baseId': baseId,
            'quoteId': quoteId,
            'settleId': settleId,
            'type': type,
            'spot': spot,
            'margin': False,
            'swap': swap,
            'future': future,
            'option': False,
            'active': active,
            'contract': contract,
            'linear': linear,
            'inverse': isInverse,
            'quanto': isQuanto,
            'taker': self.safe_number(market, 'takerFee'),
            'maker': self.safe_number(market, 'makerFee'),
            'contractSize': contractSize,
            'expiry': expiry,
            'expiryDatetime': expiryDatetime,
            'strike': self.safe_number(market, 'optionStrikePrice'),
            'optionType': None,
            'precision': {
                'amount': self.safe_number(market, 'lotSize'),
                'price': self.safe_number(market, 'tickSize'),
            },
            'limits': {
                'leverage': {
                    'min': self.parse_number('1') if contract else None,
                    'max': maxLeverage if contract else None,
                },
                'amount': {
                    'min': None,
                    'max': None if positionIsQuote else maxOrderQty,
                },
                'price': {
                    'min': None,
                    'max': self.safe_number(market, 'maxPrice'),
                },
                'cost': {
                    'min': None,
                    'max': maxOrderQty if positionIsQuote else None,
                },
            },
            'created': self.parse8601(self.safe_string(market, 'listing')),
            'info': market,
        }

    def parse_balance(self, response) -> Balances:
        #
        #     [
        #         {
        #             "account":1455728,
        #             "currency":"XBt",
        #             "riskLimit":1000000000000,
        #             "prevState":"",
        #             "state":"",
        #             "action":"",
        #             "amount":263542,
        #             "pendingCredit":0,
        #             "pendingDebit":0,
        #             "confirmedDebit":0,
        #             "prevRealisedPnl":0,
        #             "prevUnrealisedPnl":0,
        #             "grossComm":0,
        #             "grossOpenCost":0,
        #             "grossOpenPremium":0,
        #             "grossExecCost":0,
        #             "grossMarkValue":0,
        #             "riskValue":0,
        #             "taxableMargin":0,
        #             "initMargin":0,
        #             "maintMargin":0,
        #             "sessionMargin":0,
        #             "targetExcessMargin":0,
        #             "varMargin":0,
        #             "realisedPnl":0,
        #             "unrealisedPnl":0,
        #             "indicativeTax":0,
        #             "unrealisedProfit":0,
        #             "syntheticMargin":null,
        #             "walletBalance":263542,
        #             "marginBalance":263542,
        #             "marginBalancePcnt":1,
        #             "marginLeverage":0,
        #             "marginUsedPcnt":0,
        #             "excessMargin":263542,
        #             "excessMarginPcnt":1,
        #             "availableMargin":263542,
        #             "withdrawableMargin":263542,
        #             "timestamp":"2020-08-03T12:01:01.246Z",
        #             "grossLastValue":0,
        #             "commission":null
        #         }
        #     ]
        #
        result: dict = {'info': response}
        for i in range(0, len(response)):
            balance = response[i]
            currencyId = self.safe_string(balance, 'currency')
            code = self.safe_currency_code(currencyId)
            account = self.account()
            free = self.safe_string(balance, 'availableMargin')
            total = self.safe_string(balance, 'marginBalance')
            account['free'] = self.convert_to_real_amount(code, free)
            account['total'] = self.convert_to_real_amount(code, total)
            result[code] = account
        return self.safe_balance(result)

    async def fetch_balance(self, params={}) -> Balances:
        """
        query for balance and get the amount of funds available for trading or funds locked in orders
        :see: https://www.bitmex.com/api/explorer/#not /User/User_getMargin
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `balance structure <https://docs.ccxt_zagrava.com/#/?id=balance-structure>`
        """
        await self.load_markets()
        request: dict = {
            'currency': 'all',
        }
        response = await self.privateGetUserMargin(self.extend(request, params))
        #
        #     [
        #         {
        #             "account":1455728,
        #             "currency":"XBt",
        #             "riskLimit":1000000000000,
        #             "prevState":"",
        #             "state":"",
        #             "action":"",
        #             "amount":263542,
        #             "pendingCredit":0,
        #             "pendingDebit":0,
        #             "confirmedDebit":0,
        #             "prevRealisedPnl":0,
        #             "prevUnrealisedPnl":0,
        #             "grossComm":0,
        #             "grossOpenCost":0,
        #             "grossOpenPremium":0,
        #             "grossExecCost":0,
        #             "grossMarkValue":0,
        #             "riskValue":0,
        #             "taxableMargin":0,
        #             "initMargin":0,
        #             "maintMargin":0,
        #             "sessionMargin":0,
        #             "targetExcessMargin":0,
        #             "varMargin":0,
        #             "realisedPnl":0,
        #             "unrealisedPnl":0,
        #             "indicativeTax":0,
        #             "unrealisedProfit":0,
        #             "syntheticMargin":null,
        #             "walletBalance":263542,
        #             "marginBalance":263542,
        #             "marginBalancePcnt":1,
        #             "marginLeverage":0,
        #             "marginUsedPcnt":0,
        #             "excessMargin":263542,
        #             "excessMarginPcnt":1,
        #             "availableMargin":263542,
        #             "withdrawableMargin":263542,
        #             "timestamp":"2020-08-03T12:01:01.246Z",
        #             "grossLastValue":0,
        #             "commission":null
        #         }
        #     ]
        #
        return self.parse_balance(response)

    async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
        """
        fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
        :see: https://www.bitmex.com/api/explorer/#not /OrderBook/OrderBook_getL2
        :param str symbol: unified symbol of the market to fetch the order book for
        :param int [limit]: the maximum amount of order book entries to return
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: A dictionary of `order book structures <https://docs.ccxt_zagrava.com/#/?id=order-book-structure>` indexed by market symbols
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        if limit is not None:
            request['depth'] = limit
        response = await self.publicGetOrderBookL2(self.extend(request, params))
        result: dict = {
            'symbol': symbol,
            'bids': [],
            'asks': [],
            'timestamp': None,
            'datetime': None,
            'nonce': None,
        }
        for i in range(0, len(response)):
            order = response[i]
            side = 'asks' if (order['side'] == 'Sell') else 'bids'
            amount = self.convert_from_raw_quantity(symbol, self.safe_string(order, 'size'))
            price = self.safe_number(order, 'price')
            # https://github.com/ccxt_zagrava/ccxt_zagrava/issues/4926
            # https://github.com/ccxt_zagrava/ccxt_zagrava/issues/4927
            # the exchange sometimes returns null price in the orderbook
            if price is not None:
                result[side].append([price, amount])
        result['bids'] = self.sort_by(result['bids'], 0, True)
        result['asks'] = self.sort_by(result['asks'], 0)
        return result

    async def fetch_order(self, id: str, symbol: Str = None, params={}):
        """
        fetches information on an order made by the user
        :see: https://www.bitmex.com/api/explorer/#not /Order/Order_getOrders
        :param str symbol: unified symbol of the market the order was made in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: An `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        filter: dict = {
            'filter': {
                'orderID': id,
            },
        }
        response = await self.fetch_orders(symbol, None, None, self.deep_extend(filter, params))
        numResults = len(response)
        if numResults == 1:
            return response[0]
        raise OrderNotFound(self.id + ': The order ' + id + ' not found.')

    async def fetch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        :see: https://www.bitmex.com/api/explorer/#not /Order/Order_getOrders
        fetches information on multiple orders made by the user
        :param str symbol: unified market symbol of the market orders were made in
        :param int [since]: the earliest time in ms to fetch orders for
        :param int [limit]: the maximum number of order structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the earliest time in ms to fetch orders for
        :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt_zagrava/ccxt_zagrava/wiki/Manual#pagination-params)
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchOrders', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_dynamic('fetchOrders', symbol, since, limit, params, 100)
        market = None
        request: dict = {}
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        if since is not None:
            request['startTime'] = self.iso8601(since)
        if limit is not None:
            request['count'] = limit
        until = self.safe_integer_2(params, 'until', 'endTime')
        if until is not None:
            params = self.omit(params, ['until'])
            request['endTime'] = self.iso8601(until)
        request = self.deep_extend(request, params)
        # why the hassle? urlencode in python is kinda broken for nested dicts.
        # E.g. self.urlencode({"filter": {"open": True}}) will return "filter={'open':+True}"
        # Bitmex doesn't like that. Hence resorting to self hack.
        if 'filter' in request:
            request['filter'] = self.json(request['filter'])
        response = await self.privateGetOrder(request)
        return self.parse_orders(response, market, since, limit)

    async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetch all unfilled currently open orders
        :see: https://www.bitmex.com/api/explorer/#not /Order/Order_getOrders
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch open orders for
        :param int [limit]: the maximum number of  open orders structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        request: dict = {
            'filter': {
                'open': True,
            },
        }
        return await self.fetch_orders(symbol, since, limit, self.deep_extend(request, params))

    async def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetches information on multiple closed orders made by the user
        :see: https://www.bitmex.com/api/explorer/#not /Order/Order_getOrders
        :param str symbol: unified market symbol of the market orders were made in
        :param int [since]: the earliest time in ms to fetch orders for
        :param int [limit]: the maximum number of order structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns Order[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        # Bitmex barfs if you set 'open': False in the filter...
        orders = await self.fetch_orders(symbol, since, limit, params)
        return self.filter_by_array(orders, 'status', ['closed', 'canceled'], False)

    async def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetch all trades made by the user
        :see: https://www.bitmex.com/api/explorer/#not /Execution/Execution_getTradeHistory
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch trades for
        :param int [limit]: the maximum number of trades structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt_zagrava/ccxt_zagrava/wiki/Manual#pagination-params)
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=trade-structure>`
        """
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchMyTrades', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_dynamic('fetchMyTrades', symbol, since, limit, params, 100)
        market = None
        request: dict = {}
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        if since is not None:
            request['startTime'] = self.iso8601(since)
        if limit is not None:
            request['count'] = limit
        until = self.safe_integer_2(params, 'until', 'endTime')
        if until is not None:
            params = self.omit(params, ['until'])
            request['endTime'] = self.iso8601(until)
        request = self.deep_extend(request, params)
        # why the hassle? urlencode in python is kinda broken for nested dicts.
        # E.g. self.urlencode({"filter": {"open": True}}) will return "filter={'open':+True}"
        # Bitmex doesn't like that. Hence resorting to self hack.
        if 'filter' in request:
            request['filter'] = self.json(request['filter'])
        response = await self.privateGetExecutionTradeHistory(request)
        #
        #     [
        #         {
        #             "execID": "string",
        #             "orderID": "string",
        #             "clOrdID": "string",
        #             "clOrdLinkID": "string",
        #             "account": 0,
        #             "symbol": "string",
        #             "side": "string",
        #             "lastQty": 0,
        #             "lastPx": 0,
        #             "underlyingLastPx": 0,
        #             "lastMkt": "string",
        #             "lastLiquidityInd": "string",
        #             "simpleOrderQty": 0,
        #             "orderQty": 0,
        #             "price": 0,
        #             "displayQty": 0,
        #             "stopPx": 0,
        #             "pegOffsetValue": 0,
        #             "pegPriceType": "string",
        #             "currency": "string",
        #             "settlCurrency": "string",
        #             "execType": "string",
        #             "ordType": "string",
        #             "timeInForce": "string",
        #             "execInst": "string",
        #             "contingencyType": "string",
        #             "exDestination": "string",
        #             "ordStatus": "string",
        #             "triggered": "string",
        #             "workingIndicator": True,
        #             "ordRejReason": "string",
        #             "simpleLeavesQty": 0,
        #             "leavesQty": 0,
        #             "simpleCumQty": 0,
        #             "cumQty": 0,
        #             "avgPx": 0,
        #             "commission": 0,
        #             "tradePublishIndicator": "string",
        #             "multiLegReportingType": "string",
        #             "text": "string",
        #             "trdMatchID": "string",
        #             "execCost": 0,
        #             "execComm": 0,
        #             "homeNotional": 0,
        #             "foreignNotional": 0,
        #             "transactTime": "2019-03-05T12:47:02.762Z",
        #             "timestamp": "2019-03-05T12:47:02.762Z"
        #         }
        #     ]
        #
        return self.parse_trades(response, market, since, limit)

    def parse_ledger_entry_type(self, type):
        types: dict = {
            'Withdrawal': 'transaction',
            'RealisedPNL': 'margin',
            'UnrealisedPNL': 'margin',
            'Deposit': 'transaction',
            'Transfer': 'transfer',
            'AffiliatePayout': 'referral',
            'SpotTrade': 'trade',
        }
        return self.safe_string(types, type, type)

    def parse_ledger_entry(self, item: dict, currency: Currency = None):
        #
        #     {
        #         "transactID": "69573da3-7744-5467-3207-89fd6efe7a47",
        #         "account":  24321,
        #         "currency": "XBt",
        #         "transactType": "Withdrawal",  # "AffiliatePayout", "Transfer", "Deposit", "RealisedPNL", ...
        #         "amount":  -1000000,
        #         "fee":  300000,
        #         "transactStatus": "Completed",  # "Canceled", ...
        #         "address": "1Ex4fkF4NhQaQdRWNoYpqiPbDBbq18Kdd9",
        #         "tx": "3BMEX91ZhhKoWtsH9QRb5dNXnmnGpiEetA",
        #         "text": "",
        #         "transactTime": "2017-03-21T20:05:14.388Z",
        #         "walletBalance":  0,  # balance after
        #         "marginBalance":  null,
        #         "timestamp": "2017-03-22T13:09:23.514Z"
        #     }
        #
        # ButMEX returns the unrealized pnl from the wallet history endpoint.
        # The unrealized pnl transaction has an empty timestamp.
        # It is not related to historical pnl it has status set to "Pending".
        # Therefore it's not a part of the history at all.
        # https://github.com/ccxt_zagrava/ccxt_zagrava/issues/6047
        #
        #     {
        #         "transactID":"00000000-0000-0000-0000-000000000000",
        #         "account":121210,
        #         "currency":"XBt",
        #         "transactType":"UnrealisedPNL",
        #         "amount":-5508,
        #         "fee":0,
        #         "transactStatus":"Pending",
        #         "address":"XBTUSD",
        #         "tx":"",
        #         "text":"",
        #         "transactTime":null,  # ←---------------------------- null
        #         "walletBalance":139198767,
        #         "marginBalance":139193259,
        #         "timestamp":null  # ←---------------------------- null
        #     }
        #
        id = self.safe_string(item, 'transactID')
        account = self.safe_string(item, 'account')
        referenceId = self.safe_string(item, 'tx')
        referenceAccount = None
        type = self.parse_ledger_entry_type(self.safe_string(item, 'transactType'))
        currencyId = self.safe_string(item, 'currency')
        code = self.safe_currency_code(currencyId, currency)
        amountString = self.safe_string(item, 'amount')
        amount = self.convert_to_real_amount(code, amountString)
        timestamp = self.parse8601(self.safe_string(item, 'transactTime'))
        if timestamp is None:
            # https://github.com/ccxt_zagrava/ccxt_zagrava/issues/6047
            # set the timestamp to zero, 1970 Jan 1 00:00:00
            # for unrealized pnl and other transactions without a timestamp
            timestamp = 0  # see comments above
        feeCost = self.safe_string(item, 'fee')
        if feeCost is not None:
            feeCost = self.convert_to_real_amount(code, feeCost)
        fee = {
            'cost': self.parse_number(feeCost),
            'currency': code,
        }
        after = self.safe_string(item, 'walletBalance')
        if after is not None:
            after = self.convert_to_real_amount(code, after)
        before = self.parse_number(Precise.string_sub(self.number_to_string(after), self.number_to_string(amount)))
        direction = None
        if Precise.string_lt(amountString, '0'):
            direction = 'out'
            amount = self.convert_to_real_amount(code, Precise.string_abs(amountString))
        else:
            direction = 'in'
        status = self.parse_transaction_status(self.safe_string(item, 'transactStatus'))
        return {
            'id': id,
            'info': item,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'direction': direction,
            'account': account,
            'referenceId': referenceId,
            'referenceAccount': referenceAccount,
            'type': type,
            'currency': code,
            'amount': amount,
            'before': before,
            'after': self.parse_number(after),
            'status': status,
            'fee': fee,
        }

    async def fetch_ledger(self, code: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetch the history of changes, actions done by the user or operations that altered balance of the user
        :see: https://www.bitmex.com/api/explorer/#not /User/User_getWalletHistory
        :param str code: unified currency code, default is None
        :param int [since]: timestamp in ms of the earliest ledger entry, default is None
        :param int [limit]: max number of ledger entrys to return, default is None
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `ledger structure <https://docs.ccxt_zagrava.com/#/?id=ledger-structure>`
        """
        await self.load_markets()
        request: dict = {
            # 'start': 123,
        }
        #
        #     if since is not None:
        #         # date-based pagination not supported
        #     }
        #
        if limit is not None:
            request['count'] = limit
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        response = await self.privateGetUserWalletHistory(self.extend(request, params))
        #
        #     [
        #         {
        #             "transactID": "69573da3-7744-5467-3207-89fd6efe7a47",
        #             "account":  24321,
        #             "currency": "XBt",
        #             "transactType": "Withdrawal",  # "AffiliatePayout", "Transfer", "Deposit", "RealisedPNL", ...
        #             "amount":  -1000000,
        #             "fee":  300000,
        #             "transactStatus": "Completed",  # "Canceled", ...
        #             "address": "1Ex4fkF4NhQaQdRWNoYpqiPbDBbq18Kdd9",
        #             "tx": "3BMEX91ZhhKoWtsH9QRb5dNXnmnGpiEetA",
        #             "text": "",
        #             "transactTime": "2017-03-21T20:05:14.388Z",
        #             "walletBalance":  0,  # balance after
        #             "marginBalance":  null,
        #             "timestamp": "2017-03-22T13:09:23.514Z"
        #         }
        #     ]
        #
        return self.parse_ledger(response, currency, since, limit)

    async def fetch_deposits_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch history of deposits and withdrawals
        :see: https://www.bitmex.com/api/explorer/#not /User/User_getWalletHistory
        :param str [code]: unified currency code for the currency of the deposit/withdrawals, default is None
        :param int [since]: timestamp in ms of the earliest deposit/withdrawal, default is None
        :param int [limit]: max number of deposit/withdrawals to return, default is None
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a list of `transaction structure <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        await self.load_markets()
        request: dict = {
            'currency': 'all',
            # 'start': 123,
        }
        #
        #     if since is not None:
        #         # date-based pagination not supported
        #     }
        #
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        if limit is not None:
            request['count'] = limit
        response = await self.privateGetUserWalletHistory(self.extend(request, params))
        transactions = self.filter_by_array(response, 'transactType', ['Withdrawal', 'Deposit'], False)
        return self.parse_transactions(transactions, currency, since, limit)

    def parse_transaction_status(self, status: Str):
        statuses: dict = {
            'Confirmed': 'pending',
            'Canceled': 'canceled',
            'Completed': 'ok',
            'Pending': 'pending',
        }
        return self.safe_string(statuses, status, status)

    def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
        #
        #    {
        #        "transactID": "ffe699c2-95ee-4c13-91f9-0faf41daec25",
        #        "account": 123456,
        #        "currency": "XBt",
        #        "network":'',  # "tron" for USDt, etc...
        #        "transactType": "Withdrawal",
        #        "amount": -100100000,
        #        "fee": 100000,
        #        "transactStatus": "Completed",
        #        "address": "385cR5DM96n1HvBDMzLHPYcw89fZAXULJP",
        #        "tx": "3BMEXabcdefghijklmnopqrstuvwxyz123",
        #        "text": '',
        #        "transactTime": "2019-01-02T01:00:00.000Z",
        #        "walletBalance": 99900000,  # self field might be inexistent
        #        "marginBalance": None,  # self field might be inexistent
        #        "timestamp": "2019-01-02T13:00:00.000Z"
        #    }
        #
        currencyId = self.safe_string(transaction, 'currency')
        currency = self.safe_currency(currencyId, currency)
        # For deposits, transactTime == timestamp
        # For withdrawals, transactTime is submission, timestamp is processed
        transactTime = self.parse8601(self.safe_string(transaction, 'transactTime'))
        timestamp = self.parse8601(self.safe_string(transaction, 'timestamp'))
        type = self.safe_string_lower(transaction, 'transactType')
        # Deposits have no from address or to address, withdrawals have both
        address = None
        addressFrom = None
        addressTo = None
        if type == 'withdrawal':
            address = self.safe_string(transaction, 'address')
            addressFrom = self.safe_string(transaction, 'tx')
            addressTo = address
        elif type == 'deposit':
            addressTo = self.safe_string(transaction, 'address')
            addressFrom = self.safe_string(transaction, 'tx')
        amountString = self.safe_string(transaction, 'amount')
        amountStringAbs = Precise.string_abs(amountString)
        amount = self.convert_to_real_amount(currency['code'], amountStringAbs)
        feeCostString = self.safe_string(transaction, 'fee')
        feeCost = self.convert_to_real_amount(currency['code'], feeCostString)
        status = self.safe_string(transaction, 'transactStatus')
        if status is not None:
            status = self.parse_transaction_status(status)
        return {
            'info': transaction,
            'id': self.safe_string(transaction, 'transactID'),
            'txid': self.safe_string(transaction, 'tx'),
            'type': type,
            'currency': currency['code'],
            'network': self.network_id_to_code(self.safe_string(transaction, 'network'), currency['code']),
            'amount': self.parse_number(amount),
            'status': status,
            'timestamp': transactTime,
            'datetime': self.iso8601(transactTime),
            'address': address,
            'addressFrom': addressFrom,
            'addressTo': addressTo,
            'tag': None,
            'tagFrom': None,
            'tagTo': None,
            'updated': timestamp,
            'internal': None,
            'comment': None,
            'fee': {
                'currency': currency['code'],
                'cost': self.parse_number(feeCost),
                'rate': None,
            },
        }

    async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
        """
        fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
        :see: https://www.bitmex.com/api/explorer/#not /Instrument/Instrument_get
        :param str symbol: unified symbol of the market to fetch the ticker for
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `ticker structure <https://docs.ccxt_zagrava.com/#/?id=ticker-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        response = await self.publicGetInstrument(self.extend(request, params))
        ticker = self.safe_value(response, 0)
        if ticker is None:
            raise BadSymbol(self.id + ' fetchTicker() symbol ' + symbol + ' not found')
        return self.parse_ticker(ticker, market)

    async def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
        """
        fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
        :see: https://www.bitmex.com/api/explorer/#not /Instrument/Instrument_getActiveAndIndices
        :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `ticker structures <https://docs.ccxt_zagrava.com/#/?id=ticker-structure>`
        """
        await self.load_markets()
        symbols = self.market_symbols(symbols)
        response = await self.publicGetInstrumentActiveAndIndices(params)
        # same response "fetchMarkets"
        result: dict = {}
        for i in range(0, len(response)):
            ticker = self.parse_ticker(response[i])
            symbol = self.safe_string(ticker, 'symbol')
            if symbol is not None:
                result[symbol] = ticker
        return self.filter_by_array_tickers(result, 'symbol', symbols)

    def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
        # see response sample under "fetchMarkets" because same endpoint is being used here
        marketId = self.safe_string(ticker, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        timestamp = self.parse8601(self.safe_string(ticker, 'timestamp'))
        open = self.safe_string(ticker, 'prevPrice24h')
        last = self.safe_string(ticker, 'lastPrice')
        return self.safe_ticker({
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_string(ticker, 'highPrice'),
            'low': self.safe_string(ticker, 'lowPrice'),
            'bid': self.safe_string(ticker, 'bidPrice'),
            'bidVolume': None,
            'ask': self.safe_string(ticker, 'askPrice'),
            'askVolume': None,
            'vwap': self.safe_string(ticker, 'vwap'),
            'open': open,
            'close': last,
            'last': last,
            'previousClose': None,
            'change': None,
            'percentage': None,
            'average': None,
            'baseVolume': self.safe_string(ticker, 'homeNotional24h'),
            'quoteVolume': self.safe_string(ticker, 'foreignNotional24h'),
            'info': ticker,
        }, market)

    def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
        #
        #     {
        #         "timestamp":"2015-09-25T13:38:00.000Z",
        #         "symbol":"XBTUSD",
        #         "open":237.45,
        #         "high":237.45,
        #         "low":237.45,
        #         "close":237.45,
        #         "trades":0,
        #         "volume":0,
        #         "vwap":null,
        #         "lastSize":null,
        #         "turnover":0,
        #         "homeNotional":0,
        #         "foreignNotional":0
        #     }
        #
        marketId = self.safe_string(ohlcv, 'symbol')
        market = self.safe_market(marketId, market)
        volume = self.convert_from_raw_quantity(market['symbol'], self.safe_string(ohlcv, 'volume'))
        return [
            self.parse8601(self.safe_string(ohlcv, 'timestamp')),
            self.safe_number(ohlcv, 'open'),
            self.safe_number(ohlcv, 'high'),
            self.safe_number(ohlcv, 'low'),
            self.safe_number(ohlcv, 'close'),
            volume,
        ]

    async def fetch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
        """
        fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
        :see: https://www.bitmex.com/api/explorer/#not /Trade/Trade_getBucketed
        :param str symbol: unified symbol of the market to fetch OHLCV data for
        :param str timeframe: the length of time each candle represents
        :param int [since]: timestamp in ms of the earliest candle to fetch
        :param int [limit]: the maximum amount of candles to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt_zagrava/ccxt_zagrava/wiki/Manual#pagination-params)
        :returns int[][]: A list of candles ordered, open, high, low, close, volume
        """
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchOHLCV', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_deterministic('fetchOHLCV', symbol, since, limit, timeframe, params)
        # send JSON key/value pairs, such as {"key": "value"}
        # filter by individual fields and do advanced queries on timestamps
        # filter: Dict = {'key': 'value'}
        # send a bare series(e.g. XBU) to nearest expiring contract in that series
        # you can also send a timeframe, e.g. XBU:monthly
        # timeframes: daily, weekly, monthly, quarterly, and biquarterly
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
            'binSize': self.safe_string(self.timeframes, timeframe, timeframe),
            'partial': True,     # True == include yet-incomplete current bins
            # 'filter': filter,  # filter by individual fields and do advanced queries
            # 'columns': [],    # will return all columns if omitted
            # 'start': 0,       # starting point for results(wtf?)
            # 'reverse': False,  # True == newest first
            # 'endTime': '',    # ending date filter for results
        }
        if limit is not None:
            request['count'] = limit  # default 100, max 500
        until = self.safe_integer_2(params, 'until', 'endTime')
        if until is not None:
            params = self.omit(params, ['until'])
            request['endTime'] = self.iso8601(until)
        duration = self.parse_timeframe(timeframe) * 1000
        fetchOHLCVOpenTimestamp = self.safe_bool(self.options, 'fetchOHLCVOpenTimestamp', True)
        # if since is not set, they will return candles starting from 2017-01-01
        if since is not None:
            timestamp = since
            if fetchOHLCVOpenTimestamp:
                timestamp = self.sum(timestamp, duration)
            startTime = self.iso8601(timestamp)
            request['startTime'] = startTime  # starting date filter for results
        else:
            request['reverse'] = True
        response = await self.publicGetTradeBucketed(self.extend(request, params))
        #
        #     [
        #         {"timestamp":"2015-09-25T13:38:00.000Z","symbol":"XBTUSD","open":237.45,"high":237.45,"low":237.45,"close":237.45,"trades":0,"volume":0,"vwap":null,"lastSize":null,"turnover":0,"homeNotional":0,"foreignNotional":0},
        #         {"timestamp":"2015-09-25T13:39:00.000Z","symbol":"XBTUSD","open":237.45,"high":237.45,"low":237.45,"close":237.45,"trades":0,"volume":0,"vwap":null,"lastSize":null,"turnover":0,"homeNotional":0,"foreignNotional":0},
        #         {"timestamp":"2015-09-25T13:40:00.000Z","symbol":"XBTUSD","open":237.45,"high":237.45,"low":237.45,"close":237.45,"trades":0,"volume":0,"vwap":null,"lastSize":null,"turnover":0,"homeNotional":0,"foreignNotional":0}
        #     ]
        #
        result = self.parse_ohlcvs(response, market, timeframe, since, limit)
        if fetchOHLCVOpenTimestamp:
            # bitmex returns the candle's close timestamp - https://github.com/ccxt_zagrava/ccxt_zagrava/issues/4446
            # we can emulate the open timestamp by shifting all the timestamps one place
            # so the previous close becomes the current open, and we drop the first candle
            for i in range(0, len(result)):
                result[i][0] = result[i][0] - duration
        return result

    def parse_trade(self, trade: dict, market: Market = None) -> Trade:
        #
        # fetchTrades(public)
        #
        #     {
        #         "timestamp": "2018-08-28T00:00:02.735Z",
        #         "symbol": "XBTUSD",
        #         "side": "Buy",
        #         "size": 2000,
        #         "price": 6906.5,
        #         "tickDirection": "PlusTick",
        #         "trdMatchID": "b9a42432-0a46-6a2f-5ecc-c32e9ca4baf8",
        #         "grossValue": 28958000,
        #         "homeNotional": 0.28958,
        #         "foreignNotional": 2000
        #     }
        #
        # fetchMyTrades(private)
        #
        #     {
        #         "execID": "string",
        #         "orderID": "string",
        #         "clOrdID": "string",
        #         "clOrdLinkID": "string",
        #         "account": 0,
        #         "symbol": "string",
        #         "side": "string",
        #         "lastQty": 0,
        #         "lastPx": 0,
        #         "underlyingLastPx": 0,
        #         "lastMkt": "string",
        #         "lastLiquidityInd": "string",
        #         "simpleOrderQty": 0,
        #         "orderQty": 0,
        #         "price": 0,
        #         "displayQty": 0,
        #         "stopPx": 0,
        #         "pegOffsetValue": 0,
        #         "pegPriceType": "string",
        #         "currency": "string",
        #         "settlCurrency": "string",
        #         "execType": "string",
        #         "ordType": "string",
        #         "timeInForce": "string",
        #         "execInst": "string",
        #         "contingencyType": "string",
        #         "exDestination": "string",
        #         "ordStatus": "string",
        #         "triggered": "string",
        #         "workingIndicator": True,
        #         "ordRejReason": "string",
        #         "simpleLeavesQty": 0,
        #         "leavesQty": 0,
        #         "simpleCumQty": 0,
        #         "cumQty": 0,
        #         "avgPx": 0,
        #         "commission": 0,
        #         "tradePublishIndicator": "string",
        #         "multiLegReportingType": "string",
        #         "text": "string",
        #         "trdMatchID": "string",
        #         "execCost": 0,
        #         "execComm": 0,
        #         "homeNotional": 0,
        #         "foreignNotional": 0,
        #         "transactTime": "2019-03-05T12:47:02.762Z",
        #         "timestamp": "2019-03-05T12:47:02.762Z"
        #     }
        #
        marketId = self.safe_string(trade, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        timestamp = self.parse8601(self.safe_string(trade, 'timestamp'))
        priceString = self.safe_string_2(trade, 'avgPx', 'price')
        amountString = self.convert_from_raw_quantity(symbol, self.safe_string_2(trade, 'size', 'lastQty'))
        execCost = self.number_to_string(self.convert_from_raw_cost(symbol, self.safe_string(trade, 'execCost')))
        id = self.safe_string(trade, 'trdMatchID')
        order = self.safe_string(trade, 'orderID')
        side = self.safe_string_lower(trade, 'side')
        # price * amount doesn't work for all symbols(e.g. XBT, ETH)
        fee = None
        feeCostString = self.number_to_string(self.convert_from_raw_cost(symbol, self.safe_string(trade, 'execComm')))
        if feeCostString is not None:
            currencyId = self.safe_string_2(trade, 'settlCurrency', 'currency')
            fee = {
                'cost': feeCostString,
                'currency': self.safe_currency_code(currencyId),
                'rate': self.safe_string(trade, 'commission'),
            }
        # Trade or Funding
        execType = self.safe_string(trade, 'execType')
        takerOrMaker = None
        if feeCostString is not None and execType == 'Trade':
            takerOrMaker = 'maker' if Precise.string_lt(feeCostString, '0') else 'taker'
        type = self.safe_string_lower(trade, 'ordType')
        return self.safe_trade({
            'info': trade,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'id': id,
            'order': order,
            'type': type,
            'takerOrMaker': takerOrMaker,
            'side': side,
            'price': priceString,
            'cost': Precise.string_abs(execCost),
            'amount': amountString,
            'fee': fee,
        }, market)

    def parse_order_status(self, status: Str):
        statuses: dict = {
            'New': 'open',
            'PartiallyFilled': 'open',
            'Filled': 'closed',
            'DoneForDay': 'open',
            'Canceled': 'canceled',
            'PendingCancel': 'open',
            'PendingNew': 'open',
            'Rejected': 'rejected',
            'Expired': 'expired',
            'Stopped': 'open',
            'Untriggered': 'open',
            'Triggered': 'open',
        }
        return self.safe_string(statuses, status, status)

    def parse_time_in_force(self, timeInForce: Str):
        timeInForces: dict = {
            'Day': 'Day',
            'GoodTillCancel': 'GTC',
            'ImmediateOrCancel': 'IOC',
            'FillOrKill': 'FOK',
        }
        return self.safe_string(timeInForces, timeInForce, timeInForce)

    def parse_order(self, order: dict, market: Market = None) -> Order:
        #
        #     {
        #         "orderID":"56222c7a-9956-413a-82cf-99f4812c214b",
        #         "clOrdID":"",
        #         "clOrdLinkID":"",
        #         "account":1455728,
        #         "symbol":"XBTUSD",
        #         "side":"Sell",
        #         "simpleOrderQty":null,
        #         "orderQty":1,
        #         "price":40000,
        #         "displayQty":null,
        #         "stopPx":null,
        #         "pegOffsetValue":null,
        #         "pegPriceType":"",
        #         "currency":"USD",
        #         "settlCurrency":"XBt",
        #         "ordType":"Limit",
        #         "timeInForce":"GoodTillCancel",
        #         "execInst":"",
        #         "contingencyType":"",
        #         "exDestination":"XBME",
        #         "ordStatus":"New",
        #         "triggered":"",
        #         "workingIndicator":true,
        #         "ordRejReason":"",
        #         "simpleLeavesQty":null,
        #         "leavesQty":1,
        #         "simpleCumQty":null,
        #         "cumQty":0,
        #         "avgPx":null,
        #         "multiLegReportingType":"SingleSecurity",
        #         "text":"Submitted via API.",
        #         "transactTime":"2021-01-02T21:38:49.246Z",
        #         "timestamp":"2021-01-02T21:38:49.246Z"
        #     }
        #
        marketId = self.safe_string(order, 'symbol')
        market = self.safe_market(marketId, market)
        symbol = market['symbol']
        qty = self.safe_string(order, 'orderQty')
        cost = None
        amount = None
        isInverse = False
        if marketId is None:
            defaultSubType = self.safe_string(self.options, 'defaultSubType', 'linear')
            isInverse = (defaultSubType == 'inverse')
        else:
            isInverse = self.safe_bool(market, 'inverse', False)
        if isInverse:
            cost = self.convert_from_raw_quantity(symbol, qty)
        else:
            amount = self.convert_from_raw_quantity(symbol, qty)
        average = self.safe_string(order, 'avgPx')
        filled = None
        cumQty = self.number_to_string(self.convert_from_raw_quantity(symbol, self.safe_string(order, 'cumQty')))
        if isInverse:
            filled = Precise.string_div(cumQty, average)
        else:
            filled = cumQty
        execInst = self.safe_string(order, 'execInst')
        postOnly = None
        if execInst is not None:
            postOnly = (execInst == 'ParticipateDoNotInitiate')
        timestamp = self.parse8601(self.safe_string(order, 'timestamp'))
        stopPrice = self.safe_number(order, 'stopPx')
        remaining = self.safe_string(order, 'leavesQty')
        return self.safe_order({
            'info': order,
            'id': self.safe_string(order, 'orderID'),
            'clientOrderId': self.safe_string(order, 'clOrdID'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': self.parse8601(self.safe_string(order, 'transactTime')),
            'symbol': symbol,
            'type': self.safe_string_lower(order, 'ordType'),
            'timeInForce': self.parse_time_in_force(self.safe_string(order, 'timeInForce')),
            'postOnly': postOnly,
            'side': self.safe_string_lower(order, 'side'),
            'price': self.safe_string(order, 'price'),
            'stopPrice': stopPrice,
            'triggerPrice': stopPrice,
            'amount': amount,
            'cost': cost,
            'average': average,
            'filled': filled,
            'remaining': self.convert_from_raw_quantity(symbol, remaining),
            'status': self.parse_order_status(self.safe_string(order, 'ordStatus')),
            'fee': None,
            'trades': None,
        }, market)

    async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
        """
        get the list of most recent trades for a particular symbol
        :see: https://www.bitmex.com/api/explorer/#not /Trade/Trade_get
        :param str symbol: unified symbol of the market to fetch trades for
        :param int [since]: timestamp in ms of the earliest trade to fetch
        :param int [limit]: the maximum amount of trades to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt_zagrava/ccxt_zagrava/wiki/Manual#pagination-params)
        :returns Trade[]: a list of `trade structures <https://docs.ccxt_zagrava.com/#/?id=public-trades>`
        """
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchTrades', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_dynamic('fetchTrades', symbol, since, limit, params)
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        if since is not None:
            request['startTime'] = self.iso8601(since)
        else:
            # by default reverse=false, i.e. trades are fetched since the time of market inception(year 2015 for XBTUSD)
            request['reverse'] = True
        if limit is not None:
            request['count'] = min(limit, 1000)  # api maximum 1000
        until = self.safe_integer_2(params, 'until', 'endTime')
        if until is not None:
            params = self.omit(params, ['until'])
            request['endTime'] = self.iso8601(until)
        response = await self.publicGetTrade(self.extend(request, params))
        #
        #     [
        #         {
        #             "timestamp": "2018-08-28T00:00:02.735Z",
        #             "symbol": "XBTUSD",
        #             "side": "Buy",
        #             "size": 2000,
        #             "price": 6906.5,
        #             "tickDirection": "PlusTick",
        #             "trdMatchID": "b9a42432-0a46-6a2f-5ecc-c32e9ca4baf8",
        #             "grossValue": 28958000,
        #             "homeNotional": 0.28958,
        #             "foreignNotional": 2000
        #         },
        #         {
        #             "timestamp": "2018-08-28T00:00:03.778Z",
        #             "symbol": "XBTUSD",
        #             "side": "Sell",
        #             "size": 1000,
        #             "price": 6906,
        #             "tickDirection": "MinusTick",
        #             "trdMatchID": "0d4f1682-5270-a800-569b-4a0eb92db97c",
        #             "grossValue": 14480000,
        #             "homeNotional": 0.1448,
        #             "foreignNotional": 1000
        #         },
        #     ]
        #
        return self.parse_trades(response, market, since, limit)

    async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
        create a trade order
        :see: https://www.bitmex.com/api/explorer/#not /Order/Order_new
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of currency you want to trade in units of base currency
        :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param dict [params.triggerPrice]: the price at which a trigger order is triggered at
        :param dict [params.triggerDirection]: the direction whenever the trigger happens with relation to price - 'above' or 'below'
        :param float [params.trailingAmount]: the quote amount to trail away from the current market price
        :returns dict: an `order structure <https://github.com/ccxt_zagrava/ccxt_zagrava/wiki/Manual#order-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        orderType = self.capitalize(type)
        reduceOnly = self.safe_value(params, 'reduceOnly')
        if reduceOnly is not None:
            if (not market['swap']) and (not market['future']):
                raise InvalidOrder(self.id + ' createOrder() does not support reduceOnly for ' + market['type'] + ' orders, reduceOnly orders are supported for swap and future markets only')
        brokerId = self.safe_string(self.options, 'brokerId', 'ccxt_zagrava')
        qty = self.parse_to_int(self.amount_to_precision(symbol, amount))
        request: dict = {
            'symbol': market['id'],
            'side': self.capitalize(side),
            'orderQty': qty,  # lot size multiplied by the number of contracts
            'ordType': orderType,
            'text': brokerId,
        }
        # support for unified trigger format
        triggerPrice = self.safe_number_n(params, ['triggerPrice', 'stopPx', 'stopPrice'])
        trailingAmount = self.safe_string_2(params, 'trailingAmount', 'pegOffsetValue')
        isTriggerOrder = triggerPrice is not None
        isTrailingAmountOrder = trailingAmount is not None
        if isTriggerOrder or isTrailingAmountOrder:
            triggerDirection = self.safe_string(params, 'triggerDirection')
            triggerAbove = (triggerDirection == 'above')
            if (type == 'limit') or (type == 'market'):
                self.check_required_argument('createOrder', triggerDirection, 'triggerDirection', ['above', 'below'])
            if type == 'limit':
                if side == 'buy':
                    orderType = 'StopLimit' if triggerAbove else 'LimitIfTouched'
                else:
                    orderType = 'LimitIfTouched' if triggerAbove else 'StopLimit'
            elif type == 'market':
                if side == 'buy':
                    orderType = 'Stop' if triggerAbove else 'MarketIfTouched'
                else:
                    orderType = 'MarketIfTouched' if triggerAbove else 'Stop'
            if isTrailingAmountOrder:
                isStopSellOrder = (side == 'sell') and ((orderType == 'Stop') or (orderType == 'StopLimit'))
                isBuyIfTouchedOrder = (side == 'buy') and ((orderType == 'MarketIfTouched') or (orderType == 'LimitIfTouched'))
                if isStopSellOrder or isBuyIfTouchedOrder:
                    trailingAmount = '-' + trailingAmount
                request['pegOffsetValue'] = self.parse_to_numeric(trailingAmount)
                request['pegPriceType'] = 'TrailingStopPeg'
            else:
                if triggerPrice is None:
                    # if exchange specific trigger types were provided
                    raise ArgumentsRequired(self.id + ' createOrder() requires a triggerPrice(stopPx|stopPrice) parameter for the ' + orderType + ' order type')
                request['stopPx'] = self.parse_to_numeric(self.price_to_precision(symbol, triggerPrice))
            request['ordType'] = orderType
            params = self.omit(params, ['triggerPrice', 'stopPrice', 'stopPx', 'triggerDirection', 'trailingAmount'])
        if (orderType == 'Limit') or (orderType == 'StopLimit') or (orderType == 'LimitIfTouched'):
            request['price'] = self.parse_to_numeric(self.price_to_precision(symbol, price))
        clientOrderId = self.safe_string_2(params, 'clOrdID', 'clientOrderId')
        if clientOrderId is not None:
            request['clOrdID'] = clientOrderId
            params = self.omit(params, ['clOrdID', 'clientOrderId'])
        response = await self.privatePostOrder(self.extend(request, params))
        return self.parse_order(response, market)

    async def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
        await self.load_markets()
        request: dict = {}
        trailingAmount = self.safe_string_2(params, 'trailingAmount', 'pegOffsetValue')
        isTrailingAmountOrder = trailingAmount is not None
        if isTrailingAmountOrder:
            triggerDirection = self.safe_string(params, 'triggerDirection')
            triggerAbove = (triggerDirection == 'above')
            if (type == 'limit') or (type == 'market'):
                self.check_required_argument('createOrder', triggerDirection, 'triggerDirection', ['above', 'below'])
            orderType = None
            if type == 'limit':
                if side == 'buy':
                    orderType = 'StopLimit' if triggerAbove else 'LimitIfTouched'
                else:
                    orderType = 'LimitIfTouched' if triggerAbove else 'StopLimit'
            elif type == 'market':
                if side == 'buy':
                    orderType = 'Stop' if triggerAbove else 'MarketIfTouched'
                else:
                    orderType = 'MarketIfTouched' if triggerAbove else 'Stop'
            isStopSellOrder = (side == 'sell') and ((orderType == 'Stop') or (orderType == 'StopLimit'))
            isBuyIfTouchedOrder = (side == 'buy') and ((orderType == 'MarketIfTouched') or (orderType == 'LimitIfTouched'))
            if isStopSellOrder or isBuyIfTouchedOrder:
                trailingAmount = '-' + trailingAmount
            request['pegOffsetValue'] = self.parse_to_numeric(trailingAmount)
            params = self.omit(params, ['triggerDirection', 'trailingAmount'])
        origClOrdID = self.safe_string_2(params, 'origClOrdID', 'clientOrderId')
        if origClOrdID is not None:
            request['origClOrdID'] = origClOrdID
            clientOrderId = self.safe_string(params, 'clOrdID', 'clientOrderId')
            if clientOrderId is not None:
                request['clOrdID'] = clientOrderId
            params = self.omit(params, ['origClOrdID', 'clOrdID', 'clientOrderId'])
        else:
            request['orderID'] = id
        if amount is not None:
            qty = self.parse_to_int(self.amount_to_precision(symbol, amount))
            request['orderQty'] = qty
        if price is not None:
            request['price'] = price
        brokerId = self.safe_string(self.options, 'brokerId', 'ccxt_zagrava')
        request['text'] = brokerId
        response = await self.privatePutOrder(self.extend(request, params))
        return self.parse_order(response)

    async def cancel_order(self, id: str, symbol: Str = None, params={}):
        """
        cancels an open order
        :see: https://www.bitmex.com/api/explorer/#not /Order/Order_cancel
        :param str id: order id
        :param str symbol: not used by bitmex cancelOrder()
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: An `order structure <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        # https://github.com/ccxt_zagrava/ccxt_zagrava/issues/6507
        clientOrderId = self.safe_value_2(params, 'clOrdID', 'clientOrderId')
        request: dict = {}
        if clientOrderId is None:
            request['orderID'] = id
        else:
            request['clOrdID'] = clientOrderId
            params = self.omit(params, ['clOrdID', 'clientOrderId'])
        response = await self.privateDeleteOrder(self.extend(request, params))
        order = self.safe_value(response, 0, {})
        error = self.safe_string(order, 'error')
        if error is not None:
            if error.find('Unable to cancel order due to existing state') >= 0:
                raise OrderNotFound(self.id + ' cancelOrder() failed: ' + error)
        return self.parse_order(order)

    async def cancel_orders(self, ids, symbol: Str = None, params={}):
        """
        cancel multiple orders
        :see: https://www.bitmex.com/api/explorer/#not /Order/Order_cancel
        :param str[] ids: order ids
        :param str symbol: not used by bitmex cancelOrders()
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        # return await self.cancel_order(ids, symbol, params)
        await self.load_markets()
        # https://github.com/ccxt_zagrava/ccxt_zagrava/issues/6507
        clientOrderId = self.safe_value_2(params, 'clOrdID', 'clientOrderId')
        request: dict = {}
        if clientOrderId is None:
            request['orderID'] = ids
        else:
            request['clOrdID'] = clientOrderId
            params = self.omit(params, ['clOrdID', 'clientOrderId'])
        response = await self.privateDeleteOrder(self.extend(request, params))
        return self.parse_orders(response)

    async def cancel_all_orders(self, symbol: Str = None, params={}):
        """
        cancel all open orders
        :see: https://www.bitmex.com/api/explorer/#not /Order/Order_cancelAll
        :param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `order structures <https://docs.ccxt_zagrava.com/#/?id=order-structure>`
        """
        await self.load_markets()
        request: dict = {}
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        response = await self.privateDeleteOrderAll(self.extend(request, params))
        #
        #     [
        #         {
        #             "orderID": "string",
        #             "clOrdID": "string",
        #             "clOrdLinkID": "string",
        #             "account": 0,
        #             "symbol": "string",
        #             "side": "string",
        #             "simpleOrderQty": 0,
        #             "orderQty": 0,
        #             "price": 0,
        #             "displayQty": 0,
        #             "stopPx": 0,
        #             "pegOffsetValue": 0,
        #             "pegPriceType": "string",
        #             "currency": "string",
        #             "settlCurrency": "string",
        #             "ordType": "string",
        #             "timeInForce": "string",
        #             "execInst": "string",
        #             "contingencyType": "string",
        #             "exDestination": "string",
        #             "ordStatus": "string",
        #             "triggered": "string",
        #             "workingIndicator": True,
        #             "ordRejReason": "string",
        #             "simpleLeavesQty": 0,
        #             "leavesQty": 0,
        #             "simpleCumQty": 0,
        #             "cumQty": 0,
        #             "avgPx": 0,
        #             "multiLegReportingType": "string",
        #             "text": "string",
        #             "transactTime": "2020-06-01T09:36:35.290Z",
        #             "timestamp": "2020-06-01T09:36:35.290Z"
        #         }
        #     ]
        #
        return self.parse_orders(response, market)

    async def cancel_all_orders_after(self, timeout: Int, params={}):
        """
        dead man's switch, cancel all orders after the given timeout
        :see: https://www.bitmex.com/api/explorer/#not /Order/Order_cancelAllAfter
        :param number timeout: time in milliseconds, 0 represents cancel the timer
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: the api result
        """
        await self.load_markets()
        request: dict = {
            'timeout': self.parse_to_int(timeout / 1000) if (timeout > 0) else 0,
        }
        response = await self.privatePostOrderCancelAllAfter(self.extend(request, params))
        #
        #     {
        #         now: '2024-04-09T09:01:56.560Z',
        #         cancelTime: '2024-04-09T09:01:56.660Z'
        #     }
        #
        return response

    async def fetch_leverages(self, symbols: Strings = None, params={}) -> Leverages:
        """
        fetch the set leverage for all contract markets
        :see: https://www.bitmex.com/api/explorer/#not /Position/Position_get
        :param str[] [symbols]: a list of unified market symbols
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a list of `leverage structures <https://docs.ccxt_zagrava.com/#/?id=leverage-structure>`
        """
        await self.load_markets()
        leverages = await self.fetch_positions(symbols, params)
        return self.parse_leverages(leverages, symbols, 'symbol')

    def parse_leverage(self, leverage: dict, market: Market = None) -> Leverage:
        marketId = self.safe_string(leverage, 'symbol')
        return {
            'info': leverage,
            'symbol': self.safe_symbol(marketId, market),
            'marginMode': self.safe_string_lower(leverage, 'marginMode'),
            'longLeverage': self.safe_integer(leverage, 'leverage'),
            'shortLeverage': self.safe_integer(leverage, 'leverage'),
        }

    async def fetch_positions(self, symbols: Strings = None, params={}):
        """
        fetch all open positions
        :see: https://www.bitmex.com/api/explorer/#not /Position/Position_get
        :param str[]|None symbols: list of unified market symbols
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `position structure <https://docs.ccxt_zagrava.com/#/?id=position-structure>`
        """
        await self.load_markets()
        response = await self.privateGetPosition(params)
        #
        #     [
        #         {
        #             "account": 0,
        #             "symbol": "string",
        #             "currency": "string",
        #             "underlying": "string",
        #             "quoteCurrency": "string",
        #             "commission": 0,
        #             "initMarginReq": 0,
        #             "maintMarginReq": 0,
        #             "riskLimit": 0,
        #             "leverage": 0,
        #             "crossMargin": True,
        #             "deleveragePercentile": 0,
        #             "rebalancedPnl": 0,
        #             "prevRealisedPnl": 0,
        #             "prevUnrealisedPnl": 0,
        #             "prevClosePrice": 0,
        #             "openingTimestamp": "2020-11-09T06:53:59.892Z",
        #             "openingQty": 0,
        #             "openingCost": 0,
        #             "openingComm": 0,
        #             "openOrderBuyQty": 0,
        #             "openOrderBuyCost": 0,
        #             "openOrderBuyPremium": 0,
        #             "openOrderSellQty": 0,
        #             "openOrderSellCost": 0,
        #             "openOrderSellPremium": 0,
        #             "execBuyQty": 0,
        #             "execBuyCost": 0,
        #             "execSellQty": 0,
        #             "execSellCost": 0,
        #             "execQty": 0,
        #             "execCost": 0,
        #             "execComm": 0,
        #             "currentTimestamp": "2020-11-09T06:53:59.893Z",
        #             "currentQty": 0,
        #             "currentCost": 0,
        #             "currentComm": 0,
        #             "realisedCost": 0,
        #             "unrealisedCost": 0,
        #             "grossOpenCost": 0,
        #             "grossOpenPremium": 0,
        #             "grossExecCost": 0,
        #             "isOpen": True,
        #             "markPrice": 0,
        #             "markValue": 0,
        #             "riskValue": 0,
        #             "homeNotional": 0,
        #             "foreignNotional": 0,
        #             "posState": "string",
        #             "posCost": 0,
        #             "posCost2": 0,
        #             "posCross": 0,
        #             "posInit": 0,
        #             "posComm": 0,
        #             "posLoss": 0,
        #             "posMargin": 0,
        #             "posMaint": 0,
        #             "posAllowance": 0,
        #             "taxableMargin": 0,
        #             "initMargin": 0,
        #             "maintMargin": 0,
        #             "sessionMargin": 0,
        #             "targetExcessMargin": 0,
        #             "varMargin": 0,
        #             "realisedGrossPnl": 0,
        #             "realisedTax": 0,
        #             "realisedPnl": 0,
        #             "unrealisedGrossPnl": 0,
        #             "longBankrupt": 0,
        #             "shortBankrupt": 0,
        #             "taxBase": 0,
        #             "indicativeTaxRate": 0,
        #             "indicativeTax": 0,
        #             "unrealisedTax": 0,
        #             "unrealisedPnl": 0,
        #             "unrealisedPnlPcnt": 0,
        #             "unrealisedRoePcnt": 0,
        #             "simpleQty": 0,
        #             "simpleCost": 0,
        #             "simpleValue": 0,
        #             "simplePnl": 0,
        #             "simplePnlPcnt": 0,
        #             "avgCostPrice": 0,
        #             "avgEntryPrice": 0,
        #             "breakEvenPrice": 0,
        #             "marginCallPrice": 0,
        #             "liquidationPrice": 0,
        #             "bankruptPrice": 0,
        #             "timestamp": "2020-11-09T06:53:59.894Z",
        #             "lastPrice": 0,
        #             "lastValue": 0
        #         }
        #     ]
        #
        results = self.parse_positions(response, symbols)
        return self.filter_by_array_positions(results, 'symbol', symbols, False)

    def parse_position(self, position: dict, market: Market = None):
        #
        #     {
        #         "account": 9371654,
        #         "symbol": "ETHUSDT",
        #         "currency": "USDt",
        #         "underlying": "ETH",
        #         "quoteCurrency": "USDT",
        #         "commission": 0.00075,
        #         "initMarginReq": 0.3333333333333333,
        #         "maintMarginReq": 0.01,
        #         "riskLimit": 1000000000000,
        #         "leverage": 3,
        #         "crossMargin": False,
        #         "deleveragePercentile": 1,
        #         "rebalancedPnl": 0,
        #         "prevRealisedPnl": 0,
        #         "prevUnrealisedPnl": 0,
        #         "prevClosePrice": 2053.738,
        #         "openingTimestamp": "2022-05-21T04:00:00.000Z",
        #         "openingQty": 0,
        #         "openingCost": 0,
        #         "openingComm": 0,
        #         "openOrderBuyQty": 0,
        #         "openOrderBuyCost": 0,
        #         "openOrderBuyPremium": 0,
        #         "openOrderSellQty": 0,
        #         "openOrderSellCost": 0,
        #         "openOrderSellPremium": 0,
        #         "execBuyQty": 2000,
        #         "execBuyCost": 39260000,
        #         "execSellQty": 0,
        #         "execSellCost": 0,
        #         "execQty": 2000,
        #         "execCost": 39260000,
        #         "execComm": 26500,
        #         "currentTimestamp": "2022-05-21T04:35:16.397Z",
        #         "currentQty": 2000,
        #         "currentCost": 39260000,
        #         "currentComm": 26500,
        #         "realisedCost": 0,
        #         "unrealisedCost": 39260000,
        #         "grossOpenCost": 0,
        #         "grossOpenPremium": 0,
        #         "grossExecCost": 39260000,
        #         "isOpen": True,
        #         "markPrice": 1964.195,
        #         "markValue": 39283900,
        #         "riskValue": 39283900,
        #         "homeNotional": 0.02,
        #         "foreignNotional": -39.2839,
        #         "posState": "",
        #         "posCost": 39260000,
        #         "posCost2": 39260000,
        #         "posCross": 0,
        #         "posInit": 13086667,
        #         "posComm": 39261,
        #         "posLoss": 0,
        #         "posMargin": 13125928,
        #         "posMaint": 435787,
        #         "posAllowance": 0,
        #         "taxableMargin": 0,
        #         "initMargin": 0,
        #         "maintMargin": 13149828,
        #         "sessionMargin": 0,
        #         "targetExcessMargin": 0,
        #         "varMargin": 0,
        #         "realisedGrossPnl": 0,
        #         "realisedTax": 0,
        #         "realisedPnl": -26500,
        #         "unrealisedGrossPnl": 23900,
        #         "longBankrupt": 0,
        #         "shortBankrupt": 0,
        #         "taxBase": 0,
        #         "indicativeTaxRate": null,
        #         "indicativeTax": 0,
        #         "unrealisedTax": 0,
        #         "unrealisedPnl": 23900,
        #         "unrealisedPnlPcnt": 0.0006,
        #         "unrealisedRoePcnt": 0.0018,
        #         "simpleQty": null,
        #         "simpleCost": null,
        #         "simpleValue": null,
        #         "simplePnl": null,
        #         "simplePnlPcnt": null,
        #         "avgCostPrice": 1963,
        #         "avgEntryPrice": 1963,
        #         "breakEvenPrice": 1964.35,
        #         "marginCallPrice": 1328.5,
        #         "liquidationPrice": 1328.5,
        #         "bankruptPrice": 1308.7,
        #         "timestamp": "2022-05-21T04:35:16.397Z",
        #         "lastPrice": 1964.195,
        #         "lastValue": 39283900
        #     }
        #
        market = self.safe_market(self.safe_string(position, 'symbol'), market)
        symbol = market['symbol']
        datetime = self.safe_string(position, 'timestamp')
        crossMargin = self.safe_value(position, 'crossMargin')
        marginMode = 'cross' if (crossMargin is True) else 'isolated'
        notionalString = Precise.string_abs(self.safe_string_2(position, 'foreignNotional', 'homeNotional'))
        settleCurrencyCode = self.safe_string(market, 'settle')
        maintenanceMargin = self.convert_to_real_amount(settleCurrencyCode, self.safe_string(position, 'maintMargin'))
        unrealisedPnl = self.convert_to_real_amount(settleCurrencyCode, self.safe_string(position, 'unrealisedPnl'))
        contracts = self.parse_number(Precise.string_abs(self.safe_string(position, 'currentQty')))
        contractSize = self.safe_number(market, 'contractSize')
        side = None
        homeNotional = self.safe_string(position, 'homeNotional')
        if homeNotional is not None:
            if homeNotional[0] == '-':
                side = 'short'
            else:
                side = 'long'
        return self.safe_position({
            'info': position,
            'id': self.safe_string(position, 'account'),
            'symbol': symbol,
            'timestamp': self.parse8601(datetime),
            'datetime': datetime,
            'lastUpdateTimestamp': None,
            'hedged': None,
            'side': side,
            'contracts': contracts,
            'contractSize': contractSize,
            'entryPrice': self.safe_number(position, 'avgEntryPrice'),
            'markPrice': self.safe_number(position, 'markPrice'),
            'lastPrice': None,
            'notional': self.parse_number(notionalString),
            'leverage': self.safe_number(position, 'leverage'),
            'collateral': None,
            'initialMargin': self.safe_number(position, 'initMargin'),
            'initialMarginPercentage': self.safe_number(position, 'initMarginReq'),
            'maintenanceMargin': maintenanceMargin,
            'maintenanceMarginPercentage': self.safe_number(position, 'maintMarginReq'),
            'unrealizedPnl': unrealisedPnl,
            'liquidationPrice': self.safe_number(position, 'liquidationPrice'),
            'marginMode': marginMode,
            'marginRatio': None,
            'percentage': self.safe_number(position, 'unrealisedPnlPcnt'),
            'stopLossPrice': None,
            'takeProfitPrice': None,
        })

    async def withdraw(self, code: str, amount: float, address: str, tag=None, params={}):
        """
        make a withdrawal
        :see: https://www.bitmex.com/api/explorer/#not /User/User_requestWithdrawal
        :param str code: unified currency code
        :param float amount: the amount to withdraw
        :param str address: the address to withdraw to
        :param str tag:
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `transaction structure <https://docs.ccxt_zagrava.com/#/?id=transaction-structure>`
        """
        tag, params = self.handle_withdraw_tag_and_params(tag, params)
        self.check_address(address)
        await self.load_markets()
        currency = self.currency(code)
        qty = self.convert_from_real_amount(code, amount)
        networkCode = None
        networkCode, params = self.handle_network_code_and_params(params)
        request: dict = {
            'currency': currency['id'],
            'amount': qty,
            'address': address,
            'network': self.network_code_to_id(networkCode, currency['code']),
            # 'otpToken': '123456',  # requires if two-factor auth(OTP) is enabled
            # 'fee': 0.001,  # bitcoin network fee
        }
        response = await self.privatePostUserRequestWithdrawal(self.extend(request, params))
        #
        #     {
        #         "transactID": "3aece414-bb29-76c8-6c6d-16a477a51a1e",
        #         "account": 1403035,
        #         "currency": "USDt",
        #         "network": "tron",
        #         "transactType": "Withdrawal",
        #         "amount": -11000000,
        #         "fee": 1000000,
        #         "transactStatus": "Pending",
        #         "address": "TAf5JxcAQQsC2Nm2zu21XE2iDtnisxPo1x",
        #         "tx": "",
        #         "text": "",
        #         "transactTime": "2022-12-16T07:37:06.500Z",
        #         "timestamp": "2022-12-16T07:37:06.500Z",
        #     }
        #
        return self.parse_transaction(response, currency)

    async def fetch_funding_rates(self, symbols: Strings = None, params={}):
        """
        fetch the funding rate for multiple markets
        :see: https://www.bitmex.com/api/explorer/#not /Instrument/Instrument_getActiveAndIndices
        :param str[]|None symbols: list of unified market symbols
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `funding rates structures <https://docs.ccxt_zagrava.com/#/?id=funding-rates-structure>`, indexe by market symbols
        """
        await self.load_markets()
        response = await self.publicGetInstrumentActiveAndIndices(params)
        # same response "fetchMarkets"
        filteredResponse = []
        for i in range(0, len(response)):
            item = response[i]
            marketId = self.safe_string(item, 'symbol')
            market = self.safe_market(marketId)
            swap = self.safe_bool(market, 'swap', False)
            if swap:
                filteredResponse.append(item)
        symbols = self.market_symbols(symbols)
        result = self.parse_funding_rates(filteredResponse)
        return self.filter_by_array(result, 'symbol', symbols)

    def parse_funding_rate(self, contract, market: Market = None):
        # see response sample under "fetchMarkets" because same endpoint is being used here
        datetime = self.safe_string(contract, 'timestamp')
        marketId = self.safe_string(contract, 'symbol')
        fundingDatetime = self.safe_string(contract, 'fundingTimestamp')
        return {
            'info': contract,
            'symbol': self.safe_symbol(marketId, market),
            'markPrice': self.safe_number(contract, 'markPrice'),
            'indexPrice': None,
            'interestRate': None,
            'estimatedSettlePrice': self.safe_number(contract, 'indicativeSettlePrice'),
            'timestamp': self.parse8601(datetime),
            'datetime': datetime,
            'fundingRate': self.safe_number(contract, 'fundingRate'),
            'fundingTimestamp': self.iso8601(fundingDatetime),
            'fundingDatetime': fundingDatetime,
            'nextFundingRate': self.safe_number(contract, 'indicativeFundingRate'),
            'nextFundingTimestamp': None,
            'nextFundingDatetime': None,
            'previousFundingRate': None,
            'previousFundingTimestamp': None,
            'previousFundingDatetime': None,
        }

    async def fetch_funding_rate_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        Fetches the history of funding rates
        :see: https://www.bitmex.com/api/explorer/#not /Funding/Funding_get
        :param str symbol: unified symbol of the market to fetch the funding rate history for
        :param int [since]: timestamp in ms of the earliest funding rate to fetch
        :param int [limit]: the maximum amount of `funding rate structures <https://docs.ccxt_zagrava.com/#/?id=funding-rate-history-structure>` to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms for ending date filter
        :param bool [params.reverse]: if True, will sort results newest first
        :param int [params.start]: starting point for results
        :param str [params.columns]: array of column names to fetch in info, if omitted, will return all columns
        :param str [params.filter]: generic table filter, send json key/value pairs, such as {"key": "value"}, you can key on individual fields, and do more advanced querying on timestamps, see the `timestamp docs <https://www.bitmex.com/app/restAPI#Timestamp-Filters>` for more details
        :returns dict[]: a list of `funding rate structures <https://docs.ccxt_zagrava.com/#/?id=funding-rate-history-structure>`
        """
        await self.load_markets()
        request: dict = {}
        market = None
        if symbol in self.currencies:
            code = self.currency(symbol)
            request['symbol'] = code['id']
        elif symbol is not None:
            splitSymbol = symbol.split(':')
            splitSymbolLength = len(splitSymbol)
            timeframes = ['nearest', 'daily', 'weekly', 'monthly', 'quarterly', 'biquarterly', 'perpetual']
            if (splitSymbolLength > 1) and self.in_array(splitSymbol[1], timeframes):
                code = self.currency(splitSymbol[0])
                symbol = code['id'] + ':' + splitSymbol[1]
                request['symbol'] = symbol
            else:
                market = self.market(symbol)
                request['symbol'] = market['id']
        if since is not None:
            request['startTime'] = self.iso8601(since)
        if limit is not None:
            request['count'] = limit
        until = self.safe_integer(params, 'until')
        params = self.omit(params, ['until'])
        if until is not None:
            request['endTime'] = self.iso8601(until)
        if (since is None) and (until is None):
            request['reverse'] = True
        response = await self.publicGetFunding(self.extend(request, params))
        #
        #    [
        #        {
        #            "timestamp": "2016-05-07T12:00:00.000Z",
        #            "symbol": "ETHXBT",
        #            "fundingInterval": "2000-01-02T00:00:00.000Z",
        #            "fundingRate": 0.0010890000000000001,
        #            "fundingRateDaily": 0.0010890000000000001
        #        }
        #    ]
        #
        return self.parse_funding_rate_histories(response, market, since, limit)

    def parse_funding_rate_history(self, info, market: Market = None):
        #
        #    {
        #        "timestamp": "2016-05-07T12:00:00.000Z",
        #        "symbol": "ETHXBT",
        #        "fundingInterval": "2000-01-02T00:00:00.000Z",
        #        "fundingRate": 0.0010890000000000001,
        #        "fundingRateDaily": 0.0010890000000000001
        #    }
        #
        marketId = self.safe_string(info, 'symbol')
        datetime = self.safe_string(info, 'timestamp')
        return {
            'info': info,
            'symbol': self.safe_symbol(marketId, market),
            'fundingRate': self.safe_number(info, 'fundingRate'),
            'timestamp': self.parse8601(datetime),
            'datetime': datetime,
        }

    async def set_leverage(self, leverage: Int, symbol: Str = None, params={}):
        """
        set the level of leverage for a market
        :see: https://www.bitmex.com/api/explorer/#not /Position/Position_updateLeverage
        :param float leverage: the rate of leverage
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: response from the exchange
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' setLeverage() requires a symbol argument')
        if (leverage < 0.01) or (leverage > 100):
            raise BadRequest(self.id + ' leverage should be between 0.01 and 100')
        await self.load_markets()
        market = self.market(symbol)
        if market['type'] != 'swap' and market['type'] != 'future':
            raise BadSymbol(self.id + ' setLeverage() supports future and swap contracts only')
        request: dict = {
            'symbol': market['id'],
            'leverage': leverage,
        }
        return await self.privatePostPositionLeverage(self.extend(request, params))

    async def set_margin_mode(self, marginMode: str, symbol: Str = None, params={}):
        """
        set margin mode to 'cross' or 'isolated'
        :see: https://www.bitmex.com/api/explorer/#not /Position/Position_isolateMargin
        :param str marginMode: 'cross' or 'isolated'
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: response from the exchange
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' setMarginMode() requires a symbol argument')
        marginMode = marginMode.lower()
        if marginMode != 'isolated' and marginMode != 'cross':
            raise BadRequest(self.id + ' setMarginMode() marginMode argument should be isolated or cross')
        await self.load_markets()
        market = self.market(symbol)
        if (market['type'] != 'swap') and (market['type'] != 'future'):
            raise BadSymbol(self.id + ' setMarginMode() supports swap and future contracts only')
        enabled = False if (marginMode == 'cross') else True
        request: dict = {
            'symbol': market['id'],
            'enabled': enabled,
        }
        return await self.privatePostPositionIsolate(self.extend(request, params))

    async def fetch_deposit_address(self, code: str, params={}):
        """
        fetch the deposit address for a currency associated with self account
        :see: https://www.bitmex.com/api/explorer/#not /User/User_getDepositAddress
        :param str code: unified currency code
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.network]: deposit chain, can view all chains via self.publicGetWalletAssets, default is eth, unless the currency has a default chain within self.options['networks']
        :returns dict: an `address structure <https://docs.ccxt_zagrava.com/#/?id=address-structure>`
        """
        await self.load_markets()
        networkCode = None
        networkCode, params = self.handle_network_code_and_params(params)
        if networkCode is None:
            raise ArgumentsRequired(self.id + ' fetchDepositAddress requires params["network"]')
        currency = self.currency(code)
        params = self.omit(params, 'network')
        request: dict = {
            'currency': currency['id'],
            'network': self.network_code_to_id(networkCode, currency['code']),
        }
        response = await self.privateGetUserDepositAddress(self.extend(request, params))
        #
        #    '"bc1qmex3puyrzn2gduqcnlu70c2uscpyaa9nm2l2j9le2lt2wkgmw33sy7ndjg"'
        #
        return {
            'currency': code,
            'address': response.replace('"', '').replace('"', ''),  # Done twice because some languages only replace the first instance
            'tag': None,
            'network': networkCode,
            'info': response,
        }

    def parse_deposit_withdraw_fee(self, fee, currency: Currency = None):
        #
        #    {
        #        "asset": "XBT",
        #        "currency": "XBt",
        #        "majorCurrency": "XBT",
        #        "name": "Bitcoin",
        #        "currencyType": "Crypto",
        #        "scale": "8",
        #        "enabled": True,
        #        "isMarginCurrency": True,
        #        "minDepositAmount": "10000",
        #        "minWithdrawalAmount": "1000",
        #        "maxWithdrawalAmount": "100000000000000",
        #        "networks": [
        #            {
        #                "asset": "btc",
        #                "tokenAddress": '',
        #                "depositEnabled": True,
        #                "withdrawalEnabled": True,
        #                "withdrawalFee": "20000",
        #                "minFee": "20000",
        #                "maxFee": "10000000"
        #            }
        #        ]
        #    }
        #
        networks = self.safe_value(fee, 'networks', [])
        networksLength = len(networks)
        result: dict = {
            'info': fee,
            'withdraw': {
                'fee': None,
                'percentage': None,
            },
            'deposit': {
                'fee': None,
                'percentage': None,
            },
            'networks': {},
        }
        if networksLength != 0:
            scale = self.safe_string(fee, 'scale')
            precision = self.parse_precision(scale)
            for i in range(0, networksLength):
                network = networks[i]
                networkId = self.safe_string(network, 'asset')
                currencyCode = self.safe_string(currency, 'code')
                networkCode = self.network_id_to_code(networkId, currencyCode)
                withdrawalFeeId = self.safe_string(network, 'withdrawalFee')
                withdrawalFee = self.parse_number(Precise.string_mul(withdrawalFeeId, precision))
                result['networks'][networkCode] = {
                    'deposit': {'fee': None, 'percentage': None},
                    'withdraw': {'fee': withdrawalFee, 'percentage': False},
                }
                if networksLength == 1:
                    result['withdraw']['fee'] = withdrawalFee
                    result['withdraw']['percentage'] = False
        return result

    async def fetch_deposit_withdraw_fees(self, codes: Strings = None, params={}):
        """
        fetch deposit and withdraw fees
        :see: https://www.bitmex.com/api/explorer/#not /Wallet/Wallet_getAssetsConfig
        :param str[]|None codes: list of unified currency codes
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a list of `fee structures <https://docs.ccxt_zagrava.com/#/?id=fee-structure>`
        """
        await self.load_markets()
        assets = await self.publicGetWalletAssets(params)
        #
        #    [
        #        {
        #            "asset": "XBT",
        #            "currency": "XBt",
        #            "majorCurrency": "XBT",
        #            "name": "Bitcoin",
        #            "currencyType": "Crypto",
        #            "scale": "8",
        #            "enabled": True,
        #            "isMarginCurrency": True,
        #            "minDepositAmount": "10000",
        #            "minWithdrawalAmount": "1000",
        #            "maxWithdrawalAmount": "100000000000000",
        #            "networks": [
        #                {
        #                    "asset": "btc",
        #                    "tokenAddress": '',
        #                    "depositEnabled": True,
        #                    "withdrawalEnabled": True,
        #                    "withdrawalFee": "20000",
        #                    "minFee": "20000",
        #                    "maxFee": "10000000"
        #                }
        #            ]
        #        },
        #        ...
        #    ]
        #
        return self.parse_deposit_withdraw_fees(assets, codes, 'asset')

    def calculate_rate_limiter_cost(self, api, method, path, params, config={}):
        isAuthenticated = self.check_required_credentials(False)
        cost = self.safe_value(config, 'cost', 1)
        if cost != 1:  # trading endpoints
            if isAuthenticated:
                return cost
            else:
                return 20
        return cost

    async def fetch_liquidations(self, symbol: str, since: Int = None, limit: Int = None, params={}):
        """
        retrieves the public liquidations of a trading pair
        :see: https://www.bitmex.com/api/explorer/#not /Liquidation/Liquidation_get
        :param str symbol: unified ccxt_zagrava market symbol
        :param int [since]: the earliest time in ms to fetch liquidations for
        :param int [limit]: the maximum number of liquidation structures to retrieve
        :param dict [params]: exchange specific parameters for the bitmex api endpoint
        :param int [params.until]: timestamp in ms of the latest liquidation
        :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt_zagrava/ccxt_zagrava/wiki/Manual#pagination-params)
        :returns dict: an array of `liquidation structures <https://docs.ccxt_zagrava.com/#/?id=liquidation-structure>`
        """
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchLiquidations', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_dynamic('fetchLiquidations', symbol, since, limit, params)
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        if since is not None:
            request['startTime'] = since
        if limit is not None:
            request['count'] = limit
        request, params = self.handle_until_option('endTime', request, params)
        response = await self.publicGetLiquidation(self.extend(request, params))
        #
        #     [
        #         {
        #             "orderID": "string",
        #             "symbol": "string",
        #             "side": "string",
        #             "price": 0,
        #             "leavesQty": 0
        #         }
        #     ]
        #
        return self.parse_liquidations(response, market, since, limit)

    def parse_liquidation(self, liquidation, market: Market = None):
        #
        #     {
        #         "orderID": "string",
        #         "symbol": "string",
        #         "side": "string",
        #         "price": 0,
        #         "leavesQty": 0
        #     }
        #
        marketId = self.safe_string(liquidation, 'symbol')
        return self.safe_liquidation({
            'info': liquidation,
            'symbol': self.safe_symbol(marketId, market),
            'contracts': None,
            'contractSize': self.safe_number(market, 'contractSize'),
            'price': self.safe_number(liquidation, 'price'),
            'baseValue': None,
            'quoteValue': None,
            'timestamp': None,
            'datetime': None,
        })

    def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
        if response is None:
            return None
        if code == 429:
            raise DDoSProtection(self.id + ' ' + body)
        if code >= 400:
            error = self.safe_value(response, 'error', {})
            message = self.safe_string(error, 'message')
            feedback = self.id + ' ' + body
            self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
            self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
            if code == 400:
                raise BadRequest(feedback)
            raise ExchangeError(feedback)  # unknown message
        return None

    def nonce(self):
        return self.milliseconds()

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        query = '/api/' + self.version + '/' + path
        if method == 'GET':
            if params:
                query += '?' + self.urlencode(params)
        else:
            format = self.safe_string(params, '_format')
            if format is not None:
                query += '?' + self.urlencode({'_format': format})
                params = self.omit(params, '_format')
        url = self.urls['api'][api] + query
        isAuthenticated = self.check_required_credentials(False)
        if api == 'private' or (api == 'public' and isAuthenticated):
            self.check_required_credentials()
            auth = method + query
            expires = self.safe_integer(self.options, 'api-expires')
            headers = {
                'Content-Type': 'application/json',
                'api-key': self.apiKey,
            }
            expires = self.sum(self.seconds(), expires)
            stringExpires = str(expires)
            auth += stringExpires
            headers['api-expires'] = stringExpires
            if method == 'POST' or method == 'PUT' or method == 'DELETE':
                if params:
                    body = self.json(params)
                    auth += body
            headers['api-signature'] = self.hmac(self.encode(auth), self.encode(self.secret), hashlib.sha256)
        return {'url': url, 'method': method, 'body': body, 'headers': headers}
