perf(price): parallelize price fetching with thread pool for faster processing
- Use ThreadPoolExecutor to fetch stock prices concurrently across multiple holdings - Pre-fetch FX rates for unique non-GBP currencies before parallel price fetching - Group price fetching and FX calculation into separate processing phases - Reduce I/O wait time by executing network requests in parallel instead of sequentially
This commit is contained in:
parent
f971708c5e
commit
ddec1e3683
60
price.py
60
price.py
@ -4,6 +4,7 @@ import csv
|
|||||||
import sys
|
import sys
|
||||||
import requests
|
import requests
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
|
|
||||||
FX_CACHE = {}
|
FX_CACHE = {}
|
||||||
|
|
||||||
@ -18,6 +19,9 @@ def get_fx_rate(currency):
|
|||||||
FX_CACHE[currency] = rate
|
FX_CACHE[currency] = rate
|
||||||
return rate
|
return rate
|
||||||
|
|
||||||
|
def get_currency(market):
|
||||||
|
return {"GB": "GBP", "US": "USD"}.get(market, "GBP")
|
||||||
|
|
||||||
def get_price(market, ticker):
|
def get_price(market, ticker):
|
||||||
url = f"https://web.freetrade.io/universe/{market}/{ticker}"
|
url = f"https://web.freetrade.io/universe/{market}/{ticker}"
|
||||||
response = requests.get(url, headers={
|
response = requests.get(url, headers={
|
||||||
@ -29,39 +33,37 @@ def get_price(market, ticker):
|
|||||||
return float(m.group(1).replace(",", ""))
|
return float(m.group(1).replace(",", ""))
|
||||||
return None
|
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"
|
holdings_file = sys.argv[1] if len(sys.argv) > 1 else "holdings.csv"
|
||||||
|
|
||||||
results = []
|
holdings = []
|
||||||
with open(holdings_file) as f:
|
with open(holdings_file) as f:
|
||||||
reader = csv.reader(f)
|
reader = csv.reader(f)
|
||||||
for row in reader:
|
for row in reader:
|
||||||
ticker, market, shares = row[0].strip(), row[1].strip(), float(row[2].strip())
|
ticker, market = row[0].strip(), row[1].strip()
|
||||||
|
shares = float(row[2].strip())
|
||||||
currency = get_currency(market)
|
currency = get_currency(market)
|
||||||
price = get_price(market, ticker)
|
holdings.append({"ticker": ticker, "market": market, "shares": shares, "currency": currency})
|
||||||
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))
|
unique_currencies = {h["currency"] for h in holdings if h["currency"] != "GBP"}
|
||||||
|
for currency in unique_currencies:
|
||||||
|
get_fx_rate(currency)
|
||||||
|
|
||||||
|
with ThreadPoolExecutor(max_workers=10) as pool:
|
||||||
|
future_to_idx = {
|
||||||
|
pool.submit(get_price, h["market"], h["ticker"]): i
|
||||||
|
for i, h in enumerate(holdings)
|
||||||
|
}
|
||||||
|
for future in as_completed(future_to_idx):
|
||||||
|
idx = future_to_idx[future]
|
||||||
|
holdings[idx]["price"] = future.result()
|
||||||
|
|
||||||
|
for h in holdings:
|
||||||
|
if h["price"] is not None:
|
||||||
|
fx = FX_CACHE.get(h["currency"], 1.0)
|
||||||
|
h["fx_rate"] = fx
|
||||||
|
h["total_value_gbp"] = round(h["price"] * h["shares"] * fx, 2)
|
||||||
|
else:
|
||||||
|
h["fx_rate"] = None
|
||||||
|
h["total_value_gbp"] = None
|
||||||
|
|
||||||
|
print(json.dumps(holdings, indent=2))
|
||||||
Loading…
x
Reference in New Issue
Block a user