1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
| 代码如下: import pandas as pd import numpy as np import scipy.optimize as sco import matplotlib.pyplot as plt
np.random.seed(42)
tickers = ['600519.SH', '300750.SZ', '600036.SH', '002594.SZ', '000651.SZ'] ticker_names = ['贵州茅台', '宁德时代', '招商银行', '比亚迪', '格力电器'] name_map = dict(zip(tickers, ticker_names))
n_days = 252 * 5 base_prices = [1000, 200, 30, 150, 30] prices_data = [] for i in range(len(tickers)): 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)
prices = pd.DataFrame( np.array(prices_data).T, columns=tickers, index=pd.date_range(start='2019-01-01', periods=n_days, freq='B') )
daily_returns = np.log(prices / prices.shift(1)).dropna()
mu = daily_returns.mean() * 252
cov = daily_returns.cov() * 252
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
def minimize_volatility(weights, mu, cov): return portfolio_performance(weights, mu, cov)[1]
def maximize_sharpe(weights, mu, cov): return -portfolio_performance(weights, mu, cov)[2]
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))
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)
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)
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)
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()
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}")
|