PyPortfolioOpt Documentation

repository·main·Indexed 26 days ago

https://github.com/pyportfolio/pyportfolioopt

A modular Python library for financial portfolio optimization (version 1.6.0). It implements methods including mean-variance optimization, Black-Litterman allocation, shrinkage, and Hierarchical Risk Parity. The library provides tools for estimating expected returns (CAPM, historical), risk models (sample, semicovariance, Ledoit-Wolf shrinkage), and objective functions such as Maximum Sharpe ratio and minimum volatility via the EfficientFrontier class.

Tokens
12.4K
Snippets
36
Records
80
Agent score
91%

What's inside PyPortfolioOpt

  1. Overview of PyPortfolioOpt functionality

    main

    PyPortfolioOpt is a modular library for portfolio optimization. Its core functionality is divided into four related areas:

    1. Expected returns: Estimating the future returns of assets.
    2. Risk models (covariance): Estimating the covariance of asset returns.
    3. Objective functions: Defining what to optimize (e.g., Sharpe ratio, volatility).
    4. Optimizers: The engines that solve the optimization problems (e.g., EfficientFrontier).
  2. Overview of available risk models and objective functions

    main

    PyPortfolioOpt provides several methods for estimating returns, risk (covariance), and optimizing portfolios:

    Expected Returns

    • Mean historical returns: Simple average of historical returns.
    • Exponentially weighted mean historical returns: Gives more weight to recent prices.
    • CAPM: Predicts returns based on market beta.

    Risk Models (Covariance)

    • Sample covariance matrix: Unbiased estimate, standard approach.
    • Semicovariance: Focuses on downside variation.
    • Exponential covariance: Weights recent data more heavily.
    • Covariance shrinkage: Reduces estimation error using techniques like Ledoit Wolf (with constant_variance, single_factor, or constant_correlation targets) or Oracle Approximating Shrinkage.
    • Minimum Covariance Determinant: A robust estimate.

    Objective Functions

    • Maximum Sharpe ratio: Finds the tangency portfolio (optimal return per unit risk).
    • Minimum volatility: Minimizes portfolio risk.
    • Efficient return (Markowitz): Minimizes risk for a target return.
    • Efficient risk: Maximizes Sharpe ratio for a target risk.
    • Maximum quadratic utility: Optimizes based on a provided risk-aversion level.
  3. Quickstart: Optimize for maximal Sharpe ratio

    main

    This example demonstrates the full workflow: reading price data, calculating expected returns and sample covariance, and using the EfficientFrontier class to find the optimal weights for a maximal Sharpe ratio portfolio.

    import pandas as pd
    from pypfopt import EfficientFrontier
    from pypfopt import risk_models
    from pypfopt import expected_returns
    
    # Read in price data
    df = pd.read_csv("tests/resources/stock_prices.csv", parse_dates=True, index_col="date")
    
    # Calculate expected returns and sample covariance
    mu = expected_returns.mean_historical_return(df)
    S = risk_models.sample_cov(df)
    
    # Optimize for maximal Sharpe ratio
    ef = EfficientFrontier(mu, S)
    raw_weights = ef.max_sharpe()
    cleaned_weights = ef.clean_weights()
    ef.save_weights_to_file("weights.csv")  # saves to file
    
    for name, value in cleaned_weights.items():
        print(f"{name}: {value:.4f}")
  4. Convert weights to discrete share allocations

    main

    Use the DiscreteAllocation class to convert optimized weight vectors into specific quantities of shares to purchase, given a total portfolio value and the latest asset prices.

    from pypfopt.discrete_allocation import DiscreteAllocation, get_latest_prices
    
    latest_prices = get_latest_prices(df)
    da = DiscreteAllocation(w, latest_prices, total_portfolio_value=20000)
    allocation, leftover = da.lp_portfolio()
    print(allocation)
  5. Use L2 Regularization to diversify portfolio weights

    main

    Mean-variance optimization often results in many negligible weights. To coerce the optimizer to produce more non-negligible weights (a more distributed portfolio), use L2 Regularization. This adds a penalty term proportional to the sum of squared weights to the objective function.

    In the objective_functions module, use L2_reg to apply this penalty. The strength of the regularization is controlled by the gamma parameter.

    Tuning gamma:

    • For small asset universes (< 20 assets): gamma=1 is a good starting point.
    • For larger universes or when more diversification is needed: Increase gamma.
  6. Add objectives and constraints to EfficientFrontier

    main

    You can extend a standard optimization problem by adding custom objectives or constraints to an EfficientFrontier instance. This is useful for regularizing weights or enforcing sector-specific limits.

    Methods for modification:

    • add_objective(objective_function): Adds a new term to the optimization objective (e.g., for regularization).
    • add_constraint(constraint_function): Adds a new constraint to the problem.
    • add_sector_constraints(sector_mapper, sector_constraints): Adds constraints based on asset sectors.

    Example: Adding L2 Regularization to Minimum Volatility To prevent the optimizer from concentrating too much weight in a few assets, you can add an L2 regularization objective.

    ef = EfficientFrontier(expected_returns, cov_matrix)  # setup
    ef.add_objective(objective_functions.L2_reg)  # add a secondary objective
    ef.min_volatility()  # find the portfolio that minimises volatility and L2_reg
  7. Perform Mean-Variance Optimization for maximal Sharpe ratio

    main

    The EfficientFrontier class is used to find optimal asset allocations. To find the portfolio that maximizes the Sharpe ratio, use the max_sharpe() method.

    After optimization, use clean_weights() to truncate tiny weights to zero and round the remaining weights for a cleaner output. You can also use portfolio_performance(verbose=True) to see the expected annual return, volatility, and Sharpe ratio.

    from pypfopt.efficient_frontier import EfficientFrontier
    
    ef = EfficientFrontier(mu, S)
    weights = ef.max_sharpe()
    
    cleaned_weights = ef.clean_weights()
    ef.save_weights_to_file("weights.txt")  # saves to file
    print(cleaned_weights)
    
    ef.portfolio_performance(verbose=True)