# -*- 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
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
from ccxt.base.precise import Precise


class aax(Exchange):

    def describe(self):
        return self.deep_extend(super(aax, self).describe(), {
            'id': 'aax',
            'name': 'AAX',
            'countries': ['MT'],  # Malta
            # 6000 /  hour => 100 per minute => 1.66 requests per second => rateLimit = 600
            # market endpoints ratelimits arent mentioned in docs so they are also set to "all other authenticated endpoints"
            # 5000 / hour => weight = 1.2("all other authenticated endpoints")
            # 600 / hour => weight = 10
            # 200 / hour => weight = 30
            'rateLimit': 600,
            'version': 'v2',
            'hostname': 'aaxpro.com',  # aax.com
            'pro': True,
            'has': {
                'CORS': None,
                'spot': True,
                'margin': True,
                'swap': True,
                'future': False,
                'option': False,
                'addMargin': False,
                'cancelAllOrders': True,
                'cancelOrder': True,
                'cancelOrders': True,
                'createDepositAddress': False,
                'createOrder': True,
                'createReduceOnlyOrder': False,
                'createStopLimitOrder': True,
                'createStopMarketOrder': True,
                'createStopOrder': True,
                'editOrder': True,
                'fetchAccounts': True,
                'fetchBalance': True,
                'fetchBidsAsks': None,
                'fetchBorrowRate': False,
                'fetchBorrowRateHistories': False,
                'fetchBorrowRateHistory': False,
                'fetchBorrowRates': False,
                'fetchBorrowRatesPerSymbol': False,
                'fetchCanceledOrders': True,
                'fetchClosedOrder': None,
                'fetchClosedOrders': True,
                'fetchCurrencies': True,
                'fetchDeposit': False,
                'fetchDepositAddress': True,
                'fetchDepositAddresses': False,
                'fetchDepositAddressesByNetwork': None,
                'fetchDeposits': True,
                'fetchFundingHistory': True,
                'fetchFundingRate': True,
                'fetchFundingRateHistory': True,
                'fetchFundingRates': False,
                'fetchIndexOHLCV': False,
                'fetchL3OrderBook': None,
                'fetchLedger': None,
                'fetchLedgerEntry': None,
                'fetchLeverage': None,
                'fetchLeverageTiers': False,
                'fetchMarginMode': False,
                'fetchMarketLeverageTiers': False,
                'fetchMarkets': True,
                'fetchMarkOHLCV': False,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenInterest': True,
                'fetchOpenInterestHistory': False,
                'fetchOpenOrder': None,
                'fetchOpenOrders': True,
                'fetchOrder': True,
                'fetchOrderBook': True,
                'fetchOrderBooks': False,
                'fetchOrders': True,
                'fetchOrderTrades': None,
                'fetchPosition': True,
                'fetchPositionMode': False,
                'fetchPositions': True,
                'fetchPositionsRisk': False,
                'fetchPremiumIndexOHLCV': False,
                'fetchStatus': True,
                'fetchTicker': 'emulated',
                'fetchTickers': True,
                'fetchTime': True,
                'fetchTrades': True,
                'fetchTradingFee': False,
                'fetchTradingFees': False,
                'fetchTradingLimits': None,
                'fetchTransactionFee': None,
                'fetchTransactionFees': None,
                'fetchTransactions': None,
                'fetchTransfer': False,
                'fetchTransfers': True,
                'fetchWithdrawal': False,
                'fetchWithdrawals': True,
                'fetchWithdrawalWhitelist': False,
                'reduceMargin': False,
                'setLeverage': True,
                'setMargin': True,
                'setMarginMode': False,
                'setPositionMode': None,
                'signIn': None,
                'transfer': True,
                'withdraw': False,
            },
            '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.aax.com',  # string website URL
                'doc': 'https://www.aax.com/apidoc/index.html',
                'fees': 'https://www.aax.com/en-US/vip/',
                'referral': 'https://www.aax.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': {
                        'currencies': 1.2,
                        'announcement/maintenance': 1.2,  # System Maintenance Notice
                        'time': 1.2,
                        'instruments': 1.2,  # Retrieve all trading pairs information
                        'market/orderbook': 1.2,  # Order Book
                        'futures/position/openInterest': 1.2,  # Open Interest
                        'market/tickers': 1.2,  # Get the Last 24h Market Summary
                        'market/candles': 1.2,  # Get Current Candlestick
                        'market/history/candles': 1.2,  # Get Current Candlestick
                        'market/trades': 1.2,  # Get the Most Recent Trades
                        'market/markPrice': 1.2,  # Get Current Mark Price
                        'futures/funding/predictedFunding/{symbol}': 1.2,  # Get Predicted Funding Rate
                        'futures/funding/prevFundingRate/{symbol}': 1.2,  # Get Last Funding Rate
                        'futures/funding/fundingRate': 1.2,
                        'market/candles/index': 1.2,  # * Deprecated
                        'market/index/candles': 1.2,
                    },
                },
                'private': {
                    'get': {
                        'user/info': 1.2,  # Retrieve user information
                        'account/balances': 1.2,  # Get Account Balances
                        'account/deposit/address': 1.2,  # undocumented
                        'account/deposits': 1.2,  # Get account deposits history
                        'account/transfer': 1.2,
                        'account/withdraws': 1.2,  # Get account withdrawals history
                        'spot/trades': 1.2,  # Retrieve trades details for a spot order
                        'spot/openOrders': 1.2,  # Retrieve spot open orders
                        'spot/orders': 1.2,  # Retrieve historical spot orders
                        'futures/position': 1.2,  # Get positions for all contracts
                        'futures/position/closed': 1.2,  # Get closed positions
                        'futures/trades': 1.2,  # Retrieve trade details for a futures order
                        'futures/openOrders': 1.2,  # Retrieve futures open orders
                        'futures/orders': 1.2,  # Retrieve historical futures orders
                        'futures/funding/fundingFee': 1.2,
                        'futures/funding/predictedFundingFee/{symbol}': 1.2,  # Get predicted funding fee
                    },
                    'post': {
                        'account/transfer': 1.2,  # Asset Transfer
                        'spot/orders': 1.2,  # Create a new spot order
                        'spot/orders/cancelAllOnTimeout': 10,  # Automatically cancel all your spot orders after a specified timeout.
                        'futures/orders': 1.2,  # Create a new futures order
                        'futures/orders/cancelAllOnTimeout': 10,  # Automatically cancel all your futures orders after a specified timeout.
                        'futures/position/sltp': 1.2,  # Set take profit and stop loss orders for an opening position
                        'futures/position/close': 1.2,  # Close position
                        'futures/position/leverage': 30,  # Update leverage for position
                        'futures/position/margin': 1.2,  # Modify Isolated Position Margin
                    },
                    'put': {
                        'spot/orders': 1.2,  # Amend spot order
                        'futures/orders': 1.2,  # Amend the quantity of an open futures order
                    },
                    'delete': {
                        'spot/orders/cancel/{orderID}': 1,  # Cancel a spot order
                        'spot/orders/cancel/all': 10,  # Batch cancel spot orders
                        'futures/orders/cancel/{orderID}': 1,  # Cancel a futures order
                        'futures/orders/cancel/all': 10,  # Batch cancel futures orders
                    },
                },
            },
            'fees': {
                'trading': {
                    'tierBased': False,
                    'percentage': True,
                    'maker': self.parse_number('0.0006'),
                    'taker': self.parse_number('0.001'),
                },
                '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'
                'accountsByType': {
                    'spot': 'SPTP',
                    'future': 'FUTP',
                    'otc': 'F2CP',
                    'saving': 'VLTP',
                },
                'accountsById': {
                    'SPTP': 'spot',
                    'FUTP': 'future',
                    'F2CP': 'otc',
                    'VLTP': 'saving',
                },
                'networks': {
                    'ETH': 'ERC20',
                    'TRX': 'TRC20',
                    'SOL': 'SPL',
                },
                'transfer': {
                    'fillResponseFromRequest': True,
                },
            },
        })

    def fetch_time(self, params={}):
        """
        fetches the current integer timestamp in milliseconds from the exchange server
        :param dict params: extra parameters specific to the aax api endpoint
        :returns int: the current integer timestamp in milliseconds from the exchange server
        """
        response = self.publicGetTime(params)
        #
        #    {
        #        "code": 1,
        #        "data": 1573542445411,  # unit: millisecond
        #        "message": "success",
        #        "ts": 1573542445411
        #    }
        #
        return self.safe_integer(response, 'data')

    def fetch_status(self, params={}):
        """
        the latest known information on the availability of the exchange API
        :param dict params: extra parameters specific to the aax api endpoint
        :returns dict: a `status structure <https://docs.ccxt.com/en/latest/manual.html#exchange-status-structure>`
        """
        response = self.publicGetAnnouncementMaintenance(params)
        #
        # note, when there is no maintenance, then data is `null`
        #
        #     {
        #         "code": 1,
        #         "data": {
        #             "startTime":"2020-06-25T02:15:00.000Z",
        #             "endTime":"2020-06-25T02:45:00.000Z"，
        #             "description":"Spot Trading :UTC Jun 25, 2020 02:15 to 02:45(HKT Jun 25 10:15 to 10:45),Futures Trading: UTC Jun 25, 2020 02:15 to 02:45(HKT Jun 25 10:15 to 10:45).We apologize for any inconvenience caused. Thank you for your patience and understanding.Should you have any enquiries, please do not hesitate our live chat support or via email at cs@aax.com.",
        #             "haltReason":1,
        #             "systemStatus":{
        #                 "spotTrading":"readOnly",
        #                 "futuresTreading":"closeOnly",
        #                 "walletOperating":"enable",
        #                 "otcTrading":"disable"
        #             },
        #         },
        #         "message":"success",
        #         "ts":1593043237000
        #     }
        #
        timestamp = self.milliseconds()
        updated = self.safe_integer(response, 'ts', timestamp)
        data = self.safe_value(response, 'data', {})
        status = None
        eta = None
        if data:
            startTime = self.parse8601(self.safe_string(data, 'startTime'))
            endTime = self.parse8601(self.safe_string(data, 'endTime'))
            if endTime is not None:
                startTimeIsOk = True if (startTime is None) else (updated < startTime)
                isOk = (updated > endTime) or startTimeIsOk
                eta = endTime
                status = 'ok' if isOk else 'maintenance'
            else:
                status = data
        else:
            eta = None
            status = 'ok'
        return {
            'status': status,
            'updated': updated,
            'eta': eta,
            'url': None,
            'info': response,
        }

    def fetch_markets(self, params={}):
        """
        retrieves data on all markets for aax
        :param dict params: extra parameters specific to the exchange api endpoint
        :returns [dict]: an array of objects representing market data
        """
        response = 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')
            settleId = self.safe_string(market, 'settleCurrency')
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            settle = self.safe_currency_code(settleId)
            status = self.safe_string(market, 'status')
            marketType = self.safe_string(market, 'type')
            inverse = None
            linear = None
            quanto = None
            spot = (marketType == 'spot')
            swap = (marketType == 'futures')
            settleType = self.safe_string_lower(market, 'settleType')
            if settleType is not None:
                inverse = (settleType == 'inverse')
                linear = (settleType == 'vanilla')
                quanto = (settleType == 'quanto')
            symbol = base + '/' + quote
            type = 'spot'
            contractSize = None
            minLeverage = None
            maxLeverage = None
            if swap:
                symbol = symbol + ':' + settle
                type = 'swap'
                contractSize = self.safe_number(market, 'multiplier')
                minLeverage = '1'
                imRate = self.safe_string(market, 'imRate')
                maxLeverage = Precise.string_div('1', imRate)
            result.append({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'settle': settle,
                'baseId': baseId,
                'quoteId': quoteId,
                'settleId': settleId,
                'type': type,
                'spot': spot,
                'margin': False,
                'swap': swap,
                'future': False,
                'option': False,
                'active': (status == 'enable'),
                'contract': swap,
                'linear': linear,
                'inverse': inverse,
                'quanto': quanto,
                'taker': self.safe_number(market, 'takerFee'),
                'maker': self.safe_number(market, 'makerFee'),
                'contractSize': contractSize,
                'expiry': None,
                'expiryDatetime': None,
                'strike': None,
                'optionType': None,
                'precision': {
                    'amount': self.safe_number(market, 'lotSize'),
                    'price': self.safe_number(market, 'tickSize'),
                },
                'limits': {
                    'leverage': {
                        'min': self.parse_number(minLeverage),
                        'max': self.parse_number(maxLeverage),
                    },
                    'amount': {
                        'min': self.safe_number(market, 'minQuantity'),
                        'max': self.safe_number(market, 'maxQuantity'),
                    },
                    'price': {
                        'min': self.safe_number(market, 'minPrice'),
                        'max': self.safe_number(market, 'maxPrice'),
                    },
                    'cost': {
                        'min': None,
                        'max': None,
                    },
                },
                'info': market,
            })
        return result

    def fetch_currencies(self, params={}):
        """
        fetches all available currencies on an exchange
        :param dict params: extra parameters specific to the aax api endpoint
        :returns dict: an associative dictionary of currencies
        """
        response = self.publicGetCurrencies(params)
        #
        #     {
        #         "code": 1,
        #         "data": [
        #             {
        #                 "chain": "BTC",
        #                 "displayName": "Bitcoin",
        #                 "withdrawFee": "0.0004",
        #                 "withdrawMin": "0.001",
        #                 "otcFee": "0",
        #                 "enableOTC": True,
        #                 "visible": True,
        #                 "enableTransfer": True,
        #                 "transferMin": "0.00001",
        #                 "depositMin": "0.0005",
        #                 "enableWithdraw": True,
        #                 "enableDeposit": True,
        #                 "addrWithMemo": False,
        #                 "withdrawPrecision": "0.00000001",
        #                 "currency": "BTC",
        #                 "network": "BTC",  # ETH, ERC20, TRX, TRC20, OMNI, LTC, XRP, XLM, ...
        #                 "minConfirm": "2"
        #             },
        #         ],
        #         "message": "success",
        #         "ts": 1624330530697
        #     }
        #
        result = {}
        data = self.safe_value(response, 'data', [])
        for i in range(0, len(data)):
            currency = data[i]
            id = self.safe_string(currency, 'currency')
            code = self.safe_currency_code(id)
            networkId = self.safe_string(currency, 'network')
            enableWithdraw = self.safe_value(currency, 'enableWithdraw')
            enableDeposit = self.safe_value(currency, 'enableDeposit')
            visible = self.safe_value(currency, 'visible')
            active = (enableWithdraw and enableDeposit and visible)
            network = {
                'info': currency,
                'id': networkId,
                'network': self.safe_currency_code(networkId),
                'limits': {
                    'withdraw': {
                        'min': self.safe_number(currency, 'withdrawMin'),
                        'max': None,
                    },
                    'deposit': {
                        'min': self.safe_number(currency, 'depositMin'),
                        'max': None,
                    },
                },
                'active': active,
                'withdraw': enableWithdraw and visible,
                'deposit': enableDeposit and visible,
                'fee': self.safe_number(currency, 'withdrawFee'),
                'precision': self.safe_number(currency, 'withdrawPrecision'),
            }
            resultItem = self.safe_value(result, code)
            fee = self.safe_string(currency, 'withdrawFee')
            precision = self.safe_string(currency, 'withdrawPrecision')
            depositMin = self.safe_string(currency, 'depositMin')
            withdrawMin = self.safe_string(currency, 'withdrawMin')
            if resultItem is not None:
                resultItem['networks'].append(network)
                previousPrecision = str(resultItem['precision'])
                previousDepositMin = str(resultItem['limits']['deposit']['min'])
                previousWithdrawMin = str(resultItem['limits']['withdraw']['min'])
                previousFee = str(resultItem['fee'])
                resultItem['precision'] = self.parse_number(Precise.string_max(previousPrecision, precision))
                resultItem['limits']['deposit']['min'] = self.parse_number(Precise.string_min(previousDepositMin, depositMin))
                resultItem['limits']['withdraw']['min'] = self.parse_number(Precise.string_min(previousWithdrawMin, withdrawMin))
                resultItem['fee'] = self.parse_number(Precise.string_min(previousFee, fee))
            else:
                name = self.safe_string(currency, 'displayName')
                deposit = (enableDeposit and visible)
                withdraw = (enableWithdraw and visible)
                result[code] = {
                    'info': {},
                    'id': id,
                    'name': name,
                    'code': code,
                    'precision': self.parse_number(precision),
                    'active': active,
                    'deposit': deposit,
                    'withdraw': withdraw,
                    'fee': self.parse_number(fee),
                    'networks': [network],
                    'limits': {
                        'amount': {
                            'min': None,
                            'max': None,
                        },
                        'deposit': {
                            'min': self.parse_number(depositMin),
                            'max': None,
                        },
                        'withdraw': {
                            'min': self.parse_number(withdrawMin),
                            '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_string(ticker, 'c')
        open = self.safe_string(ticker, 'o')
        quoteVolume = self.safe_string(ticker, 'v')
        return self.safe_ticker({
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': None,
            'high': self.safe_string(ticker, 'h'),
            'low': self.safe_string(ticker, 'l'),
            'bid': None,
            'bidVolume': None,
            'ask': None,
            'askVolume': None,
            'vwap': None,
            'open': open,
            'close': last,
            'last': last,
            'previousClose': None,
            'change': None,
            'percentage': None,
            'average': None,
            'baseVolume': None,
            'quoteVolume': quoteVolume,
            'info': ticker,
        }, market)

    def set_margin(self, symbol, amount, params={}):
        """
        Either adds or reduces margin in an isolated position in order to set the margin to a specific value
        see https://www.aax.com/apidoc/index.html#modify-isolated-position-margin
        :param str symbol: unified market symbol of the market to set margin in
        :param float amount: the amount to set the margin to
        :param dict params: parameters specific to the aax api endpoint
        :returns dict: A `margin structure <https://docs.ccxt.com/en/latest/manual.html#add-margin-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
            'margin': amount,
        }
        response = self.privatePostFuturesPositionMargin(self.extend(request, params))
        #
        #     {
        #         code: '1',
        #         data: {
        #             autoMarginCall: False,
        #             avgEntryPrice: '0.52331',
        #             bankruptPrice: '0.3185780400',
        #             base: 'ADA',
        #             code: 'FP',
        #             commission: '0.00031399',
        #             currentQty: '1',
        #             funding: '0',
        #             fundingStatus: null,
        #             id: '447888550222172160',
        #             leverage: '5.25',
        #             liquidationPrice: '0.324007',
        #             marketPrice: '0',
        #             openTime: '2022-05-20T14:30:42.759Z',
        #             posLeverage: '2.56',
        #             posMargin: '0.20473196',
        #             quote: 'USDT',
        #             realisedPnl: '0',
        #             riskLimit: '10000000',
        #             riskyPrice: '0.403728',
        #             settleType: 'VANILLA',
        #             stopLossPrice: '0',
        #             stopLossSource: '0',
        #             symbol: 'ADAUSDTFP',
        #             takeProfitPrice: '0',
        #             takeProfitSource: '0',
        #             unrealisedPnl: '-0.00151000',
        #             userID: '3311296'
        #         },
        #         message: 'success',
        #         ts: '1653057280756'
        #     }
        #
        data = self.safe_value(response, 'data', {})
        return self.parse_modify_margin(data, market)

    def parse_modify_margin(self, data, market=None):
        #
        #     {
        #         autoMarginCall: False,
        #         avgEntryPrice: '0.52331',
        #         bankruptPrice: '0.3185780400',
        #         base: 'ADA',
        #         code: 'FP',
        #         commission: '0.00031399',
        #         currentQty: '1',
        #         funding: '0',
        #         fundingStatus: null,
        #         id: '447888550222172160',
        #         leverage: '5.25',
        #         liquidationPrice: '0.324007',
        #         marketPrice: '0',
        #         openTime: '2022-05-20T14:30:42.759Z',
        #         posLeverage: '2.56',
        #         posMargin: '0.20473196',
        #         quote: 'USDT',
        #         realisedPnl: '0',
        #         riskLimit: '10000000',
        #         riskyPrice: '0.403728',
        #         settleType: 'VANILLA',
        #         stopLossPrice: '0',
        #         stopLossSource: '0',
        #         symbol: 'ADAUSDTFP',
        #         takeProfitPrice: '0',
        #         takeProfitSource: '0',
        #         unrealisedPnl: '-0.00151000',
        #         userID: '3315296'
        #     }
        #
        marketId = self.safe_string(data, 'symbol')
        quote = self.safe_string(data, 'quote')
        return {
            'info': data,
            'type': 'set',
            'amount': None,
            'total': self.safe_number(data, 'posMargin'),
            'code': self.safe_currency_code(quote),
            'symbol': self.safe_symbol(marketId, market),
            'status': None,
        }

    def fetch_tickers(self, symbols=None, params={}):
        """
        fetches price tickers for multiple markets, statistical calculations with the information calculated over the past 24 hours each market
        :param [str]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
        :param dict params: extra parameters specific to the aax api endpoint
        :returns dict: an array of `ticker structures <https://docs.ccxt.com/en/latest/manual.html#ticker-structure>`
        """
        self.load_markets()
        symbols = self.market_symbols(symbols)
        response = 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)

    def fetch_order_book(self, symbol, limit=None, params={}):
        """
        fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
        :param str symbol: unified symbol of the market to fetch the order book for
        :param int|None limit: the maximum amount of order book entries to return
        :param dict params: extra parameters specific to the aax api endpoint
        :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/en/latest/manual.html#order-book-structure>` indexed by market symbols
        """
        self.load_markets()
        market = self.market(symbol)
        symbol = 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 = 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, symbol, timestamp)

    def parse_trade(self, trade, market=None):
        #
        # public fetchTrades
        #
        #     {
        #         "i":"T1qzQeZG9g",
        #         "p":"-61348.81000000",
        #         "q":"0.045400",
        #         "s":"sell",
        #         "t":1635731102731
        #     }
        #
        # 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')
        id = self.safe_string(trade, 'i', id)
        marketId = self.safe_string(trade, 'symbol')
        market = self.safe_market(marketId, market)
        priceString = self.safe_string_2(trade, 'p', 'filledPrice')
        amountString = self.safe_string_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 = 'sell' if (priceString[0] == '-') else 'buy'
        priceString = Precise.string_abs(priceString)
        orderType = self.parse_order_type(self.safe_string(trade, 'orderType'))
        fee = None
        feeCost = self.safe_string(trade, 'commission')
        if feeCost is not None:
            feeCurrency = None
            if side == 'buy':
                feeCurrency = market['base']
            elif side == 'sell':
                feeCurrency = market['quote']
            fee = {
                'currency': feeCurrency,
                'cost': feeCost,
            }
        return self.safe_trade({
            'info': trade,
            'id': id,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': market['symbol'],
            'type': orderType,
            'side': side,
            'order': orderId,
            'takerOrMaker': takerOrMaker,
            'price': priceString,
            'amount': amountString,
            'cost': None,
            'fee': fee,
        }, market)

    def fetch_transfers(self, code=None, since=None, limit=None, params={}):
        """
        fetch a history of internal transfers made on an account
        :param str|None code: unified currency code of the currency transferred
        :param int|None since: the earliest time in ms to fetch transfers for
        :param int|None limit: the maximum number of  transfers structures to retrieve
        :param dict params: extra parameters specific to the aax api endpoint
        :returns [dict]: a list of `transfer structures <https://docs.ccxt.com/en/latest/manual.html#transfer-structure>`
        """
        self.load_markets()
        currency = None
        request = {}
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        if since is not None:
            request['startTime'] = since
        response = self.privateGetAccountTransfer(self.extend(request, params))
        #
        #      {
        #          code: '1',
        #          data: [{
        #                  quantity: '0.000010000000',
        #                  transferID: '480975741034369024',
        #                  transferTime: '2022-03-24T13:53:07.042Z',
        #                  fromPurse: 'VLTP',
        #                  toPurse: 'SPTP',
        #                  currency: 'ETH'
        #              },
        #          ],
        #          message: 'success',
        #          ts: '1648338516932'
        #      }
        #
        transfers = self.safe_value(response, 'data', [])
        return self.parse_transfers(transfers, currency, since, limit)

    def fetch_trades(self, symbol, since=None, limit=None, params={}):
        """
        get the list of most recent trades for a particular symbol
        :param str symbol: unified symbol of the market to fetch trades for
        :param int|None since: timestamp in ms of the earliest trade to fetch
        :param int|None limit: the maximum amount of trades to fetch
        :param dict params: extra parameters specific to the aax api endpoint
        :returns [dict]: a list of `trade structures <https://docs.ccxt.com/en/latest/manual.html?#public-trades>`
        """
        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 = self.publicGetMarketTrades(request)
        #
        #     {
        #         "e":"BTCUSDT@trades",
        #         "trades":[
        #             {"i":"T1qzQeZG9g","p":"-61348.81000000","q":"0.045400","s":"sell","t":1635731102731},
        #             {"i":"T1qzQeU6UK","p":"61343.10000000","q":"0.179300","s":"buy","t":1635731102133},
        #             {"i":"T1qzQe5BQm","p":"-61346.02000000","q":"0.021100","s":"sell","t":1635731099231},
        #         ]
        #     }
        #
        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
        #         1374.66736,  # 4 quote-volume
        #         1611514800,  # 5 timestamp
        #         32421.4,  # 6 base-volume
        #     ]
        #
        return [
            self.safe_integer(ohlcv, 5),  # timestamp
            self.safe_number(ohlcv, 0),  # open
            self.safe_number(ohlcv, 1),  # high
            self.safe_number(ohlcv, 2),  # low
            self.safe_number(ohlcv, 3),  # close
            self.safe_number(ohlcv, 6),  # base-volume
        ]

    def fetch_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
        """
        fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
        :param str symbol: unified symbol of the market to fetch OHLCV data for
        :param str timeframe: the length of time each candle represents
        :param int|None since: timestamp in ms of the earliest candle to fetch
        :param int|None limit: the maximum amount of candles to fetch
        :param dict params: extra parameters specific to the aax api endpoint
        :returns [[int]]: A list of candles ordered as timestamp, open, high, low, close, volume
        """
        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 = self.publicGetMarketHistoryCandles(self.extend(request, params))
        #
        #     {
        #         "data":[
        #             [0.042398,0.042684,0.042366,0.042386,1374.66736,1611514800,32421.4],
        #             ...
        #         ],
        #         "success":true,
        #         "t":1611875157
        #     }
        #
        data = self.safe_value(response, 'data', [])
        return self.parse_ohlcvs(data, market, timeframe, since, limit)

    def fetch_accounts(self, params={}):
        """
        fetch all the accounts associated with a profile
        :param dict params: extra parameters specific to the aax api endpoint
        :returns dict: a dictionary of `account structures <https://docs.ccxt.com/en/latest/manual.html#account-structure>` indexed by the account type
        """
        response = self.privateGetAccountBalances(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', {})
        return self.parse_accounts(data)

    def parse_account(self, account):
        #
        #    {
        #        "purseType":"FUTP",
        #        "currency":"USDT",
        #        "available":"0.21000000",
        #        "unvaliable":"0.00000000"
        #    }
        #
        currencyId = self.safe_string(account, 'currency')
        accountId = self.safe_string(account, 'purseType')
        accountsById = self.safe_value(self.options, 'accountsById', {})
        return {
            'info': account,
            'id': None,
            'code': self.safe_currency_code(currencyId),
            'type': self.safe_string(accountsById, accountId, accountId),
        }

    def fetch_balance(self, params={}):
        """
        query for balance and get the amount of funds available for trading or funds locked in orders
        :param dict params: extra parameters specific to the aax api endpoint
        :returns dict: a `balance structure <https://docs.ccxt.com/en/latest/manual.html?#balance-structure>`
        """
        self.load_markets()
        defaultType = self.safe_string_2(self.options, 'fetchBalance', 'defaultType', 'spot')
        type = self.safe_string(params, 'type', defaultType)
        types = self.safe_value(self.options, 'accountsByType', {})
        purseType = self.safe_string(types, type, type)
        request = {
            'purseType': purseType,
        }
        params = self.omit(params, 'type')
        response = 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', [])
        timestamp = self.safe_integer(response, 'ts')
        result = {
            'info': response,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
        }
        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_string(balance, 'available')
                account['used'] = self.safe_string(balance, 'unavailable')
                result[code] = account
        return self.safe_balance(result)

    def create_order(self, symbol, type, side, amount, price=None, params={}):
        """
        create a trade order
        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of currency you want to trade in units of base currency
        :param float|None price: the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
        :param dict params: extra parameters specific to the aax api endpoint
        :returns dict: an `order structure <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        orderType = type.upper()
        orderSide = side.upper()
        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 and spot
        }
        clientOrderId = self.safe_string_2(params, 'clOrdID', 'clientOrderId')
        if clientOrderId is not None:
            request['clOrdID'] = clientOrderId
        postOnly = self.is_post_only(orderType == 'MARKET', None, params)
        timeInForce = self.safe_string(params, 'timeInForce')
        if postOnly:
            request['execInst'] = 'Post-Only'
        if timeInForce is not None and timeInForce != 'PO':
            request['timeInForce'] = timeInForce
        stopPrice = self.safe_value_2(params, 'triggerPrice', 'stopPrice')
        params = self.omit(params, ['clOrdID', 'clientOrderId', 'postOnly', 'timeInForce', 'stopPrice', 'triggerPrice'])
        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)
        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['contract']:
            method = 'privatePostFuturesOrders'
        response = 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)

    def edit_order(self, id, symbol, type, side, amount, price=None, params={}):
        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_value_2(params, 'triggerPrice', '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['contract']:
            method = 'privatePutFuturesOrders'
        response = 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)

    def cancel_order(self, id, symbol=None, params={}):
        """
        cancels an open order
        :param str id: order id
        :param str|None symbol: unified symbol of the market the order was made in
        :param dict params: extra parameters specific to the aax api endpoint
        :returns dict: An `order structure <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        self.load_markets()
        request = {
            'orderID': id,
        }
        market = None
        if symbol is not None:
            market = self.market(symbol)
        marketType, query = self.handle_market_type_and_params('cancelOrder', market, params)
        method = self.get_supported_mapping(marketType, {
            'spot': 'privateDeleteSpotOrdersCancelOrderID',
            'swap': 'privateDeleteFuturesOrdersCancelOrderID',
            'future': 'privateDeleteFuturesOrdersCancelOrderID',
        })
        response = getattr(self, method)(self.extend(request, query))
        #
        # 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)

    def cancel_orders(self, ids, symbol=None, params={}):
        """
        cancel all open orders in a market
        :param str symbol: unified market symbol
        :param dict params: extra parameters specific to the aax api endpoint
        :returns [dict]: raw data of order ids queued for cancelation
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrders() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        method = None
        if market['spot']:
            method = 'privateDeleteSpotOrdersCancelAll'
        elif market['contract']:
            method = 'privateDeleteFuturesOrdersCancelAll'
        clientOrderIds = self.safe_value(params, 'clientOrderIds')
        # cannot cancel both by orderId and clientOrderId in the same request
        # aax throws an error saying order not found
        if clientOrderIds is not None:
            params = self.omit(params, ['clientOrderIds'])
            request['clOrdID'] = ','.join(clientOrderIds)
        elif ids is not None:
            request['orderID'] = ','.join(ids)
        #
        #  {
        #      "code": 1,
        #      "data": ["2gaB7mSf72", "2gaB79T5UA"],
        #      "message": "success",
        #      "ts": 1663021367883
        #  }
        #
        return getattr(self, method)(self.extend(request, params))

    def cancel_all_orders(self, symbol=None, params={}):
        """
        cancel all open orders in a market
        :param str symbol: unified market symbol
        :param dict params: extra parameters specific to the aax api endpoint
        :returns [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelAllOrders() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        method = None
        if market['spot']:
            method = 'privateDeleteSpotOrdersCancelAll'
        elif market['contract']:
            method = 'privateDeleteFuturesOrdersCancelAll'
        response = getattr(self, method)(self.extend(request, params))
        #
        #     {
        #         "code":1,
        #         "data":[
        #             "vBC9rXsEE",
        #             "vBCc46OI0"
        #             ],
        #         "message":"success",
        #         "ts":1572597435470
        #     }
        #
        return response

    def fetch_order(self, id, symbol=None, params={}):
        """
        fetches information on an order made by the user
        :param str|None symbol: unified symbol of the market the order was made in
        :param dict params: extra parameters specific to the aax api endpoint
        :returns dict: An `order structure <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        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 = 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

    def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        """
        fetch all unfilled currently open orders
        :param str|None symbol: unified market symbol
        :param int|None since: the earliest time in ms to fetch open orders for
        :param int|None limit: the maximum number of  open orders structures to retrieve
        :param dict params: extra parameters specific to the aax api endpoint
        :returns [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        self.load_markets()
        request = {
            # 'pageNum': '1',
            # 'pageSize': '10',
            # 'symbol': market['id'],
            # 'orderID': id,
            # 'side': 'None',  # BUY, SELL
            # 'clOrdID': clientOrderId,
        }
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        marketType, query = self.handle_market_type_and_params('fetchOpenOrders', market, params)
        method = self.get_supported_mapping(marketType, {
            'spot': 'privateGetSpotOpenOrders',
            'swap': 'privateGetFuturesOpenOrders',
            'future': 'privateGetFuturesOpenOrders',
        })
        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
        response = getattr(self, method)(self.extend(request, query))
        #
        # 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 fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
        """
        fetches information on multiple closed orders made by the user
        :param str|None symbol: unified market symbol of the market orders were made in
        :param int|None since: the earliest time in ms to fetch orders for
        :param int|None limit: the maximum number of  orde structures to retrieve
        :param dict params: extra parameters specific to the aax api endpoint
        :returns [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        request = {
            'orderStatus': '2',  # 1 new, 2 filled, 3 canceled
        }
        return self.fetch_orders(symbol, since, limit, self.extend(request, params))

    def fetch_canceled_orders(self, symbol=None, since=None, limit=None, params={}):
        """
        fetches information on multiple canceled orders made by the user
        :param str|None symbol: unified market symbol of the market orders were made in
        :param int|None since: timestamp in ms of the earliest order, default is None
        :param int|None limit: max number of orders to return, default is None
        :param dict params: extra parameters specific to the aax api endpoint
        :returns dict: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        request = {
            'orderStatus': '3',  # 1 new, 2 filled, 3 canceled
        }
        return self.fetch_orders(symbol, since, limit, self.extend(request, params))

    def fetch_orders(self, symbol=None, since=None, limit=None, params={}):
        """
        fetches information on multiple orders made by the user
        :param str|None symbol: unified market symbol of the market orders were made in
        :param int|None since: the earliest time in ms to fetch orders for
        :param int|None limit: the maximum number of  orde structures to retrieve
        :param dict params: extra parameters specific to the aax api endpoint
        :returns [dict]: a list of `order structures <https://docs.ccxt.com/en/latest/manual.html#order-structure>`
        """
        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.yyyymmdd(since),
            # 'endDate': self.yyyymmdd(self.milliseconds()),
            # 'orderType': None,  # MARKET, LIMIT, STOP, STOP-LIMIT
            # 'side': 'None',  # BUY, SELL
            # 'clOrdID': clientOrderId,
        }
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        marketType, query = self.handle_market_type_and_params('fetchOrders', market, params)
        method = self.get_supported_mapping(marketType, {
            'spot': 'privateGetSpotOrders',
            'swap': 'privateGetFuturesOrders',
            'future': '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.yyyymmdd(since)
        response = getattr(self, method)(self.extend(request, query))
        #
        # 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, str):
            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)
        price = self.safe_string(order, 'price')
        stopPrice = self.safe_number(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_string(order, 'avgPrice')
        amount = self.safe_string(order, 'orderQty')
        filled = self.safe_string(order, 'cumQty')
        remaining = self.safe_string(order, 'leavesQty')
        if (Precise.string_equals(filled, '0')) and (Precise.string_equals(remaining, '0')):
            remaining = None
        lastTradeTimestamp = self.safe_value(order, 'transactTime')
        if isinstance(lastTradeTimestamp, str):
            lastTradeTimestamp = self.parse8601(lastTradeTimestamp)
        fee = None
        feeCost = self.safe_number(order, 'commission')
        if feeCost is not None:
            feeCurrency = None
            if side == 'buy':
                feeCurrency = market['base']
            elif side == 'sell':
                feeCurrency = market['quote']
            fee = {
                'currency': feeCurrency,
                'cost': feeCost,
            }
        return self.safe_order({
            'id': id,
            'info': order,
            'clientOrderId': clientOrderId,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': lastTradeTimestamp,
            'status': status,
            'symbol': market['symbol'],
            'type': type,
            'timeInForce': timeInForce,
            'postOnly': postOnly,
            'side': side,
            'price': price,
            'stopPrice': stopPrice,
            'average': average,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'cost': None,
            'trades': None,
            'fee': fee,
        }, market)

    def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
        """
        fetch all trades made by the user
        :param str|None symbol: unified market symbol
        :param int|None since: the earliest time in ms to fetch trades for
        :param int|None limit: the maximum number of trades structures to retrieve
        :param dict params: extra parameters specific to the aax api endpoint
        :returns [dict]: a list of `trade structures <https://docs.ccxt.com/en/latest/manual.html#trade-structure>`
        """
        self.load_markets()
        request = {
            # 'pageNum': '1',
            # 'pageSize': '10',
            # 'symbol': market['id'],
            # 'orderID': id,
            # 'base': market['baseId'],
            # 'quote': market['quoteId'],
            # 'startDate': self.yyyymmdd(since),
            # 'endDate': self.yyyymmdd(self.milliseconds()),
            # 'orderType': None,  # MARKET, LIMIT, STOP, STOP-LIMIT
            # 'side': 'None',  # BUY, SELL
        }
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        marketType, query = self.handle_market_type_and_params('fetchMyTrades', market, params)
        method = self.get_supported_mapping(marketType, {
            'spot': 'privateGetSpotTrades',
            'swap': 'privateGetFuturesTrades',
            'future': 'privateGetFuturesTrades',
        })
        if limit is not None:
            request['pageSize'] = limit  # default 10
        if since is not None:
            request['startDate'] = self.yyyymmdd(since)
        response = getattr(self, method)(self.extend(request, query))
        #
        #     {
        #         "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)

    def fetch_deposit_address(self, code, params={}):
        """
        fetch the deposit address for a currency associated with self account
        :param str code: unified currency code
        :param dict params: extra parameters specific to the aax api endpoint
        :returns dict: an `address structure <https://docs.ccxt.com/en/latest/manual.html#address-structure>`
        """
        self.load_markets()
        currency = self.currency(code)
        request = {
            'currency': currency['id'],
            # 'network': None,  # 'ERC20
        }
        if 'network' in params:
            networks = self.safe_value(self.options, 'networks', {})
            network = self.safe_string_upper(params, 'network')
            params = self.omit(params, 'network')
            request['network'] = self.safe_string_upper(networks, network, network)
        response = 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 fetch_deposits(self, code=None, since=None, limit=None, params={}):
        """
        fetch all deposits made to an account
        :param str|None code: unified currency code
        :param int|None since: the earliest time in ms to fetch deposits for
        :param int|None limit: the maximum number of deposits structures to retrieve
        :param dict params: extra parameters specific to the aax api endpoint
        :returns [dict]: a list of `transaction structures <https://docs.ccxt.com/en/latest/manual.html#transaction-structure>`
        """
        self.load_markets()
        request = {
            # status Not required -  Deposit status, "1: pending,2: confirmed, 3:failed"
            # currency: Not required -  str Currency
            # startTime Not required Integer Default: 90 days from current timestamp.
            # endTime Not required Integer Default: present timestamp.
        }
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        if since is not None:
            startTime = int(since / 1000)
            request['startTime'] = startTime
            request['endTime'] = self.sum(startTime, 90 * 24 * 60 * 60)  # Only allows a 90 day window between start and end
        response = self.privateGetAccountDeposits(self.extend(request, params))
        # {   "code": 1,
        #     "data": [{
        #         "currency": "USDT",
        #         "network": "USDT",
        #         "quantity": "19.000000000000",
        #         "txHash": "75eb2e5f037b025c535664c49a0f7cc8f601dae218a5f4fe82290ff652c43f3d",
        #         "address": "1GkB7Taf7uttcguKEb2DmmyRTnihskJ9Le",
        #         "status": "2",
        #         "createdTime": "2021-01-08T19:45:01.354Z",
        #         "updatedTime": "2021-01-08T20:03:05.000Z",
        #     }]
        #     "message": "success",
        #     "ts": 1573561743499
        # }
        data = self.safe_value(response, 'data', [])
        return self.parse_transactions(data, currency, since, limit)

    def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
        """
        fetch all withdrawals made from an account
        :param str|None code: unified currency code
        :param int|None since: the earliest time in ms to fetch withdrawals for
        :param int|None limit: the maximum number of withdrawals structures to retrieve
        :param dict params: extra parameters specific to the aax api endpoint
        :returns [dict]: a list of `transaction structures <https://docs.ccxt.com/en/latest/manual.html#transaction-structure>`
        """
        self.load_markets()
        request = {
            # status Not required : "0: Under Review, 1: Manual Review, 2: On Chain, 3: Review Failed, 4: On Chain, 5: Completed, 6: Failed"
            # currency: Not required -  str Currency
            # startTime Not required Integer Default: 30 days from current timestamp.
            # endTime Not required Integer Default: present timestamp.
            # Note difference between endTime and startTime must be 90 days or less
        }
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['currency'] = currency['id']
        if since is not None:
            startTime = int(since / 1000)
            request['startTime'] = startTime
            request['endTime'] = self.sum(startTime, 90 * 24 * 60 * 60)  # Only allows a 90 day window between start and end
        response = self.privateGetAccountWithdraws(self.extend(request, params))
        # {
        #     "code":1,
        #     "data": [
        #       {
        #            "currency":"USDT",
        #            "network":"USDT",
        #            "quantity":"19.000000000000",
        #            "fee":"0.10000"
        #            "txHash":"75eb2e5f037b025c535664c49a0f7cc8f601dae218a5f4fe82290ff652c43f3d",
        #            "address":"1GkB7Taf7uttcguKEb2DmmyRTnihskJ9Le",
        #            "addressTag": "",
        #            "status":"2",
        #            "createdTime":"2021-01-08T19:45:01.354Z",
        #            "updatedTime":"2021-01-08T20:03:05.000Z",
        #       }
        #  ]
        #     "message":"success",
        #     "ts":1573561743499
        #  }
        data = self.safe_value(response, 'data', [])
        return self.parse_transactions(data, currency, since, limit)

    def parse_transaction_status_by_type(self, status, type=None):
        statuses = {
            'deposit': {
                '1': 'pending',
                '2': 'ok',
                '3': 'failed',
            },
            'withdrawal': {
                '0': 'pending',  # under review
                '1': 'pending',  # manual review
                '2': 'pending',  # on chain
                '3': 'failed',  # failed
                '4': 'pending',  # on chain
                '5': 'ok',      # completed
                '6': 'failed',  # failed
            },
        }
        return self.safe_string(self.safe_value(statuses, type, {}), status, status)

    def parse_address_by_type(self, address, tag, type=None):
        addressFrom = None
        addressTo = None
        tagFrom = None
        tagTo = None
        if type == 'deposit':
            addressFrom = address
            tagFrom = tag
        elif type == 'withdrawal':
            addressTo = address
            tagTo = tag
        return [addressFrom, tagFrom, addressTo, tagTo]

    def parse_transaction(self, transaction, currency=None):
        #
        # fetchDeposits
        #
        #    {
        #         "currency": "USDT",
        #         "network": "USDT",
        #         "quantity": "19.000000000000",
        #         "txHash": "75eb2e5f037b025c535664c49a0f7cc8f601dae218a5f4fe82290ff652c43f3d",
        #         "address": "1GkB7Taf7uttcguKEb2DmmyRTnihskJ9Le",
        #         "status": "2",
        #         "createdTime": "2021-01-08T19:45:01.354Z",
        #         "updatedTime": "2021-01-08T20:03:05.000Z",
        #     }
        #
        # fetchWithdrawals
        #
        #     {
        #         "currency":"USDT",
        #         "network":"USDT",
        #         "quantity":"19.000000000000",
        #         "fee":"0.10000"
        #         "txHash":"75eb2e5f037b025c535664c49a0f7cc8f601dae218a5f4fe82290ff652c43f3d",
        #         "address":"1GkB7Taf7uttcguKEb2DmmyRTnihskJ9Le",
        #         "addressTag": "",
        #         "status":"2",
        #         "createdTime":"2021-01-08T19:45:01.354Z",
        #         "updatedTime":"2021-01-08T20:03:05.000Z",
        #      }
        #
        fee = self.safe_string(transaction, 'fee')
        type = 'withdrawal'
        if fee is None:
            type = 'deposit'
        code = self.safe_currency_code(self.safe_string(transaction, 'currency'))
        txid = self.safe_string(transaction, 'txHash')
        address = self.safe_string(transaction, 'address')
        tag = self.safe_string(transaction, 'addressTag')  # withdrawals only
        addressFrom, tagFrom, addressTo, tagTo = self.parse_address_by_type(address, tag, type)
        amountString = self.safe_string(transaction, 'quantity')
        timestamp = self.parse8601(self.safe_string(transaction, 'createdTime'))
        updated = self.parse8601(self.safe_string(transaction, 'updatedTime'))
        status = self.parse_transaction_status_by_type(self.safe_string(transaction, 'status'), type)
        network = self.safe_string(transaction, 'network')
        return {
            'id': None,
            'info': transaction,
            'txid': txid,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'network': network,
            'addressFrom': addressFrom,
            'address': address,
            'addressTo': addressTo,
            'amount': self.parse_number(amountString),
            'type': type,
            'currency': code,
            'status': status,
            'updated': updated,
            'tagFrom': tagFrom,
            'tag': tag,
            'tagTo': tagTo,
            'comment': None,
            'fee': fee,
        }

    def fetch_funding_rate(self, symbol, params={}):
        """
        fetch the current funding rate
        :param str symbol: unified market symbol
        :param dict params: extra parameters specific to the aax api endpoint
        :returns dict: a `funding rate structure <https://docs.ccxt.com/en/latest/manual.html#funding-rate-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        if not market['swap']:
            raise BadRequest('Funding rates only exist for swap contracts')
        request = {
            'symbol': market['id'],
        }
        response = self.publicGetFuturesFundingPrevFundingRateSymbol(self.extend(request, params))
        #
        #    {
        #        "code": 1,
        #        "data": {
        #           "symbol": "BTCUSDFP",
        #           "markPrice": "11192.5",
        #           "fundingRate": "0.001",
        #           "fundingTime": "2020-08-12T08:00:00Z",
        #           "nextFundingTime": "2020-08-12T16:00:00Z"
        #        },
        #        "message": "success",
        #        "ts": 1573542445411
        #    }
        #
        data = self.safe_value(response, 'data')
        return self.parse_funding_rate(data)

    def parse_funding_rate(self, contract, market=None):
        #
        #    {
        #        "symbol": "BTCUSDFP",
        #        "markPrice": "11192.5",
        #        "fundingRate": "0.001",
        #        "fundingTime": "2020-08-12T08:00:00Z",
        #        "nextFundingTime": "2020-08-12T16:00:00Z"
        #    }
        #
        marketId = self.safe_string(contract, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        markPrice = self.safe_number(contract, 'markPrice')
        previousFundingRate = self.safe_number(contract, 'fundingRate')
        previousFundingDatetime = self.safe_string(contract, 'fundingTime')
        fundingDatetime = self.safe_string(contract, 'nextFundingTime')
        return {
            'info': contract,
            'symbol': symbol,
            'markPrice': markPrice,
            'indexPrice': None,
            'interestRate': None,
            'estimatedSettlePrice': None,
            'timestamp': None,
            'datetime': None,
            'fundingRate': None,
            'fundingTimestamp': self.parse8601(fundingDatetime),
            'fundingDatetime': fundingDatetime,
            'nextFundingRate': None,
            'nextFundingTimestamp': None,
            'nextFundingDatetime': None,
            'previousFundingRate': previousFundingRate,
            'previousFundingTimestamp': self.parse8601(previousFundingDatetime),
            'previousFundingDatetime': previousFundingDatetime,
        }

    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')
        network = self.safe_string(depositAddress, 'network')
        if network is not None:
            currencyId = currencyId.replace(network, '')
        code = self.safe_currency_code(currencyId)
        return {
            'info': depositAddress,
            'currency': code,
            'address': address,
            'tag': tag,
            'network': network,
        }

    def fetch_funding_rate_history(self, symbol=None, since=None, limit=None, params={}):
        """
        fetches historical funding rate prices
        :param str|None symbol: unified symbol of the market to fetch the funding rate history for
        :param int|None since: timestamp in ms of the earliest funding rate to fetch
        :param int|None limit: the maximum amount of `funding rate structures <https://docs.ccxt.com/en/latest/manual.html?#funding-rate-history-structure>` to fetch
        :param dict params: extra parameters specific to the aax api endpoint
        :param int|None params['until']: timestamp in ms of the latest funding rate to fetch
        :returns [dict]: a list of `funding rate structures <https://docs.ccxt.com/en/latest/manual.html?#funding-rate-history-structure>`
        """
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchFundingRateHistory() requires a symbol argument')
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        if since is not None:
            request['startTime'] = int(since / 1000)
        till = self.safe_integer_2(params, 'until', 'till')  # unified in milliseconds
        params = self.omit(params, ['till', 'until'])
        if till is not None:
            request['endTime'] = int(till / 1000)
        if limit is not None:
            request['limit'] = limit
        response = self.publicGetFuturesFundingFundingRate(self.extend(request, params))
        #
        #    {
        #        "code": 1,
        #        "data": [
        #            {
        #                "fundingRate": "0.00033992",
        #                "fundingTime": "2021-12-31T00:00:00.000Z",
        #                "symbol": "ETHUSDTFP"
        #            },
        #        ]
        #    }
        #
        data = self.safe_value(response, 'data', [])
        rates = []
        for i in range(0, len(data)):
            entry = data[i]
            marketId = self.safe_string(entry, 'symbol')
            symbol = self.safe_symbol(marketId)
            datetime = self.safe_string(entry, 'fundingTime')
            rates.append({
                'info': entry,
                'symbol': symbol,
                'fundingRate': self.safe_number(entry, 'fundingRate'),
                'timestamp': self.parse8601(datetime),
                'datetime': datetime,
            })
        sorted = self.sort_by(rates, 'timestamp')
        return self.filter_by_symbol_since_limit(sorted, market['symbol'], since, limit)

    def fetch_funding_history(self, symbol=None, since=None, limit=None, params={}):
        """
        fetch the history of funding payments paid and received on self account
        :param str symbol: unified market symbol
        :param int|None since: the earliest time in ms to fetch funding history for
        :param int|None limit: the maximum number of funding history structures to retrieve
        :param dict params: extra parameters specific to the aax api endpoint
        :returns dict: a `funding history structure <https://docs.ccxt.com/en/latest/manual.html#funding-history-structure>`
        """
        self.load_markets()
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchFundingHistory() requires a symbol argument')
        if limit is None:
            limit = 100  # Default
        elif limit > 1000:
            raise BadRequest(self.id + ' fetchFundingHistory() limit argument cannot exceed 1000')
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
            'limit': limit,
        }
        if since is not None:
            request['startTime'] = since
        response = self.privateGetFuturesFundingFundingFee(self.extend(request, params))
        #
        #    {
        #        "code": 1,
        #        "data": [
        #            {
        #                "symbol": "BTCUSDTFP",
        #                "fundingRate":"0.001",
        #                "fundingFee":"100",
        #                "currency":"USDT",
        #                "fundingTime": "2020-08-12T08:00:00Z",
        #                "markPrice": "11192.5",
        #            }
        #        ],
        #        "message": "success",
        #        "ts": 1573542445411
        #    }
        #
        data = self.safe_value(response, 'data', [])
        result = []
        for i in range(0, len(data)):
            entry = data[i]
            datetime = self.safe_string(entry, 'fundingTime')
            result.append({
                'info': entry,
                'symbol': symbol,
                'code': self.safe_currency_code(self.safe_string(entry, 'currency')),
                'timestamp': self.parse8601(datetime),
                'datetime': datetime,
                'id': None,
                'amount': self.safe_number(entry, 'fundingFee'),
            })
        return result

    def set_leverage(self, leverage, symbol=None, params={}):
        """
        set the level of leverage for a market
        :param float leverage: the rate of leverage
        :param str symbol: unified market symbol
        :param dict params: extra parameters specific to the aax api endpoint
        :returns dict: response from the exchange
        """
        self.load_markets()
        if symbol is None:
            raise ArgumentsRequired(self.id + ' setLeverage() requires a symbol argument')
        if (leverage < 1) or (leverage > 100):
            raise BadRequest(self.id + ' leverage should be between 1 and 100')
        market = self.market(symbol)
        if market['type'] != 'swap':
            raise BadSymbol(self.id + ' setLeverage() supports swap contracts only')
        request = {
            'symbol': market['id'],
            'leverage': leverage,
        }
        return self.privatePostFuturesPositionLeverage(self.extend(request, params))

    def parse_transfer(self, transfer, currency=None):
        #     {
        #          quantity: '0.000010000000',
        #          transferID: '480975741034369024',
        #          transferTime: '2022-03-24T13:53:07.042Z',
        #          fromPurse: 'VLTP',
        #          toPurse: 'SPTP',
        #          currency: 'ETH'
        #     },
        id = self.safe_string(transfer, 'transferID')
        amount = self.safe_number(transfer, 'quantity')
        timestamp = self.parse8601(self.safe_string(transfer, 'transferTime'))
        accounts = self.safe_value(self.options, 'accounts', {})
        fromId = self.safe_string(transfer, 'fromPurse')
        toId = self.safe_string(transfer, 'toPurse')
        fromAccount = self.safe_string(accounts, fromId)
        toAccount = self.safe_string(accounts, toId)
        currencyId = self.safe_string(transfer, 'currency')
        currencyCode = self.safe_currency_code(currencyId, currency)
        return {
            'info': transfer,
            'id': id,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'currency': currencyCode,
            'amount': amount,
            'fromAccount': fromAccount,
            'toAccount': toAccount,
            'status': None,
        }

    def parse_transfer_status(self, status):
        statuses = {
            '1': 'ok',
        }
        return self.safe_string(statuses, status, 'canceled')

    def transfer(self, code, amount, fromAccount, toAccount, params={}):
        """
        transfer currency internally between wallets on the same account
        :param str code: unified currency code
        :param float amount: amount to transfer
        :param str fromAccount: account to transfer from
        :param str toAccount: account to transfer to
        :param dict params: extra parameters specific to the aax api endpoint
        :returns dict: a `transfer structure <https://docs.ccxt.com/en/latest/manual.html#transfer-structure>`
        """
        self.load_markets()
        currency = self.currency(code)
        accountTypes = self.safe_value(self.options, 'accountsByType', {})
        fromId = self.safe_string(accountTypes, fromAccount, fromAccount)
        toId = self.safe_string(accountTypes, toAccount, toAccount)
        request = {
            'currency': currency['id'],
            'fromPurse': fromId,
            'toPurse': toId,
            'quantity': amount,
        }
        response = self.privatePostAccountTransfer(self.extend(request, params))
        #
        #     {
        #         "code": 1,
        #         "data": {
        #             "transferID": 888561,
        #             "transferTime": "2022-03-22T15:29:05.197Z"
        #         },
        #         "message": "success",
        #         "ts": 1647962945151
        #     }
        #
        data = self.safe_value(response, 'data', {})
        transfer = self.parse_transfer(data, currency)
        transferOptions = self.safe_value(self.options, 'transfer', {})
        fillResponseFromRequest = self.safe_value(transferOptions, 'fillResponseFromRequest', True)
        if fillResponseFromRequest:
            if transfer['fromAccount'] is None:
                transfer['fromAccount'] = fromAccount
            if transfer['toAccount'] is None:
                transfer['toAccount'] = toAccount
            if transfer['amount'] is None:
                transfer['amount'] = amount
        transfer['status'] = self.parse_transfer_status(self.safe_string(response, 'code'))
        return transfer

    def parse_position(self, position, market=None):
        #
        #    {
        #        "autoMarginCall": False,
        #        "avgEntryPrice": "3706.03",
        #        "bankruptPrice": "2963.3415880000",
        #        "base": "ETH",
        #        "code": "FP",
        #        "commission": "0.02964824",
        #        "currentQty": "2",
        #        "funding": "-0.04827355",
        #        "fundingStatus": null,
        #        "id": "385839395735639395",
        #        "leverage": "5",
        #        "liquidationPrice": "2983.07",
        #        "marketPrice": "3731.84",
        #        "openTime": "2021-12-31T18:57:25.930Z",
        #        "posLeverage": "5.00",
        #        "posMargin": "14.85376824",
        #        "quote": "USDT",
        #        "realisedPnl": "-0.07792179",
        #        "riskLimit": "10000000",
        #        "riskyPrice": "3272.25",
        #        "settleType": "VANILLA",
        #        "stopLossPrice": "0",
        #        "stopLossSource": 1,
        #        "symbol": "ETHUSDTFP",
        #        "takeProfitPrice": "0",
        #        "takeProfitSource": 1,
        #        "unrealisedPnl": "0.51620000",
        #        "userID": "3829384",
        #        "ts": 1641027194500
        #    }
        #
        contract = self.safe_string(position, 'symbol')
        market = self.safe_market(contract, market)
        size = self.safe_string(position, 'currentQty')
        side = None
        if Precise.string_gt(size, '0'):
            side = 'long'
        elif Precise.string_lt(size, '0'):
            side = 'short'
        leverage = self.safe_string(position, 'leverage')
        unrealisedPnl = self.safe_string(position, 'unrealisedPnl')
        currentQty = self.safe_string(position, 'currentQty')
        contractSize = self.safe_string(market, 'contractSize')
        initialQuote = Precise.string_mul(currentQty, contractSize)
        marketPrice = self.safe_string(position, 'marketPrice')
        timestamp = self.safe_integer(position, 'ts')
        liquidationPrice = self.safe_string(position, 'liquidationPrice')
        marketInfo = self.safe_value(market, 'info')
        multiplier = self.safe_string(marketInfo, 'multiplier')
        settleType = self.safe_string(position, 'settleType')
        avgEntryPrice = self.safe_string(position, 'avgEntryPrice')
        commission = self.safe_string(position, 'commission')
        initialMargin = None
        maintenanceMargin = None
        notional = None
        # https://support.aax.com/en/articles/5295653-what-is-margin
        if settleType == 'VANILLA':
            notional = Precise.string_mul(initialQuote, marketPrice)
            # Initial Margin(Limit order) = Number of contracts * Price * Multiplier / Leverage
            initialMargin = Precise.string_div(Precise.string_mul(Precise.string_mul(currentQty, avgEntryPrice), multiplier), leverage)
            # Maintenance Margin = (Number of contracts/ Entry Price * Multiplier / Leverage) + Commission fees
            tmp = Precise.string_div(Precise.string_mul(currentQty, multiplier), Precise.string_mul(avgEntryPrice, leverage))
            maintenanceMargin = Precise.string_add(tmp, commission)
        else:
            # inverse contracts
            notional = Precise.string_div(initialQuote, marketPrice)
            # Initial Margin(Limit Order) = Number of contracts / Entry Price / Leverage
            # ^ no brackets /<::>\
            initialMargin = Precise.string_div(currentQty, Precise.string_mul(leverage, avgEntryPrice))
            # Maintenance Margin = Number of contracts / Entry price / Leverage
            maintenanceMargin = initialMargin
        collateral = self.safe_string(position, 'posMargin')
        percentage = Precise.string_div(unrealisedPnl, initialMargin)
        marginRatio = Precise.string_div(maintenanceMargin, collateral)
        return {
            'info': position,
            'id': None,
            'symbol': self.safe_string(market, 'symbol'),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'initialMargin': self.parse_number(initialMargin),
            'initialMarginPercentage': self.parse_number(Precise.string_div(initialMargin, notional)),
            'maintenanceMargin': self.parse_number(maintenanceMargin),
            'maintenanceMarginPercentage': self.parse_number(Precise.string_div(maintenanceMargin, notional)),
            'entryPrice': self.parse_number(avgEntryPrice),
            'notional': self.parse_number(notional),
            'leverage': self.parse_number(leverage),
            'unrealizedPnl': self.parse_number(unrealisedPnl),
            'contracts': self.parse_number(size),
            'contractSize': self.parse_number(contractSize),
            'marginRatio': self.parse_number(marginRatio),
            'liquidationPrice': liquidationPrice,
            'markPrice': self.safe_number(position, 'marketPrice'),
            'collateral': self.parse_number(collateral),
            'marginMode': 'isolated',
            'side': side,
            'percentage': self.parse_number(percentage),
        }

    def fetch_position(self, symbol=None, params={}):
        """
        fetch data on a single open contract trade position
        :param str symbol: unified market symbol of the market the position is held in, default is None
        :param dict params: extra parameters specific to the aax api endpoint
        :returns dict: a `position structure <https://docs.ccxt.com/en/latest/manual.html#position-structure>`
        """
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        response = self.privateGetFuturesPosition(self.extend(request, params))
        #
        #    {
        #        "code": 1,
        #        "data": [
        #            {
        #                "autoMarginCall": False,
        #                "avgEntryPrice": "3706.03",
        #                "bankruptPrice": "2963.3415880000",
        #                "base": "ETH",
        #                "code": "FP",
        #                "commission": "0.02964824",
        #                "currentQty": "2",
        #                "funding": "-0.04827355",
        #                "fundingStatus": null,
        #                "id": "385839395735639395",
        #                "leverage": "5",
        #                "liquidationPrice": "2983.07",
        #                "marketPrice": "3731.84",
        #                "openTime": "2021-12-31T18:57:25.930Z",
        #                "posLeverage": "5.00",
        #                "posMargin": "14.85376824",
        #                "quote": "USDT",
        #                "realisedPnl": "-0.07792179",
        #                "riskLimit": "10000000",
        #                "riskyPrice": "3272.25",
        #                "settleType": "VANILLA",
        #                "stopLossPrice": "0",
        #                "stopLossSource": 1,
        #                "symbol": "ETHUSDTFP",
        #                "takeProfitPrice": "0",
        #                "takeProfitSource": 1,
        #                "unrealisedPnl": "0.51620000",
        #                "userID": "3829384"
        #            }
        #            ...
        #        ],
        #        "message": "success",
        #        "ts": 1641026778068
        #    }
        #
        positions = self.safe_value(response, 'data', [])
        timestamp = self.safe_integer(response, 'ts')
        first = self.safe_value(positions, 0)
        position = self.parse_position(first)
        return self.extend(position, {
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
        })

    def fetch_positions(self, symbols=None, params={}):
        """
        fetch all open positions
        :param [str]|None symbols: list of unified market symbols
        :param dict params: extra parameters specific to the aax api endpoint
        :returns [dict]: a list of `position structure <https://docs.ccxt.com/en/latest/manual.html#position-structure>`
        """
        self.load_markets()
        request = {}
        if symbols is not None:
            symbol = None
            if isinstance(symbols, list):
                symbolsLength = len(symbols)
                if symbolsLength > 1:
                    raise BadRequest(self.id + ' fetchPositions() symbols argument cannot contain more than 1 symbol')
                symbol = symbols[0]
            else:
                symbol = symbols
            symbols = self.market_symbols(symbols)
            market = self.market(symbol)
            request['symbol'] = market['id']
        response = self.privateGetFuturesPosition(self.extend(request, params))
        #
        #    {
        #        "code": 1,
        #        "data": [
        #            {
        #                "autoMarginCall": False,
        #                "avgEntryPrice": "3706.03",
        #                "bankruptPrice": "2963.3415880000",
        #                "base": "ETH",
        #                "code": "FP",
        #                "commission": "0.02964824",
        #                "currentQty": "2",
        #                "funding": "-0.04827355",
        #                "fundingStatus": null,
        #                "id": "385839395735639395",
        #                "leverage": "5",
        #                "liquidationPrice": "2983.07",
        #                "marketPrice": "3731.84",
        #                "openTime": "2021-12-31T18:57:25.930Z",
        #                "posLeverage": "5.00",
        #                "posMargin": "14.85376824",
        #                "quote": "USDT",
        #                "realisedPnl": "-0.07792179",
        #                "riskLimit": "10000000",
        #                "riskyPrice": "3272.25",
        #                "settleType": "VANILLA",
        #                "stopLossPrice": "0",
        #                "stopLossSource": 1,
        #                "symbol": "ETHUSDTFP",
        #                "takeProfitPrice": "0",
        #                "takeProfitSource": 1,
        #                "unrealisedPnl": "0.51620000",
        #                "userID": "3829384"
        #            }
        #            ...
        #        ],
        #        "message": "success",
        #        "ts": 1641026778068
        #    }
        #
        result = []
        positions = self.safe_value(response, 'data', [])
        timestamp = self.safe_integer(response, 'ts')
        for i in range(0, len(positions)):
            position = self.parse_position(positions[i])
            result.append(self.extend(position, {
                'timestamp': timestamp,
                'datetime': self.iso8601(timestamp),
            }))
        return self.filter_by_array(result, 'symbol', symbols, False)

    def fetch_open_interest(self, symbol, params={}):
        """
        Retrieves the open interest of a currency
        see https://www.aax.com/apidoc/index.html#open-interest
        :param str symbol: Unified CCXT market symbol
        :param dict params: exchange specific parameters
        :returns dict} an open interest structure{@link https://docs.ccxt.com/en/latest/manual.html#interest-history-structure:
        """
        self.load_markets()
        market = self.market(symbol)
        if not market['contract']:
            raise BadRequest(self.id + ' fetchOpenInterest() supports contract markets only')
        request = {
            'symbol': market['id'],
        }
        response = self.publicGetFuturesPositionOpenInterest(self.extend(request, params))
        #
        #     {
        #         "code": 1,
        #         "data": {
        #             "openInterest": "37137299.49007",
        #             "openInterestUSD": "721016725.9898761994667",
        #             "symbol": "BTCUSDTFP"
        #         },
        #         "message": "success",
        #         "ts": 1664486817471
        #     }
        #
        data = self.safe_value(response, 'data', {})
        timestamp = self.safe_integer(response, 'ts')
        openInterest = self.parse_open_interest(data, market)
        return self.extend(openInterest, {
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
        })

    def parse_open_interest(self, interest, market=None):
        #
        #     {
        #         "openInterest": "37137299.49007",
        #         "openInterestUSD": "721016725.9898761994667",
        #         "symbol": "BTCUSDTFP"
        #     }
        #
        id = self.safe_string(interest, 'symbol')
        market = self.safe_market(id, market)
        amount = self.safe_number(interest, 'openInterest')
        value = self.safe_number(interest, 'openInterestUSD')
        return {
            'symbol': self.safe_symbol(id),
            'openInterestAmount': amount,
            'baseVolume': amount,  # deprecated
            'openInterestValue': value,
            'quoteVolume': value,  # deprecated
            'timestamp': None,
            'datetime': None,
            'info': interest,
        }

    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_hostname(self.urls['api'][api]) + 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)
