def iconomi_pair_to_world(pair: str) -> Tuple[Asset, Asset]: """May raise: - UnsupportedAsset - UnknownAsset """ tx_asset = asset_from_iconomi(pair[:3]) native_asset = asset_from_iconomi(pair[3:]) return tx_asset, native_asset
def test_iconomi_assets_are_known( database, inquirer, # pylint: disable=unused-argument ): unsupported_assets = set(UNSUPPORTED_ICONOMI_ASSETS) common_items = unsupported_assets.intersection( set(WORLD_TO_ICONOMI.values())) assert not common_items, f'Iconomi assets {common_items} should not be unsupported' # use a real Iconomi instance so that we always get the latest data iconomi = Iconomi( name='iconomi1', api_key=make_api_key(), secret=make_api_secret(), database=database, msg_aggregator=MessagesAggregator(), ) supported_tickers = iconomi.query_supported_tickers() for ticker in supported_tickers: try: _ = asset_from_iconomi(ticker) except UnknownAsset as e: test_warnings.warn( UserWarning( f'Found unknown asset {e.asset_name} in ICONOMI. ' f'Support for it has to be added', ))
def query_balances(self, **kwargs: Any) -> ExchangeQueryBalances: assets_balance: Dict[Asset, Balance] = {} try: resp_info = self._api_query('get', 'user/balance') except RemoteError as e: msg = ( 'ICONOMI API request failed. Could not reach ICONOMI due ' 'to {}'.format(e) ) log.error(msg) return None, msg if resp_info['currency'] != 'USD': raise RemoteError('Iconomi API did not return values in USD') for balance_info in resp_info['assetList']: ticker = balance_info['ticker'] try: asset = asset_from_iconomi(ticker) # There seems to be a bug in the ICONOMI API regarding balance_info['value']. # The value is supposed to be in USD, but is actually returned # in EUR. So let's use the Inquirer for now. try: usd_price = Inquirer().find_usd_price(asset=asset) except RemoteError as e: self.msg_aggregator.add_error( f'Error processing ICONOMI balance entry due to inability to ' f'query USD price: {str(e)}. Skipping balance entry', ) continue try: amount = deserialize_asset_amount(balance_info['balance']) except DeserializationError as e: self.msg_aggregator.add_warning( f'Skipping iconomi balance entry {balance_info} due to {str(e)}', ) continue assets_balance[asset] = Balance( amount=amount, usd_value=amount * usd_price, ) except (UnknownAsset, UnsupportedAsset) as e: asset_tag = 'unknown' if isinstance(e, UnknownAsset) else 'unsupported' self.msg_aggregator.add_warning( f'Found {asset_tag} ICONOMI asset {ticker}. ' f' Ignoring its balance query.', ) continue for balance_info in resp_info['daaList']: ticker = balance_info['ticker'] self.msg_aggregator.add_warning( f'Found unsupported ICONOMI strategy {ticker}. ' f' Ignoring its balance query.', ) return assets_balance, ''
def trade_from_iconomi(raw_trade: Dict) -> Trade: """Turn an iconomi trade entry to our own trade format May raise: - UnknownAsset - DeserializationError - KeyError """ timestamp = raw_trade['timestamp'] if raw_trade['type'] == 'buy_asset': trade_type = TradeType.BUY tx_asset = asset_from_iconomi(raw_trade['target_ticker']) tx_amount = deserialize_asset_amount(raw_trade['target_amount']) native_asset = asset_from_iconomi(raw_trade['source_ticker']) native_amount = deserialize_asset_amount(raw_trade['source_amount']) elif raw_trade['type'] == 'sell_asset': trade_type = TradeType.SELL tx_asset = asset_from_iconomi(raw_trade['source_ticker']) tx_amount = deserialize_asset_amount(raw_trade['source_amount']) native_amount = deserialize_asset_amount(raw_trade['target_amount']) native_asset = asset_from_iconomi(raw_trade['target_ticker']) amount = tx_amount rate = Price(native_amount / tx_amount) fee_amount = deserialize_fee(raw_trade['fee_amount']) fee_asset = asset_from_iconomi(raw_trade['fee_ticker']) return Trade( timestamp=timestamp, location=Location.ICONOMI, base_asset=tx_asset, quote_asset=native_asset, trade_type=trade_type, amount=amount, rate=rate, fee=fee_amount, fee_currency=fee_asset, link=str(raw_trade['transactionId']), )
def test_iconomi_assets_are_known( database, inquirer, # pylint: disable=unused-argument ): # use a real Iconomi instance so that we always get the latest data iconomi = Iconomi( api_key=make_api_key(), secret=make_api_secret(), database=database, msg_aggregator=MessagesAggregator(), ) supported_tickers = iconomi.query_supported_tickers() for ticker in supported_tickers: try: _ = asset_from_iconomi(ticker) except UnknownAsset as e: test_warnings.warn( UserWarning( f'Found unknown asset {e.asset_name} in ICONOMI. ' f'Support for it has to be added', ))
def query_balances(self, **kwargs: Any) -> ExchangeQueryBalances: assets_balance: Dict[Asset, Balance] = {} try: resp_info = self._api_query('get', 'user/balance') except RemoteError as e: msg = ('ICONOMI API request failed. Could not reach ICONOMI due ' 'to {}'.format(e)) log.error(msg) return None, msg if resp_info['currency'] != 'USD': raise RemoteError('Iconomi API did not return values in USD') for balance_info in resp_info['assetList']: ticker = balance_info['ticker'] try: asset = asset_from_iconomi(ticker) try: usd_value = deserialize_fval(balance_info['value'], 'usd_value', 'iconomi') except (DeserializationError, KeyError) as e: msg = str(e) if isinstance(e, KeyError): msg = f'missing key entry for {msg}.' self.msg_aggregator.add_warning( f'Skipping iconomi balance entry {balance_info} due to {msg}', ) continue try: amount = deserialize_asset_amount(balance_info['balance']) except (DeserializationError, KeyError) as e: msg = str(e) if isinstance(e, KeyError): msg = f'missing key entry for {msg}.' self.msg_aggregator.add_warning( f'Skipping iconomi balance entry {balance_info} due to {msg}', ) continue assets_balance[asset] = Balance( amount=amount, usd_value=usd_value, ) except (UnknownAsset, UnsupportedAsset) as e: asset_tag = 'unknown' if isinstance( e, UnknownAsset) else 'unsupported' self.msg_aggregator.add_warning( f'Found {asset_tag} ICONOMI asset {ticker}. ' f' Ignoring its balance query.', ) continue for balance_info in resp_info['daaList']: ticker = balance_info['ticker'] if ticker == 'AUSTS': # The AUSTS strategy is 'ICONOMI Earn'. We know that this strategy holds its # value in Anchor UST (AUST). That's why we report the user balance for this # strategy as usd_value / AUST price. try: aust_usd_price = Inquirer().find_usd_price(asset=A_AUST) except RemoteError as e: self.msg_aggregator.add_error( f'Error processing ICONOMI balance entry due to inability to ' f'query USD price: {str(e)}. Skipping balance entry', ) continue if aust_usd_price == ZERO: self.msg_aggregator.add_error( 'Error processing ICONOMI balance entry because the USD price ' 'for AUST was reported as 0. Skipping balance entry', ) continue try: usd_value = deserialize_fval(balance_info['value'], 'usd_value', 'iconomi') except (DeserializationError, KeyError) as e: msg = str(e) if isinstance(e, KeyError): msg = f'missing key entry for {msg}.' self.msg_aggregator.add_warning( f'Skipping iconomi balance entry {balance_info} due to {msg}', ) continue assets_balance[A_AUST] = Balance( amount=usd_value / aust_usd_price, usd_value=usd_value, ) else: self.msg_aggregator.add_warning( f'Found unsupported ICONOMI strategy {ticker}. ' f' Ignoring its balance query.', ) return assets_balance, ''