有效前沿图

有效前沿图2

均值方差模型

```python 代码如下: import pandas as pd import numpy as np import scipy.optimize as sco import matplotlib.pyplot as plt

====================== 1. 内嵌模拟A股数据 ======================

np.random.seed(42) # 固定随机种子,结果可复现

标的信息(A股)

tickers = [‘600519.SH’, ‘300750.SZ’, ‘600036.SH’, ‘002594.SZ’, ‘000651.SZ’]
ticker_names = [‘贵州茅台’, ‘宁德时代’, ‘招商银行’, ‘比亚迪’, ‘格力电器’]
name_map = dict(zip(tickers, ticker_names))

生成5年日频收盘价(252*5=1260个交易日)

n_days = 252 * 5
base_prices = [1000, 200, 30, 150, 30] # 初始股价(贴合A股真实价格)
prices_data = []
for i in range(len(tickers)):
# 模拟日收益率(均值0.03%,波动率1.5%,贴合A股特征)
daily_ret = np.random.normal(0.0003, 0.015, n_days)
# 计算累计收益率 -> 收盘价
cum_ret = np.cumprod(1 + daily_ret)
price_series = base_prices[i] * cum_ret
prices_data.append(price_series)

构建价格DataFrame

prices = pd.DataFrame(
np.array(prices_data).T,
columns=tickers,
index=pd.date_range(start=’2019-01-01’, periods=n_days, freq=’B’) # B=工作日
)

计算日对数收益率(剔除首行缺失值)

daily_returns = np.log(prices / prices.shift(1)).dropna()

====================== 2. 均值方差模型核心逻辑 ======================

年化预期收益率(日均值 * 252)

mu = daily_returns.mean() * 252

年化协方差矩阵(日协方差 * 252)

cov = daily_returns.cov() * 252

无风险利率(国内1年期国债收益率,取2%)

risk_free_rate = 0.02

定义组合收益/风险/夏普比率计算函数

def portfolio_performance(weights, mu, cov):
port_return = np.sum(mu * weights) # 组合收益
port_vol = np.sqrt(np.dot(weights.T, np.dot(cov, weights))) # 组合波动率
sharpe_ratio = (port_return - risk_free_rate) / port_vol # 夏普比率
return port_return, port_vol, sharpe_ratio

优化目标1:最小化组合波动率(用于有效前沿/最小方差组合)

def minimize_volatility(weights, mu, cov):
return portfolio_performance(weights, mu, cov)[1]

优化目标2:最大化夏普比率(等价于最小化负夏普比率)

def maximize_sharpe(weights, mu, cov):
return -portfolio_performance(weights, mu, cov)[2]

约束条件:权重和为1;边界:权重∈[0,1](不允许做空)

constraints = ({‘type’: ‘eq’, ‘fun’: lambda x: np.sum(x) - 1})
bounds = tuple((0, 1) for _ in range(len(tickers)))
initial_guess = np.array([1/len(tickers)] * len(tickers)) # 等权初始值

2.1 最小方差组合(风险最低)

min_vol_result = sco.minimize(
minimize_volatility, initial_guess,
args=(mu, cov), method=’SLSQP’,
bounds=bounds, constraints=constraints
)
min_vol_weights = min_vol_result[‘x’]
min_vol_return, min_vol_vol, _ = portfolio_performance(min_vol_weights, mu, cov)

2.2 最大夏普比率组合(风险调整收益最高)

max_sharpe_result = sco.minimize(
maximize_sharpe, initial_guess,
args=(mu, cov), method=’SLSQP’,
bounds=bounds, constraints=constraints
)
max_sharpe_weights = max_sharpe_result[‘x’]
max_sharpe_return, max_sharpe_vol, _ = portfolio_performance(max_sharpe_weights, mu, cov)

2.3 生成有效前沿(遍历不同目标收益,求解最小风险)

target_returns = np.linspace(min_vol_return, mu.max(), 50)
target_vols = []
for target_ret in target_returns:
# 新增约束:组合收益=目标收益
constraints_target = (
{‘type’: ‘eq’, ‘fun’: lambda x: np.sum(x) - 1},
{‘type’: ‘eq’, ‘fun’: lambda x: portfolio_performance(x, mu, cov)[0] - target_ret}
)
# 求解该收益下的最小波动率
result = sco.minimize(
minimize_volatility, initial_guess,
args=(mu, cov), method=’SLSQP’,
bounds=bounds, constraints=constraints_target
)
target_vols.append(result[‘fun’])
target_vols = np.array(target_vols)

====================== 3. 可视化有效前沿 ======================

plt.rcParams[‘font.sans-serif’] = [‘SimHei’] # 解决中文显示问题
plt.rcParams[‘axes.unicode_minus’] = False # 解决负号显示问题

plt.figure(figsize=(10, 6))

绘制有效前沿曲线

plt.plot(target_vols, target_returns, ‘b-‘, linewidth=2, label=’有效前沿’)

标注最小方差组合(红色)

plt.scatter(min_vol_vol, min_vol_return, c=’red’, s=120, label=’最小方差组合’)

标注最大夏普比率组合(绿色)

plt.scatter(max_sharpe_vol, max_sharpe_return, c=’green’, s=120, label=’最大夏普比率组合’)

标注单个资产(橙色)

for i, ticker in enumerate(tickers):
asset_vol = np.sqrt(cov.iloc[i, i]) # 单资产波动率
asset_ret = mu[i] # 单资产收益率
plt.scatter(asset_vol, asset_ret, c=’orange’, s=90, label=ticker_names[i] if i==0 else “”)
plt.text(asset_vol + 0.005, asset_ret, ticker_names[i], fontsize=9)

图表样式设置

plt.xlabel(‘年化波动率(风险)’, fontsize=11)
plt.ylabel(‘年化收益率’, fontsize=11)
plt.title(‘均值方差模型 - A股有效前沿(模拟数据)’, fontsize=12)
plt.legend(loc=’upper left’, fontsize=9)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

====================== 4. 输出结果 ======================

print(“=”*40)
print(“均值方差模型计算结果(A股模拟数据)”)
print(“=”*40)

print(“\n【最小方差组合】(风险最低)”)
print(“-“*30)
for ticker, weight in zip(tickers, min_vol_weights):
print(f”{name_map[ticker]} ({ticker}): {weight:.2%}”)
print(f”组合年化收益:{min_vol_return:.2%}”)
print(f”组合年化波动率:{min_vol_vol:.2%}”)

print(“\n【最大夏普比率组合】(风险调整收益最高)”)
print(“-“*30)
for ticker, weight in zip(tickers, max_sharpe_weights):
print(f”{name_map[ticker]} ({ticker}): {weight:.2%}”)
print(f”组合年化收益:{max_sharpe_return:.2%}”)
print(f”组合年化波动率:{max_sharpe_vol:.2%}”)
print(f”组合夏普比率:{(max_sharpe_return - risk_free_rate)/max_sharpe_vol:.2f}”)
```python