# -*- 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
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': {
                },
            },
        })

    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 = getattr(self, method)(query)
        #
        # spot
        #
        #     {
        #         "code":200,
        #         "data":1633375641837
        #     }
        #
        # contract
        #
        #     {
        #         "success":true,
        #         "code":0,
        #         "data":1634095541710
        #     }
        #
        return self.safe_integer(response, 'data')

    def fetch_status(self, params={}):
        response = 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

    def fetch_currencies(self, params={}):
        response = 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

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

    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 self.fetch_spot_markets(query)
        elif swap:
            return self.fetch_contract_markets(query)

    def fetch_contract_markets(self, params={}):
        response = 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

    def fetch_spot_markets(self, params={}):
        response = 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

    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)
        query = self.omit(params, 'type')
        method = 'spotPublicGetMarketTicker'
        if type == 'swap':
            method = 'contractPublicGetTicker'
        response = 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)

    def fetch_ticker(self, symbol, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        method = None
        if market['spot']:
            method = 'spotPublicGetMarketTicker'
        elif market['swap']:
            method = 'contractPublicGetTicker'
        response = 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)

    def fetch_order_book(self, symbol, limit=None, params={}):
        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 = 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

    def fetch_trades(self, symbol, since=None, limit=None, params={}):
        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 = 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,
        }

    def fetch_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
        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 = 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),
        ]

    def fetch_balance(self, params={}):
        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 = 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,
        }

    def fetch_deposit_addresses_by_network(self, code, params={}):
        self.load_markets()
        currency = self.currency(code)
        request = {
            'currency': currency['id'],
        }
        response = 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')

    def fetch_deposit_address(self, code, params={}):
        rawNetwork = self.safe_string_upper(params, 'network')
        params = self.omit(params, 'network')
        response = 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

    def fetch_deposits(self, code=None, since=None, limit=None, params={}):
        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 = 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)

    def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
        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 = 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)

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

    def fetch_positions(self, symbols=None, params={}):
        self.load_markets()
        response = 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

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

    def create_spot_order(self, symbol, type, side, amount, price=None, params={}):
        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 = self.spotPrivatePostOrderPlace(self.extend(request, params))
        #
        #     {"code":200,"data":"2ff3163e8617443cb9c6fc19d42b1ca4"}
        #
        return self.parse_order(response, market)

    def create_swap_order(self, symbol, type, side, amount, price=None, params={}):
        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 = self.contractPrivatePostOrderSubmit(self.extend(request, params))
        #
        #     {"code":200,"data":"2ff3163e8617443cb9c6fc19d42b1ca4"}
        #
        return self.parse_order(response, market)

    def cancel_order(self, id, symbol=None, params={}):
        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 = 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)

    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')
        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 = 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)

    def fetch_order(self, id, symbol=None, params={}):
        self.load_markets()
        request = {
            'order_ids': id,
        }
        response = 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)

    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')
        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 = self.spotPrivateGetOrderList(self.extend(request, params))
        data = self.safe_value(response, 'data', [])
        return self.parse_orders(data, market, since, limit)

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

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

    def cancel_all_orders(self, symbol=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        response = 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

    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')
        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 = 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)

    def fetch_order_trades(self, id, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        market = None
        if symbol is not None:
            market = self.market(symbol)
        request = {
            'order_id': id,
        }
        response = 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)

    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')
        self.load_markets()
        request = {
            'positionId': positionId,
            'amount': amount,
            'type': addOrReduce,
        }
        response = self.contractPrivatePostPositionChangeMargin(self.extend(request, params))
        #
        #     {
        #         "success": True,
        #         "code": 0
        #     }
        return response

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

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

    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')
        self.load_markets()
        request = {
            'positionId': positionId,
            'leverage': leverage,
        }
        return self.contractPrivatePostPositionChangeLeverage(self.extend(request, params))

    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)
        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 = 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)

    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')
        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 = 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)
