text: # -*- coding: utf-8 -*-
  
  from ccxt.async.base.exchange import Exchange
  import base64
  import hashlib
  from ccxt.base.errors import ExchangeError
  
  
  class gemini (Exchange):
  
      def describe(self):
          return self.deep_extend(super(gemini, self).describe(), {
              'id': 'gemini',
              'name': 'Gemini',
              'countries': 'US',
              'rateLimit': 1500,  # 200 for private API
              'version': 'v1',
              'hasCORS': False,
              'urls': {
                  'logo': 'https://user-images.githubusercontent.com/1294454/27816857-ce7be644-6096-11e7-82d6-3c257263229c.jpg',
                  'api': 'https://api.gemini.com',
                  'www': 'https://gemini.com',
                  'doc': 'https://docs.gemini.com/rest-api',
              },
              'api': {
                  'public': {
                      'get': [
                          'symbols',
                          'pubticker/{symbol}',
                          'book/{symbol}',
                          'trades/{symbol}',
                          'auction/{symbol}',
                          'auction/{symbol}/history',
                      ],
                  },
                  'private': {
                      'post': [
                          'order/new',
                          'order/cancel',
                          'order/cancel/session',
                          'order/cancel/all',
                          'order/status',
                          'orders',
                          'mytrades',
                          'tradevolume',
                          'balances',
                          'deposit/{currency}/newAddress',
                          'withdraw/{currency}',
                          'heartbeat',
                      ],
                  },
              },
          })
  
      async def fetch_markets(self):
          markets = await self.publicGetSymbols()
          result = []
          for p in range(0, len(markets)):
              id = markets[p]
              market = id
              uppercase = market.upper()
              base = uppercase[0:3]
              quote = uppercase[3:6]
              symbol = base + '/' + quote
              result.append({
                  'id': id,
                  'symbol': symbol,
                  'base': base,
                  'quote': quote,
                  'info': market,
              })
          return result
  
      async def fetch_order_book(self, symbol, params={}):
          await self.load_markets()
          orderbook = await self.publicGetBookSymbol(self.extend({
              'symbol': self.market_id(symbol),
          }, params))
          return self.parse_order_book(orderbook, None, 'bids', 'asks', 'price', 'amount')
  
      async def fetch_ticker(self, symbol, params={}):
          await self.load_markets()
          market = self.market(symbol)
          ticker = await self.publicGetPubtickerSymbol(self.extend({
              'symbol': market['id'],
          }, params))
          timestamp = ticker['volume']['timestamp']
          baseVolume = market['base']
          quoteVolume = market['quote']
          return {
              'symbol': symbol,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'high': None,
              'low': None,
              'bid': float(ticker['bid']),
              'ask': float(ticker['ask']),
              'vwap': None,
              'open': None,
              'close': None,
              'first': None,
              'last': float(ticker['last']),
              'change': None,
              'percentage': None,
              'average': None,
              'baseVolume': float(ticker['volume'][baseVolume]),
              'quoteVolume': float(ticker['volume'][quoteVolume]),
              'info': ticker,
          }
  
      def parse_trade(self, trade, market):
          timestamp = trade['timestampms']
          return {
              'id': str(trade['tid']),
              'info': trade,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'symbol': market['symbol'],
              'type': None,
              'side': trade['type'],
              'price': float(trade['price']),
              'amount': float(trade['amount']),
          }
  
      async def fetch_trades(self, symbol, params={}):
          await self.load_markets()
          market = self.market(symbol)
          response = await self.publicGetTradesSymbol(self.extend({
              'symbol': market['id'],
          }, params))
          return self.parse_trades(response, market)
  
      async def fetch_balance(self, params={}):
          await self.load_markets()
          balances = await self.privatePostBalances()
          result = {'info': balances}
          for b in range(0, len(balances)):
              balance = balances[b]
              currency = balance['currency']
              account = {
                  'free': float(balance['available']),
                  'used': 0.0,
                  'total': float(balance['amount']),
              }
              account['used'] = account['total'] - account['free']
              result[currency] = account
          return self.parse_balance(result)
  
      async def create_order(self, symbol, type, side, amount, price=None, params={}):
          await self.load_markets()
          if type == 'market':
              raise ExchangeError(self.id + ' allows limit orders only')
          order = {
              'client_order_id': self.nonce(),
              'symbol': self.market_id(symbol),
              'amount': str(amount),
              'price': str(price),
              'side': side,
              'type': 'exchange limit',  # gemini allows limit orders only
          }
          response = await self.privatePostOrderNew(self.extend(order, params))
          return {
              'info': response,
              'id': response['order_id'],
          }
  
      async def cancel_order(self, id, symbol=None, params={}):
          await self.load_markets()
          return await self.privatePostCancelOrder({'order_id': id})
  
      def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
          url = '/' + self.version + '/' + self.implode_params(path, params)
          query = self.omit(params, self.extract_params(path))
          if api == 'public':
              if query:
                  url += '?' + self.urlencode(query)
          else:
              nonce = self.nonce()
              request = self.extend({
                  'request': url,
                  'nonce': nonce,
              }, query)
              payload = self.json(request)
              payload = base64.b64encode(self.encode(payload))
              signature = self.hmac(payload, self.encode(self.secret), hashlib.sha384)
              headers = {
                  'Content-Type': 'text/plain',
                  'X-GEMINI-APIKEY': self.apiKey,
                  'X-GEMINI-PAYLOAD': payload,
                  'X-GEMINI-SIGNATURE': signature,
              }
          url = self.urls['api'] + url
          return {'url': url, 'method': method, 'body': body, 'headers': headers}
  
      async def request(self, path, api='public', method='GET', params={}, headers=None, body=None):
          response = await self.fetch2(path, api, method, params, headers, body)
          if 'result' in response:
              if response['result'] == 'error':
                  raise ExchangeError(self.id + ' ' + self.json(response))
          return response
  