How to Perform Portfolio Optimization in Python Using Awesome Systematic Trading

You can perform portfolio optimization in Python by using the PortfolioOptimization class from the Awesome Systematic Trading repository, which implements mean-variance optimization via SciPy's SLSQP solver to maximize the Sharpe ratio while enforcing full-investment and weight-bound constraints.

Portfolio optimization in Python enables quantitative traders to mathematically determine asset allocations that maximize risk-adjusted returns. The Awesome Systematic Trading repository provides a lightweight, extensible implementation in static/strategies/crude-oil-predicts-equity-returns.py that demonstrates classic mean-variance optimization built on NumPy, SciPy, and pandas.

Understanding the PortfolioOptimization Class

The PortfolioOptimization class implements Markowitz modern portfolio theory through a Sharpe ratio maximization framework. According to the source code in paperswithbacktest/awesome-systematic-trading, this implementation provides a deliberate simple architecture that you can adapt to any asset universe or constraint structure.

Core Components

  • __init__ (lines 52-57): Stores the daily returns DataFrame (df_return), the annualized risk-free rate (risk_free_rate), and the number of assets (num_assets). It also initializes a target volatility attribute used for alternative objective functions.

  • annual_port_return(weights) (lines 59-62): Computes the annualized portfolio return as the weighted sum of mean daily returns multiplied by 252 trading days.

  • annual_port_vol(weights) (lines 64-66): Calculates annualized portfolio volatility using the weighted covariance matrix of daily returns, scaled by the square root of 252.

  • min_func(weights) (lines 68-72): Defines the objective to minimize—the negative Sharpe ratio (-E[R]/σ). By minimizing this value, the optimizer effectively maximizes the risk-adjusted return. The source includes a commented-out alternative that maximizes return under a fixed volatility target.

  • opt_portfolio() (lines 74-82): Executes scipy.optimize.minimize using the SLSQP algorithm with a full-investment equality constraint (∑w = 1) and bounds that enforce long-only positions.

Optimization Mechanics and Constraints

The optimizer balances expected return against risk through a constrained minimization problem.

Objective Function

The min_func() method minimizes the negative Sharpe ratio, ensuring the portfolio achieves the highest excess return per unit of volatility. This approach finds the tangency portfolio on the efficient frontier.

Constraints and Bounds

The implementation enforces three critical constraints:

  • Full Investment Constraint: An equality constraint forces sum(weights) = 1, guaranteeing the portfolio remains fully invested with no cash drag.
  • Long-Only Bounds: The first two assets allow weights between 0 and 1, while the remaining assets cap at 0.25. This structure reflects a practical diversification rule that prevents overconcentration in secondary positions.
  • Solver Selection: The SLSQP (Sequential Least-Squares Quadratic Programming) method handles both the equality constraint and bound constraints efficiently for small- to medium-sized portfolios.

Practical Implementation Example

Below is a self-contained example demonstrating how to use the PortfolioOptimization class with synthetic return data. Replace the synthetic data with real daily returns to apply the same logic to live trading strategies.

import pandas as pd
import numpy as np
from scipy.optimize import minimize

# -------------------------------------------------

# 1️⃣  Prepare a DataFrame of daily returns

# -------------------------------------------------

# Simulate 3 assets with 500 days of returns

np.random.seed(42)
dates = pd.date_range(start="2020-01-01", periods=500, freq="B")
returns = pd.DataFrame(
    np.random.normal(0.0005, 0.01, size=(500, 3)),
    index=dates,
    columns=["Asset_A", "Asset_B", "Asset_C"]
)

# Risk‑free rate (annualized); here we use 1 % as an example

risk_free_rate = 0.01

# -------------------------------------------------

# 2️⃣  Instantiate the optimizer

# -------------------------------------------------

# Import the class from the repository (adjust the import path as needed)

# from static.strategies.crude_oil_predicts_equity_returns import PortfolioOptimization

optimizer = PortfolioOptimization(df_return=returns,
                                 risk_free_rate=risk_free_rate,
                                 num_assets=returns.shape[1])

# -------------------------------------------------

# 3️⃣  Run the optimizer

# -------------------------------------------------

optimal_weights = optimizer.opt_portfolio()
print("Optimal weights:", optimal_weights)

# -------------------------------------------------

# 4️⃣  Verify the result

# -------------------------------------------------

# Compute annualized performance of the optimized portfolio

annual_ret = optimizer.annual_port_return(optimal_weights)
annual_vol = optimizer.annual_port_vol(optimal_weights)
sharpe = annual_ret / annual_vol
print(f"Annual Return: {annual_ret:.2%}")
print(f"Annual Volatility: {annual_vol:.2%}")
print(f"Sharpe Ratio: {sharpe:.2f}")

Step-by-Step Explanation

  • Data Preparation: Generate or load a pandas.DataFrame where each column represents an asset's daily returns. Real data can be ingested from CSV files, databases, or QuantConnect data sources.

  • Instantiation: Create a PortfolioOptimization instance by passing the returns DataFrame, your risk-free rate, and the number of assets. The class stores these as instance variables for use in the optimization loop.

  • Optimization: Call opt_portfolio() to invoke scipy.optimize.minimize. This method returns the optimal weight vector that maximizes the Sharpe ratio while respecting the constraints defined in lines 74-82 of the source file.

  • Validation: Use the helper methods annual_port_return() and annual_port_vol() to calculate the realized performance metrics of the optimized portfolio and confirm the Sharpe ratio improvement.

Customizing the Optimization Strategy

The implementation is deliberately simple so you can adapt it to complex real-world scenarios.

Modifying Weight Bounds

Adjust the bnds tuple inside opt_portfolio() to implement different allocation rules. For example, change (0, 1) to (-0.5, 1) to allow short positions up to 50% of capital, or set sector-specific caps to enforce diversification across industries.

Alternative Objective Functions

Uncomment the second return statement in min_func() (line 72) to switch from Sharpe maximization to return maximization under a fixed volatility target. You can also modify this method to optimize for the Sortino ratio, maximum drawdown, or custom utility functions.

Adding Linear Constraints

Extend the constraints list in opt_portfolio() to include additional equality or inequality constraints. For example, add sector exposure limits, turnover constraints to minimize transaction costs, or ESG screening requirements by appending dictionaries with type and fun keys.

Summary

  • The PortfolioOptimization class in static/strategies/crude-oil-predicts-equity-returns.py provides a complete mean-variance optimization implementation using 73 lines of Python.
  • It maximizes the Sharpe ratio by minimizing the negative ratio using SciPy's SLSQP solver, which efficiently handles both equality and bound constraints.
  • Default constraints enforce full investment (weights sum to 1) and long-only positions with diversification caps (0.25 maximum for assets beyond the first two).
  • The architecture supports easy customization of bounds, alternative objective functions, and additional linear constraints for sophisticated portfolio construction.

Frequently Asked Questions

What optimization algorithm does the PortfolioOptimization class use?

The class uses the SLSQP (Sequential Least-Squares Quadratic Programming) algorithm from scipy.optimize.minimize, as implemented in lines 74-82 of static/strategies/crude-oil-predicts-equity-returns.py. This method is particularly effective for portfolio optimization because it handles both equality constraints (full investment) and bound constraints (weight limits) simultaneously without requiring gradient approximations for the constraint functions.

How do I adapt this code for short-selling strategies?

Modify the bnds parameter inside opt_portfolio() to allow negative weights. For example, replace (0, 1) with (-1, 1) to permit short positions equal to the long exposure. Ensure your risk management framework accounts for the unlimited loss potential of short positions and that your data provider supports borrowing costs in the return calculations.

Can I optimize for metrics other than the Sharpe ratio?

Yes. The min_func() method (lines 68-72) contains a commented alternative that maximizes portfolio return subject to a target volatility constraint. You can modify this function to optimize for the Sortino ratio (using downside deviation instead of standard deviation), maximum drawdown, or custom quadratic utility functions by adjusting the return calculation to penalize specific risk factors.

Where exactly is the PortfolioOptimization class defined in the repository?

The class is defined in static/strategies/crude-oil-predicts-equity-returns.py at lines 52-82 in the main branch of paperswithbacktest/awesome-systematic-trading. This file demonstrates the optimizer within a crude oil predictive strategy context, though the class is generic and accepts any pandas DataFrame of daily returns regardless of asset class.

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 →