https://github.com/flash286/portfolio-tracker
https://github.com/flash286/portfolio-tracker
Last synced: 3 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/flash286/portfolio-tracker
- Owner: flash286
- License: mit
- Created: 2026-02-24T19:34:12.000Z (4 months ago)
- Default Branch: master
- Last Pushed: 2026-02-25T13:38:54.000Z (4 months ago)
- Last Synced: 2026-02-25T14:59:58.451Z (4 months ago)
- Language: Python
- Size: 669 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Agents: AGENTS.md
Awesome Lists containing this project
README
# Portfolio Tracker
[](https://github.com/flash286/portfolio-tracker/actions/workflows/ci.yml)
[](https://www.python.org/)
[](LICENSE)
[](https://buymeacoffee.com/flash286)
Track your ETF portfolio with full **German tax compliance** — Abgeltungssteuer,
Teilfreistellung, Vorabpauschale, FIFO lots — all from the command line.
Built for European ETF investors living in Germany.
> **Designed for:** DE tax residents · EUR-denominated ETF portfolios · buy-and-hold investors
>
> **Not designed for:** US investors, multi-currency portfolios, real-time trading
---
Web dashboard — allocation, holdings, tax summary
CLI — pt holdings list and pt stats summary
---
## Features
- **Portfolio & holdings** — multiple portfolios, buys/sells/dividends, FIFO lots
- **German tax engine** — Abgeltungssteuer (25%) + Soli (5.5%), Teilfreistellung, Freistellungsauftrag
- **Vorabpauschale** — annual prepayment tax for accumulating ETFs (§18 InvStG)
- **Rebalancing** — target allocations by ISIN/type, deviation check, trade suggestions
- **Price fetching** — automatic yfinance lookups for European ETFs (`.DE` `.L` `.AS` …)
- **Cash tracking** — full ledger: top-ups, buys, sells, dividends, fees
- **Performance history** — Time-Weighted Return over 1m/3m/6m/1y/2y/all, auto-fetches historical prices
- **Revolut import** — idempotent one-command CSV import
- **Web dashboard** — tabbed SPA: allocation charts, sortable holdings table, P&L bars, FSA progress, tax summary, rebalancing, AI Analysis
- **AI Analysis** — one-click portfolio review by Claude / GPT / Gemini in the dashboard
- **AI import** — Claude Code skill imports any broker CSV without writing code
---
## Setup
**Requirements:** Python 3.10+ and [uv](https://docs.astral.sh/uv/)
```bash
git clone https://github.com/flash286/portfolio-tracker.git
cd portfolio-tracker
make setup # installs Python deps + dashboard (Node.js optional)
uv run pt --help
```
Don't have uv yet?
```bash
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
```
Prefer plain pip?
```bash
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
pt --help
```
### First-run configuration
```bash
pt setup run
```
The wizard configures your tax profile (country, FSA amount, Zusammenveranlagung,
church tax, exchange suffix) and optionally sets up an AI provider for dashboard
analysis. Settings are saved to `config.json` (gitignored).
### Make targets
```bash
make setup # install all deps (Python + dashboard)
make check # lint + tests
make dev ID=1 # Vite HMR dev server
make build # build dashboard to dist/
make help # show all targets
```
### Shell completion
Enable tab completion for all `pt` commands and subcommands:
```bash
# zsh (add to ~/.zshrc)
eval "$(_PT_COMPLETE=source_zsh pt)"
# bash (add to ~/.bashrc)
eval "$(_PT_COMPLETE=source_bash pt)"
```
Restart your shell, then `pt ` and `pt stats ` will autocomplete.
---
## Quick start
```bash
pt portfolio create "My Portfolio" # → ID 1
pt holdings add 1 IE00BK5BQT80 etf \
--name "Vanguard FTSE All-World" \
--ticker VWCE --tfs-rate 0.3
pt tx buy 1 10 115.42 --date 2025-01-15
pt prices fetch 1
pt stats summary 1
```
```
Portfolio: My Portfolio
Total Cost Basis €1,154.20
Holdings Value €1,282.78
Cash Balance €0.00
Portfolio Value €1,282.78
Unrealized P&L +€128.58 (+11.1%)
```
---
## Revolut import
### Export from Revolut
1. **Revolut app** → Investments → portfolio → **···** → **Export transactions**
2. Download:
- **Transactions CSV** — buys, dividends, cash flows
- **P&L CSV** — realized gains with ISIN details (recommended)
### Run the import
```bash
pt import revolut data/revolut_transactions.csv --pnl data/revolut_pnl.csv
```
```
╭──────────── Import result ─────────────╮
│ Portfolio Revolut (ID 1) │
│ Holdings created 8 │
│ Buys imported 56 │
│ Dividends imported 42 │
│ Cash rows imported 146 │
╰────────────────────────────────────────╯
Done. Run pt holdings list to verify.
```
The import is **idempotent** — re-running on the same file skips all duplicates (SHA-256 deduplication). Use `--dry-run` to validate without writing.
### Options
| Flag | Description |
|------|-------------|
| `--pnl ` | P&L CSV for ISIN enrichment (recommended) |
| `--portfolio-name / -n` | Portfolio name to create or reuse |
| `--portfolio-id / -p` | Use an existing portfolio by ID |
| `--dry-run / -d` | Validate without writing to DB |
| `--no-interactive` | Skip prompts for unknown tickers (CI/script mode) |
---
## Manual portfolio setup
```bash
pt portfolio create "Trade Republic"
pt holdings add 1 IE00BK5BQT80 etf --name "VWCE" --ticker VWCE --tfs-rate 0.3
pt holdings add 1 IE00BMC38736 etf --name "VVSM" --ticker VVSM --tfs-rate 0.3
pt tx buy 1 100 115.42 --date 2025-01-15
pt cash add 1 1000 --type top_up --desc "January deposit"
```
---
## Prices
```bash
pt prices fetch 1 # fetch current prices for all holdings
pt prices history
```
yfinance is used for lookups. European ETFs require exchange suffixes — the tracker handles this automatically (tries `.DE` → `.L` → `.AS` → `.PA` → `.MI`).
Add overrides in `src/portfolio_tracker/external/price_fetcher.py`:
```python
TICKER_OVERRIDES = {
"VWCE": "VWCE.DE",
...
}
```
---
## Statistics
```bash
pt stats summary 1 # P&L, allocation, estimated tax
pt stats allocation 1 # breakdown by asset type
pt stats performance 1 # value history + Time-Weighted Return
pt stats performance 1 --period 2y # longer range: 1m|3m|6m|1y|2y|all
```
The tax estimate in `summary` applies Teilfreistellung → Freistellungsauftrag → Abgeltungssteuer + Soli. This is a hypothetical estimate (as if you sold everything today).
`pt stats performance` automatically backfills missing history from yfinance the first time you run it for a given period — no manual setup needed.
---
## Performance snapshots
```bash
pt snapshot take 1 # record today's portfolio value
pt snapshot backfill 1 # fill history from yfinance (all time)
pt snapshot backfill 1 --since 2024-01-01 --interval 1d # daily, from a date
```
Snapshots power the performance chart in `pt stats performance` and the dashboard. `pt dashboard open` records a snapshot automatically on every open.
---
## Tax commands
### Realized gains
```bash
pt tax realized 1 --year 2025
```
Shows all sells with FIFO-matched cost basis, Teilfreistellung, FSA deduction, and estimated Abgeltungssteuer + Soli.
### FIFO tax lots
```bash
pt tax lots
```
Shows individual cost lots — acquired date, quantity, cost/unit, remaining.
### Vorabpauschale
```bash
pt tax vorabpauschale 1 --year 2024
```
Calculates the annual prepayment tax for accumulating ETFs (§ 18 InvStG):
```
Basisertrag/share = Kurs(Jan 1) × Basiszins × 0.7
Vorabpauschale = min(Basisertrag, Fondszuwachs) × Anteile
```
**Basiszins** (BMF-published):
| Year | Basiszins |
|------|-----------|
| 2023 | 2.55% |
| 2024 | 2.29% |
| 2025 | 2.53% |
Run once in January after the previous year's prices are final. Results are cached and shown in the dashboard.
---
## Rebalancing
```bash
pt rebalance target 1 # set target allocations interactively
pt rebalance check 1 # show current deviation from targets
pt rebalance suggest 1 # suggest trades with cash impact
pt rebalance execute 1 # execute trades and record transactions
```
---
## Web dashboard
```bash
pt dashboard open 1
pt dashboard open 1 --output /tmp/portfolio.html # save to file
```
Opens a local HTTP server at `http://127.0.0.1:` and launches the browser. Press **Ctrl+C** in the terminal to stop. No CDN, no external requests (except the optional AI Analysis call).
### Development mode
```bash
pt dashboard dev 1 # legacy live-reload (watches dashboard.html)
pt dashboard dev 1 --vite # Preact + Vite HMR (requires Node.js)
pt dashboard dev 1 --data-refresh # re-collect portfolio data on each reload
```
The `--vite` mode starts a Vite dev server with hot module replacement and a Python API backend for live data. Requires Node.js; dependencies are installed automatically on first run.
The dashboard is organised into **5 tabs**:
| Tab | Content |
|-----|---------|
| **Overview** | KPI cards · allocation donut charts · P&L bar chart per holding |
| **Holdings** | Sortable, filterable table (click any column header to sort) |
| **Tax** | Freistellungsauftrag progress bar · tax summary · realized gains |
| **Rebalancing** | Target vs. actual deviation bars |
| **AI Analysis** | One-click deep analysis by your configured AI provider |
Other features:
- **Copy as Markdown** — exports a full portfolio snapshot for AI review
- Auto-records a snapshot on every open (used by the performance chart)
---
## AI Analysis
The dashboard includes a built-in **AI Analysis** panel powered by your choice of LLM. Click **Generate Analysis** to get a structured financial advisor review of your portfolio.

### What the AI produces
| Section | Content |
|---------|---------|
| **Overall** | Rating (strong / good / fair / weak) + 2–3 sentence summary |
| **Performance Highlights** | Top winners and underperformers with commentary |
| **Risk & Diversification** | Concentration risk, geographic exposure, ETF overlap warnings |
| **Tax Optimization** | Freistellungsauftrag usage, Vorabpauschale notes, actions needed |
| **Recommendations** | Numbered, prioritised action items |
### Supported providers
| Provider | Default model |
|----------|--------------|
| **Anthropic** | `claude-sonnet-4-6` |
| **OpenAI** | `o3` |
| **Google Gemini** | `gemini-2.5-pro` |
### Setup
```bash
pt setup run
# → answer the "AI Analysis" step at the end
# → choose provider, paste API key, optionally override model
```
Or edit `config.json` directly:
```json
{
"ai_provider": "anthropic",
"ai_api_key": "sk-ant-...",
"ai_model": ""
}
```
Then reopen the dashboard:
```bash
pt dashboard open 1
```
> The API key is stored only in `config.json` (gitignored) and embedded in the locally-generated
> temp HTML file. It is never sent anywhere except the chosen provider's API endpoint.
---
## Cash management
```bash
pt cash balance 1 # current balance
pt cash history 1 # full ledger
pt cash add 1 500 --type top_up
```
Types: `top_up`, `withdrawal`, `buy`, `sell`, `dividend`, `fee`. Buy/sell transactions automatically create matching cash entries.
---
## AI import (Claude Code skill)
This project includes [Claude Code Skills](https://docs.anthropic.com/en/docs/claude-code/skills) — reusable AI workflows that drive the CLI. No separate server or API key needed beyond your existing Claude Code session.
### Skills
| Command | Description |
|---------|-------------|
| `/portfolio import ` | Import from any broker CSV |
| `/portfolio summary` | Full portfolio overview + tax snapshot |
| `/portfolio` | Show menu |
The skill lives in `.claude/skills/portfolio/SKILL.md` and is committed to the repo —
available to anyone who clones it.
### Universal broker import
```
/portfolio import data/trading212_export.csv
```
Claude reads the columns, maps them to `pt` commands, and imports:
1. Shows a dry-run summary (counts by type, date range, tickers)
2. Asks for confirmation before writing anything
3. Creates missing holdings and records all transactions chronologically
> Unlike `pt import revolut`, the AI import is **not idempotent**. Running it twice creates
> duplicates — the skill always shows a preview and asks before proceeding.
### Portfolio overview
```
/portfolio summary
```
Runs `pt stats`, `pt holdings list`, `pt rebalance check`, and optional tax commands,
then gives 1–3 actionable recommendations based on the data.
---
## CLI reference
```
pt portfolio create | list | show | delete
pt holdings add | list | remove
pt tx buy | sell | list
pt prices fetch | history
pt stats summary | allocation | performance
pt snapshot take | backfill
pt rebalance target | check | suggest | execute
pt cash balance | history | add
pt tax realized | lots | vorabpauschale
pt import revolut
pt dashboard open | dev
pt setup run
```
---
## Stack
| | |
|---|---|
| Language | Python 3.10+ |
| Package manager | [uv](https://docs.astral.sh/uv/) |
| CLI | [Typer](https://typer.tiangolo.com) + [Rich](https://github.com/Textualize/rich) |
| Database | SQLite (`portfolio.db`) |
| Prices | [yfinance](https://github.com/ranaroussi/yfinance) |
| Dashboard | [Preact](https://preactjs.com) + [Vite](https://vite.dev) — builds to a single HTML file |
| AI Providers | Anthropic Claude, OpenAI, Google Gemini (optional) |
---
## Privacy
All data stays **local**:
- `portfolio.db` — SQLite on your machine
- `config.json` — local config, gitignored
- No accounts, no cloud sync, no telemetry
- AI Analysis calls your configured provider's API directly with your own key
---
## Disclaimer
This tool provides estimates for informational purposes only.
It is **not financial advice** and **not a substitute for professional tax counsel**.
Always verify Vorabpauschale and Abgeltungssteuer calculations with a Steuerberater
or your Finanzamt before filing.
---
## License
[MIT](LICENSE)