Demo
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Background
A self-hosted dividend portfolio tracker built with Flask + yfinance + Chart.js. Designed for long-term investors who want full visibility into their dividend income, portfolio health, and progress toward financial independence.
No subscriptions. No cloud. Runs entirely on your machine.
Featured On Product Hunt
Features
- Overview — Live portfolio snapshot: total value, annual dividend income, yield on cost, forward yield, beta vs S&P 500, and progress toward your passive income goal
- Holdings — Full breakdown of each position: current price, gain/loss, dividend yield, annual income per holding, yield on cost, payout ratio, and dividend safety score
- Diversification — Sector and industry allocation charts so you can spot concentration risk at a glance
- Income — Annual and monthly dividend income breakdown by holding, with historical trends
- Calendar — 12-month forward projection of expected dividend payments, month by month
- Goals — Set your annual passive income target and track milestone progress
- Performance — Portfolio returns vs benchmark over time
- Dividend Predictor — 12-quarter forward dividend projection using historical CAGR, with adjustable growth rate assumptions
- Stock Deep-Dive — Per-stock financials: revenue, net income, EPS, FCF, payout history, profit margins, dividend growth history, and forward analyst estimates — sourced from both yfinance and Alpha Vantage
Mac
The setup is identical to Windows with two differences:
- To open a terminal inside the app folder: right-click the folder → New Terminal at Folder (or open Terminal from Spotlight and drag the folder in)
- If
pythonis not recognized, trypython3instead — macOS sometimes ships with both
Everything else (pip install, running the app, the browser address) is the same.
Linux
Option A — Run directly:
git clone https://github.com/GavrielP1/SALARY-2045.git
cd SALARY-2045
pip install flask flask-cors yfinance requests python-dotenv
cp holdings.example.json holdings.json
python3 app.py
Then open http://localhost:5000
If
pipis not found, trypip3instead.
Option B — Docker:
git clone https://github.com/GavrielP1/SALARY-2045.git
cd SALARY-2045
cp holdings.example.json holdings.json
docker compose up --build
Then open http://localhost:5000
Docker support is new and not yet tested on Linux .
Docker
A Dockerfile and docker-compose.yml are included in the repo.
Before running, make sure you have created your local files:
cp holdings.example.json holdings.json
cp goal.example.json goal.json # optional
cp .env.example .env # optional, for Alpha Vantage key
Run:
docker compose up --build
Then open http://localhost:5000
Volume mounts are configured for holdings.json, goal.json, .env, and financials_cache/ — your data is stored on the host and persists across container restarts.
Setup
1. Clone the repository
git clone https://github.com/GavrielP1/SALARY-2045.git
cd salary2045
2. Install dependencies
pip install flask flask-cors yfinance requests python-dotenv
3. Configure your API key
Get a free API key from alphavantage.co, then:
cp .env.example .env
# Edit .env and replace the placeholder with your actual key
4. Add your holdings
cp holdings.example.json holdings.json
# Edit holdings.json with your actual positions
Each entry follows this format:
{"ticker": "SCHD", "shares": 10, "avgCost": 75.00}
5. (Optional) Set your income goal
cp goal.example.json goal.json
# Edit goal.json to set your annual income target and milestones
6. Run the app
python app.py
Open http://localhost:5000 in your browser.
Files not committed to Git
The following files are listed in .gitignore and must be created locally:
| File | Purpose |
|---|---|
.env |
Your Alpha Vantage API key |
holdings.json |
Your personal portfolio positions |
goal.json |
Your income goal and milestones |
portfolio.json |
Auto-generated cache (do not edit) |
financials_cache/ |
Auto-generated per-ticker cache |
Notes
- Alpha Vantage free tier: 25 requests/day, 5 requests/minute. The app sleeps between calls automatically.
financials_cache/stores fetched data for 90 days to avoid redundant API calls.- The CASH holding type is supported: add
{"ticker": "CASH", "shares": 1, "avgCost": 1000}to represent uninvested cash.











Comments