在CVXPY中定义软约束:投资组合优化场景的技术问询
在CVXPY中实现投资组合优化的软约束方案
针对你的投资组合优化场景,我来分享几种实用的软约束实现思路,直接结合你的现有代码来落地:
核心思路:将硬约束转化为"约束+惩罚"的形式
软约束的本质是允许约束被轻微违反,但违反的部分会作为惩罚项加入目标函数,让优化器在收益、风险和约束违反代价之间自动权衡。惩罚系数越大,约束越接近硬约束;系数越小,约束越容易被突破。
方案1:使用CVXPY内置函数快速实现软约束
我们可以直接利用pos()(取正值)、abs()等凸函数,把约束的违反量转化为惩罚项,加到原目标函数中。以下是修改后的完整代码:
from cvxpy import * import numpy as np np.random.seed(1) n = 10 Sigma = np.random.randn(n, n) Sigma = Sigma.T.dot(Sigma) # 构造正定协方差矩阵 orig_weight = np.array([0.15,0.25,0.15,0.05,0.20,0,0.1,0,0.1,0]) w = Variable(n) mu = np.abs(np.random.randn(n, 1)) # 随机生成预期收益 # 原目标:最大化收益 - 风险惩罚 lambda_ = 5 # 风险厌恶系数 ret = mu.T @ w risk = quad_form(w, Sigma) # ---------------------- 软约束相关定义 ---------------------- # 1. 资金全投约束:sum(w) == 1 → 允许偏差,惩罚偏差的绝对值 penalty_total = 10 # 总权重偏差的惩罚系数,越大越接近硬约束 dev_total = sum(w) - 1 penalty_total_term = penalty_total * abs(dev_total) # 2. 无卖空约束:w >= 0 → 允许负权重,但惩罚负的部分 penalty_short = 20 # 卖空惩罚系数,越大越禁止卖空 penalty_short_term = penalty_short * sum(pos(-w)) # pos(-w)仅当w<0时取正值 # 3. 权重偏离约束:sum(abs(w-orig_weight)) <= 0.75 → 允许超过,惩罚超额部分 penalty_dev = 15 # 偏离原组合的惩罚系数 dev_deviation = sum(abs(w - orig_weight)) - 0.75 penalty_dev_term = penalty_dev * pos(dev_deviation) # pos仅当超额时取正值 # 最终目标:最大化收益 - 风险惩罚 - 所有软约束惩罚 objective = Maximize(ret - lambda_ * risk - penalty_total_term - penalty_short_term - penalty_dev_term) # 这里可以保留一些必须严格满足的硬约束(如果需要),比如完全禁止极端权重 constraints = [] # 若不需要硬约束可留空,也可以加w <= 0.5这类硬限制 # 求解 prob = Problem(objective, constraints) prob.solve() print("优化后的权重:", w.value.round(4)) print("总权重偏差:", (sum(w.value)-1).round(4)) print("卖空权重总和:", np.sum(np.minimum(w.value, 0)).round(4)) print("偏离原组合的L1范数:", np.sum(np.abs(w.value - orig_weight)).round(4))
方案2:引入辅助变量实现更灵活的软约束
如果需要对不同资产的约束违反设置差异化惩罚(比如某些资产卖空惩罚更重),可以引入辅助变量来拆解约束:
# 以无卖空软约束为例,给每个资产设置不同的卖空惩罚系数 penalty_short_per_asset = np.array([20, 30, 15, 25, 20, 10, 18, 22, 19, 21]) z = Variable(n) # z[i]表示第i个资产允许的卖空额度 constraints = [w >= -z, z >= 0] # w可以负,但不能小于-z penalty_short_term = sum(penalty_short_per_asset * z) # 每个资产的卖空惩罚单独计算
这种方式的优势是可以精细化控制每个约束的惩罚力度,适合对特定资产有特殊要求的场景。
参数调整技巧
- 惩罚系数的大小需要根据业务场景调整:比如如果希望总权重尽可能接近1,就把
penalty_total设大;如果几乎不能接受卖空,就把penalty_short调到很高。 - 可以通过交叉验证或者网格搜索来找到最优的惩罚系数组合,平衡收益、风险和约束合规性。
内容的提问来源于stack exchange,提问作者ThatQuantDude




