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.base.exchange import Exchange
  import hashlib
  import math
  from ccxt.base.errors import ExchangeError
  from ccxt.base.errors import AuthenticationError
  from ccxt.base.errors import PermissionDenied
  from ccxt.base.errors import AccountSuspended
  from ccxt.base.errors import InsufficientFunds
  from ccxt.base.errors import InvalidOrder
  from ccxt.base.errors import OrderNotFound
  from ccxt.base.errors import OrderNotCached
  from ccxt.base.errors import CancelPending
  from ccxt.base.errors import DDoSProtection
  from ccxt.base.errors import ExchangeNotAvailable
  from ccxt.base.errors import InvalidNonce
  from ccxt.base.errors import RequestTimeout
  
  
  class poloniex (Exchange):
  
      def describe(self):
          return self.deep_extend(super(poloniex, self).describe(), {
              'id': 'poloniex',
              'name': 'Poloniex',
              'countries': ['US'],
              'rateLimit': 1000,  # up to 6 calls per second
              'has': {
                  'CORS': False,
                  'createDepositAddress': True,
                  'createMarketOrder': False,
                  'editOrder': True,
                  'fetchClosedOrders': 'emulated',
                  'fetchCurrencies': True,
                  'fetchDepositAddress': True,
                  'fetchDeposits': 'emulated',
                  'fetchMyTrades': True,
                  'fetchOHLCV': True,
                  'fetchOpenOrder': True,  # True endpoint for a single open order
                  'fetchOpenOrders': True,  # True endpoint for open orders
                  'fetchOrder': 'emulated',  # no endpoint for a single open-or-closed order(just for an open order only)
                  'fetchOrderBooks': True,
                  'fetchOrders': 'emulated',  # no endpoint for open-or-closed orders(just for open orders only)
                  'fetchOrderStatus': 'emulated',  # no endpoint for status of a single open-or-closed order(just for open orders only)
                  'fetchOrderTrades': True,  # True endpoint for trades of a single open or closed order
                  'fetchTickers': True,
                  'fetchTradingFees': True,
                  'fetchTransactions': True,
                  'fetchWithdrawals': 'emulated',  # but almost True )
                  'withdraw': True,
              },
              'timeframes': {
                  '5m': 300,
                  '15m': 900,
                  '30m': 1800,
                  '2h': 7200,
                  '4h': 14400,
                  '1d': 86400,
              },
              'urls': {
                  'logo': 'https://user-images.githubusercontent.com/1294454/27766817-e9456312-5ee6-11e7-9b3c-b628ca5626a5.jpg',
                  'api': {
                      'public': 'https://poloniex.com/public',
                      'private': 'https://poloniex.com/tradingApi',
                  },
                  'www': 'https://poloniex.com',
                  'doc': [
                      'https://docs.poloniex.com',
                      'https://pastebin.com/dMX7mZE0',
                  ],
                  'fees': 'https://poloniex.com/fees',
              },
              'api': {
                  'public': {
                      'get': [
                          'return24hVolume',
                          'returnChartData',
                          'returnCurrencies',
                          'returnLoanOrders',
                          'returnOrderBook',
                          'returnTicker',
                          'returnTradeHistory',
                      ],
                  },
                  'private': {
                      'post': [
                          'buy',
                          'cancelLoanOffer',
                          'cancelOrder',
                          'closeMarginPosition',
                          'createLoanOffer',
                          'generateNewAddress',
                          'getMarginPosition',
                          'marginBuy',
                          'marginSell',
                          'moveOrder',
                          'returnActiveLoans',
                          'returnAvailableAccountBalances',
                          'returnBalances',
                          'returnCompleteBalances',
                          'returnDepositAddresses',
                          'returnDepositsWithdrawals',
                          'returnFeeInfo',
                          'returnLendingHistory',
                          'returnMarginAccountSummary',
                          'returnOpenLoanOffers',
                          'returnOpenOrders',
                          'returnOrderTrades',
                          'returnTradableBalances',
                          'returnTradeHistory',
                          'sell',
                          'toggleAutoRenew',
                          'transferBalance',
                          'withdraw',
                      ],
                  },
              },
              # Fees are tier-based. More info: https://poloniex.com/fees/
              # Rates below are highest possible.
              'fees': {
                  'trading': {
                      'maker': 0.001,
                      'taker': 0.002,
                  },
                  'funding': {},
              },
              'limits': {
                  'amount': {
                      'min': 0.00000001,
                      'max': 1000000000,
                  },
                  'price': {
                      'min': 0.00000001,
                      'max': 1000000000,
                  },
                  'cost': {
                      'min': 0.00000000,
                      'max': 1000000000,
                  },
              },
              'precision': {
                  'amount': 8,
                  'price': 8,
              },
              'commonCurrencies': {
                  'AIR': 'AirCoin',
                  'APH': 'AphroditeCoin',
                  'BCC': 'BTCtalkcoin',
                  'BDG': 'Badgercoin',
                  'BTM': 'Bitmark',
                  'CON': 'Coino',
                  'GOLD': 'GoldEagles',
                  'GPUC': 'GPU',
                  'HOT': 'Hotcoin',
                  'ITC': 'Information Coin',
                  'PLX': 'ParallaxCoin',
                  'KEY': 'KEYCoin',
                  'STR': 'XLM',
                  'SOC': 'SOCC',
                  'XAP': 'API Coin',
              },
              'options': {
                  'limits': {
                      'cost': {
                          'min': {
                              'BTC': 0.0001,
                              'ETH': 0.0001,
                              'XMR': 0.0001,
                              'USDT': 1.0,
                          },
                      },
                  },
              },
              'exceptions': {
                  'exact': {
                      'You may only place orders that reduce your position.': InvalidOrder,
                      'Invalid order number, or you are not the person who placed the order.': OrderNotFound,
                      'Permission denied': PermissionDenied,
                      'Connection timed out. Please try again.': RequestTimeout,
                      'Internal error. Please try again.': ExchangeNotAvailable,
                      'Order not found, or you are not the person who placed it.': OrderNotFound,
                      'Invalid API key/secret pair.': AuthenticationError,
                      'Please do not make more than 8 API calls per second.': DDoSProtection,
                      'Rate must be greater than zero.': InvalidOrder,  # {"error":"Rate must be greater than zero."}
                  },
                  'broad': {
                      'Total must be at least': InvalidOrder,  # {"error":"Total must be at least 0.0001."}
                      'This account is frozen.': AccountSuspended,
                      'Not enough': InsufficientFunds,
                      'Nonce must be greater': InvalidNonce,
                      'You have already called cancelOrder or moveOrder on self order.': CancelPending,
                      'Amount must be at least': InvalidOrder,  # {"error":"Amount must be at least 0.000001."}
                  },
              },
          })
  
      def calculate_fee(self, symbol, type, side, amount, price, takerOrMaker='taker', params={}):
          market = self.markets[symbol]
          key = 'quote'
          rate = market[takerOrMaker]
          cost = float(self.cost_to_precision(symbol, amount * rate))
          if side == 'sell':
              cost *= price
          else:
              key = 'base'
          return {
              'type': takerOrMaker,
              'currency': market[key],
              'rate': rate,
              'cost': float(self.fee_to_precision(symbol, cost)),
          }
  
      def parse_ohlcv(self, ohlcv, market=None, timeframe='5m', since=None, limit=None):
          return [
              ohlcv['date'] * 1000,
              ohlcv['open'],
              ohlcv['high'],
              ohlcv['low'],
              ohlcv['close'],
              ohlcv['quoteVolume'],
          ]
  
      def fetch_ohlcv(self, symbol, timeframe='5m', since=None, limit=None, params={}):
          self.load_markets()
          market = self.market(symbol)
          if since is None:
              since = 0
          request = {
              'currencyPair': market['id'],
              'period': self.timeframes[timeframe],
              'start': int(since / 1000),
          }
          if limit is not None:
              request['end'] = self.sum(request['start'], limit * self.timeframes[timeframe])
          response = self.publicGetReturnChartData(self.extend(request, params))
          return self.parse_ohlcvs(response, market, timeframe, since, limit)
  
      def fetch_markets(self, params={}):
          markets = self.publicGetReturnTicker()
          keys = list(markets.keys())
          result = []
          for p in range(0, len(keys)):
              id = keys[p]
              market = markets[id]
              quoteId, baseId = id.split('_')
              base = self.common_currency_code(baseId)
              quote = self.common_currency_code(quoteId)
              symbol = base + '/' + quote
              minCost = self.safe_float(self.options['limits']['cost']['min'], quote, 0.0)
              precision = {
                  'amount': 6,
                  'price': 8,
              }
              result.append(self.extend(self.fees['trading'], {
                  'id': id,
                  'symbol': symbol,
                  'baseId': baseId,
                  'quoteId': quoteId,
                  'base': base,
                  'quote': quote,
                  'active': market['isFrozen'] != '1',
                  'precision': precision,
                  'limits': {
                      'amount': {
                          'min': math.pow(10, -precision['amount']),
                          'max': None,
                      },
                      'price': {
                          'min': math.pow(10, -precision['price']),
                          'max': None,
                      },
                      'cost': {
                          'min': minCost,
                          'max': None,
                      },
                  },
                  'info': market,
              }))
          return result
  
      def fetch_balance(self, params={}):
          self.load_markets()
          balances = self.privatePostReturnCompleteBalances(self.extend({
              'account': 'all',
          }, params))
          result = {'info': balances}
          currencies = list(balances.keys())
          for c in range(0, len(currencies)):
              id = currencies[c]
              balance = balances[id]
              currency = self.common_currency_code(id)
              account = {
                  'free': float(balance['available']),
                  'used': float(balance['onOrders']),
                  'total': 0.0,
              }
              account['total'] = self.sum(account['free'], account['used'])
              result[currency] = account
          return self.parse_balance(result)
  
      def fetch_trading_fees(self, params={}):
          self.load_markets()
          fees = self.privatePostReturnFeeInfo()
          return {
              'info': fees,
              'maker': self.safe_float(fees, 'makerFee'),
              'taker': self.safe_float(fees, 'takerFee'),
              'withdraw': {},
              'deposit': {},
          }
  
      def fetch_order_book(self, symbol, limit=None, params={}):
          self.load_markets()
          request = {
              'currencyPair': self.market_id(symbol),
          }
          if limit is not None:
              request['depth'] = limit  # 100
          response = self.publicGetReturnOrderBook(self.extend(request, params))
          orderbook = self.parse_order_book(response)
          orderbook['nonce'] = self.safe_integer(response, 'seq')
          return orderbook
  
      def fetch_order_books(self, symbols=None, params={}):
          self.load_markets()
          request = {
              'currencyPair': 'all',
          }
          #
          #     if limit is not None:
          #         request['depth'] = limit  # 100
          #     }
          #
          response = self.publicGetReturnOrderBook(self.extend(request, params))
          marketIds = list(response.keys())
          result = {}
          for i in range(0, len(marketIds)):
              marketId = marketIds[i]
              symbol = None
              if marketId in self.markets_by_id:
                  symbol = self.markets_by_id[marketId]['symbol']
              else:
                  quoteId, baseId = marketId.split('_')
                  base = self.common_currency_code(baseId)
                  quote = self.common_currency_code(quoteId)
                  symbol = base + '/' + quote
              orderbook = self.parse_order_book(response[marketId])
              orderbook['nonce'] = self.safe_integer(response[marketId], 'seq')
              result[symbol] = orderbook
          return result
  
      def parse_ticker(self, ticker, market=None):
          timestamp = self.milliseconds()
          symbol = None
          if market:
              symbol = market['symbol']
          open = None
          change = None
          average = None
          last = self.safe_float(ticker, 'last')
          relativeChange = self.safe_float(ticker, 'percentChange')
          if relativeChange != -1:
              open = last / self.sum(1, relativeChange)
              change = last - open
              average = self.sum(last, open) / 2
          return {
              'symbol': symbol,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'high': self.safe_float(ticker, 'high24hr'),
              'low': self.safe_float(ticker, 'low24hr'),
              'bid': self.safe_float(ticker, 'highestBid'),
              'bidVolume': None,
              'ask': self.safe_float(ticker, 'lowestAsk'),
              'askVolume': None,
              'vwap': None,
              'open': open,
              'close': last,
              'last': last,
              'previousClose': None,
              'change': change,
              'percentage': relativeChange * 100,
              'average': average,
              'baseVolume': self.safe_float(ticker, 'quoteVolume'),
              'quoteVolume': self.safe_float(ticker, 'baseVolume'),
              'info': ticker,
          }
  
      def fetch_tickers(self, symbols=None, params={}):
          self.load_markets()
          tickers = self.publicGetReturnTicker(params)
          ids = list(tickers.keys())
          result = {}
          for i in range(0, len(ids)):
              id = ids[i]
              symbol = None
              market = None
              if id in self.markets_by_id:
                  market = self.markets_by_id[id]
                  symbol = market['symbol']
              else:
                  quoteId, baseId = id.split('_')
                  base = self.common_currency_code(baseId)
                  quote = self.common_currency_code(quoteId)
                  symbol = base + '/' + quote
                  market = {'symbol': symbol}
              ticker = tickers[id]
              result[symbol] = self.parse_ticker(ticker, market)
          return result
  
      def fetch_currencies(self, params={}):
          currencies = self.publicGetReturnCurrencies(params)
          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
              # to add support for multiple withdrawal/deposit methods and
              # differentiated fees for each particular method
              precision = 8  # default precision, todo: fix "magic constants"
              code = self.common_currency_code(id)
              active = (currency['delisted'] == 0) and not currency['disabled']
              result[code] = {
                  'id': id,
                  'code': code,
                  'info': currency,
                  'name': currency['name'],
                  'active': active,
                  'fee': self.safe_float(currency, 'txFee'),  # todo: redesign
                  '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': currency['txFee'],
                          'max': math.pow(10, precision),
                      },
                  },
              }
          return result
  
      def fetch_ticker(self, symbol, params={}):
          self.load_markets()
          market = self.market(symbol)
          tickers = self.publicGetReturnTicker(params)
          ticker = tickers[market['id']]
          return self.parse_ticker(ticker, market)
  
      def parse_trade(self, trade, market=None):
          timestamp = self.parse8601(trade['date'])
          symbol = None
          base = None
          quote = None
          if (not market) and('currencyPair' in list(trade.keys())):
              currencyPair = trade['currencyPair']
              if currencyPair in self.markets_by_id:
                  market = self.markets_by_id[currencyPair]
              else:
                  parts = currencyPair.split('_')
                  quote = parts[0]
                  base = parts[1]
                  symbol = base + '/' + quote
          if market is not None:
              symbol = market['symbol']
              base = market['base']
              quote = market['quote']
          side = trade['type']
          fee = None
          cost = self.safe_float(trade, 'total')
          amount = self.safe_float(trade, 'amount')
          if 'fee' in trade:
              rate = self.safe_float(trade, 'fee')
              feeCost = None
              currency = None
              if side == 'buy':
                  currency = base
                  feeCost = amount * rate
              else:
                  currency = quote
                  if cost is not None:
                      feeCost = cost * rate
              fee = {
                  'type': None,
                  'rate': rate,
                  'cost': feeCost,
                  'currency': currency,
              }
          return {
              'info': trade,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'symbol': symbol,
              'id': self.safe_string(trade, 'tradeID'),
              'order': self.safe_string(trade, 'orderNumber'),
              'type': 'limit',
              'side': side,
              'price': self.safe_float(trade, 'rate'),
              'amount': amount,
              'cost': cost,
              'fee': fee,
          }
  
      def fetch_trades(self, symbol, since=None, limit=None, params={}):
          self.load_markets()
          market = self.market(symbol)
          request = {
              'currencyPair': market['id'],
          }
          if since is not None:
              request['start'] = int(since / 1000)
              request['end'] = self.seconds()  # last 50000 trades by default
          trades = self.publicGetReturnTradeHistory(self.extend(request, params))
          return self.parse_trades(trades, market, since, limit)
  
      def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
          self.load_markets()
          market = None
          if symbol is not None:
              market = self.market(symbol)
          pair = market['id'] if market else 'all'
          request = {'currencyPair': pair}
          if since is not None:
              request['start'] = int(since / 1000)
              request['end'] = self.seconds() + 1  # adding 1 is a fix for  #3411
          # limit is disabled(does not really work as expected)
          if limit is not None:
              request['limit'] = int(limit)
          response = self.privatePostReturnTradeHistory(self.extend(request, params))
          result = []
          if market is not None:
              result = self.parse_trades(response, market)
          else:
              if response:
                  ids = list(response.keys())
                  for i in range(0, len(ids)):
                      id = ids[i]
                      market = None
                      if id in self.markets_by_id:
                          market = self.markets_by_id[id]
                          trades = self.parse_trades(response[id], market)
                          for j in range(0, len(trades)):
                              result.append(trades[j])
                      else:
                          baseId, quoteId = id.split('_')
                          base = self.common_currency_code(baseId)
                          quote = self.common_currency_code(quoteId)
                          symbol = base + '/' + quote
                          trades = response[id]
                          for j in range(0, len(trades)):
                              result.append(self.extend(self.parse_trade(trades[j]), {
                                  'symbol': symbol,
                              }))
          return self.filter_by_since_limit(result, since, limit)
  
      def parse_order_status(self, status):
          statuses = {
              'Open': 'open',
              'Partially filled': 'open',
          }
          return self.safe_string(statuses, status, status)
  
      def parse_order(self, order, market=None):
          #
          # fetchOpenOrder
          #
          #     {
          #         status: 'Open',
          #         rate: '0.40000000',
          #         amount: '1.00000000',
          #         currencyPair: 'BTC_ETH',
          #         date: '2018-10-17 17:04:50',
          #         total: '0.40000000',
          #         type: 'buy',
          #         startingAmount: '1.00000',
          #     }
          #
          # fetchOpenOrders
          #
          #     {
          #         orderNumber: '514514894224',
          #         type: 'buy',
          #         rate: '0.00001000',
          #         startingAmount: '100.00000000',
          #         amount: '100.00000000',
          #         total: '0.00100000',
          #         date: '2018-10-23 17:38:53',
          #         margin: 0,
          #     }
          #
          timestamp = self.safe_integer(order, 'timestamp')
          if not timestamp:
              timestamp = self.parse8601(order['date'])
          trades = None
          if 'resultingTrades' in order:
              trades = self.parse_trades(order['resultingTrades'], market)
          symbol = None
          marketId = self.safe_string(order, 'currencyPair')
          market = self.safe_value(self.markets_by_id, marketId, market)
          if market is not None:
              symbol = market['symbol']
          price = self.safe_float_2(order, 'price', 'rate')
          remaining = self.safe_float(order, 'amount')
          amount = self.safe_float(order, 'startingAmount', remaining)
          filled = None
          cost = 0
          if amount is not None:
              if remaining is not None:
                  filled = amount - remaining
                  if price is not None:
                      cost = filled * price
          if filled is None:
              if trades is not None:
                  filled = 0
                  cost = 0
                  for i in range(0, len(trades)):
                      trade = trades[i]
                      tradeAmount = trade['amount']
                      tradePrice = trade['price']
                      filled = self.sum(filled, tradeAmount)
                      cost += tradePrice * tradeAmount
          status = self.parse_order_status(self.safe_string(order, 'status'))
          type = self.safe_string(order, 'type')
          side = self.safe_string(order, 'side', type)
          if type == side:
              type = None
          return {
              'info': order,
              'id': order['orderNumber'],
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'lastTradeTimestamp': None,
              'status': status,
              'symbol': symbol,
              'type': type,
              'side': side,
              'price': price,
              'cost': cost,
              'amount': amount,
              'filled': filled,
              'remaining': remaining,
              'trades': trades,
              'fee': None,
          }
  
      def parse_open_orders(self, orders, market, result):
          for i in range(0, len(orders)):
              order = orders[i]
              extended = self.extend(order, {
                  'status': 'open',
                  'type': 'limit',
                  'side': order['type'],
                  'price': order['rate'],
              })
              result.append(self.parse_order(extended, market))
          return result
  
      def fetch_orders(self, symbol=None, since=None, limit=None, params={}):
          self.load_markets()
          market = None
          if symbol is not None:
              market = self.market(symbol)
          pair = market['id'] if market else 'all'
          response = self.privatePostReturnOpenOrders(self.extend({
              'currencyPair': pair,
          }))
          openOrders = []
          if market is not None:
              openOrders = self.parse_open_orders(response, market, openOrders)
          else:
              marketIds = list(response.keys())
              for i in range(0, len(marketIds)):
                  marketId = marketIds[i]
                  orders = response[marketId]
                  m = self.markets_by_id[marketId]
                  openOrders = self.parse_open_orders(orders, m, openOrders)
          for j in range(0, len(openOrders)):
              self.orders[openOrders[j]['id']] = openOrders[j]
          openOrdersIndexedById = self.index_by(openOrders, 'id')
          cachedOrderIds = list(self.orders.keys())
          result = []
          for k in range(0, len(cachedOrderIds)):
              id = cachedOrderIds[k]
              if id in openOrdersIndexedById:
                  self.orders[id] = self.extend(self.orders[id], openOrdersIndexedById[id])
              else:
                  order = self.orders[id]
                  if order['status'] == 'open':
                      order = self.extend(order, {
                          'status': 'closed',
                          'cost': None,
                          'filled': order['amount'],
                          'remaining': 0.0,
                      })
                      if order['cost'] is None:
                          if order['filled'] is not None:
                              order['cost'] = order['filled'] * order['price']
                      self.orders[id] = order
              order = self.orders[id]
              if market is not None:
                  if order['symbol'] == symbol:
                      result.append(order)
              else:
                  result.append(order)
          return self.filter_by_since_limit(result, since, limit)
  
      def fetch_order(self, id, symbol=None, params={}):
          since = self.safe_value(params, 'since')
          limit = self.safe_value(params, 'limit')
          request = self.omit(params, ['since', 'limit'])
          orders = self.fetch_orders(symbol, since, limit, request)
          for i in range(0, len(orders)):
              if orders[i]['id'] == id:
                  return orders[i]
          raise OrderNotCached(self.id + ' order id ' + str(id) + ' is not in "open" state and not found in cache')
  
      def filter_orders_by_status(self, orders, status):
          result = []
          for i in range(0, len(orders)):
              if orders[i]['status'] == status:
                  result.append(orders[i])
          return result
  
      def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
          orders = self.fetch_orders(symbol, since, limit, params)
          return self.filter_orders_by_status(orders, 'open')
  
      def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
          orders = self.fetch_orders(symbol, since, limit, params)
          return self.filter_orders_by_status(orders, 'closed')
  
      def create_order(self, symbol, type, side, amount, price=None, params={}):
          if type == 'market':
              raise ExchangeError(self.id + ' allows limit orders only')
          self.load_markets()
          method = 'privatePost' + self.capitalize(side)
          market = self.market(symbol)
          request = {
              'currencyPair': market['id'],
              'rate': self.price_to_precision(symbol, price),
              'amount': self.amount_to_precision(symbol, amount),
          }
          response = getattr(self, method)(self.extend(request, params))
          timestamp = self.milliseconds()
          order = self.parse_order(self.extend({
              'timestamp': timestamp,
              'status': 'open',
              'type': type,
              'side': side,
              'price': price,
              'amount': amount,
          }, response), market)
          id = order['id']
          self.orders[id] = order
          return self.extend({'info': response}, order)
  
      def edit_order(self, id, symbol, type, side, amount, price=None, params={}):
          self.load_markets()
          price = float(price)
          request = {
              'orderNumber': id,
              'rate': self.price_to_precision(symbol, price),
          }
          if amount is not None:
              request['amount'] = self.amount_to_precision(symbol, amount)
          response = self.privatePostMoveOrder(self.extend(request, params))
          result = None
          if id in self.orders:
              self.orders[id]['status'] = 'canceled'
              newid = response['orderNumber']
              self.orders[newid] = self.extend(self.orders[id], {
                  'id': newid,
                  'price': price,
                  'status': 'open',
              })
              if amount is not None:
                  self.orders[newid]['amount'] = amount
              result = self.extend(self.orders[newid], {'info': response})
          else:
              market = None
              if symbol is not None:
                  market = self.market(symbol)
              result = self.parse_order(response, market)
              self.orders[result['id']] = result
          return result
  
      def cancel_order(self, id, symbol=None, params={}):
          self.load_markets()
          response = None
          try:
              response = self.privatePostCancelOrder(self.extend({
                  'orderNumber': id,
              }, params))
          except Exception as e:
              if isinstance(e, CancelPending):
                  # A request to cancel the order has been sent already.
                  # If we then attempt to cancel the order the second time
                  # before the first request is processed the exchange will
                  # raise a CancelPending exception. Poloniex won't show the
                  # order in the list of active(open) orders and the cached
                  # order will be marked as 'closed'(see  #1801 for details).
                  # To avoid that we proactively mark the order as 'canceled'
                  # here. If for some reason the order does not get canceled
                  # and still appears in the active list then the order cache
                  # will eventually get back in sync on a call to `fetchOrder`.
                  if id in self.orders:
                      self.orders[id]['status'] = 'canceled'
              raise e
          if id in self.orders:
              self.orders[id]['status'] = 'canceled'
          return response
  
      def fetch_open_order(self, id, symbol=None, params={}):
          self.load_markets()
          id = str(id)
          response = self.privatePostReturnOrderStatus(self.extend({
              'orderNumber': id,
          }, params))
          #
          #     {
          #         success: 1,
          #         result: {
          #             '6071071': {
          #                 status: 'Open',
          #                 rate: '0.40000000',
          #                 amount: '1.00000000',
          #                 currencyPair: 'BTC_ETH',
          #                 date: '2018-10-17 17:04:50',
          #                 total: '0.40000000',
          #                 type: 'buy',
          #                 startingAmount: '1.00000',
          #             },
          #         },
          #     }
          #
          result = self.safe_value(response['result'], id)
          if result is None:
              raise OrderNotFound(self.id + ' order id ' + id + ' not found')
          order = self.parse_order(result)
          order['id'] = id
          self.orders[id] = order
          return order
  
      def fetch_order_status(self, id, symbol=None, params={}):
          self.load_markets()
          orders = self.fetch_open_orders(symbol, None, None, params)
          indexed = self.index_by(orders, 'id')
          return 'open' if (id in list(indexed.keys())) else 'closed'
  
      def fetch_order_trades(self, id, symbol=None, since=None, limit=None, params={}):
          self.load_markets()
          trades = self.privatePostReturnOrderTrades(self.extend({
              'orderNumber': id,
          }, params))
          return self.parse_trades(trades)
  
      def create_deposit_address(self, code, params={}):
          self.load_markets()
          currency = self.currency(code)
          response = self.privatePostGenerateNewAddress({
              'currency': currency['id'],
          })
          address = None
          tag = None
          if response['success'] == 1:
              address = self.safe_string(response, 'response')
          self.check_address(address)
          depositAddress = self.safe_string(currency['info'], 'depositAddress')
          if depositAddress is not None:
              tag = address
              address = depositAddress
          return {
              'currency': code,
              'address': address,
              'tag': tag,
              'info': response,
          }
  
      def fetch_deposit_address(self, code, params={}):
          self.load_markets()
          currency = self.currency(code)
          response = self.privatePostReturnDepositAddresses()
          currencyId = currency['id']
          address = self.safe_string(response, currencyId)
          tag = None
          self.check_address(address)
          depositAddress = self.safe_string(currency['info'], 'depositAddress')
          if depositAddress is not None:
              tag = address
              address = depositAddress
          return {
              'currency': code,
              'address': address,
              'tag': tag,
              'info': response,
          }
  
      def withdraw(self, code, amount, address, tag=None, params={}):
          self.check_address(address)
          self.load_markets()
          currency = self.currency(code)
          request = {
              'currency': currency['id'],
              'amount': amount,
              'address': address,
          }
          if tag:
              request['paymentId'] = tag
          result = self.privatePostWithdraw(self.extend(request, params))
          return {
              'info': result,
              'id': result['response'],
          }
  
      def fetch_transactions_helper(self, code=None, since=None, limit=None, params={}):
          self.load_markets()
          year = 31104000  # 60 * 60 * 24 * 30 * 12 = one year of history, why not
          now = self.seconds()
          start = int(since / 1000) if (since is not None) else now - 10 * year
          request = {
              'start': start,  # UNIX timestamp, required
              'end': now,  # UNIX timestamp, required
          }
          if limit is not None:
              request['limit'] = limit
          response = self.privatePostReturnDepositsWithdrawals(self.extend(request, params))
          #
          #     {   deposits: [{     currency: "BTC",
          #                              address: "1MEtiqJWru53FhhHrfJPPvd2tC3TPDVcmW",
          #                               amount: "0.01063000",
          #                        confirmations:  1,
          #                                 txid: "952b0e1888d6d491591facc0d37b5ebec540ac1efb241fdbc22bcc20d1822fb6",
          #                            timestamp:  1507916888,
          #                               status: "COMPLETE"                                                          },
          #                      {     currency: "ETH",
          #                              address: "0x20108ba20b65c04d82909e91df06618107460197",
          #                               amount: "4.00000000",
          #                        confirmations:  38,
          #                                 txid: "0x4be260073491fe63935e9e0da42bd71138fdeb803732f41501015a2d46eb479d",
          #                            timestamp:  1525060430,
          #                               status: "COMPLETE"                                                            }  ],
          #       withdrawals: [{withdrawalNumber:  8224394,
          #                                currency: "EMC2",
          #                                 address: "EYEKyCrqTNmVCpdDV8w49XvSKRP9N3EUyF",
          #                                  amount: "63.10796020",
          #                                     fee: "0.01000000",
          #                               timestamp:  1510819838,
          #                                  status: "COMPLETE: d37354f9d02cb24d98c8c4fc17aa42f475530b5727effdf668ee5a43ce667fd6",
          #                               ipAddress: "5.220.220.200"                                                               },
          #                      {withdrawalNumber:  9290444,
          #                                currency: "ETH",
          #                                 address: "0x191015ff2e75261d50433fbd05bd57e942336149",
          #                                  amount: "0.15500000",
          #                                     fee: "0.00500000",
          #                               timestamp:  1514099289,
          #                                  status: "COMPLETE: 0x12d444493b4bca668992021fd9e54b5292b8e71d9927af1f076f554e4bea5b2d",
          #                               ipAddress: "5.228.227.214"                                                                 },
          #                      {withdrawalNumber:  11518260,
          #                                currency: "BTC",
          #                                 address: "8JoDXAmE1GY2LRK8jD1gmAmgRPq54kXJ4t",
          #                                  amount: "0.20000000",
          #                                     fee: "0.00050000",
          #                               timestamp:  1527918155,
          #                                  status: "COMPLETE: 1864f4ebb277d90b0b1ff53259b36b97fa1990edc7ad2be47c5e0ab41916b5ff",
          #                               ipAddress: "211.8.195.26"                                                                }    ]}
          #
          return response
  
      def fetch_transactions(self, code=None, since=None, limit=None, params={}):
          self.load_markets()
          response = self.fetch_transactions_helper(code, since, limit, params)
          for i in range(0, len(response['deposits'])):
              response['deposits'][i]['type'] = 'deposit'
          for i in range(0, len(response['withdrawals'])):
              response['withdrawals'][i]['type'] = 'withdrawal'
          currency = None
          if code is not None:
              currency = self.currency(code)
          withdrawals = self.parseTransactions(response['withdrawals'], currency, since, limit)
          deposits = self.parseTransactions(response['deposits'], currency, since, limit)
          transactions = self.array_concat(deposits, withdrawals)
          return self.filterByCurrencySinceLimit(self.sort_by(transactions, 'timestamp'), code, since, limit)
  
      def fetch_withdrawals(self, code=None, since=None, limit=None, params={}):
          response = self.fetch_transactions_helper(code, since, limit, params)
          for i in range(0, len(response['withdrawals'])):
              response['withdrawals'][i]['type'] = 'withdrawal'
          currency = None
          if code is not None:
              currency = self.currency(code)
          withdrawals = self.parseTransactions(response['withdrawals'], currency, since, limit)
          return self.filterByCurrencySinceLimit(withdrawals, code, since, limit)
  
      def fetch_deposits(self, code=None, since=None, limit=None, params={}):
          response = self.fetch_transactions_helper(code, since, limit, params)
          for i in range(0, len(response['deposits'])):
              response['deposits'][i]['type'] = 'deposit'
          currency = None
          if code is not None:
              currency = self.currency(code)
          deposits = self.parseTransactions(response['deposits'], currency, since, limit)
          return self.filterByCurrencySinceLimit(deposits, code, since, limit)
  
      def parse_transaction_status(self, status):
          statuses = {
              'COMPLETE': 'ok',
          }
          return self.safe_string(statuses, status, status)
  
      def parse_transaction(self, transaction, currency=None):
          #
          # deposits
          #
          #      {     currency: "BTC",
          #              address: "1MEtiqJWru53FhhHrfJPPvd2tC3TPDVcmW",
          #               amount: "0.01063000",
          #        confirmations:  1,
          #                 txid: "6b2b0e1888d6d491591facc0d37b5ebec540ac1efb241fdbc22bcc20d1822fb6",
          #            timestamp:  1507916888,
          #               status: "COMPLETE"                                                          }
          #
          # withdrawals
          #
          #      {withdrawalNumber:  9290444,
          #                currency: "ETH",
          #                 address: "0x731015ff2e75261d50433fbd05bd57e942336149",
          #                  amount: "0.15500000",
          #                     fee: "0.00500000",
          #               timestamp:  1514099289,
          #                  status: "COMPLETE: 0x74d444493b4bca668992021fd9e54b5292b8e71d9927af1f076f554e4bea5b2d",
          #               ipAddress: "5.228.227.214"                                                                 },
          #
          timestamp = self.safe_integer(transaction, 'timestamp')
          if timestamp is not None:
              timestamp = timestamp * 1000
          code = None
          currencyId = self.safe_string(transaction, 'currency')
          currency = self.safe_value(self.currencies_by_id, currencyId)
          if currency is None:
              code = self.common_currency_code(currencyId)
          if currency is not None:
              code = currency['code']
          status = self.safe_string(transaction, 'status', 'pending')
          txid = self.safe_string(transaction, 'txid')
          if status is not None:
              parts = status.split(': ')
              numParts = len(parts)
              status = parts[0]
              if (numParts > 1) and(txid is None):
                  txid = parts[1]
              status = self.parse_transaction_status(status)
          id = self.safe_string(transaction, 'withdrawalNumber')
          type = 'withdrawal' if (id is not None) else 'deposit'
          amount = self.safe_float(transaction, 'amount')
          address = self.safe_string(transaction, 'address')
          feeCost = self.safe_float(transaction, 'fee')
          if feeCost is None:
              # according to https://poloniex.com/fees/
              feeCost = 0  # FIXME: remove hardcoded value that may change any time
          if type == 'withdrawal':
              # poloniex withdrawal amount includes the fee
              amount = amount - feeCost
          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 nonce(self):
          return self.milliseconds()
  
      def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
          url = self.urls['api'][api]
          query = self.extend({'command': path}, params)
          if api == 'public':
              url += '?' + self.urlencode(query)
          else:
              self.check_required_credentials()
              query['nonce'] = self.nonce()
              body = self.urlencode(query)
              headers = {
                  'Content-Type': 'application/x-www-form-urlencoded',
                  'Key': self.apiKey,
                  'Sign': self.hmac(self.encode(body), self.encode(self.secret), hashlib.sha512),
              }
          return {'url': url, 'method': method, 'body': body, 'headers': headers}
  
      def handle_errors(self, code, reason, url, method, headers, body, response):
          if response is None:
              return
          # {"error":"Permission denied."}
          if 'error' in response:
              message = response['error']
              feedback = self.id + ' ' + self.json(response)
              exact = self.exceptions['exact']
              if message in exact:
                  raise exact[message](feedback)
              broad = self.exceptions['broad']
              broadKey = self.findBroadlyMatchedKey(broad, message)
              if broadKey is not None:
                  raise broad[broadKey](feedback)
              raise ExchangeError(feedback)  # unknown message
  