Exemplo n.º 1
0
    def query_historical_price(self, from_asset, to_asset, timestamp):
        """
        Query the historical price on `timestamp` for `from_asset` in `to_asset`.
        So how much `to_asset` does 1 unit of `from_asset` cost.

        Args:
            from_asset (str): The ticker symbol of the asset for which we want to know
                              the price.
            to_asset (str): The ticker symbol of the asset against which we want to
                            know the price.
            timestamp (int): The timestamp at which to query the price
        """
        log.debug(
            'Querying historical price',
            from_asset=from_asset,
            to_asset=to_asset,
            timestamp=timestamp,
        )
        if from_asset == to_asset:
            return 1

        if from_asset not in self.cryptocompare_coin_list:
            raise PriceQueryUnknownFromAsset(from_asset)

        data = self.get_historical_data(from_asset, to_asset, timestamp)

        # all data are sorted and timestamps are always increasing by 1 hour
        # find the closest entry to the provided timestamp
        assert timestamp > data[0]['time']

        index = convert_to_int((timestamp - data[0]['time']) / 3600,
                               accept_only_exact=False)
        # print("timestamp: {} index: {} data_length: {}".format(timestamp, index, len(data)))
        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 None or data[index]['low'] is None:
            # If we get some None in the hourly set price to 0 so that we check daily price
            price = FVal(0)
        else:
            price = FVal((data[index]['high'] + data[index]['low'])) / 2

        if price == 0:
            if from_asset != 'BTC' and to_asset != 'BTC':
                log.debug(
                    f"Coudn't find historical price from {from_asset} to "
                    f"{to_asset}. Comparing with BTC...", )
                # Just get the BTC price
                asset_btc_price = self.query_historical_price(
                    from_asset, 'BTC', timestamp)
                btc_to_asset_price = self.query_historical_price(
                    'BTC', to_asset, timestamp)
                price = asset_btc_price * btc_to_asset_price
            else:
                log.debug(
                    f"Coudn't find historical price from {from_asset} to "
                    f"{to_asset}. Attempting to get daily price...", )
                # attempt to get the daily price by timestamp
                cc_from_asset = world_to_cryptocompare(from_asset)
                cc_to_asset = world_to_cryptocompare(to_asset)
                log.debug(
                    'Querying cryptocompare for daily historical price',
                    from_asset=from_asset,
                    to_asset=to_asset,
                    timestamp=timestamp,
                )
                query_string = (
                    'https://min-api.cryptocompare.com/data/pricehistorical?'
                    'fsym={}&tsyms={}&ts={}'.format(
                        cc_from_asset,
                        cc_to_asset,
                        timestamp,
                    ))
                if to_asset == 'BTC':
                    query_string += '&tryConversion=false'
                resp = request_get(query_string)

                if cc_from_asset not in resp:
                    error_message = 'Failed to query cryptocompare for: "{}"'.format(
                        query_string)
                    log.error(
                        'Cryptocompare query for daily historical price failed',
                        from_asset=from_asset,
                        to_asset=to_asset,
                        timestamp=timestamp,
                        error=error_message,
                    )
                    raise ValueError(error_message)

                price = FVal(resp[cc_from_asset][cc_to_asset])

                if price == 0:
                    raise NoPriceForGivenTimestamp(
                        from_asset, to_asset,
                        tsToDate(timestamp, formatstr='%d/%m/%Y, %H:%M:%S'))

        log.debug('Got historical price',
                  from_asset=from_asset,
                  to_asset=to_asset,
                  timestamp=timestamp,
                  price=price)
        return price
Exemplo n.º 2
0
    def get_historical_data(self, from_asset, to_asset, timestamp):
        """Get historical price data from cryptocompare"""
        log.debug(
            'Retrieving historical price data',
            from_asset=from_asset,
            to_asset=to_asset,
            timestamp=timestamp,
        )

        if from_asset not in self.cryptocompare_coin_list:
            raise ValueError('Attempted to query historical price data for '
                             'unknown asset "{}"'.format(from_asset))

        if to_asset not in self.cryptocompare_coin_list and to_asset not in FIAT_CURRENCIES:
            raise ValueError('Attempted to query historical price data for '
                             'unknown asset "{}"'.format(to_asset))

        cache_key = from_asset + '_' + to_asset
        got_cached_value = self.got_cached_price(cache_key, timestamp)
        if got_cached_value:
            return self.price_history[cache_key]['data']

        now_ts = int(time.time())
        cryptocompare_hourquerylimit = 2000
        calculated_history = list()

        if self.historical_data_start <= timestamp:
            end_date = self.historical_data_start
        else:
            end_date = timestamp
        while True:
            pr_end_date = end_date
            end_date = end_date + (cryptocompare_hourquerylimit) * 3600

            log.debug(
                'Querying cryptocompare for hourly historical price',
                from_asset=from_asset,
                to_asset=to_asset,
                cryptocompare_hourquerylimit=cryptocompare_hourquerylimit,
                end_date=end_date,
            )
            query_string = ('https://min-api.cryptocompare.com/data/histohour?'
                            'fsym={}&tsym={}&limit={}&toTs={}'.format(
                                world_to_cryptocompare(from_asset),
                                world_to_cryptocompare(to_asset),
                                cryptocompare_hourquerylimit,
                                end_date,
                            ))

            resp = request_get(query_string)
            if 'Response' not in resp or resp['Response'] != 'Success':
                error_message = 'Failed to query cryptocompare for: "{}"'.format(
                    query_string)
                if 'Message' in resp:
                    error_message += ". Error: {}".format(resp['Message'])

                log.error(
                    'Cryptocompare hourly historical price query failed',
                    error=error_message,
                )
                raise ValueError(error_message)

            if pr_end_date != resp['TimeFrom']:
                # If we get more than we needed, since we are close to the now_ts
                # then skip all the already included entries
                diff = pr_end_date - resp['TimeFrom']
                if resp['Data'][diff // 3600]['time'] != pr_end_date:
                    raise ValueError(
                        'Expected to find the previous date timestamp during '
                        'historical data fetching')
                # just add only the part from the previous timestamp and on
                resp['Data'] = resp['Data'][diff // 3600:]

            if end_date < now_ts and resp['TimeTo'] != end_date:
                raise ValueError('End dates no match')

            # If last time slot and first new are the same, skip the first new slot
            last_entry_equal_to_first = (len(calculated_history) != 0
                                         and calculated_history[-1]['time']
                                         == resp['Data'][0]['time'])
            if last_entry_equal_to_first:
                resp['Data'] = resp['Data'][1:]
            calculated_history += resp['Data']
            if end_date >= now_ts:
                break

        # Let's always check for data sanity for the hourly prices.
        assert check_hourly_data_sanity(calculated_history, from_asset,
                                        to_asset)
        self.price_history[cache_key] = {
            'data': calculated_history,
            'start_time': self.historical_data_start,
            'end_time': now_ts
        }
        # and now since we actually queried the data let's also save them locally
        filename = os.path.join(self.data_directory,
                                'price_history_' + cache_key + '.json')
        log.info('Updating price history cache',
                 filename=filename,
                 from_asset=from_asset,
                 to_asset=to_asset)
        write_history_data_in_file(calculated_history, filename,
                                   self.historical_data_start, now_ts)
        self.price_history_file[cache_key] = filename

        return calculated_history