Python中基于scipy.optimize的CVaR投资组合优化报错求助
解决CVaR投资组合优化的代码错误与实现调整
核心问题分析
你的代码出现AttributeError: 'tuple' object has no attribute 'get'是因为约束条件结构错误,同时优化变量的定义完全不符合CVaR优化的数学模型——CVaR优化需要同时求解资产权重、VaR值(var)和每个场景的偏差(var_dev),而非仅把权重作为变量。
具体修正步骤
1. 修正约束条件结构
constraints_1末尾多了一个逗号,导致它变成单元素元组,scipy要求约束是字典组成的列表/元组,不能嵌套元组。- 所有约束需合并为一个统一序列,避免拆分成多个变量再嵌套。
2. 重新定义优化变量
CVaR优化的变量包含三部分:
weights:n个资产权重var:VaR值(单个标量)var_dev:S个场景的偏差(每个场景对应一个非负变量)
因此初始猜测x0的长度为n + 1 + S。
3. 调整目标函数与约束函数
目标函数和约束函数的输入是整个优化变量向量x,需从x中拆分出weights、var、var_dev:
- 前n个元素为weights
- 第n+1个元素为var
- 最后S个元素为var_dev
4. 优化var_dev的约束
var_dev的非负约束直接通过bounds定义,比循环生成S个约束更高效。
修正后的完整代码
import numpy as np from scipy.optimize import minimize def minimize_cvar(sim_corr_rets, er, target_return, alpha=0.01): """ Returns the optimal weights (n x 1) that achieve the target return and minimize cvar, based on a set of simulated correlated returns (n x S) and an array of expected returns (er)=(n x 1) """ n = er.shape[0] # 资产数量 S = sim_corr_rets.shape[1] # 模拟场景数量 # 构建初始猜测:[资产权重(n个), VaR值(1个), 场景偏差(S个)] init_weights = np.repeat(1/n, n) init_var = 0.0 init_var_dev = np.zeros(S) init_guess = np.concatenate([init_weights, [init_var], init_var_dev]) # 定义变量边界:权重0-1,VaR无边界,场景偏差非负 bounds = ((0.0, 1.0),) * n + ((-np.inf, np.inf),) + ((0.0, np.inf),) * S def cvar(x): weights = x[:n] var = x[n] var_dev = x[n+1:] return var + np.sum(var_dev) / (S * (1 - alpha)) # 约束1:资产权重和为1 constraint_sum_weights = {'type': 'eq', 'fun': lambda x: np.sum(x[:n]) - 1} # 约束2:组合收益达到目标值 constraint_target_return = {'type': 'eq', 'fun': lambda x: target_return - x[:n].T @ er} # 约束3:每个场景的偏差满足 var_dev[i] >= -(weights@returns_i - var) constraints_scenarios = [] for i in range(S): def scenario_constraint(x, i=i): weights = x[:n] var = x[n] var_dev = x[n+1:] return var_dev[i] + var - weights.T @ sim_corr_rets.iloc[:, i] constraints_scenarios.append({'type': 'ineq', 'fun': scenario_constraint}) # 合并所有约束 constraints = [constraint_sum_weights, constraint_target_return] + constraints_scenarios results = minimize(cvar, init_guess, method='SLSQP', options={'disp': True}, # 开启显示便于调试优化过程 constraints=constraints, bounds=bounds) # 仅返回资产权重部分 return results.x[:n]
关键说明
- var_dev的角色:必须作为优化变量,它是CVaR模型中衡量每个场景损失超过VaR的核心部分,非负约束确保它仅捕捉损失超VaR的情况。
- 约束效率:var_dev的非负约束通过
bounds直接定义,比循环生成S个不等式约束更高效。 - 调试建议:开启
disp=True可查看优化过程输出,帮助判断是否收敛。
内容的提问来源于stack exchange,提问作者Chris23
相关产品推荐
相关产品推荐

