|
|
@@ -25,7 +25,7 @@ from decimal import Decimal
|
|
|
from collections import namedtuple
|
|
|
|
|
|
from mmgen.color import red, yellow, green, blue, orange, gray
|
|
|
-from mmgen.util import msg, msg_r, rmsg, Msg, Msg_r, die, fmt, fmt_list, fmt_dict, list_gen, suf
|
|
|
+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
|
|
|
|
|
|
homedir = os.getenv('HOME')
|
|
|
@@ -182,9 +182,10 @@ class DataSource:
|
|
|
net_data_type = 'json'
|
|
|
has_verbose = True
|
|
|
dfl_asset_limit = 2000
|
|
|
+ max_asset_idx = 1_000_000
|
|
|
|
|
|
def __init__(self):
|
|
|
- self.asset_limit = int(cfg.asset_limit or self.dfl_asset_limit)
|
|
|
+ self.asset_limit = int(cfg.asset_limit) if is_int(cfg.asset_limit) else self.dfl_asset_limit
|
|
|
|
|
|
def rate_limit_errmsg(self, elapsed):
|
|
|
rem = self.timeout - elapsed
|
|
|
@@ -573,9 +574,20 @@ def main():
|
|
|
return
|
|
|
|
|
|
if gcfg.list_ids:
|
|
|
- do_pager('\n'.join(e.data['id'] for e in src_data['cc']))
|
|
|
+ do_pager('\n'.join(e['id'] for e in src_data['cc'].data))
|
|
|
return
|
|
|
|
|
|
+ global cfg
|
|
|
+
|
|
|
+ if cfg.asset_range:
|
|
|
+ func = DataSource.coinpaprika.parse_asset_id
|
|
|
+ n, m = cfg.asset_range
|
|
|
+ cfg = cfg._replace(rows =
|
|
|
+ tuple(func(e['id'], require_label=False) for e in src_data['cc'].data[n-1:m])
|
|
|
+ + tuple(['-'])
|
|
|
+ + tuple([func('btc-bitcoin', require_label=True)])
|
|
|
+ + tuple(r for r in cfg.rows if isinstance(r, tuple) and r.source == 'fi'))
|
|
|
+
|
|
|
global now
|
|
|
now = 1659465400 if gcfg.test_suite else time.time() # 1659524400 1659445900
|
|
|
|
|
|
@@ -606,7 +618,7 @@ def make_cfg(gcfg_arg):
|
|
|
return tuple(gen())
|
|
|
|
|
|
def parse_percent_cols(arg):
|
|
|
- if arg is None:
|
|
|
+ if arg is None or arg.lower() in ('none', ''):
|
|
|
return []
|
|
|
res = arg.lower().split(',')
|
|
|
for s in res:
|
|
|
@@ -638,9 +650,26 @@ def make_cfg(gcfg_arg):
|
|
|
source = parsed_id.source)
|
|
|
|
|
|
cl_opt = getattr(gcfg, key)
|
|
|
+ if (cl_opt or '').lower() in ('', 'none'):
|
|
|
+ return ()
|
|
|
cf_opt = cfg_in.cfg.get(key,[]) if use_cf_file else []
|
|
|
return tuple(parse_parm(s) for s in (cl_opt.split(',') if cl_opt else cf_opt))
|
|
|
|
|
|
+ def parse_asset_range(s):
|
|
|
+ max_idx = DataSource.coinpaprika.max_asset_idx
|
|
|
+ match s.split('-'):
|
|
|
+ case [a, b] if is_int(a) and is_int(b):
|
|
|
+ n, m = (int(a), int(b))
|
|
|
+ case [a] if is_int(a):
|
|
|
+ n, m = (1, int(a))
|
|
|
+ case _:
|
|
|
+ return None
|
|
|
+ if n < 1 or m < 1 or n > m:
|
|
|
+ raise ValueError(f'‘{s}’: invalid asset range specifier')
|
|
|
+ if m > max_idx:
|
|
|
+ raise ValueError(f'‘{s}’: end of range must be <= {max_idx}')
|
|
|
+ return (n, m)
|
|
|
+
|
|
|
def parse_query_arg(s):
|
|
|
"""
|
|
|
asset_id:amount[:to_asset_id[:to_amount]]
|
|
|
@@ -712,11 +741,24 @@ def make_cfg(gcfg_arg):
|
|
|
rows += (hdr,) + uniq_data
|
|
|
return rows
|
|
|
|
|
|
+ def get_cfg_var(name):
|
|
|
+ if name in gcfg._uopts:
|
|
|
+ return getattr(gcfg, name)
|
|
|
+ else:
|
|
|
+ return getattr(gcfg, name) or cfg_in.cfg.get(name)
|
|
|
+
|
|
|
+ def get_proxy(name):
|
|
|
+ proxy = getattr(gcfg, name)
|
|
|
+ return (
|
|
|
+ '' if proxy == '' else 'none' if (proxy and proxy.lower() == 'none')
|
|
|
+ else (proxy or cfg_in.cfg.get(name)))
|
|
|
+
|
|
|
cfg_tuple = namedtuple('global_cfg',[
|
|
|
'rows',
|
|
|
'usr_rows',
|
|
|
'usr_columns',
|
|
|
'query',
|
|
|
+ 'asset_range',
|
|
|
'adjust',
|
|
|
'clsname',
|
|
|
'btc_only',
|
|
|
@@ -743,7 +785,6 @@ def make_cfg(gcfg_arg):
|
|
|
src_cls = {k: getattr(DataSource, v) for k, v in DataSource.get_sources().items()}
|
|
|
fi_pat = src_cls['fi'].asset_id_pat
|
|
|
|
|
|
- cmd_args = gcfg._args
|
|
|
cfg_in = get_cfg_in()
|
|
|
|
|
|
if gcfg.test_suite: # required for testing with overlay
|
|
|
@@ -751,15 +792,17 @@ def make_cfg(gcfg_arg):
|
|
|
this_mod.src_cls = src_cls
|
|
|
this_mod.cfg_in = cfg_in
|
|
|
|
|
|
+ if cmd_args := gcfg._args:
|
|
|
+ if len(cmd_args) > 1:
|
|
|
+ die(1, 'Only one command-line argument is allowed')
|
|
|
+ asset_range = parse_asset_range(cmd_args[0])
|
|
|
+ query = None if asset_range else parse_query_arg(cmd_args[0])
|
|
|
+ else:
|
|
|
+ asset_range = None
|
|
|
+ query = None
|
|
|
+
|
|
|
usr_rows = parse_usr_asset_arg('add_rows')
|
|
|
usr_columns = parse_usr_asset_arg('add_columns', use_cf_file=True)
|
|
|
- query = parse_query_arg(cmd_args[0]) if cmd_args else None
|
|
|
-
|
|
|
- def get_proxy(name):
|
|
|
- proxy = getattr(gcfg, name)
|
|
|
- return (
|
|
|
- '' if proxy == '' else 'none' if (proxy and proxy.lower() == 'none')
|
|
|
- else (proxy or cfg_in.cfg.get(name)))
|
|
|
|
|
|
proxy = get_proxy('proxy')
|
|
|
proxy = None if proxy == 'none' else proxy
|
|
|
@@ -770,29 +813,27 @@ def make_cfg(gcfg_arg):
|
|
|
usr_rows = usr_rows,
|
|
|
usr_columns = usr_columns,
|
|
|
query = query,
|
|
|
+ asset_range = asset_range,
|
|
|
adjust = (lambda x: (100 + x) / 100 if x else 1)(Decimal(gcfg.adjust or 0)),
|
|
|
clsname = 'trading' if query else 'overview',
|
|
|
- btc_only = gcfg.btc or cfg_in.cfg.get('btc'),
|
|
|
- add_prec = parse_add_precision(gcfg.add_precision or cfg_in.cfg.get('add_precision')),
|
|
|
- cachedir = gcfg.cachedir or cfg_in.cfg.get('cachedir') or dfl_cachedir,
|
|
|
+ btc_only = get_cfg_var('btc'),
|
|
|
+ add_prec = parse_add_precision(get_cfg_var('add_precision')),
|
|
|
+ cachedir = get_cfg_var('cachedir') or dfl_cachedir,
|
|
|
proxy = proxy,
|
|
|
proxy2 = None if proxy2 == 'none' else '' if proxy2 == '' else (proxy2 or proxy),
|
|
|
portfolio =
|
|
|
- get_portfolio()
|
|
|
- if cfg_in.portfolio
|
|
|
- and (gcfg.portfolio or cfg_in.cfg.get('portfolio'))
|
|
|
- and not query
|
|
|
+ get_portfolio() if cfg_in.portfolio and get_cfg_var('portfolio') and not query
|
|
|
else None,
|
|
|
- percent_cols = parse_percent_cols(gcfg.percent_cols or cfg_in.cfg.get('percent_cols')),
|
|
|
- asset_limit = gcfg.asset_limit or cfg_in.cfg.get('asset_limit'),
|
|
|
- cached_data = gcfg.cached_data or cfg_in.cfg.get('cached_data'),
|
|
|
- elapsed = gcfg.elapsed or cfg_in.cfg.get('elapsed'),
|
|
|
- name_labels = gcfg.name_labels or cfg_in.cfg.get('name_labels'),
|
|
|
- pager = gcfg.pager or cfg_in.cfg.get('pager'),
|
|
|
- thousands_comma = gcfg.thousands_comma or cfg_in.cfg.get('thousands_comma'),
|
|
|
- update_time = gcfg.update_time or cfg_in.cfg.get('update_time'),
|
|
|
- quiet = gcfg.quiet or cfg_in.cfg.get('quiet'),
|
|
|
- verbose = gcfg.verbose or cfg_in.cfg.get('verbose'))
|
|
|
+ percent_cols = parse_percent_cols(get_cfg_var('percent_cols')),
|
|
|
+ asset_limit = get_cfg_var('asset_limit'),
|
|
|
+ cached_data = get_cfg_var('cached_data'),
|
|
|
+ elapsed = get_cfg_var('elapsed'),
|
|
|
+ name_labels = get_cfg_var('name_labels'),
|
|
|
+ pager = get_cfg_var('pager'),
|
|
|
+ thousands_comma = get_cfg_var('thousands_comma'),
|
|
|
+ update_time = get_cfg_var('update_time'),
|
|
|
+ quiet = get_cfg_var('quiet'),
|
|
|
+ verbose = get_cfg_var('verbose'))
|
|
|
|
|
|
def get_cfg_in():
|
|
|
ret = namedtuple('cfg_in_data', ['cfg', 'portfolio', 'cfg_file', 'portfolio_file'])
|
|
|
@@ -920,12 +961,14 @@ class Ticker:
|
|
|
if self.table_hdr:
|
|
|
yield self.table_hdr
|
|
|
|
|
|
- for row in self.rows:
|
|
|
+ for n, row in enumerate(self.rows, cfg.asset_range[0] if cfg.asset_range else 1):
|
|
|
if isinstance(row, str):
|
|
|
+ if cfg.asset_range:
|
|
|
+ return
|
|
|
yield ('-' * self.hl_wid)
|
|
|
else:
|
|
|
try:
|
|
|
- yield self.fmt_row(self.data[row.id])
|
|
|
+ yield self.fmt_row(self.data[row.id], idx=n)
|
|
|
except KeyError:
|
|
|
yield gray(f'(no data for {row.id})')
|
|
|
|
|
|
@@ -979,7 +1022,7 @@ class Ticker:
|
|
|
d['price_usd'] / self.col_usd_prices[k]
|
|
|
) * self.adjust for k in self.col_ids}
|
|
|
|
|
|
- def fmt_row(self, d, amt=None, amt_fmt=None):
|
|
|
+ def fmt_row(self, d, amt=None, amt_fmt=None, idx=None):
|
|
|
|
|
|
def fmt_pct(n):
|
|
|
return gray(' --') if n is None else (red, green)[n>=0](f'{n:+7.2f}')
|
|
|
@@ -992,6 +1035,7 @@ class Ticker:
|
|
|
amt_fmt = amt_fmt.rstrip('0').rstrip('.')
|
|
|
|
|
|
return self.fs_num.format(
|
|
|
+ idx = idx,
|
|
|
lbl = self.create_label(d['id']) if cfg.name_labels else d['symbol'],
|
|
|
pc1 = fmt_pct(d.get('percent_change_7d')),
|
|
|
pc2 = fmt_pct(d.get('percent_change_24h')),
|
|
|
@@ -1051,6 +1095,15 @@ class Ticker:
|
|
|
self.fs_num2 = ''.join(col_fs_data[c].fs_num for c in cols2)
|
|
|
self.hl_wid2 = sum(col_fs_data[c].wid for c in cols2)
|
|
|
|
|
|
+ if cfg.asset_range:
|
|
|
+ def get_col1_w():
|
|
|
+ for n, r in enumerate(cfg.rows):
|
|
|
+ if isinstance(r, str):
|
|
|
+ return len(str(n))
|
|
|
+ col1_w = get_col1_w()
|
|
|
+ self.fs_str = ' ' * (col1_w + 2) + self.fs_str
|
|
|
+ self.fs_num = f'{{idx:{col1_w}}}) ' + self.fs_num
|
|
|
+
|
|
|
@property
|
|
|
def table_hdr(self):
|
|
|
return self.fs_str.format(
|
|
|
@@ -1126,7 +1179,7 @@ class Ticker:
|
|
|
self.fs_str += ' {upd}'
|
|
|
self.hl_wid += self.upd_w + 2
|
|
|
|
|
|
- def fmt_row(self, d):
|
|
|
+ def fmt_row(self, d, idx=None):
|
|
|
id = d['id']
|
|
|
p = self.prices[id][self.asset.id] * self.asset.amount
|
|
|
p_spot = '{:{}{}.{}f}'.format(p, self.max_wid, self.comma, 8+cfg.add_prec)
|