def _parse_query(query): coin, interval_string = _split_query(query) coin = coin.upper() coin2 = None if '/' in coin: coin, coin2 = coin.split('/', 1) if coins_names.coin_name(coin) == '' and currencies_names.currency_name(coin) == '': raise SyntaxError("Invalid coin/currency name: %s" % coin) if coin2 and coins_names.coin_name(coin2) == '' and currencies_names.currency_name(coin2) == '': raise SyntaxError("Invalid coin/currency name: %s" % coin2) try: time_begin, time_end = interval.parse_interval(interval_string) except OverflowError: # to be fixed: ranges like yesterday, today, now and so on raise RuntimeError("Wrong range specification: %s" % interval_string) if time_begin is None or time_end is None: raise SyntaxError("Invalid time interval specification: %s" % interval_string) return coin, coin2, time_begin, time_end
def _parse_query(query): coin, interval_string = _split_query(query) coin = coin.upper() coin2 = None if '/' in coin: coin, coin2 = coin.split('/', 1) if coins_names.coin_name(coin) == '' and currencies_names.currency_name( coin) == '': raise SyntaxError("Invalid coin/currency name: %s" % coin) if coin2 and coins_names.coin_name( coin2) == '' and currencies_names.currency_name(coin2) == '': raise SyntaxError("Invalid coin/currency name: %s" % coin2) try: time_begin, time_end = interval.parse_interval(interval_string) except OverflowError: # to be fixed: ranges like yesterday, today, now and so on raise RuntimeError("Wrong range specification: %s" % interval_string) if time_begin is None or time_end is None: raise SyntaxError("Invalid time interval specification: %s" % interval_string) return coin, coin2, time_begin, time_end
def _make_header(self): coin_symbol = self.data['meta']['symbol'] coin_name = coins_names.coin_name(coin_symbol) meta = self.data['meta'] interval_name = interval.from_secs(self.interval) time_interval = "%s +%s" % (self._format_time( meta['time_begin']), interval_name) #self._format_time(meta['time_end'], show_date=True, show_time=True), output = "\n" if self.currency == 'USD': output += u"{-1▶ %s (%s) }{1▶}" % (coin_name, coin_symbol) else: currency_symbol = self.currency currency_name = currencies_names.currency_name(self.currency) if not currency_name: currency_name = coins_names.coin_name(self.currency) output += u"{-1▶ %s (%s) to %s (%s) }{1▶}" % \ (coin_name, coin_symbol, currency_name, currency_symbol) #output += u"{1%s (%s)}," % (coin_name, coin_symbol) output += " %s" % (time_interval) output += " %s\n" % self._show_change_percentage()[1] output += "\n\n" return output
def _make_header(self): coin_symbol = self.data['meta']['symbol'] coin_name = coins_names.coin_name(coin_symbol) meta = self.data['meta'] interval_name = interval.from_secs(self.interval) time_interval = "%s +%s" % ( self._format_time(meta['time_begin']), interval_name) #self._format_time(meta['time_end'], show_date=True, show_time=True), output = "\n" if self.currency == 'USD': output += u"{-1▶ %s (%s) }{1▶}" % (coin_name, coin_symbol) else: currency_symbol = self.currency currency_name = currencies_names.currency_name(self.currency) if not currency_name: currency_name = coins_names.coin_name(self.currency) output += u"{-1▶ %s (%s) to %s (%s) }{1▶}" % \ (coin_name, coin_symbol, currency_name, currency_symbol) #output += u"{1%s (%s)}," % (coin_name, coin_symbol) output += " %s" % (time_interval) output += " %s\n" % self._show_change_percentage()[1] output += "\n\n" return output
def view(query, use_currency=None): """ Main rendering function, entry point for this module. Returns rendered view for the ``query``. If currency is specified in ``query``, it overrides ``currency``. If ``currency`` is not specified, USD is used. """ try: coin, coin2, time_begin, time_end = _parse_query(query) except SyntaxError as e_msg: raise RuntimeError("%s" % e_msg) # if currency is specified in the domain name (use_currency) # but not in the query, then we use it as the output currency if use_currency \ and not coin2 \ and (coins_names.coin_name(use_currency) != '' \ or currencies_names.currency_name(use_currency) != ''): coin2 = use_currency print(coin2) ticks = 80 if coin2: data = aggregate.get_aggregated_pair(coin, coin2, time_begin, time_end, ticks) else: data = aggregate.get_aggregated_coin(coin, time_begin, time_end, ticks) if data['ticks'] == []: raise RuntimeError("No data found for your query. Wrong range?") #import json #print json.dumps(data['meta'], indent=True) warnings = [] if data['meta']['time_begin'] - time_begin > 3600 * 24: warnings.append( "for the moment, rate.sx has historical data only starting from 2018-Jan-07" ) options = dict( width=80, height=25, msg_interval='@' not in query, currency=coin2 or 'USD', warnings=warnings, ) dia = Diagram(data, (time_begin, time_end), options=options) return dia.make_view()
def view(query, use_currency=None): """ Main rendering function, entry point for this module. Returns rendered view for the ``query``. If currency is specified in ``query``, it overrides ``currency``. If ``currency`` is not specified, USD is used. """ try: coin, coin2, time_begin, time_end = _parse_query(query) except SyntaxError as e_msg: raise RuntimeError("%s" % e_msg) # if currency is specified in the domain name (use_currency) # but not in the query, then we use it as the output currency if use_currency \ and not coin2 \ and (coins_names.coin_name(use_currency) != '' \ or currencies_names.currency_name(use_currency) != ''): coin2 = use_currency print coin2 ticks = 80 if coin2: data = aggregate.get_aggregated_pair(coin, coin2, time_begin, time_end, ticks) else: data = aggregate.get_aggregated_coin(coin, time_begin, time_end, ticks) if data['ticks'] == []: raise RuntimeError("No data found for your query. Wrong range?") #import json #print json.dumps(data['meta'], indent=True) warnings = [] if data['meta']['time_begin'] - time_begin > 3600*24: warnings.append( "for the moment, rate.sx has historical data only starting from 2018-Jan-07") options = dict( width=80, height=25, msg_interval='@' not in query, currency=coin2 or 'USD', warnings=warnings, ) dia = Diagram(data, (time_begin, time_end), options=options) return dia.make_view()
def get_data(query, use_currency=None): try: coin, coin2, time_begin, time_end = _parse_query(query) except SyntaxError as e_msg: raise RuntimeError("%s" % e_msg) # if currency is specified in the domain name (use_currency) # but not in the query, then we use it as the output currency if use_currency \ and not coin2 \ and (coins_names.coin_name(use_currency) != '' \ or currencies_names.currency_name(use_currency) != ''): coin2 = use_currency ticks = 80 if coin2: data = aggregate.get_aggregated_pair(coin, coin2, time_begin, time_end, ticks) else: data = aggregate.get_aggregated_coin(coin, time_begin, time_end, ticks) return data
def get_aggregated_pair(coin1, coin2, time_start, time_end, number_of_ticks, key=None): # pylint: disable=too-many-locals,too-many-arguments,too-many-branches,too-many-statements """ Aggregate coin pairs (or coin and currency pairs). It works this way: find data for ``coin1`` and find data for ``coin2``, after that divide ``coin1`` data by ``coin2`` pairwise. This method is approximate for aggregated values. ``coin2`` can be a currency. """ coin2_is_currency = bool(currency_name(coin2)) desired_interval = (time_end - time_start) / number_of_ticks chosen_interval = None for interval_name, interval_size in sorted(INTERVAL.items(), key=lambda x: -x[1]): if interval_size < desired_interval: chosen_interval = interval_name # if interval is so small, we need to use the raw and not the aggregated data collection_name = None if chosen_interval: collection_name = 'coins_%s' % chosen_interval if key is None: key = "price_usd" entries1 = MONGO_READER.get_raw_data(coin1, time_start, time_end, collection_name=collection_name) if entries1 == []: return {'meta': {}, 'ticks': []} # depeding on (1) that we have a currency in coin2 or not # and (2) if data is aggregated, we have to read entries2 from different collections # and in one case postprocess them if coin2_is_currency: if collection_name is None: currencies_collection = 'currencies' else: currencies_collection = collection_name.replace( 'coins_', 'currencies_') fields = {'timestamp': 1, coin2: 1} entries2 = MONGO_READER.get_raw_data( None, time_start, time_end, collection_name=currencies_collection, fields=fields) new_entries2 = [] for entry in entries2: entry.update({key: entry[coin2]}) new_entries2.append(entry) entries2 = new_entries2 else: entries2 = MONGO_READER.get_raw_data(coin2, time_start, time_end, collection_name=collection_name) meta = {} ticks = [] sum_ = 0 if collection_name is None: # not aggregated data meta = { 'symbol': entries1[0]['symbol'], 'begin': entries1[0][key] / entries2[0][key], 'end': entries1[-1][key] / entries2[-1][key], 'time_begin': entries1[0]['timestamp'], 'time_end': entries1[-1]['timestamp'], 'min': entries1[0][key] / entries2[0][key], 'max': entries1[0][key] / entries2[0][key], 'time_min': entries1[0]['timestamp'], 'time_max': entries1[0]['timestamp'], } for entry1, entry2 in zip(entries1, entries2): this_value = entry1[key] / entry2[key] ticks.append(this_value) sum_ += this_value if this_value > meta['max']: meta['max'] = this_value meta['time_max'] = entry1['timestamp'] if this_value < meta['min']: meta['min'] = this_value meta['time_min'] = entry1['timestamp'] else: # aggregated data # this parameter should be taken to the ticks take_this = 'avg' meta = { 'symbol': entries1[0]['symbol'], 'begin': entries1[0][key]['begin'] / entries2[0][key]['begin'], 'end': entries1[-1][key]['end'] / entries2[-1][key]['end'], 'time_begin': entries1[0]['timestamp'], 'time_end': entries1[-1]['time_end'], 'min': entries1[0][key]['min'] / entries2[0][key]['max'], 'max': entries1[0][key]['max'] / entries2[0][key]['min'], 'time_min': entries1[0][key]['time_min'], 'time_max': entries1[0][key]['time_max'], } for entry1, entry2 in zip(entries1, entries2): this_value = entry1[key][take_this] / entry2[key][take_this] ticks.append(this_value) sum_ += this_value if this_value > meta['max']: meta['max'] = this_value meta['time_max'] = entry1[key]['time_max'] if this_value < meta['min']: meta['min'] = this_value meta['time_min'] = entry1[key]['time_min'] meta['avg'] = sum_ / len(ticks) return { 'ticks': ticks, 'meta': meta, }
def get_aggregated_pair(coin1, coin2, time_start, time_end, number_of_ticks, key=None): # pylint: disable=too-many-locals,too-many-arguments,too-many-branches,too-many-statements """ Aggregate coin pairs (or coin and currency pairs). It works this way: find data for ``coin1`` and find data for ``coin2``, after that divide ``coin1`` data by ``coin2`` pairwise. This method is approximate for aggregated values. ``coin2`` can be a currency. """ coin2_is_currency = bool(currency_name(coin2)) desired_interval = (time_end-time_start)/number_of_ticks chosen_interval = None for interval_name, interval_size in sorted(INTERVAL.items(), key=lambda x: -x[1]): if interval_size < desired_interval: chosen_interval = interval_name # if interval is so small, we need to use the raw and not the aggregated data collection_name = None if chosen_interval: collection_name = 'coins_%s' % chosen_interval if key is None: key = "price_usd" entries1 = MONGO_READER.get_raw_data( coin1, time_start, time_end, collection_name=collection_name) if entries1 == []: return {'meta':{}, 'ticks':[]} # depeding on (1) that we have a currency in coin2 or not # and (2) if data is aggregated, we have to read entries2 from different collections # and in one case postprocess them if coin2_is_currency: if collection_name is None: currencies_collection = 'currencies' else: currencies_collection = collection_name.replace('coins_', 'currencies_') fields = {'timestamp': 1, coin2: 1} entries2 = MONGO_READER.get_raw_data( None, time_start, time_end, collection_name=currencies_collection, fields=fields) new_entries2 = [] for entry in entries2: entry.update({key: entry[coin2]}) new_entries2.append(entry) entries2 = new_entries2 else: entries2 = MONGO_READER.get_raw_data( coin2, time_start, time_end, collection_name=collection_name) meta = {} ticks = [] sum_ = 0 if collection_name is None: # not aggregated data meta = { 'symbol': entries1[0]['symbol'], 'begin': entries1[0][key]/entries2[0][key], 'end': entries1[-1][key]/entries2[-1][key], 'time_begin': entries1[0]['timestamp'], 'time_end': entries1[-1]['timestamp'], 'min': entries1[0][key]/entries2[0][key], 'max': entries1[0][key]/entries2[0][key], 'time_min': entries1[0]['timestamp'], 'time_max': entries1[0]['timestamp'], } for entry1, entry2 in zip(entries1, entries2): this_value = entry1[key]/entry2[key] ticks.append(this_value) sum_ += this_value if this_value > meta['max']: meta['max'] = this_value meta['time_max'] = entry1['timestamp'] if this_value < meta['min']: meta['min'] = this_value meta['time_min'] = entry1['timestamp'] else: # aggregated data # this parameter should be taken to the ticks take_this = 'avg' meta = { 'symbol': entries1[0]['symbol'], 'begin': entries1[0][key]['begin']/entries2[0][key]['begin'], 'end': entries1[-1][key]['end']/entries2[-1][key]['end'], 'time_begin': entries1[0]['timestamp'], 'time_end': entries1[-1]['time_end'], 'min': entries1[0][key]['min']/entries2[0][key]['max'], 'max': entries1[0][key]['max']/entries2[0][key]['min'], 'time_min': entries1[0][key]['time_min'], 'time_max': entries1[0][key]['time_max'], } for entry1, entry2 in zip(entries1, entries2): this_value = entry1[key][take_this]/entry2[key][take_this] ticks.append(this_value) sum_ += this_value if this_value > meta['max']: meta['max'] = this_value meta['time_max'] = entry1[key]['time_max'] if this_value < meta['min']: meta['min'] = this_value meta['time_min'] = entry1[key]['time_min'] meta['avg'] = sum_/len(ticks) return { 'ticks': ticks, 'meta': meta, }