Exemple #1
0
def test_fallback_to_coingecko(inquirer):  # pylint: disable=unused-argument
    """Cryptocompare does not return current prices for some assets.
    For those we are going to be using coingecko"""
    price = inquirer.find_usd_price(Asset('RARI'))
    assert price != Price(ZERO)
    price = inquirer.find_usd_price(Asset('TLN'))
    assert price != Price(ZERO)
Exemple #2
0
def query_cryptocompare_for_fiat_price(asset: Asset) -> Price:
    log.debug('Get usd price from cryptocompare', asset=asset)
    cc_asset_str = asset.to_cryptocompare()
    resp = retry_calls(
        5,
        'find_usd_price',
        'requests.get',
        requests.get,
        u'https://min-api.cryptocompare.com/data/price?'
        'fsym={}&tsyms=USD'.format(cc_asset_str),
    )

    if resp.status_code != 200:
        raise RemoteError(
            'Cant reach cryptocompare to get USD value of {}'.format(asset))

    resp = rlk_jsonloads_dict(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 Price(ZERO)

    price = Price(FVal(resp['USD']))
    log.debug('Got usd price from cryptocompare', asset=asset, price=price)
    return price
Exemple #3
0
    def _check_and_get_special_histohour_price(
        from_asset: Asset,
        to_asset: Asset,
        timestamp: Timestamp,
    ) -> Price:
        """For the given timestamp, check whether the from..to asset price
        (or viceversa) is a special histohour API case. If so, return the price
        based on the assets pair, otherwise return zero.

        NB: special histohour API cases are the ones where this Cryptocompare
        API returns zero prices per hour.
        """
        price = Price(ZERO)
        if (from_asset in CRYPTOCOMPARE_SPECIAL_HISTOHOUR_CASES
                and to_asset == A_USD or from_asset == A_USD
                and to_asset in CRYPTOCOMPARE_SPECIAL_HISTOHOUR_CASES):
            asset_data = (CRYPTOCOMPARE_SPECIAL_HISTOHOUR_CASES[from_asset]
                          if to_asset == A_USD else
                          CRYPTOCOMPARE_SPECIAL_HISTOHOUR_CASES[to_asset])
            if timestamp <= asset_data.timestamp:
                price = (asset_data.usd_price if to_asset == A_USD else Price(
                    FVal('1') / asset_data.usd_price))
                log.warning(
                    f'Query price of: {from_asset.identifier} in {to_asset.identifier} '
                    f'at timestamp {timestamp} may return zero price. '
                    f'Setting price to {price}, from timestamp {asset_data.timestamp}.',
                )
        return price
Exemple #4
0
    def query_current_price(
        self,
        from_asset: Asset,
        to_asset: Asset,
        handling_special_case: bool = False,
    ) -> Price:
        """Returns the current price of an asset compared to another asset

        - May raise RemoteError if there is a problem reaching the cryptocompare server
        or with reading the response returned by the server
        - May raise PriceQueryUnsupportedAsset if from/to assets are not known to cryptocompare
        """
        special_asset = (from_asset in CRYPTOCOMPARE_SPECIAL_CASES
                         or to_asset in CRYPTOCOMPARE_SPECIAL_CASES)
        if special_asset and not handling_special_case:
            return self._special_case_handling(
                method_name='query_current_price',
                from_asset=from_asset,
                to_asset=to_asset,
            )
        try:
            cc_from_asset_symbol = from_asset.to_cryptocompare()
            cc_to_asset_symbol = to_asset.to_cryptocompare()
        except UnsupportedAsset as e:
            raise PriceQueryUnsupportedAsset(e.asset_name) from e

        query_path = f'price?fsym={cc_from_asset_symbol}&tsyms={cc_to_asset_symbol}'
        result = self._api_query(path=query_path)
        # Up until 23/09/2020 cryptocompare may return {} due to bug.
        # Handle that case by assuming 0 if that happens
        if cc_to_asset_symbol not in result:
            return Price(ZERO)

        return Price(FVal(result[cc_to_asset_symbol]))
Exemple #5
0
    def _update_assets_prices_in_address_balances(
        address_balances: AddressBalances,
        known_asset_price: AssetPrice,
        unknown_asset_price: AssetPrice,
    ) -> None:
        """Update the pools underlying assets prices in USD (prices obtained
        via Inquirer and the Uniswap subgraph)
        """
        for lps in address_balances.values():
            for lp in lps:
                # Try to get price from either known or unknown asset price.
                # Otherwise keep existing price (zero)
                total_user_balance = FVal(0)
                for asset in lp.assets:
                    asset_ethereum_address = asset.asset.ethereum_address
                    asset_usd_price = known_asset_price.get(
                        asset_ethereum_address,
                        unknown_asset_price.get(asset_ethereum_address,
                                                Price(ZERO)),
                    )
                    # Update <LiquidityPoolAsset> if asset USD price exists
                    if asset_usd_price != Price(ZERO):
                        asset.usd_price = asset_usd_price
                        asset.user_balance.usd_value = FVal(
                            asset.user_balance.amount * asset_usd_price, )

                    total_user_balance += asset.user_balance.usd_value

                # Update <LiquidityPool> total balance in USD
                lp.user_balance.usd_value = total_user_balance
Exemple #6
0
def test_fallback_to_cached_values_within_a_month(inquirer):  # pylint: disable=unused-argument
    def mock_api_remote_fail(url, timeout):  # pylint: disable=unused-argument
        return MockResponse(500, '{"msg": "shit hit the fan"')

    # Get a date 15 days ago and insert a cached entry for EUR JPY then
    # Get a date 31 days ago and insert a cache entry for EUR CNY then
    now = ts_now()
    eurjpy_val = Price(FVal('124.123'))
    cache_data = [
        HistoricalPrice(
            from_asset=A_EUR,
            to_asset=A_JPY,
            source=HistoricalPriceOracle.XRATESCOM,
            timestamp=Timestamp(now - 86400 * 15),
            price=eurjpy_val,
        ),
        HistoricalPrice(
            from_asset=A_EUR,
            to_asset=A_CNY,
            source=HistoricalPriceOracle.XRATESCOM,
            timestamp=Timestamp(now - 86400 * 31),
            price=Price(FVal('7.719')),
        )
    ]
    GlobalDBHandler().add_historical_prices(cache_data)

    with patch('requests.get', side_effect=mock_api_remote_fail):
        # We fail to find a response but then go back 15 days and find the cached response
        result = inquirer._query_fiat_pair(A_EUR, A_JPY)
        assert result == eurjpy_val
        # The cached response for EUR CNY is too old so we will fail here
        with pytest.raises(RemoteError):
            result = inquirer._query_fiat_pair(A_EUR, A_CNY)
Exemple #7
0
    def mock_query_cryptocompare_for_fiat_price(asset: Asset) -> Price:
        if asset == A_ETH:
            return Price(eth_usd_rate)
        elif asset == A_BTC:
            return Price(btc_usd_rate)

        # else
        raise AssertionError(f'Unexpected asset {asset} at mock cryptocompare query')
Exemple #8
0
def _dict_history_to_entries(data: List[Dict[str, Any]]) -> List[PriceHistoryEntry]:
    """Turns a list of dict of history entries to a list of proper objects"""
    return [
        PriceHistoryEntry(
            time=Timestamp(entry['time']),
            low=Price(FVal(entry['low'])),
            high=Price(FVal(entry['high'])),
        ) for entry in data
    ]
Exemple #9
0
def test_fallback_to_coingecko(inquirer):  # pylint: disable=unused-argument
    """Cryptocompare does not return current prices for some assets.
    For those we are going to be using coingecko"""
    price = inquirer.find_usd_price(
        EthereumToken('0xFca59Cd816aB1eaD66534D82bc21E7515cE441CF')
    )  # RARRI # noqa: E501
    assert price != Price(ZERO)
    price = inquirer.find_usd_price(
        EthereumToken(
            '0x679131F591B4f369acB8cd8c51E68596806c3916'))  # TLN # noqa: E501
    assert price != Price(ZERO)
Exemple #10
0
def get_underlying_asset_price(token: EthereumToken) -> Optional[Price]:
    """Gets the underlying asset price for the given ethereum token

    TODO: This should be eventually pulled from the assets DB. All of these
    need to be updated, to contain proper protocol, and underlying assets.

    This function is neither in inquirer.py or chain/ethereum/defi.py
    due to recursive import problems
    """
    price = None
    if token.protocol == UNISWAP_PROTOCOL:
        price = Inquirer().find_uniswap_v2_lp_price(token)
    elif token.protocol == CURVE_POOL_PROTOCOL:
        price = Inquirer().find_curve_pool_price(token)
    elif token.protocol == YEARN_VAULTS_V2_PROTOCOL:
        price = Inquirer().find_yearn_price(token)

    if token == A_YV1_ALINK:
        price = Inquirer().find_usd_price(A_ALINK_V1)
    elif token == A_YV1_GUSD:
        price = Inquirer().find_usd_price(A_GUSD)
    elif token in (A_YV1_DAI, A_FARM_DAI):
        price = Inquirer().find_usd_price(A_DAI)
    elif token in (A_FARM_WETH, A_YV1_WETH):
        price = Inquirer().find_usd_price(A_ETH)
    elif token == A_YV1_YFI:
        price = Inquirer().find_usd_price(A_YFI)
    elif token in (A_FARM_USDT, A_YV1_USDT):
        price = Inquirer().find_usd_price(A_USDT)
    elif token in (A_FARM_USDC, A_YV1_USDC):
        price = Inquirer().find_usd_price(A_USDC)
    elif token in (A_FARM_TUSD, A_YV1_TUSD):
        price = Inquirer().find_usd_price(A_TUSD)
    elif token in ASSETS_UNDERLYING_BTC:
        price = Inquirer().find_usd_price(A_BTC)

    # At this point we have to return the price if it's not None. If we don't do this and got
    # a price for a token that has underlying assets, the code will enter the if statement after
    # this block and the value for price will change becoming incorrect.
    if price is not None:
        return price

    custom_token = GlobalDBHandler().get_ethereum_token(token.ethereum_address)
    if custom_token and custom_token.underlying_tokens is not None:
        usd_price = ZERO
        for underlying_token in custom_token.underlying_tokens:
            token = EthereumToken(underlying_token.address)
            usd_price += Inquirer().find_usd_price(
                token) * underlying_token.weight
        if usd_price != Price(ZERO):
            price = Price(usd_price)

    return price
Exemple #11
0
    def get_fiat_usd_exchange_rates(currencies: Iterable[Asset]) -> Dict[Asset, Price]:
        """Gets the USD exchange rate of any of the given assets

        In case of failure to query a rate it's returned as zero"""
        rates = {A_USD: Price(FVal(1))}
        for currency in currencies:
            try:
                rates[currency] = Inquirer()._query_fiat_pair(A_USD, currency)
            except RemoteError:
                rates[currency] = Price(ZERO)

        return rates
Exemple #12
0
    def query_endpoint_pricehistorical(
            self,
            from_asset: Asset,
            to_asset: Asset,
            timestamp: Timestamp,
            handling_special_case: bool = False,
    ) -> Price:
        """Queries the historical daily price of from_asset to to_asset for timestamp

        - May raise RemoteError if there is a problem reaching the cryptocompare server
        or with reading the response returned by the server
        - May raise PriceQueryUnsupportedAsset if from/to assets are not known to cryptocompare
        """
        log.debug(
            'Querying cryptocompare for daily historical price',
            from_asset=from_asset,
            to_asset=to_asset,
            timestamp=timestamp,
        )
        special_asset = (
            from_asset in CRYPTOCOMPARE_SPECIAL_CASES or to_asset in CRYPTOCOMPARE_SPECIAL_CASES
        )
        if special_asset and not handling_special_case:
            return self._special_case_handling(
                method_name='query_endpoint_pricehistorical',
                from_asset=from_asset,
                to_asset=to_asset,
                timestamp=timestamp,
            )

        try:
            cc_from_asset_symbol = from_asset.to_cryptocompare()
            cc_to_asset_symbol = to_asset.to_cryptocompare()
        except UnsupportedAsset as e:
            raise PriceQueryUnsupportedAsset(e.asset_name) from e

        query_path = (
            f'pricehistorical?fsym={cc_from_asset_symbol}&tsyms={cc_to_asset_symbol}'
            f'&ts={timestamp}'
        )
        if to_asset == 'BTC':
            query_path += '&tryConversion=false'
        result = self._api_query(query_path)
        # Up until 23/09/2020 cryptocompare may return {} due to bug.
        # Handle that case by assuming 0 if that happens
        if (
            cc_from_asset_symbol not in result or
            cc_to_asset_symbol not in result[cc_from_asset_symbol]
        ):
            return Price(ZERO)

        return Price(FVal(result[cc_from_asset_symbol][cc_to_asset_symbol]))
Exemple #13
0
    def _retrieve_price_from_data(
        data: Optional[List[PriceHistoryEntry]],
        from_asset: Asset,
        to_asset: Asset,
        timestamp: Timestamp,
    ) -> Price:
        """Reads historical price data list returned from cryptocompare histohour
        or cache and returns a price.

        If nothing is found it returns Price(0). This can also happen if cryptocompare
        returns a list of 0s for the timerange.
        """
        price = Price(ZERO)
        if data is None or len(data) == 0:
            return price

        # all data are sorted and timestamps are always increasing by 1 hour
        # find the closest entry to the provided timestamp
        if timestamp >= data[0].time:
            index_in_bounds = True
            # convert_to_int can't raise here due to its input
            index = convert_to_int((timestamp - data[0].time) / 3600,
                                   accept_only_exact=False)
            if index > len(data) - 1:  # index out of bounds
                # Try to see if index - 1 is there and if yes take it
                if index > len(data):
                    index = index - 1
                else:  # give up. This happened: https://github.com/rotki/rotki/issues/1534
                    log.error(
                        f'Expected data index in cryptocompare historical hour price '
                        f'not found. Queried price of: {from_asset.identifier} in '
                        f'{to_asset.identifier} at {timestamp}. Data '
                        f'index: {index}. Length of returned data: {len(data)}. '
                        f'https://github.com/rotki/rotki/issues/1534. Attempting other methods...',
                    )
                    index_in_bounds = False

            if index_in_bounds:
                diff = abs(data[index].time - timestamp)
                if index + 1 <= len(data) - 1:
                    diff_p1 = abs(data[index + 1].time - timestamp)
                    if diff_p1 < diff:
                        index = index + 1

                if data[index].high is not None and data[index].low is not None:
                    price = Price((data[index].high + data[index].low) / 2)

        else:
            # no price found in the historical data from/to asset, try alternatives
            price = Price(ZERO)

        return price
Exemple #14
0
    def mock_query_price(from_asset, to_asset):
        assert from_asset.identifier == 'ETH'
        assert to_asset.identifier == 'USD'
        nonlocal call_count
        if call_count == 0:
            price = Price(FVal('1'))
        elif call_count in (1, 2):
            price = Price(FVal('2'))
        else:
            raise AssertionError('Called too many times for this test')

        call_count += 1
        return price
Exemple #15
0
    def query_historical_price(
        self,
        from_asset: Asset,
        to_asset: Asset,
        timestamp: Timestamp,
    ) -> Price:
        vs_currency = Coingecko.check_vs_currencies(
            from_asset=from_asset,
            to_asset=to_asset,
            location='historical price',
        )
        if not vs_currency:
            return Price(ZERO)

        try:
            from_coingecko_id = from_asset.to_coingecko()
        except UnsupportedAsset:
            log.warning(
                f'Tried to query coingecko historical price from {from_asset.identifier} '
                f'to {to_asset.identifier}. But from_asset is not supported in coingecko',
            )
            return Price(ZERO)

        date = timestamp_to_date(timestamp, formatstr='%d-%m-%Y')
        cached_price = self._get_cached_price(from_asset=from_asset,
                                              to_asset=to_asset,
                                              date=date)
        if cached_price is not None:
            return cached_price

        result = self._query(
            module='coins',
            subpath=f'{from_coingecko_id}/history',
            options={
                'date': date,
                'localization': False,
            },
        )

        try:
            price = Price(
                FVal(result['market_data']['current_price'][vs_currency]))
        except KeyError as e:
            log.warning(
                f'Queried coingecko historical price from {from_asset.identifier} '
                f'to {to_asset.identifier}. But got key error for {str(e)} when '
                f'processing the result.', )
            return Price(ZERO)

        self._save_cached_price(from_asset, to_asset, date, price)
        return price
Exemple #16
0
def test_find_usd_price_no_price_found(inquirer):
    """Test zero price is returned when all the oracles returned zero price
    requesting the USD price of an asset.
    """
    inquirer._oracle_instances = [MagicMock() for _ in inquirer._oracles]

    for oracle_instance in inquirer._oracle_instances:
        oracle_instance.query_current_price.return_value = Price(ZERO)

    price = inquirer.find_usd_price(A_BTC)

    assert price == Price(ZERO)
    for oracle_instance in inquirer._oracle_instances:
        assert oracle_instance.query_current_price.call_count == 1
Exemple #17
0
def _deserialize_transaction(grant_id: int, rawtx: Dict[str,
                                                        Any]) -> LedgerAction:
    """May raise:
    - DeserializationError
    - KeyError
    - UnknownAsset
    """
    timestamp = deserialize_timestamp_from_date(
        date=rawtx['timestamp'],
        formatstr='%Y-%m-%dT%H:%M:%S',
        location='Gitcoin API',
        skip_milliseconds=True,
    )
    asset = get_gitcoin_asset(symbol=rawtx['asset'],
                              token_address=rawtx['token_address'])
    raw_amount = deserialize_int_from_str(symbol=rawtx['amount'],
                                          location='gitcoin api')
    amount = asset_normalized_value(raw_amount, asset)
    if amount == ZERO:
        raise ZeroGitcoinAmount()

    # let's use gitcoin's calculated rate for now since they include it in the response
    usd_value = Price(
        ZERO) if rawtx['usd_value'] is None else deserialize_price(
            rawtx['usd_value'])  # noqa: E501
    rate = Price(ZERO) if usd_value == ZERO else Price(usd_value / amount)
    raw_txid = rawtx['tx_hash']
    tx_type, tx_id = process_gitcoin_txid(key='tx_hash', entry=rawtx)
    # until we figure out if we can use it https://github.com/gitcoinco/web/issues/9255#issuecomment-874537144  # noqa: E501
    clr_round = _calculate_clr_round(timestamp, rawtx)
    notes = f'Gitcoin grant {grant_id} event' if not clr_round else f'Gitcoin grant {grant_id} event in clr_round {clr_round}'  # noqa: E501
    return LedgerAction(
        identifier=1,  # whatever -- does not end up in the DB
        timestamp=timestamp,
        action_type=LedgerActionType.DONATION_RECEIVED,
        location=Location.GITCOIN,
        amount=AssetAmount(amount),
        asset=asset,
        rate=rate,
        rate_asset=A_USD,
        link=raw_txid,
        notes=notes,
        extra_data=GitcoinEventData(
            tx_id=tx_id,
            grant_id=grant_id,
            clr_round=clr_round,
            tx_type=tx_type,
        ),
    )
Exemple #18
0
def test_cryptocompare_histohour_data_going_backward(data_dir, database,
                                                     freezer):
    """Test that the cryptocompare histohour data retrieval works properly

    This test checks that doing an additional query in the past workd properly
    and that the cached data are properly appended to the cached result. In production
    this scenario should not happen often. Only way to happen if cryptocompare somehow adds
    older data than what was previously queried.
    """
    globaldb = GlobalDBHandler()
    # first timestamp cryptocompare has histohour BTC/USD when queried from this test is
    btc_start_ts = 1279936800
    # first timestamp cryptocompare has histohour BTC/USD is: 1279940400
    now_ts = btc_start_ts + 3600 * 2000 + 122
    # create a cache file for BTC_USD
    cache_data = [
        HistoricalPrice(
            from_asset=A_BTC,
            to_asset=A_USD,
            source=HistoricalPriceOracle.CRYPTOCOMPARE,
            timestamp=Timestamp(1301536800),
            price=Price(FVal('0.298')),
        ),
        HistoricalPrice(
            from_asset=A_BTC,
            to_asset=A_USD,
            source=HistoricalPriceOracle.CRYPTOCOMPARE,
            timestamp=Timestamp(1301540400),
            price=Price(FVal('0.298')),
        )
    ]
    globaldb.add_historical_prices(cache_data)

    freezer.move_to(datetime.fromtimestamp(now_ts))
    cc = Cryptocompare(data_directory=data_dir, database=database)
    cc.query_and_store_historical_data(
        from_asset=A_BTC,
        to_asset=A_USD,
        timestamp=now_ts - 3600 * 2 - 55,
    )
    result = get_globaldb_cache_entries(from_asset=A_BTC, to_asset=A_USD)
    assert len(result) == CRYPTOCOMPARE_HOURQUERYLIMIT * 3 + 2
    check_cc_result(result, forward=False)
    data_range = globaldb.get_historical_price_range(
        A_BTC, A_USD, HistoricalPriceOracle.CRYPTOCOMPARE)  # noqa: E501
    assert data_range[0] == btc_start_ts
    assert data_range[
        1] == 1301540400  # that's the closest ts to now_ts cc returns
Exemple #19
0
def test_find_usd_price_cache(inquirer, freezer):  # pylint: disable=unused-argument
    call_count = 0

    def mock_query_price(from_asset, to_asset):
        assert from_asset.identifier == 'ETH'
        assert to_asset.identifier == 'USD'
        nonlocal call_count
        if call_count == 0:
            price = Price(FVal('1'))
        elif call_count in (1, 2):
            price = Price(FVal('2'))
        else:
            raise AssertionError('Called too many times for this test')

        call_count += 1
        return price

    cc_patch = patch.object(
        inquirer._cryptocompare,
        'query_current_price',
        wraps=mock_query_price,
    )
    inquirer.set_oracles_order(oracles=[CurrentPriceOracle.CRYPTOCOMPARE])

    with cc_patch as cc:
        price = inquirer.find_usd_price(A_ETH)
        assert cc.call_count == 1
        assert price == Price(FVal('1'))

        # next time we run, make sure it's the cache
        price = inquirer.find_usd_price(A_ETH)
        assert cc.call_count == 1
        assert price == Price(FVal('1'))

        # now move forward in time to invalidate the cache
        freezer.move_to(
            datetime.fromtimestamp(ts_now() + CURRENT_PRICE_CACHE_SECS + 1))
        price = inquirer.find_usd_price(A_ETH)
        assert cc.call_count == 2
        assert price == Price(FVal('2'))

        # also test that ignore_cache works
        price = inquirer.find_usd_price(A_ETH)
        assert cc.call_count == 2
        assert price == Price(FVal('2'))
        price = inquirer.find_usd_price(A_ETH, ignore_cache=True)
        assert cc.call_count == 3
        assert price == Price(FVal('2'))
Exemple #20
0
def _query_exchanges_rateapi(base: Asset, quote: Asset) -> Optional[Price]:
    assert base.is_fiat(), 'fiat currency should have been provided'
    assert quote.is_fiat(), 'fiat currency should have been provided'
    log.debug(
        'Querying api.exchangeratesapi.io fiat pair',
        base_currency=base.identifier,
        quote_currency=quote.identifier,
    )
    querystr = (
        f'https://api.exchangeratesapi.io/latest?base={base.identifier}&symbols={quote.identifier}'
    )
    try:
        resp = request_get_dict(querystr)
        return Price(FVal(resp['rates'][quote.identifier]))
    except (
            RemoteError,
            KeyError,
            requests.exceptions.TooManyRedirects,
            UnableToDecryptRemoteData,
    ):
        log.error(
            'Querying api.exchangeratesapi.io for fiat pair failed',
            base_currency=base.identifier,
            quote_currency=quote.identifier,
        )
        return None
Exemple #21
0
    def find_yearn_price(
        self,
        token: EthereumToken,
    ) -> Optional[Price]:
        """
        Query price for a yearn vault v2 token using the pricePerShare method
        and the price of the underlying token.
        """
        assert self._ethereum is not None, 'Inquirer ethereum manager should have been initialized'  # noqa: E501

        maybe_underlying_token = GlobalDBHandler().fetch_underlying_tokens(
            token.ethereum_address)
        if maybe_underlying_token is None or len(maybe_underlying_token) != 1:
            log.error(f'Yearn vault token {token} without an underlying asset')
            return None

        underlying_token = EthereumToken(maybe_underlying_token[0].address)
        underlying_token_price = self.find_usd_price(underlying_token)
        # Get the price per share from the yearn contract
        contract = EthereumContract(
            address=token.ethereum_address,
            abi=YEARN_VAULT_V2_ABI,
            deployed_block=0,
        )
        try:
            price_per_share = contract.call(self._ethereum, 'pricePerShare')
            return Price(price_per_share * underlying_token_price /
                         10**token.decimals)
        except (RemoteError, BlockchainQueryError) as e:
            log.error(
                f'Failed to query pricePerShare method in Yearn v2 Vault. {str(e)}'
            )

        return None
Exemple #22
0
    def find_price(
        from_asset: Asset,
        to_asset: Asset,
        ignore_cache: bool = False,
    ) -> Price:
        """Returns the current price of 'from_asset' in 'to_asset' valuation.
        NB: prices for special symbols in any currency but USD are not supported.

        Returns Price(ZERO) if all options have been exhausted and errors are logged in the logs
        """
        if from_asset == to_asset:
            return Price(FVal('1'))

        instance = Inquirer()
        if to_asset == A_USD:
            return instance.find_usd_price(asset=from_asset,
                                           ignore_cache=ignore_cache)

        if ignore_cache is False:
            cache = instance.get_cached_current_price_entry(
                cache_key=(from_asset, to_asset))
            if cache is not None:
                return cache.price

        oracle_price = instance._query_oracle_instances(from_asset=from_asset,
                                                        to_asset=to_asset)
        return oracle_price
Exemple #23
0
    def query_historical_price(
        cls,
        from_asset: Asset,
        to_asset: Asset,
        timestamp: Timestamp,
    ) -> Price:
        price_entry = GlobalDBHandler().get_historical_price(
            from_asset=from_asset,
            to_asset=to_asset,
            timestamp=timestamp,
            max_seconds_distance=3600,
            source=HistoricalPriceOracle.MANUAL,
        )
        if price_entry and price_entry.price != Price(ZERO):
            log.debug('Got historical manual price',
                      from_asset=from_asset,
                      to_asset=to_asset,
                      timestamp=timestamp)  # noqa: E501
            return price_entry.price

        raise NoPriceForGivenTimestamp(
            from_asset=from_asset,
            to_asset=to_asset,
            date=timestamp_to_date(
                timestamp,
                formatstr='%d/%m/%Y, %H:%M:%S',
                treat_as_local=True,
            ),
        )
Exemple #24
0
    def _get_single_balance(
            self, entry: Tuple[Tuple[str, str, str, int], int]) -> DefiBalance:
        metadata = entry[0]
        balance_value = entry[1]
        decimals = metadata[3]
        normalized_value = token_normalized_value(balance_value, decimals)
        token_symbol = metadata[2]

        try:
            asset = Asset(token_symbol)
            usd_price = Inquirer().find_usd_price(asset)
        except (UnknownAsset, UnsupportedAsset):
            if not _is_symbol_non_standard(token_symbol):
                self.msg_aggregator.add_error(
                    f'Unsupported asset {token_symbol} encountered during DeFi protocol queries',
                )
            usd_price = Price(ZERO)

        usd_value = normalized_value * usd_price
        defi_balance = DefiBalance(
            token_address=to_checksum_address(metadata[0]),
            token_name=metadata[1],
            token_symbol=token_symbol,
            balance=Balance(amount=normalized_value, usd_value=usd_value),
        )
        return defi_balance
Exemple #25
0
def deserialize_price(amount: AcceptableFValInitInput) -> Price:
    try:
        result = Price(FVal(amount))
    except ValueError as e:
        raise DeserializationError(f'Failed to deserialize a price/rate entry: {str(e)}')

    return result
Exemple #26
0
def test_get_associated_locations(
    rotkehlchen_api_server_with_exchanges,
    added_exchanges,
    ethereum_accounts,  # pylint: disable=unused-argument
    start_with_valid_premium,  # pylint: disable=unused-argument
):
    rotki = rotkehlchen_api_server_with_exchanges.rest_api.rotkehlchen
    mock_exchange_data_in_db(added_exchanges, rotki)
    db = rotki.data.db

    db.add_trades([Trade(
        timestamp=Timestamp(1595833195),
        location=Location.NEXO,
        base_asset=A_ETH,
        quote_asset=A_EUR,
        trade_type=TradeType.BUY,
        amount=AssetAmount(FVal('1.0')),
        rate=Price(FVal('281.14')),
        fee=Fee(ZERO),
        fee_currency=A_EUR,
        link='',
        notes='',
    )])

    # get locations
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server_with_exchanges,
            'associatedlocations',
        ),
    )
    result = assert_proper_response_with_result(response)
    assert set(result) == {'nexo', 'binance', 'poloniex'}
Exemple #27
0
    def _get_known_asset_price(
            known_assets: Set[EthereumToken],
            unknown_assets: Set[UnknownEthereumToken],
    ) -> AssetPrice:
        """Get the tokens prices via Inquirer

        Given an asset, if `find_usd_price()` returns zero, it will be added
        into `unknown_assets`.
        """
        asset_price: AssetPrice = {}

        for known_asset in known_assets:
            asset_usd_price = Inquirer().find_usd_price(known_asset)

            if asset_usd_price != Price(ZERO):
                asset_price[known_asset.ethereum_address] = asset_usd_price
            else:
                unknown_asset = UnknownEthereumToken(
                    ethereum_address=known_asset.ethereum_address,
                    symbol=known_asset.identifier,
                    name=known_asset.name,
                    decimals=known_asset.decimals,
                )
                unknown_assets.add(unknown_asset)

        return asset_price
Exemple #28
0
def test_market_request():
    """Test that we can query bisq for market prices"""
    price = get_bisq_market_price(A_BSQ)
    assert price != Price(ZERO)
    # Test that error is correctly raised when there is no market
    with pytest.raises(RemoteError):
        get_bisq_market_price(A_3CRV)
Exemple #29
0
def test_deserialize_v1_trade(mock_kucoin):
    raw_result = {
        'id': 'xxxx',
        'symbol': 'NANO-ETH',
        'dealPrice': '0.015743',
        'dealValue': '0.00003441',
        'amount': '0.002186',
        'fee': '0.00000003',
        'side': 'sell',
        'createdAt': 1520471876,
    }
    expected_trade = Trade(
        timestamp=Timestamp(1520471876),
        location=Location.KUCOIN,
        pair=TradePair('NANO_ETH'),
        trade_type=TradeType.SELL,
        amount=AssetAmount(FVal('0.002186')),
        rate=Price(FVal('0.015743')),
        fee=Fee(FVal('0.00000003')),
        fee_currency=Asset('ETH'),
        link='xxxx',
        notes='',
    )
    trade = mock_kucoin._deserialize_trade(
        raw_result=raw_result,
        case=KucoinCase.OLD_TRADES,
    )
    assert trade == expected_trade
Exemple #30
0
    def query_endpoint_pricehistorical(
            self,
            from_asset: Asset,
            to_asset: Asset,
            timestamp: Timestamp,
    ) -> Price:
        """Queries the historical daily price of from_asset to to_asset for timestamp

        - May raise RemoteError if there is a problem reaching the cryptocompare server
        or with reading the response returned by the server
        """
        log.debug(
            'Querying cryptocompare for daily historical price',
            from_asset=from_asset,
            to_asset=to_asset,
            timestamp=timestamp,
        )
        # These two can raise but them raising here is a bug
        cc_from_asset_symbol = from_asset.to_cryptocompare()
        cc_to_asset_symbol = to_asset.to_cryptocompare()
        query_path = (
            f'pricehistorical?fsym={cc_from_asset_symbol}&tsyms={cc_to_asset_symbol}'
            f'&ts={timestamp}'
        )
        if to_asset == 'BTC':
            query_path += '&tryConversion=false'
        result = self._api_query(query_path)
        return Price(FVal(result[cc_from_asset_symbol][cc_to_asset_symbol]))