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

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

from ccxt.async_support.base.exchange import Exchange
from ccxt.abstract.bitmart import ImplicitAPI
import hashlib
from ccxt.base.types import Any, Balances, BorrowInterest, Currencies, Currency, DepositAddress, FundingHistory, Int, IsolatedBorrowRate, IsolatedBorrowRates, LedgerEntry, Market, Num, Order, OrderBook, OrderRequest, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, FundingRate, Trade, TradingFeeInterface, Transaction, MarketInterface, TransferEntry
from typing import List
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import AuthenticationError
from ccxt.base.errors import PermissionDenied
from ccxt.base.errors import AccountSuspended
from ccxt.base.errors import ArgumentsRequired
from ccxt.base.errors import BadRequest
from ccxt.base.errors import BadSymbol
from ccxt.base.errors import InsufficientFunds
from ccxt.base.errors import 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) -> Any:
        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,
                'createReduceOnlyOrder': 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': True,
                'fetchFundingRate': True,
                'fetchFundingRateHistory': False,
                'fetchFundingRates': False,
                'fetchIsolatedBorrowRate': True,
                'fetchIsolatedBorrowRates': True,
                'fetchLedger': True,
                'fetchLiquidations': False,
                'fetchMarginMode': False,
                'fetchMarkets': True,
                'fetchMarkOHLCV': True,
                'fetchMyLiquidations': True,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenInterest': True,
                'fetchOpenInterestHistory': False,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrders': False,
                'fetchOrderTrades': True,
                'fetchPosition': True,
                'fetchPositionMode': True,
                'fetchPositions': True,
                'fetchStatus': True,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': True,
                'fetchTrades': True,
                'fetchTradingFee': True,
                'fetchTradingFees': False,
                'fetchTransactionFee': True,
                'fetchTransactionFees': False,
                'fetchTransfer': False,
                'fetchTransfers': True,
                'fetchWithdrawAddresses': True,
                'fetchWithdrawAddressesByNetwork': False,
                'fetchWithdrawal': True,
                'fetchWithdrawals': True,
                'reduceMargin': False,
                'repayCrossMargin': False,
                'repayIsolatedMargin': True,
                'setLeverage': True,
                'setMarginMode': False,
                'setPositionMode': True,
                'transfer': True,
                'withdraw': True,
            },
            'hostname': 'bitmart.com',  # bitmart.info, bitmart.news for Hong Kong users
            'urls': {
                'logo': 'https://github.com/user-attachments/assets/0623e9c4-f50e-48c9-82bd-65c3908c3a14',
                'api': {
                    'spot': 'https://api-cloud.{hostname}',
                    'swap': 'https://api-cloud-v2.{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/funding-rate-history': 30,
                        'contract/public/kline': 6,  # should be 5 but errors
                        'account/v1/currencies': 30,
                        'contract/public/markprice-kline': 5,  # 6 times per 1 second
                    },
                },
                '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,
                        'account/v1/withdraw/address/list': 30,  # 2 times per 2 seconds
                        # 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/position-v2': 10,
                        'contract/private/get-open-orders': 1.2,
                        'contract/private/current-plan-order': 1.2,
                        'contract/private/trades': 10,
                        'contract/private/position-risk': 10,
                        'contract/private/affilate/rebate-list': 10,
                        'contract/private/affilate/trade-list': 10,
                        'contract/private/transaction-history': 10,
                        'contract/private/get-position-mode': 1,
                    },
                    '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/cancel_all': 90,
                        '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.5,  # 20 times per second
                        '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,
                        'contract/private/submit-tp-sl-order': 2.5,
                        'contract/private/modify-plan-order': 2.5,
                        'contract/private/modify-preset-plan-order': 2.5,
                        'contract/private/modify-limit-order': 2.5,
                        'contract/private/modify-tp-sl-order': 2.5,
                        'contract/private/submit-trail-order': 2.5,  # weight is not provided by the exchange, is set order
                        'contract/private/cancel-trail-order': 1.5,  # weight is not provided by the exchange, is set order
                        'contract/private/set-position-mode': 1,
                    },
                },
            },
            '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': InvalidOrder,  # 403, ClientOrderId only allows a combination of numbers and letters
                    '40049': InvalidOrder,  # 403, The maximum length of clientOrderId cannot exceed 32
                    '40050': InvalidOrder,  # 403, Client OrderId duplicated with existing orders
                },
                'broad': {
                    'You contract account available balance not enough': InsufficientFunds,
                    'you contract account available balance not enough': InsufficientFunds,
                },
            },
            'commonCurrencies': {
                '$GM': 'GOLDMINER',
                '$HERO': 'Step Hero',
                '$PAC': 'PAC',
                'BP': 'BEYOND',
                'GDT': 'Gorilla Diamond',
                'GLD': 'Goldario',
                'MVP': 'MVP Coin',
                'TRU': 'Truebit',  # conflict with TrueFi
            },
            'options': {
                'defaultNetworks': {
                    'USDT': 'TRC20',
                    'BTC': 'BTC',
                    'ETH': 'ERC20',
                },
                'timeDifference': 0,  # the difference between system clock and exchange clock
                'adjustForTimeDifference': False,  # controls the adjustment logic upon instantiation
                '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',
                    'HRC20': 'HECO',
                    'XDC': 'XDC',
                    'ONE': 'ONE',
                    'LAT': 'LAT',
                    'CSPR': 'Casper',
                    'ICP': 'Computer',
                    'XTZ': 'XTZ',
                    'MINA': 'MINA',
                    'BEP20': 'BSC_BNB',
                    '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),
                },
                'networksById': {
                    'ETH': 'ERC20',
                    'Ethereum': 'ERC20',
                    'USDT': 'OMNI',  # the default USDT network for bitmart is OMNI
                    'Bitcoin': 'BTC',
                },
                'defaultType': 'spot',  # 'spot', 'swap'
                'fetchBalance': {
                    'type': 'spot',  # 'spot', 'swap', 'account'
                },
                'accountsByType': {
                    'spot': 'spot',
                    'swap': 'swap',
                },
                'createMarketBuyOrderRequiresPrice': True,
                'brokerId': 'CCXTxBitmart000',
            },
            'features': {
                'default': {
                    'sandbox': False,
                    'createOrder': {
                        'marginMode': True,
                        'triggerPrice': False,
                        'triggerPriceType': None,
                        'triggerDirection': False,
                        'stopLossPrice': False,
                        'takeProfitPrice': False,
                        'attachedStopLossTakeProfit': None,
                        'timeInForce': {
                            'IOC': True,
                            'FOK': False,
                            'PO': True,
                            'GTD': False,
                        },
                        'hedged': False,
                        'trailing': False,
                        'marketBuyRequiresPrice': False,  # todo: https://developer-pro.bitmart.com/en/spot/#new-order-v2-signed
                        'marketBuyByCost': True,
                        'leverage': True,  # todo: implement
                        'selfTradePrevention': False,
                        'iceberg': False,
                    },
                    'createOrders': {
                        'max': 10,
                    },
                    'fetchMyTrades': {
                        'marginMode': True,
                        'limit': 200,
                        'daysBack': None,
                        'untilDays': 99999,
                        'symbolRequired': False,
                    },
                    'fetchOrder': {
                        'marginMode': False,
                        'trigger': False,
                        'trailing': False,
                        'symbolRequired': False,
                    },
                    'fetchOpenOrders': {
                        'marginMode': True,
                        'limit': 200,
                        'trigger': False,
                        'trailing': False,
                        'symbolRequired': False,
                    },
                    'fetchOrders': None,
                    'fetchClosedOrders': {
                        'marginMode': True,
                        'limit': 200,
                        'daysBack': None,
                        'daysBackCanceled': None,
                        'untilDays': None,
                        'trigger': False,
                        'trailing': False,
                        'symbolRequired': False,
                    },
                    'fetchOHLCV': {
                        'limit': 1000,  # variable timespans for recent endpoint, 200 for historical
                    },
                },
                'forDerivatives': {
                    'extends': 'default',
                    'createOrder': {
                        'marginMode': True,
                        'triggerPrice': True,
                        'triggerPriceType': {
                            'last': True,
                            'mark': True,
                            'index': False,
                        },
                        'triggerDirection': True,  # todo: implementation broken
                        'stopLossPrice': True,
                        'takeProfitPrice': True,
                        'attachedStopLossTakeProfit': {
                            'triggerPriceType': {
                                'last': True,
                                'mark': True,
                                'index': False,
                            },
                            'price': False,
                        },
                        'timeInForce': {
                            'IOC': True,
                            'FOK': True,
                            'PO': True,
                            'GTD': False,
                        },
                        'hedged': False,
                        'trailing': True,
                        'marketBuyRequiresPrice': True,
                        'marketBuyByCost': True,
                        # exchange-supported features
                        # 'selfTradePrevention': True,
                        # 'twap': False,
                        # 'iceberg': False,
                        # 'oco': False,
                    },
                    'fetchMyTrades': {
                        'marginMode': True,
                        'limit': None,
                        'daysBack': None,
                        'untilDays': 99999,
                    },
                    'fetchOrder': {
                        'marginMode': False,
                        'trigger': False,
                        'trailing': True,
                    },
                    'fetchOpenOrders': {
                        'marginMode': False,
                        'limit': 100,
                        'trigger': True,
                        'trailing': False,
                    },
                    'fetchClosedOrders': {
                        'marginMode': True,
                        'limit': 200,
                        'daysBack': None,
                        'daysBackCanceled': None,
                        'untilDays': None,
                        'trigger': False,
                        'trailing': False,
                    },
                    'fetchOHLCV': {
                        'limit': 500,
                    },
                },
                'spot': {
                    'extends': 'default',
                },
                'swap': {
                    'linear': {
                        'extends': 'forDerivatives',
                    },
                    'inverse': {
                        'extends': 'forDerivatives',
                    },
                },
                'future': {
                    'linear': None,
                    'inverse': None,
                },
            },
        })

    async def fetch_time(self, params={}) -> Int:
        """
        fetches the current integer timestamp in milliseconds from the exchange server

        https://developer-pro.bitmart.com/en/spot/#get-system-time

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

    async def fetch_status(self, params={}):
        """
        the latest known information on the availability of the exchange API

        https://developer-pro.bitmart.com/en/spot/#get-system-service-status

        :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_dict(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 = await 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_dict(response, 'data', {})
        services = self.safe_list(data, 'service', [])
        servicesByType = self.index_by(services, 'service_type')
        if type == 'swap':
            type = 'contract'
        service = self.safe_string(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,
        }

    async def fetch_spot_markets(self, params={}) -> List[MarketInterface]:
        response = await 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_dict(response, 'data', {})
        symbols = self.safe_list(data, 'symbols', [])
        result = []
        fees = self.fees['trading']
        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(self.safe_market_structure({
                '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': self.safe_string_lower_2(market, 'status', 'trade_status') == 'trading',
                'contract': False,
                'linear': None,
                'inverse': None,
                'contractSize': None,
                'expiry': None,
                'expiryDatetime': None,
                'strike': None,
                'optionType': None,
                'maker': fees['maker'],
                'taker': fees['taker'],
                '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

    async def fetch_contract_markets(self, params={}) -> List[MarketInterface]:
        response = await self.publicGetContractPublicDetails(params)
        #
        #     {
        #         "code": 1000,
        #         "message": "Ok",
        #         "data": {
        #             "symbols": [
        #                 {
        #                     "symbol": "BTCUSDT",
        #                     "product_type": 1,
        #                     "open_timestamp": 1645977600000,
        #                     "expire_timestamp": 0,
        #                     "settle_timestamp": 0,
        #                     "base_currency": "BTC",
        #                     "quote_currency": "USDT",
        #                     "last_price": "63547.4",
        #                     "volume_24h": "110938430",
        #                     "turnover_24h": "7004836342.6944",
        #                     "index_price": "63587.85404255",
        #                     "index_name": "BTCUSDT",
        #                     "contract_size": "0.001",
        #                     "min_leverage": "1",
        #                     "max_leverage": "100",
        #                     "price_precision": "0.1",
        #                     "vol_precision": "1",
        #                     "max_volume": "1000000",
        #                     "min_volume": "1",
        #                     "funding_rate": "0.0000801",
        #                     "expected_funding_rate": "-0.0000035",
        #                     "open_interest": "278214",
        #                     "open_interest_value": "17555316.9355496",
        #                     "high_24h": "64109.4",
        #                     "low_24h": "61857.6",
        #                     "change_24h": "0.0239264900886327",
        #                     "funding_time": 1726819200000
        #                 },
        #             ]
        #         }
        #     }
        #
        data = self.safe_dict(response, 'data', {})
        symbols = self.safe_list(data, 'symbols', [])
        result = []
        fees = self.fees['trading']
        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(self.safe_market_structure({
                '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': self.safe_string_lower(market, 'status') == 'trading',
                'contract': True,
                'linear': True,
                'inverse': False,
                'contractSize': self.safe_number(market, 'contract_size'),
                'expiry': expiry,
                'expiryDatetime': self.iso8601(expiry),
                'strike': None,
                'optionType': None,
                'maker': fees['maker'],
                'taker': fees['taker'],
                '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

    async def fetch_markets(self, params={}) -> List[Market]:
        """
        retrieves data on all markets for bitmart

        https://developer-pro.bitmart.com/en/spot/#get-trading-pair-details-v1
        https://developer-pro.bitmart.com/en/futuresv2/#get-contract-details

        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: an array of objects representing market data
        """
        if self.options['adjustForTimeDifference']:
            await self.load_time_difference()
        spot = await self.fetch_spot_markets(params)
        contract = await self.fetch_contract_markets(params)
        return self.array_concat(spot, contract)

    async def fetch_currencies(self, params={}) -> Currencies:
        """
        fetches all available currencies on an exchange

        https://developer-pro.bitmart.com/en/spot/#get-currency-list-v1

        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an associative dictionary of currencies
        """
        response = await self.publicGetAccountV1Currencies(params)
        #
        #     {
        #         "message": "OK",
        #         "code": 1000,
        #         "trace": "619294ecef584282b26a3be322b1e01f.66.17403093228242229",
        #         "data": {
        #             "currencies": [
        #                 {
        #                     "currency": "BTC",
        #                     "name": "Bitcoin",
        #                     "recharge_minsize": '0.00000001',
        #                     "contract_address": null,
        #                     "network": "BTC",
        #                     "withdraw_enabled": True,
        #                     "deposit_enabled": True,
        #                     "withdraw_minsize": "0.0003",
        #                     "withdraw_minfee": "9.61",
        #                     "withdraw_fee_estimate": "9.61",
        #                     "withdraw_fee": "0.0001"
        #                 }
        #             ]
        #         }
        #     }
        #
        data = self.safe_dict(response, 'data', {})
        currencies = self.safe_list(data, 'currencies', [])
        result = {}
        for i in range(0, len(currencies)):
            currency = currencies[i]
            fullId = self.safe_string(currency, 'currency')
            currencyId = fullId
            networkId = self.safe_string(currency, 'network')
            isNtf = (fullId.find('NFT') >= 0)
            if not isNtf:
                parts = fullId.split('-')
                currencyId = self.safe_string(parts, 0)
                second = self.safe_string(parts, 1)
                if second is not None:
                    networkId = second.upper()
            currencyCode = self.safe_currency_code(currencyId)
            entry = self.safe_dict(result, currencyCode)
            if entry is None:
                entry = {
                    'info': currency,
                    'id': currencyId,
                    'code': currencyCode,
                    'precision': None,
                    'name': self.safe_string(currency, 'name'),
                    'deposit': None,
                    'withdraw': None,
                    'active': None,
                    'networks': {},
                    'type': 'other' if isNtf else 'crypto',
                }
            networkCode = self.network_id_to_code(networkId)
            withdraw = self.safe_bool(currency, 'withdraw_enabled')
            deposit = self.safe_bool(currency, 'deposit_enabled')
            entry['networks'][networkCode] = {
                'info': currency,
                'id': networkId,
                'code': networkCode,
                'withdraw': withdraw,
                'deposit': deposit,
                'active': withdraw and deposit,
                'fee': self.safe_number(currency, 'withdraw_fee'),
                'limits': {
                    'withdraw': {
                        'min': self.safe_number(currency, 'withdraw_minsize'),
                        'max': None,
                    },
                    'deposit': {
                        'min': None,
                        'max': None,
                    },
                },
            }
            result[currencyCode] = entry
        keys = list(result.keys())
        for i in range(0, len(keys)):
            key = keys[i]
            currency = result[key]
            result[key] = self.safe_currency_structure(currency)
        return result

    def get_currency_id_from_code_and_network(self, currencyCode: Str, networkCode: Str) -> Str:
        if networkCode is None:
            networkCode = self.default_network_code(currencyCode)  # use default network code if not provided
        currency = self.currency(currencyCode)
        id = currency['id']
        idFromNetwork: Str = None
        networks = self.safe_dict(currency, 'networks', {})
        networkInfo: dict = {}
        if networkCode is None:
            # network code is not provided and there is no default network code
            network = self.safe_dict(networks, currencyCode)  # trying to find network that has the same code
            if network is None:
                # use the first network in the networks list if there is no network code with the same code
                keys = list(networks.keys())
                length = len(keys)
                if length > 0:
                    network = self.safe_value(networks, keys[0])
            networkInfo = self.safe_dict(network, 'info', {})
            idFromNetwork = self.safe_string(networkInfo, 'currency')  # use currency name from network
        else:
            providedOrDefaultNetwork = self.safe_dict(networks, networkCode)
            if providedOrDefaultNetwork is not None:
                networkInfo = self.safe_dict(providedOrDefaultNetwork, 'info', {})
                idFromNetwork = self.safe_string(networkInfo, 'currency')  # use currency name from network
            else:
                id += '-' + self.network_code_to_id(networkCode, currencyCode)  # use concatenated currency id and network code if network is not found
        return idFromNetwork if (idFromNetwork is not None) else id

    async 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
        :param str [params.network]: the network code of the currency
        :returns dict: a `fee structure <https://docs.ccxt.com/#/?id=fee-structure>`
        """
        await self.load_markets()
        currency = self.currency(code)
        network: Str = None
        network, params = self.handle_network_code_and_params(params)
        request: dict = {
            'currency': self.get_currency_id_from_code_and_network(currency['code'], network),
        }
        response = await 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': {},
        }

    async def fetch_deposit_withdraw_fee(self, code: str, params={}):
        """
        fetch the fee for deposits and withdrawals

        https://developer-pro.bitmart.com/en/spot/#withdraw-quota-keyed

        :param str code: unified currency code
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.network]: the network code of the currency
        :returns dict: a `fee structure <https://docs.ccxt.com/#/?id=fee-structure>`
        """
        await self.load_markets()
        network: Str = None
        network, params = self.handle_network_code_and_params(params)
        request: dict = {
            'currency': self.get_currency_id_from_code_and_network(code, network),
        }
        response = await 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
        #
        #     {
        #         "symbol": "BTCUSDT",
        #         "product_type": 1,
        #         "open_timestamp": 1645977600000,
        #         "expire_timestamp": 0,
        #         "settle_timestamp": 0,
        #         "base_currency": "BTC",
        #         "quote_currency": "USDT",
        #         "last_price": "63547.4",
        #         "volume_24h": "110938430",
        #         "turnover_24h": "7004836342.6944",
        #         "index_price": "63587.85404255",
        #         "index_name": "BTCUSDT",
        #         "contract_size": "0.001",
        #         "min_leverage": "1",
        #         "max_leverage": "100",
        #         "price_precision": "0.1",
        #         "vol_precision": "1",
        #         "max_volume": "1000000",
        #         "min_volume": "1",
        #         "funding_rate": "0.0000801",
        #         "expected_funding_rate": "-0.0000035",
        #         "open_interest": "278214",
        #         "open_interest_value": "17555316.9355496",
        #         "high_24h": "64109.4",
        #         "low_24h": "61857.6",
        #         "change_24h": "0.0239264900886327",
        #         "funding_time": 1726819200000
        #     }
        #
        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_2(ticker, 'price_change_percent_24h', 'change_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_n(ticker, ['base_volume_24h', 'v_24h', 'volume_24h'])
        quoteVolume = self.safe_string_lower_n(ticker, ['quote_volume_24h', 'qv_24h', 'turnover_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': None,
            'percentage': percentage,
            'average': average,
            'baseVolume': baseVolume,
            'quoteVolume': quoteVolume,
            'indexPrice': self.safe_string(ticker, 'index_price'),
            'info': ticker,
        }, market)

    async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
        """
        fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market

        https://developer-pro.bitmart.com/en/spot/#get-ticker-of-a-trading-pair-v3
        https://developer-pro.bitmart.com/en/futuresv2/#get-contract-details

        :param str symbol: unified symbol of the market to fetch the ticker for
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {}
        response = None
        if market['swap']:
            request['symbol'] = market['id']
            response = await self.publicGetContractPublicDetails(self.extend(request, params))
            #
            #     {
            #         "code": 1000,
            #         "message": "Ok",
            #         "data": {
            #             "symbols": [
            #                 {
            #                     "symbol": "BTCUSDT",
            #                     "product_type": 1,
            #                     "open_timestamp": 1645977600000,
            #                     "expire_timestamp": 0,
            #                     "settle_timestamp": 0,
            #                     "base_currency": "BTC",
            #                     "quote_currency": "USDT",
            #                     "last_price": "63547.4",
            #                     "volume_24h": "110938430",
            #                     "turnover_24h": "7004836342.6944",
            #                     "index_price": "63587.85404255",
            #                     "index_name": "BTCUSDT",
            #                     "contract_size": "0.001",
            #                     "min_leverage": "1",
            #                     "max_leverage": "100",
            #                     "price_precision": "0.1",
            #                     "vol_precision": "1",
            #                     "max_volume": "1000000",
            #                     "min_volume": "1",
            #                     "funding_rate": "0.0000801",
            #                     "expected_funding_rate": "-0.0000035",
            #                     "open_interest": "278214",
            #                     "open_interest_value": "17555316.9355496",
            #                     "high_24h": "64109.4",
            #                     "low_24h": "61857.6",
            #                     "change_24h": "0.0239264900886327",
            #                     "funding_time": 1726819200000
            #                 },
            #             ]
            #         }
            #     }
            #
        elif market['spot']:
            request['symbol'] = market['id']
            response = await 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'
        tickers = []
        ticker: dict = {}
        if market['spot']:
            ticker = self.safe_dict(response, 'data', {})
        else:
            data = self.safe_dict(response, 'data', {})
            tickers = self.safe_list(data, 'symbols', [])
            ticker = self.safe_dict(tickers, 0, {})
        return self.parse_ticker(ticker, market)

    async def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
        """
        fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market

        https://developer-pro.bitmart.com/en/spot/#get-ticker-of-all-pairs-v3
        https://developer-pro.bitmart.com/en/futuresv2/#get-contract-details

        :param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
        """
        await self.load_markets()
        symbols = self.market_symbols(symbols)
        type = None
        market = None
        if symbols is not None:
            symbol = self.safe_string(symbols, 0)
            market = self.market(symbol)
        type, params = self.handle_market_type_and_params('fetchTickers', market, params)
        response = None
        if type == 'spot':
            response = await 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 = await self.publicGetContractPublicDetails(params)
            #
            #     {
            #         "code": 1000,
            #         "message": "Ok",
            #         "data": {
            #             "symbols": [
            #                 {
            #                     "symbol": "BTCUSDT",
            #                     "product_type": 1,
            #                     "open_timestamp": 1645977600000,
            #                     "expire_timestamp": 0,
            #                     "settle_timestamp": 0,
            #                     "base_currency": "BTC",
            #                     "quote_currency": "USDT",
            #                     "last_price": "63547.4",
            #                     "volume_24h": "110938430",
            #                     "turnover_24h": "7004836342.6944",
            #                     "index_price": "63587.85404255",
            #                     "index_name": "BTCUSDT",
            #                     "contract_size": "0.001",
            #                     "min_leverage": "1",
            #                     "max_leverage": "100",
            #                     "price_precision": "0.1",
            #                     "vol_precision": "1",
            #                     "max_volume": "1000000",
            #                     "min_volume": "1",
            #                     "funding_rate": "0.0000801",
            #                     "expected_funding_rate": "-0.0000035",
            #                     "open_interest": "278214",
            #                     "open_interest_value": "17555316.9355496",
            #                     "high_24h": "64109.4",
            #                     "low_24h": "61857.6",
            #                     "change_24h": "0.0239264900886327",
            #                     "funding_time": 1726819200000
            #                 },
            #             ]
            #         }
            #     }
            #
        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, 'symbols', [])
        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)

    async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
        """
        fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data

        https://developer-pro.bitmart.com/en/spot/#get-depth-v3
        https://developer-pro.bitmart.com/en/futuresv2/#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
        """
        await 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 = await self.publicGetSpotQuotationV3Books(self.extend(request, params))
        elif market['swap']:
            response = await 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_dict(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)

    async 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

        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>`
        """
        await 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 = await 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'),
            ]

    async def fetch_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
        """
        fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market

        https://developer-pro.bitmart.com/en/spot/#get-history-k-line-v3
        https://developer-pro.bitmart.com/en/futuresv2/#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
        """
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchOHLCV', 'paginate', False)
        if paginate:
            return await self.fetch_paginated_call_deterministic('fetchOHLCV', symbol, since, limit, timeframe, params, 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 = 500
            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']:
            price = self.safe_string(params, 'price')
            if price == 'mark':
                params = self.omit(params, 'price')
                response = await self.publicGetContractPublicMarkpriceKline(self.extend(request, params))
            else:
                response = await self.publicGetContractPublicKline(self.extend(request, params))
        else:
            response = await 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)

    async def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """

        https://developer-pro.bitmart.com/en/spot/#account-trade-list-v4-signed
        https://developer-pro.bitmart.com/en/futuresv2/#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)
        :param str [params.stpMode]: self-trade prevention only for spot, defaults to none, ['none', 'cancel_maker', 'cancel_taker', 'cancel_both']
        :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
        """
        await 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_dict(self.options, 'fetchMyTrades', {})
            maxLimit = 200
            defaultLimit = self.safe_integer(options, 'limit', maxLimit)
            if limit is None:
                limit = defaultLimit
            request['limit'] = min(limit, maxLimit)
            if since is not None:
                request['startTime'] = since
            if until is not None:
                request['endTime'] = until
            response = await 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 = await 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)

    async def fetch_order_trades(self, id: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """

        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
        :param str [params.stpMode]: self-trade prevention only for spot, defaults to none, ['none', 'cancel_maker', 'cancel_taker', 'cancel_both']
        :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
        """
        await self.load_markets()
        request: dict = {
            'orderId': id,
        }
        response = await 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_dict(response, 'data', {})
        wallet = None
        if marketType == 'swap':
            wallet = self.safe_list(response, 'data', [])
        elif marketType == 'margin':
            wallet = self.safe_list(data, 'symbols', [])
        else:
            wallet = self.safe_list(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_dict(entry, 'base', {})
                quote = self.safe_dict(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_n(balance, ['unAvailable', '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

    async def fetch_balance(self, params={}) -> Balances:
        """
        query for balance and get the amount of funds available for trading or funds locked in orders

        https://developer-pro.bitmart.com/en/spot/#get-spot-wallet-balance-keyed
        https://developer-pro.bitmart.com/en/futuresv2/#get-contract-assets-keyed
        https://developer-pro.bitmart.com/en/spot/#get-account-balance-keyed
        https://developer-pro.bitmart.com/en/spot/#get-margin-account-details-isolated-keyed

        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
        """
        await 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 = await self.privateGetSpotV1Wallet(params)
        elif marketType == 'swap':
            response = await self.privateGetContractPrivateAssetsDetail(params)
        elif marketType == 'account':
            response = await self.privateGetAccountV1Wallet(params)
        elif marketType == 'margin':
            response = await 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","available_usd_valuation":null},
        #                 {"currency":"ETH","name":"Ethereum","available":"0.00002277","frozen":"0.00000000","available_usd_valuation":null}
        #             ]
        #         }
        #     }
        #
        # 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,
        }

    async def fetch_trading_fee(self, symbol: str, params={}) -> TradingFeeInterface:
        """
        fetch the trading fees for a market

        https://developer-pro.bitmart.com/en/spot/#get-actual-trade-fee-rate-keyed

        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `fee structure <https://docs.ccxt.com/#/?id=fee-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        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 = await 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_dict(response, 'data', {})
        return self.parse_trading_fee(data)

    def parse_order(self, order: dict, market: Market = None) -> Order:
        #
        # createOrder, editOrder
        #
        #     {
        #         "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_2(order, 'client_order_id', 'clientOrderId'),
            '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),
            '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_dict(statusesByType, type, {})
        return self.safe_string(statuses, status, status)

    async def create_market_buy_order_with_cost(self, symbol: str, cost: float, params={}):
        """
        create a market buy order by providing the symbol and cost

        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>`
        """
        await self.load_markets()
        market = self.market(symbol)
        if not market['spot']:
            raise NotSupported(self.id + ' createMarketBuyOrderWithCost() supports spot orders only')
        params['createMarketBuyOrderRequiresPrice'] = False
        return await self.create_order(symbol, 'market', 'buy', cost, None, params)

    async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
        create a trade order

        https://developer-pro.bitmart.com/en/spot/#new-order-v2-signed
        https://developer-pro.bitmart.com/en/spot/#new-margin-order-v1-signed
        https://developer-pro.bitmart.com/en/futuresv2/#submit-order-signed
        https://developer-pro.bitmart.com/en/futuresv2/#submit-plan-order-signed
        https://developer-pro.bitmart.com/en/futuresv2/#submit-tp-sl-order-signed
        https://developer-pro.bitmart.com/en/futuresv2/#submit-trail-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 fulfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.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
        :param str [params.stopLossPrice]: *swap only* the price to trigger a stop-loss order
        :param str [params.takeProfitPrice]: *swap only* the price to trigger a take-profit order
        :param int [params.plan_category]: *swap tp/sl only* 1: tp/sl, 2: position tp/sl, default is 1
        :param str [params.stpMode]: self-trade prevention only for spot, defaults to none, ['none', 'cancel_maker', 'cancel_taker', 'cancel_both']
        :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await 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'])
        stopLossPrice = self.safe_string(params, 'stopLossPrice')
        takeProfitPrice = self.safe_string(params, 'takeProfitPrice')
        isStopLoss = stopLossPrice is not None
        isTakeProfit = takeProfitPrice is not None
        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 = await self.privatePostSpotV1MarginSubmitOrder(spotRequest)
            else:
                response = await self.privatePostSpotV2SubmitOrder(spotRequest)
        else:
            swapRequest = self.create_swap_order_request(symbol, type, side, amount, price, params)
            activationPrice = self.safe_string(swapRequest, 'activation_price')
            if activationPrice is not None:
                # if type is trailing
                response = await self.privatePostContractPrivateSubmitTrailOrder(swapRequest)
            elif isTriggerOrder:
                response = await self.privatePostContractPrivateSubmitPlanOrder(swapRequest)
            elif isStopLoss or isTakeProfit:
                response = await self.privatePostContractPrivateSubmitTpSlOrder(swapRequest)
            else:
                response = await 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_dict(response, 'data', {})
        order = self.parse_order(data, market)
        order['type'] = type
        order['side'] = side
        order['amount'] = amount
        order['price'] = price
        return order

    async def create_orders(self, orders: List[OrderRequest], params={}):
        """
        create a list of trade orders

        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
        :param str [params.stpMode]: self-trade prevention only for spot, defaults to none, ['none', 'cancel_maker', 'cancel_taker', 'cancel_both']
        :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await 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 = await 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
        https://developer-pro.bitmart.com/en/futuresv2/#submit-order-signed
        https://developer-pro.bitmart.com/en/futuresv2/#submit-plan-order-signed
        https://developer-pro.bitmart.com/en/futuresv2/#submit-tp-sl-order-signed
        https://developer-pro.bitmart.com/en/futuresv2/#submit-trail-order-signed
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market', 'limit', 'trailing', 'stop_loss', or 'take_profit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of currency you want to trade in units of base currency
        :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param 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
        :param str [params.stopLossPrice]: *swap only* the price to trigger a stop-loss order
        :param str [params.takeProfitPrice]: *swap only* the price to trigger a take-profit order
        :param int [params.plan_category]: *swap tp/sl only* 1: tp/sl, 2: position tp/sl, default is 1
        :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        market = self.market(symbol)
        stopLossPrice = self.safe_string(params, 'stopLossPrice')
        takeProfitPrice = self.safe_string(params, 'takeProfitPrice')
        isStopLoss = stopLossPrice is not None
        isTakeProfit = takeProfitPrice is not None
        if isStopLoss:
            type = 'stop_loss'
        elif isTakeProfit:
            type = 'take_profit'
        request: dict = {
            'symbol': market['id'],
            '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_bool(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:
            type = 'trailing'
            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:
            if isLimitOrder or price is not None:
                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
        marginMode = None
        marginMode, params = self.handle_margin_mode_and_params('createOrder', params, 'cross')
        if isStopLoss or isTakeProfit:
            reduceOnly = True
            request['price_type'] = self.safe_integer(params, 'price_type', 1)
            request['executive_price'] = self.price_to_precision(symbol, price)
            if isStopLoss:
                request['trigger_price'] = self.price_to_precision(symbol, stopLossPrice)
            else:
                request['trigger_price'] = self.price_to_precision(symbol, takeProfitPrice)
        else:
            request['open_type'] = marginMode
        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
        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')
        params = self.omit(params, ['timeInForce', 'postOnly', 'reduceOnly', 'leverage', 'trailingTriggerPrice', 'trailingPercent', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice'])
        if leverage is not None:
            request['leverage'] = self.number_to_string(leverage)
        elif isTriggerOrder:
            request['leverage'] = '1'  # for plan orders leverage is required, if not available default to 1
        if type != 'trailing':
            request['type'] = type
        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
        https://developer-pro.bitmart.com/en/spot/#new-order-v2-signed
        https://developer-pro.bitmart.com/en/spot/#new-margin-order-v1-signed
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of currency you want to trade in units of base currency
        :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param 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)

    async def cancel_order(self, id: str, symbol: Str = None, params={}):
        """
        cancels an open order

        https://developer-pro.bitmart.com/en/futuresv2/#cancel-order-signed
        https://developer-pro.bitmart.com/en/spot/#cancel-order-v3-signed
        https://developer-pro.bitmart.com/en/futuresv2/#cancel-plan-order-signed
        https://developer-pro.bitmart.com/en/futuresv2/#cancel-order-signed
        https://developer-pro.bitmart.com/en/futuresv2/#cancel-trail-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.trigger]: *swap only* whether the order is a trigger order
        :param boolean [params.trailing]: *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')
        await 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 = await self.privatePostSpotV3CancelOrder(self.extend(request, params))
        else:
            trigger = self.safe_bool_2(params, 'stop', 'trigger')
            trailing = self.safe_bool(params, 'trailing')
            params = self.omit(params, ['stop', 'trigger'])
            if trigger:
                response = await self.privatePostContractPrivateCancelPlanOrder(self.extend(request, params))
            elif trailing:
                response = await self.privatePostContractPrivateCancelTrailOrder(self.extend(request, params))
            else:
                response = await self.privatePostContractPrivateCancelOrder(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 self.safe_order({'info': 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

    async def cancel_orders(self, ids: List[str], symbol: Str = None, params={}) -> List[Order]:
        """
        cancel multiple orders

        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')
        await 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 = await 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

    async def cancel_all_orders(self, symbol: Str = None, params={}):
        """
        cancel all open orders in a market

        https://developer-pro.bitmart.com/en/spot/#cancel-all-order-v4-signed
        https://developer-pro.bitmart.com/en/futuresv2/#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>`
        """
        await 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 = await self.privatePostSpotV4CancelAll(self.extend(request, params))
        elif type == 'swap':
            if symbol is None:
                raise ArgumentsRequired(self.id + ' cancelAllOrders() requires a symbol argument')
            response = await 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 [self.safe_order({'info': response})]

    async 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')
        await 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 = await 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_dict(response, 'data', {})
        orders = self.safe_list(data, 'orders', [])
        return self.parse_orders(orders, market, since, limit)

    async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """

        https://developer-pro.bitmart.com/en/spot/#current-open-orders-v4-signed
        https://developer-pro.bitmart.com/en/futuresv2/#get-all-open-orders-keyed
        https://developer-pro.bitmart.com/en/futuresv2/#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
        :param str [params.stpMode]: self-trade prevention only for spot, defaults to none, ['none', 'cancel_maker', 'cancel_taker', 'cancel_both']
        :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        market = None
        request: dict = {}
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        type = None
        response = None
        type, params = self.handle_market_type_and_params('fetchOpenOrders', market, params)
        if type == 'spot':
            if limit is not None:
                request['limit'] = min(limit, 200)
            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 = await self.privatePostSpotV4QueryOpenOrders(self.extend(request, params))
        elif type == 'swap':
            if limit is not None:
                request['limit'] = min(limit, 100)
            isTrigger = self.safe_bool_2(params, 'stop', 'trigger')
            params = self.omit(params, ['stop', 'trigger'])
            if isTrigger:
                response = await 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 = await 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)

    async def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """

        https://developer-pro.bitmart.com/en/spot/#account-orders-v4-signed
        https://developer-pro.bitmart.com/en/futuresv2/#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
        :param str [params.stpMode]: self-trade prevention only for spot, defaults to none, ['none', 'cancel_maker', 'cancel_taker', 'cancel_both']
        :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        market = None
        request: dict = {}
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        type = None
        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')
        if since is not None:
            startTimeKey = 'startTime' if (type == 'spot') else 'start_time'
            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':
            marginMode = None
            marginMode, params = self.handle_margin_mode_and_params('fetchClosedOrders', params)
            if marginMode == 'isolated':
                request['orderMode'] = 'iso_margin'
            response = await self.privatePostSpotV4QueryHistoryOrders(self.extend(request, params))
        else:
            response = await self.privateGetContractPrivateOrderHistory(self.extend(request, params))
        data = self.safe_list(response, 'data', [])
        return self.parse_orders(data, market, since, limit)

    async def fetch_canceled_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetches information on multiple canceled orders made by the user
        :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 await self.fetch_orders_by_status('canceled', symbol, since, limit, params)

    async def fetch_order(self, id: str, symbol: Str = None, params={}):
        """
        fetches information on an order made by the user

        https://developer-pro.bitmart.com/en/spot/#query-order-by-id-v4-signed
        https://developer-pro.bitmart.com/en/spot/#query-order-by-clientorderid-v4-signed
        https://developer-pro.bitmart.com/en/futuresv2/#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
        :param str [params.stpMode]: self-trade prevention only for spot, defaults to none, ['none', 'cancel_maker', 'cancel_taker', 'cancel_both']
        :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await 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 = await self.privatePostSpotV4QueryClientOrder(self.extend(request, params))
            else:
                response = await 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 = await 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)

    async def fetch_deposit_address(self, code: str, params={}) -> DepositAddress:
        """
        fetch the deposit address for a currency associated with self account

        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>`
        """
        await self.load_markets()
        currency = self.currency(code)
        network: Str = None
        network, params = self.handle_network_code_and_params(params)
        request: dict = {
            'currency': self.get_currency_id_from_code_and_network(code, network),
        }
        response = await 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) -> DepositAddress:
        #
        # fetchDepositAddress
        #    {
        #        currency: 'ETH',
        #        chain: 'Ethereum',
        #        address: '0x99B5EEc2C520f86F0F62F05820d28D05D36EccCf',
        #        address_memo: ''
        #    }
        #
        # fetchWithdrawAddress
        #     {
        #         "currency": "ETH",
        #         "network": "ETH",
        #         "address": "0x1121",
        #         "memo": "12",
        #         "remark": "12",
        #         "addressType": 0,
        #         "verifyStatus": 0
        #     }
        #
        currencyId = self.safe_string(depositAddress, 'currency')
        network = self.safe_string_2(depositAddress, 'chain', 'network')
        if currencyId.find('NFT') < 0:
            parts = currencyId.split('-')
            currencyId = self.safe_string(parts, 0)
            secondPart = self.safe_string(parts, 1)
            if secondPart is not None:
                network = secondPart
        address = self.safe_string(depositAddress, 'address')
        currency = self.safe_currency(currencyId, currency)
        self.check_address(address)
        return {
            'info': depositAddress,
            'currency': self.safe_string(currency, 'code'),
            'network': self.network_id_to_code(network),
            'address': address,
            'tag': self.safe_string_2(depositAddress, 'address_memo', 'memo'),
        }

    async def withdraw(self, code: str, amount: float, address: str, tag: Str = None, params={}) -> Transaction:
        """
        make a withdrawal

        https://developer-pro.bitmart.com/en/spot/#withdraw-signed

        :param str code: unified currency code
        :param float amount: the amount to withdraw
        :param str address: the address to withdraw to
        :param str tag:
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.network]: the network name for self withdrawal
        :returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
        """
        tag, params = self.handle_withdraw_tag_and_params(tag, params)
        self.check_address(address)
        await self.load_markets()
        currency = self.currency(code)
        network: Str = None
        network, params = self.handle_network_code_and_params(params)
        request: dict = {
            'currency': self.get_currency_id_from_code_and_network(code, network),
            'amount': amount,
            'destination': 'To Digital Address',  # To Digital Address, To Binance, To OKEX
            'address': address,
        }
        if tag is not None:
            request['address_memo'] = tag
        response = await self.privatePostAccountV1WithdrawApply(self.extend(request, params))
        #
        #     {
        #         "code": 1000,
        #         "trace":"886fb6ae-456b-4654-b4e0-d681ac05cea1",
        #         "message": "OK",
        #         "data": {
        #             "withdraw_id": "121212"
        #         }
        #     }
        #
        data = self.safe_dict(response, 'data', {})
        transaction = self.parse_transaction(data, currency)
        return self.extend(transaction, {
            'code': code,
            'address': address,
            'tag': tag,
        })

    async def fetch_transactions_by_type(self, type, code: Str = None, since: Int = None, limit: Int = None, params={}):
        await self.load_markets()
        if limit is None:
            limit = 1000  # max 1000
        request: dict = {
            'operation_type': type,  # deposit or withdraw
            'N': limit,
        }
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        if since is not None:
            request['startTime'] = since
        until = self.safe_integer(params, 'until')
        if until is not None:
            params = self.omit(params, 'until')
            request['endTime'] = until
        response = await 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_dict(response, 'data', {})
        records = self.safe_list(data, 'records', [])
        return self.parse_transactions(records, currency, since, limit)

    async def fetch_deposit(self, id: str, code: Str = None, params={}):
        """
        fetch information on a deposit

        https://developer-pro.bitmart.com/en/spot/#get-a-deposit-or-withdraw-detail-keyed

        :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>`
        """
        await self.load_markets()
        request: dict = {
            'id': id,
        }
        response = await 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_dict(response, 'data', {})
        record = self.safe_dict(data, 'record', {})
        return self.parse_transaction(record)

    async def fetch_deposits(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch all deposits made to an account

        https://developer-pro.bitmart.com/en/spot/#get-deposit-and-withdraw-history-keyed

        :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 await self.fetch_transactions_by_type('deposit', code, since, limit, params)

    async def fetch_withdrawal(self, id: str, code: Str = None, params={}):
        """
        fetch data on a currency withdrawal via the withdrawal id

        https://developer-pro.bitmart.com/en/spot/#get-a-deposit-or-withdraw-detail-keyed

        :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>`
        """
        await self.load_markets()
        request: dict = {
            'id': id,
        }
        response = await 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_dict(response, 'data', {})
        record = self.safe_dict(data, 'record', {})
        return self.parse_transaction(record)

    async def fetch_withdrawals(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Transaction]:
        """
        fetch all withdrawals made from an account

        https://developer-pro.bitmart.com/en/spot/#get-deposit-and-withdraw-history-keyed

        :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 await 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')
        networkId: Str = None
        if currencyId is not None:
            if currencyId.find('NFT') < 0:
                parts = currencyId.split('-')
                currencyId = self.safe_string(parts, 0)
                networkId = self.safe_string(parts, 1)
        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': self.network_id_to_code(networkId),
            '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,
        }

    async def repay_isolated_margin(self, symbol: str, code: str, amount, params={}):
        """
        repay borrowed margin and interest

        https://developer-pro.bitmart.com/en/spot/#margin-repay-isolated-signed

        :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>`
        """
        await 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 = await self.privatePostSpotV1MarginIsolatedRepay(self.extend(request, params))
        #
        #     {
        #         "message": "OK",
        #         "code": 1000,
        #         "trace": "b0a60b4c-e986-4b54-a190-8f7c05ddf685",
        #         "data": {
        #             "repay_id": "2afcc16d99bd4707818c5a355dc89bed"
        #         }
        #     }
        #
        data = self.safe_dict(response, 'data', {})
        transaction = self.parse_margin_loan(data, currency)
        return self.extend(transaction, {
            'amount': amount,
            'symbol': symbol,
        })

    async def borrow_isolated_margin(self, symbol: str, code: str, amount: float, params={}):
        """
        create a loan to borrow margin

        https://developer-pro.bitmart.com/en/spot/#margin-borrow-isolated-signed

        :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>`
        """
        await 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 = await self.privatePostSpotV1MarginIsolatedBorrow(self.extend(request, params))
        #
        #     {
        #         "message": "OK",
        #         "code": 1000,
        #         "trace": "e6fda683-181e-4e78-ac9c-b27c4c8ba035",
        #         "data": {
        #             "borrow_id": "629a7177a4ed4cf09869c6a4343b788c"
        #         }
        #     }
        #
        data = self.safe_dict(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,
        }

    async def fetch_isolated_borrow_rate(self, symbol: str, params={}) -> IsolatedBorrowRate:
        """
        fetch the rate of interest to borrow a currency for margin trading

        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>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        response = await 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_dict(response, 'data', {})
        symbols = self.safe_list(data, 'symbols', [])
        borrowRate = self.safe_dict(symbols, 0, [])
        return self.parse_isolated_borrow_rate(borrowRate, market)

    def parse_isolated_borrow_rate(self, info: dict, 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_dict(info, 'base', {})
        quoteData = self.safe_dict(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,
        }

    async def fetch_isolated_borrow_rates(self, params={}) -> IsolatedBorrowRates:
        """
        fetch the borrow interest rates of all currencies, currently only works for isolated margin

        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>`
        """
        await self.load_markets()
        response = await 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_dict(response, 'data', {})
        symbols = self.safe_list(data, 'symbols', [])
        return self.parse_isolated_borrow_rates(symbols)

    async 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

        https://developer-pro.bitmart.com/en/spot/#margin-asset-transfer-signed
        https://developer-pro.bitmart.com/en/futuresv2/#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>`
        """
        await 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 = await self.privatePostSpotV1MarginIsolatedTransfer(self.extend(request, params))
        elif (fromAccount == 'swap') or (toAccount == 'swap'):
            response = await 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_dict(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')),
        }

    async def fetch_transfers(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[TransferEntry]:
        """
        fetch a history of internal transfers made on an account, only transfers between spot and swap are supported

        https://developer-pro.bitmart.com/en/futuresv2/#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>`
        """
        await 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 = await 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_dict(response, 'data', {})
        records = self.safe_list(data, 'records', [])
        return self.parse_transfers(records, currency, since, limit)

    async def fetch_borrow_interest(self, code: Str = None, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[BorrowInterest]:
        """
        fetch the interest owed by the user for borrowing currency for margin trading

        https://developer-pro.bitmart.com/en/spot/#get-borrow-record-isolated-keyed

        :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')
        await 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 = await 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_dict(response, 'data', {})
        rows = self.safe_list(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) -> BorrowInterest:
        #
        #     {
        #         "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 {
            'info': info,
            'symbol': self.safe_string(market, 'symbol'),
            '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'),
            'marginMode': 'isolated',
            'timestamp': timestamp,  # borrow creation time
            'datetime': self.iso8601(timestamp),
        }

    async def fetch_open_interest(self, symbol: str, params={}):
        """
        Retrieves the open interest of a currency

        https://developer-pro.bitmart.com/en/futuresv2/#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:
        """
        await 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 = await 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)

    async def set_leverage(self, leverage: int, symbol: Str = None, params={}):
        """
        set the level of leverage for a market

        https://developer-pro.bitmart.com/en/futuresv2/#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'])
        await 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 await self.privatePostContractPrivateSubmitLeverage(self.extend(request, params))

    async def fetch_funding_rate(self, symbol: str, params={}) -> FundingRate:
        """
        fetch the current funding rate

        https://developer-pro.bitmart.com/en/futuresv2/#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>`
        """
        await 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 = await self.publicGetContractPublicFundingRate(self.extend(request, params))
        #
        #     {
        #         "code": 1000,
        #         "message": "Ok",
        #         "data": {
        #             "symbol": "BTCUSDT",
        #             "expected_rate": "-0.0000238",
        #             "rate_value": "0.000009601106",
        #             "funding_time": 1761292800000,
        #             "funding_upper_limit": "0.0375",
        #             "funding_lower_limit": "-0.0375",
        #             "timestamp": 1761291544336
        #         },
        #         "trace": "64b7a589-e1e-4ac2-86b1-41058757421"
        #     }
        #
        data = self.safe_dict(response, 'data', {})
        return self.parse_funding_rate(data, market)

    async def fetch_funding_rate_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetches historical funding rate prices

        https://developer-pro.bitmart.com/en/futuresv2/#get-funding-rate-history

        :param str symbol: unified symbol of the market to fetch the funding rate history for
        :param int [since]: not sent to exchange api, exchange api always returns the most recent data, only used to filter exchange response
        :param int [limit]: the maximum amount of funding rate structures to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-history-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchFundingRateHistory() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        if limit is not None:
            request['limit'] = limit
        response = await self.publicGetContractPublicFundingRateHistory(self.extend(request, params))
        #
        #     {
        #         "code": 1000,
        #         "message": "Ok",
        #         "data": {
        #             "list": [
        #                 {
        #                     "symbol": "BTCUSDT",
        #                     "funding_rate": "0.000091412174",
        #                     "funding_time": "1734336000000"
        #                 },
        #             ]
        #         },
        #         "trace": "fg73d949fgfdf6a40c8fc7f5ae6738.54.345345345345"
        #     }
        #
        data = self.safe_dict(response, 'data', {})
        result = self.safe_list(data, 'list', [])
        rates = []
        for i in range(0, len(result)):
            entry = result[i]
            marketId = self.safe_string(entry, 'symbol')
            symbolInner = self.safe_symbol(marketId, market, '-', 'swap')
            timestamp = self.safe_integer(entry, 'funding_time')
            rates.append({
                'info': entry,
                'symbol': symbolInner,
                'fundingRate': self.safe_number(entry, 'funding_rate'),
                'timestamp': timestamp,
                'datetime': self.iso8601(timestamp),
            })
        sorted = self.sort_by(rates, 'timestamp')
        return self.filter_by_symbol_since_limit(sorted, market['symbol'], since, limit)

    def parse_funding_rate(self, contract, market: Market = None) -> FundingRate:
        #
        #     {
        #         "symbol": "BTCUSDT",
        #         "expected_rate": "-0.0000238",
        #         "rate_value": "0.000009601106",
        #         "funding_time": 1761292800000,
        #         "funding_upper_limit": "0.0375",
        #         "funding_lower_limit": "-0.0375",
        #         "timestamp": 1761291544336
        #     }
        #
        marketId = self.safe_string(contract, 'symbol')
        timestamp = self.safe_integer(contract, 'timestamp')
        fundingTimestamp = self.safe_integer(contract, 'funding_time')
        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': fundingTimestamp,
            'fundingDatetime': self.iso8601(fundingTimestamp),
            'nextFundingRate': None,
            'nextFundingTimestamp': None,
            'nextFundingDatetime': None,
            'previousFundingRate': self.safe_number(contract, 'rate_value'),
            'previousFundingTimestamp': None,
            'previousFundingDatetime': None,
            'interval': None,
        }

    async def fetch_position(self, symbol: str, params={}):
        """
        fetch data on a single open contract trade position

        https://developer-pro.bitmart.com/en/futuresv2/#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>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'symbol': market['id'],
        }
        response = await 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_list(response, 'data', [])
        first = self.safe_dict(data, 0, {})
        return self.parse_position(first, market)

    async def fetch_positions(self, symbols: Strings = None, params={}) -> List[Position]:
        """
        fetch all open contract positions

        https://developer-pro.bitmart.com/en/futuresv2/#get-current-position-keyed
        https://developer-pro.bitmart.com/en/futuresv2/#get-current-position-v2-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>`
        """
        await 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 = await self.privateGetContractPrivatePositionV2(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_list(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,
        })

    async def fetch_my_liquidations(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        retrieves the users liquidated positions

        https://developer-pro.bitmart.com/en/futuresv2/#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')
        await 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 = await 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_list(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),
        })

    async def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}) -> Order:
        """
        edits an open order

        https://developer-pro.bitmart.com/en/futuresv2/#modify-plan-order-signed
        https://developer-pro.bitmart.com/en/futuresv2/#modify-tp-sl-order-signed
        https://developer-pro.bitmart.com/en/futuresv2/#modify-preset-plan-order-signed
        https://developer-pro.bitmart.com/en/futuresv2/#modify-limit-order-signed

        :param str id: order id
        :param str symbol: unified symbol of the market to edit an order in
        :param str type: 'market' or 'limit'
        :param str side: 'buy' or 'sell'
        :param float [amount]: how much you want to trade in units of the base currency
        :param float [price]: the price to fulfill the order, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.triggerPrice]: *swap only* the price to trigger a stop order
        :param str [params.stopLossPrice]: *swap only* the price to trigger a stop-loss order
        :param str [params.takeProfitPrice]: *swap only* the price to trigger a take-profit order
        :param str [params.stopLoss.triggerPrice]: *swap only* the price to trigger a preset stop-loss order
        :param str [params.takeProfit.triggerPrice]: *swap only* the price to trigger a preset take-profit order
        :param str [params.clientOrderId]: client order id of the order
        :param int [params.price_type]: *swap only* 1: last price, 2: fair price, default is 1
        :param int [params.plan_category]: *swap tp/sl only* 1: tp/sl, 2: position tp/sl, default is 1
        :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        if not market['swap']:
            raise NotSupported(self.id + ' editOrder() does not support ' + market['type'] + ' markets, only swap markets are supported')
        stopLossPrice = self.safe_string(params, 'stopLossPrice')
        takeProfitPrice = self.safe_string(params, 'takeProfitPrice')
        triggerPrice = self.safe_string_n(params, ['triggerPrice', 'stopPrice', 'trigger_price'])
        stopLoss = self.safe_dict(params, 'stopLoss', {})
        takeProfit = self.safe_dict(params, 'takeProfit', {})
        presetStopLoss = self.safe_string(stopLoss, 'triggerPrice')
        presetTakeProfit = self.safe_string(takeProfit, 'triggerPrice')
        isTriggerOrder = triggerPrice is not None
        isStopLoss = stopLossPrice is not None
        isTakeProfit = takeProfitPrice is not None
        isPresetStopLoss = presetStopLoss is not None
        isPresetTakeProfit = presetTakeProfit is not None
        isLimitOrder = (type == 'limit')
        request: dict = {
            'symbol': market['id'],
        }
        clientOrderId = self.safe_string(params, 'clientOrderId')
        if clientOrderId is not None:
            params = self.omit(params, 'clientOrderId')
            request['client_order_id'] = clientOrderId
        if id is not None:
            request['order_id'] = id
        params = self.omit(params, ['triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice', 'stopLoss', 'takeProfit'])
        response = None
        if isTriggerOrder or isStopLoss or isTakeProfit:
            request['price_type'] = self.safe_integer(params, 'price_type', 1)
            if price is not None:
                request['executive_price'] = self.price_to_precision(symbol, price)
        if isTriggerOrder:
            request['type'] = type
            request['trigger_price'] = self.price_to_precision(symbol, triggerPrice)
            response = await self.privatePostContractPrivateModifyPlanOrder(self.extend(request, params))
            #
            #     {
            #         "code": 1000,
            #         "message": "Ok",
            #         "data": {
            #             "order_id": "3000023150003503"
            #         },
            #         "trace": "324523453245.108.1734567125596324575"
            #     }
            #
        elif isStopLoss or isTakeProfit:
            request['category'] = type
            if isStopLoss:
                request['trigger_price'] = self.price_to_precision(symbol, stopLossPrice)
            else:
                request['trigger_price'] = self.price_to_precision(symbol, takeProfitPrice)
            response = await self.privatePostContractPrivateModifyTpSlOrder(self.extend(request, params))
            #
            #     {
            #         "code": 1000,
            #         "message": "Ok",
            #         "data": {
            #             "order_id": "3000023150003480"
            #         },
            #         "trace": "23452345.104.1724536582682345459"
            #     }
            #
        elif isPresetStopLoss or isPresetTakeProfit:
            if isPresetStopLoss:
                request['preset_stop_loss_price_type'] = self.safe_integer(params, 'price_type', 1)
                request['preset_stop_loss_price'] = self.price_to_precision(symbol, presetStopLoss)
            else:
                request['preset_take_profit_price_type'] = self.safe_integer(params, 'price_type', 1)
                request['preset_take_profit_price'] = self.price_to_precision(symbol, presetTakeProfit)
            response = await self.privatePostContractPrivateModifyPresetPlanOrder(self.extend(request, params))
            #
            #     {
            #         "code": 1000,
            #         "message": "Ok",
            #         "data": {
            #             "order_id": "3000023150003496"
            #         },
            #         "trace": "a5c3234534534a836bc476a203.123452.172716624359200197"
            #     }
            #
        elif isLimitOrder:
            request['order_id'] = self.parse_to_int(id)  # reparse id self endpoint is the only one requiring it
            if amount is not None:
                request['size'] = self.amount_to_precision(symbol, amount)
            if price is not None:
                request['price'] = self.price_to_precision(symbol, price)
            response = await self.privatePostContractPrivateModifyLimitOrder(self.extend(request, params))
        else:
            raise NotSupported(self.id + ' editOrder() only supports limit, trigger, stop loss and take profit orders')
        data = self.safe_dict(response, 'data', {})
        return self.parse_order(data, market)

    async def fetch_ledger(self, code: Str = None, since: Int = None, limit: Int = None, params={}) -> List[LedgerEntry]:
        """
        fetch the history of changes, actions done by the user or operations that altered the balance of the user

        https://developer-pro.bitmart.com/en/futuresv2/#get-transaction-history-keyed

        :param str [code]: unified currency code
        :param int [since]: timestamp in ms of the earliest ledger entry
        :param int [limit]: max number of ledger entries to return
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms of the latest ledger entry
        :returns dict[]: a list of `ledger structures <https://docs.ccxt.com/#/?id=ledger>`
        """
        await self.load_markets()
        currency = None
        if code is not None:
            currency = self.currency(code)
        request: dict = {}
        request, params = self.handle_until_option('end_time', request, params)
        transactionsRequest = self.fetch_transactions_request(0, None, since, limit, params)
        response = await self.privateGetContractPrivateTransactionHistory(transactionsRequest)
        #
        #     {
        #         "code": 1000,
        #         "message": "Ok",
        #         "data": [
        #             {
        #                 "time": "1734422402121",
        #                 "type": "Funding Fee",
        #                 "amount": "-0.00008253",
        #                 "asset": "USDT",
        #                 "symbol": "LTCUSDT",
        #                 "tran_id": "1734422402121",
        #                 "flow_type": 3
        #             },
        #         ],
        #         "trace": "4cd11f83c71egfhfgh842790f07241e.23.173442343427772866"
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_ledger(data, currency, since, limit)

    def parse_ledger_entry(self, item: dict, currency: Currency = None) -> LedgerEntry:
        #
        #     {
        #         "time": "1734422402121",
        #         "type": "Funding Fee",
        #         "amount": "-0.00008253",
        #         "asset": "USDT",
        #         "symbol": "LTCUSDT",
        #         "tran_id": "1734422402121",
        #         "flow_type": 3
        #     }
        #
        amount = self.safe_string(item, 'amount')
        direction = None
        if Precise.string_le(amount, '0'):
            direction = 'out'
            amount = Precise.string_mul('-1', amount)
        else:
            direction = 'in'
        currencyId = self.safe_string(item, 'asset')
        currency = self.safe_currency(currencyId, currency)
        timestamp = self.safe_integer(item, 'time')
        type = self.safe_string(item, 'type')
        return self.safe_ledger_entry({
            'info': item,
            'id': self.safe_string(item, 'tran_id'),
            'direction': direction,
            'account': None,
            'referenceAccount': None,
            'referenceId': self.safe_string(item, 'tradeId'),
            'type': self.parse_ledger_entry_type(type),
            'currency': currency['code'],
            'amount': self.parse_number(amount),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'before': None,
            'after': None,
            'status': None,
            'fee': None,
        }, currency)

    def parse_ledger_entry_type(self, type):
        ledgerType: dict = {
            'Commission Fee': 'fee',
            'Funding Fee': 'fee',
            'Realized PNL': 'trade',
            'Transfer': 'transfer',
            'Liquidation Clearance': 'settlement',
        }
        return self.safe_string(ledgerType, type, type)

    def fetch_transactions_request(self, flowType: Int = None, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        request: dict = {}
        if flowType is not None:
            request['flow_type'] = flowType
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        if since is not None:
            request['start_time'] = since
        if limit is not None:
            request['page_size'] = limit
        request, params = self.handle_until_option('end_time', request, params)
        return self.extend(request, params)

    async def fetch_funding_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[FundingHistory]:
        """
        fetch the history of funding payments paid and received on self account

        https://developer-pro.bitmart.com/en/futuresv2/#get-transaction-history-keyed

        :param str [symbol]: unified market symbol
        :param int [since]: the starting timestamp in milliseconds
        :param int [limit]: the number of entries to return
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: the latest time in ms to fetch funding history for
        :returns dict[]: a list of `funding history structures <https://docs.ccxt.com/#/?id=funding-history-structure>`
        """
        await self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
        request: dict = {}
        request, params = self.handle_until_option('end_time', request, params)
        transactionsRequest = self.fetch_transactions_request(3, symbol, since, limit, params)
        response = await self.privateGetContractPrivateTransactionHistory(transactionsRequest)
        #
        #     {
        #         "code": 1000,
        #         "message": "Ok",
        #         "data": [
        #             {
        #                 "time": "1734422402121",
        #                 "type": "Funding Fee",
        #                 "amount": "-0.00008253",
        #                 "asset": "USDT",
        #                 "symbol": "LTCUSDT",
        #                 "tran_id": "1734422402121",
        #                 "flow_type": 3
        #             },
        #         ],
        #         "trace": "4cd11f83c71egfhfgh842790f07241e.23.173442343427772866"
        #     }
        #
        data = self.safe_list(response, 'data', [])
        return self.parse_funding_histories(data, market, since, limit)

    def parse_funding_history(self, contract, market: Market = None):
        #
        #     {
        #         "time": "1734422402121",
        #         "type": "Funding Fee",
        #         "amount": "-0.00008253",
        #         "asset": "USDT",
        #         "symbol": "LTCUSDT",
        #         "tran_id": "1734422402121",
        #         "flow_type": 3
        #     }
        #
        marketId = self.safe_string(contract, 'symbol')
        currencyId = self.safe_string(contract, 'asset')
        timestamp = self.safe_integer(contract, 'time')
        return {
            'info': contract,
            'symbol': self.safe_symbol(marketId, market, None, 'swap'),
            'code': self.safe_currency_code(currencyId),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'id': self.safe_string(contract, 'tran_id'),
            'amount': self.safe_number(contract, 'amount'),
        }

    def parse_funding_histories(self, contracts, market=None, since: Int = None, limit: Int = None) -> List[FundingHistory]:
        result = []
        for i in range(0, len(contracts)):
            contract = contracts[i]
            result.append(self.parse_funding_history(contract, market))
        sorted = self.sort_by(result, 'timestamp')
        return self.filter_by_since_limit(sorted, since, limit)

    async def fetch_withdraw_addresses(self, code: str, note=None, networkCode=None, params={}):
        await self.load_markets()
        codes = None
        if code is not None:
            currency = self.currency(code)
            code = currency['code']
            codes = [code]
        response = await self.privateGetAccountV1WithdrawAddressList(params)
        #
        #     {
        #         "message": "OK",
        #         "code": 1000,
        #         "trace": "0e6edd79-f77f-4251-abe5-83ba75d06c1a",
        #         "data": {
        #             "list": [
        #                 {
        #                     "currency": "ETH",
        #                     "network": "ETH",
        #                     "address": "0x1121",
        #                     "memo": "12",
        #                     "remark": "12",
        #                     "addressType": 0,
        #                     "verifyStatus": 0
        #                 }
        #             ]
        #         }
        #     }
        #
        data = self.safe_dict(response, 'data', {})
        list = self.safe_list(data, 'list', [])
        allAddresses = self.parse_deposit_addresses(list, codes, False)
        addresses = []
        for i in range(0, len(allAddresses)):
            address = allAddresses[i]
            noteMatch = (note is None) or (address['note'] == note)
            networkMatch = (networkCode is None) or (address['network'] == networkCode)
            if noteMatch and networkMatch:
                addresses.append(address)
        return addresses

    async def set_position_mode(self, hedged: bool, symbol: Str = None, params={}):
        """
        set hedged to True or False for a market

        https://developer-pro.bitmart.com/en/futuresv2/#submit-leverage-signed

        :param bool hedged: set to True to use dualSidePosition
        :param str symbol: not used by bingx setPositionMode()
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: response from the exchange
        """
        await self.load_markets()
        positionMode = None
        if hedged:
            positionMode = 'hedge_mode'
        else:
            positionMode = 'one_way_mode'
        request: dict = {
            'position_mode': positionMode,
        }
        #
        # {
        #     "code": 1000,
        #     "trace": "0cc6f4c4-8b8c-4253-8e90-8d3195aa109c",
        #     "message": "Ok",
        #     "data": {
        #       "position_mode":"one_way_mode"
        #     }
        # }
        #
        return await self.privatePostContractPrivateSetPositionMode(self.extend(request, params))

    async def fetch_position_mode(self, symbol: Str = None, params={}):
        """
        fetchs the position mode, hedged or one way, hedged for binance is set identically for all linear markets or all inverse markets

        https://developer-pro.bitmart.com/en/futuresv2/#get-position-mode-keyed

        :param str symbol: not used
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an object detailing whether the market is in hedged or one-way mode
        """
        response = await self.privateGetContractPrivateGetPositionMode(params)
        #
        # {
        #     "code": 1000,
        #     "trace": "0cc6f4c4-8b8c-4253-8e90-8d3195aa109c",
        #     "message": "Ok",
        #     "data": {
        #       "position_mode":"one_way_mode"
        #     }
        # }
        #
        data = self.safe_dict(response, 'data')
        positionMode = self.safe_string(data, 'position_mode')
        return {
            'info': response,
            'hedged': (positionMode == 'hedge_mode'),
        }

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

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        parts = path.split('/')
        # to do: refactor api endpoints with spot/swap sections
        category = self.safe_string(parts, 0, 'spot')
        market = 'spot' if (category == 'spot' or category == 'account') else 'swap'
        baseUrl = self.implode_hostname(self.urls['api'][market])
        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.nonce())
            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":{}}
        #     {"code":40012,"message":"You contract account available balance not enough.","trace":"..."}
        #
        # 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'], message, feedback)
            self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
            self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
            self.throw_broadly_matched_exception(self.exceptions['broad'], errorCode, feedback)
            raise ExchangeError(feedback)  # unknown message
        return None
