def price_converted_to_usd(self,
                               currency_symbol,
                               exchange_name='aggregate'):
        result = WeightedAverage()
        for a in self.alive_exchanges:
            if a.currency_symbol != currency_symbol:
                continue
            if a.price_usd is not None and a.price_usd != 0:
                price_in_usd = a.price_usd
                volume_in_usd = 0 if a.volume_usd is None else a.volume_usd
            elif a.price_dai is not None and a.price_dai != 0:
                price_in_usd = a.price_dai
                volume_in_usd = 0 if a.volume_dai is None else a.volume_dai
            elif a.price_eth is not None and a.price_eth != 0:
                price_in_usd = a.price_eth * self.eth_price_usd()
                volume_in_usd = 0 if a.volume_eth is None else a.volume_eth * self.eth_price_usd(
                )
            elif a.price_btc is not None and a.price_btc != 0:
                price_in_usd = a.price_btc * self.eth_price_usd()
                volume_in_usd = 0 if a.volume_btc is None else a.volume_btc * self.eth_price_usd(
                )

            if exchange_name == 'aggregate' or a.exchange_name == exchange_name:
                result.add(price_in_usd, volume_in_usd)
        return result.average()
Example #2
0
    async def _update(self, timeout=10.0):
        if self.market_id_vs_eth is None:
            self.market_id_vs_eth = await self._get_market_id(
                self.currency_symbol, "ETH")
        if self.market_id_vs_btc is None:
            self.market_id_vs_btc = await self._get_market_id(
                self.currency_symbol, "BTC")
        if self.market_id_eth_btc is None:
            self.market_id_eth_btc = await self._get_market_id("ETH", "BTC")

        if self.market_id_vs_eth is None or self.market_id_vs_btc is None:
            raise RuntimeError(
                "Failed to get market ids for asset code '{}'".format(
                    self.currency_symbol))

        # grab market data for the desired currency vs ETH and BTC
        self.price_eth, self.volume_eth, change_eth = await self._fetch_market_data(
            self.market_id_vs_eth)
        self.price_btc, self.volume_btc, change_btc = await self._fetch_market_data(
            self.market_id_vs_btc)

        # grab market data for ETH/BTC so we can interpret relative data
        eth_price_in_btc, _, _ = await self._fetch_market_data(
            self.market_id_eth_btc)

        if self.volume_btc == 0:
            ratio_of_eth_vs_btc_volume = 100000
        else:
            ratio_of_eth_vs_btc_volume = self.volume_eth / (self.volume_btc /
                                                            eth_price_in_btc)

        average = WeightedAverage()
        average.add(change_eth, ratio_of_eth_vs_btc_volume)
        average.add(change_btc, 1)
        self.change_24h = average.average()
Example #3
0
 def btc_price_usd(self, api_name='aggregate'):
     result = WeightedAverage()
     for a in self.alive_apis:
         if a.btc_price_usd == None:
             continue
         if api_name == 'aggregate' or a.api_name == api_name:
             result.add(a.btc_price_usd, a.volume_eth)
     return result.average()
Example #4
0
 def change_24h(self, currency_symbol='0xBTC', api_name='aggregate'):
     result = WeightedAverage()
     for a in self.alive_apis:
         if a.currency_symbol != currency_symbol:
             continue
         if a.change_24h == None:
             continue
         if api_name == 'aggregate' or a.api_name == api_name:
             result.add(a.change_24h, a.volume_eth)
     return result.average()
 def btc_price_usd(self, exchange_name='aggregate'):
     result = WeightedAverage()
     for a in self.alive_exchanges:
         if a.btc_price_usd == None:
             continue
         if exchange_name == 'aggregate' or a.exchange_name == exchange_name:
             if a.currency_symbol == 'BTC':
                 result.add(a.price_usd, a.volume_usd / a.price_usd)
             else:
                 result.add(a.btc_price_usd, a.volume_btc)
     return result.average()
Example #6
0
 def price_usd(self, currency_symbol, exchange_name='aggregate'):
     result = WeightedAverage()
     for a in self.alive_exchanges:
         if a.currency_symbol != currency_symbol:
             continue
         if a.price_usd == None:
             continue
         if a.volume_eth == None:
             continue
         if exchange_name == 'aggregate' or a.exchange_name == exchange_name:
             result.add(a.price_usd, a.volume_eth)
     return result.average()
    async def _update_all_values(self, should_update_volume=False, timeout=10):

        if should_update_volume:
            current_eth_block = self._w3.eth.blockNumber

        # get price of eth
        eth_prices = [
            get_price(self._w3, "DAI", "WETH"),
            get_price(self._w3, "USDT", "WETH"),
            get_price(self._w3, "USDC", "WETH"),
        ]
        self.eth_price_usd = sum(eth_prices) / len(eth_prices)  # TODO: should be weighted average

        # get token price (in USD), liquidity (in tokens), and volume (in tokens) for
        # each pair. Note if liquidity is low for a pair, its voluem is not checked.
        price_usd_weighted_average = WeightedAverage()
        total_liquidity_tokens = 0
        total_volume_tokens = 0
        for exchange_contract in self._exchanges:
            try:
                price_usd, liquidity_tokens = await self._get_price_and_liquidity_at_exchange_contract(exchange_contract)
            except (NoTokenMatchError, PairNotDefinedError) as e:
                logging.warning(f"Failed to update quickswap exchange: {str(e)}")
                continue
            except NoLiquidityException:
                # no liquidity is not an error; simply skip this exchange
                continue
            else:
                price_usd_weighted_average.add(price_usd, liquidity_tokens)
                total_liquidity_tokens += liquidity_tokens

                if should_update_volume and liquidity_tokens > _MINIMUM_ALLOWED_LIQUIDITY_TOKENS_TO_CHECK_VOLUME:
                    try:
                        volume_tokens, volume_pair = await self._get_volume_at_exchange_contract(exchange_contract, current_eth_block=current_eth_block, timeout=timeout)
                        total_volume_tokens += volume_tokens
                    except requests.exceptions.ReadTimeout:
                        logging.warning(f"Failed to update SushiSwapPolygonAPI volume: ReadTimeout")

        self.price_usd = price_usd_weighted_average.average()
        self.price_eth = self.price_usd / self.eth_price_usd
        self.liquidity_tokens = total_liquidity_tokens
        self.liquidity_eth = self.liquidity_tokens * self.price_eth
        if should_update_volume:
            self.hourly_volume_tokens.append(total_volume_tokens)
            # trim list to 168 hours (7 days)
            self.hourly_volume_tokens = self.hourly_volume_tokens[-168:]
            # use last 24 hours for volume
            self.volume_tokens = sum(self.hourly_volume_tokens[-24:])
            self.volume_eth = self.volume_tokens * self.price_eth
            # NOTE: this sets _time_volume_last_updated even if all volume updates
            #       failed. This is OK for now, it throttles struggling APIs (matic) but
            #       may not be the ideal behavior.
            self._mark_volume_as_updated()
Example #8
0
    async def _update(self, timeout=10.0):
        total_liquidity_eth = 0
        total_liquidity_dai = 0
        total_liquidity_tokens = 0

        price_eth_avg = WeightedAverage()
        price_dai_avg = WeightedAverage()

        for exchange_address in self._exchange_addresses:
            liquidity_eth, liquidity_dai, liquidity_tokens, price_eth, price_dai = await self._get_liquidity_and_price_at_pool_addr(
                exchange_address)

            price_eth_avg.add(price_eth, liquidity_eth)
            price_dai_avg.add(price_dai, liquidity_dai)

            total_liquidity_eth += liquidity_eth
            total_liquidity_dai += liquidity_dai
            total_liquidity_tokens += liquidity_tokens

        if total_liquidity_tokens == 0:
            raise NoLiquidityException("Pool has no liquidity")

        self.liquidity_dai = total_liquidity_dai
        self.liquidity_eth = total_liquidity_eth
        self.liquidity_tokens = total_liquidity_tokens

        self.price_eth = price_eth_avg.average()
        self.price_usd = price_dai_avg.average(
        )  # note: this is usually 0 because there are no dai pairs defined on balancer

        # update volume once every hour since it (potentially) loads eth api
        if time.time() - self._time_volume_last_updated > 60 * 60:
            try:
                await self._update_volume()
            except requests.exceptions.ReadTimeout:
                logging.warning(
                    f"Failed to update QuickSwapAPI volume: ReadTimeout")

            self._time_volume_last_updated = time.time()
 def price_eth(self, currency_symbol, exchange_name='aggregate'):
     result = WeightedAverage()
     for a in self.alive_exchanges:
         if a.currency_symbol != currency_symbol:
             continue
         if a.price_eth == None:
             continue
         if a.volume_eth == None:
             # use 0 eth as fallback so it does not affect weighted price
             volume = 0
         else:
             volume = a.volume_eth
         if exchange_name == 'aggregate' or a.exchange_name == exchange_name:
             result.add(a.price_eth, volume)
     return result.average()
    def _estimated_hashrate_n_days(self, days):
        # MUST CALL UPDATE() BEFORE THIS FUNCTION
        eth_blocks_in_window = int(days * 60 * 60 * 24 / SECONDS_PER_ETH_BLOCK)
        eth_block_at_start = self._current_eth_block - eth_blocks_in_window
        epoch_at_start = self._contract.functions.epochCount().call(
            block_identifier=-eth_blocks_in_window)
        #epoch_at_start = self._w3.toInt(self._w3.eth.getStorageAt(self.address, 0x7, eth_block_at_start))
        epochs_in_window = self._epoch_count - epoch_at_start
        epochs_per_eth_block = epochs_in_window / eth_blocks_in_window
        epochs_per_second = epochs_per_eth_block / SECONDS_PER_ETH_BLOCK
        seconds_per_reward = 1 / epochs_per_second

        # if current diff started before beginning of the window, the math is
        # simple - one difficulty only
        if self.last_difficulty_start_block <= eth_block_at_start:
            estimated_hashrate_24h = self.difficulty * 2**22 / seconds_per_reward
        else:
            # difficulty changed within the window - so calculation must
            # consider multiple difficulties
            previous_mining_target = self._contract.functions.getMiningTarget(
            ).call(block_identifier=self.last_difficulty_start_block - 1)
            previous_difficulty = int(self.max_target / previous_mining_target)

            # load the eth block where the difficulty changed to the *previous*
            # difficulty. If it is inside the window, exit completely, because
            # it means the window contains 3 or more difficulties.
            eth_block_two_readjustments_ago = self._contract.functions.latestDifficultyPeriodStarted(
            ).call(block_identifier=self.last_difficulty_start_block - 1)
            if eth_block_two_readjustments_ago >= eth_block_at_start:
                raise RuntimeError(
                    "Average window too large: this function only supports at most two difficulty periods."
                )

            from weighted_average import WeightedAverage
            wa = WeightedAverage()
            # add hashrate based on current difficulty weighted by how many eth
            # blocks occured during that difficulty
            wa.add(self.difficulty * 2**22 / seconds_per_reward,
                   self._current_eth_block - self.last_difficulty_start_block)
            # add hashrate based on last difficulty weighted by how many eth
            # blocks occured during that difficulty
            wa.add(previous_difficulty * 2**22 / seconds_per_reward,
                   self.last_difficulty_start_block - eth_block_at_start)
            estimated_hashrate_24h = wa.average()

        return estimated_hashrate_24h
    async def _update(self, timeout=10.0):
        eth_prices = [
            get_price(self._w3, "DAI", "WETH"),
            get_price(self._w3, "USDT", "WETH"),
            get_price(self._w3, "USDC", "WETH"),
        ]
        # TODO: weighted average would be better than a simple average
        self.eth_price_usd = sum(eth_prices) / len(eth_prices)

        # matic_price_eth = get_price(self._w3, "WETH", "WMATIC")
        # self.matic_price_usd = matic_price_eth * self.eth_price_usd

        # swam_price_eth = get_price(self._w3, "WETH", "SWAM")
        # self.swam_price_usd = swam_price_eth * self.eth_price_usd

        total_liquidity_tokens = 0
        price_usd_weighted_average = WeightedAverage()
        # check each token that <self.currency_symbol> is paired with
        for exchange_contract in self._exchanges:
            token0_address = exchange_contract.functions.token0().call().lower(
            )
            token1_address = exchange_contract.functions.token1().call().lower(
            )
            paired_token_address = token0_address if token1_address.lower(
            ) == Token().from_symbol(
                self.currency_symbol).address.lower() else token1_address
            try:
                paired_token_symbol = Token().from_address(
                    paired_token_address).symbol
            except NoTokenMatchError:
                logging.warning(
                    f"no token with address {paired_token_address} found (need to edit token_class.py); skipping"
                )
                continue

            try:
                liquidity_tokens, liquidity_pair = get_reserves(
                    self._w3, self.currency_symbol, paired_token_symbol)
            except PairNotDefinedError:
                logging.warning(
                    f"pair {self.currency_symbol}-{paired_token_symbol} not found; skipping"
                )
                continue

            if liquidity_tokens < 0.001:
                continue

            total_liquidity_tokens += liquidity_tokens

            if paired_token_symbol == "WETH":
                self.price_eth = get_price(self._w3, paired_token_symbol,
                                           self.currency_symbol)
                price_usd_weighted_average.add(
                    self.price_eth * self.eth_price_usd, liquidity_tokens)
                self.liquidity_eth = liquidity_pair
            else:

                # get the paired token's price in Eth. If there is less than $500 in
                # liquidity to determine this, then skip this pair when determining price.
                try:
                    liquidity_eth, _ = get_reserves(self._w3, "WETH",
                                                    paired_token_symbol)
                except PairNotDefinedError:
                    logging.warning(
                        f"pair WETH-{paired_token_symbol} not found; skipping")
                    continue

                if liquidity_eth < 500 / self.eth_price_usd:
                    continue

                paired_token_price_in_eth = get_price(self._w3, "WETH",
                                                      paired_token_symbol)
                paired_token_price_in_usd = paired_token_price_in_eth * self.eth_price_usd

                # get the price <self.currency_symbol> in terms of the paired token
                price_in_paired_token = get_price(self._w3,
                                                  paired_token_symbol,
                                                  self.currency_symbol)

                price_usd_weighted_average.add(
                    price_in_paired_token * paired_token_price_in_usd,
                    liquidity_tokens)

        self.liquidity_tokens = total_liquidity_tokens
        self.price_usd = price_usd_weighted_average.average()

        try:
            self.price_eth = get_price(self._w3, "WETH", self.currency_symbol)
        except PairNotDefinedError:
            logging.warning(
                f"Failed to get WETH pair for {self.currency_symbol}; calculating backwards using average USD price"
            )
            self.price_eth = self.price_usd / self.eth_price_usd

        self.volume_tokens = await self._update_24h_volume()
        self.volume_eth = self.volume_tokens * self.price_eth
Example #12
0
    async def _update_all_values(self,
                                 timeout=10.0,
                                 should_update_volume=False):

        if should_update_volume:
            current_eth_block = self._w3.eth.blockNumber

        self.price_eth = None

        eth_prices = [
            get_price(self._uniswap_api, "DAI", "WETH", _DEFAULT_PAIR_FEE),
            get_price(self._uniswap_api, "USDT", "WETH", _DEFAULT_PAIR_FEE),
            get_price(self._uniswap_api, "USDC", "WETH", _DEFAULT_PAIR_FEE),
        ]
        self.eth_price_usd = sum(eth_prices) / len(
            eth_prices)  # TODO: should be weighted average

        price_usd_weighted_average = WeightedAverage()
        total_liquidity_tokens = 0
        total_volume_tokens = 0

        for exchange_address in getExchangeAddressesForToken(
                self.currency_symbol):
            token0_name, token1_name, fee = getTokensFromExchangeAddress(
                exchange_address)
            token0_address = Token().from_symbol(token0_name).address
            token1_address = Token().from_symbol(token1_name).address
            #paired_token_address = token0_address if token1_address.lower() == Token().from_symbol(self.currency_symbol).address.lower() else token1_address
            #paired_token_symbol = Token().from_address(paired_token_address).symbol

            try:
                price_usd, liquidity_tokens = await self._get_price_and_liquidity_for_pair(
                    token0_address, token1_address, fee)
            except (NoTokenMatchError, PairNotDefinedError) as e:
                logging.warning(
                    f"Failed to update {self.exchange_name} pair: {str(e)}")
                continue
            except NoLiquidityException:
                # no liquidity is not an error; simply skip this exchange
                continue
            else:
                price_usd_weighted_average.add(price_usd, liquidity_tokens)
                total_liquidity_tokens += liquidity_tokens

                if should_update_volume and liquidity_tokens > _MINIMUM_ALLOWED_LIQUIDITY_TOKENS_TO_CHECK_VOLUME:
                    try:
                        volume_tokens, volume_pair = await self._get_volume_for_pair(
                            token0_address,
                            token1_address,
                            fee,
                            current_eth_block=current_eth_block,
                            timeout=timeout)
                        total_volume_tokens += volume_tokens
                    except requests.exceptions.ReadTimeout:
                        logging.warning(
                            f"Failed to update Uniswapv3API volume: ReadTimeout"
                        )

        self.price_usd = price_usd_weighted_average.average()
        self.price_eth = self.price_usd / self.eth_price_usd
        self.liquidity_tokens = total_liquidity_tokens
        self.liquidity_eth = self.liquidity_tokens * self.price_eth

        if should_update_volume:
            self.hourly_volume_tokens.append(total_volume_tokens)
            # trim list to 168 hours (7 days)
            self.hourly_volume_tokens = self.hourly_volume_tokens[-168:]
            # use last 24 hours for volume
            self.volume_tokens = sum(self.hourly_volume_tokens[-24:])
            self.volume_eth = self.volume_tokens * self.price_eth
            # NOTE: this sets _time_volume_last_updated even if all volume updates
            #       failed. This is OK for now, it throttles struggling APIs (matic) but
            #       may not be the ideal behavior.
            self._mark_volume_as_updated()
    async def _update(self, timeout=10.0):
        method = "/coin/{}".format(self.currency_symbol)

        data = await self._get_json_from_url(self._SERVER_URL+method)

        volume_usd = 0
        volume_usd_eth = 0
        volume_usd_btc = 0

        wavg_eth_price_usd = WeightedAverage()
        wavg_btc_price_usd = WeightedAverage()

        wavg_price_eth = WeightedAverage()
        wavg_price_btc = WeightedAverage()
        wavg_price_usd = WeightedAverage()

        for exchange_data in data['data']:
            # skip reverse-pairings
            if exchange_data['base'] != self.currency_symbol:
                continue

            # last_price_in_usd = data['data'][0]['usd']
            # last_price_in_eth = data['data'][0]['rate']
            # last_eth_price = data['data'][0]['lastq']
            base_pair = exchange_data['quote']
            relative_volume = float(exchange_data['volumep'])

            # NOTE: this entire if statement is ONLY to collect price of eth and btc
            if base_pair == "ETH":
                # average price of base pairs, they are NOT all the same
                wavg_eth_price_usd.add(exchange_data['lastq'], relative_volume)
            elif (base_pair in ["AUD", "CAD", "CNY", "DAI", "EUR", "EURO", "GBP", "JPY", "KRW", "RUB", "USDT", "USD"]):
                # allow all fiat pairings to count towards volume
                pass
            elif (base_pair in ["BTC"]):
                # average price of base pairs, they are NOT all the same
                wavg_btc_price_usd.add(exchange_data['lastq'], relative_volume)
                # allow BTC pairings to count towards volume
                pass
            else:
                # if base pair is unknown, don't use for calcualted volume/price
                continue

            # only let allowed_apis to count toward price
            if self.allowed_apis == 'all' or exchange_data['exchange'] in self.allowed_apis:
                wavg_price_usd.add(exchange_data['usd'], relative_volume)
                volume_usd += exchange_data['volume']
                
                if base_pair == "ETH":
                    wavg_price_eth.add(exchange_data['rate'], relative_volume)
                    volume_usd_eth += exchange_data['volume']
                
                if base_pair == "BTC":
                    wavg_price_btc.add(exchange_data['rate'], relative_volume)
                    volume_usd_btc += exchange_data['volume']

        self.price_usd = wavg_price_usd.average()

        self.eth_price_usd = wavg_eth_price_usd.average()
        self.btc_price_usd = wavg_btc_price_usd.average()

        if self.currency_symbol == "ETH":
            self.price_eth = 1
            self.eth_price_usd = self.price_usd
        else:
            self.price_eth = wavg_price_eth.average()

        self.volume_usd = volume_usd

        if self.eth_price_usd != None and self.eth_price_usd != 0:
            # TODO: volume_eth should really represent quantity of volume in eth,
            # not quantity of all volume converted to price of eth. This
            # calculation includes btc in eth volume.
            self.volume_eth = volume_usd_eth / self.eth_price_usd

        if self.btc_price_usd != None and self.btc_price_usd != 0:
            # TODO: volume_eth should really represent quantity of volume in eth,
            # not quantity of all volume converted to price of eth. This
            # calculation includes btc in eth volume.
            self.volume_btc = volume_usd_btc / self.btc_price_usd

        if self.currency_symbol == "BTC":
            self.price_btc = 1
            self.btc_price_usd = self.price_usd
        else:
            self.price_btc = wavg_price_btc.average()
    def _update(self, timeout=10.0):
        method = "/coin/{}".format(self.currency_symbol)

        response = urlopen(self._SERVER_URL+method, timeout=timeout)
        response = response.read().decode("utf-8") 
        try:
            data = json.loads(response)
        except json.decoder.JSONDecodeError:
            if "be right back" in response:
                raise TimeoutError("api is down - got 404 page")
            else:
            	raise TimeoutError("api sent bad data ({})".format(repr(response)))
            

        volume_usd = 0

        wavg_eth_price_usd = WeightedAverage()
        wavg_btc_price_usd = WeightedAverage()

        wavg_price_eth = WeightedAverage()
        wavg_price_usd = WeightedAverage()

        for exchange_data in data['data']:
            # skip reverse-pairings
            if exchange_data['base'] != self.currency_symbol:
                continue

            # last_price_in_usd = data['data'][0]['usd']
            # last_price_in_eth = data['data'][0]['rate']
            # last_eth_price = data['data'][0]['lastq']
            base_pair = exchange_data['quote']
            relative_volume = float(exchange_data['volumep'])

            # NOTE: this entire if statement is ONLY to collect price of eth and btc
            if base_pair == "ETH":
                # average price of base pairs, they are NOT all the same
                wavg_eth_price_usd.add(exchange_data['lastq'], relative_volume)
                wavg_price_eth.add(exchange_data['rate'], relative_volume)
            elif (base_pair in ["AUD", "CAD", "CNY", "DAI", "EUR", "EURO", "GBP", "JPY", "KRW", "RUB", "USDT", "USD"]):
                # allow all fiat pairings to count towards volume
                pass
            elif (base_pair in ["BTC"]):
                # average price of base pairs, they are NOT all the same
                wavg_btc_price_usd.add(exchange_data['lastq'], relative_volume)
                # allow BTC pairings to count towards volume
                pass
            else:
                #pprint.pprint(exchange_data)
                logging.debug('Unknown base_pair {}'.format(base_pair))
                # if base pair is unknown, don't use for calcualted volume/price
                continue

            # only let allowed_apis to count toward price
            if self.allowed_apis == 'all' or exchange_data['exchange'] in self.allowed_apis:
                wavg_price_usd.add(exchange_data['usd'], relative_volume)
                volume_usd += exchange_data['volume']



        self.price_usd = wavg_price_usd.average()

        self.eth_price_usd = wavg_eth_price_usd.average()
        self.btc_price_usd = wavg_btc_price_usd.average()

        if self.currency_symbol == "ETH":
            self.price_eth = 1
            self.eth_price_usd = self.price_usd
        else:
            self.price_eth = wavg_price_eth.average()

        self.volume_usd = volume_usd

        if self.eth_price_usd != None and self.eth_price_usd != 0:
            # TODO: volume_eth should really represent quantity of volume in eth,
            # not quantity of all volume converted to price of eth. This
            # calculation includes btc in eth volume.
            self.volume_eth = self.volume_usd / self.eth_price_usd

        if self.currency_symbol == "BTC":
            self.btc_price_usd = self.price_usd