text: # -*- coding: utf-8 -*-
  
  # PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
  # https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
  
  from ccxt.async_support.base.exchange import Exchange
  
  # -----------------------------------------------------------------------------
  
  try:
      basestring  # Python 3
  except NameError:
      basestring = str  # Python 2
  import base64
  import hashlib
  import math
  from ccxt.base.errors import ExchangeError
  from ccxt.base.errors import AuthenticationError
  from ccxt.base.errors import PermissionDenied
  from ccxt.base.errors import ArgumentsRequired
  from ccxt.base.errors import InsufficientFunds
  from ccxt.base.errors import InvalidAddress
  from ccxt.base.errors import InvalidOrder
  from ccxt.base.errors import OrderNotFound
  from ccxt.base.errors import CancelPending
  from ccxt.base.errors import NotSupported
  from ccxt.base.errors import DDoSProtection
  from ccxt.base.errors import ExchangeNotAvailable
  from ccxt.base.errors import InvalidNonce
  from ccxt.base.decimal_to_precision import TRUNCATE
  from ccxt.base.decimal_to_precision import DECIMAL_PLACES
  
  
  class kraken (Exchange):
  
      def describe(self):
          return self.deep_extend(super(kraken, self).describe(), {
              'id': 'kraken',
              'name': 'Kraken',
              'countries': ['US'],
              'version': '0',
              'rateLimit': 3000,
              'certified': True,
              'has': {
                  'createDepositAddress': True,
                  'fetchDepositAddress': True,
                  'fetchTradingFee': True,
                  'fetchTradingFees': True,
                  'CORS': False,
                  'fetchCurrencies': True,
                  'fetchTickers': True,
                  'fetchOHLCV': True,
                  'fetchOrder': True,
                  'fetchOpenOrders': True,
                  'fetchClosedOrders': True,
                  'fetchMyTrades': True,
                  'fetchWithdrawals': True,
                  'fetchDeposits': True,
                  'withdraw': True,
                  'fetchLedgerEntry': True,
                  'fetchLedger': True,
              },
              'marketsByAltname': {},
              'timeframes': {
                  '1m': '1',
                  '5m': '5',
                  '15m': '15',
                  '30m': '30',
                  '1h': '60',
                  '4h': '240',
                  '1d': '1440',
                  '1w': '10080',
                  '2w': '21600',
              },
              'urls': {
                  'logo': 'https://user-images.githubusercontent.com/1294454/27766599-22709304-5ede-11e7-9de1-9f33732e1509.jpg',
                  'api': {
                      'public': 'https://api.kraken.com',
                      'private': 'https://api.kraken.com',
                      'zendesk': 'https://support.kraken.com/hc/en-us/articles/',
                  },
                  'www': 'https://www.kraken.com',
                  'doc': 'https://www.kraken.com/features/api',
                  'fees': 'https://www.kraken.com/en-us/features/fee-schedule',
              },
              'fees': {
                  'trading': {
                      'tierBased': True,
                      'percentage': True,
                      'taker': 0.26 / 100,
                      'maker': 0.16 / 100,
                      'tiers': {
                          'taker': [
                              [0, 0.0026],
                              [50000, 0.0024],
                              [100000, 0.0022],
                              [250000, 0.0020],
                              [500000, 0.0018],
                              [1000000, 0.0016],
                              [2500000, 0.0014],
                              [5000000, 0.0012],
                              [10000000, 0.0001],
                          ],
                          'maker': [
                              [0, 0.0016],
                              [50000, 0.0014],
                              [100000, 0.0012],
                              [250000, 0.0010],
                              [500000, 0.0008],
                              [1000000, 0.0006],
                              [2500000, 0.0004],
                              [5000000, 0.0002],
                              [10000000, 0.0],
                          ],
                      },
                  },
                  # self is a bad way of hardcoding fees that change on daily basis
                  # hardcoding is now considered obsolete, we will remove all of it eventually
                  'funding': {
                      'tierBased': False,
                      'percentage': False,
                      'withdraw': {
                          'BTC': 0.001,
                          'ETH': 0.005,
                          'XRP': 0.02,
                          'XLM': 0.00002,
                          'LTC': 0.02,
                          'DOGE': 2,
                          'ZEC': 0.00010,
                          'ICN': 0.02,
                          'REP': 0.01,
                          'ETC': 0.005,
                          'MLN': 0.003,
                          'XMR': 0.05,
                          'DASH': 0.005,
                          'GNO': 0.01,
                          'EOS': 0.5,
                          'BCH': 0.001,
                          'XTZ': 0.05,
                          'USD': 5,  # if domestic wire
                          'EUR': 5,  # if domestic wire
                          'CAD': 10,  # CAD EFT Withdrawal
                          'JPY': 300,  # if domestic wire
                      },
                      'deposit': {
                          'BTC': 0,
                          'ETH': 0,
                          'XRP': 0,
                          'XLM': 0,
                          'LTC': 0,
                          'DOGE': 0,
                          'ZEC': 0,
                          'ICN': 0,
                          'REP': 0,
                          'ETC': 0,
                          'MLN': 0,
                          'XMR': 0,
                          'DASH': 0,
                          'GNO': 0,
                          'EOS': 0,
                          'BCH': 0,
                          'XTZ': 0.05,
                          'USD': 5,  # if domestic wire
                          'EUR': 0,  # free deposit if EUR SEPA Deposit
                          'CAD': 5,  # if domestic wire
                          'JPY': 0,  # Domestic Deposit(Free, ¥5,000 deposit minimum)
                      },
                  },
              },
              'api': {
                  'zendesk': {
                      'get': [
                          # we should really refrain from putting fixed fee numbers and stop hardcoding
                          # we will be using their web APIs to scrape all numbers from these articles
                          '205893708-What-is-the-minimum-order-size-',
                          '201396777-What-are-the-deposit-fees-',
                          '201893608-What-are-the-withdrawal-fees-',
                      ],
                  },
                  'public': {
                      'get': [
                          'Assets',
                          'AssetPairs',
                          'Depth',
                          'OHLC',
                          'Spread',
                          'Ticker',
                          'Time',
                          'Trades',
                      ],
                  },
                  'private': {
                      'post': [
                          'AddOrder',
                          'AddExport',
                          'Balance',
                          'CancelOrder',
                          'ClosedOrders',
                          'DepositAddresses',
                          'DepositMethods',
                          'DepositStatus',
                          'ExportStatus',
                          'Ledgers',
                          'OpenOrders',
                          'OpenPositions',
                          'QueryLedgers',
                          'QueryOrders',
                          'QueryTrades',
                          'RetrieveExport',
                          'RemoveExport',
                          'TradeBalance',
                          'TradesHistory',
                          'TradeVolume',
                          'Withdraw',
                          'WithdrawCancel',
                          'WithdrawInfo',
                          'WithdrawStatus',
                      ],
                  },
              },
              'commonCurrencies': {
                  'XBT': 'BTC',
                  'XDG': 'DOGE',
              },
              'options': {
                  'cacheDepositMethodsOnFetchDepositAddress': True,  # will issue up to two calls in fetchDepositAddress
                  'depositMethods': {},
                  'delistedMarketsById': {},
                  # cannot withdraw/deposit these
                  'inactiveCurrencies': ['CAD', 'USD', 'JPY', 'GBP'],
              },
              'exceptions': {
                  'EAPI:Invalid key': AuthenticationError,
                  'EFunding:Unknown withdraw key': ExchangeError,
                  'EFunding:Invalid amount': InsufficientFunds,
                  'EService:Unavailable': ExchangeNotAvailable,
                  'EDatabase:Internal error': ExchangeNotAvailable,
                  'EService:Busy': ExchangeNotAvailable,
                  'EQuery:Unknown asset': ExchangeError,
                  'EAPI:Rate limit exceeded': DDoSProtection,
                  'EOrder:Rate limit exceeded': DDoSProtection,
                  'EGeneral:Internal error': ExchangeNotAvailable,
                  'EGeneral:Temporary lockout': DDoSProtection,
                  'EGeneral:Permission denied': PermissionDenied,
              },
          })
  
      def cost_to_precision(self, symbol, cost):
          return self.decimal_to_precision(cost, TRUNCATE, self.markets[symbol]['precision']['price'], DECIMAL_PLACES)
  
      def fee_to_precision(self, symbol, fee):
          return self.decimal_to_precision(fee, TRUNCATE, self.markets[symbol]['precision']['amount'], DECIMAL_PLACES)
  
      async def fetch_min_order_amounts(self):
          html = await self.zendeskGet205893708WhatIsTheMinimumOrderSize()
          parts = html.split('<td class="wysiwyg-text-align-right">')
          numParts = len(parts)
          if numParts < 3:
              raise NotSupported(self.id + ' fetchMinOrderAmounts HTML page markup has changed: https://support.kraken.com/hc/en-us/articles/205893708-What-is-the-minimum-order-size-')
          result = {}
          # skip the part before the header and the header itself
          for i in range(2, len(parts)):
              part = parts[i]
              chunks = part.split('</td>')
              amountAndCode = chunks[0]
              if amountAndCode != 'To Be Announced':
                  pieces = amountAndCode.split(' ')
                  numPieces = len(pieces)
                  if numPieces == 2:
                      amount = float(pieces[0])
                      code = self.safe_currency_code(pieces[1])
                      result[code] = amount
          return result
  
      async def fetch_markets(self, params={}):
          response = await self.publicGetAssetPairs(params)
          limits = await self.fetch_min_order_amounts()
          keys = list(response['result'].keys())
          result = []
          for i in range(0, len(keys)):
              id = keys[i]
              market = response['result'][id]
              baseId = market['base']
              quoteId = market['quote']
              base = self.safe_currency_code(baseId)
              quote = self.safe_currency_code(quoteId)
              darkpool = id.find('.d') >= 0
              symbol = market['altname'] if darkpool else (base + '/' + quote)
              maker = None
              if 'fees_maker' in market:
                  maker = float(market['fees_maker'][0][1]) / 100
              precision = {
                  'amount': market['lot_decimals'],
                  'price': market['pair_decimals'],
              }
              minAmount = math.pow(10, -precision['amount'])
              if base in limits:
                  minAmount = limits[base]
              result.append({
                  'id': id,
                  'symbol': symbol,
                  'base': base,
                  'quote': quote,
                  'baseId': baseId,
                  'quoteId': quoteId,
                  'darkpool': darkpool,
                  'info': market,
                  'altname': market['altname'],
                  'maker': maker,
                  'taker': float(market['fees'][0][1]) / 100,
                  'active': True,
                  'precision': precision,
                  'limits': {
                      'amount': {
                          'min': minAmount,
                          'max': math.pow(10, precision['amount']),
                      },
                      'price': {
                          'min': math.pow(10, -precision['price']),
                          'max': None,
                      },
                      'cost': {
                          'min': 0,
                          'max': None,
                      },
                  },
              })
          result = self.append_inactive_markets(result)
          self.marketsByAltname = self.index_by(result, 'altname')
          return result
  
      def safe_currency_code(self, currencyId, currency=None):
          if len(currencyId) > 3:
              if (currencyId.find('X') == 0) or (currencyId.find('Z') == 0):
                  currencyId = currencyId[1:]
          return super(kraken, self).safe_currency_code(currencyId, currency)
  
      def append_inactive_markets(self, result):
          # result should be an array to append to
          precision = {'amount': 8, 'price': 8}
          costLimits = {'min': 0, 'max': None}
          priceLimits = {'min': math.pow(10, -precision['price']), 'max': None}
          amountLimits = {'min': math.pow(10, -precision['amount']), 'max': math.pow(10, precision['amount'])}
          limits = {'amount': amountLimits, 'price': priceLimits, 'cost': costLimits}
          defaults = {
              'darkpool': False,
              'info': None,
              'maker': None,
              'taker': None,
              'active': False,
              'precision': precision,
              'limits': limits,
          }
          markets = [
              # {'id': 'XXLMZEUR', 'symbol': 'XLM/EUR', 'base': 'XLM', 'quote': 'EUR', 'altname': 'XLMEUR'},
          ]
          for i in range(0, len(markets)):
              result.append(self.extend(defaults, markets[i]))
          return result
  
      async def fetch_currencies(self, params={}):
          response = await self.publicGetAssets(params)
          #
          #     {
          #         "error": [],
          #         "result": {
          #             "ADA": {"aclass": "currency", "altname": "ADA", "decimals": 8, "display_decimals": 6},
          #             "BCH": {"aclass": "currency", "altname": "BCH", "decimals": 10, "display_decimals": 5},
          #             ...
          #         },
          #     }
          #
          currencies = self.safe_value(response, 'result')
          ids = list(currencies.keys())
          result = {}
          for i in range(0, len(ids)):
              id = ids[i]
              currency = currencies[id]
              # todo: will need to rethink the fees
              # see: https://support.kraken.com/hc/en-us/articles/201893608-What-are-the-withdrawal-fees-
              # to add support for multiple withdrawal/deposit methods and
              # differentiated fees for each particular method
              code = self.safe_currency_code(self.safe_string(currency, 'altname'))
              precision = self.safe_integer(currency, 'decimals')
              # assumes all currencies are active except those listed above
              active = not self.in_array(code, self.options['inactiveCurrencies'])
              result[code] = {
                  'id': id,
                  'code': code,
                  'info': currency,
                  'name': code,
                  'active': active,
                  'fee': None,
                  'precision': precision,
                  'limits': {
                      'amount': {
                          'min': math.pow(10, -precision),
                          'max': math.pow(10, precision),
                      },
                      'price': {
                          'min': math.pow(10, -precision),
                          'max': math.pow(10, precision),
                      },
                      'cost': {
                          'min': None,
                          'max': None,
                      },
                      'withdraw': {
                          'min': None,
                          'max': math.pow(10, precision),
                      },
                  },
              }
          return result
  
      async def fetch_trading_fees(self, params={}):
          await self.load_markets()
          self.check_required_credentials()
          response = await self.privatePostTradeVolume(params)
          tradedVolume = self.safe_float(response['result'], 'volume')
          tiers = self.fees['trading']['tiers']
          taker = tiers['taker'][1]
          maker = tiers['maker'][1]
          for i in range(0, len(tiers['taker'])):
              if tradedVolume >= tiers['taker'][i][0]:
                  taker = tiers['taker'][i][1]
          for i in range(0, len(tiers['maker'])):
              if tradedVolume >= tiers['maker'][i][0]:
                  maker = tiers['maker'][i][1]
          return {
              'info': response,
              'maker': maker,
              'taker': taker,
          }
  
      async def fetch_order_book(self, symbol, limit=None, params={}):
          await self.load_markets()
          market = self.market(symbol)
          if market['darkpool']:
              raise ExchangeError(self.id + ' does not provide an order book for darkpool symbol ' + symbol)
          request = {
              'pair': market['id'],
          }
          if limit is not None:
              request['count'] = limit  # 100
          response = await self.publicGetDepth(self.extend(request, params))
          orderbook = response['result'][market['id']]
          return self.parse_order_book(orderbook)
  
      def parse_ticker(self, ticker, market=None):
          timestamp = self.milliseconds()
          symbol = None
          if market:
              symbol = market['symbol']
          baseVolume = float(ticker['v'][1])
          vwap = float(ticker['p'][1])
          quoteVolume = None
          if baseVolume is not None and vwap is not None:
              quoteVolume = baseVolume * vwap
          last = float(ticker['c'][0])
          return {
              'symbol': symbol,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'high': float(ticker['h'][1]),
              'low': float(ticker['l'][1]),
              'bid': float(ticker['b'][0]),
              'bidVolume': None,
              'ask': float(ticker['a'][0]),
              'askVolume': None,
              'vwap': vwap,
              'open': self.safe_float(ticker, 'o'),
              'close': last,
              'last': last,
              'previousClose': None,
              'change': None,
              'percentage': None,
              'average': None,
              'baseVolume': baseVolume,
              'quoteVolume': quoteVolume,
              'info': ticker,
          }
  
      async def fetch_tickers(self, symbols=None, params={}):
          await self.load_markets()
          symbols = self.symbols if (symbols is None) else symbols
          marketIds = []
          for i in range(0, len(self.symbols)):
              symbol = self.symbols[i]
              market = self.markets[symbol]
              if market['active'] and not market['darkpool']:
                  marketIds.append(market['id'])
          request = {
              'pair': ','.join(marketIds),
          }
          response = await self.publicGetTicker(self.extend(request, params))
          tickers = response['result']
          ids = list(tickers.keys())
          result = {}
          for i in range(0, len(ids)):
              id = ids[i]
              market = self.markets_by_id[id]
              symbol = market['symbol']
              ticker = tickers[id]
              if self.in_array(symbol, symbols):
                  result[symbol] = self.parse_ticker(ticker, market)
          return result
  
      async def fetch_ticker(self, symbol, params={}):
          await self.load_markets()
          darkpool = symbol.find('.d') >= 0
          if darkpool:
              raise ExchangeError(self.id + ' does not provide a ticker for darkpool symbol ' + symbol)
          market = self.market(symbol)
          request = {
              'pair': market['id'],
          }
          response = await self.publicGetTicker(self.extend(request, params))
          ticker = response['result'][market['id']]
          return self.parse_ticker(ticker, market)
  
      def parse_ohlcv(self, ohlcv, market=None, timeframe='1m', since=None, limit=None):
          return [
              ohlcv[0] * 1000,
              float(ohlcv[1]),
              float(ohlcv[2]),
              float(ohlcv[3]),
              float(ohlcv[4]),
              float(ohlcv[6]),
          ]
  
      async def fetch_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
          await self.load_markets()
          market = self.market(symbol)
          request = {
              'pair': market['id'],
              'interval': self.timeframes[timeframe],
          }
          if since is not None:
              request['since'] = int((since - 1) / 1000)
          response = await self.publicGetOHLC(self.extend(request, params))
          ohlcvs = response['result'][market['id']]
          return self.parse_ohlcvs(ohlcvs, market, timeframe, since, limit)
  
      def parse_ledger_entry_type(self, type):
          types = {
              'trade': 'trade',
              'withdrawal': 'transaction',
              'deposit': 'transaction',
              'transfer': 'transfer',
              'margin': 'margin',
          }
          return self.safe_string(types, type, type)
  
      def parse_ledger_entry(self, item, currency=None):
          #
          #     {
          #         'LTFK7F-N2CUX-PNY4SX': {
          #             refid: "TSJTGT-DT7WN-GPPQMJ",
          #             time:  1520102320.555,
          #             type: "trade",
          #             aclass: "currency",
          #             asset: "XETH",
          #             amount: "0.1087194600",
          #             fee: "0.0000000000",
          #             balance: "0.2855851000"
          #         },
          #         ...
          #     }
          #
          id = self.safe_string(item, 'id')
          direction = None
          account = None
          referenceId = self.safe_string(item, 'refid')
          referenceAccount = None
          type = self.parse_ledger_entry_type(self.safe_string(item, 'type'))
          code = self.safe_currency_code(self.safe_string(item, 'asset'), currency)
          amount = self.safe_float(item, 'amount')
          if amount < 0:
              direction = 'out'
              amount = abs(amount)
          else:
              direction = 'in'
          time = self.safe_float(item, 'time')
          timestamp = None
          if time is not None:
              timestamp = int(time * 1000)
          fee = {
              'cost': self.safe_float(item, 'fee'),
              '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,
          }
  
      async def fetch_ledger(self, code=None, since=None, limit=None, params={}):
          # https://www.kraken.com/features/api#get-ledgers-info
          await self.load_markets()
          request = {}
          currency = None
          if code is not None:
              currency = self.currency(code)
              request['asset'] = currency['id']
          if since is not None:
              request['start'] = int(since / 1000)
          response = await self.privatePostLedgers(self.extend(request, params))
          # { error: [],
          #   result: {ledger: {'LPUAIB-TS774-UKHP7X': {  refid: "A2B4HBV-L4MDIE-JU4N3N",
          #                                                   time:  1520103488.314,
          #                                                   type: "withdrawal",
          #                                                 aclass: "currency",
          #                                                  asset: "XETH",
          #                                                 amount: "-0.2805800000",
          #                                                    fee: "0.0050000000",
          #                                                balance: "0.0000051000"           },
          result = self.safe_value(response, 'result', {})
          ledger = self.safe_value(result, 'ledger', {})
          keys = list(ledger.keys())
          items = []
          for i in range(0, len(keys)):
              key = keys[i]
              value = ledger[key]
              value['id'] = key
              items.append(value)
          return self.parse_ledger(items, currency, since, limit)
  
      async def fetch_ledger_entries_by_ids(self, ids, code=None, params={}):
          # https://www.kraken.com/features/api#query-ledgers
          await self.load_markets()
          ids = ','.join(ids)
          request = self.extend({
              'id': ids,
          }, params)
          response = await self.privatePostQueryLedgers(request)
          # { error: [],
          #   result: {'LPUAIB-TS774-UKHP7X': {  refid: "A2B4HBV-L4MDIE-JU4N3N",
          #                                         time:  1520103488.314,
          #                                         type: "withdrawal",
          #                                       aclass: "currency",
          #                                        asset: "XETH",
          #                                       amount: "-0.2805800000",
          #                                          fee: "0.0050000000",
          #                                      balance: "0.0000051000"           }} }
          result = response['result']
          keys = list(result.keys())
          items = []
          for i in range(0, len(keys)):
              key = keys[i]
              value = result[key]
              value['id'] = key
              items.append(value)
          return self.parse_ledger(items)
  
      async def fetch_ledger_entry(self, id, code=None, params={}):
          items = await self.fetchLedgerEntrysByIds([id], code, params)
          return items[0]
  
      def parse_trade(self, trade, market=None):
          timestamp = None
          side = None
          type = None
          price = None
          amount = None
          id = None
          order = None
          fee = None
          marketId = self.safe_string(trade, 'pair')
          foundMarket = self.find_market_by_altname_or_id(marketId)
          symbol = None
          if foundMarket is not None:
              market = foundMarket
          elif marketId is not None:
              # delisted market ids go here
              market = self.get_delisted_market_by_id(marketId)
          if market is not None:
              symbol = market['symbol']
          if 'ordertxid' in trade:
              order = trade['ordertxid']
              id = self.safe_string_2(trade, 'id', 'postxid')
              timestamp = int(trade['time'] * 1000)
              side = trade['type']
              type = trade['ordertype']
              price = self.safe_float(trade, 'price')
              amount = self.safe_float(trade, 'vol')
              if 'fee' in trade:
                  currency = None
                  if market:
                      currency = market['quote']
                  fee = {
                      'cost': self.safe_float(trade, 'fee'),
                      'currency': currency,
                  }
          else:
              timestamp = int(trade[2] * 1000)
              side = 'sell' if (trade[3] == 's') else 'buy'
              type = 'limit' if (trade[4] == 'l') else 'market'
              price = float(trade[0])
              amount = float(trade[1])
              tradeLength = len(trade)
              if tradeLength > 6:
                  id = trade[6]  # artificially added as per  #1794
          return {
              'id': id,
              'order': order,
              'info': trade,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'symbol': symbol,
              'type': type,
              'side': side,
              'takerOrMaker': None,
              'price': price,
              'amount': amount,
              'cost': price * amount,
              'fee': fee,
          }
  
      async def fetch_trades(self, symbol, since=None, limit=None, params={}):
          await self.load_markets()
          market = self.market(symbol)
          id = market['id']
          request = {
              'pair': id,
          }
          response = await self.publicGetTrades(self.extend(request, params))
          #
          #     {
          #         "error": [],
          #         "result": {
          #             "XETHXXBT": [
          #                 ["0.032310","4.28169434",1541390792.763,"s","l",""]
          #             ],
          #             "last": "1541439421200678657"
          #         }
          #     }
          #
          result = response['result']
          trades = result[id]
          # trades is a sorted array: last(most recent trade) goes last
          length = len(trades)
          if length <= 0:
              return []
          lastTrade = trades[length - 1]
          lastTradeId = self.safe_string(result, 'last')
          lastTrade.append(lastTradeId)
          return self.parse_trades(trades, market, since, limit)
  
      async def fetch_balance(self, params={}):
          response = await self.privatePostBalance(params)
          balances = self.safe_value(response, 'result')
          if balances is None:
              raise ExchangeNotAvailable(self.id + ' fetchBalance failed due to a malformed response ' + self.json(response))
          result = {'info': balances}
          currencyIds = list(balances.keys())
          for i in range(0, len(currencyIds)):
              currencyId = currencyIds[i]
              code = self.safe_currency_code(currencyId)
              account = self.account()
              account['total'] = self.safe_float(balances, currencyId)
              result[code] = account
          return self.parse_balance(result)
  
      async def create_order(self, symbol, type, side, amount, price=None, params={}):
          await self.load_markets()
          market = self.market(symbol)
          request = {
              'pair': market['id'],
              'type': side,
              'ordertype': type,
              'volume': self.amount_to_precision(symbol, amount),
          }
          priceIsDefined = (price is not None)
          marketOrder = (type == 'market')
          limitOrder = (type == 'limit')
          shouldIncludePrice = limitOrder or (not marketOrder and priceIsDefined)
          if shouldIncludePrice:
              request['price'] = self.price_to_precision(symbol, price)
          response = await self.privatePostAddOrder(self.extend(request, params))
          id = self.safe_value(response['result'], 'txid')
          if id is not None:
              if isinstance(id, list):
                  length = len(id)
                  id = id if (length > 1) else id[0]
          return {
              'id': id,
              'info': response,
              'timestamp': None,
              'datetime': None,
              'lastTradeTimestamp': None,
              'symbol': symbol,
              'type': type,
              'side': side,
              'price': price,
              'amount': amount,
              'cost': None,
              'average': None,
              'filled': None,
              'remaining': None,
              'status': None,
              'fee': None,
              'trades': None,
          }
  
      def find_market_by_altname_or_id(self, id):
          if id in self.marketsByAltname:
              return self.marketsByAltname[id]
          elif id in self.markets_by_id:
              return self.markets_by_id[id]
          return None
  
      def get_delisted_market_by_id(self, id):
          if id is None:
              return id
          market = self.safe_value(self.options['delistedMarketsById'], id)
          if market is not None:
              return market
          baseIdStart = 0
          baseIdEnd = 3
          quoteIdStart = 3
          quoteIdEnd = 6
          if len(id) == 8:
              baseIdEnd = 4
              quoteIdStart = 4
              quoteIdEnd = 8
          elif len(id) == 7:
              baseIdEnd = 4
              quoteIdStart = 4
              quoteIdEnd = 7
          baseId = id[baseIdStart:baseIdEnd]
          quoteId = id[quoteIdStart:quoteIdEnd]
          base = self.safe_currency_code(baseId)
          quote = self.safe_currency_code(quoteId)
          symbol = base + '/' + quote
          market = {
              'symbol': symbol,
              'base': base,
              'quote': quote,
              'baseId': baseId,
              'quoteId': quoteId,
          }
          self.options['delistedMarketsById'][id] = market
          return market
  
      def parse_order_status(self, status):
          statuses = {
              'pending': 'open',  # order pending book entry
              'open': 'open',
              'closed': 'closed',
              'canceled': 'canceled',
              'expired': 'expired',
          }
          return self.safe_string(statuses, status, status)
  
      def parse_order(self, order, market=None):
          description = self.safe_value(order, 'descr', {})
          side = self.safe_string(description, 'type')
          type = self.safe_string(description, 'ordertype')
          marketId = self.safe_string(description, 'pair')
          foundMarket = self.find_market_by_altname_or_id(marketId)
          symbol = None
          if foundMarket is not None:
              market = foundMarket
          elif marketId is not None:
              # delisted market ids go here
              market = self.get_delisted_market_by_id(marketId)
          timestamp = self.safe_integer(order, 'opentm') * 1000
          amount = self.safe_float(order, 'vol')
          filled = self.safe_float(order, 'vol_exec')
          remaining = amount - filled
          fee = None
          cost = self.safe_float(order, 'cost')
          price = self.safe_float(description, 'price')
          if (price is None) or (price == 0):
              price = self.safe_float(description, 'price2')
          if (price is None) or (price == 0):
              price = self.safe_float(order, 'price', price)
          average = self.safe_float(order, 'price')
          if market is not None:
              symbol = market['symbol']
              if 'fee' in order:
                  flags = order['oflags']
                  feeCost = self.safe_float(order, 'fee')
                  fee = {
                      'cost': feeCost,
                      'rate': None,
                  }
                  if flags.find('fciq') >= 0:
                      fee['currency'] = market['quote']
                  elif flags.find('fcib') >= 0:
                      fee['currency'] = market['base']
          status = self.parse_order_status(self.safe_string(order, 'status'))
          id = self.safe_string(order, 'id')
          return {
              'id': id,
              'info': order,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'lastTradeTimestamp': None,
              'status': status,
              'symbol': symbol,
              'type': type,
              'side': side,
              'price': price,
              'cost': cost,
              'amount': amount,
              'filled': filled,
              'average': average,
              'remaining': remaining,
              'fee': fee,
              # 'trades': self.parse_trades(order['trades'], market),
          }
  
      def parse_orders(self, orders, market=None, since=None, limit=None, params={}):
          result = []
          ids = list(orders.keys())
          symbol = None
          if market is not None:
              symbol = market['symbol']
          for i in range(0, len(ids)):
              id = ids[i]
              order = self.extend({'id': id}, orders[id])
              result.append(self.extend(self.parse_order(order, market), params))
          return self.filter_by_symbol_since_limit(result, symbol, since, limit)
  
      async def fetch_order(self, id, symbol=None, params={}):
          await self.load_markets()
          response = await self.privatePostQueryOrders(self.extend({
              'trades': True,  # whether or not to include trades in output(optional, default False)
              'txid': id,  # do not comma separate a list of ids - use fetchOrdersByIds instead
              # 'userref': 'optional',  # restrict results to given user reference id(optional)
          }, params))
          orders = response['result']
          order = self.parse_order(self.extend({'id': id}, orders[id]))
          return self.extend({'info': response}, order)
  
      async def fetch_orders_by_ids(self, ids, symbol=None, params={}):
          await self.load_markets()
          response = await self.privatePostQueryOrders(self.extend({
              'trades': True,  # whether or not to include trades in output(optional, default False)
              'txid': ','.join(ids),  # comma delimited list of transaction ids to query info about(20 maximum)
          }, params))
          result = self.safe_value(response, 'result', {})
          orders = []
          orderIds = list(result.keys())
          for i in range(0, len(orderIds)):
              id = orderIds[i]
              item = result[id]
              order = self.parse_order(self.extend({'id': id}, item))
              orders.append(order)
          return orders
  
      async def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
          await self.load_markets()
          request = {
              # 'type': 'all',  # any position, closed position, closing position, no position
              # 'trades': False,  # whether or not to include trades related to position in output
              # 'start': 1234567890,  # starting unix timestamp or trade tx id of results(exclusive)
              # 'end': 1234567890,  # ending unix timestamp or trade tx id of results(inclusive)
              # 'ofs' = result offset
          }
          if since is not None:
              request['start'] = int(since / 1000)
          response = await self.privatePostTradesHistory(self.extend(request, params))
          #
          #     {
          #         "error": [],
          #         "result": {
          #             "trades": {
          #                 "GJ3NYQ-XJRTF-THZABF": {
          #                     "ordertxid": "TKH2SE-ZIF5E-CFI7LT",
          #                     "postxid": "OEN3VX-M7IF5-JNBJAM",
          #                     "pair": "XICNXETH",
          #                     "time": 1527213229.4491,
          #                     "type": "sell",
          #                     "ordertype": "limit",
          #                     "price": "0.001612",
          #                     "cost": "0.025792",
          #                     "fee": "0.000026",
          #                     "vol": "16.00000000",
          #                     "margin": "0.000000",
          #                     "misc": ""
          #                 },
          #                 ...
          #             },
          #             "count": 9760,
          #         },
          #     }
          #
          trades = response['result']['trades']
          ids = list(trades.keys())
          for i in range(0, len(ids)):
              trades[ids[i]]['id'] = ids[i]
          result = self.parse_trades(trades, None, since, limit)
          if symbol is None:
              return result
          return self.filter_by_symbol(result, symbol)
  
      async def cancel_order(self, id, symbol=None, params={}):
          await self.load_markets()
          response = None
          try:
              response = await self.privatePostCancelOrder(self.extend({
                  'txid': id,
              }, params))
          except Exception as e:
              if self.last_http_response:
                  if self.last_http_response.find('EOrder:Unknown order') >= 0:
                      raise OrderNotFound(self.id + ' cancelOrder() error ' + self.last_http_response)
              raise e
          return response
  
      async def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
          await self.load_markets()
          request = {}
          if since is not None:
              request['start'] = int(since / 1000)
          response = await self.privatePostOpenOrders(self.extend(request, params))
          orders = self.parse_orders(response['result']['open'], None, since, limit)
          if symbol is None:
              return orders
          return self.filter_by_symbol(orders, symbol)
  
      async def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
          await self.load_markets()
          request = {}
          if since is not None:
              request['start'] = int(since / 1000)
          response = await self.privatePostClosedOrders(self.extend(request, params))
          orders = self.parse_orders(response['result']['closed'], None, since, limit)
          if symbol is None:
              return orders
          return self.filter_by_symbol(orders, symbol)
  
      async def fetch_deposit_methods(self, code, params={}):
          await self.load_markets()
          currency = self.currency(code)
          request = {
              'asset': currency['id'],
          }
          response = await self.privatePostDepositMethods(self.extend(request, params))
          return response['result']
  
      def parse_transaction_status(self, status):
          # IFEX transaction states
          statuses = {
              'Initial': 'pending',
              'Pending': 'pending',
              'Success': 'ok',
              'Settled': 'ok',
              'Failure': 'failed',
              'Partial': 'ok',
          }
          return self.safe_string(statuses, status, status)
  
      def parse_transaction(self, transaction, currency=None):
          #
          # fetchDeposits
          #
          #     {method: "Ether(Hex)",
          #       aclass: "currency",
          #        asset: "XETH",
          #        refid: "Q2CANKL-LBFVEE-U4Y2WQ",
          #         txid: "0x57fd704dab1a73c20e24c8696099b695d596924b401b261513cfdab23…",
          #         info: "0x615f9ba7a9575b0ab4d571b2b36b1b324bd83290",
          #       amount: "7.9999257900",
          #          fee: "0.0000000000",
          #         time:  1529223212,
          #       status: "Success"                                                       }
          #
          # fetchWithdrawals
          #
          #     {method: "Ether",
          #       aclass: "currency",
          #        asset: "XETH",
          #        refid: "A2BF34S-O7LBNQ-UE4Y4O",
          #         txid: "0x288b83c6b0904d8400ef44e1c9e2187b5c8f7ea3d838222d53f701a15b5c274d",
          #         info: "0x7cb275a5e07ba943fee972e165d80daa67cb2dd0",
          #       amount: "9.9950000000",
          #          fee: "0.0050000000",
          #         time:  1530481750,
          #       status: "Success"                                                             }
          #
          id = self.safe_string(transaction, 'refid')
          txid = self.safe_string(transaction, 'txid')
          timestamp = self.safe_integer(transaction, 'time')
          if timestamp is not None:
              timestamp = timestamp * 1000
          currencyId = self.safe_string(transaction, 'asset')
          code = self.safe_currency_code(currencyId, currency)
          address = self.safe_string(transaction, 'info')
          amount = self.safe_float(transaction, 'amount')
          status = self.parse_transaction_status(self.safe_string(transaction, 'status'))
          type = self.safe_string(transaction, 'type')  # injected from the outside
          feeCost = self.safe_float(transaction, 'fee')
          if feeCost is None:
              if type == 'deposit':
                  feeCost = 0
          return {
              'info': transaction,
              'id': id,
              'currency': code,
              'amount': amount,
              'address': address,
              'tag': None,
              'status': status,
              'type': type,
              'updated': None,
              'txid': txid,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'fee': {
                  'currency': code,
                  'cost': feeCost,
              },
          }
  
      def parse_transactions_by_type(self, type, transactions, code=None, since=None, limit=None):
          result = []
          for i in range(0, len(transactions)):
              transaction = self.parse_transaction(self.extend({
                  'type': type,
              }, transactions[i]))
              result.append(transaction)
          return self.filterByCurrencySinceLimit(result, code, since, limit)
  
      async def fetch_deposits(self, code=None, since=None, limit=None, params={}):
          await self.load_markets()
          # https://www.kraken.com/en-us/help/api#deposit-status
          if code is None:
              raise ArgumentsRequired(self.id + ' fetchDeposits requires a currency code argument')
          currency = self.currency(code)
          request = {
              'asset': currency['id'],
          }
          response = await self.privatePostDepositStatus(self.extend(request, params))
          #
          #     { error: [],
          #       result: [{method: "Ether(Hex)",
          #                   aclass: "currency",
          #                    asset: "XETH",
          #                    refid: "Q2CANKL-LBFVEE-U4Y2WQ",
          #                     txid: "0x57fd704dab1a73c20e24c8696099b695d596924b401b261513cfdab23…",
          #                     info: "0x615f9ba7a9575b0ab4d571b2b36b1b324bd83290",
          #                   amount: "7.9999257900",
          #                      fee: "0.0000000000",
          #                     time:  1529223212,
          #                   status: "Success"                                                       }]}
          #
          return self.parse_transactions_by_type('deposit', response['result'], code, since, limit)
  
      async def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
          await self.load_markets()
          # https://www.kraken.com/en-us/help/api#withdraw-status
          if code is None:
              raise ArgumentsRequired(self.id + ' fetchWithdrawals requires a currency code argument')
          currency = self.currency(code)
          request = {
              'asset': currency['id'],
          }
          response = await self.privatePostWithdrawStatus(self.extend(request, params))
          #
          #     { error: [],
          #       result: [{method: "Ether",
          #                   aclass: "currency",
          #                    asset: "XETH",
          #                    refid: "A2BF34S-O7LBNQ-UE4Y4O",
          #                     txid: "0x298c83c7b0904d8400ef43e1c9e2287b518f7ea3d838822d53f704a1565c274d",
          #                     info: "0x7cb275a5e07ba943fee972e165d80daa67cb2dd0",
          #                   amount: "9.9950000000",
          #                      fee: "0.0050000000",
          #                     time:  1530481750,
          #                   status: "Success"                                                             }]}
          #
          return self.parse_transactions_by_type('withdrawal', response['result'], code, since, limit)
  
      async def create_deposit_address(self, code, params={}):
          request = {
              'new': 'true',
          }
          response = await self.fetch_deposit_address(code, self.extend(request, params))
          address = self.safe_string(response, 'address')
          self.check_address(address)
          return {
              'currency': code,
              'address': address,
              'info': response,
          }
  
      async def fetch_deposit_address(self, code, params={}):
          await self.load_markets()
          currency = self.currency(code)
          # eslint-disable-next-line quotes
          method = self.safe_string(params, 'method')
          if method is None:
              if self.options['cacheDepositMethodsOnFetchDepositAddress']:
                  # cache depositMethods
                  if not(code in list(self.options['depositMethods'].keys())):
                      self.options['depositMethods'][code] = await self.fetch_deposit_methods(code)
                  method = self.options['depositMethods'][code][0]['method']
              else:
                  raise ExchangeError(self.id + ' fetchDepositAddress() requires an extra `method` parameter. Use fetchDepositMethods("' + code + '") to get a list of available deposit methods or enable the exchange property .options["cacheDepositMethodsOnFetchDepositAddress"] = True')
          request = {
              'asset': currency['id'],
              'method': method,
          }
          response = await self.privatePostDepositAddresses(self.extend(request, params))  # overwrite methods
          result = response['result']
          numResults = len(result)
          if numResults < 1:
              raise InvalidAddress(self.id + ' privatePostDepositAddresses() returned no addresses')
          address = self.safe_string(result[0], 'address')
          tag = self.safe_string_2(result[0], 'tag', 'memo')
          self.check_address(address)
          return {
              'currency': code,
              'address': address,
              'tag': tag,
              'info': response,
          }
  
      async def withdraw(self, code, amount, address, tag=None, params={}):
          self.check_address(address)
          if 'key' in params:
              await self.load_markets()
              currency = self.currency(code)
              request = {
                  'asset': currency['id'],
                  'amount': amount,
                  # 'address': address,  # they don't allow withdrawals to direct addresses
              }
              response = await self.privatePostWithdraw(self.extend(request, params))
              return {
                  'info': response,
                  'id': response['result'],
              }
          raise ExchangeError(self.id + " withdraw requires a 'key' parameter(withdrawal key name, as set up on your account)")
  
      def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
          url = '/' + self.version + '/' + api + '/' + path
          if api == 'public':
              if params:
                  url += '?' + self.urlencode(params)
          elif api == 'private':
              self.check_required_credentials()
              nonce = str(self.nonce())
              body = self.urlencode(self.extend({'nonce': nonce}, params))
              auth = self.encode(nonce + body)
              hash = self.hash(auth, 'sha256', 'binary')
              binary = self.encode(url)
              binhash = self.binary_concat(binary, hash)
              secret = base64.b64decode(self.secret)
              signature = self.hmac(binhash, secret, hashlib.sha512, 'base64')
              headers = {
                  'API-Key': self.apiKey,
                  'API-Sign': self.decode(signature),
                  'Content-Type': 'application/x-www-form-urlencoded',
              }
          else:
              url = '/' + path
          url = self.urls['api'][api] + url
          return {'url': url, 'method': method, 'body': body, 'headers': headers}
  
      def nonce(self):
          return self.milliseconds()
  
      def handle_errors(self, code, reason, url, method, headers, body, response):
          if code == 520:
              raise ExchangeNotAvailable(self.id + ' ' + str(code) + ' ' + reason)
          # todo: rewrite self for "broad" exceptions matching
          if body.find('Invalid order') >= 0:
              raise InvalidOrder(self.id + ' ' + body)
          if body.find('Invalid nonce') >= 0:
              raise InvalidNonce(self.id + ' ' + body)
          if body.find('Insufficient funds') >= 0:
              raise InsufficientFunds(self.id + ' ' + body)
          if body.find('Cancel pending') >= 0:
              raise CancelPending(self.id + ' ' + body)
          if body.find('Invalid arguments:volume') >= 0:
              raise InvalidOrder(self.id + ' ' + body)
          if body[0] == '{':
              if not isinstance(response, basestring):
                  if 'error' in response:
                      numErrors = len(response['error'])
                      if numErrors:
                          message = self.id + ' ' + self.json(response)
                          for i in range(0, len(response['error'])):
                              if response['error'][i] in self.exceptions:
                                  raise self.exceptions[response['error'][i]](message)
                          raise ExchangeError(message)
  