# -*- 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
import hashlib
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import AuthenticationError
from ccxt.base.errors import PermissionDenied
from ccxt.base.errors import AccountNotEnabled
from ccxt.base.errors import AccountSuspended
from ccxt.base.errors import ArgumentsRequired
from ccxt.base.errors import BadRequest
from ccxt.base.errors import BadSymbol
from ccxt.base.errors import InsufficientFunds
from ccxt.base.errors import InvalidOrder
from ccxt.base.errors import OrderNotFound
from ccxt.base.errors import OrderImmediatelyFillable
from ccxt.base.errors import NotSupported
from ccxt.base.errors import RateLimitExceeded
from ccxt.base.errors import ExchangeNotAvailable
from ccxt.base.decimal_to_precision import TICK_SIZE
from ccxt.base.precise import Precise


class gate(Exchange):

    def describe(self):
        return self.deep_extend(super(gate, self).describe(), {
            'id': 'gate',
            'name': 'Gate.io',
            'countries': ['KR'],
            'rateLimit': 10 / 3,  # 300 requests per second or 3.33ms
            '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/apiv4/en/index.html',
                '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',
                    },
                    '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',
                    },
                },
                'test': {
                    'public': {
                        'futures': 'https://fx-api-testnet.gateio.ws/api/v4',
                        'delivery': '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',
                    },
                },
                'referral': {
                    'url': 'https://www.gate.io/ref/2436035',
                    'discount': 0.2,
                },
            },
            'has': {
                'CORS': None,
                'spot': True,
                'margin': True,
                'swap': True,
                'future': True,
                'option': None,
                'borrowMargin': True,
                'cancelAllOrders': True,
                'cancelOrder': True,
                'createMarketOrder': False,
                'createOrder': True,
                'createPostOnlyOrder': True,
                'createStopLimitOrder': True,
                'createStopMarketOrder': False,
                'createStopOrder': True,
                'fetchBalance': True,
                'fetchBorrowRate': False,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchBorrowRates': False,
                'fetchClosedOrders': True,
                'fetchCurrencies': True,
                'fetchDepositAddress': True,
                'fetchDeposits': True,
                'fetchFundingHistory': True,
                'fetchFundingRate': True,
                'fetchFundingRateHistory': True,
                'fetchFundingRates': True,
                'fetchIndexOHLCV': True,
                'fetchLeverage': False,
                'fetchLeverageTiers': True,
                'fetchMarginMode': False,
                'fetchMarketLeverageTiers': 'emulated',
                'fetchMarkets': True,
                'fetchMarkOHLCV': True,
                'fetchMyTrades': True,
                'fetchNetworkDepositAddress': True,
                'fetchOHLCV': True,
                'fetchOpenInterestHistory': False,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchPositionMode': False,
                'fetchPositions': True,
                'fetchPremiumIndexOHLCV': False,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': False,
                'fetchTrades': True,
                'fetchTradingFee': True,
                'fetchTradingFees': True,
                'fetchTransactionFees': True,
                'fetchWithdrawals': True,
                'repayMargin': True,
                'setLeverage': True,
                'setMarginMode': False,
                'transfer': True,
                'withdraw': True,
            },
            'api': {
                'public': {
                    'wallet': {
                        'get': {
                            'currency_chains': 1.5,
                        },
                    },
                    'spot': {
                        'get': {
                            'currencies': 1,
                            'currencies/{currency}': 1,
                            'currency_pairs': 1,
                            'currency_pairs/{currency_pair}': 1,
                            'tickers': 1,
                            'order_book': 1,
                            'trades': 1,
                            'candlesticks': 1,
                        },
                    },
                    'margin': {
                        'get': {
                            'currency_pairs': 1,
                            'currency_pairs/{currency_pair}': 1,
                            'cross/currencies': 1,
                            'cross/currencies/{currency}': 1,
                            'funding_book': 1,
                        },
                    },
                    'futures': {
                        'get': {
                            '{settle}/contracts': 1.5,
                            '{settle}/contracts/{contract}': 1.5,
                            '{settle}/order_book': 1.5,
                            '{settle}/trades': 1.5,
                            '{settle}/candlesticks': 1.5,
                            '{settle}/tickers': 1.5,
                            '{settle}/funding_rate': 1.5,
                            '{settle}/insurance': 1.5,
                            '{settle}/contract_stats': 1.5,
                            '{settle}/liq_orders': 1.5,
                        },
                    },
                    'delivery': {
                        'get': {
                            '{settle}/contracts': 1.5,
                            '{settle}/contracts/{contract}': 1.5,
                            '{settle}/order_book': 1.5,
                            '{settle}/trades': 1.5,
                            '{settle}/candlesticks': 1.5,
                            '{settle}/tickers': 1.5,
                            '{settle}/insurance': 1.5,
                        },
                    },
                    'options': {
                        'get': {
                            'underlyings': 1.5,
                            'expirations': 1.5,
                            'contracts': 1.5,
                            'contracts/{contract}': 1.5,
                            'settlements': 1.5,
                            'settlements/{contract}': 1.5,
                            'order_book': 1.5,
                            'tickers': 1.5,
                            'underlying/tickers/{underlying}': 1.5,
                            'candlesticks': 1.5,
                            'underlying/candlesticks': 1.5,
                            'trades': 1.5,
                        },
                    },
                },
                'private': {
                    'withdrawals': {
                        'post': {
                            'withdrawals': 3000,  # 3000 = 10 seconds
                        },
                        'delete': {
                            'withdrawals/{withdrawal_id}': 300,
                        },
                    },
                    'wallet': {
                        'get': {
                            'deposit_address': 300,
                            'withdrawals': 300,
                            'deposits': 300,
                            'sub_account_transfers': 300,
                            'withdraw_status': 300,
                            'sub_account_balances': 300,
                            'fee': 300,
                            'total_balance': 300,
                        },
                        'post': {
                            'transfers': 300,
                            'sub_account_transfers': 300,
                        },
                    },
                    'subAccounts': {
                        'get': {
                            'sub_accounts': 1,
                            'sub_accounts/{user_id}': 1,
                            'sub_accounts/{user_id}/keys': 1,
                            'sub_accounts/{user_id}/keys/{key}': 1,
                        },
                        'post': {
                            'sub_accounts': 1,
                            'sub_accounts/{user_id}/keys': 1,
                            'sub_accounts/{user_id}/lock': 1,
                            'sub_accounts/{user_id}/unlock': 1,
                        },
                        'put': {
                            'sub_accounts/{user_id}/keys/{key}': 1,
                        },
                        'delete': {
                            'sub_accounts/{user_id}/keys/{key}': 1,
                        },
                    },
                    'spot': {
                        'get': {
                            'accounts': 1,
                            'open_orders': 1,
                            'orders': 1,
                            'orders/{order_id}': 1,
                            'my_trades': 1,
                            'price_orders': 1,
                            'price_orders/{order_id}': 1,
                        },
                        'post': {
                            'batch_orders': 1,
                            'orders': 1,
                            'cancel_batch_orders': 1,
                            'price_orders': 1,
                        },
                        'delete': {
                            'orders': 1,
                            'orders/{order_id}': 1,
                            'price_orders': 1,
                            'price_orders/{order_id}': 1,
                        },
                    },
                    'margin': {
                        'get': {
                            'accounts': 1.5,
                            'account_book': 1.5,
                            'funding_accounts': 1.5,
                            'loans': 1.5,
                            'loans/{loan_id}': 1.5,
                            'loans/{loan_id}/repayment': 1.5,
                            'loan_records': 1.5,
                            'loan_records/{load_record_id}': 1.5,
                            'auto_repay': 1.5,
                            'transferable': 1.5,
                            'cross/accounts': 1.5,
                            'cross/account_book': 1.5,
                            'cross/loans': 1.5,
                            'cross/loans/{loan_id}': 1.5,
                            'cross/loans/repayments': 1.5,
                            'cross/transferable': 1.5,
                            'loan_records/{loan_record_id}': 1.5,
                            'borrowable': 1.5,
                            'cross/repayments': 1.5,
                            'cross/borrowable': 1.5,
                        },
                        'post': {
                            'loans': 1.5,
                            'merged_loans': 1.5,
                            'loans/{loan_id}/repayment': 1.5,
                            'auto_repay': 1.5,
                            'cross/loans': 1.5,
                            'cross/loans/repayments': 1.5,
                            'cross/repayments': 1.5,
                        },
                        'patch': {
                            'loans/{loan_id}': 1.5,
                            'loan_records/{loan_record_id}': 1.5,
                        },
                        'delete': {
                            'loans/{loan_id}': 1.5,
                        },
                    },
                    'futures': {
                        'get': {
                            '{settle}/accounts': 1.5,
                            '{settle}/account_book': 1.5,
                            '{settle}/positions': 1.5,
                            '{settle}/positions/{contract}': 1.5,
                            '{settle}/orders': 1.5,
                            '{settle}/orders/{order_id}': 1.5,
                            '{settle}/my_trades': 1.5,
                            '{settle}/position_close': 1.5,
                            '{settle}/liquidates': 1.5,
                            '{settle}/price_orders': 1.5,
                            '{settle}/price_orders/{order_id}': 1.5,
                            '{settle}/dual_comp/positions/{contract}': 1.5,
                        },
                        'post': {
                            '{settle}/positions/{contract}/margin': 1.5,
                            '{settle}/positions/{contract}/leverage': 1.5,
                            '{settle}/positions/{contract}/risk_limit': 1.5,
                            '{settle}/dual_mode': 1.5,
                            '{settle}/dual_comp/positions/{contract}': 1.5,
                            '{settle}/dual_comp/positions/{contract}/margin': 1.5,
                            '{settle}/dual_comp/positions/{contract}/leverage': 1.5,
                            '{settle}/dual_comp/positions/{contract}/risk_limit': 1.5,
                            '{settle}/orders': 1.5,
                            '{settle}/price_orders': 1.5,
                        },
                        'delete': {
                            '{settle}/orders': 1.5,
                            '{settle}/orders/{order_id}': 1.5,
                            '{settle}/price_orders': 1.5,
                            '{settle}/price_orders/{order_id}': 1.5,
                        },
                    },
                    'delivery': {
                        'get': {
                            '{settle}/accounts': 1.5,
                            '{settle}/account_book': 1.5,
                            '{settle}/positions': 1.5,
                            '{settle}/positions/{contract}': 1.5,
                            '{settle}/orders': 1.5,
                            '{settle}/orders/{order_id}': 1.5,
                            '{settle}/my_trades': 1.5,
                            '{settle}/position_close': 1.5,
                            '{settle}/liquidates': 1.5,
                            '{settle}/price_orders': 1.5,
                            '{settle}/price_orders/{order_id}': 1.5,
                            '{settle}/settlements': 1.5,
                        },
                        'post': {
                            '{settle}/positions/{contract}/margin': 1.5,
                            '{settle}/positions/{contract}/leverage': 1.5,
                            '{settle}/positions/{contract}/risk_limit': 1.5,
                            '{settle}/orders': 1.5,
                            '{settle}/price_orders': 1.5,
                        },
                        'delete': {
                            '{settle}/orders': 1.5,
                            '{settle}/orders/{order_id}': 1.5,
                            '{settle}/price_orders': 1.5,
                            '{settle}/price_orders/{order_id}': 1.5,
                        },
                    },
                    'options': {
                        'get': {
                            'accounts': 1.5,
                            'account_book': 1.5,
                            'positions': 1.5,
                            'positions/{contract}': 1.5,
                            'position_close': 1.5,
                            'orders': 1.5,
                            'orders/{order_id}': 1.5,
                            'my_trades': 1.5,
                        },
                        'post': {
                            'orders': 1.5,
                        },
                        'delete': {
                            'orders': 1.5,
                            'orders/{order_id}': 1.5,
                        },
                    },
                },
            },
            'timeframes': {
                '10s': '10s',
                '1m': '1m',
                '5m': '5m',
                '15m': '15m',
                '30m': '30m',
                '1h': '1h',
                '4h': '4h',
                '8h': '8h',
                '1d': '1d',
                '7d': '7d',
                '1w': '7d',
            },
            # copied from gatev2
            'commonCurrencies': {
                '88MPH': 'MPH',
                'AXIS': 'Axis DeFi',
                'BIFI': 'Bitcoin File',
                'BOX': 'DefiBox',
                'BTCBEAR': 'BEAR',
                'BTCBULL': 'BULL',
                '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
                'RAI': 'Rai Reflex Index',  # conflict with RAI Finance
                'SBTC': 'Super Bitcoin',
                'TNC': 'Trinity Network Credit',
                'TON': 'TONToken',
                'VAI': 'VAIOT',
            },
            'requiredCredentials': {
                'apiKey': True,
                'secret': True,
            },
            'headers': {
                'X-Gate-Channel-Id': 'ccxt',
            },
            'options': {
                'createOrder': {
                    'expiration': 86400,  # for conditional orders
                },
                'networks': {
                    'TRC20': 'TRX',
                    'ERC20': 'ETH',
                    'BEP20': 'BSC',
                },
                'timeInForce': {
                    'GTC': 'gtc',
                    'IOC': 'ioc',
                    'PO': 'poc',
                    'POC': 'poc',
                },
                'accountsByType': {
                    'funding': 'spot',
                    'spot': 'spot',
                    'margin': 'margin',
                    'cross_margin': 'cross_margin',
                    'cross': 'cross_margin',
                    'isolated': 'margin',
                    'swap': 'futures',
                    'future': 'delivery',
                    'futures': 'futures',
                    'delivery': 'delivery',
                },
                'defaultType': 'spot',
                'swap': {
                    'fetchMarkets': {
                        'settlementCurrencies': ['usdt', 'btc'],
                    },
                },
                'future': {
                    'fetchMarkets': {
                        'settlementCurrencies': ['usdt', 'btc'],
                    },
                },
            },
            '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/apiv4/en/index.html#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,
                    '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"}
                },
            },
            'broad': {},
        })

    async def fetch_markets(self, params={}):
        """
        retrieves data on all markets for gate
        :param dict params: extra parameters specific to the exchange api endpoint
        :returns [dict]: an array of objects representing market data
        """
        result = []
        type, query = self.handle_market_type_and_params('fetchMarkets', None, params)
        if type == 'spot' or type == 'margin':
            result = await self.fetch_spot_markets(query)
        if type == 'swap' or type == 'future':
            result = await self.fetch_contract_markets(query)  # futures and swaps
        if type == 'option':
            result = await self.fetch_option_markets(query)
        resultLength = len(result)
        if resultLength == 0:
            raise ExchangeError(self.id + " does not support '" + type + "' type, set exchange.options['defaultType'] to " + "'spot', 'margin', 'swap', 'future' or 'option'")  # eslint-disable-line quotes
        return result

    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,
                    },
                },
                '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]
            query = params
            query['settle'] = settleId
            response = await self.publicFuturesGetSettleContracts(query)
            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]
            query = params
            query['settle'] = settleId
            response = await self.publicDeliveryGetSettleContracts(query)
            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'),
                '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,
                },
            },
            '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 = 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 i in range(0, len(response)):
                market = response[i]
                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'),
                        '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,
                        },
                    },
                    '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 = {}
        if market is not None:
            if market['contract']:
                request['contract'] = market['id']
                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 = {}
        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 = {
            '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={}):
        """
        fetches all available currencies on an exchange
        :param dict params: extra parameters specific to the gate 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
        #    }
        #
        result = {}
        for i in range(0, len(response)):
            entry = response[i]
            currencyId = self.safe_string(entry, 'currency')
            currencyIdLower = self.safe_string_lower(entry, 'currency')
            code = self.safe_currency_code(currencyId)
            delisted = self.safe_value(entry, 'delisted')
            withdrawDisabled = self.safe_value(entry, 'withdraw_disabled', False)
            depositDisabled = self.safe_value(entry, 'deposit_disabled', False)
            tradeDisabled = self.safe_value(entry, 'trade_disabled', False)
            withdrawEnabled = not withdrawDisabled
            depositEnabled = not depositDisabled
            tradeEnabled = not tradeDisabled
            listed = not delisted
            active = listed and tradeEnabled and withdrawEnabled and depositEnabled
            result[code] = {
                'id': currencyId,
                'lowerCaseId': currencyIdLower,
                'name': None,
                'code': code,
                'precision': self.parse_number('1e-6'),
                'info': entry,
                'active': active,
                'deposit': depositEnabled,
                'withdraw': withdrawEnabled,
                'fee': None,
                'fees': [],
                'limits': self.limits,
            }
        return result

    async def fetch_funding_rate(self, symbol, params={}):
        """
        fetch the current funding rate
        :param str symbol: unified market symbol
        :param dict params: extra parameters specific to the gate api endpoint
        :returns dict: a `funding rate structure <https://docs.ccxt.com/en/latest/manual.html#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=None, params={}):
        """
        fetch the funding rate for multiple markets
        :param [str]|None symbols: list of unified market symbols
        :param dict params: extra parameters specific to the gate api endpoint
        :returns dict: a dictionary of `funding rates structures <https://docs.ccxt.com/en/latest/manual.html#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=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)
        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, params={}):
        await self.load_markets()
        currency = self.currency(code)
        request = {
            'currency': currency['id'],
        }
        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 = {}
        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, params={}):
        """
        fetch the deposit address for a currency associated with self account
        :param str code: unified currency code
        :param dict params: extra parameters specific to the gate api endpoint
        :returns dict: an `address structure <https://docs.ccxt.com/en/latest/manual.html#address-structure>`
        """
        await self.load_markets()
        currency = self.currency(code)
        request = {
            'currency': currency['id'],
        }
        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)
        addressField = self.safe_string(response, 'address')
        tag = None
        address = None
        if addressField is not None:
            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': None,
        }

    async def fetch_trading_fee(self, symbol, params={}):
        """
        fetch the trading fees for a market
        :param str symbol: unified market symbol
        :param dict params: extra parameters specific to the gate api endpoint
        :returns dict: a `fee structure <https://docs.ccxt.com/en/latest/manual.html#fee-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request = {
            '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={}):
        """
        fetch the trading fees for multiple markets
        :param dict params: extra parameters specific to the gate api endpoint
        :returns dict: a dictionary of `fee structures <https://docs.ccxt.com/en/latest/manual.html#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 = {}
        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=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"
        #    }
        #
        contract = self.safe_value(market, 'contract')
        takerKey = 'futures_taker_fee' if contract else 'taker_fee'
        makerKey = 'futures_maker_fee' if contract else 'maker_fee'
        return {
            'info': info,
            'symbol': self.safe_string(market, 'symbol'),
            'maker': self.safe_number(info, makerKey),
            'taker': self.safe_number(info, takerKey),
        }

    async def fetch_transaction_fees(self, codes=None, params={}):
        """
        fetch transaction 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 gate api endpoint
        :returns dict: a list of `fee structures <https://docs.ccxt.com/en/latest/manual.html#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 = {}
        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 i in range(0, len(chainKeys)):
                    chainKey = chainKeys[i]
                    withdrawFees[chainKey] = self.parse_number(withdrawFixOnChains[chainKey])
            result[code] = {
                'withdraw': withdrawFees,
                'deposit': None,
                'info': entry,
            }
        return result

    async def fetch_funding_history(self, symbol=None, since=None, limit=None, params={}):
        """
        fetch the history of funding payments paid and received on self account
        :param str|None symbol: unified market symbol
        :param int|None since: the earliest time in ms to fetch funding history for
        :param int|None limit: the maximum number of funding history structures to retrieve
        :param dict params: extra parameters specific to the gate api endpoint
        :returns dict: a `funding history structure <https://docs.ccxt.com/en/latest/manual.html#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:
            request['from'] = since / 1000
        if limit is not None:
            request['limit'] = limit
        method = self.get_supported_mapping(type, {
            'swap': 'privateFuturesGetSettleAccountBook',
            'future': 'privateDeliveryGetSettleAccountBook',
        })
        response = await getattr(self, method)(self.extend(request, requestParams))
        #
        #    [
        #        {
        #            "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):
        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=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)
        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, limit=None, params={}):
        """
        fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
        :param str symbol: unified symbol of the market to fetch the order book for
        :param int|None limit: the maximum amount of order book entries to return
        :param dict params: extra parameters specific to the gate api endpoint
        :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/en/latest/manual.html#order-book-structure>` indexed by market symbols
        """
        await self.load_markets()
        market = self.market(symbol)
        #
        #     request = {
        #         '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, None, params)
        method = self.get_supported_mapping(market['type'], {
            'spot': 'publicSpotGetOrderBook',
            'margin': 'publicSpotGetOrderBook',
            'swap': 'publicFuturesGetSettleOrderBook',
            'future': 'publicDeliveryGetSettleOrderBook',
        })
        if limit is not None:
            request['limit'] = limit  # default 10, max 100
        request['with_id'] = True
        response = await getattr(self, method)(self.extend(request, query))
        #
        # 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"]
        #         ]
        #     }
        #
        # Perpetual Swap
        #
        #     {
        #         "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, params={}):
        """
        fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
        :param str symbol: unified symbol of the market to fetch the ticker for
        :param dict params: extra parameters specific to the gate api endpoint
        :returns dict: a `ticker structure <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request, query = self.prepare_request(market, None, params)
        method = self.get_supported_mapping(market['type'], {
            'spot': 'publicSpotGetTickers',
            'margin': 'publicSpotGetTickers',
            'swap': 'publicFuturesGetSettleTickers',
            'future': 'publicDeliveryGetSettleTickers',
        })
        response = await getattr(self, method)(self.extend(request, query))
        ticker = self.safe_value(response, 0)
        return self.parse_ticker(ticker, market)

    def parse_ticker(self, ticker, market=None):
        #
        # 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"
        #     }
        #
        marketId = self.safe_string_2(ticker, 'currency_pair', 'contract')
        symbol = self.safe_symbol(marketId, market)
        last = self.safe_string(ticker, 'last')
        ask = self.safe_string(ticker, 'lowest_ask')
        bid = self.safe_string(ticker, 'highest_bid')
        high = self.safe_string(ticker, 'high_24h')
        low = self.safe_string(ticker, 'low_24h')
        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': None,
            'datetime': None,
            'high': high,
            'low': low,
            'bid': bid,
            'bidVolume': None,
            'ask': ask,
            'askVolume': None,
            '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=None, params={}):
        """
        fetches price tickers for multiple markets, statistical calculations with the information calculated over the past 24 hours each market
        :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 gate api endpoint
        :returns dict: an array of `ticker structures <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
        """
        await self.load_markets()
        type, query = self.handle_market_type_and_params('fetchTickers', None, params)
        request, requestParams = self.prepare_request(None, type, query)
        method = self.get_supported_mapping(type, {
            'spot': 'publicSpotGetTickers',
            'margin': 'publicSpotGetTickers',
            'swap': 'publicFuturesGetSettleTickers',
            'future': 'publicDeliveryGetSettleTickers',
        })
        response = await getattr(self, method)(self.extend(request, requestParams))
        return self.parse_tickers(response, symbols)

    def fetch_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')
        return account

    async def fetch_balance(self, params={}):
        """
        :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']
        method = self.get_supported_mapping(type, {
            'spot': self.get_supported_mapping(marginMode, {
                'spot': 'privateSpotGetAccounts',
                'margin': 'privateMarginGetAccounts',
                'cross_margin': 'privateMarginGetCrossAccounts',
            }),
            'funding': 'privateMarginGetFundingAccounts',
            'swap': 'privateFuturesGetSettleAccounts',
            'future': 'privateDeliveryGetSettleAccounts',
        })
        response = await getattr(self, method)(self.extend(request, requestQuery))
        contract = (type == 'swap' or type == 'future')
        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",
        #    }
        #
        result = {
            'info': response,
        }
        crossMargin = marginMode == 'cross_margin'
        margin = 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 margin and not crossMargin:
                marketId = self.safe_string(entry, 'currency_pair')
                symbol = self.safe_symbol(marketId, None, '_')
                base = self.safe_value(entry, 'base', {})
                quote = self.safe_value(entry, 'quote', {})
                baseCode = self.safe_currency_code(self.safe_string(base, 'currency'))
                quoteCode = self.safe_currency_code(self.safe_string(quote, 'currency'))
                subResult = {}
                subResult[baseCode] = self.fetch_balance_helper(base)
                subResult[quoteCode] = self.fetch_balance_helper(quote)
                result[symbol] = self.safe_balance(subResult)
            else:
                code = self.safe_currency_code(self.safe_string(entry, 'currency'))
                result[code] = self.fetch_balance_helper(entry)
        return result if (margin and not crossMargin) else self.safe_balance(result)

    async def fetch_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
        """
        fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
        :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|None since: timestamp in ms of the earliest candle to fetch
        :param int|None limit: the maximum amount of candles to fetch
        :param dict params: extra parameters specific to the gateio api endpoint
        :param str|None params['price']: "mark" or "index" for mark price and index price candles
        :param int|None params['until']: timestamp in ms of the latest candle to fetch
        :returns [[int]]: A list of candles ordered as timestamp, open, high, low, close, volume(units in quote currency)
        """
        await self.load_markets()
        market = self.market(symbol)
        price = self.safe_string(params, 'price')
        request = {}
        request, params = self.prepare_request(market, None, params)
        request['interval'] = self.timeframes[timeframe]
        method = 'publicSpotGetCandlesticks'
        maxLimit = 1000
        if market['contract']:
            maxLimit = 1999
            limit = maxLimit if (limit is None) else min(limit, maxLimit)
            if market['future']:
                method = 'publicDeliveryGetSettleCandlesticks'
            elif market['swap']:
                method = 'publicFuturesGetSettleCandlesticks'
            isMark = (price == 'mark')
            isIndex = (price == 'index')
            if isMark or isIndex:
                request['contract'] = price + '_' + market['id']
                params = self.omit(params, 'price')
        limit = maxLimit if (limit is None) else min(limit, maxLimit)
        until = self.safe_integer(params, 'until')
        if until is not None:
            until = int(until / 1000)
            params = self.omit(params, 'until')
        if since is not None:
            duration = self.parse_timeframe(timeframe)
            request['from'] = int(since / 1000)
            toTimestamp = self.sum(request['from'], limit * duration - 1)
            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 = await getattr(self, method)(self.extend(request, params))
        return self.parse_ohlcvs(response, market, timeframe, since, limit)

    async def fetch_funding_rate_history(self, symbol=None, since=None, limit=None, params={}):
        """
        fetches historical funding rate prices
        :param str|None symbol: unified symbol of the market to fetch the funding rate history for
        :param int|None since: timestamp in ms of the earliest funding rate to fetch
        :param int|None limit: the maximum amount of `funding rate structures <https://docs.ccxt.com/en/latest/manual.html?#funding-rate-history-structure>` to fetch
        :param dict params: extra parameters specific to the gate api endpoint
        :returns [dict]: a list of `funding rate structures <https://docs.ccxt.com/en/latest/manual.html?#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
        method = 'publicFuturesGetSettleFundingRate'
        response = await getattr(self, method)(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=None):
        #
        # 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
        #    ]
        #
        #
        # 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:
            # 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, since=None, limit=None, params={}):
        """
        get the list of most recent trades for a particular symbol
        :param str symbol: unified symbol of the market to fetch trades for
        :param int|None since: timestamp in ms of the earliest trade to fetch
        :param int|None limit: the maximum amount of trades to fetch
        :param dict params: extra parameters specific to the gate api endpoint
        :returns [dict]: a list of `trade structures <https://docs.ccxt.com/en/latest/manual.html?#public-trades>`
        """
        await self.load_markets()
        market = self.market(symbol)
        #
        # spot
        #
        #     request = {
        #         '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 = {
        #         '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)
        method = self.get_supported_mapping(market['type'], {
            'spot': 'publicSpotGetTrades',
            'margin': 'publicSpotGetTrades',
            'swap': 'publicFuturesGetSettleTrades',
            'future': 'publicDeliveryGetSettleTrades',
        })
        if limit is not None:
            request['limit'] = limit  # default 100, max 1000
        if since is not None and (market['contract']):
            request['from'] = int(since / 1000)
        response = await getattr(self, method)(self.extend(request, query))
        #
        # 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",
        #         }
        #     ]
        #
        return self.parse_trades(response, market, since, limit)

    async def fetch_order_trades(self, id, symbol=None, since=None, limit=None, params={}):
        """
        fetch all the trades made from a single order
        :param str id: order id
        :param str symbol: unified market symbol
        :param int|None since: the earliest time in ms to fetch trades for
        :param int|None limit: the maximum number of trades to retrieve
        :param dict params: extra parameters specific to the binance api endpoint
        :returns [dict]: a list of `trade structures <https://docs.ccxt.com/en/latest/manual.html#trade-structure>`
        """
        await self.load_markets()
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrderTrades requires a symbol argument')
        #
        #      [
        #          {
        #              "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=None, since=None, limit=None, params={}):
        """
        Fetch personal trading history
        :param str|None symbol: unified market symbol
        :param int|None since: the earliest time in ms to fetch trades for
        :param int|None limit: the maximum number of trades structures to retrieve
        :param dict params: extra parameters specific to the gate api endpoint
        :param str|None params['marginMode']: 'cross' or 'isolated' - marginMode for margin trading if not provided self.options['defaultMarginMode'] is used
        :param str|None params['type']: 'spot', 'swap', or 'future', if not provided self.options['defaultMarginMode'] is used
        :param int|None params['until']: The latest timestamp, in ms, that fetched trades were made
        :param int|None params['page']: *spot only* Page number
        :param str|None params['order_id']: *spot only* Filter trades with specified order ID. symbol is also required if self field is present
        :param str|None params['order']: *contract only* Futures order ID, return related data only if specified
        :param int|None params['offset']: *contract only* list offset, starting from 0
        :param str|None params['last_id']: *contract only* specify list staring point using the id of last record in previous list-query results
        :param int|None params['count_total']: *contract only* whether to return total number matched, default to 0(no return)
        :returns [dict]: a list of `trade structures <https://docs.ccxt.com/en/latest/manual.html#trade-structure>`
        """
        await self.load_markets()
        type = None
        marginMode = None
        request = {}
        market = self.market(symbol) if (symbol is not None) else None
        until = self.safe_integer_2(params, 'until', 'till')
        params = self.omit(params, ['until', 'till'])
        type, params = self.handle_market_type_and_params('fetchMyTrades', market, params)
        contract = (type == 'swap') or (type == 'future')
        if contract:
            request, params = self.prepare_request(market, type, params)
        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'] = int(since / 1000)
        if until is not None:
            request['to'] = int(until / 1000)
        method = self.get_supported_mapping(type, {
            'spot': 'privateSpotGetMyTrades',
            'margin': 'privateSpotGetMyTrades',
            'swap': 'privateFuturesGetSettleMyTrades',
            'future': 'privateDeliveryGetSettleMyTrades',
        })
        response = await getattr(self, method)(self.extend(request, params))
        #
        # 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"
        #         }
        #     ]
        #
        return self.parse_trades(response, market, since, limit)

    def parse_trade(self, trade, market=None):
        #
        # 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"
        #     }
        #
        id = self.safe_string(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')
        market = self.safe_market(marketId, market)
        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': 'POINT',
            })
        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=None, since=None, limit=None, params={}):
        """
        fetch all deposits made to an account
        :param str|None code: unified currency code
        :param int|None since: the earliest time in ms to fetch deposits for
        :param int|None limit: the maximum number of deposits structures to retrieve
        :param dict params: extra parameters specific to the gate api endpoint
        :returns [dict]: a list of `transaction structures <https://docs.ccxt.com/en/latest/manual.html#transaction-structure>`
        """
        await self.load_markets()
        request = {}
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        if limit is not None:
            request['limit'] = limit
        if since is not None:
            start = int(since / 1000)
            request['from'] = start
            request['to'] = self.sum(start, 30 * 24 * 60 * 60)
        response = await self.privateWalletGetDeposits(self.extend(request, params))
        return self.parse_transactions(response, currency)

    async def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
        """
        fetch all withdrawals made from an account
        :param str|None code: unified currency code
        :param int|None since: the earliest time in ms to fetch withdrawals for
        :param int|None limit: the maximum number of withdrawals structures to retrieve
        :param dict params: extra parameters specific to the gate api endpoint
        :returns [dict]: a list of `transaction structures <https://docs.ccxt.com/en/latest/manual.html#transaction-structure>`
        """
        await self.load_markets()
        request = {}
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        if limit is not None:
            request['limit'] = limit
        if since is not None:
            start = int(since / 1000)
            request['from'] = start
            request['to'] = self.sum(start, 30 * 24 * 60 * 60)
        response = await self.privateWalletGetWithdrawals(self.extend(request, params))
        return self.parse_transactions(response, currency)

    async def withdraw(self, code, amount, address, tag=None, params={}):
        """
        make a withdrawal
        :param str code: unified currency code
        :param float amount: the amount to withdraw
        :param str address: the address to withdraw to
        :param str|None tag:
        :param dict params: extra parameters specific to the gate api endpoint
        :returns dict: a `transaction structure <https://docs.ccxt.com/en/latest/manual.html#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 = {
            'currency': currency['id'],
            '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')
        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):
        statuses = {
            'PEND': 'pending',
            'REQUEST': 'pending',
            'DMOVE': 'pending',
            'CANCEL': 'failed',
            'DONE': 'ok',
            'BCODE': 'ok',  # GateCode withdrawal
        }
        return self.safe_string(statuses, status, status)

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

    def parse_transaction(self, transaction, currency=None):
        #
        # 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
        #    }
        #
        id = self.safe_string(transaction, 'id')
        type = None
        amount = self.safe_string(transaction, 'amount')
        if id[0] == 'b':
            # GateCode handling
            type = 'deposit' if Precise.string_gt(amount, '0') else 'withdrawal'
            amount = Precise.string_abs(amount)
        elif id is not None:
            type = self.parse_transaction_type(id[0])
        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')
        fee = self.safe_number(transaction, 'fee')
        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(amount),
            'network': None,
            'address': address,
            'addressTo': None,
            'addressFrom': None,
            'tag': tag,
            'tagTo': None,
            'tagFrom': None,
            'status': status,
            'type': type,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'updated': None,
            'fee': fee,
        }

    async def create_order(self, symbol, type, side, amount, price=None, params={}):
        """
        Create an order on the exchange
        :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|None 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|None params['stopPrice']: The price at which a trigger order is triggered at
        :param str|None params['timeInForce']: "GTC", "IOC", or "PO"
        :param str|None params['marginMode']: 'cross' or 'isolated' - marginMode for margin trading if not provided self.options['defaultMarginMode'] is used
        :param int|None 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|None params['text']: User defined information
        :param str|None params['account']: *spot and margin only* "spot", "margin" or "cross_margin"
        :param bool|None 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|None params['settle']: *contract only* Unified Currency Code for settle currency
        :param bool|None params['reduceOnly']: *contract only* Indicates if self order is to reduce the size of a position
        :param bool|None params['close']: *contract only* Set as True to close the position, with size set to 0
        :param bool|None 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
        :returns dict|None: `An order structure <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        await self.load_markets()
        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')
        methodTail = 'Orders'
        reduceOnly = self.safe_value(params, 'reduceOnly')
        exchangeSpecificTimeInForce = self.safe_string_lower_2(params, 'time_in_force', 'tif')
        postOnly = self.is_post_only(type == 'market', exchangeSpecificTimeInForce == 'poc', params)
        timeInForce = self.handle_time_in_force(params)
        # 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'])
        if postOnly:
            timeInForce = 'poc'
        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 contract:
            amountToPrecision = self.amount_to_precision(symbol, amount)
            signedAmount = Precise.string_neg(amountToPrecision) if (side == 'sell') else amountToPrecision
            amount = int(signedAmount)
            if isMarketOrder:
                if (timeInForce == 'poc') or (timeInForce == 'gtc'):
                    raise ExchangeError(self.id + ' createOrder() timeInForce for market orders must be "IOC"')
                timeInForce = 'ioc'
                price = 0
        elif not isLimitOrder:
            # exchange doesn't have market orders for spot
            raise InvalidOrder(self.id + ' createOrder() does not support ' + type + ' orders for ' + market['type'] + ' markets')
        request = None
        if not isStopOrder and (trigger is None):
            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
                    'price': self.price_to_precision(symbol, price),  # 0 for market order with tif set as ioc
                    # 'close': False,  # True to close the position, with size set to 0
                    # 'reduce_only': False,  # St as True 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
                    'settle': market['settleId'],  # filled in prepareRequest above
                }
                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,
                    'amount': self.amount_to_precision(symbol, amount),
                    'price': self.price_to_precision(symbol, price),
                    # '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 timeInForce is not None:
                    request['time_in_force'] = timeInForce
            clientOrderId = self.safe_string_2(params, 'text', 'clientOrderId')
            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'])
                if clientOrderId[0] != 't':
                    clientOrderId = 't-' + clientOrderId
                request['text'] = clientOrderId
        else:
            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)
                    request['trigger'] = {
                        # 'strategy_type': 0,  # 0 = by price, 1 = by price gap, only 0 is supported currently
                        'price_type': 0,  # 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)
                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': 'gtc',  # gtc, ioc for taker only
                    },
                    '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
                    }
                if timeInForce is not None:
                    request['put']['time_in_force'] = timeInForce
            methodTail = 'PriceOrders'
        method = self.get_supported_mapping(market['type'], {
            'spot': 'privateSpotPost' + methodTail,
            'margin': 'privateSpotPost' + methodTail,
            'swap': 'privateFuturesPostSettle' + methodTail,
            'future': 'privateDeliveryPostSettle' + methodTail,
        })
        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}
        #
        # future and perpetual swaps
        #
        #     {
        #         "id": 95938572327,
        #         "contract": "ETH_USDT",
        #         "mkfr": "0",
        #         "tkfr": "0.0005",
        #         "tif": "gtc",
        #         "is_reduce_only": False,
        #         "create_time": 1637384600.08,
        #         "price": "3000",
        #         "size": 1,
        #         "refr": "0",
        #         "left": 1,
        #         "text": "api",
        #         "fill_price": "0",
        #         "user": 2436035,
        #         "status": "open",
        #         "is_liq": False,
        #         "refu": 0,
        #         "is_close": False,
        #         "iceberg": 0
        #     }
        #
        # futures and perpetual swaps conditionals
        #
        #     {"id": 7615567}
        #
        return self.parse_order(response, market)

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

    def parse_order(self, order, market=None):
        #
        # SPOT
        # createOrder/cancelOrder/fetchOrder
        #
        #    {
        #        "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 AND SWAP
        # 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": ""
        #    }
        #
        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)
        remaining = self.safe_string(order, 'left')
        filled = Precise.string_sub(amount, remaining)
        cost = self.safe_string(order, 'filled_total')
        rawStatus = None
        average = None
        if put:
            remaining = amount
            filled = '0'
            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(order, 'finish_as', 'open')
            average = self.safe_number(order, 'fill_price')
        else:
            rawStatus = self.safe_string(order, 'status')
        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')
        exchangeSymbol = self.safe_string_2(order, 'currency_pair', 'market', contract)
        # 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)
        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': self.safe_symbol(exchangeSymbol),
            'type': type,
            'timeInForce': timeInForce,
            'postOnly': postOnly,
            'reduceOnly': self.safe_value(order, 'is_reduce_only'),
            'side': side,
            'price': self.parse_number(price),
            'stopPrice': self.safe_number(trigger, 'price'),
            'average': average,
            'amount': self.parse_number(Precise.string_abs(amount)),
            'cost': Precise.string_abs(cost),
            'filled': self.parse_number(Precise.string_abs(filled)),
            'remaining': self.parse_number(Precise.string_abs(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, symbol=None, params={}):
        """
        Retrieves information on an order
        :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/en/latest/manual.html#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')
        request, requestParams = self.prepare_request(market, type, query) if contract else self.spot_order_prepare_request(market, stop, query)
        request['order_id'] = orderId
        methodMiddle = 'PriceOrders' if stop else 'Orders'
        method = self.get_supported_mapping(type, {
            'spot': 'privateSpotGet' + methodMiddle + 'OrderId',
            'margin': 'privateSpotGet' + methodMiddle + 'OrderId',
            'swap': 'privateFuturesGetSettle' + methodMiddle + 'OrderId',
            'future': 'privateDeliveryGetSettle' + methodMiddle + 'OrderId',
        })
        response = await getattr(self, method)(self.extend(request, requestParams))
        return self.parse_order(response, market)

    async def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        """
        fetch all unfilled currently open orders
        :param str|None symbol: unified market symbol
        :param int|None since: the earliest time in ms to fetch open orders for
        :param int|None limit: the maximum number of  open orders structures to retrieve
        :param dict params: extra parameters specific to the gate 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 [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        return await self.fetch_orders_by_status('open', symbol, since, limit, params)

    async def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
        """
        fetches information on multiple closed orders made by the user
        :param str|None symbol: unified market symbol of the market orders were made in
        :param int|None since: the earliest time in ms to fetch orders for
        :param int|None limit: the maximum number of  orde structures to retrieve
        :param dict params: extra parameters specific to the gate 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 [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        return await self.fetch_orders_by_status('finished', symbol, since, limit, params)

    async def fetch_orders_by_status(self, status, symbol=None, since=None, limit=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'] = int(since / 1000)
        methodTail = 'PriceOrders' if stop else 'Orders'
        openSpotOrders = spot and (status == 'open') and not stop
        if openSpotOrders:
            methodTail = 'OpenOrders'
        method = self.get_supported_mapping(type, {
            'spot': 'privateSpotGet' + methodTail,
            'margin': 'privateSpotGet' + methodTail,
            'swap': 'privateFuturesGetSettle' + methodTail,
            'future': 'privateDeliveryGetSettle' + methodTail,
        })
        response = await getattr(self, method)(self.extend(request, requestParams))
        #
        # 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"
        #        }
        #    ]
        #
        # Perpetual 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"
        #        }
        #    ]
        #
        result = response
        if openSpotOrders:
            result = []
            for i in range(0, len(response)):
                orders = self.safe_value(response[i], 'orders')
                result = self.array_concat(result, orders)
        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, symbol=None, params={}):
        """
        Cancels an open order
        :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/en/latest/manual.html#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
        pathMiddle = 'Price' if stop else ''
        method = self.get_supported_mapping(type, {
            'spot': 'privateSpotDelete' + pathMiddle + 'OrdersOrderId',
            'margin': 'privateSpotDelete' + pathMiddle + 'OrdersOrderId',
            'swap': 'privateFuturesDeleteSettle' + pathMiddle + 'OrdersOrderId',
            'future': 'privateDeliveryDeleteSettle' + pathMiddle + 'OrdersOrderId',
        })
        response = await getattr(self, method)(self.extend(request, requestParams))
        #
        # 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"
        #     }
        #
        # perpetual swaps
        #
        #     {
        #         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=None, params={}):
        """
        cancel all open orders
        :param str|None 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 gate api endpoint
        :returns [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        await self.load_markets()
        market = None if (symbol is None) else self.market(symbol)
        stop = self.safe_value(params, 'stop')
        params = self.omit(params, 'stop')
        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)
        methodTail = 'PriceOrders' if stop else 'Orders'
        method = self.get_supported_mapping(type, {
            'spot': 'privateSpotDelete' + methodTail,
            'margin': 'privateSpotDelete' + methodTail,
            'swap': 'privateFuturesDeleteSettle' + methodTail,
            'future': 'privateDeliveryDeleteSettle' + methodTail,
        })
        response = await getattr(self, method)(self.extend(request, requestParams))
        #
        #    [
        #        {
        #            "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, amount, fromAccount, toAccount, params={}):
        """
        transfer currency internally between wallets on the same account
        :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 gate api endpoint
        :param str|None params['symbol']: Unified market symbol *required for type == margin*
        :returns: A `transfer structure <https://docs.ccxt.com/en/latest/manual.html#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 = {
            'currency': currency['id'],
            '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['lowerCaseId']
        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"
        #    }
        #
        transfer = self.parse_transfer(response, currency)
        return self.extend(transfer, {
            'fromAccount': fromAccount,
            'toAccount': toAccount,
            'amount': self.parse_number(truncated),
        })

    def parse_transfer(self, transfer, currency=None):
        timestamp = self.milliseconds()
        return {
            'id': None,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'currency': self.safe_currency_code(None, currency),
            'amount': None,
            'fromAccount': None,
            'toAccount': None,
            'status': None,
            'info': transfer,
        }

    async def set_leverage(self, leverage, symbol=None, params={}):
        """
        set the level of leverage for a market
        :param float leverage: the rate of leverage
        :param str symbol: unified market symbol
        :param dict params: extra parameters specific to the gate 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)
        method = self.get_supported_mapping(market['type'], {
            'swap': 'privateFuturesPostSettlePositionsContractLeverage',
            'future': 'privateDeliveryPostSettlePositionsContractLeverage',
        })
        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)
        if crossLeverageLimit is not None:
            marginMode = 'cross'
            leverage = crossLeverageLimit
        if marginMode == 'cross' or marginMode == 'cross_margin':
            request['cross_leverage_limit'] = str(leverage)
            request['leverage'] = '0'
        else:
            request['leverage'] = str(leverage)
        response = await getattr(self, method)(self.extend(request, query))
        #
        #     {
        #         "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, market=None):
        #
        #     {
        #         value: "12.475572",
        #         leverage: "0",
        #         mode: "single",
        #         realised_point: "0",
        #         contract: "BTC_USDT",
        #         entry_price: "62422.6",
        #         mark_price: "62377.86",
        #         history_point: "0",
        #         realised_pnl: "-0.00624226",
        #         close_order:  null,
        #         size: "2",
        #         cross_leverage_limit: "25",
        #         pending_orders: "0",
        #         adl_ranking: "5",
        #         maintenance_rate: "0.005",
        #         unrealised_pnl: "-0.008948",
        #         user: "663337",
        #         leverage_max: "100",
        #         history_pnl: "14.98868396636",
        #         risk_limit: "1000000",
        #         margin: "0.740721495056",
        #         last_close_pnl: "-0.041996015",
        #         liq_price: "59058.58"
        #     }
        #
        contract = self.safe_string(position, 'contract')
        market = self.safe_market(contract, market)
        size = self.safe_string(position, 'size')
        side = 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 == '0':
            marginMode = 'cross'
        else:
            marginMode = 'isolated'
        unrealisedPnl = self.safe_string(position, 'unrealised_pnl')
        # 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 as Taker Fee Rate(0.075%).
        takerFee = '0.00075'
        feePaid = Precise.string_mul(takerFee, notional)
        initialMarginString = Precise.string_add(Precise.string_div(notional, leverage), feePaid)
        percentage = Precise.string_mul(Precise.string_div(unrealisedPnl, initialMarginString), '100')
        return {
            'info': position,
            'id': None,
            'symbol': self.safe_string(market, 'symbol'),
            'timestamp': None,
            'datetime': None,
            '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.parse_number(unrealisedPnl),
            'contracts': self.parse_number(Precise.string_abs(size)),
            'contractSize': self.safe_value(market, 'contractSize'),
            # 'realisedPnl': position['realised_pnl'],
            'marginRatio': None,
            'liquidationPrice': self.safe_number(position, 'liq_price'),
            'markPrice': self.safe_number(position, 'mark_price'),
            'collateral': self.safe_number(position, 'margin'),
            'marginMode': marginMode,
            'side': side,
            'percentage': self.parse_number(percentage),
        }

    async def fetch_positions(self, symbols=None, params={}):
        """
        fetch all open positions
        :param [str]|None symbols: Not used by gate, but parsed internally by CCXT
        :param dict params: extra parameters specific to the gate 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 or future, if not provided self.options['defaultType'] is used
        :returns [dict]: a list of `position structure <https://docs.ccxt.com/en/latest/manual.html#position-structure>`
        """
        await self.load_markets()
        market = None
        if symbols is not None:
            symbols = self.market_symbols(symbols)
            symbolsLength = len(symbols)
            if symbolsLength > 0:
                market = self.market(symbols[0])
                for i in range(1, len(symbols)):
                    checkMarket = self.market(symbols[i])
                    if checkMarket['type'] != market['type']:
                        raise BadRequest(self.id + ' fetchPositions() does not support multiple types of positions at the same time')
        type, query = self.handle_market_type_and_params('fetchPositions', market, params)
        if type != 'swap' and type != 'future':
            raise ArgumentsRequired(self.id + ' fetchPositions requires a type parameter, "swap" or "future"')
        request, requestParams = self.prepare_request(None, type, query)
        method = self.get_supported_mapping(type, {
            'swap': 'privateFuturesGetSettlePositions',
            'future': 'privateDeliveryGetSettlePositions',
        })
        response = await getattr(self, method)(self.extend(request, requestParams))
        #
        #     [
        #         {
        #             value: "12.475572",
        #             leverage: "0",
        #             mode: "single",
        #             realised_point: "0",
        #             contract: "BTC_USDT",
        #             entry_price: "62422.6",
        #             mark_price: "62377.86",
        #             history_point: "0",
        #             realised_pnl: "-0.00624226",
        #             close_order:  null,
        #             size: "2",
        #             cross_leverage_limit: "25",
        #             pending_orders: "0",
        #             adl_ranking: "5",
        #             maintenance_rate: "0.005",
        #             unrealised_pnl: "-0.008948",
        #             user: "6693577",
        #             leverage_max: "100",
        #             history_pnl: "14.98868396636",
        #             risk_limit: "1000000",
        #             margin: "0.740721495056",
        #             last_close_pnl: "-0.041996015",
        #             liq_price: "59058.58"
        #         }
        #     ]
        #
        return self.parse_positions(response, symbols)

    async def fetch_leverage_tiers(self, symbols=None, params={}):
        """
        retrieve information on the maximum leverage, and maintenance margin for trades of varying trade sizes
        :param [str]|None symbols: list of unified market symbols
        :param dict params: extra parameters specific to the gate api endpoint
        :returns dict: a dictionary of `leverage tiers structures <https://docs.ccxt.com/en/latest/manual.html#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')
        method = self.get_supported_mapping(type, {
            'swap': 'publicFuturesGetSettleContracts',
            'future': 'publicDeliveryGetSettleContracts',
        })
        response = await getattr(self, method)(self.extend(request, requestParams))
        #
        # 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')

    def parse_market_leverage_tiers(self, info, market=None):
        """
         * @ignore
        https://www.gate.io/help/futures/perpetual/22162/instrctions-of-risk-limit
        :param dict info: Exchange market response for 1 market
        :param dict market: CCXT market
        """
        #
        # 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
        #    }
        #
        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

    async def repay_margin(self, code, amount, symbol=None, params={}):
        """
        repay borrowed margin and interest
        see https://www.gate.io/docs/apiv4/en/#repay-cross-margin-loan
        see https://www.gate.io/docs/apiv4/en/#repay-a-loan
        :param str code: unified currency code of the currency to repay
        :param float amount: the amount to repay
        :param str|None symbol: unified market symbol, required for isolated margin
        :param dict params: extra parameters specific to the gate 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/en/latest/manual.html#margin-loan-structure>`
        """
        await self.load_markets()
        currency = self.currency(code)
        market = None
        if symbol is not None:
            market = self.market(symbol)
        request = {
            'currency': currency['id'],
            'amount': self.currency_to_precision(code, amount),
        }
        defaultMarginMode = self.safe_string_2(self.options, 'defaultMarginMode', 'marginMode', 'cross')
        marginMode = self.safe_string(params, 'marginMode', defaultMarginMode)  # cross or isolated
        method = 'privateMarginPostCrossRepayments'
        if marginMode == 'isolated':
            if symbol is None:
                raise ArgumentsRequired(self.id + ' repayMargin() requires a symbol argument for isolated margin')
            mode = self.safe_string(params, 'mode')  # 'all' or 'partial'
            if mode is None:
                raise ArgumentsRequired(self.id + ' repayMargin() requires a mode parameter for isolated margin')
            id = self.safe_string_2(params, 'loan_id', 'id')
            if id is None:
                raise ArgumentsRequired(self.id + ' repayMargin() requires an id parameter for isolated margin')
            method = 'privateMarginPostLoansLoanIdRepayment'
            request['currency_pair'] = market['id']
            request['mode'] = mode
            request['loan_id'] = id
        params = self.omit(params, ['marginMode', 'mode', 'loan_id', 'id'])
        response = await getattr(self, method)(self.extend(request, params))
        #
        # 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": "finished",
        #         "side": "borrow",
        #         "currency": "USDT",
        #         "rate": "0.0002",
        #         "amount": "100",
        #         "days": 10,
        #         "auto_renew": False,
        #         "currency_pair": "LTC_USDT",
        #         "left": "0",
        #         "repaid": "100",
        #         "paid_interest": "0.003333333333",
        #         "unpaid_interest": "0"
        #     }
        #
        if marginMode == 'cross':
            response = response[0]
        return self.parse_margin_loan(response, currency)

    async def borrow_margin(self, code, amount, symbol=None, params={}):
        """
        create a loan to borrow margin
        see https://www.gate.io/docs/apiv4/en/#create-a-cross-margin-borrow-loan
        see https://www.gate.io/docs/apiv4/en/#lend-or-borrow
        :param str code: unified currency code of the currency to borrow
        :param float amount: the amount to borrow
        :param str|None symbol: unified market symbol, required for isolated margin
        :param dict params: extra parameters specific to the gate 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/en/latest/manual.html#margin-loan-structure>`
        """
        await self.load_markets()
        currency = self.currency(code)
        market = None
        if symbol is not None:
            market = self.market(symbol)
            symbol = market['symbol']
        request = {
            'currency': currency['id'],
            'amount': self.currency_to_precision(code, amount),
        }
        defaultMarginMode = self.safe_string_2(self.options, 'defaultMarginMode', 'marginMode', 'cross')
        marginMode = self.safe_string(params, 'marginMode', defaultMarginMode)  # cross or isolated
        method = 'privateMarginPostCrossLoans'
        if marginMode == 'isolated':
            if symbol is None:
                raise ArgumentsRequired(self.id + ' borrowMargin() requires a symbol argument for isolated margin')
            request['currency_pair'] = market['id']
            rate = self.safe_string(params, 'rate')
            if rate is None:
                raise ArgumentsRequired(self.id + ' borrowMargin() requires a rate parameter for isolated margin')
            request['rate'] = rate  # Only rates '0.0002', '0.002' are supported.
            request['side'] = 'borrow'
            method = 'privateMarginPostLoans'
        params = self.omit(params, ['marginMode', 'rate'])
        response = await getattr(self, method)(self.extend(request, params))
        #
        # 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"
        #     }
        #
        return self.parse_margin_loan(response, currency)

    def parse_margin_loan(self, info, 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),
            '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))
        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' 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:
                if query:
                    queryString = self.urlencode(query)
                    url += '?' + queryString
            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}

    def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
        if response is None:
            return
        #
        #    {"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)
