text: # -*- coding: utf-8 -*-
  
  # PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
  # https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
  
  from ccxt.async_support.base.exchange import Exchange
  import base64
  import hashlib
  import math
  from ccxt.base.errors import ExchangeError
  from ccxt.base.errors import AuthenticationError
  from ccxt.base.errors import InvalidNonce
  
  
  class bitsane (Exchange):
  
      def describe(self):
          return self.deep_extend(super(bitsane, self).describe(), {
              'id': 'bitsane',
              'name': 'Bitsane',
              'countries': ['IE'],  # Ireland
              'has': {
                  'fetchCurrencies': True,
                  'fetchTickers': True,
                  'fetchOpenOrders': True,
                  'fetchDepositAddress': True,
                  'withdraw': True,
              },
              'urls': {
                  'logo': 'https://user-images.githubusercontent.com/1294454/41387105-d86bf4c6-6f8d-11e8-95ea-2fa943872955.jpg',
                  'api': 'https://bitsane.com/api',
                  'www': 'https://bitsane.com',
                  'doc': 'https://bitsane.com/help/api',
                  'fees': 'https://bitsane.com/help/fees',
                  'referral': 'https://bitsane.com?ref=CGOvuPA2LR3GcdOUOKjc',
              },
              'api': {
                  'public': {
                      'get': [
                          'assets/currencies',
                          'assets/pairs',
                          'ticker',
                          'orderbook',
                          'trades',
                      ],
                  },
                  'private': {
                      'post': [
                          'balances',
                          'order/cancel',
                          'order/new',
                          'order/status',
                          'orders',
                          'orders/history',
                          'deposit/address',
                          'withdraw',
                          'withdrawal/status',
                          'transactions/history',
                          'vouchers',
                          'vouchers/create',
                          'vouchers/redeem',
                      ],
                  },
              },
              'fees': {
                  'trading': {
                      'maker': 0.15 / 100,
                      'taker': 0.25 / 100,
                  },
              },
              'exceptions': {
                  '3': AuthenticationError,
                  '4': AuthenticationError,
                  '5': AuthenticationError,
                  '6': InvalidNonce,
                  '7': AuthenticationError,
                  '8': InvalidNonce,
                  '9': AuthenticationError,
                  '10': AuthenticationError,
                  '11': AuthenticationError,
              },
              'options': {
                  'defaultCurrencyPrecision': 2,
              },
          })
  
      async def fetch_currencies(self, params={}):
          currencies = await self.publicGetAssetsCurrencies(params)
          ids = list(currencies.keys())
          result = {}
          for i in range(0, len(ids)):
              id = ids[i]
              currency = currencies[id]
              precision = self.safe_integer(currency, 'precision', self.options['defaultCurrencyPrecision'])
              code = self.common_currency_code(id)
              canWithdraw = self.safe_value(currency, 'withdrawal', True)
              canDeposit = self.safe_value(currency, 'deposit', True)
              active = canWithdraw and canDeposit
              result[code] = {
                  'id': id,
                  'code': code,
                  'name': self.safe_string(currency, 'full_name', code),
                  'active': active,
                  'precision': precision,
                  'funding': {
                      'withdraw': {
                          'active': canWithdraw,
                          'fee': self.safe_value(currency, 'withdrawal_fee'),
                      },
                      'deposit': {
                          'active': canDeposit,
                          'fee': self.safe_value(currency, 'deposit_fee'),
                      },
                  },
                  'limits': {
                      'amount': {
                          'min': self.safe_float(currency, 'minAmountTrade'),
                          'max': math.pow(10, precision),
                      },
                      'price': {
                          'min': math.pow(10, -precision),
                          'max': math.pow(10, precision),
                      },
                      'cost': {
                          'min': None,
                          'max': None,
                      },
                  },
                  'info': currency,
              }
          return result
  
      async def fetch_markets(self, params={}):
          markets = await self.publicGetAssetsPairs(params)
          result = []
          marketIds = list(markets.keys())
          for i in range(0, len(marketIds)):
              id = marketIds[i]
              market = markets[id]
              baseId = self.safe_string(market, 'base')
              quoteId = self.safe_string(market, 'quote')
              base = self.common_currency_code(baseId)
              quote = self.common_currency_code(quoteId)
              symbol = base + '/' + quote
              limits = self.safe_value(market, 'limits')
              minLimit = None
              maxLimit = None
              if limits is not None:
                  minLimit = self.safe_float(limits, 'minimum')
                  maxLimit = self.safe_float(limits, 'maximum')
              precision = {
                  'amount': self.safe_integer(market, 'precision'),
                  'price': 8,
              }
              result.append({
                  'id': id,
                  'symbol': symbol,
                  'base': base,
                  'quote': quote,
                  'baseId': baseId,
                  'quoteId': quoteId,
                  'active': True,
                  'precision': precision,
                  'limits': {
                      'amount': {
                          'min': minLimit,
                          'max': maxLimit,
                      },
                      'price': {
                          'min': math.pow(10, -precision['price']),
                          'max': math.pow(10, precision['price']),
                      },
                      'cost': {
                          'min': None,
                          'max': None,
                      },
                  },
                  'info': id,
              })
          return result
  
      def parse_ticker(self, ticker, market=None):
          symbol = None
          if market is not None:
              symbol = market['symbol']
          timestamp = self.milliseconds()
          last = self.safe_float(ticker, 'last')
          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': None,
              'close': last,
              'last': last,
              'previousClose': None,
              'change': None,
              'percentage': self.safe_float(ticker, 'percentChange'),
              'average': None,
              'baseVolume': self.safe_float(ticker, 'baseVolume'),
              'quoteVolume': self.safe_float(ticker, 'quoteVolume'),
              'info': ticker,
          }
  
      async def fetch_ticker(self, symbol, params={}):
          tickers = await self.fetch_tickers([symbol], params)
          return tickers[symbol]
  
      async def fetch_tickers(self, symbols=None, params={}):
          await self.load_markets()
          request = {}
          if symbols:
              ids = self.market_ids(symbols)
              request['pairs'] = ','.join(ids)
          response = await self.publicGetTicker(self.extend(request, params))
          marketIds = list(response.keys())
          result = {}
          for i in range(0, len(marketIds)):
              id = marketIds[i]
              market = self.safe_value(self.marketsById, id)
              if market is not None:
                  symbol = market['symbol']
                  result[symbol] = self.parse_ticker(response[id], market)
          return result
  
      async def fetch_order_book(self, symbol, limit=None, params={}):
          await self.load_markets()
          request = {
              'pair': self.market_id(symbol),
          }
          response = await self.publicGetOrderbook(self.extend(request, params))
          return self.parse_order_book(response['result'], None, 'bids', 'asks', 'price', 'amount')
  
      def parse_trade(self, trade, market=None):
          symbol = None
          if market is not None:
              symbol = market['symbol']
          timestamp = self.safe_integer(trade, 'timestamp')
          if timestamp is not None:
              timestamp *= 1000
          price = self.safe_float(trade, 'price')
          amount = self.safe_float(trade, 'amount')
          cost = None
          if amount is not None:
              if price is not None:
                  cost = float(self.cost_to_precision(symbol, price * amount))
          id = self.safe_string(trade, 'tid')
          return {
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'symbol': symbol,
              'id': id,
              'order': None,
              'type': None,
              'side': None,
              'price': price,
              'amount': amount,
              'cost': cost,
              'fee': None,
              'info': trade,
          }
  
      async def fetch_trades(self, symbol, since=None, limit=None, params={}):
          await self.load_markets()
          market = self.market(symbol)
          request = {
              'pair': market['id'],
          }
          if since is not None:
              request['since'] = int(since / 1000)
          if limit is not None:
              request['limit'] = limit
          response = await self.publicGetTrades(self.extend(request, params))
          return self.parse_trades(response['result'], market, since, limit)
  
      async def fetch_balance(self, params={}):
          await self.load_markets()
          response = await self.privatePostBalances(params)
          result = {'info': response}
          balances = self.safe_value(response, 'result')
          ids = list(balances.keys())
          for i in range(0, len(ids)):
              id = ids[i]
              balance = balances[id]
              code = id
              if id in self.currencies_by_id:
                  code = self.currencies_by_id[id]['code']
              else:
                  code = self.common_currency_code(code)
              account = {
                  'free': self.safe_float(balance, 'amount'),
                  'used': self.safe_float(balance, 'locked'),
                  'total': self.safe_float(balance, 'total'),
              }
              result[code] = account
          return self.parse_balance(result)
  
      def parse_order(self, order, market=None):
          symbol = None
          marketId = self.safe_string(order, 'pair')
          market = self.safe_value(self.marketsById, marketId, market)
          if market is not None:
              symbol = market['symbol']
          timestamp = self.safe_integer(order, 'timestamp') * 1000
          price = self.safe_float(order, 'price')
          amount = self.safe_float(order, 'original_amount')
          filled = self.safe_float(order, 'executed_amount')
          remaining = self.safe_float(order, 'remaining_amount')
          isCanceled = self.safe_value(order, 'is_cancelled')
          isLive = self.safe_value(order, 'is_live')
          status = 'closed'
          if isCanceled:
              status = 'canceled'
          elif isLive:
              status = 'open'
          return {
              'id': self.safe_string(order, 'id'),
              'datetime': self.iso8601(timestamp),
              'timestamp': timestamp,
              'status': status,
              'symbol': symbol,
              'type': self.safe_string(order, 'type'),
              'side': self.safe_string(order, 'side'),
              'price': price,
              'cost': None,
              'amount': amount,
              'filled': filled,
              'remaining': remaining,
              'trades': None,
              'fee': None,
              'info': self.safe_value(order, 'info', order),
          }
  
      async def create_order(self, symbol, type, side, amount, price=None, params={}):
          await self.load_markets()
          market = self.market(symbol)
          order = {
              'pair': market['id'],
              'amount': amount,
              'type': type,
              'side': side,
          }
          if type != 'market':
              order['price'] = price
          response = await self.privatePostOrderNew(self.extend(order, params))
          order['id'] = response['result']['order_id']
          order['timestamp'] = self.seconds()
          order['original_amount'] = order['amount']
          order['info'] = response
          order = self.parse_order(order, market)
          id = order['id']
          self.orders[id] = order
          return order
  
      async def cancel_order(self, id, symbol=None, params={}):
          await self.load_markets()
          request = {
              'order_id': id,
          }
          response = await self.privatePostOrderCancel(self.extend(request, params))
          return self.parse_order(response['result'])
  
      async def fetch_order(self, id, symbol=None, params={}):
          await self.load_markets()
          request = {
              'order_id': id,
          }
          response = await self.privatePostOrderStatus(self.extend(request, params))
          return self.parse_order(response['result'])
  
      async def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
          await self.load_markets()
          response = await self.privatePostOrders(params)
          return self.parse_orders(response['result'], None, since, limit)
  
      async def fetch_deposit_address(self, code, params={}):
          await self.load_markets()
          currency = self.currency(code)
          request = {
              'currency': currency['id'],
          }
          response = await self.privatePostDepositAddress(self.extend(request, params))
          result = self.safe_value(response, 'result', {})
          address = self.safe_string(result, 'address')
          return {
              'currency': code,
              'address': address,
              'tag': None,
              'info': response,
          }
  
      async def withdraw(self, code, amount, address, tag=None, params={}):
          await self.load_markets()
          currency = self.currency(code)
          request = {
              'currency': currency['id'],
              'amount': amount,
              'address': address,
          }
          if tag is not None:
              request['additional'] = tag
          response = await self.privatePostWithdraw(self.extend(request, params))
          result = self.safe_value(response, 'result', {})
          id = self.safe_string(result, 'withdrawal_id')
          return {
              'id': id,
              'info': response,
          }
  
      def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
          url = self.urls['api'] + '/' + api + '/' + path
          if api == 'public':
              if params:
                  url += '?' + self.urlencode(params)
          else:
              self.check_required_credentials()
              body = self.extend({
                  'nonce': self.nonce(),
              }, params)
              payload = self.json(body)
              payload64 = base64.b64encode(self.encode(payload))
              body = self.decode(payload64)
              headers = {
                  'X-BS-APIKEY': self.apiKey,
                  'X-BS-PAYLOAD': body,
                  'X-BS-SIGNATURE': self.hmac(payload64, self.encode(self.secret), hashlib.sha384),
              }
          return {'url': url, 'method': method, 'body': body, 'headers': headers}
  
      def handle_errors(self, httpCode, reason, url, method, headers, body, response):
          if response is None:
              return  # fallback to default error handler
          statusCode = self.safe_string(response, 'statusCode')
          if statusCode is not None:
              if statusCode != '0':
                  feedback = self.id + ' ' + self.json(response)
                  exceptions = self.exceptions
                  if statusCode in exceptions:
                      raise exceptions[statusCode](feedback)
                  else:
                      raise ExchangeError(self.id + ' ' + self.json(response))
  