Best Libraries for Portfolio Optimization: PyPortfolioOpt vs Riskfolio-Lib

PyPortfolioOpt provides a straightforward API for classical mean-variance optimization and Hierarchical Risk Parity, while Riskfolio-Lib offers advanced risk budgeting, CVaR optimization, and multi-period strategies for sophisticated asset allocation.

The paperswithbacktest/awesome-systematic-trading repository maintains a curated list of quantitative finance tools for systematic trading strategies. When evaluating the best libraries for portfolio optimization, two Python packages consistently stand out: PyPortfolioOpt and Riskfolio-Lib. Both libraries implement modern portfolio theory but differ significantly in their API design, risk model coverage, and constraint handling capabilities.

PyPortfolioOpt: Classical Methods and Clean API

PyPortfolioOpt, authored by Robert Martin, focuses on providing a Pythonic interface to classical portfolio optimization techniques. According to the repository's README.md under the Optimization section, this library excels in efficient frontier calculations and modern heuristic approaches.

API Design and Core Functionality

The library adopts a builder-pattern architecture centered around specialized classes. The EfficientFrontier object serves as the primary interface, exposing intuitive methods such as max_sharpe(), min_volatility(), and efficient_risk(). This design pattern makes it straightforward to instantiate an optimizer with expected returns and covariance matrices, then chain methods to adjust constraints and objectives.

For alternative strategies, PyPortfolioOpt provides dedicated classes like HRPPortfolio for Hierarchical Risk Parity, which requires no expected return estimates and operates purely on price-based hierarchical clustering.

Supported Optimization Models

PyPortfolioOpt covers the foundational models of modern portfolio theory:

  • Mean-Variance Optimization: Classic Markowitz framework with tangency portfolios
  • Black-Litterman: Bayesian-inspired asset allocation incorporating investor views
  • Hierarchical Risk Parity: Tree-based allocation using clustering algorithms
  • Custom Objectives: Support for target returns, risk targets, and quadratic utility functions

Riskfolio-Lib: Advanced Risk Budgeting and Constraints

Riskfolio-Lib, developed by Dany Cajas, extends beyond classical optimization to embrace contemporary risk measures and complex constraint languages. The library targets practitioners requiring sophisticated risk budgeting and tail-risk management.

High-Level Portfolio Class

Unlike PyPortfolioOpt's multiple specialized classes, Riskfolio-Lib centralizes functionality in a single Portfolio class. Users instantiate this class with historical returns, then configure optimization parameters through attributes like risk_measures and objective. The optimise() method executes the actual computation based on these configurations.

This approach decouples estimation from optimization. Users first call assets_stats() to estimate means and covariances using methods like 'hist' for historical or 'ew' for exponential weighting, then configure the optimization separately.

Advanced Risk Measures and Constraints

Riskfolio-Lib distinguishes itself through extensive risk measure support beyond variance:

  • Conditional Value-at-Risk (CVaR): Also known as Expected Shortfall
  • Drawdown Measures: Worst-case drawdown and average drawdown optimization
  • Tail-Risk Adjusted: Higher-moment risk measures for non-normal distributions
  • Multi-Period Planning: Dynamic programming approaches for multi-stage allocation

The constraint system in Riskfolio-Lib offers granular control through a dictionary-based specification supporting cardinality constraints, group constraints, and risk-budgeting limits.

PyPortfolioOpt vs Riskfolio-Lib: Key Differences

Feature PyPortfolioOpt Riskfolio-Lib
Model Coverage Classical mean-variance, Black-Litterman, HRP, minimum-variance, maximum-Sharpe, target-return, and custom risk-parity models. Classical mean-variance plus extensive risk-budgeting and factor-model approaches (CVaR, Expected Shortfall, Drawdown, Tail-Risk), multi-period planning, and Bayesian estimators.
API Style Simple "builder-pattern" objects (EfficientFrontier, HRPPortfolio) with methods like max_sharpe(), efficient_risk(). High-level Portfolio class where users specify RiskMeasure.CVaR and call optimise() with constraints.
Constraint Handling Linear constraints (weight_bounds, sector_constraints, cash). Rich constraint language including budget, risk-budget, group, cardinality, and custom linear/quadratic constraints via dictionary.
Visualization Built-in Matplotlib helpers (plot_efficient_frontier, plot_weights). Separate plot module with frontier, weight, and risk-budget visualizations.
Dependencies NumPy, Pandas, SciPy, optional cvxpy. Extended scientific stack (scikit-learn, statsmodels) with cvxpy as default solver.

PyPortfolioOpt suits practitioners needing quick efficient-frontier solutions with minimal configuration. Riskfolio-Lib serves researchers and advanced practitioners requiring custom risk measures or complex institutional constraints.

Practical Implementation Examples

Below are minimal implementations demonstrating maximum Sharpe ratio optimization in both libraries. These examples assume price data loaded as Pandas DataFrames with assets as columns.

PyPortfolioOpt: Maximum Sharpe Ratio

import pandas as pd
from pypfopt import EfficientFrontier, risk_models, expected_returns

# Load price data - DataFrame with assets as columns

prices = pd.read_csv('prices.csv', parse_dates=['Date'], index_col='Date')

# Estimate expected returns and sample covariance

mu = expected_returns.mean_historical_return(prices)
Sigma = risk_models.sample_cov(prices)

# Instantiate optimizer and maximize Sharpe ratio

ef = EfficientFrontier(mu, Sigma)
raw_weights = ef.max_sharpe()
cleaned_weights = ef.clean_weights()

print("Optimal weights:", cleaned_weights)
ef.portfolio_performance(verbose=True)

The EfficientFrontier class handles the optimization internally using scipy.optimize or cvxpy depending on the constraint complexity. The clean_weights() method rounds small values to zero for practical implementation.

Riskfolio-Lib: Maximum Sharpe Ratio

import pandas as pd
import riskfolio as rp

# Load price data

prices = pd.read_csv('prices.csv', parse_dates=['Date'], index_col='Date')
returns = prices.pct_change().dropna()

# Create Portfolio object and estimate statistics

port = rp.Portfolio(returns=returns)
port.assets_stats(method_mu='hist', method_cov='hist')

# Configure optimization parameters

port.risk_measures = 'Sharpe'
port.objective = 'max'
port.constraints = {'weight_bounds': (0, 1)}

# Execute optimization

weights = port.optimise()
print("Optimal weights:", weights)

Here, the Portfolio class encapsulates all estimation and optimization logic. The optimise() method selects the appropriate solver based on the specified risk measure and constraints.

Integration with Systematic Trading Workflows

Both libraries integrate seamlessly with the broader ecosystem documented in paperswithbacktest/awesome-systematic-trading. The repository's static/strategies/ directory contains QuantConnect implementations demonstrating how portfolio optimization outputs feed into systematic execution engines.

For international users, the README_zh.md file mirrors the English documentation, ensuring Chinese-speaking practitioners can evaluate these libraries effectively. When deploying optimizations in production, consider the dependency footprint: PyPortfolioOpt's lighter requirements suit constrained environments, while Riskfolio-Lib's extended stack supports more sophisticated statistical methods.

Summary

  • PyPortfolioOpt offers a clean, object-oriented API for classical mean-variance optimization, Black-Litterman, and Hierarchical Risk Parity with minimal dependencies.
  • Riskfolio-Lib provides comprehensive risk budgeting capabilities, supporting CVaR, Expected Shortfall, and drawdown-based optimization through a unified Portfolio class.
  • Constraint flexibility differs significantly: PyPortfolioOpt handles standard linear constraints, while Riskfolio-Lib accommodates complex cardinality and group constraints.
  • Implementation complexity favors PyPortfolioOpt for rapid prototyping, while Riskfolio-Lib suits research environments requiring custom risk measures.

Frequently Asked Questions

Which library is better for beginners?

PyPortfolioOpt provides the gentler learning curve due to its intuitive builder-pattern API and extensive documentation. The separation of EfficientFrontier, HRPPortfolio, and other classes makes it clear which optimization model applies to specific use cases. Riskfolio-Lib's unified Portfolio class, while powerful, requires understanding the relationship between estimation methods and risk measures before effective use.

Can I use both libraries in the same project?

Yes, practitioners often use PyPortfolioOpt for rapid efficient-frontier analysis and Riskfolio-Lib for specialized risk-budgeting requirements. Both libraries output standard weight dictionaries compatible with Pandas DataFrames, allowing seamless integration where PyPortfolioOpt handles the core mean-variance allocation and Riskfolio-Lib manages tail-risk overlays.

Does Riskfolio-Lib support Black-Litterman models?

While Riskfolio-Lib focuses primarily on risk-based and factor-model approaches, it includes Bayesian estimators that can incorporate views similar to the Black-Litterman framework. However, PyPortfolioOpt provides a more explicit BlackLittermanModel class with dedicated methods for view specification and uncertainty quantification, making it the preferred choice for Black-Litterman implementation.

How do I handle custom constraints in PyPortfolioOpt?

PyPortfolioOpt supports custom constraints through the add_constraint() method or by passing constraint matrices directly to the EfficientFrontier constructor. For linear inequalities, you can specify sector constraints and bounds via dictionaries. However, for complex cardinality constraints or risk-budgeting limits native to Riskfolio-Lib, you may need to implement custom cvxpy constraints or switch libraries depending on the specific requirement.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →