Stock Portfolio Analyzer
A Streamlit app that turns your buy and sell history into current holdings, allocation, profit, XIRR and a value-over-time chart. Market prices come from Yahoo Finance via yfinance.
1. Quick start
You need uv. It installs Python 3.12 and the app's dependencies for you.
git clone https://github.com/programmer11/stock-portfolio-analyzer.git
cd stock-portfolio-analyzer
uv sync # create .venv and install dependencies
uv run streamlit run app.py # open http://localhost:8501
In the app, open Input Transactions and click Load sample data to try it with the bundled example.
2. CSV format
One row per trade. Column names are not case-sensitive.
| Column | Meaning | Also accepted as |
|---|---|---|
ticker | Stock symbol, e.g. AAPL. Add the exchange suffix for non-US stocks, e.g. RELIANCE.NS | symbol, stock, scrip |
date | Trade date, e.g. 2024-01-03 | trade date, transaction date |
transaction_type | BUY or SELL (B/S also work) | type, side, action |
quantity | Number of shares, greater than 0 | qty, shares, units |
price | Price per share | rate, unit price |
ticker,date,transaction_type,quantity,price
AAPL,2024-01-03,BUY,10,185.07
MSFT,2024-04-03,BUY,10,420.3
AAPL,2024-07-03,SELL,7,215.8
If any row is invalid, nothing is imported and the app lists the CSV line numbers to fix.
3. Using the app
Tab 1: Input Transactions
- CSV Upload: upload a file and check the preview. Then click Import, ticking "Replace existing transactions" first if you want to start over.
- Manual Upload: add a single trade with the form.
- All transactions: edit or delete rows, then click Save edits. Clear all removes everything.
Transactions are saved to data/transactions.csv on your computer, so they're still there after a restart. That file is never committed to Git.
Tab 2: Current Portfolio
- Summary figures: Current Value, Cost Basis, Profit & Gains (unrealized) and Realized Profit.
- Portfolio Allocation: a pie chart of market value by stock.
- Stock Price Breakdown: ticker symbol, current quantity, average cost basis, current price, profit and gains (amount and %), realized profit, total cost, market value, allocation and price source.
Tab 3: Historical Performance
- Total Lifetime Investment, Total Sales, Lifetime Proceeds, Current Portfolio Value, Total Return and XIRR.
- Portfolio Value Over Time: daily market value of your holdings, compared with net cash invested.
4. How the numbers are calculated
Trades are replayed in date order using the average-cost method.
| Metric | Formula |
|---|---|
| Average cost basis | Total cost of shares held ÷ shares held. A sale removes cost at the average price. |
| Profit & Gains (unrealized) | Shares held × current price − cost of shares held |
| Realized profit | (Sale price − average cost) × shares sold, added up over every sale |
| Total Lifetime Investment | Sum of quantity × price over all BUYs |
| Total Sales | Sum of quantity × price over all SELLs |
| Lifetime Proceeds | Total Sales + Current Portfolio Value |
| Total Return | Lifetime Proceeds − Total Lifetime Investment (equal to realized + unrealized profit) |
| XIRR | Annualised rate that makes the net present value of all dated cash flows zero. BUYs are outflows; SELLs and today's portfolio value are inflows. |
| Net invested (chart) | Running total of BUY cash − SELL cash |
A sale larger than the shares you hold is reduced to the shares held.
5. Prices
- Current prices are the latest daily close from Yahoo Finance, cached for 15 minutes. Price history for the chart is cached for 60 minutes.
- If a stock can't be priced, the app uses its last transaction price, and the table shows Last trade as the source.
- In the sidebar you can turn off live prices for fully offline use, or click Refresh prices to fetch them again.
6. Project structure
app.py the three tabs (screen layout only)
portfolio/
loader.py read and check the CSV, save/load transactions
holdings.py replay trades -> positions, average cost, realized profit
prices.py yfinance calls, cached
performance.py lifetime metrics, XIRR, daily value series
tests/ tests for the math, no internet needed
docs/ this documentation (GitHub Pages)
See the architecture diagram for how data flows between these modules.
7. Testing
uv run pytest
The tests check the calculations against figures worked out by hand from sample_data.csv. They need no internet. For example, the sample's holdings come out as 10 AAPL at 182.39, 9 MSFT at 415.495 and 4 TSLA at 196.865.
8. Troubleshooting
uv: command not found: install uv, or add its install folder to yourPATHand open a new terminal.- A price shows "Last trade": check the ticker. Non-US stocks need an exchange suffix such as
.NS,.BOor.L. - Upload rejected: fix the CSV lines listed in the error. Dates must be valid, the type must be BUY or SELL, and quantity and price must be positive numbers.
- Port 8501 in use: another copy of the app is running. Streamlit picks the next free port (8502, and so on).