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
  from ccxt.base.errors import ExchangeError
  from ccxt.base.errors import AuthenticationError
  from ccxt.base.errors import AccountSuspended
  from ccxt.base.errors import ArgumentsRequired
  from ccxt.base.errors import InsufficientFunds
  from ccxt.base.errors import NotSupported
  from ccxt.base.errors import InvalidNonce
  
  
  class stronghold(Exchange):
  
      def describe(self):
          return self.deep_extend(super(stronghold, self).describe(), {
              'id': 'stronghold',
              'name': 'Stronghold',
              'country': ['US'],
              'rateLimit': 1000,
              'version': 'v1',
              'comment': 'This comment is optional',
              'urls': {
                  'logo': 'https://user-images.githubusercontent.com/1294454/52160042-98c1f300-26be-11e9-90dd-da8473944c83.jpg',
                  'api': {
                      'public': 'https://api.stronghold.co',
                      'private': 'https://api.stronghold.co',
                  },
                  'www': 'https://stronghold.co',
                  'doc': [
                      'https://docs.stronghold.co',
                  ],
              },
              'requiredCredentials': {
                  'apiKey': True,
                  'secret': True,
                  'password': True,
              },
              'has': {
                  'cancelOrder': True,
                  'createDepositAddress': True,
                  'createOrder': True,
                  'fetchAccounts': True,
                  'fetchBalance': True,
                  'fetchDepositAddress': False,
                  'fetchCurrencies': True,
                  'fetchMarkets': True,
                  'fetchMyTrades': True,
                  'fetchOpenOrders': True,
                  'fetchOrderBook': True,
                  'fetchTicker': False,
                  'fetchTickers': False,
                  'fetchTime': True,
                  'fetchTrades': True,
                  'fetchTransactions': True,
                  'withdraw': True,
              },
              'api': {
                  'public': {
                      'get': [
                          'utilities/time',
                          'utilities/uuid',
                          'venues',
                          'venues/{venueId}/assets',
                          'venues/{venueId}/markets',
                          'venues/{venueId}/markets/{marketId}/orderbook',
                          'venues/{venueId}/markets/{marketId}/trades',
                      ],
                      'post': [
                          'venues/{venueId}/assets',
                          'iam/credentials',
                          'identities',
                      ],
                      'patch': [
                          'identities',
                      ],
                      'put': [
                          'iam/credentials/{credentialId}',
                      ],
                      'delete': [
                          'iam/credentials/{credentialId}',
                      ],
                  },
                  'private': {
                      'get': [
                          'venues',
                          'venues/{venueId}/accounts',
                          'venues/{venueId}/accounts/{accountId}',
                          'venues/{venueId}/accounts/{accountId}/payments/{paymentId}',
                          'venues/{venueId}/accounts/{accountId}/orders',
                          'venues/{venueId}/accounts/{accountId}/trades',
                          'venues/{venueId}/accounts/{accountId}/transactions',
                      ],
                      'post': [
                          'venues/{venueId}/accounts',
                          'venues/{venueId}/accounts/{accountId}/orders',
                          'venues/{venueId}/accounts/{accountId}/deposit',
                          'venues/{venueId}/accounts/{accountId}/withdrawal',
                          'venues/{venueId}/accounts/{accountId}/payments',
                          'venues/{venueId}/accounts/{accountId}/payments/{paymentId}/stop',
                          'venues/{venueId}/custody/accounts/{accountId}/operations/{operationId}/signatures',
                          'venues/{venueId}/anchor/withdrawal',
                          'venues/{venueId}/testing/friendbot',
                      ],
                      'delete': [
                          'venues/{venueId}/accounts/{accountId}/orders/{orderId}',
                      ],
                  },
              },
              'options': {
                  'accountId': None,
                  'venueId': 'trade-public',
                  'venues': {
                      'trade': 'trade-public',
                      'sandbox': 'sandbox-public',
                  },
                  'paymentMethods': {
                      'ETH': 'ethereum',
                      'BTC': 'bitcoin',
                      'XLM': 'stellar',
                      'XRP': 'ripple',
                      'LTC': 'litecoin',
                      'SHX': 'stellar',
                  },
              },
              'exceptions': {
                  'CREDENTIAL_MISSING': AuthenticationError,
                  'CREDENTIAL_INVALID': AuthenticationError,
                  'CREDENTIAL_REVOKED': AccountSuspended,
                  'CREDENTIAL_NO_IDENTITY': AuthenticationError,
                  'PASSPHRASE_INVALID': AuthenticationError,
                  'SIGNATURE_INVALID': AuthenticationError,
                  'TIME_INVALID': InvalidNonce,
                  'BYPASS_INVALID': AuthenticationError,
                  'INSUFFICIENT_FUNDS': InsufficientFunds,
              },
          })
  
      async def get_active_account(self):
          if self.options['accountId'] is not None:
              return self.options['accountId']
          await self.load_accounts()
          numAccounts = len(self.accounts)
          if numAccounts > 0:
              return self.accounts[0]['id']
          raise ExchangeError(self.id + ' requires an accountId.')
  
      async def fetch_accounts(self, params={}):
          request = {
              'venueId': self.options['venueId'],
          }
          response = await self.privateGetVenuesVenueIdAccounts(self.extend(request, params))
          #
          #   [{id: '34080200-b25a-483d-a734-255d30ba324d',
          #       venueSpecificId: ''} ...]
          #
          return response['result']
  
      async def fetch_time(self, params={}):
          response = await self.publicGetUtilitiesTime(params)
          #
          #     {
          #         "requestId": "6de8f506-ad9d-4d0d-94f3-ec4d55dfcdb9",
          #         "timestamp": 1536436649207281,
          #         "success": True,
          #         "statusCode": 200,
          #         "result": {
          #             "timestamp": "2018-09-08T19:57:29.207282Z"
          #         }
          #     }
          #
          return self.parse8601(self.safe_string(response['result'], 'timestamp'))
  
      async def fetch_markets(self, params={}):
          request = {
              'venueId': self.options['venueId'],
          }
          response = await self.publicGetVenuesVenueIdMarkets(self.extend(request, params))
          data = response['result']
          #
          #     [
          #         {
          #             id: 'SHXUSD',
          #             baseAssetId: 'SHX/stronghold.co',
          #             counterAssetId: 'USD/stronghold.co',
          #             minimumOrderSize: '1.0000000',
          #             minimumOrderIncrement: '1.0000000',
          #             minimumPriceIncrement: '0.00010000',
          #             displayDecimalsPrice: 4,
          #             displayDecimalsAmount: 0
          #         },
          #         ...
          #     ]
          #
          result = {}
          for i in range(0, len(data)):
              entry = data[i]
              marketId = entry['id']
              baseId = self.safe_string(entry, 'baseAssetId')
              quoteId = self.safe_string(entry, 'counterAssetId')
              baseAssetId = baseId.split('/')[0]
              quoteAssetId = quoteId.split('/')[0]
              base = self.safe_currency_code(baseAssetId)
              quote = self.safe_currency_code(quoteAssetId)
              symbol = base + '/' + quote
              limits = {
                  'amount': {
                      'min': self.safe_float(entry, 'minimumOrderSize'),
                      'max': None,
                  },
              }
              precision = {
                  'price': self.safe_integer(entry, 'displayDecimalsPrice'),
                  'amount': self.safe_integer(entry, 'displayDecimalsAmount'),
              }
              result[symbol] = {
                  'symbol': symbol,
                  'id': marketId,
                  'base': base,
                  'quote': quote,
                  'baseId': baseId,
                  'quoteId': quoteId,
                  'precision': precision,
                  'info': entry,
                  'limits': limits,
                  'active': None,
              }
          return result
  
      async def fetch_currencies(self, params={}):
          request = {
              'venueId': self.options['venueId'],
          }
          response = await self.publicGetVenuesVenueIdAssets(self.extend(request, params))
          #
          #     [
          #         {
          #             id: 'XLM/native',
          #             alias: '',
          #             code: 'XLM',
          #             name: '',
          #             displayDecimalsFull: 7,
          #             displayDecimalsSignificant: 2,
          #         },
          #         ...
          #     ]
          #
          data = response['result']
          result = {}
          limits = {
              'amount': {
                  'min': None,
                  'max': None,
              },
              'price': {
                  'min': None,
                  'max': None,
              },
              'cost': {
                  'min': None,
                  'max': None,
              },
              'withdraw': {
                  'min': None,
                  'max': None,
              },
          }
          for i in range(0, len(data)):
              entry = data[i]
              assetId = self.safe_string(entry, 'id')
              currencyId = self.safe_string(entry, 'code')
              code = self.safe_currency_code(currencyId)
              precision = self.safe_integer(entry, 'displayDecimalsFull')
              result[code] = {
                  'code': code,
                  'id': assetId,
                  'precision': precision,
                  'info': entry,
                  'active': None,
                  'name': None,
                  'limits': limits,
                  'fee': None,
              }
          return result
  
      async def fetch_order_book(self, symbol, limit=None, params={}):
          await self.load_markets()
          marketId = self.market_id(symbol)
          request = {
              'marketId': marketId,
              'venueId': self.options['venueId'],
          }
          response = await self.publicGetVenuesVenueIdMarketsMarketIdOrderbook(self.extend(request, params))
          #
          #     {
          #         marketId: 'ETHBTC',
          #         bids: [
          #             ['0.031500', '7.385000'],
          #             ...,
          #         ],
          #         asks: [
          #             ['0.031500', '7.385000'],
          #             ...,
          #         ],
          #     }
          #
          data = response['result']
          timestamp = self.parse8601(self.safe_string(response, 'timestamp'))
          return self.parse_order_book(data, timestamp)
  
      async def fetch_trades(self, symbol, since=None, limit=None, params={}):
          await self.load_markets()
          market = self.market(symbol)
          request = {
              'marketId': market['id'],
              'venueId': self.options['venueId'],
          }
          response = await self.publicGetVenuesVenueIdMarketsMarketIdTrades(self.extend(request, params))
          #
          #     {
          #         "requestId": "4d343700-b53f-4975-afcc-732ae9d3c828",
          #         "timestamp": "2018-11-08T19:22:11.399543Z",
          #         "success": True,
          #         "statusCode": 200,
          #         "result": {
          #             "marketId": "",
          #             "trades": [
          #                 ["0.9", "3.10", "sell", "2018-11-08T19:22:11.399547Z"],
          #                 ...
          #             ],
          #         }
          #     }
          #
          return self.parse_trades(response['result']['trades'], market, since, limit)
  
      def parse_trade(self, trade, market=None):
          #
          # fetchTrades(public)
          #
          #      ['0.03177000', '0.0643501', 'sell', '2019-01-27T23:02:04Z']
          #
          # fetchMyTrades(private)
          #
          #     {
          #         id: '9cdb109c-d035-47e2-81f8-a0c802c9c5f9',
          #         orderId: 'a38d8bcb-9ff5-4c52-81a0-a40196a66462',
          #         marketId: 'XLMUSD',
          #         side: 'sell',
          #         size: '1.0000000',
          #         price: '0.10440600',
          #         settled: True,
          #         maker: False,
          #         executedAt: '2019-02-01T18:44:21Z'
          #     }
          #
          id = None
          takerOrMaker = None
          price = None
          amount = None
          cost = None
          side = None
          timestamp = None
          orderId = None
          if isinstance(trade, list):
              price = float(trade[0])
              amount = float(trade[1])
              side = trade[2]
              timestamp = self.parse8601(trade[3])
          else:
              id = self.safe_string(trade, 'id')
              price = self.safe_float(trade, 'price')
              amount = self.safe_float(trade, 'size')
              side = self.safe_string(trade, 'side')
              timestamp = self.parse8601(self.safe_string(trade, 'executedAt'))
              orderId = self.safe_string(trade, 'orderId')
              marketId = self.safe_string(trade, 'marketId')
              market = self.safe_value(self.markets_by_id, marketId)
              isMaker = self.safe_value(trade, 'maker')
              takerOrMaker = 'maker' if isMaker else 'taker'
          if amount is not None and price is not None:
              cost = amount * price
          symbol = None
          if market is not None:
              symbol = market['symbol']
          return {
              'id': id,
              'info': trade,
              'timestamp': timestamp,
              'datetime': self.iso8601(timestamp),
              'symbol': symbol,
              'type': None,
              'order': orderId,
              'side': side,
              'price': price,
              'amount': amount,
              'cost': cost,
              'takerOrMaker': takerOrMaker,
              'fee': {
                  'cost': None,
                  'currency': None,
                  'rate': None,
              },
          }
  
      async def fetch_transactions(self, code=None, since=None, limit=None, params={}):
          await self.load_markets()
          request = self.extend({
              'venueId': self.options['venueId'],
              'accountId': await self.get_active_account(),
          }, params)
          if not request['accountId']:
              raise ArgumentsRequired(self.id + " fetchTransactions requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'.")
          response = await self.privateGetVenuesVenueIdAccountsAccountIdTransactions(request)
          currency = None
          if code is not None:
              currency = self.currency(code)
          return self.parse_transactions(response['result'], currency, since, limit)
  
      def parse_transaction_status(self, status):
          statuses = {
              'queued': 'pending',
              'settling': 'pending',
          }
          return self.safe_string(statuses, status, status)
  
      def parse_transaction(self, transaction, currency=None):
          # {
          #     "id": "6408e003-0f14-4457-9340-ba608992ad5c",
          #     "status": "queued",
          #     "direction": "outgoing",
          #     "amount": "98.95000000",
          #     "assetId": "XLM/native",
          #     "sourceAccount": {
          #       "id": "774fa8ef-600b-4636-b9ed-cd6d23421915",
          #       "venueSpecificId": "GC5FIBIQZTQRMJE34GYF5EKH77GEQ3OHFX3NIP5OKDIZFA6VERLZSHY6"
          #     },
          #     "destinationAccount": {
          #       "id": "f72b9fb5-9607-4dd3-b31f-6ded21337056",
          #       "venueSpecificId": "GAOWV6CYBE7DEWSWPODXLMI5YB75VXXZJX5OYVQ2YLZH2TVA3TMMSNYW"
          #     }
          #   }
          id = self.safe_string(transaction, 'id')
          assetId = self.safe_string(transaction, 'assetId')
          code = None
          if assetId is not None:
              currencyId = assetId.split('/')[0]
              code = self.safe_currency_code(currencyId)
          else:
              if currency is not None:
                  code = currency['code']
          amount = self.safe_float(transaction, 'amount')
          status = self.parse_transaction_status(self.safe_string(transaction, 'status'))
          feeCost = self.safe_float(transaction, 'feeAmount')
          feeRate = None
          if feeCost is not None:
              feeRate = feeCost / amount
          direction = self.safe_string(transaction, 'direction')
          datetime = self.safe_string(transaction, 'requestedAt')
          timestamp = self.parse8601(datetime)
          updated = self.parse8601(self.safe_string(transaction, 'updatedAt'))
          type = 'withdrawal' if (direction == 'outgoing' or direction == 'withdrawal') else 'deposit'
          fee = {
              'cost': feeCost,
              'rate': feeRate,
          }
          return {
              'id': id,
              'info': transaction,
              'currency': code,
              'amount': amount,
              'status': status,
              'fee': fee,
              'tag': None,
              'type': type,
              'updated': updated,
              'address': None,
              'txid': None,
              'timestamp': timestamp,
              'datetime': datetime,
          }
  
      async def create_order(self, symbol, type, side, amount, price=None, params={}):
          await self.load_markets()
          market = self.market(symbol)
          request = self.extend({
              'venueId': self.options['venueId'],
              'accountId': await self.get_active_account(),
              'marketID': market['id'],
              'type': type,
              'side': side,
              'size': self.amount_to_precision(symbol, amount),
              'price': self.price_to_precision(symbol, price),
          }, params)
          if not request['accountId']:
              raise ArgumentsRequired(self.id + " createOrder requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'.")
          response = await self.privatePostVenuesVenueIdAccountsAccountIdOrders(request)
          return self.parse_order(response, market)
  
      async def cancel_order(self, id, symbol=None, params={}):
          request = self.extend({
              'venueId': self.options['venueId'],
              'accountId': await self.get_active_account(),
              'orderId': id,
          }, params)
          if not request['accountId']:
              raise ArgumentsRequired(self.id + " cancelOrder requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'.")
          response = await self.privateDeleteVenuesVenueIdAccountsAccountIdOrdersOrderId(request)
          return self.parse_order(response)
  
      async def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
          await self.load_markets()
          market = None
          if symbol is not None:
              market = self.market(symbol)
          request = self.extend({
              'venueId': self.options['venueId'],
              'accountId': await self.get_active_account(),
          }, params)
          if not request['accountId']:
              raise ArgumentsRequired(self.id + " cancelOrder requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'.")
          response = await self.privateGetVenuesVenueIdAccountsAccountIdOrders(request)
          return self.parse_orders(response['result'], market, since, limit)
  
      def parse_order(self, order, market=None):
          # {id: '178596',
          #   marketId: 'XLMUSD',
          #   side: 'buy',
          #   size: '1.0000000',
          #   sizeFilled: '0',
          #   price: '0.10000000',
          #   placedAt: '2019-02-01T19:47:52Z'}
          marketId = self.safe_string(order, 'marketId')
          if marketId is not None:
              market = self.safe_value(self.marketsById, marketId)
          symbol = None
          if market is not None:
              symbol = market['symbol']
          id = self.safe_string(order, 'id')
          datetime = self.safe_string(order, 'placedAt')
          amount = self.safe_float(order, 'size')
          price = self.safe_float(order, 'price')
          filled = self.safe_float(order, 'sizeFilled')
          cost = None
          remaining = None
          if amount is not None:
              if filled is not None:
                  remaining = amount - filled
              if price is not None:
                  cost = amount * price
          return {
              'id': id,
              'clientOrderId': None,
              'info': order,
              'symbol': symbol,
              'datetime': datetime,
              'timestamp': self.parse8601(datetime),
              'side': self.safe_string(order, 'side'),
              'amount': amount,
              'filled': filled,
              'remaining': remaining,
              'price': price,
              'cost': cost,
              'trades': [],
              'lastTradeTimestamp': None,
              'status': None,
              'type': None,
              'average': None,
              'fee': None,
          }
  
      def nonce(self):
          return self.seconds()
  
      def set_sandbox_mode(self, enabled):
          if enabled:
              self.options['venueId'] = self.options['venues']['sandbox']
          else:
              self.options['venueId'] = self.options['venues']['trade']
  
      async def fetch_balance(self, params={}):
          request = self.extend({
              'venueId': self.options['venueId'],
              'accountId': await self.get_active_account(),
          }, params)
          if not ('accountId' in request):
              raise ArgumentsRequired(self.id + " fetchBalance requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'.")
          response = await self.privateGetVenuesVenueIdAccountsAccountId(request)
          balances = self.safe_value(response['result'], 'balances')
          result = {'info': response}
          for i in range(0, len(balances)):
              balance = balances[i]
              assetId = self.safe_string(balance, 'assetId')
              if assetId is not None:
                  currencyId = assetId.split('/')[0]
                  code = self.safe_currency_code(currencyId)
                  account = {}
                  account['total'] = self.safe_float(balance, 'amount')
                  account['free'] = self.safe_float(balance, 'availableForTrade')
                  result[code] = account
          return self.parse_balance(result)
  
      async def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
          await self.load_markets()
          request = self.extend({
              'venueId': self.options['venueId'],
              'accountId': await self.get_active_account(),
          }, params)
          if not request['accountId']:
              raise ArgumentsRequired(self.id + " fetchMyTrades requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'.")
          response = await self.privateGetVenuesVenueIdAccountsAccountIdTrades(request)
          market = None
          if symbol is not None:
              market = self.market(symbol)
          return self.parse_trades(response['result'], market, since, limit)
  
      async def create_deposit_address(self, code, params={}):
          await self.load_markets()
          paymentMethod = self.safe_string(self.options['paymentMethods'], code)
          if paymentMethod is None:
              raise NotSupported(self.id + ' createDepositAddress requires code to be BTC, ETH, or XLM')
          request = self.extend({
              'venueId': self.options['venueId'],
              'accountId': await self.get_active_account(),
              'assetId': self.currency_id(code),
              'paymentMethod': paymentMethod,
          }, params)
          if not request['accountId']:
              raise ArgumentsRequired(self.id + " createDepositAddress requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'.")
          response = await self.privatePostVenuesVenueIdAccountsAccountIdDeposit(request)
          #
          #     {
          #         assetId: 'BTC/stronghold.co',
          #         paymentMethod: 'bitcoin',
          #         paymentMethodInstructions: {
          #             deposit_address: 'mzMT9Cfw8JXVWK7rMonrpGfY9tt57ytHt4',
          #             reference: 'sometimes-exists',
          #         },
          #         direction: 'deposit',
          #     }
          #
          data = response['result']['paymentMethodInstructions']
          address = data['deposit_address']
          tag = self.safe_string(data, 'reference')
          return {
              'currency': code,
              'address': self.check_address(address),
              'tag': tag,
              'info': response,
          }
  
      async def withdraw(self, code, amount, address, tag=None, params={}):
          await self.load_markets()
          paymentMethod = self.safe_string(self.options['paymentMethods'], code)
          if paymentMethod is None:
              raise NotSupported(self.id + ' withdraw requires code to be BTC, ETH, or XLM')
          request = self.extend({
              'venueId': self.options['venueId'],
              'accountId': await self.get_active_account(),
              'assetId': self.currency_id(code),
              'amount': amount,
              'paymentMethod': paymentMethod,
              'paymentMethodDetails': {
                  'withdrawal_address': address,
              },
          }, params)
          if tag is not None:
              request['paymentMethodDetails']['reference'] = tag
          if not request['accountId']:
              raise ArgumentsRequired(self.id + " withdraw requires either the 'accountId' extra parameter or exchange.options['accountId'] = 'YOUR_ACCOUNT_ID'.")
          response = await self.privatePostVenuesVenueIdAccountsAccountIdWithdrawal(request)
          #
          #     {
          #         "id": "5be48892-1b6e-4431-a3cf-34b38811e82c",
          #         "assetId": "BTC/stronghold.co",
          #         "amount": "10",
          #         "feeAmount": "0.01",
          #         "paymentMethod": "bitcoin",
          #         "paymentMethodDetails": {
          #             "withdrawal_address": "1vHysJeXYV6nqhroBaGi52QWFarbJ1dmQ"
          #         },
          #         "direction": "withdrawal",
          #         "status": "pending"
          #     }
          #
          data = response['result']
          return {
              'id': self.safe_string(data, 'id'),
              'info': response,
          }
  
      def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
          if not response:
              return  # fallback to base error handler by default
          #
          #     {
          #         requestId: '3e7d17ab-b316-4721-b5aa-f7e6497eeab9',
          #         timestamp: '2019-01-31T21:59:06.696855Z',
          #         success: True,
          #         statusCode: 200,
          #         result: []
          #     }
          #
          errorCode = self.safe_string(response, 'errorCode')
          if errorCode in self.exceptions:
              Exception = self.exceptions[errorCode]
              raise Exception(self.id + ' ' + body)
          success = self.safe_value(response, 'success')
          if not success:
              raise ExchangeError(self.id + ' ' + body)
  
      def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
          request = '/' + self.version + '/' + self.implode_params(path, params)
          query = self.omit(params, self.extract_params(path))
          url = self.urls['api'][api] + request
          if query:
              if method == 'GET':
                  url += '?' + self.urlencode(query)
              else:
                  body = self.json(query)
          if api == 'private':
              self.check_required_credentials()
              timestamp = str(self.nonce())
              payload = timestamp + method + request
              if body is not None:
                  payload += body
              secret = base64.b64decode(self.secret)
              headers = {
                  'SH-CRED-ID': self.apiKey,
                  'SH-CRED-SIG': self.hmac(self.encode(payload), secret, hashlib.sha256, 'base64'),
                  'SH-CRED-TIME': timestamp,
                  'SH-CRED-PASS': self.password,
                  'Content-Type': 'application/json',
              }
          return {'url': url, 'method': method, 'body': body, 'headers': headers}
  