mmnode-ticker: display percent change columns in terms of non-USD assets

Any crypto or finance asset may be specified.

Examples:

    # Display percentage changes in relation to Bitcoin:
    $ mmnode-ticker --widest --pchg-unit=btc

    # In relation to Gold:
    $ mmnode-ticker --widest --pchg-unit=gc=f

    # In relation to Euros:
    $ mmnode-ticker --widest --pchg-unit=eurusd=x

    # In relation to the Nasdaq Index:
    $ mmnode-ticker --widest --pchg-unit=^ixic
This commit is contained in:
The MMGen Project 2025-10-20 09:14:34 +00:00
commit 84e8ea65d0
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
5 changed files with 82 additions and 3 deletions

View file

@ -535,6 +535,10 @@ def gen_data(data):
'name': 'US Dollar',
'price_usd': Decimal(1),
'price_btc': Decimal(1) / btcusd,
'percent_change_24h': 0.0,
'percent_change_7d': 0.0,
'percent_change_30d': 0.0,
'percent_change_1y': 0.0,
'last_updated': None})
def cache_data(data_src, no_overwrite=False):
@ -742,7 +746,8 @@ def make_cfg(gcfg_arg):
{k: tuple(parse_asset_id(e) for e in v) for k, v in cfg_in.cfg['assets'].items()})
for hdr, data in (
('user_uniq', get_usr_assets()),
('portfolio_uniq', get_portfolio_assets())):
('portfolio_uniq', get_portfolio_assets()),
('pchg_unit_uniq', [pchg_unit] if pchg_unit else None)):
if data:
if uniq_data := tuple(gen_uniq(data, 'symbol', preload=rows)):
rows[hdr] = uniq_data
@ -791,6 +796,7 @@ def make_cfg(gcfg_arg):
'portfolio',
'sort',
'percent_cols',
'pchg_unit',
'asset_limit',
'cached_data',
'elapsed',
@ -833,6 +839,9 @@ def make_cfg(gcfg_arg):
if portfolio and asset_range:
die(1, '--portfolio not supported in market cap view')
pchg_unit = (lambda s: parse_asset_id(s, require_label=False) if s else None)(
get_cfg_var('pchg_unit'))
cfg = cfg_tuple(
rows = create_rows(),
usr_rows = usr_rows,
@ -849,6 +858,7 @@ def make_cfg(gcfg_arg):
portfolio = portfolio,
sort = get_sort_opt(),
percent_cols = parse_percent_cols(get_cfg_var('percent_cols')),
pchg_unit = pchg_unit,
asset_limit = get_cfg_var('asset_limit'),
cached_data = get_cfg_var('cached_data'),
elapsed = get_cfg_var('elapsed'),
@ -890,7 +900,7 @@ class Ticker:
offer = None
to_asset = None
hidden_groups = ('extra',)
hidden_groups = ('extra', 'pchg_unit_uniq')
def __init__(self, data):
@ -925,6 +935,14 @@ class Ticker:
cfg = cfg._replace(
portfolio = sorted(cfg.portfolio, key=pf_sort_func, reverse=reverse))
if cfg.pchg_unit:
self.pchg_data = self.data[self.get_id(cfg.pchg_unit)]
self.pchg_factors = {k: (self.pchg_data[k] / 100) + 1 for k in (
'percent_change_24h',
'percent_change_7d',
'percent_change_30d',
'percent_change_1y')}
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')
@ -995,6 +1013,11 @@ class Ticker:
text = sort_params[cfg.sort[0]].desc + ('' if cfg.sort[1] else ' [reversed]')
yield f'Sort order: {pink(text.upper())}'
if cfg.pchg_unit:
yield 'Percent change unit: {}'.format(orange('{} ({})'.format(
self.pchg_data['symbol'],
self.pchg_data['name'].upper())))
for asset in self.usr_col_assets:
if asset.symbol != 'USD':
usdprice = self.data[asset.id]['price_usd']
@ -1085,6 +1108,8 @@ class Ticker:
def fmt_pct(d, key, wid=7):
if (n := d.get(key)) is None:
return gray(' --')
if cfg.pchg_unit:
n = ((((n / 100) + 1) / self.pchg_factors[key]) - 1) * 100
return (red, green)[n>=0](f'{n:+{wid}.2f}')
p = self.prices[d['id']]

View file

@ -1 +1 @@
3.6.dev8
3.6.dev9

View file

@ -63,6 +63,8 @@ opts_data = {
-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
-U, --pchg-unit=A Use asset A as unit of reference for percentage
change columns (default: USD)
-v, --verbose Be more verbose
-w, --wide Display most optional columns (same as -unT -p d,w)
-W, --widest Display all optional columns (same as -unT -p d,w,m,y)

View file

@ -98,6 +98,9 @@ class CmdTestScripts(CmdTestBase):
('ticker27', 'ticker [--sort=rp -r algo,ada]'),
('ticker28', 'ticker [--sort=d -r algo,ada]'),
('ticker29', 'ticker [--sort=y -r algo,ada]'),
('ticker30', 'ticker [--cached-data --wide --pchg-unit=btc --sort=d] (cf with config file)'),
('ticker31', 'ticker [--cached-data --wide --pchg-unit=usd] (cf with no USD)'),
('ticker32', 'ticker [--cached-data --wide --pchg-unit=gc=f]'),
)
}
@ -438,3 +441,38 @@ class CmdTestScripts(CmdTestBase):
[],
['ETHEREUM', 'BITCOIN', 'MONERO', 'S&P', 'DOW', 'NASDAQ', 'CARDANO', 'ALGORAND'],
add_opts = ['--widest', '-s', 'y', '-r', 'ada,algo'])
def ticker30(self):
self.copy_file('ticker-cfg-sort-pchg.yaml', 'ticker-cfg.yaml')
t = self.ticker(add_opts=['--wide'])
chk1 = '\n'.join(t.read().splitlines()[5:-2])
self.rm_file('ticker-cfg.yaml')
self.copy_file('ticker-cfg-bad.yaml', 'ticker-cfg.yaml')
t = self.ticker(add_opts=['--wide', '--pchg-unit=btc', '--sort=d'], no_msg=True)
chk2 = '\n'.join(t.read().splitlines()[5:-2])
self.rm_file('ticker-cfg.yaml')
assert chk1 == chk2, f'\nOUTPUT 1\n{chk1}\n!= OUTPUT 2\n{chk2}\n'
return t
def ticker31(self):
t = self.ticker(add_opts=['--wide'])
chk1 = '\n'.join(t.read().splitlines()[5:-2])
t = self.ticker(add_opts=['--wide', '--pchg-unit=usd'], no_msg=True)
chk2 = '\n'.join(t.read().splitlines()[6:-2])
assert chk1 == chk2, f'\nOUTPUT 1\n{chk1}\n!= OUTPUT 2\n{chk2}\n'
return t
def ticker32(self):
return self.ticker(
[],
[
'BITCOIN', r'\+10.99', r'\+7.06', '-1.18', r'\+1.05',
'ETHEREUM',
'GOLD', r'\+0.00', r'\+0.00', r'\+0.00', r'\+0.00',
'SILVER'
],
add_opts = ['--widest', '--pchg-unit=gc=f'])

View file

@ -0,0 +1,14 @@
sort: d
pchg_unit: btc
assets:
coin1:
- btc-bitcoin
- ltc-litecoin
- eth-ethereum
- xmr-monero
- bad-badcoin
commodity:
- gc=f
- si=f
- bz=f