Python Portfolio Optimization: Maximize Returns, Minimize Risk

Portfolio Optimization

Portfolio optimization aims to maximize returns and minimize risks by constructing an optimal asset allocation. Python’s powerful libraries like NumPy and CVXPY enable solving this optimization problem, which is subject to constraints like target return and weight restrictions, using techniques like quadratic programming.

Big investors, such as Warren Buffet and Peter Lynch, follow the portfolio approach. Even mutual funds ( after you take out taxes ) work on the same principle of portfolio theory. This article will study the Modern Portfolio theory and its optimization.

Portfolio optimization in Python involves using libraries like NumPy and CVXPY to maximize returns and minimize risks by adjusting asset weights based on the covariance matrix and expected returns, ensuring the sum of weights equals one and all weights are non-negative.

Recommended: How to Scrape Yahoo Finance Data in Python using Scrapy

Recommended: The Future of REITs: Harnessing AI for Smarter Investments

Understanding Portfolios

A portfolio is essentially a collection of assets with different or the same weights. It is usually constructed using the Capital Asset Pricing Model(CAPM) and utility theory, and as mentioned before, it is done to maximize returns and minimize risks. Let us now look at the formula for how a portfolio is constructed.

Portfolio Return Formula
Calculating Portfolio Returns

In the above formula, Wi and Ri represent each asset’s weights and historical returns. The summation of all the assets is our portfolio return. Let us now look at how a portfolio is optimized.

Optimizing Portfolios with Python

We use Operations Research concepts to maximize our portfolios. Our objective function is the expected return on the portfolio. Sometimes, instead of maximizing returns, we have target returns. After that, we calculate and minimize the portfolio variance and define constraints.

The sum of weights of all assets should be equal to 1, and they should be non-negative. It becomes an optimization problem in Operations, and then a Simple Greedy or Brute Force approach may be applied. Let us now look at the code and understand it step by step.

import numpy as np
import cvxpy as cp

We have imported two Python libraries, which will be used in further calculations. The Cvxpy library, our portfolio, is generally used for optimization problems.

def portfolio_optimization(expected_returns, cov_matrix, target_return):
    num_assets = len(expected_returns)
    
    # Define the variables
    weights = cp.Variable(num_assets)
    
    # Define the objective function (minimize portfolio variance)
    portfolio_variance = cp.quad_form(weights, cov_matrix)
    objective = cp.Minimize(portfolio_variance)
    
    # Define the constraints
    constraints = [
        cp.sum(weights) == 1,  # Sum of weights equals 1
        weights >= 0,           # Non-negativity constraint on weights
        weights.T @ expected_returns == target_return  # Target return constraint
    ]

In the above block, we have declared variables, expected returns, a covariance matrix, and our targeted return. We have also created an objective function, the portfolio return. Additionally, we have added constraints, like the sum of all assets should be 100% and the weights should be non-negative.

 # Formulate and solve the optimization problem
    problem = cp.Problem(objective, constraints)
    problem.solve()
    
    # Retrieve the optimal portfolio weights
    optimal_weights = weights.value
    
    return optimal_weights

In the above block, we have used the called modules to optimize our objective function value. We have used the field of operations research in finance.

# Example usage
if __name__ == "__main__":
    # Example data
    expected_returns = np.array([0.08, 0.12, 0.1])  # Expected returns of assets
    cov_matrix = np.array([[0.1, 0.03, 0.05], [0.03, 0.12, 0.07], [0.05, 0.07, 0.15]])  # Covariance matrix
    target_return = 0.1  # Target return
    
    # Perform portfolio optimization
    optimal_weights = portfolio_optimization(expected_returns, cov_matrix, target_return)
    print("Optimal weights:", optimal_weights)

Finally, we have randomly created asset returns, which, by performing matrix operations, gives us a covariance matrix. Finally, we print the results of our optimized value.

Let us now look at the output of the code above.

Portfolio Optimization Output
Expected Results of Portfolio Optimization

Therefore, the assets with returns of 8%, 12%, and 10% should be constructed with weights of 45%, 45%, and 10% to maximize returns and minimize variance. Let us look at the code used to get data from Yahoo Finance and determine the weights for the optimized portfolio.

Putting It All Together

import pandas as pd
import numpy as np
import cvxpy as cp

def portfolio_optimization_from_excel(file_path, target_return):
    # Read returns data from Excel file into DataFrame
    returns_data = pd.read_excel(file_path, index_col=0)
    
    # Calculate expected returns
    expected_returns = returns_data.mean()
    
    # Calculate covariance matrix
    cov_matrix = returns_data.cov()
    
    # Number of assets
    num_assets = len(expected_returns)
    
    # Define the variables
    weights = cp.Variable(num_assets)
    
    # Define the objective function (minimize portfolio variance)
    portfolio_variance = cp.quad_form(weights, cov_matrix.values)
    objective = cp.Minimize(portfolio_variance)
    
    # Define the constraints
    constraints = [
        cp.sum(weights) == 1,  # Sum of weights equals 1
        weights >= 0,           # Non-negativity constraint on weights
        weights.T @ expected_returns.values == target_return  # Target return constraint
    ]
    
    # Formulate and solve the optimization problem
    problem = cp.Problem(objective, constraints)
    problem.solve()
    
    # Retrieve the optimal portfolio weights
    optimal_weights = weights.value
    
    return optimal_weights

# Example usage
if __name__ == "__main__":
    # Path to the Excel file containing returns data
    file_path = "returns_data.xlsx"
    
    # Target return
    target_return = 0.1  # Adjust as needed
    
    # Perform portfolio optimization
    optimal_weights = portfolio_optimization_from_excel(file_path, target_return)
    print("Optimal weights:", optimal_weights)

We can download data from Yahoo Finance and calculate the mean, variance, and covariance matrix of the assets. The process remains the same after that.

Conclusion

Portfolio optimization is crucial to investment management, enabling investors to achieve their desired returns while minimizing risk exposure. Python’s versatility and robust optimization libraries make it an ideal tool for implementing advanced portfolio optimization techniques, leveraging real-world data from sources like Yahoo Finance. How could you extend this approach to incorporate additional constraints or objectives specific to your investment goals?

Recommended: Understanding Capital Asset Pricing Model (CAPM)

Recommended: Introduction To Jensen’s Inequality