def test_bitpanda_exchange_assets_are_known(): """Since normal Bitpanda API has no endpoint listing supposrted assets https://developers.bitpanda.com/platform/#bitpanda-public-api Bitpanda PRO leasts some of the same assets but not all. So this test catches some, but unfortunately not all assets """ request_url = 'https://api.exchange.bitpanda.com/public/v1/currencies' try: response = requests.get(request_url) except requests.exceptions.RequestException as e: raise RemoteError( f'Bitpanda get request at {request_url} connection error: {str(e)}.', ) from e if response.status_code != 200: raise RemoteError( f'Bitpanda query responded with error status code: {response.status_code} ' f'and text: {response.text}', ) try: response_list = jsonloads_list(response.text) except json.JSONDecodeError as e: raise RemoteError( f'Bitpanda returned invalid JSON response: {response.text}') from e for entry in response_list: try: asset_from_bitpanda(entry['code']) except UnknownAsset as e: test_warnings.warn( UserWarning( f'Found unknown asset {e.asset_name} in bitpanda. ' f'Support for it has to be added', ))
def query_balances(self) -> ExchangeQueryBalances: try: wallets, _, _ = self._api_query('wallets') # asset_wallets = self._api_query('asset-wallets') fiat_wallets, _, _ = self._api_query('fiatwallets') except RemoteError as e: msg = f'Failed to query Bitpanda balances. {str(e)}' return None, msg assets_balance: DefaultDict[Asset, Balance] = defaultdict(Balance) wallets_len = len(wallets) for idx, entry in enumerate(wallets + fiat_wallets): if idx < wallets_len: symbol_key = 'cryptocoin_symbol' else: symbol_key = 'fiat_symbol' try: amount = deserialize_asset_amount( entry['attributes']['balance']) asset = asset_from_bitpanda(entry['attributes'][symbol_key]) except UnknownAsset as e: self.msg_aggregator.add_warning( f'Found unsupported/unknown Bitpanda asset {e.asset_name}. ' f' Ignoring its balance query.', ) continue except (DeserializationError, KeyError) as e: msg = str(e) if isinstance(e, KeyError): msg = f'Missing key entry for {msg}.' self.msg_aggregator.add_error( 'Error processing Bitpanda balance. Check logs ' 'for details. Ignoring it.', ) log.error( 'Error processing bitpanda balance', entry=entry, error=msg, ) continue if amount == ZERO: continue try: usd_price = Inquirer().find_usd_price(asset=asset) except RemoteError as e: self.msg_aggregator.add_error( f'Error processing Bitpanda balance entry due to inability to ' f'query USD price: {str(e)}. Skipping balance entry', ) continue assets_balance[asset] += Balance( amount=amount, usd_value=amount * usd_price, ) return dict(assets_balance), ''
def first_connection(self) -> None: if self.first_connection_made: return try: wallets, _, _ = self._api_query('wallets') fiat_wallets, _, _ = self._api_query('fiatwallets') except RemoteError as e: self.msg_aggregator.add_error( f'Failed to query Bitpanda wallets at first connection. {str(e)}', ) return wallets_len = len(wallets) for idx, entry in enumerate(wallets + fiat_wallets): if idx < wallets_len: id_key = 'cryptocoin_id' symbol_key = 'cryptocoin_symbol' mapping = self.cryptocoin_map else: id_key = 'fiat_id' symbol_key = 'fiat_symbol' mapping = self.fiat_map try: coin_id = entry['attributes'][id_key] asset = asset_from_bitpanda(entry['attributes'][symbol_key]) except UnknownAsset as e: self.msg_aggregator.add_warning( f'Found unsupported/unknown Bitpanda asset {e.asset_name}. ' f' Not adding asset to mapping during first connection.', ) continue except (DeserializationError, KeyError) as e: msg = str(e) if isinstance(e, KeyError): msg = f'Missing key entry for {msg}.' self.msg_aggregator.add_error( 'Error processing Bitpanda wallets query. Check logs ' 'for details. Ignoring it.', ) log.error( 'Error processing bitpanda wallet entry at first connection', entry=entry, error=msg, ) continue mapping[coin_id] = asset self.first_connection_made = True