为投资组合优化函数的wi添加求和为0的约束求助
你需要让优化过程中生成的wi满足每行和为0的约束(即每个时期的资产权重总和为0),这里有两种可行的方案,分别适配不同的需求:
方案一:直接调整wi满足约束(无需修改优化方法)
这种方法会在计算出原始wi后,自动调整每个时期的权重向量,使其总和为0,不需要约束优化参数x,可以继续使用BFGS方法。
修改后的目标函数pps如下:
pps = function(x, wb, nt, ret, m12, mktcap, btm, rr){ # 计算原始权重 wi = wb + nt * (x[1]*m12 + x[2]*mktcap + x[3]*btm) # 调整权重,确保每行(每个时期)的权重和为0 asset_count = ncol(wi) row_total = rowSums(wi) wi_neutral = wi - row_total / asset_count # 计算加权收益并返回目标值 wret = rowSums(wi_neutral * ret, na.rm = TRUE) ut = ((1 + wret)^(1 - rr))/(1 - rr) u = -mean(ut) return(u) }
你的循环代码不需要做任何修改,优化逻辑还是针对参数x,只是目标函数内部自动处理了wi的约束。
方案二:使用带约束的优化方法(constrOptim)
如果希望严格通过约束参数x来保证wi的和为0(而非事后调整),可以将约束转化为线性约束,使用constrOptim替代optim。
步骤1:推导线性约束
约束条件为每个时期的sum(wi[t, ]) = 0,展开后得到:sum(wb[t, ]) + nt*(x1*sum(m12[t, ]) + x2*sum(mktcap[t, ]) + x3*sum(btm[t, ])) = 0
整理为线性约束形式:A %*% x = b,其中:
A是每行对应c(sum(m12[t, ]), sum(mktcap[t, ]), sum(btm[t, ]))的矩阵b是每个元素为-sum(wb[t, ])/nt的向量
步骤2:修改循环代码
res_save = matrix(NA, 330, 3) weights = matrix(NA, 330, ncol(ret)) for(i in 1:330){ # 提取当前窗口的数据 window_ret = ret[1:(48 + i), ] window_m12 = m12[1:(48 + i), ] window_mktcap = mktcap[1:(48 + i), ] window_btm = btm[1:(48 + i), ] window_wb = wb[1:(48 + i), ] # 构造约束矩阵A和向量b A = cbind(rowSums(window_m12), rowSums(window_mktcap), rowSums(window_btm)) b = -rowSums(window_wb)/nt # 使用constrOptim进行带约束优化(等式约束需要同时设置upper和lower) opt = constrOptim( par = c(0, 0, 0), f = pps, grad = NULL, # 让函数自动计算梯度 ui = rbind(A, -A), ci = c(b, -b), wb = window_wb, nt = nt, ret = window_ret, m12 = window_m12, mktcap = window_mktcap, btm = window_btm, rr = rr ) res = opt$par w = wb[i + 49, ] + nt*(res[1]*m12[i + 49, ] + res[2]*mktcap[i + 49, ] + res[3]*btm[i + 49, ]) # 验证权重和是否为0(可选) # stopifnot(abs(sum(w)) < 1e-6) weights[i, ] = w res_save[i, ] = res }
注意事项:
- 这种方法要求约束是可行的(即存在
x满足所有时期的约束),如果约束之间线性无关且数量超过3,会导致无解,此时建议使用方案一。 constrOptim的速度会比BFGS慢一些,因为需要处理约束。
两种方案中,方案一更简单且鲁棒,适合大多数场景;方案二更严格,适合需要直接通过参数约束来保证wi性质的场景。
内容的提问来源于stack exchange,提问作者HakFar
相关产品推荐
相关产品推荐

