# -*- 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': {
                'cancelOrder': True,
                'createMarketOrder': False,
                'createOrder': True,
                'fetchBalance': True,
                'fetchClosedOrders': True,
                'fetchCurrencies': True,
                'fetchDeposits': True,
                'fetchFundingRate': True,
                'fetchFundingRateHistory': True,
                'fetchFundingRates': True,
                'fetchIndexOHLCV': True,
                'fetchMarkets': True,
                'fetchMarkOHLCV': True,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchPremiumIndexOHLCV': False,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': False,
                'fetchTrades': True,
                'fetchWithdrawals': 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,
                        },
                    },
                    '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,
                        },
                    },
                },
                '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,
                        },
                    },
                },
            },
            'timeframes': {
                '10s': '10s',
                '1m': '1m',
                '5m': '5m',
                '15m': '15m',
                '30m': '30m',
                '1h': '1h',
                '4h': '4h',
                '8h': '8h',
                '1d': '1d',
                '7d': '7d',
            },
            # copied from gateiov2
            'commonCurrencies': {
                '88MPH': 'MPH',
                'BIFI': 'Bitcoin File',
                'BOX': 'DefiBox',
                'BTCBEAR': 'BEAR',
                'BTCBULL': 'BULL',
                'BYN': 'Beyond Finance',
                'EGG': 'Goose Finance',
                'GTC': 'Game.com',  # conflict with Gitcoin and Gastrocoin
                'GTC_HT': 'Game.com HT',
                'GTC_BSC': 'Game.com BSC',
                'HIT': 'HitChain',
                'MPH': 'Morpher',  # conflict with 88MPH
                'RAI': 'Rai Reflex Index',  # conflict with RAI Finance
                'SBTC': 'Super Bitcoin',
                'STX': 'Stox',
                'TNC': 'Trinity Network Credit',
                'TON': 'TONToken',
                'VAI': 'VAIOT',
            },
            'requiredCredentials': {
                'apiKey': True,
                'secret': True,
            },
            'options': {
                'networks': {
                    'TRC20': 'TRX',
                    'ERC20': 'ETH',
                    'BEP20': 'BSC',
                },
                'accountsByType': {
                    'spot': 'spot',
                    'margin': 'margin',
                    'futures': 'futures',
                    'delivery': 'delivery',
                },
                'defaultType': 'spot',
                'swap': {
                    'fetchMarkets': {
                        'settlementCurrencies': ['usdt', 'btc'],
                    },
                },
                'futures': {
                    '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': {
                '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,
            },
        })

    def fetch_markets(self, params={}):
        # :param params['type']: 'spot', 'margin', 'futures' 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')
        futures = (type == 'futures')
        swap = (type == 'swap')
        option = (type == 'option')
        if not spot and not margin and not futures and not swap:
            raise ExchangeError(self.id + " does not support '" + type + "' type, set exchange.options['defaultType'] to " + "'spot', 'margin', 'swap' or 'futures'")  # eslint-disable-line quotes
        response = None
        result = []
        method = self.get_supported_mapping(type, {
            'spot': 'publicSpotGetCurrencyPairs',
            'margin': 'publicMarginGetCurrencyPairs',
            'swap': 'publicFuturesGetSettleContracts',
            'futures': 'publicDeliveryGetSettleContracts',
        })
        if swap or futures or option:
            settlementCurrencies = self.get_settlement_currencies(type, 'fetchMarkets')
            for c in range(0, len(settlementCurrencies)):
                settle = settlementCurrencies[c]
                query['settle'] = settle
                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')
                    baseId, quoteId, date = id.split('_')
                    linear = quoteId.lower() == settle
                    inverse = baseId.lower() == settle
                    base = self.safe_currency_code(baseId)
                    quote = self.safe_currency_code(quoteId)
                    symbol = ''
                    if date:
                        symbol = base + '/' + quote + '-' + date + ':' + self.safe_currency_code(settle)
                    else:
                        symbol = base + '/' + quote + ':' + self.safe_currency_code(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)
                    feeIndex = 'swap' if (type == 'futures') else type
                    pricePrecision = self.safe_number(market, 'order_price_round')
                    result.append({
                        'info': market,
                        'id': id,
                        'baseId': baseId,
                        'quoteId': quoteId,
                        'settleId': self.safe_symbol(settle),
                        'base': base,
                        'quote': quote,
                        'symbol': symbol,
                        'type': type,
                        'spot': spot,
                        'margin': margin,
                        'futures': futures,
                        'swap': swap,
                        'option': option,
                        'derivative': True,
                        'contract': True,
                        'linear': linear,
                        'inverse': inverse,
                        # 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': self.safe_string(market, 'quanto_multiplier'),
                        '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,
                            },
                        },
                        'expiry': self.safe_integer(market, 'expire_time'),
                        'fees': self.safe_value(self.fees, feeIndex, {}),
                    })
        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')
                futures = (type == 'futures')
                swap = (type == 'swap')
                option = (type == 'option')
                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({
                    'info': market,
                    'id': id,
                    'baseId': baseId,
                    'quoteId': quoteId,
                    'base': base,
                    'quote': quote,
                    'symbol': symbol,
                    'type': type,
                    'spot': spot,
                    'margin': margin,
                    'futures': futures,
                    'swap': swap,
                    'option': option,
                    'contract': False,
                    'derivative': False,
                    'linear': False,
                    'inverse': False,
                    # 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')),
                    'precision': {
                        'amount': amountPrecision,
                        'price': pricePrecision,
                    },
                    'active': tradeStatus == 'tradable',
                    'limits': {
                        'amount': {
                            'min': amountPrecision,
                            'max': None,
                        },
                        'price': {
                            'min': pricePrecision,
                            'max': None,
                        },
                        'cost': {
                            'min': self.safe_number(market, 'min_quote_amount'),
                            'max': None,
                        },
                        'leverage': {
                            'max': self.safe_number(market, 'lever', 1),
                        },
                    },
                })
        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 = type == ['usdt'] if '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')
            code = self.safe_currency_code(currencyId)
            delisted = self.safe_value(entry, 'delisted')
            withdraw_disabled = self.safe_value(entry, 'withdraw_disabled')
            deposit_disabled = self.safe_value(entry, 'disabled_disabled')
            trade_disabled = self.safe_value(entry, 'trade_disabled')
            active = not (delisted and withdraw_disabled and deposit_disabled and trade_disabled)
            result[code] = {
                'id': currencyId,
                'name': None,
                'code': code,
                'precision': amountPrecision,
                'info': entry,
                'active': active,
                'fee': None,
                'fees': [],
                'limits': self.limits,
            }
        return result

    def fetch_funding_rate(self, symbol, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = {
            'contract': market['id'],
            'settle': market['quote'].lower(),
        }
        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(params, 'settle')  # TODO: Save settle in markets?
        request = {
            'settle': settle.lower(),
        }
        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')
        fundingInterval = self.safe_string(contract, 'funding_interval') * 1000
        nextFundingTime = self.safe_integer(contract, 'funding_next_apply') * 1000
        previousFundingTime = (self.safe_number(contract, 'funding_next_apply') * 1000) - fundingInterval
        fundingRateIndicative = self.safe_number(contract, 'funding_rate_indicative')
        timestamp = self.milliseconds()
        return {
            'info': contract,
            'symbol': symbol,
            'markPrice': markPrice,
            'indexPrice': indexPrice,
            'interestRate': interestRate,
            'estimatedSettlePrice': None,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'previousFundingRate': fundingRate,
            'nextFundingRate': fundingRateIndicative,
            'previousFundingTimestamp': previousFundingTime,
            'nextFundingTimestamp': nextFundingTime,
            'previousFundingDatetime': self.iso8601(previousFundingTime),
            '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={}):
        self.load_markets()
        # defaultType = 'future'
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchFundingHistory() requires the argument "symbol"')
        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',
            'futures': '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'),
            })
        return result

    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)
        spot = market['spot']
        method = self.get_supported_mapping(market['type'], {
            'spot': 'publicSpotGetOrderBook',
            # 'margin': 'publicMarginGetOrderBook',
            'swap': 'publicFuturesGetSettleOrderBook',
            'futures': '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 spot:
            timestamp = timestamp * 1000
        priceKey = 0 if spot else 'p'
        amountKey = 1 if spot 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': 'publicMarginGetTickers',
            'swap': 'publicFuturesGetSettleTickers',
            'futures': '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(ticker, 'base_volume', 'volume_24h_base')
        quoteVolume = self.safe_number(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': 'publicMarginGetTickers',
            'swap': 'publicFuturesGetSettleTickers',
            'futures': 'publicDeliveryGetSettleTickers',
        })
        request = {}
        futures = type == 'futures'
        swap = type == 'swap'
        if (swap or futures) and not params['settle']:
            request['settle'] = 'usdt' if swap else 'btc'
        response = getattr(self, method)(self.extend(request, params))
        return self.parse_tickers(response, symbols)

    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 futures
        self.load_markets()
        defaultType = self.safe_string_2(self.options, 'fetchBalance', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        params = self.omit(params, 'type')
        swap = type == 'swap'
        futures = type == 'futures'
        method = self.get_supported_mapping(type, {
            'spot': 'privateSpotGetAccounts',
            # 'margin': 'publicMarginGetTickers',
            'swap': 'privateFuturesGetSettleAccounts',
            'futures': 'privateDeliveryGetSettleAccounts',
        })
        request = {}
        response = []
        if swap or futures:
            defaultSettle = 'usdt' if swap else 'btc'
            request['settle'] = self.safe_string(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"
        #       },
        #       ...
        #     ]
        #
        #  Perpetual Swap
        #  {
        #     order_margin: "0",
        #     point: "0",
        #     bonus: "0",
        #     history: {
        #       dnw: "2.1321",
        #       pnl: "11.5351",
        #       refr: "0",
        #       point_fee: "0",
        #       fund: "-0.32340576684",
        #       bonus_dnw: "0",
        #       point_refr: "0",
        #       bonus_offset: "0",
        #       fee: "-0.20132775",
        #       point_dnw: "0",
        #     },
        #     unrealised_pnl: "13.315100000006",
        #     total: "12.51345151332",
        #     available: "0",
        #     in_dual_mode: False,
        #     currency: "USDT",
        #     position_margin: "12.51345151332",
        #     user: "6333333",
        #   }
        #
        #   Delivery Future
        #   {
        #     order_margin: "0",
        #     point: "0",
        #     history: {
        #       dnw: "1",
        #       pnl: "0",
        #       refr: "0",
        #       point_fee: "0",
        #       point_dnw: "0",
        #       settle: "0",
        #       settle_fee: "0",
        #       point_refr: "0",
        #       fee: "0",
        #     },
        #     unrealised_pnl: "0",
        #     total: "1",
        #     available: "1",
        #     currency: "USDT",
        #     position_margin: "0",
        #     user: "6333333",
        #   }
        result = {}
        for i in range(0, len(response)):
            entry = response[i]
            account = self.account()
            currencyId = self.safe_string(entry, 'currency')
            code = self.safe_currency_code(currencyId)
            account['used'] = self.safe_string_2(entry, 'locked', 'position_margin')
            account['free'] = self.safe_string(entry, 'available')
            result[code] = account
        return self.parse_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']:
            if market['futures']:
                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')
        if since is None:
            if limit is not None:
                request['limit'] = limit
        else:
            request['from'] = int(since / 1000)
            if limit is not None:
                request['to'] = self.sum(request['from'], limit * self.parse_timeframe(timeframe) - 1)
        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, limit=None, since=None, params={}):
        self.load_markets()
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchFundingRateHistory() requires a symbol argument')
        market = self.market(symbol)
        request = {
            'contract': market['id'],
            'settle': market['quote'].lower(),
        }
        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),
            })
        return self.sort_by(rates, 'timestamp')

    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, futures
        #
        #     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': 'publicMarginGetTickers',
            'swap': 'publicFuturesGetSettleTrades',
            'futures': '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': 'publicMarginGetCurrencyPairs',
            'swap': 'privateFuturesGetSettleMyTrades',
            'futures': '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')
        costString = Precise.string_abs(Precise.string_mul(amountString, priceString))
        price = self.parse_number(priceString)
        cost = self.parse_number(costString)
        contractSide = 'sell' if Precise.string_lt(amountString, '0') else 'buy'
        amountString = Precise.string_abs(amountString)
        amount = self.parse_number(amountString)
        side = self.safe_string(trade, 'side', contractSide)
        orderId = self.safe_string(trade, 'order_id')
        gtFee = self.safe_string(trade, 'gt_fee')
        feeCurrency = None
        feeCost = None
        if gtFee == '0':
            feeCurrency = self.safe_string(trade, 'fee_currency')
            feeCost = self.safe_number(trade, 'fee')
        else:
            feeCurrency = 'GT'
            feeCost = self.parse_number(gtFee)
        fee = {
            'cost': feeCost,
            'currency': feeCurrency,
        }
        takerOrMaker = self.safe_string(trade, 'role')
        return {
            'info': trade,
            'id': id,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'order': orderId,
            'type': None,
            'side': side,
            'takerOrMaker': takerOrMaker,
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': fee,
        }

    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,
            'address': address,
            'tag': tag,
            'status': status,
            'type': type,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'fee': fee,
        }

    def create_order(self, symbol, type, side, amount, price=None, params={}):
        #
        # :param(str) symbol: base/quote currency pair
        # :param(str) type: Order type(limit, market, ...)
        # :param(str) side: buy or sell
        # :param(number) amount: Amount of base currency ordered
        # :param(number) price: Price of the base currency using quote currency
        # :param(dict) params:
        #          - type: market type(spot, futures, ...)
        #          - reduceOnly
        self.load_markets()
        market = self.market(symbol)
        defaultType = self.safe_string_2(self.options, 'createOrder', 'defaultType', 'spot')
        marketType = self.safe_string(params, 'type', defaultType)
        contract = market['contract']
        request = self.prepare_request(market)
        reduceOnly = self.safe_value(params, 'reduceOnly')
        params = self.omit(params, 'reduceOnly')
        if reduceOnly is not None:
            if not contract:
                raise InvalidOrder(self.id + ' createOrder() does not support reduceOnly for ' + marketType + ' orders, reduceOnly orders are supported for futures and perpetuals only')
            request['reduce_only'] = reduceOnly
        if contract:
            if side == 'sell':
                amount = 0 - amount
            request['size'] = self.parse_number(self.amount_to_precision(symbol, amount))
        else:
            request['side'] = side
            request['type'] = type
            request['amount'] = self.amount_to_precision(symbol, amount)
            request['account'] = marketType
            # if margin:
            #     if entering trade:
            #         request['auto_borrow'] = True
            #     elif exiting trade:
            #         request['auto_repay'] = True
            #     }
            # }
        if type == 'limit':
            if not price:
                raise ArgumentsRequired('Argument price is required for ' + self.id + '.createOrder for limit orders')
            request['price'] = self.price_to_precision(symbol, price)
        elif (type == 'market') and contract:
            request['tif'] = 'ioc'
            request['price'] = 0
        method = self.get_supported_mapping(market['type'], {
            'spot': 'privateSpotPostOrders',
            # 'margin': 'privateSpotPostOrders',
            'swap': 'privateFuturesPostSettleOrders',
            'future': 'privateDeliveryPostSettleOrders',
        })
        response = getattr(self, method)(self.extend(request, params))
        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')
        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:
            side = 'buy' if Precise.string_gt(amountRaw, '0') else 'sell'
            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_order2({
            'id': id,
            'clientOrderId': id,
            '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': average,
            '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': 'publicMarginGetTickers',
            'swap': 'privateFuturesGetSettleOrdersOrderId',
            'futures': '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={}):
        self.load_markets()
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrdersByStatus requires a symbol argument')
        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',
            'futures': '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={}):
        self.load_markets()
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrders requires a symbol parameter')
        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',
            'futures': 'privateDeliveryDeleteSettleOrdersOrderId',
        })
        response = getattr(self, method)(self.extend(request, params))
        # Perpetual swap
        # {
        #     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 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 == 'futures') or (toId == 'delivery'):
            request['settle'] = currency['id']
        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',
            'futures': '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 sign(self, path, api=[], method='GET', params={}, headers=None, body=None):
        authentication = api[0]  # public, private
        type = api[1]  # spot, margin, futures, delivery
        query = self.omit(params, self.extract_params(path))
        path = self.implode_params(path, params)
        endPart = (path == '' if '' 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):
        label = self.safe_string(response, 'label')
        if label is not None:
            message = self.safe_string_2(response, 'message', 'detail', '')
            Error = self.safe_value(self.exceptions, label, ExchangeError)
            raise Error(self.id + ' ' + message)