# -*- 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.gate import ImplicitAPI
import asyncio
import hashlib
from ccxt.base.types import Any, Balances, BorrowInterest, Bool, Currencies, Currency, DepositAddress, FundingHistory, Greeks, Int, LedgerEntry, Leverage, Leverages, LeverageTier, LeverageTiers, MarginModification, Market, Num, Option, OptionChain, Order, OrderBook, OrderRequest, CancellationRequest, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, FundingRate, FundingRates, Trade, TradingFeeInterface, TradingFees, 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 AccountNotEnabled
from ccxt.base.errors import AccountSuspended
from ccxt.base.errors import ArgumentsRequired
from ccxt.base.errors import BadRequest
from ccxt.base.errors import BadSymbol
from ccxt.base.errors import InsufficientFunds
from ccxt.base.errors import InvalidOrder
from ccxt.base.errors import OrderNotFound
from ccxt.base.errors import OrderImmediatelyFillable
from ccxt.base.errors import NotSupported
from ccxt.base.errors import RateLimitExceeded
from ccxt.base.errors import ExchangeNotAvailable
from ccxt.base.decimal_to_precision import TICK_SIZE
from ccxt.base.precise import Precise


class gate(Exchange, ImplicitAPI):

    def describe(self) -> Any:
        return self.deep_extend(super(gate, self).describe(), {
            'id': 'gate',
            'name': 'Gate.io',
            'countries': ['KR'],
            'rateLimit': 50,  # 200 requests per 10 second or 50ms
            'version': 'v4',
            'certified': True,
            'pro': True,
            'urls': {
                'logo': 'https://github.com/user-attachments/assets/64f988c5-07b6-4652-b5c1-679a6bf67c85',
                'doc': 'https://www.gate.io/docs/developers/apiv4/en/',
                'www': 'https://gate.io/',
                'api': {
                    'public': {
                        'wallet': 'https://api.gateio.ws/api/v4',
                        'futures': 'https://api.gateio.ws/api/v4',
                        'margin': 'https://api.gateio.ws/api/v4',
                        'delivery': 'https://api.gateio.ws/api/v4',
                        'spot': 'https://api.gateio.ws/api/v4',
                        'options': 'https://api.gateio.ws/api/v4',
                        'sub_accounts': 'https://api.gateio.ws/api/v4',
                        'earn': 'https://api.gateio.ws/api/v4',
                    },
                    'private': {
                        'withdrawals': 'https://api.gateio.ws/api/v4',
                        'wallet': 'https://api.gateio.ws/api/v4',
                        'futures': 'https://api.gateio.ws/api/v4',
                        'margin': 'https://api.gateio.ws/api/v4',
                        'delivery': 'https://api.gateio.ws/api/v4',
                        'spot': 'https://api.gateio.ws/api/v4',
                        'options': 'https://api.gateio.ws/api/v4',
                        'subAccounts': 'https://api.gateio.ws/api/v4',
                        'unified': 'https://api.gateio.ws/api/v4',
                        'rebate': 'https://api.gateio.ws/api/v4',
                        'earn': 'https://api.gateio.ws/api/v4',
                        'account': 'https://api.gateio.ws/api/v4',
                        'loan': 'https://api.gateio.ws/api/v4',
                    },
                },
                'test': {
                    'public': {
                        'futures': 'https://fx-api-testnet.gateio.ws/api/v4',
                        'delivery': 'https://fx-api-testnet.gateio.ws/api/v4',
                        'options': 'https://fx-api-testnet.gateio.ws/api/v4',
                    },
                    'private': {
                        'futures': 'https://fx-api-testnet.gateio.ws/api/v4',
                        'delivery': 'https://fx-api-testnet.gateio.ws/api/v4',
                        'options': 'https://fx-api-testnet.gateio.ws/api/v4',
                    },
                },
                'referral': {
                    'url': 'https://www.gate.io/signup/2436035',
                    'discount': 0.2,
                },
            },
            'has': {
                'CORS': None,
                'spot': True,
                'margin': True,
                'swap': True,
                'future': True,
                'option': True,
                'addMargin': True,
                'borrowCrossMargin': True,
                'borrowIsolatedMargin': True,
                'cancelAllOrders': True,
                'cancelOrder': True,
                'cancelOrders': True,
                'cancelOrdersForSymbols': True,
                'createMarketBuyOrderWithCost': True,
                'createMarketOrder': True,
                'createMarketOrderWithCost': False,
                'createMarketSellOrderWithCost': False,
                'createOrder': True,
                'createOrders': True,
                'createPostOnlyOrder': True,
                'createReduceOnlyOrder': True,
                'createStopLimitOrder': True,
                'createStopLossOrder': True,
                'createStopMarketOrder': False,
                'createStopOrder': True,
                'createTakeProfitOrder': True,
                'createTriggerOrder': True,
                'editOrder': True,
                'fetchBalance': True,
                'fetchBorrowInterest': True,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchClosedOrders': True,
                'fetchCrossBorrowRate': False,
                'fetchCrossBorrowRates': False,
                'fetchCurrencies': True,
                'fetchDepositAddress': True,
                'fetchDepositAddresses': False,
                'fetchDepositAddressesByNetwork': False,
                'fetchDeposits': True,
                'fetchDepositWithdrawFee': 'emulated',
                'fetchDepositWithdrawFees': True,
                'fetchFundingHistory': True,
                'fetchFundingRate': True,
                'fetchFundingRateHistory': True,
                'fetchFundingRates': True,
                'fetchGreeks': True,
                'fetchIndexOHLCV': True,
                'fetchIsolatedBorrowRate': False,
                'fetchIsolatedBorrowRates': False,
                'fetchLedger': True,
                'fetchLeverage': True,
                'fetchLeverages': True,
                'fetchLeverageTiers': True,
                'fetchLiquidations': True,
                'fetchMarginAdjustmentHistory': False,
                'fetchMarginMode': False,
                'fetchMarketLeverageTiers': True,
                'fetchMarkets': True,
                'fetchMarkOHLCV': True,
                'fetchMyLiquidations': True,
                'fetchMySettlementHistory': True,
                'fetchMyTrades': True,
                'fetchNetworkDepositAddress': True,
                'fetchOHLCV': True,
                'fetchOpenInterest': False,
                'fetchOpenInterestHistory': True,
                'fetchOpenOrders': True,
                'fetchOption': True,
                'fetchOptionChain': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchPosition': True,
                'fetchPositionHistory': 'emulated',
                'fetchPositionMode': False,
                'fetchPositions': True,
                'fetchPositionsHistory': True,
                'fetchPremiumIndexOHLCV': False,
                'fetchSettlementHistory': True,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': True,
                'fetchTrades': True,
                'fetchTradingFee': True,
                'fetchTradingFees': True,
                'fetchTransactionFees': True,
                'fetchUnderlyingAssets': True,
                'fetchVolatilityHistory': False,
                'fetchWithdrawals': True,
                'reduceMargin': True,
                'repayCrossMargin': True,
                'repayIsolatedMargin': True,
                'sandbox': True,
                'setLeverage': True,
                'setMarginMode': False,
                'setPositionMode': True,
                'signIn': False,
                'transfer': True,
                'withdraw': True,
            },
            'api': {
                'public': {
                    # All public endpoints 200r/10s per endpoint
                    'wallet': {
                        'get': {
                            'currency_chains': 1,
                        },
                    },
                    'spot': {
                        'get': {
                            'currencies': 1,
                            'currencies/{currency}': 1,
                            'currency_pairs': 1,
                            'currency_pairs/{currency_pair}': 1,
                            'tickers': 1,
                            'order_book': 1,
                            'trades': 1,
                            'candlesticks': 1,
                            'time': 1,
                        },
                    },
                    'margin': {
                        'get': {
                            'currency_pairs': 1,
                            'currency_pairs/{currency_pair}': 1,
                            'funding_book': 1,
                            'cross/currencies': 1,
                            'cross/currencies/{currency}': 1,
                            'uni/currency_pairs': 1,
                            'uni/currency_pairs/{currency_pair}': 1,
                        },
                    },
                    'flash_swap': {
                        'get': {
                            'currencies': 1,
                        },
                    },
                    'futures': {
                        'get': {
                            '{settle}/contracts': 1,
                            '{settle}/contracts/{contract}': 1,
                            '{settle}/order_book': 1,
                            '{settle}/trades': 1,
                            '{settle}/candlesticks': 1,
                            '{settle}/premium_index': 1,
                            '{settle}/tickers': 1,
                            '{settle}/funding_rate': 1,
                            '{settle}/insurance': 1,
                            '{settle}/contract_stats': 1,
                            '{settle}/index_constituents/{index}': 1,
                            '{settle}/liq_orders': 1,
                            '{settle}/risk_limit_tiers': 1,
                        },
                    },
                    'delivery': {
                        'get': {
                            '{settle}/contracts': 1,
                            '{settle}/contracts/{contract}': 1,
                            '{settle}/order_book': 1,
                            '{settle}/trades': 1,
                            '{settle}/candlesticks': 1,
                            '{settle}/tickers': 1,
                            '{settle}/insurance': 1,
                        },
                    },
                    'options': {
                        'get': {
                            'underlyings': 1,
                            'expirations': 1,
                            'contracts': 1,
                            'contracts/{contract}': 1,
                            'settlements': 1,
                            'settlements/{contract}': 1,
                            'order_book': 1,
                            'tickers': 1,
                            'underlying/tickers/{underlying}': 1,
                            'candlesticks': 1,
                            'underlying/candlesticks': 1,
                            'trades': 1,
                        },
                    },
                    'earn': {
                        'get': {
                            'uni/currencies': 1,
                            'uni/currencies/{currency}': 1,
                        },
                    },
                },
                'private': {
                    # private endpoints default is 150r/10s per endpoint
                    'withdrawals': {
                        'post': {
                            'withdrawals': 20,  # 1r/s cost = 20 / 1 = 20
                            'push': 1,
                        },
                        'delete': {
                            'withdrawals/{withdrawal_id}': 1,
                        },
                    },
                    'wallet': {
                        'get': {
                            'deposit_address': 1,
                            'withdrawals': 1,
                            'deposits': 1,
                            'sub_account_transfers': 1,
                            'order_status': 1,
                            'withdraw_status': 1,
                            'sub_account_balances': 2.5,
                            'sub_account_margin_balances': 2.5,
                            'sub_account_futures_balances': 2.5,
                            'sub_account_cross_margin_balances': 2.5,
                            'saved_address': 1,
                            'fee': 1,
                            'total_balance': 2.5,
                            'small_balance': 1,
                            'small_balance_history': 1,
                            'push': 1,
                        },
                        'post': {
                            'transfers': 2.5,  # 8r/s cost = 20 / 8 = 2.5
                            'sub_account_transfers': 2.5,
                            'sub_account_to_sub_account': 2.5,
                            'small_balance': 1,
                        },
                    },
                    'subAccounts': {
                        'get': {
                            'sub_accounts': 2.5,
                            'sub_accounts/{user_id}': 2.5,
                            'sub_accounts/{user_id}/keys': 2.5,
                            'sub_accounts/{user_id}/keys/{key}': 2.5,
                        },
                        'post': {
                            'sub_accounts': 2.5,
                            'sub_accounts/{user_id}/keys': 2.5,
                            'sub_accounts/{user_id}/lock': 2.5,
                            'sub_accounts/{user_id}/unlock': 2.5,
                        },
                        'put': {
                            'sub_accounts/{user_id}/keys/{key}': 2.5,
                        },
                        'delete': {
                            'sub_accounts/{user_id}/keys/{key}': 2.5,
                        },
                    },
                    'unified': {
                        'get': {
                            'accounts': 20 / 15,
                            'account_mode': 20 / 15,
                            'borrowable': 20 / 15,
                            'transferable': 20 / 15,
                            'loans': 20 / 15,
                            'loan_records': 20 / 15,
                            'interest_records': 20 / 15,
                            'estimate_rate': 20 / 15,
                            'currency_discount_tiers': 20 / 15,
                            'risk_units': 20 / 15,
                            'unified_mode': 20 / 15,
                            'loan_margin_tiers': 20 / 15,
                            'leverage/user_currency_config': 20 / 15,
                            'leverage/user_currency_setting': 20 / 15,
                        },
                        'post': {
                            'account_mode': 20 / 15,
                            'loans': 200 / 15,  # 15r/10s cost = 20 / 1.5 = 13.33
                            'portfolio_calculator': 20 / 15,
                            'leverage/user_currency_setting': 20 / 15,
                        },
                        'put': {
                            'unified_mode': 20 / 15,
                        },
                    },
                    'spot': {
                        # default is 200r/10s
                        'get': {
                            'fee': 1,
                            'batch_fee': 1,
                            'accounts': 1,
                            'account_book': 1,
                            'open_orders': 1,
                            'orders': 1,
                            'orders/{order_id}': 1,
                            'my_trades': 1,
                            'price_orders': 1,
                            'price_orders/{order_id}': 1,
                        },
                        'post': {
                            'batch_orders': 0.4,
                            'cross_liquidate_orders': 1,
                            'orders': 0.4,
                            'cancel_batch_orders': 20 / 75,
                            'countdown_cancel_all': 20 / 75,
                            'amend_batch_orders': 0.4,
                            'price_orders': 0.4,
                        },
                        'delete': {
                            'orders': 20 / 75,
                            'orders/{order_id}': 20 / 75,
                            'price_orders': 20 / 75,
                            'price_orders/{order_id}': 20 / 75,
                        },
                        'patch': {
                            'orders/{order_id}': 0.4,
                        },
                    },
                    'margin': {
                        'get': {
                            'accounts': 20 / 15,
                            'account_book': 20 / 15,
                            'funding_accounts': 20 / 15,
                            'auto_repay': 20 / 15,
                            'transferable': 20 / 15,
                            'loans': 20 / 15,
                            'loans/{loan_id}': 20 / 15,
                            'loans/{loan_id}/repayment': 20 / 15,
                            'loan_records': 20 / 15,
                            'loan_records/{loan_record_id}': 20 / 15,
                            'borrowable': 20 / 15,
                            'cross/accounts': 20 / 15,
                            'cross/account_book': 20 / 15,
                            'cross/loans': 20 / 15,
                            'cross/loans/{loan_id}': 20 / 15,
                            'cross/repayments': 20 / 15,
                            'cross/interest_records': 20 / 15,
                            'cross/transferable': 20 / 15,
                            'cross/estimate_rate': 20 / 15,
                            'cross/borrowable': 20 / 15,
                            'uni/estimate_rate': 20 / 15,
                            'uni/loans': 20 / 15,
                            'uni/loan_records': 20 / 15,
                            'uni/interest_records': 20 / 15,
                            'uni/borrowable': 20 / 15,
                        },
                        'post': {
                            'auto_repay': 20 / 15,
                            'loans': 20 / 15,
                            'merged_loans': 20 / 15,
                            'loans/{loan_id}/repayment': 20 / 15,
                            'cross/loans': 20 / 15,
                            'cross/repayments': 20 / 15,
                            'uni/loans': 20 / 15,
                        },
                        'patch': {
                            'loans/{loan_id}': 20 / 15,
                            'loan_records/{loan_record_id}': 20 / 15,
                        },
                        'delete': {
                            'loans/{loan_id}': 20 / 15,
                        },
                    },
                    'flash_swap': {
                        'get': {
                            'currencies': 1,
                            'currency_pairs': 1,
                            'orders': 1,
                            'orders/{order_id}': 1,
                        },
                        'post': {
                            'orders': 1,
                            'orders/preview': 1,
                        },
                    },
                    'futures': {
                        'get': {
                            '{settle}/accounts': 1,
                            '{settle}/account_book': 1,
                            '{settle}/positions': 1,
                            '{settle}/positions/{contract}': 1,
                            '{settle}/dual_comp/positions/{contract}': 1,
                            '{settle}/orders': 1,
                            '{settle}/orders_timerange': 1,
                            '{settle}/orders/{order_id}': 1,
                            '{settle}/my_trades': 1,
                            '{settle}/my_trades_timerange': 1,
                            '{settle}/position_close': 1,
                            '{settle}/liquidates': 1,
                            '{settle}/auto_deleverages': 1,
                            '{settle}/fee': 1,
                            '{settle}/risk_limit_tiers': 1,
                            '{settle}/price_orders': 1,
                            '{settle}/price_orders/{order_id}': 1,
                        },
                        'post': {
                            '{settle}/positions/{contract}/margin': 1,
                            '{settle}/positions/{contract}/leverage': 1,
                            '{settle}/positions/{contract}/risk_limit': 1,
                            '{settle}/dual_mode': 1,
                            '{settle}/dual_comp/positions/{contract}/margin': 1,
                            '{settle}/dual_comp/positions/{contract}/leverage': 1,
                            '{settle}/dual_comp/positions/{contract}/risk_limit': 1,
                            '{settle}/orders': 0.4,
                            '{settle}/batch_orders': 0.4,
                            '{settle}/countdown_cancel_all': 0.4,
                            '{settle}/batch_cancel_orders': 0.4,
                            '{settle}/price_orders': 0.4,
                        },
                        'put': {
                            '{settle}/orders/{order_id}': 1,
                        },
                        'delete': {
                            '{settle}/orders': 20 / 75,
                            '{settle}/orders/{order_id}': 20 / 75,
                            '{settle}/price_orders': 20 / 75,
                            '{settle}/price_orders/{order_id}': 20 / 75,
                        },
                    },
                    'delivery': {
                        'get': {
                            '{settle}/accounts': 20 / 15,
                            '{settle}/account_book': 20 / 15,
                            '{settle}/positions': 20 / 15,
                            '{settle}/positions/{contract}': 20 / 15,
                            '{settle}/orders': 20 / 15,
                            '{settle}/orders/{order_id}': 20 / 15,
                            '{settle}/my_trades': 20 / 15,
                            '{settle}/position_close': 20 / 15,
                            '{settle}/liquidates': 20 / 15,
                            '{settle}/settlements': 20 / 15,
                            '{settle}/price_orders': 20 / 15,
                            '{settle}/price_orders/{order_id}': 20 / 15,
                        },
                        'post': {
                            '{settle}/positions/{contract}/margin': 20 / 15,
                            '{settle}/positions/{contract}/leverage': 20 / 15,
                            '{settle}/positions/{contract}/risk_limit': 20 / 15,
                            '{settle}/orders': 20 / 15,
                            '{settle}/price_orders': 20 / 15,
                        },
                        'delete': {
                            '{settle}/orders': 20 / 15,
                            '{settle}/orders/{order_id}': 20 / 15,
                            '{settle}/price_orders': 20 / 15,
                            '{settle}/price_orders/{order_id}': 20 / 15,
                        },
                    },
                    'options': {
                        'get': {
                            'my_settlements': 20 / 15,
                            'accounts': 20 / 15,
                            'account_book': 20 / 15,
                            'positions': 20 / 15,
                            'positions/{contract}': 20 / 15,
                            'position_close': 20 / 15,
                            'orders': 20 / 15,
                            'orders/{order_id}': 20 / 15,
                            'my_trades': 20 / 15,
                            'mmp': 20 / 15,
                        },
                        'post': {
                            'orders': 20 / 15,
                            'countdown_cancel_all': 20 / 15,
                            'mmp': 20 / 15,
                            'mmp/reset': 20 / 15,
                        },
                        'delete': {
                            'orders': 20 / 15,
                            'orders/{order_id}': 20 / 15,
                        },
                    },
                    'earn': {
                        'get': {
                            'uni/currencies': 20 / 15,
                            'uni/currencies/{currency}': 20 / 15,
                            'uni/lends': 20 / 15,
                            'uni/lend_records': 20 / 15,
                            'uni/interests/{currency}': 20 / 15,
                            'uni/interest_records': 20 / 15,
                            'uni/interest_status/{currency}': 20 / 15,
                        },
                        'post': {
                            'uni/lends': 20 / 15,
                        },
                        'put': {
                            'uni/interest_reinvest': 20 / 15,
                        },
                        'patch': {
                            'uni/lends': 20 / 15,
                        },
                    },
                    'loan': {
                        'get': {
                            'collateral/orders': 20 / 15,
                            'collateral/orders/{order_id}': 20 / 15,
                            'collateral/repay_records': 20 / 15,
                            'collateral/collaterals': 20 / 15,
                            'collateral/total_amount': 20 / 15,
                            'collateral/ltv': 20 / 15,
                            'collateral/currencies': 20 / 15,
                            'multi_collateral/orders': 20 / 15,
                            'multi_collateral/orders/{order_id}': 20 / 15,
                            'multi_collateral/repay': 20 / 15,
                            'multi_collateral/mortgage': 20 / 15,
                            'multi_collateral/currency_quota': 20 / 15,
                            'multi_collateral/currencies': 20 / 15,
                            'multi_collateral/ltv': 20 / 15,
                            'multi_collateral/fixed_rate': 20 / 15,
                            'multi_collateral/current_rate': 20 / 15,
                        },
                        'post': {
                            'collateral/orders': 20 / 15,
                            'collateral/repay': 20 / 15,
                            'collateral/collaterals': 20 / 15,
                            'multi_collateral/orders': 20 / 15,
                            'multi_collateral/repay': 20 / 15,
                            'multi_collateral/mortgage': 20 / 15,
                        },
                    },
                    'account': {
                        'get': {
                            'detail': 20 / 15,
                            'rate_limit': 20 / 15,
                            'stp_groups': 20 / 15,
                            'stp_groups/{stp_id}/users': 20 / 15,
                            'stp_groups/debit_fee': 20 / 15,
                        },
                        'post': {
                            'stp_groups': 20 / 15,
                            'stp_groups/{stp_id}/users': 20 / 15,
                        },
                        'delete': {
                            'stp_groups/{stp_id}/users': 20 / 15,
                        },
                    },
                    'rebate': {
                        'get': {
                            'agency/transaction_history': 20 / 15,
                            'agency/commission_history': 20 / 15,
                        },
                    },
                },
            },
            'timeframes': {
                '10s': '10s',
                '1m': '1m',
                '5m': '5m',
                '15m': '15m',
                '30m': '30m',
                '1h': '1h',
                '2h': '2h',
                '4h': '4h',
                '8h': '8h',
                '1d': '1d',
                '7d': '7d',
                '1w': '7d',
            },
            # copied from gatev2
            'commonCurrencies': {
                'ORT': 'XREATORS',
                'ASS': 'ASSF',
                '88MPH': 'MPH',
                'AXIS': 'AXISDEFI',
                'BIFI': 'BITCOINFILE',
                'BOX': 'DEFIBOX',
                'BYN': 'BEYONDFI',
                'EGG': 'GOOSEFINANCE',
                'GTC': 'GAMECOM',  # conflict with Gitcoin and Gastrocoin
                'GTC_HT': 'GAMECOM_HT',
                'GTC_BSC': 'GAMECOM_BSC',
                'HIT': 'HITCHAIN',
                'MM': 'MILLION',  # conflict with MilliMeter
                'MPH': 'MORPHER',  # conflict with 88MPH
                'POINT': 'GATEPOINT',
                'RAI': 'RAIREFLEXINDEX',  # conflict with RAI Finance
                'RED': 'RedLang',
                'SBTC': 'SUPERBITCOIN',
                'TNC': 'TRINITYNETWORKCREDIT',
                'VAI': 'VAIOT',
                'TRAC': 'TRACO',  # conflict with OriginTrail(TRAC)
            },
            'requiredCredentials': {
                'apiKey': True,
                'secret': True,
            },
            'headers': {
                'X-Gate-Channel-Id': 'ccxt',
            },
            'options': {
                'timeDifference': 0,  # the difference between system clock and exchange clock
                'adjustForTimeDifference': False,  # controls the adjustment logic upon instantiation
                'sandboxMode': False,
                'unifiedAccount': None,
                'createOrder': {
                    'expiration': 86400,  # for conditional orders
                },
                'createMarketBuyOrderRequiresPrice': True,
                'networks': {
                    'BTC': 'BTC',
                    'BRC20': 'BTCBRC',  # for eg: ORDI, RATS, ...
                    'ETH': 'ETH',
                    'ERC20': 'ETH',
                    'TRX': 'TRX',
                    'TRC20': 'TRX',
                    'HECO': 'HT',
                    'HRC20': 'HT',
                    'BSC': 'BSC',
                    'BEP20': 'BSC',
                    'SOL': 'SOL',
                    'POLYGON': 'POL',
                    'MATIC': 'POL',
                    'OP': 'OPETH',
                    'OPTIMISM': 'OPETH',
                    'ADA': 'ADA',  # CARDANO
                    'AVAXC': 'AVAX_C',
                    'NEAR': 'NEAR',
                    'ARBONE': 'ARBEVM',
                    'BASE': 'BASEEVM',
                    'SUI': 'SUI',
                    'CRONOS': 'CRO',
                    'CRO': 'CRO',
                    'APT': 'APT',
                    'SCROLL': 'SCROLLETH',
                    'TAIKO': 'TAIKOETH',
                    'HYPE': 'HYPE',
                    'ALGO': 'ALGO',
                    # KAVA: ['KAVA', 'KAVAEVM']
                    # SEI: ['SEI', 'SEIEVM']
                    'LINEA': 'LINEAETH',
                    'BLAST': 'BLASTETH',
                    'XLM': 'XLM',
                    'RSK': 'RBTC',
                    'TON': 'TON',
                    'MNT': 'MNT',
                    # 'RUNE': 'BTCRUNES', probably, cant verify atm
                    'CELO': 'CELO',
                    'HBAR': 'HBAR',
                    # 'FTM': SONIC REBRAND, todo
                    'ZKSERA': 'ZKSERA',
                    'KLAY': 'KLAY',
                    'EOS': 'EOS',
                    'ACA': 'ACA',
                    # TLOS: ['TLOS', 'TLOSEVM']
                    # ASTR: ['ASTR', 'ASTREVM']
                    # CFX: ['CFX', 'CFXEVM']
                    'XTZ': 'XTZ',
                    'EGLD': 'EGLD',
                    'GLMR': 'GLMR',
                    'AURORA': 'AURORAEVM',
                    # others
                    'KON': 'KONET',
                    'GATECHAIN': 'GTEVM',
                    'KUSAMA': 'KSMSM',
                    'OKC': 'OKT',
                    'POLKADOT': 'DOTSM',  # todo: DOT for main DOT
                    'LUNA': 'LUNC',
                },
                'networksById': {
                    'OPETH': 'OP',
                },
                'timeInForce': {
                    'GTC': 'gtc',
                    'IOC': 'ioc',
                    'PO': 'poc',
                    'POC': 'poc',
                    'FOK': 'fok',
                },
                'accountsByType': {
                    'funding': 'spot',
                    'spot': 'spot',
                    'margin': 'margin',
                    'cross_margin': 'cross_margin',
                    'cross': 'cross_margin',
                    'isolated': 'margin',
                    'swap': 'futures',
                    'future': 'delivery',
                    'futures': 'futures',
                    'delivery': 'delivery',
                    'option': 'options',
                    'options': 'options',
                },
                'swap': {
                    'fetchMarkets': {
                        'settlementCurrencies': ['usdt', 'btc'],
                    },
                },
                'future': {
                    'fetchMarkets': {
                        'settlementCurrencies': ['usdt'],
                    },
                },
            },
            'features': {
                'default': {
                    'sandbox': True,
                    'createOrder': {
                        'marginMode': True,
                        'triggerPrice': True,
                        'triggerDirection': True,  # todo: implementation edit needed
                        'triggerPriceType': None,
                        'stopLossPrice': True,
                        'takeProfitPrice': True,
                        'attachedStopLossTakeProfit': None,
                        'timeInForce': {
                            'IOC': True,
                            'FOK': True,
                            'PO': True,
                            'GTD': False,
                        },
                        'hedged': False,
                        'trailing': False,
                        'iceberg': True,  # todo implement
                        'selfTradePrevention': True,  # todo implement
                        'leverage': False,
                        'marketBuyByCost': True,
                        'marketBuyRequiresPrice': True,
                    },
                    'createOrders': {
                        'max': 40,  # NOTE! max 10 per symbol
                    },
                    'fetchMyTrades': {
                        'marginMode': True,
                        'limit': 1000,
                        'daysBack': None,
                        'untilDays': 30,
                        'symbolRequired': False,
                    },
                    'fetchOrder': {
                        'marginMode': False,
                        'trigger': True,
                        'trailing': False,
                        'symbolRequired': True,
                    },
                    'fetchOpenOrders': {
                        'marginMode': True,
                        'trigger': True,
                        'trailing': False,
                        'limit': 100,
                        'symbolRequired': False,
                    },
                    'fetchOrders': None,
                    'fetchClosedOrders': {
                        'marginMode': True,
                        'trigger': True,
                        'trailing': False,
                        'limit': 100,
                        'untilDays': 30,
                        'daysBack': None,
                        'daysBackCanceled': None,
                        'symbolRequired': False,
                    },
                    'fetchOHLCV': {
                        'limit': 1000,
                    },
                },
                'spot': {
                    'extends': 'default',
                },
                'forDerivatives': {
                    'extends': 'spot',
                    'createOrder': {
                        'marginMode': False,
                        'triggerPriceType': {
                            'last': True,
                            'mark': True,
                            'index': True,
                        },
                    },
                    'createOrders': {
                        'max': 10,
                    },
                    'fetchMyTrades': {
                        'marginMode': False,
                        'untilDays': None,
                    },
                    'fetchOpenOrders': {
                        'marginMode': False,
                    },
                    'fetchClosedOrders': {
                        'marginMode': False,
                        'untilDays': None,
                        'limit': 1000,
                    },
                    'fetchOHLCV': {
                        'limit': 1999,
                    },
                },
                'swap': {
                    'linear': {
                        'extends': 'forDerivatives',
                    },
                    'inverse': {
                        'extends': 'forDerivatives',
                    },
                },
                'future': {
                    'linear': {
                        'extends': 'forDerivatives',
                    },
                    'inverse': {
                        'extends': 'forDerivatives',
                    },
                },
            },
            'precisionMode': TICK_SIZE,
            'fees': {
                'trading': {
                    'tierBased': True,
                    'feeSide': 'get',
                    'percentage': True,
                    'maker': self.parse_number('0.002'),
                    'taker': self.parse_number('0.002'),
                    'tiers': {
                        # volume is in BTC
                        'maker': [
                            [self.parse_number('0'), self.parse_number('0.002')],
                            [self.parse_number('1.5'), self.parse_number('0.00185')],
                            [self.parse_number('3'), self.parse_number('0.00175')],
                            [self.parse_number('6'), self.parse_number('0.00165')],
                            [self.parse_number('12.5'), self.parse_number('0.00155')],
                            [self.parse_number('25'), self.parse_number('0.00145')],
                            [self.parse_number('75'), self.parse_number('0.00135')],
                            [self.parse_number('200'), self.parse_number('0.00125')],
                            [self.parse_number('500'), self.parse_number('0.00115')],
                            [self.parse_number('1250'), self.parse_number('0.00105')],
                            [self.parse_number('2500'), self.parse_number('0.00095')],
                            [self.parse_number('3000'), self.parse_number('0.00085')],
                            [self.parse_number('6000'), self.parse_number('0.00075')],
                            [self.parse_number('11000'), self.parse_number('0.00065')],
                            [self.parse_number('20000'), self.parse_number('0.00055')],
                            [self.parse_number('40000'), self.parse_number('0.00055')],
                            [self.parse_number('75000'), self.parse_number('0.00055')],
                        ],
                        'taker': [
                            [self.parse_number('0'), self.parse_number('0.002')],
                            [self.parse_number('1.5'), self.parse_number('0.00195')],
                            [self.parse_number('3'), self.parse_number('0.00185')],
                            [self.parse_number('6'), self.parse_number('0.00175')],
                            [self.parse_number('12.5'), self.parse_number('0.00165')],
                            [self.parse_number('25'), self.parse_number('0.00155')],
                            [self.parse_number('75'), self.parse_number('0.00145')],
                            [self.parse_number('200'), self.parse_number('0.00135')],
                            [self.parse_number('500'), self.parse_number('0.00125')],
                            [self.parse_number('1250'), self.parse_number('0.00115')],
                            [self.parse_number('2500'), self.parse_number('0.00105')],
                            [self.parse_number('3000'), self.parse_number('0.00095')],
                            [self.parse_number('6000'), self.parse_number('0.00085')],
                            [self.parse_number('11000'), self.parse_number('0.00075')],
                            [self.parse_number('20000'), self.parse_number('0.00065')],
                            [self.parse_number('40000'), self.parse_number('0.00065')],
                            [self.parse_number('75000'), self.parse_number('0.00065')],
                        ],
                    },
                },
                'swap': {
                    'tierBased': True,
                    'feeSide': 'base',
                    'percentage': True,
                    'maker': self.parse_number('0.0'),
                    'taker': self.parse_number('0.0005'),
                    'tiers': {
                        'maker': [
                            [self.parse_number('0'), self.parse_number('0.0000')],
                            [self.parse_number('1.5'), self.parse_number('-0.00005')],
                            [self.parse_number('3'), self.parse_number('-0.00005')],
                            [self.parse_number('6'), self.parse_number('-0.00005')],
                            [self.parse_number('12.5'), self.parse_number('-0.00005')],
                            [self.parse_number('25'), self.parse_number('-0.00005')],
                            [self.parse_number('75'), self.parse_number('-0.00005')],
                            [self.parse_number('200'), self.parse_number('-0.00005')],
                            [self.parse_number('500'), self.parse_number('-0.00005')],
                            [self.parse_number('1250'), self.parse_number('-0.00005')],
                            [self.parse_number('2500'), self.parse_number('-0.00005')],
                            [self.parse_number('3000'), self.parse_number('-0.00008')],
                            [self.parse_number('6000'), self.parse_number('-0.01000')],
                            [self.parse_number('11000'), self.parse_number('-0.01002')],
                            [self.parse_number('20000'), self.parse_number('-0.01005')],
                            [self.parse_number('40000'), self.parse_number('-0.02000')],
                            [self.parse_number('75000'), self.parse_number('-0.02005')],
                        ],
                        'taker': [
                            [self.parse_number('0'), self.parse_number('0.00050')],
                            [self.parse_number('1.5'), self.parse_number('0.00048')],
                            [self.parse_number('3'), self.parse_number('0.00046')],
                            [self.parse_number('6'), self.parse_number('0.00044')],
                            [self.parse_number('12.5'), self.parse_number('0.00042')],
                            [self.parse_number('25'), self.parse_number('0.00040')],
                            [self.parse_number('75'), self.parse_number('0.00038')],
                            [self.parse_number('200'), self.parse_number('0.00036')],
                            [self.parse_number('500'), self.parse_number('0.00034')],
                            [self.parse_number('1250'), self.parse_number('0.00032')],
                            [self.parse_number('2500'), self.parse_number('0.00030')],
                            [self.parse_number('3000'), self.parse_number('0.00030')],
                            [self.parse_number('6000'), self.parse_number('0.00030')],
                            [self.parse_number('11000'), self.parse_number('0.00030')],
                            [self.parse_number('20000'), self.parse_number('0.00030')],
                            [self.parse_number('40000'), self.parse_number('0.00030')],
                            [self.parse_number('75000'), self.parse_number('0.00030')],
                        ],
                    },
                },
            },
            # https://www.gate.io/docs/developers/apiv4/en/#label-list
            'exceptions': {
                'exact': {
                    'INVALID_PARAM_VALUE': BadRequest,
                    'INVALID_PROTOCOL': BadRequest,
                    'INVALID_ARGUMENT': BadRequest,
                    'INVALID_REQUEST_BODY': BadRequest,
                    'MISSING_REQUIRED_PARAM': ArgumentsRequired,
                    'BAD_REQUEST': BadRequest,
                    'INVALID_CONTENT_TYPE': BadRequest,
                    'NOT_ACCEPTABLE': BadRequest,
                    'METHOD_NOT_ALLOWED': BadRequest,
                    'NOT_FOUND': ExchangeError,
                    'AUTHENTICATION_FAILED': AuthenticationError,
                    'INVALID_CREDENTIALS': AuthenticationError,
                    'INVALID_KEY': AuthenticationError,
                    'IP_FORBIDDEN': AuthenticationError,
                    'READ_ONLY': PermissionDenied,
                    'INVALID_SIGNATURE': AuthenticationError,
                    'MISSING_REQUIRED_HEADER': AuthenticationError,
                    'REQUEST_EXPIRED': AuthenticationError,
                    'ACCOUNT_LOCKED': AccountSuspended,
                    'FORBIDDEN': PermissionDenied,
                    'SUB_ACCOUNT_NOT_FOUND': ExchangeError,
                    'SUB_ACCOUNT_LOCKED': AccountSuspended,
                    'MARGIN_BALANCE_EXCEPTION': ExchangeError,
                    'MARGIN_TRANSFER_FAILED': ExchangeError,
                    'TOO_MUCH_FUTURES_AVAILABLE': ExchangeError,
                    'FUTURES_BALANCE_NOT_ENOUGH': InsufficientFunds,
                    'ACCOUNT_EXCEPTION': ExchangeError,
                    'SUB_ACCOUNT_TRANSFER_FAILED': ExchangeError,
                    'ADDRESS_NOT_USED': ExchangeError,
                    'TOO_FAST': RateLimitExceeded,
                    'WITHDRAWAL_OVER_LIMIT': ExchangeError,
                    'API_WITHDRAW_DISABLED': ExchangeNotAvailable,
                    'INVALID_WITHDRAW_ID': ExchangeError,
                    'INVALID_WITHDRAW_CANCEL_STATUS': ExchangeError,
                    'INVALID_PRECISION': InvalidOrder,
                    'INVALID_CURRENCY': BadSymbol,
                    'INVALID_CURRENCY_PAIR': BadSymbol,
                    'POC_FILL_IMMEDIATELY': OrderImmediatelyFillable,  # {"label":"POC_FILL_IMMEDIATELY","message":"Order would match and take immediately so its cancelled"}
                    'ORDER_NOT_FOUND': OrderNotFound,
                    'CLIENT_ID_NOT_FOUND': OrderNotFound,
                    'ORDER_CLOSED': InvalidOrder,
                    'ORDER_CANCELLED': InvalidOrder,
                    'QUANTITY_NOT_ENOUGH': InvalidOrder,
                    'BALANCE_NOT_ENOUGH': InsufficientFunds,
                    'MARGIN_NOT_SUPPORTED': InvalidOrder,
                    'MARGIN_BALANCE_NOT_ENOUGH': InsufficientFunds,
                    'AMOUNT_TOO_LITTLE': InvalidOrder,
                    'AMOUNT_TOO_MUCH': InvalidOrder,
                    'REPEATED_CREATION': InvalidOrder,
                    'LOAN_NOT_FOUND': OrderNotFound,
                    'LOAN_RECORD_NOT_FOUND': OrderNotFound,
                    'NO_MATCHED_LOAN': ExchangeError,
                    'NOT_MERGEABLE': ExchangeError,
                    'NO_CHANGE': ExchangeError,
                    'REPAY_TOO_MUCH': ExchangeError,
                    'TOO_MANY_CURRENCY_PAIRS': InvalidOrder,
                    'TOO_MANY_ORDERS': InvalidOrder,
                    'TOO_MANY_REQUESTS': RateLimitExceeded,
                    'MIXED_ACCOUNT_TYPE': InvalidOrder,
                    'AUTO_BORROW_TOO_MUCH': ExchangeError,
                    'TRADE_RESTRICTED': InsufficientFunds,
                    'USER_NOT_FOUND': AccountNotEnabled,
                    'CONTRACT_NO_COUNTER': ExchangeError,
                    'CONTRACT_NOT_FOUND': BadSymbol,
                    'RISK_LIMIT_EXCEEDED': ExchangeError,
                    'INSUFFICIENT_AVAILABLE': InsufficientFunds,
                    'LIQUIDATE_IMMEDIATELY': InvalidOrder,
                    'LEVERAGE_TOO_HIGH': InvalidOrder,
                    'LEVERAGE_TOO_LOW': InvalidOrder,
                    'ORDER_NOT_OWNED': ExchangeError,
                    'ORDER_FINISHED': ExchangeError,
                    'POSITION_CROSS_MARGIN': ExchangeError,
                    'POSITION_IN_LIQUIDATION': ExchangeError,
                    'POSITION_IN_CLOSE': ExchangeError,
                    'POSITION_EMPTY': InvalidOrder,
                    'REMOVE_TOO_MUCH': ExchangeError,
                    'RISK_LIMIT_NOT_MULTIPLE': ExchangeError,
                    'RISK_LIMIT_TOO_HIGH': ExchangeError,
                    'RISK_LIMIT_TOO_lOW': ExchangeError,
                    'PRICE_TOO_DEVIATED': InvalidOrder,
                    'SIZE_TOO_LARGE': InvalidOrder,
                    'SIZE_TOO_SMALL': InvalidOrder,
                    'PRICE_OVER_LIQUIDATION': InvalidOrder,
                    'PRICE_OVER_BANKRUPT': InvalidOrder,
                    'ORDER_POC_IMMEDIATE': OrderImmediatelyFillable,  # {"label":"ORDER_POC_IMMEDIATE","detail":"order price 1700 while counter price 1793.55"}
                    'INCREASE_POSITION': InvalidOrder,
                    'CONTRACT_IN_DELISTING': ExchangeError,
                    'INTERNAL': ExchangeNotAvailable,
                    'SERVER_ERROR': ExchangeNotAvailable,
                    'TOO_BUSY': ExchangeNotAvailable,
                    'CROSS_ACCOUNT_NOT_FOUND': ExchangeError,
                    'RISK_LIMIT_TOO_LOW': BadRequest,  # {"label":"RISK_LIMIT_TOO_LOW","detail":"limit 1000000"}
                    'AUTO_TRIGGER_PRICE_LESS_LAST': InvalidOrder,  # {"label":"AUTO_TRIGGER_PRICE_LESS_LAST","message":"invalid argument: Trigger.Price must < last_price"}
                    'AUTO_TRIGGER_PRICE_GREATE_LAST': InvalidOrder,  # {"label":"AUTO_TRIGGER_PRICE_GREATE_LAST","message":"invalid argument: Trigger.Price must > last_price"}
                    'POSITION_HOLDING': BadRequest,
                    'USER_LOAN_EXCEEDED': BadRequest,  # {"label":"USER_LOAN_EXCEEDED","message":"Max loan amount per user would be exceeded"}
                },
                'broad': {},
            },
        })

    def set_sandbox_mode(self, enable: bool):
        super(gate, self).set_sandbox_mode(enable)
        self.options['sandboxMode'] = enable

    async def load_unified_status(self, params={}):
        """
        :param dict [params]: extra parameters specific to the exchange API endpoint
        returns unifiedAccount so the user can check if the unified account is enabled

        https://www.gate.io/docs/developers/apiv4/#get-account-detail

        :returns boolean: True or False if the enabled unified account is enabled or not and sets the unifiedAccount option if it is None
        """
        unifiedAccount = self.safe_bool(self.options, 'unifiedAccount')
        if unifiedAccount is None:
            try:
                #
                #     {
                #         "user_id": 10406147,
                #         "ip_whitelist": [],
                #         "currency_pairs": [],
                #         "key": {
                #             "mode": 1
                #         },
                #         "tier": 0,
                #         "tier_expire_time": "0001-01-01T00:00:00Z",
                #         "copy_trading_role": 0
                #     }
                #
                response = await self.privateAccountGetDetail(params)
                result = self.safe_dict(response, 'key', {})
                self.options['unifiedAccount'] = self.safe_integer(result, 'mode') == 2
            except Exception as e:
                # if the request fails, the unifiedAccount is disabled
                self.options['unifiedAccount'] = False
        return self.options['unifiedAccount']

    async def upgrade_unified_trade_account(self, params={}):
        return await self.privateUnifiedPutUnifiedMode(params)

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

        https://www.gate.io/docs/developers/apiv4/en/#get-server-current-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.publicSpotGetTime(params)
        #
        #     {
        #         "server_time": 1731447921098
        #     }
        #
        return self.safe_integer(response, 'server_time')

    def create_expired_option_market(self, symbol: str):
        # support expired option contracts
        quote = 'USDT'
        settle = quote
        optionParts = symbol.split('-')
        symbolBase = symbol.split('/')
        marketIdBase = symbol.split('_')
        base = None
        expiry = self.safe_string(optionParts, 1)
        if symbol.find('/') > -1:
            base = self.safe_string(symbolBase, 0)
        else:
            base = self.safe_string(marketIdBase, 0)
            expiry = expiry[2:8]  # convert 20230728 to 230728
        strike = self.safe_string(optionParts, 2)
        optionType = self.safe_string(optionParts, 3)
        datetime = self.convert_expire_date(expiry)
        timestamp = self.parse8601(datetime)
        return {
            'id': base + '_' + quote + '-' + '20' + expiry + '-' + strike + '-' + optionType,
            'symbol': base + '/' + quote + ':' + settle + '-' + expiry + '-' + strike + '-' + optionType,
            'base': base,
            'quote': quote,
            'settle': settle,
            'baseId': base,
            'quoteId': quote,
            'settleId': settle,
            'active': False,
            'type': 'option',
            'linear': None,
            'inverse': None,
            'spot': False,
            'swap': False,
            'future': False,
            'option': True,
            'margin': False,
            'contract': True,
            'contractSize': self.parse_number('1'),
            'expiry': timestamp,
            'expiryDatetime': datetime,
            'optionType': 'call' if (optionType == 'C') else 'put',
            'strike': self.parse_number(strike),
            'precision': {
                'amount': self.parse_number('1'),
                'price': None,
            },
            'limits': {
                'amount': {
                    'min': None,
                    'max': None,
                },
                'price': {
                    'min': None,
                    'max': None,
                },
                'cost': {
                    'min': None,
                    'max': None,
                },
            },
            'info': None,
        }

    def safe_market(self, marketId: Str = None, market: Market = None, delimiter: Str = None, marketType: Str = None) -> MarketInterface:
        isOption = (marketId is not None) and ((marketId.find('-C') > -1) or (marketId.find('-P') > -1))
        if isOption and not (marketId in self.markets_by_id):
            # handle expired option contracts
            return self.create_expired_option_market(marketId)
        return super(gate, self).safe_market(marketId, market, delimiter, marketType)

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

        https://www.gate.io/docs/developers/apiv4/en/#list-all-currency-pairs-supported                                     # spot
        https://www.gate.io/docs/developers/apiv4/en/#list-all-supported-currency-pairs-supported-in-margin-trading         # margin
        https://www.gate.io/docs/developers/apiv4/en/#list-all-futures-contracts                                            # swap
        https://www.gate.io/docs/developers/apiv4/en/#list-all-futures-contracts-2                                          # future
        https://www.gate.io/docs/developers/apiv4/en/#list-all-the-contracts-with-specified-underlying-and-expiration-time  # option

        :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()
        sandboxMode = self.safe_bool(self.options, 'sandboxMode', False)
        rawPromises = [
            self.fetch_contract_markets(params),
            self.fetch_option_markets(params),
        ]
        if not sandboxMode:
            # gate does not have a sandbox for spot markets
            mainnetOnly = [self.fetch_spot_markets(params)]
            rawPromises = self.array_concat(rawPromises, mainnetOnly)
        promises = await asyncio.gather(*rawPromises)
        spotMarkets = self.safe_value(promises, 0, [])
        contractMarkets = self.safe_value(promises, 1, [])
        optionMarkets = self.safe_value(promises, 2, [])
        markets = self.array_concat(spotMarkets, contractMarkets)
        return self.array_concat(markets, optionMarkets)

    async def fetch_spot_markets(self, params={}):
        marginPromise = self.publicMarginGetCurrencyPairs(params)
        spotMarketsPromise = self.publicSpotGetCurrencyPairs(params)
        marginResponse, spotMarketsResponse = await asyncio.gather(*[marginPromise, spotMarketsPromise])
        marginMarkets = self.index_by(marginResponse, 'id')
        #
        #  Spot
        #
        #     [
        #         {
        #             "id": "QTUM_ETH",
        #             "base": "QTUM",
        #             "quote": "ETH",
        #             "fee": "0.2",
        #             "min_base_amount": "0.01",
        #             "min_quote_amount": "0.001",
        #             "amount_precision": 3,
        #             "precision": 6,
        #             "trade_status": "tradable",
        #             "sell_start": 0,
        #             "buy_start": 0
        #         }
        #     ]
        #
        #  Margin
        #
        #     [
        #         {
        #             "id": "ETH_USDT",
        #             "base": "ETH",
        #             "quote": "USDT",
        #             "leverage": 3,
        #             "min_base_amount": "0.01",
        #             "min_quote_amount": "100",
        #             "max_quote_amount": "1000000"
        #         }
        #     ]
        #
        result = []
        for i in range(0, len(spotMarketsResponse)):
            spotMarket = spotMarketsResponse[i]
            id = self.safe_string(spotMarket, 'id')
            marginMarket = self.safe_value(marginMarkets, id)
            market = self.deep_extend(marginMarket, spotMarket)
            baseId, quoteId = id.split('_')
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            takerPercent = self.safe_string(market, 'fee')
            makerPercent = self.safe_string(market, 'maker_fee_rate', takerPercent)
            amountPrecision = self.parse_number(self.parse_precision(self.safe_string(market, 'amount_precision')))
            tradeStatus = self.safe_string(market, 'trade_status')
            leverage = self.safe_number(market, 'leverage')
            margin = leverage is not None
            result.append({
                'id': id,
                'symbol': base + '/' + quote,
                'base': base,
                'quote': quote,
                'settle': None,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': None,
                'type': 'spot',
                'spot': True,
                'margin': margin,
                'swap': False,
                'future': False,
                'option': False,
                'active': (tradeStatus == 'tradable'),
                'contract': False,
                'linear': None,
                'inverse': None,
                # Fee is in %, so divide by 100
                'taker': self.parse_number(Precise.string_div(takerPercent, '100')),
                'maker': self.parse_number(Precise.string_div(makerPercent, '100')),
                'contractSize': None,
                'expiry': None,
                'expiryDatetime': None,
                'strike': None,
                'optionType': None,
                'precision': {
                    'amount': amountPrecision,
                    'price': self.parse_number(self.parse_precision(self.safe_string(market, 'precision'))),
                },
                'limits': {
                    'leverage': {
                        'min': self.parse_number('1'),
                        'max': self.safe_number(market, 'leverage', 1),
                    },
                    'amount': {
                        'min': self.safe_number(spotMarket, 'min_base_amount', amountPrecision),
                        'max': None,
                    },
                    'price': {
                        'min': None,
                        'max': None,
                    },
                    'cost': {
                        'min': self.safe_number(market, 'min_quote_amount'),
                        'max': self.safe_number(market, 'max_quote_amount') if margin else None,
                    },
                },
                'created': None,
                'info': market,
            })
        return result

    async def fetch_contract_markets(self, params={}):
        result = []
        swapSettlementCurrencies = self.get_settlement_currencies('swap', 'fetchMarkets')
        futureSettlementCurrencies = self.get_settlement_currencies('future', 'fetchMarkets')
        for c in range(0, len(swapSettlementCurrencies)):
            settleId = swapSettlementCurrencies[c]
            request: dict = {
                'settle': settleId,
            }
            response = await self.publicFuturesGetSettleContracts(self.extend(request, params))
            for i in range(0, len(response)):
                parsedMarket = self.parse_contract_market(response[i], settleId)
                result.append(parsedMarket)
        for c in range(0, len(futureSettlementCurrencies)):
            settleId = futureSettlementCurrencies[c]
            request: dict = {
                'settle': settleId,
            }
            response = await self.publicDeliveryGetSettleContracts(self.extend(request, params))
            for i in range(0, len(response)):
                parsedMarket = self.parse_contract_market(response[i], settleId)
                result.append(parsedMarket)
        return result

    def parse_contract_market(self, market, settleId):
        #
        #  Perpetual swap
        #
        #    {
        #        "name": "BTC_USDT",
        #        "type": "direct",
        #        "quanto_multiplier": "0.0001",
        #        "ref_discount_rate": "0",
        #        "order_price_deviate": "0.5",
        #        "maintenance_rate": "0.005",
        #        "mark_type": "index",
        #        "last_price": "38026",
        #        "mark_price": "37985.6",
        #        "index_price": "37954.92",
        #        "funding_rate_indicative": "0.000219",
        #        "mark_price_round": "0.01",
        #        "funding_offset": 0,
        #        "in_delisting": False,
        #        "risk_limit_base": "1000000",
        #        "interest_rate": "0.0003",
        #        "order_price_round": "0.1",
        #        "order_size_min": 1,
        #        "ref_rebate_rate": "0.2",
        #        "funding_interval": 28800,
        #        "risk_limit_step": "1000000",
        #        "leverage_min": "1",
        #        "leverage_max": "100",
        #        "risk_limit_max": "8000000",
        #        "maker_fee_rate": "-0.00025",
        #        "taker_fee_rate": "0.00075",
        #        "funding_rate": "0.002053",
        #        "order_size_max": 1000000,
        #        "funding_next_apply": 1610035200,
        #        "short_users": 977,
        #        "config_change_time": 1609899548,
        #        "trade_size": 28530850594,
        #        "position_size": 5223816,
        #        "long_users": 455,
        #        "funding_impact_value": "60000",
        #        "orders_limit": 50,
        #        "trade_id": 10851092,
        #        "orderbook_id": 2129638396
        #    }
        #
        #  Delivery Futures
        #
        #    {
        #        "name": "BTC_USDT_20200814",
        #        "underlying": "BTC_USDT",
        #        "cycle": "WEEKLY",
        #        "type": "direct",
        #        "quanto_multiplier": "0.0001",
        #        "mark_type": "index",
        #        "last_price": "9017",
        #        "mark_price": "9019",
        #        "index_price": "9005.3",
        #        "basis_rate": "0.185095",
        #        "basis_value": "13.7",
        #        "basis_impact_value": "100000",
        #        "settle_price": "0",
        #        "settle_price_interval": 60,
        #        "settle_price_duration": 1800,
        #        "settle_fee_rate": "0.0015",
        #        "expire_time": 1593763200,
        #        "order_price_round": "0.1",
        #        "mark_price_round": "0.1",
        #        "leverage_min": "1",
        #        "leverage_max": "100",
        #        "maintenance_rate": "1000000",
        #        "risk_limit_base": "140.726652109199",
        #        "risk_limit_step": "1000000",
        #        "risk_limit_max": "8000000",
        #        "maker_fee_rate": "-0.00025",
        #        "taker_fee_rate": "0.00075",
        #        "ref_discount_rate": "0",
        #        "ref_rebate_rate": "0.2",
        #        "order_price_deviate": "0.5",
        #        "order_size_min": 1,
        #        "order_size_max": 1000000,
        #        "orders_limit": 50,
        #        "orderbook_id": 63,
        #        "trade_id": 26,
        #        "trade_size": 435,
        #        "position_size": 130,
        #        "config_change_time": 1593158867,
        #        "in_delisting": False
        #    }
        #
        id = self.safe_string(market, 'name')
        parts = id.split('_')
        baseId = self.safe_string(parts, 0)
        quoteId = self.safe_string(parts, 1)
        date = self.safe_string(parts, 2)
        base = self.safe_currency_code(baseId)
        quote = self.safe_currency_code(quoteId)
        settle = self.safe_currency_code(settleId)
        expiry = self.safe_timestamp(market, 'expire_time')
        symbol = ''
        marketType = 'swap'
        if date is not None:
            symbol = base + '/' + quote + ':' + settle + '-' + self.yymmdd(expiry, '')
            marketType = 'future'
        else:
            symbol = base + '/' + quote + ':' + settle
        priceDeviate = self.safe_string(market, 'order_price_deviate')
        markPrice = self.safe_string(market, 'mark_price')
        minMultiplier = Precise.string_sub('1', priceDeviate)
        maxMultiplier = Precise.string_add('1', priceDeviate)
        minPrice = Precise.string_mul(minMultiplier, markPrice)
        maxPrice = Precise.string_mul(maxMultiplier, markPrice)
        takerPercent = self.safe_string(market, 'taker_fee_rate')
        makerPercent = self.safe_string(market, 'maker_fee_rate', takerPercent)
        isLinear = quote == settle
        contractSize = self.safe_string(market, 'quanto_multiplier')
        # exception only for one market: https://api.gateio.ws/api/v4/futures/btc/contracts
        if contractSize == '0':
            contractSize = '1'  # 1 USD in WEB: https://i.imgur.com/MBBUI04.png
        return {
            'id': id,
            'symbol': symbol,
            'base': base,
            'quote': quote,
            'settle': settle,
            'baseId': baseId,
            'quoteId': quoteId,
            'settleId': settleId,
            'type': marketType,
            'spot': False,
            'margin': False,
            'swap': marketType == 'swap',
            'future': marketType == 'future',
            'option': marketType == 'option',
            'active': True,
            'contract': True,
            'linear': isLinear,
            'inverse': not isLinear,
            'taker': self.parse_number(Precise.string_div(takerPercent, '100')),  # Fee is in %, so divide by 100
            'maker': self.parse_number(Precise.string_div(makerPercent, '100')),
            'contractSize': self.parse_number(contractSize),
            'expiry': expiry,
            'expiryDatetime': self.iso8601(expiry),
            'strike': None,
            'optionType': None,
            'precision': {
                'amount': self.parse_number('1'),  # all contracts have self step size
                'price': self.safe_number(market, 'order_price_round'),
            },
            'limits': {
                'leverage': {
                    'min': self.safe_number(market, 'leverage_min'),
                    'max': self.safe_number(market, 'leverage_max'),
                },
                'amount': {
                    'min': self.safe_number(market, 'order_size_min'),
                    'max': self.safe_number(market, 'order_size_max'),
                },
                'price': {
                    'min': self.parse_number(minPrice),
                    'max': self.parse_number(maxPrice),
                },
                'cost': {
                    'min': None,
                    'max': None,
                },
            },
            'created': None,
            'info': market,
        }

    async def fetch_option_markets(self, params={}):
        result = []
        underlyings = await self.fetch_option_underlyings()
        for i in range(0, len(underlyings)):
            underlying = underlyings[i]
            query = self.extend({}, params)
            query['underlying'] = underlying
            response = await self.publicOptionsGetContracts(query)
            #
            #    [
            #        {
            #            "orders_limit": "50",
            #            "order_size_max": "100000",
            #            "mark_price_round": "0.1",
            #            "order_size_min": "1",
            #            "position_limit": "1000000",
            #            "orderbook_id": "575967",
            #            "order_price_deviate": "0.9",
            #            "is_call": True,  # True means Call False means Put
            #            "last_price": "93.9",
            #            "bid1_size": "0",
            #            "bid1_price": "0",
            #            "taker_fee_rate": "0.0004",
            #            "underlying": "BTC_USDT",
            #            "create_time": "1646381188",
            #            "price_limit_fee_rate": "0.1",
            #            "maker_fee_rate": "0.0004",
            #            "trade_id": "727",
            #            "order_price_round": "0.1",
            #            "settle_fee_rate": "0.0001",
            #            "trade_size": "1982",
            #            "ref_rebate_rate": "0",
            #            "name": "BTC_USDT-20220311-44000-C",
            #            "underlying_price": "39194.26",
            #            "strike_price": "44000",
            #            "multiplier": "0.0001",
            #            "ask1_price": "0",
            #            "ref_discount_rate": "0",
            #            "expiration_time": "1646985600",
            #            "mark_price": "12.15",
            #            "position_size": "4",
            #            "ask1_size": "0",
            #            "tag": "WEEK"
            #        }
            #    ]
            #
            for j in range(0, len(response)):
                market = response[j]
                id = self.safe_string(market, 'name')
                parts = underlying.split('_')
                baseId = self.safe_string(parts, 0)
                quoteId = self.safe_string(parts, 1)
                base = self.safe_currency_code(baseId)
                quote = self.safe_currency_code(quoteId)
                symbol = base + '/' + quote
                expiry = self.safe_timestamp(market, 'expiration_time')
                strike = self.safe_string(market, 'strike_price')
                isCall = self.safe_value(market, 'is_call')
                optionLetter = 'C' if isCall else 'P'
                optionType = 'call' if isCall else 'put'
                symbol = symbol + ':' + quote + '-' + self.yymmdd(expiry) + '-' + strike + '-' + optionLetter
                priceDeviate = self.safe_string(market, 'order_price_deviate')
                markPrice = self.safe_string(market, 'mark_price')
                minMultiplier = Precise.string_sub('1', priceDeviate)
                maxMultiplier = Precise.string_add('1', priceDeviate)
                minPrice = Precise.string_mul(minMultiplier, markPrice)
                maxPrice = Precise.string_mul(maxMultiplier, markPrice)
                takerPercent = self.safe_string(market, 'taker_fee_rate')
                makerPercent = self.safe_string(market, 'maker_fee_rate', takerPercent)
                result.append({
                    'id': id,
                    'symbol': symbol,
                    'base': base,
                    'quote': quote,
                    'settle': quote,
                    'baseId': baseId,
                    'quoteId': quoteId,
                    'settleId': quoteId,
                    'type': 'option',
                    'spot': False,
                    'margin': False,
                    'swap': False,
                    'future': False,
                    'option': True,
                    'active': True,
                    'contract': True,
                    'linear': True,
                    'inverse': False,
                    'taker': self.parse_number(Precise.string_div(takerPercent, '100')),  # Fee is in %, so divide by 100
                    'maker': self.parse_number(Precise.string_div(makerPercent, '100')),
                    'contractSize': self.parse_number('1'),
                    'expiry': expiry,
                    'expiryDatetime': self.iso8601(expiry),
                    'strike': strike,
                    'optionType': optionType,
                    'precision': {
                        'amount': self.parse_number('1'),  # all options have self step size
                        'price': self.safe_number(market, 'order_price_round'),
                    },
                    'limits': {
                        'leverage': {
                            'min': None,
                            'max': None,
                        },
                        'amount': {
                            'min': self.safe_number(market, 'order_size_min'),
                            'max': self.safe_number(market, 'order_size_max'),
                        },
                        'price': {
                            'min': self.parse_number(minPrice),
                            'max': self.parse_number(maxPrice),
                        },
                        'cost': {
                            'min': None,
                            'max': None,
                        },
                    },
                    'created': self.safe_timestamp(market, 'create_time'),
                    'info': market,
                })
        return result

    async def fetch_option_underlyings(self):
        underlyingsResponse = await self.publicOptionsGetUnderlyings()
        #
        #    [
        #        {
        #            "index_time": "1646915796",
        #            "name": "BTC_USDT",
        #            "index_price": "39142.73"
        #        }
        #    ]
        #
        underlyings = []
        for i in range(0, len(underlyingsResponse)):
            underlying = underlyingsResponse[i]
            name = self.safe_string(underlying, 'name')
            if name is not None:
                underlyings.append(name)
        return underlyings

    def prepare_request(self, market=None, type=None, params={}):
        """
 @ignore
        Fills request params contract, settle, currency_pair, market and account where applicable
        :param dict market: CCXT market, required when type is None
        :param str type: 'spot', 'swap', or 'future', required when market is None
        :param dict [params]: request parameters
        :returns: the api request object, and the new params object with non-needed parameters removed
        """
        # * Do not call for multi spot order methods like cancelAllOrders and fetchOpenOrders. Use multiOrderSpotPrepareRequest instead
        request: dict = {}
        if market is not None:
            if market['contract']:
                request['contract'] = market['id']
                if not market['option']:
                    request['settle'] = market['settleId']
            else:
                request['currency_pair'] = market['id']
        else:
            swap = type == 'swap'
            future = type == 'future'
            if swap or future:
                defaultSettle = 'usdt' if swap else 'btc'
                settle = self.safe_string_lower(params, 'settle', defaultSettle)
                params = self.omit(params, 'settle')
                request['settle'] = settle
        return [request, params]

    def spot_order_prepare_request(self, market=None, trigger=False, params={}):
        """
 @ignore
        Fills request params currency_pair, market and account where applicable for spot order methods like fetchOpenOrders, cancelAllOrders
        :param dict market: CCXT market
        :param bool trigger: True if for a trigger order
        :param dict [params]: request parameters
        :returns: the api request object, and the new params object with non-needed parameters removed
        """
        marginMode, query = self.get_margin_mode(trigger, params)
        request: dict = {}
        if not trigger:
            if market is None:
                raise ArgumentsRequired(self.id + ' spotOrderPrepareRequest() requires a market argument for non-trigger orders')
            request['account'] = marginMode
            request['currency_pair'] = market['id']  # Should always be set for non-trigger
        return [request, query]

    def multi_order_spot_prepare_request(self, market=None, trigger=False, params={}):
        """
 @ignore
        Fills request params currency_pair, market and account where applicable for spot order methods like fetchOpenOrders, cancelAllOrders
        :param dict market: CCXT market
        :param bool trigger: True if for a trigger order
        :param dict [params]: request parameters
        :returns: the api request object, and the new params object with non-needed parameters removed
        """
        marginMode, query = self.get_margin_mode(trigger, params)
        request: dict = {
            'account': marginMode,
        }
        if market is not None:
            if trigger:
                # gate spot and margin trigger orders use the term market instead of currency_pair, and normal instead of spot. Neither parameter is used when fetching/cancelling a single order. They are used for creating a single trigger order, but createOrder does not call self method
                request['market'] = market['id']
            else:
                request['currency_pair'] = market['id']
        return [request, query]

    def get_margin_mode(self, trigger, params):
        """
 @ignore
        Gets the margin type for self api call
        :param bool trigger: True if for a trigger order
        :param dict [params]: Request params
        :returns: The marginMode and the updated request params with marginMode removed, marginMode value is the value that can be read by the "account" property specified in gates api docs
        """
        defaultMarginMode = self.safe_string_lower_2(self.options, 'defaultMarginMode', 'marginMode', 'spot')  # 'margin' is isolated margin on gate's api
        marginMode = self.safe_string_lower_2(params, 'marginMode', 'account', defaultMarginMode)
        params = self.omit(params, ['marginMode', 'account'])
        if marginMode == 'cross':
            marginMode = 'cross_margin'
        elif marginMode == 'isolated':
            marginMode = 'margin'
        elif marginMode == '':
            marginMode = 'spot'
        if trigger:
            if marginMode == 'spot':
                # gate spot trigger orders use the term normal instead of spot
                marginMode = 'normal'
            if marginMode == 'cross_margin':
                raise BadRequest(self.id + ' getMarginMode() does not support trigger orders for cross margin')
        isUnifiedAccount = False
        isUnifiedAccount, params = self.handle_option_and_params(params, 'getMarginMode', 'unifiedAccount')
        if isUnifiedAccount:
            marginMode = 'unified'
        return [marginMode, params]

    def get_settlement_currencies(self, type, method):
        options = self.safe_value(self.options, type, {})  # ['BTC', 'USDT'] unified codes
        fetchMarketsContractOptions = self.safe_value(options, method, {})
        defaultSettle = ['usdt'] if (type == 'swap') else ['btc']
        return self.safe_value(fetchMarketsContractOptions, 'settlementCurrencies', defaultSettle)

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

        https://www.gate.io/docs/developers/apiv4/en/#list-all-currencies-details

        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an associative dictionary of currencies
        """
        # sandbox/testnet only supports future markets
        apiBackup = self.safe_value(self.urls, 'apiBackup')
        if apiBackup is not None:
            return None
        if self.check_required_credentials(False):
            await self.load_unified_status()
        response = await self.publicSpotGetCurrencies(params)
        #
        #  [
        #   {
        #       "currency": "USDT_ETH",
        #       "name": "Tether",
        #       "delisted": False,
        #       "withdraw_disabled": False,
        #       "withdraw_delayed": False,
        #       "deposit_disabled": False,
        #       "trade_disabled": True,
        #       "chain": "ETH"
        #    },
        #  ]
        #
        indexedCurrencies = self.index_by(response, 'currency')
        result: dict = {}
        for i in range(0, len(response)):
            entry = response[i]
            currencyId = self.safe_string(entry, 'currency')
            parts = currencyId.split('_')
            partFirst = self.safe_string(parts, 0)
            # if there's an underscore then the second part is always the chain name(except the _OLD suffix)
            currencyName = currencyId if currencyId.endswith('_OLD') else partFirst
            withdrawDisabled = self.safe_bool(entry, 'withdraw_disabled', False)
            depositDisabled = self.safe_bool(entry, 'deposit_disabled', False)
            tradeDisabled = self.safe_bool(entry, 'trade_disabled', False)
            precision = self.parse_number('0.0001')  # temporary safe default, because no value provided from API
            code = self.safe_currency_code(currencyName)
            # check leveraged tokens(e.g. BTC3S, ETH5L)
            isLeveragedToken = False
            if currencyId.endswith('3S') or currencyId.endswith('3L') or currencyId.endswith('5S') or currencyId.endswith('5L'):
                realCurrencyId = currencyId[0:-2]
                if realCurrencyId in indexedCurrencies:
                    isLeveragedToken = True
            type = 'leveraged' if isLeveragedToken else 'crypto'
            # some networks are null, they are mostly obsolete & unsupported dead tokens, so we can default their networkId to their tokenname
            networkId = self.safe_string(entry, 'chain', currencyId)
            networkCode = self.network_id_to_code(networkId, code)
            networkEntry = {
                'info': entry,
                'id': networkId,
                'network': networkCode,
                'limits': {
                    'deposit': {
                        'min': None,
                        'max': None,
                    },
                    'withdraw': {
                        'min': None,
                        'max': None,
                    },
                },
                'active': not tradeDisabled,
                'deposit': not depositDisabled,
                'withdraw': not withdrawDisabled,
                'fee': None,
                'precision': precision,
            }
            # check if first entry for the specific currency
            if not (code in result):
                result[code] = {
                    'id': currencyName,
                    'lowerCaseId': currencyName.lower(),
                    'code': code,
                    'type': type,
                    'precision': precision,
                    'limits': None,
                    'networks': {},
                    'info': [],  # will be filled below
                }
            result[code]['networks'][networkCode] = networkEntry
            info = self.safe_list(result[code], 'info', [])
            info.append(entry)
            result[code]['info'] = info
            result[code] = self.safe_currency_structure(result[code])  # self is needed after adding network entry
        return result

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

        https://www.gate.io/docs/developers/apiv4/en/#get-a-single-contract

        :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, query = self.prepare_request(market, None, params)
        response = await self.publicFuturesGetSettleContractsContract(self.extend(request, query))
        #
        #    [
        #        {
        #            "name": "BTC_USDT",
        #            "type": "direct",
        #            "quanto_multiplier": "0.0001",
        #            "ref_discount_rate": "0",
        #            "order_price_deviate": "0.5",
        #            "maintenance_rate": "0.005",
        #            "mark_type": "index",
        #            "last_price": "38026",
        #            "mark_price": "37985.6",
        #            "index_price": "37954.92",
        #            "funding_rate_indicative": "0.000219",
        #            "mark_price_round": "0.01",
        #            "funding_offset": 0,
        #            "in_delisting": False,
        #            "risk_limit_base": "1000000",
        #            "interest_rate": "0.0003",
        #            "order_price_round": "0.1",
        #            "order_size_min": 1,
        #            "ref_rebate_rate": "0.2",
        #            "funding_interval": 28800,
        #            "risk_limit_step": "1000000",
        #            "leverage_min": "1",
        #            "leverage_max": "100",
        #            "risk_limit_max": "8000000",
        #            "maker_fee_rate": "-0.00025",
        #            "taker_fee_rate": "0.00075",
        #            "funding_rate": "0.002053",
        #            "order_size_max": 1000000,
        #            "funding_next_apply": 1610035200,
        #            "short_users": 977,
        #            "config_change_time": 1609899548,
        #            "trade_size": 28530850594,
        #            "position_size": 5223816,
        #            "long_users": 455,
        #            "funding_impact_value": "60000",
        #            "orders_limit": 50,
        #            "trade_id": 10851092,
        #            "orderbook_id": 2129638396
        #        }
        #    ]
        #
        return self.parse_funding_rate(response)

    async def fetch_funding_rates(self, symbols: Strings = None, params={}) -> FundingRates:
        """
        fetch the funding rate for multiple markets

        https://www.gate.io/docs/developers/apiv4/en/#list-all-futures-contracts

        :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 `funding rate structures <https://docs.ccxt.com/#/?id=funding-rates-structure>`, indexed by market symbols
        """
        await self.load_markets()
        symbols = self.market_symbols(symbols)
        market = None
        if symbols is not None:
            firstSymbol = self.safe_string(symbols, 0)
            market = self.market(firstSymbol)
        request, query = self.prepare_request(market, 'swap', params)
        response = await self.publicFuturesGetSettleContracts(self.extend(request, query))
        #
        #    [
        #        {
        #            "name": "BTC_USDT",
        #            "type": "direct",
        #            "quanto_multiplier": "0.0001",
        #            "ref_discount_rate": "0",
        #            "order_price_deviate": "0.5",
        #            "maintenance_rate": "0.005",
        #            "mark_type": "index",
        #            "last_price": "38026",
        #            "mark_price": "37985.6",
        #            "index_price": "37954.92",
        #            "funding_rate_indicative": "0.000219",
        #            "mark_price_round": "0.01",
        #            "funding_offset": 0,
        #            "in_delisting": False,
        #            "risk_limit_base": "1000000",
        #            "interest_rate": "0.0003",
        #            "order_price_round": "0.1",
        #            "order_size_min": 1,
        #            "ref_rebate_rate": "0.2",
        #            "funding_interval": 28800,
        #            "risk_limit_step": "1000000",
        #            "leverage_min": "1",
        #            "leverage_max": "100",
        #            "risk_limit_max": "8000000",
        #            "maker_fee_rate": "-0.00025",
        #            "taker_fee_rate": "0.00075",
        #            "funding_rate": "0.002053",
        #            "order_size_max": 1000000,
        #            "funding_next_apply": 1610035200,
        #            "short_users": 977,
        #            "config_change_time": 1609899548,
        #            "trade_size": 28530850594,
        #            "position_size": 5223816,
        #            "long_users": 455,
        #            "funding_impact_value": "60000",
        #            "orders_limit": 50,
        #            "trade_id": 10851092,
        #            "orderbook_id": 2129638396
        #        }
        #    ]
        #
        return self.parse_funding_rates(response, symbols)

    def parse_funding_rate(self, contract, market: Market = None) -> FundingRate:
        #
        #    {
        #        "name": "BTC_USDT",
        #        "type": "direct",
        #        "quanto_multiplier": "0.0001",
        #        "ref_discount_rate": "0",
        #        "order_price_deviate": "0.5",
        #        "maintenance_rate": "0.005",
        #        "mark_type": "index",
        #        "last_price": "38026",
        #        "mark_price": "37985.6",
        #        "index_price": "37954.92",
        #        "funding_rate_indicative": "0.000219",
        #        "mark_price_round": "0.01",
        #        "funding_offset": 0,
        #        "in_delisting": False,
        #        "risk_limit_base": "1000000",
        #        "interest_rate": "0.0003",
        #        "order_price_round": "0.1",
        #        "order_size_min": 1,
        #        "ref_rebate_rate": "0.2",
        #        "funding_interval": 28800,
        #        "risk_limit_step": "1000000",
        #        "leverage_min": "1",
        #        "leverage_max": "100",
        #        "risk_limit_max": "8000000",
        #        "maker_fee_rate": "-0.00025",
        #        "taker_fee_rate": "0.00075",
        #        "funding_rate": "0.002053",
        #        "order_size_max": 1000000,
        #        "funding_next_apply": 1610035200,
        #        "short_users": 977,
        #        "config_change_time": 1609899548,
        #        "trade_size": 28530850594,
        #        "position_size": 5223816,
        #        "long_users": 455,
        #        "funding_impact_value": "60000",
        #        "orders_limit": 50,
        #        "trade_id": 10851092,
        #        "orderbook_id": 2129638396
        #    }
        #
        marketId = self.safe_string(contract, 'name')
        symbol = self.safe_symbol(marketId, market, '_', 'swap')
        markPrice = self.safe_number(contract, 'mark_price')
        indexPrice = self.safe_number(contract, 'index_price')
        interestRate = self.safe_number(contract, 'interest_rate')
        fundingRate = self.safe_number(contract, 'funding_rate')
        fundingTime = self.safe_timestamp(contract, 'funding_next_apply')
        fundingRateIndicative = self.safe_number(contract, 'funding_rate_indicative')
        fundingInterval = Precise.string_mul('1000', self.safe_string(contract, 'funding_interval'))
        return {
            'info': contract,
            'symbol': symbol,
            'markPrice': markPrice,
            'indexPrice': indexPrice,
            'interestRate': interestRate,
            'estimatedSettlePrice': None,
            'timestamp': None,
            'datetime': None,
            'fundingRate': fundingRate,
            'fundingTimestamp': fundingTime,
            'fundingDatetime': self.iso8601(fundingTime),
            'nextFundingRate': fundingRateIndicative,
            'nextFundingTimestamp': None,
            'nextFundingDatetime': None,
            'previousFundingRate': None,
            'previousFundingTimestamp': None,
            'previousFundingDatetime': None,
            'interval': self.parse_funding_interval(fundingInterval),
        }

    def parse_funding_interval(self, interval):
        intervals: dict = {
            '3600000': '1h',
            '14400000': '4h',
            '28800000': '8h',
            '57600000': '16h',
            '86400000': '24h',
        }
        return self.safe_string(intervals, interval, interval)

    async def fetch_network_deposit_address(self, code: str, params={}):
        await self.load_markets()
        currency = self.currency(code)
        request: dict = {
            'currency': currency['id'],  # todo: currencies have network-junctions
        }
        response = await self.privateWalletGetDepositAddress(self.extend(request, params))
        addresses = self.safe_value(response, 'multichain_addresses')
        currencyId = self.safe_string(response, 'currency')
        code = self.safe_currency_code(currencyId)
        result: dict = {}
        for i in range(0, len(addresses)):
            entry = addresses[i]
            #
            #    {
            #        "chain": "ETH",
            #        "address": "0x359a697945E79C7e17b634675BD73B33324E9408",
            #        "payment_id": "",
            #        "payment_name": "",
            #        "obtain_failed": "0"
            #    }
            #
            obtainFailed = self.safe_integer(entry, 'obtain_failed')
            if obtainFailed:
                continue
            network = self.safe_string(entry, 'chain')
            address = self.safe_string(entry, 'address')
            tag = self.safe_string(entry, 'payment_id')
            result[network] = {
                'info': entry,
                'code': code,  # kept here for backward-compatibility, but will be removed soon
                'currency': code,
                'address': address,
                'tag': tag,
            }
        return result

    async def fetch_deposit_addresses_by_network(self, code: str, params={}) -> List[DepositAddress]:
        """
        fetch a dictionary of addresses for a currency, indexed by network
        :param str code: unified currency code of the currency for the deposit address
        :param dict [params]: extra parameters specific to the api endpoint
        :returns dict: a dictionary of `address structures <https://docs.ccxt.com/#/?id=address-structure>` indexed by the network
        """
        await self.load_markets()
        currency = self.currency(code)
        request = {
            'currency': currency['id'],
        }
        response = await self.privateWalletGetDepositAddress(self.extend(request, params))
        chains = self.safe_value(response, 'multichain_addresses', [])
        currencyId = self.safe_string(response, 'currency')
        currency = self.safe_currency(currencyId, currency)
        parsed = self.parse_deposit_addresses(chains, [currency['code']], False, {
            'currency': currency['id'],
        })
        return self.index_by(parsed, 'network')

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

        https://www.gate.io/docs/developers/apiv4/en/#generate-currency-deposit-address

        :param str code: unified currency code
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.network]: unified network code(not used directly by gate.io but used by ccxt to filter the response)
        :returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
        """
        await self.load_markets()
        networkCode = None
        networkCode, params = self.handle_network_code_and_params(params)
        chainsIndexedById = await self.fetch_deposit_addresses_by_network(code, params)
        selectedNetworkId = self.select_network_code_from_unified_networks(code, networkCode, chainsIndexedById)
        return chainsIndexedById[selectedNetworkId]

    def parse_deposit_address(self, depositAddress, currency=None):
        #
        #     {
        #         chain: "BTC",
        #         address: "1Nxu.......Ys",
        #         payment_id: "",
        #         payment_name: "",
        #         obtain_failed: "0",
        #     }
        #
        address = self.safe_string(depositAddress, 'address')
        self.check_address(address)
        return {
            'info': depositAddress,
            'currency': self.safe_string(currency, 'code'),
            'address': address,
            'tag': self.safe_string(depositAddress, 'payment_id'),
            'network': self.network_id_to_code(self.safe_string(depositAddress, 'chain')),
        }

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

        https://www.gate.io/docs/developers/apiv4/en/#retrieve-personal-trading-fee

        :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)
        request: dict = {
            'currency_pair': market['id'],
        }
        response = await self.privateWalletGetFee(self.extend(request, params))
        #
        #    {
        #        "user_id": 1486602,
        #        "taker_fee": "0.002",
        #        "maker_fee": "0.002",
        #        "gt_discount": True,
        #        "gt_taker_fee": "0.0015",
        #        "gt_maker_fee": "0.0015",
        #        "loan_fee": "0.18",
        #        "point_type": "0",
        #        "futures_taker_fee": "0.0005",
        #        "futures_maker_fee": "0"
        #    }
        #
        return self.parse_trading_fee(response, market)

    async def fetch_trading_fees(self, params={}) -> TradingFees:
        """
        fetch the trading fees for multiple markets

        https://www.gate.io/docs/developers/apiv4/en/#retrieve-personal-trading-fee

        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `fee structures <https://docs.ccxt.com/#/?id=fee-structure>` indexed by market symbols
        """
        await self.load_markets()
        response = await self.privateWalletGetFee(params)
        #
        #    {
        #        "user_id": 1486602,
        #        "taker_fee": "0.002",
        #        "maker_fee": "0.002",
        #        "gt_discount": True,
        #        "gt_taker_fee": "0.0015",
        #        "gt_maker_fee": "0.0015",
        #        "loan_fee": "0.18",
        #        "point_type": "0",
        #        "futures_taker_fee": "0.0005",
        #        "futures_maker_fee": "0"
        #    }
        #
        return self.parse_trading_fees(response)

    def parse_trading_fees(self, response):
        result: dict = {}
        for i in range(0, len(self.symbols)):
            symbol = self.symbols[i]
            market = self.market(symbol)
            result[symbol] = self.parse_trading_fee(response, market)
        return result

    def parse_trading_fee(self, info, market: Market = None):
        #
        #    {
        #        "user_id": 1486602,
        #        "taker_fee": "0.002",
        #        "maker_fee": "0.002",
        #        "gt_discount": True,
        #        "gt_taker_fee": "0.0015",
        #        "gt_maker_fee": "0.0015",
        #        "loan_fee": "0.18",
        #        "point_type": "0",
        #        "futures_taker_fee": "0.0005",
        #        "futures_maker_fee": "0"
        #    }
        #
        gtDiscount = self.safe_value(info, 'gt_discount')
        taker = 'gt_taker_fee' if gtDiscount else 'taker_fee'
        maker = 'gt_maker_fee' if gtDiscount else 'maker_fee'
        contract = self.safe_value(market, 'contract')
        takerKey = 'futures_taker_fee' if contract else taker
        makerKey = 'futures_maker_fee' if contract else maker
        return {
            'info': info,
            'symbol': self.safe_string(market, 'symbol'),
            'maker': self.safe_number(info, makerKey),
            'taker': self.safe_number(info, takerKey),
            'percentage': None,
            'tierBased': None,
        }

    async def fetch_transaction_fees(self, codes: Strings = None, params={}):
        """
 @deprecated
        please use fetchDepositWithdrawFees instead

        https://www.gate.io/docs/developers/apiv4/en/#retrieve-withdrawal-status

        :param str[]|None codes: list of unified currency codes
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a list of `fee structures <https://docs.ccxt.com/#/?id=fee-structure>`
        """
        await self.load_markets()
        response = await self.privateWalletGetWithdrawStatus(params)
        #
        #    {
        #        "currency": "MTN",
        #        "name": "Medicalchain",
        #        "name_cn": "Medicalchain",
        #        "deposit": "0",
        #        "withdraw_percent": "0%",
        #        "withdraw_fix": "900",
        #        "withdraw_day_limit": "500000",
        #        "withdraw_day_limit_remain": "500000",
        #        "withdraw_amount_mini": "900.1",
        #        "withdraw_eachtime_limit": "90000000000",
        #        "withdraw_fix_on_chains": {
        #            "ETH": "900"
        #        }
        #    }
        #
        result: dict = {}
        withdrawFees = {}
        for i in range(0, len(response)):
            withdrawFees = {}
            entry = response[i]
            currencyId = self.safe_string(entry, 'currency')
            code = self.safe_currency_code(currencyId)
            if (codes is not None) and not self.in_array(code, codes):
                continue
            withdrawFixOnChains = self.safe_value(entry, 'withdraw_fix_on_chains')
            if withdrawFixOnChains is None:
                withdrawFees = self.safe_number(entry, 'withdraw_fix')
            else:
                chainKeys = list(withdrawFixOnChains.keys())
                for j in range(0, len(chainKeys)):
                    chainKey = chainKeys[j]
                    withdrawFees[chainKey] = self.parse_number(withdrawFixOnChains[chainKey])
            result[code] = {
                'withdraw': withdrawFees,
                'deposit': None,
                'info': entry,
            }
        return result

    async def fetch_deposit_withdraw_fees(self, codes: Strings = None, params={}):
        """
        fetch deposit and withdraw fees

        https://www.gate.io/docs/developers/apiv4/en/#retrieve-withdrawal-status

        :param str[]|None codes: list of unified currency codes
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a list of `fee structures <https://docs.ccxt.com/#/?id=fee-structure>`
        """
        await self.load_markets()
        response = await self.privateWalletGetWithdrawStatus(params)
        #
        #    [
        #        {
        #            "currency": "MTN",
        #            "name": "Medicalchain",
        #            "name_cn": "Medicalchain",
        #            "deposit": "0",
        #            "withdraw_percent": "0%",
        #            "withdraw_fix": "900",
        #            "withdraw_day_limit": "500000",
        #            "withdraw_day_limit_remain": "500000",
        #            "withdraw_amount_mini": "900.1",
        #            "withdraw_eachtime_limit": "90000000000",
        #            "withdraw_fix_on_chains": {
        #                "ETH": "900"
        #            }
        #        }
        #    ]
        #
        return self.parse_deposit_withdraw_fees(response, codes, 'currency')

    def parse_deposit_withdraw_fee(self, fee, currency: Currency = None):
        #
        #    {
        #        "currency": "MTN",
        #        "name": "Medicalchain",
        #        "name_cn": "Medicalchain",
        #        "deposit": "0",
        #        "withdraw_percent": "0%",
        #        "withdraw_fix": "900",
        #        "withdraw_day_limit": "500000",
        #        "withdraw_day_limit_remain": "500000",
        #        "withdraw_amount_mini": "900.1",
        #        "withdraw_eachtime_limit": "90000000000",
        #        "withdraw_fix_on_chains": {
        #            "ETH": "900"
        #        }
        #    }
        #
        withdrawFixOnChains = self.safe_value(fee, 'withdraw_fix_on_chains')
        result: dict = {
            'info': fee,
            'withdraw': {
                'fee': self.safe_number(fee, 'withdraw_fix'),
                'percentage': False,
            },
            'deposit': {
                'fee': self.safe_number(fee, 'deposit'),
                'percentage': False,
            },
            'networks': {},
        }
        if withdrawFixOnChains is not None:
            chainKeys = list(withdrawFixOnChains.keys())
            for i in range(0, len(chainKeys)):
                chainKey = chainKeys[i]
                networkCode = self.network_id_to_code(chainKey, self.safe_string(fee, 'currency'))
                result['networks'][networkCode] = {
                    'withdraw': {
                        'fee': self.parse_number(withdrawFixOnChains[chainKey]),
                        'percentage': False,
                    },
                    'deposit': {
                        'fee': None,
                        'percentage': None,
                    },
                }
        return result

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

        https://www.gate.io/docs/developers/apiv4/en/#query-account-book-2
        https://www.gate.io/docs/developers/apiv4/en/#query-account-book-3

        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch funding history for
        :param int [limit]: the maximum number of funding history structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `funding history structure <https://docs.ccxt.com/#/?id=funding-history-structure>`
        """
        await self.load_markets()
        # defaultType = 'future'
        market = None
        if symbol is not None:
            market = self.market(symbol)
            symbol = market['symbol']
        type, query = self.handle_market_type_and_params('fetchFundingHistory', market, params)
        request, requestParams = self.prepare_request(market, type, query)
        request['type'] = 'fund'  # 'dnw' 'pnl' 'fee' 'refr' 'fund' 'point_dnw' 'point_fee' 'point_refr'
        if since is not None:
            # from should be integer
            request['from'] = self.parse_to_int(since / 1000)
        if limit is not None:
            request['limit'] = limit
        response = None
        if type == 'swap':
            response = await self.privateFuturesGetSettleAccountBook(self.extend(request, requestParams))
        elif type == 'future':
            response = await self.privateDeliveryGetSettleAccountBook(self.extend(request, requestParams))
        else:
            raise NotSupported(self.id + ' fetchFundingHistory() only support swap & future market type')
        #
        #    [
        #        {
        #            "time": 1646899200,
        #            "change": "-0.027722",
        #            "balance": "11.653120591841",
        #            "text": "XRP_USDT",
        #            "type": "fund"
        #        },
        #        ...
        #    ]
        #
        return self.parse_funding_histories(response, symbol, since, limit)

    def parse_funding_histories(self, response, symbol, since, limit) -> List[FundingHistory]:
        result = []
        for i in range(0, len(response)):
            entry = response[i]
            funding = self.parse_funding_history(entry)
            result.append(funding)
        sorted = self.sort_by(result, 'timestamp')
        return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)

    def parse_funding_history(self, info, market: Market = None):
        #
        #    {
        #        "time": 1646899200,
        #        "change": "-0.027722",
        #        "balance": "11.653120591841",
        #        "text": "XRP_USDT",
        #        "type": "fund"
        #    }
        #
        timestamp = self.safe_timestamp(info, 'time')
        marketId = self.safe_string(info, 'text')
        market = self.safe_market(marketId, market, '_', 'swap')
        return {
            'info': info,
            'symbol': self.safe_string(market, 'symbol'),
            'code': self.safe_string(market, 'settle'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'id': None,
            'amount': self.safe_number(info, 'change'),
        }

    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://www.gate.io/docs/developers/apiv4/en/#retrieve-order-book
        https://www.gate.io/docs/developers/apiv4/en/#futures-order-book
        https://www.gate.io/docs/developers/apiv4/en/#futures-order-book-2
        https://www.gate.io/docs/developers/apiv4/en/#options-order-book

        :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 = {
        #         'currency_pair': market['id'],
        #         'interval': '0',  # depth, 0 means no aggregation is applied, default to 0
        #         'limit': limit,  # maximum number of order depth data in asks or bids
        #         'with_id': True,  # return order book ID
        #     }
        #
        request, query = self.prepare_request(market, market['type'], params)
        if limit is not None:
            request['limit'] = limit  # default 10, max 100
        request['with_id'] = True
        response = None
        if market['spot'] or market['margin']:
            response = await self.publicSpotGetOrderBook(self.extend(request, query))
        elif market['swap']:
            response = await self.publicFuturesGetSettleOrderBook(self.extend(request, query))
        elif market['future']:
            response = await self.publicDeliveryGetSettleOrderBook(self.extend(request, query))
        elif market['option']:
            response = await self.publicOptionsGetOrderBook(self.extend(request, query))
        else:
            raise NotSupported(self.id + ' fetchOrderBook() not support self market type')
        #
        # spot
        #
        #     {
        #         "id": 6358770031
        #         "current": 1634345973275,
        #         "update": 1634345973271,
        #         "asks": [
        #             ["2.2241","12449.827"],
        #             ["2.2242","200"],
        #             ["2.2244","826.931"],
        #             ["2.2248","3876.107"],
        #             ["2.225","2377.252"],
        #             ["2.22509","439.484"],
        #             ["2.2251","1489.313"],
        #             ["2.2253","714.582"],
        #             ["2.2254","1349.784"],
        #             ["2.2256","234.701"]],
        #          "bids": [
        #             ["2.2236","32.465"],
        #             ["2.2232","243.983"],
        #             ["2.2231","32.207"],
        #             ["2.223","449.827"],
        #             ["2.2228","7.918"],
        #             ["2.2227","12703.482"],
        #             ["2.2226","143.033"],
        #             ["2.2225","143.027"],
        #             ["2.2224","1369.352"],
        #             ["2.2223","756.063"]
        #         ]
        #     }
        #
        # swap, future and option
        #
        #     {
        #         "id": 6358770031
        #         "current": 1634350208.745,
        #         "asks": [
        #             {"s": 24909, "p": "61264.8"},
        #             {"s": 81, "p": "61266.6"},
        #             {"s": 2000, "p": "61267.6"},
        #             {"s": 490, "p": "61270.2"},
        #             {"s": 12, "p": "61270.4"},
        #             {"s": 11782, "p": "61273.2"},
        #             {"s": 14666, "p": "61273.3"},
        #             {"s": 22541, "p": "61273.4"},
        #             {"s": 33, "p": "61273.6"},
        #             {"s": 11980, "p": "61274.5"}
        #         ],
        #         "bids": [
        #             {"s": 41844, "p": "61264.7"},
        #             {"s": 13783, "p": "61263.3"},
        #             {"s": 1143, "p": "61259.8"},
        #             {"s": 81, "p": "61258.7"},
        #             {"s": 2471, "p": "61257.8"},
        #             {"s": 2471, "p": "61257.7"},
        #             {"s": 2471, "p": "61256.5"},
        #             {"s": 3, "p": "61254.2"},
        #             {"s": 114, "p": "61252.4"},
        #             {"s": 14372, "p": "61248.6"}
        #         ],
        #         "update": 1634350208.724
        #     }
        #
        timestamp = self.safe_integer(response, 'current')
        if not market['spot']:
            timestamp = timestamp * 1000
        priceKey = 0 if market['spot'] else 'p'
        amountKey = 1 if market['spot'] else 's'
        nonce = self.safe_integer(response, 'id')
        result = self.parse_order_book(response, symbol, timestamp, 'bids', 'asks', priceKey, amountKey)
        result['nonce'] = nonce
        return result

    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://www.gate.io/docs/developers/apiv4/en/#get-details-of-a-specifc-order
        https://www.gate.io/docs/developers/apiv4/en/#list-futures-tickers
        https://www.gate.io/docs/developers/apiv4/en/#list-futures-tickers-2
        https://www.gate.io/docs/developers/apiv4/en/#list-tickers-of-options-contracts

        :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, query = self.prepare_request(market, None, params)
        response = None
        if market['spot'] or market['margin']:
            response = await self.publicSpotGetTickers(self.extend(request, query))
        elif market['swap']:
            response = await self.publicFuturesGetSettleTickers(self.extend(request, query))
        elif market['future']:
            response = await self.publicDeliveryGetSettleTickers(self.extend(request, query))
        elif market['option']:
            marketId = market['id']
            optionParts = marketId.split('-')
            request['underlying'] = self.safe_string(optionParts, 0)
            response = await self.publicOptionsGetTickers(self.extend(request, query))
        else:
            raise NotSupported(self.id + ' fetchTicker() not support self market type')
        ticker = None
        if market['option']:
            for i in range(0, len(response)):
                entry = response[i]
                if entry['name'] == market['id']:
                    ticker = entry
                    break
        else:
            ticker = self.safe_value(response, 0)
        return self.parse_ticker(ticker, market)

    def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
        #
        # SPOT
        #
        #     {
        #         "currency_pair": "KFC_USDT",
        #         "last": "7.255",
        #         "lowest_ask": "7.298",
        #         "highest_bid": "7.218",
        #         "change_percentage": "-1.18",
        #         "base_volume": "1219.053687865",
        #         "quote_volume": "8807.40299875455",
        #         "high_24h": "7.262",
        #         "low_24h": "7.095"
        #     }
        #
        # LINEAR/DELIVERY
        #
        #     {
        #         "contract": "BTC_USDT",
        #         "last": "6432",
        #         "low_24h": "6278",
        #         "high_24h": "6790",
        #         "change_percentage": "4.43",
        #         "total_size": "32323904",
        #         "volume_24h": "184040233284",
        #         "volume_24h_btc": "28613220",
        #         "volume_24h_usd": "184040233284",
        #         "volume_24h_base": "28613220",
        #         "volume_24h_quote": "184040233284",
        #         "volume_24h_settle": "28613220",
        #         "mark_price": "6534",
        #         "funding_rate": "0.0001",
        #         "funding_rate_indicative": "0.0001",
        #         "index_price": "6531"
        #     }
        #
        # bookTicker
        #    {
        #        "t": 1671363004228,
        #        "u": 9793320464,
        #        "s": "BTC_USDT",
        #        "b": "16716.8",  # best bid price
        #        "B": "0.0134",  # best bid size
        #        "a": "16716.9",  # best ask price
        #        "A": "0.0353"  # best ask size
        #     }
        #
        # option
        #
        #     {
        #         "vega": "0.00002",
        #         "leverage": "12.277188268663",
        #         "ask_iv": "0",
        #         "delta": "-0.99999",
        #         "last_price": "0",
        #         "theta": "-0.00661",
        #         "bid1_price": "1096",
        #         "mark_iv": "0.7799",
        #         "name": "BTC_USDT-20230608-28500-P",
        #         "bid_iv": "0",
        #         "ask1_price": "2935",
        #         "mark_price": "2147.3",
        #         "position_size": 0,
        #         "bid1_size": 12,
        #         "ask1_size": -14,
        #         "gamma": "0"
        #     }
        #
        marketId = self.safe_string_n(ticker, ['currency_pair', 'contract', 'name'])
        marketType = 'contract' if ('mark_price' in ticker) else 'spot'
        symbol = self.safe_symbol(marketId, market, '_', marketType)
        last = self.safe_string_2(ticker, 'last', 'last_price')
        ask = self.safe_string_n(ticker, ['lowest_ask', 'a', 'ask1_price'])
        bid = self.safe_string_n(ticker, ['highest_bid', 'b', 'bid1_price'])
        high = self.safe_string(ticker, 'high_24h')
        low = self.safe_string(ticker, 'low_24h')
        bidVolume = self.safe_string_2(ticker, 'B', 'bid1_size')
        askVolume = self.safe_string_2(ticker, 'A', 'ask1_size')
        timestamp = self.safe_integer(ticker, 't')
        baseVolume = self.safe_string_2(ticker, 'base_volume', 'volume_24h_base')
        if baseVolume == 'nan':
            baseVolume = '0'
        quoteVolume = self.safe_string_2(ticker, 'quote_volume', 'volume_24h_quote')
        if quoteVolume == 'nan':
            quoteVolume = '0'
        percentage = self.safe_string(ticker, 'change_percentage')
        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': None,
            'close': last,
            'last': last,
            'previousClose': None,
            'change': None,
            'percentage': percentage,
            'average': None,
            'baseVolume': baseVolume,
            'quoteVolume': quoteVolume,
            'markPrice': self.safe_string(ticker, 'mark_price'),
            'indexPrice': self.safe_string(ticker, 'index_price'),
            'info': 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://www.gate.io/docs/developers/apiv4/en/#get-details-of-a-specifc-order
        https://www.gate.io/docs/developers/apiv4/en/#list-futures-tickers
        https://www.gate.io/docs/developers/apiv4/en/#list-futures-tickers-2
        https://www.gate.io/docs/developers/apiv4/en/#list-tickers-of-options-contracts

        :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)
        first = self.safe_string(symbols, 0)
        market = None
        if first is not None:
            market = self.market(first)
        type, query = self.handle_market_type_and_params('fetchTickers', market, params)
        request, requestParams = self.prepare_request(None, type, query)
        response = None
        request['timezone'] = 'utc0'  # default to utc
        if type == 'spot' or type == 'margin':
            response = await self.publicSpotGetTickers(self.extend(request, requestParams))
        elif type == 'swap':
            response = await self.publicFuturesGetSettleTickers(self.extend(request, requestParams))
        elif type == 'future':
            response = await self.publicDeliveryGetSettleTickers(self.extend(request, requestParams))
        elif type == 'option':
            self.check_required_argument('fetchTickers', symbols, 'symbols')
            marketId = market['id']
            optionParts = marketId.split('-')
            request['underlying'] = self.safe_string(optionParts, 0)
            response = await self.publicOptionsGetTickers(self.extend(request, requestParams))
        else:
            raise NotSupported(self.id + ' fetchTickers() not support self market type, provide symbols or set params["defaultType"] to one from spot/margin/swap/future/option')
        return self.parse_tickers(response, symbols)

    def parse_balance_helper(self, entry):
        account = self.account()
        account['used'] = self.safe_string_2(entry, 'freeze', 'locked')
        account['free'] = self.safe_string(entry, 'available')
        account['total'] = self.safe_string(entry, 'total')
        if 'borrowed' in entry:
            account['debt'] = self.safe_string(entry, 'borrowed')
        return account

    async def fetch_balance(self, params={}) -> Balances:
        """
        :param dict [params]: exchange specific parameters
        :param str [params.type]: spot, margin, swap or future, if not provided self.options['defaultType'] is used
        :param str [params.settle]: 'btc' or 'usdt' - settle currency for perpetual swap and future - default="usdt" for swap and "btc" for future
        :param str [params.marginMode]: 'cross' or 'isolated' - marginMode for margin trading if not provided self.options['defaultMarginMode'] is used
        :param str [params.symbol]: margin only - unified ccxt symbol
        :param boolean [params.unifiedAccount]: default False, set to True for fetching the unified account balance
        :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
        """
        await self.load_markets()
        await self.load_unified_status()
        symbol = self.safe_string(params, 'symbol')
        params = self.omit(params, 'symbol')
        isUnifiedAccount = False
        isUnifiedAccount, params = self.handle_option_and_params(params, 'fetchBalance', 'unifiedAccount')
        type, query = self.handle_market_type_and_params('fetchBalance', None, params)
        request, requestParams = self.prepare_request(None, type, query)
        marginMode, requestQuery = self.get_margin_mode(False, requestParams)
        if symbol is not None:
            market = self.market(symbol)
            request['currency_pair'] = market['id']
        response = None
        if isUnifiedAccount:
            response = await self.privateUnifiedGetAccounts(self.extend(request, params))
        elif type == 'spot':
            if marginMode == 'spot':
                response = await self.privateSpotGetAccounts(self.extend(request, requestQuery))
            elif marginMode == 'margin':
                response = await self.privateMarginGetAccounts(self.extend(request, requestQuery))
            elif marginMode == 'cross_margin':
                response = await self.privateMarginGetCrossAccounts(self.extend(request, requestQuery))
            else:
                raise NotSupported(self.id + ' fetchBalance() not support self marginMode')
        elif type == 'funding':
            response = await self.privateMarginGetFundingAccounts(self.extend(request, requestQuery))
        elif type == 'swap':
            response = await self.privateFuturesGetSettleAccounts(self.extend(request, requestQuery))
        elif type == 'future':
            response = await self.privateDeliveryGetSettleAccounts(self.extend(request, requestQuery))
        elif type == 'option':
            response = await self.privateOptionsGetAccounts(self.extend(request, requestQuery))
        else:
            raise NotSupported(self.id + ' fetchBalance() not support self market type')
        contract = ((type == 'swap') or (type == 'future') or (type == 'option'))
        if contract:
            response = [response]
        #
        # Spot / margin funding
        #
        #     [
        #         {
        #             "currency": "DBC",
        #             "available": "0",
        #             "locked": "0"
        #             "lent": "0",  # margin funding only
        #             "total_lent": "0"  # margin funding only
        #         },
        #         ...
        #     ]
        #
        #  Margin
        #
        #    [
        #        {
        #            "currency_pair": "DOGE_USDT",
        #            "locked": False,
        #            "risk": "9999.99",
        #            "base": {
        #                "currency": "DOGE",
        #                "available": "0",
        #                "locked": "0",
        #                "borrowed": "0",
        #                "interest": "0"
        #            },
        #            "quote": {
        #                "currency": "USDT",
        #                "available": "0.73402",
        #                "locked": "0",
        #                "borrowed": "0",
        #                "interest": "0"
        #            }
        #        },
        #        ...
        #    ]
        #
        # Cross margin
        #
        #    {
        #        "user_id": 10406147,
        #        "locked": False,
        #        "balances": {
        #            "USDT": {
        #                "available": "1",
        #                "freeze": "0",
        #                "borrowed": "0",
        #                "interest": "0"
        #            }
        #        },
        #        "total": "1",
        #        "borrowed": "0",
        #        "interest": "0",
        #        "risk": "9999.99"
        #    }
        #
        #  Perpetual Swap
        #
        #    {
        #        "order_margin": "0",
        #        "point": "0",
        #        "bonus": "0",
        #        "history": {
        #            "dnw": "2.1321",
        #            "pnl": "11.5351",
        #            "refr": "0",
        #            "point_fee": "0",
        #            "fund": "-0.32340576684",
        #            "bonus_dnw": "0",
        #            "point_refr": "0",
        #            "bonus_offset": "0",
        #            "fee": "-0.20132775",
        #            "point_dnw": "0",
        #        },
        #        "unrealised_pnl": "13.315100000006",
        #        "total": "12.51345151332",
        #        "available": "0",
        #        "in_dual_mode": False,
        #        "currency": "USDT",
        #        "position_margin": "12.51345151332",
        #        "user": "6333333",
        #    }
        #
        # Delivery Future
        #
        #    {
        #        "order_margin": "0",
        #        "point": "0",
        #        "history": {
        #            "dnw": "1",
        #            "pnl": "0",
        #            "refr": "0",
        #            "point_fee": "0",
        #            "point_dnw": "0",
        #            "settle": "0",
        #            "settle_fee": "0",
        #            "point_refr": "0",
        #            "fee": "0",
        #        },
        #        "unrealised_pnl": "0",
        #        "total": "1",
        #        "available": "1",
        #        "currency": "USDT",
        #        "position_margin": "0",
        #        "user": "6333333",
        #    }
        #
        # option
        #
        #     {
        #         "order_margin": "0",
        #         "bid_order_margin": "0",
        #         "init_margin": "0",
        #         "history": {
        #             "dnw": "32",
        #             "set": "0",
        #             "point_fee": "0",
        #             "point_dnw": "0",
        #             "prem": "0",
        #             "point_refr": "0",
        #             "insur": "0",
        #             "fee": "0",
        #             "refr": "0"
        #         },
        #         "total": "32",
        #         "available": "32",
        #         "liq_triggered": False,
        #         "maint_margin": "0",
        #         "ask_order_margin": "0",
        #         "point": "0",
        #         "position_notional_limit": "2000000",
        #         "unrealised_pnl": "0",
        #         "equity": "32",
        #         "user": 5691076,
        #         "currency": "USDT",
        #         "short_enabled": False,
        #         "orders_limit": 10
        #     }
        #
        # unified
        #
        #     {
        #         "user_id": 10001,
        #         "locked": False,
        #         "balances": {
        #             "ETH": {
        #                 "available": "0",
        #                 "freeze": "0",
        #                 "borrowed": "0.075393666654",
        #                 "negative_liab": "0",
        #                 "futures_pos_liab": "0",
        #                 "equity": "1016.1",
        #                 "total_freeze": "0",
        #                 "total_liab": "0"
        #             },
        #             "POINT": {
        #                 "available": "9999999999.017023138734",
        #                 "freeze": "0",
        #                 "borrowed": "0",
        #                 "negative_liab": "0",
        #                 "futures_pos_liab": "0",
        #                 "equity": "12016.1",
        #                 "total_freeze": "0",
        #                 "total_liab": "0"
        #             },
        #             "USDT": {
        #                 "available": "0.00000062023",
        #                 "freeze": "0",
        #                 "borrowed": "0",
        #                 "negative_liab": "0",
        #                 "futures_pos_liab": "0",
        #                 "equity": "16.1",
        #                 "total_freeze": "0",
        #                 "total_liab": "0"
        #             }
        #         },
        #         "total": "230.94621713",
        #         "borrowed": "161.66395521",
        #         "total_initial_margin": "1025.0524665088",
        #         "total_margin_balance": "3382495.944473949183",
        #         "total_maintenance_margin": "205.01049330176",
        #         "total_initial_margin_rate": "3299.827135672679",
        #         "total_maintenance_margin_rate": "16499.135678363399",
        #         "total_available_margin": "3381470.892007440383",
        #         "unified_account_total": "3381470.892007440383",
        #         "unified_account_total_liab": "0",
        #         "unified_account_total_equity": "100016.1",
        #         "leverage": "2"
        #     }
        #
        result: dict = {
            'info': response,
        }
        isolated = marginMode == 'margin'
        data = response
        if 'balances' in data:  # True for cross_margin and unified
            flatBalances = []
            balances = self.safe_value(data, 'balances', [])
            # inject currency and create an artificial balance object
            # so it can follow the existent flow
            keys = list(balances.keys())
            for i in range(0, len(keys)):
                currencyId = keys[i]
                content = balances[currencyId]
                content['currency'] = currencyId
                flatBalances.append(content)
            data = flatBalances
        for i in range(0, len(data)):
            entry = data[i]
            if isolated:
                marketId = self.safe_string(entry, 'currency_pair')
                symbolInner = self.safe_symbol(marketId, None, '_', 'margin')
                base = self.safe_value(entry, 'base', {})
                quote = self.safe_value(entry, 'quote', {})
                baseCode = self.safe_currency_code(self.safe_string(base, 'currency'))
                quoteCode = self.safe_currency_code(self.safe_string(quote, 'currency'))
                subResult: dict = {}
                subResult[baseCode] = self.parse_balance_helper(base)
                subResult[quoteCode] = self.parse_balance_helper(quote)
                result[symbolInner] = self.safe_balance(subResult)
            else:
                code = self.safe_currency_code(self.safe_string(entry, 'currency'))
                result[code] = self.parse_balance_helper(entry)
        returnResult = result if isolated else self.safe_balance(result)
        return returnResult

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

        https://www.gate.io/docs/developers/apiv4/en/#market-candlesticks       # spot
        https://www.gate.io/docs/developers/apiv4/en/#get-futures-candlesticks  # swap
        https://www.gate.io/docs/developers/apiv4/en/#market-candlesticks       # future
        https://www.gate.io/docs/developers/apiv4/en/#get-options-candlesticks  # option

        :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, limit is conflicted with since and params["until"], If either since and params["until"] is specified, request will be rejected
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.price]: "mark" or "index" for mark price and index price candles
        :param int [params.until]: timestamp in ms of the latest candle to fetch
        :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
        :returns int[][]: A list of candles ordered, open, high, low, close, volume(units in quote currency)
        """
        await self.load_markets()
        market = self.market(symbol)
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchOHLCV', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_deterministic('fetchOHLCV', symbol, since, limit, timeframe, params, 1000)
        if market['option']:
            return await self.fetch_option_ohlcv(symbol, timeframe, since, limit, params)
        price = self.safe_string(params, 'price')
        request: dict = {}
        request, params = self.prepare_request(market, None, params)
        request['interval'] = self.safe_string(self.timeframes, timeframe, timeframe)
        maxLimit = 1999 if market['contract'] else 1000
        limit = maxLimit if (limit is None) else min(limit, maxLimit)
        until = self.safe_integer(params, 'until')
        if until is not None:
            until = self.parse_to_int(until / 1000)
            params = self.omit(params, 'until')
        if since is not None:
            duration = self.parse_timeframe(timeframe)
            request['from'] = self.parse_to_int(since / 1000)
            distance = (limit - 1) * duration
            toTimestamp = self.sum(request['from'], distance)
            currentTimestamp = self.seconds()
            to = min(toTimestamp, currentTimestamp)
            if until is not None:
                request['to'] = min(to, until)
            else:
                request['to'] = to
        else:
            if until is not None:
                request['to'] = until
            request['limit'] = limit
        response = None
        if market['contract']:
            isMark = (price == 'mark')
            isIndex = (price == 'index')
            if isMark or isIndex:
                request['contract'] = price + '_' + market['id']
                params = self.omit(params, 'price')
            if market['future']:
                response = await self.publicDeliveryGetSettleCandlesticks(self.extend(request, params))
            elif market['swap']:
                response = await self.publicFuturesGetSettleCandlesticks(self.extend(request, params))
        else:
            response = await self.publicSpotGetCandlesticks(self.extend(request, params))
        return self.parse_ohlcvs(response, market, timeframe, since, limit)

    async def fetch_option_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}):
        # separated option logic because the from, to and limit parameters weren't functioning
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {}
        request, params = self.prepare_request(market, None, params)
        request['interval'] = self.safe_string(self.timeframes, timeframe, timeframe)
        response = await self.publicOptionsGetCandlesticks(self.extend(request, params))
        return self.parse_ohlcvs(response, market, timeframe, since, limit)

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

        https://www.gate.io/docs/developers/apiv4/en/#funding-rate-history

        :param str symbol: unified symbol of the market to fetch the funding rate history for
        :param int [since]: timestamp in ms of the earliest funding rate to fetch
        :param int [limit]: the maximum amount of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-history-structure>` to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms of the latest funding rate to fetch
        :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
        :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()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchFundingRateHistory', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_deterministic('fetchFundingRateHistory', symbol, since, limit, '8h', params)
        market = self.market(symbol)
        if not market['swap']:
            raise BadSymbol(self.id + ' fetchFundingRateHistory() supports swap contracts only')
        request: dict = {}
        request, params = self.prepare_request(market, None, params)
        if limit is not None:
            request['limit'] = limit
        if since is not None:
            request['from'] = self.parse_to_int(since / 1000)
        until = self.safe_integer(params, 'until')
        if until is not None:
            params = self.omit(params, 'until')
            request['to'] = self.parse_to_int(until / 1000)
        response = await self.publicFuturesGetSettleFundingRate(self.extend(request, params))
        #
        #     {
        #         "r": "0.00063521",
        #         "t": "1621267200000",
        #     }
        #
        rates = []
        for i in range(0, len(response)):
            entry = response[i]
            timestamp = self.safe_timestamp(entry, 't')
            rates.append({
                'info': entry,
                'symbol': symbol,
                'fundingRate': self.safe_number(entry, 'r'),
                '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_ohlcv(self, ohlcv, market: Market = None) -> list:
        #
        # Spot market candles
        #
        #    [
        #        "1660957920",  # timestamp
        #        "6227.070147198573",  # quote volume
        #        "0.0000133485",  # close
        #        "0.0000133615",  # high
        #        "0.0000133347",  # low
        #        "0.0000133468",  # open
        #        "466641934.99"  # base volume
        #    ]
        #
        #
        # Swap, Future, Option, Mark and Index price candles
        #
        #     {
        #          "t":1632873600,         # Unix timestamp in seconds
        #          "o": "41025",           # Open price
        #          "h": "41882.17",        # Highest price
        #          "c": "41776.92",        # Close price
        #          "l": "40783.94"         # Lowest price
        #     }
        #
        if isinstance(ohlcv, list):
            return [
                self.safe_timestamp(ohlcv, 0),   # unix timestamp in seconds
                self.safe_number(ohlcv, 5),      # open price
                self.safe_number(ohlcv, 3),      # highest price
                self.safe_number(ohlcv, 4),      # lowest price
                self.safe_number(ohlcv, 2),      # close price
                self.safe_number(ohlcv, 6),      # trading volume
            ]
        else:
            # Swap, Future, Option, Mark and Index price candles
            return [
                self.safe_timestamp(ohlcv, 't'),  # unix timestamp in seconds
                self.safe_number(ohlcv, 'o'),    # open price
                self.safe_number(ohlcv, 'h'),    # highest price
                self.safe_number(ohlcv, 'l'),    # lowest price
                self.safe_number(ohlcv, 'c'),    # close price
                self.safe_number(ohlcv, 'v'),    # trading volume, None for mark or index price
            ]

    async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
        """
        get the list of most recent trades for a particular symbol

        https://www.gate.io/docs/developers/apiv4/en/#retrieve-market-trades
        https://www.gate.io/docs/developers/apiv4/en/#futures-trading-history
        https://www.gate.io/docs/developers/apiv4/en/#futures-trading-history-2
        https://www.gate.io/docs/developers/apiv4/en/#options-trade-history

        :param str symbol: unified symbol of the market to fetch trades for
        :param int [since]: timestamp in ms of the earliest trade to fetch
        :param int [limit]: the maximum amount of trades to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param int [params.until]: timestamp in ms of the latest trade to fetch
        :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
        :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
        """
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchTrades', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_dynamic('fetchTrades', symbol, since, limit, params)
        market = self.market(symbol)
        #
        # spot
        #
        #     request: Dict = {
        #         'currency_pair': market['id'],
        #         'limit': limit,  # maximum number of records to be returned in a single list
        #         'last_id': 'id',  # specify list staring point using the id of last record in previous list-query results
        #         'reverse': False,  # True to retrieve records where id is smaller than the specified last_id, False to retrieve records where id is larger than the specified last_id
        #     }
        #
        # swap, future
        #
        #     request: Dict = {
        #         'settle': market['settleId'],
        #         'contract': market['id'],
        #         'limit': limit,  # maximum number of records to be returned in a single list
        #         'last_id': 'id',  # specify list staring point using the id of last record in previous list-query results
        #         'from': since / 1000),  # starting time in seconds, if not specified, to and limit will be used to limit response items
        #         'to': self.seconds(),  # end time in seconds, default to current time
        #     }
        #
        request, query = self.prepare_request(market, None, params)
        until = self.safe_integer_2(params, 'to', 'until')
        if until is not None:
            params = self.omit(params, ['until'])
            request['to'] = self.parse_to_int(until / 1000)
        if limit is not None:
            request['limit'] = min(limit, 1000)  # default 100, max 1000
        if since is not None and (market['contract']):
            request['from'] = self.parse_to_int(since / 1000)
        response = None
        if market['type'] == 'spot' or market['type'] == 'margin':
            response = await self.publicSpotGetTrades(self.extend(request, query))
        elif market['swap']:
            response = await self.publicFuturesGetSettleTrades(self.extend(request, query))
        elif market['future']:
            response = await self.publicDeliveryGetSettleTrades(self.extend(request, query))
        elif market['type'] == 'option':
            response = await self.publicOptionsGetTrades(self.extend(request, query))
        else:
            raise NotSupported(self.id + ' fetchTrades() not support self market type.')
        #
        # spot
        #
        #     [
        #         {
        #             "id": "1852958144",
        #             "create_time": "1634673259",
        #             "create_time_ms": "1634673259378.105000",
        #             "currency_pair": "ADA_USDT",
        #             "side": "sell",
        #             "amount": "307.078",
        #             "price": "2.104",
        #         }
        #     ]
        #
        # perpetual swap
        #
        #     [
        #         {
        #              "size": "2",
        #              "id": "2522911",
        #              "create_time_ms": "1634673380.182",
        #              "create_time": "1634673380.182",
        #              "contract": "ADA_USDT",
        #              "price": "2.10486",
        #         }
        #     ]
        #
        # option
        #
        #     [
        #         {
        #             "size": -5,
        #             "id": 25,
        #             "create_time": 1682378573,
        #             "contract": "ETH_USDT-20230526-2000-P",
        #             "price": "209.1"
        #         }
        #     ]
        #
        return self.parse_trades(response, market, since, limit)

    async def fetch_order_trades(self, id: str, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetch all the trades made from a single order

        https://www.gate.io/docs/developers/apiv4/en/#list-personal-trading-history
        https://www.gate.io/docs/developers/apiv4/en/#list-personal-trading-history-2
        https://www.gate.io/docs/developers/apiv4/en/#list-personal-trading-history-3
        https://www.gate.io/docs/developers/apiv4/en/#list-personal-trading-history-4

        :param str id: order id
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch trades for
        :param int [limit]: the maximum number of trades to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrderTrades() requires a symbol argument')
        await self.load_markets()
        #
        #      [
        #          {
        #              "id":"3711449544",
        #              "create_time":"1655486040",
        #              "create_time_ms":"1655486040177.599900",
        #              "currency_pair":"SHIB_USDT",
        #              "side":"buy",
        #              "role":"taker",
        #              "amount":"1360039",
        #              "price":"0.0000081084",
        #              "order_id":"169717399644",
        #              "fee":"2720.078",
        #              "fee_currency":"SHIB",
        #              "point_fee":"0",
        #              "gt_fee":"0"
        #          }
        #      ]
        #
        response = await self.fetch_my_trades(symbol, since, limit, {'order_id': id})
        return response

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

        https://www.gate.io/docs/developers/apiv4/en/#list-personal-trading-history
        https://www.gate.io/docs/developers/apiv4/en/#list-personal-trading-history-2
        https://www.gate.io/docs/developers/apiv4/en/#list-personal-trading-history-3
        https://www.gate.io/docs/developers/apiv4/en/#list-personal-trading-history-4

        :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 str [params.marginMode]: 'cross' or 'isolated' - marginMode for margin trading if not provided self.options['defaultMarginMode'] is used
        :param str [params.type]: 'spot', 'swap', or 'future', if not provided self.options['defaultMarginMode'] is used
        :param int [params.until]: The latest timestamp, in ms, that fetched trades were made
        :param int [params.page]: *spot only* Page number
        :param str [params.order_id]: *spot only* Filter trades with specified order ID. symbol is also required if self field is present
        :param str [params.order]: *contract only* Futures order ID, return related data only if specified
        :param int [params.offset]: *contract only* list offset, starting from 0
        :param str [params.last_id]: *contract only* specify list staring point using the id of last record in previous list-query results
        :param int [params.count_total]: *contract only* whether to return total number matched, default to 0(no return)
        :param bool [params.unifiedAccount]: set to True for fetching trades in a unified account
        :param bool [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
        :returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
        """
        await self.load_markets()
        await self.load_unified_status()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchMyTrades', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_dynamic('fetchMyTrades', symbol, since, limit, params)
        type = None
        marginMode = None
        request: dict = {}
        market = self.market(symbol) if (symbol is not None) else None
        until = self.safe_integer(params, 'until')
        params = self.omit(params, ['until'])
        type, params = self.handle_market_type_and_params('fetchMyTrades', market, params)
        contract = (type == 'swap') or (type == 'future') or (type == 'option')
        if contract:
            request, params = self.prepare_request(market, type, params)
            if type == 'option':
                params = self.omit(params, 'order_id')
        else:
            if market is not None:
                request['currency_pair'] = market['id']  # Should always be set for non-trigger
            marginMode, params = self.get_margin_mode(False, params)
            request['account'] = marginMode
        if limit is not None:
            request['limit'] = limit  # default 100, max 1000
        if since is not None:
            request['from'] = self.parse_to_int(since / 1000)
        if until is not None:
            request['to'] = self.parse_to_int(until / 1000)
        response = None
        if type == 'spot' or type == 'margin':
            response = await self.privateSpotGetMyTrades(self.extend(request, params))
        elif type == 'swap':
            response = await self.privateFuturesGetSettleMyTradesTimerange(self.extend(request, params))
        elif type == 'future':
            response = await self.privateDeliveryGetSettleMyTrades(self.extend(request, params))
        elif type == 'option':
            response = await self.privateOptionsGetMyTrades(self.extend(request, params))
        else:
            raise NotSupported(self.id + ' fetchMyTrades() not support self market type.')
        #
        # spot
        #
        #     [
        #         {
        #             "id": "2876130500",
        #             "create_time": "1645464610",
        #             "create_time_ms": "1645464610777.399200",
        #             "currency_pair": "DOGE_USDT",
        #             "side": "sell",
        #             "role": "taker",
        #             "amount": "10.97",
        #             "price": "0.137384",
        #             "order_id": "125924049993",
        #             "fee": "0.00301420496",
        #             "fee_currency": "USDT",
        #             "point_fee": "0",
        #             "gt_fee": "0"
        #         }
        #     ]
        #
        # perpetual swap
        #
        #     [
        #         {
        #             "size": -5,
        #             "order_id": "130264979823",
        #             "id": 26884791,
        #             "role": "taker",
        #             "create_time": 1645465199.5472,
        #             "contract": "DOGE_USDT",
        #             "price": "0.136888"
        #         }
        #     ]
        #
        # future
        #
        #     [
        #         {
        #             "id": 121234231,
        #             "create_time": 1514764800.123,
        #             "contract": "BTC_USDT",
        #             "order_id": "21893289839",
        #             "size": 100,
        #             "price": "100.123",
        #             "role": "taker"
        #         }
        #     ]
        #
        # option
        #
        #     [
        #         {
        #             "underlying_price": "26817.84",
        #             "size": -1,
        #             "contract": "BTC_USDT-20230602-26500-C",
        #             "id": 16,
        #             "role": "taker",
        #             "create_time": 1685594770,
        #             "order_id": 2611026125,
        #             "price": "333"
        #         }
        #     ]
        #
        return self.parse_trades(response, market, since, limit)

    def parse_trade(self, trade: dict, market: Market = None) -> Trade:
        #
        # public
        #
        #  spot:
        #     {
        #         "id": "1334253759",
        #         "create_time": "1626342738",
        #         "create_time_ms": "1626342738331.497000",
        #         "currency_pair": "BTC_USDT",
        #         "side": "sell",
        #         "amount": "0.0022",
        #         "price": "32452.16"
        #     }
        #
        #  swap:
        #
        #    {
        #        "id": "442288327",
        #        "contract": "BTC_USDT",
        #        "create_time": "1739814676.707",
        #        "create_time_ms": "1739814676.707",
        #        "size": "-105",
        #        "price": "95594.8"
        #    }
        #
        #
        # public ws
        #
        #     {
        #         "id": 221994511,
        #         "time": 1580311438.618647,
        #         "price": "9309",
        #         "amount": "0.0019",
        #         "type": "sell"
        #     }
        #
        # spot rest
        #
        #     {
        #         "id": "2876130500",
        #         "create_time": "1645464610",
        #         "create_time_ms": "1645464610777.399200",
        #         "currency_pair": "DOGE_USDT",
        #         "side": "sell",
        #         "role": "taker",
        #         "amount": "10.97",
        #         "price": "0.137384",
        #         "order_id": "125924049993",
        #         "fee": "0.00301420496",
        #         "fee_currency": "USDT",
        #         "point_fee": "1.1",
        #         "gt_fee":"2.2"
        #     }
        #
        # perpetual swap rest
        #
        #     {
        #         "size": -5,
        #         "order_id": "130264979823",
        #         "id": 26884791,
        #         "role": "taker",
        #         "create_time": 1645465199.5472,
        #         "contract": "DOGE_USDT",
        #         "price": "0.136888"
        #     }
        #
        # future rest
        #
        #     {
        #         "id": 121234231,
        #         "create_time": 1514764800.123,
        #         "contract": "BTC_USDT",
        #         "order_id": "21893289839",
        #         "size": 100,
        #         "price": "100.123",
        #         "role": "taker"
        #     }
        #
        # fetchTrades: option
        #
        #     {
        #         "size": -5,
        #         "id": 25,
        #         "create_time": 1682378573,
        #         "contract": "ETH_USDT-20230526-2000-P",
        #         "price": "209.1"
        #     }
        #
        # fetchMyTrades: option
        #
        #     {
        #         "underlying_price": "26817.84",
        #         "size": -1,
        #         "contract": "BTC_USDT-20230602-26500-C",
        #         "id": 16,
        #         "role": "taker",
        #         "create_time": 1685594770,
        #         "order_id": 2611026125,
        #         "price": "333"
        #     }
        #
        id = self.safe_string_2(trade, 'id', 'trade_id')
        timestamp: Int = None
        msString = self.safe_string(trade, 'create_time_ms')
        if msString is not None:
            msString = Precise.string_mul(msString, '1000')
            msString = msString[0:13]
            timestamp = self.parse_to_int(msString)
        else:
            timestamp = self.safe_timestamp_2(trade, 'time', 'create_time')
        marketId = self.safe_string_2(trade, 'currency_pair', 'contract')
        marketType = 'contract' if ('contract' in trade) else 'spot'
        market = self.safe_market(marketId, market, '_', marketType)
        amountString = self.safe_string_2(trade, 'amount', 'size')
        priceString = self.safe_string(trade, 'price')
        contractSide = 'sell' if Precise.string_lt(amountString, '0') else 'buy'
        amountString = Precise.string_abs(amountString)
        side = self.safe_string_2(trade, 'side', 'type', contractSide)
        orderId = self.safe_string(trade, 'order_id')
        feeAmount = self.safe_string(trade, 'fee')
        gtFee = self.omit_zero(self.safe_string(trade, 'gt_fee'))
        pointFee = self.omit_zero(self.safe_string(trade, 'point_fee'))
        fees = []
        if feeAmount is not None:
            feeCurrencyId = self.safe_string(trade, 'fee_currency')
            feeCurrencyCode = self.safe_currency_code(feeCurrencyId)
            if feeCurrencyCode is None:
                feeCurrencyCode = self.safe_string(market, 'settle')
            fees.append({
                'cost': feeAmount,
                'currency': feeCurrencyCode,
            })
        if gtFee is not None:
            fees.append({
                'cost': gtFee,
                'currency': 'GT',
            })
        if pointFee is not None:
            fees.append({
                'cost': pointFee,
                'currency': 'GatePoint',
            })
        takerOrMaker = self.safe_string(trade, 'role')
        return self.safe_trade({
            'info': trade,
            'id': id,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': market['symbol'],
            'order': orderId,
            'type': None,
            'side': side,
            'takerOrMaker': takerOrMaker,
            'price': priceString,
            'amount': amountString,
            'cost': None,
            'fee': None,
            'fees': fees,
        }, market)

    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://www.gate.io/docs/developers/apiv4/en/#retrieve-deposit-records

        :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
        :param int [params.until]: end time in ms
        :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
        :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
        """
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchDeposits', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_dynamic('fetchDeposits', code, since, limit, params)
        request: dict = {}
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']  # todo: currencies have network-junctions
        if limit is not None:
            request['limit'] = limit
        if since is not None:
            start = self.parse_to_int(since / 1000)
            request['from'] = start
            request['to'] = self.sum(start, 30 * 24 * 60 * 60)
        request, params = self.handle_until_option('to', request, params)
        response = await self.privateWalletGetDeposits(self.extend(request, params))
        return self.parse_transactions(response, currency)

    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://www.gate.io/docs/developers/apiv4/en/#retrieve-withdrawal-records

        :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
        :param int [params.until]: end time in ms
        :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
        :returns dict[]: a list of `transaction structures <https://docs.ccxt.com/#/?id=transaction-structure>`
        """
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchWithdrawals', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_dynamic('fetchWithdrawals', code, since, limit, params)
        request: dict = {}
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']  # todo: currencies have network-junctions
        if limit is not None:
            request['limit'] = limit
        if since is not None:
            start = self.parse_to_int(since / 1000)
            request['from'] = start
            request['to'] = self.sum(start, 30 * 24 * 60 * 60)
        request, params = self.handle_until_option('to', request, params)
        response = await self.privateWalletGetWithdrawals(self.extend(request, params))
        return self.parse_transactions(response, currency)

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

        https://www.gate.io/docs/developers/apiv4/en/#withdraw

        :param str code: unified currency code
        :param float amount: the amount to withdraw
        :param str address: the address to withdraw to
        :param str tag:
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `transaction structure <https://docs.ccxt.com/#/?id=transaction-structure>`
        """
        tag, params = self.handle_withdraw_tag_and_params(tag, params)
        self.check_address(address)
        await self.load_markets()
        currency = self.currency(code)
        request: dict = {
            'currency': currency['id'],  # todo: currencies have network-junctions
            'address': address,
            'amount': self.currency_to_precision(code, amount),
        }
        if tag is not None:
            request['memo'] = tag
        networkCode = None
        networkCode, params = self.handle_network_code_and_params(params)
        if networkCode is not None:
            request['chain'] = self.network_code_to_id(networkCode)
        response = await self.privateWithdrawalsPostWithdrawals(self.extend(request, params))
        #
        #    {
        #        "id": "w13389675",
        #        "currency": "USDT",
        #        "amount": "50",
        #        "address": "TUu2rLFrmzUodiWfYki7QCNtv1akL682p1",
        #        "memo": null
        #    }
        #
        return self.parse_transaction(response, currency)

    def parse_transaction_status(self, status: Str):
        statuses: dict = {
            'PEND': 'pending',
            'REQUEST': 'pending',
            'DMOVE': 'pending',
            'MANUAL': 'pending',
            'VERIFY': 'pending',
            'PROCES': 'pending',
            'EXTPEND': 'pending',
            'SPLITPEND': 'pending',
            'CANCEL': 'canceled',
            'FAIL': 'failed',
            'INVALID': 'failed',
            'DONE': 'ok',
            'BCODE': 'ok',  # GateCode withdrawal
        }
        return self.safe_string(statuses, status, status)

    def parse_transaction_type(self, type):
        types: dict = {
            'd': 'deposit',
            'w': 'withdrawal',
        }
        return self.safe_string(types, type, type)

    def parse_transaction(self, transaction: dict, currency: Currency = None) -> Transaction:
        #
        # fetchDeposits
        #
        #     {
        #         "id": "d33361395",
        #         "currency": "USDT_TRX",
        #         "address": "TErdnxenuLtXfnMafLbfappYdHtnXQ5U4z",
        #         "amount": "100",
        #         "txid": "ae9374de34e558562fe18cbb1bf9ab4d9eb8aa7669d65541c9fa2a532c1474a0",
        #         "timestamp": "1626345819",
        #         "status": "DONE",
        #         "memo": ""
        #     }
        #
        # withdraw
        #
        #     {
        #         "id":"w64413318",
        #         "currency":"usdt",
        #         "amount":"10150",
        #         "address":"0x0ab891497116f7f5532a4c2f4f7b1784488628e1",
        #         "memo":null,
        #         "status":"REQUEST",
        #         "chain":"eth",
        #         "withdraw_order_id":"",
        #         "fee_amount":"4.15000000"
        #     }
        #
        # fetchWithdrawals
        #
        #     {
        #         "id": "210496",
        #         "timestamp": "1542000000",
        #         "withdraw_order_id": "order_123456",
        #         "currency": "USDT",
        #         "address": "1HkxtBAMrA3tP5ENnYY2CZortjZvFDH5Cs",
        #         "txid": "128988928203223323290",
        #         "block_number": "41575382",
        #         "amount": "222.61",
        #         "fee": "0.01",
        #         "memo": "",
        #         "status": "DONE",
        #         "chain": "TRX"
        #     }
        #
        #     {
        #         "id": "w13389675",
        #         "currency": "USDT",
        #         "amount": "50",
        #         "address": "TUu2rLFrmzUodiWfYki7QCNtv1akL682p1",
        #         "memo": null
        #     }
        #
        #     {
        #         "currency":"usdt",
        #         "address":"0x01c0A9b7b4CdE774AF0f3E47CB4f1c2CCdBa0806",
        #         "amount":"1880",
        #         "chain":"eth"
        #     }
        #
        id = self.safe_string(transaction, 'id')
        type = None
        amountString = self.safe_string(transaction, 'amount')
        if id is not None:
            if id[0] == 'b':
                # GateCode handling
                type = 'deposit' if Precise.string_gt(amountString, '0') else 'withdrawal'
                amountString = Precise.string_abs(amountString)
            else:
                type = self.parse_transaction_type(id[0])
        feeCostString = self.safe_string_2(transaction, 'fee', 'fee_amount')
        if type == 'withdrawal':
            amountString = Precise.string_sub(amountString, feeCostString)
        networkId = self.safe_string_upper(transaction, 'chain')
        currencyId = self.safe_string(transaction, 'currency')
        code = self.safe_currency_code(currencyId)
        txid = self.safe_string(transaction, 'txid')
        rawStatus = self.safe_string(transaction, 'status')
        status = self.parse_transaction_status(rawStatus)
        address = self.safe_string(transaction, 'address')
        tag = self.safe_string(transaction, 'memo')
        timestamp = self.safe_timestamp(transaction, 'timestamp')
        return {
            'info': transaction,
            'id': id,
            'txid': txid,
            'currency': code,
            'amount': self.parse_number(amountString),
            'network': self.network_id_to_code(networkId),
            'address': address,
            'addressTo': None,
            'addressFrom': None,
            'tag': tag,
            'tagTo': None,
            'tagFrom': None,
            'status': status,
            'type': type,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'updated': None,
            'internal': None,
            'comment': None,
            'fee': {
                'currency': code,
                'cost': self.parse_number(feeCostString),
            },
        }

    async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
        Create an order on the exchange

        https://www.gate.io/docs/developers/apiv4/en/#create-an-order
        https://www.gate.io/docs/developers/apiv4/en/#create-a-price-triggered-order
        https://www.gate.io/docs/developers/apiv4/en/#create-a-futures-order
        https://www.gate.io/docs/developers/apiv4/en/#create-a-price-triggered-order-2
        https://www.gate.io/docs/developers/apiv4/en/#create-a-futures-order-2
        https://www.gate.io/docs/developers/apiv4/en/#create-a-price-triggered-order-3
        https://www.gate.io/docs/developers/apiv4/en/#create-an-options-order

        :param str symbol: Unified CCXT market symbol
        :param str type: 'limit' or 'market' *"market" is contract only*
        :param str side: 'buy' or 'sell'
        :param float amount: the amount of currency to trade
        :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 float [params.triggerPrice]: The price at which a trigger order is triggered at
        :param str [params.timeInForce]: "GTC", "IOC", or "PO"
        :param float [params.stopLossPrice]: The price at which a stop loss order is triggered at
        :param float [params.takeProfitPrice]: The price at which a take profit order is triggered at
        :param str [params.marginMode]: 'cross' or 'isolated' - marginMode for margin trading if not provided self.options['defaultMarginMode'] is used
        :param int [params.iceberg]: Amount to display for the iceberg order, Null or 0 for normal orders, Set to -1 to hide the order completely
        :param str [params.text]: User defined information
        :param str [params.account]: *spot and margin only* "spot", "margin" or "cross_margin"
        :param bool [params.auto_borrow]: *margin only* Used in margin or cross margin trading to allow automatic loan of insufficient amount if balance is not enough
        :param str [params.settle]: *contract only* Unified Currency Code for settle currency
        :param bool [params.reduceOnly]: *contract only* Indicates if self order is to reduce the size of a position
        :param bool [params.close]: *contract only* Set to close the position, with size set to 0
        :param bool [params.auto_size]: *contract only* Set side to close dual-mode position, close_long closes the long side, while close_short the short one, size also needs to be set to 0
        :param int [params.price_type]: *contract only* 0 latest deal price, 1 mark price, 2 index price
        :param float [params.cost]: *spot market buy only* the quote quantity that can be used alternative for the amount
        :param bool [params.unifiedAccount]: set to True for creating an order in the unified account
        :returns dict|None: `An order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        await self.load_unified_status()
        market = self.market(symbol)
        trigger = self.safe_value(params, 'trigger')
        triggerPrice = self.safe_value_2(params, 'triggerPrice', 'stopPrice')
        stopLossPrice = self.safe_value(params, 'stopLossPrice', triggerPrice)
        takeProfitPrice = self.safe_value(params, 'takeProfitPrice')
        isStopLossOrder = stopLossPrice is not None
        isTakeProfitOrder = takeProfitPrice is not None
        isTpsl = isStopLossOrder or isTakeProfitOrder
        nonTriggerOrder = not isTpsl and (trigger is None)
        orderRequest = self.create_order_request(symbol, type, side, amount, price, params)
        response = None
        if market['spot'] or market['margin']:
            if nonTriggerOrder:
                response = await self.privateSpotPostOrders(orderRequest)
            else:
                response = await self.privateSpotPostPriceOrders(orderRequest)
        elif market['swap']:
            if nonTriggerOrder:
                response = await self.privateFuturesPostSettleOrders(orderRequest)
            else:
                response = await self.privateFuturesPostSettlePriceOrders(orderRequest)
        elif market['future']:
            if nonTriggerOrder:
                response = await self.privateDeliveryPostSettleOrders(orderRequest)
            else:
                response = await self.privateDeliveryPostSettlePriceOrders(orderRequest)
        else:
            response = await self.privateOptionsPostOrders(orderRequest)
        # response = await getattr(self, method)(self.deep_extend(request, params))
        #
        # spot
        #
        #     {
        #         "id": "95282841887",
        #         "text": "apiv4",
        #         "create_time": "1637383156",
        #         "update_time": "1637383156",
        #         "create_time_ms": 1637383156017,
        #         "update_time_ms": 1637383156017,
        #         "status": "open",
        #         "currency_pair": "ETH_USDT",
        #         "type": "limit",
        #         "account": "spot",
        #         "side": "buy",
        #         "amount": "0.01",
        #         "price": "3500",
        #         "time_in_force": "gtc",
        #         "iceberg": "0",
        #         "left": "0.01",
        #         "fill_price": "0",
        #         "filled_total": "0",
        #         "fee": "0",
        #         "fee_currency": "ETH",
        #         "point_fee": "0",
        #         "gt_fee": "0",
        #         "gt_discount": False,
        #         "rebated_fee": "0",
        #         "rebated_fee_currency": "USDT"
        #     }
        #
        # spot conditional
        #
        #     {"id": 5891843}
        #
        # futures, perpetual swaps and options
        #
        #     {
        #         "id": 95938572327,
        #         "contract": "ETH_USDT",
        #         "mkfr": "0",
        #         "tkfr": "0.0005",
        #         "tif": "gtc",
        #         "is_reduce_only": False,
        #         "create_time": 1637384600.08,
        #         "price": "3000",
        #         "size": 1,
        #         "refr": "0",
        #         "left": 1,
        #         "text": "api",
        #         "fill_price": "0",
        #         "user": 2436035,
        #         "status": "open",
        #         "is_liq": False,
        #         "refu": 0,
        #         "is_close": False,
        #         "iceberg": 0
        #     }
        #
        # futures and perpetual swaps conditionals
        #
        #     {"id": 7615567}
        #
        return self.parse_order(response, market)

    def create_orders_request(self, orders: List[OrderRequest], params={}):
        ordersRequests = []
        orderSymbols = []
        ordersLength = len(orders)
        if ordersLength == 0:
            raise BadRequest(self.id + ' createOrders() requires at least one order')
        if ordersLength > 10:
            raise BadRequest(self.id + ' createOrders() accepts a maximum of 10 orders at a time')
        for i in range(0, len(orders)):
            rawOrder = orders[i]
            marketId = self.safe_string(rawOrder, 'symbol')
            orderSymbols.append(marketId)
            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_value(rawOrder, 'params', {})
            extendedParams = self.extend(orderParams, params)  # the request does not accept extra params since it's a list, so we're extending each order with the common params
            triggerValue = self.safe_value_n(orderParams, ['triggerPrice', 'stopPrice', 'takeProfitPrice', 'stopLossPrice'])
            if triggerValue is not None:
                raise NotSupported(self.id + ' createOrders() does not support advanced order properties(stopPrice, takeProfitPrice, stopLossPrice)')
            extendedParams['textIsRequired'] = True  # Gate.io requires a text parameter for each order here
            orderRequest = self.create_order_request(marketId, type, side, amount, price, extendedParams)
            ordersRequests.append(orderRequest)
        symbols = self.market_symbols(orderSymbols, None, False, True, True)
        market = self.market(symbols[0])
        if market['future'] or market['option']:
            raise NotSupported(self.id + ' createOrders() does not support futures or options markets')
        return ordersRequests

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

        https://www.gate.io/docs/developers/apiv4/en/#get-a-single-order-2
        https://www.gate.io/docs/developers/apiv4/en/#create-a-batch-of-orders
        https://www.gate.io/docs/developers/apiv4/en/#create-a-batch-of-futures-orders

        :param Array orders: list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        await self.load_unified_status()
        ordersRequests = self.create_orders_request(orders, params)
        firstOrder = orders[0]
        market = self.market(firstOrder['symbol'])
        response = None
        if market['spot']:
            response = await self.privateSpotPostBatchOrders(ordersRequests)
        elif market['swap']:
            response = await self.privateFuturesPostSettleBatchOrders(ordersRequests)
        return self.parse_orders(response)

    def create_order_request(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        market = self.market(symbol)
        contract = market['contract']
        trigger = self.safe_value(params, 'trigger')
        triggerPrice = self.safe_value_2(params, 'triggerPrice', 'stopPrice')
        stopLossPrice = self.safe_value(params, 'stopLossPrice', triggerPrice)
        takeProfitPrice = self.safe_value(params, 'takeProfitPrice')
        isStopLossOrder = stopLossPrice is not None
        isTakeProfitOrder = takeProfitPrice is not None
        isTpsl = isStopLossOrder or isTakeProfitOrder
        if isStopLossOrder and isTakeProfitOrder:
            raise ExchangeError(self.id + ' createOrder() stopLossPrice and takeProfitPrice cannot both be defined')
        reduceOnly = self.safe_value(params, 'reduceOnly')
        exchangeSpecificTimeInForce = self.safe_string_lower_n(params, ['timeInForce', 'tif', 'time_in_force'])
        postOnly = None
        postOnly, params = self.handle_post_only(type == 'market', exchangeSpecificTimeInForce == 'poc', params)
        timeInForce = self.handle_time_in_force(params)
        if postOnly:
            timeInForce = 'poc'
        # we only omit the unified params here
        # self is because the other params will get extended into the request
        params = self.omit(params, ['stopPrice', 'triggerPrice', 'stopLossPrice', 'takeProfitPrice', 'reduceOnly', 'timeInForce', 'postOnly'])
        isLimitOrder = (type == 'limit')
        isMarketOrder = (type == 'market')
        if isLimitOrder and price is None:
            raise ArgumentsRequired(self.id + ' createOrder() requires a price argument for ' + type + ' orders')
        if isMarketOrder:
            if (timeInForce == 'poc') or (timeInForce == 'gtc'):
                raise ExchangeError(self.id + ' createOrder() timeInForce for market order can only be "FOK" or "IOC"')
            else:
                if timeInForce is None:
                    defaultTif = self.safe_string(self.options, 'defaultTimeInForce', 'IOC')
                    exchangeSpecificTif = self.safe_string(self.options['timeInForce'], defaultTif, 'ioc')
                    timeInForce = exchangeSpecificTif
            if contract:
                price = 0
        if contract:
            isClose = self.safe_value(params, 'close')
            if isClose:
                amount = 0
            else:
                amountToPrecision = self.amount_to_precision(symbol, amount)
                signedAmount = Precise.string_neg(amountToPrecision) if (side == 'sell') else amountToPrecision
                amount = int(signedAmount)
        request = None
        nonTriggerOrder = not isTpsl and (trigger is None)
        if nonTriggerOrder:
            if contract:
                # contract order
                request = {
                    'contract': market['id'],  # filled in prepareRequest above
                    'size': amount,  # int64, positive = bid, negative = ask
                    # 'iceberg': 0,  # int64, display size for iceberg order, 0 for non-iceberg, note that you will have to pay the taker fee for the hidden size
                    # 'close': False,  # True to close the position, with size set to 0
                    # 'reduce_only': False,  # St to be reduce-only order
                    # 'tif': 'gtc',  # gtc, ioc, poc PendingOrCancelled == postOnly order
                    # 'text': clientOrderId,  # 't-abcdef1234567890',
                    # 'auto_size': '',  # close_long, close_short, note size also needs to be set to 0
                }
                if not market['option']:
                    request['settle'] = market['settleId']  # filled in prepareRequest above
                if isMarketOrder:
                    request['price'] = '0'  # set to 0 for market orders
                else:
                    request['price'] = '0' if (price == 0) else self.price_to_precision(symbol, price)
                if reduceOnly is not None:
                    request['reduce_only'] = reduceOnly
                if timeInForce is not None:
                    request['tif'] = timeInForce
            else:
                marginMode = None
                marginMode, params = self.get_margin_mode(False, params)
                # spot order
                request = {
                    # 'text': clientOrderId,  # 't-abcdef1234567890',
                    'currency_pair': market['id'],  # filled in prepareRequest above
                    'type': type,
                    'account': marginMode,  # spot, margin, cross_margin, unified
                    'side': side,
                    # 'time_in_force': 'gtc',  # gtc, ioc, poc PendingOrCancelled == postOnly order
                    # 'iceberg': 0,  # amount to display for the iceberg order, null or 0 for normal orders, set to -1 to hide the order completely
                    # 'auto_borrow': False,  # used in margin or cross margin trading to allow automatic loan of insufficient amount if balance is not enough
                    # 'auto_repay': False,  # automatic repayment for automatic borrow loan generated by cross margin order, diabled by default
                }
                if isMarketOrder and (side == 'buy'):
                    quoteAmount = None
                    createMarketBuyOrderRequiresPrice = True
                    createMarketBuyOrderRequiresPrice, params = self.handle_option_and_params(params, 'createOrder', 'createMarketBuyOrderRequiresPrice', True)
                    cost = self.safe_number(params, 'cost')
                    params = self.omit(params, 'cost')
                    if cost is not None:
                        quoteAmount = self.cost_to_precision(symbol, cost)
                    elif createMarketBuyOrderRequiresPrice:
                        if price 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(quote quantity) in the amount argument')
                        else:
                            amountString = self.number_to_string(amount)
                            priceString = self.number_to_string(price)
                            costRequest = Precise.string_mul(amountString, priceString)
                            quoteAmount = self.cost_to_precision(symbol, costRequest)
                    else:
                        quoteAmount = self.cost_to_precision(symbol, amount)
                    request['amount'] = quoteAmount
                else:
                    request['amount'] = self.amount_to_precision(symbol, amount)
                if isLimitOrder:
                    request['price'] = self.price_to_precision(symbol, price)
                if timeInForce is not None:
                    request['time_in_force'] = timeInForce
            clientOrderId = self.safe_string_2(params, 'text', 'clientOrderId')
            textIsRequired = self.safe_bool(params, 'textIsRequired', False)
            if clientOrderId is not None:
                # user-defined, must follow the rules if not empty
                #     prefixed with t-
                #     no longer than 28 bytes without t- prefix
                #     can only include 0-9, A-Z, a-z, underscores(_), hyphens(-) or dots(.)
                if len(clientOrderId) > 28:
                    raise BadRequest(self.id + ' createOrder() clientOrderId or text param must be up to 28 characters')
                params = self.omit(params, ['text', 'clientOrderId', 'textIsRequired'])
                if clientOrderId[0] != 't':
                    clientOrderId = 't-' + clientOrderId
                request['text'] = clientOrderId
            else:
                if textIsRequired:
                    # batchOrders requires text in the request
                    request['text'] = 't-' + self.uuid16()
        else:
            if market['option']:
                raise NotSupported(self.id + ' createOrder() conditional option orders are not supported')
            if contract:
                # contract conditional order
                request = {
                    'initial': {
                        'contract': market['id'],
                        'size': amount,  # positive = buy, negative = sell, set to 0 to close the position
                        # 'price': '0' if (price == 0) else self.price_to_precision(symbol, price),  # set to 0 to use market price
                        # 'close': False,  # set to True if trying to close the position
                        # 'tif': 'gtc',  # gtc, ioc, if using market price, only ioc is supported
                        # 'text': clientOrderId,  # web, api, app
                        # 'reduce_only': False,
                    },
                    'settle': market['settleId'],
                }
                if type == 'market':
                    request['initial']['price'] = '0'
                else:
                    request['initial']['price'] = '0' if (price == 0) else self.price_to_precision(symbol, price)
                if trigger is None:
                    rule = None
                    triggerOrderPrice = None
                    if isStopLossOrder:
                        # we trigger orders be aliases for stopLoss orders because
                        # gateio doesn't accept conventional trigger orders for spot markets
                        rule = 1 if (side == 'buy') else 2
                        triggerOrderPrice = self.price_to_precision(symbol, stopLossPrice)
                    elif isTakeProfitOrder:
                        rule = 2 if (side == 'buy') else 1
                        triggerOrderPrice = self.price_to_precision(symbol, takeProfitPrice)
                    priceType = self.safe_integer(params, 'price_type', 0)
                    if priceType < 0 or priceType > 2:
                        raise BadRequest(self.id + ' createOrder() price_type should be 0 latest deal price, 1 mark price, 2 index price')
                    params = self.omit(params, ['price_type'])
                    request['trigger'] = {
                        # 'strategy_type': 0,  # 0 = by price, 1 = by price gap, only 0 is supported currently
                        'price_type': priceType,  # 0 latest deal price, 1 mark price, 2 index price
                        'price': self.price_to_precision(symbol, triggerOrderPrice),  # price or gap
                        'rule': rule,  # 1 means price_type >= price, 2 means price_type <= price
                        # 'expiration': expiration, how many seconds to wait for the condition to be triggered before cancelling the order
                    }
                if reduceOnly is not None:
                    request['initial']['reduce_only'] = reduceOnly
                if timeInForce is not None:
                    request['initial']['tif'] = timeInForce
            else:
                # spot conditional order
                options = self.safe_value(self.options, 'createOrder', {})
                marginMode = None
                marginMode, params = self.get_margin_mode(True, params)
                if timeInForce is None:
                    timeInForce = 'gtc'
                request = {
                    'put': {
                        'type': type,
                        'side': side,
                        'price': self.price_to_precision(symbol, price),
                        'amount': self.amount_to_precision(symbol, amount),
                        'account': marginMode,
                        'time_in_force': timeInForce,  # gtc, ioc(ioc is for taker only, so shouldnt't be in conditional order)
                    },
                    'market': market['id'],
                }
                if trigger is None:
                    defaultExpiration = self.safe_integer(options, 'expiration')
                    expiration = self.safe_integer(params, 'expiration', defaultExpiration)
                    rule = None
                    triggerOrderPrice = None
                    if isStopLossOrder:
                        # we trigger orders be aliases for stopLoss orders because
                        # gateio doesn't accept conventional trigger orders for spot markets
                        rule = '>=' if (side == 'buy') else '<='
                        triggerOrderPrice = self.price_to_precision(symbol, stopLossPrice)
                    elif isTakeProfitOrder:
                        rule = '<=' if (side == 'buy') else '>='
                        triggerOrderPrice = self.price_to_precision(symbol, takeProfitPrice)
                    request['trigger'] = {
                        'price': self.price_to_precision(symbol, triggerOrderPrice),
                        'rule': rule,  # >= triggered when market price larger than or equal to price field, <= triggered when market price less than or equal to price field
                        'expiration': expiration,  # required, how long(in seconds) to wait for the condition to be triggered before cancelling the order
                    }
        return self.extend(request, params)

    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://www.gate.io/docs/developers/apiv4/en/#create-an-order

        :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
        :param bool [params.unifiedAccount]: set to True for creating a unified account order
        :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        await self.load_unified_status()
        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)

    def edit_order_request(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
        market = self.market(symbol)
        marketType = None
        marketType, params = self.handle_market_type_and_params('editOrder', market, params)
        account = self.convert_type_to_account(marketType)
        isUnifiedAccount = False
        isUnifiedAccount, params = self.handle_option_and_params(params, 'editOrder', 'unifiedAccount')
        if isUnifiedAccount:
            account = 'unified'
        isLimitOrder = (type == 'limit')
        if account == 'spot':
            if not isLimitOrder:
                # exchange doesn't have market orders for spot
                raise InvalidOrder(self.id + ' editOrder() does not support ' + type + ' orders for ' + marketType + ' markets')
        request: dict = {
            'order_id': str(id),
            'currency_pair': market['id'],
            'account': account,
        }
        if amount is not None:
            if market['spot']:
                request['amount'] = self.amount_to_precision(symbol, amount)
            else:
                if side == 'sell':
                    request['size'] = self.parse_to_numeric(Precise.string_neg(self.amount_to_precision(symbol, amount)))
                else:
                    request['size'] = self.parse_to_numeric(self.amount_to_precision(symbol, amount))
        if price is not None:
            request['price'] = self.price_to_precision(symbol, price)
        if not market['spot']:
            request['settle'] = market['settleId']
        return self.extend(request, params)

    async def edit_order(self, id: str, symbol: str, type: OrderType, side: OrderSide, amount: Num = None, price: Num = None, params={}):
        """
        edit a trade order, gate currently only supports the modification of the price or amount fields

        https://www.gate.io/docs/developers/apiv4/en/#amend-an-order
        https://www.gate.io/docs/developers/apiv4/en/#amend-an-order-2

        :param str id: order id
        :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 the currency you want to trade in units of the 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 bool [params.unifiedAccount]: set to True for editing an order in a unified account
        :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        await self.load_unified_status()
        market = self.market(symbol)
        extendedRequest = self.edit_order_request(id, symbol, type, side, amount, price, params)
        response = None
        if market['spot']:
            response = await self.privateSpotPatchOrdersOrderId(extendedRequest)
        else:
            response = await self.privateFuturesPutSettleOrdersOrderId(extendedRequest)
        #
        #     {
        #         "id": "243233276443",
        #         "text": "apiv4",
        #         "create_time": "1670908873",
        #         "update_time": "1670914102",
        #         "create_time_ms": 1670908873077,
        #         "update_time_ms": 1670914102241,
        #         "status": "open",
        #         "currency_pair": "ADA_USDT",
        #         "type": "limit",
        #         "account": "spot",
        #         "side": "sell",
        #         "amount": "10",
        #         "price": "0.6",
        #         "time_in_force": "gtc",
        #         "iceberg": "0",
        #         "left": "10",
        #         "fill_price": "0",
        #         "filled_total": "0",
        #         "fee": "0",
        #         "fee_currency": "USDT",
        #         "point_fee": "0",
        #         "gt_fee": "0",
        #         "gt_maker_fee": "0",
        #         "gt_taker_fee": "0",
        #         "gt_discount": False,
        #         "rebated_fee": "0",
        #         "rebated_fee_currency": "ADA"
        #     }
        #
        return self.parse_order(response, market)

    def parse_order_status(self, status: Str):
        statuses: dict = {
            'open': 'open',
            '_new': 'open',
            'filled': 'closed',
            'cancelled': 'canceled',
            'liquidated': 'closed',
            'ioc': 'canceled',
            'failed': 'canceled',
            'expired': 'canceled',
            'finished': 'closed',
            'finish': 'closed',
            'succeeded': 'closed',
        }
        return self.safe_string(statuses, status, status)

    def parse_order(self, order: dict, market: Market = None) -> Order:
        #
        # SPOT
        # createOrder/cancelOrder/fetchOrder/editOrder
        #
        #    {
        #        "id": "62364648575",
        #        "text": "apiv4",
        #        "create_time": "1626354834",
        #        "update_time": "1626354834",
        #        "create_time_ms": "1626354833544",
        #        "update_time_ms": "1626354833544",
        #        "status": "open",
        #        "currency_pair": "BTC_USDT",
        #        "type": "limit",
        #        "account": "spot",
        #        "side": "buy",
        #        "amount": "0.0001",
        #        "price": "30000",
        #        "time_in_force": "gtc",
        #        "iceberg": "0",
        #        "left": "0.0001",
        #        "fill_price": "0",
        #        "filled_total": "0",
        #        "fee": "0",
        #        "fee_currency": "BTC",
        #        "point_fee": "0",
        #        "gt_fee": "0",
        #        "gt_discount": True,
        #        "rebated_fee": "0",
        #        "rebated_fee_currency": "USDT"
        #     }
        #
        # SPOT TRIGGER ORDERS
        # createOrder
        #
        #    {
        #        "id": 12604556
        #    }
        #
        # fetchOrder/cancelOrder
        #
        #    {
        #        "market": "ADA_USDT",
        #        "user": 6392049,
        #        "trigger": {
        #            "price": "1.08",  # stopPrice
        #            "rule": "\u003e=",
        #            "expiration": 86400
        #        },
        #        "put": {
        #            "type": "limit",
        #            "side": "buy",
        #            "price": "1.08",  # order price
        #            "amount": "1.00000000000000000000",
        #            "account": "normal",
        #            "time_in_force": "gtc"
        #        },
        #        "id": 71639298,
        #        "ctime": 1643945985,
        #        "status": "open"
        #    }
        #
        # FUTURE, SWAP AND OPTION
        # createOrder/cancelOrder/fetchOrder
        #
        #    {
        #        "id": 123028481731,
        #        "contract": "ADA_USDT",
        #        "mkfr": "-0.00005",
        #        "tkfr": "0.00048",
        #        "tif": "ioc",
        #        "is_reduce_only": False,
        #        "create_time": 1643950262.68,
        #        "finish_time": 1643950262.68,
        #        "price": "0",
        #        "size": 1,
        #        "refr": "0",
        #        "left":0,
        #        "text": "api",
        #        "fill_price": "1.05273",
        #        "user":6329238,
        #        "finish_as": "filled",
        #        "status": "finished",
        #        "is_liq": False,
        #        "refu":0,
        #        "is_close": False,
        #        "iceberg": 0
        #    }
        #
        # TRIGGER ORDERS(FUTURE AND SWAP)
        # createOrder
        #
        #    {
        #        "id": 12604556
        #    }
        #
        # fetchOrder/cancelOrder
        #
        #    {
        #        "user": 6320300,
        #        "trigger": {
        #            "strategy_type": 0,
        #            "price_type": 0,
        #            "price": "1.03",  # stopPrice
        #            "rule": 2,
        #            "expiration": 0
        #        },
        #        "initial": {
        #            "contract": "ADA_USDT",
        #            "size": -1,
        #            "price": "1.02",
        #            "tif": "gtc",
        #            "text": "",
        #            "iceberg": 0,
        #            "is_close": False,
        #            "is_reduce_only": False,
        #            "auto_size": ""
        #        },
        #        "id": 126393906,
        #        "trade_id": 0,
        #        "status": "open",
        #        "reason": "",
        #        "create_time": 1643953482,
        #        "finish_time": 1643953482,
        #        "is_stop_order": False,
        #        "stop_trigger": {
        #            "rule": 0,
        #            "trigger_price": "",
        #            "order_price": ""
        #        },
        #        "me_order_id": 0,
        #        "order_type": ""
        #    }
        #
        #    {
        #        "text": "t-d18baf9ac44d82e2",
        #        "succeeded": False,
        #        "label": "BALANCE_NOT_ENOUGH",
        #        "message": "Not enough balance"
        #    }
        #
        #  {"user_id":10406147,"id":"id","succeeded":false,"message":"INVALID_PROTOCOL","label":"INVALID_PROTOCOL"}
        #
        succeeded = self.safe_bool(order, 'succeeded', True)
        if not succeeded:
            # cancelOrders response
            return self.safe_order({
                'clientOrderId': self.safe_string(order, 'text'),
                'info': order,
                'status': 'rejected',
                'id': self.safe_string(order, 'id'),
            })
        put = self.safe_value_2(order, 'put', 'initial', {})
        trigger = self.safe_value(order, 'trigger', {})
        contract = self.safe_string(put, 'contract')
        type = self.safe_string(put, 'type')
        timeInForce = self.safe_string_upper_2(put, 'time_in_force', 'tif')
        amount = self.safe_string_2(put, 'amount', 'size')
        side = self.safe_string(put, 'side')
        price = self.safe_string(put, 'price')
        contract = self.safe_string(order, 'contract', contract)
        type = self.safe_string(order, 'type', type)
        timeInForce = self.safe_string_upper_2(order, 'time_in_force', 'tif', timeInForce)
        if timeInForce == 'POC':
            timeInForce = 'PO'
        postOnly = (timeInForce == 'PO')
        amount = self.safe_string_2(order, 'amount', 'size', amount)
        side = self.safe_string(order, 'side', side)
        price = self.safe_string(order, 'price', price)
        remainingString = self.safe_string(order, 'left')
        cost = self.safe_string(order, 'filled_total')
        triggerPrice = self.safe_number(trigger, 'price')
        average = self.safe_number_2(order, 'avg_deal_price', 'fill_price')
        if triggerPrice:
            remainingString = amount
            cost = '0'
        if contract:
            isMarketOrder = Precise.string_equals(price, '0') and (timeInForce == 'IOC')
            type = 'market' if isMarketOrder else 'limit'
            side = 'buy' if Precise.string_gt(amount, '0') else 'sell'
        rawStatus = self.safe_string_n(order, ['finish_as', 'status', 'open'])
        timestamp = self.safe_integer(order, 'create_time_ms')
        if timestamp is None:
            timestamp = self.safe_timestamp_2(order, 'create_time', 'ctime')
        lastTradeTimestamp = self.safe_integer(order, 'update_time_ms')
        if lastTradeTimestamp is None:
            lastTradeTimestamp = self.safe_timestamp_2(order, 'update_time', 'finish_time')
        marketType = 'contract'
        if ('currency_pair' in order) or ('market' in order):
            marketType = 'spot'
        exchangeSymbol = self.safe_string_2(order, 'currency_pair', 'market', contract)
        symbol = self.safe_symbol(exchangeSymbol, market, '_', marketType)
        # Everything below self(above return) is related to fees
        fees = []
        gtFee = self.safe_string(order, 'gt_fee')
        if gtFee is not None:
            fees.append({
                'currency': 'GT',
                'cost': gtFee,
            })
        fee = self.safe_string(order, 'fee')
        if fee is not None:
            fees.append({
                'currency': self.safe_currency_code(self.safe_string(order, 'fee_currency')),
                'cost': fee,
            })
        rebate = self.safe_string(order, 'rebated_fee')
        if rebate is not None:
            fees.append({
                'currency': self.safe_currency_code(self.safe_string(order, 'rebated_fee_currency')),
                'cost': Precise.string_neg(rebate),
            })
        numFeeCurrencies = len(fees)
        multipleFeeCurrencies = numFeeCurrencies > 1
        status = self.parse_order_status(rawStatus)
        remaining = Precise.string_abs(remainingString)
        # handle spot market buy
        account = self.safe_string(order, 'account')  # using self instead of market type because of the conflicting ids
        if account == 'spot':
            averageString = self.safe_string(order, 'avg_deal_price')
            average = self.parse_number(averageString)
            if (type == 'market') and (side == 'buy'):
                remaining = Precise.string_div(remainingString, averageString)
                price = None  # arrives
                cost = amount
                amount = Precise.string_div(amount, averageString)
        return self.safe_order({
            'id': self.safe_string(order, 'id'),
            'clientOrderId': self.safe_string(order, 'text'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': lastTradeTimestamp,
            'status': status,
            'symbol': symbol,
            'type': type,
            'timeInForce': timeInForce,
            'postOnly': postOnly,
            'reduceOnly': self.safe_value(order, 'is_reduce_only'),
            'side': side,
            'price': price,
            'triggerPrice': triggerPrice,
            'average': average,
            'amount': Precise.string_abs(amount),
            'cost': Precise.string_abs(cost),
            'filled': None,
            'remaining': remaining,
            'fee': None if multipleFeeCurrencies else self.safe_value(fees, 0),
            'fees': fees if multipleFeeCurrencies else [],
            'trades': None,
            'info': order,
        }, market)

    def fetch_order_request(self, id: str, symbol: Str = None, params={}):
        market = None if (symbol is None) else self.market(symbol)
        trigger = self.safe_bool_n(params, ['trigger', 'is_stop_order', 'stop'], False)
        params = self.omit(params, ['is_stop_order', 'stop', 'trigger'])
        clientOrderId = self.safe_string_2(params, 'text', 'clientOrderId')
        orderId = id
        if clientOrderId is not None:
            params = self.omit(params, ['text', 'clientOrderId'])
            if clientOrderId[0] != 't':
                clientOrderId = 't-' + clientOrderId
            orderId = clientOrderId
        type, query = self.handle_market_type_and_params('fetchOrder', market, params)
        contract = (type == 'swap') or (type == 'future') or (type == 'option')
        request, requestParams = self.prepare_request(market, type, query) if contract else self.spot_order_prepare_request(market, trigger, query)
        request['order_id'] = str(orderId)
        return [request, requestParams]

    async def fetch_order(self, id: str, symbol: Str = None, params={}):
        """
        Retrieves information on an order

        https://www.gate.io/docs/developers/apiv4/en/#get-a-single-order
        https://www.gate.io/docs/developers/apiv4/en/#get-a-single-order-2
        https://www.gate.io/docs/developers/apiv4/en/#get-a-single-order-3
        https://www.gate.io/docs/developers/apiv4/en/#get-a-single-order-4

        :param str id: Order id
        :param str symbol: Unified market symbol, *required for spot and margin*
        :param dict [params]: Parameters specified by the exchange api
        :param bool [params.trigger]: True if the order being fetched is a trigger order
        :param str [params.marginMode]: 'cross' or 'isolated' - marginMode for margin trading if not provided self.options['defaultMarginMode'] is used
        :param str [params.type]: 'spot', 'swap', or 'future', if not provided self.options['defaultMarginMode'] is used
        :param str [params.settle]: 'btc' or 'usdt' - settle currency for perpetual swap and future - market settle currency is used if symbol is not None, default="usdt" for swap and "btc" for future
        :param bool [params.unifiedAccount]: set to True for fetching a unified account order
        :returns: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        await self.load_unified_status()
        market = None if (symbol is None) else self.market(symbol)
        result = self.handle_market_type_and_params('fetchOrder', market, params)
        type = self.safe_string(result, 0)
        trigger = self.safe_bool_n(params, ['trigger', 'is_stop_order', 'stop'], False)
        request, requestParams = self.fetch_order_request(id, symbol, params)
        response = None
        if type == 'spot' or type == 'margin':
            if trigger:
                response = await self.privateSpotGetPriceOrdersOrderId(self.extend(request, requestParams))
            else:
                response = await self.privateSpotGetOrdersOrderId(self.extend(request, requestParams))
        elif type == 'swap':
            if trigger:
                response = await self.privateFuturesGetSettlePriceOrdersOrderId(self.extend(request, requestParams))
            else:
                response = await self.privateFuturesGetSettleOrdersOrderId(self.extend(request, requestParams))
        elif type == 'future':
            if trigger:
                response = await self.privateDeliveryGetSettlePriceOrdersOrderId(self.extend(request, requestParams))
            else:
                response = await self.privateDeliveryGetSettleOrdersOrderId(self.extend(request, requestParams))
        elif type == 'option':
            response = await self.privateOptionsGetOrdersOrderId(self.extend(request, requestParams))
        else:
            raise NotSupported(self.id + ' fetchOrder() not support self market type')
        return self.parse_order(response, market)

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

        https://www.gate.io/docs/developers/apiv4/en/#list-all-open-orders
        https://www.gate.io/docs/developers/apiv4/en/#retrieve-running-auto-order-list

        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch open orders for
        :param int [limit]: the maximum number of  open orders structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param bool [params.trigger]: True for fetching trigger orders
        :param str [params.type]: spot, margin, swap or future, if not provided self.options['defaultType'] is used
        :param str [params.marginMode]: 'cross' or 'isolated' - marginMode for type='margin', if not provided self.options['defaultMarginMode'] is used
        :param bool [params.unifiedAccount]: set to True for fetching unified account orders
        :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
        """
        return await self.fetch_orders_by_status('open', symbol, since, limit, params)

    async def fetch_closed_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
        """
        fetches information on multiple closed orders made by the user

        https://www.gate.io/docs/developers/apiv4/en/#list-orders
        https://www.gate.io/docs/developers/apiv4/en/#retrieve-running-auto-order-list
        https://www.gate.io/docs/developers/apiv4/en/#list-futures-orders
        https://www.gate.io/docs/developers/apiv4/en/#list-all-auto-orders
        https://www.gate.io/docs/developers/apiv4/en/#list-futures-orders-2
        https://www.gate.io/docs/developers/apiv4/en/#list-all-auto-orders-2
        https://www.gate.io/docs/developers/apiv4/en/#list-options-orders
        https://www.gate.io/docs/developers/apiv4/en/#list-futures-orders-by-time-range

        :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 bool [params.trigger]: True for fetching trigger orders
        :param str [params.type]: spot, swap or future, if not provided self.options['defaultType'] is used
        :param str [params.marginMode]: 'cross' or 'isolated' - marginMode for margin trading if not provided self.options['defaultMarginMode'] is used
        :param boolean [params.historical]: *swap only* True for using historical endpoint
        :param bool [params.unifiedAccount]: set to True for fetching unified account orders
        :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        await self.load_unified_status()
        until = self.safe_integer(params, 'until')
        market = None
        if symbol is not None:
            market = self.market(symbol)
            symbol = market['symbol']
        res = self.handle_market_type_and_params('fetchClosedOrders', market, params)
        type = self.safe_string(res, 0)
        useHistorical = False
        useHistorical, params = self.handle_option_and_params(params, 'fetchClosedOrders', 'historical', False)
        if not useHistorical and ((since is None and until is None) or (type != 'swap')):
            return await self.fetch_orders_by_status('finished', symbol, since, limit, params)
        params = self.omit(params, 'type')
        request = {}
        request, params = self.prepare_request(market, type, params)
        if since is not None:
            request['from'] = self.parse_to_int(since / 1000)
        if until is not None:
            params = self.omit(params, 'until')
            request['to'] = self.parse_to_int(until / 1000)
        if limit is not None:
            request['limit'] = limit
        response = await self.privateFuturesGetSettleOrdersTimerange(self.extend(request, params))
        return self.parse_orders(response, market, since, limit)

    def prepare_orders_by_status_request(self, status, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        market = None
        if symbol is not None:
            market = self.market(symbol)
            symbol = market['symbol']
        trigger: Bool = None
        trigger, params = self.handle_param_bool_2(params, 'trigger', 'stop')
        type: Str = None
        type, params = self.handle_market_type_and_params('fetchOrdersByStatus', market, params)
        spot = (type == 'spot') or (type == 'margin')
        request: dict = {}
        request, params = self.multi_order_spot_prepare_request(market, trigger, params) if spot else self.prepare_request(market, type, params)
        if spot and trigger:
            request = self.omit(request, 'account')
        if status == 'closed':
            status = 'finished'
        request['status'] = status
        if limit is not None:
            request['limit'] = limit
        if spot:
            if since is not None:
                request['from'] = self.parse_to_int(since / 1000)
            until = self.safe_integer(params, 'until')
            if until is not None:
                params = self.omit(params, 'until')
                request['to'] = self.parse_to_int(until / 1000)
        lastId, finalParams = self.handle_param_string_2(params, 'lastId', 'last_id')
        if lastId is not None:
            request['last_id'] = lastId
        return [request, finalParams]

    async def fetch_orders_by_status(self, status, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        await self.load_markets()
        await self.load_unified_status()
        market = None
        if symbol is not None:
            market = self.market(symbol)
            symbol = market['symbol']
        # don't omit here, omits done in prepareOrdersByStatusRequest
        trigger: Bool = self.safe_bool_2(params, 'trigger', 'stop')
        res = self.handle_market_type_and_params('fetchOrdersByStatus', market, params)
        type = self.safe_string(res, 0)
        request, requestParams = self.prepare_orders_by_status_request(status, symbol, since, limit, params)
        spot = (type == 'spot') or (type == 'margin')
        openStatus = (status == 'open')
        openSpotOrders = spot and openStatus and not trigger
        response = None
        if spot:
            if not trigger:
                if openStatus:
                    response = await self.privateSpotGetOpenOrders(self.extend(request, requestParams))
                else:
                    response = await self.privateSpotGetOrders(self.extend(request, requestParams))
            else:
                response = await self.privateSpotGetPriceOrders(self.extend(request, requestParams))
        elif type == 'swap':
            if trigger:
                response = await self.privateFuturesGetSettlePriceOrders(self.extend(request, requestParams))
            else:
                response = await self.privateFuturesGetSettleOrders(self.extend(request, requestParams))
        elif type == 'future':
            if trigger:
                response = await self.privateDeliveryGetSettlePriceOrders(self.extend(request, requestParams))
            else:
                response = await self.privateDeliveryGetSettleOrders(self.extend(request, requestParams))
        elif type == 'option':
            response = await self.privateOptionsGetOrders(self.extend(request, requestParams))
        else:
            raise NotSupported(self.id + ' fetchOrders() not support self market type')
        #
        # spot open orders
        #
        #    [
        #        {
        #            "currency_pair": "ADA_USDT",
        #            "total": 2,
        #            "orders": [
        #                {
        #                    "id": "155498539874",
        #                    "text": "apiv4",
        #                    "create_time": "1652406843",
        #                    "update_time": "1652406843",
        #                    "create_time_ms": 1652406843295,
        #                    "update_time_ms": 1652406843295,
        #                    "status": "open",
        #                    "currency_pair": "ADA_USDT",
        #                    "type": "limit",
        #                    "account": "spot",
        #                    "side": "buy",
        #                    "amount": "3",
        #                    "price": "0.35",
        #                    "time_in_force": "gtc",
        #                    "iceberg": "0",
        #                    "left": "3",
        #                    "fill_price": "0",
        #                    "filled_total": "0",
        #                    "fee": "0",
        #                    "fee_currency": "ADA",
        #                    "point_fee": "0",
        #                    "gt_fee": "0",
        #                    "gt_discount": False,
        #                    "rebated_fee": "0",
        #                    "rebated_fee_currency": "USDT"
        #                },
        #                ...
        #            ]
        #        },
        #        ...
        #    ]
        #
        # spot
        #
        #    [
        #        {
        #           "id": "8834234273",
        #           "text": "3",
        #           "create_time": "1635406193",
        #           "update_time": "1635406193",
        #           "create_time_ms": 1635406193361,
        #           "update_time_ms": 1635406193361,
        #           "status": "closed",
        #           "currency_pair": "BTC_USDT",
        #           "type": "limit",
        #           "account": "spot",  # margin for margin orders
        #           "side": "sell",
        #           "amount": "0.0002",
        #           "price": "58904.01",
        #           "time_in_force": "gtc",
        #           "iceberg": "0",
        #           "left": "0.0000",
        #           "fill_price": "11.790516",
        #           "filled_total": "11.790516",
        #           "fee": "0.023581032",
        #           "fee_currency": "USDT",
        #           "point_fee": "0",
        #           "gt_fee": "0",
        #           "gt_discount": False,
        #           "rebated_fee_currency": "BTC"
        #        }
        #    ]
        #
        # spot trigger
        #
        #    [
        #        {
        #            "market": "ADA_USDT",
        #            "user": 10406147,
        #            "trigger": {
        #                "price": "0.65",
        #                "rule": "\u003c=",
        #                "expiration": 86400
        #            },
        #            "put": {
        #                "type": "limit",
        #                "side": "sell",
        #                "price": "0.65",
        #                "amount": "2.00000000000000000000",
        #                "account": "normal",  # margin for margin orders
        #                "time_in_force": "gtc"
        #            },
        #            "id": 8449909,
        #            "ctime": 1652188982,
        #            "status": "open"
        #        }
        #    ]
        #
        # swap
        #
        #    [
        #        {
        #           "status": "finished",
        #           "size": -1,
        #           "left": 0,
        #           "id": 82750739203,
        #           "is_liq": False,
        #           "is_close": False,
        #           "contract": "BTC_USDT",
        #           "text": "web",
        #           "fill_price": "60721.3",
        #           "finish_as": "filled",
        #           "iceberg": 0,
        #           "tif": "ioc",
        #           "is_reduce_only": True,
        #           "create_time": 1635403475.412,
        #           "finish_time": 1635403475.4127,
        #           "price": "0"
        #        }
        #    ]
        #
        # option
        #
        #     [
        #         {
        #             "id": 2593450699,
        #             "contract": "BTC_USDT-20230601-27500-C",
        #             "mkfr": "0.0003",
        #             "tkfr": "0.0003",
        #             "tif": "gtc",
        #             "is_reduce_only": False,
        #             "create_time": 1685503873,
        #             "price": "200",
        #             "size": 1,
        #             "refr": "0",
        #             "left": 1,
        #             "text": "api",
        #             "fill_price": "0",
        #             "user": 5691076,
        #             "status": "open",
        #             "is_liq": False,
        #             "refu": 0,
        #             "is_close": False,
        #             "iceberg": 0
        #         }
        #     ]
        #
        result = response
        if openSpotOrders:
            result = []
            for i in range(0, len(response)):
                ordersInner = self.safe_value(response[i], 'orders')
                result = self.array_concat(result, ordersInner)
        orders = self.parse_orders(result, market, since, limit)
        return self.filter_by_symbol_since_limit(orders, symbol, since, limit)

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

        https://www.gate.io/docs/developers/apiv4/en/#cancel-a-single-order
        https://www.gate.io/docs/developers/apiv4/en/#cancel-a-single-order-2
        https://www.gate.io/docs/developers/apiv4/en/#cancel-a-single-order-3
        https://www.gate.io/docs/developers/apiv4/en/#cancel-a-single-order-4

        :param str id: Order id
        :param str symbol: Unified market symbol
        :param dict [params]: Parameters specified by the exchange api
        :param bool [params.trigger]: True if the order to be cancelled is a trigger order
        :param bool [params.unifiedAccount]: set to True for canceling unified account orders
        :returns: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        await self.load_unified_status()
        market = None if (symbol is None) else self.market(symbol)
        trigger = self.safe_bool_n(params, ['is_stop_order', 'stop', 'trigger'], False)
        params = self.omit(params, ['is_stop_order', 'stop', 'trigger'])
        type, query = self.handle_market_type_and_params('cancelOrder', market, params)
        request, requestParams = self.spot_order_prepare_request(market, trigger, query) if (type == 'spot' or type == 'margin') else self.prepare_request(market, type, query)
        request['order_id'] = id
        response = None
        if type == 'spot' or type == 'margin':
            if trigger:
                response = await self.privateSpotDeletePriceOrdersOrderId(self.extend(request, requestParams))
            else:
                response = await self.privateSpotDeleteOrdersOrderId(self.extend(request, requestParams))
        elif type == 'swap':
            if trigger:
                response = await self.privateFuturesDeleteSettlePriceOrdersOrderId(self.extend(request, requestParams))
            else:
                response = await self.privateFuturesDeleteSettleOrdersOrderId(self.extend(request, requestParams))
        elif type == 'future':
            if trigger:
                response = await self.privateDeliveryDeleteSettlePriceOrdersOrderId(self.extend(request, requestParams))
            else:
                response = await self.privateDeliveryDeleteSettleOrdersOrderId(self.extend(request, requestParams))
        elif type == 'option':
            response = await self.privateOptionsDeleteOrdersOrderId(self.extend(request, requestParams))
        else:
            raise NotSupported(self.id + ' cancelOrder() not support self market type')
        #
        # spot
        #
        #     {
        #         "id": "95282841887",
        #         "text": "apiv4",
        #         "create_time": "1637383156",
        #         "update_time": "1637383235",
        #         "create_time_ms": 1637383156017,
        #         "update_time_ms": 1637383235085,
        #         "status": "cancelled",
        #         "currency_pair": "ETH_USDT",
        #         "type": "limit",
        #         "account": "spot",
        #         "side": "buy",
        #         "amount": "0.01",
        #         "price": "3500",
        #         "time_in_force": "gtc",
        #         "iceberg": "0",
        #         "left": "0.01",
        #         "fill_price": "0",
        #         "filled_total": "0",
        #         "fee": "0",
        #         "fee_currency": "ETH",
        #         "point_fee": "0",
        #         "gt_fee": "0",
        #         "gt_discount": False,
        #         "rebated_fee": "0",
        #         "rebated_fee_currency": "USDT"
        #     }
        #
        # spot conditional
        #
        #     {
        #         "market": "ETH_USDT",
        #         "user": 2436035,
        #         "trigger": {
        #             "price": "3500",
        #             "rule": "\u003c=",
        #             "expiration": 86400
        #         },
        #         "put": {
        #             "type": "limit",
        #             "side": "buy",
        #             "price": "3500",
        #             "amount": "0.01000000000000000000",
        #             "account": "normal",
        #             "time_in_force": "gtc"
        #         },
        #         "id": 5891843,
        #         "ctime": 1637382379,
        #         "ftime": 1637382673,
        #         "status": "canceled"
        #     }
        #
        # swap, future and option
        #
        #     {
        #         "id": "82241928192",
        #         "contract": "BTC_USDT",
        #         "mkfr": "0",
        #         "tkfr": "0.0005",
        #         "tif": "gtc",
        #         "is_reduce_only": False,
        #         "create_time": "1635196145.06",
        #         "finish_time": "1635196233.396",
        #         "price": "61000",
        #         "size": "4",
        #         "refr": "0",
        #         "left": "4",
        #         "text": "web",
        #         "fill_price": "0",
        #         "user": "6693577",
        #         "finish_as": "cancelled",
        #         "status": "finished",
        #         "is_liq": False,
        #         "refu": "0",
        #         "is_close": False,
        #         "iceberg": "0",
        #     }
        #
        return self.parse_order(response, market)

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

        https://www.gate.io/docs/developers/apiv4/en/#cancel-a-batch-of-orders-with-an-id-list
        https://www.gate.io/docs/developers/apiv4/en/#cancel-a-batch-of-orders-with-an-id-list-2

        :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 bool [params.unifiedAccount]: set to True for canceling unified account orders
        :returns dict: an list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        await self.load_unified_status()
        market = None
        if symbol is not None:
            market = self.market(symbol)
        type = None
        defaultSettle = 'usdt' if (market is None) else market['settle']
        settle = self.safe_string_lower(params, 'settle', defaultSettle)
        type, params = self.handle_market_type_and_params('cancelOrders', market, params)
        isSpot = (type == 'spot')
        if isSpot and (symbol is None):
            raise ArgumentsRequired(self.id + ' cancelOrders requires a symbol argument for spot markets')
        if isSpot:
            ordersRequests = []
            for i in range(0, len(ids)):
                id = ids[i]
                orderItem: dict = {
                    'id': id,
                    'symbol': symbol,
                }
                ordersRequests.append(orderItem)
            return await self.cancel_orders_for_symbols(ordersRequests, params)
        request = {
            'settle': settle,
        }
        finalList = [request]  # hacky but needs to be done here
        for i in range(0, len(ids)):
            finalList.append(ids[i])
        response = await self.privateFuturesPostSettleBatchCancelOrders(finalList)
        return self.parse_orders(response)

    async def cancel_orders_for_symbols(self, orders: List[CancellationRequest], params={}):
        """
        cancel multiple orders for multiple symbols

        https://www.gate.io/docs/developers/apiv4/en/#cancel-a-batch-of-orders-with-an-id-list

        :param CancellationRequest[] orders: list of order ids with symbol, example [{"id": "a", "symbol": "BTC/USDT"}, {"id": "b", "symbol": "ETH/USDT"}]
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str[] [params.clientOrderIds]: client order ids
        :param bool [params.unifiedAccount]: set to True for canceling unified account orders
        :returns dict: an list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        await self.load_unified_status()
        ordersRequests = []
        for i in range(0, len(orders)):
            order = orders[i]
            symbol = self.safe_string(order, 'symbol')
            market = self.market(symbol)
            if not market['spot']:
                raise NotSupported(self.id + ' cancelOrdersForSymbols() supports only spot markets')
            id = self.safe_string(order, 'id')
            orderItem: dict = {
                'id': id,
                'currency_pair': market['id'],
            }
            ordersRequests.append(orderItem)
        response = await self.privateSpotPostCancelBatchOrders(ordersRequests)
        #
        # [
        #     {
        #       "currency_pair": "BTC_USDT",
        #       "id": "123456"
        #     }
        # ]
        #
        return self.parse_orders(response)

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

        https://www.gate.io/docs/developers/apiv4/en/#cancel-all-open-orders-in-specified-currency-pair
        https://www.gate.io/docs/developers/apiv4/en/#cancel-all-open-orders-matched
        https://www.gate.io/docs/developers/apiv4/en/#cancel-all-open-orders-matched-2
        https://www.gate.io/docs/developers/apiv4/en/#cancel-all-open-orders-matched-3

        :param str symbol: unified market symbol, only orders in the market of self symbol are cancelled when symbol is not None
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param bool [params.unifiedAccount]: set to True for canceling unified account orders
        :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        await self.load_unified_status()
        market = None if (symbol is None) else self.market(symbol)
        trigger = self.safe_bool_2(params, 'stop', 'trigger')
        params = self.omit(params, ['stop', 'trigger'])
        type, query = self.handle_market_type_and_params('cancelAllOrders', market, params)
        request, requestParams = self.multi_order_spot_prepare_request(market, trigger, query) if (type == 'spot') else self.prepare_request(market, type, query)
        response = None
        if type == 'spot' or type == 'margin':
            if trigger:
                response = await self.privateSpotDeletePriceOrders(self.extend(request, requestParams))
            else:
                response = await self.privateSpotDeleteOrders(self.extend(request, requestParams))
        elif type == 'swap':
            if trigger:
                response = await self.privateFuturesDeleteSettlePriceOrders(self.extend(request, requestParams))
            else:
                response = await self.privateFuturesDeleteSettleOrders(self.extend(request, requestParams))
        elif type == 'future':
            if trigger:
                response = await self.privateDeliveryDeleteSettlePriceOrders(self.extend(request, requestParams))
            else:
                response = await self.privateDeliveryDeleteSettleOrders(self.extend(request, requestParams))
        elif type == 'option':
            response = await self.privateOptionsDeleteOrders(self.extend(request, requestParams))
        else:
            raise NotSupported(self.id + ' cancelAllOrders() not support self market type')
        #
        #    [
        #        {
        #            "id": 139797004085,
        #            "contract": "ADA_USDT",
        #            "mkfr": "0",
        #            "tkfr": "0.0005",
        #            "tif": "gtc",
        #            "is_reduce_only": False,
        #            "create_time": 1647911169.343,
        #            "finish_time": 1647911226.849,
        #            "price": "0.8",
        #            "size": 1,
        #            "refr": "0.3",
        #            "left": 1,
        #            "text": "api",
        #            "fill_price": "0",
        #            "user": 6693577,
        #            "finish_as": "cancelled",
        #            "status": "finished",
        #            "is_liq": False,
        #            "refu": 2436035,
        #            "is_close": False,
        #            "iceberg": 0
        #        }
        #        ...
        #    ]
        #
        return self.parse_orders(response, market)

    async def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
        """
        transfer currency internally between wallets on the same account

        https://www.gate.io/docs/developers/apiv4/en/#transfer-between-trading-accounts

        :param str code: unified currency code for currency being transferred
        :param float amount: the amount of currency to transfer
        :param str fromAccount: the account to transfer currency from
        :param str toAccount: the account to transfer currency to
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.symbol]: Unified market symbol *required for type == margin*
        :returns: A `transfer structure <https://docs.ccxt.com/#/?id=transfer-structure>`
        """
        await self.load_markets()
        currency = self.currency(code)
        fromId = self.convert_type_to_account(fromAccount)
        toId = self.convert_type_to_account(toAccount)
        truncated = self.currency_to_precision(code, amount)
        request: dict = {
            'currency': currency['id'],  # todo: currencies have network-junctions
            'amount': truncated,
        }
        if not (fromId in self.options['accountsByType']):
            request['from'] = 'margin'
            request['currency_pair'] = fromId
        else:
            request['from'] = fromId
        if not (toId in self.options['accountsByType']):
            request['to'] = 'margin'
            request['currency_pair'] = toId
        else:
            request['to'] = toId
        if fromId == 'margin' or toId == 'margin':
            symbol = self.safe_string_2(params, 'symbol', 'currency_pair')
            if symbol is None:
                raise ArgumentsRequired(self.id + ' transfer requires params["symbol"] for isolated margin transfers')
            market = self.market(symbol)
            request['currency_pair'] = market['id']
            params = self.omit(params, 'symbol')
        if (toId == 'futures') or (toId == 'delivery') or (fromId == 'futures') or (fromId == 'delivery'):
            request['settle'] = currency['id']  # todo: currencies have network-junctions
        response = await self.privateWalletPostTransfers(self.extend(request, params))
        #
        # according to the docs(however actual response seems to be an empty string '')
        #
        #    {
        #        "currency": "BTC",
        #        "from": "spot",
        #        "to": "margin",
        #        "amount": "1",
        #        "currency_pair": "BTC_USDT"
        #    }
        #
        return self.parse_transfer(response, currency)

    def parse_transfer(self, transfer: dict, currency: Currency = None) -> TransferEntry:
        #
        #    {
        #        "currency": "BTC",
        #        "from": "spot",
        #        "to": "margin",
        #        "amount": "1",
        #        "currency_pair": "BTC_USDT"
        #    }
        #
        return {
            'id': self.safe_string(transfer, 'tx_id'),
            'timestamp': None,
            'datetime': None,
            'currency': self.safe_currency_code(None, currency),
            'amount': None,
            'fromAccount': None,
            'toAccount': None,
            'status': None,
            'info': transfer,
        }

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

        https://www.gate.io/docs/developers/apiv4/en/#update-position-leverage
        https://www.gate.io/docs/developers/apiv4/en/#update-position-leverage-2

        :param float leverage: the rate of leverage
        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: response from the exchange
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' setLeverage() requires a symbol argument')
        # WARNING: THIS WILL INCREASE LIQUIDATION PRICE FOR OPEN ISOLATED LONG POSITIONS
        # AND DECREASE LIQUIDATION PRICE FOR OPEN ISOLATED SHORT POSITIONS
        if (leverage < 0) or (leverage > 100):
            raise BadRequest(self.id + ' setLeverage() leverage should be between 1 and 100')
        await self.load_markets()
        market = self.market(symbol)
        request, query = self.prepare_request(market, None, params)
        defaultMarginMode = self.safe_string_2(self.options, 'marginMode', 'defaultMarginMode')
        crossLeverageLimit = self.safe_string(query, 'cross_leverage_limit')
        marginMode = self.safe_string(query, 'marginMode', defaultMarginMode)
        stringifiedMargin = self.number_to_string(leverage)
        if crossLeverageLimit is not None:
            marginMode = 'cross'
            stringifiedMargin = crossLeverageLimit
        if marginMode == 'cross' or marginMode == 'cross_margin':
            request['cross_leverage_limit'] = stringifiedMargin
            request['leverage'] = '0'
        else:
            request['leverage'] = stringifiedMargin
        response = None
        if market['swap']:
            response = await self.privateFuturesPostSettlePositionsContractLeverage(self.extend(request, query))
        elif market['future']:
            response = await self.privateDeliveryPostSettlePositionsContractLeverage(self.extend(request, query))
        else:
            raise NotSupported(self.id + ' setLeverage() not support self market type')
        #
        #     {
        #         "value": "0",
        #         "leverage": "5",
        #         "mode": "single",
        #         "realised_point": "0",
        #         "contract": "BTC_USDT",
        #         "entry_price": "0",
        #         "mark_price": "62035.86",
        #         "history_point": "0",
        #         "realised_pnl": "0",
        #         "close_order": null,
        #         "size": 0,
        #         "cross_leverage_limit": "0",
        #         "pending_orders": 0,
        #         "adl_ranking": 6,
        #         "maintenance_rate": "0.005",
        #         "unrealised_pnl": "0",
        #         "user": 2436035,
        #         "leverage_max": "100",
        #         "history_pnl": "0",
        #         "risk_limit": "1000000",
        #         "margin": "0",
        #         "last_close_pnl": "0",
        #         "liq_price": "0"
        #     }
        #
        return response

    def parse_position(self, position: dict, market: Market = None):
        #
        # swap and future
        #
        #     {
        #         "value": "4.60516",
        #         "leverage": "0",
        #         "mode": "single",
        #         "realised_point": "0",
        #         "contract": "BTC_USDT",
        #         "entry_price": "46030.3",
        #         "mark_price": "46051.6",
        #         "history_point": "0",
        #         "realised_pnl": "-0.002301515",
        #         "close_order": null,
        #         "size": 1,
        #         "cross_leverage_limit": "0",
        #         "pending_orders": 0,
        #         "adl_ranking": 5,
        #         "maintenance_rate": "0.004",
        #         "unrealised_pnl": "0.00213",
        #         "user": 5691076,
        #         "leverage_max": "125",
        #         "history_pnl": "0",
        #         "risk_limit": "1000000",
        #         "margin": "8.997698485",
        #         "last_close_pnl": "0",
        #         "liq_price": "0",
        #         "update_time": 1705034246,
        #         "update_id": 1,
        #         "initial_margin": "0",
        #         "maintenance_margin": "0",
        #         "open_time": 1705034246,
        #         "trade_max_size": "0"
        #     }
        #
        # option
        #
        #     {
        #         "close_order": null,
        #         "size": 1,
        #         "vega": "5.29756",
        #         "theta": "-98.98917",
        #         "gamma": "0.00056",
        #         "delta": "0.68691",
        #         "contract": "BTC_USDT-20230602-26500-C",
        #         "entry_price": "529",
        #         "unrealised_pnl": "-1.0131",
        #         "user": 5691076,
        #         "mark_price": "427.69",
        #         "underlying_price": "26810.2",
        #         "underlying": "BTC_USDT",
        #         "realised_pnl": "-0.08042877",
        #         "mark_iv": "0.4224",
        #         "pending_orders": 0
        #     }
        #
        # fetchPositionsHistory(swap and future)
        #
        #    {
        #        "contract": "SLERF_USDT",         # Futures contract
        #        "text": "web",                    # Text of close order
        #        "long_price": "0.766306",         # When 'side' is 'long,' it indicates the opening average price; when 'side' is 'short,' it indicates the closing average price.
        #        "pnl": "-23.41702352",            # PNL
        #        "pnl_pnl": "-22.7187",            # Position P/L
        #        "pnl_fee": "-0.06527125",         # Transaction Fees
        #        "pnl_fund": "-0.63305227",        # Funding Fees
        #        "accum_size": "100",
        #        "time": 1711279263,               # Position close time
        #        "short_price": "0.539119",        # When 'side' is 'long,' it indicates the opening average price; when 'side' is 'short,' it indicates the closing average price
        #        "side": "long",                   # Position side, long or short
        #        "max_size": "100",                # Max Trade Size
        #        "first_open_time": 1711037985     # First Open Time
        #    }
        #
        contract = self.safe_string(position, 'contract')
        market = self.safe_market(contract, market, '_', 'contract')
        size = self.safe_string_2(position, 'size', 'accum_size')
        side = self.safe_string(position, 'side')
        if side is None:
            if Precise.string_gt(size, '0'):
                side = 'long'
            elif Precise.string_lt(size, '0'):
                side = 'short'
        maintenanceRate = self.safe_string(position, 'maintenance_rate')
        notional = self.safe_string(position, 'value')
        leverage = self.safe_string(position, 'leverage')
        marginMode = None
        if leverage is not None:
            if leverage == '0':
                marginMode = 'cross'
            else:
                marginMode = 'isolated'
        # Initial Position Margin = ( Position Value / Leverage ) + Close Position Fee
        # *The default leverage under the full position is the highest leverage in the market.
        # *Trading fee is charged Fee Rate(0.075%).
        feePaid = self.safe_string(position, 'pnl_fee')
        initialMarginString = None
        if feePaid is None:
            takerFee = '0.00075'
            feePaid = Precise.string_mul(takerFee, notional)
            initialMarginString = Precise.string_add(Precise.string_div(notional, leverage), feePaid)
        timestamp = self.safe_timestamp_2(position, 'open_time', 'first_open_time')
        if timestamp == 0:
            timestamp = None
        return self.safe_position({
            'info': position,
            'id': None,
            'symbol': self.safe_string(market, 'symbol'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastUpdateTimestamp': self.safe_timestamp_2(position, 'update_time', 'time'),
            'initialMargin': self.parse_number(initialMarginString),
            'initialMarginPercentage': self.parse_number(Precise.string_div(initialMarginString, notional)),
            'maintenanceMargin': self.parse_number(Precise.string_mul(maintenanceRate, notional)),
            'maintenanceMarginPercentage': self.parse_number(maintenanceRate),
            'entryPrice': self.safe_number(position, 'entry_price'),
            'notional': self.parse_number(notional),
            'leverage': self.safe_number(position, 'leverage'),
            'unrealizedPnl': self.safe_number(position, 'unrealised_pnl'),
            'realizedPnl': self.safe_number_2(position, 'realised_pnl', 'pnl'),
            'contracts': self.parse_number(Precise.string_abs(size)),
            'contractSize': self.safe_number(market, 'contractSize'),
            'marginRatio': None,
            'liquidationPrice': self.safe_number(position, 'liq_price'),
            'markPrice': self.safe_number(position, 'mark_price'),
            'lastPrice': None,
            'collateral': self.safe_number(position, 'margin'),
            'marginMode': marginMode,
            'side': side,
            'percentage': None,
            'stopLossPrice': None,
            'takeProfitPrice': None,
        })

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

        https://www.gate.io/docs/developers/apiv4/en/#get-single-position
        https://www.gate.io/docs/developers/apiv4/en/#get-single-position-2
        https://www.gate.io/docs/developers/apiv4/en/#get-specified-contract-position

        :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)
        if not market['contract']:
            raise BadRequest(self.id + ' fetchPosition() supports contract markets only')
        request: dict = {}
        request, params = self.prepare_request(market, market['type'], params)
        extendedRequest = self.extend(request, params)
        response = None
        if market['swap']:
            response = await self.privateFuturesGetSettlePositionsContract(extendedRequest)
        elif market['future']:
            response = await self.privateDeliveryGetSettlePositionsContract(extendedRequest)
        elif market['type'] == 'option':
            response = await self.privateOptionsGetPositionsContract(extendedRequest)
        #
        # swap and future
        #
        #     {
        #         "value": "4.60516",
        #         "leverage": "0",
        #         "mode": "single",
        #         "realised_point": "0",
        #         "contract": "BTC_USDT",
        #         "entry_price": "46030.3",
        #         "mark_price": "46051.6",
        #         "history_point": "0",
        #         "realised_pnl": "-0.002301515",
        #         "close_order": null,
        #         "size": 1,
        #         "cross_leverage_limit": "0",
        #         "pending_orders": 0,
        #         "adl_ranking": 5,
        #         "maintenance_rate": "0.004",
        #         "unrealised_pnl": "0.00213",
        #         "user": 5691076,
        #         "leverage_max": "125",
        #         "history_pnl": "0",
        #         "risk_limit": "1000000",
        #         "margin": "8.997698485",
        #         "last_close_pnl": "0",
        #         "liq_price": "0",
        #         "update_time": 1705034246,
        #         "update_id": 1,
        #         "initial_margin": "0",
        #         "maintenance_margin": "0",
        #         "open_time": 1705034246,
        #         "trade_max_size": "0"
        #     }
        #
        # option
        #
        #     {
        #         "close_order": null,
        #         "size": 1,
        #         "vega": "5.29756",
        #         "theta": "-98.98917",
        #         "gamma": "0.00056",
        #         "delta": "0.68691",
        #         "contract": "BTC_USDT-20230602-26500-C",
        #         "entry_price": "529",
        #         "unrealised_pnl": "-1.0131",
        #         "user": 5691076,
        #         "mark_price": "427.69",
        #         "underlying_price": "26810.2",
        #         "underlying": "BTC_USDT",
        #         "realised_pnl": "-0.08042877",
        #         "mark_iv": "0.4224",
        #         "pending_orders": 0
        #     }
        #
        return self.parse_position(response, market)

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

        https://www.gate.io/docs/developers/apiv4/en/#list-all-positions-of-a-user
        https://www.gate.io/docs/developers/apiv4/en/#list-all-positions-of-a-user-2
        https://www.gate.io/docs/developers/apiv4/en/#list-user-s-positions-of-specified-underlying

        :param str[]|None symbols: Not used by gate, but parsed internally by CCXT
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.settle]: 'btc' or 'usdt' - settle currency for perpetual swap and future - default="usdt" for swap and "btc" for future
        :param str [params.type]: swap, future or option, if not provided self.options['defaultType'] is used
        :returns dict[]: a list of `position structure <https://docs.ccxt.com/#/?id=position-structure>`
        """
        await self.load_markets()
        market = None
        symbols = self.market_symbols(symbols, None, True, True, True)
        if symbols is not None:
            symbolsLength = len(symbols)
            if symbolsLength > 0:
                market = self.market(symbols[0])
        type = None
        request: dict = {}
        type, params = self.handle_market_type_and_params('fetchPositions', market, params)
        if (type is None) or (type == 'spot'):
            type = 'swap'  # default to swap
        if type == 'option':
            if symbols is not None:
                marketId = market['id']
                optionParts = marketId.split('-')
                request['underlying'] = self.safe_string(optionParts, 0)
        else:
            request, params = self.prepare_request(None, type, params)
        response = None
        if type == 'swap':
            response = await self.privateFuturesGetSettlePositions(self.extend(request, params))
        elif type == 'future':
            response = await self.privateDeliveryGetSettlePositions(self.extend(request, params))
        elif type == 'option':
            response = await self.privateOptionsGetPositions(self.extend(request, params))
        #
        # swap and future
        #
        #     [
        #         {
        #             "value": "4.602828",
        #             "leverage": "0",
        #             "mode": "single",
        #             "realised_point": "0",
        #             "contract": "BTC_USDT",
        #             "entry_price": "46030.3",
        #             "mark_price": "46028.28",
        #             "history_point": "0",
        #             "realised_pnl": "-0.002301515",
        #             "close_order": null,
        #             "size": 1,
        #             "cross_leverage_limit": "0",
        #             "pending_orders": 0,
        #             "adl_ranking": 5,
        #             "maintenance_rate": "0.004",
        #             "unrealised_pnl": "-0.000202",
        #             "user": 5691076,
        #             "leverage_max": "125",
        #             "history_pnl": "0",
        #             "risk_limit": "1000000",
        #             "margin": "8.997698485",
        #             "last_close_pnl": "0",
        #             "liq_price": "0",
        #             "update_time": 1705034246,
        #             "update_id": 1,
        #             "initial_margin": "0",
        #             "maintenance_margin": "0",
        #             "open_time": 1705034246,
        #             "trade_max_size": "0"
        #         }
        #     ]
        #
        # option
        #
        #     [
        #         {
        #             "close_order": null,
        #             "size": 0,
        #             "vega": "0.01907",
        #             "theta": "-3.04888",
        #             "gamma": "0.00001",
        #             "delta": "0.0011",
        #             "contract": "BTC_USDT-20230601-27500-C",
        #             "entry_price": "0",
        #             "unrealised_pnl": "0",
        #             "user": 5691076,
        #             "mark_price": "0.07",
        #             "underlying_price": "26817.27",
        #             "underlying": "BTC_USDT",
        #             "realised_pnl": "0",
        #             "mark_iv": "0.4339",
        #             "pending_orders": 0
        #         }
        #     ]
        #
        return self.parse_positions(response, symbols)

    async def fetch_leverage_tiers(self, symbols: Strings = None, params={}) -> LeverageTiers:
        """
        retrieve information on the maximum leverage, and maintenance margin for trades of varying trade sizes

        https://www.gate.io/docs/developers/apiv4/en/#list-all-futures-contracts
        https://www.gate.io/docs/developers/apiv4/en/#list-all-futures-contracts-2

        :param str[] [symbols]: list of unified market symbols
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a dictionary of `leverage tiers structures <https://docs.ccxt.com/#/?id=leverage-tiers-structure>`, indexed by market symbols
        """
        await self.load_markets()
        type, query = self.handle_market_type_and_params('fetchLeverageTiers', None, params)
        request, requestParams = self.prepare_request(None, type, query)
        if type != 'future' and type != 'swap':
            raise BadRequest(self.id + ' fetchLeverageTiers only supports swap and future')
        response = None
        if type == 'swap':
            response = await self.publicFuturesGetSettleContracts(self.extend(request, requestParams))
        elif type == 'future':
            response = await self.publicDeliveryGetSettleContracts(self.extend(request, requestParams))
        else:
            raise NotSupported(self.id + ' fetchLeverageTiers() not support self market type')
        #
        # Perpetual swap
        #
        #    [
        #        {
        #            "name": "BTC_USDT",
        #            "type": "direct",
        #            "quanto_multiplier": "0.0001",
        #            "ref_discount_rate": "0",
        #            "order_price_deviate": "0.5",
        #            "maintenance_rate": "0.005",
        #            "mark_type": "index",
        #            "last_price": "38026",
        #            "mark_price": "37985.6",
        #            "index_price": "37954.92",
        #            "funding_rate_indicative": "0.000219",
        #            "mark_price_round": "0.01",
        #            "funding_offset": 0,
        #            "in_delisting": False,
        #            "risk_limit_base": "1000000",
        #            "interest_rate": "0.0003",
        #            "order_price_round": "0.1",
        #            "order_size_min": 1,
        #            "ref_rebate_rate": "0.2",
        #            "funding_interval": 28800,
        #            "risk_limit_step": "1000000",
        #            "leverage_min": "1",
        #            "leverage_max": "100",
        #            "risk_limit_max": "8000000",
        #            "maker_fee_rate": "-0.00025",
        #            "taker_fee_rate": "0.00075",
        #            "funding_rate": "0.002053",
        #            "order_size_max": 1000000,
        #            "funding_next_apply": 1610035200,
        #            "short_users": 977,
        #            "config_change_time": 1609899548,
        #            "trade_size": 28530850594,
        #            "position_size": 5223816,
        #            "long_users": 455,
        #            "funding_impact_value": "60000",
        #            "orders_limit": 50,
        #            "trade_id": 10851092,
        #            "orderbook_id": 2129638396
        #        }
        #    ]
        #
        # Delivery Futures
        #
        #    [
        #        {
        #            "name": "BTC_USDT_20200814",
        #            "underlying": "BTC_USDT",
        #            "cycle": "WEEKLY",
        #            "type": "direct",
        #            "quanto_multiplier": "0.0001",
        #            "mark_type": "index",
        #            "last_price": "9017",
        #            "mark_price": "9019",
        #            "index_price": "9005.3",
        #            "basis_rate": "0.185095",
        #            "basis_value": "13.7",
        #            "basis_impact_value": "100000",
        #            "settle_price": "0",
        #            "settle_price_interval": 60,
        #            "settle_price_duration": 1800,
        #            "settle_fee_rate": "0.0015",
        #            "expire_time": 1593763200,
        #            "order_price_round": "0.1",
        #            "mark_price_round": "0.1",
        #            "leverage_min": "1",
        #            "leverage_max": "100",
        #            "maintenance_rate": "1000000",
        #            "risk_limit_base": "140.726652109199",
        #            "risk_limit_step": "1000000",
        #            "risk_limit_max": "8000000",
        #            "maker_fee_rate": "-0.00025",
        #            "taker_fee_rate": "0.00075",
        #            "ref_discount_rate": "0",
        #            "ref_rebate_rate": "0.2",
        #            "order_price_deviate": "0.5",
        #            "order_size_min": 1,
        #            "order_size_max": 1000000,
        #            "orders_limit": 50,
        #            "orderbook_id": 63,
        #            "trade_id": 26,
        #            "trade_size": 435,
        #            "position_size": 130,
        #            "config_change_time": 1593158867,
        #            "in_delisting": False
        #        }
        #    ]
        #
        return self.parse_leverage_tiers(response, symbols, 'name')

    async def fetch_market_leverage_tiers(self, symbol: str, params={}) -> List[LeverageTier]:
        """
        retrieve information on the maximum leverage, and maintenance margin for trades of varying trade sizes for a single market

        https://www.gate.io/docs/developers/apiv4/en/#list-risk-limit-tiers

        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `leverage tiers structure <https://docs.ccxt.com/#/?id=leverage-tiers-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        type, query = self.handle_market_type_and_params('fetchMarketLeverageTiers', market, params)
        request, requestParams = self.prepare_request(market, type, query)
        if type != 'future' and type != 'swap':
            raise BadRequest(self.id + ' fetchMarketLeverageTiers only supports swap and future')
        response = await self.privateFuturesGetSettleRiskLimitTiers(self.extend(request, requestParams))
        #
        #     [
        #         {
        #             "maintenance_rate": "0.004",
        #             "tier": 1,
        #             "initial_rate": "0.008",
        #             "leverage_max": "125",
        #             "risk_limit": "1000000"
        #         }
        #     ]
        #
        return self.parse_market_leverage_tiers(response, market)

    def parse_emulated_leverage_tiers(self, info, market=None) -> List[LeverageTier]:
        marketId = self.safe_string(info, 'name')
        maintenanceMarginUnit = self.safe_string(info, 'maintenance_rate')  # '0.005',
        leverageMax = self.safe_string(info, 'leverage_max')  # '100',
        riskLimitStep = self.safe_string(info, 'risk_limit_step')  # '1000000',
        riskLimitMax = self.safe_string(info, 'risk_limit_max')  # '16000000',
        initialMarginUnit = Precise.string_div('1', leverageMax)
        maintenanceMarginRate = maintenanceMarginUnit
        initialMarginRatio = initialMarginUnit
        floor = '0'
        tiers = []
        while(Precise.string_lt(floor, riskLimitMax)):
            cap = Precise.string_add(floor, riskLimitStep)
            tiers.append({
                'tier': self.parse_number(Precise.string_div(cap, riskLimitStep)),
                'symbol': self.safe_symbol(marketId, market, None, 'contract'),
                'currency': self.safe_string(market, 'settle'),
                'minNotional': self.parse_number(floor),
                'maxNotional': self.parse_number(cap),
                'maintenanceMarginRate': self.parse_number(maintenanceMarginRate),
                'maxLeverage': self.parse_number(Precise.string_div('1', initialMarginRatio)),
                'info': info,
            })
            maintenanceMarginRate = Precise.string_add(maintenanceMarginRate, maintenanceMarginUnit)
            initialMarginRatio = Precise.string_add(initialMarginRatio, initialMarginUnit)
            floor = cap
        return tiers

    def parse_market_leverage_tiers(self, info, market: Market = None) -> List[LeverageTier]:
        #
        #     [
        #         {
        #             "maintenance_rate": "0.004",
        #             "tier": 1,
        #             "initial_rate": "0.008",
        #             "leverage_max": "125",
        #             "risk_limit": "1000000"
        #         }
        #     ]
        #
        if not isinstance(info, list):
            return self.parse_emulated_leverage_tiers(info, market)
        minNotional = 0
        tiers = []
        for i in range(0, len(info)):
            item = info[i]
            maxNotional = self.safe_number(item, 'risk_limit')
            tiers.append({
                'tier': self.sum(i, 1),
                'symbol': market['symbol'],
                'currency': market['base'],
                'minNotional': minNotional,
                'maxNotional': maxNotional,
                'maintenanceMarginRate': self.safe_number(item, 'maintenance_rate'),
                'maxLeverage': self.safe_number(item, 'leverage_max'),
                'info': item,
            })
            minNotional = maxNotional
        return tiers

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

        https://www.gate.io/docs/apiv4/en/#repay-a-loan

        :param str symbol: unified market symbol
        :param str code: unified currency code of the currency to repay
        :param float amount: the amount to repay
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.mode]: 'all' or 'partial' payment mode, extra parameter required for isolated margin
        :param str [params.id]: '34267567' loan id, extra parameter required for isolated margin
        :returns dict: a `margin loan structure <https://docs.ccxt.com/#/?id=margin-loan-structure>`
        """
        await self.load_markets()
        currency = self.currency(code)
        request: dict = {
            'currency': currency['id'].upper(),  # todo: currencies have network-junctions
            'amount': self.currency_to_precision(code, amount),
        }
        market = self.market(symbol)
        request['currency_pair'] = market['id']
        request['type'] = 'repay'
        response = await self.privateMarginPostUniLoans(self.extend(request, params))
        #
        # empty response
        #
        return self.parse_margin_loan(response, currency)

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

        https://www.gate.io/docs/developers/apiv4/en/#cross-margin-repayments
        https://www.gate.io/docs/developers/apiv4/en/#borrow-or-repay

        :param str code: unified currency code of the currency to repay
        :param float amount: the amount to repay
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.mode]: 'all' or 'partial' payment mode, extra parameter required for isolated margin
        :param str [params.id]: '34267567' loan id, extra parameter required for isolated margin
        :param boolean [params.unifiedAccount]: set to True for repaying in the unified account
        :returns dict: a `margin loan structure <https://docs.ccxt.com/#/?id=margin-loan-structure>`
        """
        await self.load_markets()
        await self.load_unified_status()
        currency = self.currency(code)
        request: dict = {
            'currency': currency['id'].upper(),  # todo: currencies have network-junctions
            'amount': self.currency_to_precision(code, amount),
        }
        isUnifiedAccount = False
        isUnifiedAccount, params = self.handle_option_and_params(params, 'repayCrossMargin', 'unifiedAccount')
        response = None
        if isUnifiedAccount:
            request['type'] = 'repay'
            response = await self.privateUnifiedPostLoans(self.extend(request, params))
        else:
            response = await self.privateMarginPostCrossRepayments(self.extend(request, params))
            response = self.safe_dict(response, 0)
            #
            #     [
            #         {
            #             "id": "17",
            #             "create_time": 1620381696159,
            #             "update_time": 1620381696159,
            #             "currency": "EOS",
            #             "amount": "110.553635",
            #             "text": "web",
            #             "status": 2,
            #             "repaid": "110.506649705159",
            #             "repaid_interest": "0.046985294841",
            #             "unpaid_interest": "0.0000074393366667"
            #         }
            #     ]
            #
        return self.parse_margin_loan(response, currency)

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

        https://www.gate.io/docs/developers/apiv4/en/#marginuni

        :param str symbol: unified market symbol, required for isolated margin
        :param str code: unified currency code of the currency to borrow
        :param float amount: the amount to borrow
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.rate]: '0.0002' or '0.002' extra parameter required for isolated margin
        :returns dict: a `margin loan structure <https://docs.ccxt.com/#/?id=margin-loan-structure>`
        """
        await self.load_markets()
        currency = self.currency(code)
        request: dict = {
            'currency': currency['id'].upper(),  # todo: currencies have network-junctions
            'amount': self.currency_to_precision(code, amount),
        }
        response = None
        market = self.market(symbol)
        request['currency_pair'] = market['id']
        request['type'] = 'borrow'
        response = await self.privateMarginPostUniLoans(self.extend(request, params))
        #
        #     {
        #         "id": "34267567",
        #         "create_time": "1656394778",
        #         "expire_time": "1657258778",
        #         "status": "loaned",
        #         "side": "borrow",
        #         "currency": "USDT",
        #         "rate": "0.0002",
        #         "amount": "100",
        #         "days": 10,
        #         "auto_renew": False,
        #         "currency_pair": "LTC_USDT",
        #         "left": "0",
        #         "repaid": "0",
        #         "paid_interest": "0",
        #         "unpaid_interest": "0.003333333333"
        #     }
        #
        return self.parse_margin_loan(response, currency)

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

        https://www.gate.io/docs/apiv4/en/#create-a-cross-margin-borrow-loan
        https://www.gate.io/docs/developers/apiv4/en/#borrow-or-repay

        :param str code: unified currency code of the currency to borrow
        :param float amount: the amount to borrow
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.rate]: '0.0002' or '0.002' extra parameter required for isolated margin
        :param boolean [params.unifiedAccount]: set to True for borrowing in the unified account
        :returns dict: a `margin loan structure <https://docs.ccxt.com/#/?id=margin-loan-structure>`
        """
        await self.load_markets()
        await self.load_unified_status()
        currency = self.currency(code)
        request: dict = {
            'currency': currency['id'].upper(),  # todo: currencies have network-junctions
            'amount': self.currency_to_precision(code, amount),
        }
        isUnifiedAccount = False
        isUnifiedAccount, params = self.handle_option_and_params(params, 'borrowCrossMargin', 'unifiedAccount')
        response = None
        if isUnifiedAccount:
            request['type'] = 'borrow'
            response = await self.privateUnifiedPostLoans(self.extend(request, params))
        else:
            response = await self.privateMarginPostCrossLoans(self.extend(request, params))
            #
            #     {
            #         "id": "17",
            #         "create_time": 1620381696159,
            #         "update_time": 1620381696159,
            #         "currency": "EOS",
            #         "amount": "110.553635",
            #         "text": "web",
            #         "status": 2,
            #         "repaid": "110.506649705159",
            #         "repaid_interest": "0.046985294841",
            #         "unpaid_interest": "0.0000074393366667"
            #     }
            #
        return self.parse_margin_loan(response, currency)

    def parse_margin_loan(self, info, currency: Currency = None):
        #
        # Cross
        #
        #     {
        #         "id": "17",
        #         "create_time": 1620381696159,
        #         "update_time": 1620381696159,
        #         "currency": "EOS",
        #         "amount": "110.553635",
        #         "text": "web",
        #         "status": 2,
        #         "repaid": "110.506649705159",
        #         "repaid_interest": "0.046985294841",
        #         "unpaid_interest": "0.0000074393366667"
        #     }
        #
        # Isolated
        #
        #     {
        #         "id": "34267567",
        #         "create_time": "1656394778",
        #         "expire_time": "1657258778",
        #         "status": "loaned",
        #         "side": "borrow",
        #         "currency": "USDT",
        #         "rate": "0.0002",
        #         "amount": "100",
        #         "days": 10,
        #         "auto_renew": False,
        #         "currency_pair": "LTC_USDT",
        #         "left": "0",
        #         "repaid": "0",
        #         "paid_interest": "0",
        #         "unpaid_interest": "0.003333333333"
        #     }
        #
        marginMode = self.safe_string_2(self.options, 'defaultMarginMode', 'marginMode', 'cross')
        timestamp = self.safe_integer(info, 'create_time')
        if marginMode == 'isolated':
            timestamp = self.safe_timestamp(info, 'create_time')
        currencyId = self.safe_string(info, 'currency')
        marketId = self.safe_string(info, 'currency_pair')
        return {
            'id': self.safe_integer(info, 'id'),
            'currency': self.safe_currency_code(currencyId, currency),
            'amount': self.safe_number(info, 'amount'),
            'symbol': self.safe_symbol(marketId, None, '_', 'margin'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'info': info,
        }

    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://www.gate.io/docs/developers/apiv4/en/#list-interest-records
        https://www.gate.io/docs/developers/apiv4/en/#interest-records-for-the-cross-margin-account
        https://www.gate.io/docs/developers/apiv4/en/#list-interest-records-2

        :param str [code]: unified currency code
        :param str [symbol]: unified market symbol when fetching interest in isolated markets
        :param int [since]: the earliest time in ms to fetch borrow interest for
        :param int [limit]: the maximum number of structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param boolean [params.unifiedAccount]: set to True for fetching borrow interest in the unified account
        :returns dict[]: a list of `borrow interest structures <https://docs.ccxt.com/#/?id=borrow-interest-structure>`
        """
        await self.load_markets()
        await self.load_unified_status()
        isUnifiedAccount = False
        isUnifiedAccount, params = self.handle_option_and_params(params, 'fetchBorrowInterest', 'unifiedAccount')
        request: dict = {}
        request, params = self.handle_until_option('to', request, params)
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        market = None
        if symbol is not None:
            market = self.market(symbol)
        if since is not None:
            request['from'] = since
        if limit is not None:
            request['limit'] = limit
        response = None
        marginMode = None
        marginMode, params = self.handle_margin_mode_and_params('fetchBorrowInterest', params, 'cross')
        if isUnifiedAccount:
            response = await self.privateUnifiedGetInterestRecords(self.extend(request, params))
        elif marginMode == 'isolated':
            if market is not None:
                request['currency_pair'] = market['id']
            response = await self.privateMarginGetUniInterestRecords(self.extend(request, params))
        elif marginMode == 'cross':
            response = await self.privateMarginGetCrossInterestRecords(self.extend(request, params))
        interest = self.parse_borrow_interests(response, market)
        return self.filter_by_currency_since_limit(interest, code, since, limit)

    def parse_borrow_interest(self, info: dict, market: Market = None) -> BorrowInterest:
        marketId = self.safe_string(info, 'currency_pair')
        market = self.safe_market(marketId, market)
        marginMode = 'isolated' if (marketId is not None) else 'cross'
        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'),
            'interestRate': self.safe_number(info, 'actual_rate'),
            'amountBorrowed': None,
            'marginMode': marginMode,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
        }

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

    def sign(self, path, api=[], method='GET', params={}, headers=None, body=None):
        authentication = api[0]  # public, private
        type = api[1]  # spot, margin, future, delivery
        query = self.omit(params, self.extract_params(path))
        containsSettle = path.find('settle') > -1
        if containsSettle and path.endswith('batch_cancel_orders'):  # weird check to prevent $settle in php and converting {settle} to array(settle)
            # special case where we need to extract the settle from the path
            # but the body is an array of strings
            settle = self.safe_dict(params, 0)
            path = self.implode_params(path, settle)
            # remove the first element from params
            newParams = []
            anyParams = params
            for i in range(1, len(anyParams)):
                newParams.append(params[i])
            params = newParams
            query = newParams
        elif isinstance(params, list):
            # endpoints like createOrders use an array instead of an object
            # so we infer the settle from one of the elements
            # they have to be all the same so relying on the first one is fine
            first = self.safe_value(params, 0, {})
            path = self.implode_params(path, first)
        else:
            path = self.implode_params(path, params)
        endPart = '' if (path == '') else ('/' + path)
        entirePath = '/' + type + endPart
        if (type == 'subAccounts') or (type == 'withdrawals'):
            entirePath = endPart
        url = self.urls['api'][authentication][type]
        if url is None:
            raise NotSupported(self.id + ' does not have a testnet for the ' + type + ' market type.')
        url += entirePath
        if authentication == 'public':
            if query:
                url += '?' + self.urlencode(query)
        else:
            self.check_required_credentials()
            queryString = ''
            requiresURLEncoding = False
            if ((type == 'futures') or (type == 'delivery')) and method == 'POST':
                pathParts = path.split('/')
                secondPart = self.safe_string(pathParts, 1, '')
                requiresURLEncoding = (secondPart.find('dual') >= 0) or (secondPart.find('positions') >= 0)
            if (method == 'GET') or (method == 'DELETE') or requiresURLEncoding or (method == 'PATCH'):
                if query:
                    queryString = self.urlencode(query)
                    # https://github.com/ccxt/ccxt/issues/25570
                    if queryString.find('currencies=') >= 0 and queryString.find('%2C') >= 0:
                        queryString = queryString.replace('%2C', ',')
                    url += '?' + queryString
                if method == 'PATCH':
                    body = self.json(query)
            else:
                urlQueryParams = self.safe_value(query, 'query', {})
                if urlQueryParams:
                    queryString = self.urlencode(urlQueryParams)
                    url += '?' + queryString
                query = self.omit(query, 'query')
                body = self.json(query)
            bodyPayload = '' if (body is None) else body
            bodySignature = self.hash(self.encode(bodyPayload), 'sha512')
            nonce = self.nonce()
            timestamp = self.parse_to_int(nonce / 1000)
            timestampString = str(timestamp)
            signaturePath = '/api/' + self.version + entirePath
            payloadArray = [method.upper(), signaturePath, queryString, bodySignature, timestampString]
            # eslint-disable-next-line quotes
            payload = "\n".join(payloadArray)
            signature = self.hmac(self.encode(payload), self.encode(self.secret), hashlib.sha512)
            headers = {
                'KEY': self.apiKey,
                'Timestamp': timestampString,
                'SIGN': signature,
                'Content-Type': 'application/json',
            }
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    async def modify_margin_helper(self, symbol: str, amount, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request, query = self.prepare_request(market, None, params)
        request['change'] = self.number_to_string(amount)
        response = None
        if market['swap']:
            response = await self.privateFuturesPostSettlePositionsContractMargin(self.extend(request, query))
        elif market['future']:
            response = await self.privateDeliveryPostSettlePositionsContractMargin(self.extend(request, query))
        else:
            raise NotSupported(self.id + ' modifyMarginHelper() not support self market type')
        return self.parse_margin_modification(response, market)

    def parse_margin_modification(self, data: dict, market: Market = None) -> MarginModification:
        #
        #     {
        #         "value": "11.9257",
        #         "leverage": "5",
        #         "mode": "single",
        #         "realised_point": "0",
        #         "contract": "ETH_USDT",
        #         "entry_price": "1203.45",
        #         "mark_price": "1192.57",
        #         "history_point": "0",
        #         "realised_pnl": "-0.00577656",
        #         "close_order": null,
        #         "size": "1",
        #         "cross_leverage_limit": "0",
        #         "pending_orders": "0",
        #         "adl_ranking": "5",
        #         "maintenance_rate": "0.005",
        #         "unrealised_pnl": "-0.1088",
        #         "user": "1486602",
        #         "leverage_max": "100",
        #         "history_pnl": "0",
        #         "risk_limit": "1000000",
        #         "margin": "5.415925875",
        #         "last_close_pnl": "0",
        #         "liq_price": "665.69"
        #     }
        #
        contract = self.safe_string(data, 'contract')
        market = self.safe_market(contract, market, '_', 'contract')
        total = self.safe_number(data, 'margin')
        return {
            'info': data,
            'symbol': market['symbol'],
            'type': None,
            'marginMode': 'isolated',
            'amount': None,
            'total': total,
            'code': self.safe_value(market, 'quote'),
            'status': 'ok',
            'timestamp': None,
            'datetime': None,
        }

    async def reduce_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
        """
        remove margin from a position

        https://www.gate.io/docs/developers/apiv4/en/#update-position-margin
        https://www.gate.io/docs/developers/apiv4/en/#update-position-margin-2

        :param str symbol: unified market symbol
        :param float amount: the amount of margin to remove
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `margin structure <https://docs.ccxt.com/#/?id=reduce-margin-structure>`
        """
        return await self.modify_margin_helper(symbol, -amount, params)

    async def add_margin(self, symbol: str, amount: float, params={}) -> MarginModification:
        """
        add margin

        https://www.gate.io/docs/developers/apiv4/en/#update-position-margin
        https://www.gate.io/docs/developers/apiv4/en/#update-position-margin-2

        :param str symbol: unified market symbol
        :param float amount: amount of margin to add
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `margin structure <https://docs.ccxt.com/#/?id=add-margin-structure>`
        """
        return await self.modify_margin_helper(symbol, amount, params)

    async def fetch_open_interest_history(self, symbol: str, timeframe='5m', since: Int = None, limit: Int = None, params={}):
        """
        Retrieves the open interest of a currency

        https://www.gate.io/docs/developers/apiv4/en/#futures-stats

        :param str symbol: Unified CCXT market symbol
        :param str timeframe: "5m", "15m", "30m", "1h", "4h", "1d"
        :param int [since]: the time(ms) of the earliest record to retrieve unix timestamp
        :param int [limit]: default 30
        :param dict [params]: exchange specific parameters
        :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
        :returns dict} an open interest structure{@link https://docs.ccxt.com/#/?id=open-interest-structure:
        """
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchOpenInterestHistory', 'paginate', False)
        if paginate:
            return await self.fetch_paginated_call_deterministic('fetchOpenInterestHistory', symbol, since, limit, timeframe, params, 100)
        market = self.market(symbol)
        if not market['swap']:
            raise BadRequest(self.id + ' fetchOpenInterest() supports swap markets only')
        request: dict = {
            'contract': market['id'],
            'settle': market['settleId'],
            'interval': self.safe_string(self.timeframes, timeframe, timeframe),
        }
        if limit is not None:
            request['limit'] = limit
        if since is not None:
            request['from'] = since
        response = await self.publicFuturesGetSettleContractStats(self.extend(request, params))
        #
        #    [
        #        {
        #            "long_liq_size": "0",
        #            "short_liq_size": "0",
        #            "short_liq_usd": "0",
        #            "lsr_account": "3.2808988764045",
        #            "mark_price": "0.34619",
        #            "top_lsr_size": "0",
        #            "time": "1674057000",
        #            "short_liq_amount": "0",
        #            "long_liq_amount": "0",
        #            "open_interest_usd": "9872386.7775",
        #            "top_lsr_account": "0",
        #            "open_interest": "2851725",
        #            "long_liq_usd": "0",
        #            "lsr_taker": "9.3765153315902"
        #        },
        #        ...
        #    ]
        #
        return self.parse_open_interests_history(response, market, since, limit)

    def parse_open_interest(self, interest, market: Market = None):
        #
        #    {
        #        "long_liq_size": "0",
        #        "short_liq_size": "0",
        #        "short_liq_usd": "0",
        #        "lsr_account": "3.2808988764045",
        #        "mark_price": "0.34619",
        #        "top_lsr_size": "0",
        #        "time": "1674057000",
        #        "short_liq_amount": "0",
        #        "long_liq_amount": "0",
        #        "open_interest_usd": "9872386.7775",
        #        "top_lsr_account": "0",
        #        "open_interest": "2851725",
        #        "long_liq_usd": "0",
        #        "lsr_taker": "9.3765153315902"
        #    }
        #
        timestamp = self.safe_timestamp(interest, 'time')
        return {
            'symbol': self.safe_string(market, 'symbol'),
            'openInterestAmount': self.safe_number(interest, 'open_interest'),
            'openInterestValue': self.safe_number(interest, 'open_interest_usd'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'info': interest,
        }

    async def fetch_settlement_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetches historical settlement records

        https://www.gate.io/docs/developers/apiv4/en/#list-settlement-history-2

        :param str symbol: unified market symbol of the settlement history, required on gate
        :param int [since]: timestamp in ms
        :param int [limit]: number of records
        :param dict [params]: exchange specific params
        :returns dict[]: a list of `settlement history objects <https://docs.ccxt.com/#/?id=settlement-history-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchSettlementHistory() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        type = None
        type, params = self.handle_market_type_and_params('fetchSettlementHistory', market, params)
        if type != 'option':
            raise NotSupported(self.id + ' fetchSettlementHistory() supports option markets only')
        marketId = market['id']
        optionParts = marketId.split('-')
        request: dict = {
            'underlying': self.safe_string(optionParts, 0),
        }
        if since is not None:
            request['from'] = since
        if limit is not None:
            request['limit'] = limit
        response = await self.publicOptionsGetSettlements(self.extend(request, params))
        #
        #     [
        #         {
        #             "time": 1685952000,
        #             "profit": "18.266806892718",
        #             "settle_price": "26826.68068927182",
        #             "fee": "0.040240021034",
        #             "contract": "BTC_USDT-20230605-25000-C",
        #             "strike_price": "25000"
        #         }
        #     ]
        #
        settlements = self.parse_settlements(response, market)
        sorted = self.sort_by(settlements, 'timestamp')
        return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)

    async def fetch_my_settlement_history(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        """
        fetches historical settlement records of the user

        https://www.gate.io/docs/developers/apiv4/en/#list-my-options-settlements

        :param str symbol: unified market symbol of the settlement history
        :param int [since]: timestamp in ms
        :param int [limit]: number of records
        :param dict [params]: exchange specific params
        :returns dict[]: a list of [settlement history objects]
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchMySettlementHistory() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        type = None
        type, params = self.handle_market_type_and_params('fetchMySettlementHistory', market, params)
        if type != 'option':
            raise NotSupported(self.id + ' fetchMySettlementHistory() supports option markets only')
        marketId = market['id']
        optionParts = marketId.split('-')
        request: dict = {
            'underlying': self.safe_string(optionParts, 0),
            'contract': marketId,
        }
        if since is not None:
            request['from'] = since
        if limit is not None:
            request['limit'] = limit
        response = await self.privateOptionsGetMySettlements(self.extend(request, params))
        #
        #     [
        #         {
        #             "size": -1,
        #             "settle_profit": "0",
        #             "contract": "BTC_USDT-20220624-26000-C",
        #             "strike_price": "26000",
        #             "time": 1656057600,
        #             "settle_price": "20917.461281337048",
        #             "underlying": "BTC_USDT",
        #             "realised_pnl": "-0.00116042",
        #             "fee": "0"
        #         }
        #     ]
        #
        result = self.safe_value(response, 'result', {})
        data = self.safe_value(result, 'list', [])
        settlements = self.parse_settlements(data, market)
        sorted = self.sort_by(settlements, 'timestamp')
        return self.filter_by_symbol_since_limit(sorted, market['symbol'], since, limit)

    def parse_settlement(self, settlement, market):
        #
        # fetchSettlementHistory
        #
        #     {
        #         "time": 1685952000,
        #         "profit": "18.266806892718",
        #         "settle_price": "26826.68068927182",
        #         "fee": "0.040240021034",
        #         "contract": "BTC_USDT-20230605-25000-C",
        #         "strike_price": "25000"
        #     }
        #
        # fetchMySettlementHistory
        #
        #     {
        #         "size": -1,
        #         "settle_profit": "0",
        #         "contract": "BTC_USDT-20220624-26000-C",
        #         "strike_price": "26000",
        #         "time": 1656057600,
        #         "settle_price": "20917.461281337048",
        #         "underlying": "BTC_USDT",
        #         "realised_pnl": "-0.00116042",
        #         "fee": "0"
        #     }
        #
        timestamp = self.safe_timestamp(settlement, 'time')
        marketId = self.safe_string(settlement, 'contract')
        return {
            'info': settlement,
            'symbol': self.safe_symbol(marketId, market),
            'price': self.safe_number(settlement, 'settle_price'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
        }

    def parse_settlements(self, settlements, market):
        #
        # fetchSettlementHistory
        #
        #     [
        #         {
        #             "time": 1685952000,
        #             "profit": "18.266806892718",
        #             "settle_price": "26826.68068927182",
        #             "fee": "0.040240021034",
        #             "contract": "BTC_USDT-20230605-25000-C",
        #             "strike_price": "25000"
        #         }
        #     ]
        #
        # fetchMySettlementHistory
        #
        #     [
        #         {
        #             "size": -1,
        #             "settle_profit": "0",
        #             "contract": "BTC_USDT-20220624-26000-C",
        #             "strike_price": "26000",
        #             "time": 1656057600,
        #             "settle_price": "20917.461281337048",
        #             "underlying": "BTC_USDT",
        #             "realised_pnl": "-0.00116042",
        #             "fee": "0"
        #         }
        #     ]
        #
        result = []
        for i in range(0, len(settlements)):
            result.append(self.parse_settlement(settlements[i], market))
        return result

    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://www.gate.io/docs/developers/apiv4/en/#query-account-book
        https://www.gate.io/docs/developers/apiv4/en/#list-margin-account-balance-change-history
        https://www.gate.io/docs/developers/apiv4/en/#query-account-book-2
        https://www.gate.io/docs/developers/apiv4/en/#query-account-book-3
        https://www.gate.io/docs/developers/apiv4/en/#list-account-changing-history

        :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]: end time in ms
        :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [available parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
        :returns dict: a `ledger structure <https://docs.ccxt.com/#/?id=ledger>`
        """
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchLedger', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_dynamic('fetchLedger', code, since, limit, params)
        type = None
        currency = None
        response = None
        request: dict = {}
        type, params = self.handle_market_type_and_params('fetchLedger', None, params)
        if (type == 'spot') or (type == 'margin'):
            if code is not None:
                currency = self.currency(code)
                request['currency'] = currency['id']  # todo: currencies have network-junctions
        if (type == 'swap') or (type == 'future'):
            defaultSettle = 'usdt' if (type == 'swap') else 'btc'
            settle = self.safe_string_lower(params, 'settle', defaultSettle)
            params = self.omit(params, 'settle')
            request['settle'] = settle
        if since is not None:
            request['from'] = since
        if limit is not None:
            request['limit'] = limit
        request, params = self.handle_until_option('to', request, params)
        if type == 'spot':
            response = await self.privateSpotGetAccountBook(self.extend(request, params))
        elif type == 'margin':
            response = await self.privateMarginGetAccountBook(self.extend(request, params))
        elif type == 'swap':
            response = await self.privateFuturesGetSettleAccountBook(self.extend(request, params))
        elif type == 'future':
            response = await self.privateDeliveryGetSettleAccountBook(self.extend(request, params))
        elif type == 'option':
            response = await self.privateOptionsGetAccountBook(self.extend(request, params))
        #
        # spot
        #
        #     [
        #         {
        #             "id": "123456",
        #             "time": 1547633726123,
        #             "currency": "BTC",
        #             "change": "1.03",
        #             "balance": "4.59316525194",
        #             "type": "margin_in"
        #         }
        #     ]
        #
        # margin
        #
        #     [
        #         {
        #             "id": "123456",
        #             "time": "1547633726",
        #             "time_ms": 1547633726123,
        #             "currency": "BTC",
        #             "currency_pair": "BTC_USDT",
        #             "change": "1.03",
        #             "balance": "4.59316525194"
        #         }
        #     ]
        #
        # swap and future
        #
        #     [
        #         {
        #             "time": 1682294400.123456,
        #             "change": "0.000010152188",
        #             "balance": "4.59316525194",
        #             "text": "ETH_USD:6086261",
        #             "type": "fee"
        #         }
        #     ]
        #
        # option
        #
        #     [
        #         {
        #             "time": 1685594770,
        #             "change": "3.33",
        #             "balance": "29.87911771",
        #             "text": "BTC_USDT-20230602-26500-C:2611026125",
        #             "type": "prem"
        #         }
        #     ]
        #
        return self.parse_ledger(response, currency, since, limit)

    def parse_ledger_entry(self, item: dict, currency: Currency = None) -> LedgerEntry:
        #
        # spot
        #
        #     {
        #         "id": "123456",
        #         "time": 1547633726123,
        #         "currency": "BTC",
        #         "change": "1.03",
        #         "balance": "4.59316525194",
        #         "type": "margin_in"
        #     }
        #
        # margin
        #
        #     {
        #         "id": "123456",
        #         "time": "1547633726",
        #         "time_ms": 1547633726123,
        #         "currency": "BTC",
        #         "currency_pair": "BTC_USDT",
        #         "change": "1.03",
        #         "balance": "4.59316525194"
        #     }
        #
        # swap and future
        #
        #     {
        #         "time": 1682294400.123456,
        #         "change": "0.000010152188",
        #         "balance": "4.59316525194",
        #         "text": "ETH_USD:6086261",
        #         "type": "fee"
        #     }
        #
        # option
        #
        #     {
        #         "time": 1685594770,
        #         "change": "3.33",
        #         "balance": "29.87911771",
        #         "text": "BTC_USDT-20230602-26500-C:2611026125",
        #         "type": "prem"
        #     }
        #
        direction = None
        amount = self.safe_string(item, 'change')
        if Precise.string_lt(amount, '0'):
            direction = 'out'
            amount = Precise.string_abs(amount)
        else:
            direction = 'in'
        currencyId = self.safe_string(item, 'currency')
        currency = self.safe_currency(currencyId, currency)
        type = self.safe_string(item, 'type')
        rawTimestamp = self.safe_string(item, 'time')
        timestamp = None
        if len(rawTimestamp) > 10:
            timestamp = int(rawTimestamp)
        else:
            timestamp = int(rawTimestamp) * 1000
        balanceString = self.safe_string(item, 'balance')
        changeString = self.safe_string(item, 'change')
        before = self.parse_number(Precise.string_sub(balanceString, changeString))
        return self.safe_ledger_entry({
            'info': item,
            'id': self.safe_string(item, 'id'),
            'direction': direction,
            'account': None,
            'referenceAccount': None,
            'referenceId': None,
            'type': self.parse_ledger_entry_type(type),
            'currency': self.safe_currency_code(currencyId, currency),
            'amount': self.parse_number(amount),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'before': before,
            'after': self.safe_number(item, 'balance'),
            'status': None,
            'fee': None,
        }, currency)

    def parse_ledger_entry_type(self, type):
        ledgerType: dict = {
            'deposit': 'deposit',
            'withdraw': 'withdrawal',
            'sub_account_transfer': 'transfer',
            'margin_in': 'transfer',
            'margin_out': 'transfer',
            'margin_funding_in': 'transfer',
            'margin_funding_out': 'transfer',
            'cross_margin_in': 'transfer',
            'cross_margin_out': 'transfer',
            'copy_trading_in': 'transfer',
            'copy_trading_out': 'transfer',
            'quant_in': 'transfer',
            'quant_out': 'transfer',
            'futures_in': 'transfer',
            'futures_out': 'transfer',
            'delivery_in': 'transfer',
            'delivery_out': 'transfer',
            'new_order': 'trade',
            'order_fill': 'trade',
            'referral_fee': 'rebate',
            'order_fee': 'fee',
            'interest': 'interest',
            'lend': 'loan',
            'redeem': 'loan',
            'profit': 'interest',
            'flash_swap_buy': 'trade',
            'flash_swap_sell': 'trade',
            'unknown': 'unknown',
            'set': 'settlement',
            'prem': 'trade',
            'point_refr': 'rebate',
            'point_fee': 'fee',
            'point_dnw': 'deposit/withdraw',
            'fund': 'fee',
            'refr': 'rebate',
            'fee': 'fee',
            'pnl': 'trade',
            'dnw': 'deposit/withdraw',
        }
        return self.safe_string(ledgerType, type, type)

    async def set_position_mode(self, hedged: bool, symbol: Str = None, params={}):
        """
        set dual/hedged mode to True or False for a swap market, make sure all positions are closed and no orders are open before setting dual mode

        https://www.gate.io/docs/developers/apiv4/en/#enable-or-disable-dual-mode

        :param bool hedged: set to True to enable dual mode
        :param str|None symbol: if passed, dual mode is set for all markets with the same settle currency
        :param dict params: extra parameters specific to the exchange API endpoint
        :param str params['settle']: settle currency
        :returns dict: response from the exchange
        """
        market = self.market(symbol) if (symbol is not None) else None
        request, query = self.prepare_request(market, 'swap', params)
        request['dual_mode'] = hedged
        return await self.privateFuturesPostSettleDualMode(self.extend(request, query))

    async def fetch_underlying_assets(self, params={}):
        """
        fetches the market ids of underlying assets for a specific contract market type

        https://www.gate.io/docs/developers/apiv4/en/#list-all-underlyings

        :param dict [params]: exchange specific params
        :param str [params.type]: the contract market type, 'option', 'swap' or 'future', the default is 'option'
        :returns dict[]: a list of `underlying assets <https://docs.ccxt.com/#/?id=underlying-assets-structure>`
        """
        await self.load_markets()
        marketType = None
        marketType, params = self.handle_market_type_and_params('fetchUnderlyingAssets', None, params)
        if (marketType is None) or (marketType == 'spot'):
            marketType = 'option'
        if marketType != 'option':
            raise NotSupported(self.id + ' fetchUnderlyingAssets() supports option markets only')
        response = await self.publicOptionsGetUnderlyings(params)
        #
        #    [
        #        {
        #            "index_time": "1646915796",
        #            "name": "BTC_USDT",
        #            "index_price": "39142.73"
        #        }
        #    ]
        #
        underlyings = []
        for i in range(0, len(response)):
            underlying = response[i]
            name = self.safe_string(underlying, 'name')
            if name is not None:
                underlyings.append(name)
        return underlyings

    async def fetch_liquidations(self, symbol: str, since: Int = None, limit: Int = None, params={}):
        """
        retrieves the public liquidations of a trading pair

        https://www.gate.io/docs/developers/apiv4/en/#retrieve-liquidation-history

        :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 exchange 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>`
        """
        await self.load_markets()
        market = self.market(symbol)
        if not market['swap']:
            raise NotSupported(self.id + ' fetchLiquidations() supports swap markets only')
        request: dict = {
            'settle': market['settleId'],
            'contract': market['id'],
        }
        if since is not None:
            request['from'] = since
        if limit is not None:
            request['limit'] = limit
        request, params = self.handle_until_option('to', request, params)
        response = await self.publicFuturesGetSettleLiqOrders(self.extend(request, params))
        #
        #     [
        #         {
        #             "contract": "BTC_USDT",
        #             "left": 0,
        #             "size": -165,
        #             "fill_price": "28070",
        #             "order_price": "28225",
        #             "time": 1696736132
        #         },
        #     ]
        #
        return self.parse_liquidations(response, market, since, limit)

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

        https://www.gate.io/docs/developers/apiv4/en/#list-liquidation-history
        https://www.gate.io/docs/developers/apiv4/en/#list-liquidation-history-2
        https://www.gate.io/docs/developers/apiv4/en/#list-user-s-liquidation-history-of-specified-underlying

        :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 exchange API endpoint
        :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)
        request: dict = {
            'contract': market['id'],
        }
        response = None
        if (market['swap']) or (market['future']):
            if limit is not None:
                request['limit'] = limit
            request['settle'] = market['settleId']
        elif market['option']:
            marketId = market['id']
            optionParts = marketId.split('-')
            request['underlying'] = self.safe_string(optionParts, 0)
        if market['swap']:
            response = await self.privateFuturesGetSettleLiquidates(self.extend(request, params))
        elif market['future']:
            response = await self.privateDeliveryGetSettleLiquidates(self.extend(request, params))
        elif market['option']:
            response = await self.privateOptionsGetPositionClose(self.extend(request, params))
        else:
            raise NotSupported(self.id + ' fetchMyLiquidations() does not support ' + market['type'] + ' orders')
        #
        # swap and future
        #
        #     [
        #         {
        #             "time": 1548654951,
        #             "contract": "BTC_USDT",
        #             "size": 600,
        #             "leverage": "25",
        #             "margin": "0.006705256878",
        #             "entry_price": "3536.123",
        #             "liq_price": "3421.54",
        #             "mark_price": "3420.27",
        #             "order_id": 317393847,
        #             "order_price": "3405",
        #             "fill_price": "3424",
        #             "left": 0
        #         }
        #     ]
        #
        # option
        #
        #     [
        #         {
        #             "time": 1631764800,
        #             "pnl": "-42914.291",
        #             "settle_size": "-10001",
        #             "side": "short",
        #             "contract": "BTC_USDT-20210916-5000-C",
        #             "text": "settled"
        #         }
        #     ]
        #
        return self.parse_liquidations(response, market, since, limit)

    def parse_liquidation(self, liquidation, market: Market = None):
        #
        # fetchLiquidations
        #
        #     {
        #         "contract": "BTC_USDT",
        #         "left": 0,
        #         "size": -165,
        #         "fill_price": "28070",
        #         "order_price": "28225",
        #         "time": 1696736132
        #     }
        #
        # swap and future: fetchMyLiquidations
        #
        #     {
        #         "time": 1548654951,
        #         "contract": "BTC_USDT",
        #         "size": 600,
        #         "leverage": "25",
        #         "margin": "0.006705256878",
        #         "entry_price": "3536.123",
        #         "liq_price": "3421.54",
        #         "mark_price": "3420.27",
        #         "order_id": 317393847,
        #         "order_price": "3405",
        #         "fill_price": "3424",
        #         "left": 0
        #     }
        #
        # option: fetchMyLiquidations
        #
        #     {
        #         "time": 1631764800,
        #         "pnl": "-42914.291",
        #         "settle_size": "-10001",
        #         "side": "short",
        #         "contract": "BTC_USDT-20210916-5000-C",
        #         "text": "settled"
        #     }
        #
        marketId = self.safe_string(liquidation, 'contract')
        timestamp = self.safe_timestamp(liquidation, 'time')
        size = self.safe_string_2(liquidation, 'size', 'settle_size')
        left = self.safe_string(liquidation, 'left', '0')
        contractsString = Precise.string_abs(Precise.string_sub(size, left))
        contractSizeString = self.safe_string(market, 'contractSize')
        priceString = self.safe_string_2(liquidation, 'liq_price', 'fill_price')
        baseValueString = Precise.string_mul(contractsString, contractSizeString)
        quoteValueString = self.safe_string(liquidation, 'pnl')
        if quoteValueString is None:
            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(Precise.string_abs(quoteValueString)),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
        })

    async def fetch_greeks(self, symbol: str, params={}) -> Greeks:
        """
        fetches an option contracts greeks, financial metrics used to measure the factors that affect the price of an options contract

        https://www.gate.io/docs/developers/apiv4/en/#list-tickers-of-options-contracts

        :param str symbol: unified symbol of the market to fetch greeks for
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `greeks structure <https://docs.ccxt.com/#/?id=greeks-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'underlying': market['info']['underlying'],
        }
        response = await self.publicOptionsGetTickers(self.extend(request, params))
        #
        #     [
        #         {
        #             "vega": "1.78992",
        #             "leverage": "6.2096777055417",
        #             "ask_iv": "0.6245",
        #             "delta": "-0.69397",
        #             "last_price": "0",
        #             "theta": "-2.5723",
        #             "bid1_price": "222.9",
        #             "mark_iv": "0.5909",
        #             "name": "ETH_USDT-20231201-2300-P",
        #             "bid_iv": "0.5065",
        #             "ask1_price": "243.6",
        #             "mark_price": "236.57",
        #             "position_size": 0,
        #             "bid1_size": 368,
        #             "ask1_size": -335,
        #             "gamma": "0.00116"
        #         },
        #     ]
        #
        marketId = market['id']
        for i in range(0, len(response)):
            entry = response[i]
            entryMarketId = self.safe_string(entry, 'name')
            if entryMarketId == marketId:
                return self.parse_greeks(entry, market)
        return None

    def parse_greeks(self, greeks: dict, market: Market = None) -> Greeks:
        #
        #     {
        #         "vega": "1.78992",
        #         "leverage": "6.2096777055417",
        #         "ask_iv": "0.6245",
        #         "delta": "-0.69397",
        #         "last_price": "0",
        #         "theta": "-2.5723",
        #         "bid1_price": "222.9",
        #         "mark_iv": "0.5909",
        #         "name": "ETH_USDT-20231201-2300-P",
        #         "bid_iv": "0.5065",
        #         "ask1_price": "243.6",
        #         "mark_price": "236.57",
        #         "position_size": 0,
        #         "bid1_size": 368,
        #         "ask1_size": -335,
        #         "gamma": "0.00116"
        #     }
        #
        marketId = self.safe_string(greeks, 'name')
        symbol = self.safe_symbol(marketId, market)
        return {
            'symbol': symbol,
            'timestamp': None,
            'datetime': None,
            'delta': self.safe_number(greeks, 'delta'),
            'gamma': self.safe_number(greeks, 'gamma'),
            'theta': self.safe_number(greeks, 'theta'),
            'vega': self.safe_number(greeks, 'vega'),
            'rho': None,
            'bidSize': self.safe_number(greeks, 'bid1_size'),
            'askSize': self.safe_number(greeks, 'ask1_size'),
            'bidImpliedVolatility': self.safe_number(greeks, 'bid_iv'),
            'askImpliedVolatility': self.safe_number(greeks, 'ask_iv'),
            'markImpliedVolatility': self.safe_number(greeks, 'mark_iv'),
            'bidPrice': self.safe_number(greeks, 'bid1_price'),
            'askPrice': self.safe_number(greeks, 'ask1_price'),
            'markPrice': self.safe_number(greeks, 'mark_price'),
            'lastPrice': self.safe_number(greeks, 'last_price'),
            'underlyingPrice': self.parse_number(market['info']['underlying_price']),
            'info': greeks,
        }

    async def close_position(self, symbol: str, side: OrderSide = None, params={}) -> Order:
        """
        closes open positions for a market

        https://www.gate.io/docs/developers/apiv4/en/#create-a-futures-order
        https://www.gate.io/docs/developers/apiv4/en/#create-a-futures-order-2
        https://www.gate.io/docs/developers/apiv4/en/#create-an-options-order

        :param str symbol: Unified CCXT market symbol
        :param str side: 'buy' or 'sell'
        :param dict [params]: extra parameters specific to the okx api endpoint
        :returns dict[]: `A list of position structures <https://docs.ccxt.com/#/?id=position-structure>`
        """
        request: dict = {
            'close': True,
        }
        params = self.extend(request, params)
        if side is None:
            side = ''  # side is not used but needs to be present, otherwise crashes in php
        return await self.create_order(symbol, 'market', side, 0, None, params)

    async def fetch_leverage(self, symbol: str, params={}) -> Leverage:
        """
        fetch the set leverage for a market

        https://www.gate.io/docs/developers/apiv4/en/#get-unified-account-information
        https://www.gate.io/docs/developers/apiv4/en/#get-detail-of-lending-market
        https://www.gate.io/docs/developers/apiv4/en/#query-one-single-margin-currency-pair-deprecated

        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param boolean [params.unified]: default False, set to True for fetching the unified accounts leverage
        :returns dict: a `leverage structure <https://docs.ccxt.com/#/?id=leverage-structure>`
        """
        await self.load_markets()
        market = None
        if symbol is not None:
            # unified account does not require a symbol
            market = self.market(symbol)
        request: dict = {}
        response = None
        isUnified = self.safe_bool(params, 'unified')
        params = self.omit(params, 'unified')
        if market['spot']:
            request['currency_pair'] = market['id']
            if isUnified:
                response = await self.publicMarginGetUniCurrencyPairsCurrencyPair(self.extend(request, params))
                #
                #     {
                #         "currency_pair": "BTC_USDT",
                #         "base_min_borrow_amount": "0.0001",
                #         "quote_min_borrow_amount": "1",
                #         "leverage": "10"
                #     }
                #
            else:
                response = await self.publicMarginGetCurrencyPairsCurrencyPair(self.extend(request, params))
                #
                #     {
                #         "id": "BTC_USDT",
                #         "base": "BTC",
                #         "quote": "USDT",
                #         "leverage": 10,
                #         "min_base_amount": "0.0001",
                #         "min_quote_amount": "1",
                #         "max_quote_amount": "40000000",
                #         "status": 1
                #     }
                #
        elif isUnified:
            response = await self.privateUnifiedGetAccounts(self.extend(request, params))
            #
            #     {
            #         "user_id": 10001,
            #         "locked": False,
            #         "balances": {
            #             "ETH": {
            #                 "available": "0",
            #                 "freeze": "0",
            #                 "borrowed": "0.075393666654",
            #                 "negative_liab": "0",
            #                 "futures_pos_liab": "0",
            #                 "equity": "1016.1",
            #                 "total_freeze": "0",
            #                 "total_liab": "0"
            #             },
            #             "POINT": {
            #                 "available": "9999999999.017023138734",
            #                 "freeze": "0",
            #                 "borrowed": "0",
            #                 "negative_liab": "0",
            #                 "futures_pos_liab": "0",
            #                 "equity": "12016.1",
            #                 "total_freeze": "0",
            #                 "total_liab": "0"
            #             },
            #             "USDT": {
            #                 "available": "0.00000062023",
            #                 "freeze": "0",
            #                 "borrowed": "0",
            #                 "negative_liab": "0",
            #                 "futures_pos_liab": "0",
            #                 "equity": "16.1",
            #                 "total_freeze": "0",
            #                 "total_liab": "0"
            #             }
            #         },
            #         "total": "230.94621713",
            #         "borrowed": "161.66395521",
            #         "total_initial_margin": "1025.0524665088",
            #         "total_margin_balance": "3382495.944473949183",
            #         "total_maintenance_margin": "205.01049330176",
            #         "total_initial_margin_rate": "3299.827135672679",
            #         "total_maintenance_margin_rate": "16499.135678363399",
            #         "total_available_margin": "3381470.892007440383",
            #         "unified_account_total": "3381470.892007440383",
            #         "unified_account_total_liab": "0",
            #         "unified_account_total_equity": "100016.1",
            #         "leverage": "2"
            #     }
            #
        else:
            raise NotSupported(self.id + ' fetchLeverage() does not support ' + market['type'] + ' markets')
        return self.parse_leverage(response, market)

    async def fetch_leverages(self, symbols: Strings = None, params={}) -> Leverages:
        """
        fetch the set leverage for all leverage markets, only spot margin is supported on gate

        https://www.gate.io/docs/developers/apiv4/en/#list-lending-markets
        https://www.gate.io/docs/developers/apiv4/en/#list-all-supported-currency-pairs-supported-in-margin-trading-deprecated

        :param str[] symbols: a list of unified market symbols
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param boolean [params.unified]: default False, set to True for fetching unified account leverages
        :returns dict: a list of `leverage structures <https://docs.ccxt.com/#/?id=leverage-structure>`
        """
        await self.load_markets()
        symbols = self.market_symbols(symbols)
        response = None
        isUnified = self.safe_bool(params, 'unified')
        params = self.omit(params, 'unified')
        marketIdRequest = 'id'
        if isUnified:
            marketIdRequest = 'currency_pair'
            response = await self.publicMarginGetUniCurrencyPairs(params)
            #
            #     [
            #         {
            #             "currency_pair": "1INCH_USDT",
            #             "base_min_borrow_amount": "8",
            #             "quote_min_borrow_amount": "1",
            #             "leverage": "3"
            #         },
            #     ]
            #
        else:
            response = await self.publicMarginGetCurrencyPairs(params)
            #
            #     [
            #         {
            #             "id": "1CAT_USDT",
            #             "base": "1CAT",
            #             "quote": "USDT",
            #             "leverage": 3,
            #             "min_base_amount": "71",
            #             "min_quote_amount": "1",
            #             "max_quote_amount": "10000",
            #             "status": 1
            #         },
            #     ]
            #
        return self.parse_leverages(response, symbols, marketIdRequest, 'spot')

    def parse_leverage(self, leverage: dict, market: Market = None) -> Leverage:
        marketId = self.safe_string_2(leverage, 'currency_pair', 'id')
        leverageValue = self.safe_integer(leverage, 'leverage')
        return {
            'info': leverage,
            'symbol': self.safe_symbol(marketId, market, '_', 'spot'),
            'marginMode': None,
            'longLeverage': leverageValue,
            'shortLeverage': leverageValue,
        }

    async def fetch_option(self, symbol: str, params={}) -> Option:
        """
        fetches option data that is commonly found in an option chain

        https://www.gate.io/docs/developers/apiv4/en/#query-specified-contract-detail

        :param str symbol: unified market symbol
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an `option chain structure <https://docs.ccxt.com/#/?id=option-chain-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'contract': market['id'],
        }
        response = await self.publicOptionsGetContractsContract(self.extend(request, params))
        #
        #     {
        #         "is_active": True,
        #         "mark_price_round": "0.01",
        #         "settle_fee_rate": "0.00015",
        #         "bid1_size": 30,
        #         "taker_fee_rate": "0.0003",
        #         "price_limit_fee_rate": "0.1",
        #         "order_price_round": "0.1",
        #         "tag": "month",
        #         "ref_rebate_rate": "0",
        #         "name": "ETH_USDT-20240628-4500-C",
        #         "strike_price": "4500",
        #         "ask1_price": "280.5",
        #         "ref_discount_rate": "0",
        #         "order_price_deviate": "0.2",
        #         "ask1_size": -19,
        #         "mark_price_down": "155.45",
        #         "orderbook_id": 11724695,
        #         "is_call": True,
        #         "last_price": "188.7",
        #         "mark_price": "274.26",
        #         "underlying": "ETH_USDT",
        #         "create_time": 1688024882,
        #         "settle_limit_fee_rate": "0.1",
        #         "orders_limit": 10,
        #         "mark_price_up": "403.83",
        #         "position_size": 80,
        #         "order_size_max": 10000,
        #         "position_limit": 100000,
        #         "multiplier": "0.01",
        #         "order_size_min": 1,
        #         "trade_size": 229,
        #         "underlying_price": "3326.6",
        #         "maker_fee_rate": "0.0003",
        #         "expiration_time": 1719561600,
        #         "trade_id": 15,
        #         "bid1_price": "269.3"
        #     }
        #
        return self.parse_option(response, None, market)

    async def fetch_option_chain(self, code: str, params={}) -> OptionChain:
        """
        fetches data for an underlying asset that is commonly found in an option chain

        https://www.gate.io/docs/developers/apiv4/en/#list-all-the-contracts-with-specified-underlying-and-expiration-time

        :param str code: base currency to fetch an option chain for
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param str [params.underlying]: the underlying asset, can be obtained from fetchUnderlyingAssets()
        :param int [params.expiration]: unix timestamp of the expiration time
        :returns dict: a list of `option chain structures <https://docs.ccxt.com/#/?id=option-chain-structure>`
        """
        await self.load_markets()
        currency = self.currency(code)
        request: dict = {
            'underlying': currency['code'] + '_USDT',  # todo: currency['id'].upper() &  network junctions
        }
        response = await self.publicOptionsGetContracts(self.extend(request, params))
        #
        #     [
        #         {
        #             "is_active": True,
        #             "mark_price_round": "0.1",
        #             "settle_fee_rate": "0.00015",
        #             "bid1_size": 434,
        #             "taker_fee_rate": "0.0003",
        #             "price_limit_fee_rate": "0.1",
        #             "order_price_round": "1",
        #             "tag": "day",
        #             "ref_rebate_rate": "0",
        #             "name": "BTC_USDT-20240324-63500-P",
        #             "strike_price": "63500",
        #             "ask1_price": "387",
        #             "ref_discount_rate": "0",
        #             "order_price_deviate": "0.15",
        #             "ask1_size": -454,
        #             "mark_price_down": "124.3",
        #             "orderbook_id": 29600,
        #             "is_call": False,
        #             "last_price": "0",
        #             "mark_price": "366.6",
        #             "underlying": "BTC_USDT",
        #             "create_time": 1711118829,
        #             "settle_limit_fee_rate": "0.1",
        #             "orders_limit": 10,
        #             "mark_price_up": "630",
        #             "position_size": 0,
        #             "order_size_max": 10000,
        #             "position_limit": 10000,
        #             "multiplier": "0.01",
        #             "order_size_min": 1,
        #             "trade_size": 0,
        #             "underlying_price": "64084.65",
        #             "maker_fee_rate": "0.0003",
        #             "expiration_time": 1711267200,
        #             "trade_id": 0,
        #             "bid1_price": "307"
        #         },
        #     ]
        #
        return self.parse_option_chain(response, None, 'name')

    def parse_option(self, chain: dict, currency: Currency = None, market: Market = None) -> Option:
        #
        #     {
        #         "is_active": True,
        #         "mark_price_round": "0.1",
        #         "settle_fee_rate": "0.00015",
        #         "bid1_size": 434,
        #         "taker_fee_rate": "0.0003",
        #         "price_limit_fee_rate": "0.1",
        #         "order_price_round": "1",
        #         "tag": "day",
        #         "ref_rebate_rate": "0",
        #         "name": "BTC_USDT-20240324-63500-P",
        #         "strike_price": "63500",
        #         "ask1_price": "387",
        #         "ref_discount_rate": "0",
        #         "order_price_deviate": "0.15",
        #         "ask1_size": -454,
        #         "mark_price_down": "124.3",
        #         "orderbook_id": 29600,
        #         "is_call": False,
        #         "last_price": "0",
        #         "mark_price": "366.6",
        #         "underlying": "BTC_USDT",
        #         "create_time": 1711118829,
        #         "settle_limit_fee_rate": "0.1",
        #         "orders_limit": 10,
        #         "mark_price_up": "630",
        #         "position_size": 0,
        #         "order_size_max": 10000,
        #         "position_limit": 10000,
        #         "multiplier": "0.01",
        #         "order_size_min": 1,
        #         "trade_size": 0,
        #         "underlying_price": "64084.65",
        #         "maker_fee_rate": "0.0003",
        #         "expiration_time": 1711267200,
        #         "trade_id": 0,
        #         "bid1_price": "307"
        #     }
        #
        marketId = self.safe_string(chain, 'name')
        market = self.safe_market(marketId, market)
        timestamp = self.safe_timestamp(chain, 'create_time')
        return {
            'info': chain,
            'currency': None,
            'symbol': market['symbol'],
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'impliedVolatility': None,
            'openInterest': None,
            'bidPrice': self.safe_number(chain, 'bid1_price'),
            'askPrice': self.safe_number(chain, 'ask1_price'),
            'midPrice': None,
            'markPrice': self.safe_number(chain, 'mark_price'),
            'lastPrice': self.safe_number(chain, 'last_price'),
            'underlyingPrice': self.safe_number(chain, 'underlying_price'),
            'change': None,
            'percentage': None,
            'baseVolume': None,
            'quoteVolume': None,
        }

    async def fetch_positions_history(self, symbols: Strings = None, since: Int = None, limit: Int = None, params={}) -> List[Position]:
        """
        fetches historical positions

        https://www.gate.io/docs/developers/apiv4/#list-position-close-history
        https://www.gate.io/docs/developers/apiv4/#list-position-close-history-2

        :param str[] symbols: unified conract symbols, must all have the same settle currency and the same market type
        :param int [since]: the earliest time in ms to fetch positions for
        :param int [limit]: the maximum amount of records to fetch, default=1000
        :param dict params: extra parameters specific to the exchange api endpoint
        :param int [params.until]: the latest time in ms to fetch positions for

 EXCHANGE SPECIFIC PARAMETERS
        :param int [params.offset]: list offset, starting from 0
        :param str [params.side]: long or short
        :param str [params.pnl]: query profit or loss
        :returns dict[]: a list of `position structures <https://docs.ccxt.com/#/?id=position-structure>`
        """
        await self.load_markets()
        market = None
        if symbols is not None:
            symbolsLength = len(symbols)
            if symbolsLength == 1:
                market = self.market(symbols[0])
        marketType = None
        marketType, params = self.handle_market_type_and_params('fetchPositionsHistory', market, params, 'swap')
        until = self.safe_integer(params, 'until')
        params = self.omit(params, 'until')
        request: dict = {}
        request, params = self.prepare_request(market, marketType, params)
        if limit is not None:
            request['limit'] = limit
        if since is not None:
            request['from'] = self.parse_to_int(since / 1000)
        if until is not None:
            request['to'] = self.parse_to_int(until / 1000)
        response = None
        if marketType == 'swap':
            response = await self.privateFuturesGetSettlePositionClose(self.extend(request, params))
        elif marketType == 'future':
            response = await self.privateDeliveryGetSettlePositionClose(self.extend(request, params))
        else:
            raise NotSupported(self.id + ' fetchPositionsHistory() does not support markets of type ' + marketType)
        #
        #    [
        #        {
        #            "contract": "SLERF_USDT",
        #            "text": "web",
        #            "long_price": "0.766306",
        #            "pnl": "-23.41702352",
        #            "pnl_pnl": "-22.7187",
        #            "pnl_fee": "-0.06527125",
        #            "pnl_fund": "-0.63305227",
        #            "accum_size": "100",
        #            "time": 1711279263,
        #            "short_price": "0.539119",
        #            "side": "long",
        #            "max_size": "100",
        #            "first_open_time": 1711037985
        #        },
        #        ...
        #    ]
        #
        return self.parse_positions(response, symbols, params)

    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
        #
        #    {"label": "ORDER_NOT_FOUND", "message": "Order not found"}
        #    {"label": "INVALID_PARAM_VALUE", "message": "invalid argument: status"}
        #    {"label": "INVALID_PARAM_VALUE", "message": "invalid argument: Trigger.rule"}
        #    {"label": "INVALID_PARAM_VALUE", "message": "invalid argument: trigger.expiration invalid range"}
        #    {"label": "INVALID_ARGUMENT", "detail": "invalid size"}
        #    {"user_id":10406147,"id":"id","succeeded":false,"message":"INVALID_PROTOCOL","label":"INVALID_PROTOCOL"}
        #
        label = self.safe_string(response, 'label')
        if label is not None:
            feedback = self.id + ' ' + body
            self.throw_exactly_matched_exception(self.exceptions['exact'], label, feedback)
            raise ExchangeError(feedback)
        return None
