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
This commit is contained in:
The MMGen Project 2022-08-06 09:48:48 +00:00
commit f9ba7b89c8
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
4 changed files with 54 additions and 16 deletions

View file

@ -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)

View file

@ -1 +1 @@
3.1.dev5
3.1.dev6

View file

@ -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

View file

@ -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',
])