# -*- 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 Balances, Currencies, Currency, FundingHistory, Greeks, Int, Leverage, Leverages, LeverageTier, LeverageTiers, MarginModification, Market, MarketInterface, Num, Option, OptionChain, Order, OrderBook, OrderRequest, OrderSide, OrderType, Position, Str, Strings, Ticker, Tickers, Trade, TradingFeeInterface, TradingFees, Transaction, 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 BadResponse
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):
        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://user-images.githubusercontent.com/1294454/31784029-0313c702-b509-11e7-9ccc-bc0da6a0e435.jpg',
                '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,
                '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,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchClosedOrders': True,
                'fetchCrossBorrowRate': False,
                'fetchCrossBorrowRates': False,
                'fetchCurrencies': True,
                'fetchDepositAddress': True,
                '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': False,
                '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,
                        },
                    },
                    '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
                        },
                        'delete': {
                            'withdrawals/{withdrawal_id}': 1,
                        },
                    },
                    'wallet': {
                        'get': {
                            'deposit_address': 1,
                            'withdrawals': 1,
                            'deposits': 1,
                            'sub_account_transfers': 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,
                        },
                        '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,
                        },
                        'post': {
                            'account_mode': 20 / 15,
                            'loans': 200 / 15,  # 15r/10s cost = 20 / 1.5 = 13.33
                        },
                    },
                    '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,
                        },
                        'post': {
                            'orders': 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,
                        },
                        '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,
                            'stp_groups': 20 / 15,
                            'stp_groups/{stp_id}/users': 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': {
                '88MPH': 'MPH',
                'AXIS': 'Axis DeFi',
                'BIFI': 'Bitcoin File',
                'BOX': 'DefiBox',
                'BYN': 'BeyondFi',
                'EGG': 'Goose Finance',
                'GTC': 'Game.com',  # conflict with Gitcoin and Gastrocoin
                'GTC_HT': 'Game.com HT',
                'GTC_BSC': 'Game.com BSC',
                'HIT': 'HitChain',
                'MM': 'Million',  # conflict with MilliMeter
                'MPH': 'Morpher',  # conflict with 88MPH
                'POINT': 'GatePoint',
                'RAI': 'Rai Reflex Index',  # conflict with RAI Finance
                'SBTC': 'Super Bitcoin',
                'TNC': 'Trinity Network Credit',
                'VAI': 'VAIOT',
                'TRAC': 'TRACO',  # conflict with OriginTrail(TRAC)
            },
            'requiredCredentials': {
                'apiKey': True,
                'secret': True,
            },
            'headers': {
                'X-Gate-Channel-Id': 'ccxt',
            },
            'options': {
                'sandboxMode': False,
                'createOrder': {
                    'expiration': 86400,  # for conditional orders
                },
                'networks': {
                    'AVAXC': 'AVAX_C',
                    'BEP20': 'BSC',
                    'EOS': 'EOS',
                    'ERC20': 'ETH',
                    'GATECHAIN': 'GTEVM',
                    'HRC20': 'HT',
                    'KUSAMA': 'KSMSM',
                    'NEAR': 'NEAR',
                    'OKC': 'OKT',
                    'OPTIMISM': 'OPETH',
                    'POLKADOT': 'DOTSM',
                    'TRC20': 'TRX',
                },
                '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'],
                    },
                },
            },
            '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,
                    '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

    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
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-all-currency-pairs-supported                                     # spot
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-all-supported-currency-pairs-supported-in-margin-trading         # margin
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-all-futures-contracts                                            # swap
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-all-futures-contracts-2                                          # future
        :see: 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
        """
        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={}):
        marginResponse = await self.publicMarginGetCurrencyPairs(params)
        spotMarketsResponse = await self.publicSpotGetCurrencyPairs(params)
        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
        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.safe_number(market, 'quanto_multiplier'),
            '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, stop=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 stop: True if for a stop 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(stop, params)
        request: dict = {}
        if not stop:
            if market is None:
                raise ArgumentsRequired(self.id + ' spotOrderPrepareRequest() requires a market argument for non-stop orders')
            request['account'] = marginMode
            request['currency_pair'] = market['id']  # Should always be set for non-stop
        return [request, query]

    def multi_order_spot_prepare_request(self, market=None, stop=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 stop: True if for a stop 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(stop, params)
        request: dict = {
            'account': marginMode,
        }
        if market is not None:
            if stop:
                # gate spot and margin stop 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 stop 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, stop, params):
        """
         * @ignore
        Gets the margin type for self api call
        :param bool stop: True if for a stop 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 stop:
            if marginMode == 'spot':
                # gate spot stop orders use the term normal instead of spot
                marginMode = 'normal'
            if marginMode == 'cross_margin':
                raise BadRequest(self.id + ' getMarginMode() does not support stop orders for cross margin')
        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
        :see: 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
        response = await self.publicSpotGetCurrencies(params)
        #
        #    {
        #        "currency": "BCN",
        #        "delisted": False,
        #        "withdraw_disabled": True,
        #        "withdraw_delayed": False,
        #        "deposit_disabled": True,
        #        "trade_disabled": False
        #    }
        #
        #    {
        #        "currency":"USDT_ETH",
        #        "delisted":false,
        #        "withdraw_disabled":false,
        #        "withdraw_delayed":false,
        #        "deposit_disabled":false,
        #        "trade_disabled":false,
        #        "chain":"ETH"
        #    }
        #
        result: dict = {}
        for i in range(0, len(response)):
            entry = response[i]
            currencyId = self.safe_string(entry, 'currency')
            currencyIdLower = self.safe_string_lower(entry, 'currency')
            parts = currencyId.split('_')
            currency = parts[0]
            code = self.safe_currency_code(currency)
            networkId = self.safe_string(entry, 'chain')
            networkCode = None
            if networkId is not None:
                networkCode = self.network_id_to_code(networkId, code)
            delisted = self.safe_value(entry, 'delisted')
            withdrawDisabled = self.safe_bool(entry, 'withdraw_disabled', False)
            depositDisabled = self.safe_bool(entry, 'deposit_disabled', False)
            tradeDisabled = self.safe_bool(entry, 'trade_disabled', False)
            withdrawEnabled = not withdrawDisabled
            depositEnabled = not depositDisabled
            tradeEnabled = not tradeDisabled
            listed = not delisted
            active = listed and tradeEnabled and withdrawEnabled and depositEnabled
            if self.safe_value(result, code) is None:
                result[code] = {
                    'id': code.lower(),
                    'code': code,
                    'info': None,
                    'name': None,
                    'active': active,
                    'deposit': depositEnabled,
                    'withdraw': withdrawEnabled,
                    'fee': None,
                    'fees': [],
                    'precision': self.parse_number('1e-4'),
                    'limits': self.limits,
                    'networks': {},
                }
            depositAvailable = self.safe_value(result[code], 'deposit')
            depositAvailable = depositEnabled if (depositEnabled) else depositAvailable
            withdrawAvailable = self.safe_value(result[code], 'withdraw')
            withdrawAvailable = withdrawEnabled if (withdrawEnabled) else withdrawAvailable
            networks = self.safe_value(result[code], 'networks', {})
            if networkCode is not None:
                networks[networkCode] = {
                    'info': entry,
                    'id': networkId,
                    'network': networkCode,
                    'currencyId': currencyId,
                    'lowerCaseCurrencyId': currencyIdLower,
                    'deposit': depositEnabled,
                    'withdraw': withdrawEnabled,
                    'active': active,
                    'fee': None,
                    'precision': self.parse_number('1e-4'),
                    'limits': {
                        'amount': {
                            'min': None,
                            'max': None,
                        },
                        'withdraw': {
                            'min': None,
                            'max': None,
                        },
                        'deposit': {
                            'min': None,
                            'max': None,
                        },
                    },
                }
            result[code]['networks'] = networks
            info = self.safe_value(result[code], 'info', [])
            info.append(entry)
            result[code]['info'] = info
            result[code]['active'] = depositAvailable and withdrawAvailable
            result[code]['deposit'] = depositAvailable
            result[code]['withdraw'] = withdrawAvailable
        return result

    async def fetch_funding_rate(self, symbol: str, params={}):
        """
        fetch the current funding rate
        :see: 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={}):
        """
        fetch the funding rate for multiple markets
        :see: 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 dictionary of `funding rates structures <https://docs.ccxt.com/#/?id=funding-rates-structure>`, indexe by market symbols
        """
        await self.load_markets()
        symbols = self.market_symbols(symbols)
        request, query = self.prepare_request(None, '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
        #        }
        #    ]
        #
        result = self.parse_funding_rates(response)
        return self.filter_by_array(result, 'symbol', symbols)

    def parse_funding_rate(self, contract, market: Market = None):
        #
        #    {
        #        "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')
        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,
        }

    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_address(self, code: str, params={}):
        """
        fetch the deposit address for a currency associated with self account
        :see: 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()
        currency = self.currency(code)
        rawNetwork = self.safe_string_upper(params, 'network')
        params = self.omit(params, 'network')
        request: dict = {
            'currency': currency['id'],  # todo: currencies have network-junctions
        }
        response = await self.privateWalletGetDepositAddress(self.extend(request, params))
        #
        #    {
        #        "currency": "XRP",
        #        "address": "rHcFoo6a9qT5NHiVn1THQRhsEGcxtYCV4d 391331007",
        #        "multichain_addresses": [
        #            {
        #                "chain": "XRP",
        #                "address": "rHcFoo6a9qT5NHiVn1THQRhsEGcxtYCV4d",
        #                "payment_id": "391331007",
        #                "payment_name": "Tag",
        #                "obtain_failed": 0
        #            }
        #        ]
        #    }
        #
        currencyId = self.safe_string(response, 'currency')
        code = self.safe_currency_code(currencyId)
        networkId = self.network_code_to_id(rawNetwork, code)
        network = None
        tag = None
        address = None
        if networkId is not None:
            addresses = self.safe_value(response, 'multichain_addresses')
            for i in range(0, len(addresses)):
                entry = addresses[i]
                entryNetwork = self.safe_string(entry, 'chain')
                if networkId == entryNetwork:
                    obtainFailed = self.safe_integer(entry, 'obtain_failed')
                    if obtainFailed:
                        break
                    address = self.safe_string(entry, 'address')
                    tag = self.safe_string(entry, 'payment_id')
                    network = self.network_id_to_code(networkId, code)
                    break
        else:
            addressField = self.safe_string(response, 'address')
            if addressField is not None:
                if addressField.find('New address is being generated for you, please wait') >= 0:
                    raise BadResponse(self.id + ' ' + 'New address is being generated for you, please wait a few seconds and try again to get the address.')
                if addressField.find(' ') >= 0:
                    splitted = addressField.split(' ')
                    address = splitted[0]
                    tag = splitted[1]
                else:
                    address = addressField
        self.check_address(address)
        return {
            'info': response,
            'code': code,  # kept here for backward-compatibility, but will be removed soon
            'currency': code,
            'address': address,
            'tag': tag,
            'network': network,
        }

    async def fetch_trading_fee(self, symbol: str, params={}) -> TradingFeeInterface:
        """
        fetch the trading fees for a market
        :see: 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
        :see: 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
        :see: 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
        :see: 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]
                result['networks'][chainKey] = {
                    '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
        :see: https://www.gate.io/docs/developers/apiv4/en/#query-account-book-2
        :see: 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
        :see: https://www.gate.io/docs/developers/apiv4/en/#retrieve-order-book
        :see: https://www.gate.io/docs/developers/apiv4/en/#futures-order-book
        :see: https://www.gate.io/docs/developers/apiv4/en/#futures-order-book-2
        :see: 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
        :see: https://www.gate.io/docs/developers/apiv4/en/#get-details-of-a-specifc-order
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-futures-tickers
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-futures-tickers-2
        :see: 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,
            '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
        :see: https://www.gate.io/docs/developers/apiv4/en/#get-details-of-a-specifc-order
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-futures-tickers
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-futures-tickers-2
        :see: 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
        """
        await self.load_markets()
        symbol = self.safe_string(params, 'symbol')
        params = self.omit(params, 'symbol')
        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 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
        #     }
        #
        result: dict = {
            'info': response,
        }
        isolated = marginMode == 'margin'
        data = response
        if 'balances' in data:  # True for cross_margin
            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
        :see: https://www.gate.io/docs/developers/apiv4/en/#market-candlesticks       # spot
        :see: https://www.gate.io/docs/developers/apiv4/en/#get-futures-candlesticks  # swap
        :see: https://www.gate.io/docs/developers/apiv4/en/#market-candlesticks       # future
        :see: 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']:
            maxLimit = 1999
            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
        :see: 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
        :returns dict[]: a list of `funding rate structures <https://docs.ccxt.com/#/?id=funding-rate-history-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchFundingRateHistory() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        if not market['swap']:
            raise BadSymbol(self.id + ' fetchFundingRateHistory() supports swap contracts only')
        request, query = self.prepare_request(market, None, params)
        if limit is not None:
            request['limit'] = limit
        response = await self.publicFuturesGetSettleFundingRate(self.extend(request, query))
        #
        #     {
        #         "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
        :see: https://www.gate.io/docs/developers/apiv4/en/#retrieve-market-trades
        :see: https://www.gate.io/docs/developers/apiv4/en/#futures-trading-history
        :see: https://www.gate.io/docs/developers/apiv4/en/#futures-trading-history-2
        :see: 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
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-personal-trading-history
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-personal-trading-history-2
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-personal-trading-history-3
        :see: 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
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-personal-trading-history
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-personal-trading-history-2
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-personal-trading-history-3
        :see: 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 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=trade-structure>`
        """
        await self.load_markets()
        paginate = False
        paginate, params = self.handle_option_and_params(params, 'fetchMyTrades', 'paginate')
        if paginate:
            return await self.fetch_paginated_call_dynamic('fetchMyTrades', symbol, since, limit, params)
        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-stop
            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
        #
        #     {
        #         "id": "1334253759",
        #         "create_time": "1626342738",
        #         "create_time_ms": "1626342738331.497000",
        #         "currency_pair": "BTC_USDT",
        #         "side": "sell",
        #         "amount": "0.0022",
        #         "price": "32452.16"
        #     }
        #
        # 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 = self.safe_timestamp_2(trade, 'time', 'create_time')
        timestamp = self.safe_integer(trade, 'create_time_ms', timestamp)
        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.safe_string(trade, 'gt_fee')
        pointFee = 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
        :see: 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 int [params.until]: end time in ms
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param boolean [params.paginate]: default False, when True will automatically paginate by calling self endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/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
        :see: 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={}):
        """
        make a withdrawal
        :see: 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
        networks = self.safe_value(self.options, 'networks', {})
        network = self.safe_string_upper(params, 'network')  # self line allows the user to specify either ERC20 or ETH
        network = self.safe_string_lower(networks, network, network)  # handle ETH>ERC20 alias
        if network is not None:
            request['chain'] = network
            params = self.omit(params, 'network')
        else:
            request['chain'] = currency['id']  # todo: currencies have network-junctions
        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:
        #
        # deposits
        #
        #    {
        #        "id": "d33361395",
        #        "currency": "USDT_TRX",
        #        "address": "TErdnxenuLtXfnMafLbfappYdHtnXQ5U4z",
        #        "amount": "100",
        #        "txid": "ae9374de34e558562fe18cbb1bf9ab4d9eb8aa7669d65541c9fa2a532c1474a0",
        #        "timestamp": "1626345819",
        #        "status": "DONE",
        #        "memo": ""
        #    }
        #
        # withdraw
        #
        #    {
        #        "id": "w13389675",
        #        "currency": "USDT",
        #        "amount": "50",
        #        "address": "TUu2rLFrmzUodiWfYki7QCNtv1akL682p1",
        #        "memo": null
        #    }
        #
        #     {
        #         "currency":"usdt",
        #         "address":"0x01b0A9b7b4CdE774AF0f3E47CB4f1c2CCdBa0806",
        #         "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(transaction, 'fee')
        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
        :see: https://www.gate.io/docs/developers/apiv4/en/#create-an-order
        :see: https://www.gate.io/docs/developers/apiv4/en/#create-a-price-triggered-order
        :see: https://www.gate.io/docs/developers/apiv4/en/#create-a-futures-order
        :see: https://www.gate.io/docs/developers/apiv4/en/#create-a-price-triggered-order-2
        :see: https://www.gate.io/docs/developers/apiv4/en/#create-a-futures-order-2
        :see: https://www.gate.io/docs/developers/apiv4/en/#create-a-price-triggered-order-3
        :see: 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]: *ignored in "market" orders* the price at which the order is to be fullfilled at in units of the quote currency
        :param dict [params]:  extra parameters specific to the exchange API endpoint
        :param float [params.stopPrice]: 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
        :returns dict|None: `An order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        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
        isStopOrder = isStopLossOrder or isTakeProfitOrder
        nonTriggerOrder = not isStopOrder 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)

    async def create_orders(self, orders: List[OrderRequest], params={}):
        """
        create a list of trade orders
        :see: https://www.gate.io/docs/developers/apiv4/en/#get-a-single-order-2
        :see: https://www.gate.io/docs/developers/apiv4/en/#create-a-batch-of-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
        :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        ordersRequests = []
        orderSymbols = []
        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')
        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
        isStopOrder = 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 isStopOrder 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'] = price  # set to 0 for market orders
                else:
                    request['price'] = 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'
                    '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': 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 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
        :see: 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
        :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        if not market['spot']:
            raise NotSupported(self.id + ' createMarketBuyOrderWithCost() supports spot orders only')
        params['createMarketBuyOrderRequiresPrice'] = False
        return await self.create_order(symbol, 'market', 'buy', cost, None, params)

    async def 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
        :see: https://www.gate.io/docs/developers/apiv4/en/#amend-an-order
        :see: 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 fullfilled, in units of the base currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        marketType, query = self.handle_market_type_and_params('editOrder', market, params)
        account = self.convert_type_to_account(marketType)
        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': 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'] = Precise.string_neg(self.amount_to_precision(symbol, amount))
                else:
                    request['size'] = self.amount_to_precision(symbol, amount)
        if price is not None:
            request['price'] = self.price_to_precision(symbol, price)
        response = None
        if market['spot']:
            response = await self.privateSpotPatchOrdersOrderId(self.extend(request, query))
        else:
            request['settle'] = market['settleId']
            response = await self.privateFuturesPutSettleOrdersOrderId(self.extend(request, query))
        #
        #     {
        #         "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"
        #    }
        #
        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',
            })
        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:
            fees.append({
                'currency': 'GT',
                'cost': gtFee,
            })
        fee = self.safe_string(order, 'fee')
        if fee:
            fees.append({
                'currency': self.safe_currency_code(self.safe_string(order, 'fee_currency')),
                'cost': fee,
            })
        rebate = self.safe_string(order, 'rebated_fee')
        if rebate:
            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,
            'stopPrice': triggerPrice,
            '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)

    async def fetch_order(self, id: str, symbol: Str = None, params={}):
        """
        Retrieves information on an order
        :see: https://www.gate.io/docs/developers/apiv4/en/#get-a-single-order
        :see: https://www.gate.io/docs/developers/apiv4/en/#get-a-single-order-2
        :see: https://www.gate.io/docs/developers/apiv4/en/#get-a-single-order-3
        :see: 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.stop]: 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
        :returns: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        stop = self.safe_value_2(params, 'is_stop_order', 'stop', False)
        params = self.omit(params, ['is_stop_order', 'stop'])
        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
        market = None if (symbol is None) else self.market(symbol)
        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, stop, query)
        request['order_id'] = orderId
        response = None
        if type == 'spot' or type == 'margin':
            if stop:
                response = await self.privateSpotGetPriceOrdersOrderId(self.extend(request, requestParams))
            else:
                response = await self.privateSpotGetOrdersOrderId(self.extend(request, requestParams))
        elif type == 'swap':
            if stop:
                response = await self.privateFuturesGetSettlePriceOrdersOrderId(self.extend(request, requestParams))
            else:
                response = await self.privateFuturesGetSettleOrdersOrderId(self.extend(request, requestParams))
        elif type == 'future':
            if stop:
                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
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-all-open-orders
        :param str symbol: unified market symbol
        :param int [since]: the earliest time in ms to fetch open orders for
        :param int [limit]: the maximum number of  open orders structures to retrieve
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :param bool [params.stop]: True for fetching stop 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
        :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
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-orders
        :see: https://www.gate.io/docs/developers/apiv4/en/#retrieve-running-auto-order-list
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-futures-orders
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-all-auto-orders
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-futures-orders-2
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-all-auto-orders-2
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-options-orders
        :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.stop]: True for fetching stop 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
        :returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
        """
        return await self.fetch_orders_by_status('finished', symbol, since, limit, params)

    async def fetch_orders_by_status(self, status, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
        await self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
            symbol = market['symbol']
        stop = self.safe_value(params, 'stop')
        params = self.omit(params, 'stop')
        type, query = self.handle_market_type_and_params('fetchOrdersByStatus', market, params)
        spot = (type == 'spot') or (type == 'margin')
        request, requestParams = self.multi_order_spot_prepare_request(market, stop, query) if spot else self.prepare_request(market, type, query)
        if status == 'closed':
            status = 'finished'
        request['status'] = status
        if limit is not None:
            request['limit'] = limit
        if since is not None and spot:
            request['from'] = self.parse_to_int(since / 1000)
        openSpotOrders = spot and (status == 'open') and not stop
        response = None
        if type == 'spot' or type == 'margin':
            if openSpotOrders:
                response = await self.privateSpotGetOpenOrders(self.extend(request, requestParams))
            elif stop:
                response = await self.privateSpotGetPriceOrders(self.extend(request, requestParams))
            else:
                response = await self.privateSpotGetOrders(self.extend(request, requestParams))
        elif type == 'swap':
            if stop:
                response = await self.privateFuturesGetSettlePriceOrders(self.extend(request, requestParams))
            else:
                response = await self.privateFuturesGetSettleOrders(self.extend(request, requestParams))
        elif type == 'future':
            if stop:
                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 stop
        #
        #    [
        #        {
        #            "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
        :see: https://www.gate.io/docs/developers/apiv4/en/#cancel-a-single-order
        :see: https://www.gate.io/docs/developers/apiv4/en/#cancel-a-single-order-2
        :see: https://www.gate.io/docs/developers/apiv4/en/#cancel-a-single-order-3
        :see: 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.stop]: True if the order to be cancelled is a trigger order
        :returns: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        market = None if (symbol is None) else self.market(symbol)
        stop = self.safe_value_2(params, 'is_stop_order', 'stop', False)
        params = self.omit(params, ['is_stop_order', 'stop'])
        type, query = self.handle_market_type_and_params('cancelOrder', market, params)
        request, requestParams = self.spot_order_prepare_request(market, stop, 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 stop:
                response = await self.privateSpotDeletePriceOrdersOrderId(self.extend(request, requestParams))
            else:
                response = await self.privateSpotDeleteOrdersOrderId(self.extend(request, requestParams))
        elif type == 'swap':
            if stop:
                response = await self.privateFuturesDeleteSettlePriceOrdersOrderId(self.extend(request, requestParams))
            else:
                response = await self.privateFuturesDeleteSettleOrdersOrderId(self.extend(request, requestParams))
        elif type == 'future':
            if stop:
                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_all_orders(self, symbol: Str = None, params={}):
        """
        cancel all open orders
        :see: https://www.gate.io/docs/developers/apiv4/en/#cancel-all-open-orders-in-specified-currency-pair
        :see: https://www.gate.io/docs/developers/apiv4/en/#cancel-all-open-orders-matched
        :see: https://www.gate.io/docs/developers/apiv4/en/#cancel-all-open-orders-matched-2
        :see: 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
        :returns dict[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        market = None if (symbol is None) else self.market(symbol)
        stop = 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, stop, query) if (type == 'spot') else self.prepare_request(market, type, query)
        response = None
        if type == 'spot' or type == 'margin':
            if stop:
                response = await self.privateSpotDeletePriceOrders(self.extend(request, requestParams))
            else:
                response = await self.privateSpotDeleteOrders(self.extend(request, requestParams))
        elif type == 'swap':
            if stop:
                response = await self.privateFuturesDeleteSettlePriceOrders(self.extend(request, requestParams))
            else:
                response = await self.privateFuturesDeleteSettleOrders(self.extend(request, requestParams))
        elif type == 'future':
            if stop:
                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
        :see: 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
        :see: https://www.gate.io/docs/developers/apiv4/en/#update-position-leverage
        :see: 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
        :see: https://www.gate.io/docs/developers/apiv4/en/#get-single-position
        :see: https://www.gate.io/docs/developers/apiv4/en/#get-single-position-2
        :see: 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={}):
        """
        fetch all open positions
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-all-positions-of-a-user
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-all-positions-of-a-user-2
        :see: 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
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-all-futures-contracts
        :see: 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
        :see: 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):
        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)),
                '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),
                '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
        :see: 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
        :see: https://www.gate.io/docs/developers/apiv4/en/#cross-margin-repayments
        :param str code: unified currency code of the currency to repay
        :param float amount: the amount to repay
        :param str symbol: unified market symbol, required for isolated margin
        :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),
        }
        response = await self.privateMarginPostCrossRepayments(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"
        #         }
        #     ]
        #
        response = self.safe_value(response, 0)
        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
        :see: https://www.gate.io/docs/developers/apiv4/en/#marginuni
        :param str code: unified currency code of the currency to borrow
        :param float amount: the amount to borrow
        :param str symbol: unified market symbol, required for isolated margin
        :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
        :see: https://www.gate.io/docs/apiv4/en/#create-a-cross-margin-borrow-loan
        :param str code: unified currency code of the currency to borrow
        :param float amount: the amount to borrow
        :param str symbol: unified market symbol, required for isolated margin
        :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 = 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,
        }

    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))
        if 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)
                    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')
            timestamp = self.seconds()
            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
        :see: https://www.gate.io/docs/developers/apiv4/en/#update-position-margin
        :see: 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
        :see: https://www.gate.io/docs/developers/apiv4/en/#update-position-margin
        :see: 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
        :see: 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(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
        :see: 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
        :see: 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={}):
        """
        fetch the history of changes, actions done by the user or operations that altered the balance of the user
        :see: https://www.gate.io/docs/developers/apiv4/en/#query-account-book
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-margin-account-balance-change-history
        :see: https://www.gate.io/docs/developers/apiv4/en/#query-account-book-2
        :see: https://www.gate.io/docs/developers/apiv4/en/#query-account-book-3
        :see: 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 [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
        :returns dict: a `ledger structure <https://docs.ccxt.com/#/?id=ledger-structure>`
        """
        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):
        #
        # 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')
        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 {
            '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,
            'info': item,
        }

    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
        :see: 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
        :see: 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
        :see: 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
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-liquidation-history
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-liquidation-history-2
        :see: 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
        :see: 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
        :see: https://www.gate.io/docs/developers/apiv4/en/#create-a-futures-order
        :see: https://www.gate.io/docs/developers/apiv4/en/#create-a-futures-order-2
        :see: 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
        :see: https://www.gate.io/docs/developers/apiv4/en/#get-unified-account-information
        :see: https://www.gate.io/docs/developers/apiv4/en/#get-detail-of-lending-market
        :see: 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
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-lending-markets
        :see: 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
        :see: 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
        :see: https://www.gate.io/docs/developers/apiv4/en/#list-all-the-contracts-with-specified-underlying-and-expiration-time
        :param str currency: 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
        :see: https://www.gate.io/docs/developers/apiv4/#list-position-close-history
        :see: 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 offset: list offset, starting from 0
        :param str side: long or short
        :param str 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"}
        #
        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
