# -*- 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 ArgumentsRequired
from ccxt.base.errors import BadRequest
from ccxt.base.errors import BadSymbol
from ccxt.base.errors import InsufficientFunds
from ccxt.base.errors import InvalidAddress
from ccxt.base.errors import InvalidOrder


class xena(Exchange):

    def describe(self):
        return self.deep_extend(super(xena, self).describe(), {
            'id': 'xena',
            'name': 'Xena Exchange',
            'countries': ['VC', 'UK'],
            'rateLimit': 100,
            'certified': True,
            'has': {
                'CORS': False,
                'cancelAllOrders': True,
                'cancelOrder': True,
                'createDepositAddress': True,
                'createOrder': True,
                'editOrder': True,
                'fetchBalance': True,
                'fetchClosedOrders': True,
                'fetchCurrencies': True,
                'fetchDepositAddress': True,
                'fetchDeposits': True,
                'fetchLedger': True,
                'fetchMarkets': True,
                'fetchMyTrades': True,
                'fetchOHLCV': True,
                'fetchOpenOrders': True,
                'fetchOrderBook': True,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchTime': True,
                'fetchTrades': True,
                'fetchWithdrawals': True,
                'withdraw': True,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/51840849/87489843-bb469280-c64c-11ea-91aa-69c6326506af.jpg',
                'api': {
                    'public': 'https://trading.xena.exchange/api',
                    'private': 'https://api.xena.exchange',
                },
                'www': 'https://xena.exchange',
                'doc': 'https://support.xena.exchange/support/solutions/44000808700',
                'fees': 'https://trading.xena.exchange/en/platform-specification/fee-schedule',
            },
            'timeframes': {
                '1m': '1m',
                '5m': '5m',
                '15m': '15m',
                '30m': '30m',
                '1h': '1h',
                '4h': '4h',
                '12h': '12h',
                '1d': '1d',
                '1w': '1w',
            },
            'api': {
                'public': {
                    'get': [
                        'common/currencies',
                        'common/instruments',
                        'common/features',
                        'common/commissions',
                        'common/news',
                        'market-data/candles/{marketId}/{timeframe}',
                        'market-data/market-watch',
                        'market-data/dom/{symbol}',
                        'market-data/candles/{symbol}/{timeframe}',
                        'market-data/trades/{symbol}',
                        'market-data/server-time',
                        'market-data/v2/candles/{symbol}/{timeframe}',
                        'market-data/v2/trades/{symbol}',
                        'market-data/v2/dom/{symbol}/',
                        'market-data/v2/server-time',
                    ],
                },
                'private': {
                    'get': [
                        'trading/accounts/{accountId}/order',
                        'trading/accounts/{accountId}/active-orders',
                        'trading/accounts/{accountId}/last-order-statuses',
                        'trading/accounts/{accountId}/positions',
                        'trading/accounts/{accountId}/positions-history',
                        'trading/accounts/{accountId}/margin-requirements',
                        'trading/accounts',
                        'trading/accounts/{accountId}/balance',
                        'trading/accounts/{accountId}/trade-history',
                        # 'trading/accounts/{accountId}/trade-history?symbol=BTC/USDT&client_order_id=EMBB8Veke&trade_id=220143254',
                        'transfers/accounts',
                        'transfers/accounts/{accountId}',
                        'transfers/accounts/{accountId}/deposit-address/{currency}',
                        'transfers/accounts/{accountId}/deposits',
                        'transfers/accounts/{accountId}/trusted-addresses',
                        'transfers/accounts/{accountId}/withdrawals',
                        'transfers/accounts/{accountId}/balance-history',
                        # 'transfers/accounts/{accountId}/balance-history?currency={currency}&from={time}&to={time}&kind={kind}&kind={kind}',
                        # 'transfers/accounts/{accountId}/balance-history?page={page}&limit={limit}',
                        # 'transfers/accounts/{accountId}/balance-history?txid=3e1db982c4eed2d6355e276c5bae01a52a27c9cef61574b0e8c67ee05fc26ccf',
                    ],
                    'post': [
                        'trading/order/new',
                        'trading/order/heartbeat',
                        'trading/order/cancel',
                        'trading/order/mass-cancel',
                        'trading/order/replace',
                        'trading/position/maintenance',
                        'transfers/accounts/{accountId}/withdrawals',
                        'transfers/accounts/{accountId}/deposit-address/{currency}',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'maker': 0.0005,
                    'taker': 0.001,
                    'tierBased': True,
                    'percentage': True,
                },
                'funding': {
                    'tierBased': False,
                    'percentage': False,
                    'withdraw': {},
                    'deposit': {},
                },
            },
            'exceptions': {
                'exact': {
                    'Validation failed': BadRequest,
                    'Unknown derivative symbol': BadSymbol,  # {"error":"Unknown derivative symbol"}
                    'Unknown account': BadRequest,  # {"error":"Unknown account"}
                    'Wrong TransactTime': BadRequest,  # {"error":"Wrong TransactTime"}
                    'ClOrdId is empty': BadRequest,  # {"error":"ClOrdId is empty"}
                },
                'broad': {
                    'Invalid aggregation ratio or depth': BadRequest,
                    'address': InvalidAddress,
                    'Money not enough': InsufficientFunds,
                    'parse error': BadRequest,
                    'Not enough': InsufficientFunds,  # {"error":"Not enough free margin"}
                },
            },
            'options': {
                'defaultType': 'margin',  # 'margin',
                'accountId': None,  # '1012838157',
            },
        })

    def fetch_time(self, params={}):
        response = self.publicGetMarketDataV2ServerTime(params)
        #
        #     {
        #         "msgType":"0",
        #         "transactTime":1594774454112817637
        #     }
        #
        transactTime = self.safe_integer(response, 'transactTime')
        return int(transactTime / 1000000)

    def fetch_markets(self, params={}):
        response = self.publicGetCommonInstruments(params)
        #
        #     [
        #         {
        #             "id":"ETHUSD_3M_250920",
        #             "type":"Margin",
        #             "marginType":"XenaFuture",
        #             "symbol":"ETHUSD_3M_250920",
        #             "baseCurrency":"ETH",
        #             "quoteCurrency":"USD",
        #             "settlCurrency":"BTC",
        #             "tickSize":2,
        #             "minOrderQuantity":"1",
        #             "orderQtyStep":"1",
        #             "limitOrderMaxDistance":"10",
        #             "priceInputMask":"0000.00",
        #             "enabled":true,
        #             "liquidationMaxDistance":"0.01",
        #             "contractValue":"1",
        #             "contractCurrency":"BTC",
        #             "lotSize":"1",
        #             "tickValue":"0.00000001",  # linear contracts only
        #             "maxOrderQty":"175000",
        #             "maxPosVolume":"1750000",
        #             "mark":".ETHUSD_3M_250920",
        #             "underlying":".ETHUSD_TWAP",
        #             "openInterest":".ETHUSD_3M_250920_OpenInterest",
        #             "floatingPL":"BidAsk",  # perpetual contracts only
        #             "addUvmToFreeMargin":"ProfitAndLoss",
        #             "margin":{
        #                 "netting":"PositionsAndOrders",
        #                 "rates":[
        #                     {"maxVolume":"175000","initialRate":"0.05","maintenanceRate":"0.0125"},
        #                     {"maxVolume":"350000","initialRate":"0.1","maintenanceRate":"0.025"},
        #                     {"maxVolume":"500000","initialRate":"0.2","maintenanceRate":"0.05"},
        #                     {"maxVolume":"750000","initialRate":"0.3","maintenanceRate":"0.075"},
        #                     {"maxVolume":"1050000","initialRate":"0.4","maintenanceRate":"0.1"},
        #                     {"maxVolume":"1400000","initialRate":"0.5","maintenanceRate":"0.125"},
        #                     {"maxVolume":"1750000","initialRate":"1","maintenanceRate":"0.25"}
        #                 ],
        #                 "rateMultipliers":{
        #                     "LimitBuy":"1",
        #                     "LimitSell":"1",
        #                     "Long":"1",
        #                     "MarketBuy":"1",
        #                     "MarketSell":"1",
        #                     "Short":"1",
        #                     "StopBuy":"0",
        #                     "StopSell":"0"
        #                 }
        #             },
        #             "clearing":{"enabled":true,"index":".ETHUSD_3M_250920"},
        #             "premium":{"enabled":true,"index":".XBTUSD_Premium_IR_Corrected"},  # perpetual contracts only
        #             "riskAdjustment":{"enabled":true,"index":".RiskAdjustment_IR"},
        #             "expiration":{"enabled":true,"index":".ETHUSD_TWAP"},  # futures only
        #             "pricePrecision":3,
        #             "priceRange":{
        #                 "enabled":true,
        #                 "distance":"0.03",
        #                 "movingBoundary":"0",
        #                 "lowIndex":".ETHUSD_3M_250920_LOWRANGE",
        #                 "highIndex":".ETHUSD_3M_250920_HIGHRANGE"
        #             },
        #             "priceLimits":{
        #                 "enabled":true,
        #                 "distance":"0.5",
        #                 "movingBoundary":"0",
        #                 "lowIndex":".ETHUSD_3M_250920_LOWLIMIT",
        #                 "highIndex":".ETHUSD_3M_250920_HIGHLIMIT"
        #             },
        #             "inverse":true,  # inverse contracts only
        #             "serie":"ETHUSD",  # futures only
        #             "tradingStartDate":"2020-03-27 07:00:00",
        #             "expiryDate":"2020-09-25 08:00:00"  # futures only
        #         },
        #         {
        #             "type":"Index",
        #             "symbol":".ETHUSD_Premium_IR_Corrected",
        #             "tickSize":6,
        #             "enabled":true,
        #             "basis":365
        #         },
        #     ]
        #
        result = []
        for i in range(0, len(response)):
            market = response[i]
            type = self.safe_string_lower(market, 'type')
            id = self.safe_string(market, 'symbol')
            numericId = self.safe_string(market, 'id')
            marginType = self.safe_string(market, 'marginType')
            baseId = self.safe_string(market, 'baseCurrency')
            quoteId = self.safe_string(market, 'quoteCurrency')
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            symbol = id
            if type == 'margin':
                if marginType == 'XenaFuture':
                    type = 'future'
                elif marginType == 'XenaListedPerpetual':
                    type = 'swap'
                    symbol = base + '/' + quote
            future = (type == 'future')
            swap = (type == 'swap')
            pricePrecision = self.safe_integer_2(market, 'tickSize', 'pricePrecision')
            precision = {
                'price': pricePrecision,
                'amount': 0,
            }
            maxCost = self.safe_float(market, 'maxOrderQty')
            minCost = self.safe_float(market, 'minOrderQuantity')
            limits = {
                'amount': {
                    'min': None,
                    'max': None,
                },
                'price': {
                    'min': None,
                    'max': None,
                },
                'cost': {
                    'min': minCost,
                    'max': maxCost,
                },
            }
            active = self.safe_value(market, 'enabled', False)
            inverse = self.safe_value(market, 'inverse', False)
            result.append({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'baseId': baseId,
                'quoteId': quoteId,
                'numericId': numericId,
                'active': active,
                'type': type,
                'spot': False,
                'future': future,
                'swap': swap,
                'inverse': inverse,
                'precision': precision,
                'limits': limits,
                'info': market,
            })
        return result

    def fetch_currencies(self, params={}):
        response = self.publicGetCommonCurrencies(params)
        #
        #     {
        #         "BAB": {
        #             "name":"BAB",
        #             "title":"Bitcoin ABC",
        #             "blockchain":{
        #                 "name":"BAB",
        #                 "title":"Bitcoin ABC",
        #                 "deposit":{"confirmations":6},
        #                 "withdraw":{"confirmations":1},
        #                 "addressReuseAllowed":false,
        #                 "view":{
        #                     "uriTemplate":"bitcoinabc:%s?message=Xena Exchange",
        #                     "recommendedFee":"0.00001",
        #                     "transactionUrl":"https://blockchair.com/bitcoin-cash/transaction/${txId}",
        #                     "walletUrl":"https://blockchair.com/bitcoin-cash/address/${walletId}"
        #                 }
        #             },
        #             "precision":5,
        #             "withdraw":{"minAmount":"0.01","commission":"0.001"},
        #             "view":{
        #                 "color":"#DC7C08",
        #                 "site":"https://www.bitcoinabc.org"
        #             },
        #             "enabled":true
        #         },
        #     }
        ids = list(response.keys())
        result = {}
        for i in range(0, len(ids)):
            id = ids[i]
            currency = response[id]
            code = self.safe_currency_code(id)
            name = self.safe_string(currency, 'title')
            precision = self.safe_integer(currency, 'precision')
            enabled = self.safe_value(currency, 'enabled')
            active = (enabled is True)
            withdraw = self.safe_value(currency, 'withdraw', {})
            result[code] = {
                'id': id,
                'code': code,
                'info': currency,
                'name': name,
                'active': active,
                'fee': self.safe_float(withdraw, 'commission'),
                'precision': precision,
                'limits': {
                    'amount': {
                        'min': None,
                        'max': None,
                    },
                    'price': {
                        'min': None,
                        'max': None,
                    },
                    'cost': {
                        'min': None,
                        'max': None,
                    },
                    'withdraw': {
                        'min': self.safe_float(withdraw, 'minAmount'),
                        'max': None,
                    },
                },
            }
        return result

    def parse_ticker(self, ticker, market=None):
        #
        # fetchTicker, fetchTickers
        #
        #     {
        #         "symbol":".XBTUSD_3M_250920_MID",
        #         "firstPx":"9337.49",
        #         "lastPx":"9355.81",
        #         "highPx":"9579.42",
        #         "lowPx":"9157.63",
        #         "buyVolume":"0",
        #         "sellVolume":"0",
        #         "bid":"0",
        #         "ask":"0"
        #     }
        #
        timestamp = self.milliseconds()
        marketId = self.safe_string(ticker, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        last = self.safe_float(ticker, 'lastPx')
        open = self.safe_float(ticker, 'firstPx')
        percentage = None
        change = None
        average = None
        if (last is not None) and (open is not None):
            change = last - open
            average = self.sum(last, open) / 2
            if open > 0:
                percentage = change / open * 100
        buyVolume = self.safe_float(ticker, 'buyVolume')
        sellVolume = self.safe_float(ticker, 'sellVolume')
        baseVolume = self.sum(buyVolume, sellVolume)
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_float(ticker, 'highPx'),
            'low': self.safe_float(ticker, 'lowPx'),
            'bid': self.safe_float(ticker, 'bid'),
            'bidVolume': None,
            'ask': self.safe_float(ticker, 'ask'),
            'askVolume': None,
            'vwap': None,
            'open': open,
            'close': last,
            'last': last,
            'previousClose': None,
            'change': change,
            'percentage': percentage,
            'average': average,
            'baseVolume': baseVolume,
            'quoteVolume': None,
            'info': ticker,
        }

    def fetch_ticker(self, symbol, params={}):
        self.load_markets()
        tickers = self.fetch_tickers(None, params)
        if symbol in tickers:
            return tickers[symbol]
        raise BadSymbol(self.id + ' fetchTicker could not find a ticker with symbol ' + symbol)

    def fetch_tickers(self, symbols=None, params={}):
        self.load_markets()
        tickers = self.publicGetMarketDataMarketWatch(params)
        #
        #     [
        #         {
        #             "symbol":".XBTUSD_3M_250920_MID",
        #             "firstPx":"9337.49",
        #             "lastPx":"9355.81",
        #             "highPx":"9579.42",
        #             "lowPx":"9157.63",
        #             "buyVolume":"0",
        #             "sellVolume":"0",
        #             "bid":"0",
        #             "ask":"0"
        #         }
        #     ]
        #
        result = {}
        for i in range(0, len(tickers)):
            ticker = self.parse_ticker(tickers[i])
            symbol = ticker['symbol']
            result[symbol] = ticker
        return self.filter_by_array(result, 'symbol', symbols)

    def fetch_order_book(self, symbol, limit=None, params={}):
        self.load_markets()
        request = {
            'symbol': self.market_id(symbol),
        }
        if limit is not None:
            request['depth'] = limit
        response = self.publicGetMarketDataV2DomSymbol(self.extend(request, params))
        #
        #     {
        #         "msgType":"W",
        #         "mdStreamId":"DOM:XBTUSD:aggregated",
        #         "lastUpdateTime":1594772683037691997,
        #         "mdBookType":"2",
        #         "symbol":"XBTUSD",
        #         "lowRangePx":"9132.24",
        #         "highRangePx":"9410.36",
        #         "lowLimitPx":"9132.24",
        #         "highLimitPx":"9410.36",
        #         "clearingPx":"9253.4",
        #         "bestBid":"9269.8",
        #         "bestAsk":"9275.9",
        #         "mdEntry":[
        #             {"mdEntryType":"1","mdEntryPx":"9275.9","mdEntrySize":"3000","numberOfOrders":1},
        #             {"mdEntryType":"1","mdEntryPx":"9277.7","mdEntrySize":"50000","numberOfOrders":1},
        #             {"mdEntryType":"1","mdEntryPx":"9277.8","mdEntrySize":"2000","numberOfOrders":1},
        #             {"mdEntryType":"0","mdEntryPx":"9269.8","mdEntrySize":"2000","numberOfOrders":1},
        #             {"mdEntryType":"0","mdEntryPx":"9267.9","mdEntrySize":"3000","numberOfOrders":1},
        #             {"mdEntryType":"0","mdEntryPx":"9267.8","mdEntrySize":"50000","numberOfOrders":1},
        #         ]
        #     }
        #
        mdEntry = self.safe_value(response, 'mdEntry', [])
        mdEntriesByType = self.group_by(mdEntry, 'mdEntryType')
        lastUpdateTime = self.safe_integer(response, 'lastUpdateTime')
        timestamp = int(lastUpdateTime / 1000000)
        return self.parse_order_book(mdEntriesByType, timestamp, '0', '1', 'mdEntryPx', 'mdEntrySize')

    def fetch_accounts(self, params={}):
        response = self.privateGetTradingAccounts(params)
        #
        #     {
        #         "accounts": [
        #             {"id":8273231, "kind": "Spot"},
        #             {"id":10012833469, "kind": "Margin", "currency": "BTC"}
        #         ]
        #     }
        #
        accounts = self.safe_value(response, 'accounts')
        result = []
        for i in range(0, len(accounts)):
            account = accounts[i]
            accountId = self.safe_string(account, 'id')
            currencyId = self.safe_string(account, 'currency')
            code = self.safe_currency_code(currencyId)
            type = self.safe_string_lower(account, 'kind')
            result.append({
                'id': accountId,
                'type': type,
                'currency': code,
                'info': account,
            })
        return result

    def find_account_by_type(self, type):
        self.load_markets()
        self.load_accounts()
        accountsByType = self.group_by(self.accounts, 'type')
        accounts = self.safe_value(accountsByType, type)
        if accounts is None:
            raise ExchangeError(self.id + " findAccountByType() could not find an accountId with type '" + type + "', specify the 'accountId' parameter instead")  # eslint-disable-line quotes
        numAccounts = len(accounts)
        if numAccounts > 1:
            raise ExchangeError(self.id + " findAccountByType() found more than one accountId with type '" + type + "', specify the 'accountId' parameter instead")  # eslint-disable-line quotes
        return accounts[0]

    def get_account_id(self, params):
        self.load_markets()
        self.load_accounts()
        defaultAccountId = self.safe_string(self.options, 'accountId')
        accountId = self.safe_string(params, 'accountId', defaultAccountId)
        if accountId is not None:
            return accountId
        defaultType = self.safe_string(self.options, 'defaultType', 'margin')
        type = self.safe_string(params, 'type', defaultType)
        params = self.omit(params, 'type')
        if type is None:
            raise ArgumentsRequired(self.id + " requires an 'accountId' parameter or a 'type' parameter('spot' or 'margin')")
        account = self.find_account_by_type(type)
        return account['id']

    def fetch_balance(self, params={}):
        self.load_markets()
        self.load_accounts()
        accountId = self.get_account_id(params)
        request = {
            'accountId': accountId,
        }
        response = self.privateGetTradingAccountsAccountIdBalance(self.extend(request, params))
        #
        #     {
        #         "balances": [
        #             {"available":"0","onHold":"0","settled":"0","equity":"0","currency":"BAB","lastUpdated":1564811790485125345},
        #             {"available":"0","onHold":"0","settled":"0","equity":"0","currency":"BSV","lastUpdated":1564811790485125345},
        #             {"available":"0","onHold":"0","settled":"0","equity":"0","currency":"BTC","lastUpdated":1564811790485125345},
        #         ]
        #     }
        #
        result = {'info': response}
        balances = self.safe_value(response, 'balances', [])
        for i in range(0, len(balances)):
            balance = balances[i]
            currencyId = self.safe_string(balance, 'currency')
            code = self.safe_currency_code(currencyId)
            account = self.account()
            account['free'] = self.safe_float(balance, 'available')
            account['used'] = self.safe_float(balance, 'onHold')
            result[code] = account
        return self.parse_balance(result)

    def parse_trade(self, trade, market=None):
        #
        #     {
        #         "mdUpdateAction":"0",
        #         "mdEntryType":"2",
        #         "mdEntryPx":"9225.16",
        #         "mdEntrySize":"10000",
        #         "transactTime":1594728504524977655,
        #         "tradeId":"6ac51bb7-7505-4f35-85ef-61eb738cb4d9",
        #         "aggressorSide":"1"
        #     }
        #
        # fetchMyTrades
        #
        #     {
        #         "msgType":"8",
        #         "account":1012838158,
        #         "clOrdId":"xXWKLQVl3",
        #         "orderId":"89eee8bd-98ae-4d06-97dc-ee2d12997fe7",
        #         "symbol":"ETHUSD",
        #         "transactTime":1595143349089739000,
        #         "execId":"c4bd0ee2330930924e0f6fdde4630e56751692a4",
        #         "tradeId":"30a394b2-6d53-4bc4-b276-d8e19f470ba1",
        #         "side":"2",
        #         "lastQty":"1",
        #         "lastPx":"234.58",
        #         "avgPx":"234.58",
        #         "calculatedCcyLastQty":"0",
        #         "netMoney":"0",
        #         "lastLiquidityInd":"2",
        #         "commission":"0.00000011",
        #         "commRate":"0.00045",
        #         "commCurrency":"BTC",
        #         "positionId":132162662,
        #         "positionEffect":"C"
        #     }
        #
        id = self.safe_string(trade, 'tradeId')
        timestamp = self.safe_integer(trade, 'transactTime')
        if timestamp is not None:
            timestamp = int(timestamp / 1000000)
        side = self.safe_string_lower_2(trade, 'side', 'aggressorSide')
        if side == '1':
            side = 'buy'
        elif side == '2':
            side = 'sell'
        orderId = self.safe_string(trade, 'orderId')
        marketId = self.safe_string(trade, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        price = self.safe_float_2(trade, 'lastPx', 'mdEntryPx')
        amount = self.safe_float_2(trade, 'lastQty', 'mdEntrySize')
        cost = None
        if price is not None:
            if amount is not None:
                cost = price * amount
        fee = None
        feeCost = self.safe_float(trade, 'commission')
        if feeCost is not None:
            feeCurrencyId = self.safe_string(trade, 'commCurrency')
            feeCurrencyCode = self.safe_currency_code(feeCurrencyId)
            feeRate = self.safe_float(trade, 'commRate')
            fee = {
                'cost': feeCost,
                'rate': feeRate,
                'currency': feeCurrencyCode,
            }
        return {
            'id': id,
            'info': trade,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'type': None,
            'order': orderId,
            'side': side,
            'takerOrMaker': None,
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': fee,
        }

    def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        self.load_accounts()
        accountId = self.get_account_id(params)
        request = {
            'accountId': accountId,
            # 'page': 1,
            # 'limit': integer,
            # 'from': time,
            # 'to': time,
            # 'symbol': currency['id'],
            # 'trade_id': id,
            # 'client_order_id': id,
        }
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        if since is not None:
            request['from'] = since * 1000000
        if limit is not None:
            request['limit'] = limit
        response = self.privateGetTradingAccountsAccountIdTradeHistory(self.extend(request, params))
        #
        #     [
        #         {
        #             "msgType":"8",
        #             "account":1012838158,
        #             "clOrdId":"xXWKLQVl3",
        #             "orderId":"89eee8bd-98ae-4d06-97dc-ee2d12997fe7",
        #             "symbol":"ETHUSD",
        #             "transactTime":1595143349089739000,
        #             "execId":"c4bd0ee2330930924e0f6fdde4630e56751692a4",
        #             "tradeId":"30a394b2-6d53-4bc4-b276-d8e19f470ba1",
        #             "side":"2",
        #             "lastQty":"1",
        #             "lastPx":"234.58",
        #             "avgPx":"234.58",
        #             "calculatedCcyLastQty":"0",
        #             "netMoney":"0",
        #             "lastLiquidityInd":"2",
        #             "commission":"0.00000011",
        #             "commRate":"0.00045",
        #             "commCurrency":"BTC",
        #             "positionId":132162662,
        #             "positionEffect":"C"
        #         },
        #         {
        #             "msgType":"8",
        #             "account":1012838158,
        #             "clOrdId":"3ce8c305-9936-4e97-9206-71ae3ff40305",
        #             "orderId":"a93c686d-990e-44d9-9cbe-61107744b990",
        #             "symbol":"ETHUSD",
        #             "transactTime":1595143315369226000,
        #             "execId":"1c745881722ad966a4ce71600cd058d59da0d1c3",
        #             "tradeId":"77f75bd8-27c4-4b1a-a5e8-0d59239ce216",
        #             "side":"1",
        #             "lastQty":"1",
        #             "lastPx":"234.72",
        #             "avgPx":"234.72",
        #             "calculatedCcyLastQty":"0",
        #             "netMoney":"0",
        #             "lastLiquidityInd":"2",
        #             "commission":"0.00000011",
        #             "commRate":"0.00045",
        #             "commCurrency":"BTC",
        #             "positionId":132162662,
        #             "positionEffect":"O"
        #         }
        #     ]
        #
        return self.parse_trades(response, market, since, limit)

    def parse_ohlcv(self, ohlcv, market=None):
        #
        #     {
        #         "transactTime":1594784700000000000,
        #         "firstPx":"9246.3",
        #         "lastPx":"9232.8",
        #         "highPx":"9246.3",
        #         "lowPx":"9232.8",
        #         "buyVolume":"0",
        #         "sellVolume":"0"
        #     }
        #
        transactTime = self.safe_integer(ohlcv, 'transactTime')
        timestamp = int(transactTime / 1000000)
        buyVolume = self.safe_float(ohlcv, 'buyVolume')
        sellVolume = self.safe_float(ohlcv, 'sellVolume')
        volume = self.sum(buyVolume, sellVolume)
        return [
            timestamp,
            self.safe_float(ohlcv, 'firstPx'),
            self.safe_float(ohlcv, 'highPx'),
            self.safe_float(ohlcv, 'lowPx'),
            self.safe_float(ohlcv, 'lastPx'),
            volume,
        ]

    def fetch_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
            'timeframe': self.timeframes[timeframe],
        }
        durationInSeconds = self.parse_timeframe(timeframe)
        duration = durationInSeconds * 1000
        if since is not None:
            request['from'] = since * 1000000
            if limit is not None:
                request['to'] = self.sum(since, limit * duration) * 1000000
        else:
            now = self.milliseconds()
            # max limit is 1000
            if limit is not None:
                request['from'] = (now - limit * duration) * 1000000
        response = self.publicGetMarketDataV2CandlesSymbolTimeframe(self.extend(request, params))
        #
        #     {
        #         "mdEntry":[
        #             {"transactTime":1594784700000000000,"firstPx":"9246.3","lastPx":"9232.8","highPx":"9246.3","lowPx":"9232.8","buyVolume":"0","sellVolume":"0"},
        #             {"transactTime":1594785600000000000,"firstPx":"9231.8","lastPx":"9227.3","highPx":"9232.8","lowPx":"9227.3","buyVolume":"0","sellVolume":"0"},
        #             {"transactTime":1594786500000000000,"firstPx":"9226.3","lastPx":"9230.3","highPx":"9230.3","lowPx":"9220.6","buyVolume":"0","sellVolume":"0"}
        #         ]
        #     }
        #
        mdEntry = self.safe_value(response, 'mdEntry', [])
        return self.parse_ohlcvs(mdEntry, market, timeframe, since, limit)

    def fetch_trades(self, symbol, since=None, limit=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
            # 'from': self.iso8601(since),
            # 'to': self.iso8601(self.milliseconds()),
            # 'page': 1,
            # 'limit': limit,
        }
        if since is not None:
            request['from'] = self.iso8601(since)
        if limit is not None:
            request['limit'] = limit
        response = self.publicGetMarketDataV2TradesSymbol(self.extend(request, params))
        #
        #     {
        #         "msgType":"W",
        #         "lastUpdateTime":1594737830902223803,
        #         "symbol":"XBTUSD",
        #         "mdEntry":[
        #             {
        #                 "mdUpdateAction":"0",
        #                 "mdEntryType":"2",
        #                 "mdEntryPx":"9225.16",
        #                 "mdEntrySize":"10000",
        #                 "transactTime":1594728504524977655,
        #                 "tradeId":"6ac51bb7-7505-4f35-85ef-61eb738cb4d9",
        #                 "aggressorSide":"1"
        #             },
        #         ]
        #     }
        #
        mdEntry = self.safe_value(response, 'mdEntry', [])
        return self.parse_trades(mdEntry, market, since, limit)

    def parse_order_status(self, status):
        statuses = {
            'A': 'open',  # PendingNew
            '0': 'open',  # New
            '1': 'open',  # PartiallyFilled
            '2': 'closed',  # Filled
            '6': 'canceled',  # PendingCancel
            '4': 'canceled',  # Cancelled
            'E': 'open',  # PendingReplace
            '8': 'rejected',  # Rejected
        }
        return self.safe_string(statuses, status, status)

    def parse_order(self, order, market=None):
        #
        # createOrder
        #
        #     {
        #         "msgType":"8",
        #         "account":1012838720,
        #         "clOrdId":"XAq0pRQ1g",
        #         "orderId":"64d7a06a-27e5-422e-99d9-3cadc04f5a35",
        #         "symbol":"XBTUSD",
        #         "ordType":"2",
        #         "price":"9000",
        #         "transactTime":1593778763271127920,
        #         "execId":"ff5fb8153652f0516bf07b6979255bed053c84b9",
        #         "execType":"I",
        #         "ordStatus":"0",
        #         "side":"1",
        #         "orderQty":"1",
        #         "leavesQty":"1",
        #         "cumQty":"0",
        #         "positionEffect":"O",
        #         "marginAmt":"0.00000556",
        #         "marginAmtType":"11"
        #     }
        #
        id = self.safe_string(order, 'orderId')
        clientOrderId = self.safe_string(order, 'clOrdId')
        transactTime = self.safe_integer(order, 'transactTime')
        timestamp = int(transactTime / 1000000)
        status = self.parse_order_status(self.safe_string(order, 'ordStatus'))
        marketId = self.safe_string(order, 'symbol')
        symbol = self.safe_symbol(marketId, market)
        price = self.safe_float(order, 'price')
        amount = self.safe_float(order, 'orderQty')
        filled = self.safe_float(order, 'cumQty')
        remaining = self.safe_float(order, 'leavesQty')
        cost = None
        side = self.safe_string_lower(order, 'side')
        if side == '1':
            side = 'buy'
        elif side == '1':
            side = 'sell'
        type = self.safe_string_lower(order, 'ordType')
        if type == '1':
            type = 'market'
        elif type == '2':
            type = 'limit'
        elif type == '3':
            type = 'stop'
        elif type == '4':
            type = 'stop-limit'
        if cost is None:
            if (price is not None) and (filled is not None):
                cost = price * filled
        return {
            'id': id,
            'clientOrderId': clientOrderId,
            'info': order,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'lastTradeTimestamp': None,
            'symbol': symbol,
            'type': type,
            'timeInForce': None,
            'postOnly': None,
            'side': side,
            'price': price,
            'stopPrice': None,
            'amount': amount,
            'cost': cost,
            'average': None,
            'filled': filled,
            'remaining': remaining,
            'status': status,
            'fee': None,
            'trades': None,
        }

    def create_order(self, symbol, type, side, amount, price=None, params={}):
        self.load_markets()
        self.load_accounts()
        accountId = self.get_account_id(params)
        orderTypes = {
            'market': '1',
            'limit': '2',
            'stop': '3',
            'stop-limit': '4',
        }
        orderType = self.safe_string(orderTypes, type)
        if orderType is None:
            raise InvalidOrder(self.id + ' createOrder does not support order type ' + type + ', supported order types are market, limit, stop, stop-limit')
        orderSides = {
            'buy': '1',
            'sell': '2',
        }
        orderSide = self.safe_string(orderSides, side)
        if orderSide is None:
            raise InvalidOrder(self.id + ' createOrder does not support order side ' + side + ', supported order sides are buy, sell')
        market = self.market(symbol)
        request = {
            'account': int(accountId),
            'symbol': market['id'],
            'ordType': orderType,
            'side': orderSide,
            'orderQty': self.amount_to_precision(symbol, amount),
            'transactTime': self.milliseconds() * 1000000,
            # 'clOrdId': self.uuid(),  # required
            # 'price': self.price_to_precision(symbol, price),  # required for limit and stop-limit orders
            # 'stopPx': self.price_to_precision(symbol, stopPx),  # required for stop and stop-limit orders
            # 'timeInForce': '1',  # default '1' = GoodTillCancelled, '3' = ImmediateOrCancel, '4' = FillOrKill
            # 'execInst': '0',
            #     '0' = StayOnOfferSide, maker only, reject instead of aggressive execution
            #     '9' = PegToOfferSide, maker only, best available level instead of aggressive execution
            #     'o' = CancelOnConnectionLoss
            # 'positionID': 1013838923,  # required when positionEffect == 'C' with hedged accounting
            # 'positionEffect': 'O',  # 'C' = Close, 'O' = Open, send C along with the positionID if the order must close a position with hedged accounting mode
            # 'text': 'comment',  # optional
            # 'grpID': 'group-identifier',  # group identifier for cancel on disconnect orders
        }
        if (type == 'limit') or (type == 'stop-limit'):
            if price is None:
                raise InvalidOrder(self.id + ' createOrder() requires a price argument for order type ' + type)
            request['price'] = self.price_to_precision(symbol, price)
        if (type == 'stop') or (type == 'stop-limit'):
            stopPx = self.safe_float(params, 'stopPx')
            if stopPx is None:
                raise InvalidOrder(self.id + ' createOrder() requires a stopPx param for order type ' + type)
            request['stopPx'] = self.price_to_precision(symbol, stopPx)
            params = self.omit(params, 'stopPx')
        clientOrderId = self.safe_string_2(params, 'clientOrderId', 'clOrdId', self.uuid())
        if clientOrderId is not None:
            request['clOrdId'] = clientOrderId
            params = self.omit(params, ['clientOrderId', 'clOrdId'])
        response = self.privatePostTradingOrderNew(self.extend(request, params))
        #
        #     {
        #         "msgType":"8",
        #         "account":1012838720,
        #         "clOrdId":"XAq0pRQ1g",
        #         "orderId":"64d7a06a-27e5-422e-99d9-3cadc04f5a35",
        #         "symbol":"XBTUSD",
        #         "ordType":"2",
        #         "price":"9000",
        #         "transactTime":1593778763271127920,
        #         "execId":"ff5fb8153652f0516bf07b6979255bed053c84b9",
        #         "execType":"I",
        #         "ordStatus":"0",
        #         "side":"1",
        #         "orderQty":"1",
        #         "leavesQty":"1",
        #         "cumQty":"0",
        #         "positionEffect":"O",
        #         "marginAmt":"0.00000556",
        #         "marginAmtType":"11"
        #     }
        #
        return self.parse_order(response, market)

    def edit_order(self, id, symbol, type, side, amount=None, price=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
        self.load_markets()
        self.load_accounts()
        accountId = self.get_account_id(params)
        market = self.market(symbol)
        request = {
            'account': int(accountId),
            'clOrdId': self.uuid(),
            'symbol': market['id'],
            'transactTime': self.milliseconds() * 1000000,
            # 'origClOrdId': self.uuid(),  # one of orderId or origClOrdId is required
            # 'orderId': id,
            # 'side': '1',  # 1 = buy, 2 = sell
            # 'execInst': '0',
            #     '0' = StayOnOfferSide, maker only, reject instead of aggressive execution
            #     '9' = PegToOfferSide, maker only, best available level instead of aggressive execution
            #     'o' = CancelOnConnectionLoss
            # 'orderQty': 38 M decimal
            # 'price': self.price_to_precision(symbol, price),  # required for limit and stop-limit orders
            # 'stopPx': self.price_to_precision(symbol, stopPx),  # required for stop and stop-limit orders
            # 'capPrice': self.price_to_precision(symbol, capPrice),  # the price beyond which the order will not move for trailing stop and attempt-zero-loss
            # 'pegPriceType': '8',  # '8' = TrailingStopPeg, identifies a trailing stop or an attempt-zero-loss order
            # 'pegOffsetType': '2',  # '2' = BasisPoints, the unit of the distance to the stop price for a trailing stop or an attempt-zero-loss order
            # 'pegOffsetValue': 123,  # distance to the trailing stop or attempt-zero-loss
        }
        clientOrderId = self.safe_string_2(params, 'clientOrderId', 'origClOrdId')
        if clientOrderId is not None:
            request['origClOrdId'] = clientOrderId
            params = self.omit(params, ['clientOrderId', 'origClOrdId'])
        else:
            request['orderId'] = id
        if amount is not None:
            request['orderQty'] = self.amount_to_precision(symbol, amount)
        if price is not None:
            request['price'] = self.price_to_precision(symbol, price)
        stopPx = self.safe_float(params, 'stopPx')
        if stopPx is not None:
            request['stopPx'] = self.price_to_precision(symbol, stopPx)
            params = self.omit(params, 'stopPx')
        capPrice = self.safe_float(params, 'capPrice')
        if capPrice is not None:
            request['capPrice'] = self.price_to_precision(symbol, capPrice)
            params = self.omit(params, 'capPrice')
        response = self.privatePostTradingOrderReplace(self.extend(request, params))
        return self.parse_order(response, market)

    def cancel_order(self, id, symbol=None, params={}):
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrder() requires a symbol argument')
        self.load_markets()
        self.load_accounts()
        accountId = self.get_account_id(params)
        clientOrderId = self.safe_string_2(params, 'clientOrderId', 'origClOrdId')
        params = self.omit(params, ['clientOrderId', 'origClOrdId'])
        market = self.market(symbol)
        request = {
            'account': int(accountId),
            'symbol': market['id'],
            'clOrdId': self.uuid(),
            'transactTime': self.milliseconds() * 1000000,
        }
        if clientOrderId is not None:
            request['origClOrdId'] = clientOrderId
        else:
            request['orderId'] = id
        response = self.privatePostTradingOrderCancel(self.extend(request, params))
        #
        #     {
        #         "msgType":"8",
        #         "account":1012838158,
        #         "clOrdId":"0fa3fb55-9dc0-4cfc-a1db-6aa8b7dd2d98",
        #         "origClOrdId":"3b2878bb-24d8-4922-9d2a-5b8009416677",
        #         "orderId":"665b418e-9d09-4461-b733-d317f6bff43f",
        #         "symbol":"ETHUSD",
        #         "ordType":"2",
        #         "price":"640",
        #         "transactTime":1595060080941618739,
        #         "execId":"c541c0ca437c0e6501c3a50a9d4dc8f575f49972",
        #         "execType":"6",
        #         "ordStatus":"6",
        #         "side":"2",
        #         "orderQty":"1",
        #         "leavesQty":"0",
        #         "cumQty":"0",
        #         "positionEffect":"O",
        #         "marginAmt":"0.000032",
        #         "marginAmtType":"11"
        #     }
        #
        return self.parse_order(response, market)

    def cancel_all_orders(self, symbol=None, params={}):
        self.load_markets()
        self.load_accounts()
        accountId = self.get_account_id(params)
        request = {
            'account': int(accountId),
            'clOrdId': self.uuid(),
            # 'side': '1',  # 1 = buy, 2 = sell, optional filter, cancel only orders with the given side
            # 'positionEffect': 'C',  # C = Close, O = Open, optional filter, cancel only orders with the given positionEffect, applicable only for accounts with hedged accounting
        }
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
            request['massCancelRequestType'] = '1'  # CancelOrdersForASecurity
        else:
            request['massCancelRequestType'] = '7'  # CancelAllOrders
        response = self.privatePostTradingOrderMassCancel(self.extend(request, params))
        #
        #     {
        #         "msgType":"r",
        #         "clOrdId":"b3e95759-e43e-4b3a-b664-a4d213e281a7",
        #         "massActionReportID":"e915b6f4-a7ca-4c5c-b8d6-e39862530248",
        #         "massCancelResponse":"1",
        #         "symbol":"ETHUSD",
        #         "transactTime":1595065630133756426,
        #         "totalAffectedOrders":2,
        #         "account":1012838158
        #     }
        #
        return response

    def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        self.load_accounts()
        accountId = self.get_account_id(params)
        request = {
            'accountId': accountId,
            # 'symbol': market['id'],
        }
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        response = self.privateGetTradingAccountsAccountIdActiveOrders(self.extend(request, params))
        #
        #     [
        #         {
        #             "msgType":"8",
        #             "account":1012838720,
        #             "clOrdId":"XAq0pRQ1g",
        #             "orderId":"64d7a06a-27e5-422e-99d9-3cadc04f5a35",
        #             "symbol":"XBTUSD",
        #             "ordType":"2",
        #             "price":"9000",
        #             "transactTime":1593778763271127920,
        #             "execId":"ff5fb8153652f0516bf07b6979255bed053c84b9",
        #             "execType":"I",
        #             "ordStatus":"0",
        #             "side":"1",
        #             "orderQty":"1",
        #             "leavesQty":"1",
        #             "cumQty":"0",
        #             "positionEffect":"O",
        #             "marginAmt":"0.00000556",
        #             "marginAmtType":"11"
        #         }
        #     ]
        #
        return self.parse_orders(response, market, since, limit)

    def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        self.load_accounts()
        accountId = self.get_account_id(params)
        request = {
            'accountId': accountId,
            # 'from': self.iso8601(since) * 1000000,
            # 'to': self.iso8601(self.milliseconds()) * 1000000,  # max range is 7 days
            # 'symbol': market['id'],
            # 'limit': 100,
        }
        market = None
        if symbol is not None:
            market = self.market(symbol)
            request['symbol'] = market['id']
        if since is not None:
            request['from'] = self.iso8601(since) * 1000000
        if limit is not None:
            request['limit'] = limit
        response = self.privateGetTradingAccountsAccountIdLastOrderStatuses(self.extend(request, params))
        #
        #     [
        #         {
        #             "msgType":"8",
        #             "account":1012838720,
        #             "clOrdId":"XAq0pRQ1g",
        #             "orderId":"64d7a06a-27e5-422e-99d9-3cadc04f5a35",
        #             "symbol":"XBTUSD",
        #             "ordType":"2",
        #             "price":"9000",
        #             "transactTime":1593778763271127920,
        #             "execId":"ff5fb8153652f0516bf07b6979255bed053c84b9",
        #             "execType":"I",
        #             "ordStatus":"0",
        #             "side":"1",
        #             "orderQty":"1",
        #             "leavesQty":"1",
        #             "cumQty":"0",
        #             "positionEffect":"O",
        #             "marginAmt":"0.00000556",
        #             "marginAmtType":"11"
        #         }
        #     ]
        #
        return self.parse_orders(response, market, since, limit)

    def create_deposit_address(self, code, params={}):
        self.load_markets()
        self.load_accounts()
        accountId = self.get_account_id(params)
        currency = self.currency(code)
        request = {
            'accountId': accountId,
            'currency': currency['id'],
        }
        response = self.privatePostTransfersAccountsAccountIdDepositAddressCurrency(self.extend(request, params))
        #
        #     {
        #         "address": "mu5GceHFAG38mGRYCFqafe5ZiNKLX3rKk9",
        #         "uri": "bitcoin:mu5GceHFAG38mGRYCFqafe5ZiNKLX3rKk9?message=Xena Exchange",
        #         "allowsRenewal": True
        #     }
        #
        address = self.safe_value(response, 'address')
        tag = None
        self.check_address(address)
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'info': response,
        }

    def fetch_deposit_address(self, code, params={}):
        self.load_markets()
        self.load_accounts()
        accountId = self.get_account_id(params)
        currency = self.currency(code)
        request = {
            'accountId': accountId,
            'currency': currency['id'],
        }
        response = self.privateGetTransfersAccountsAccountIdDepositAddressCurrency(self.extend(request, params))
        #
        #     {
        #         "address": "mu5GceHFAG38mGRYCFqafe5ZiNKLX3rKk9",
        #         "uri": "bitcoin:mu5GceHFAG38mGRYCFqafe5ZiNKLX3rKk9?message=Xena Exchange",
        #         "allowsRenewal": True
        #     }
        #
        address = self.safe_value(response, 'address')
        tag = None
        self.check_address(address)
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'info': response,
        }

    def fetch_transactions_by_type(self, type, code=None, since=None, limit=None, params={}):
        if code is None:
            raise ArgumentsRequired(self.id + ' fetchTransactions() requires a currency `code` argument')
        self.load_markets()
        self.load_accounts()
        accountId = self.get_account_id(params)
        currency = self.currency(code)
        request = {
            'currency': currency['id'],
            'accountId': accountId,
        }
        if since is not None:
            request['since'] = int(since / 1000)
        method = 'privateGetTransfersAccountsAccountId' + self.capitalize(type)
        response = getattr(self, method)(self.extend(request, params))
        #
        #     {
        #         "withdrawals": [
        #             {
        #                 "withdrawalRequestId": 47383243,
        #                 "externalId": "...",    # external ID submitted by the client when creating the request
        #                 "status": 1,
        #                 "statusMessage": "Pending confirmation",
        #                 "amount": "10.2",
        #                 "currency": "BTC",
        #                 "lastUpdated": <UNIX nanoseconds>,
        #                 "blockchain": "Bitcoin",
        #                 "address": "mu5GceHFAG38mGRYCFqafe5ZiNKLX3rKk9",
        #                 "txId": "0xfbb1b73c4f0bda4f67dca266ce6ef42f520fbb98"
        #             }
        #         ]
        #     }
        #
        #     {
        #         "deposits": [
        #             {
        #                 "currency": "BTC",
        #                 "amount": "1.2",
        #                 "status": 1,
        #                 "statusMessage": "Processing",
        #                 "blockchain": "Bitcoin",
        #                 "txId": "0xfbb1b73c4f0bda4f67dca266ce6ef42f520fbb98",
        #                 "address": "mu5GceHFAG38mGRYCFqafe5ZiNKLX3rKk9",
        #                 "lastUpdated": <UNIX nanoseconds>
        #                 "confirmations": 2,
        #                 "requiredConfirmations": 6
        #             }
        #         ]
        #     }
        #
        #
        transactions = self.safe_value(response, type, [])
        return self.parse_transactions(transactions, currency, since, limit)

    def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
        return self.fetch_transactions_by_type('withdrawals', code, since, limit, params)

    def fetch_deposits(self, code=None, since=None, limit=None, params={}):
        return self.fetch_transactions_by_type('deposits', code, since, limit, params)

    def parse_transaction(self, transaction, currency=None):
        #
        # withdraw()
        #
        #     {
        #         "withdrawalRequestId": 47383243,
        #         "status": 1,
        #         "statusMessage": "Pending confirmation"
        #     }
        #
        # fetchWithdrawals
        #
        #     {
        #         "withdrawalRequestId": 47383243,
        #         "externalId": "...",    # external ID submitted by the client when creating the request
        #         "status": 1,
        #         "statusMessage": "Pending confirmation",
        #         "amount": "10.2",
        #         "currency": "BTC",
        #         "lastUpdated": <UNIX nanoseconds>,
        #         "blockchain": "Bitcoin",
        #         "address": "mu5GceHFAG38mGRYCFqafe5ZiNKLX3rKk9",
        #         "txId": "0xfbb1b73c4f0bda4f67dca266ce6ef42f520fbb98"
        #     }
        #
        # fetchDeposits
        #
        #     {
        #         "currency": "BTC",
        #         "amount": "1.2",
        #         "status": 1,
        #         "statusMessage": "Processing",
        #         "blockchain": "Bitcoin",
        #         "txId": "0xfbb1b73c4f0bda4f67dca266ce6ef42f520fbb98",
        #         "address": "mu5GceHFAG38mGRYCFqafe5ZiNKLX3rKk9",
        #         "lastUpdated": <UNIX nanoseconds>
        #         "confirmations": 2,
        #         "requiredConfirmations": 6
        #     }
        #
        id = self.safe_string(transaction, 'withdrawalRequestId')
        type = 'deposit' if (id is None) else 'withdrawal'
        updated = self.safe_integer(transaction, 'lastUpdated')
        if updated is not None:
            updated = int(updated / 1000000)
        timestamp = None
        txid = self.safe_string(transaction, 'txId')
        currencyId = self.safe_string(transaction, 'currency')
        code = self.safe_currency_code(currencyId, currency)
        address = self.safe_string(transaction, 'address')
        addressFrom = None
        addressTo = address
        amount = self.safe_float(transaction, 'amount')
        status = self.parse_transaction_status(self.safe_string(transaction, 'status'))
        fee = None
        return {
            'info': transaction,
            'id': id,
            'txid': txid,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'addressFrom': addressFrom,
            'addressTo': addressTo,
            'address': address,
            'tagFrom': None,
            'tagTo': None,
            'tag': None,
            'type': type,
            'amount': amount,
            'currency': code,
            'status': status,
            'updated': updated,
            'fee': fee,
        }

    def parse_transaction_status(self, status):
        statuses = {
            '1': 'pending',  # new
            '2': 'ok',  # completed
            '3': 'failed',  # duplicate
            '4': 'failed',  # not enough money
            '5': 'pending',  # waiting for manual approval from XENA
            '100': 'pending',  # request is being processed
            '101': 'pending',  # request is being processed
            '102': 'pending',  # request is being processed
            '103': 'pending',  # request is being processed
        }
        return self.safe_string(statuses, status, status)

    def withdraw(self, code, amount, address, tag=None, params={}):
        self.check_address(address)
        self.load_markets()
        self.load_accounts()
        accountId = self.get_account_id(params)
        currency = self.currency(code)
        uuid = self.uuid()
        uuid = uuid.split('-')
        uuid = ''.join(uuid)
        request = {
            'currency': currency['id'],
            'accountId': accountId,
            'amount': self.currency_to_precision(code, amount),
            'address': address,
            'id': uuid,  # mandatory external ID(string), used by the client to identify his request
        }
        response = self.privatePostTransfersAccountsAccountIdWithdrawals(self.extend(request, params))
        #
        #     {
        #         "withdrawalRequestId": 47383243,
        #         "status": 1,
        #         "statusMessage": "Pending confirmation"
        #     }
        #
        return self.parse_transaction(response, currency)

    def parse_ledger_entry_type(self, type):
        types = {
            'deposit': 'transaction',
            'withdrawal': 'transaction',
            'internal deposit': 'transfer',
            'internal withdrawal': 'transfer',
            'rebate': 'rebate',
            'reward': 'reward',
        }
        return self.safe_string(types, type, type)

    def parse_ledger_entry(self, item, currency=None):
        #
        #     {
        #         "accountId":8263118,
        #         "ts":1551974415000000000,
        #         "amount":"-1",
        #         "currency":"BTC",
        #         "kind":"internal withdrawal",
        #         "commission":"0",
        #         "id":96
        #     }
        #
        id = self.safe_string(item, 'id')
        direction = None
        account = self.safe_string(item, 'accountId')
        referenceId = None
        referenceAccount = None
        type = self.parse_ledger_entry_type(self.safe_string(item, 'kind'))
        code = self.safe_currency_code(self.safe_string(item, 'currency'), currency)
        amount = self.safe_float(item, 'amount')
        if amount < 0:
            direction = 'out'
            amount = abs(amount)
        else:
            direction = 'in'
        timestamp = self.safe_integer(item, 'ts')
        if timestamp is not None:
            timestamp = int(timestamp / 1000000)
        fee = {
            'cost': self.safe_float(item, 'commission'),
            'currency': code,
        }
        before = None
        after = self.safe_float(item, 'balance')
        status = 'ok'
        return {
            'info': item,
            'id': id,
            'direction': direction,
            'account': account,
            'referenceId': referenceId,
            'referenceAccount': referenceAccount,
            'type': type,
            'currency': code,
            'amount': amount,
            'before': before,
            'after': after,
            'status': status,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'fee': fee,
        }

    def fetch_ledger(self, code=None, since=None, limit=None, params={}):
        self.load_markets()
        self.load_accounts()
        accountId = self.get_account_id(params)
        request = {
            'accountId': accountId,
            # 'page': 1,
            # 'limit': 5000,  # max 5000
            # 'from': time,
            # 'to': time,
            # 'symbol': currency['id'],
            # 'trade_id': id,
            # 'client_order_id': id,
            # 'txid': txid,
            # 'kind': 'deposit',  # 'withdrawal, 'internal deposit', 'internal withdrawal', 'rebate', 'reward'
        }
        currency = None
        if code is not None:
            currency = self.currency(code)
            request['symbol'] = currency['id']
        if since is not None:
            request['from'] = since * 1000000
        if limit is not None:
            request['limit'] = limit  # max 5000
        response = self.privateGetTransfersAccountsAccountIdBalanceHistory(self.extend(request, params))
        #
        #     [
        #         {
        #             "accountId":8263118,
        #             "ts":1551974415000000000,
        #             "amount":"-1",
        #             "currency":"BTC",
        #             "kind":"internal withdrawal",
        #             "commission":"0",
        #             "id":96
        #         },
        #         {
        #             "accountId":8263118,
        #             "ts":1551964677000000000,
        #             "amount":"-1",
        #             "currency":"BTC",
        #             "kind":"internal deposit",
        #             "commission":"0",
        #             "id":95
        #         }
        #     ]
        #
        return self.parse_ledger(response, currency, since, limit)

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

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        url = self.urls['api'][api] + '/' + self.implode_params(path, params)
        query = self.omit(params, self.extract_params(path))
        if api == 'public':
            if query:
                url += '?' + self.urlencode(query)
        elif api == 'private':
            self.check_required_credentials()
            nonce = self.nonce()
            # php does not format it properly
            # therefore we use string concatenation here
            # nonce *= 1000000
            nonce = str(nonce)
            nonce = nonce + '000000'  # see the comment a few lines above
            payload = 'AUTH' + nonce
            secret = self.secret[14:78]
            ecdsa = self.ecdsa(payload, secret, 'p256', 'sha256')
            signature = ecdsa['r'] + ecdsa['s']
            headers = {
                'X-AUTH-API-KEY': self.apiKey,
                'X-AUTH-API-PAYLOAD': payload,
                'X-AUTH-API-SIGNATURE': signature,
                'X-AUTH-API-NONCE': nonce,
            }
            if method == 'GET':
                if query:
                    url += '?' + self.urlencode(query)
            elif method == 'POST':
                body = self.json(query)
                headers['Content-Type'] = 'application/json'
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
        if response is None:
            return
        #
        #     {"error":"Validation failed","fields":["address"]}
        #     {"error":"Money not enough. You have only: 0 ETH","fields":["amount"]}
        #
        if code >= 400:
            feedback = self.id + ' ' + self.json(response)
            message = self.safe_string(response, 'error')
            exact = self.exceptions['exact']
            if message in exact:
                raise exact[message](feedback)
            broad = self.exceptions['broad']
            broadKey = self.find_broadly_matched_key(broad, body)
            if broadKey is not None:
                raise broad[broadKey](feedback)
            raise ExchangeError(feedback)  # unknown message
