A Python package for tracking and analyzing stock portfolios with multi-currency support and automatic dividend tracking.
Tests, instructions and docstrings written using Claude, I tried to find any incorrect information but some may have slipped through the crack.
- Portfolio Management: Track multiple stock holdings with buy/sell transactions
- Dynamic Price Tracking: Automatically fetch and store historical stock prices using yfinance
- Multi-Currency Support: Handle stocks traded in different currencies with automatic conversion
- Cash Management: Maintain accurate cash balances accounting for buy/sell transactions and dividend payments
- Dividend Tracking: Automatically capture and account for dividend payments
- Historical Analysis: Query portfolio composition and value at any point in time
- Stock Returns Analysis: Calculate individual stock performance accounting for position changes
- Index Comparison: Compare your portfolio returns against benchmark indices
- Comprehensive Logging: Track all operations with detailed logging
- Input Validation: Validate all transaction data before processing
Install the package using pip:
pip install FinTrackFor development:
git clone https://github.com/arofredriksson/FinTrack.git
cd FinTrack
pip install -e ".[dev]"
pytest tests/Create transactions.csv:
Date;Ticker;Type;Amount;Price
2023-01-15;AAPL;Buy;10;150.00
2023-02-20;MSFT;Buy;5;250.00
2023-03-10;AAPL;Sell;5;165.00from FinTrack import FinTrack
from datetime import date
# Create portfolio
portfolio = FinTrack(
initial_cash=150000,
currency="USD",
csv_file="transactions.csv"
)
# Update with latest data
portfolio.update_portfolio()
# Get current holdings
holdings = portfolio.get_current_holdings()
print(f"Holdings: {holdings}")
# Get portfolio value over time
values = portfolio.get_portfolio_value(
date(2023, 1, 1),
date(2023, 12, 31)
)
# Get portfolio summary
summary = portfolio.get_portfolio_summary()
print(f"Total Value: {summary['total_value']:,.2f} {summary['currency']}")Initialize a portfolio tracker.
Parameters:
initial_cash: Starting cash amount (must be non-negative)currency: Base currency code (3-letter code, e.g., 'USD', 'EUR')csv_file: Path to transactions CSV fileuser_id: Optional identifier for multi-user setups (default: 'default')
Raises:
FileNotFoundError: If CSV file doesn't existValidationError: If parameters are invalid
Get list of current stock holdings with company names.
Get portfolio value for each day in date range.
Get cash balance on specific date.
Get comprehensive portfolio summary including:
- Current holdings with prices and values
- Cash balance
- Total portfolio value
New in v1.1.1: Calculate returns for each stock held during the period.
Accounts for position changes (buys/sells) during the period using a Modified Dietz-style calculation. This method properly handles:
- Stocks held throughout the entire period
- Stocks purchased during the period
- Stocks sold during the period
- Partial position changes
Parameters:
from_date: Start dateto_date: End date
Returns:
- Dictionary mapping ticker symbols to returns (as decimals, e.g., 0.062 = 6.2%)
Example:
returns = portfolio.get_stock_returns(
date(2023, 1, 1),
date(2023, 12, 31)
)
for ticker, ret in returns.items():
print(f"{ticker}: {ret:.2%}")New in v1.1.1: Print a formatted table of stock returns.
Parameters:
from_date: Start dateto_date: End datesort_by: How to sort results - "return" (default), "ticker", or "alpha"
Example:
portfolio.print_stock_returns(
date(2023, 1, 1),
date(2023, 12, 31)
)
# Output:
# Stock Returns (2023-01-01 to 2023-12-31)
# ==================================================
# Apple Inc. 12.50%
# Microsoft Corporation 8.23%
# Tesla, Inc. -5.12%
# ==================================================
# Average Return: 5.20%Get daily returns for a benchmark index. Improved in v1.1.1 with more robust data handling and better error recovery.
Returns are calculated as (price - initial_price) / initial_price.
Parameters:
ticker: Yahoo Finance ticker (e.g., '^GSPC' for S&P 500)start_date: Start dateend_date: End date
Returns:
- List of daily returns (as decimals, e.g., 0.02 = 2%)
Raises:
DataFetchError: If index data cannot be fetched
Example:
returns = portfolio.get_index_returns(
'^GSPC',
date(2023, 1, 1),
date(2023, 12, 31)
)
print(f"S&P 500 final return: {returns[-1]:.2%}")Refresh portfolio with latest data from Yahoo Finance.
Data is stored in user's home directory:
~/.fintrack/
├── default/ # Default user
│ └── data/
│ └── portfolio.db # Portfolio database
├── user123/ # Custom user
│ └── data/
│ └── portfolio.db
└── logs/
└── fintrack.log # Activity log
Access paths programmatically:
from FinTrack import Config
data_dir = Config.get_data_dir("user123")
db_path = Config.get_db_path("user123")
logs_dir = Config.get_logs_dir()FinTrack uses Python's standard logging module:
from FinTrack import setup_logger, get_logger
import logging
# Set up with custom level
logger = setup_logger("my_app", level=logging.DEBUG)
# Or get existing logger
logger = get_logger(__name__)
logger.info("Portfolio initialized")
logger.debug("Detailed debug information")
logger.warning("Warning about something")
logger.error("An error occurred")Logs are written to ~/.fintrack/logs/fintrack.log by default.
FinTrack provides specific exception types:
from FinTrack import (
FinTrackError, # Base exception
ValidationError, # Input validation failed
DataFetchError, # Yahoo Finance fetch failed
PriceError, # Price data unavailable
DatabaseError, # Database operation failed
ConfigError # Configuration issue
)
try:
portfolio = FinTrack(150000, "USD", "transactions.csv")
except ValidationError as e:
print(f"Invalid input: {e}")
except FinTrackError as e:
print(f"FinTrack error: {e}")All transaction data is automatically validated:
from FinTrack import TransactionValidator, ValidationError
df = pd.read_csv("transactions.csv", sep=";")
is_valid, errors = TransactionValidator.validate_dataframe(df)
if not is_valid:
for error in errors:
print(f" - {error}")Validation checks:
- ✓ Date format (YYYY-MM-DD)
- ✓ Ticker symbols (non-empty, alphanumeric)
- ✓ Transaction type (Buy or Sell)
- ✓ Amount (positive integer)
- ✓ Price (positive number)
Delimiter: Semicolon (;)
Required columns:
| Column | Type | Description |
|---|---|---|
| Date | YYYY-MM-DD | Transaction date |
| Ticker | String | Stock ticker symbol |
| Type | Buy/Sell | Transaction type |
| Amount | Integer | Number of shares |
| Price | Number | Price per share |
Optional columns:
- Custom price specifications (to override Yahoo Finance data)
- Additional metadata
Example:
Date;Ticker;Type;Amount;Price
2023-01-15;AAPL;Buy;10;150.50
2023-02-20;MSFT;Buy;5;250.75
2023-03-10;AAPL;Sell;5;165.25
2023-04-05;TSLA;Buy;2;800.00FinTrack uses SQLite with three main tables:
- portfolio: Holdings for each date
- cash: Cash balance tracking
- prices: Daily stock prices in base currency
- Prices automatically fetched from Yahoo Finance
- Multi-currency portfolios: prices converted to base currency
- Forward-filling for missing trading days
- Custom prices from CSV supported
Cash balance updated for:
- Stock purchases (deduct)
- Stock sales (add)
- Dividend payments (add)
The get_stock_returns() method uses a Modified Dietz approach to calculate time-weighted returns that account for:
- Initial position value
- Cash flows (buys/sells) during the period
- Timing of transactions
- Final position value
This provides accurate performance metrics even when position sizes change during the analysis period.
Works with any currency pair available on Yahoo Finance:
# Major currencies
portfolio = FinTrack(100000, "USD") # US Dollar
portfolio = FinTrack(100000, "EUR") # Euro
portfolio = FinTrack(100000, "GBP") # British Pound
portfolio = FinTrack(100000, "JPY") # Japanese Yen
portfolio = FinTrack(100000, "SEK") # Swedish Krona
# Emerging markets and others available
portfolio = FinTrack(100000, "INR") # Indian Rupee
portfolio = FinTrack(100000, "BRL") # Brazilian Real- Python >= 3.8
- pandas >= 1.3.0
- yfinance >= 0.2.0
# Run all tests
pytest tests/
# With coverage
pytest tests/ --cov=src/FinTrack --cov-report=html
# Specific test file
pytest tests/test_validation.py
# Specific test
pytest tests/test_validation.py::TestTransactionValidator::test_valid_transaction_row# Format code
black src/
# Lint
flake8 src/
# Type checking
mypy src/- Prices are fetched from Yahoo Finance; verify data quality
- Daily resolution only (intra-day trading not supported)
- Corporate actions (stock splits, mergers) must be manually adjusted
- Past dividend data depends on Yahoo Finance records
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
MIT License - see LICENSE file for details.
This software is provided as-is for educational and informational purposes. Always verify your portfolio calculations independently. The author is not responsible for any financial losses resulting from use of this software.
See CHANGELOG.md for detailed release notes.
For issues, questions, or suggestions:
- GitHub Issues
- Email: arofre903@gmail.com
- v1.1.1 (2026-02-15): Added stock returns analysis methods and improved index returns handling
- v1.1.0 (2026-02-14): Major refactoring with full test suite, proper error handling, logging, and pandas 2.0 compatibility
- v1.0.0 (2026-02-13): Initial release
Built by: Aron Fredriksson
License: MIT
Last Updated: February 2026