Karl Hudgell f971708c5e refactor(price): enhance currency handling with FX conversion and cache support
- Add `get_fx_rate()` function with caching to fetch foreign exchange rates from open.er-api.com
- Introduce `get_currency()` helper to map market codes to currency identifiers
- Update price parsing regex to support multiple currency symbols (£, $, €)
- Convert shares to float to accommodate fractional holdings
- Add currency, fx_rate, and total_value_gbp fields to output JSON
- Support fractional share counts (e.g., 3.6185 AAPL shares)
2026-04-20 11:48:15 +01:00

67 lines
1.9 KiB
Python

import re
import json
import csv
import sys
import requests
from bs4 import BeautifulSoup
FX_CACHE = {}
def get_fx_rate(currency):
if currency in FX_CACHE:
return FX_CACHE[currency]
if currency == "GBP":
return 1.0
url = f"https://open.er-api.com/v6/latest/{currency}"
r = requests.get(url, timeout=20)
rate = r.json()["rates"]["GBP"]
FX_CACHE[currency] = rate
return rate
def get_price(market, ticker):
url = f"https://web.freetrade.io/universe/{market}/{ticker}"
response = requests.get(url, headers={
"User-Agent": "Mozilla/5.0"
}, timeout=20)
text = BeautifulSoup(response.text, "html.parser").get_text(" ", strip=True)
m = re.search(r"Latest price\s*:\s*[£$€]([0-9,.]+)", text)
if m:
return float(m.group(1).replace(",", ""))
return None
def get_currency(market):
return {"GB": "GBP", "US": "USD"}.get(market, "GBP")
holdings_file = sys.argv[1] if len(sys.argv) > 1 else "holdings.csv"
results = []
with open(holdings_file) as f:
reader = csv.reader(f)
for row in reader:
ticker, market, shares = row[0].strip(), row[1].strip(), float(row[2].strip())
currency = get_currency(market)
price = get_price(market, ticker)
if price is None:
results.append({
"ticker": ticker,
"market": market,
"currency": currency,
"shares": shares,
"price": None,
"fx_rate": None,
"total_value_gbp": None
})
continue
fx = get_fx_rate(currency)
total_gbp = round(price * shares * fx, 2)
results.append({
"ticker": ticker,
"market": market,
"currency": currency,
"shares": shares,
"price": price,
"fx_rate": fx,
"total_value_gbp": total_gbp
})
print(json.dumps(results, indent=2))