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

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

from ccxt.base.exchange import Exchange
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 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 RateLimitExceeded
from ccxt.base.errors import ExchangeNotAvailable
from ccxt.base.decimal_to_precision import TICK_SIZE
from ccxt.base.precise import Precise


class gateio(Exchange):

    def describe(self):
        return self.deep_extend(super(gateio, self).describe(), {
            'id': 'gateio',
            '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': 'https://api.gateio.ws/api/v4',
                    'private': 'https://api.gateio.ws/api/v4',
                },
                'referral': {
                    'url': 'https://www.gate.io/ref/2436035',
                    'discount': 0.2,
                },
            },
            'has': {
                'margin': True,
                'swap': True,
                'future': True,
                'cancelAllOrders': True,
                'cancelOrder': True,
                'createMarketOrder': False,
                'createOrder': True,
                'fetchBalance': True,
                'fetchBorrowRate': False,
                'fetchBorrowRateHistory': False,
                'fetchBorrowRates': False,
                'fetchClosedOrders': True,
                'fetchCurrencies': True,
                'fetchDepositAddress': True,
                'fetchDeposits': True,
                'fetchFundingFees': True,
                'fetchFundingHistory': True,
                'fetchFundingRate': True,
                'fetchFundingRateHistory': True,
                'fetchFundingRates': True,
                'fetchIndexOHLCV': True,
                'fetchMarkets': True,
                'fetchMarkOHLCV': True,
                'fetchMyTrades': True,
                'fetchNetworkDepositAddress': True,
                'fetchOHLCV': True,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrdersByStatus': True,
                'fetchPositions': True,
                'fetchPremiumIndexOHLCV': False,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': False,
                'fetchTrades': True,
                'fetchTradingFees': True,
                'fetchWithdrawals': True,
                'setLeverage': True,
                'transfer': True,
                'withdraw': True,
            },
            'api': {
                'public': {
                    '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': {
                            '': 3000,  # 3000 = 10 seconds
                        },
                        'delete': {
                            '{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,
                        },
                        'post': {
                            'transfers': 300,
                            'sub_account_transfers': 300,
                        },
                    },
                    '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,
                        },
                        '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,
                        },
                        '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,
                        },
                        '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,
                        },
                        '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 gateiov2
            'commonCurrencies': {
                '88MPH': 'MPH',
                '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,
            },
            'options': {
                'createOrder': {
                    'expiration': 86400,  # for conditional orders
                },
                'networks': {
                    'TRC20': 'TRX',
                    'ERC20': 'ETH',
                    'BEP20': 'BSC',
                },
                'accountsByType': {
                    'spot': 'spot',
                    'margin': 'margin',
                    'future': 'futures',
                    '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': ExchangeError,
                    'ORDER_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': ExchangeError,
                    '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': InvalidOrder,
                    'INCREASE_POSITION': InvalidOrder,
                    'CONTRACT_IN_DELISTING': ExchangeError,
                    'INTERNAL': ExchangeError,
                    'SERVER_ERROR': ExchangeError,
                    'TOO_BUSY': ExchangeNotAvailable,
                },
            },
            'broad': {},
        })

    def fetch_markets(self, params={}):
        # :param params['type']: 'spot', 'margin', 'future' or 'delivery'
        # :param params['settle']: The quote currency
        defaultType = self.safe_string_2(self.options, 'fetchMarkets', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        query = self.omit(params, 'type')
        spot = (type == 'spot')
        margin = (type == 'margin')
        future = (type == 'future')
        swap = (type == 'swap')
        option = (type == 'option')
        if not spot and not margin and not future and not swap:
            raise ExchangeError(self.id + " does not support '" + type + "' type, set exchange.options['defaultType'] to " + "'spot', 'margin', 'swap' or 'future'")  # eslint-disable-line quotes
        response = None
        result = []
        method = self.get_supported_mapping(type, {
            'spot': 'publicSpotGetCurrencyPairs',
            'margin': 'publicMarginGetCurrencyPairs',
            'swap': 'publicFuturesGetSettleContracts',
            'future': 'publicDeliveryGetSettleContracts',
        })
        if swap or future or option:
            settlementCurrencies = self.get_settlement_currencies(type, 'fetchMarkets')
            for c in range(0, len(settlementCurrencies)):
                settleId = settlementCurrencies[c]
                query['settle'] = settleId
                response = getattr(self, method)(query)
                #  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
                #          }
                #        ]
                #
                for i in range(0, len(response)):
                    market = response[i]
                    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)
                    linear = quote == settle
                    inverse = base == settle
                    expiry = self.safe_timestamp(market, 'expire_time')
                    symbol = ''
                    if date is not None:
                        symbol = base + '/' + quote + ':' + settle + '-' + self.yymmdd(expiry, '')
                    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)
                    pricePrecision = self.safe_number(market, 'order_price_round')
                    # 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'))
                    result.append({
                        'info': market,
                        'id': id,
                        'symbol': symbol,
                        'base': base,
                        'quote': quote,
                        'settle': settle,
                        'baseId': baseId,
                        'quoteId': quoteId,
                        'settleId': settleId,
                        'type': type,
                        'spot': spot,
                        'margin': margin,
                        'swap': swap,
                        'future': future,
                        'option': option,
                        'active': True,
                        'contract': True,
                        'linear': linear,
                        'inverse': inverse,
                        'taker': taker,
                        'maker': maker,
                        'contractSize': self.safe_number(market, 'quanto_multiplier'),
                        'expiry': expiry,
                        'expiryDatetime': self.iso8601(expiry),
                        'strike': None,
                        'optionType': None,
                        'precision': {
                            'amount': self.parse_number('1'),
                            'price': pricePrecision,
                        },
                        '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': minPrice,
                                'max': maxPrice,
                            },
                            'cost': {
                                'min': None,
                                'max': None,
                            },
                        },
                    })
        else:
            response = getattr(self, method)(query)
            #
            #  Spot
            #      [
            #           {
            #             "id": "DEGO_USDT",
            #             "base": "DEGO",
            #             "quote": "USDT",
            #             "fee": "0.2",
            #             "min_quote_amount": "1",
            #             "amount_precision": "4",
            #             "precision": "4",
            #             "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"
            #         }
            #       ]
            #
            for i in range(0, len(response)):
                market = response[i]
                id = self.safe_string(market, 'id')
                spot = (type == 'spot')
                baseId, quoteId = id.split('_')
                base = self.safe_currency_code(baseId)
                quote = self.safe_currency_code(quoteId)
                symbol = base + '/' + quote
                takerPercent = self.safe_string(market, 'fee')
                makerPercent = self.safe_string(market, 'maker_fee_rate', takerPercent)
                amountPrecisionString = self.safe_string(market, 'amount_precision')
                pricePrecisionString = self.safe_string(market, 'precision')
                amountPrecision = self.parse_number(self.parse_precision(amountPrecisionString))
                pricePrecision = self.parse_number(self.parse_precision(pricePrecisionString))
                tradeStatus = self.safe_string(market, 'trade_status')
                result.append({
                    'id': id,
                    'symbol': symbol,
                    'base': base,
                    'quote': quote,
                    'settle': None,
                    'baseId': baseId,
                    'quoteId': quoteId,
                    'settleId': None,
                    'type': type,
                    'spot': spot,
                    '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': pricePrecision,
                    },
                    'limits': {
                        'leverage': {
                            'min': self.parse_number('1'),
                            'max': self.safe_number(market, 'lever', 1),
                        },
                        'amount': {
                            'min': amountPrecision,
                            'max': None,
                        },
                        'price': {
                            'min': pricePrecision,
                            'max': None,
                        },
                        'cost': {
                            'min': self.safe_number(market, 'min_quote_amount'),
                            'max': None,
                        },
                    },
                    'info': market,
                })
        return result

    def prepare_request(self, market):
        if market['contract']:
            return {
                'contract': market['id'],
                'settle': market['settleId'],
            }
        else:
            return {
                'currency_pair': market['id'],
            }

    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)

    def fetch_currencies(self, params={}):
        response = self.publicSpotGetCurrencies(params)
        #
        #     {
        #       "currency": "BCN",
        #       "delisted": False,
        #       "withdraw_disabled": True,
        #       "withdraw_delayed": False,
        #       "deposit_disabled": True,
        #       "trade_disabled": False
        #     }
        #
        result = {}
        # TODO: remove magic constants
        amountPrecision = self.parse_number('1e-6')
        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': amountPrecision,
                'info': entry,
                'active': active,
                'deposit': depositEnabled,
                'withdraw': withdrawEnabled,
                'fee': None,
                'fees': [],
                'limits': self.limits,
            }
        return result

    def fetch_funding_rate(self, symbol, params={}):
        self.load_markets()
        market = self.market(symbol)
        if not market['swap']:
            raise BadRequest('Funding rates only exist for swap contracts')
        request = self.prepare_request(market)
        response = self.publicFuturesGetSettleContractsContract(self.extend(request, params))
        #
        # [
        #     {
        #       "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)

    def fetch_funding_rates(self, symbols=None, params={}):
        self.load_markets()
        settle = self.safe_string_lower(params, 'settle')
        request = {
            'settle': settle,
        }
        response = self.publicFuturesGetSettleContracts(self.extend(request, params))
        #
        # [
        #     {
        #       "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_string(contract, 'funding_rate')
        nextFundingTime = self.safe_integer(contract, 'funding_next_apply') * 1000
        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,
            'previousFundingRate': fundingRate,
            'nextFundingRate': fundingRateIndicative,
            'previousFundingTimestamp': None,
            'nextFundingTimestamp': nextFundingTime,
            'previousFundingDatetime': None,
            'nextFundingDatetime': self.iso8601(nextFundingTime),
        }

    def fetch_network_deposit_address(self, code, params={}):
        self.load_markets()
        currency = self.currency(code)
        request = {
            'currency': currency['id'],
        }
        response = 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')
            tagLength = len(tag)
            tag = tag if tagLength else None
            result[network] = {
                'info': entry,
                'code': code,
                'address': address,
                'tag': tag,
            }
        return result

    def fetch_deposit_address(self, code, params={}):
        self.load_markets()
        currency = self.currency(code)
        request = {
            'currency': currency['id'],
        }
        response = 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.find(' ') >= 0:
            splitted = addressField.split(' ')
            address = splitted[0]
            tag = splitted[1]
        else:
            address = addressField
        return {
            'info': response,
            'code': code,
            'address': address,
            'tag': tag,
            'network': None,
        }

    def fetch_trading_fees(self, params={}):
        self.load_markets()
        response = 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"
        #     }
        #
        result = {}
        taker = self.safe_number(response, 'taker_fee')
        maker = self.safe_number(response, 'maker_fee')
        for i in range(0, len(self.symbols)):
            symbol = self.symbols[i]
            result[symbol] = {
                'maker': maker,
                'taker': taker,
                'info': response,
                'symbol': symbol,
            }
        return result

    def fetch_funding_fees(self, params={}):
        self.load_markets()
        response = 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"
        #       }
        #     }
        #
        withdrawFees = {}
        for i in range(0, len(response)):
            entry = response[i]
            currencyId = self.safe_string(entry, 'currency')
            code = self.safe_currency_code(currencyId)
            withdrawFees[code] = {}
            withdrawFix = self.safe_value(entry, 'withdraw_fix_on_chains')
            if withdrawFix is None:
                withdrawFix = {}
                withdrawFix[code] = self.safe_number(entry, 'withdraw_fix')
            keys = list(withdrawFix.keys())
            for i in range(0, len(keys)):
                key = keys[i]
                withdrawFees[code][key] = self.parse_number(withdrawFix[key])
        return {
            'info': response,
            'withdraw': withdrawFees,
            'deposit': {},
        }

    def fetch_funding_history(self, symbol=None, since=None, limit=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchFundingHistory() requires a symbol argument')
        self.load_markets()
        # defaultType = 'future'
        market = self.market(symbol)
        request = self.prepare_request(market)
        request['type'] = 'fund'  # 'dnw' 'pnl' 'fee' 'refr' 'fund' 'point_dnw' 'point_fee' 'point_refr'
        if since is not None:
            request['from'] = since
        if limit is not None:
            request['limit'] = limit
        method = self.get_supported_mapping(market['type'], {
            'swap': 'privateFuturesGetSettleAccountBook',
            'future': 'privateDeliveryGetSettleAccountBook',
        })
        response = getattr(self, method)(self.extend(request, params))
        result = []
        for i in range(0, len(response)):
            entry = response[i]
            timestamp = self.safe_timestamp(entry, 'time')
            result.append({
                'info': entry,
                'symbol': symbol,
                'code': self.safe_currency_code(self.safe_string(entry, 'text')),
                'timestamp': timestamp,
                'datetime': self.iso8601(timestamp),
                'id': None,
                'amount': self.safe_number(entry, 'change'),
            })
        sorted = self.sort_by(result, 'timestamp')
        return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)

    def fetch_order_book(self, symbol, limit=None, params={}):
        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 = self.prepare_request(market)
        spotOrMargin = market['spot'] or market['margin']
        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
        response = getattr(self, method)(self.extend(request, params))
        #
        # SPOT
        #
        #     {
        #         "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
        #
        #     {
        #         "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 spotOrMargin:
            timestamp = timestamp * 1000
        priceKey = 0 if spotOrMargin else 'p'
        amountKey = 1 if spotOrMargin else 's'
        return self.parse_order_book(response, symbol, timestamp, 'bids', 'asks', priceKey, amountKey)

    def fetch_ticker(self, symbol, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = self.prepare_request(market)
        method = self.get_supported_mapping(market['type'], {
            'spot': 'publicSpotGetTickers',
            'margin': 'publicSpotGetTickers',
            'swap': 'publicFuturesGetSettleTickers',
            'future': 'publicDeliveryGetSettleTickers',
        })
        response = getattr(self, method)(self.extend(request, params))
        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_number(ticker, 'last')
        ask = self.safe_number(ticker, 'lowest_ask')
        bid = self.safe_number(ticker, 'highest_bid')
        high = self.safe_number(ticker, 'high_24h')
        low = self.safe_number(ticker, 'low_24h')
        baseVolume = self.safe_number_2(ticker, 'base_volume', 'volume_24h_base')
        quoteVolume = self.safe_number_2(ticker, 'quote_volume', 'volume_24h_quote')
        percentage = self.safe_number(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)

    def fetch_tickers(self, symbols=None, params={}):
        self.load_markets()
        defaultType = self.safe_string_2(self.options, 'fetchTickers', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        params = self.omit(params, 'type')
        method = self.get_supported_mapping(type, {
            'spot': 'publicSpotGetTickers',
            'margin': 'publicSpotGetTickers',
            'swap': 'publicFuturesGetSettleTickers',
            'future': 'publicDeliveryGetSettleTickers',
        })
        request = {}
        future = type == 'future'
        swap = type == 'swap'
        defaultSettle = 'usdt' if swap else 'btc'
        settle = self.safe_string_lower(params, 'settle', defaultSettle)
        if swap or future:
            request['settle'] = settle
        response = getattr(self, method)(self.extend(request, params))
        return self.parse_tickers(response, symbols)

    def fetch_balance_helper(self, entry):
        account = self.account()
        account['used'] = self.safe_string_2(entry, 'locked', 'position_margin')
        account['free'] = self.safe_string(entry, 'available')
        return account

    def fetch_balance(self, params={}):
        # :param params.type: spot, margin, crossMargin, swap or future
        # :param params.settle: Settle currency(usdt or btc) for perpetual swap and future
        self.load_markets()
        type = None
        type, params = self.handle_market_type_and_params('fetchBalance', None, params)
        swap = type == 'swap'
        future = type == 'future'
        method = self.get_supported_mapping(type, {
            'spot': 'privateSpotGetAccounts',
            'margin': 'privateMarginGetAccounts',
            'swap': 'privateFuturesGetSettleAccounts',
            'future': 'privateDeliveryGetSettleAccounts',
        })
        request = {}
        response = []
        if swap or future:
            defaultSettle = 'usdt' if swap else 'btc'
            request['settle'] = self.safe_string_lower(params, 'settle', defaultSettle)
            response_item = getattr(self, method)(self.extend(request, params))
            response = [response_item]
        else:
            response = getattr(self, method)(self.extend(request, params))
        # Spot
        #
        #     [
        #         {
        #             "currency": "DBC",
        #             "available": "0",
        #             "locked": "0"
        #         },
        #         ...
        #     ]
        #
        #  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"
        #             }
        #         },
        #         ...
        #    ]
        #
        #  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",
        #     }
        #
        margin = type == 'margin'
        result = {
            'info': response,
        }
        for i in range(0, len(response)):
            entry = response[i]
            if margin:
                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 else self.safe_balance(result)

    def fetch_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        price = self.safe_string(params, 'price')
        request = self.prepare_request(market)
        request['interval'] = self.timeframes[timeframe]
        method = 'publicSpotGetCandlesticks'
        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')
        else:
            maxLimit = 1000
            limit = maxLimit if (limit is None) else min(limit, maxLimit)
            request['limit'] = limit
        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()
            request['to'] = min(toTimestamp, currentTimestamp)
        response = getattr(self, method)(self.extend(request, params))
        return self.parse_ohlcvs(response, market, timeframe, since, limit)

    def fetch_mark_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
        request = {
            'price': 'mark',
        }
        return self.fetch_ohlcv(symbol, timeframe, since, limit, self.extend(request, params))

    def fetch_funding_rate_history(self, symbol=None, since=None, limit=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchFundingRateHistory() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        if not market['swap']:
            raise BadRequest('Funding rates only exist for swap contracts')
        request = {
            'contract': market['id'],
            'settle': market['settleId'],
        }
        if limit is not None:
            request['limit'] = limit
        method = 'publicFuturesGetSettleFundingRate'
        response = getattr(self, method)(self.extend(request, params))
        #
        #     {
        #         "r": "0.00063521",
        #         "t": "1621267200000",
        #     }
        #
        rates = []
        for i in range(0, len(response)):
            entry = response[i]
            timestamp = self.safe_timestamp(entry, 't')
            rates.append({
                'info': entry,
                'symbol': symbol,
                'fundingRate': self.safe_number(entry, 'r'),
                'timestamp': timestamp,
                'datetime': self.iso8601(timestamp),
            })
        sorted = self.sort_by(rates, 'timestamp')
        return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)

    def fetch_index_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
        request = {
            'price': 'index',
        }
        return self.fetch_ohlcv(symbol, timeframe, since, limit, self.extend(request, params))

    def parse_ohlcv(self, ohlcv, market=None):
        #
        # Spot market candles
        #
        #     [
        #         "1626163200",           # Unix timestamp in seconds
        #         "346711.933138181617",  # Trading volume
        #         "33165.23",             # Close price
        #         "33260",                # Highest price
        #         "33117.6",              # Lowest price
        #         "33184.47"              # Open price
        #     ]
        #
        # 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, 1),      # 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
            ]

    def fetch_trades(self, symbol, since=None, limit=None, params={}):
        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 = self.prepare_request(market)
        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 = getattr(self, method)(self.extend(request, params))
        #
        # 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)

    def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        #
        #     request = {
        #         'currency_pair': market['id'],
        #         # 'limit': limit,
        #         # 'page': 0,
        #         # 'order_id': 'Order ID',
        #         # 'account': 'spot',  # default to spot and margin account if not specified, set to cross_margin to operate against margin account
        #         # 'from': since,  # default to 7 days before current time
        #         # 'to': self.milliseconds(),  # default to current time
        #     }
        #
        request = self.prepare_request(market)
        if limit is not None:
            request['limit'] = limit  # default 100, max 1000
        if since is not None:
            request['from'] = int(since / 1000)
            # request['to'] = since + 7 * 24 * 60 * 60
        method = self.get_supported_mapping(market['type'], {
            'spot': 'privateSpotGetMyTrades',
            'margin': 'privateSpotGetMyTrades',
            'swap': 'privateFuturesGetSettleMyTrades',
            'future': 'privateDeliveryGetSettleMyTrades',
        })
        response = getattr(self, method)(self.extend(request, params))
        # SPOT
        # [{
        #     id: "1851927191",
        #     create_time: "1634333360",
        #     create_time_ms: "1634333360359.901000",
        #     currency_pair: "BTC_USDT",
        #     side: "buy",
        #     role: "taker",
        #     amount: "0.0001",
        #     price: "62547.51",
        #     order_id: "93475897349",
        #     fee: "2e-07",
        #     fee_currency: "BTC",
        #     point_fee: "0",
        #     gt_fee: "0",
        #   }]
        # Perpetual Swap
        # [{
        #   size: "-13",
        #   order_id: "79723658958",
        #   id: "47612669",
        #   role: "taker",
        #   create_time: "1634600263.326",
        #   contract: "BTC_USDT",
        #   price: "61987.8",
        # }]
        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"
        #     }
        #
        # private
        #
        #     {
        #         "id": "218087755",
        #         "create_time": "1578958740",
        #         "create_time_ms": "1578958740122.710000",
        #         "currency_pair": "BTC_USDT",
        #         "side": "sell",
        #         "role": "taker",
        #         "amount": "0.0004",
        #         "price": "8112.77",
        #         "order_id": "8445563839",
        #         "fee": "0.006490216",
        #         "fee_currency": "USDT",
        #         "point_fee": "0",
        #         "gt_fee": "0"
        #     }
        #
        id = self.safe_string(trade, 'id')
        timestampStringContract = self.safe_string(trade, 'create_time')
        timestampString = self.safe_string_2(trade, 'create_time_ms', 'time', timestampStringContract)
        timestamp = None
        if timestampString.find('.') > 0:
            milliseconds = timestampString.split('.')
            timestamp = int(milliseconds[0])
        if market['contract']:
            timestamp = timestamp * 1000
        marketId = self.safe_string_2(trade, 'currency_pair', 'contract')
        symbol = self.safe_symbol(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(trade, 'side', contractSide)
        orderId = self.safe_string(trade, 'order_id')
        gtFee = self.safe_string(trade, 'gt_fee')
        feeCurrency = None
        feeCostString = None
        if gtFee == '0':
            feeCurrency = self.safe_string(trade, 'fee_currency')
            feeCostString = self.safe_string(trade, 'fee')
        else:
            feeCurrency = 'GT'
            feeCostString = gtFee
        fee = {
            'cost': feeCostString,
            'currency': feeCurrency,
        }
        takerOrMaker = self.safe_string(trade, 'role')
        return self.safe_trade({
            'info': trade,
            'id': id,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'order': orderId,
            'type': None,
            'side': side,
            'takerOrMaker': takerOrMaker,
            'price': priceString,
            'amount': amountString,
            'cost': None,
            'fee': fee,
        }, market)

    def fetch_deposits(self, code=None, since=None, limit=None, params={}):
        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:
            request['from'] = int(since / 1000)
            request['to'] = since + 30 * 24 * 60 * 60
        response = self.privateWalletGetDeposits(self.extend(request, params))
        return self.parse_transactions(response, currency)

    def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
        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:
            request['from'] = int(since / 1000)
            request['to'] = since + 30 * 24 * 60 * 60
        response = self.privateWalletGetWithdrawals(self.extend(request, params))
        return self.parse_transactions(response, currency)

    def withdraw(self, code, amount, address, tag=None, params={}):
        tag, params = self.handle_withdraw_tag_and_params(tag, params)
        self.check_address(address)
        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 = self.privateWithdrawalsPost(self.extend(request, params))
        #
        #     {
        #       "id": "w13389675",
        #       "currency": "USDT",
        #       "amount": "50",
        #       "address": "TUu2rLFrmzUodiWfYki7QCNtv1akL682p1",
        #       "memo": null
        #     }
        #
        currencyId = self.safe_string(response, 'currency')
        id = self.safe_string(response, 'id')
        return {
            'info': response,
            'id': id,
            'code': self.safe_currency_code(currencyId),
            'amount': self.safe_number(response, 'amount'),
            'address': self.safe_string(response, 'address'),
            'tag': self.safe_string(response, 'memo'),
        }

    def parse_transaction_status(self, status):
        statuses = {
            'PEND': 'pending',
            'REQUEST': 'pending',
            'DMOVE': 'pending',
            'CANCEL': 'failed',
            'DONE': 'ok',
        }
        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": ""
        #     }
        #
        # withdrawals
        id = self.safe_string(transaction, 'id')
        type = None
        if id is not None:
            type = self.parse_transaction_type(id[0])
        currencyId = self.safe_string(transaction, 'currency')
        code = self.safe_currency_code(currencyId)
        amount = self.safe_number(transaction, 'amount')
        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')
        if tag == '':
            tag = None
        timestamp = self.safe_timestamp(transaction, 'timestamp')
        return {
            'info': transaction,
            'id': id,
            'txid': txid,
            'currency': code,
            'amount': 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,
        }

    def create_order(self, symbol, type, side, amount, price=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        contract = market['contract']
        stopPrice = self.safe_number(params, 'stopPrice')
        methodTail = 'Orders'
        reduceOnly = self.safe_value_2(params, 'reduce_only', 'reduceOnly')
        defaultTimeInForce = self.safe_value_2(params, 'tif', 'time_in_force', 'gtc')
        timeInForce = self.safe_value(params, 'timeInForce', defaultTimeInForce)
        params = self.omit(params, ['stopPrice', 'reduce_only', 'reduceOnly', 'tif', 'time_in_force', 'timeInForce'])
        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:
                timeInForce = 'ioc'
                price = 0
        elif not isLimitOrder:
            # Gateio doesn't have market orders for spot
            raise InvalidOrder(self.id + ' createOrder() does not support ' + type + ' orders for ' + market['type'] + ' markets')
        request = None
        trigger = self.safe_value(params, 'trigger')
        if stopPrice is None 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:
                options = self.safe_value(self.options, 'createOrder', {})
                defaultAccount = self.safe_string(options, 'account', 'spot')
                account = self.safe_string(params, 'account', defaultAccount)
                params = self.omit(params, 'account')
                # spot order
                request = {
                    # 'text': clientOrderId,  # 't-abcdef1234567890',
                    'currency_pair': market['id'],  # filled in prepareRequest above
                    'type': type,
                    'account': account,  # '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
                rule = 1 if (side == 'sell') else 2
                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,
                    },
                    '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, stopPrice),  # 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
                    },
                    'settle': market['settleId'],
                }
                expiration = self.safe_integer(params, 'expiration')
                if expiration is not None:
                    request['trigger']['expiration'] = expiration
                    params = self.omit(params, 'expiration')
                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', {})
                defaultAccount = self.safe_string(options, 'account', 'normal')
                account = self.safe_string(params, 'account', defaultAccount)
                params = self.omit(params, 'account')
                defaultExpiration = self.safe_integer(options, 'expiration')
                expiration = self.safe_integer(params, 'expiration', defaultExpiration)
                rule = '>=' if (side == 'sell') else '<='
                triggerPrice = self.safe_value(trigger, 'price', stopPrice)
                request = {
                    'trigger': {
                        'price': self.price_to_precision(symbol, triggerPrice),
                        '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
                    },
                    'put': {
                        'type': type,
                        'side': side,
                        'price': self.price_to_precision(symbol, price),
                        'amount': self.amount_to_precision(symbol, amount),
                        'account': account,  # normal, margin
                        'time_in_force': timeInForce,  # gtc, ioc for taker only
                    },
                    'market': market['id'],
                }
            methodTail = 'PriceOrders'
        method = self.get_supported_mapping(market['type'], {
            'spot': 'privateSpotPost' + methodTail,
            'margin': 'privateSpotPost' + methodTail,
            'swap': 'privateFuturesPostSettle' + methodTail,
            'future': 'privateDeliveryPostSettle' + methodTail,
        })
        response = 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 = {
            'filled': 'closed',
            'cancelled': 'canceled',
            'liquidated': 'closed',
        }
        return self.safe_string(statuses, status, status)

    def parse_order(self, order, market=None):
        #
        # createOrder, spot
        #
        #     {
        #       "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"
        #     }
        #
        #
        id = self.safe_string(order, 'id')
        clientOrderId = self.safe_string(order, 'text')
        marketId = self.safe_string_2(order, 'currency_pair', 'contract')
        symbol = self.safe_symbol(marketId, market)
        timestamp = self.safe_timestamp(order, 'create_time')
        timestamp = self.safe_integer(order, 'create_time_ms', timestamp)
        lastTradeTimestamp = self.safe_timestamp(order, 'update_time')
        lastTradeTimestamp = self.safe_integer(order, 'update_time_ms', lastTradeTimestamp)
        amountRaw = self.safe_string_2(order, 'amount', 'size')
        amount = Precise.string_abs(amountRaw)
        price = self.safe_string(order, 'price')
        # average = self.safe_string(order, 'fill_price')
        remaining = self.safe_string(order, 'left')
        cost = self.safe_string(order, 'filled_total')  # same as filled_price
        rawStatus = None
        side = None
        contract = self.safe_value(market, 'contract')
        if contract:
            if amount:
                side = 'buy' if Precise.string_gt(amountRaw, '0') else 'sell'
            else:
                side = None
            rawStatus = self.safe_string(order, 'finish_as', 'open')
        else:
            # open, closed, cancelled - almost already ccxt unified!
            rawStatus = self.safe_string(order, 'status')
            side = self.safe_string(order, 'side')
        status = self.parse_order_status(rawStatus)
        type = self.safe_string(order, 'type')
        timeInForce = self.safe_string_upper_2(order, 'time_in_force', 'tif')
        fees = []
        gtFee = self.safe_number(order, 'gt_fee')
        if gtFee:
            fees.append({
                'currency': 'GT',
                'cost': gtFee,
            })
        fee = self.safe_number(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': self.parse_number(Precise.string_neg(rebate)),
            })
        mkfr = self.safe_number(order, 'mkfr')
        tkfr = self.safe_number(order, 'tkfr')
        if mkfr:
            fees.append({
                'currency': self.safe_currency_code(self.safe_string(order, 'settleId')),
                'cost': mkfr,
            })
        if tkfr:
            fees.append({
                'currency': self.safe_currency_code(self.safe_string(market, 'settleId')),
                'cost': tkfr,
            })
        return self.safe_order({
            'id': id,
            'clientOrderId': clientOrderId,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': lastTradeTimestamp,
            'status': status,
            'symbol': symbol,
            'type': type,
            'timeInForce': timeInForce,
            'postOnly': None,
            'side': side,
            'price': price,
            'stopPrice': None,
            'average': None,
            'amount': amount,
            'cost': cost,
            'filled': None,
            'remaining': remaining,
            'fee': None,
            'fees': fees,
            'trades': None,
            'info': order,
        }, market)

    def fetch_order(self, id, symbol=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrder() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request = {
            'order_id': id,
        }
        if market['spot'] or market['margin']:
            request['currency_pair'] = market['id']
        else:
            request['settle'] = market['settleId']
        method = self.get_supported_mapping(market['type'], {
            'spot': 'privateSpotGetOrdersOrderId',
            'margin': 'privateSpotGetOrdersOrderId',
            'swap': 'privateFuturesGetSettleOrdersOrderId',
            'future': 'privateDeliveryGetSettlePriceOrdersOrderId',
        })
        response = getattr(self, method)(self.extend(request, params))
        return self.parse_order(response, market)

    def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        defaultType = self.safe_string_2(self.options, 'fetchMarkets', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        if symbol is None and (type == 'spot') or type == 'margin' or type == 'cross_margin':
            request = {
                # 'page': 1,
                # 'limit': limit,
                'account': type,  # spot/margin(default), cross_margin
            }
            if limit is not None:
                request['limit'] = limit
            response = self.privateSpotGetOpenOrders(self.extend(request, params))
            #
            #     [
            #         {
            #             "currency_pair": "ETH_BTC",
            #             "total": 1,
            #             "orders": [
            #                 {
            #                     "id": "12332324",
            #                     "text": "t-123456",
            #                     "create_time": "1548000000",
            #                     "update_time": "1548000100",
            #                     "currency_pair": "ETH_BTC",
            #                     "status": "open",
            #                     "type": "limit",
            #                     "account": "spot",
            #                     "side": "buy",
            #                     "amount": "1",
            #                     "price": "5.00032",
            #                     "time_in_force": "gtc",
            #                     "left": "0.5",
            #                     "filled_total": "2.50016",
            #                     "fee": "0.005",
            #                     "fee_currency": "ETH",
            #                     "point_fee": "0",
            #                     "gt_fee": "0",
            #                     "gt_discount": False,
            #                     "rebated_fee": "0",
            #                     "rebated_fee_currency": "BTC"
            #                 }
            #             ]
            #         },
            #         ...
            #     ]
            #
            allOrders = []
            for i in range(0, len(response)):
                entry = response[i]
                orders = self.safe_value(entry, 'orders', [])
                parsed = self.parse_orders(orders, None, since, limit)
                allOrders = self.array_concat(allOrders, parsed)
            return self.filter_by_since_limit(allOrders, since, limit)
        return self.fetch_orders_by_status('open', symbol, since, limit, params)

    def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
        return self.fetch_orders_by_status('finished', symbol, since, limit, params)

    def fetch_orders_by_status(self, status, symbol=None, since=None, limit=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrdersByStatus requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request = self.prepare_request(market)
        request['status'] = status
        if limit is not None:
            request['limit'] = limit
        if since is not None and (market['spot'] or market['margin']):
            request['start'] = int(since / 1000)
        method = self.get_supported_mapping(market['type'], {
            'spot': 'privateSpotGetOrders',
            'margin': 'privateSpotGetOrders',
            'swap': 'privateFuturesGetSettleOrders',
            'future': 'privateDeliveryGetSettleOrders',
        })
        if market['type'] == 'margin' or market['type'] == 'cross_margin':
            request['account'] = market['type']
        response = getattr(self, method)(self.extend(request, params))
        # 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",
        #     "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"
        # }
        # 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"
        # }
        return self.parse_orders(response, market, since, limit)

    def cancel_order(self, id, symbol=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request = {
            'order_id': id,
        }
        if market['contract']:
            request['settle'] = market['settleId']
        else:
            request['currency_pair'] = market['id']
        method = self.get_supported_mapping(market['type'], {
            'spot': 'privateSpotDeleteOrdersOrderId',
            'margin': 'privateSpotDeleteOrdersOrderId',
            'swap': 'privateFuturesDeleteSettleOrdersOrderId',
            'future': 'privateDeliveryDeleteSettleOrdersOrderId',
        })
        response = getattr(self, method)(self.extend(request, params))
        #
        # 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)

    def cancel_all_orders(self, symbol=None, params={}):
        self.load_markets()
        request = {}
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        return self.privateSpotDeleteOrders(self.extend(request, params))

    def transfer(self, code, amount, fromAccount, toAccount, params={}):
        self.load_markets()
        currency = self.currency(code)
        accountsByType = self.safe_value(self.options, 'accountsByType', {})
        fromId = self.safe_string(accountsByType, fromAccount, fromAccount)
        toId = self.safe_string(accountsByType, toAccount, toAccount)
        if fromId is None:
            keys = list(accountsByType.keys())
            raise ExchangeError(self.id + ' fromAccount must be one of ' + ', '.join(keys))
        if toId is None:
            keys = list(accountsByType.keys())
            raise ExchangeError(self.id + ' toAccount must be one of ' + ', '.join(keys))
        truncated = self.currency_to_precision(code, amount)
        request = {
            'currency': currency['id'],
            'from': fromId,
            'to': toId,
            'amount': truncated,
        }
        if (toId == 'future') or (toId == 'delivery'):
            request['settle'] = currency['lowerCaseId']
        response = self.privateWalletPostTransfers(self.extend(request, params))
        #
        # according to the docs
        #
        #     {
        #       "currency": "BTC",
        #       "from": "spot",
        #       "to": "margin",
        #       "amount": "1",
        #       "currency_pair": "BTC_USDT"
        #     }
        #
        # actual response
        #
        #  POST https://api.gateio.ws/api/v4/wallet/transfers 204 No Content
        #
        return {
            'info': response,
            'from': fromId,
            'to': toId,
            'amount': truncated,
            'code': code,
        }

    def set_leverage(self, leverage, symbol=None, params={}):
        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 + ' leverage should be between 1 and 100')
        self.load_markets()
        market = self.market(symbol)
        method = self.get_supported_mapping(market['type'], {
            'swap': 'privateFuturesPostSettlePositionsContractLeverage',
            'future': 'privateDeliveryPostSettlePositionsContractLeverage',
        })
        request = self.prepare_request(market)
        request['query'] = {
            'leverage': str(leverage),
        }
        if 'cross_leverage_limit' in params:
            if leverage != 0:
                raise BadRequest(self.id + ' cross margin leverage(valid only when leverage is 0)')
            request['cross_leverage_limit'] = str(params['cross_leverage_limit'])
            params = self.omit(params, 'cross_leverage_limit')
        response = getattr(self, method)(self.extend(request, params))
        #
        #     {
        #         "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 = 'buy'
        elif Precise.string_lt(size, '0'):
            side = 'sell'
        maintenanceRate = self.safe_string(position, 'maintenance_rate')
        notional = self.safe_string(position, 'value')
        leverage = self.safe_string(position, 'leverage')
        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,
            '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(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'),
            'marginType': None,
            'side': side,
            'percentage': self.parse_number(percentage),
        }

    def parse_positions(self, positions):
        result = []
        for i in range(0, len(positions)):
            result.append(self.parse_position(positions[i]))
        return result

    def fetch_positions(self, symbols=None, params={}):
        # :param symbols: Not used by Gateio
        # :param params:
        #    settle: The currency that derivative contracts are settled in
        #    Other exchange specific params
        #
        self.load_markets()
        defaultType = self.safe_string_2(self.options, 'fetchPositions', 'defaultType', 'swap')
        type = self.safe_string(params, 'type', defaultType)
        method = self.get_supported_mapping(type, {
            'swap': 'privateFuturesGetSettlePositions',
            'future': 'privateDeliveryGetSettlePositions',
        })
        defaultSettle = 'usdt' if (type == 'swap') else 'btc'
        settle = self.safe_string_lower(params, 'settle', defaultSettle)
        request = {
            'settle': settle,
        }
        response = getattr(self, method)(request)
        #
        #     [
        #         {
        #             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"
        #         }
        #     ]
        #
        result = self.parse_positions(response)
        return self.filter_by_array(result, 'symbol', symbols, False)

    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
        url = self.urls['api'][authentication] + entirePath
        if authentication == 'public':
            if query:
                url += '?' + self.urlencode(query)
        else:
            queryString = ''
            if (method == 'GET') or (method == 'DELETE'):
                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)
