diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index a8b6595..d3045d4 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -24,7 +24,7 @@ from subprocess import run, PIPE, CalledProcessError from decimal import Decimal from collections import namedtuple -from mmgen.color import red, yellow, green, blue, orange, gray, cyan +from mmgen.color import red, yellow, green, blue, orange, gray, cyan, pink from mmgen.util import msg, msg_r, rmsg, Msg, Msg_r, die, fmt, fmt_list, fmt_dict, list_gen, suf, is_int from mmgen.ui import do_pager @@ -41,6 +41,15 @@ percent_cols = { 'm': 'month', 'y': 'year'} +sp = namedtuple('sort_parameter', ['key', 'desc']) +sort_params = { + 'd': sp('percent_change_24h', '1-day % change'), + 'w': sp('percent_change_7d', '1-week % change'), + 'm': sp('percent_change_30d', '1-month % change'), + 'y': sp('percent_change_1y', '1-year % change'), + 'p': sp('price_usd', 'asset price'), + 'c': sp('market_cap', 'market cap')} + class RowDict(dict): def __iter__(self): @@ -274,6 +283,7 @@ class DataSource: 'percent_change_30d': data['pct_chg_4wks'], 'percent_change_7d': data['pct_chg_1wk'], 'percent_change_24h': data['regularMarketChangePercent']['raw'] * 100, + 'market_cap': 0, # dummy - required for sorting 'last_updated': data['regularMarketTime']} def rate_limit_errmsg(self, elapsed): @@ -752,6 +762,19 @@ def make_cfg(gcfg_arg): '' if proxy == '' else 'none' if (proxy and proxy.lower() == 'none') else (proxy or cfg_in.cfg.get(name))) + def get_sort_opt(): + match get_cfg_var('sort'): + case None: + return None + case s if s in sort_params: + return (s, True) + case s if s in ['r' + ch for ch in sort_params]: + return (s[1], False) + case s: + die(1, + f'{s!r}: invalid parameter for --sort option (must be one of {fmt_list(sort_params)})' + '\nTo reverse the sort, prefix the code letter with ‘r’') + cfg_tuple = namedtuple('global_cfg',[ 'rows', 'usr_rows', @@ -766,6 +789,7 @@ def make_cfg(gcfg_arg): 'proxy', 'proxy2', 'portfolio', + 'sort', 'percent_cols', 'asset_limit', 'cached_data', @@ -823,6 +847,7 @@ def make_cfg(gcfg_arg): proxy = proxy, proxy2 = None if proxy2 == 'none' else '' if proxy2 == '' else (proxy2 or proxy), portfolio = portfolio, + sort = get_sort_opt(), percent_cols = parse_percent_cols(get_cfg_var('percent_cols')), asset_limit = get_cfg_var('asset_limit'), cached_data = get_cfg_var('cached_data'), @@ -869,6 +894,8 @@ class Ticker: def __init__(self, data): + global cfg + self.comma = ',' if cfg.thousands_comma else '' self.col1_wid = max(len('TOTAL'), ( @@ -885,6 +912,18 @@ class Ticker: for row in rows: self.max_rank = max(self.max_rank, int(data[row.id]['rank'])) + if cfg.sort: + code, reverse = cfg.sort + key = sort_params[code].key + sort_func = lambda row: data[row.id][key] + pf_sort_func = lambda row: data[row[0]][key] + for group in self.rows.keys(): + if group not in self.hidden_groups: + self.rows[group] = sorted(self.rows[group], key=sort_func, reverse=reverse) + if cfg.portfolio: + cfg = cfg._replace( + portfolio = sorted(cfg.portfolio, key=pf_sort_func, reverse=reverse)) + self.col_usd_prices = {k: self.data[k]['price_usd'] for k in self.col_ids} self.prices = {row.id: self.get_row_prices(row.id) for row in self.rows if row.id in data} self.prices['usd-us-dollar'] = self.get_row_prices('usd-us-dollar') @@ -951,6 +990,10 @@ class Ticker: yield 'Current time: {}'.format(cyan(time.strftime('%F %X', time.gmtime(now)) + ' UTC')) + if cfg.sort: + text = sort_params[cfg.sort[0]].desc + ('' if cfg.sort[1] else ' [reversed]') + yield f'Sort order: {pink(text.upper())}' + for asset in self.usr_col_assets: if asset.symbol != 'USD': usdprice = self.data[asset.id]['price_usd'] diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index 921f776..1de14a5 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.6.dev6 +3.6.dev7 diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index e533e62..7be5e90 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -57,6 +57,9 @@ opts_data = { -r, --add-rows=LIST Add rows for asset specifiers in LIST (comma-separated, see ASSET SPECIFIERS below). Can also be used to supply a USD exchange rate for missing assets. +-s, --sort=P Sort output according to parameter P. Valid parameters + are {sp_codes}. See SORT PARAMETERS below. + To reverse the sort, prefix the parameter with ‘r’. -t, --testing Print command(s) to be executed to stdout and exit -T, --thousands-comma Use comma as a thousands separator -u, --update-time Include UPDATED (last update time) column @@ -133,6 +136,10 @@ A TRADE_SPECIFIER is a single argument in the format: a USD rate for the missing asset(s) must be supplied via the --add-columns or --add-rows options. +SORT PARAMETERS: + + {sp_fmt} + PROXY NOTE @@ -225,6 +232,7 @@ To add a portfolio, edit the file dfl_cachedir = os.path.relpath(dfl_cachedir, start=homedir), ds = fmt_dict(DataSource.get_sources(), fmt='equal_compact'), al = DataSource.coinpaprika.dfl_asset_limit, + sp_codes = fmt_list(sort_params, fmt='fancy'), pc = fmt_list(Ticker.percent_cols, fmt='fancy')), 'notes': lambda s: s.format( assets = fmt_list(assets_list_gen(cfg_in), fmt='col', indent=' '), @@ -232,6 +240,7 @@ To add a portfolio, edit the file pf_cfg = os.path.relpath(cfg_in.portfolio_file, start=homedir), al = DataSource.coinpaprika.dfl_asset_limit, cc = src_cls['cc'](), + sp_fmt = '\n '.join(f'‘{k}’ - {v.desc}' for k, v in sort_params.items()), fi = src_cls['fi']()) } } @@ -246,7 +255,7 @@ gcfg = Config(opts_data=opts_data, caller_post_init=True) src_cls, cfg_in = Ticker.make_cfg(gcfg) -from .Ticker import dfl_cachedir, homedir, DataSource, assets_list_gen +from .Ticker import dfl_cachedir, homedir, DataSource, assets_list_gen, sort_params gcfg._post_init() diff --git a/test/cmdtest_d/misc.py b/test/cmdtest_d/misc.py index 3e909f4..275117f 100755 --- a/test/cmdtest_d/misc.py +++ b/test/cmdtest_d/misc.py @@ -90,6 +90,14 @@ class CmdTestScripts(CmdTestBase): ('ticker19', 'ticker [--cached-data 1-5]'), ('ticker20', 'ticker [--cached-data 2-5]'), ('ticker21', 'ticker [--cached-data 5-5]'), + ('ticker22', 'ticker [--sort=rp]'), + ('ticker23', 'ticker [--sort=rp xmr:10]'), + ('ticker24', 'ticker [--sort=p]'), + ('ticker25', 'ticker [--sort=p 200]'), + ('ticker26', 'ticker [--sort=c -r algo,ada]'), + ('ticker27', 'ticker [--sort=rp -r algo,ada]'), + ('ticker28', 'ticker [--sort=d -r algo,ada]'), + ('ticker29', 'ticker [--sort=y -r algo,ada]'), ) } @@ -365,3 +373,58 @@ class CmdTestScripts(CmdTestBase): r'8\) ADA 17.11161 0.51 0.4764 0.00002180', ], add_opts = ['--add-columns=eurusd=x']) + + def ticker22(self): + return self.ticker( + [], + ['MONERO', 'ETHEREUM', 'BITCOIN', 'SILVER', 'BRENT', 'GOLD'], + add_opts = ['--name-labels', '--sort=rp']) + + def ticker23(self): + return self.ticker( + [], + ['MONERO', 'ETHEREUM', 'BITCOIN', 'SILVER', 'BRENT', 'GOLD'], + add_opts = ['--name-labels', '--sort=rp', 'xmr:10']) + + def ticker24(self): + return self.ticker( + [], + ['BITCOIN', 'ETHEREUM', 'MONERO', 'GOLD', 'BRENT', 'SILVER'], + add_opts = ['--name-labels', '--sort=p']) + + def ticker25(self): + return self.ticker( + [], + [ + r' 1\) BITCOIN', + r' 2\) ETHEREUM', + r'30\) MONERO', + r'23\) LITECOIN', + r' 8\) CARDANO', + r'33\) ALGORAND' + ], + add_opts = ['--name-labels', '--sort=p', '200']) + + def ticker26(self): + return self.ticker( + [], + ['BITCOIN', 'ETHEREUM', 'MONERO', 'CARDANO', 'ALGORAND'], + add_opts = ['--name-labels', '--sort=c', '-r', 'ada,algo']) + + def ticker27(self): + return self.ticker( + [], + ['MONERO', 'ETHEREUM', 'BITCOIN', 'S&P', 'NASDAQ', 'DOW', 'ALGORAND', 'CARDANO'], + add_opts = ['--name-labels', '--sort=rp', '--add-rows=ada-cardano,algo-algorand']) + + def ticker28(self): + return self.ticker( + [], + ['ETHEREUM', 'MONERO', 'BITCOIN', 'NASDAQ', 'S&P', 'DOW', 'CARDANO', 'ALGORAND'], + add_opts = ['--widest', '--sort=d', '-r', 'ada,algo']) + + def ticker29(self): + return self.ticker( + [], + ['ETHEREUM', 'BITCOIN', 'MONERO', 'S&P', 'DOW', 'NASDAQ', 'CARDANO', 'ALGORAND'], + add_opts = ['--widest', '-s', 'y', '-r', 'ada,algo'])