#!/usr/bin/env python3 # AUTHOR: Daesorin # CREATED: 2026-05-26 12:45 +0100 # UPDATED: 2026-07-12 15:13 +0100 """ rate v0.6 terminal multi-asset valuation engine. runs entirely on the python standard library with zero external dependencies. supports specialized workflows for forex, precious metals, energy futures, market indices, crypto, equities, and ETFs. """ import json import os import re import socket import sys import urllib.error import urllib.parse import urllib.request # FORCE IPV4 RESOLUTION # prevent unreachable network errors on broken ipv6 internet gateways _old_getaddrinfo = socket.getaddrinfo def _ipv4_getaddrinfo(*args, **kwargs): responses = _old_getaddrinfo(*args, **kwargs) return [res for res in responses if res[0] == socket.AF_INET] socket.getaddrinfo = _ipv4_getaddrinfo # TERMINAL COLOURS def get_colours(): if not sys.stdout.isatty() or os.environ.get("NO_COLOR"): return { "red": "", "green": "", "yellow": "", "blue": "", "cyan": "", "nc": "", } return { "red": "\033[1;31m", "green": "\033[1;32m", "yellow": "\033[1;33m", "blue": "\033[1;34m", "cyan": "\033[1;36m", "nc": "\033[0m", } COL = get_colours() # ASSET MAPS CRYPTO_MAP = { "btc": "bitcoin", "eth": "ethereum", "usdt": "tether", "bnb": "binancecoin", "sol": "solana", "xrp": "ripple", "usdc": "usd-coin", "ada": "cardano", "doge": "dogecoin", "ton": "the-open-network", "trx": "tron", "avax": "avalanche-2", "shib": "shiba-inu", "dot": "polkadot", "link": "chainlink", } METAL_MAP = { "gold": "GC=F", "silver": "SI=F", "platinum": "PL=F", "palladium": "PA=F", "copper": "HG=F", } ENERGY_MAP = { "oil": "CL=F", "wti": "CL=F", "brent": "BZ=F", "gas": "NG=F", "natgas": "NG=F", "heating": "HO=F", } INDEX_MAP = { "sp500": "^GSPC", "s&p500": "^GSPC", "nasdaq": "^IXIC", "dow": "^DJI", "dji": "^DJI", "ftse": "^FTSE", "nikkei": "^N225", "dax": "^GDAXI", } # FORMATTING UTILITIES def fmt_num(val, decimals=2): return f"{val:,.{decimals}f}" def fmt_rate(val): if val == 0: return "0.00" if abs(val) < 0.0001: return f"{val:,.8f}".rstrip("0").rstrip(".") if abs(val) < 1.0: return f"{val:,.4f}" return f"{val:,.2f}" # CORE HTTP ENGINE def fetch_json(url, params=None): if params: url = f"{url}?{urllib.parse.urlencode(params)}" req = urllib.request.Request( url, headers={ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", "Accept": "application/json", }, ) try: with urllib.request.urlopen(req, timeout=10) as response: return json.loads(response.read().decode("utf-8")) except urllib.error.HTTPError as e: print( f"{COL['red']}error: api request failed with status {e.code}.{COL['nc']}", file=sys.stderr, ) sys.exit(1) except urllib.error.URLError: print( f"{COL['red']}error: network connection timed out or unreachable.{COL['nc']}", file=sys.stderr, ) sys.exit(1) except json.JSONDecodeError: print( f"{COL['red']}error: failed to parse remote json payload.{COL['nc']}", file=sys.stderr, ) sys.exit(1) def get_forex_rate(base, target): base, target = base.upper(), target.upper() if base == target: return 1.0 data = fetch_json(f"https://open.er-api.com/v6/latest/{base}") rates = data.get("rates", {}) if target not in rates: print( f"{COL['red']}error: currency target '{target}' unsupported.{COL['nc']}", file=sys.stderr, ) sys.exit(1) return float(rates[target]) def fetch_yahoo_market_data(ticker): encoded = urllib.parse.quote(ticker) url = f"https://query2.finance.yahoo.com/v8/finance/chart/{encoded}" data = fetch_json(url, {"interval": "1d", "range": "1d"}) result = data.get("chart", {}).get("result") if not result: print( f"{COL['red']}error: market asset '{ticker}' not found on yahoo finance.{COL['nc']}", file=sys.stderr, ) sys.exit(1) meta = result[0].get("meta", {}) price = float(meta.get("regularMarketPrice", 0.0)) prev_close = float( meta.get("chartPreviousClose") or meta.get("previousClose") or price ) return { "symbol": meta.get("symbol", ticker.upper()), "name": ( meta.get("shortName") or meta.get("longName") or ticker ).strip(), "price": price, "currency": meta.get("currency", "USD").upper(), "change_pct": ((price - prev_close) / prev_close * 100.0) if prev_close else 0.0, } # UTILITY PARSER FOR MARKET INPUTS def parse_market_args(args, default_target="NGN"): amount = 1.0 target = default_target if len(args) >= 2: if re.match(r"^[0-9]+(\.[0-9]+)?$", args[1]): amount = float(args[1]) if len(args) >= 3: target = args[2] else: target = args[1] if len(args) >= 3 and re.match(r"^[0-9]+(\.[0-9]+)?$", args[2]): amount = float(args[2]) return amount, target.upper() # SUBCOMMAND EXECUTORS def cmd_forex(args): amount, base, target = 1.0, "USD", "NGN" if len(args) == 1: if re.match(r"^[0-9]+(\.[0-9]+)?$", args[0]): amount = float(args[0]) else: base = args[0] elif len(args) == 2: if re.match(r"^[0-9]+(\.[0-9]+)?$", args[0]): amount, base = float(args[0]), args[1] else: base, target = args[0], args[1] elif len(args) >= 3: if re.match(r"^[0-9]+(\.[0-9]+)?$", args[0]): amount, base, target = float(args[0]), args[1], args[2] else: base, target, amount = args[0], args[1], float(args[2]) base, target = base.upper(), target.upper() print( f"{COL['blue']}fetching exchange rate for {base}/{target}...{COL['nc']}", file=sys.stderr, ) rate = get_forex_rate(base, target) val = amount * rate print("-----------------------------------") print(f"pair: {base} / {target}") print(f"rate: {COL['yellow']}1 {base} = {fmt_rate(rate)} {target}{COL['nc']}") print( f"value: {COL['green']}{target} {fmt_num(val, 2)}{COL['nc']} ({base} {fmt_num(amount, 2)})" ) print("-----------------------------------") def cmd_yahoo_generic(ticker, amount, target_curr, class_label): print( f"{COL['blue']}fetching evaluation for {class_label} [{ticker}]...{COL['nc']}", file=sys.stderr, ) market = fetch_yahoo_market_data(ticker) native_curr = market["currency"] native_price = market["price"] total_native = amount * native_price if native_curr != target_curr: fx_rate = get_forex_rate(native_curr, target_curr) final_val = total_native * fx_rate rate_disp = f"{fmt_rate(native_price)} {native_curr} (~{fmt_num(native_price * fx_rate, 2)} {target_curr})" val_disp = f"{COL['green']}{fmt_num(final_val, 2)} {target_curr}{COL['nc']} ({fmt_num(total_native, 2)} {native_curr})" else: rate_disp = f"{fmt_rate(native_price)} {native_curr}" val_disp = f"{COL['green']}{fmt_num(total_native, 2)} {native_curr}{COL['nc']}" chg = market["change_pct"] chg_col = COL["green"] if chg >= 0 else COL["red"] chg_sign = "+" if chg >= 0 else "" print("-----------------------------------") print(f"asset: {market['name']} ({market['symbol']})") print(f"valuation: {COL['yellow']}{rate_disp}{COL['nc']}") print(f"24h move: {chg_col}{chg_sign}{chg:.2f}%{COL['nc']}") if amount != 1.0 or native_curr != target_curr: print(f"total sum: {val_disp}") print("-----------------------------------") def cmd_metal(args): if not args: print( f"{COL['red']}error: missing metal identifier.{COL['nc']}", file=sys.stderr, ) sys.exit(1) query = args[0].lower().strip() ticker = METAL_MAP.get(query, args[0].upper()) amount, target = parse_market_args(args) cmd_yahoo_generic(ticker, amount, target, "Precious Metal") def cmd_energy(args): if not args: print( f"{COL['red']}error: missing energy identifier.{COL['nc']}", file=sys.stderr, ) sys.exit(1) query = args[0].lower().strip() ticker = ENERGY_MAP.get(query, args[0].upper()) amount, target = parse_market_args(args) cmd_yahoo_generic(ticker, amount, target, "Energy Future") def cmd_indices(args): if not args: print( f"{COL['red']}error: missing index symbol.{COL['nc']}", file=sys.stderr, ) sys.exit(1) query = args[0].lower().strip() ticker = INDEX_MAP.get(query, args[0].upper()) amount, target = parse_market_args(args) cmd_yahoo_generic(ticker, amount, target, "Market Index") def cmd_stock(args): if not args: print( f"{COL['red']}error: missing stock ticker.{COL['nc']}", file=sys.stderr, ) sys.exit(1) ticker = args[0].upper() amount, target = parse_market_args(args) cmd_yahoo_generic(ticker, amount, target, "Equity Asset") def cmd_etf(args): if not args: print( f"{COL['red']}error: missing ETF symbol.{COL['nc']}", file=sys.stderr, ) sys.exit(1) ticker = args[0].upper() amount, target = parse_market_args(args) cmd_yahoo_generic(ticker, amount, target, "Exchange Traded Fund") def cmd_crypto(args): if not args: print( f"{COL['red']}error: missing crypto token name or symbol.{COL['nc']}", file=sys.stderr, ) sys.exit(1) query = args[0].lower().strip() amount, vs_curr = parse_market_args(args, default_target="ngn") vs_target = vs_curr.lower() if query in CRYPTO_MAP: coin_id, symbol = CRYPTO_MAP[query], query.upper() else: print( f"{COL['blue']}searching coingecko for '{query}'...{COL['nc']}", file=sys.stderr, ) search = fetch_json( "https://api.coingecko.com/api/v3/search", {"query": query} ) coins = search.get("coins", []) if not coins: print( f"{COL['red']}error: token '{query}' not found.{COL['nc']}", file=sys.stderr, ) sys.exit(1) coin_id, symbol = coins[0]["id"], coins[0]["symbol"].upper() print( f"{COL['blue']}fetching {symbol} spot data in {vs_curr.upper()}...{COL['nc']}", file=sys.stderr, ) data = fetch_json( "https://api.coingecko.com/api/v3/simple/price", { "ids": coin_id, "vs_currencies": vs_target, "include_24hr_change": "true", }, ) coin_data = data.get(coin_id, {}) if vs_target not in coin_data: print( f"{COL['red']}error: target market currency '{vs_curr}' unsupported.{COL['nc']}", file=sys.stderr, ) sys.exit(1) rate = float(coin_data[vs_target]) change = float(coin_data.get(f"{vs_target}_24h_change", 0.0)) val = amount * rate chg_col = COL["green"] if change >= 0 else COL["red"] chg_sign = "+" if change >= 0 else "" print("-----------------------------------") print(f"crypto: {symbol} ({coin_id})") print(f"spot rate: {COL['yellow']}{fmt_rate(rate)} {vs_curr.upper()}{COL['nc']}") print(f"24h move: {chg_col}{chg_sign}{change:.2f}%{COL['nc']}") if amount != 1.0: print( f"valuation: {COL['green']}{fmt_num(val, 2)} {vs_curr.upper()}{COL['nc']} ({symbol} {fmt_num(amount, 4)})" ) print("-----------------------------------") # ROUTING MATRIX def show_help(): print("rate - multi-asset evaluation utility") print("usage:") print(" rate forex [amt] [target] fiat configurations (alias: fx)") print(" rate metal precious spots contracts (alias: met)") print(" rate energy fuel commodity options (alias: nrg)") print(" rate indices global market indices (alias: ind)") print(" rate crypto [amt] [curr] digital tokens valuations (alias: c)") print(" rate stock [amt] [curr] corporate equities (alias: s)") print(" rate etf [amt] [curr] exchange traded funds (alias: e)") sys.exit(0) def main(): args = sys.argv[1:] if not args or args[0] in ("-h", "--help", "help"): show_help() cmd = args[0].lower() sub_args = args[1:] if cmd in ("forex", "fx", "to", "from"): if cmd == "to": cmd_forex(["1", "NGN", sub_args[0]] if sub_args else ["NGN"]) elif cmd == "from": cmd_forex(["1", sub_args[0], "NGN"] if sub_args else ["USD", "NGN"]) else: cmd_forex(sub_args) elif cmd in ("metal", "met"): cmd_metal(sub_args) elif cmd in ("energy", "nrg", "eng"): cmd_energy(sub_args) elif cmd in ("indices", "ind", "idx"): cmd_indices(sub_args) elif cmd in ("crypto", "c"): cmd_crypto(sub_args) elif cmd in ("stock", "stocks", "s"): cmd_stock(sub_args) elif cmd in ("etf", "e"): cmd_etf(sub_args) else: # direct default fallback to legacy raw strings evaluation cmd_forex(args) if __name__ == "__main__": main()