# -*- 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

# -----------------------------------------------------------------------------

try:
    basestring  # Python 3
except NameError:
    basestring = str  # Python 2
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 InvalidOrder
from ccxt.base.errors import OrderNotFound
from ccxt.base.errors import CancelPending
from ccxt.base.errors import RateLimitExceeded
from ccxt.base.errors import ExchangeNotAvailable
from ccxt.base.decimal_to_precision import TICK_SIZE


class aax(Exchange):

    def describe(self):
        return self.deep_extend(super(aax, self).describe(), {
            'id': 'aax',
            'name': 'AAX',
            'countries': ['MT'],  # Malta
            'enableRateLimit': True,
            'rateLimit': 500,
            'version': 'v2',
            'hostname': 'aaxpro.com',  # aax.com
            'certified': True,
            'has': {
                'cancelAllOrders': True,
                'cancelOrder': True,
                'createOrder': True,
                'editOrder': True,
                'fetchBalance': True,
                'fetchCanceledOrders': True,
                'fetchClosedOrders': True,
                'fetchDepositAddress': True,
                'fetchMarkets': True,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrders': True,
                'fetchTicker': 'emulated',
                'fetchTickers': True,
                'fetchTrades': True,
            },
            'timeframes': {
                '1m': '1m',
                '5m': '5m',
                '15m': '15m',
                '30m': '30m',
                '1h': '1h',
                '2h': '2h',
                '4h': '4h',
                '12h': '12h',
                '1d': '1d',
                '3d': '3d',
                '1w': '1w',
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/104140087-a27f2580-53c0-11eb-87c1-5d9e81208fe9.jpg',
                'test': {
                    'v1': 'https://api.testnet.{hostname}/marketdata/v1',
                    'public': 'https://api.testnet.{hostname}',
                    'private': 'https://api.testnet.{hostname}',
                },
                'api': {
                    'v1': 'https://api.{hostname}/marketdata/v1',
                    'public': 'https://api.{hostname}',
                    'private': 'https://api.{hostname}',
                },
                'www': 'https://www.aaxpro.com',  # string website URL
                'doc': 'https://www.aaxpro.com/apidoc/index.html',
                'fees': 'https://www.aaxpro.com/en-US/fees/',
                'referral': 'https://www.aaxpro.com/invite/sign-up?inviteCode=JXGm5Fy7R2MB',
            },
            'api': {
                'v1': {
                    'get': [
                        'getHistMarketData',  # Get OHLC k line of specific market
                    ],
                },
                'public': {
                    # these endpoints are not documented
                    # 'get': [
                    #     'order_book',  # Get the order book of specified market
                    #     'order_book/{market}',
                    #     'trades',  # Get recent trades on market, each trade is included only once Trades are sorted in reverse creation order.
                    #     'trades/{market}',
                    #     'tickers',  # Get ticker of all markets
                    #     'tickers/{market}',  # Get ticker of specific market
                    # ],
                    'get': [
                        'announcement/maintenance',  # System Maintenance Notice
                        'instruments',  # Retrieve all trading pairs information
                        'market/orderbook',  # Order Book
                        'futures/position/openInterest',  # Open Interest
                        'market/tickers',  # Get the Last 24h Market Summary
                        'market/candles',  # Get Current Candlestick
                        'market/history/candles',  # Get Current Candlestick
                        'market/trades',  # Get the Most Recent Trades
                        'market/markPrice',  # Get Current Mark Price
                        'futures/funding/predictedFunding/{symbol}',  # Get Predicted Funding Rate
                        'futures/funding/prevFundingRate/{symbol}',  # Get Last Funding Rate
                        'market/candles/index',  # Get Current Index Candlestick
                    ],
                },
                'private': {
                    'get': [
                        'user/info',  # Retrieve user information
                        'account/balances',  # Get Account Balances
                        'account/deposit/address',  # undocumented
                        'spot/trades',  # Retrieve trades details for a spot order
                        'spot/openOrders',  # Retrieve spot open orders
                        'spot/orders',  # Retrieve historical spot orders
                        'futures/position',  # Get positions for all contracts
                        'futures/position/closed',  # Get closed positions
                        'futures/trades',  # Retrieve trade details for a futures order
                        'futures/openOrders',  # Retrieve futures open orders
                        'futures/orders',  # Retrieve historical futures orders
                        'futures/funding/predictedFundingFee/{symbol}',  # Get predicted funding fee
                    ],
                    'post': [
                        'account/transfer',  # Asset Transfer
                        'spot/orders',  # Create a new spot order
                        'spot/orders/cancelAllOnTimeout',  # Automatically cancel all your spot orders after a specified timeout.
                        'futures/orders',  # Create a new futures order
                        'futures/orders/cancelAllOnTimeout',  # Automatically cancel all your futures orders after a specified timeout.
                        'futures/position/sltp',  # Set take profit and stop loss orders for an opening position
                        'futures/position/close',  # Close position
                        'futures/position/leverage',  # Update leverage for position
                        'futures/position/margin',  # Modify Isolated Position Margin
                    ],
                    'put': [
                        'spot/orders',  # Amend spot order
                        'futures/orders',  # Amend the quantity of an open futures order
                    ],
                    'delete': [
                        'spot/orders/cancel/{orderID}',  # Cancel a spot order
                        'spot/orders/cancel/all',  # Batch cancel spot orders
                        'futures/orders/cancel/{orderID}',  # Cancel a futures order
                        'futures/orders/cancel/all',  # Batch cancel futures orders
                    ],
                },
            },
            'fees': {
                'trading': {
                    'tierBased': False,
                    'percentage': True,
                    'maker': 0.06 / 100,
                    'taker': 0.10 / 100,
                },
                'funding': {
                    'tierBased': False,
                    'percentage': True,
                    'withdraw': {},  # There is only 1% fee on withdrawals to your bank account.
                },
            },
            'commonCurrencies': {
                'XBT': 'XBT',
            },
            'exceptions': {
                'exact': {
                    '2002': InsufficientFunds,
                    '2003': OrderNotFound,
                    '10003': BadRequest,  # Parameter validation error
                    '10006': AuthenticationError,  # Session expired, please relogin
                    '10007': AuthenticationError,  # Invalid authentication key or token
                    '11007': AuthenticationError,  # Invalid key format
                    '20001': InsufficientFunds,  # Insufficient balance. Please deposit to trade.
                    '20009': InvalidOrder,  # Order amount must be positive
                    '30000': OrderNotFound,  # {"code":30000,"data":null,"message":"The order does not exist","ts":1610259732263}
                    '30001': InvalidOrder,  # The order is being submitted, please try again later
                    '30004': InvalidOrder,  # Minimum quantity is {0}
                    '30005': InvalidOrder,  # Quantity maximum precision is {0} decimal places
                    '30006': InvalidOrder,  # Price maximum precision is {0} decimal places
                    '30007': InvalidOrder,  # Minimum price is {0}
                    '30008': InvalidOrder,  # Stop price maximum precision is {0} decimal places
                    '30009': InvalidOrder,  # Stop Price cannot be less than {0}
                    '30010': InvalidOrder,  # Market price cannot be empty
                    '30011': CancelPending,  # The order is being cancelled, please wait.
                    '30012': BadRequest,  # Unknown currency
                    '30013': BadSymbol,  # Unknown symbol
                    '30014': OrderNotFound,  # Futures order cannot be found
                    '30015': InvalidOrder,  # This is not an open order and cannot modified
                    '30016': ExchangeError,  # No position found
                    '30017': InvalidOrder,  # The current close position is 0. It is recommended that you cancel the current order closing order.
                    '30018': InvalidOrder,  # Order price cannot be greater than {0}
                    '30019': InvalidOrder,  # Order quantity cannot be greater than {0}
                    '30020': InvalidOrder,  # Order price must be a multiple of {0}
                    '30021': InvalidOrder,  # Margin adjustement must be greater than 0
                    '30022': InvalidOrder,  # New quantity must be greater than filled quantity
                    '30023': InvalidOrder,  # Order failed, please try again
                    '30024': InvalidOrder,  # TimeInForce error, only GTC or IOC are allowed
                    '30025': InvalidOrder,  # TimeInForce error, only GTC is allowed
                    '30026': InvalidOrder,  # Quantity is not a multiple of {0}
                    '30027': InvalidOrder,  # Close position failed, it is recommended that you cancel the current order and then close the position.
                    '30028': BadSymbol,  # Symbol cannot be traded at self time
                    '30029': InvalidOrder,  # Modified quantity or price cannot be empty
                    '30030': InvalidOrder,  # Price cannot be specified for market orders
                    '30031': InvalidOrder,  # Liquidation orders cannot be modified
                    '30032': InvalidOrder,  # Leverage cannot be greater than {0}
                    '30033': InvalidOrder,  # Leverage cannot be smaller than {0}
                    '30034': RateLimitExceeded,  # The max number of open orders is {0}. To place a new order, please cancel a previous one
                    '30035': RateLimitExceeded,  # The max number of {0} open orders is {1}. To place a new order, please cancel a previous one
                    '30036': ExchangeNotAvailable,  # Liquidation is in progress, please try again later
                    '30037': InvalidOrder,  # Once stop limit order triggered, stop price cannot be amended
                    '30038': ExchangeError,  # The total value of your orders has exceeded the current risk limit. Please adjust the risk limit
                    '30039': InsufficientFunds,  # Your risk limit has now been changed to {0}, your maximum leverage less than 1, please readjust accordingly
                    '30040': InvalidOrder,  # Order status has changed, please try again later
                    '30041': InvalidOrder,  # Liquidation orders cannot be cancelled
                    '30042': InvalidOrder,  # Order cannot be placed as you will be breaching you max limit value of {1} BTC for {0}
                    '30043': InvalidOrder,  # The risk limit cannot be less than 0
                    '30044': BadRequest,  # Timeout cannot be greater than 60 minutes
                    '30045': InvalidOrder,  # Side is not valid, it should be BUY or SELL
                    '30046': InvalidOrder,  # Order type is not valid, it should be MARKET or LIMIT or STOP-LIMIT or STOP
                    '30047': InvalidOrder,  # The order is closed. Can't cancel
                    '30048': InvalidOrder,  # Market orders cannot be modified
                    '30049': InvalidOrder,  # The order is being modified, please wait
                    '30050': InvalidOrder,  # Maximum 10 orders
                    '40004': BadRequest,  # Requested resource doesn't exist
                    '40009': RateLimitExceeded,  # Too many requests
                    '40102': AuthenticationError,  # {"code":40102,"message":"Unauthorized(invalid key)"}
                    '40103': AuthenticationError,  # {"code":40103,"message":"Unauthorized(invalid sign)"}
                    '40303': PermissionDenied,  # {"code":40303,"message":"Forbidden(invalid scopes)"}
                    '41001': BadRequest,  # Incorrect HTTP request
                    '41002': BadRequest,  # Unsupported HTTP request method
                    '42001': ExchangeNotAvailable,  # Duplicated data entry, please check and try again
                    '50001': ExchangeError,  # Server side exception, please try again later
                    '50002': ExchangeError,  # Server is busy, please try again later
                },
                'broad': {},
            },
            'precisionMode': TICK_SIZE,
            'options': {
                'defaultType': 'spot',  # 'spot', 'future'
            },
        })

    async def fetch_markets(self, params={}):
        response = await self.publicGetInstruments(params)
        #
        #     {
        #         "code":1,
        #         "message":"success",
        #         "ts":1610159448962,
        #         "data":[
        #             {
        #                 "tickSize":"0.01",
        #                 "lotSize":"1",
        #                 "base":"BTC",
        #                 "quote":"USDT",
        #                 "minQuantity":"1.0000000000",
        #                 "maxQuantity":"30000",
        #                 "minPrice":"0.0100000000",
        #                 "maxPrice":"999999.0000000000",
        #                 "status":"readOnly",
        #                 "symbol":"BTCUSDTFP",
        #                 "code":"FP",
        #                 "takerFee":"0.00040",
        #                 "makerFee":"0.00020",
        #                 "multiplier":"0.001000000000",
        #                 "mmRate":"0.00500",
        #                 "imRate":"0.01000",
        #                 "type":"futures",
        #                 "settleType":"Vanilla",
        #                 "settleCurrency":"USDT"
        #             },
        #             {
        #                 "tickSize":"0.5",
        #                 "lotSize":"10",
        #                 "base":"BTC",
        #                 "quote":"USD",
        #                 "minQuantity":"10.0000000000",
        #                 "maxQuantity":"300000",
        #                 "minPrice":"0.5000000000",
        #                 "maxPrice":"999999.0000000000",
        #                 "status":"readOnly",
        #                 "symbol":"BTCUSDFP",
        #                 "code":"FP",
        #                 "takerFee":"0.00040",
        #                 "makerFee":"0.00020",
        #                 "multiplier":"1.000000000000",
        #                 "mmRate":"0.00500",
        #                 "imRate":"0.01000",
        #                 "type":"futures",
        #                 "settleType":"Inverse",
        #                 "settleCurrency":"BTC"
        #             },
        #             {
        #                 "tickSize":"0.0001",
        #                 "lotSize":"0.01",
        #                 "base":"AAB",
        #                 "quote":"USDT",
        #                 "minQuantity":"5.0000000000",
        #                 "maxQuantity":"50000.0000000000",
        #                 "minPrice":"0.0001000000",
        #                 "maxPrice":"999999.0000000000",
        #                 "status":"readOnly",
        #                 "symbol":"AABUSDT",
        #                 "code":null,
        #                 "takerFee":"0.00100",
        #                 "makerFee":"0.00100",
        #                 "multiplier":"1.000000000000",
        #                 "mmRate":"0.02500",
        #                 "imRate":"0.05000",
        #                 "type":"spot",
        #                 "settleType":null,
        #                 "settleCurrency":null
        #             },
        #         ]
        #     }
        #
        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, 'base')
            quoteId = self.safe_string(market, 'quote')
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            status = self.safe_string(market, 'status')
            active = (status == 'enable')
            taker = self.safe_float(market, 'takerFee')
            maker = self.safe_float(market, 'makerFee')
            type = self.safe_string(market, 'type')
            inverse = None
            linear = None
            quanto = None
            spot = (type == 'spot')
            futures = (type == 'futures')
            settleType = self.safe_string_lower(market, 'settleType')
            if settleType is not None:
                inverse = (settleType == 'inverse')
                linear = (settleType == 'vanilla')
                quanto = (settleType == 'quanto')
            symbol = id
            if type == 'spot':
                symbol = base + '/' + quote
            precision = {
                'amount': self.safe_float(market, 'lotSize'),
                'price': self.safe_float(market, 'tickSize'),
            }
            result.append({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'baseId': baseId,
                'quoteId': quoteId,
                'type': type,
                'spot': spot,
                'futures': futures,
                'inverse': inverse,
                'linear': linear,
                'quanto': quanto,
                'precision': precision,
                'info': market,
                'active': active,
                'taker': taker,
                'maker': maker,
                'percentage': False,
                'tierBased': True,
                'limits': {
                    'amount': {
                        'min': self.safe_string(market, 'minQuantity'),
                        'max': self.safe_string(market, 'maxQuantity'),
                    },
                    'price': {
                        'min': self.safe_string(market, 'minPrice'),
                        'max': self.safe_string(market, 'maxPrice'),
                    },
                    'cost': {
                        'min': None,
                        'max': None,
                    },
                },
            })
        return result

    def parse_ticker(self, ticker, market=None):
        #
        #     {
        #         "t":1610162685342,  # timestamp
        #         "a":"0.00000000",  # trading volume in USD in the last 24 hours, futures only
        #         "c":"435.20000000",  # close
        #         "d":"4.22953489",  # change
        #         "h":"455.04000000",  # high
        #         "l":"412.78000000",  # low
        #         "o":"417.54000000",  # open
        #         "s":"BCHUSDTFP",  # market id
        #         "v":"2031068.00000000",  # trading volume in quote currency of last 24 hours
        #     }
        #
        timestamp = self.safe_integer(ticker, 't')
        marketId = self.safe_string(ticker, 's')
        symbol = self.safe_symbol(marketId, market)
        last = self.safe_float(ticker, 'c')
        open = self.safe_float(ticker, 'o')
        change = None
        percentage = None
        average = None
        if last is not None and open is not None:
            change = last - open
            if open > 0:
                percentage = change / open * 100
            average = self.sum(last, open) / 2
        quoteVolume = self.safe_float(ticker, 'v')
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_float(ticker, 'h'),
            'low': self.safe_float(ticker, 'l'),
            'bid': None,
            'bidVolume': None,
            'ask': None,
            'askVolume': None,
            'vwap': None,
            'open': open,
            'close': last,
            'last': last,
            'previousClose': None,
            'change': change,
            'percentage': percentage,
            'average': average,
            'baseVolume': None,
            'quoteVolume': quoteVolume,
            'info': ticker,
        }

    async def fetch_ticker(self, symbol, params={}):
        tickers = await self.fetch_tickers(None, params)
        if symbol in tickers:
            return tickers[symbol]
        raise BadSymbol(self.id + ' fetchTicker() symbol ' + symbol + ' ticker not found')

    async def fetch_tickers(self, symbols=None, params={}):
        await self.load_markets()
        response = await self.publicGetMarketTickers(params)
        #
        #     {
        #         "e":"tickers",
        #         "t":1610162685342,
        #         "tickers":[
        #             {
        #                 "a":"0.00000000",
        #                 "c":"435.20000000",
        #                 "d":"4.22953489",
        #                 "h":"455.04000000",
        #                 "l":"412.78000000",
        #                 "o":"417.54000000",
        #                 "s":"BCHUSDTFP",
        #                 "v":"2031068.00000000",
        #             },
        #         ],
        #     }
        #
        tickers = self.safe_value(response, 'tickers', [])
        result = []
        timestamp = self.safe_integer(response, 't')
        for i in range(0, len(tickers)):
            ticker = self.parse_ticker(self.extend(tickers[i], {'t': timestamp}))
            result.append(ticker)
        return self.filter_by_array(result, 'symbol', symbols)

    async def fetch_order_book(self, symbol, limit=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        if limit is None:
            limit = 20
        else:
            if (limit != 20) and (limit != 50):
                raise BadRequest(self.id + ' fetchOrderBook() limit argument must be None, 20 or 50')
        request = {
            'symbol': market['id'],
            'level': limit,  # required
        }
        #
        response = await self.publicGetMarketOrderbook(self.extend(request, params))
        #
        #     {
        #         "asks":[
        #             ["10823.00000000","0.004000"],
        #             ["10823.10000000","0.100000"],
        #             ["10823.20000000","0.010000"]
        #         ],
        #         "bids":[
        #             ["10821.20000000","0.002000"],
        #             ["10821.10000000","0.005000"],
        #             ["10820.40000000","0.013000"]
        #         ],
        #         "e":"BTCUSDT@book_50",
        #         "t":1561543614756
        #     }
        #
        timestamp = self.safe_integer(response, 't')  # need unix type
        return self.parse_order_book(response, timestamp)

    def parse_trade(self, trade, market=None):
        #
        # public fetchTrades
        #
        #     {
        #         "p":"9395.50000000",
        #         "q":"50.000000",
        #         "t":1592563996718
        #     }
        #
        # private fetchMyTrades
        #
        #     {
        #         "avgPrice":"1199.8",
        #         "base":"ETH",
        #         "clOrdID":null,
        #         "commission":"0.00002",
        #         "createTime":"2021-01-11T02:47:51.512Z",
        #         "cumQty":"0.02",
        #         "filledOrderID":"1eUD4F5rwK",
        #         "filledPrice":"1199.8",
        #         "filledQty":"0.02",
        #         "leavesQty":"0",
        #         "oCreateTime":"2021-01-11T02:47:51.377Z",
        #         "orderID":"1eUD4EHfdU",
        #         "orderQty":"0.02",
        #         "orderStatus":3,
        #         "orderType":1,
        #         "price":"1198.25",
        #         "quote":"USDT",
        #         "rejectCode":null,
        #         "rejectReason":null,
        #         "side":1,
        #         "stopPrice":"0",
        #         "symbol":"ETHUSDT",
        #         "taker":true,
        #         "tradeID":"E04WTIgfmULU",
        #         "transactTime":"2021-01-11T02:47:51.389Z",
        #         "updateTime":null,
        #         "userID":"1362494"
        #     }
        #
        timestamp = self.safe_integer(trade, 't')
        if timestamp is None:
            timestamp = self.parse8601(self.safe_string(trade, 'createTime'))
        id = self.safe_string_2(trade, 'tid', 'tradeID')
        symbol = None
        marketId = self.safe_string(trade, 'symbol')
        market = self.safe_market(marketId, market)
        if market is not None:
            symbol = market['symbol']
        price = self.safe_float_2(trade, 'p', 'filledPrice')
        amount = self.safe_float_2(trade, 'q', 'filledQty')
        orderId = self.safe_string(trade, 'orderID')
        isTaker = self.safe_value(trade, 'taker')
        takerOrMaker = None
        if isTaker is not None:
            takerOrMaker = 'taker' if isTaker else 'maker'
        side = self.safe_string(trade, 'side')
        if side == '1':
            side = 'buy'
        elif side == '2':
            side = 'sell'
        if side is None:
            side = 'buy' if (price > 0) else 'sell'
        side = 'buy' if (price > 0) else 'sell'
        price = abs(price)
        cost = None
        if (price is not None) and (amount is not None):
            cost = price * amount
        orderType = self.parse_order_type(self.safe_string(trade, 'orderType'))
        fee = None
        feeCost = self.safe_float(trade, 'commission')
        if feeCost is not None:
            feeCurrency = None
            if market is not None:
                if side == 'buy':
                    feeCurrency = market['base']
                elif side == 'sell':
                    feeCurrency = market['quote']
            fee = {
                'currency': feeCurrency,
                'cost': feeCost,
            }
        return {
            'info': trade,
            'id': id,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'type': orderType,
            'side': side,
            'order': orderId,
            'takerOrMaker': takerOrMaker,
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': fee,
        }

    async def fetch_trades(self, symbol, since=None, limit=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        limit = 2000 if (limit is None) else limit
        limit = min(limit, 2000)
        request = {
            'symbol': market['id'],
            'limit': limit,
        }
        response = await self.publicGetMarketTrades(request)
        #
        #     {
        #         "e":"BTCUSDFP@trades",
        #         "trades": [
        #             {"p":"9395.50000000","q":"50.000000","t":1592563996718},
        #             {"p":"9395.50000000","q":"50.000000","t":1592563993577},
        #         ],
        #     }
        #
        trades = self.safe_value(response, 'trades', [])
        return self.parse_trades(trades, market, since, limit)

    def parse_ohlcv(self, ohlcv, market=None):
        #
        #     [
        #         0.042398,  # 0 open
        #         0.042684,  # 1 high
        #         0.042366,  # 2 low
        #         0.042386,  # 3 close
        #         0.93734243,  # 4 volume
        #         1611514800,  # 5 timestamp
        #     ]
        #
        return [
            self.safe_timestamp(ohlcv, 5),
            self.safe_float(ohlcv, 0),
            self.safe_float(ohlcv, 1),
            self.safe_float(ohlcv, 2),
            self.safe_float(ohlcv, 3),
            self.safe_float(ohlcv, 4),
        ]

    async def fetch_ohlcv(self, symbol, timeframe='1h', since=None, limit=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            # 'limit': limit,  # if set counts from now into the past
            'symbol': market['id'],
            'timeFrame': self.timeframes[timeframe],
        }
        limit = 500 if (limit is None) else limit
        duration = self.parse_timeframe(timeframe)
        if since is None:
            end = self.seconds()
            request['start'] = end - duration * limit
            request['end'] = end
        else:
            start = int(since / 1000)
            request['start'] = start
            request['end'] = self.sum(start, duration * limit)
        response = await self.publicGetMarketHistoryCandles(self.extend(request, params))
        #
        #     {
        #         "data":[
        #             [0.042398,0.042684,0.042366,0.042386,0.93734243,1611514800],
        #             [0.042386,0.042602,0.042234,0.042373,1.01925239,1611518400],
        #             [0.042373,0.042558,0.042362,0.042389,0.93801705,1611522000],
        #         ],
        #         "success":true,
        #         "t":1611875157
        #     }
        #
        data = self.safe_value(response, 'data', [])
        return self.parse_ohlcvs(data, market, timeframe, since, limit)

    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)
        types = {
            'spot': 'SPTP',
            'future': 'FUTP',
            'otc': 'F2CP',
            'saving': 'VLTP',
        }
        purseType = self.safe_string(types, type, type)
        request = {
            'purseType': purseType,
        }
        params = self.omit(params, 'type')
        response = await self.privateGetAccountBalances(self.extend(request, params))
        #
        #     {
        #         "code":1,
        #         "data":[
        #             {
        #                 "purseType":"FUTP",
        #                 "currency":"BTC",
        #                 "available":"0.41000000",
        #                 "unavailable":"0.00000000"
        #             },
        #             {
        #                 "purseType":"FUTP",
        #                 "currency":"USDT",
        #                 "available":"0.21000000",
        #                 "unvaliable":"0.00000000"
        #             }
        #         ]
        #         "message":"success",
        #         "ts":1573530401020
        #     }
        #
        data = self.safe_value(response, 'data')
        result = {'info': response}
        for i in range(0, len(data)):
            balance = data[i]
            balanceType = self.safe_string(balance, 'purseType')
            if balanceType == purseType:
                currencyId = self.safe_string(balance, 'currency')
                code = self.safe_currency_code(currencyId)
                account = self.account()
                account['free'] = self.safe_float(balance, 'available')
                account['used'] = self.safe_float(balance, 'unavailable')
                result[code] = account
        return self.parse_balance(result)

    async def create_order(self, symbol, type, side, amount, price=None, params={}):
        orderType = type.upper()
        orderSide = side.upper()
        await self.load_markets()
        market = self.market(symbol)
        request = {
            # 'orderType': orderType,  # MARKET, LIMIT, STOP, STOP-LIMIT
            'symbol': market['id'],
            'orderQty': self.amount_to_precision(symbol, amount),
            'side': orderSide,
            # 'stopPrice': self.price_to_precision(symbol, stopPrice),
            # 'clOrdID': clientOrderId,  # up to 20 chars, lowercase and uppercase letters only
            # 'timeInForce': 'GTC',  # GTC, IOC, FOK, default is GTC
            # 'execInst': 'Post-Only',  # the only value supported by the exchange, futures-only
        }
        timeInForce = self.safe_string(params, 'timeInForce')
        if timeInForce is not None:
            request['timeInForce'] = timeInForce
            params = self.omit(params, 'timeInForce')
        clientOrderId = self.safe_string_2(params, 'clOrdID', 'clientOrderId')
        if clientOrderId is not None:
            request['clOrdID'] = clientOrderId
            params = self.omit(params, ['clOrdID', 'clientOrderId'])
        stopPrice = self.safe_float(params, 'stopPrice')
        if stopPrice is None:
            if (orderType == 'STOP-LIMIT') or (orderType == 'STOP'):
                raise ArgumentsRequired(self.id + ' createOrder() requires a stopPrice parameter for ' + orderType + ' orders')
        else:
            if orderType == 'LIMIT':
                orderType = 'STOP-LIMIT'
            elif orderType == 'MARKET':
                orderType = 'STOP'
            request['stopPrice'] = self.price_to_precision(symbol, stopPrice)
            params = self.omit(params, 'stopPrice')
        if orderType == 'LIMIT' or orderType == 'STOP-LIMIT':
            request['price'] = self.price_to_precision(symbol, price)
        request['orderType'] = orderType
        method = None
        if market['spot']:
            method = 'privatePostSpotOrders'
        elif market['futures']:
            method = 'privatePostFuturesOrders'
        response = await getattr(self, method)(self.extend(request, params))
        #
        # spot
        #
        #     {
        #         "code":1,
        #         "data":{
        #             "symbol":"ETHUSDT",
        #             "orderType":2,
        #             "avgPrice":"0",
        #             "execInst":null,
        #             "orderStatus":0,
        #             "userID":"1362494",
        #             "quote":"USDT",
        #             "rejectReason":null,
        #             "rejectCode":null,
        #             "price":"1500",
        #             "orderQty":"1",
        #             "commission":"0",
        #             "id":"268323430253735936",
        #             "timeInForce":1,
        #             "isTriggered":false,
        #             "side":2,
        #             "orderID":"1eO51MDSpQ",
        #             "leavesQty":"0",
        #             "cumQty":"0",
        #             "updateTime":null,
        #             "lastQty":"0",
        #             "clOrdID":null,
        #             "stopPrice":null,
        #             "createTime":null,
        #             "transactTime":null,
        #             "base":"ETH",
        #             "lastPrice":"0"
        #         },
        #         "message":"success",
        #         "ts":1610245290980
        #     }
        #
        # futures
        #
        #     {
        #         "code":1,
        #         "data":{
        #             "liqType":0,
        #             "symbol":"ETHUSDTFP",
        #             "orderType":2,
        #             "leverage":"1",
        #             "marketPrice":"1318.3150000000",
        #             "code":"FP",
        #             "avgPrice":"0",
        #             "execInst":null,
        #             "orderStatus":0,
        #             "userID":"1362494",
        #             "quote":"USDT",
        #             "rejectReason":null,
        #             "rejectCode":null,
        #             "price":"500",
        #             "orderQty":"1",
        #             "commission":"0",
        #             "id":"268346885133053953",
        #             "timeInForce":1,
        #             "isTriggered":false,
        #             "side":1,
        #             "orderID":"1eOuPUAAkq",
        #             "leavesQty":"1",
        #             "cumQty":"0",
        #             "updateTime":null,
        #             "lastQty":null,
        #             "clOrdID":null,
        #             "stopPrice":null,
        #             "createTime":null,
        #             "transactTime":null,
        #             "settleType":"VANILLA",
        #             "base":"ETH",
        #             "lastPrice":"0"
        #         },
        #         "message":"success",
        #         "ts":1610250883059
        #     }
        #
        data = self.safe_value(response, 'data', {})
        return self.parse_order(data, market)

    async def edit_order(self, id, symbol, type, side, amount, price=None, params={}):
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'orderID': id,
            # 'orderQty': self.amount_to_precision(symbol, amount),
            # 'price': self.price_to_precision(symbol, price),
            # 'stopPrice': self.price_to_precision(symbol, stopPrice),
        }
        stopPrice = self.safe_float(params, 'stopPrice')
        if stopPrice is not None:
            request['stopPrice'] = self.price_to_precision(symbol, stopPrice)
            params = self.omit(params, 'stopPrice')
        if price is not None:
            request['price'] = self.price_to_precision(symbol, price)
        if amount is not None:
            request['orderQty'] = self.amount_to_precision(symbol, amount)
        method = None
        if market['spot']:
            method = 'privatePutSpotOrders'
        elif market['futures']:
            method = 'privatePutFuturesOrders'
        response = await getattr(self, method)(self.extend(request, params))
        #
        # spot
        #
        #     {
        #         "code":1,
        #         "data":{
        #             "symbol":"ETHUSDT",
        #             "orderType":2,
        #             "avgPrice":"0",
        #             "execInst":null,
        #             "orderStatus":0,
        #             "userID":"1362494",
        #             "quote":"USDT",
        #             "rejectReason":null,
        #             "rejectCode":null,
        #             "price":"1500",
        #             "orderQty":"1",
        #             "commission":"0",
        #             "id":"268323430253735936",
        #             "timeInForce":1,
        #             "isTriggered":false,
        #             "side":2,
        #             "orderID":"1eO51MDSpQ",
        #             "leavesQty":"0",
        #             "cumQty":"0",
        #             "updateTime":null,
        #             "lastQty":"0",
        #             "clOrdID":null,
        #             "stopPrice":null,
        #             "createTime":null,
        #             "transactTime":null,
        #             "base":"ETH",
        #             "lastPrice":"0"
        #         },
        #         "message":"success",
        #         "ts":1610245290980
        #     }
        #
        # futures
        #
        #     {
        #         "code":1,
        #         "data":{
        #             "liqType":0,
        #             "symbol":"ETHUSDTFP",
        #             "orderType":2,
        #             "leverage":"1",
        #             "marketPrice":"1318.3150000000",
        #             "code":"FP",
        #             "avgPrice":"0",
        #             "execInst":null,
        #             "orderStatus":0,
        #             "userID":"1362494",
        #             "quote":"USDT",
        #             "rejectReason":null,
        #             "rejectCode":null,
        #             "price":"500",
        #             "orderQty":"1",
        #             "commission":"0",
        #             "id":"268346885133053953",
        #             "timeInForce":1,
        #             "isTriggered":false,
        #             "side":1,
        #             "orderID":"1eOuPUAAkq",
        #             "leavesQty":"1",
        #             "cumQty":"0",
        #             "updateTime":null,
        #             "lastQty":null,
        #             "clOrdID":null,
        #             "stopPrice":null,
        #             "createTime":null,
        #             "transactTime":null,
        #             "settleType":"VANILLA",
        #             "base":"ETH",
        #             "lastPrice":"0"
        #         },
        #         "message":"success",
        #         "ts":1610250883059
        #     }
        #
        data = self.safe_value(response, 'data', {})
        return self.parse_order(data, market)

    async def cancel_order(self, id, symbol=None, params={}):
        await self.load_markets()
        request = {
            'orderID': id,
        }
        method = None
        defaultType = self.safe_string_2(self.options, 'cancelOrder', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        params = self.omit(params, 'type')
        market = None
        if symbol is not None:
            market = self.market(symbol)
            type = market['type']
        if type == 'spot':
            method = 'privateDeleteSpotOrdersCancelOrderID'
        elif type == 'futures':
            method = 'privateDeleteFuturesOrdersCancelOrderID'
        response = await getattr(self, method)(self.extend(request, params))
        #
        # spot
        #
        #     {
        #         "code":1,
        #         "data":{
        #             "avgPrice":"0",
        #             "base":"BTC",
        #             "clOrdID":"aax",
        #             "commission":"0",
        #             "createTime":"2019-11-12T03:46:41Z",
        #             "cumQty":"0",
        #             "id":"114330021504606208",
        #             "isTriggered":false,
        #             "lastPrice":"0",
        #             "lastQty":"0",
        #             "leavesQty":"0",
        #             "orderID":"wJ4L366KB",
        #             "orderQty":"0.05",
        #             "orderStatus":1,
        #             "orderType":2,
        #             "price":"8000",
        #             "quote":"USDT",
        #             "rejectCode":0,
        #             "rejectReason":null,
        #             "side":1,
        #             "stopPrice":"0",
        #             "symbol":"BTCUSDT",
        #             "transactTime":null,
        #             "updateTime":"2019-11-12T03:46:41Z",
        #             "timeInForce":1,
        #             "userID":"216214"
        #         },
        #         "message":"success",
        #         "ts":1573530402029
        #     }
        #
        # futures
        #
        #     {
        #         "code":1,
        #         "data":{
        #             "avgPrice":"0",
        #             "base":"BTC",
        #             "clOrdID":"aax_futures",
        #             "code":"FP",
        #             "commission":"0",
        #             "createTime":"2019-11-12T06:48:58Z",
        #             "cumQty":"0",
        #             "id":"114375893764395008",
        #             "isTriggered":false,
        #             "lastPrice":"0",
        #             "lastQty":null,
        #             "leavesQty":"300",
        #             "leverage":"1",
        #             "liqType":0,
        #             "marketPrice":"8760.75",
        #             "orderID":"wJTewQc81",
        #             "orderQty":"300",
        #             "orderStatus":1,
        #             "orderType":2,
        #             "price":"8000",
        #             "quote":"USD",
        #             "rejectCode":0,
        #             "rejectReason":null,
        #             "settleType":"INVERSE",
        #             "side":1,
        #             "stopPrice":"0",
        #             "symbol":"BTCUSDFP",
        #             "transactTime":"2019-11-12T06:48:58Z",
        #             "updateTime":"2019-11-12T06:48:58Z",
        #             "timeInForce":1,
        #             "execInst": "",
        #             "userID":"216214"
        #         },
        #         "message":"success",
        #         "ts":1573541642970
        #     }
        #
        data = self.safe_value(response, 'data', {})
        return self.parse_order(data, market)

    async def cancel_all_orders(self, symbol=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelAllOrders() requires a symbol argument')
        await self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        method = None
        if market['spot']:
            method = 'privateDeleteSpotOrdersCancelAll'
        elif market['futures']:
            method = 'privateDeleteFuturesOrdersCancelAll'
        response = await getattr(self, method)(self.extend(request, params))
        #
        #     {
        #         "code":1,
        #         "data":[
        #             "vBC9rXsEE",
        #             "vBCc46OI0"
        #             ],
        #         "message":"success",
        #         "ts":1572597435470
        #     }
        #
        return response

    async def fetch_order(self, id, symbol=None, params={}):
        await self.load_markets()
        defaultType = self.safe_string_2(self.options, 'fetchOrder', 'defaultType', 'spot')
        params['type'] = self.safe_string(params, 'type', defaultType)
        request = {}
        clientOrderId = self.safe_string_2(params, 'clOrdID', 'clientOrderId')
        if clientOrderId is None:
            request['orderID'] = id
        else:
            request['clOrdID'] = clientOrderId
            params = self.omit(params, ['clOrdID', 'clientOrderId'])
        orders = await self.fetch_orders(symbol, None, None, self.extend(request, params))
        order = self.safe_value(orders, 0)
        if order is None:
            if clientOrderId is None:
                raise OrderNotFound(self.id + ' fetchOrder() could not find order id ' + id)
            else:
                raise OrderNotFound(self.id + ' fetchOrder() could not find order clientOrderID ' + clientOrderId)
        return order

    async def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        await self.load_markets()
        request = {
            # 'pageNum': '1',
            # 'pageSize': '10',
            # 'symbol': market['id'],
            # 'orderID': id,
            # 'side': 'None',  # BUY, SELL
            # 'clOrdID': clientOrderId,
        }
        defaultType = self.safe_string_2(self.options, 'fetchOpenOrders', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        params = self.omit(params, 'type')
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
            type = market['type']
        clientOrderId = self.safe_string_2(params, 'clOrdID', 'clientOrderId')
        if clientOrderId is not None:
            request['clOrdID'] = clientOrderId
            params = self.omit(params, ['clOrdID', 'clientOrderId'])
        method = None
        if type == 'spot':
            method = 'privateGetSpotOpenOrders'
        elif type == 'futures':
            method = 'privateGetFuturesOpenOrders'
        if limit is not None:
            request['pageSize'] = limit  # default 10
        response = await getattr(self, method)(self.extend(request, params))
        #
        # spot
        #
        #     {
        #         "code":1,
        #         "data":{
        #             "total":19,
        #             "pageSize":10,
        #             "list":[
        #                 {
        #                     "orderType":2,
        #                     "symbol":"BTCUSDT",
        #                     "avgPrice":"0",
        #                     "orderStatus":0,
        #                     "userID":"7225",
        #                     "quote":"USDT",
        #                     "rejectReason":null,
        #                     "rejectCode":null,
        #                     "price":"0",
        #                     "orderQty":"0.002",
        #                     "commission":"0",
        #                     "id":"110419975166304256",
        #                     "isTriggered":null,
        #                     "side":1,
        #                     "orderID":"vBGlDcLwk",
        #                     "cumQty":"0",
        #                     "leavesQty":"0",
        #                     "updateTime":null,
        #                     "clOrdID":"0001",
        #                     "lastQty":"0",
        #                     "stopPrice":"0",
        #                     "createTime":"2019-11-01T08:49:33Z",
        #                     "transactTime":null,
        #                     "timeInForce":1,
        #                     "base":"BTC",
        #                     "lastPrice":"0"
        #                 }
        #             ],
        #             "pageNum":1
        #         },
        #         "message":"success",
        #         "ts":1572598173682
        #     }
        #
        # futures
        #
        #     {
        #         "code":1,
        #         "data":{
        #             "list":[
        #                 {
        #                     "avgPrice":"8768.99999999484997",
        #                     "base":"BTC",
        #                     "clOrdID":null,
        #                     "code":"FP",
        #                     "commission":"0.00000913",
        #                     "createTime":"2019-11-12T07:05:52.000Z,
        #                     "cumQty":"100",
        #                     "id":"114380149603028993",
        #                     "isTriggered":false,
        #                     "lastPrice":"8769",
        #                     "lastQty":"100",
        #                     "leavesQty":"0",
        #                     "leverage":"1",
        #                     "liqType":1,
        #                     "marketPrice":"8769.75",
        #                     "orderID":"wJXURIFBT",
        #                     "orderQty":"100",
        #                     "orderStatus":3,
        #                     "orderType":1,
        #                     "price":"8769.75",
        #                     "quote":"USD",
        #                     "rejectCode":0,
        #                     "rejectReason":null,
        #                     "settleType":"INVERSE",
        #                     "side":2,
        #                     "stopPrice":"0",
        #                     "symbol":"BTCUSDFP",
        #                     "transactTime":"2019-11-12T07:05:52.000Z,
        #                     "updateTime":"2019-11-12T07:05:52.000Z,
        #                     "timeInForce":1,
        #                     "execInst": "",
        #                     "userID":"216214"
        #                 },
        #             ],
        #             "pageNum":1,
        #             "pageSize":10,
        #             "total":21
        #         },
        #         "message":"success",
        #         "ts":1573546960172
        #     }
        #
        data = self.safe_value(response, 'data', {})
        orders = self.safe_value(data, 'list', [])
        return self.parse_orders(orders, market, since, limit)

    async def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
        request = {
            'orderStatus': '2',  # 1 new, 2 filled, 3 canceled
        }
        return await self.fetch_orders(symbol, since, limit, self.extend(request, params))

    async def fetch_canceled_orders(self, symbol=None, since=None, limit=None, params={}):
        request = {
            'orderStatus': '3',  # 1 new, 2 filled, 3 canceled
        }
        return await self.fetch_orders(symbol, since, limit, self.extend(request, params))

    async def fetch_orders(self, symbol=None, since=None, limit=None, params={}):
        await self.load_markets()
        request = {
            # 'pageNum': '1',
            # 'pageSize': '10',
            # 'symbol': market['id'],
            # 'orderID': id,
            # 'base': market['baseId'],
            # 'quote': market['quoteId'],
            # 'orderStatus': None,  # 1 new, 2 filled, 3 canceled
            # 'startDate': self.ymd(since),
            # 'endDate': self.ymd(self.milliseconds()),
            # 'orderType': None,  # MARKET, LIMIT, STOP, STOP-LIMIT
            # 'side': 'None',  # BUY, SELL
            # 'clOrdID': clientOrderId,
        }
        method = None
        defaultType = self.safe_string_2(self.options, 'fetchOrders', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        params = self.omit(params, 'type')
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
            type = market['type']
        if type == 'spot':
            method = 'privateGetSpotOrders'
        elif type == 'futures':
            method = 'privateGetFuturesOrders'
        clientOrderId = self.safe_string_2(params, 'clOrdID', 'clientOrderId')
        if clientOrderId is not None:
            request['clOrdID'] = clientOrderId
            params = self.omit(params, ['clOrdID', 'clientOrderId'])
        if limit is not None:
            request['pageSize'] = limit  # default 10
        if since is not None:
            request['startDate'] = self.ymd(since)
        response = await getattr(self, method)(self.extend(request, params))
        #
        # spot
        #
        #     {
        #         "code":1,
        #         "data":{
        #             "total":19,
        #             "pageSize":10,
        #             "list":[
        #                 {
        #                     "orderType":2,
        #                     "symbol":"BTCUSDT",
        #                     "avgPrice":"0",
        #                     "orderStatus":0,
        #                     "userID":"7225",
        #                     "quote":"USDT",
        #                     "rejectReason":null,
        #                     "rejectCode":null,
        #                     "price":"0",
        #                     "orderQty":"0.002",
        #                     "commission":"0",
        #                     "id":"110419975166304256",
        #                     "isTriggered":null,
        #                     "side":1,
        #                     "orderID":"vBGlDcLwk",
        #                     "cumQty":"0",
        #                     "leavesQty":"0",
        #                     "updateTime":null,
        #                     "clOrdID":"0001",
        #                     "lastQty":"0",
        #                     "stopPrice":"0",
        #                     "createTime":"2019-11-01T08:49:33Z",
        #                     "transactTime":null,
        #                     "timeInForce":1,
        #                     "base":"BTC",
        #                     "lastPrice":"0"
        #                 }
        #             ],
        #             "pageNum":1
        #         },
        #         "message":"success",
        #         "ts":1572598173682
        #     }
        #
        # futures
        #
        #     {
        #         "code":1,
        #         "data":{
        #             "list":[
        #                 {
        #                     "avgPrice":"8768.99999999484997",
        #                     "base":"BTC",
        #                     "clOrdID":null,
        #                     "code":"FP",
        #                     "commission":"0.00000913",
        #                     "createTime":"2019-11-12T07:05:52.000Z,
        #                     "cumQty":"100",
        #                     "id":"114380149603028993",
        #                     "isTriggered":false,
        #                     "lastPrice":"8769",
        #                     "lastQty":"100",
        #                     "leavesQty":"0",
        #                     "leverage":"1",
        #                     "liqType":1,
        #                     "marketPrice":"8769.75",
        #                     "orderID":"wJXURIFBT",
        #                     "orderQty":"100",
        #                     "orderStatus":3,
        #                     "orderType":1,
        #                     "price":"8769.75",
        #                     "quote":"USD",
        #                     "rejectCode":0,
        #                     "rejectReason":null,
        #                     "settleType":"INVERSE",
        #                     "side":2,
        #                     "stopPrice":"0",
        #                     "symbol":"BTCUSDFP",
        #                     "transactTime":"2019-11-12T07:05:52.000Z,
        #                     "updateTime":"2019-11-12T07:05:52.000Z,
        #                     "timeInForce":1,
        #                     "execInst": "",
        #                     "userID":"216214"
        #                 },
        #             ],
        #             "pageNum":1,
        #             "pageSize":10,
        #             "total":21
        #         },
        #         "message":"success",
        #         "ts":1573546960172
        #     }
        #
        data = self.safe_value(response, 'data', {})
        orders = self.safe_value(data, 'list', [])
        return self.parse_orders(orders, market, since, limit)

    def parse_order_status(self, status):
        statuses = {
            '0': 'open',  # pending new
            '1': 'open',  # new
            '2': 'open',  # partially-filled
            '3': 'closed',  # filled
            '4': 'canceled',  # cancel-reject
            '5': 'canceled',  # canceled
            '6': 'rejected',  # rejected
            '10': 'expired',  # expired
            '11': 'rejected',  # business-reject
        }
        return self.safe_string(statuses, status, status)

    def parse_order_type(self, status):
        statuses = {
            '1': 'market',
            '2': 'limit',
            '3': 'stop',
            '4': 'stop-limit',
            '7': 'stop-loss',
            '8': 'take-profit',
        }
        return self.safe_string(statuses, status, status)

    def parse_time_in_force(self, timeInForce):
        timeInForces = {
            '1': 'GTC',
            '3': 'IOC',
            '4': 'FOK',
        }
        return self.safe_string(timeInForces, timeInForce, timeInForce)

    def parse_order(self, order, market=None):
        #
        #     {
        #         "avgPrice":"8768.99999999484997",
        #         "base":"BTC",
        #         "clOrdID":null,
        #         "code":"FP",  # futures only
        #         "commission":"0.00000913",
        #         "createTime":"2019-11-12T07:05:52.000Z,
        #         "cumQty":"100",
        #         "id":"114380149603028993",  # futures only
        #         "isTriggered":false,
        #         "lastPrice":"8769",
        #         "lastQty":"100",
        #         "leavesQty":"0",
        #         "leverage":"1",  # futures only
        #         "liqType":1,  # futures only
        #         "marketPrice":"8769.75",  # futures only
        #         "orderID":"wJXURIFBT",
        #         "orderQty":"100",
        #         "orderStatus":3,
        #         "orderType":1,
        #         "price":"8769.75",
        #         "quote":"USD",
        #         "rejectCode":0,
        #         "rejectReason":null,
        #         "settleType":"INVERSE",  # futures only
        #         "side":2,
        #         "stopPrice":"0",
        #         "symbol":"BTCUSDFP",
        #         "transactTime":"2019-11-12T07:05:52.000Z,
        #         "updateTime":"2019-11-12T07:05:52.000Z,
        #         "timeInForce":1,
        #         "execInst": "",
        #         "userID":"216214"
        #     }
        #
        # sometimes the timestamp is returned in milliseconds
        timestamp = self.safe_value(order, 'createTime')
        if isinstance(timestamp, basestring):
            timestamp = self.parse8601(timestamp)
        status = self.parse_order_status(self.safe_string(order, 'orderStatus'))
        type = self.parse_order_type(self.safe_string(order, 'orderType'))
        side = self.safe_string(order, 'side')
        if side == '1':
            side = 'buy'
        elif side == '2':
            side = 'sell'
        id = self.safe_string(order, 'orderID')
        clientOrderId = self.safe_string(order, 'clOrdID')
        marketId = self.safe_string(order, 'symbol')
        market = self.safe_market(marketId, market)
        symbol = market['symbol']
        price = self.safe_float(order, 'price')
        stopPrice = self.safe_float(order, 'stopPrice')
        timeInForce = self.parse_time_in_force(self.safe_string(order, 'timeInForce'))
        execInst = self.safe_string(order, 'execInst')
        postOnly = (execInst == 'Post-Only')
        average = self.safe_float(order, 'avgPrice')
        amount = self.safe_float(order, 'orderQty')
        filled = self.safe_float(order, 'cumQty')
        remaining = self.safe_string(order, 'leavesQty')
        cost = None
        lastTradeTimestamp = None
        if filled is not None:
            if price is not None:
                cost = filled * price
            if filled > 0:
                lastTradeTimestamp = self.safe_value(order, 'transactTime')
                if isinstance(lastTradeTimestamp, basestring):
                    lastTradeTimestamp = self.parse8601(lastTradeTimestamp)
        fee = None
        feeCost = self.safe_float(order, 'commission')
        if feeCost is not None:
            feeCurrency = None
            if market is not None:
                if side == 'buy':
                    feeCurrency = market['base']
                elif side == 'sell':
                    feeCurrency = market['quote']
            fee = {
                'currency': feeCurrency,
                'cost': feeCost,
            }
        return {
            'id': id,
            'info': order,
            'clientOrderId': clientOrderId,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': lastTradeTimestamp,
            'status': status,
            'symbol': symbol,
            'type': type,
            'timeInForce': timeInForce,
            'postOnly': postOnly,
            'side': side,
            'price': price,
            'stopPrice': stopPrice,
            'average': average,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'cost': cost,
            'trades': None,
            'fee': fee,
        }

    async def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
        await self.load_markets()
        request = {
            # 'pageNum': '1',
            # 'pageSize': '10',
            # 'symbol': market['id'],
            # 'orderID': id,
            # 'base': market['baseId'],
            # 'quote': market['quoteId'],
            # 'startDate': self.ymd(since),
            # 'endDate': self.ymd(self.milliseconds()),
            # 'orderType': None,  # MARKET, LIMIT, STOP, STOP-LIMIT
            # 'side': 'None',  # BUY, SELL
        }
        method = None
        defaultType = self.safe_string_2(self.options, 'fetchMyTrades', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        params = self.omit(params, 'type')
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
            type = market['type']
        if type == 'spot':
            method = 'privateGetSpotTrades'
        elif type == 'futures':
            method = 'privateGetFuturesTrades'
        if limit is not None:
            request['pageSize'] = limit  # default 10
        if since is not None:
            request['startDate'] = self.ymd(since)
        response = await getattr(self, method)(self.extend(request, params))
        #
        #     {
        #         "code":1,
        #         "data":{
        #             "list":[
        #                 {
        #                     "avgPrice":"1199.8",
        #                     "base":"ETH",
        #                     "clOrdID":null,
        #                     "commission":"0.00002",
        #                     "createTime":"2021-01-11T02:47:51.512Z",
        #                     "cumQty":"0.02",
        #                     "filledOrderID":"1eUD4F5rwK",
        #                     "filledPrice":"1199.8",
        #                     "filledQty":"0.02",
        #                     "leavesQty":"0",
        #                     "oCreateTime":"2021-01-11T02:47:51.377Z",
        #                     "orderID":"1eUD4EHfdU",
        #                     "orderQty":"0.02",
        #                     "orderStatus":3,
        #                     "orderType":1,
        #                     "price":"1198.25",
        #                     "quote":"USDT",
        #                     "rejectCode":null,
        #                     "rejectReason":null,
        #                     "side":1,
        #                     "stopPrice":"0",
        #                     "symbol":"ETHUSDT",
        #                     "taker":true,
        #                     "tradeID":"E04WTIgfmULU",
        #                     "transactTime":"2021-01-11T02:47:51.389Z",
        #                     "updateTime":null,
        #                     "userID":"1362494"
        #                 }
        #             ],
        #             "pageNum":1,
        #             "pageSize":10,
        #             "total":1
        #         },
        #         "message":"success",
        #         "ts":1610333278042
        #     }
        #
        data = self.safe_value(response, 'data', {})
        trades = self.safe_value(data, 'list', [])
        return self.parse_trades(trades, market, since, limit)

    async def fetch_deposit_address(self, code, params={}):
        await self.load_markets()
        currency = self.currency(code)
        request = {
            'currency': currency['id'],
            # 'network': None,  # 'ERC20
        }
        response = await self.privateGetAccountDepositAddress(self.extend(request, params))
        #
        #     {
        #         "code":1,
        #         "data":{
        #             "address":"0x080c5c667381404cca9be0be9a04b2e47691ff86",
        #             "tag":null,
        #             "currency":"USDT",
        #             "network":"ERC20"
        #         },
        #         "message":"success",
        #         "ts":1610270465132
        #     }
        #
        data = self.safe_value(response, 'data', {})
        return self.parse_deposit_address(data, currency)

    def parse_deposit_address(self, depositAddress, currency=None):
        #
        #     {
        #         "address":"0x080c5c667381404cca9be0be9a04b2e47691ff86",
        #         "tag":null,
        #         "currency":"USDT",
        #         "network":"ERC20"
        #     }
        #
        address = self.safe_string(depositAddress, 'address')
        tag = self.safe_string(depositAddress, 'tag')
        currencyId = self.safe_string(depositAddress, 'currency')
        code = self.safe_currency_code(currencyId)
        return {
            'info': depositAddress,
            'code': code,
            'address': address,
            'tag': tag,
        }

    def nonce(self):
        return self.milliseconds()

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        url = '/' + self.implode_params(path, params)
        query = self.omit(params, self.extract_params(path))
        if api == 'v1':
            if query:
                url += '?' + self.urlencode(query)
        else:
            url = '/' + self.version + url
            if api == 'public':
                if query:
                    url += '?' + self.urlencode(query)
            elif api == 'private':
                self.check_required_credentials()
                nonce = str(self.nonce())
                headers = {
                    'X-ACCESS-KEY': self.apiKey,
                    'X-ACCESS-NONCE': nonce,
                }
                auth = nonce + ':' + method
                if method == 'GET':
                    if query:
                        url += '?' + self.urlencode(query)
                    auth += url
                else:
                    headers['Content-Type'] = 'application/json'
                    body = self.json(query)
                    auth += url + body
                signature = self.hmac(self.encode(auth), self.encode(self.secret))
                headers['X-ACCESS-SIGN'] = signature
        url = self.implode_params(self.urls['api'][api], {'hostname': self.hostname}) + url
        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  # fallback to default error handler
        #
        #     {"code":40102,"message":"Unauthorized(invalid key)"}
        #
        errorCode = self.safe_string(response, 'code')
        if (errorCode is not None) and (errorCode != '1'):
            feedback = self.id + ' ' + self.json(response)
            self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, feedback)
            self.throw_broadly_matched_exception(self.exceptions['broad'], body, feedback)
