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
  
  # -----------------------------------------------------------------------------
  
  try:
      basestring  # Python 3
  except NameError:
      basestring = str  # Python 2
  import json
  from ccxt.base.errors import ExchangeError
  from ccxt.base.errors import AuthenticationError
  from ccxt.base.errors import ArgumentsRequired
  from ccxt.base.errors import NullResponse
  from ccxt.base.errors import InsufficientFunds
  from ccxt.base.errors import InvalidOrder
  from ccxt.base.errors import OrderNotFound
  from ccxt.base.errors import NotSupported
  from ccxt.base.errors import DDoSProtection
  from ccxt.base.errors import RateLimitExceeded
  from ccxt.base.errors import InvalidNonce
  
  
  class cex(Exchange):
  
      def describe(self):
          return self.deep_extend(super(cex, self).describe(), {
              'id': 'cex',
              'name': 'CEX.IO',
              'countries': ['GB', 'EU', 'CY', 'RU'],
              'rateLimit': 1500,
              'has': {
                  'CORS': None,
                  'spot': True,
                  'margin': None,
                  'swap': None,
                  'future': None,
                  'option': None,
                  'cancelOrder': True,
                  'createOrder': True,
                  'editOrder': True,
                  'fetchBalance': True,
                  'fetchClosedOrders': True,
                  'fetchCurrencies': True,
                  'fetchDepositAddress': True,
                  'fetchMarkets': True,
                  'fetchOHLCV': True,
                  'fetchOpenOrders': True,
                  'fetchOrder': True,
                  'fetchOrderBook': True,
                  'fetchOrders': True,
                  'fetchTicker': True,
                  'fetchTickers': True,
                  'fetchTrades': True,
              },
              'timeframes': {
                  '1m': '1m',
                  '1h': '1h',
                  '1d': '1d',
              },
              'urls': {
                  'logo': 'https://user-images.githubusercontent.com/1294454/27766442-8ddc33b0-5ed8-11e7-8b98-f786aef0f3c9.jpg',
                  'api': 'https://cex.io/api',
                  'www': 'https://cex.io',
                  'doc': 'https://cex.io/cex-api',
                  'fees': [
                      'https://cex.io/fee-schedule',
                      'https://cex.io/limits-commissions',
                  ],
                  'referral': 'https://cex.io/r/0/up105393824/0/',
              },
              'requiredCredentials': {
                  'apiKey': True,
                  'secret': True,
                  'uid': True,
              },
              'api': {
                  'public': {
                      'get': [
                          'currency_profile',
                          'currency_limits/',
                          'last_price/{pair}/',
                          'last_prices/{currencies}/',
                          'ohlcv/hd/{yyyymmdd}/{pair}',
                          'order_book/{pair}/',
                          'ticker/{pair}/',
                          'tickers/{currencies}/',
                          'trade_history/{pair}/',
                      ],
                      'post': [
                          'convert/{pair}',
                          'price_stats/{pair}',
                      ],
                  },
                  'private': {
                      'post': [
                          'active_orders_status/',
                          'archived_orders/{pair}/',
                          'balance/',
                          'cancel_order/',
                          'cancel_orders/{pair}/',
                          'cancel_replace_order/{pair}/',
                          'close_position/{pair}/',
                          'get_address/',
                          'get_myfee/',
                          'get_order/',
                          'get_order_tx/',
                          'open_orders/{pair}/',
                          'open_orders/',
                          'open_position/{pair}/',
                          'open_positions/{pair}/',
                          'place_order/{pair}/',
                          'raw_tx_history',
                      ],
                  },
              },
              'fees': {
                  'trading': {
                      'maker': self.parse_number('0.0016'),
                      'taker': self.parse_number('0.0025'),
                  },
                  'funding': {
                      'withdraw': {},
                      'deposit': {
                          # 'USD': amount => amount * 0.035 + 0.25,
                          # 'EUR': amount => amount * 0.035 + 0.24,
                          # 'RUB': amount => amount * 0.05 + 15.57,
                          # 'GBP': amount => amount * 0.035 + 0.2,
                          'BTC': 0.0,
                          'ETH': 0.0,
                          'BCH': 0.0,
                          'DASH': 0.0,
                          'BTG': 0.0,
                          'ZEC': 0.0,
                          'XRP': 0.0,
                          'XLM': 0.0,
                      },
                  },
              },
              'exceptions': {
                  'exact': {},
                  'broad': {
                      'Insufficient funds': InsufficientFunds,
                      'Nonce must be incremented': InvalidNonce,
                      'Invalid Order': InvalidOrder,
                      'Order not found': OrderNotFound,
                      'limit exceeded': RateLimitExceeded,  # {"error":"rate limit exceeded"}
                      'Invalid API key': AuthenticationError,
                      'There was an error while placing your order': InvalidOrder,
                      'Sorry, too many clients already': DDoSProtection,
                  },
              },
              'options': {
                  'fetchOHLCVWarning': True,
                  'createMarketBuyOrderRequiresPrice': True,
                  'order': {
                      'status': {
                          'c': 'canceled',
                          'd': 'closed',
                          'cd': 'canceled',
                          'a': 'open',
                      },
                  },
              },
          })
  
      def fetch_currencies_from_cache(self, params={}):
          # self method is now redundant
          # currencies are now fetched before markets
          options = self.safe_value(self.options, 'fetchCurrencies', {})
          timestamp = self.safe_integer(options, 'timestamp')
          expires = self.safe_integer(options, 'expires', 1000)
          now = self.milliseconds()
          if (timestamp is None) or ((now - timestamp) > expires):
              response = self.publicGetCurrencyProfile(params)
              self.options['fetchCurrencies'] = self.extend(options, {
                  'response': response,
                  'timestamp': now,
              })
          return self.safe_value(self.options['fetchCurrencies'], 'response')
  
      def fetch_currencies(self, params={}):
          response = self.fetch_currencies_from_cache(params)
          self.options['currencies'] = {
              'timestamp': self.milliseconds(),
              'response': response,
          }
          #
          #     {
          #         "e":"currency_profile",
          #         "ok":"ok",
          #         "data":{
          #             "symbols":[
          #                 {
          #                     "code":"GHS",
          #                     "contract":true,
          #                     "commodity":true,
          #                     "fiat":false,
          #                     "description":"CEX.IO doesn't provide cloud mining services anymore.",
          #                     "precision":8,
          #                     "scale":0,
          #                     "minimumCurrencyAmount":"0.00000001",
          #                     "minimalWithdrawalAmount":-1
          #                 },
          #                 {
          #                     "code":"BTC",
          #                     "contract":false,
          #                     "commodity":false,
          #                     "fiat":false,
          #                     "description":"",
          #                     "precision":8,
          #                     "scale":0,
          #                     "minimumCurrencyAmount":"0.00000001",
          #                     "minimalWithdrawalAmount":0.002
          #                 },
          #                 {
          #                     "code":"ETH",
          #                     "contract":false,
          #                     "commodity":false,
          #                     "fiat":false,
          #                     "description":"",
          #                     "precision":8,
          #                     "scale":2,
          #                     "minimumCurrencyAmount":"0.00000100",
          #                     "minimalWithdrawalAmount":0.01
          #                 }
          #             ],
          #             "pairs":[
          #                 {
          #                     "symbol1":"BTC",
          #                     "symbol2":"USD",
          #                     "pricePrecision":1,
          #                     "priceScale":"/1000000",
          #                     "minLotSize":0.002,
          #                     "minLotSizeS2":20
          #                 },
          #                 {
          #                     "symbol1":"ETH",
          #                     "symbol2":"USD",
          #                     "pricePrecision":2,
          #                     "priceScale":"/10000",
          #                     "minLotSize":0.1,
          #                     "minLotSizeS2":20
          #                 }
          #             ]
          #         }
          #     }
          #
          data = self.safe_value(response, 'data', [])
          currencies = self.safe_value(data, 'symbols', [])
          result = {}
          for i in range(0, len(currencies)):
              currency = currencies[i]
              id = self.safe_string(currency, 'code')
              code = self.safe_currency_code(id)
              precision = self.safe_integer(currency, 'precision')
              active = True
              result[code] = {
                  'id': id,
                  'code': code,
                  'name': id,
                  'active': active,
                  'deposit': None,
                  'withdraw': None,
                  'precision': precision,
                  'fee': None,
                  'limits': {
                      'amount': {
                          'min': self.safe_number(currency, 'minimumCurrencyAmount'),
                          'max': None,
                      },
                      'withdraw': {
                          'min': self.safe_number(currency, 'minimalWithdrawalAmount'),
                          'max': None,
                      },
                  },
                  'info': currency,
              }
          return result
  
      def fetch_markets(self, params={}):
          currenciesResponse = self.fetch_currencies_from_cache(params)
          currenciesData = self.safe_value(currenciesResponse, 'data', {})
          currencies = self.safe_value(currenciesData, 'symbols', [])
          currenciesById = self.index_by(currencies, 'code')
          pairs = self.safe_value(currenciesData, 'pairs', [])
          response = self.publicGetCurrencyLimits(params)
          #
          #     {
          #         "e":"currency_limits",
          #         "ok":"ok",
          #         "data": {
          #             "pairs":[
          #                 {
          #                     "symbol1":"BTC",
          #                     "symbol2":"USD",
          #                     "minLotSize":0.002,
          #                     "minLotSizeS2":20,
          #                     "maxLotSize":30,
          #                     "minPrice":"1500",
          #                     "maxPrice":"35000"
          #                 },
          #                 {
          #                     "symbol1":"BCH",
          #                     "symbol2":"EUR",
          #                     "minLotSize":0.1,
          #                     "minLotSizeS2":20,
          #                     "maxLotSize":null,
          #                     "minPrice":"25",
          #                     "maxPrice":"8192"
          #                 }
          #             ]
          #         }
          #     }
          #
          result = []
          markets = self.safe_value(response['data'], 'pairs')
          for i in range(0, len(markets)):
              market = markets[i]
              baseId = self.safe_string(market, 'symbol1')
              quoteId = self.safe_string(market, 'symbol2')
              id = baseId + '/' + quoteId
              base = self.safe_currency_code(baseId)
              quote = self.safe_currency_code(quoteId)
              symbol = base + '/' + quote
              baseCurrency = self.safe_value(currenciesById, baseId, {})
              quoteCurrency = self.safe_value(currenciesById, quoteId, {})
              pricePrecision = self.safe_integer(quoteCurrency, 'precision', 8)
              for j in range(0, len(pairs)):
                  pair = pairs[j]
                  if (pair['symbol1'] == baseId) and (pair['symbol2'] == quoteId):
                      # we might need to account for `priceScale` here
                      pricePrecision = self.safe_integer(pair, 'pricePrecision', pricePrecision)
              baseCcyPrecision = self.safe_integer(baseCurrency, 'precision', 8)
              baseCcyScale = self.safe_integer(baseCurrency, 'scale', 0)
              amountPrecision = baseCcyPrecision - baseCcyScale
              precision = {
                  'amount': amountPrecision,
                  'price': pricePrecision,
              }
              result.append({
                  'id': id,
                  'info': market,
                  'symbol': symbol,
                  'base': base,
                  'quote': quote,
                  'baseId': baseId,
                  'quoteId': quoteId,
                  'type': 'spot',
                  'spot': True,
                  'margin': False,
                  'future': False,
                  'swap': False,
                  'option': False,
                  'optionType': None,
                  'strike': None,
                  'linear': None,
                  'inverse': None,
                  'contract': False,
                  'contractSize': None,
                  'settle': None,
                  'settleId': None,
                  'expiry': None,
                  'expiryDatetime': None,
                  'active': None,
                  'precision': precision,
                  'limits': {
                      'amount': {
                          'min': self.safe_number(market, 'minLotSize'),
                          'max': self.safe_number(market, 'maxLotSize'),
                      },
                      'price': {
                          'min': self.safe_number(market, 'minPrice'),
                          'max': self.safe_number(market, 'maxPrice'),
                      },
                      'cost': {
                          'min': self.safe_number(market, 'minLotSizeS2'),
                          'max': None,
                      },
                  },
              })
          return result
  
      def parse_balance(self, response):
          result = {'info': response}
          ommited = ['username', 'timestamp']
          balances = self.omit(response, ommited)
          currencyIds = list(balances.keys())
          for i in range(0, len(currencyIds)):
              currencyId = currencyIds[i]
              balance = self.safe_value(balances, currencyId, {})
              account = self.account()
              account['free'] = self.safe_string(balance, 'available')
              # https://github.com/ccxt/ccxt/issues/5484
              account['used'] = self.safe_string(balance, 'orders', '0')
              code = self.safe_currency_code(currencyId)
              result[code] = account
          return self.safe_balance(result)
  
      def fetch_balance(self, params={}):
          self.load_markets()
          response = self.privatePostBalance(params)
          return self.parse_balance(response)
  
      def fetch_order_book(self, symbol, limit=None, params={}):
          self.load_markets()
          request = {
              'pair': self.market_id(symbol),
          }
          if limit is not None:
              request['depth'] = limit
          response = self.publicGetOrderBookPair(self.extend(request, params))
          timestamp = self.safe_timestamp(response, 'timestamp')
          return self.parse_order_book(response, symbol, timestamp)
  
      def parse_ohlcv(self, ohlcv, market=None):
          #
          #     [
          #         1591403940,
          #         0.024972,
          #         0.024972,
          #         0.024969,
          #         0.024969,
          #         0.49999900
          #     ]
          #
          return [
              self.safe_timestamp(ohlcv, 0),
              self.safe_number(ohlcv, 1),
              self.safe_number(ohlcv, 2),
              self.safe_number(ohlcv, 3),
              self.safe_number(ohlcv, 4),
              self.safe_number(ohlcv, 5),
          ]
  
      def fetch_ohlcv(self, symbol, timeframe='1m', since=None, limit=None, params={}):
          self.load_markets()
          market = self.market(symbol)
          if since is None:
              since = self.milliseconds() - 86400000  # yesterday
          else:
              if self.options['fetchOHLCVWarning']:
                  raise ExchangeError(self.id + " fetchOHLCV warning: CEX can return historical candles for a certain date only, self might produce an empty or None reply. Set exchange.options['fetchOHLCVWarning'] = False or add({'options': {'fetchOHLCVWarning': False}}) to constructor params to suppress self warning message.")
          request = {
              'pair': market['id'],
              'yyyymmdd': self.yyyymmdd(since, ''),
          }
          try:
              response = self.publicGetOhlcvHdYyyymmddPair(self.extend(request, params))
              #
              #     {
              #         "time":20200606,
              #         "data1m":"[[1591403940,0.024972,0.024972,0.024969,0.024969,0.49999900]]",
              #     }
              #
              key = 'data' + self.timeframes[timeframe]
              data = self.safe_string(response, key)
              ohlcvs = json.loads(data)
              return self.parse_ohlcvs(ohlcvs, market, timeframe, since, limit)
          except Exception as e:
              if isinstance(e, NullResponse):
                  return []
  
      def parse_ticker(self, ticker, market=None):
          timestamp = self.safe_timestamp(ticker, 'timestamp')
          volume = self.safe_string(ticker, 'volume')
          high = self.safe_string(ticker, 'high')
          low = self.safe_string(ticker, 'low')
          bid = self.safe_string(ticker, 'bid')
          ask = self.safe_string(ticker, 'ask')
          last = self.safe_string(ticker, 'last')
          symbol = self.safe_symbol(None, market)
          return self.safe_ticker({
              'symbol': symbol,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'high': high,
              'low': low,
              'bid': bid,
              'bidVolume': None,
              'ask': ask,
              'askVolume': None,
              'vwap': None,
              'open': None,
              'close': last,
              'last': last,
              'previousClose': None,
              'change': None,
              'percentage': None,
              'average': None,
              'baseVolume': volume,
              'quoteVolume': None,
              'info': ticker,
          }, market, False)
  
      def fetch_tickers(self, symbols=None, params={}):
          self.load_markets()
          currencies = list(self.currencies.keys())
          request = {
              'currencies': '/'.join(currencies),
          }
          response = self.publicGetTickersCurrencies(self.extend(request, params))
          tickers = response['data']
          result = {}
          for t in range(0, len(tickers)):
              ticker = tickers[t]
              symbol = ticker['pair'].replace(':', '/')
              market = self.markets[symbol]
              result[symbol] = self.parse_ticker(ticker, market)
          return self.filter_by_array(result, 'symbol', symbols)
  
      def fetch_ticker(self, symbol, params={}):
          self.load_markets()
          market = self.market(symbol)
          request = {
              'pair': market['id'],
          }
          ticker = self.publicGetTickerPair(self.extend(request, params))
          return self.parse_ticker(ticker, market)
  
      def parse_trade(self, trade, market=None):
          #
          # fetchTrades(public)
          #
          #      {
          #          "type": "sell",
          #          "date": "1638401878",
          #          "amount": "0.401000",
          #          "price": "249",
          #          "tid": "11922"
          #      }
          #
          timestamp = self.safe_timestamp(trade, 'date')
          id = self.safe_string(trade, 'tid')
          type = None
          side = self.safe_string(trade, 'type')
          priceString = self.safe_string(trade, 'price')
          amountString = self.safe_string(trade, 'amount')
          symbol = None
          if market is not None:
              symbol = market['symbol']
          return self.safe_trade({
              'info': trade,
              'id': id,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'symbol': symbol,
              'type': type,
              'side': side,
              'order': None,
              'takerOrMaker': None,
              'price': priceString,
              'amount': amountString,
              'cost': None,
              'fee': None,
          }, market)
  
      def fetch_trades(self, symbol, since=None, limit=None, params={}):
          self.load_markets()
          market = self.market(symbol)
          request = {
              'pair': market['id'],
          }
          response = self.publicGetTradeHistoryPair(self.extend(request, params))
          return self.parse_trades(response, market, since, limit)
  
      def create_order(self, symbol, type, side, amount, price=None, params={}):
          # for market buy it requires the amount of quote currency to spend
          if (type == 'market') and (side == 'buy'):
              if self.options['createMarketBuyOrderRequiresPrice']:
                  if price is None:
                      raise InvalidOrder(self.id + " createOrder() requires the price argument with market buy orders to calculate total order cost(amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = False to supply the cost in the amount argument(the exchange-specific behaviour)")
                  else:
                      amount = amount * price
          self.load_markets()
          request = {
              'pair': self.market_id(symbol),
              'type': side,
              'amount': amount,
          }
          if type == 'limit':
              request['price'] = price
          else:
              request['order_type'] = type
          response = self.privatePostPlaceOrderPair(self.extend(request, params))
          #
          #     {
          #         "id": "12978363524",
          #         "time": 1586610022259,
          #         "type": "buy",
          #         "price": "0.033934",
          #         "amount": "0.10722802",
          #         "pending": "0.10722802",
          #         "complete": False
          #     }
          #
          placedAmount = self.safe_number(response, 'amount')
          remaining = self.safe_number(response, 'pending')
          timestamp = self.safe_value(response, 'time')
          complete = self.safe_value(response, 'complete')
          status = 'closed' if complete else 'open'
          filled = None
          if (placedAmount is not None) and (remaining is not None):
              filled = max(placedAmount - remaining, 0)
          return {
              'id': self.safe_string(response, 'id'),
              'info': response,
              'clientOrderId': None,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'lastTradeTimestamp': None,
              'type': type,
              'side': self.safe_string(response, 'type'),
              'symbol': symbol,
              'status': status,
              'price': self.safe_number(response, 'price'),
              'amount': placedAmount,
              'cost': None,
              'average': None,
              'remaining': remaining,
              'filled': filled,
              'fee': None,
              'trades': None,
          }
  
      def cancel_order(self, id, symbol=None, params={}):
          self.load_markets()
          request = {
              'id': id,
          }
          return self.privatePostCancelOrder(self.extend(request, params))
  
      def parse_order(self, order, market=None):
          # Depending on the call, 'time' can be a unix int, unix string or ISO string
          # Yes, really
          timestamp = self.safe_value(order, 'time')
          if isinstance(timestamp, basestring) and timestamp.find('T') >= 0:
              # ISO8601 string
              timestamp = self.parse8601(timestamp)
          else:
              # either integer or string integer
              timestamp = int(timestamp)
          symbol = None
          if market is None:
              baseId = self.safe_string(order, 'symbol1')
              quoteId = self.safe_string(order, 'symbol2')
              base = self.safe_currency_code(baseId)
              quote = self.safe_currency_code(quoteId)
              symbol = base + '/' + quote
              if symbol in self.markets:
                  market = self.market(symbol)
          status = self.parse_order_status(self.safe_string(order, 'status'))
          price = self.safe_number(order, 'price')
          amount = self.safe_number(order, 'amount')
          # sell orders can have a negative amount
          # https://github.com/ccxt/ccxt/issues/5338
          if amount is not None:
              amount = abs(amount)
          remaining = self.safe_number_2(order, 'pending', 'remains')
          filled = amount - remaining
          fee = None
          cost = None
          if market is not None:
              symbol = market['symbol']
              taCost = self.safe_number(order, 'ta:' + market['quote'])
              ttaCost = self.safe_number(order, 'tta:' + market['quote'])
              cost = self.sum(taCost, ttaCost)
              baseFee = 'fa:' + market['base']
              baseTakerFee = 'tfa:' + market['base']
              quoteFee = 'fa:' + market['quote']
              quoteTakerFee = 'tfa:' + market['quote']
              feeRate = self.safe_number(order, 'tradingFeeMaker')
              if not feeRate:
                  feeRate = self.safe_number(order, 'tradingFeeTaker', feeRate)
              if feeRate:
                  feeRate /= 100.0  # convert to mathematically-correct percentage coefficients: 1.0 = 100%
              if (baseFee in order) or (baseTakerFee in order):
                  baseFeeCost = self.safe_number_2(order, baseFee, baseTakerFee)
                  fee = {
                      'currency': market['base'],
                      'rate': feeRate,
                      'cost': baseFeeCost,
                  }
              elif (quoteFee in order) or (quoteTakerFee in order):
                  quoteFeeCost = self.safe_number_2(order, quoteFee, quoteTakerFee)
                  fee = {
                      'currency': market['quote'],
                      'rate': feeRate,
                      'cost': quoteFeeCost,
                  }
          if not cost:
              cost = price * filled
          side = order['type']
          trades = None
          orderId = order['id']
          if 'vtx' in order:
              trades = []
              for i in range(0, len(order['vtx'])):
                  item = order['vtx'][i]
                  tradeSide = self.safe_string(item, 'type')
                  if tradeSide == 'cancel':
                      # looks like self might represent the cancelled part of an order
                      #   {id: '4426729543',
                      #     type: 'cancel',
                      #     time: '2017-09-22T00:24:30.476Z',
                      #     user: 'up106404164',
                      #     c: 'user:up106404164:a:BCH',
                      #     d: 'order:4426728375:a:BCH',
                      #     a: '0.09935956',
                      #     amount: '0.09935956',
                      #     balance: '0.42580261',
                      #     symbol: 'BCH',
                      #     order: '4426728375',
                      #     buy: null,
                      #     sell: null,
                      #     pair: null,
                      #     pos: null,
                      #     cs: '0.42580261',
                      #     ds: 0}
                      continue
                  tradePrice = self.safe_number(item, 'price')
                  if tradePrice is None:
                      # self represents the order
                      #   {
                      #     "a": "0.47000000",
                      #     "c": "user:up106404164:a:EUR",
                      #     "d": "order:6065499239:a:EUR",
                      #     "cs": "1432.93",
                      #     "ds": "476.72",
                      #     "id": "6065499249",
                      #     "buy": null,
                      #     "pos": null,
                      #     "pair": null,
                      #     "sell": null,
                      #     "time": "2018-04-22T13:07:22.152Z",
                      #     "type": "buy",
                      #     "user": "up106404164",
                      #     "order": "6065499239",
                      #     "amount": "-715.97000000",
                      #     "symbol": "EUR",
                      #     "balance": "1432.93000000"}
                      continue
                  # todo: deal with these
                  if tradeSide == 'costsNothing':
                      continue
                  # --
                  # if side != tradeSide:
                  #     raise Error(json.dumps(order, null, 2))
                  # if orderId != item['order']:
                  #     raise Error(json.dumps(order, null, 2))
                  # --
                  # partial buy trade
                  #   {
                  #     "a": "0.01589885",
                  #     "c": "user:up106404164:a:BTC",
                  #     "d": "order:6065499239:a:BTC",
                  #     "cs": "0.36300000",
                  #     "ds": 0,
                  #     "id": "6067991213",
                  #     "buy": "6065499239",
                  #     "pos": null,
                  #     "pair": null,
                  #     "sell": "6067991206",
                  #     "time": "2018-04-22T23:09:11.773Z",
                  #     "type": "buy",
                  #     "user": "up106404164",
                  #     "order": "6065499239",
                  #     "price": 7146.5,
                  #     "amount": "0.01589885",
                  #     "symbol": "BTC",
                  #     "balance": "0.36300000",
                  #     "symbol2": "EUR",
                  #     "fee_amount": "0.19"}
                  # --
                  # trade with zero amount, but non-zero fee
                  #   {
                  #     "a": "0.00000000",
                  #     "c": "user:up106404164:a:EUR",
                  #     "d": "order:5840654423:a:EUR",
                  #     "cs": 559744,
                  #     "ds": 0,
                  #     "id": "5840654429",
                  #     "buy": "5807238573",
                  #     "pos": null,
                  #     "pair": null,
                  #     "sell": "5840654423",
                  #     "time": "2018-03-15T03:20:14.010Z",
                  #     "type": "sell",
                  #     "user": "up106404164",
                  #     "order": "5840654423",
                  #     "price": 730,
                  #     "amount": "0.00000000",
                  #     "symbol": "EUR",
                  #     "balance": "5597.44000000",
                  #     "symbol2": "BCH",
                  #     "fee_amount": "0.01"}
                  # --
                  # trade which should have an amount of exactly 0.002BTC
                  #   {
                  #     "a": "16.70000000",
                  #     "c": "user:up106404164:a:GBP",
                  #     "d": "order:9927386681:a:GBP",
                  #     "cs": "86.90",
                  #     "ds": 0,
                  #     "id": "9927401610",
                  #     "buy": "9927401601",
                  #     "pos": null,
                  #     "pair": null,
                  #     "sell": "9927386681",
                  #     "time": "2019-08-21T15:25:37.777Z",
                  #     "type": "sell",
                  #     "user": "up106404164",
                  #     "order": "9927386681",
                  #     "price": 8365,
                  #     "amount": "16.70000000",
                  #     "office": "UK",
                  #     "symbol": "GBP",
                  #     "balance": "86.90000000",
                  #     "symbol2": "BTC",
                  #     "fee_amount": "0.03"
                  #   }
                  tradeTimestamp = self.parse8601(self.safe_string(item, 'time'))
                  tradeAmount = self.safe_number(item, 'amount')
                  feeCost = self.safe_number(item, 'fee_amount')
                  absTradeAmount = -tradeAmount if (tradeAmount < 0) else tradeAmount
                  tradeCost = None
                  if tradeSide == 'sell':
                      tradeCost = absTradeAmount
                      absTradeAmount = self.sum(feeCost, tradeCost) / tradePrice
                  else:
                      tradeCost = absTradeAmount * tradePrice
                  trades.append({
                      'id': self.safe_string(item, 'id'),
                      'timestamp': tradeTimestamp,
                      'datetime': self.iso8601(tradeTimestamp),
                      'order': orderId,
                      'symbol': symbol,
                      'price': tradePrice,
                      'amount': absTradeAmount,
                      'cost': tradeCost,
                      'side': tradeSide,
                      'fee': {
                          'cost': feeCost,
                          'currency': market['quote'],
                      },
                      'info': item,
                      'type': None,
                      'takerOrMaker': None,
                  })
          return {
              'id': orderId,
              'clientOrderId': None,
              'datetime': self.iso8601(timestamp),
              'timestamp': timestamp,
              'lastTradeTimestamp': None,
              'status': status,
              'symbol': symbol,
              'type': 'market' if (price is None) else 'limit',
              'timeInForce': None,
              'postOnly': None,
              'side': side,
              'price': price,
              'stopPrice': None,
              'cost': cost,
              'amount': amount,
              'filled': filled,
              'remaining': remaining,
              'trades': trades,
              'fee': fee,
              'info': order,
              'average': None,
          }
  
      def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
          self.load_markets()
          request = {}
          method = 'privatePostOpenOrders'
          market = None
          if symbol is not None:
              market = self.market(symbol)
              request['pair'] = market['id']
              method += 'Pair'
          orders = getattr(self, method)(self.extend(request, params))
          for i in range(0, len(orders)):
              orders[i] = self.extend(orders[i], {'status': 'open'})
          return self.parse_orders(orders, market, since, limit)
  
      def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
          self.load_markets()
          method = 'privatePostArchivedOrdersPair'
          if symbol is None:
              raise ArgumentsRequired(self.id + ' fetchClosedOrders() requires a symbol argument')
          market = self.market(symbol)
          request = {'pair': market['id']}
          response = getattr(self, method)(self.extend(request, params))
          return self.parse_orders(response, market, since, limit)
  
      def fetch_order(self, id, symbol=None, params={}):
          self.load_markets()
          request = {
              'id': str(id),
          }
          response = self.privatePostGetOrderTx(self.extend(request, params))
          data = self.safe_value(response, 'data', {})
          #
          #     {
          #         "id": "5442731603",
          #         "type": "sell",
          #         "time": 1516132358071,
          #         "lastTxTime": 1516132378452,
          #         "lastTx": "5442734452",
          #         "pos": null,
          #         "user": "up106404164",
          #         "status": "d",
          #         "symbol1": "ETH",
          #         "symbol2": "EUR",
          #         "amount": "0.50000000",
          #         "kind": "api",
          #         "price": "923.3386",
          #         "tfacf": "1",
          #         "fa:EUR": "0.55",
          #         "ta:EUR": "369.77",
          #         "remains": "0.00000000",
          #         "tfa:EUR": "0.22",
          #         "tta:EUR": "91.95",
          #         "a:ETH:cds": "0.50000000",
          #         "a:EUR:cds": "461.72",
          #         "f:EUR:cds": "0.77",
          #         "tradingFeeMaker": "0.15",
          #         "tradingFeeTaker": "0.23",
          #         "tradingFeeStrategy": "userVolumeAmount",
          #         "tradingFeeUserVolumeAmount": "2896912572",
          #         "orderId": "5442731603",
          #         "next": False,
          #         "vtx": [
          #             {
          #                 "id": "5442734452",
          #                 "type": "sell",
          #                 "time": "2018-01-16T19:52:58.452Z",
          #                 "user": "up106404164",
          #                 "c": "user:up106404164:a:EUR",
          #                 "d": "order:5442731603:a:EUR",
          #                 "a": "104.53000000",
          #                 "amount": "104.53000000",
          #                 "balance": "932.71000000",
          #                 "symbol": "EUR",
          #                 "order": "5442731603",
          #                 "buy": "5442734443",
          #                 "sell": "5442731603",
          #                 "pair": null,
          #                 "pos": null,
          #                 "office": null,
          #                 "cs": "932.71",
          #                 "ds": 0,
          #                 "price": 923.3386,
          #                 "symbol2": "ETH",
          #                 "fee_amount": "0.16"
          #             },
          #             {
          #                 "id": "5442731609",
          #                 "type": "sell",
          #                 "time": "2018-01-16T19:52:38.071Z",
          #                 "user": "up106404164",
          #                 "c": "user:up106404164:a:EUR",
          #                 "d": "order:5442731603:a:EUR",
          #                 "a": "91.73000000",
          #                 "amount": "91.73000000",
          #                 "balance": "563.49000000",
          #                 "symbol": "EUR",
          #                 "order": "5442731603",
          #                 "buy": "5442618127",
          #                 "sell": "5442731603",
          #                 "pair": null,
          #                 "pos": null,
          #                 "office": null,
          #                 "cs": "563.49",
          #                 "ds": 0,
          #                 "price": 924.0092,
          #                 "symbol2": "ETH",
          #                 "fee_amount": "0.22"
          #             },
          #             {
          #                 "id": "5442731604",
          #                 "type": "sell",
          #                 "time": "2018-01-16T19:52:38.071Z",
          #                 "user": "up106404164",
          #                 "c": "order:5442731603:a:ETH",
          #                 "d": "user:up106404164:a:ETH",
          #                 "a": "0.50000000",
          #                 "amount": "-0.50000000",
          #                 "balance": "15.80995000",
          #                 "symbol": "ETH",
          #                 "order": "5442731603",
          #                 "buy": null,
          #                 "sell": null,
          #                 "pair": null,
          #                 "pos": null,
          #                 "office": null,
          #                 "cs": "0.50000000",
          #                 "ds": "15.80995000"
          #             }
          #         ]
          #     }
          #
          return self.parse_order(data)
  
      def fetch_orders(self, symbol=None, since=None, limit=None, params={}):
          self.load_markets()
          market = self.market(symbol)
          request = {
              'limit': limit,
              'pair': market['id'],
              'dateFrom': since,
          }
          response = self.privatePostArchivedOrdersPair(self.extend(request, params))
          results = []
          for i in range(0, len(response)):
              # cancelled(unfilled):
              #    {id: '4005785516',
              #     type: 'sell',
              #     time: '2017-07-18T19:08:34.223Z',
              #     lastTxTime: '2017-07-18T19:08:34.396Z',
              #     lastTx: '4005785522',
              #     pos: null,
              #     status: 'c',
              #     symbol1: 'ETH',
              #     symbol2: 'GBP',
              #     amount: '0.20000000',
              #     price: '200.5625',
              #     remains: '0.20000000',
              #     'a:ETH:cds': '0.20000000',
              #     tradingFeeMaker: '0',
              #     tradingFeeTaker: '0.16',
              #     tradingFeeUserVolumeAmount: '10155061217',
              #     orderId: '4005785516'}
              # --
              # cancelled(partially filled buy):
              #    {id: '4084911657',
              #     type: 'buy',
              #     time: '2017-08-05T03:18:39.596Z',
              #     lastTxTime: '2019-03-19T17:37:46.404Z',
              #     lastTx: '8459265833',
              #     pos: null,
              #     status: 'cd',
              #     symbol1: 'BTC',
              #     symbol2: 'GBP',
              #     amount: '0.05000000',
              #     price: '2241.4692',
              #     tfacf: '1',
              #     remains: '0.03910535',
              #     'tfa:GBP': '0.04',
              #     'tta:GBP': '24.39',
              #     'a:BTC:cds': '0.01089465',
              #     'a:GBP:cds': '112.26',
              #     'f:GBP:cds': '0.04',
              #     tradingFeeMaker: '0',
              #     tradingFeeTaker: '0.16',
              #     tradingFeeUserVolumeAmount: '13336396963',
              #     orderId: '4084911657'}
              # --
              # cancelled(partially filled sell):
              #    {id: '4426728375',
              #     type: 'sell',
              #     time: '2017-09-22T00:24:20.126Z',
              #     lastTxTime: '2017-09-22T00:24:30.476Z',
              #     lastTx: '4426729543',
              #     pos: null,
              #     status: 'cd',
              #     symbol1: 'BCH',
              #     symbol2: 'BTC',
              #     amount: '0.10000000',
              #     price: '0.11757182',
              #     tfacf: '1',
              #     remains: '0.09935956',
              #     'tfa:BTC': '0.00000014',
              #     'tta:BTC': '0.00007537',
              #     'a:BCH:cds': '0.10000000',
              #     'a:BTC:cds': '0.00007537',
              #     'f:BTC:cds': '0.00000014',
              #     tradingFeeMaker: '0',
              #     tradingFeeTaker: '0.18',
              #     tradingFeeUserVolumeAmount: '3466715450',
              #     orderId: '4426728375'}
              # --
              # filled:
              #    {id: '5342275378',
              #     type: 'sell',
              #     time: '2018-01-04T00:28:12.992Z',
              #     lastTxTime: '2018-01-04T00:28:12.992Z',
              #     lastTx: '5342275393',
              #     pos: null,
              #     status: 'd',
              #     symbol1: 'BCH',
              #     symbol2: 'BTC',
              #     amount: '0.10000000',
              #     kind: 'api',
              #     price: '0.17',
              #     remains: '0.00000000',
              #     'tfa:BTC': '0.00003902',
              #     'tta:BTC': '0.01699999',
              #     'a:BCH:cds': '0.10000000',
              #     'a:BTC:cds': '0.01699999',
              #     'f:BTC:cds': '0.00003902',
              #     tradingFeeMaker: '0.15',
              #     tradingFeeTaker: '0.23',
              #     tradingFeeUserVolumeAmount: '1525951128',
              #     orderId: '5342275378'}
              # --
              # market order(buy):
              #    {"id": "6281946200",
              #     "pos": null,
              #     "time": "2018-05-23T11:55:43.467Z",
              #     "type": "buy",
              #     "amount": "0.00000000",
              #     "lastTx": "6281946210",
              #     "status": "d",
              #     "amount2": "20.00",
              #     "orderId": "6281946200",
              #     "remains": "0.00000000",
              #     "symbol1": "ETH",
              #     "symbol2": "EUR",
              #     "tfa:EUR": "0.05",
              #     "tta:EUR": "19.94",
              #     "a:ETH:cds": "0.03764100",
              #     "a:EUR:cds": "20.00",
              #     "f:EUR:cds": "0.05",
              #     "lastTxTime": "2018-05-23T11:55:43.467Z",
              #     "tradingFeeTaker": "0.25",
              #     "tradingFeeUserVolumeAmount": "55998097"}
              # --
              # market order(sell):
              #   {"id": "6282200948",
              #     "pos": null,
              #     "time": "2018-05-23T12:42:58.315Z",
              #     "type": "sell",
              #     "amount": "-0.05000000",
              #     "lastTx": "6282200958",
              #     "status": "d",
              #     "orderId": "6282200948",
              #     "remains": "0.00000000",
              #     "symbol1": "ETH",
              #     "symbol2": "EUR",
              #     "tfa:EUR": "0.07",
              #     "tta:EUR": "26.49",
              #     "a:ETH:cds": "0.05000000",
              #     "a:EUR:cds": "26.49",
              #     "f:EUR:cds": "0.07",
              #     "lastTxTime": "2018-05-23T12:42:58.315Z",
              #     "tradingFeeTaker": "0.25",
              #     "tradingFeeUserVolumeAmount": "56294576"}
              order = response[i]
              status = self.parse_order_status(self.safe_string(order, 'status'))
              baseId = self.safe_string(order, 'symbol1')
              quoteId = self.safe_string(order, 'symbol2')
              base = self.safe_currency_code(baseId)
              quote = self.safe_currency_code(quoteId)
              symbol = base + '/' + quote
              side = self.safe_string(order, 'type')
              baseAmount = self.safe_number(order, 'a:' + baseId + ':cds')
              quoteAmount = self.safe_number(order, 'a:' + quoteId + ':cds')
              fee = self.safe_number(order, 'f:' + quoteId + ':cds')
              amount = self.safe_number(order, 'amount')
              price = self.safe_number(order, 'price')
              remaining = self.safe_number(order, 'remains')
              filled = amount - remaining
              orderAmount = None
              cost = None
              average = None
              type = None
              if not price:
                  type = 'market'
                  orderAmount = baseAmount
                  cost = quoteAmount
                  average = orderAmount / cost
              else:
                  ta = self.safe_number(order, 'ta:' + quoteId, 0)
                  tta = self.safe_number(order, 'tta:' + quoteId, 0)
                  fa = self.safe_number(order, 'fa:' + quoteId, 0)
                  tfa = self.safe_number(order, 'tfa:' + quoteId, 0)
                  if side == 'sell':
                      cost = self.sum(self.sum(ta, tta), self.sum(fa, tfa))
                  else:
                      cost = self.sum(ta, tta) - self.sum(fa, tfa)
                  type = 'limit'
                  orderAmount = amount
                  average = cost / filled
              time = self.safe_string(order, 'time')
              lastTxTime = self.safe_string(order, 'lastTxTime')
              timestamp = self.parse8601(time)
              results.append({
                  'id': self.safe_string(order, 'id'),
                  'timestamp': timestamp,
                  'datetime': self.iso8601(timestamp),
                  'lastUpdated': self.parse8601(lastTxTime),
                  'status': status,
                  'symbol': symbol,
                  'side': side,
                  'price': price,
                  'amount': orderAmount,
                  'average': average,
                  'type': type,
                  'filled': filled,
                  'cost': cost,
                  'remaining': remaining,
                  'fee': {
                      'cost': fee,
                      'currency': quote,
                  },
                  'info': order,
              })
          return results
  
      def parse_order_status(self, status):
          return self.safe_string(self.options['order']['status'], status, status)
  
      def edit_order(self, id, symbol, type, side, amount=None, price=None, params={}):
          if amount is None:
              raise ArgumentsRequired(self.id + ' editOrder() requires a amount argument')
          if price is None:
              raise ArgumentsRequired(self.id + ' editOrder() requires a price argument')
          self.load_markets()
          market = self.market(symbol)
          # see: https://cex.io/rest-api#/definitions/CancelReplaceOrderRequest
          request = {
              'pair': market['id'],
              'type': side,
              'amount': amount,
              'price': price,
              'order_id': id,
          }
          response = self.privatePostCancelReplaceOrderPair(self.extend(request, params))
          return self.parse_order(response, market)
  
      def fetch_deposit_address(self, code, params={}):
          if code == 'XRP' or code == 'XLM':
              # https://github.com/ccxt/ccxt/pull/2327#issuecomment-375204856
              raise NotSupported(self.id + ' fetchDepositAddress does not support XRP and XLM addresses yet(awaiting docs from CEX.io)')
          self.load_markets()
          currency = self.currency(code)
          request = {
              'currency': currency['id'],
          }
          response = self.privatePostGetAddress(self.extend(request, params))
          address = self.safe_string(response, 'data')
          self.check_address(address)
          return {
              'currency': code,
              'address': address,
              'tag': None,
              'network': None,
              'info': response,
          }
  
      def nonce(self):
          return self.milliseconds()
  
      def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
          url = self.urls['api'] + '/' + self.implode_params(path, params)
          query = self.omit(params, self.extract_params(path))
          if api == 'public':
              if query:
                  url += '?' + self.urlencode(query)
          else:
              self.check_required_credentials()
              nonce = str(self.nonce())
              auth = nonce + self.uid + self.apiKey
              signature = self.hmac(self.encode(auth), self.encode(self.secret))
              body = self.json(self.extend({
                  'key': self.apiKey,
                  'signature': signature.upper(),
                  'nonce': nonce,
              }, query))
              headers = {
                  'Content-Type': 'application/json',
              }
          return {'url': url, 'method': method, 'body': body, 'headers': headers}
  
      def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
          if isinstance(response, list):
              return response  # public endpoints may return []-arrays
          if body == 'true':
              return
          if response is None:
              raise NullResponse(self.id + ' returned ' + self.json(response))
          if 'e' in response:
              if 'ok' in response:
                  if response['ok'] == 'ok':
                      return
          if 'error' in response:
              message = self.safe_string(response, 'error')
              feedback = self.id + ' ' + body
              self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback)
              self.throw_broadly_matched_exception(self.exceptions['broad'], message, feedback)
              raise ExchangeError(feedback)
  