You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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]

关键说明

  1. var_dev的角色:必须作为优化变量,它是CVaR模型中衡量每个场景损失超过VaR的核心部分,非负约束确保它仅捕捉损失超VaR的情况。
  2. 约束效率:var_dev的非负约束通过bounds直接定义,比循环生成S个不等式约束更高效。
  3. 调试建议:开启disp=True可查看优化过程输出,帮助判断是否收敛。

内容的提问来源于stack exchange,提问作者Chris23

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 13:35:16