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

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

from ccxt.base.exchange import Exchange
from ccxt.abstract.bitmart import ImplicitAPI
import hashlib
from ccxt.base.types import Balances, Currencies, Currency, Int, IsolatedBorrowRate, IsolatedBorrowRates, Market, Num, Order, OrderBook, OrderRequest, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, TradingFeeInterface, Transaction, TransferEntry, TransferEntries
from typing import List
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import AuthenticationError
from ccxt.base.errors import PermissionDenied
from ccxt.base.errors import AccountSuspended
from ccxt.base.errors import ArgumentsRequired
from ccxt.base.errors import BadRequest
from ccxt.base.errors import BadSymbol
from ccxt.base.errors import InsufficientFunds
from ccxt.base.errors import InvalidAddress
from ccxt.base.errors import InvalidOrder
from ccxt.base.errors import OrderNotFound
from ccxt.base.errors import NotSupported
from ccxt.base.errors import NetworkError
from ccxt.base.errors import RateLimitExceeded
from ccxt.base.errors import ExchangeNotAvailable
from ccxt.base.errors import OnMaintenance
from ccxt.base.errors import InvalidNonce
from ccxt.base.decimal_to_precision import TRUNCATE
from ccxt.base.decimal_to_precision import TICK_SIZE
from ccxt.base.precise import Precise


class bitmart(Exchange, ImplicitAPI):

    def describe(self):
        return self.deep_extend(super(bitmart, self).describe(), {
            'id': 'bitmart',
            'name': 'BitMart',
            'countries': ['US', 'CN', 'HK', 'KR'],
            # 150 per 5 seconds = 30 per second
            # rateLimit = 1000ms / 30 ~= 33.334
            'rateLimit': 33.34,
            'version': 'v2',
            'certified': True,
            'pro': True,
            'has': {
                'CORS': None,
                'spot': True,
                'margin': True,
                'swap': True,
                'future': False,
                'option': False,
                'borrowCrossMargin': False,
                'borrowIsolatedMargin': True,
                'cancelAllOrders': True,
                'cancelOrder': True,
                'cancelOrders': True,
                'createMarketBuyOrderWithCost': True,
                'createMarketOrderWithCost': False,
                'createMarketSellOrderWithCost': False,
                'createOrder': True,
                'createOrders': True,
                'createPostOnlyOrder': True,
                'createStopLimitOrder': False,
                'createStopMarketOrder': False,
                'createStopOrder': False,
                'createTrailingPercentOrder': True,
                'fetchBalance': True,
                'fetchBorrowInterest': True,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchCanceledOrders': True,
                'fetchClosedOrders': True,
                'fetchCrossBorrowRate': False,
                'fetchCrossBorrowRates': False,
                'fetchCurrencies': True,
                'fetchDeposit': True,
                'fetchDepositAddress': True,
                'fetchDepositAddresses': False,
                'fetchDepositAddressesByNetwork': False,
                'fetchDeposits': True,
                'fetchDepositWithdrawFee': True,
                'fetchDepositWithdrawFees': False,
                'fetchFundingHistory': None,
                'fetchFundingRate': True,
                'fetchFundingRateHistory': False,
                'fetchFundingRates': False,
                'fetchIsolatedBorrowRate': True,
                'fetchIsolatedBorrowRates': True,
                'fetchLiquidations': False,
                'fetchMarginMode': False,
                'fetchMarkets': True,
                'fetchMyLiquidations': True,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenInterest': True,
                'fetchOpenInterestHistory': False,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrders': False,
                'fetchOrderTrades': True,
                'fetchPosition': True,
                'fetchPositionMode': False,
                'fetchPositions': True,
                'fetchStatus': True,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': True,
                'fetchTrades': True,
                'fetchTradingFee': True,
                'fetchTradingFees': False,
                'fetchTransactionFee': True,
                'fetchTransactionFees': False,
                'fetchTransfer': False,
                'fetchTransfers': True,
                'fetchWithdrawAddressesByNetwork': False,
                'fetchWithdrawal': True,
                'fetchWithdrawals': True,
                'reduceMargin': False,
                'repayCrossMargin': False,
                'repayIsolatedMargin': True,
                'setLeverage': True,
                'setMarginMode': False,
                'transfer': True,
                'withdraw': True,
            },
            'hostname': 'bitmart.com',  # bitmart.info, bitmart.news for Hong Kong users
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/129991357-8f47464b-d0f4-41d6-8a82-34122f0d1398.jpg',
                'api': {
                    'rest': 'https://api-cloud.{hostname}',  # bitmart.info for Hong Kong users
                },
                'www': 'https://www.bitmart.com/',
                'doc': 'https://developer-pro.bitmart.com/',
                'referral': {
                    'url': 'http://www.bitmart.com/?r=rQCFLh',
                    'discount': 0.3,
                },
                'fees': 'https://www.bitmart.com/fee/en',
            },
            'requiredCredentials': {
                'apiKey': True,
                'secret': True,
                'uid': True,
            },
            'api': {
                'public': {
                    'get': {
                        'system/time': 3,  # 10 times/sec => 30/10 = 3
                        'system/service': 3,
                        # spot markets
                        'spot/v1/currencies': 7.5,
                        'spot/v1/symbols': 7.5,
                        'spot/v1/symbols/details': 5,
                        'spot/quotation/v3/tickers': 6,  # 10 times/2 sec = 5/s => 30/5 = 6
                        'spot/quotation/v3/ticker': 4,  # 15 times/2 sec = 7.5/s => 30/7.5 = 4
                        'spot/quotation/v3/lite-klines': 5,  # should be 4 but errors
                        'spot/quotation/v3/klines': 7,  # should be 6 but errors
                        'spot/quotation/v3/books': 4,  # 15 times/2 sec = 7.5/s => 30/7.5 = 4
                        'spot/quotation/v3/trades': 4,  # 15 times/2 sec = 7.5/s => 30/7.5 = 4
                        'spot/v1/ticker': 5,
                        'spot/v2/ticker': 30,
                        'spot/v1/ticker_detail': 5,  # 12 times/2 sec = 6/s => 30/6 = 5
                        'spot/v1/steps': 30,
                        'spot/v1/symbols/kline': 6,  # should be 5 but errors
                        'spot/v1/symbols/book': 5,
                        'spot/v1/symbols/trades': 5,
                        # contract markets
                        'contract/v1/tickers': 15,
                        'contract/public/details': 5,
                        'contract/public/depth': 5,
                        'contract/public/open-interest': 30,
                        'contract/public/funding-rate': 30,
                        'contract/public/kline': 6,  # should be 5 but errors
                        'account/v1/currencies': 30,
                    },
                },
                'private': {
                    'get': {
                        # sub-account
                        'account/sub-account/v1/transfer-list': 7.5,
                        'account/sub-account/v1/transfer-history': 7.5,
                        'account/sub-account/main/v1/wallet': 5,
                        'account/sub-account/main/v1/subaccount-list': 7.5,
                        'account/contract/sub-account/main/v1/wallet': 5,
                        'account/contract/sub-account/main/v1/transfer-list': 7.5,
                        'account/contract/sub-account/v1/transfer-history': 7.5,
                        # account
                        'account/v1/wallet': 5,
                        'account/v1/currencies': 30,
                        'spot/v1/wallet': 5,
                        'account/v1/deposit/address': 30,
                        'account/v1/withdraw/charge': 32,  # should be 30 but errors
                        'account/v2/deposit-withdraw/history': 7.5,
                        'account/v1/deposit-withdraw/detail': 7.5,
                        # order
                        'spot/v1/order_detail': 1,
                        'spot/v2/orders': 5,
                        'spot/v1/trades': 5,
                        # newer order endpoint
                        'spot/v2/trades': 4,
                        'spot/v3/orders': 5,
                        'spot/v2/order_detail': 1,
                        # margin
                        'spot/v1/margin/isolated/borrow_record': 1,
                        'spot/v1/margin/isolated/repay_record': 1,
                        'spot/v1/margin/isolated/pairs': 30,
                        'spot/v1/margin/isolated/account': 5,
                        'spot/v1/trade_fee': 30,
                        'spot/v1/user_fee': 30,
                        # broker
                        'spot/v1/broker/rebate': 1,
                        # contract
                        'contract/private/assets-detail': 5,
                        'contract/private/order': 1.2,
                        'contract/private/order-history': 10,
                        'contract/private/position': 10,
                        'contract/private/get-open-orders': 1.2,
                        'contract/private/current-plan-order': 1.2,
                        'contract/private/trades': 10,
                    },
                    'post': {
                        # sub-account endpoints
                        'account/sub-account/main/v1/sub-to-main': 30,
                        'account/sub-account/sub/v1/sub-to-main': 30,
                        'account/sub-account/main/v1/main-to-sub': 30,
                        'account/sub-account/sub/v1/sub-to-sub': 30,
                        'account/sub-account/main/v1/sub-to-sub': 30,
                        'account/contract/sub-account/main/v1/sub-to-main': 7.5,
                        'account/contract/sub-account/main/v1/main-to-sub': 7.5,
                        'account/contract/sub-account/sub/v1/sub-to-main': 7.5,
                        # account
                        'account/v1/withdraw/apply': 7.5,
                        # transaction and trading
                        'spot/v1/submit_order': 1,
                        'spot/v1/batch_orders': 1,
                        'spot/v2/cancel_order': 1,
                        'spot/v1/cancel_orders': 15,
                        'spot/v4/query/order': 1,  # 60 times/2 sec = 30/s => 30/30 = 1
                        'spot/v4/query/client-order': 1,  # 60 times/2 sec = 30/s => 30/30 = 1
                        'spot/v4/query/open-orders': 5,  # 12 times/2 sec = 6/s => 30/6 = 5
                        'spot/v4/query/history-orders': 5,  # 12 times/2 sec = 6/s => 30/6 = 5
                        'spot/v4/query/trades': 5,  # 12 times/2 sec = 6/s => 30/6 = 5
                        'spot/v4/query/order-trades': 5,  # 12 times/2 sec = 6/s => 30/6 = 5
                        'spot/v4/cancel_orders': 3,
                        'spot/v4/batch_orders': 3,
                        # newer endpoint
                        'spot/v3/cancel_order': 1,
                        'spot/v2/batch_orders': 1,
                        'spot/v2/submit_order': 1,
                        # margin
                        'spot/v1/margin/submit_order': 1,
                        'spot/v1/margin/isolated/borrow': 30,
                        'spot/v1/margin/isolated/repay': 30,
                        'spot/v1/margin/isolated/transfer': 30,
                        # contract
                        'account/v1/transfer-contract-list': 60,
                        'account/v1/transfer-contract': 60,
                        'contract/private/submit-order': 2.5,
                        'contract/private/cancel-order': 1.5,
                        'contract/private/cancel-orders': 30,
                        'contract/private/submit-plan-order': 2.5,
                        'contract/private/cancel-plan-order': 1.5,
                        'contract/private/submit-leverage': 2.5,
                    },
                },
            },
            'timeframes': {
                '1m': 1,
                '3m': 3,
                '5m': 5,
                '15m': 15,
                '30m': 30,
                '45m': 45,
                '1h': 60,
                '2h': 120,
                '3h': 180,
                '4h': 240,
                '1d': 1440,
                '1w': 10080,
                '1M': 43200,
            },
            'fees': {
                'trading': {
                    'tierBased': True,
                    'percentage': True,
                    'taker': self.parse_number('0.0040'),
                    'maker': self.parse_number('0.0035'),
                    'tiers': {
                        'taker': [
                            [self.parse_number('0'), self.parse_number('0.0020')],
                            [self.parse_number('10'), self.parse_number('0.18')],
                            [self.parse_number('50'), self.parse_number('0.0016')],
                            [self.parse_number('250'), self.parse_number('0.0014')],
                            [self.parse_number('1000'), self.parse_number('0.0012')],
                            [self.parse_number('5000'), self.parse_number('0.0010')],
                            [self.parse_number('25000'), self.parse_number('0.0008')],
                            [self.parse_number('50000'), self.parse_number('0.0006')],
                        ],
                        'maker': [
                            [self.parse_number('0'), self.parse_number('0.001')],
                            [self.parse_number('10'), self.parse_number('0.0009')],
                            [self.parse_number('50'), self.parse_number('0.0008')],
                            [self.parse_number('250'), self.parse_number('0.0007')],
                            [self.parse_number('1000'), self.parse_number('0.0006')],
                            [self.parse_number('5000'), self.parse_number('0.0005')],
                            [self.parse_number('25000'), self.parse_number('0.0004')],
                            [self.parse_number('50000'), self.parse_number('0.0003')],
                        ],
                    },
                },
            },
            'precisionMode': TICK_SIZE,
            'exceptions': {
                'exact': {
                    # general errors
                    '30000': ExchangeError,  # 404, Not found
                    '30001': AuthenticationError,  # 401, Header X-BM-KEY is empty
                    '30002': AuthenticationError,  # 401, Header X-BM-KEY not found
                    '30003': AccountSuspended,  # 401, Header X-BM-KEY has frozen
                    '30004': AuthenticationError,  # 401, Header X-BM-SIGN is empty
                    '30005': AuthenticationError,  # 401, Header X-BM-SIGN is wrong
                    '30006': AuthenticationError,  # 401, Header X-BM-TIMESTAMP is empty
                    '30007': AuthenticationError,  # 401, Header X-BM-TIMESTAMP range. Within a minute
                    '30008': AuthenticationError,  # 401, Header X-BM-TIMESTAMP invalid format
                    '30010': PermissionDenied,  # 403, IP is forbidden. We recommend enabling IP whitelist for API trading. After that reauth your account
                    '30011': AuthenticationError,  # 403, Header X-BM-KEY over expire time
                    '30012': AuthenticationError,  # 403, Header X-BM-KEY is forbidden to request it
                    '30013': RateLimitExceeded,  # 429, Request too many requests
                    '30014': ExchangeNotAvailable,  # 503, Service unavailable
                    '30016': OnMaintenance,  # 200, Service maintenance, the function is temporarily unavailable
                    '30017': RateLimitExceeded,  # 418, Your account request is temporarily rejected due to violation of current limiting rules
                    '30018': BadRequest,  # 503, Request Body requires JSON format
                    '30019': PermissionDenied,  # 200, You do not have the permissions to perform self operation
                    # funding account & sub account errors
                    '60000': BadRequest,  # 400, Invalid request(maybe the body is empty, or the int parameter passes string data)
                    '60001': BadRequest,  # 400, Asset account type does not exist
                    '60002': BadRequest,  # 400, currency does not exist
                    '60003': ExchangeError,  # 400, Currency has been closed recharge channel, if there is any problem, please consult customer service
                    '60004': ExchangeError,  # 400, Currency has been closed withdraw channel, if there is any problem, please consult customer service
                    '60005': ExchangeError,  # 400, Minimum amount is %s
                    '60006': ExchangeError,  # 400, Maximum withdraw precision is %d
                    '60007': InvalidAddress,  # 400, Only withdrawals from added addresses are allowed
                    '60008': InsufficientFunds,  # 400, Balance not enough
                    '60009': ExchangeError,  # 400, Beyond the limit
                    '60010': ExchangeError,  # 400, Withdraw id or deposit id not found
                    '60011': InvalidAddress,  # 400, Address is not valid
                    '60012': ExchangeError,  # 400, This action is not hasattr(self, supported) currency(If IOTA, HLX recharge and withdraw calls are prohibited)
                    '60020': PermissionDenied,  # 403, Your account is not allowed to recharge
                    '60021': PermissionDenied,  # 403, Your account is not allowed to withdraw
                    '60022': PermissionDenied,  # 403, No withdrawals for 24 hours
                    '60026': PermissionDenied,  # 403, Sub-account does not have permission to operate
                    '60027': PermissionDenied,  # 403, Only supports sub-account calls
                    '60028': AccountSuspended,  # 403, Account is disabled for security reasons, please contact customer service
                    '60029': AccountSuspended,  # 403, The account is frozen by the master account, please contact the master account to unfreeze the account
                    '60030': BadRequest,  # 405, Method Not Allowed
                    '60031': BadRequest,  # 415, Unsupported Media Type
                    '60050': ExchangeError,  # 500, User account not found
                    '60051': ExchangeError,  # 500, Internal Server Error
                    '61001': InsufficientFunds,  # {"message":"Balance not enough","code":61001,"trace":"b85ea1f8-b9af-4001-ac5f-9e061fe93d78","data":{}}
                    '61003': BadRequest,  # 400, {"message":"sub-account not found","code":61003,"trace":"b35ec2fd-0bc9-4ef2-a3c0-6f78d4f335a4","data":{}}
                    '61004': BadRequest,  # 400, Duplicate requests(such an existing requestNo)
                    '61005': BadRequest,  # 403, Asset transfer between accounts is not available
                    '61006': NotSupported,  # 403, The sub-account api only supports organization accounts
                    '61007': ExchangeError,  # 403, Please complete your institution verification to enable withdrawal function.
                    '61008': ExchangeError,  # 403, Suspend transfer out
                    # spot public errors
                    '70000': ExchangeError,  # 200, no data
                    '70001': BadRequest,  # 200, request param can not be null
                    '70002': BadSymbol,  # 200, symbol is invalid
                    '70003': NetworkError,  # {"code":70003,"trace":"81a9d57b63be4819b65d3065e6a4682b.105.17105295323593915","message":"net error, please try later","data":null}
                    '71001': BadRequest,  # 200, after is invalid
                    '71002': BadRequest,  # 200, before is invalid
                    '71003': BadRequest,  # 200, request after or before is invalid
                    '71004': BadRequest,  # 200, request kline count limit
                    '71005': BadRequest,  # 200, request step error
                    # spot & margin errors
                    '50000': BadRequest,  # 400, Bad Request
                    '50001': BadSymbol,  # 400, Symbol not found
                    '50002': BadRequest,  # 400, From Or To format error
                    '50003': BadRequest,  # 400, Step format error
                    '50004': BadRequest,  # 400, Kline size over 500
                    '50005': OrderNotFound,  # 400, Order Id not found
                    '50006': InvalidOrder,  # 400, Minimum size is %s
                    '50007': InvalidOrder,  # 400, Maximum size is %s
                    '50008': InvalidOrder,  # 400, Minimum price is %s
                    '50009': InvalidOrder,  # 400, Minimum count*price is %s
                    '50010': InvalidOrder,  # 400, RequestParam size is required
                    '50011': InvalidOrder,  # 400, RequestParam price is required
                    '50012': InvalidOrder,  # 400, RequestParam notional is required
                    '50013': InvalidOrder,  # 400, Maximum limit*offset is %d
                    '50014': BadRequest,  # 400, RequestParam limit is required
                    '50015': BadRequest,  # 400, Minimum limit is 1
                    '50016': BadRequest,  # 400, Maximum limit is %d
                    '50017': BadRequest,  # 400, RequestParam offset is required
                    '50018': BadRequest,  # 400, Minimum offset is 1
                    '50019': ExchangeError,  # 400, Invalid status. validate status is [1=Failed, 2=Success, 3=Frozen Failed, 4=Frozen Success, 5=Partially Filled, 6=Fully Fulled, 7=Canceling, 8=Canceled]                    '50020': InsufficientFunds,  # 400, Balance not enough
                    '50020': InsufficientFunds,  # 400, Balance not enough
                    '50021': BadRequest,  # 400, Invalid %s
                    '50022': ExchangeNotAvailable,  # 400, Service unavailable
                    '50023': BadSymbol,  # 400, This Symbol can't place order by api
                    '50024': BadRequest,  # 400, Order book size over 200
                    '50025': BadRequest,  # 400, Maximum price is %s
                    '50026': BadRequest,  # 400, The buy order price cannot be higher than the open price
                    '50027': BadRequest,  # 400, The sell order price cannot be lower than the open price
                    '50028': BadRequest,  # 400, Missing parameters
                    '50029': InvalidOrder,  # 400, {"message":"param not match : size * price >=1000","code":50029,"trace":"f931f030-b692-401b-a0c5-65edbeadc598","data":{}}
                    '50030': OrderNotFound,  # 400, {"message":"Order is already canceled","code":50030,"trace":"8d6f64ee-ad26-45a4-9efd-1080f9fca1fa","data":{}}
                    '50031': OrderNotFound,  # 400, Order is already completed
                    '50032': OrderNotFound,  # 400, {"message":"Order does not exist","code":50032,"trace":"8d6b482d-4bf2-4e6c-aab2-9dcd22bf2481","data":{}}
                    '50033': InvalidOrder,  # 400, The order quantity should be greater than 0 and less than or equal to 10
                    # below Error codes used interchangeably for both failed postOnly and IOC orders depending on market price and order side
                    '50034': InvalidOrder,  # 400, {"message":"The price is high and there is no matching depth","code":50034,"trace":"ebfae59a-ba69-4735-86b2-0ed7b9ca14ea","data":{}}
                    '50035': InvalidOrder,  # 400, {"message":"The price is low and there is no matching depth","code":50035,"trace":"677f01c7-8b88-4346-b097-b4226c75c90e","data":{}}
                    '50036': ExchangeError,  # 400, Cancel failed, order is not revocable status
                    '50037': BadRequest,  # 400, The maximum length of clientOrderId cannot exceed 32
                    '50038': BadRequest,  # 400, ClientOrderId only allows a combination of numbers and letters
                    '50039': BadRequest,  # 400, Order_id and clientOrderId cannot be empty at the same time
                    '50040': BadSymbol,  # 400, Symbol Not Available
                    '50041': ExchangeError,  # 400, Out of query time range
                    '50042': BadRequest,  # 400, clientOrderId is duplicate
                    '51000': BadSymbol,  # 400, Currency not found
                    '51001': ExchangeError,  # 400, Margin Account not Opened
                    '51002': ExchangeError,  # 400, Margin Account Not Available
                    '51003': ExchangeError,  # 400, Account Limit
                    '51004': InsufficientFunds,  # 400, {"message":"Exceed the maximum number of borrows available.","code":51004,"trace":"4030b753-9beb-44e6-8352-1633c5edcd47","data":{}}
                    '51005': InvalidOrder,  # 400, Less than the minimum borrowable amount
                    '51006': InvalidOrder,  # 400, Exceeds the amount to be repaid
                    '51007': BadRequest,  # 400, order_mode not found
                    '51008': ExchangeError,  # 400, Operation is limited, please try again later
                    '51009': InvalidOrder,  # 400, Parameter mismatch: limit order/market order quantity should be greater than the minimum number of should buy/sell
                    '51010': InvalidOrder,  # 400, Parameter mismatch: limit order price should be greater than the minimum buy price
                    '51011': InvalidOrder,  # 400, {"message":"param not match : size * price >=5","code":51011,"trace":"525e1d27bfd34d60b2d90ba13a7c0aa9.74.16696421352220797","data":{}}
                    '51012': InvalidOrder,  # 400, Parameter mismatch: limit order price should be greater than the minimum buy price
                    '51013': InvalidOrder,  # 400, Parameter mismatch: Limit order quantity * price should be greater than the minimum transaction amount
                    '51014': InvalidOrder,  # 400, Participation mismatch: the number of market order buy orders should be greater than the minimum buyable amount
                    '51015': InvalidOrder,  # 400, Parameter mismatch: the price of market order buy order placed is too small
                    '52000': BadRequest,  # 400, Unsupported OrderMode Type
                    '52001': BadRequest,  # 400, Unsupported Trade Type
                    '52002': BadRequest,  # 400, Unsupported Side Type
                    '52003': BadRequest,  # 400, Unsupported Query State Type
                    '52004': BadRequest,  # 400, End time must be greater than or equal to Start time
                    '53000': AccountSuspended,  # 403, Your account is frozen due to security policies. Please contact customer service
                    '53001': AccountSuspended,  # 403, {"message":"Your kyc country is restricted. Please contact customer service.","code":53001,"trace":"8b445940-c123-4de9-86d7-73c5be2e7a24","data":{}}
                    '53002': PermissionDenied,  # 403, Your account has not yet completed the kyc advanced certification, please complete first
                    '53003': PermissionDenied,  # 403 No permission, please contact the main account
                    '53005': PermissionDenied,  # 403 Don't have permission to access the interface
                    '53006': PermissionDenied,  # 403 Please complete your personal verification(Starter)
                    '53007': PermissionDenied,  # 403 Please complete your personal verification(Advanced)
                    '53008': PermissionDenied,  # 403 Services is not available in your countries and areas
                    '53009': PermissionDenied,  # 403 Your account has not yet completed the qr code certification, please complete first
                    '53010': PermissionDenied,  # 403 This account is restricted from borrowing
                    '57001': BadRequest,  # 405, Method Not Allowed
                    '58001': BadRequest,  # 415, Unsupported Media Type
                    '59001': ExchangeError,  # 500, User account not found
                    '59002': ExchangeError,  # 500, Internal Server Error
                    '59003': ExchangeError,  # 500, Spot wallet call fail
                    '59004': ExchangeError,  # 500, Margin wallet service call exception
                    '59005': PermissionDenied,  # 500, Margin wallet service restricted
                    '59006': ExchangeError,  # 500, Transfer fail
                    '59007': ExchangeError,  # 500, Get symbol risk data fail
                    '59008': ExchangeError,  # 500, Trading order failure
                    '59009': ExchangeError,  # 500, Loan success,but trading order failure
                    '59010': InsufficientFunds,  # 500, Insufficient loan amount.
                    '59011': ExchangeError,  # 500, The Get Wallet Balance service call fail, please try again later
                    # contract errors
                    '40001': ExchangeError,  # 400, Cloud account not found
                    '40002': ExchangeError,  # 400, out_trade_no not found
                    '40003': ExchangeError,  # 400, out_trade_no already existed
                    '40004': ExchangeError,  # 400, Cloud account count limit
                    '40005': ExchangeError,  # 400, Transfer vol precision error
                    '40006': PermissionDenied,  # 400, Invalid ip error
                    '40007': BadRequest,  # 400, Parse parameter error
                    '40008': InvalidNonce,  # 400, Check nonce error
                    '40009': BadRequest,  # 400, Check ver error
                    '40010': BadRequest,  # 400, Not found func error
                    '40011': BadRequest,  # 400, Invalid request
                    '40012': ExchangeError,  # 500, System error
                    '40013': ExchangeError,  # 400, Access too often" CLIENT_TIME_INVALID, "Please check your system time.
                    '40014': BadSymbol,  # 400, This contract is offline
                    '40015': BadSymbol,  # 400, This contract's exchange has been paused
                    '40016': InvalidOrder,  # 400, This order would trigger user position liquidate
                    '40017': InvalidOrder,  # 400, It is not possible to open and close simultaneously in the same position
                    '40018': InvalidOrder,  # 400, Your position is closed
                    '40019': ExchangeError,  # 400, Your position is in liquidation delegating
                    '40020': InvalidOrder,  # 400, Your position volume is not enough
                    '40021': ExchangeError,  # 400, The position is not exsit
                    '40022': ExchangeError,  # 400, The position is not isolated
                    '40023': ExchangeError,  # 400, The position would liquidate when sub margin
                    '40024': ExchangeError,  # 400, The position would be warnning of liquidation when sub margin
                    '40025': ExchangeError,  # 400, The position’s margin shouldn’t be lower than the base limit
                    '40026': ExchangeError,  # 400, You cross margin position is in liquidation delegating
                    '40027': InsufficientFunds,  # 400, You contract account available balance not enough
                    '40028': PermissionDenied,  # 400, Your plan order's count is more than system maximum limit.
                    '40029': InvalidOrder,  # 400, The order's leverage is too large.
                    '40030': InvalidOrder,  # 400, The order's leverage is too small.
                    '40031': InvalidOrder,  # 400, The deviation between current price and trigger price is too large.
                    '40032': InvalidOrder,  # 400, The plan order's life cycle is too long.
                    '40033': InvalidOrder,  # 400, The plan order's life cycle is too short.
                    '40034': BadSymbol,  # 400, This contract is not found
                    '40035': OrderNotFound,  # 400, The order is not exist
                    '40036': InvalidOrder,  # 400, The order status is invalid
                    '40037': OrderNotFound,  # 400, The order id is not exist
                    '40038': BadRequest,  # 400, The k-line step is invalid
                    '40039': BadRequest,  # 400, The timestamp is invalid
                    '40040': InvalidOrder,  # 400, The order leverage is invalid
                    '40041': InvalidOrder,  # 400, The order side is invalid
                    '40042': InvalidOrder,  # 400, The order type is invalid
                    '40043': InvalidOrder,  # 400, The order precision is invalid
                    '40044': InvalidOrder,  # 400, The order range is invalid
                    '40045': InvalidOrder,  # 400, The order open type is invalid
                    '40046': PermissionDenied,  # 403, The account is not opened futures
                    '40047': PermissionDenied,  # 403, Services is not available in you countries and areas
                    '40048': BadRequest,  # 403, ClientOrderId only allows a combination of numbers and letters
                    '40049': BadRequest,  # 403, The maximum length of clientOrderId cannot exceed 32
                    '40050': InvalidOrder,  # 403, Client OrderId duplicated with existing orders
                },
                'broad': {},
            },
            'commonCurrencies': {
                '$GM': 'GOLDMINER',
                '$HERO': 'Step Hero',
                '$PAC': 'PAC',
                'BP': 'BEYOND',
                'GDT': 'Gorilla Diamond',
                'GLD': 'Goldario',
                'MVP': 'MVP Coin',
                'TRU': 'Truebit',  # conflict with TrueFi
            },
            'options': {
                'defaultNetwork': 'ERC20',
                'defaultNetworks': {
                    'USDT': 'ERC20',
                },
                'networks': {
                    'ERC20': 'ERC20',
                    'SOL': 'SOL',
                    'BTC': 'BTC',
                    'TRC20': 'TRC20',
                    # todo: should be TRX after unification
                    # 'TRC20': ['TRC20', 'trc20', 'TRON'],  # todo: after unification i.e. TRON is returned from fetchDepositAddress
                    # 'ERC20': ['ERC20', 'ERC-20', 'ERC20 '],  # todo: after unification
                    'OMNI': 'OMNI',
                    'XLM': 'XLM',
                    'EOS': 'EOS',
                    'NEO': 'NEO',
                    'BTM': 'BTM',
                    'BCH': 'BCH',
                    'LTC': 'LTC',
                    'BSV': 'BSV',
                    'XRP': 'XRP',
                    # 'VECHAIN': ['VET', 'Vechain'],  # todo: after unification
                    'PLEX': 'PLEX',
                    'XCH': 'XCH',
                    # 'AVALANCHE_C': ['AVAX', 'AVAX-C'],  # todo: after unification
                    'NEAR': 'NEAR',
                    'FIO': 'FIO',
                    'SCRT': 'SCRT',
                    'IOTX': 'IOTX',
                    'ALGO': 'ALGO',
                    'ATOM': 'ATOM',
                    'DOT': 'DOT',
                    'ADA': 'ADA',
                    'DOGE': 'DOGE',
                    'XYM': 'XYM',
                    'GLMR': 'GLMR',
                    'MOVR': 'MOVR',
                    'ZIL': 'ZIL',
                    'INJ': 'INJ',
                    'KSM': 'KSM',
                    'ZEC': 'ZEC',
                    'NAS': 'NAS',
                    # 'POLYGON': ['MATIC', 'Polygon', 'POLYGON'],  # todo: after unification
                    'HRC20': 'HECO',
                    'XDC': 'XDC',
                    'ONE': 'ONE',
                    'LAT': 'LAT',
                    'CSPR': 'Casper',
                    'ICP': 'Computer',
                    'XTZ': 'XTZ',
                    'MINA': 'MINA',
                    # 'BEP20': ['BEP20', 'BSC_BNB', 'bep20'],  # todo: after unification
                    'THETA': 'THETA',
                    'AKT': 'AKT',
                    'AR': 'AR',
                    'CELO': 'CELO',
                    'FIL': 'FIL',
                    'NULS': 'NULS',
                    'ETC': 'ETC',
                    'DASH': 'DASH',
                    'DGB': 'DGB',
                    'BEP2': 'BEP2',
                    'GRIN': 'GRIN',
                    'WAVES': 'WAVES',
                    'ABBC': 'ABBC',
                    'ACA': 'ACA',
                    'QTUM': 'QTUM',
                    'PAC': 'PAC',
                    # 'TERRACLASSIC': 'LUNC',  # TBD
                    # 'TERRA': 'Terra',  # TBD
                    # 'HEDERA': ['HBAR', 'Hedera', 'Hedera Mainnet'],  # todo: after unification
                    'TLOS': 'TLOS',
                    'KARDIA': 'KardiaChain',
                    'FUSE': 'FUSE',
                    'TRC10': 'TRC10',
                    'FIRO': 'FIRO',
                    'FTM': 'Fantom',
                    # 'KLAYTN': ['klaytn', 'KLAY', 'Klaytn'],  # todo: after unification
                    # 'ELROND': ['EGLD', 'Elrond eGold', 'MultiversX'],  # todo: after unification
                    'EVER': 'EVER',
                    'KAVA': 'KAVA',
                    'HYDRA': 'HYDRA',
                    'PLCU': 'PLCU',
                    'BRISE': 'BRISE',
                    # 'CRC20': ['CRO', 'CRO_Chain'],  # todo: after unification
                    # 'CONFLUX': ['CFX eSpace', 'CFX'],  # todo: after unification
                    'OPTIMISM': 'OPTIMISM',
                    'REEF': 'REEF',
                    'SYS': 'SYS',  # NEVM is different
                    'VITE': 'VITE',
                    'STX': 'STX',
                    'SXP': 'SXP',
                    'BITCI': 'BITCI',
                    # 'ARBITRUM': ['ARBI', 'Arbitrum'],  # todo: after unification
                    'XRD': 'XRD',
                    'ASTR': 'ASTAR',
                    'ZEN': 'HORIZEN',
                    'LTO': 'LTO',
                    'ETHW': 'ETHW',
                    'ETHF': 'ETHF',
                    'IOST': 'IOST',
                    # 'CHILIZ': ['CHZ', 'CHILIZ'],  # todo: after unification
                    'APT': 'APT',
                    # 'FLOW': ['FLOW', 'Flow'],  # todo: after unification
                    'ONT': 'ONT',
                    'EVMOS': 'EVMOS',
                    'XMR': 'XMR',
                    'OASYS': 'OAS',
                    'OSMO': 'OSMO',
                    'OMAX': 'OMAX Chain',
                    'DESO': 'DESO',
                    'BFIC': 'BFIC',
                    'OHO': 'OHO',
                    'CS': 'CS',
                    'CHEQ': 'CHEQ',
                    'NODL': 'NODL',
                    'NEM': 'XEM',
                    'FRA': 'FRA',
                    'ERGO': 'ERG',
                    # todo: below will be uncommented after unification
                    # 'BITCOINHD': 'BHD',
                    # 'CRUST': 'CRU',
                    # 'MINTME': 'MINTME',
                    # 'ZENITH': 'ZENITH',
                    # 'ZENIQ': 'ZENIQ',  # "ZEN-20" is different
                    # 'BITCOINVAULT': 'BTCV',
                    # 'MOBILECOIN': 'MBX',
                    # 'PINETWORK': 'PI',
                    # 'PI': 'PI',
                    # 'REBUS': 'REBUS',
                    # 'XODEX': 'XODEX',
                    # 'ULTRONGLOW': 'UTG'
                    # 'QIBLOCKCHAIN': 'QIE',
                    # 'XIDEN': 'XDEN',
                    # 'PHAETON': 'PHAE',
                    # 'REDLIGHT': 'REDLC',
                    # 'VERITISE': 'VTS',
                    # 'VERIBLOCK': 'VBK',
                    # 'RAMESTTA': 'RAMA',
                    # 'BITICA': 'BDCC',
                    # 'CROWNSOVEREIGN': 'CSOV',
                    # 'DRAC': 'DRC20',
                    # 'QCHAIN': 'QDT',
                    # 'KINGARU': 'KRU',
                    # 'PROOFOFMEMES': 'POM',
                    # 'CUBE': 'CUBE',
                    # 'CADUCEUS': 'CMP',
                    # 'VEIL': 'VEIL',
                    # 'ENERGYWEB': 'EWT',
                    # 'CYPHERIUM': 'CPH',
                    # 'LBRY': 'LBC',
                    # 'ETHERCOIN': 'ETE',
                    # undetermined chains:
                    # LEX(for LexThum), TAYCAN(for TRICE), SFL(probably TAYCAN), OMNIA(for APEX), NAC(for NAC), KAG(Kinesis), CEM(crypto emergency), XVM(for Venidium), NEVM(for NEVM), IGT20(for IGNITE), FILM(FILMCredits), CC(CloudCoin), MERGE(MERGE), LTNM(Bitcoin latinum), PLUGCN( PlugChain), DINGO(dingo), LED(LEDGIS), AVAT(AVAT), VSOL(Vsolidus), EPIC(EPIC cash), NFC(netflowcoin), mrx(Metrix Coin), Idena(idena network), PKT(PKT Cash), BondDex(BondDex), XBN(XBN), KALAM(Kalamint), REV(RChain), KRC20(MyDeFiPet), ARC20(Hurricane Token), GMD(Coop network), BERS(Berith), ZEBI(Zebi), BRC(Baer Chain), DAPS(DAPS Coin), APL(Gold Secured Currency), NDAU(NDAU), WICC(WICC), UPG(Unipay God), TSL(TreasureSL), MXW(Maxonrow), CLC(Cifculation), SMH(SMH Coin), XIN(CPCoin), RDD(ReddCoin), OK(Okcash), KAR(KAR), CCX(ConcealNetwork),
                },
                'defaultType': 'spot',  # 'spot', 'swap'
                'fetchBalance': {
                    'type': 'spot',  # 'spot', 'swap', 'account'
                },
                'accountsByType': {
                    'spot': 'spot',
                    'swap': 'swap',
                },
                'createMarketBuyOrderRequiresPrice': True,
                'brokerId': 'CCXTxBitmart000',
            },
        })

    def fetch_time(self, params={}):
        """
        fetches the current integer timestamp in milliseconds from the exchange server
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns int: the current integer timestamp in milliseconds from the exchange server
        """
        response = self.publicGetSystemTime(params)
        #
        #     {
        #         "message":"OK",
        #         "code":1000,
        #         "trace":"c4e5e5b7-fe9f-4191-89f7-53f6c5bf9030",
        #         "data":{
        #             "server_time":1599843709578
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        return self.safe_integer(data, 'server_time')

    def fetch_status(self, params={}):
        """
        the latest known information on the availability of the exchange API
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `status structure <https://docs.ccxt.com/#/?id=exchange-status-structure>`
        """
        options = self.safe_value(self.options, 'fetchStatus', {})
        defaultType = self.safe_string(self.options, 'defaultType')
        type = self.safe_string(options, 'type', defaultType)
        type = self.safe_string(params, 'type', type)
        params = self.omit(params, 'type')
        response = self.publicGetSystemService(params)
        #
        #     {
        #         "message": "OK",
        #         "code": 1000,
        #         "trace": "1d3f28b0-763e-4f78-90c4-5e3ad19dc595",
        #         "data": {
        #           "service": [
        #             {
        #               "title": "Spot API Stop",
        #               "service_type": "spot",
        #               "status": 2,
        #               "start_time": 1648639069125,
        #               "end_time": 1648639069125
        #             },
        #             {
        #               "title": "Contract API Stop",
        #               "service_type": "contract",
        #               "status": 2,
        #               "start_time": 1648639069125,
        #               "end_time": 1648639069125
        #             }
        #           ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        services = self.safe_value(data, 'service', [])
        servicesByType = self.index_by(services, 'service_type')
        if type == 'swap':
            type = 'contract'
        service = self.safe_value(servicesByType, type)
        status = None
        eta = None
        if service is not None:
            statusCode = self.safe_integer(service, 'status')
            if statusCode == 2:
                status = 'ok'
            else:
                status = 'maintenance'
                eta = self.safe_integer(service, 'end_time')
        return {
            'status': status,
            'updated': None,
            'eta': eta,
            'url': None,
            'info': response,
        }

    def fetch_spot_markets(self, params={}):
        response = self.publicGetSpotV1SymbolsDetails(params)
        #
        #     {
        #         "message":"OK",
        #         "code":1000,
        #         "trace":"a67c9146-086d-4d3f-9897-5636a9bb26e1",
        #         "data":{
        #             "symbols":[
        #               {
        #                  "symbol": "BTC_USDT",
        #                  "symbol_id": 53,
        #                  "base_currency": "BTC",
        #                  "quote_currency": "USDT",
        #                  "base_min_size": "0.000010000000000000000000000000",
        #                  "base_max_size": "100000000.000000000000000000000000000000",
        #                  "price_min_precision": -1,
        #                  "price_max_precision": 2,
        #                  "quote_increment": "0.00001",  # Api docs says "The minimum order quantity is also the minimum order quantity increment", however I think they mistakenly use the term 'order quantity'
        #                  "expiration": "NA",
        #                  "min_buy_amount": "5.000000000000000000000000000000",
        #                  "min_sell_amount": "5.000000000000000000000000000000",
        #                  "trade_status": "trading"
        #               },
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        symbols = self.safe_value(data, 'symbols', [])
        result = []
        for i in range(0, len(symbols)):
            market = symbols[i]
            id = self.safe_string(market, 'symbol')
            numericId = self.safe_integer(market, 'symbol_id')
            baseId = self.safe_string(market, 'base_currency')
            quoteId = self.safe_string(market, 'quote_currency')
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            symbol = base + '/' + quote
            minBuyCost = self.safe_string(market, 'min_buy_amount')
            minSellCost = self.safe_string(market, 'min_sell_amount')
            minCost = Precise.string_max(minBuyCost, minSellCost)
            baseMinSize = self.safe_number(market, 'base_min_size')
            result.append({
                'id': id,
                'numericId': numericId,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'settle': None,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': None,
                'type': 'spot',
                'spot': True,
                'margin': False,
                'swap': False,
                'future': False,
                'option': False,
                'active': True,
                'contract': False,
                'linear': None,
                'inverse': None,
                'contractSize': None,
                'expiry': None,
                'expiryDatetime': None,
                'strike': None,
                'optionType': None,
                'precision': {
                    'amount': baseMinSize,
                    'price': self.parse_number(self.parse_precision(self.safe_string(market, 'price_max_precision'))),
                },
                'limits': {
                    'leverage': {
                        'min': None,
                        'max': None,
                    },
                    'amount': {
                        'min': baseMinSize,
                        'max': self.safe_number(market, 'base_max_size'),
                    },
                    'price': {
                        'min': None,
                        'max': None,
                    },
                    'cost': {
                        'min': self.parse_number(minCost),
                        'max': None,
                    },
                },
                'created': None,
                'info': market,
            })
        return result

    def fetch_contract_markets(self, params={}):
        response = self.publicGetContractPublicDetails(params)
        #
        #     {
        #       "code": 1000,
        #       "message": "Ok",
        #       "trace": "9b92a999-9463-4c96-91a4-93ad1cad0d72",
        #       "data": {
        #       "symbols": [{
        #             "symbol": "BTCUSDT",
        #             "product_type": 1,
        #             "open_timestamp": 1594080000,
        #             "expire_timestamp": 0,
        #             "settle_timestamp": 0,
        #             "base_currency": "BTC",
        #             "quote_currency": "USDT",
        #             "last_price": "23920",
        #             "volume_24h": "18969368",
        #             "turnover_24h": "458933659.7858",
        #             "index_price": "23945.25191635",
        #             "index_name": "BTCUSDT",
        #             "contract_size": "0.001",
        #             "min_leverage": "1",
        #             "max_leverage": "100",
        #             "price_precision": "0.1",
        #             "vol_precision": "1",
        #             "max_volume": "500000",
        #             "min_volume": "1"
        #           },
        #           ...
        #         ]
        #       }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        symbols = self.safe_value(data, 'symbols', [])
        result = []
        for i in range(0, len(symbols)):
            market = symbols[i]
            id = self.safe_string(market, 'symbol')
            baseId = self.safe_string(market, 'base_currency')
            quoteId = self.safe_string(market, 'quote_currency')
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            settleId = 'USDT'  # self is bitmart's ID for usdt
            settle = self.safe_currency_code(settleId)
            symbol = base + '/' + quote + ':' + settle
            productType = self.safe_integer(market, 'product_type')
            isSwap = (productType == 1)
            isFutures = (productType == 2)
            expiry = self.safe_integer(market, 'expire_timestamp')
            if not isFutures and (expiry == 0):
                expiry = None
            result.append({
                'id': id,
                'numericId': None,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'settle': settle,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': settleId,
                'type': 'swap' if isSwap else 'future',
                'spot': False,
                'margin': False,
                'swap': isSwap,
                'future': isFutures,
                'option': False,
                'active': True,
                'contract': True,
                'linear': True,
                'inverse': False,
                'contractSize': self.safe_number(market, 'contract_size'),
                'expiry': expiry,
                'expiryDatetime': self.iso8601(expiry),
                'strike': None,
                'optionType': None,
                'precision': {
                    'amount': self.safe_number(market, 'vol_precision'),
                    'price': self.safe_number(market, 'price_precision'),
                },
                'limits': {
                    'leverage': {
                        'min': self.safe_number(market, 'min_leverage'),
                        'max': self.safe_number(market, 'max_leverage'),
                    },
                    'amount': {
                        'min': self.safe_number(market, 'min_volume'),
                        'max': self.safe_number(market, 'max_volume'),
                    },
                    'price': {
                        'min': None,
                        'max': None,
                    },
                    'cost': {
                        'min': None,
                        'max': None,
                    },
                },
                'created': self.safe_integer(market, 'open_timestamp'),
                'info': market,
            })
        return result

    def fetch_markets(self, params={}) -> List[Market]:
        """
        retrieves data on all markets for bitmart
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: an array of objects representing market data
        """
        spot = self.fetch_spot_markets(params)
        contract = self.fetch_contract_markets(params)
        return self.array_concat(spot, contract)

    def fetch_currencies(self, params={}) -> Currencies:
        """
        fetches all available currencies on an exchange
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an associative dictionary of currencies
        """
        response = self.publicGetSpotV1Currencies(params)
        #
        #     {
        #         "message":"OK",
        #         "code":1000,
        #         "trace":"8c768b3c-025f-413f-bec5-6d6411d46883",
        #         "data":{
        #             "currencies":[
        #                 {"currency":"MATIC","name":"Matic Network","withdraw_enabled":true,"deposit_enabled":true},
        #                 {"currency":"KTN","name":"Kasoutuuka News","withdraw_enabled":true,"deposit_enabled":false},
        #                 {"currency":"BRT","name":"Berith","withdraw_enabled":true,"deposit_enabled":true},
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        currencies = self.safe_value(data, 'currencies', [])
        result: dict = {}
        for i in range(0, len(currencies)):
            currency = currencies[i]
            id = self.safe_string(currency, 'id')
            code = self.safe_currency_code(id)
            name = self.safe_string(currency, 'name')
            withdrawEnabled = self.safe_value(currency, 'withdraw_enabled')
            depositEnabled = self.safe_value(currency, 'deposit_enabled')
            active = withdrawEnabled and depositEnabled
            result[code] = {
                'id': id,
                'code': code,
                'name': name,
                'info': currency,  # the original payload
                'active': active,
                'deposit': depositEnabled,
                'withdraw': withdrawEnabled,
                'fee': None,
                'precision': None,
                'limits': {
                    'amount': {'min': None, 'max': None},
                    'withdraw': {'min': None, 'max': None},
                },
            }
        return result

    def fetch_transaction_fee(self, code: str, params={}):
        """
         * @deprecated
        please use fetchDepositWithdrawFee instead
        :param str code: unified currency code
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `fee structure <https://docs.ccxt.com/#/?id=fee-structure>`
        """
        self.load_markets()
        currency = self.currency(code)
        request: dict = {
            'currency': currency['id'],
        }
        response = self.privateGetAccountV1WithdrawCharge(self.extend(request, params))
        #
        #     {
        #         "message": "OK",
        #         "code": "1000",
        #         "trace": "3ecc0adf-91bd-4de7-aca1-886c1122f54f",
        #         "data": {
        #             "today_available_withdraw_BTC": "100.0000",
        #             "min_withdraw": "0.005",
        #             "withdraw_precision": "8",
        #             "withdraw_fee": "0.000500000000000000000000000000"
        #         }
        #     }
        #
        data = response['data']
        withdrawFees: dict = {}
        withdrawFees[code] = self.safe_number(data, 'withdraw_fee')
        return {
            'info': response,
            'withdraw': withdrawFees,
            'deposit': {},
        }

    def parse_deposit_withdraw_fee(self, fee, currency: Currency = None):
        #
        #    {
        #        "today_available_withdraw_BTC": "100.0000",
        #        "min_withdraw": "0.005",
        #        "withdraw_precision": "8",
        #        "withdraw_fee": "0.000500000000000000000000000000"
        #    }
        #
        return {
            'info': fee,
            'withdraw': {
                'fee': self.safe_number(fee, 'withdraw_fee'),
                'percentage': None,
            },
            'deposit': {
                'fee': None,
                'percentage': None,
            },
            'networks': {},
        }

    def fetch_deposit_withdraw_fee(self, code: str, params={}):
        """
        fetch the fee for deposits and withdrawals
        :param str code: unified currency code
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `fee structure <https://docs.ccxt.com/#/?id=fee-structure>`
        """
        self.load_markets()
        currency = self.currency(code)
        request: dict = {
            'currency': currency['id'],
        }
        response = self.privateGetAccountV1WithdrawCharge(self.extend(request, params))
        #
        #     {
        #         "message": "OK",
        #         "code": "1000",
        #         "trace": "3ecc0adf-91bd-4de7-aca1-886c1122f54f",
        #         "data": {
        #             "today_available_withdraw_BTC": "100.0000",
        #             "min_withdraw": "0.005",
        #             "withdraw_precision": "8",
        #             "withdraw_fee": "0.000500000000000000000000000000"
        #         }
        #     }
        #
        data = response['data']
        return self.parse_deposit_withdraw_fee(data)

    def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
        #
        # spot(REST) fetchTickers
        #
        #     {
        #         'result': [
        #             "AFIN_USDT",     # symbol
        #             "0.001047",      # last
        #             "11110",         # v_24h
        #             "11.632170",     # qv_24h
        #             "0.001048",      # open_24h
        #             "0.001048",      # high_24h
        #             "0.001047",      # low_24h
        #             "-0.00095",      # price_change_24h
        #             "0.001029",      # bid_px
        #             "5555",          # bid_sz
        #             "0.001041",      # ask_px
        #             "5297",          # ask_sz
        #             "1717122550482"  # timestamp
        #         ]
        #     }
        #
        # spot(REST) fetchTicker
        #
        #     {
        #         "symbol": "BTC_USDT",
        #         "last": "68500.00",
        #         "v_24h": "10491.65490",
        #         "qv_24h": "717178990.42",
        #         "open_24h": "68149.75",
        #         "high_24h": "69499.99",
        #         "low_24h": "67132.40",
        #         "fluctuation": "0.00514",
        #         "bid_px": "68500",
        #         "bid_sz": "0.00162",
        #         "ask_px": "68500.01",
        #         "ask_sz": "0.01722",
        #         "ts": "1717131391671"
        #     }
        #
        # spot(WS)
        #
        #      {
        #          "symbol":"BTC_USDT",
        #          "last_price":"146.24",
        #          "open_24h":"147.17",
        #          "high_24h":"147.48",
        #          "low_24h":"143.88",
        #          "base_volume_24h":"117387.58",  # NOT base, but quote currencynot !!
        #          "s_t": 1610936002
        #      }
        #
        # swap
        #
        #      {
        #          "contract_symbol":"DOGEUSDT",
        #          "last_price":"0.130340",
        #          "index_price":"0.13048245",
        #          "last_funding_rate":"0.00002287",
        #          "price_change_percent_24h":"-2.074",
        #          "volume_24h":"113705028.59482228",
        #          "url":"https://futures.bitmart.com/en?symbol=DOGEUSDT",
        #          "high_price":"0.134520",
        #          "low_price":"0.128570",
        #          "legal_coin_price":"0.1302699"
        #      }
        #
        result = self.safe_list(ticker, 'result', [])
        average = self.safe_string_2(ticker, 'avg_price', 'index_price')
        marketId = self.safe_string_2(ticker, 'symbol', 'contract_symbol')
        timestamp = self.safe_integer_2(ticker, 'timestamp', 'ts')
        last = self.safe_string_2(ticker, 'last_price', 'last')
        percentage = self.safe_string(ticker, 'price_change_percent_24h')
        change = self.safe_string(ticker, 'fluctuation')
        high = self.safe_string_2(ticker, 'high_24h', 'high_price')
        low = self.safe_string_2(ticker, 'low_24h', 'low_price')
        bid = self.safe_string_2(ticker, 'best_bid', 'bid_px')
        bidVolume = self.safe_string_2(ticker, 'best_bid_size', 'bid_sz')
        ask = self.safe_string_2(ticker, 'best_ask', 'ask_px')
        askVolume = self.safe_string_2(ticker, 'best_ask_size', 'ask_sz')
        open = self.safe_string(ticker, 'open_24h')
        baseVolume = self.safe_string_2(ticker, 'base_volume_24h', 'v_24h')
        quoteVolume = self.safe_string_lower_2(ticker, 'quote_volume_24h', 'qv_24h')
        listMarketId = self.safe_string(result, 0)
        if listMarketId is not None:
            marketId = listMarketId
            timestamp = self.safe_integer(result, 12)
            high = self.safe_string(result, 5)
            low = self.safe_string(result, 6)
            bid = self.safe_string(result, 8)
            bidVolume = self.safe_string(result, 9)
            ask = self.safe_string(result, 10)
            askVolume = self.safe_string(result, 11)
            open = self.safe_string(result, 4)
            last = self.safe_string(result, 1)
            change = self.safe_string(result, 7)
            baseVolume = self.safe_string(result, 2)
            quoteVolume = self.safe_string_lower(result, 3)
        market = self.safe_market(marketId, market)
        symbol = market['symbol']
        if timestamp is None:
            # ticker from WS has a different field(in seconds)
            timestamp = self.safe_integer_product(ticker, 's_t', 1000)
        if percentage is None:
            percentage = Precise.string_mul(change, '100')
        if quoteVolume is None:
            if baseVolume is None:
                # self is swap
                quoteVolume = self.safe_string(ticker, 'volume_24h', quoteVolume)
            else:
                # self is a ticker from websockets
                # contrary to name and documentation, base_volume_24h is actually the quote volume
                quoteVolume = baseVolume
                baseVolume = None
        return self.safe_ticker({
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': high,
            'low': low,
            'bid': bid,
            'bidVolume': bidVolume,
            'ask': ask,
            'askVolume': askVolume,
            'vwap': None,
            'open': open,
            'close': last,
            'last': last,
            'previousClose': None,
            'change': change,
            'percentage': percentage,
            'average': average,
            'baseVolume': baseVolume,
            'quoteVolume': quoteVolume,
            'info': ticker,
        }, market)

    def fetch_ticker(self, symbol: str, params={}) -> Ticker:
        """
        fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
        :see: https://developer-pro.bitmart.com/en/spot/#get-ticker-of-a-trading-pair-v3
        :param str symbol: unified symbol of the market to fetch the ticker for
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {}
        response = None
        if market['swap']:
            request['contract_symbol'] = market['id']
            response = self.publicGetContractV1Tickers(self.extend(request, params))
            #
            #      {
            #          "message":"OK",
            #          "code":1000,
            #          "trace":"4a0ebceb-d3f7-45a3-8feb-f61e230e24cd",
            #          "data":{
            #              "tickers":[
            #                  {
            #                      "contract_symbol":"DOGEUSDT",
            #                      "last_price":"0.130180",
            #                      "index_price":"0.13028635",
            #                      "last_funding_rate":"0.00002025",
            #                      "price_change_percent_24h":"-2.326",
            #                      "volume_24h":"116789313.01797258",
            #                      "url":"https://futures.bitmart.com/en?symbol=DOGEUSDT",
            #                      "high_price":"0.134520",
            #                      "low_price":"0.128570",
            #                      "legal_coin_price":"0.13017401"
            #                  }
            #              ]
            #          }
            #      }
            #
        elif market['spot']:
            request['symbol'] = market['id']
            response = self.publicGetSpotQuotationV3Ticker(self.extend(request, params))
            #
            #     {
            #         "code": 1000,
            #         "trace": "f2194c2c202d2.99.1717535",
            #         "message": "success",
            #         "data": {
            #             "symbol": "BTC_USDT",
            #             "last": "68500.00",
            #             "v_24h": "10491.65490",
            #             "qv_24h": "717178990.42",
            #             "open_24h": "68149.75",
            #             "high_24h": "69499.99",
            #             "low_24h": "67132.40",
            #             "fluctuation": "0.00514",
            #             "bid_px": "68500",
            #             "bid_sz": "0.00162",
            #             "ask_px": "68500.01",
            #             "ask_sz": "0.01722",
            #             "ts": "1717131391671"
            #         }
            #     }
            #
        else:
            raise NotSupported(self.id + ' fetchTicker() does not support ' + market['type'] + ' markets, only spot and swap markets are accepted')
        # fails in naming for contract tickers 'contract_symbol'
        tickersById = None
        tickers = []
        ticker: dict = {}
        if market['spot']:
            ticker = self.safe_dict(response, 'data', {})
        else:
            data = self.safe_dict(response, 'data', {})
            tickers = self.safe_list(data, 'tickers', [])
            tickersById = self.index_by(tickers, 'contract_symbol')
            ticker = self.safe_dict(tickersById, market['id'])
        return self.parse_ticker(ticker, market)

    def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
        """
        fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
        :see: https://developer-pro.bitmart.com/en/spot/#get-ticker-of-all-pairs-v3
        :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
        """
        self.load_markets()
        symbols = self.market_symbols(symbols)
        type = None
        market = None
        if symbols is not None:
            symbol = self.safe_value(symbols, 0)
            market = self.market(symbol)
        type, params = self.handle_market_type_and_params('fetchTickers', market, params)
        response = None
        if type == 'spot':
            response = self.publicGetSpotQuotationV3Tickers(params)
            #
            #     {
            #         "code": 1000,
            #         "trace": "17c5e5d9ac49f9b71efca2bed55f1a.105.171225637482393",
            #         "message": "success",
            #         "data": [
            #             [
            #                 "AFIN_USDT",
            #                 "0.001047",
            #                 "11110",
            #                 "11.632170",
            #                 "0.001048",
            #                 "0.001048",
            #                 "0.001047",
            #                 "-0.00095",
            #                 "0.001029",
            #                 "5555",
            #                 "0.001041",
            #                 "5297",
            #                 "1717122550482"
            #             ],
            #         ]
            #     }
            #
        elif type == 'swap':
            response = self.publicGetContractV1Tickers(params)
            #
            #     {
            #         "message": "OK",
            #         "code": 1000,
            #         "trace": "c1dec681c24ea5d.105.171712565",
            #         "data": {
            #             "tickers": [
            #                 {
            #                     "contract_symbol": "SNTUSDT",
            #                     "last_price": "0.0366600",
            #                     "index_price": "0.03587373",
            #                     "last_funding_rate": "0.00005000",
            #                     "price_change_percent_24h": "-2.629",
            #                     "volume_24h": "10102540.19909109848",
            #                     "url": "https://futures.bitmart.com/en?symbol=SNTUSDT",
            #                     "high_price": "0.0405600",
            #                     "low_price": "0.0355000",
            #                     "legal_coin_price": "0.03666697"
            #                 },
            #             ]
            #         }
            #     }
            #
        else:
            raise NotSupported(self.id + ' fetchTickers() does not support ' + type + ' markets, only spot and swap markets are accepted')
        tickers = []
        if type == 'spot':
            tickers = self.safe_list(response, 'data', [])
        else:
            data = self.safe_dict(response, 'data', {})
            tickers = self.safe_list(data, 'tickers', [])
        result: dict = {}
        for i in range(0, len(tickers)):
            ticker: dict = {}
            if type == 'spot':
                ticker = self.parse_ticker({'result': tickers[i]})
            else:
                ticker = self.parse_ticker(tickers[i])
            symbol = ticker['symbol']
            result[symbol] = ticker
        return self.filter_by_array_tickers(result, 'symbol', symbols)

    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://developer-pro.bitmart.com/en/spot/#get-depth-v3
        :see: https://developer-pro.bitmart.com/en/futures/#get-market-depth
        :param str symbol: unified symbol of the market to fetch the order book for
        :param int [limit]: the maximum amount of order book entries to return
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        response = None
        if market['spot']:
            if limit is not None:
                request['limit'] = limit  # default 35, max 50
            response = self.publicGetSpotQuotationV3Books(self.extend(request, params))
        elif market['swap']:
            response = self.publicGetContractPublicDepth(self.extend(request, params))
        else:
            raise NotSupported(self.id + ' fetchOrderBook() does not support ' + market['type'] + ' markets, only spot and swap markets are accepted')
        #
        # spot
        #
        #     {
        #         "code": 1000,
        #         "message": "success",
        #         "data": {
        #             "ts": "1695264191808",
        #             "symbol": "BTC_USDT",
        #             "asks": [
        #                 ["26942.57","0.06492"],
        #                 ["26942.73","0.05447"],
        #                 ["26943.00","0.07154"]
        #             ],
        #             "bids": [
        #                 ["26942.45","0.00074"],
        #                 ["26941.53","0.00371"],
        #                 ["26940.94","0.08992"]
        #             ]
        #         },
        #         "trace": "430a7f69581d4258a8e4b424dfb10782.73.16952341919017619"
        #     }
        #
        # swap
        #
        #     {
        #         "code": 1000,
        #         "message": "Ok",
        #         "data": {
        #             "asks": [
        #                 ["26938.3","3499","3499"],
        #                 ["26938.5","14702","18201"],
        #                 ["26938.6","20457","38658"]
        #             ],
        #             "bids": [
        #                 ["26938.2","20","20"],
        #                 ["26937.9","1913","1933"],
        #                 ["26937.8","2588","4521"]
        #             ],
        #             "timestamp": 1695264383999,
        #             "symbol": "BTCUSDT"
        #         },
        #         "trace": "4cad855074664097ac6ba5258c47305d.72.16952643834721135"
        #     }
        #
        data = self.safe_value(response, 'data', {})
        timestamp = self.safe_integer_2(data, 'ts', 'timestamp')
        return self.parse_order_book(data, market['symbol'], timestamp)

    def parse_trade(self, trade: dict, market: Market = None) -> Trade:
        #
        # public fetchTrades spot( amount = count * price )
        #
        #     [
        #         "BTC_USDT",      # symbol
        #         "1717212457302",  # timestamp
        #         "67643.11",      # price
        #         "0.00106",       # size
        #         "sell"           # side
        #     ]
        #
        # spot: fetchMyTrades
        #
        #    {
        #        "tradeId":"182342999769370687",
        #        "orderId":"183270218784142990",
        #        "clientOrderId":"183270218784142990",
        #        "symbol":"ADA_USDT",
        #        "side":"buy",
        #        "orderMode":"spot",
        #        "type":"market",
        #        "price":"0.245948",
        #        "size":"20.71",
        #        "notional":"5.09358308",
        #        "fee":"0.00509358",
        #        "feeCoinName":"USDT",
        #        "tradeRole":"taker",
        #        "createTime":1695658457836,
        #    }
        #
        # swap: fetchMyTrades
        #
        #    {
        #        "order_id": "230930336848609",
        #        "trade_id": "6212604014",
        #        "symbol": "BTCUSDT",
        #        "side": 3,
        #        "price": "26910.4",
        #        "vol": "1",
        #        "exec_type": "Taker",
        #        "profit": False,
        #        "create_time": 1695961596692,
        #        "realised_profit": "-0.0003",
        #        "paid_fees": "0.01614624"
        #    }
        #
        # ws swap
        #
        #    {
        #        'fee': '-0.000044502',
        #        'feeCcy': 'USDT',
        #        'fillPrice': '74.17',
        #        'fillQty': '1',
        #        'lastTradeID': 6802340762
        #    }
        #
        timestamp = self.safe_integer_n(trade, ['createTime', 'create_time', 1])
        isPublic = self.safe_string(trade, 0)
        isPublicTrade = (isPublic is not None)
        amount = None
        cost = None
        type = None
        side = None
        if isPublicTrade:
            amount = self.safe_string_2(trade, 'count', 3)
            cost = self.safe_string(trade, 'amount')
            side = self.safe_string_2(trade, 'type', 4)
        else:
            amount = self.safe_string_n(trade, ['size', 'vol', 'fillQty'])
            cost = self.safe_string(trade, 'notional')
            type = self.safe_string(trade, 'type')
            side = self.parse_order_side(self.safe_string(trade, 'side'))
        marketId = self.safe_string_2(trade, 'symbol', 0)
        market = self.safe_market(marketId, market)
        feeCostString = self.safe_string_2(trade, 'fee', 'paid_fees')
        fee = None
        if feeCostString is not None:
            feeCurrencyId = self.safe_string(trade, 'feeCoinName')
            feeCurrencyCode = self.safe_currency_code(feeCurrencyId)
            if feeCurrencyCode is None:
                feeCurrencyCode = market['base'] if (side == 'buy') else market['quote']
            fee = {
                'cost': feeCostString,
                'currency': feeCurrencyCode,
            }
        return self.safe_trade({
            'info': trade,
            'id': self.safe_string_n(trade, ['tradeId', 'trade_id', 'lastTradeID']),
            'order': self.safe_string_2(trade, 'orderId', 'order_id'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': market['symbol'],
            'type': type,
            'side': side,
            'price': self.safe_string_n(trade, ['price', 'fillPrice', 2]),
            'amount': amount,
            'cost': cost,
            'takerOrMaker': self.safe_string_lower_2(trade, 'tradeRole', 'exec_type'),
            'fee': fee,
        }, market)

    def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
        """
        get a list of the most recent trades for a particular symbol
        :see: https://developer-pro.bitmart.com/en/spot/#get-recent-trades-v3
        :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 number of trades to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
        """
        self.load_markets()
        market = self.market(symbol)
        if not market['spot']:
            raise NotSupported(self.id + ' fetchTrades() does not support ' + market['type'] + ' orders, only spot orders are accepted')
        request: dict = {
            'symbol': market['id'],
        }
        if limit is not None:
            request['limit'] = limit
        response = self.publicGetSpotQuotationV3Trades(self.extend(request, params))
        #
        #     {
        #         "code": 1000,
        #         "trace": "58031f9a5bd.111.17117",
        #         "message": "success",
        #         "data": [
        #             [
        #                 "BTC_USDT",
        #                 "1717212457302",
        #                 "67643.11",
        #                 "0.00106",
        #                 "sell"
        #             ],
        #         ]
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_trades(data, market, since, limit)

    def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
        #
        # spot
        #    [
        #        "1699512060",  # timestamp
        #        "36746.49",  # open
        #        "36758.71",  # high
        #        "36736.13",  # low
        #        "36755.99",  # close
        #        "2.83965",  # base volume
        #        "104353.57"  # quote volume
        #    ]
        #
        # swap
        #    {
        #        "low_price": "20090.3",
        #        "high_price": "20095.5",
        #        "open_price": "20092.6",
        #        "close_price": "20091.4",
        #        "volume": "8748",
        #        "timestamp": 1665002281
        #    }
        #
        # ws
        #    [
        #        1631056350,  # timestamp
        #        "46532.83",  # open
        #        "46555.71",  # high
        #        "46511.41",  # low
        #        "46555.71",  # close
        #        "0.25",  # volume
        #    ]
        #
        # ws swap
        #    {
        #        "symbol":"BTCUSDT",
        #        "o":"146.24",
        #        "h":"146.24",
        #        "l":"146.24",
        #        "c":"146.24",
        #        "v":"146"
        #    }
        #
        if isinstance(ohlcv, list):
            return [
                self.safe_timestamp(ohlcv, 0),
                self.safe_number(ohlcv, 1),
                self.safe_number(ohlcv, 2),
                self.safe_number(ohlcv, 3),
                self.safe_number(ohlcv, 4),
                self.safe_number(ohlcv, 5),
            ]
        else:
            return [
                self.safe_timestamp_2(ohlcv, 'timestamp', 'ts'),
                self.safe_number_2(ohlcv, 'open_price', 'o'),
                self.safe_number_2(ohlcv, 'high_price', 'h'),
                self.safe_number_2(ohlcv, 'low_price', 'l'),
                self.safe_number_2(ohlcv, 'close_price', 'c'),
                self.safe_number_2(ohlcv, 'volume', 'v'),
            ]

    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://developer-pro.bitmart.com/en/spot/#get-history-k-line-v3
        :see: https://developer-pro.bitmart.com/en/futures/#get-k-line
        :param str symbol: unified symbol of the market to fetch OHLCV data for
        :param str timeframe: the length of time each candle represents
        :param int [since]: timestamp in ms of the earliest candle to fetch
        :param int [limit]: the maximum amount of candles to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp of the latest candle in ms
        :param boolean [params.paginate]: *spot only* default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
        :returns int[][]: A list of candles ordered, open, high, low, close, volume
        """
        self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchOHLCV', 'paginate', False)
        if paginate:
            return self.fetch_paginated_call_deterministic('fetchOHLCV', symbol, since, limit, timeframe, params, 200)
        market = self.market(symbol)
        duration = self.parse_timeframe(timeframe)
        parsedTimeframe = self.safe_integer(self.timeframes, timeframe)
        request: dict = {
            'symbol': market['id'],
        }
        if parsedTimeframe is not None:
            request['step'] = parsedTimeframe
        else:
            request['step'] = timeframe
        if market['spot']:
            request, params = self.handle_until_option('before', request, params, 0.001)
            if limit is not None:
                request['limit'] = limit
            if since is not None:
                request['after'] = self.parse_to_int((since / 1000)) - 1
        else:
            maxLimit = 1200
            if limit is None:
                limit = maxLimit
            limit = min(maxLimit, limit)
            now = self.parse_to_int(self.milliseconds() / 1000)
            if since is None:
                start = now - limit * duration
                request['start_time'] = start
                request['end_time'] = now
            else:
                start = self.parse_to_int((since / 1000)) - 1
                end = self.sum(start, limit * duration)
                request['start_time'] = start
                request['end_time'] = min(end, now)
            request, params = self.handle_until_option('end_time', request, params, 0.001)
        response = None
        if market['swap']:
            response = self.publicGetContractPublicKline(self.extend(request, params))
        else:
            response = self.publicGetSpotQuotationV3Klines(self.extend(request, params))
        #
        # spot
        #
        #     {
        #         "code": 1000,
        #         "message": "success",
        #         "data": [
        #             ["1699512060","36746.49","36758.71","36736.13","36755.99","2.83965","104353.57"],
        #             ["1699512120","36756.00","36758.70","36737.14","36737.63","1.96070","72047.10"],
        #             ["1699512180","36737.63","36740.45","36737.62","36740.44","0.63194","23217.62"]
        #         ],
        #         "trace": "6591fc7b508845359d5fa442e3b3a4fb.72.16995122398750695"
        #     }
        #
        # swap
        #
        #     {
        #         "code": 1000,
        #         "message": "Ok",
        #         "data": [
        #             {
        #                 "low_price": "20090.3",
        #                 "high_price": "20095.5",
        #                 "open_price": "20092.6",
        #                 "close_price": "20091.4",
        #                 "volume": "8748",
        #                 "timestamp": 1665002281
        #             },
        #             ...
        #         ],
        #         "trace": "96c989db-e0f5-46f5-bba6-60cfcbde699b"
        #     }
        #
        ohlcv = self.safe_list(response, 'data', [])
        return self.parse_ohlcvs(ohlcv, market, timeframe, since, limit)

    def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        :see: https://developer-pro.bitmart.com/en/spot/#account-trade-list-v4-signed
        :see: https://developer-pro.bitmart.com/en/futures/#get-order-trade-keyed
        fetch all trades made by the user
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch trades for
        :param int [limit]: the maximum number of trades structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the latest time in ms to fetch trades for
        :param boolean [params.marginMode]: *spot* whether to fetch trades for margin orders or spot orders, defaults to spot orders(only isolated margin orders are supported)
        :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
        """
        self.load_markets()
        market = None
        request: dict = {}
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        type = None
        response = None
        type, params = self.handle_market_type_and_params('fetchMyTrades', market, params)
        until = self.safe_integer_n(params, ['until', 'endTime', 'end_time'])
        params = self.omit(params, ['until'])
        if type == 'spot':
            marginMode = None
            marginMode, params = self.handle_margin_mode_and_params('fetchMyTrades', params)
            if marginMode == 'isolated':
                request['orderMode'] = 'iso_margin'
            options = self.safe_value(self.options, 'fetchMyTrades', {})
            defaultLimit = self.safe_integer(options, 'limit', 200)
            if limit is None:
                limit = defaultLimit
            request['limit'] = limit
            if since is not None:
                request['startTime'] = since
            if until is not None:
                request['endTime'] = until
            response = self.privatePostSpotV4QueryTrades(self.extend(request, params))
        elif type == 'swap':
            if symbol is None:
                raise ArgumentsRequired(self.id + ' fetchMyTrades() requires a symbol argument')
            if since is not None:
                request['start_time'] = since
            if until is not None:
                request['end_time'] = until
            response = self.privateGetContractPrivateTrades(self.extend(request, params))
        else:
            raise NotSupported(self.id + ' fetchMyTrades() does not support ' + type + ' orders, only spot and swap orders are accepted')
        #
        # spot
        #
        #    {
        #        "code":1000,
        #        "message":"success",
        #        "data":[
        #           {
        #              "tradeId":"182342999769370687",
        #              "orderId":"183270218784142990",
        #              "clientOrderId":"183270218784142990",
        #              "symbol":"ADA_USDT",
        #              "side":"buy",
        #              "orderMode":"spot",
        #              "type":"market",
        #              "price":"0.245948",
        #              "size":"20.71",
        #              "notional":"5.09358308",
        #              "fee":"0.00509358",
        #              "feeCoinName":"USDT",
        #              "tradeRole":"taker",
        #              "createTime":1695658457836,
        #              "updateTime":1695658457836
        #           }
        #        ],
        #        "trace":"fbaee9e0e2f5442fba5b3262fc86b0ac.65.16956593456523085"
        #    }
        #
        # swap
        #
        #     {
        #         "code": 1000,
        #         "message": "Ok",
        #         "data": [
        #             {
        #                 "order_id": "230930336848609",
        #                 "trade_id": "6212604014",
        #                 "symbol": "BTCUSDT",
        #                 "side": 3,
        #                 "price": "26910.4",
        #                 "vol": "1",
        #                 "exec_type": "Taker",
        #                 "profit": False,
        #                 "create_time": 1695961596692,
        #                 "realised_profit": "-0.0003",
        #                 "paid_fees": "0.01614624"
        #             },
        #         ],
        #         "trace": "4cad855074634097ac6ba5257c47305d.62.16959616054873723"
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_trades(data, market, since, limit)

    def fetch_order_trades(self, id: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        :see: https://developer-pro.bitmart.com/en/spot/#order-trade-list-v4-signed
        fetch all the trades made from a single order
        :param str id: order id
        :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 to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
        """
        self.load_markets()
        request: dict = {
            'orderId': id,
        }
        response = self.privatePostSpotV4QueryOrderTrades(self.extend(request, params))
        data = self.safe_list(response, 'data', [])
        return self.parse_trades(data, None, since, limit)

    def custom_parse_balance(self, response, marketType) -> Balances:
        data = self.safe_value(response, 'data', {})
        wallet = None
        if marketType == 'swap':
            wallet = self.safe_value(response, 'data', [])
        elif marketType == 'margin':
            wallet = self.safe_value(data, 'symbols', [])
        else:
            wallet = self.safe_value(data, 'wallet', [])
        result = {'info': response}
        if marketType == 'margin':
            for i in range(0, len(wallet)):
                entry = wallet[i]
                marketId = self.safe_string(entry, 'symbol')
                symbol = self.safe_symbol(marketId, None, '_')
                base = self.safe_value(entry, 'base', {})
                quote = self.safe_value(entry, 'quote', {})
                baseCode = self.safe_currency_code(self.safe_string(base, 'currency'))
                quoteCode = self.safe_currency_code(self.safe_string(quote, 'currency'))
                subResult: dict = {}
                subResult[baseCode] = self.parse_balance_helper(base)
                subResult[quoteCode] = self.parse_balance_helper(quote)
                result[symbol] = self.safe_balance(subResult)
            return result
        else:
            for i in range(0, len(wallet)):
                balance = wallet[i]
                currencyId = self.safe_string_2(balance, 'id', 'currency')
                currencyId = self.safe_string(balance, 'coin_code', currencyId)
                code = self.safe_currency_code(currencyId)
                account = self.account()
                account['free'] = self.safe_string_2(balance, 'available', 'available_balance')
                account['used'] = self.safe_string_2(balance, 'frozen', 'frozen_balance')
                result[code] = account
            return self.safe_balance(result)

    def parse_balance_helper(self, entry):
        account = self.account()
        account['used'] = self.safe_string(entry, 'frozen')
        account['free'] = self.safe_string(entry, 'available')
        account['total'] = self.safe_string(entry, 'total_asset')
        debt = self.safe_string(entry, 'borrow_unpaid')
        interest = self.safe_string(entry, 'interest_unpaid')
        account['debt'] = Precise.string_add(debt, interest)
        return account

    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://developer-pro.bitmart.com/en/spot/#get-spot-wallet-balance
        :see: https://developer-pro.bitmart.com/en/futures/#get-contract-assets-detail
        :see: https://developer-pro.bitmart.com/en/spot/#get-account-balance
        :see: https://developer-pro.bitmart.com/en/spot/#get-margin-account-details-isolated
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
        """
        self.load_markets()
        marketType = None
        marketType, params = self.handle_market_type_and_params('fetchBalance', None, params)
        marginMode = self.safe_string(params, 'marginMode')
        isMargin = self.safe_bool(params, 'margin', False)
        params = self.omit(params, ['margin', 'marginMode'])
        if marginMode is not None or isMargin:
            marketType = 'margin'
        response = None
        if marketType == 'spot':
            response = self.privateGetSpotV1Wallet(params)
        elif marketType == 'swap':
            response = self.privateGetContractPrivateAssetsDetail(params)
        elif marketType == 'account':
            response = self.privateGetAccountV1Wallet(params)
        elif marketType == 'margin':
            response = self.privateGetSpotV1MarginIsolatedAccount(params)
        else:
            raise NotSupported(self.id + ' fetchBalance() does not support ' + marketType + ' markets, only spot, swap and account and margin markets are accepted')
        #
        # spot
        #
        #     {
        #         "message":"OK",
        #         "code":1000,
        #         "trace":"39069916-72f9-44c7-acde-2ad5afd21cad",
        #         "data":{
        #             "wallet":[
        #                 {"id":"BTC","name":"Bitcoin","available":"0.00000062","frozen":"0.00000000"},
        #                 {"id":"ETH","name":"Ethereum","available":"0.00002277","frozen":"0.00000000"},
        #                 {"id":"BMX","name":"BitMart Token","available":"0.00000000","frozen":"0.00000000"}
        #             ]
        #         }
        #     }
        #
        # account
        #
        #     {
        #         "message":"OK",
        #         "code":1000,
        #         "trace":"5c3b7fc7-93b2-49ef-bb59-7fdc56915b59",
        #         "data":{
        #             "wallet":[
        #                 {"currency":"BTC","name":"Bitcoin","available":"0.00000062","frozen":"0.00000000"},
        #                 {"currency":"ETH","name":"Ethereum","available":"0.00002277","frozen":"0.00000000"}
        #             ]
        #         }
        #     }
        #
        # swap
        #
        #     {
        #         "code": 1000,
        #         "message": "Ok",
        #         "data": [
        #             {
        #                 "currency": "USDT",
        #                 "available_balance": "0",
        #                 "frozen_balance": "0",
        #                 "unrealized": "0",
        #                 "equity": "0",
        #                 "position_deposit": "0"
        #             },
        #             ...
        #         ],
        #         "trace": "f9da3a39-cf45-42e7-914d-294f565dfc33"
        #     }
        #
        # margin
        #
        #     {
        #         "message": "OK",
        #         "code": 1000,
        #         "trace": "61dd6ab265c04064b72d8bc9b205f741.71.16701055600915302",
        #         "data": {
        #             "symbols": [
        #                 {
        #                     "symbol": "BTC_USDT",
        #                     "risk_rate": "999.00",
        #                     "risk_level": "1",
        #                     "buy_enabled": False,
        #                     "sell_enabled": False,
        #                     "liquidate_price": null,
        #                     "liquidate_rate": "1.15",
        #                     "base": {
        #                         "currency": "BTC",
        #                         "borrow_enabled": True,
        #                         "borrowed": "0.00000000",
        #                         "available": "0.00000000",
        #                         "frozen": "0.00000000",
        #                         "net_asset": "0.00000000",
        #                         "net_assetBTC": "0.00000000",
        #                         "total_asset": "0.00000000",
        #                         "borrow_unpaid": "0.00000000",
        #                         "interest_unpaid": "0.00000000"
        #                     },
        #                     "quote": {
        #                         "currency": "USDT",
        #                         "borrow_enabled": True,
        #                         "borrowed": "0.00000000",
        #                         "available": "20.00000000",
        #                         "frozen": "0.00000000",
        #                         "net_asset": "20.00000000",
        #                         "net_assetBTC": "0.00118008",
        #                         "total_asset": "20.00000000",
        #                         "borrow_unpaid": "0.00000000",
        #                         "interest_unpaid": "0.00000000"
        #                     }
        #                 }
        #             ]
        #         }
        #     }
        #
        return self.custom_parse_balance(response, marketType)

    def parse_trading_fee(self, fee: dict, market: Market = None) -> TradingFeeInterface:
        #
        #     {
        #         "symbol": "ETH_USDT",
        #         "taker_fee_rate": "0.0025",
        #         "maker_fee_rate": "0.0025"
        #     }
        #
        marketId = self.safe_string(fee, 'symbol')
        symbol = self.safe_symbol(marketId)
        return {
            'info': fee,
            'symbol': symbol,
            'maker': self.safe_number(fee, 'maker_fee_rate'),
            'taker': self.safe_number(fee, 'taker_fee_rate'),
            'percentage': None,
            'tierBased': None,
        }

    def fetch_trading_fee(self, symbol: str, params={}) -> TradingFeeInterface:
        """
        fetch the trading fees for a market
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `fee structure <https://docs.ccxt.com/#/?id=fee-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        if not market['spot']:
            raise NotSupported(self.id + ' fetchTradingFee() does not support ' + market['type'] + ' orders, only spot orders are accepted')
        request: dict = {
            'symbol': market['id'],
        }
        response = self.privateGetSpotV1TradeFee(self.extend(request, params))
        #
        #     {
        #         "message": "OK",
        #         "code": "1000",
        #         "trace": "5a6f1e40-37fe-4849-a494-03279fadcc62",
        #         "data": {
        #             "symbol": "ETH_USDT",
        #             "taker_fee_rate": "0.0025",
        #             "maker_fee_rate": "0.0025"
        #         }
        #     }
        #
        data = self.safe_value(response, 'data')
        return self.parse_trading_fee(data)

    def parse_order(self, order: dict, market: Market = None) -> Order:
        #
        # createOrder
        #
        #     {
        #         "order_id": 2707217580
        #     }
        #
        # swap
        #   "data": {
        #       "order_id": 231116359426639,
        #       "price": "market price"
        #    },
        #
        # cancelOrder
        #
        #     "2707217580"  # order id
        #
        # spot fetchOrder, fetchOrdersByStatus, fetchOpenOrders, fetchClosedOrders
        #
        #     {
        #         "order_id":1736871726781,
        #         "symbol":"BTC_USDT",
        #         "create_time":1591096004000,
        #         "side":"sell",
        #         "type":"market",  # limit, market, limit_maker, ioc
        #         "price":"0.00",
        #         "price_avg":"0.00",
        #         "size":"0.02000",
        #         "notional":"0.00000000",
        #         "filled_notional":"0.00000000",
        #         "filled_size":"0.00000",
        #         "status":"8"
        #     }
        #
        # spot v4
        #    {
        #        "orderId" : "118100034543076010",
        #        "clientOrderId" : "118100034543076010",
        #        "symbol" : "BTC_USDT",
        #        "side" : "buy",
        #        "orderMode" : "spot",
        #        "type" : "limit",
        #        "state" : "filled",
        #        "price" : "48800.00",
        #        "priceAvg" : "39999.00",
        #        "size" : "0.10000",
        #        "filledSize" : "0.10000",
        #        "notional" : "4880.00000000",
        #        "filledNotional" : "3999.90000000",
        #        "createTime" : 1681701557927,
        #        "updateTime" : 1681701559408
        #    }
        #
        # swap: fetchOrder, fetchOpenOrders, fetchClosedOrders
        #
        #     {
        #         "order_id": "230935812485489",
        #         "client_order_id": "",
        #         "price": "24000",
        #         "size": "1",
        #         "symbol": "BTCUSDT",
        #         "state": 2,
        #         "side": 1,
        #         "type": "limit",
        #         "leverage": "10",
        #         "open_type": "isolated",
        #         "deal_avg_price": "0",
        #         "deal_size": "0",
        #         "create_time": 1695702258629,
        #         "update_time": 1695702258642,
        #         "activation_price_type": 0,
        #         "activation_price": "",
        #         "callback_rate": ""
        #     }
        #
        id = None
        if isinstance(order, str):
            id = order
            order = {}
        id = self.safe_string_2(order, 'order_id', 'orderId', id)
        timestamp = self.safe_integer_2(order, 'create_time', 'createTime')
        marketId = self.safe_string(order, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        market = self.safe_market(symbol, market)
        orderType = self.safe_string(market, 'type', 'spot')
        type = self.safe_string(order, 'type')
        timeInForce = None
        postOnly = None
        if type == 'limit_maker':
            type = 'limit'
            postOnly = True
            timeInForce = 'PO'
        if type == 'ioc':
            type = 'limit'
            timeInForce = 'IOC'
        priceString = self.safe_string(order, 'price')
        if priceString == 'market price':
            priceString = None
        trailingActivationPrice = self.safe_number(order, 'activation_price')
        return self.safe_order({
            'id': id,
            'clientOrderId': self.safe_string(order, 'client_order_id'),
            'info': order,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': self.safe_integer(order, 'update_time'),
            'symbol': symbol,
            'type': type,
            'timeInForce': timeInForce,
            'postOnly': postOnly,
            'side': self.parse_order_side(self.safe_string(order, 'side')),
            'price': self.omit_zero(priceString),
            'stopPrice': trailingActivationPrice,
            'triggerPrice': trailingActivationPrice,
            'amount': self.omit_zero(self.safe_string(order, 'size')),
            'cost': self.safe_string_2(order, 'filled_notional', 'filledNotional'),
            'average': self.safe_string_n(order, ['price_avg', 'priceAvg', 'deal_avg_price']),
            'filled': self.safe_string_n(order, ['filled_size', 'filledSize', 'deal_size']),
            'remaining': None,
            'status': self.parse_order_status_by_type(orderType, self.safe_string_2(order, 'status', 'state')),
            'fee': None,
            'trades': None,
        }, market)

    def parse_order_side(self, side):
        sides: dict = {
            '1': 'buy',
            '2': 'buy',
            '3': 'sell',
            '4': 'sell',
        }
        return self.safe_string(sides, side, side)

    def parse_order_status_by_type(self, type, status):
        statusesByType: dict = {
            'spot': {
                '1': 'rejected',  # Order failure
                '2': 'open',  # Placing order
                '3': 'rejected',  # Order failure, Freeze failure
                '4': 'open',  # Order success, Pending for fulfilment
                '5': 'open',  # Partially filled
                '6': 'closed',  # Fully filled
                '7': 'canceled',  # Canceling
                '8': 'canceled',  # Canceled
                'new': 'open',
                'partially_filled': 'open',
                'filled': 'closed',
                'partially_canceled': 'canceled',
            },
            'swap': {
                '1': 'open',  # Submitting
                '2': 'open',  # Commissioned
                '4': 'closed',  # Completed
            },
        }
        statuses = self.safe_value(statusesByType, type, {})
        return self.safe_string(statuses, status, status)

    def create_market_buy_order_with_cost(self, symbol: str, cost: float, params={}):
        """
        create a market buy order by providing the symbol and cost
        :see: https://developer-pro.bitmart.com/en/spot/#new-order-v2-signed
        :param str symbol: unified symbol of the market to create an order in
        :param float cost: how much you want to trade in units of the quote currency
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        if not market['spot']:
            raise NotSupported(self.id + ' createMarketBuyOrderWithCost() supports spot orders only')
        params['createMarketBuyOrderRequiresPrice'] = False
        return self.create_order(symbol, 'market', 'buy', cost, None, params)

    def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
        create a trade order
        :see: https://developer-pro.bitmart.com/en/spot/#new-order-v2-signed
        :see: https://developer-pro.bitmart.com/en/spot/#place-margin-order
        :see: https://developer-pro.bitmart.com/en/futures/#submit-order-signed
        :see: https://developer-pro.bitmart.com/en/futures/#submit-plan-order-signed
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market', 'limit' or 'trailing' for swap markets only
        :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 fullfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.marginMode]: 'cross' or 'isolated'
        :param str [params.leverage]: *swap only* leverage level
        :param str [params.clientOrderId]: client order id of the order
        :param boolean [params.reduceOnly]: *swap only* reduce only
        :param boolean [params.postOnly]: make sure the order is posted to the order book and not matched immediately
        :param str [params.triggerPrice]: *swap only* the price to trigger a stop order
        :param int [params.price_type]: *swap only* 1: last price, 2: fair price, default is 1
        :param int [params.price_way]: *swap only* 1: price way long, 2: price way short
        :param int [params.activation_price_type]: *swap trailing order only* 1: last price, 2: fair price, default is 1
        :param str [params.trailingPercent]: *swap only* the percent to trail away from the current market price, min 0.1 max 5
        :param str [params.trailingTriggerPrice]: *swap only* the price to trigger a trailing order, default uses the price argument
        :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        result = self.handle_margin_mode_and_params('createOrder', params)
        marginMode = self.safe_string(result, 0)
        triggerPrice = self.safe_string_n(params, ['triggerPrice', 'stopPrice', 'trigger_price'])
        isTriggerOrder = triggerPrice is not None
        response = None
        if market['spot']:
            spotRequest = self.create_spot_order_request(symbol, type, side, amount, price, params)
            if marginMode == 'isolated':
                response = self.privatePostSpotV1MarginSubmitOrder(spotRequest)
            else:
                response = self.privatePostSpotV2SubmitOrder(spotRequest)
        else:
            swapRequest = self.create_swap_order_request(symbol, type, side, amount, price, params)
            if isTriggerOrder:
                response = self.privatePostContractPrivateSubmitPlanOrder(swapRequest)
            else:
                response = self.privatePostContractPrivateSubmitOrder(swapRequest)
        #
        # spot and margin
        #
        #     {
        #         "code": 1000,
        #         "trace":"886fb6ae-456b-4654-b4e0-d681ac05cea1",
        #         "message": "OK",
        #         "data": {
        #             "order_id": 2707217580
        #         }
        #     }
        #
        # swap
        # {"code":1000,"message":"Ok","data":{"order_id":231116359426639,"price":"market price"},"trace":"7f9c94e10f9d4513bc08a7bfc2a5559a.62.16996369620521911"}
        #
        data = self.safe_value(response, 'data', {})
        order = self.parse_order(data, market)
        order['type'] = type
        order['side'] = side
        order['amount'] = amount
        order['price'] = price
        return order

    def create_orders(self, orders: List[OrderRequest], params={}):
        """
        create a list of trade orders
        :see: https://developer-pro.bitmart.com/en/spot/#new-batch-order-v4-signed
        :param Array orders: list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params
        :param dict [params]:  extra parameters specific to the exchange API endpoint
        :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        self.load_markets()
        ordersRequests = []
        symbol = None
        market = None
        for i in range(0, len(orders)):
            rawOrder = orders[i]
            marketId = self.safe_string(rawOrder, 'symbol')
            market = self.market(marketId)
            if not market['spot']:
                raise NotSupported(self.id + ' createOrders() supports spot orders only')
            if symbol is None:
                symbol = marketId
            else:
                if symbol != marketId:
                    raise BadRequest(self.id + ' createOrders() requires all orders to have the same symbol')
            type = self.safe_string(rawOrder, 'type')
            side = self.safe_string(rawOrder, 'side')
            amount = self.safe_value(rawOrder, 'amount')
            price = self.safe_value(rawOrder, 'price')
            orderParams = self.safe_dict(rawOrder, 'params', {})
            orderRequest = self.create_spot_order_request(marketId, type, side, amount, price, orderParams)
            orderRequest = self.omit(orderRequest, ['symbol'])  # not needed because it goes in the outter object
            ordersRequests.append(orderRequest)
        request: dict = {
            'symbol': market['id'],
            'orderParams': ordersRequests,
        }
        response = self.privatePostSpotV4BatchOrders(request)
        #
        # {
        #     "message": "OK",
        #     "code": 1000,
        #     "trace": "5fc697fb817a4b5396284786a9b2609a.263.17022620476480263",
        #     "data": {
        #       "code": 0,
        #       "msg": "success",
        #       "data": {
        #         "orderIds": [
        #           "212751308355553320"
        #         ]
        #       }
        #     }
        # }
        #
        data = self.safe_dict(response, 'data', {})
        innderData = self.safe_dict(data, 'data', {})
        orderIds = self.safe_list(innderData, 'orderIds', [])
        parsedOrders = []
        for i in range(0, len(orderIds)):
            orderId = orderIds[i]
            order = self.safe_order({'id': orderId}, market)
            parsedOrders.append(order)
        return parsedOrders

    def create_swap_order_request(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
         * @ignore
        create a trade order
        :see: https://developer-pro.bitmart.com/en/futures/#submit-order-signed
        :see: https://developer-pro.bitmart.com/en/futures/#submit-plan-order-signed
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market', 'limit' or 'trailing'
        :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 fullfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.leverage]: leverage level
        :param boolean [params.reduceOnly]: *swap only* reduce only
        :param str [params.marginMode]: 'cross' or 'isolated', default is 'cross'
        :param str [params.clientOrderId]: client order id of the order
        :param str [params.triggerPrice]: *swap only* the price to trigger a stop order
        :param int [params.price_type]: *swap only* 1: last price, 2: fair price, default is 1
        :param int [params.price_way]: *swap only* 1: price way long, 2: price way short
        :param int [params.activation_price_type]: *swap trailing order only* 1: last price, 2: fair price, default is 1
        :param str [params.trailingPercent]: *swap only* the percent to trail away from the current market price, min 0.1 max 5
        :param str [params.trailingTriggerPrice]: *swap only* the price to trigger a trailing order, default uses the price argument
        :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
            'type': type,
            'size': int(self.amount_to_precision(symbol, amount)),
        }
        timeInForce = self.safe_string(params, 'timeInForce')
        mode = self.safe_integer(params, 'mode')  # only for swap
        isMarketOrder = type == 'market'
        postOnly = None
        reduceOnly = self.safe_value(params, 'reduceOnly')
        isExchangeSpecificPo = (mode == 4)
        postOnly, params = self.handle_post_only(isMarketOrder, isExchangeSpecificPo, params)
        ioc = ((timeInForce == 'IOC') or (mode == 3))
        isLimitOrder = (type == 'limit') or postOnly or ioc
        if timeInForce == 'GTC':
            request['mode'] = 1
        elif timeInForce == 'FOK':
            request['mode'] = 2
        elif timeInForce == 'IOC':
            request['mode'] = 3
        if postOnly:
            request['mode'] = 4
        triggerPrice = self.safe_string_n(params, ['triggerPrice', 'stopPrice', 'trigger_price'])
        isTriggerOrder = triggerPrice is not None
        trailingTriggerPrice = self.safe_string_2(params, 'trailingTriggerPrice', 'activation_price', self.number_to_string(price))
        trailingPercent = self.safe_string_2(params, 'trailingPercent', 'callback_rate')
        isTrailingPercentOrder = trailingPercent is not None
        if isLimitOrder:
            request['price'] = self.price_to_precision(symbol, price)
        elif type == 'trailing' or isTrailingPercentOrder:
            request['callback_rate'] = trailingPercent
            request['activation_price'] = self.price_to_precision(symbol, trailingTriggerPrice)
            request['activation_price_type'] = self.safe_integer(params, 'activation_price_type', 1)
        if isTriggerOrder:
            request['executive_price'] = self.price_to_precision(symbol, price)
            request['trigger_price'] = self.price_to_precision(symbol, triggerPrice)
            request['price_type'] = self.safe_integer(params, 'price_type', 1)
            if side == 'buy':
                if reduceOnly:
                    request['price_way'] = 2
                else:
                    request['price_way'] = 1
            elif side == 'sell':
                if reduceOnly:
                    request['price_way'] = 1
                else:
                    request['price_way'] = 2
        if side == 'buy':
            if reduceOnly:
                request['side'] = 2  # buy close short
            else:
                request['side'] = 1  # buy open long
        elif side == 'sell':
            if reduceOnly:
                request['side'] = 3  # sell close long
            else:
                request['side'] = 4  # sell open short
        marginMode = None
        marginMode, params = self.handle_margin_mode_and_params('createOrder', params, 'cross')
        request['open_type'] = marginMode
        clientOrderId = self.safe_string(params, 'clientOrderId')
        if clientOrderId is not None:
            params = self.omit(params, 'clientOrderId')
            request['client_order_id'] = clientOrderId
        leverage = self.safe_integer(params, 'leverage', 1)
        params = self.omit(params, ['timeInForce', 'postOnly', 'reduceOnly', 'leverage', 'trailingTriggerPrice', 'trailingPercent', 'triggerPrice', 'stopPrice'])
        request['leverage'] = self.number_to_string(leverage)
        return self.extend(request, params)

    def create_spot_order_request(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
         * @ignore
        create a spot order request
        :see: https://developer-pro.bitmart.com/en/spot/#place-spot-order
        :see: https://developer-pro.bitmart.com/en/spot/#place-margin-order
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of currency you want to trade in units of base currency
        :param float [price]: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.marginMode]: 'cross' or 'isolated'
        :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
            'side': side,
            'type': type,
        }
        timeInForce = self.safe_string(params, 'timeInForce')
        if timeInForce == 'FOK':
            raise InvalidOrder(self.id + ' createOrder() only accepts timeInForce parameter values of IOC or PO')
        mode = self.safe_integer(params, 'mode')  # only for swap
        isMarketOrder = type == 'market'
        postOnly = None
        isExchangeSpecificPo = (type == 'limit_maker') or (mode == 4)
        postOnly, params = self.handle_post_only(isMarketOrder, isExchangeSpecificPo, params)
        params = self.omit(params, ['timeInForce', 'postOnly'])
        ioc = ((timeInForce == 'IOC') or (type == 'ioc'))
        isLimitOrder = (type == 'limit') or postOnly or ioc
        # method = 'privatePostSpotV2SubmitOrder'
        if isLimitOrder:
            request['size'] = self.amount_to_precision(symbol, amount)
            request['price'] = self.price_to_precision(symbol, price)
        elif isMarketOrder:
            # for market buy it requires the amount of quote currency to spend
            if side == 'buy':
                notional = self.safe_string_2(params, 'cost', 'notional')
                params = self.omit(params, 'cost')
                createMarketBuyOrderRequiresPrice = True
                createMarketBuyOrderRequiresPrice, params = self.handle_option_and_params(params, 'createOrder', 'createMarketBuyOrderRequiresPrice', True)
                if createMarketBuyOrderRequiresPrice:
                    if (price is None) and (notional is None):
                        raise InvalidOrder(self.id + ' createOrder() requires the price argument for market buy orders to calculate the total cost to spend(amount * price), alternatively set the createMarketBuyOrderRequiresPrice option or param to False and pass the cost to spend in the amount argument or in the "notional" extra parameter(the exchange-specific behaviour)')
                    else:
                        amountString = self.number_to_string(amount)
                        priceString = self.number_to_string(price)
                        notional = Precise.string_mul(amountString, priceString)
                else:
                    notional = self.number_to_string(amount) if (notional is None) else notional
                request['notional'] = self.decimal_to_precision(notional, TRUNCATE, market['precision']['price'], self.precisionMode)
            elif side == 'sell':
                request['size'] = self.amount_to_precision(symbol, amount)
        if postOnly:
            request['type'] = 'limit_maker'
        if ioc:
            request['type'] = 'ioc'
        clientOrderId = self.safe_string(params, 'clientOrderId')
        if clientOrderId is not None:
            params = self.omit(params, 'clientOrderId')
            request['client_order_id'] = clientOrderId
        return self.extend(request, params)

    def cancel_order(self, id: str, symbol: Str = None, params={}):
        """
        cancels an open order
        :see: https://developer-pro.bitmart.com/en/futures/#cancel-order-signed
        :see: https://developer-pro.bitmart.com/en/spot/#cancel-order-v3-signed
        :see: https://developer-pro.bitmart.com/en/futures/#cancel-plan-order-signed
        :see: https://developer-pro.bitmart.com/en/futures/#cancel-plan-order-signed
        :param str id: order id
        :param str symbol: unified symbol of the market the order was made in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.clientOrderId]: *spot only* the client order id of the order to cancel
        :param boolean [params.stop]: *swap only* whether the order is a stop order
        :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        clientOrderId = self.safe_string_2(params, 'clientOrderId', 'client_order_id')
        if clientOrderId is not None:
            request['client_order_id'] = clientOrderId
        else:
            request['order_id'] = str(id)
        params = self.omit(params, ['clientOrderId'])
        response = None
        if market['spot']:
            response = self.privatePostSpotV3CancelOrder(self.extend(request, params))
        else:
            stop = self.safe_value_2(params, 'stop', 'trigger')
            params = self.omit(params, ['stop', 'trigger'])
            if not stop:
                response = self.privatePostContractPrivateCancelOrder(self.extend(request, params))
            else:
                response = self.privatePostContractPrivateCancelPlanOrder(self.extend(request, params))
        # swap
        # {"code":1000,"message":"Ok","trace":"7f9c94e10f9d4513bc08a7bfc2a5559a.55.16959817848001851"}
        #
        # spot
        #
        #     {
        #         "code": 1000,
        #         "trace":"886fb6ae-456b-4654-b4e0-d681ac05cea1",
        #         "message": "OK",
        #         "data": {
        #             "result": True
        #         }
        #     }
        #
        # spot alternative
        #
        #     {
        #         "code": 1000,
        #         "trace":"886fb6ae-456b-4654-b4e0-d681ac05cea1",
        #         "message": "OK",
        #         "data": True
        #     }
        #
        if market['swap']:
            return response
        data = self.safe_value(response, 'data')
        if data is True:
            return self.safe_order({'id': id}, market)
        succeeded = self.safe_value(data, 'succeed')
        if succeeded is not None:
            id = self.safe_string(succeeded, 0)
            if id is None:
                raise InvalidOrder(self.id + ' cancelOrder() failed to cancel ' + symbol + ' order id ' + id)
        else:
            result = self.safe_value(data, 'result')
            if not result:
                raise InvalidOrder(self.id + ' cancelOrder() ' + symbol + ' order id ' + id + ' is filled or canceled')
        order = self.safe_order({'id': id, 'symbol': market['symbol'], 'info': {}}, market)
        return order

    def cancel_orders(self, ids: List[str], symbol: Str = None, params={}):
        """
        cancel multiple orders
        :see: https://developer-pro.bitmart.com/en/spot/#cancel-batch-order-v4-signed
        :param str[] ids: order ids
        :param str symbol: unified symbol of the market the order was made in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str[] [params.clientOrderIds]: client order ids
        :returns dict: an list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrders() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        if not market['spot']:
            raise NotSupported(self.id + ' cancelOrders() does not support ' + market['type'] + ' orders, only spot orders are accepted')
        clientOrderIds = self.safe_list(params, 'clientOrderIds')
        params = self.omit(params, ['clientOrderIds'])
        request: dict = {
            'symbol': market['id'],
        }
        if clientOrderIds is not None:
            request['clientOrderIds'] = clientOrderIds
        else:
            request['orderIds'] = ids
        response = self.privatePostSpotV4CancelOrders(self.extend(request, params))
        #
        #  {
        #      "message": "OK",
        #      "code": 1000,
        #      "trace": "c4edbce860164203954f7c3c81d60fc6.309.17022669632770001",
        #      "data": {
        #        "successIds": [
        #          "213055379155243012"
        #        ],
        #        "failIds": [],
        #        "totalCount": 1,
        #        "successCount": 1,
        #        "failedCount": 0
        #      }
        #  }
        #
        data = self.safe_dict(response, 'data', {})
        allOrders = []
        successIds = self.safe_list(data, 'successIds', [])
        for i in range(0, len(successIds)):
            id = successIds[i]
            allOrders.append(self.safe_order({'id': id, 'status': 'canceled'}, market))
        failIds = self.safe_list(data, 'failIds', [])
        for i in range(0, len(failIds)):
            id = failIds[i]
            allOrders.append(self.safe_order({'id': id, 'status': 'failed'}, market))
        return allOrders

    def cancel_all_orders(self, symbol: Str = None, params={}):
        """
        cancel all open orders in a market
        :see: https://developer-pro.bitmart.com/en/spot/#cancel-all-orders
        :see: https://developer-pro.bitmart.com/en/futures/#cancel-all-orders-signed
        :param str symbol: unified market symbol of the market to cancel orders in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.side]: *spot only* 'buy' or 'sell'
        :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
        """
        self.load_markets()
        request: dict = {}
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        response = None
        type = None
        type, params = self.handle_market_type_and_params('cancelAllOrders', market, params)
        if type == 'spot':
            response = self.privatePostSpotV1CancelOrders(self.extend(request, params))
        elif type == 'swap':
            if symbol is None:
                raise ArgumentsRequired(self.id + ' cancelAllOrders() requires a symbol argument')
            response = self.privatePostContractPrivateCancelOrders(self.extend(request, params))
        #
        # spot
        #
        #     {
        #         "code": 1000,
        #         "trace":"886fb6ae-456b-4654-b4e0-d681ac05cea1",
        #         "message": "OK",
        #         "data": {}
        #     }
        #
        # swap
        #
        #     {
        #         "code": 1000,
        #         "message": "Ok",
        #         "trace": "7f9c94e10f9d4513bc08a7bfc2a5559a.70.16954131323145323"
        #     }
        #
        return response

    def fetch_orders_by_status(self, status, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrdersByStatus() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        if not market['spot']:
            raise NotSupported(self.id + ' fetchOrdersByStatus() does not support ' + market['type'] + ' orders, only spot orders are accepted')
        request: dict = {
            'symbol': market['id'],
            'offset': 1,  # max offset * limit < 500
            'N': 100,  # max limit is 100
        }
        if status == 'open':
            request['status'] = 9
        elif status == 'closed':
            request['status'] = 6
        elif status == 'canceled':
            request['status'] = 8
        else:
            request['status'] = status
        response = self.privateGetSpotV3Orders(self.extend(request, params))
        #
        # spot
        #
        #     {
        #         "message":"OK",
        #         "code":1000,
        #         "trace":"70e7d427-7436-4fb8-8cdd-97e1f5eadbe9",
        #         "data":{
        #             "current_page":1,
        #             "orders":[
        #                 {
        #                     "order_id":2147601241,
        #                     "symbol":"BTC_USDT",
        #                     "create_time":1591099963000,
        #                     "side":"sell",
        #                     "type":"limit",
        #                     "price":"9000.00",
        #                     "price_avg":"0.00",
        #                     "size":"1.00000",
        #                     "notional":"9000.00000000",
        #                     "filled_notional":"0.00000000",
        #                     "filled_size":"0.00000",
        #                     "status":"4"
        #                 }
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        orders = self.safe_list(data, 'orders', [])
        return self.parse_orders(orders, market, since, limit)

    def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        :see: https://developer-pro.bitmart.com/en/spot/#current-open-orders-v4-signed
        :see: https://developer-pro.bitmart.com/en/futures/#get-all-open-orders-keyed
        :see: https://developer-pro.bitmart.com/en/futures/#get-all-current-plan-orders-keyed
        fetch all unfilled currently open orders
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch open orders for
        :param int [limit]: the maximum number of open order structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param boolean [params.marginMode]: *spot* whether to fetch trades for margin orders or spot orders, defaults to spot orders(only isolated margin orders are supported)
        :param int [params.until]: *spot* the latest time in ms to fetch orders for
        :param str [params.type]: *swap* order type, 'limit' or 'market'
        :param str [params.order_state]: *swap* the order state, 'all' or 'partially_filled', default is 'all'
        :param str [params.orderType]: *swap only* 'limit', 'market', or 'trailing'
        :param boolean [params.trailing]: *swap only* set to True if you want to fetch trailing orders
        :param boolean [params.trigger]: *swap only* set to True if you want to fetch trigger orders
        :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
        """
        self.load_markets()
        market = None
        request: dict = {}
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        if limit is not None:
            request['limit'] = limit
        type = None
        response = None
        type, params = self.handle_market_type_and_params('fetchOpenOrders', market, params)
        if type == 'spot':
            marginMode = None
            marginMode, params = self.handle_margin_mode_and_params('fetchOpenOrders', params)
            if marginMode == 'isolated':
                request['orderMode'] = 'iso_margin'
            if since is not None:
                request['startTime'] = since
            until = self.safe_integer_2(params, 'until', 'endTime')
            if until is not None:
                params = self.omit(params, ['endTime'])
                request['endTime'] = until
            response = self.privatePostSpotV4QueryOpenOrders(self.extend(request, params))
        elif type == 'swap':
            isStop = self.safe_value_2(params, 'stop', 'trigger')
            params = self.omit(params, ['stop', 'trigger'])
            if isStop:
                response = self.privateGetContractPrivateCurrentPlanOrder(self.extend(request, params))
            else:
                trailing = self.safe_bool(params, 'trailing', False)
                orderType = self.safe_string(params, 'orderType')
                params = self.omit(params, ['orderType', 'trailing'])
                if trailing:
                    orderType = 'trailing'
                if orderType is not None:
                    request['type'] = orderType
                response = self.privateGetContractPrivateGetOpenOrders(self.extend(request, params))
        else:
            raise NotSupported(self.id + ' fetchOpenOrders() does not support ' + type + ' orders, only spot and swap orders are accepted')
        #
        # spot
        #
        #     {
        #         "code": 1000,
        #         "message": "success",
        #         "data": [
        #             {
        #                 "orderId": "183299373022163211",
        #                 "clientOrderId": "183299373022163211",
        #                 "symbol": "BTC_USDT",
        #                 "side": "buy",
        #                 "orderMode": "spot",
        #                 "type": "limit",
        #                 "state": "new",
        #                 "price": "25000.00",
        #                 "priceAvg": "0.00",
        #                 "size": "0.00020",
        #                 "filledSize": "0.00000",
        #                 "notional": "5.00000000",
        #                 "filledNotional": "0.00000000",
        #                 "createTime": 1695703703338,
        #                 "updateTime": 1695703703359
        #             }
        #         ],
        #         "trace": "15f11d48e3234c81a2e786cr2e7a38e6.71.16957022303515933"
        #     }
        #
        # swap
        #
        #     {
        #         "code": 1000,
        #         "message": "Ok",
        #         "data": [
        #             {
        #                 "order_id": "230935812485489",
        #                 "client_order_id": "",
        #                 "price": "24000",
        #                 "size": "1",
        #                 "symbol": "BTCUSDT",
        #                 "state": 2,
        #                 "side": 1,
        #                 "type": "limit",
        #                 "leverage": "10",
        #                 "open_type": "isolated",
        #                 "deal_avg_price": "0",
        #                 "deal_size": "0",
        #                 "create_time": 1695702258629,
        #                 "update_time": 1695702258642
        #             }
        #         ],
        #         "trace": "7f9d94g10f9d4513bc08a7rfc3a5559a.71.16957022303515933"
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_orders(data, market, since, limit)

    def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        :see: https://developer-pro.bitmart.com/en/spot/#account-orders-v4-signed
        :see: https://developer-pro.bitmart.com/en/futures/#get-order-history-keyed
        fetches information on multiple closed orders made by the user
        :param str symbol: unified market symbol of the market orders were made in
        :param int [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]: timestamp in ms of the latest entry
        :param str [params.marginMode]: *spot only* 'cross' or 'isolated', for margin trading
        :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
        """
        self.load_markets()
        market = None
        request: dict = {}
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        type = None
        type, params = self.handle_market_type_and_params('fetchClosedOrders', market, params)
        if type != 'spot':
            if symbol is None:
                raise ArgumentsRequired(self.id + ' fetchClosedOrders() requires a symbol argument')
        marginMode = None
        marginMode, params = self.handle_margin_mode_and_params('fetchClosedOrders', params)
        if marginMode == 'isolated':
            request['orderMode'] = 'iso_margin'
        startTimeKey = 'startTime' if (type == 'spot') else 'start_time'
        if since is not None:
            request[startTimeKey] = since
        endTimeKey = 'endTime' if (type == 'spot') else 'end_time'
        until = self.safe_integer_2(params, 'until', endTimeKey)
        if until is not None:
            params = self.omit(params, ['until'])
            request[endTimeKey] = until
        response = None
        if type == 'spot':
            response = self.privatePostSpotV4QueryHistoryOrders(self.extend(request, params))
        else:
            response = self.privateGetContractPrivateOrderHistory(self.extend(request, params))
        data = self.safe_list(response, 'data', [])
        return self.parse_orders(data, market, since, limit)

    def fetch_canceled_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetches information on multiple canceled orders made by the user
        :param str symbol: unified market symbol of the market orders were made in
        :param int [since]: timestamp in ms of the earliest order, default is None
        :param int [limit]: max number of orders to return, default is None
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
        """
        return self.fetch_orders_by_status('canceled', symbol, since, limit, params)

    def fetch_order(self, id: str, symbol: Str = None, params={}):
        """
        fetches information on an order made by the user
        :see: https://developer-pro.bitmart.com/en/spot/#query-order-by-id-v4-signed
        :see: https://developer-pro.bitmart.com/en/spot/#query-order-by-clientorderid-v4-signed
        :see: https://developer-pro.bitmart.com/en/futures/#get-order-detail-keyed
        :param str id: the id of the order
        :param str symbol: unified symbol of the market the order was made in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.clientOrderId]: *spot* fetch the order by client order id instead of order id
        :param str [params.orderType]: *swap only* 'limit', 'market', 'liquidate', 'bankruptcy', 'adl' or 'trailing'
        :param boolean [params.trailing]: *swap only* set to True if you want to fetch a trailing order
        :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        self.load_markets()
        request: dict = {}
        type = None
        market = None
        response = None
        if symbol is not None:
            market = self.market(symbol)
        type, params = self.handle_market_type_and_params('fetchOrder', market, params)
        if type == 'spot':
            clientOrderId = self.safe_string(params, 'clientOrderId')
            if not clientOrderId:
                request['orderId'] = id
            if clientOrderId is not None:
                response = self.privatePostSpotV4QueryClientOrder(self.extend(request, params))
            else:
                response = self.privatePostSpotV4QueryOrder(self.extend(request, params))
        elif type == 'swap':
            if symbol is None:
                raise ArgumentsRequired(self.id + ' fetchOrder() requires a symbol argument')
            trailing = self.safe_bool(params, 'trailing', False)
            orderType = self.safe_string(params, 'orderType')
            params = self.omit(params, ['orderType', 'trailing'])
            if trailing:
                orderType = 'trailing'
            if orderType is not None:
                request['type'] = orderType
            request['symbol'] = market['id']
            request['order_id'] = id
            response = self.privateGetContractPrivateOrder(self.extend(request, params))
        #
        # spot
        #
        #     {
        #         "code": 1000,
        #         "message": "success",
        #         "data": {
        #             "orderId": "183347420821295423",
        #             "clientOrderId": "183347420821295423",
        #             "symbol": "BTC_USDT",
        #             "side": "buy",
        #             "orderMode": "spot",
        #             "type": "limit",
        #             "state": "new",
        #             "price": "24000.00",
        #             "priceAvg": "0.00",
        #             "size": "0.00022",
        #             "filledSize": "0.00000",
        #             "notional": "5.28000000",
        #             "filledNotional": "0.00000000",
        #             "createTime": 1695783014734,
        #             "updateTime": 1695783014762
        #         },
        #         "trace": "ce3e6422c8b44d5fag855348a68693ed.63.14957831547451715"
        #     }
        #
        # swap
        #
        #     {
        #         "code": 1000,
        #         "message": "Ok",
        #         "data": {
        #             "order_id": "230927283405028",
        #             "client_order_id": "",
        #             "price": "23000",
        #             "size": "1",
        #             "symbol": "BTCUSDT",
        #             "state": 2,
        #             "side": 1,
        #             "type": "limit",
        #             "leverage": "10",
        #             "open_type": "isolated",
        #             "deal_avg_price": "0",
        #             "deal_size": "0",
        #             "create_time": 1695783433600,
        #             "update_time": 1695783433613
        #         },
        #         "trace": "4cad855075664097af6ba5257c47605d.63.14957831547451715"
        #     }
        #
        data = self.safe_dict(response, 'data', {})
        return self.parse_order(data, market)

    def fetch_deposit_address(self, code: str, params={}):
        """
        fetch the deposit address for a currency associated with self account
        :see: https://developer-pro.bitmart.com/en/spot/#deposit-address-keyed
        :param str code: unified currency code
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
        """
        self.load_markets()
        currency = self.currency(code)
        request: dict = {
            'currency': currency['id'],
        }
        if code == 'USDT':
            defaultNetworks = self.safe_value(self.options, 'defaultNetworks')
            defaultNetwork = self.safe_string_upper(defaultNetworks, code)
            networks = self.safe_value(self.options, 'networks', {})
            networkInner = self.safe_string_upper(params, 'network', defaultNetwork)  # self line allows the user to specify either ERC20 or ETH
            networkInner = self.safe_string(networks, networkInner, networkInner)  # handle ERC20>ETH alias
            if networkInner is not None:
                request['currency'] = request['currency'] + '-' + networkInner  # when network the currency need to be changed to currency + '-' + network https://developer-pro.bitmart.com/en/account/withdraw_apply.html on the end of page
                params = self.omit(params, 'network')
        response = self.privateGetAccountV1DepositAddress(self.extend(request, params))
        #
        #    {
        #        "message": "OK",
        #        "code": 1000,
        #        "trace": "0e6edd79-f77f-4251-abe5-83ba75d06c1a",
        #        "data": {
        #            currency: 'ETH',
        #            chain: 'Ethereum',
        #            address: '0x99B5EEc2C520f86F0F62F05820d28D05D36EccCf',
        #            address_memo: ''
        #        }
        #    }
        #
        data = self.safe_dict(response, 'data', {})
        return self.parse_deposit_address(data, currency)

    def parse_deposit_address(self, depositAddress, currency=None):
        #
        #    {
        #        currency: 'ETH',
        #        chain: 'Ethereum',
        #        address: '0x99B5EEc2C520f86F0F62F05820d28D05D36EccCf',
        #        address_memo: ''
        #    }
        #
        currencyId = self.safe_string(depositAddress, 'currency')
        address = self.safe_string(depositAddress, 'address')
        chain = self.safe_string(depositAddress, 'chain')
        network = None
        currency = self.safe_currency(currencyId, currency)
        if chain is not None:
            parts = chain.split('-')
            partsLength = len(parts)
            networkId = self.safe_string(parts, partsLength - 1)
            network = self.safe_network_code(networkId, currency)
        self.check_address(address)
        return {
            'info': depositAddress,
            'currency': self.safe_string(currency, 'code'),
            'address': address,
            'tag': self.safe_string(depositAddress, 'address_memo'),
            'network': network,
        }

    def safe_network_code(self, networkId, currency=None):
        name = self.safe_string(currency, 'name')
        if networkId == name:
            code = self.safe_string(currency, 'code')
            return code
        return self.network_id_to_code(networkId)

    def withdraw(self, code: str, amount: float, address: str, tag=None, params={}):
        """
        make a withdrawal
        :param str code: unified currency code
        :param float amount: the amount to withdraw
        :param str address: the address to withdraw to
        :param str tag:
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
        """
        tag, params = self.handle_withdraw_tag_and_params(tag, params)
        self.check_address(address)
        self.load_markets()
        currency = self.currency(code)
        request: dict = {
            'currency': currency['id'],
            'amount': amount,
            'destination': 'To Digital Address',  # To Digital Address, To Binance, To OKEX
            'address': address,
        }
        if tag is not None:
            request['address_memo'] = tag
        if code == 'USDT':
            defaultNetworks = self.safe_value(self.options, 'defaultNetworks')
            defaultNetwork = self.safe_string_upper(defaultNetworks, code)
            networks = self.safe_value(self.options, 'networks', {})
            network = self.safe_string_upper(params, 'network', defaultNetwork)  # self line allows the user to specify either ERC20 or ETH
            network = self.safe_string(networks, network, network)  # handle ERC20>ETH alias
            if network is not None:
                request['currency'] = request['currency'] + '-' + network  # when network the currency need to be changed to currency + '-' + network https://developer-pro.bitmart.com/en/account/withdraw_apply.html on the end of page
                params = self.omit(params, 'network')
        response = self.privatePostAccountV1WithdrawApply(self.extend(request, params))
        #
        #     {
        #         "code": 1000,
        #         "trace":"886fb6ae-456b-4654-b4e0-d681ac05cea1",
        #         "message": "OK",
        #         "data": {
        #             "withdraw_id": "121212"
        #         }
        #     }
        #
        data = self.safe_value(response, 'data')
        transaction = self.parse_transaction(data, currency)
        return self.extend(transaction, {
            'code': code,
            'address': address,
            'tag': tag,
        })

    def fetch_transactions_by_type(self, type, code: Str = None, since: Int = None, limit: Int = None, params={}):
        self.load_markets()
        if limit is None:
            limit = 50  # max 50
        request: dict = {
            'operation_type': type,  # deposit or withdraw
            'offset': 1,
            'N': limit,
        }
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        if code == 'USDT':
            defaultNetworks = self.safe_value(self.options, 'defaultNetworks')
            defaultNetwork = self.safe_string_upper(defaultNetworks, code)
            networks = self.safe_value(self.options, 'networks', {})
            network = self.safe_string_upper(params, 'network', defaultNetwork)  # self line allows the user to specify either ERC20 or ETH
            network = self.safe_string(networks, network, network)  # handle ERC20>ETH alias
            if network is not None:
                request['currency'] += '-' + network  # when network the currency need to be changed to currency + '-' + network https://developer-pro.bitmart.com/en/account/withdraw_apply.html on the end of page
                currency['code'] = request['currency']  # update currency code to filter
                params = self.omit(params, 'network')
        response = self.privateGetAccountV2DepositWithdrawHistory(self.extend(request, params))
        #
        #     {
        #         "message":"OK",
        #         "code":1000,
        #         "trace":"142bf92a-fc50-4689-92b6-590886f90b97",
        #         "data":{
        #             "records":[
        #                 {
        #                     "withdraw_id":"1679952",
        #                     "deposit_id":"",
        #                     "operation_type":"withdraw",
        #                     "currency":"BMX",
        #                     "apply_time":1588867374000,
        #                     "arrival_amount":"59.000000000000",
        #                     "fee":"1.000000000000",
        #                     "status":0,
        #                     "address":"0xe57b69a8776b37860407965B73cdFFBDFe668Bb5",
        #                     "address_memo":"",
        #                     "tx_id":""
        #                 },
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        records = self.safe_list(data, 'records', [])
        return self.parse_transactions(records, currency, since, limit)

    def fetch_deposit(self, id: str, code: Str = None, params={}):
        """
        fetch information on a deposit
        :param str id: deposit id
        :param str code: not used by bitmart fetchDeposit()
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
        """
        self.load_markets()
        request: dict = {
            'id': id,
        }
        response = self.privateGetAccountV1DepositWithdrawDetail(self.extend(request, params))
        #
        #     {
        #         "message":"OK",
        #         "code":1000,
        #         "trace":"f7f74924-14da-42a6-b7f2-d3799dd9a612",
        #         "data":{
        #             "record":{
        #                 "withdraw_id":"",
        #                 "deposit_id":"1679952",
        #                 "operation_type":"deposit",
        #                 "currency":"BMX",
        #                 "apply_time":1588867374000,
        #                 "arrival_amount":"59.000000000000",
        #                 "fee":"1.000000000000",
        #                 "status":0,
        #                 "address":"0xe57b69a8776b37860407965B73cdFFBDFe668Bb5",
        #                 "address_memo":"",
        #                 "tx_id":""
        #             }
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        record = self.safe_dict(data, 'record', {})
        return self.parse_transaction(record)

    def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch all deposits made to an account
        :param str code: unified currency code
        :param int [since]: the earliest time in ms to fetch deposits for
        :param int [limit]: the maximum number of deposits structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
        """
        return self.fetch_transactions_by_type('deposit', code, since, limit, params)

    def fetch_withdrawal(self, id: str, code: Str = None, params={}):
        """
        fetch data on a currency withdrawal via the withdrawal id
        :param str id: withdrawal id
        :param str code: not used by bitmart.fetchWithdrawal
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
        """
        self.load_markets()
        request: dict = {
            'id': id,
        }
        response = self.privateGetAccountV1DepositWithdrawDetail(self.extend(request, params))
        #
        #     {
        #         "message":"OK",
        #         "code":1000,
        #         "trace":"f7f74924-14da-42a6-b7f2-d3799dd9a612",
        #         "data":{
        #             "record":{
        #                 "withdraw_id":"1679952",
        #                 "deposit_id":"",
        #                 "operation_type":"withdraw",
        #                 "currency":"BMX",
        #                 "apply_time":1588867374000,
        #                 "arrival_amount":"59.000000000000",
        #                 "fee":"1.000000000000",
        #                 "status":0,
        #                 "address":"0xe57b69a8776b37860407965B73cdFFBDFe668Bb5",
        #                 "address_memo":"",
        #                 "tx_id":""
        #             }
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        record = self.safe_dict(data, 'record', {})
        return self.parse_transaction(record)

    def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch all withdrawals made from an account
        :param str code: unified currency code
        :param int [since]: the earliest time in ms to fetch withdrawals for
        :param int [limit]: the maximum number of withdrawals structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
        """
        return self.fetch_transactions_by_type('withdraw', code, since, limit, params)

    def parse_transaction_status(self, status: Str):
        statuses: dict = {
            '0': 'pending',  # Create
            '1': 'pending',  # Submitted, waiting for withdrawal
            '2': 'pending',  # Processing
            '3': 'ok',  # Success
            '4': 'canceled',  # Cancel
            '5': 'failed',  # Fail
        }
        return self.safe_string(statuses, status, status)

    def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
        #
        # withdraw
        #
        #     {
        #         "withdraw_id": "121212"
        #     }
        #
        # fetchDeposits, fetchWithdrawals, fetchWithdrawal
        #
        #     {
        #         "withdraw_id":"1679952",
        #         "deposit_id":"",
        #         "operation_type":"withdraw",
        #         "currency":"BMX",
        #         "apply_time":1588867374000,
        #         "arrival_amount":"59.000000000000",
        #         "fee":"1.000000000000",
        #         "status":0,
        #         "address":"0xe57b69a8776b37860407965B73cdFFBDFe668Bb5",
        #         "address_memo":"",
        #         "tx_id":""
        #     }
        #
        id = None
        withdrawId = self.safe_string(transaction, 'withdraw_id')
        depositId = self.safe_string(transaction, 'deposit_id')
        type = None
        if (withdrawId is not None) and (withdrawId != ''):
            type = 'withdraw'
            id = withdrawId
        elif (depositId is not None) and (depositId != ''):
            type = 'deposit'
            id = depositId
        amount = self.safe_number(transaction, 'arrival_amount')
        timestamp = self.safe_integer(transaction, 'apply_time')
        currencyId = self.safe_string(transaction, 'currency')
        code = self.safe_currency_code(currencyId, currency)
        status = self.parse_transaction_status(self.safe_string(transaction, 'status'))
        feeCost = self.safe_number(transaction, 'fee')
        fee = None
        if feeCost is not None:
            fee = {
                'cost': feeCost,
                'currency': code,
            }
        txid = self.safe_string(transaction, 'tx_id')
        address = self.safe_string(transaction, 'address')
        tag = self.safe_string(transaction, 'address_memo')
        return {
            'info': transaction,
            'id': id,
            'currency': code,
            'amount': amount,
            'network': None,
            'address': address,
            'addressFrom': None,
            'addressTo': None,
            'tag': tag,
            'tagFrom': None,
            'tagTo': None,
            'status': status,
            'type': type,
            'updated': None,
            'txid': txid,
            'internal': None,
            'comment': None,
            'timestamp': timestamp if (timestamp != 0) else None,
            'datetime': self.iso8601(timestamp) if (timestamp != 0) else None,
            'fee': fee,
        }

    def repay_isolated_margin(self, symbol: str, code: str, amount, params={}):
        """
        repay borrowed margin and interest
        :see: https://developer-pro.bitmart.com/en/spot/#margin-repay-isolated
        :param str symbol: unified market symbol
        :param str code: unified currency code of the currency to repay
        :param str amount: the amount to repay
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `margin loan structure <https://docs.ccxt.com/#/?id=margin-loan-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        currency = self.currency(code)
        request: dict = {
            'symbol': market['id'],
            'currency': currency['id'],
            'amount': self.currency_to_precision(code, amount),
        }
        response = self.privatePostSpotV1MarginIsolatedRepay(self.extend(request, params))
        #
        #     {
        #         "message": "OK",
        #         "code": 1000,
        #         "trace": "b0a60b4c-e986-4b54-a190-8f7c05ddf685",
        #         "data": {
        #             "repay_id": "2afcc16d99bd4707818c5a355dc89bed"
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        transaction = self.parse_margin_loan(data, currency)
        return self.extend(transaction, {
            'amount': amount,
            'symbol': symbol,
        })

    def borrow_isolated_margin(self, symbol: str, code: str, amount: float, params={}):
        """
        create a loan to borrow margin
        :see: https://developer-pro.bitmart.com/en/spot/#margin-borrow-isolated
        :param str symbol: unified market symbol
        :param str code: unified currency code of the currency to borrow
        :param str amount: the amount to borrow
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `margin loan structure <https://docs.ccxt.com/#/?id=margin-loan-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        currency = self.currency(code)
        request: dict = {
            'symbol': market['id'],
            'currency': currency['id'],
            'amount': self.currency_to_precision(code, amount),
        }
        response = self.privatePostSpotV1MarginIsolatedBorrow(self.extend(request, params))
        #
        #     {
        #         "message": "OK",
        #         "code": 1000,
        #         "trace": "e6fda683-181e-4e78-ac9c-b27c4c8ba035",
        #         "data": {
        #             "borrow_id": "629a7177a4ed4cf09869c6a4343b788c"
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        transaction = self.parse_margin_loan(data, currency)
        return self.extend(transaction, {
            'amount': amount,
            'symbol': symbol,
        })

    def parse_margin_loan(self, info, currency: Currency = None):
        #
        # borrowMargin
        #
        #     {
        #         "borrow_id": "629a7177a4ed4cf09869c6a4343b788c",
        #     }
        #
        # repayMargin
        #
        #     {
        #         "repay_id": "2afcc16d99bd4707818c5a355dc89bed",
        #     }
        #
        return {
            'id': self.safe_string_2(info, 'borrow_id', 'repay_id'),
            'currency': self.safe_currency_code(None, currency),
            'amount': None,
            'symbol': None,
            'timestamp': None,
            'datetime': None,
            'info': info,
        }

    def fetch_isolated_borrow_rate(self, symbol: str, params={}) -> IsolatedBorrowRate:
        """
        fetch the rate of interest to borrow a currency for margin trading
        :see: https://developer-pro.bitmart.com/en/spot/#get-trading-pair-borrowing-rate-and-amount-keyed
        :param str symbol: unified symbol of the market to fetch the borrow rate for
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an `isolated borrow rate structure <https://github.com/ccxt/ccxt/wiki/Manual#isolated-borrow-rate-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        response = self.privateGetSpotV1MarginIsolatedPairs(self.extend(request, params))
        #
        #     {
        #         "message": "OK",
        #         "code": 1000,
        #         "trace": "0985a130-a5ae-4fc1-863f-4704e214f585",
        #         "data": {
        #             "symbols": [
        #                 {
        #                     "symbol": "BTC_USDT",
        #                     "max_leverage": "5",
        #                     "symbol_enabled": True,
        #                     "base": {
        #                         "currency": "BTC",
        #                         "daily_interest": "0.00055000",
        #                         "hourly_interest": "0.00002291",
        #                         "max_borrow_amount": "2.00000000",
        #                         "min_borrow_amount": "0.00000001",
        #                         "borrowable_amount": "0.00670810"
        #                     },
        #                     "quote": {
        #                         "currency": "USDT",
        #                         "daily_interest": "0.00055000",
        #                         "hourly_interest": "0.00002291",
        #                         "max_borrow_amount": "50000.00000000",
        #                         "min_borrow_amount": "0.00000001",
        #                         "borrowable_amount": "135.12575038"
        #                     }
        #                 }
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        symbols = self.safe_value(data, 'symbols', [])
        borrowRate = self.safe_value(symbols, 0)
        return self.parse_isolated_borrow_rate(borrowRate, market)

    def parse_isolated_borrow_rate(self, info, market: Market = None) -> IsolatedBorrowRate:
        #
        #     {
        #         "symbol": "BTC_USDT",
        #         "max_leverage": "5",
        #         "symbol_enabled": True,
        #         "base": {
        #             "currency": "BTC",
        #             "daily_interest": "0.00055000",
        #             "hourly_interest": "0.00002291",
        #             "max_borrow_amount": "2.00000000",
        #             "min_borrow_amount": "0.00000001",
        #             "borrowable_amount": "0.00670810"
        #         },
        #         "quote": {
        #             "currency": "USDT",
        #             "daily_interest": "0.00055000",
        #             "hourly_interest": "0.00002291",
        #             "max_borrow_amount": "50000.00000000",
        #             "min_borrow_amount": "0.00000001",
        #             "borrowable_amount": "135.12575038"
        #         }
        #     }
        #
        marketId = self.safe_string(info, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        baseData = self.safe_value(info, 'base', {})
        quoteData = self.safe_value(info, 'quote', {})
        baseId = self.safe_string(baseData, 'currency')
        quoteId = self.safe_string(quoteData, 'currency')
        return {
            'symbol': symbol,
            'base': self.safe_currency_code(baseId),
            'baseRate': self.safe_number(baseData, 'hourly_interest'),
            'quote': self.safe_currency_code(quoteId),
            'quoteRate': self.safe_number(quoteData, 'hourly_interest'),
            'period': 3600000,  # 1-Hour
            'timestamp': None,
            'datetime': None,
            'info': info,
        }

    def fetch_isolated_borrow_rates(self, params={}) -> IsolatedBorrowRates:
        """
        fetch the borrow interest rates of all currencies, currently only works for isolated margin
        :see: https://developer-pro.bitmart.com/en/spot/#get-trading-pair-borrowing-rate-and-amount-keyed
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a list of `isolated borrow rate structures <https://docs.ccxt.com/#/?id=isolated-borrow-rate-structure>`
        """
        self.load_markets()
        response = self.privateGetSpotV1MarginIsolatedPairs(params)
        #
        #     {
        #         "message": "OK",
        #         "code": 1000,
        #         "trace": "0985a130-a5ae-4fc1-863f-4704e214f585",
        #         "data": {
        #             "symbols": [
        #                 {
        #                     "symbol": "BTC_USDT",
        #                     "max_leverage": "5",
        #                     "symbol_enabled": True,
        #                     "base": {
        #                         "currency": "BTC",
        #                         "daily_interest": "0.00055000",
        #                         "hourly_interest": "0.00002291",
        #                         "max_borrow_amount": "2.00000000",
        #                         "min_borrow_amount": "0.00000001",
        #                         "borrowable_amount": "0.00670810"
        #                     },
        #                     "quote": {
        #                         "currency": "USDT",
        #                         "daily_interest": "0.00055000",
        #                         "hourly_interest": "0.00002291",
        #                         "max_borrow_amount": "50000.00000000",
        #                         "min_borrow_amount": "0.00000001",
        #                         "borrowable_amount": "135.12575038"
        #                     }
        #                 }
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        symbols = self.safe_value(data, 'symbols', [])
        return self.parse_isolated_borrow_rates(symbols)

    def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
        """
        transfer currency internally between wallets on the same account, currently only supports transfer between spot and margin
        :see: https://developer-pro.bitmart.com/en/spot/#margin-asset-transfer-signed
        :see: https://developer-pro.bitmart.com/en/futures/#transfer-signed
        :param str code: unified currency code
        :param float amount: amount to transfer
        :param str fromAccount: account to transfer from
        :param str toAccount: account to transfer to
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `transfer structure <https://docs.ccxt.com/#/?id=transfer-structure>`
        """
        self.load_markets()
        currency = self.currency(code)
        amountToPrecision = self.currency_to_precision(code, amount)
        request: dict = {
            'amount': amountToPrecision,
            'currency': currency['id'],
        }
        fromId = self.convert_type_to_account(fromAccount)
        toId = self.convert_type_to_account(toAccount)
        if fromAccount == 'spot':
            if toAccount == 'margin':
                request['side'] = 'in'
                request['symbol'] = toId
            elif toAccount == 'swap':
                request['type'] = 'spot_to_contract'
        elif toAccount == 'spot':
            if fromAccount == 'margin':
                request['side'] = 'out'
                request['symbol'] = fromId
            elif fromAccount == 'swap':
                request['type'] = 'contract_to_spot'
        else:
            raise ArgumentsRequired(self.id + ' transfer() requires either fromAccount or toAccount to be spot')
        response = None
        if (fromAccount == 'margin') or (toAccount == 'margin'):
            response = self.privatePostSpotV1MarginIsolatedTransfer(self.extend(request, params))
        elif (fromAccount == 'swap') or (toAccount == 'swap'):
            response = self.privatePostAccountV1TransferContract(self.extend(request, params))
        #
        # margin
        #
        #     {
        #         "message": "OK",
        #         "code": 1000,
        #         "trace": "b26cecec-ef5a-47d9-9531-2bd3911d3d55",
        #         "data": {
        #             "transfer_id": "ca90d97a621e47d49774f19af6b029f5"
        #         }
        #     }
        #
        # swap
        #
        #     {
        #         "message": "OK",
        #         "code": 1000,
        #         "trace": "4cad858074667097ac6ba5257c57305d.68.16953302431189455",
        #         "data": {
        #             "currency": "USDT",
        #             "amount": "5"
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        return self.extend(self.parse_transfer(data, currency), {
            'status': self.parse_transfer_status(self.safe_string_2(response, 'code', 'message')),
        })

    def parse_transfer_status(self, status: Str) -> Str:
        statuses: dict = {
            '1000': 'ok',
            'OK': 'ok',
            'FINISHED': 'ok',
        }
        return self.safe_string(statuses, status, status)

    def parse_transfer_to_account(self, type):
        types: dict = {
            'contract_to_spot': 'spot',
            'spot_to_contract': 'swap',
        }
        return self.safe_string(types, type, type)

    def parse_transfer_from_account(self, type):
        types: dict = {
            'contract_to_spot': 'swap',
            'spot_to_contract': 'spot',
        }
        return self.safe_string(types, type, type)

    def parse_transfer(self, transfer: dict, currency: Currency = None) -> TransferEntry:
        #
        # margin
        #
        #     {
        #         "transfer_id": "ca90d97a621e47d49774f19af6b029f5"
        #     }
        #
        # swap
        #
        #     {
        #         "currency": "USDT",
        #         "amount": "5"
        #     }
        #
        # fetchTransfers
        #
        #     {
        #         "transfer_id": "902463535961567232",
        #         "currency": "USDT",
        #         "amount": "5",
        #         "type": "contract_to_spot",
        #         "state": "FINISHED",
        #         "timestamp": 1695330539565
        #     }
        #
        currencyId = self.safe_string(transfer, 'currency')
        timestamp = self.safe_integer(transfer, 'timestamp')
        return {
            'id': self.safe_string(transfer, 'transfer_id'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'currency': self.safe_currency_code(currencyId, currency),
            'amount': self.safe_number(transfer, 'amount'),
            'fromAccount': self.parse_transfer_from_account(self.safe_string(transfer, 'type')),
            'toAccount': self.parse_transfer_to_account(self.safe_string(transfer, 'type')),
            'status': self.parse_transfer_status(self.safe_string(transfer, 'state')),
        }

    def fetch_transfers(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> TransferEntries:
        """
        fetch a history of internal transfers made on an account, only transfers between spot and swap are supported
        :see: https://developer-pro.bitmart.com/en/futures/#get-transfer-list-signed
        :param str code: unified currency code of the currency transferred
        :param int [since]: the earliest time in ms to fetch transfers for
        :param int [limit]: the maximum number of transfer structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.page]: the required number of pages, default is 1, max is 1000
        :param int [params.until]: the latest time in ms to fetch transfers for
        :returns dict[]: a list of `transfer structures <https://docs.ccxt.com/#/?id=transfer-structure>`
        """
        self.load_markets()
        if limit is None:
            limit = 10
        request: dict = {
            'page': self.safe_integer(params, 'page', 1),  # default is 1, max is 1000
            'limit': limit,  # default is 10, max is 100
        }
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        if since is not None:
            request['time_start'] = since
        if limit is not None:
            request['limit'] = limit
        until = self.safe_integer(params, 'until')  # unified in milliseconds
        endTime = self.safe_integer(params, 'time_end', until)  # exchange-specific in milliseconds
        params = self.omit(params, ['until'])
        if endTime is not None:
            request['time_end'] = endTime
        response = self.privatePostAccountV1TransferContractList(self.extend(request, params))
        #
        #     {
        #         "message": "OK",
        #         "code": 1000,
        #         "trace": "7f9d93e10f9g4513bc08a7btc2a5559a.69.16953325693032193",
        #         "data": {
        #             "records": [
        #                 {
        #                     "transfer_id": "902463535961567232",
        #                     "currency": "USDT",
        #                     "amount": "5",
        #                     "type": "contract_to_spot",
        #                     "state": "FINISHED",
        #                     "timestamp": 1695330539565
        #                 },
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        records = self.safe_list(data, 'records', [])
        return self.parse_transfers(records, currency, since, limit)

    def fetch_borrow_interest(self, code: Str = None, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetch the interest owed by the user for borrowing currency for margin trading
        :see: https://developer-pro.bitmart.com/en/spot/#get-borrow-record-isolated
        :param str code: unified currency code
        :param str symbol: unified market symbol when fetch interest in isolated markets
        :param int [since]: the earliest time in ms to fetch borrrow interest for
        :param int [limit]: the maximum number of structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `borrow interest structures <https://docs.ccxt.com/#/?id=borrow-interest-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchBorrowInterest() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        if limit is not None:
            request['N'] = limit
        if since is not None:
            request['start_time'] = since
        response = self.privateGetSpotV1MarginIsolatedBorrowRecord(self.extend(request, params))
        #
        #     {
        #         "message": "OK",
        #         "code": 1000,
        #         "trace": "8ea27a2a-4aba-49fa-961d-43a0137b0ef3",
        #         "data": {
        #             "records": [
        #                 {
        #                     "borrow_id": "1659045283903rNvJnuRTJNL5J53n",
        #                     "symbol": "BTC_USDT",
        #                     "currency": "USDT",
        #                     "borrow_amount": "100.00000000",
        #                     "daily_interest": "0.00055000",
        #                     "hourly_interest": "0.00002291",
        #                     "interest_amount": "0.00229166",
        #                     "create_time": 1659045284000
        #                 },
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        rows = self.safe_value(data, 'records', [])
        interest = self.parse_borrow_interests(rows, market)
        return self.filter_by_currency_since_limit(interest, code, since, limit)

    def parse_borrow_interest(self, info: dict, market: Market = None):
        #
        #     {
        #         "borrow_id": "1657664327844Lk5eJJugXmdHHZoe",
        #         "symbol": "BTC_USDT",
        #         "currency": "USDT",
        #         "borrow_amount": "20.00000000",
        #         "daily_interest": "0.00055000",
        #         "hourly_interest": "0.00002291",
        #         "interest_amount": "0.00045833",
        #         "create_time": 1657664329000
        #     }
        #
        marketId = self.safe_string(info, 'symbol')
        market = self.safe_market(marketId, market)
        timestamp = self.safe_integer(info, 'create_time')
        return {
            'symbol': self.safe_string(market, 'symbol'),
            'marginMode': 'isolated',
            'currency': self.safe_currency_code(self.safe_string(info, 'currency')),
            'interest': self.safe_number(info, 'interest_amount'),
            'interestRate': self.safe_number(info, 'hourly_interest'),
            'amountBorrowed': self.safe_number(info, 'borrow_amount'),
            'timestamp': timestamp,  # borrow creation time
            'datetime': self.iso8601(timestamp),
            'info': info,
        }

    def fetch_open_interest(self, symbol: str, params={}):
        """
        Retrieves the open interest of a currency
        :see: https://developer-pro.bitmart.com/en/futures/#get-futures-openinterest
        :param str symbol: Unified CCXT market symbol
        :param dict [params]: exchange specific parameters
        :returns dict} an open interest structure{@link https://docs.ccxt.com/#/?id=open-interest-structure:
        """
        self.load_markets()
        market = self.market(symbol)
        if not market['contract']:
            raise BadRequest(self.id + ' fetchOpenInterest() supports contract markets only')
        request: dict = {
            'symbol': market['id'],
        }
        response = self.publicGetContractPublicOpenInterest(self.extend(request, params))
        #
        #     {
        #         "code": 1000,
        #         "message": "Ok",
        #         "data": {
        #             "timestamp": 1694657502415,
        #             "symbol": "BTCUSDT",
        #             "open_interest": "265231.721368593081729069",
        #             "open_interest_value": "7006353.83988919"
        #         },
        #         "trace": "7f9c94e10f9d4513bc08a7bfc2a5559a.72.16946575108274991"
        #     }
        #
        data = self.safe_dict(response, 'data', {})
        return self.parse_open_interest(data, market)

    def parse_open_interest(self, interest, market: Market = None):
        #
        #     {
        #         "timestamp": 1694657502415,
        #         "symbol": "BTCUSDT",
        #         "open_interest": "265231.721368593081729069",
        #         "open_interest_value": "7006353.83988919"
        #     }
        #
        timestamp = self.safe_integer(interest, 'timestamp')
        id = self.safe_string(interest, 'symbol')
        return self.safe_open_interest({
            'symbol': self.safe_symbol(id, market),
            'openInterestAmount': self.safe_number(interest, 'open_interest'),
            'openInterestValue': self.safe_number(interest, 'open_interest_value'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'info': interest,
        }, market)

    def set_leverage(self, leverage: Int, symbol: Str = None, params={}):
        """
        set the level of leverage for a market
        :see: https://developer-pro.bitmart.com/en/futures/#submit-leverage-signed
        :param float leverage: the rate of leverage
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.marginMode]: 'isolated' or 'cross'
        :returns dict: response from the exchange
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' setLeverage() requires a symbol argument')
        marginMode = None
        marginMode, params = self.handle_margin_mode_and_params('setLeverage', params)
        self.check_required_argument('setLeverage', marginMode, 'marginMode', ['isolated', 'cross'])
        self.load_markets()
        market = self.market(symbol)
        if not market['swap']:
            raise BadSymbol(self.id + ' setLeverage() supports swap contracts only')
        request: dict = {
            'symbol': market['id'],
            'leverage': str(leverage),
            'open_type': marginMode,
        }
        return self.privatePostContractPrivateSubmitLeverage(self.extend(request, params))

    def fetch_funding_rate(self, symbol: str, params={}):
        """
        fetch the current funding rate
        :see: https://developer-pro.bitmart.com/en/futures/#get-current-funding-rate
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `funding rate structure <https://docs.ccxt.com/#/?id=funding-rate-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        if not market['swap']:
            raise BadSymbol(self.id + ' fetchFundingRate() supports swap contracts only')
        request: dict = {
            'symbol': market['id'],
        }
        response = self.publicGetContractPublicFundingRate(self.extend(request, params))
        #
        #     {
        #         "code": 1000,
        #         "message": "Ok",
        #         "data": {
        #             "timestamp": 1695184410697,
        #             "symbol": "BTCUSDT",
        #             "rate_value": "-0.00002614",
        #             "expected_rate": "-0.00002"
        #         },
        #         "trace": "4cad855074654097ac7ba5257c47305d.54.16951844206655589"
        #     }
        #
        data = self.safe_value(response, 'data', {})
        return self.parse_funding_rate(data, market)

    def parse_funding_rate(self, contract, market: Market = None):
        #
        #     {
        #         "timestamp": 1695184410697,
        #         "symbol": "BTCUSDT",
        #         "rate_value": "-0.00002614",
        #         "expected_rate": "-0.00002"
        #     }
        #
        marketId = self.safe_string(contract, 'symbol')
        timestamp = self.safe_integer(contract, 'timestamp')
        return {
            'info': contract,
            'symbol': self.safe_symbol(marketId, market),
            'markPrice': None,
            'indexPrice': None,
            'interestRate': None,
            'estimatedSettlePrice': None,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'fundingRate': self.safe_number(contract, 'expected_rate'),
            'fundingTimestamp': None,
            'fundingDatetime': None,
            'nextFundingRate': None,
            'nextFundingTimestamp': None,
            'nextFundingDatetime': None,
            'previousFundingRate': self.safe_number(contract, 'rate_value'),
            'previousFundingTimestamp': None,
            'previousFundingDatetime': None,
        }

    def fetch_position(self, symbol: str, params={}):
        """
        fetch data on a single open contract trade position
        :see: https://developer-pro.bitmart.com/en/futures/#get-current-position-keyed
        :param str symbol: unified market symbol of the market the position is held in
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `position structure <https://docs.ccxt.com/#/?id=position-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        response = self.privateGetContractPrivatePosition(self.extend(request, params))
        #
        #     {
        #         "code": 1000,
        #         "message": "Ok",
        #         "data": [
        #             {
        #                 "symbol": "BTCUSDT",
        #                 "leverage": "10",
        #                 "timestamp": 1696392515269,
        #                 "current_fee": "0.0014250028",
        #                 "open_timestamp": 1696392256998,
        #                 "current_value": "27.4039",
        #                 "mark_price": "27.4039",
        #                 "position_value": "27.4079",
        #                 "position_cross": "3.75723474",
        #                 "maintenance_margin": "0.1370395",
        #                 "close_vol": "0",
        #                 "close_avg_price": "0",
        #                 "open_avg_price": "27407.9",
        #                 "entry_price": "27407.9",
        #                 "current_amount": "1",
        #                 "unrealized_value": "-0.004",
        #                 "realized_value": "-0.01644474",
        #                 "position_type": 1
        #             }
        #         ],
        #         "trace":"4cad855074664097ac5ba5257c47305d.67.16963925142065945"
        #     }
        #
        data = self.safe_value(response, 'data', [])
        first = self.safe_dict(data, 0, {})
        return self.parse_position(first, market)

    def fetch_positions(self, symbols: Strings = None, params={}):
        """
        fetch all open contract positions
        :see: https://developer-pro.bitmart.com/en/futures/#get-current-position-keyed
        :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 structures <https://docs.ccxt.com/#/?id=position-structure>`
        """
        self.load_markets()
        market = None
        symbolsLength = None
        if symbols is not None:
            symbolsLength = len(symbols)
            first = self.safe_string(symbols, 0)
            market = self.market(first)
        request: dict = {}
        if symbolsLength == 1:
            # only supports symbols or sending one symbol
            request['symbol'] = market['id']
        response = self.privateGetContractPrivatePosition(self.extend(request, params))
        #
        #     {
        #         "code": 1000,
        #         "message": "Ok",
        #         "data": [
        #             {
        #                 "symbol": "BTCUSDT",
        #                 "leverage": "10",
        #                 "timestamp": 1696392515269,
        #                 "current_fee": "0.0014250028",
        #                 "open_timestamp": 1696392256998,
        #                 "current_value": "27.4039",
        #                 "mark_price": "27.4039",
        #                 "position_value": "27.4079",
        #                 "position_cross": "3.75723474",
        #                 "maintenance_margin": "0.1370395",
        #                 "close_vol": "0",
        #                 "close_avg_price": "0",
        #                 "open_avg_price": "27407.9",
        #                 "entry_price": "27407.9",
        #                 "current_amount": "1",
        #                 "unrealized_value": "-0.004",
        #                 "realized_value": "-0.01644474",
        #                 "position_type": 1
        #             },
        #         ],
        #         "trace":"4cad855074664097ac5ba5257c47305d.67.16963925142065945"
        #     }
        #
        positions = self.safe_value(response, 'data', [])
        result = []
        for i in range(0, len(positions)):
            result.append(self.parse_position(positions[i]))
        symbols = self.market_symbols(symbols)
        return self.filter_by_array_positions(result, 'symbol', symbols, False)

    def parse_position(self, position: dict, market: Market = None):
        #
        #     {
        #         "symbol": "BTCUSDT",
        #         "leverage": "10",
        #         "timestamp": 1696392515269,
        #         "current_fee": "0.0014250028",
        #         "open_timestamp": 1696392256998,
        #         "current_value": "27.4039",
        #         "mark_price": "27.4039",
        #         "position_value": "27.4079",
        #         "position_cross": "3.75723474",
        #         "maintenance_margin": "0.1370395",
        #         "close_vol": "0",
        #         "close_avg_price": "0",
        #         "open_avg_price": "27407.9",
        #         "entry_price": "27407.9",
        #         "current_amount": "1",
        #         "unrealized_value": "-0.004",
        #         "realized_value": "-0.01644474",
        #         "position_type": 1
        #     }
        #
        marketId = self.safe_string(position, 'symbol')
        market = self.safe_market(marketId, market)
        symbol = market['symbol']
        timestamp = self.safe_integer(position, 'timestamp')
        side = self.safe_integer(position, 'position_type')
        maintenanceMargin = self.safe_string(position, 'maintenance_margin')
        notional = self.safe_string(position, 'current_value')
        collateral = self.safe_string(position, 'position_cross')
        maintenanceMarginPercentage = Precise.string_div(maintenanceMargin, notional)
        marginRatio = Precise.string_div(maintenanceMargin, collateral)
        return self.safe_position({
            'info': position,
            'id': None,
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastUpdateTimestamp': None,
            'hedged': None,
            'side': 'long' if (side == 1) else 'short',
            'contracts': self.safe_number(position, 'current_amount'),
            'contractSize': self.safe_number(market, 'contractSize'),
            'entryPrice': self.safe_number(position, 'entry_price'),
            'markPrice': self.safe_number(position, 'mark_price'),
            'lastPrice': None,
            'notional': self.parse_number(notional),
            'leverage': self.safe_number(position, 'leverage'),
            'collateral': self.parse_number(collateral),
            'initialMargin': None,
            'initialMarginPercentage': None,
            'maintenanceMargin': self.parse_number(maintenanceMargin),
            'maintenanceMarginPercentage': self.parse_number(maintenanceMarginPercentage),
            'unrealizedPnl': self.safe_number(position, 'unrealized_value'),
            'realizedPnl': self.safe_number(position, 'realized_value'),
            'liquidationPrice': None,
            'marginMode': None,
            'percentage': None,
            'marginRatio': self.parse_number(marginRatio),
            'stopLossPrice': None,
            'takeProfitPrice': None,
        })

    def fetch_my_liquidations(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        retrieves the users liquidated positions
        :see: https://developer-pro.bitmart.com/en/futures/#get-order-history-keyed
        :param str symbol: unified CCXT market symbol
        :param int [since]: the earliest time in ms to fetch liquidations for
        :param int [limit]: the maximum number of liquidation structures to retrieve
        :param dict [params]: exchange specific parameters for the bitmart api endpoint
        :param int [params.until]: timestamp in ms of the latest liquidation
        :returns dict: an array of `liquidation structures <https://docs.ccxt.com/#/?id=liquidation-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchMyLiquidations() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        if not market['swap']:
            raise NotSupported(self.id + ' fetchMyLiquidations() supports swap markets only')
        request: dict = {
            'symbol': market['id'],
        }
        if since is not None:
            request['start_time'] = since
        request, params = self.handle_until_option('end_time', request, params)
        response = self.privateGetContractPrivateOrderHistory(self.extend(request, params))
        #
        #     {
        #         "code": 1000,
        #         "message": "Ok",
        #         "data": [
        #             {
        #                 "order_id": "231007865458273",
        #                 "client_order_id": "",
        #                 "price": "27407.9",
        #                 "size": "1",
        #                 "symbol": "BTCUSDT",
        #                 "state": 4,
        #                 "side": 3,
        #                 "type": "liquidate",
        #                 "leverage": "10",
        #                 "open_type": "isolated",
        #                 "deal_avg_price": "27422.6",
        #                 "deal_size": "1",
        #                 "create_time": 1696405864011,
        #                 "update_time": 1696405864045
        #             },
        #         ],
        #         "trace": "4cad855074664097ac6ba4257c47305d.71.16965658195443021"
        #     }
        #
        data = self.safe_value(response, 'data', [])
        result = []
        for i in range(0, len(data)):
            entry = data[i]
            checkLiquidation = self.safe_string(entry, 'type')
            if checkLiquidation == 'liquidate':
                result.append(entry)
        return self.parse_liquidations(result, market, since, limit)

    def parse_liquidation(self, liquidation, market: Market = None):
        #
        #     {
        #         "order_id": "231007865458273",
        #         "client_order_id": "",
        #         "price": "27407.9",
        #         "size": "1",
        #         "symbol": "BTCUSDT",
        #         "state": 4,
        #         "side": 3,
        #         "type": "market",
        #         "leverage": "10",
        #         "open_type": "isolated",
        #         "deal_avg_price": "27422.6",
        #         "deal_size": "1",
        #         "create_time": 1696405864011,
        #         "update_time": 1696405864045
        #     }
        #
        marketId = self.safe_string(liquidation, 'symbol')
        timestamp = self.safe_integer(liquidation, 'update_time')
        contractsString = self.safe_string(liquidation, 'deal_size')
        contractSizeString = self.safe_string(market, 'contractSize')
        priceString = self.safe_string(liquidation, 'deal_avg_price')
        baseValueString = Precise.string_mul(contractsString, contractSizeString)
        quoteValueString = Precise.string_mul(baseValueString, priceString)
        return self.safe_liquidation({
            'info': liquidation,
            'symbol': self.safe_symbol(marketId, market),
            'contracts': self.parse_number(contractsString),
            'contractSize': self.parse_number(contractSizeString),
            'price': self.parse_number(priceString),
            'baseValue': self.parse_number(baseValueString),
            'quoteValue': self.parse_number(quoteValueString),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
        })

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

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        baseUrl = self.implode_hostname(self.urls['api']['rest'])
        url = baseUrl + '/' + self.implode_params(path, params)
        query = self.omit(params, self.extract_params(path))
        queryString = ''
        getOrDelete = (method == 'GET') or (method == 'DELETE')
        if getOrDelete:
            if query:
                queryString = self.urlencode(query)
                url += '?' + queryString
        if api == 'private':
            self.check_required_credentials()
            timestamp = str(self.milliseconds())
            brokerId = self.safe_string(self.options, 'brokerId', 'CCXTxBitmart000')
            headers = {
                'X-BM-KEY': self.apiKey,
                'X-BM-TIMESTAMP': timestamp,
                'X-BM-BROKER-ID': brokerId,
                'Content-Type': 'application/json',
            }
            if not getOrDelete:
                body = self.json(query)
                queryString = body
            auth = timestamp + '#' + self.uid + '#' + queryString
            signature = self.hmac(self.encode(auth), self.encode(self.secret), hashlib.sha256)
            headers['X-BM-SIGN'] = signature
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    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
        #
        # spot
        #
        #     {"message":"Bad Request [to is empty]","code":50000,"trace":"f9d46e1b-4edb-4d07-a06e-4895fb2fc8fc","data":{}}
        #     {"message":"Bad Request [from is empty]","code":50000,"trace":"579986f7-c93a-4559-926b-06ba9fa79d76","data":{}}
        #     {"message":"Kline size over 500","code":50004,"trace":"d625caa8-e8ca-4bd2-b77c-958776965819","data":{}}
        #     {"message":"Balance not enough","code":50020,"trace":"7c709d6a-3292-462c-98c5-32362540aeef","data":{}}
        #
        # contract
        #
        #     {"errno":"OK","message":"INVALID_PARAMETER","code":49998,"trace":"eb5ebb54-23cd-4de2-9064-e090b6c3b2e3","data":null}
        #
        message = self.safe_string_lower(response, 'message')
        isErrorMessage = (message is not None) and (message != 'ok') and (message != 'success')
        errorCode = self.safe_string(response, 'code')
        isErrorCode = (errorCode is not None) and (errorCode != '1000')
        if isErrorCode or isErrorMessage:
            feedback = self.id + ' ' + body
            self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
            self.throw_broadly_matched_exception(self.exceptions['broad'], errorCode, feedback)
            self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
            self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
            raise ExchangeError(feedback)  # unknown message
        return None
