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

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

from ccxt.async_support.base.exchange import Exchange
import hashlib
import math
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import AuthenticationError
from ccxt.base.errors import PermissionDenied
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 InvalidAddress
from ccxt.base.errors import InvalidOrder
from ccxt.base.errors import OrderNotFound
from ccxt.base.errors import RateLimitExceeded
from ccxt.base.decimal_to_precision import TICK_SIZE
from ccxt.base.precise import Precise


class mexc(Exchange):

    def describe(self):
        return self.deep_extend(super(mexc, self).describe(), {
            'id': 'mexc',
            'name': 'MEXC Global',
            'countries': ['SC'],  # Seychelles
            'rateLimit': 50,  # default rate limit is 20 times per second
            'version': 'v2',
            'certified': True,
            'has': {
                'spot': True,
                'margin': None,
                'swap': True,
                'future': None,
                'option': None,
                'addMargin': True,
                'cancelAllOrders': True,
                'cancelOrder': True,
                'createMarketOrder': False,
                'createOrder': True,
                'fetchBalance': True,
                'fetchCanceledOrders': True,
                'fetchClosedOrders': True,
                'fetchCurrencies': True,
                'fetchDepositAddress': True,
                'fetchDepositAddressesByNetwork': True,
                'fetchDeposits': True,
                'fetchFundingRateHistory': True,
                'fetchMarkets': True,
                'fetchMarketsByType': True,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrdersByState': True,
                'fetchOrderTrades': True,
                'fetchPosition': True,
                'fetchPositions': True,
                'fetchStatus': True,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': True,
                'fetchTrades': True,
                'fetchWithdrawals': True,
                'reduceMargin': True,
                'setLeverage': True,
                'withdraw': True,
            },
            'timeframes': {
                '1m': '1m',
                '5m': '5m',
                '15m': '15m',
                '30m': '30m',
                '1h': '1h',
                '1d': '1d',
                '1w': '1w',
                '1M': '1M',
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/137283979-8b2a818d-8633-461b-bfca-de89e8c446b2.jpg',
                'api': {
                    'spot': {
                        'public': 'https://www.mexc.com/open/api/v2',
                        'private': 'https://www.mexc.com/open/api/v2',
                    },
                    'contract': {
                        'public': 'https://contract.mexc.com/api/v1/contract',
                        'private': 'https://contract.mexc.com/api/v1/private',
                    },
                },
                'www': 'https://www.mexc.com/',
                'doc': [
                    'https://mxcdevelop.github.io/APIDoc/',
                ],
                'fees': [
                    'https://www.mexc.com/fee',
                ],
                'referral': 'https://m.mexc.com/auth/signup?inviteCode=1FQ1G',
            },
            'api': {
                'contract': {
                    'public': {
                        'get': {
                            'ping': 2,
                            'detail': 2,
                            'support_currencies': 2,
                            'depth/{symbol}': 2,
                            'depth_commits/{symbol}/{limit}': 2,
                            'index_price/{symbol}': 2,
                            'fair_price/{symbol}': 2,
                            'funding_rate/{symbol}': 2,
                            'kline/{symbol}': 2,
                            'kline/index_price/{symbol}': 2,
                            'kline/fair_price/{symbol}': 2,
                            'deals/{symbol}': 2,
                            'ticker': 2,
                            'risk_reverse': 2,
                            'risk_reverse/history': 2,
                            'funding_rate/history': 2,
                        },
                    },
                    'private': {
                        'get': {
                            'account/assets': 2,
                            'account/asset/{currency}': 2,
                            'account/transfer_record': 2,
                            'position/list/history_positions': 2,
                            'position/open_positions': 2,
                            'position/funding_records': 2,
                            'order/list/open_orders/{symbol}': 2,
                            'order/list/history_orders': 2,
                            'order/external/{symbol}/{external_oid}': 2,
                            'order/get/{order_id}': 2,
                            'order/batch_query': 8,
                            'order/deal_details/{order_id}': 2,
                            'order/list/order_deals': 2,
                            'planorder/list/orders': 2,
                            'stoporder/list/orders': 2,
                            'stoporder/order_details/{stop_order_id}': 2,
                            'account/risk_limit': 2,
                            'account/tiered_fee_rate': 2,
                        },
                        'post': {
                            'position/change_margin': 2,
                            'position/change_leverage': 2,
                            'order/submit': 2,
                            'order/submit_batch': 40,
                            'order/cancel': 2,
                            'order/cancel_with_external': 2,
                            'order/cancel_all': 2,
                            'account/change_risk_level': 2,
                            'planorder/place': 2,
                            'planorder/cancel': 2,
                            'planorder/cancel_all': 2,
                            'stoporder/cancel': 2,
                            'stoporder/cancel_all': 2,
                            'stoporder/change_price': 2,
                            'stoporder/change_plan_price': 2,
                        },
                    },
                },
                'spot': {
                    'public': {
                        'get': {
                            'market/symbols': 1,
                            'market/coin/list': 2,
                            'common/timestamp': 1,
                            'common/ping': 1,
                            'market/ticker': 1,
                            'market/depth': 1,
                            'market/deals': 1,
                            'market/kline': 1,
                        },
                    },
                    'private': {
                        'get': {
                            'account/info': 1,
                            'order/open_orders': 1,
                            'order/list': 1,
                            'order/query': 1,
                            'order/deals': 1,
                            'order/deal_detail': 1,
                            'asset/deposit/address/list': 2,
                            'asset/deposit/list': 2,
                            'asset/address/list': 2,
                            'asset/withdraw/list': 2,
                        },
                        'post': {
                            'order/place': 1,
                            'order/place_batch': 1,
                            'asset/withdraw': 1,
                        },
                        'delete': {
                            'order/cancel': 1,
                            'order/cancel_by_symbol': 1,
                        },
                    },
                },
            },
            'precisionMode': TICK_SIZE,
            'fees': {
                'trading': {
                    'tierBased': False,
                    'percentage': True,
                    'maker': 0.2 / 100,  # maker / taker
                    'taker': 0.2 / 100,
                },
            },
            'options': {
                'timeframes': {
                    'spot': {
                        '1m': '1m',
                        '5m': '5m',
                        '15m': '15m',
                        '30m': '30m',
                        '1h': '1h',
                        '1d': '1d',
                        '1M': '1M',
                    },
                    'contract': {
                        '1m': 'Min1',
                        '5m': 'Min5',
                        '15m': 'Min15',
                        '30m': 'Min30',
                        '1h': 'Min60',
                        '4h': 'Hour4',
                        '8h': 'Hour8',
                        '1d': 'Day1',
                        '1w': 'Week1',
                        '1M': 'Month1',
                    },
                },
                'defaultType': 'spot',  # spot, swap
                'networks': {
                    'TRX': 'TRC-20',
                    'TRC20': 'TRC-20',
                    'ETH': 'ERC-20',
                    'ERC20': 'ERC-20',
                    'BEP20': 'BEP20(BSC)',
                },
            },
            'commonCurrencies': {
                'BYN': 'BeyondFi',
                'COFI': 'COFIX',  # conflict with CoinFi
                'DFT': 'dFuture',
                'DRK': 'DRK',
                'FLUX1': 'FLUX',  # switched places
                'FLUX': 'FLUX1',  # switched places
                'FREE': 'FreeRossDAO',  # conflict with FREE Coin
                'HERO': 'Step Hero',  # conflict with Metahero
                'MIMO': 'Mimosa',
                'PROS': 'Pros.Finance',  # conflict with Prosper
                'SIN': 'Sin City Token',
            },
            'exceptions': {
                'exact': {
                    '400': BadRequest,  # Invalid parameter
                    '401': AuthenticationError,  # Invalid signature, fail to pass the validation
                    '429': RateLimitExceeded,  # too many requests, rate limit rule is violated
                    '1000': PermissionDenied,  # {"success":false,"code":1000,"message":"Please open contract account first!"}
                    '1002': InvalidOrder,  # {"success":false,"code":1002,"message":"Contract not allow place order!"}
                    '10072': AuthenticationError,  # Invalid access key
                    '10073': AuthenticationError,  # Invalid request time
                    '10216': InvalidAddress,  # {"code":10216,"msg":"No available deposit address"}
                    '10232': BadSymbol,  # {"code":10232,"msg":"The currency not exist"}
                    '30000': BadSymbol,  # Trading is suspended for the requested symbol
                    '30001': InvalidOrder,  # Current trading type(bid or ask) is not allowed
                    '30002': InvalidOrder,  # Invalid trading amount, smaller than the symbol minimum trading amount
                    '30003': InvalidOrder,  # Invalid trading amount, greater than the symbol maximum trading amount
                    '30004': InsufficientFunds,  # Insufficient balance
                    '30005': InvalidOrder,  # Oversell error
                    '30010': InvalidOrder,  # Price out of allowed range
                    '30016': BadSymbol,  # Market is closed
                    '30019': InvalidOrder,  # Orders count over limit for batch processing
                    '30020': BadSymbol,  # Restricted symbol, API access is not allowed for the time being
                    '30021': BadSymbol,  # Invalid symbol
                    '33333': BadSymbol,  # {"code":33333,"msg":"currency can not be null"}
                },
                'broad': {
                },
            },
        })

    async def fetch_time(self, params={}):
        defaultType = self.safe_string_2(self.options, 'fetchMarkets', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        query = self.omit(params, 'type')
        method = 'spotPublicGetCommonTimestamp'
        if type == 'contract':
            method = 'contractPublicGetPing'
        response = await getattr(self, method)(query)
        #
        # spot
        #
        #     {
        #         "code":200,
        #         "data":1633375641837
        #     }
        #
        # contract
        #
        #     {
        #         "success":true,
        #         "code":0,
        #         "data":1634095541710
        #     }
        #
        return self.safe_integer(response, 'data')

    async def fetch_status(self, params={}):
        response = await self.spotPublicGetCommonPing(params)
        #
        # {"code":200}
        #
        code = self.safe_integer(response, 'code')
        if code is not None:
            status = 'ok' if (code == 200) else 'maintenance'
            self.status = self.extend(self.status, {
                'status': status,
                'updated': self.milliseconds(),
            })
        return self.status

    async def fetch_currencies(self, params={}):
        response = await self.spotPublicGetMarketCoinList(params)
        #
        #     {
        #         "code":200,
        #         "data":[
        #             {
        #                 "currency":"AGLD",
        #                 "coins":[
        #                     {
        #                         "chain":"ERC20",
        #                         "precision":18,
        #                         "fee":8.09,
        #                         "is_withdraw_enabled":true,
        #                         "is_deposit_enabled":true,
        #                         "deposit_min_confirm":16,
        #                         "withdraw_limit_max":500000.0,
        #                         "withdraw_limit_min":14.0
        #                     }
        #                 ],
        #                 "full_name":"Adventure Gold"
        #             },
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        result = {}
        for i in range(0, len(data)):
            currency = data[i]
            id = self.safe_string(currency, 'currency')
            code = self.safe_currency_code(id)
            name = self.safe_string(currency, 'full_name')
            currencyActive = False
            currencyPrecision = None
            currencyFee = None
            currencyWithdrawMin = None
            currencyWithdrawMax = None
            networks = {}
            chains = self.safe_value(currency, 'coins', [])
            for j in range(0, len(chains)):
                chain = chains[j]
                networkId = self.safe_string(chain, 'chain')
                network = self.safe_network(networkId)
                isDepositEnabled = self.safe_value(chain, 'is_deposit_enabled', False)
                isWithdrawEnabled = self.safe_value(chain, 'is_withdraw_enabled', False)
                active = (isDepositEnabled and isWithdrawEnabled)
                currencyActive = active or currencyActive
                precisionDigits = self.safe_integer(chain, 'precision')
                precision = 1 / math.pow(10, precisionDigits)
                withdrawMin = self.safe_string(chain, 'withdraw_limit_min')
                withdrawMax = self.safe_string(chain, 'withdraw_limit_max')
                currencyWithdrawMin = withdrawMin if (currencyWithdrawMin is None) else currencyWithdrawMin
                currencyWithdrawMax = withdrawMax if (currencyWithdrawMax is None) else currencyWithdrawMax
                if Precise.string_gt(currencyWithdrawMin, withdrawMin):
                    currencyWithdrawMin = withdrawMin
                if Precise.string_lt(currencyWithdrawMax, withdrawMax):
                    currencyWithdrawMax = withdrawMax
                networks[network] = {
                    'info': chain,
                    'id': networkId,
                    'network': network,
                    'active': active,
                    'fee': self.safe_number(chain, 'fee'),
                    'precision': precision,
                    'limits': {
                        'withdraw': {
                            'min': withdrawMin,
                            'max': withdrawMax,
                        },
                    },
                }
            networkKeys = list(networks.keys())
            networkKeysLength = len(networkKeys)
            if (networkKeysLength == 1) or ('NONE' in networks):
                defaultNetwork = self.safe_value_2(networks, 'NONE', networkKeysLength - 1)
                if defaultNetwork is not None:
                    currencyFee = defaultNetwork['fee']
                    currencyPrecision = defaultNetwork['precision']
            result[code] = {
                'id': id,
                'code': code,
                'info': currency,
                'name': name,
                'active': currencyActive,
                'fee': currencyFee,
                'precision': currencyPrecision,
                'limits': {
                    'amount': {
                        'min': None,
                        'max': None,
                    },
                    'withdraw': {
                        'min': currencyWithdrawMin,
                        'max': currencyWithdrawMax,
                    },
                },
                'networks': networks,
            }
        return result

    async def fetch_markets_by_type(self, type, params={}):
        method = 'fetch_' + type + '_markets'
        return await getattr(self, method)(params)

    async def fetch_markets(self, params={}):
        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')
        swap = (type == 'swap')
        if not spot and not swap:
            raise ExchangeError(self.id + " does not support '" + type + "' type, set exchange.options['defaultType'] to 'spot', 'margin', 'delivery' or 'future'")  # eslint-disable-line quotes
        if spot:
            return await self.fetch_spot_markets(query)
        elif swap:
            return await self.fetch_contract_markets(query)

    async def fetch_contract_markets(self, params={}):
        response = await self.contractPublicGetDetail(params)
        #
        #     {
        #         "success":true,
        #         "code":0,
        #         "data":[
        #             {
        #                 "symbol":"BTC_USDT",
        #                 "displayName":"BTC_USDT永续",
        #                 "displayNameEn":"BTC_USDT SWAP",
        #                 "positionOpenType":3,
        #                 "baseCoin":"BTC",
        #                 "quoteCoin":"USDT",
        #                 "settleCoin":"USDT",
        #                 "contractSize":0.0001,
        #                 "minLeverage":1,
        #                 "maxLeverage":125,
        #                 "priceScale":2,
        #                 "volScale":0,
        #                 "amountScale":4,
        #                 "priceUnit":0.5,
        #                 "volUnit":1,
        #                 "minVol":1,
        #                 "maxVol":1000000,
        #                 "bidLimitPriceRate":0.1,
        #                 "askLimitPriceRate":0.1,
        #                 "takerFeeRate":0.0006,
        #                 "makerFeeRate":0.0002,
        #                 "maintenanceMarginRate":0.004,
        #                 "initialMarginRate":0.008,
        #                 "riskBaseVol":10000,
        #                 "riskIncrVol":200000,
        #                 "riskIncrMmr":0.004,
        #                 "riskIncrImr":0.004,
        #                 "riskLevelLimit":5,
        #                 "priceCoefficientVariation":0.1,
        #                 "indexOrigin":["BINANCE","GATEIO","HUOBI","MXC"],
        #                 "state":0,  # 0 enabled, 1 delivery, 2 completed, 3 offline, 4 pause
        #                 "isNew":false,
        #                 "isHot":true,
        #                 "isHidden":false
        #             },
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        result = []
        for i in range(0, len(data)):
            market = data[i]
            id = self.safe_string(market, 'symbol')
            baseId = self.safe_string(market, 'baseCoin')
            quoteId = self.safe_string(market, 'quoteCoin')
            settleId = self.safe_string(market, 'settleCoin')
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            settle = self.safe_currency_code(settleId)
            state = self.safe_string(market, 'state')
            result.append({
                'id': id,
                'symbol': base + '/' + quote + ':' + settle,
                'base': base,
                'quote': quote,
                'settle': settle,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': settleId,
                'type': 'swap',
                'spot': False,
                'margin': False,
                'swap': True,
                'future': False,
                'option': False,
                'contract': True,
                'linear': True,
                'inverse': False,
                'taker': self.safe_number(market, 'takerFeeRate'),
                'maker': self.safe_number(market, 'makerFeeRate'),
                'contractSize': self.safe_number(market, 'contractSize'),
                'active': (state == '0'),
                'expiry': None,
                'expiryDatetime': None,
                'strike': None,
                'optionType': None,
                'precision': {
                    'price': self.safe_number(market, 'priceUnit'),
                    'amount': self.safe_number(market, 'volUnit'),
                },
                'limits': {
                    'leverage': {
                        'min': self.safe_number(market, 'minLeverage'),
                        'max': self.safe_number(market, 'maxLeverage'),
                    },
                    'amount': {
                        'min': self.safe_number(market, 'minVol'),
                        'max': self.safe_number(market, 'maxVol'),
                    },
                    'price': {
                        'min': None,
                        'max': None,
                    },
                    'cost': {
                        'min': None,
                        'max': None,
                    },
                },
                'info': market,
            })
        return result

    async def fetch_spot_markets(self, params={}):
        response = await self.spotPublicGetMarketSymbols(params)
        #
        #     {
        #         "code":200,
        #         "data":[
        #             {
        #                 "symbol":"DFD_USDT",
        #                 "state":"ENABLED",
        #                 "countDownMark":1,
        #                 "vcoinName":"DFD",
        #                 "vcoinStatus":1,
        #                 "price_scale":4,
        #                 "quantity_scale":2,
        #                 "min_amount":"5",  # not an amount = cost
        #                 "max_amount":"5000000",
        #                 "maker_fee_rate":"0.002",
        #                 "taker_fee_rate":"0.002",
        #                 "limited":true,
        #                 "etf_mark":0,
        #                 "symbol_partition":"ASSESS"
        #             },
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        result = []
        for i in range(0, len(data)):
            market = data[i]
            id = self.safe_string(market, 'symbol')
            baseId, quoteId = id.split('_')
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            symbol = base + '/' + quote
            priceScale = self.safe_integer(market, 'price_scale')
            quantityScale = self.safe_integer(market, 'quantity_scale')
            pricePrecision = 1 / math.pow(10, priceScale)
            quantityPrecision = 1 / math.pow(10, quantityScale)
            state = self.safe_string(market, 'state')
            type = 'spot'
            result.append({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'settle': None,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': None,
                'type': type,
                'spot': True,
                'margin': False,
                'swap': False,
                'future': False,
                'option': False,
                'active': (state == 'ENABLED'),
                'contract': False,
                'linear': None,
                'inverse': None,
                'taker': self.safe_number(market, 'taker_fee_rate'),
                'maker': self.safe_number(market, 'maker_fee_rate'),
                'contractSize': None,
                'expiry': None,
                'expiryDatetime': None,
                'strike': None,
                'optionType': None,
                'precision': {
                    'price': pricePrecision,
                    'amount': quantityPrecision,
                },
                'limits': {
                    'leverage': {
                        'min': None,
                        'max': None,
                    },
                    'amount': {
                        'min': None,
                        'max': None,
                    },
                    'price': {
                        'min': None,
                        'max': None,
                    },
                    'cost': {
                        'min': self.safe_number(market, 'min_amount'),
                        'max': self.safe_number(market, 'max_amount'),
                    },
                },
                'info': market,
            })
        return result

    async def fetch_tickers(self, symbols=None, params={}):
        await self.load_markets()
        defaultType = self.safe_string_2(self.options, 'fetchTickers', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        query = self.omit(params, 'type')
        method = 'spotPublicGetMarketTicker'
        if type == 'swap':
            method = 'contractPublicGetTicker'
        response = await getattr(self, method)(self.extend(query))
        #
        #     {
        #         "success":true,
        #         "code":0,
        #         "data":[
        #             {
        #                 "symbol":"NKN_USDT",
        #                 "lastPrice":0.36199,
        #                 "bid1":0.35908,
        #                 "ask1":0.36277,
        #                 "volume24":657754,
        #                 "amount24":239024.53998,
        #                 "holdVol":149969,
        #                 "lower24Price":0.34957,
        #                 "high24Price":0.37689,
        #                 "riseFallRate":0.0117,
        #                 "riseFallValue":0.00419,
        #                 "indexPrice":0.36043,
        #                 "fairPrice":0.36108,
        #                 "fundingRate":0.000535,
        #                 "maxBidPrice":0.43251,
        #                 "minAskPrice":0.28834,
        #                 "timestamp":1634163352075
        #             },
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        return self.parse_tickers(data, symbols)

    async def fetch_ticker(self, symbol, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        method = None
        if market['spot']:
            method = 'spotPublicGetMarketTicker'
        elif market['swap']:
            method = 'contractPublicGetTicker'
        response = await getattr(self, method)(self.extend(request, params))
        #
        # spot
        #
        #     {
        #         "code":200,
        #         "data":[
        #             {
        #                 "symbol":"BTC_USDT",
        #                 "volume":"880.821523",
        #                 "high":"49496.95",  # highest price over the past 24 hours
        #                 "low":"46918.4",  # lowest
        #                 "bid":"49297.64",  # current buying price == the best price you can sell for
        #                 "ask":"49297.75",  # current selling price == the best price you can buy for
        #                 "open":"48764.9",  # open price 24h ago
        #                 "last":"49297.73",  # last = close
        #                 "time":1633378200000,  # timestamp
        #                 "change_rate":"0.0109265"  #(last / open) - 1
        #             }
        #         ]
        #     }
        #
        # swap / contract
        #
        #     {
        #         "success":true,
        #         "code":0,
        #         "data":{
        #             "symbol":"ETH_USDT",
        #             "lastPrice":3581.3,
        #             "bid1":3581.25,
        #             "ask1":3581.5,
        #             "volume24":4045530,
        #             "amount24":141331823.5755,
        #             "holdVol":5832946,
        #             "lower24Price":3413.4,
        #             "high24Price":3588.7,
        #             "riseFallRate":0.0275,
        #             "riseFallValue":95.95,
        #             "indexPrice":3580.7852,
        #             "fairPrice":3581.08,
        #             "fundingRate":0.000063,
        #             "maxBidPrice":3938.85,
        #             "minAskPrice":3222.7,
        #             "timestamp":1634162885016
        #         }
        #     }
        #
        if market['spot']:
            data = self.safe_value(response, 'data', [])
            ticker = self.safe_value(data, 0)
            return self.parse_ticker(ticker, market)
        elif market['swap']:
            data = self.safe_value(response, 'data', {})
            return self.parse_ticker(data, market)

    def parse_ticker(self, ticker, market=None):
        #
        # spot
        #
        #     {
        #         "symbol":"BTC_USDT",
        #         "volume":"880.821523",
        #         "high":"49496.95",
        #         "low":"46918.4",
        #         "bid":"49297.64",
        #         "ask":"49297.75",
        #         "open":"48764.9",
        #         "last":"49297.73",
        #         "time":1633378200000,
        #         "change_rate":"0.0109265"
        #     }
        #
        # contract
        #
        #     {
        #         "symbol":"ETH_USDT",
        #         "lastPrice":3581.3,
        #         "bid1":3581.25,
        #         "ask1":3581.5,
        #         "volume24":4045530,
        #         "amount24":141331823.5755,
        #         "holdVol":5832946,
        #         "lower24Price":3413.4,
        #         "high24Price":3588.7,
        #         "riseFallRate":0.0275,
        #         "riseFallValue":95.95,
        #         "indexPrice":3580.7852,
        #         "fairPrice":3581.08,
        #         "fundingRate":0.000063,
        #         "maxBidPrice":3938.85,
        #         "minAskPrice":3222.7,
        #         "timestamp":1634162885016
        #     }
        #
        timestamp = self.safe_integer_2(ticker, 'time', 'timestamp')
        marketId = self.safe_string(ticker, 'symbol')
        symbol = self.safe_symbol(marketId, market, '_')
        baseVolume = self.safe_number_2(ticker, 'volume', 'volume24')
        quoteVolume = self.safe_number(ticker, 'amount24')
        open = self.safe_number(ticker, 'open')
        lastString = self.safe_string_2(ticker, 'last', 'lastPrice')
        last = self.parse_number(lastString)
        change = self.safe_number(ticker, 'riseFallValue')
        riseFallRate = self.safe_string(ticker, 'riseFallRate')
        percentageString = Precise.string_add(riseFallRate, '1')
        percentage = self.parse_number(percentageString)
        return self.safe_ticker({
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_number_2(ticker, 'high', 'high24Price'),
            'low': self.safe_number_2(ticker, 'low', 'lower24Price'),
            'bid': self.safe_number_2(ticker, 'bid', 'bid1'),
            'bidVolume': None,
            'ask': self.safe_number_2(ticker, 'ask', 'ask1'),
            'askVolume': None,
            'vwap': None,
            'open': open,
            'close': last,
            'last': last,
            'previousClose': None,
            'change': change,
            'percentage': percentage,
            'average': None,
            'baseVolume': baseVolume,
            'quoteVolume': quoteVolume,
            'info': ticker,
        }, market)

    async def fetch_order_book(self, symbol, limit=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        method = None
        if market['spot']:
            method = 'spotPublicGetMarketDepth'
            if limit is None:
                limit = 100  # the spot api requires a limit
            request['depth'] = limit
        elif market['swap']:
            method = 'contractPublicGetDepthSymbol'
            if limit is not None:
                request['limit'] = limit
        response = await getattr(self, method)(self.extend(request, params))
        #
        # spot
        #
        #     {
        #         "code":200,
        #         "data":{
        #             "asks":[
        #                 {"price":"49060.56","quantity":"0.099842"},
        #                 {"price":"49060.58","quantity":"0.016003"},
        #                 {"price":"49060.6","quantity":"0.023677"}
        #             ],
        #             "bids":[
        #                 {"price":"49060.45","quantity":"1.693009"},
        #                 {"price":"49060.44","quantity":"0.000843"},
        #                 {"price":"49059.98","quantity":"0.735"},
        #             ],
        #             "version":"202454074",
        #         }
        #     }
        #
        # swap / contract
        #
        #     {
        #         "success":true,
        #         "code":0,
        #         "data":{
        #             "asks":[
        #                 [3445.7,48379,1],
        #                 [3445.75,34994,1],
        #                 [3445.8,68634,2],
        #             ],
        #             "bids":[
        #                 [3445.55,44081,1],
        #                 [3445.5,24857,1],
        #                 [3445.45,50272,1],
        #             ],
        #             "version":2827730444,
        #             "timestamp":1634117846232
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        priceKey = 'price' if market['spot'] else 0
        amountKey = 'quantity' if market['spot'] else 1
        timestamp = self.safe_integer(data, 'timestamp')
        orderbook = self.parse_order_book(data, symbol, timestamp, 'bids', 'asks', priceKey, amountKey)
        orderbook['nonce'] = self.safe_integer(data, 'version')
        return orderbook

    async def fetch_trades(self, symbol, since=None, limit=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        if limit is not None:
            request['limit'] = limit  # default 100, max 100
        method = None
        if market['spot']:
            method = 'spotPublicGetMarketDeals'
        elif market['swap']:
            method = 'contractPublicGetDealsSymbol'
        response = await getattr(self, method)(self.extend(request, params))
        #
        # spot
        #
        #     {
        #         "code":200,
        #         "data":[
        #             {"trade_time":1633381766725,"trade_price":"0.068981","trade_quantity":"0.005","trade_type":"BID"},
        #             {"trade_time":1633381732705,"trade_price":"0.068979","trade_quantity":"0.006","trade_type":"BID"},
        #             {"trade_time":1633381694604,"trade_price":"0.068975","trade_quantity":"0.011","trade_type":"ASK"},
        #         ]
        #     }
        #
        # swap / contract
        #
        #     {
        #         "success":true,
        #         "code":0,
        #         "data":[
        #             {"p":3598.85,"v":52,"T":1,"O":2,"M":2,"t":1634169038038},
        #             {"p":3599.2,"v":15,"T":2,"O":3,"M":1,"t":1634169035603},
        #             {"p":3600.15,"v":229,"T":2,"O":1,"M":2,"t":1634169026354},
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        return self.parse_trades(data, market, since, limit)

    def parse_trade(self, trade, market=None):
        #
        # public fetchTrades
        #
        #     spot
        #
        #     {
        #         "trade_time":1633381766725,
        #         "trade_price":"0.068981",
        #         "trade_quantity":"0.005",
        #         "trade_type":"BID"
        #     }
        #
        #     swap / contract
        #
        #     {
        #         "p":3598.85,
        #         "v":52,
        #         "T":1,  # 1 buy, 2 sell
        #         "O":2,  # 1 opens a position, 2 does not open a position
        #         "M":2,  # self-trading, 1 yes, 2 no
        #         "t":1634169038038
        #     }
        #
        # private fetchMyTrades, fetchOrderTrades
        #
        #     {
        #         "id":"b160b8f072d9403e96289139d5544809",
        #         "symbol":"USDC_USDT",
        #         "quantity":"150",
        #         "price":"0.9997",
        #         "amount":"149.955",
        #         "fee":"0.29991",
        #         "trade_type":"ASK",
        #         "order_id":"d798765285374222990bbd14decb86cd",
        #         "is_taker":true,
        #         "fee_currency":"USDT",
        #         "create_time":1633984904000
        #     }
        #
        timestamp = self.safe_integer_2(trade, 'create_time', 'trade_time')
        timestamp = self.safe_integer(trade, 't', timestamp)
        marketId = self.safe_string(trade, 'symbol')
        market = self.safe_market(marketId, market, '_')
        symbol = market['symbol']
        priceString = self.safe_string_2(trade, 'price', 'trade_price')
        priceString = self.safe_string(trade, 'p', priceString)
        amountString = self.safe_string_2(trade, 'quantity', 'trade_quantity')
        amountString = self.safe_string(trade, 'v', amountString)
        costString = self.safe_string(trade, 'amount')
        if costString is None:
            costString = Precise.string_mul(priceString, amountString)
        price = self.parse_number(priceString)
        amount = self.parse_number(amountString)
        cost = self.parse_number(costString)
        side = self.safe_string_2(trade, 'trade_type', 'T')
        if (side == 'BID') or (side == '1'):
            side = 'buy'
        elif (side == 'ASK') or (side == '2'):
            side = 'sell'
        id = self.safe_string_2(trade, 'id', 'trade_time')
        if id is None:
            id = self.safe_string(trade, 't', id)
            if id is not None:
                id += '-' + market['id'] + '-' + amountString
        feeCost = self.safe_number(trade, 'fee')
        fee = None
        if feeCost is not None:
            feeCurrencyId = self.safe_string(trade, 'fee_currency')
            feeCurrencyCode = self.safe_currency_code(feeCurrencyId)
            fee = {
                'cost': feeCost,
                'currency': feeCurrencyCode,
            }
        orderId = self.safe_string(trade, 'order_id')
        isTaker = self.safe_value(trade, 'is_taker', True)
        takerOrMaker = 'taker' if isTaker else 'maker'
        return {
            'info': trade,
            'id': id,
            'order': orderId,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'type': None,
            'side': side,
            'takerOrMaker': takerOrMaker,
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': fee,
        }

    async def fetch_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        options = self.safe_value(self.options, 'timeframes', {})
        timeframes = self.safe_value(options, market['type'], {})
        timeframeValue = self.safe_string(timeframes, timeframe, timeframe)
        request = {
            'symbol': market['id'],
            'interval': timeframeValue,
        }
        method = None
        if market['spot']:
            method = 'spotPublicGetMarketKline'
            if since is not None:
                request['start_time'] = int(since / 1000)
            if limit is not None:
                request['limit'] = limit  # default 100
        elif market['swap']:
            method = 'contractPublicGetKlineSymbol'
            if since is not None:
                request['start'] = int(since / 1000)
            # request['end'] = self.seconds()
        response = await getattr(self, method)(self.extend(request, params))
        #
        # spot
        #
        #     {
        #         "code":200,
        #         "data":[
        #             [1633377000,"49227.47","49186.21","49227.47","49169.48","0.5984809999999999","29434.259665989997"],
        #             [1633377060,"49186.21","49187.03","49206.64","49169.18","0.3658478","17990.651234393"],
        #             [1633377120,"49187.03","49227.2","49227.2","49174.4","0.0687651","3382.353190352"],
        #         ],
        #     }
        #
        # swap / contract
        #
        #     {
        #         "success":true,
        #         "code":0,
        #         "data":{
        #             "time":[1634052300,1634052360,1634052420],
        #             "open":[3492.2,3491.3,3495.65],
        #             "close":[3491.3,3495.65,3495.2],
        #             "high":[3495.85,3496.55,3499.4],
        #             "low":[3491.15,3490.9,3494.2],
        #             "vol":[1740.0,351.0,314.0],
        #             "amount":[60793.623,12260.4885,10983.1375],
        #         }
        #     }
        #
        if market['spot']:
            data = self.safe_value(response, 'data', [])
            return self.parse_ohlcvs(data, market, timeframe, since, limit)
        elif market['swap']:
            data = self.safe_value(response, 'data', {})
            result = self.convert_trading_view_to_ohlcv(data, 'time', 'open', 'high', 'low', 'close', 'vol')
            return self.parse_ohlcvs(result, market, timeframe, since, limit)

    def parse_ohlcv(self, ohlcv, market=None):
        #
        # the ordering in spot candles is OCHLV
        #
        #     [
        #         1633377000,  # 0 timestamp(unix seconds)
        #         "49227.47",  # 1 open price
        #         "49186.21",  # 2 closing price
        #         "49227.47",  # 3 high
        #         "49169.48",  # 4 low
        #         "0.5984809999999999",  # 5 base volume
        #         "29434.259665989997",  # 6 quote volume
        #     ]
        #
        # the ordering in swap / contract candles is OHLCV
        #
        return [
            self.safe_timestamp(ohlcv, 0),
            self.safe_number(ohlcv, 1),
            self.safe_number(ohlcv, 3 if market['spot'] else 2),
            self.safe_number(ohlcv, 4 if market['spot'] else 3),
            self.safe_number(ohlcv, 2 if market['spot'] else 4),
            self.safe_number(ohlcv, 5),
        ]

    async def fetch_balance(self, params={}):
        await self.load_markets()
        defaultType = self.safe_string_2(self.options, 'fetchBalance', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        spot = (type == 'spot')
        swap = (type == 'swap')
        method = None
        if spot:
            method = 'spotPrivateGetAccountInfo'
        elif swap:
            method = 'contractPrivateGetAccountAssets'
        query = self.omit(params, 'type')
        response = await getattr(self, method)(query)
        #
        # spot
        #
        #     {
        #         code: "200",
        #         data: {
        #             USDC: {frozen: "0", available: "150"}
        #         }
        #     }
        #
        # swap / contract
        #
        #     {
        #         "success":true,
        #         "code":0,
        #         "data":[
        #             {"currency":"BSV","positionMargin":0,"availableBalance":0,"cashBalance":0,"frozenBalance":0,"equity":0,"unrealized":0,"bonus":0},
        #             {"currency":"BCH","positionMargin":0,"availableBalance":0,"cashBalance":0,"frozenBalance":0,"equity":0,"unrealized":0,"bonus":0},
        #             {"currency":"CRV","positionMargin":0,"availableBalance":0,"cashBalance":0,"frozenBalance":0,"equity":0,"unrealized":0,"bonus":0},
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', {})
        result = {
            'info': response,
            'timestamp': None,
            'datetime': None,
        }
        if spot:
            currencyIds = list(data.keys())
            for i in range(0, len(currencyIds)):
                currencyId = currencyIds[i]
                code = self.safe_currency_code(currencyId)
                balance = self.safe_value(data, currencyId, {})
                account = self.account()
                account['free'] = self.safe_string(balance, 'available')
                account['used'] = self.safe_string(balance, 'frozen')
                result[code] = account
        else:
            for i in range(0, len(data)):
                balance = data[i]
                currencyId = self.safe_string(balance, 'currency')
                code = self.safe_currency_code(currencyId)
                account = self.account()
                account['free'] = self.safe_string(balance, 'availableBalance')
                account['used'] = self.safe_string(balance, 'frozenBalance')
                result[code] = account
        return self.safe_balance(result)

    def safe_network(self, networkId):
        if networkId.find('BSC') >= 0:
            return 'BEP20'
        parts = networkId.split(' ')
        networkId = ''.join(parts)
        networkId = networkId.replace('-20', '20')
        networksById = {
            'ETH': 'ETH',
            'ERC20': 'ERC20',
            'BEP20(BSC)': 'BEP20',
            'TRX': 'TRC20',
        }
        return self.safe_string(networksById, networkId, networkId)

    def parse_deposit_address(self, depositAddress, currency=None):
        #
        #     {"chain":"ERC-20","address":"0x55cbd73db24eafcca97369e3f2db74b2490586e6"},
        #     {"chain":"MATIC","address":"0x05aa3236f1970eae0f8feb17ec19435b39574d74"},
        #     {"chain":"TRC20","address":"TGaPfhW41EXD3sAfs1grLF6DKfugfqANNw"},
        #     {"chain":"SOL","address":"5FSpUKuh2gjw4mF89T2e7sEjzUA1SkRKjBChFqP43KhV"},
        #     {"chain":"ALGO","address":"B3XTZND2JJTSYR7R2TQVCUDT4QSSYVAIZYDPWVBX34DGAYATBU3AUV43VU"}
        #
        #
        address = self.safe_string(depositAddress, 'address')
        code = self.safe_currency_code(None, currency)
        networkId = self.safe_string(depositAddress, 'chain')
        network = self.safe_network(networkId)
        self.check_address(address)
        return {
            'currency': code,
            'address': address,
            'tag': None,
            'network': network,
            'info': depositAddress,
        }

    async def fetch_deposit_addresses_by_network(self, code, params={}):
        await self.load_markets()
        currency = self.currency(code)
        request = {
            'currency': currency['id'],
        }
        response = await self.spotPrivateGetAssetDepositAddressList(self.extend(request, params))
        #
        #     {
        #         "code":200,
        #         "data":{
        #             "currency":"USDC",
        #             "chains":[
        #                 {"chain":"ERC-20","address":"0x55cbd73db24eafcca97369e3f2db74b2490586e6"},
        #                 {"chain":"MATIC","address":"0x05aa3236f1970eae0f8feb17ec19435b39574d74"},
        #                 {"chain":"TRC20","address":"TGaPfhW41EXD3sAfs1grLF6DKfugfqANNw"},
        #                 {"chain":"SOL","address":"5FSpUKuh2gjw4mF89T2e7sEjzUA1SkRKjBChFqP43KhV"},
        #                 {"chain":"ALGO","address":"B3XTZND2JJTSYR7R2TQVCUDT4QSSYVAIZYDPWVBX34DGAYATBU3AUV43VU"}
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        chains = self.safe_value(data, 'chains', [])
        depositAddresses = []
        for i in range(0, len(chains)):
            depositAddress = self.parse_deposit_address(chains[i], currency)
            depositAddresses.append(depositAddress)
        return self.index_by(depositAddresses, 'network')

    async def fetch_deposit_address(self, code, params={}):
        rawNetwork = self.safe_string_upper(params, 'network')
        params = self.omit(params, 'network')
        response = await self.fetch_deposit_addresses_by_network(code, params)
        networks = self.safe_value(self.options, 'networks', {})
        network = self.safe_string(networks, rawNetwork, rawNetwork)
        result = None
        if network is None:
            result = self.safe_value(response, code)
            if result is None:
                alias = self.safe_string(networks, code, code)
                result = self.safe_value(response, alias)
                if result is None:
                    defaultNetwork = self.safe_string(self.options, 'defaultNetwork', 'ERC20')
                    result = self.safe_value(response, defaultNetwork)
                    if result is None:
                        values = list(response.values())
                        result = self.safe_value(values, 0)
                        if result is None:
                            raise InvalidAddress(self.id + ' fetchDepositAddress() cannot find deposit address for ' + code)
            return result
        # TODO: add support for all aliases here
        result = self.safe_value(response, rawNetwork)
        if result is None:
            raise InvalidAddress(self.id + ' fetchDepositAddress() cannot find ' + network + ' deposit address for ' + code)
        return result

    async def fetch_deposits(self, code=None, since=None, limit=None, params={}):
        await self.load_markets()
        request = {
            # 'currency': currency['id'],
            # 'state': 'state',
            # 'start_time': since,  # default 1 day
            # 'end_time': self.milliseconds(),
            # 'page_num': 1,
            # 'page_size': limit,  # default 20, maximum 50
        }
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        if since is not None:
            request['start_time'] = since
        if limit is not None:
            request['limit'] = limit
        response = await self.spotPrivateGetAssetDepositList(self.extend(request, params))
        #
        #     {
        #         "code":200,
        #         "data":{
        #             "page_size":20,
        #             "total_page":1,
        #             "total_size":1,
        #             "page_num":1,
        #             "result_list":[
        #                 {
        #                     "currency":"USDC",
        #                     "amount":150.0,
        #                     "fee":0.0,
        #                     "confirmations":19,
        #                     "address":"0x55cbd73db24eafcca97369e3f2db74b2490586e6",
        #                     "state":"SUCCESS",
        #                     "tx_id":"0xc65a9b09e1b71def81bf8bb3ec724c0c1b2b4c82200c8c142e4ea4c1469fd789:0",
        #                     "require_confirmations":12,
        #                     "create_time":"2021-10-11T18:58:25.000+00:00",
        #                     "update_time":"2021-10-11T19:01:06.000+00:00"
        #                 }
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        resultList = self.safe_value(data, 'result_list', [])
        return self.parse_transactions(resultList, code, since, limit)

    async def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
        await self.load_markets()
        request = {
            # 'withdrawal_id': '4b450616042a48c99dd45cacb4b092a7',  # string
            # 'currency': currency['id'],
            # 'state': 'state',
            # 'start_time': since,  # default 1 day
            # 'end_time': self.milliseconds(),
            # 'page_num': 1,
            # 'page_size': limit,  # default 20, maximum 50
        }
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        if since is not None:
            request['start_time'] = since
        if limit is not None:
            request['limit'] = limit
        response = await self.spotPrivateGetAssetWithdrawList(self.extend(request, params))
        #
        #     {
        #         "code":200,
        #         "data":{
        #             "page_size":20,
        #             "total_page":1,
        #             "total_size":1,
        #             "page_num":1,
        #             "result_list":[
        #                 {
        #                     "id":"4b450616042a48c99dd45cacb4b092a7",
        #                     "currency":"USDT-TRX",
        #                     "address":"TRHKnx74Gb8UVcpDCMwzZVe4NqXfkdtPak",
        #                     "amount":30.0,
        #                     "fee":1.0,
        #                     "remark":"self is my first withdrawal remark",
        #                     "state":"WAIT",
        #                     "create_time":"2021-10-11T20:45:08.000+00:00"
        #                 }
        #             ]
        #         }
        #     }
        #
        data = self.safe_value(response, 'data', {})
        resultList = self.safe_value(data, 'result_list', [])
        return self.parse_transactions(resultList, code, since, limit)

    def parse_transaction(self, transaction, currency=None):
        #
        # fetchDeposits
        #
        #     {
        #         "currency":"USDC",
        #         "amount":150.0,
        #         "fee":0.0,
        #         "confirmations":19,
        #         "address":"0x55cbd73db24eafcca97369e3f2db74b2490586e6",
        #         "state":"SUCCESS",
        #         "tx_id":"0xc65a9b09e1b71def81bf8bb3ec724c0c1b2b4c82200c8c142e4ea4c1469fd789:0",
        #         "require_confirmations":12,
        #         "create_time":"2021-10-11T18:58:25.000+00:00",
        #         "update_time":"2021-10-11T19:01:06.000+00:00"
        #     }
        #
        # fetchWithdrawals
        #
        #     {
        #         "id":"4b450616042a48c99dd45cacb4b092a7",
        #         "currency":"USDT-TRX",
        #         "address":"TRHKnx74Gb8UVcpDCMwzZVe4NqXfkdtPak",
        #         "amount":30.0,
        #         "fee":1.0,
        #         "remark":"self is my first withdrawal remark",
        #         "state":"WAIT",
        #         "create_time":"2021-10-11T20:45:08.000+00:00"
        #     }
        #
        id = self.safe_string(transaction, 'id')
        type = 'deposit' if (id is None) else 'withdrawal'
        timestamp = self.parse8601(self.safe_string(transaction, 'create_time'))
        updated = self.parse8601(self.safe_string(transaction, 'update_time'))
        currencyId = self.safe_string(transaction, 'currency')
        network = None
        if (currencyId is not None) and (currencyId.find('-') >= 0):
            parts = currencyId.split('-')
            currencyId = self.safe_string(parts, 0)
            networkId = self.safe_string(parts, 1)
            network = self.safe_network(networkId)
        code = self.safe_currency_code(currencyId, currency)
        status = self.parse_transaction_status(self.safe_string(transaction, 'state'))
        amount = self.safe_number(transaction, 'amount')
        address = self.safe_string(transaction, 'address')
        txid = self.safe_string(transaction, 'tx_id')
        fee = None
        feeCost = self.safe_number(transaction, 'fee')
        if feeCost is not None:
            fee = {
                'cost': feeCost,
                'currency': code,
            }
        return {
            'info': transaction,
            'id': id,
            'txid': txid,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'network': network,
            'address': address,
            'addressTo': None,
            'addressFrom': None,
            'tag': None,
            'tagTo': None,
            'tagFrom': None,
            'type': type,
            'amount': amount,
            'currency': code,
            'status': status,
            'updated': updated,
            'fee': fee,
        }

    def parse_transaction_status(self, status):
        statuses = {
            'WAIT': 'pending',
            'SUCCESS': 'ok',
        }
        return self.safe_string(statuses, status, status)

    async def fetch_position(self, symbol, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        response = await self.fetch_positions(self.extend(request, params))
        firstPosition = self.safe_value(response, 0)
        return firstPosition

    async def fetch_positions(self, symbols=None, params={}):
        await self.load_markets()
        response = await self.contractPrivateGetPositionOpenPositions(params)
        #
        #     {
        #         "success": True,
        #         "code": 0,
        #         "data": [
        #             {
        #                 "positionId": 1394650,
        #                 "symbol": "ETH_USDT",
        #                 "positionType": 1,
        #                 "openType": 1,
        #                 "state": 1,
        #                 "holdVol": 1,
        #                 "frozenVol": 0,
        #                 "closeVol": 0,
        #                 "holdAvgPrice": 1217.3,
        #                 "openAvgPrice": 1217.3,
        #                 "closeAvgPrice": 0,
        #                 "liquidatePrice": 1211.2,
        #                 "oim": 0.1290338,
        #                 "im": 0.1290338,
        #                 "holdFee": 0,
        #                 "realised": -0.0073,
        #                 "leverage": 100,
        #                 "createTime": 1609991676000,
        #                 "updateTime": 1609991676000,
        #                 "autoAddIm": False
        #             }
        #         ]
        #     }
        #
        # todo add parsePositions, parsePosition
        data = self.safe_value(response, 'data', [])
        return data

    async def create_order(self, symbol, type, side, amount, price=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        if market['spot']:
            return await self.create_spot_order(symbol, type, side, amount, price, params)
        elif market['swap']:
            return await self.create_swap_order(symbol, type, side, amount, price, params)

    async def create_spot_order(self, symbol, type, side, amount, price=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        orderSide = None
        if side == 'buy':
            orderSide = 'BID'
        elif side == 'sell':
            orderSide = 'ASK'
        orderType = type.upper()
        postOnly = self.safe_value(params, 'postOnly', False)
        if postOnly:
            orderType = 'POST_ONLY'
        elif orderType == 'LIMIT':
            orderType = 'LIMIT_ORDER'
        elif (orderType != 'POST_ONLY') and (orderType != 'IMMEDIATE_OR_CANCEL'):
            raise InvalidOrder(self.id + ' createOrder() does not support ' + type + ' order type, specify one of LIMIT, LIMIT_ORDER, POST_ONLY or IMMEDIATE_OR_CANCEL')
        else:
            raise InvalidOrder(self.id + ' createOrder() allows limit orders only')
        request = {
            'symbol': market['id'],
            'price': self.price_to_precision(symbol, price),
            'quantity': self.amount_to_precision(symbol, amount),
            'trade_type': orderSide,
            'order_type': orderType,  # LIMIT_ORDER，POST_ONLY，IMMEDIATE_OR_CANCEL
        }
        clientOrderId = self.safe_string_2(params, 'clientOrderId', 'client_order_id')
        if clientOrderId is not None:
            request['client_order_id'] = clientOrderId
        params = self.omit(params, ['type', 'clientOrderId', 'client_order_id', 'postOnly'])
        response = await self.spotPrivatePostOrderPlace(self.extend(request, params))
        #
        #     {"code":200,"data":"2ff3163e8617443cb9c6fc19d42b1ca4"}
        #
        return self.parse_order(response, market)

    async def create_swap_order(self, symbol, type, side, amount, price=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        openType = self.safe_integer(params, 'openType')
        if openType is None:
            raise ArgumentsRequired(self.id + ' createSwapOrder() requires an integer openType parameter, 1 for isolated margin, 2 for cross margin')
        if (type != 'limit') and (type != 'market') and (type != 1) and (type != 2) and (type != 3) and (type != 4) and (type != 5) and (type != 6):
            raise InvalidOrder(self.id + ' createSwapOrder() order type must either limit, market, or 1 for limit orders, 2 for post-only orders, 3 for IOC orders, 4 for FOK orders, 5 for market orders or 6 to convert market price to current price')
        postOnly = self.safe_value(params, 'postOnly', False)
        if postOnly:
            type = 2
        elif type == 'limit':
            type = 1
        elif type == 'market':
            type = 6
        if (side != 1) and (side != 2) and (side != 3) and (side != 4):
            raise InvalidOrder(self.id + ' createSwapOrder() order side must be 1 open long, 2 close short, 3 open short or 4 close long')
        request = {
            'symbol': market['id'],
            'price': float(self.price_to_precision(symbol, price)),
            'vol': float(self.amount_to_precision(symbol, amount)),
            # 'leverage': int,  # required for isolated margin
            'side': side,  # 1 open long, 2 close short, 3 open short, 4 close long
            #
            # supported order types
            #
            #     1 limit
            #     2 post only maker(PO)
            #     3 transact or cancel instantly(IOC)
            #     4 transact completely or cancel completely(FOK)
            #     5 market orders
            #     6 convert market price to current price
            #
            'type': type,
            'openType': openType,  # 1 isolated, 2 cross
            # 'positionId': 1394650,  # long, filling in self parameter when closing a position is recommended
            # 'externalOid': clientOrderId,
            # 'stopLossPrice': self.price_to_precision(symbol, stopLossPrice),
            # 'takeProfitPrice': self.price_to_precision(symbol, takeProfitPrice),
        }
        if openType == 1:
            leverage = self.safe_integer(params, 'leverage')
            if leverage is None:
                raise ArgumentsRequired(self.id + ' createSwapOrder() requires a leverage parameter for isolated margin orders')
        clientOrderId = self.safe_string_2(params, 'clientOrderId', 'externalOid')
        if clientOrderId is not None:
            request['externalOid'] = clientOrderId
        params = self.omit(params, ['clientOrderId', 'externalOid', 'postOnly'])
        response = await self.contractPrivatePostOrderSubmit(self.extend(request, params))
        #
        #     {"code":200,"data":"2ff3163e8617443cb9c6fc19d42b1ca4"}
        #
        return self.parse_order(response, market)

    async def cancel_order(self, id, symbol=None, params={}):
        await self.load_markets()
        request = {}
        clientOrderId = self.safe_string_2(params, 'clientOrderId', 'client_order_ids')
        if clientOrderId is not None:
            params = self.omit(params, ['clientOrderId', 'client_order_ids'])
            request['client_order_ids'] = clientOrderId
        else:
            request['order_ids'] = id
        response = await self.spotPrivateDeleteOrderCancel(self.extend(request, params))
        #
        #    {"code":200,"data":{"965245851c444078a11a7d771323613b":"success"}}
        #
        data = self.safe_value(response, 'data')
        return self.parse_order(data)

    def parse_order_status(self, status):
        statuses = {
            'NEW': 'open',
            'FILLED': 'closed',
            'PARTIALLY_FILLED': 'open',
            'CANCELED': 'canceled',
            'PARTIALLY_CANCELED': 'canceled',
        }
        return self.safe_string(statuses, status, status)

    def parse_order(self, order, market=None):
        #
        # createOrder
        #
        # spot
        #
        #     {"code":200,"data":"2ff3163e8617443cb9c6fc19d42b1ca4"}
        #
        # swap / contract
        #
        #     {"success": True, "code": 0, "data": 102057569836905984}
        #
        # fetchOpenOrders
        #
        #     {
        #         "id":"965245851c444078a11a7d771323613b",
        #         "symbol":"ETH_USDT",
        #         "price":"3430",
        #         "quantity":"0.01",
        #         "state":"NEW",
        #         "type":"BID",
        #         "remain_quantity":"0.01",
        #         "remain_amount":"34.3",
        #         "create_time":1633989029039,
        #         "client_order_id":"",
        #         "order_type":"LIMIT_ORDER"
        #     }
        #
        # fetchClosedOrders, fetchCanceledOrders, fetchOrder
        #
        #     {
        #         "id":"d798765285374222990bbd14decb86cd",
        #         "symbol":"USDC_USDT",
        #         "price":"0.9988",
        #         "quantity":"150",
        #         "state":"FILLED",  # CANCELED
        #         "type":"ASK",  # BID
        #         "deal_quantity":"150",
        #         "deal_amount":"149.955",
        #         "create_time":1633984904000,
        #         "order_type":"MARKET_ORDER"  # LIMIT_ORDER
        #     }
        #
        # cancelOrder
        #
        #     {"965245851c444078a11a7d771323613b":"success"}
        #
        id = self.safe_string_2(order, 'data', 'id')
        status = None
        if id is None:
            keys = list(order.keys())
            id = self.safe_string(keys, 0)
            state = self.safe_string(order, id)
            if state == 'success':
                status = 'canceled'
        state = self.safe_string(order, 'state')
        timestamp = self.safe_integer(order, 'create_time')
        price = self.safe_string(order, 'price')
        amount = self.safe_string(order, 'quantity')
        remaining = self.safe_string(order, 'remain_quantity')
        filled = self.safe_string(order, 'deal_quantity')
        marketId = self.safe_string(order, 'symbol')
        symbol = self.safe_symbol(marketId, market, '_')
        side = None
        bidOrAsk = self.safe_string(order, 'type')
        if bidOrAsk == 'BID':
            side = 'buy'
        elif bidOrAsk == 'ASK':
            side = 'sell'
        status = self.parse_order_status(state)
        clientOrderId = self.safe_string(order, 'client_order_id')
        if clientOrderId == '':
            clientOrderId = None
        orderType = self.safe_string_lower(order, 'order_type')
        if orderType is not None:
            orderType = orderType.replace('_order', '')
        return self.safe_order({
            'id': id,
            'clientOrderId': clientOrderId,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': None,
            'status': status,
            'symbol': symbol,
            'type': orderType,
            'timeInForce': None,
            'side': side,
            'price': price,
            'stopPrice': None,
            'average': None,
            'amount': amount,
            'cost': None,
            'filled': filled,
            'remaining': remaining,
            'fee': None,
            'trades': None,
            'info': order,
        }, market)

    async def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOpenOrders() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
            # 'start_time': since,
            # 'limit': limit,  # default 50, max 1000
            # 'trade_type': 'BID',  # BID / ASK
        }
        response = await self.spotPrivateGetOrderOpenOrders(self.extend(request, params))
        #
        #     {
        #         "code":200,
        #         "data":[
        #             {
        #                 "id":"965245851c444078a11a7d771323613b",
        #                 "symbol":"ETH_USDT",
        #                 "price":"3430",
        #                 "quantity":"0.01",
        #                 "state":"NEW",
        #                 "type":"BID",
        #                 "remain_quantity":"0.01",
        #                 "remain_amount":"34.3",
        #                 "create_time":1633989029039,
        #                 "client_order_id":"",
        #                 "order_type":"LIMIT_ORDER"
        #             },
        #             {
        #                 "id":"2ff3163e8617443cb9c6fc19d42b1ca4",
        #                 "symbol":"ETH_USDT",
        #                 "price":"3420",
        #                 "quantity":"0.01",
        #                 "state":"NEW",
        #                 "type":"BID",
        #                 "remain_quantity":"0.01",
        #                 "remain_amount":"34.2",
        #                 "create_time":1633988662382,
        #                 "client_order_id":"",
        #                 "order_type":"LIMIT_ORDER"
        #             }
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        return self.parse_orders(data, market, since, limit)

    async def fetch_order(self, id, symbol=None, params={}):
        await self.load_markets()
        request = {
            'order_ids': id,
        }
        response = await self.spotPrivateGetOrderQuery(self.extend(request, params))
        #
        #     {
        #         "code":200,
        #         "data":[
        #             {
        #                 "id":"2ff3163e8617443cb9c6fc19d42b1ca4",
        #                 "symbol":"ETH_USDT",
        #                 "price":"3420",
        #                 "quantity":"0.01",
        #                 "state":"CANCELED",
        #                 "type":"BID",
        #                 "deal_quantity":"0",
        #                 "deal_amount":"0",
        #                 "create_time":1633988662000,
        #                 "order_type":"LIMIT_ORDER"
        #             }
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        firstOrder = self.safe_value(data, 0)
        if firstOrder is None:
            raise OrderNotFound(self.id + ' fetchOrder() could not find the order id ' + id)
        return self.parse_order(firstOrder)

    async def fetch_orders_by_state(self, state, symbol=None, since=None, limit=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrdersByState requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
            # 'start_time': since,  # default 7 days, max 30 days
            # 'limit': limit,  # default 50, max 1000
            # 'trade_type': 'BID',  # BID / ASK
            'states': state,  # NEW, FILLED, PARTIALLY_FILLED, CANCELED, PARTIALLY_CANCELED
        }
        if limit is not None:
            request['limit'] = limit
        if since is not None:
            request['start_time'] = since
        response = await self.spotPrivateGetOrderList(self.extend(request, params))
        data = self.safe_value(response, 'data', [])
        return self.parse_orders(data, market, since, limit)

    async def fetch_canceled_orders(self, symbol=None, since=None, limit=None, params={}):
        return await self.fetch_orders_by_state('CANCELED', symbol, since, limit, params)

    async def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
        return await self.fetch_orders_by_state('FILLED', symbol, since, limit, params)

    async def cancel_all_orders(self, symbol=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        response = await self.spotPrivateDeleteOrderCancelBySymbol(self.extend(request, params))
        #
        #     {
        #         "code": 200,
        #         "data": [
        #             {
        #                 "msg": "success",
        #                 "order_id": "75ecf99feef04538b78e4622beaba6eb",
        #                 "client_order_id": "a9329e86f2094b0d8b58e92c25029554"
        #             },
        #             {
        #                 "msg": "success",
        #                 "order_id": "139413c48f8b4c018f452ce796586bcf"
        #             },
        #             {
        #                 "msg": "success",
        #                 "order_id": "b58ef34c570e4917981f276d44091484"
        #             }
        #         ]
        #     }
        #
        return response

    async def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchMyTrades requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
            # 'start_time': since,  # default 7 days, max 30 days
            # 'limit': limit,  # default 50, max 1000
        }
        if since is not None:
            request['start_time'] = since
        if limit is not None:
            request['limit'] = limit
        response = await self.spotPrivateGetOrderDeals(self.extend(request, params))
        #
        #     {
        #         "code":200,
        #         "data":[
        #             {
        #                 "id":"b160b8f072d9403e96289139d5544809",
        #                 "symbol":"USDC_USDT",
        #                 "quantity":"150",
        #                 "price":"0.9997",
        #                 "amount":"149.955",
        #                 "fee":"0.29991",
        #                 "trade_type":"ASK",
        #                 "order_id":"d798765285374222990bbd14decb86cd",
        #                 "is_taker":true,
        #                 "fee_currency":"USDT",
        #                 "create_time":1633984904000
        #             }
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        return self.parse_trades(data, market, since, limit)

    async def fetch_order_trades(self, id, symbol=None, since=None, limit=None, params={}):
        await self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
        request = {
            'order_id': id,
        }
        response = await self.spotPrivateGetOrderDealDetail(self.extend(request, params))
        #
        #     {
        #         "code":200,
        #         "data":[
        #             {
        #                 "id":"b160b8f072d9403e96289139d5544809",
        #                 "symbol":"USDC_USDT",
        #                 "quantity":"150",
        #                 "price":"0.9997",
        #                 "amount":"149.955",
        #                 "fee":"0.29991",
        #                 "trade_type":"ASK",
        #                 "order_id":"d798765285374222990bbd14decb86cd",
        #                 "is_taker":true,
        #                 "fee_currency":"USDT",
        #                 "create_time":1633984904000
        #             }
        #         ]
        #     }
        #
        data = self.safe_value(response, 'data', [])
        return self.parse_trades(data, market, since, limit)

    async def modify_margin_helper(self, symbol, amount, addOrReduce, params={}):
        positionId = self.safe_integer(params, 'positionId')
        if positionId is None:
            raise ArgumentsRequired(self.id + ' modifyMarginHelper() requires a positionId parameter')
        await self.load_markets()
        request = {
            'positionId': positionId,
            'amount': amount,
            'type': addOrReduce,
        }
        response = await self.contractPrivatePostPositionChangeMargin(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "code": 0
        #     }
        return response

    async def reduce_margin(self, symbol, amount, params={}):
        return await self.modify_margin_helper(symbol, amount, 'SUB', params)

    async def add_margin(self, symbol, amount, params={}):
        return await self.modify_margin_helper(symbol, amount, 'ADD', params)

    async def set_leverage(self, leverage, symbol=None, params={}):
        positionId = self.safe_integer(params, 'positionId')
        if positionId is None:
            raise ArgumentsRequired(self.id + ' setLeverage() requires a positionId parameter')
        await self.load_markets()
        request = {
            'positionId': positionId,
            'leverage': leverage,
        }
        return await self.contractPrivatePostPositionChangeLeverage(self.extend(request, params))

    async def withdraw(self, code, amount, address, tag=None, params={}):
        tag, params = self.handle_withdraw_tag_and_params(tag, params)
        networks = self.safe_value(self.options, 'networks', {})
        network = self.safe_string_2(params, 'network', 'chain')  # self line allows the user to specify either ERC20 or ETH
        network = self.safe_string(networks, network, network)  # handle ETH > ERC-20 alias
        self.check_address(address)
        await self.load_markets()
        currency = self.currency(code)
        if tag is not None:
            address += ':' + tag
        request = {
            'currency': currency['id'],
            'address': address,
            'amount': amount,
        }
        if network is not None:
            request['chain'] = network
            params = self.omit(params, ['network', 'chain'])
        response = await self.spotPrivatePostAssetWithdraw(self.extend(request, params))
        data = self.safe_value(response, 'data', {})
        return self.parse_transaction(data, currency)

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        section, access = api
        url = self.urls['api'][section][access] + '/' + self.implode_params(path, params)
        params = self.omit(params, self.extract_params(path))
        if access == 'public':
            if params:
                url += '?' + self.urlencode(params)
        else:
            self.check_required_credentials()
            timestamp = str(self.milliseconds())
            auth = ''
            headers = {
                'ApiKey': self.apiKey,
                'Request-Time': timestamp,
                'Content-Type': 'application/json',
            }
            if method == 'POST':
                auth = self.json(params)
                body = auth
            else:
                params = self.keysort(params)
                if params:
                    auth += self.urlencode(params)
                    url += '?' + auth
            auth = self.apiKey + timestamp + auth
            signature = self.hmac(self.encode(auth), self.encode(self.secret), hashlib.sha256)
            headers['Signature'] = signature
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
        if response is None:
            return
        #     {"code":10232,"msg":"The currency not exist"}
        #     {"code":10216,"msg":"No available deposit address"}
        #
        #     {
        #         "success":true,
        #         "code":0,
        #         "data":1634095541710
        #     }
        #
        success = self.safe_value(response, 'success', False)
        if success is True:
            return
        responseCode = self.safe_string(response, 'code')
        if (responseCode != '200') and (responseCode != '0'):
            feedback = self.id + ' ' + body
            self.throw_exactly_matched_exception(self.exceptions['exact'], responseCode, feedback)
            raise ExchangeError(feedback)

    async def fetch_funding_rate_history(self, symbol=None, since=None, limit=None, params={}):
        #
        # Gets a history of funding rates with their timestamps
        #  (param) symbol: Future currency pair
        #  (param) limit: mexc limit is page_size default 20, maximum is 100
        #  (param) since: not used by mexc
        #  (param) params: Object containing more params for the request
        #  return: [{symbol, fundingRate, timestamp, dateTime}]
        #
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchFundingRateHistory() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
            # 'page_size': limit,  # optional
            # 'page_num': 1,  # optional, current page number, default is 1
        }
        if limit is not None:
            request['page_size'] = limit
        response = await self.contractPublicGetFundingRateHistory(self.extend(request, params))
        #
        # {
        #     "success": True,
        #     "code": 0,
        #     "data": {
        #         "pageSize": 2,
        #         "totalCount": 21,
        #         "totalPage": 11,
        #         "currentPage": 1,
        #         "resultList": [
        #             {
        #                 "symbol": "BTC_USDT",
        #                 "fundingRate": 0.000266,
        #                 "settleTime": 1609804800000
        #             },
        #             {
        #                 "symbol": "BTC_USDT",
        #                 "fundingRate": 0.00029,
        #                 "settleTime": 1609776000000
        #             }
        #         ]
        #     }
        # }
        #
        data = self.safe_value(response, 'data')
        result = self.safe_value(data, 'resultList')
        rates = []
        for i in range(0, len(result)):
            entry = result[i]
            marketId = self.safe_string(entry, 'symbol')
            symbol = self.safe_symbol(marketId)
            timestamp = self.safe_string(entry, 'settleTime')
            rates.append({
                'info': entry,
                'symbol': symbol,
                'fundingRate': self.safe_number(entry, 'fundingRate'),
                'timestamp': timestamp,
                'datetime': self.iso8601(timestamp),
            })
        sorted = self.sort_by(rates, 'timestamp')
        return self.filter_by_symbol_since_limit(sorted, symbol, since, limit)
