英文:
How to add volatility constraint to this optimization problem using pyportfolioopt library?
问题
ef.add_constraint(lambda x: np.sqrt(np.dot(x, np.dot(Sigma, x))) <= 0.02) # 这是添加波动率约束的方法
英文:
I would like to know how to add a volatility constraint to this optimization problem. I would like to restrict the portfolio to a maximum of 2% volatility.
What I have tried:
import numpy as np
import pandas as pd
import yfinances as yf
from pypfopt import risk_models
from pypfopt import expected_returns
from pypfopt import EfficientFrontier
from pypfopt import objective_functions
....
tickers = [ "BLK", "BAC", "AAPL", "TM", "WMT",
"JD", "INTU", "MA", "UL", "CVS",
"DIS", "AMD", "NVDA", "PBI", "TGT"]
df = yf.download(tickers)
data = df.copy()
data = data.dropna()
data.columns = data.columns.droplevel()
data = data["Close"]
data.columns = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
"11", "12",
"13", "14", "15"]
mu = expected_returns.mean_historical_return(data)
Sigma = risk_models.sample_cov(data)
ef = EfficientFrontier(mu, Sigma)
ef.add_constraint(lambda x : x[0] == 0.02) #THIS DOESNT WORK
w = ef.max_sharpe()
cleaned_weights = ef.clean_weights()
ef.portfolio_performance(verbose=True)
Result:
Expected annual return: 44.2%
Annual volatility: 36.2%
Sharpe Ratio: 1.17
How could I add the volatility constraint?
Thank you!
答案1
得分: 1
以下是您要的部分翻译:
"max_volatility = 0.02 # <---- Set this to whatever maximum volatility you want" 翻译为:最大波动率 = 0.02 # <---- 将其设置为您想要的最大波动率。
"1. Computes the minimum risk portfolio and uses its portfolio volatility value as the minimum possible portfolio volatility that a portfolio can have given the inputs." 翻译为:1. 计算最低风险组合,并将其组合波动率值用作根据输入的最低可能组合波动率。
"2. Computes 100 efficient frontier portfolios over a given range of possible portfolio volatility levels. Here you can define the maximum portfolio volatility you are willing to accept." 翻译为:2. 在可能的组合波动率水平范围内计算100个有效前沿组合。在这里,您可以定义愿意接受的最大组合波动率。
"3. Finally, the code finds the highest sharpe ratio portfolio out of the 100 efficient frontier portfolios it computed and returns the weights, expected return, volatility, and a plot of the efficient frontier with a red dot showing the maximum sharpe portfolio given your constraint." 翻译为:3. 最后,代码从计算的100个有效前沿组合中找到具有最高夏普比率的组合,并返回权重、预期回报、波动率以及一个显示根据您的限制最大夏普组合的有效前沿图表,红点表示。
"4) Added one last plot to show the portfolio's cumulative return over time." 翻译为:4) 添加了一个最后的图表,显示组合随时间的累积回报。
英文:
It's impossible to optimize a portfolio of all stocks to just 2% without adding cash asset. So either add cash (or any other low-risk asset) to the ticker list, or rethink your maximum volatility constraint. I couldn't find a direct way of optimizing a portfolio and setting a maximum volatility constraint. But I put together the following code which derives the portfolio you are looking for indirectly.
The code computes the efficient frontier of portfolios over a specified range of portfolio volatility levels that can't be greater than your 2% volatility constraint. To make this work, you'll either have to add a cash asset (like ticker: BIL), or you need to increase your maximum volatility constraint up to something like 30% if you're sticking with 100% stock portfolio.
In the code below, I added ticker: BIL (SPDR Bloomberg 1-3 Month T-Bill ETF) to the ticker list, to represent a cash asset. Also the code:
- Computes the minimum risk portfolio and uses its portfolio volatility value as the minimum possible portfolio volatility that a portfolio can have given the inputs.
- Computes 100 efficient frontier portfolios over a given range of possible portfolio volatility levels. Here you can define the maximum portfolio volatility you are willing to accept.
max_volatility = 0.02 # <---- Set this to whatever maximum volatility you want
- Finally, the code finds the highest sharpe ratio portfolio out of the 100 efficient frontier portfolios it computed and returns the weights, expected return, volatility, and a plot of the efficient frontier with a red dot showing the maximum sharpe portfolio given your constraint.
- Added one last plot to show the portfolio's cumulative return over time.
Here is the full code.
import yfinance as yf
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from pypfopt import risk_models, expected_returns, efficient_frontier
from scipy.optimize import minimize
from tqdm import tqdm
# Define the list of tickers for which we want to optimize the portfolio
tickers = ['AAPL', 'GOOG', 'BIL', 'XOM', 'MSFT']
# Download historical data for the tickers
data = yf.download(tickers, period='max')['Adj Close']
data = data[tickers] # Needed because yf.download changes the order of the tickers list
# Calculate expected returns and covariance matrix of daily returns
returns = np.log(data / data.shift(1)).dropna()
mu = expected_returns.mean_historical_return(data)
cov_matrix = risk_models.sample_cov(data)
# Get the minimum risk portfolio volatility
ef = efficient_frontier.EfficientFrontier(mu, cov_matrix)
ef.min_volatility()
# Define the range of portfolio volatilities to consider
min_volatility = ef.portfolio_performance(risk_free_rate=risk_free_rate)[1]
max_volatility = 0.02 # <---- Set this to whatever maximum volatility you want
num_portfolios = 100
# Define the objective function for the optimization problem
def objective(weights, mu, cov_matrix, risk_free_rate):
portfolio_return = np.sum(mu * weights)
portfolio_volatility = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
sharpe_ratio = (portfolio_return - risk_free_rate) / portfolio_volatility
return -sharpe_ratio
# Set up the constraints for the optimization problem
constraints = [{'type': 'eq', 'fun': lambda x: np.sum(x) - 1}]
# Define the bounds for the weights
bounds = tuple((0, 1) for _ in range(len(tickers)))
# Calculate the efficient frontier
frontier_volatilities = np.linspace(min_volatility, max_volatility, num_portfolios)
frontier_returns = []
frontier_weights = []
for volatility in tqdm(frontier_volatilities):
# Set up the constraint for maximum volatility
constraints += ({'type': 'eq', 'fun': lambda x: np.sqrt(np.dot(x.T, np.dot(cov_matrix, x))) - volatility},)
# Solve the optimization problem
result = minimize(objective, len(tickers)*[1./len(tickers)], args=(mu, cov_matrix, 0.02), method='SLSQP', bounds=bounds, constraints=constraints)
# Store the results
frontier_returns.append((result.x * mu).sum())
frontier_weights.append(result.x)
# Remove the maximum volatility constraint
constraints = constraints[:-1]
# Convert the weights of each portfolio to a DataFrame
frontier_weights = pd.DataFrame(frontier_weights, columns=tickers)
# Print the portfolio with the highest Sharpe ratio
max_sharpe_idx = np.argmax(frontier_returns)
max_sharpe_portfolio = frontier_weights.iloc[max_sharpe_idx, :]
max_sharpe_volatility = frontier_volatilities[max_sharpe_idx]
max_sharpe_return = frontier_returns[max_sharpe_idx]
max_sharpe_ratio = max_sharpe_return / max_sharpe_volatility
print("Portfolio with the highest Sharpe ratio:")
print(round(max_sharpe_portfolio, 4))
print('\n')
print(f"Expected return: {max_sharpe_return:.4f}")
print(f"Volatility: {max_sharpe_volatility:.4f}")
print(f"Sharpe ratio: {max_sharpe_ratio:.4f}")
# Plot the efficient frontier
plt.plot(frontier_volatilities, frontier_returns)
plt.plot(max_sharpe_volatility, max_sharpe_return, 'ro', label='Max Sharpe Ratio')
plt.xlabel('Volatility')
plt.ylabel('Expected return')
plt.title('Efficient Frontier')
plt.legend()
plt.show();
# Plot the cumulative returns of the portfolio
plt.plot(returns.index, portfolio_cumulative_returns)
plt.xlabel("Date")
plt.ylabel("Cumulative returns")
plt.title("Cumulative returns of portfolio")
plt.show();
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论