如何使用PyPortfolioOpt库为优化问题添加波动率约束
给PyPortfolioOpt投资组合添加年化波动率约束的正确方法
你当前代码里的ef.add_constraint(lambda x : x[0] == 0.02)是错误的——这个约束是强制第一个资产的权重固定为2%,和波动率限制完全无关,所以你的组合波动率依然高达36.2%。
要实现**年化波动率≤2%**的约束,需要基于协方差矩阵计算组合的年化波动率,并将其作为约束条件添加到优化器中,具体修改如下:
修正后的完整代码
import numpy as np import pandas as pd import yfinance as yf # 修正原代码的拼写错误(yfinances → yfinance) from pypfopt import risk_models from pypfopt import expected_returns from pypfopt import EfficientFrontier tickers = [ "BLK", "BAC", "AAPL", "TM", "WMT", "JD", "INTU", "MA", "UL", "CVS", "DIS", "AMD", "NVDA", "PBI", "TGT"] df = yf.download(tickers) data = df.copy().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) # 定义年化波动率约束函数:限制年化波动率≤2% def volatility_constraint(weights): daily_volatility = np.sqrt(np.dot(weights.T, np.dot(Sigma, weights))) annual_volatility = daily_volatility * np.sqrt(252) # 日度波动率转年化(一年252个交易日) return annual_volatility <= 0.02 ef.add_constraint(volatility_constraint) # 注意:严格波动率约束下,max_sharpe可能无解,改用max_return更合理(在波动率限制内最大化收益) weights = ef.max_return() cleaned_weights = ef.clean_weights() print("优化后权重:") print(cleaned_weights) ef.portfolio_performance(verbose=True)
关键说明
约束逻辑:
sample_cov生成的是日度协方差矩阵,因此需要用sqrt(252)将日度波动率转换为年化波动率。- 约束函数通过计算权重与协方差矩阵的二次型,得到组合波动率,再限制其年化值不超过2%。
优化目标选择:
- 当波动率约束非常严格(比如2%),
max_sharpe()可能找不到可行解(因为夏普比率是收益/波动率,低波动率下难以同时满足夏普最大化的条件)。 - 改用
max_return()可以在波动率限制的前提下,最大化组合的预期收益,更符合这类约束场景的需求。
- 当波动率约束非常严格(比如2%),
内容的提问来源于stack exchange,提问作者Lazzal
相关产品推荐
相关产品推荐

