From f9ba7b89c8bedb401b07812b856873ec60838bc5 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 6 Aug 2022 09:48:48 +0000 Subject: [PATCH] `mmnode-ticker`: support rate asset in asset specifier This feature adds convenience for users whose native currencies are pegged to assets other than the US Dollar. Example: # Add a column for Bulgarian Lev, which is pegged to the Euro at 0.5113 EUR: $ mmnode-ticker -wE -c eur,bgn-bulgarian-lev:0.5113r:eur Testing / demo: $ test/test.py -e scripts.ticker --- mmgen_node_tools/Ticker.py | 31 +++++++++++++++++++++---------- mmgen_node_tools/data/version | 2 +- mmgen_node_tools/main_ticker.py | 14 +++++++++----- test/test_py_d/ts_misc.py | 23 +++++++++++++++++++++++ 4 files changed, 54 insertions(+), 16 deletions(-) diff --git a/mmgen_node_tools/Ticker.py b/mmgen_node_tools/Ticker.py index b667ccd..14163eb 100755 --- a/mmgen_node_tools/Ticker.py +++ b/mmgen_node_tools/Ticker.py @@ -83,18 +83,24 @@ def gen_data(data): 'id': {r.id for r in cfg.rows if getattr(r,'id',None)} - {'usd-us-dollar'}, 'symbol': {r.symbol for r in cfg.rows if isinstance(r,tuple) and r.id is None} - {'USD'}, } + usr_rate_assets = tuple(u.rate_asset for u in cfg.usr_rows + cfg.usr_columns if u.rate_asset) + usr_rate_assets_want = { + 'id': {a.id for a in usr_rate_assets if a.id}, + 'symbol': {a.symbol for a in usr_rate_assets if not a.id} + } usr_assets = cfg.usr_rows + cfg.usr_columns + tuple(c for c in (cfg.query or ()) if c) usr_wants = { 'id': ( - {a.id for a in usr_assets if a.id} - + {a.id for a in usr_assets + usr_rate_assets if a.id} - {a.id for a in usr_assets if a.rate and a.id} - {'usd-us-dollar'} ) , 'symbol': ( - {a.symbol for a in usr_assets if not a.id} - + {a.symbol for a in usr_assets + usr_rate_assets if not a.id} - {a.symbol for a in usr_assets if a.rate} - {'USD'} ), } found = { 'id': set(), 'symbol': set() } + rate_assets = {} for k in ['id','symbol']: wants = rows_want[k] | usr_wants[k] @@ -105,6 +111,8 @@ def gen_data(data): die(1,dup_sym_errmsg(d[k])) yield (d['id'],d) found[k].add(d[k]) + if d[k] in usr_rate_assets_want[k]: + rate_assets[d['symbol']] = d # NB: using symbol instead of ID if k == 'id' and len(found[k]) == len(wants): break @@ -119,11 +127,12 @@ def gen_data(data): User-supplied rate overrides rate from source data. """ _id = asset.id or f'{asset.symbol}-user-asset-{asset.symbol}'.lower() + ra_rate = Decimal(rate_assets[asset.rate_asset.symbol]['price_usd']) if asset.rate_asset else 1 yield ( _id, { 'symbol': asset.symbol, 'id': _id, - 'price_usd': str(Decimal(1/asset.rate)), - 'price_btc': str(Decimal(1/asset.rate/btcusd)), + 'price_usd': str(Decimal(ra_rate/asset.rate)), + 'price_btc': str(Decimal(ra_rate/asset.rate/btcusd)), 'last_updated': int(now), }) @@ -297,12 +306,12 @@ def make_cfg(cmd_args,cfg_in): def parse_usr_asset_arg(s): """ - asset_id[:rate] + asset_id[:rate[:rate_asset]] """ def parse_parm(s): ss = s.split(':') - assert len(ss) in (1,2), f'{s}: malformed argument' - asset_id,rate = (*ss,None)[:2] + assert len(ss) in (1,2,3), f'{s}: malformed argument' + asset_id,rate,rate_asset = (*ss,None,None)[:3] parsed_id = parse_asset_id(asset_id) return asset_data( @@ -312,7 +321,8 @@ def make_cfg(cmd_args,cfg_in): rate = ( None if rate is None else 1 / Decimal(rate[:-1]) if rate.lower().endswith('r') else - Decimal(rate) )) + Decimal(rate) ), + rate_asset = parse_asset_id(rate_asset) if rate_asset else None ) return tuple(parse_parm(s2) for s2 in s.split(',')) if s else () @@ -326,7 +336,8 @@ def make_cfg(cmd_args,cfg_in): symbol = parsed_id.symbol, id = parsed_id.id, amount = None if amount is None else Decimal(amount), - rate = None ) + rate = None, + rate_asset = None ) ss = s.split(':') assert len(ss) in (2,3,4), f'{s}: malformed argument' @@ -400,7 +411,7 @@ def make_cfg(cmd_args,cfg_in): 'portfolio' ]) query_tuple = namedtuple('query',['asset','to_asset']) - asset_data = namedtuple('asset_data',['symbol','id','amount','rate']) + asset_data = namedtuple('asset_data',['symbol','id','amount','rate','rate_asset']) asset_tuple = namedtuple('asset_tuple',['symbol','id']) usr_rows = parse_usr_asset_arg(opt.add_rows) diff --git a/mmgen_node_tools/data/version b/mmgen_node_tools/data/version index fb75e93..2590359 100644 --- a/mmgen_node_tools/data/version +++ b/mmgen_node_tools/data/version @@ -1 +1 @@ -3.1.dev5 +3.1.dev6 diff --git a/mmgen_node_tools/main_ticker.py b/mmgen_node_tools/main_ticker.py index e843475..9a32c04 100755 --- a/mmgen_node_tools/main_ticker.py +++ b/mmgen_node_tools/main_ticker.py @@ -76,19 +76,20 @@ symbol is ambiguous, the full ID must be used. Examples: ASSET SPECIFIERS have the following format: - ASSET[:RATE] + ASSET[:RATE[:RATE_ASSET]] If the asset referred to by ASSET is not in the source data (see --list-ids), -an arbitrarily chosen label may be used. RATE is the USD exchange rate of -the asset. When RATE is postfixed with the letter ‘r’, its meaning is -reversed, i.e. interpreted as ‘ASSET/USD’ instead of ‘USD/ASSET’. Asset -specifier examples: +an arbitrarily chosen label may be used. RATE is the exchange rate of the +asset in relation to RATE_ASSET, if present, otherwise USD. When RATE is +postfixed with the letter ‘r’, its meaning is reversed, i.e. interpreted as +‘ASSET/RATE_ASSET’ instead of ‘RATE_ASSET/ASSET’. Asset specifier examples: inr:79.5 - INR is not in the source data, so supply rate of 79.5 INR to the Dollar (USD/INR) inr:0.01257r - same as above, but use reverse rate (INR/USD) inr-indian-rupee:79.5 - same as first example, but add an arbitrary label omr-omani-rial:2.59r - Omani Rial is pegged to the Dollar at 2.59 USD + bgn-bg-lev:0.5113r:eur - Bulgarian Lev is pegged to the Euro at 0.5113 EUR A TRADE_SPECIFIER is a single argument in the format: @@ -147,6 +148,9 @@ $ mmnode-ticker --btc # proxy: $ mmnode-ticker -w -c eur,omr-omani-rial:2.59r -e2 -x http://vpnhost:8118 +# Wide display, elapsed update time, add EUR, BGN columns and BGN/EUR rate: +$ mmnode-ticker -wE -c eur,bgn-bulgarian-lev:0.5113r:eur + # Wide display, use cached data from previous network query, show portfolio # (see above), pipe output to pager, add DOGE row: $ mmnode-ticker -wCFP -r doge diff --git a/test/test_py_d/ts_misc.py b/test/test_py_d/ts_misc.py index 7295da3..0d39c7e 100755 --- a/test/test_py_d/ts_misc.py +++ b/test/test_py_d/ts_misc.py @@ -86,6 +86,8 @@ class TestSuiteScripts(TestSuiteBase): ('ticker14', 'ticker [--cached-data --wide --btc]'), ('ticker15', 'ticker [--cached-data --wide --btc btc:2:usd:45000]'), ('ticker16', 'ticker [--cached-data --wide --elapsed -c eur,omr-omani-rial:2.59r'), + ('ticker17', 'ticker [--cached-data --wide --elapsed -c bgn-bulgarian-lev:0.5113r:eur'), + ('ticker18', 'ticker [--cached-data --wide --elapsed -c eur,bgn-bulgarian-lev:0.5113r:eur-euro-token'), ) } @@ -283,3 +285,24 @@ class TestSuiteScripts(TestSuiteBase): r'BITCOIN 23,250.77 22,826.6890 8,977.1328 1.00000000 \+11.15 \+0.89 10 minutes ago', 'OMANI RIAL 2.59 2.5428 1.0000 0.00011139 -- -- just now' ]) + + def ticker17(self): + # BGN pegged at 0.5113 EUR + return self.ticker( + ['--wide','--elapsed','-c','bgn-bulgarian-lev:0.5113r:eur'], + [ + r'BGN \(BULGARIAN LEV\) = 0.52080 USD', + 'USD BGN BTC CHG_7d CHG_24h UPDATED', + 'BITCOIN 23,250.77 44,644.414 1.00000000', + 'BULGARIAN LEV 0.52 1.000 0.00002240', + ]) + + def ticker18(self): + return self.ticker( + ['--wide','--elapsed','-c','eur,bgn-bulgarian-lev:0.5113r:eur-euro-token'], + [ + r'BGN \(BULGARIAN LEV\) = 0.52080 USD', + 'USD EUR BGN BTC CHG_7d CHG_24h UPDATED', + 'BITCOIN 23,250.77 22,826.6890 44,644.414 1.00000000', + 'BULGARIAN LEV 0.52 0.5113 1.000 0.00002240', + ])