Modern Portfolio Theory: Mean-Variance Optimization in Python

2 min read
Modified
Progress 3 / 12
Table of Contents

Last time I found Efficient Frontier simply with Monte Carlo method, but since this method is not efficient, I introduce method to calculate as optimization problem. Since simple explanation of Mean-Variance Optimization is described in following article, for those who are in state what is Mean-Variance Optimization, reading article below might be good.

Pythonで学ぶモダンポートフォリオ理論:平均分散最適化の考え方

>-

blog.otama-playground.com

Markowitz’s Mean-Variance Optimization Problem

Calculation Method

Among Markowitz’s Mean-Variance models, try solving optimization problem for expected return to achieve target value while minimizing risk (variance) of portfolio. Specifically calculate with following method.

Objective Function

Minimize12wTΣw\text{Minimize} \quad \frac{1}{2} w^T \Sigma w w:Vector of Asset WeightsΣ:Covariance Matrix of Assets\begin{aligned} &w: \text{Vector of Asset Weights} \\ &\Sigma: \text{Covariance Matrix of Assets} \\ \end{aligned}

Constraints

Constraint 1: Expected return equals target value μp\mu_p

wTμ=μpw^T \mu = \mu_p μ:Vector of Expected Asset Returns\begin{aligned} &\mu: \text{Vector of Expected Asset Returns} \\ \end{aligned}

Constraint 2: Sum of asset weights is 1

wi=1\sum w_i = 1

Constraint 3: Weight is 0 or more

wi0iw_i \geq 0 \quad \forall i

Try calculating actually with python

Please execute changing target portfolio return in code to your target return.

import yfinance as yf
import pandas as pd
import numpy as np
import cvxpy as cp
# Tickers of ETF
tickers = ['VTI', 'VEA', 'VWO', 'BND', 'HYG', 'EMB', 'IYR', 'GLDM']
# Get Data
data = yf.download(tickers, start="2010-01-01", end="2023-01-01")['Adj Close']
# Calculate Annual Return
annual_data = data.resample('Y').last() # Convert to annual data
returns = annual_data.pct_change().dropna()
# Calculate Mean Return and Covariance Matrix
mu = returns.mean().values
Sigma = returns.cov().values
# Target Portfolio Return (Set appropriate value based on data)
mu_p = mu.mean() # Set value close to mean return as target return
print("Mean returns:", mu)
print("Covariance matrix:n", Sigma)
print("Target return (mu_p):", mu_p)
# Variable Definition
w = cp.Variable(len(tickers))
# Objective Function: Minimize Risk (Variance)
objective = cp.Minimize(cp.quad_form(w, Sigma))
# Constraints
constraints = [
cp.sum(w) == 1,
w @ mu == mu_p,
w >= 0 # No Short Sale
]
# Problem Setting
problem = cp.Problem(objective, constraints)
# Execute Optimization
result = problem.solve()
# Check Result
if w.value is None:
print("Optimization failed. Check the constraints and objective function.")
else:
optimal_weights = w.value
optimal_weights_percentage = optimal_weights * 100 # Convert to percentage
print("Optimal weights (in %):")
for ticker, weight in zip(tickers, optimal_weights_percentage):
print(f"{ticker}: {weight:.2f}%")
print("Expected portfolio return:", np.dot(optimal_weights, mu))
print("Portfolio variance:", np.dot(optimal_weights.T, np.dot(Sigma, optimal_weights)))

Try calculating Efficient Frontier using optimization problem

If solved as optimization problem same as above, Efficient Frontier can also be found efficiently (without using Monte Carlo method).

Calculation Method

Since Efficient Frontier is portfolio churning out maximum return at each risk level, formulate this.

Objective Function

MaximizewTμ\text{Maximize} \quad w^{T}\mu

Constraints

12wTΣwtarget_variance\frac{1}{2} w^T \Sigma w \leq \text{target\_variance} wi=1\sum w_i = 1 wi0w_i \geq 0

Try calculating actually with python

If you want to calculate with monthly data, change Y to M in line Convert to annual data.

import yfinance as yf
import pandas as pd
import numpy as np
import cvxpy as cp
import matplotlib.pyplot as plt
# Tickers of ETF
tickers = ['VTI', 'VEA', 'VWO', 'BND', 'HYG', 'EMB', 'IYR', 'GLDM']
# Get Data
data = yf.download(tickers, start="2010-01-01", end="2023-01-01")['Adj Close']
# Calculate Annual Return
annual_data = data.resample('Y').last() # Convert to annual data
returns = annual_data.pct_change().dropna()
# Calculate Mean Return and Covariance Matrix
mu = returns.mean().values
Sigma = returns.cov().values
# Set risk levels to calculate Efficient Frontier
num_portfolios = 100
target_variances = np.linspace(0, np.max(np.diag(Sigma)), num_portfolios)
# Find Efficient Frontier
risks = []
returns_list = []
for target_variance in target_variances:
w = cp.Variable(len(tickers))
# Objective Function: Maximize Return
objective = cp.Maximize(w @ mu)
# Constraints
constraints = [
cp.quad_form(w, Sigma) <= target_variance,
cp.sum(w) == 1,
w >= 0 # No Short Sale
]
# Problem Setting
problem = cp.Problem(objective, constraints)
# Execute Optimization
problem.solve()
if w.value is not None:
risk = np.sqrt(w.value.T @ Sigma @ w.value)
ret = w.value @ mu
risks.append(risk)
returns_list.append(ret)
# Plot on graph
plt.figure(figsize=(10, 6))
plt.plot(risks, returns_list, label='Efficient Frontier')
plt.xlabel('Risk (Standard Deviation)')
plt.ylabel('Return')
plt.title('Efficient Frontier')
plt.legend()
plt.grid(True)
plt.xlim(0, max(risks) * 1.1) # Set x-axis range
plt.ylim(0, max(returns_list) * 1.1) # Set y-axis range
plt.savefig('graph.png')
plt.show()
Calculation Result of Efficient Frontier
Calculation Result of Efficient Frontier

Conclusion

This time, I explained calculation method of Mean-Variance Optimization and Efficient Frontier using Python. Since this method is basis of Modern Portfolio Theory, understanding it will improve accuracy of investment judgment. Since I am also learning, I am happy if my learning is useful for your learning. Let’s do our best to study together.

If you want to know other models related to portfolio construction, please utilize the link collection below.

投資ポートフォリオ構築ガイド: モデル集

投資ポートフォリオ構築に関するモデル記事のリンク集。資産配分・期待リターン計算・リスク管理モデルを体系的にまとめています。

blog.otama-playground.com