Skip to main content

Tool to keep balance a portfolio of securities while investing.

Project description

foliotrack Logo

foliotrack is a Python package to manage, optimize and rebalance securities, including Exchange-Traded Funds (ETFs). Given a set of securities and their target allocation weights, the packages methods compute the optimal investment adjustments required to align the portfolio with the desired strategy.

Key Features

  • Mathematical Optimization: Uses Mixed-Integer Quadratic Programming (MIQP) to determine the most efficient asset allocation while respecting constraints (e.g., lot sizes, transaction costs).
  • Solver Integration: Leverages CVXPY for convex optimization modeling and PySCIPOpt as the underlying solver.
  • Real-Time Data Fetching:
    • Security prices via yfinance (Yahoo Finance API).
    • Currency conversion via ecbdata (European Central bank data) for multi-currency portfolios.

Use Case

Ideal for investors, financial advisors, and algorithmic traders seeking to:

  • Automated Rebalancing – Maintains target asset allocations with minimal manual intervention, ensuring alignment with investment strategies.
  • Multi-Currency Support – Dynamically adjusts for exchange rate fluctuations, enabling accurate valuation and rebalancing of global portfolios.

Project Structure

  • main.py: Example usage and entry point.
  • foliotrack/Currency.py: Defines the Currency class to get currencies informations like symbol and exchange rate.
  • foliotrack/Security.py: Defines the Security class for representing individual securities.
  • foliotrack/Portfolio.py: Defines the Portfolio class.
  • foliotrack/Equilibrate.py: Defines the Equilibrate class which contains the portfolio optimization algorithm.

Installation

Clone the repository from Github

git clone git@github.com:PhDFlo/foliotrack.git

In the foliotrack folder create the python environment using uv:

uv sync
source .venv/bin/activate

Usage Example

import logging
from foliotrack.Security import Security
from foliotrack.Portfolio import Portfolio
from foliotrack.Equilibrate import Equilibrate

logging.basicConfig(level=logging.INFO)

def main():
    # Create security instances
    security1 = Security(
        name="Amundi MSCI World UCITS Security",
        ticker="AMDW",
        currency="EUR",
        price_in_security_currency=500.0,
        yearly_charge=0.2,
        target_share=0.5,
        number_held=20.0,
    )
    security2 = Security(
        name="Vanguard S&P 500 UCITS Security",
        ticker="VUSA.AS",
        currency="USD",
        price_in_security_currency=300.0,
        yearly_charge=0.1,
        target_share=0.2,
        number_held=1.0,
    )
    security3 = Security(
        name="iShares Core MSCI Emerging Markets IMI UCITS Security",
        ticker="EIMI.L",
        currency="EUR",
        price_in_security_currency=200.0,
        yearly_charge=0.25,
        target_share=0.3,
        number_held=3.0,
    )

    # Create a Portfolio instance
    portfolio = Portfolio()
    portfolio.add_security(security1)
    portfolio.add_security(security2)
    portfolio.add_security(security3)

    portfolio.to_json("Portfolios/investment_example.json")

    portfolio.update_security_prices()  # Update prices from yfinance
    portfolio.compute_actual_shares()

    # Solve for equilibrium
    Equilibrate.solve_equilibrium(
        portfolio.securities, investment_amount=1000.0, min_percent_to_invest=0.99
    )

    # Log portfolio info
    info = portfolio.get_portfolio_info()
    logging.info("Portfolio info:")
    for security_info in info:
        logging.info(f"Security:")
        for k, v in security_info.items():
            logging.info(f"  {k}: {v}")

if __name__ == "__main__":
    main()

Which produces the following output:

INFO:root:Security 'Amundi MSCI World UCITS Security' added to portfolio with share 0.5 and number held 20.0.
INFO:root:Security 'NVIDIA Corporation' added to portfolio with share 0.2 and number held 1.0.
INFO:root:Security 'iShares Core MSCI Emerging Markets IMI UCITS Security' added to portfolio with share 0.3 and number held 3.0.
INFO:root:Portfolio shares sum equal to 1. Portfolio is complete.
INFO:root:Portfolio saved to Portfolios/investment_example.json
INFO:root:Portfolio shares sum equal to 1. Portfolio is complete.
INFO:root:Optimisation status: optimal
INFO:root:Number of each Security to buy:
INFO:root:  Amundi MSCI World UCITS Security: 1 units
INFO:root:  NVIDIA Corporation: 1 units
INFO:root:  iShares Core MSCI Emerging Markets IMI UCITS Security: 7 units
INFO:root:Amount to spend and final share of each Security:
INFO:root:  Amundi MSCI World UCITS Security: 45.61€, Final share = 0.5678
INFO:root:  NVIDIA Corporation: 151.75€, Final share = 0.1799
INFO:root:  iShares Core MSCI Emerging Markets IMI UCITS Security: 297.99€, Final share = 0.2523
INFO:root:Total amount to invest: 495.35€
INFO:root:Portfolio info:
INFO:root:Security:
INFO:root:  name: Amundi MSCI World UCITS Security
INFO:root:  ticker: AMDW
INFO:root:  currency: EUR
INFO:root:  symbol: €
INFO:root:  exchange_rate: 1.0
INFO:root:  price_in_security_currency: 45.61
INFO:root:  price_in_portfolio_currency: 45.61
INFO:root:  yearly_charge: 0.2
INFO:root:  number_held: 20.0
INFO:root:  number_to_buy: 1
INFO:root:  amount_to_invest: 45.61
INFO:root:  amount_invested: 912.2
INFO:root:  target_share: 0.5
INFO:root:  actual_share: 0.77
INFO:root:  final_share: 0.5678
INFO:root:Security:
INFO:root:  name: NVIDIA Corporation
INFO:root:  ticker: NVDA
INFO:root:  currency: USD
INFO:root:  symbol: $
INFO:root:  exchange_rate: 0.8533879501621437
INFO:root:  price_in_security_currency: 177.82
INFO:root:  price_in_portfolio_currency: 151.7494452978324
INFO:root:  yearly_charge: 0.1
INFO:root:  number_held: 1.0
INFO:root:  number_to_buy: 1
INFO:root:  amount_to_invest: 151.75
INFO:root:  amount_invested: 151.7494452978324
INFO:root:  target_share: 0.2
INFO:root:  actual_share: 0.13
INFO:root:  final_share: 0.1799
INFO:root:Security:
INFO:root:  name: iShares Core MSCI Emerging Markets IMI UCITS Security
INFO:root:  ticker: EIMI.L
INFO:root:  currency: EUR
INFO:root:  symbol: €
INFO:root:  exchange_rate: 1.0
INFO:root:  price_in_security_currency: 42.57
INFO:root:  price_in_portfolio_currency: 42.57
INFO:root:  yearly_charge: 0.25
INFO:root:  number_held: 3.0
INFO:root:  number_to_buy: 7
INFO:root:  amount_to_invest: 297.99
INFO:root:  amount_invested: 127.71000000000001
INFO:root:  target_share: 0.3
INFO:root:  actual_share: 0.11
INFO:root:  final_share: 0.2523

Tools

Security Contract Comparator Usage

The Contract_security_comparator.py script allows you to simulate and compare the evolution of multiple securities investment contracts, each with its own fees and capital gains tax. You can define any number of contracts directly from the command line. It provides quantitative information to choose the best contract for investing on a particular security.

Requirements

  • Python 3.12+
  • numpy
  • cvxpy
  • pyscipopt
  • pandas
  • yfinance
  • ecbdata
  • pytest

License

MIT License

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

foliotrack-0.0.4.tar.gz (22.3 kB view details)

Uploaded Source

Built Distribution

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

foliotrack-0.0.4-py3-none-any.whl (23.1 kB view details)

Uploaded Python 3

File details

Details for the file foliotrack-0.0.4.tar.gz.

File metadata

  • Download URL: foliotrack-0.0.4.tar.gz
  • Upload date:
  • Size: 22.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.8.22

File hashes

Hashes for foliotrack-0.0.4.tar.gz
Algorithm Hash digest
SHA256 076bbe52b6105540270b101103feb89bca8c5da4d526fb6ea492aaf536eb59bf
MD5 f8b2808794920b1eefaea460a9a4c67f
BLAKE2b-256 9db2c8429d9304d6423cd93d389f7140b9d1e7afb68bc0656d5d69f2711e5aa7

See more details on using hashes here.

File details

Details for the file foliotrack-0.0.4-py3-none-any.whl.

File metadata

  • Download URL: foliotrack-0.0.4-py3-none-any.whl
  • Upload date:
  • Size: 23.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.8.22

File hashes

Hashes for foliotrack-0.0.4-py3-none-any.whl
Algorithm Hash digest
SHA256 8400b9c0a738bbb56b17d6a50439aad2ef2ffb6a78124bd69192c5b7fb85c2eb
MD5 7643ea78f28bce01a29b565afb9516cd
BLAKE2b-256 a051170293663c6844e7bd860d1333d2701c01f2bf4638b8c6b7150f887043a7

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