Skip to main content

A robust portfolio tracker with multi-currency support

Project description

FinTrack - Advanced Portfolio Tracker

Tests Python License

A Python package for tracking and analyzing stock portfolios with multi-currency support and automatic dividend tracking.

Important note

Tests, instructions and docstrings written using Claude, I tried to find any incorrect information but some may have slipped through the crack.

Features

  • 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

Installation

Install the package using pip:

pip install FinTrack

For development:

git clone https://github.com/arofredriksson/FinTrack.git
cd FinTrack
pip install -e ".[dev]"
pytest tests/

Quick Start

1. Create a Transaction CSV

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.00

2. Initialize and Query

from 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']}")

Documentation

Core Methods

FinTrack.__init__(initial_cash, currency, csv_file, user_id=None)

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 file
  • user_id: Optional identifier for multi-user setups (default: 'default')

Raises:

  • FileNotFoundError: If CSV file doesn't exist
  • ValidationError: If parameters are invalid

get_current_holdings() -> List[str]

Get list of current stock holdings with company names.

get_portfolio_value(from_date, to_date) -> Dict[date, float]

Get portfolio value for each day in date range.

get_portfolio_cash(date) -> Optional[float]

Get cash balance on specific date.

get_portfolio_summary() -> Dict

Get comprehensive portfolio summary including:

  • Current holdings with prices and values
  • Cash balance
  • Total portfolio value

get_stock_returns(from_date, to_date) -> Dict[str, float]

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 date
  • to_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%}")

print_stock_returns(from_date, to_date, sort_by='return')

New in v1.1.1: Print a formatted table of stock returns.

Parameters:

  • from_date: Start date
  • to_date: End date
  • sort_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_index_returns(ticker, start_date, end_date) -> List[float]

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 date
  • end_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%}")

update_portfolio()

Refresh portfolio with latest data from Yahoo Finance.

Configuration

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()

Logging

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.

Error Handling

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}")

Input Validation

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)

CSV Format

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.00

How It Works

Database Structure

FinTrack uses SQLite with three main tables:

  1. portfolio: Holdings for each date
  2. cash: Cash balance tracking
  3. prices: Daily stock prices in base currency

Price Management

  • 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 Flow Tracking

Cash balance updated for:

  • Stock purchases (deduct)
  • Stock sales (add)
  • Dividend payments (add)

Stock Returns Calculation

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.

Supported Currencies

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

Requirements

  • Python >= 3.8
  • pandas >= 1.3.0
  • yfinance >= 0.2.0

Development

Running Tests

# 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

Code Quality

# Format code
black src/

# Lint
flake8 src/

# Type checking
mypy src/

Limitations

  • 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

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. Submit a pull request

License

MIT License - see LICENSE file for details.

Disclaimer

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.

Changelog

See CHANGELOG.md for detailed release notes.

Support

For issues, questions, or suggestions:

Version History

  • 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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fintrack-1.1.1.tar.gz (36.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fintrack-1.1.1-py3-none-any.whl (23.8 kB view details)

Uploaded Python 3

File details

Details for the file fintrack-1.1.1.tar.gz.

File metadata

  • Download URL: fintrack-1.1.1.tar.gz
  • Upload date:
  • Size: 36.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for fintrack-1.1.1.tar.gz
Algorithm Hash digest
SHA256 0d67e751dd8555016cf90358426789749cf00c150b7f4bba16962fc38ffad650
MD5 ec906fb9d819a9e587b620b335141b63
BLAKE2b-256 ad591bb89ee6d89fac980c15ba8d29d63412f2154a08f870a35cf29927cf2c8a

See more details on using hashes here.

File details

Details for the file fintrack-1.1.1-py3-none-any.whl.

File metadata

  • Download URL: fintrack-1.1.1-py3-none-any.whl
  • Upload date:
  • Size: 23.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for fintrack-1.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 04a4692cd0f28794a9dc4cd5237907fbbb5ff0b39382cc8a0727e5a71ce8e7d3
MD5 2f4fb3cc051560cdf754fc05e09d8f89
BLAKE2b-256 d2b3bff79eabd8132ddde9807d784913fa75c55d8c75c59e74219eb7c6218b8b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page