def _deserialize_asset_movement( self, raw_data: Dict[str, Any]) -> Optional[AssetMovement]: """Processes a single deposit/withdrawal from bittrex and deserializes it Can log error/warning and return None if something went wrong at deserialization """ try: if raw_data['status'] != 'COMPLETED': # Don't mind failed/in progress asset movements return None if 'source' in raw_data: category = AssetMovementCategory.DEPOSIT fee = Fee(ZERO) else: category = AssetMovementCategory.WITHDRAWAL fee = deserialize_fee(raw_data.get('txCost', 0)) timestamp = deserialize_timestamp_from_date( date=raw_data['completedAt'], # we only check completed orders formatstr='iso8601', location='bittrex', ) asset = asset_from_bittrex(raw_data['currencySymbol']) return AssetMovement( location=Location.BITTREX, category=category, address=deserialize_asset_movement_address( raw_data, 'cryptoAddress', asset), transaction_id=get_key_if_has_val(raw_data, 'txId'), timestamp=timestamp, asset=asset, amount=deserialize_asset_amount_force_positive( raw_data['quantity']), fee_asset=asset, fee=fee, link=str(raw_data.get('txId', '')), ) except UnknownAsset as e: self.msg_aggregator.add_warning( f'Found bittrex deposit/withdrawal with unknown asset ' f'{e.asset_name}. Ignoring it.', ) except UnsupportedAsset as e: self.msg_aggregator.add_warning( f'Found bittrex deposit/withdrawal with unsupported asset ' f'{e.asset_name}. Ignoring it.', ) except (DeserializationError, KeyError) as e: msg = str(e) if isinstance(e, KeyError): msg = f'Missing key entry for {msg}.' self.msg_aggregator.add_error( 'Unexpected data encountered during deserialization of a bittrex ' 'asset movement. Check logs for details and open a bug report.', ) log.error( f'Unexpected data encountered during deserialization of bittrex ' f'asset_movement {raw_data}. Error was: {str(e)}', ) return None
def bittrex_pair_to_world(given_pair: str) -> TradePair: """ Turns a pair written in the bittrex way to Rotkehlchen way Throws: - UnsupportedAsset due to asset_from_bittrex() - UnprocessableTradePair if the pair can't be split into its parts """ if not isinstance(given_pair, str): raise DeserializationError( f'Could not deserialize bittrex trade pair. Expected a string ' f'but found {type(given_pair)}', ) pair = TradePair(given_pair.replace('-', '_')) # Check that there is no unsupported asset in the trade _ = asset_from_bittrex(get_pair_position_str(pair, 'first')) _ = asset_from_bittrex(get_pair_position_str(pair, 'second')) return pair
def bittrex_pair_to_world(given_pair: str) -> Tuple[Asset, Asset]: """ Turns a pair written in bittrex to way to rotki base/quote asset Throws: - UnsupportedAsset due to asset_from_bittrex() - UnprocessableTradePair if the pair can't be split into its parts """ if not isinstance(given_pair, str): raise DeserializationError( f'Could not deserialize bittrex trade pair. Expected a string ' f'but found {type(given_pair)}', ) pair = TradePair(given_pair.replace('-', '_')) base_asset = asset_from_bittrex(get_pair_position_str(pair, 'first')) quote_asset = asset_from_bittrex(get_pair_position_str(pair, 'second')) return base_asset, quote_asset
def bittrex_pair_to_world(given_pair: str) -> TradePair: """ Turns a pair written in the bittrex way to Rotkehlchen way Throws: - UnsupportedAsset due to asset_from_bittrex() - UnprocessableTradePair if the pair can't be split into its parts """ pair = TradePair(given_pair.replace('-', '_')) base_currency = asset_from_bittrex(get_pair_position_str(pair, 'first')) quote_currency = asset_from_bittrex(get_pair_position_str(pair, 'second')) # Since in Bittrex the base currency is the cost currency, iow in Bittrex # for BTC_ETH we buy ETH with BTC and sell ETH for BTC, we need to turn it # into the Rotkehlchen way which is following the base/quote approach. pair = trade_pair_from_assets(quote_currency, base_currency) return pair
def test_bittrex_assets_are_known(bittrex): currencies = bittrex.get_currencies() for bittrex_asset in currencies: symbol = bittrex_asset['Currency'] try: _ = asset_from_bittrex(symbol) except UnsupportedAsset: assert symbol in UNSUPPORTED_BITTREX_ASSETS
def query_balances(self) -> ExchangeQueryBalances: try: resp = self.api_query('balances') except RemoteError as e: msg = ('Bittrex API request failed. Could not reach bittrex due ' 'to {}'.format(e)) log.error(msg) return None, msg returned_balances: Dict[Asset, Balance] = {} for entry in resp: try: asset = asset_from_bittrex(entry['currencySymbol']) except UnsupportedAsset as e: self.msg_aggregator.add_warning( f'Found unsupported bittrex asset {e.asset_name}. ' f' Ignoring its balance query.', ) continue except UnknownAsset as e: self.msg_aggregator.add_warning( f'Found unknown bittrex asset {e.asset_name}. ' f' Ignoring its balance query.', ) continue except DeserializationError: self.msg_aggregator.add_error( f'Found bittrex asset with non-string type {type(entry["Currency"])}' f' Ignoring its balance query.', ) continue if entry['currencySymbol'] == 'BTXCRD': # skip BTXCRD balance, since it's bittrex internal and we can't query usd price continue try: usd_price = Inquirer().find_usd_price(asset=asset) except RemoteError as e: self.msg_aggregator.add_error( f'Error processing bittrex balance entry due to inability to ' f'query USD price: {str(e)}. Skipping balance entry', ) continue amount = FVal(entry['total']) usd_value = amount * usd_price returned_balances[asset] = Balance( amount=amount, usd_value=usd_value, ) log.debug( 'bittrex balance query result', sensitive_log=True, currency=asset, amount=amount, usd_value=usd_value, ) return returned_balances, ''
def test_bittrex_assets_are_known(bittrex): currencies = bittrex.get_currencies() for bittrex_asset in currencies: symbol = bittrex_asset['Currency'] try: _ = asset_from_bittrex(symbol) except UnsupportedAsset: assert symbol in UNSUPPORTED_BITTREX_ASSETS except UnknownAsset as e: test_warnings.warn(UserWarning( f'Found unknown asset {e.asset_name} in Bittrex. Support for it has to be added', ))
def query_balances( self) -> Tuple[Optional[Dict[Asset, Dict[str, Any]]], str]: try: resp = self.api_query('getbalances') except RemoteError as e: msg = ('Bittrex API request failed. Could not reach bittrex due ' 'to {}'.format(e)) log.error(msg) return None, msg returned_balances = {} for entry in resp: try: asset = asset_from_bittrex(entry['Currency']) except UnsupportedAsset as e: self.msg_aggregator.add_warning( f'Found unsupported bittrex asset {e.asset_name}. ' f' Ignoring its balance query.', ) continue except UnknownAsset as e: self.msg_aggregator.add_warning( f'Found unknown bittrex asset {e.asset_name}. ' f' Ignoring its balance query.', ) continue except DeserializationError: self.msg_aggregator.add_error( f'Found bittrex asset with non-string type {type(entry["Currency"])}' f' Ignoring its balance query.', ) continue try: usd_price = Inquirer().find_usd_price(asset=asset) except RemoteError as e: self.msg_aggregator.add_error( f'Error processing bittrex balance entry due to inability to ' f'query USD price: {str(e)}. Skipping balance entry', ) continue balance = {} balance['amount'] = FVal(entry['Balance']) balance['usd_value'] = FVal(balance['amount']) * usd_price returned_balances[asset] = balance log.debug( 'bittrex balance query result', sensitive_log=True, currency=asset, amount=balance['amount'], usd_value=balance['usd_value'], ) return returned_balances, ''
def _deserialize_asset_movement(self, raw_data: Dict[str, Any]) -> Optional[AssetMovement]: """Processes a single deposit/withdrawal from bittrex and deserializes it Can log error/warning and return None if something went wrong at deserialization """ try: if 'TxCost' in raw_data: category = AssetMovementCategory.WITHDRAWAL date_key = 'Opened' fee = deserialize_fee(raw_data['TxCost']) else: category = AssetMovementCategory.DEPOSIT date_key = 'LastUpdated' fee = Fee(ZERO) timestamp = deserialize_timestamp_from_bittrex_date(raw_data[date_key]) asset = asset_from_bittrex(raw_data['Currency']) return AssetMovement( location=Location.BITTREX, category=category, timestamp=timestamp, asset=asset, amount=deserialize_asset_amount(raw_data['Amount']), fee_asset=asset, fee=fee, link=str(raw_data['TxId']), ) except UnknownAsset as e: self.msg_aggregator.add_warning( f'Found bittrex deposit/withdrawal with unknown asset ' f'{e.asset_name}. Ignoring it.', ) except UnsupportedAsset as e: self.msg_aggregator.add_warning( f'Found bittrex deposit/withdrawal with unsupported asset ' f'{e.asset_name}. Ignoring it.', ) except (DeserializationError, KeyError) as e: msg = str(e) if isinstance(e, KeyError): msg = f'Missing key entry for {msg}.' self.msg_aggregator.add_error( f'Unexpected data encountered during deserialization of a bittrex ' f'asset movement. Check logs for details and open a bug report.', ) log.error( f'Unexpected data encountered during deserialization of bittrex ' f'asset_movement {raw_data}. Error was: {str(e)}', ) return None
def test_bittrex_assets_are_known(bittrex): unsupported_assets = set(UNSUPPORTED_BITTREX_ASSETS) common_items = unsupported_assets.intersection( set(WORLD_TO_BITTREX.values())) assert not common_items, f'Bittrex assets {common_items} should not be unsupported' currencies = bittrex.get_currencies() for bittrex_asset in currencies: symbol = bittrex_asset['symbol'] try: _ = asset_from_bittrex(symbol) except UnsupportedAsset: assert symbol in UNSUPPORTED_BITTREX_ASSETS except UnknownAsset as e: test_warnings.warn( UserWarning( f'Found unknown asset {e.asset_name} in Bittrex. Support for it has to be added', ))
def query_balances(self) -> Tuple[Optional[Dict[Asset, Dict[str, Any]]], str]: try: resp = self.api_query('getbalances') except RemoteError as e: msg = ( 'Bittrex API request failed. Could not reach bittrex due ' 'to {}'.format(e) ) log.error(msg) return None, msg returned_balances = dict() for entry in resp: try: asset = asset_from_bittrex(entry['Currency']) except UnsupportedAsset as e: self.msg_aggregator.add_warning( f'Found unsupported bittrex asset {e.asset_name}. ' f' Ignoring its balance query.', ) continue except UnknownAsset as e: self.msg_aggregator.add_warning( f'Found unknown bittrex asset {e.asset_name}. ' f' Ignoring its balance query.', ) continue usd_price = Inquirer().find_usd_price(asset=asset) balance = dict() balance['amount'] = FVal(entry['Balance']) balance['usd_value'] = FVal(balance['amount']) * usd_price returned_balances[asset] = balance log.debug( 'bittrex balance query result', sensitive_log=True, currency=asset, amount=balance['amount'], usd_value=balance['usd_value'], ) return returned_balances, ''