CVaR投资组合优化:Rglpk_solve_LP与LSopt结果差异及修正
问题根源
你的Rglpk_solve_LP实现的是单纯下侧CVaR最小化模型(仅控制风险,未纳入收益目标),而LSopt的目标是最大化你自定义的上侧条件收益(取收益高于95%分位数的均值)。当资产有正预期收益时,LP模型会优先选择低波动资产(牺牲收益换低风险),导致你自定义的CVaR值更负;而LSopt会偏向高收益资产,该值更高。当收益全为0时,两者的目标无收益差异,结果自然一致。
以下是两种修改LP模型的方式,使其纳入组合平均收益的考量:
方式一:最小化CVaR,同时约束组合平均收益不低于目标值
这种方式在控制风险(CVaR)的前提下,保证组合收益不低于设定目标。
修改后的完整代码:
library(neighbours) library(NMOF) library(Rglpk) # Return / vol ret <- c(0.039, 0.036, 0.092, 0.139, 0.094) vol <- c(0.125, 0.074, 0.101, 0.204, 0.167) ns <- 1000 na <- length(ret) R <- randomReturns(na, ns, mean=ret, sd = vol) b <- 0.95 ## 设定目标收益(这里取资产预期收益的均值,可自行调整) target_ret <- mean(ret) ## 构建LP模型 f.obj <- c(alpha = 1, x = rep(0, na), u = 1/rep((1 - b)*ns, ns)) # 基础约束:权重和为1,CVaR定义约束 C <- cbind(1, R, diag(nrow(R))) C <- rbind(c(alpha = 0, x = rep(1, na), u = rep(0, nrow(R))), C) const.dir <- c("==", rep(">=", nrow(C) - 1)) const.rhs <- c(1, rep(0, nrow(C) - 1)) # 添加平均收益约束:组合平均收益 >= target_ret C <- rbind(C, c(alpha=0, x=colMeans(R), u=rep(0, ns))) const.dir <- c(const.dir, ">=") const.rhs <- c(const.rhs, target_ret) sol.lp <- Rglpk_solve_LP(f.obj, C, const.dir, rhs = const.rhs, control = list(verbose = TRUE, presolve = TRUE)) lp.w <- sol.lp$solution[2:(1+na)] ## LSopt部分保持不变 CVaR <- function(w, R, b) { Rw <- R %*% w mean(Rw[Rw >= quantile(Rw, b)]) } nb <- neighbourfun(0, 1, type = "numeric", stepsize = 0.05) sol.ls <- LSopt(CVaR, list(x0 = rep(1/na, na), neighbour = nb, nI = 1000), R = R, b = b) ls.w <- sol.ls$xbest # 比较结果 CVaR(lp.w, R, b) CVaR(ls.w, R, b)
方式二:同时权衡收益与CVaR(最大化收益-风险加权和)
这种方式将收益和CVaR整合到同一个目标函数中,通过调整风险厌恶系数lambda平衡两者权重:
lambda越大,模型越看重风险(CVaR);越小,越看重收益。
修改后的完整代码:
library(neighbours) library(NMOF) library(Rglpk) # Return / vol ret <- c(0.039, 0.036, 0.092, 0.139, 0.094) vol <- c(0.125, 0.074, 0.101, 0.204, 0.167) ns <- 1000 na <- length(ret) R <- randomReturns(na, ns, mean=ret, sd = vol) b <- 0.95 lambda <- 1 # 风险厌恶系数,可根据需求调整 ## 优化目标:最大化(组合平均收益 - lambda*CVaR) # 转化为线性规划的最小化目标:lambda*CVaR - 组合平均收益 mean_ret_coef <- colMeans(R) f.obj <- c(alpha = lambda, x = -mean_ret_coef, u = lambda/rep((1 - b)*ns, ns)) # 基础约束:权重和为1,CVaR定义约束 C <- cbind(1, R, diag(nrow(R))) C <- rbind(c(alpha = 0, x = rep(1, na), u = rep(0, nrow(R))), C) const.dir <- c("==", rep(">=", nrow(C) - 1)) const.rhs <- c(1, rep(0, nrow(C) - 1)) sol.lp <- Rglpk_solve_LP(f.obj, C, const.dir, rhs = const.rhs, control = list(verbose = TRUE, presolve = TRUE)) lp.w <- sol.lp$solution[2:(1+na)] ## LSopt部分保持不变 CVaR <- function(w, R, b) { Rw <- R %*% w mean(Rw[Rw >= quantile(Rw, b)]) } nb <- neighbourfun(0, 1, type = "numeric", stepsize = 0.05) sol.ls <- LSopt(CVaR, list(x0 = rep(1/na, na), neighbour = nb, nI = 1000), R = R, b = b) ls.w <- sol.ls$xbest # 比较结果 CVaR(lp.w, R, b) CVaR(ls.w, R, b)
补充说明
如果你的目标是让LP模型直接优化你自定义的CVaR(即最大化收益高于95%分位数的均值),由于该目标涉及分位数筛选(非连续线性关系),无法直接用线性规划实现。此时可以考虑将LSopt的目标改为最小化下侧CVaR,或者使用整数规划(但计算成本会显著上升)。
内容的提问来源于stack exchange,提问作者RL_student
相关产品推荐
相关产品推荐

