Matlab中fmincon实现CVaR优化:波动率约束下权重无差异求助
排查CVaR优化中权重不变的问题
看起来你在CVaR优化的目标函数实现上踩了几个关键的坑,这大概率是导致权重始终不变的核心原因。我来一步步帮你拆解问题并给出修正方案:
一、目标函数的核心错误
你当前的目标函数存在三个致命问题,直接导致优化器无法感知权重变化对CVaR的影响:
Riskfun=@(w) min(quantile(w * Scen_Rets',1-beta)+((1/size(Scen_Rets,1))*(1/(1-beta)))*(max(-w * Scen_Rets'-quantile(w * Scen_Rets',1-beta),0)));
- 多余的
min()包裹:CVaR本身是一个标量值,不是需要求最小值的表达式,这个min()会把原本的CVaR计算结果(应该是标量)错误地当成向量取最小值,完全偏离了CVaR的定义。 - 符号逻辑错误:
max(-w * Scen_Rets'-quantile(...),0)的符号完全搞反了。我们要计算的是收益低于分位数的短缺部分,正确的表达式应该是max(quantile(...) - w*Scen_Rets', 0)——当组合收益w*Scen_Rets'低于分位数时,这个值为正,代表下行短缺。 - 缺少求和操作:
max()返回的是一个1×10000的向量,你直接和分位数相加,没有对这些短缺值求和取平均,导致目标函数输出的不是CVaR,而是一个无意义的向量最小值,对权重变化极不敏感。
二、修正后的CVaR目标函数
先给你一个符合标准定义的CVaR计算实现,你可以直接替换原目标函数:
% 先定义beta(比如95%置信水平,beta=0.05) beta = 0.05; % 修正后的目标函数(用嵌套函数更清晰) Riskfun = @(w) calculate_cvar(w, Scen_Rets, beta); % 独立的CVaR计算函数 function cvar_val = calculate_cvar(w, Scen_Rets, beta) % 计算组合在所有场景下的收益:Scen_Rets是10000×8,w是8×1,得到1×10000的收益向量 port_rets = w' * Scen_Rets; % 获取(1-beta)分位数(比如beta=0.05时,取5%分位数,代表最差的5%收益) q = quantile(port_rets, 1 - beta); % 筛选出低于分位数的收益样本 below_q = port_rets < q; % 计算CVaR:最差(1-beta)部分收益的平均值(标准下行CVaR定义) if any(below_q) cvar_val = mean(port_rets(below_q)); else % 极端情况:没有样本低于分位数,CVaR等于分位数 cvar_val = q; end end
如果喜欢更简洁的写法,也可以用单行匿名函数:
Riskfun = @(w) begin port_rets = w' * Scen_Rets; q = quantile(port_rets, 1 - beta); cvar_val = mean(port_rets(port_rets < q)); end;
三、下一步排查步骤
修正目标函数后,如果权重还是不变,你可以按以下顺序排查:
- 验证目标函数敏感性:手动输入两个完全不同的权重(比如全0向量和单资产权重1的向量),计算
Riskfun(w1)和Riskfun(w2),如果结果不同,说明目标函数已经正常工作;如果结果相同,检查Scen_Rets的维度是否正确(确保w'*Scen_Rets能正确生成组合收益)。 - 检查波动率约束逻辑:你用了1000个自助法协方差矩阵,要明确约束的是「所有矩阵对应的波动率都小于目标值」还是「平均波动率小于目标值」:
- 如果是前者,约束可能过于严格,导致只有唯一解(比如最小方差组合);
- 如果是后者,确认协方差矩阵的平均计算是否正确(比如
Sigma_avg = mean(Sigma_bootstrap, 3),假设Sigma_bootstrap是8×8×1000的数组)。
- 查看fmincon迭代过程:把优化选项的
Display设为'iter',观察迭代过程中目标函数值和权重的变化:
如果目标函数值始终不变,说明优化器认为当前点已经是最小值;如果目标函数变化但权重不变,检查约束是否把权重锁死了(比如上下限设置过于严格)。options = optimoptions('fmincon','Display','iter','Algorithm','sqp');
内容的提问来源于stack exchange,提问作者Maxx22
相关产品推荐
相关产品推荐

