Browse Source

`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
The MMGen Project 1 year ago
parent
commit
f9ba7b89c8

+ 21 - 10
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)

+ 1 - 1
mmgen_node_tools/data/version

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

+ 9 - 5
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

+ 23 - 0
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',
+			])