def __init__(self, data_directory, history_date_start=DEFAULT_START_DATE): self.data_directory = data_directory # get the start date for historical data self.historical_data_start = createTimeStamp(history_date_start, formatstr="%d/%m/%Y") self.price_history = dict() # TODO: Check if historical data is after the requested start date # Check the data folder and load any cached history prefix = os.path.join(self.data_directory, 'price_history_') regex = re.compile(prefix + '(.*)\.json') files_list = glob.glob(prefix + '*.json') for file_ in files_list: match = regex.match(file_) assert match cache_key = match.group(1) with open(file_, 'rb') as f: data = rlk_jsonloads(f.read()) self.price_history[cache_key] = data # Get coin list of crypto compare invalidate_cache = True coinlist_cache_path = os.path.join(self.data_directory, 'cryptocompare_coinlist.json') if os.path.isfile(coinlist_cache_path): with open(coinlist_cache_path, 'rb') as f: try: data = rlk_jsonloads(f.read()) now = ts_now() invalidate_cache = False # If we got a cache and its' over a month old then requery cryptocompare if data['time'] < now and now - data['time'] > 2629800: invalidate_cache = True data = data['data'] except JSONDecodeError: invalidate_cache = True if invalidate_cache: query_string = 'https://www.cryptocompare.com/api/data/coinlist/' resp = urlopen(Request(query_string)) resp = rlk_jsonloads(resp.read()) if 'Response' not in resp or resp['Response'] != 'Success': error_message = 'Failed to query cryptocompare for: "{}"'.format(query_string) if 'Message' in resp: error_message += ". Error: {}".format(resp['Message']) raise ValueError(error_message) data = resp['Data'] # Also save the cache with open(coinlist_cache_path, 'w') as f: write_data = {'time': ts_now(), 'data': data} f.write(rlk_jsondumps(write_data)) else: # in any case take the data data = data['data'] self.cryptocompare_coin_list = data # For some reason even though price for the following assets is returned # it's not in the coinlist so let's add them here. self.cryptocompare_coin_list['DAO'] = object() self.cryptocompare_coin_list['USDT'] = object()
def _api_query(self, command: str, req: Optional[Dict] = None) -> Dict: if req is None: req = {} if command == "returnTicker": log.debug('Querying poloniex for returnTicker') ret = self.session.get(self.public_uri + command) else: req['command'] = command with self.lock: # Protect this region with a lock since poloniex will reject # non-increasing nonces. So if two greenlets come in here at # the same time one of them will fail req['nonce'] = int(time.time() * 1000) post_data = str.encode(urlencode(req)) sign = hmac.new(self.secret, post_data, hashlib.sha512).hexdigest() self.session.headers.update({'Sign': sign}) log.debug( 'Poloniex private API query', command=command, post_data=req, ) ret = self.session.post('https://poloniex.com/tradingApi', req) result = rlk_jsonloads(ret.text) return self.post_process(result) return rlk_jsonloads(ret.text)
def _api_query(self, command, req={}): if command == "returnTicker" or command == "return24Volume": ret = self.session.get(self.public_uri + command) elif (command == "returnOrderBook"): ret = self.session.get(self.public_uri + command + '¤cyPair=' + str(req['currencyPair'])) elif (command == "returnMarketTradeHistory"): ret = self.session.get(self.public_uri + 'returnTradeHistory' + '¤cyPair=' + str(req['currencyPair'])) elif (command == "returnLoanOrders"): ret = self.session.get(self.public_uri + 'returnLoanOrders' + '¤cy=' + str(req['currency'])) else: req['command'] = command with self.lock: # Protect this region with a lock since poloniex will reject # non-increasing nonces. So if two greenlets come in here at # the same time one of them will fail req['nonce'] = int(time.time() * 1000) post_data = str.encode(urlencode(req)) sign = hmac.new(self.secret, post_data, hashlib.sha512).hexdigest() self.session.headers.update({'Sign': sign}) ret = self.session.post('https://poloniex.com/tradingApi', req) result = rlk_jsonloads(ret.text) return self.post_process(result) return rlk_jsonloads(ret.text)
def do_read_manual_margin_positions(data_directory): manual_margin_path = os.path.join(data_directory, MANUAL_MARGINS_LOGFILE) if os.path.isfile(manual_margin_path): with open(manual_margin_path, 'r') as f: margin_data = rlk_jsonloads(f.read()) else: margin_data = [] logger.info('Could not find manual margins log file at {}'.format( manual_margin_path)) # Now turn the manual margin data to our MarginPosition format # The poloniex manual data format is: # { "open_time": unix_timestamp, "close_time": unix_timestamp, # "btc_profit_loss": floating_point_number for profit or loss, # "notes": "optional string with notes on the margin position" # } margin_positions = list() for position in margin_data: margin_positions.append( MarginPosition( exchange='poloniex', open_time=position['open_time'], close_time=position['close_time'], profit_loss=FVal(position['btc_profit_loss']), pl_currency=NonEthTokenBlockchainAsset('BTC'), notes=position['notes'], )) return margin_positions
def __init__(self, ethrpc_port): self.connected = True # Note that you should create only one RPCProvider per # process, as it recycles underlying TCP/IP network connections between # your process and Ethereum node self.web3 = Web3( HTTPProvider('http://localhost:{}'.format(ethrpc_port))) try: self.web3.eth.blockNumber dir_path = os.path.dirname(os.path.realpath(__file__)) with open(os.path.join(dir_path, 'data', 'token_abi.json'), 'r') as f: self.token_abi = rlk_jsonloads(f.read()) # Also make sure we are actually connected to the Ethereum mainnet genesis_hash = self.web3.eth.getBlock(0)['hash'] target = '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3' if genesis_hash != target: logger.warn( 'Connected to a local ethereum node but it is not on the ethereum mainnet' ) self.connected = False except ConnectionError: logger.warn( 'Could not connect to a local ethereum node. Will use etherscan only' ) self.connected = False
def test_decoding(): strdata = """ {"a": 3.14, "b":5, "c": "foo", "d": "5.42323143", "e": { "u1": "3.221"}, "f": [2.1, "boo", 3, "4.2324"]}""" data = rlk_jsonloads(strdata) assert isinstance(data['a'], FVal) assert data['a'] == FVal('3.14') assert isinstance(data['b'], int) assert data['b'] == 5 assert isinstance(data['c'], (str, bytes)) assert data['c'] == 'foo' assert isinstance(data['d'], FVal) assert data['d'] == FVal('5.42323143') assert isinstance(data['e']['u1'], FVal) assert data['e']['u1'] == FVal('3.221') assert isinstance(data['f'][0], FVal) assert data['f'][0] == FVal('2.1') assert isinstance(data['f'][1], (str, bytes)) assert data['f'][1] == "boo" assert isinstance(data['f'][2], int) assert data['f'][2] == 3 assert isinstance(data['f'][3], FVal) assert data['f'][3] == FVal('4.2324')
def get_multieth_balance(self, accounts): """Returns a dict with keys being accounts and balances in ETH""" balances = {} if not self.connected: # TODO: accounts.length should be less than 20. If more we gotta do # multiple calls eth_resp = urlopen( Request( 'https://api.etherscan.io/api?module=account&action=balancemulti&address=%s' % ','.join(accounts))) eth_resp = rlk_jsonloads(eth_resp.read()) if eth_resp['status'] != 1: raise ValueError( 'Failed to query etherscan for accounts balance') eth_accounts = eth_resp['result'] for account_entry in eth_accounts: amount = FVal(account_entry['balance']) balances[account_entry['account']] = from_wei(amount) else: for account in accounts: amount = FVal(self.web3.eth.getBalance(account)) balances[account] = from_wei(amount) return balances
def find_usd_price(self, asset, asset_btc_price=None): if self.kraken and self.kraken.first_connection_made and asset_btc_price is not None: return self.query_kraken_for_price(asset, asset_btc_price) # Adjust some ETH tokens to how cryptocompare knows them if asset == 'RDN': asset = 'RDN*' # temporary if asset == 'DATAcoin': asset = 'DATA' resp = retry_calls( 5, 'find_usd_price', 'urllib2.urlopen', urlopen, Request( u'https://min-api.cryptocompare.com/data/price?fsym={}&tsyms=USD'.format( asset )) ) resp = rlk_jsonloads(resp.read()) # If there is an error in the response skip this token if 'USD' not in resp: if resp['Response'] == 'Error': print('Could not query USD price for {}. Error: "{}"'.format( asset, resp['Message']), ) else: print('Could not query USD price for {}'.format(asset)) return FVal(0) return FVal(resp['USD'])
def check_and_get_response(self, response, method): if response.status_code in (520, 525, 504): raise RecoverableRequestError('kraken', 'Usual kraken 5xx shenanigans') elif response.status_code != 200: raise ValueError( 'Kraken API request {} for {} failed with HTTP status ' 'code: {}'.format( response.url, method, response.status_code, )) result = rlk_jsonloads(response.text) if result['error']: if isinstance(result['error'], list): error = result['error'][0] else: error = result['error'] if 'Rate limit exceeded' in error: raise RecoverableRequestError('kraken', 'Rate limited exceeded') else: raise ValueError(error) return result['result']
def find_usd_price(self, asset, asset_btc_price=None): if self.kraken and self.kraken.first_connection_made and asset_btc_price is not None: return self.query_kraken_for_price(asset, asset_btc_price) # Adjust some ETH tokens to how cryptocompare knows them if asset == 'RDN': asset = 'RDN*' # temporary if asset == 'DATAcoin': asset = 'DATA' resp = retry_calls( 5, 'find_usd_price', 'requests.get', requests.get, u'https://min-api.cryptocompare.com/data/price?' 'fsym={}&tsyms=USD'.format(asset)) if resp.status_code != 200: raise RemoteError( 'Cant reach cryptocompare to get USD value of {}'.format( asset)) resp = rlk_jsonloads(resp.text) # If there is an error in the response skip this token if 'USD' not in resp: if resp['Response'] == 'Error': print( 'Could not query USD price for {}. Error: "{}"'.format( asset, resp['Message']), ) else: print('Could not query USD price for {}'.format(asset)) return FVal(0) return FVal(resp['USD'])
def api_query(self, method, options=None): """ Queries Bittrex with given method and options """ if not options: options = {} nonce = str(int(time.time() * 1000)) method_type = 'public' if method in BITTREX_MARKET_METHODS: method_type = 'market' elif method in BITTREX_ACCOUNT_METHODS: method_type = 'account' request_url = self.uri + method_type + '/' + method + '?' if method_type != 'public': request_url += 'apikey=' + self.api_key.decode() + "&nonce=" + nonce + '&' request_url += urlencode(options) signature = hmac.new( self.secret, request_url.encode(), hashlib.sha512 ).hexdigest() self.session.headers.update({'apisign': signature}) response = self.session.get(request_url) json_ret = rlk_jsonloads(response.text) if json_ret['success'] is not True: raise ValueError(json_ret['message']) return json_ret['result']
def api_query(self, method: str, options: Optional[Dict] = None) -> Union[List, Dict]: if not options: options = {} with self.lock: # Protect this region with a lock since binance will reject # non-increasing nonces. So if two greenlets come in here at # the same time one of them will fail if method in V3_ENDPOINTS: api_version = 3 # Recommended recvWindows is 5000 but we get timeouts with it options['recvWindow'] = 10000 options['timestamp'] = str(int(time.time() * 1000)) signature = hmac.new(self.secret, urlencode(options).encode('utf-8'), hashlib.sha256).hexdigest() options['signature'] = signature elif method in V1_ENDPOINTS: api_version = 1 else: raise ValueError( 'Unexpected binance api method {}'.format(method)) request_url = self.uri + 'v' + str( api_version) + '/' + method + '?' request_url += urlencode(options) log.debug('Binance API request', request_url=request_url) response = self.session.get(request_url) if response.status_code != 200: result = rlk_jsonloads(response.text) raise RemoteError( 'Binance API request {} for {} failed with HTTP status ' 'code: {}, error code: {} and error message: {}'.format( response.url, method, response.status_code, result['code'], result['msg'], )) json_ret = rlk_jsonloads(response.text) return json_ret
def __init__(self, data_directory): self.data_directory = data_directory self.db = None self.eth_tokens = [] dir_path = os.path.dirname(os.path.realpath(__file__)) with open(os.path.join(dir_path, 'data', 'eth_tokens.json'), 'r') as f: self.eth_tokens = rlk_jsonloads(f.read())
def query_private(self, method: str, req: Optional[dict] = None) -> dict: self.first_connection() if method == 'Balance': return generate_random_kraken_balance_response() elif method == 'TradesHistory': trades_num = random.randint(1, 49) start = req['start'] end = req['end'] # Trades is a dict with txid as the key trades = {} for _ in range(trades_num): trade = generate_random_kraken_trade_data( list(self.tradeable_pairs.keys()), start, end, ) trades[trade['ordertxid']] = trade response_str = json.dumps({'trades': trades, 'count': trades_num}) return rlk_jsonloads(response_str) elif method == 'Ledgers': ledgers_num = random.randint(1, 49) start = req['start'] end = req['end'] ledger_type = req['type'] # Ledgers is a dict with txid as the key ledgers = {} for _ in range(ledgers_num): ledger = generate_random_kraken_ledger_data( start_ts=start, end_ts=end, ledger_type=ledger_type, ) ledgers[ledger['refid']] = ledger response_str = json.dumps({ 'ledger': ledgers, 'count': ledgers_num }) return rlk_jsonloads(response_str) return super().query_private(method, req)
def do_read_manual_margin_positions(data_directory): manual_margin_path = os.path.join(data_directory, MANUAL_MARGINS_LOGFILE) if os.path.isfile(manual_margin_path): with open(manual_margin_path, 'r') as f: margin_data = rlk_jsonloads(f.read()) else: margin_data = [] logger.error('Could not find manual margins log file at {}'.format( manual_margin_path)) return margin_data
def __init__(self, data_directory): self.data_directory = data_directory try: with open(os.path.join(self.data_directory, 'settings.json')) as f: self.settings = rlk_jsonloads(f.read()) except JSONDecodeError as e: logger.critical( 'settings.json file could not be decoded and is corrupt: {}'. format(e)) self.settings = empty_settings except FileNotFoundError: self.settings = empty_settings self.db = None self.eth_tokens = [] dir_path = os.path.dirname(os.path.realpath(__file__)) with open(os.path.join(dir_path, 'data', 'eth_tokens.json'), 'r') as f: self.eth_tokens = rlk_jsonloads(f.read())
def __init__(self, data_dir, kraken=None): self.kraken = kraken self.session = requests.session() self.data_directory = data_dir filename = os.path.join(self.data_directory, 'price_history_forex.json') try: with open(filename, 'rb') as f: data = rlk_jsonloads(f.read()) self.cached_forex_data = data except (OSError, IOError, JSONDecodeError): self.cached_forex_data = dict()
def attempt_connect(self, ethrpc_port, mainnet_check=True) -> Tuple[bool, str]: if self.rpc_port == ethrpc_port and self.connected: # We are already connected return True, 'Already connected to an ethereum node' if self.web3: del self.web3 try: self.web3 = Web3( HTTPProvider('http://localhost:{}'.format(ethrpc_port))) except ConnectionError: logger.warn( 'Could not connect to a local ethereum node. Will use etherscan only' ) self.connected = False return False, 'Failed to connect to ethereum node at port {}'.format( ethrpc_port) if self.web3.isConnected(): dir_path = os.path.dirname(os.path.realpath(__file__)) with open(os.path.join(dir_path, 'data', 'token_abi.json'), 'r') as f: self.token_abi = rlk_jsonloads(f.read()) # Also make sure we are actually connected to the Ethereum mainnet if mainnet_check: genesis_hash = self.web3.eth.getBlock(0)['hash'].hex() target = '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3' if genesis_hash != target: logger.warn( 'Connected to a local ethereum node but it is not on the ethereum mainnet' ) self.connected = False message = ( 'Connected to ethereum node at port {} but it is not on ' 'the ethereum mainnet'.format(ethrpc_port)) return False, message self.connected = True return True, '' else: logger.warn( 'Could not connect to a local ethereum node. Will use etherscan only' ) self.connected = False message = 'Failed to connect to ethereum node at port {}'.format( ethrpc_port) # If we get here we did not connnect return False, message
def get_eth_balance(self, account): if not self.connected: eth_resp = urlopen( Request( 'https://api.etherscan.io/api?module=account&action=balance&address=%s' % account)) eth_resp = rlk_jsonloads(eth_resp.read()) if eth_resp['status'] != 1: raise ValueError( 'Failed to query etherscan for accounts balance') amount = FVal(eth_resp['result']) return from_wei(amount) else: return from_wei(self.web3.eth.getBalance(account))
def check_trades_cache(self, start_ts, end_ts, special_name=None): trades_file = self._get_cachefile_name(special_name) trades = dict() if os.path.isfile(trades_file): with open(trades_file, 'r') as f: try: trades = rlk_jsonloads(f.read()) except JSONDecodeError: pass # no need to query again if data_up_todate(trades, start_ts, end_ts): return trades['data'] return None
def process_response(self, response): result_or_error = '' success = False if response.status_code not in HANDLABLE_STATUS_CODES: result_or_error = ( 'Unexpected status response({}) from rotkehlchen server'. format(response.status_code)) else: result_or_error = rlk_jsonloads(response.text) if 'error' in result_or_error: result_or_error = result_or_error['error'] else: success = True return success, result_or_error
def got_cached_price(self, cache_key, timestamp): """Check if we got a price history for the timestamp cached""" if cache_key in self.price_history_file: if cache_key not in self.price_history: try: with open(self.price_history_file[cache_key], 'rb') as f: data = rlk_jsonloads(f.read()) self.price_history[cache_key] = data except (OSError, IOError, JSONDecodeError): return False in_range = ( self.price_history[cache_key]['start_time'] <= timestamp and self.price_history[cache_key]['end_time'] > timestamp) if in_range: return True return False
def _process_response( response: requests.Response) -> Tuple[bool, Union[Dict, List, str]]: """Processess a response returned from the Rotkehlchen server and returns the result for success or the error string if an error happened""" result_or_error: Union[Dict, List, str] = '' success = False if response.status_code not in HANDLABLE_STATUS_CODES: result_or_error = ( 'Unexpected status response({}) from rotkehlchen server'.format( response.status_code)) else: result_or_error = rlk_jsonloads(response.text) if isinstance(result_or_error, Dict) and 'error' in result_or_error: result_or_error = result_or_error['error'] else: success = True return success, result_or_error
def query_txlist(address, internal, from_block=None, to_block=None): result = list() if internal: reqstring = ('https://api.etherscan.io/api?module=account&action=' 'txlistinternal&address={}'.format(address)) else: reqstring = ('https://api.etherscan.io/api?module=account&action=' 'txlist&address={}'.format(address)) if from_block: reqstring += '&startblock={}'.format(from_block) if to_block: reqstring += '&endblock={}'.format(to_block) resp = urlopen(Request(reqstring)) resp = rlk_jsonloads(resp.read()) if 'status' not in resp or convert_to_int(resp['status']) != 1: status = convert_to_int(resp['status']) if status == 0 and resp['message'] == 'No transactions found': return list() # else unknown error raise ValueError( 'Failed to query txlist from etherscan with query: {} . ' 'Response was: {}'.format(reqstring, resp)) for v in resp['result']: # internal tx list contains no gasprice gas_price = -1 if internal else FVal(v['gasPrice']) result.append( EthereumTransaction( timestamp=convert_to_int(v['timeStamp']), block_number=convert_to_int(v['blockNumber']), hash=v['hash'], from_address=v['from'], to_address=v['to'], value=FVal(v['value']), gas=FVal(v['gas']), gas_price=gas_price, gas_used=FVal(v['gasUsed']), )) return result
def check_trades_cache( self, start_ts: typing.Timestamp, end_ts: typing.Timestamp, special_name: str = None) -> Optional[Union[List, Dict]]: trades_file = self._get_cachefile_name(special_name) trades: dict = dict() if os.path.isfile(trades_file): with open(trades_file, 'r') as f: try: trades = rlk_jsonloads(f.read()) except JSONDecodeError: pass # no need to query again if data_up_todate(trades, start_ts, end_ts): return trades['data'] return None
def _api_query(self, command: str, req: Optional[Dict] = None) -> Union[Dict, List]: if req is None: req = {} if command == "returnTicker": log.debug('Querying poloniex for returnTicker') ret = self.session.get(self.public_uri + command) else: req['command'] = command with self.lock: # Protect this region with a lock since poloniex will reject # non-increasing nonces. So if two greenlets come in here at # the same time one of them will fail req['nonce'] = int(time.time() * 1000) post_data = str.encode(urlencode(req)) sign = hmac.new(self.secret, post_data, hashlib.sha512).hexdigest() self.session.headers.update({'Sign': sign}) log.debug( 'Poloniex private API query', command=command, post_data=req, ) ret = self.session.post('https://poloniex.com/tradingApi', req) if ret.status_code != 200: raise RemoteError( f'Poloniex query responded with error status code: {ret.status_code}' f' and text: {ret.text}', ) try: result = rlk_jsonloads_dict(ret.text) except JSONDecodeError: raise RemoteError( f'Poloniex returned invalid JSON response: {ret.text}') return _post_process(result) return rlk_jsonloads(ret.text)
def find_usd_price( self, asset: typing.Asset, asset_btc_price: Optional[FVal] = None, ) -> FVal: if self.kraken and self.kraken.first_connection_made and asset_btc_price is not None: price = self.query_kraken_for_price(asset, asset_btc_price) log.debug('Get usd price from kraken', asset=asset, price=price) return price log.debug('Get usd price from cryptocompare', asset=asset) asset = world_to_cryptocompare(asset) resp = retry_calls( 5, 'find_usd_price', 'requests.get', requests.get, u'https://min-api.cryptocompare.com/data/price?' 'fsym={}&tsyms=USD'.format(asset)) if resp.status_code != 200: raise RemoteError( 'Cant reach cryptocompare to get USD value of {}'.format( asset)) resp = rlk_jsonloads(resp.text) # If there is an error in the response skip this token if 'USD' not in resp: error_message = '' if resp['Response'] == 'Error': error_message = resp['Message'] log.error( 'Cryptocompare usd price query failed', asset=asset, error=error_message, ) return FVal(0) price = FVal(resp['USD']) log.debug('Got usd price from cryptocompare', asset=asset, price=price) return price
def api_query( self, method: str, options: Optional[Dict] = None, ) -> Union[List, Dict]: """ Queries Bittrex with given method and options """ if not options: options = {} nonce = str(int(time.time() * 1000)) method_type = 'public' if method in BITTREX_MARKET_METHODS: method_type = 'market' elif method in BITTREX_ACCOUNT_METHODS: method_type = 'account' request_url = self.uri + method_type + '/' + method + '?' if method_type != 'public': request_url += 'apikey=' + self.api_key.decode( ) + "&nonce=" + nonce + '&' request_url += urlencode(options) signature = hmac.new(self.secret, request_url.encode(), hashlib.sha512).hexdigest() self.session.headers.update({'apisign': signature}) log.debug('Bittrex API query', request_url=request_url) response = self.session.get(request_url) try: json_ret = rlk_jsonloads(response.text) except JSONDecodeError: raise RemoteError('Bittrex returned invalid JSON response') if json_ret['success'] is not True: raise RemoteError(json_ret['message']) return json_ret['result']
def get_multitoken_balance(self, token_symbol, token_address, token_decimals, accounts): """Return a dictionary with keys being accounts and value balances of token Balance value is normalized through the token decimals. """ balances = {} if self.connected: token_contract = self.web3.eth.contract(address=token_address, abi=self.token_abi) for account in accounts: token_amount = FVal(token_contract.call().balanceOf(account)) if token_amount != 0: balances[account] = token_amount / (FVal(10)** FVal(token_decimals)) else: for account in accounts: print('Checking token {} for account {}'.format( token_symbol, account)) resp = urlopen( Request( 'https://api.etherscan.io/api?module=account&action=' 'tokenbalance&contractaddress={}&address={}'.format( token_address, account, ))) resp = rlk_jsonloads(resp.read()) if resp['status'] != 1: raise ValueError( 'Failed to query etherscan for {} token balance of {}'. format( token_symbol, account, )) token_amount = FVal(resp['result']) if token_amount != 0: balances[account] = token_amount / (FVal(10)** FVal(token_decimals)) return balances
def find_usd_price( self, asset: typing.Asset, asset_btc_price: Optional[FVal] = None, ) -> FVal: if self.kraken and self.kraken.first_connection_made and asset_btc_price is not None: return self.query_kraken_for_price(asset, asset_btc_price) # Adjust some ETH tokens to how cryptocompare knows them if asset == S_RDN: # remove this if cryptocompare changes the symbol asset = cast(typing.EthToken, 'RDN*') if asset == S_DATACOIN: asset = cast(typing.NonEthTokenBlockchainAsset, 'DATA') resp = retry_calls( 5, 'find_usd_price', 'requests.get', requests.get, u'https://min-api.cryptocompare.com/data/price?' 'fsym={}&tsyms=USD'.format(asset)) if resp.status_code != 200: raise RemoteError( 'Cant reach cryptocompare to get USD value of {}'.format( asset)) resp = rlk_jsonloads(resp.text) # If there is an error in the response skip this token if 'USD' not in resp: if resp['Response'] == 'Error': print( 'Could not query USD price for {}. Error: "{}"'.format( asset, resp['Message']), ) else: print('Could not query USD price for {}'.format(asset)) return FVal(0) return FVal(resp['USD'])