feat: add Freetrade portfolio tracker with price fetching and holdings CSV support
This commit is contained in:
commit
38ecbd57e0
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
venv/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
holdings.csv
|
||||
52
README.md
Normal file
52
README.md
Normal file
@ -0,0 +1,52 @@
|
||||
# Freetrade Portfolio Tracker
|
||||
|
||||
Fetches live stock prices from Freetrade and calculates portfolio holdings values.
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
python -m venv venv
|
||||
venv\Scripts\activate # Windows
|
||||
source venv/bin/activate # Linux/Mac
|
||||
|
||||
pip install requests beautifulsoup4
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Copy the sample file and add your own positions:
|
||||
|
||||
```bash
|
||||
cp holdings.sample.csv holdings.csv
|
||||
```
|
||||
|
||||
Format (`ticker,market,shares`):
|
||||
|
||||
```csv
|
||||
GGP,GB,100
|
||||
PAF,GB,200
|
||||
SVM,US,50
|
||||
```
|
||||
|
||||
Markets: `GB` for UK stocks, `US` for US-listed stocks.
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
python price.py # uses holdings.csv by default
|
||||
python price.py my_portfolio.csv # custom file
|
||||
```
|
||||
|
||||
Output (JSON):
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"ticker": "GGP",
|
||||
"market": "GB",
|
||||
"shares": 100,
|
||||
"price": 7.67,
|
||||
"total_value": 767.0
|
||||
}
|
||||
]
|
||||
```
|
||||
3
holdings.sample.csv
Normal file
3
holdings.sample.csv
Normal file
@ -0,0 +1,3 @@
|
||||
GGP,GB,100
|
||||
PAF,GB,200
|
||||
SVM,US,50
|
||||
|
35
price.py
Normal file
35
price.py
Normal file
@ -0,0 +1,35 @@
|
||||
import re
|
||||
import json
|
||||
import csv
|
||||
import sys
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
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
|
||||
|
||||
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(), int(row[2].strip())
|
||||
price = get_price(market, ticker)
|
||||
results.append({
|
||||
"ticker": ticker,
|
||||
"market": market,
|
||||
"shares": shares,
|
||||
"price": price,
|
||||
"total_value": round(price * shares, 2) if price is not None else None
|
||||
})
|
||||
|
||||
print(json.dumps(results, indent=2))
|
||||
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
requests
|
||||
beautifulsoup4
|
||||
Loading…
x
Reference in New Issue
Block a user