使用Scipy进行投资组合优化:风险得分异常偏低问题排查
问题排查:有效前沿未贴合投资组合上边界
我用Python代码解决「指定风险水平下选择最优投资组合权重以优化收益」的问题,具体配置如下:
- 4类资产构建有效前沿:
- 3只股票:仅允许多头头寸
- 1只标普500 ETF:允许多头或最高200%杠杆的空头头寸
已提前计算方差-协方差矩阵及资产统计量,但运行代码绘制有效前沿后,发现红色有效前沿线未贴合有效投资组合的上边界,不符合金融理论预期,求排查问题。
代码实现
#GENERATING RANDOM WEIGHTS FOR QUESTION 2 np.random.seed(42) num_portfolios = 10000 weights = np.zeros((num_portfolios, 4)) for i in range(num_portfolios): # Step 1: Generate initial random weights for the first three stocks w = np.random.random(3) w = w / w.sum() * 2 # Normalize these weights to sum to a value between 0 and 2 # Step 2: Weight for the S&P500 ETF can be between -1 and 1 w_spx = np.random.uniform(-1, 1) # Step 3: Combine weights and adjust to ensure they sum to 1 total_investment = w.sum() total = total_investment + w_spx # This can range from 1 to 3 weights[i, :3] = w / total # Scale down the weights for the first three stocks weights[i, 3] = w_spx / total # Adjust the S&P500 ETF weight to maintain the sum of 1 # Check that the weights sum to 1 and the S&P500 weight is at maximum -1 assert np.allclose(weights.sum(axis=1), 1) and (weights[:, 3] >= -1).all() # Portfolios returns portfolio_returns2 = np.dot(weights, annual_returns) # Portfolios volatility portfolio_volatilities2 = np.sqrt(np.diag(np.dot(weights, np.dot(cov_mat, weights.T)))) portfolio_var2 = np.diag(np.dot(weights, np.dot(cov_mat, weights.T))) portfolios_2 = pd.DataFrame({'ret':portfolio_returns2, 'risk':portfolio_var2}) weights = pd.DataFrame(weights) weights.columns = ['w1', 'w2', 'w3', 'w4'] portfolios_q2 = portfolios_2.join(weights) portfolios_q2.head() #Again, to find the efficient frontier, we generate the "grids" over portfolio returns #we want to calculate the minimum variance for all the point on the grids of portfolio returns min_var_ret2 = portfolios_2.ret.iloc[portfolios_2.risk.idxmin()] eff_ret2 = np.arange(min_var_ret2, portfolios_2.ret.max(), (portfolios_2.ret.max()-min_var_ret2)/100) eff_risk2 = np.zeros(len(eff_ret2)) #then we can use the minimize function over each point on "eff_risk": f = lambda x: x@cov_mat@x.T x0 = np.array((0.25, 0.25, 0.25, 0.25)) #set the initial value of x, y, w, z for ind in range(len(eff_ret2)): #set the optimization problem constraints: cons = ({'type':'eq','fun':lambda x:x[0] + x[1] + x[2] + x[3] - 1}, #constraint:x+y+z+w = 1 {'type':'eq','fun':lambda x:x@asset_ret - eff_ret[ind]}, #constraint: set the return of the portfolio to be constant {'type': 'ineq', 'fun': lambda x: x[0]}, # Weights of first 3 stocks must be >= 0 {'type': 'ineq', 'fun': lambda x: x[1]}, {'type': 'ineq', 'fun': lambda x: x[2]}, {'type': 'ineq', 'fun': lambda x: 1 + x[3]}, # Weight of S&P500 index can be down to -1 {'type': 'ineq', 'fun': lambda x: 2 - x[0] - x[1] - x[2]} # Sum of weights of first 3 stocks <= 2 ) #find the minimum variance of the portfolio given certain return level res = minimize(f, x0, constraints=cons) eff_risk2[ind] = res.fun #print(eff_y) #now the eff_x and eff_y describe the risk and return combination on the efficient frontier. #let's plot the efficient frontier: plt.subplots(figsize=[10,10]) plt.xlabel('Risk', fontsize=18) plt.ylabel('Return', fontsize=16) plt.scatter(portfolios_q2['risk'], portfolios_q2['ret'],marker='o', s=10, alpha=0.3) plt.scatter(eff_risk2, eff_ret2, color='r', marker='x', linestyle='-', linewidth=1)
问题排查关键点
- 约束条件错误:
- 标普500 ETF的空头限制应为权重≤-2(对应200%杠杆空头),但当前约束
1 + x[3] ≥ 0仅允许权重≥-1,直接限制了空头杠杆上限,无法构建理论上的高收益组合。 - 前三只股票权重总和约束
2 - x[0]-x[1]-x[2] ≥0(总和≤2),结合权重总和为1的条件,当ETF为-2权重时,前三只股票权重总和应为3,当前约束限制了该情况,导致无法生成更高收益的组合。
- 标普500 ETF的空头限制应为权重≤-2(对应200%杠杆空头),但当前约束
- 有效前沿收益区间不足:
eff_ret2的上限取随机组合的最大收益,但随机组合未覆盖所有可行的极端头寸组合,导致有效前沿仅覆盖部分区间,无法贴合真实上边界。
- 优化初始值局限性:
- 固定初始值
x0 = (0.25,0.25,0.25,0.25)可能导致优化陷入局部最优,未找到全局最小方差的高收益组合,建议每次迭代根据目标收益调整初始值。
- 固定初始值
- 风险指标不一致:
- 随机组合的
risk列用方差,若绘图预期用波动率(标准差),会导致轴刻度错位,需统一风险指标(方差或标准差)。
- 随机组合的
内容的提问来源于stack exchange,提问作者Maurizio Marinaro
相关产品推荐
相关产品推荐

