🧱 约束优化:把投资直觉变成数学边界

组合优化的五类约束目录(预算边界/行业风格/换手/流动性/组合级风险)、约束参数的设定算例、影子成本要记账、cvxpy 工程落地——优化器的驯化手册。

未经约束的优化器是危险的(见MPT 的两大顽疾 ),而约束正是驯化它的缰绳。本篇把组合优化中最常用的约束整理成目录——每一类都给出数学形式、金融含义、参数怎么定,并讨论约束的隐性代价。

约束目录

① 预算与边界(必选)

1
2
Σwᵢ = 1                预算约束(满仓;含现金时 =1 即可)
lᵢ ≤ wᵢ ≤ uᵢ          单票上下限(l=0 禁做空;u 常取 3%–8%)

单票上限的依据:个股黑天鹅的容忍度——上限 5% 意味着单票腰斩对组合伤害 2.5%。流动性修正:小票的上限应更低——uᵢ = min(5%, k × ADVᵢ × 清仓天数)(保证 3 日可清仓,见流动性 )。算例:某票 ADV 8000 万,按 10% 参与率 × 3 日 = 2400 万可承接;净值 5 亿时对应权重上限 4.8%——流动性给的约束比风控给的更紧。

② 行业与风格暴露(指增必选)

1
2
|Σ_{i∈行业j} wᵢ − 基准j| ≤ b_j        行业偏离上限
|wᵀF − 基准ᵀF| ≤ c_k                  风格因子暴露上限(F 为风格暴露矩阵)

参数设定不是拍脑袋而是风险预算的翻译:风格暴露上限 ≈ 跟踪误差预算的分解——TE 5% 的指增,单风格暴露常限 |z| ≤ 0.5,行业偏离 ≤ ±3%。反推方法:用风险模型算每个约束顶格时的 TE 贡献,让「所有约束同时顶格」的 TE 恰好等于预算(见组合风险 )。主动暴露 = 主动押注:每次放开约束都要能回答「这个押注的 alpha 来源是什么、由谁批准」。

③ 换手约束(成本卫士)

1
2
|w − w₀|₁ ≤ c         L1 换手预算(买卖总额)
|w − w₀|ᵢ ≤ δᵢ        单票调仓幅度(缓冲带)

参数:月频多因子的合理换手预算约 20%–60%/期(L1 双边口径);更精细的做法是把交易成本直接写进目标函数:max wᵀμ − (λ/2)wᵀΣw − Σ cᵢ|wᵢ − w₀ᵢ|——让优化器自己权衡「调仓收益 vs 成本」,这是约束法的连续版,通常更优。缓冲带(如「跌出 Top 50 才卖 Top 30 的持仓」)是无优化器场景的等效品,能把换手砍半而 IC 损失极小(见横截面策略 )。

④ 流动性与可交易性(A 股必选)

1
2
wᵢ × 净值 ≤ α × ADVᵢ × 净值          参与率约束(α≈5%–10%)
持仓 ∈ 可交易集合(非停牌、非涨停买入、非ST...)   禁交易名单

这类约束的价值在回测里体现为诚实:不加参与率约束的回测会给你一个实盘买不进的小票权重。禁交易名单是动态集合(每日更新停牌/涨跌停状态),引擎在撮合层强制执行(见回测框架设计 )。

⑤ 组合级风险约束(风险预算的落点)

1
2
3
4
wᵀΣw ≤ σ²_max            波动率上限
wᵀΣ_f w ≤ 预算            因子风险预算
CVaR(w) ≤ L_max           尾部风险约束(CVaR 优化,可线性化求解)
RC_i 均衡                 风险贡献约束(风险平价的推广,见[风险平价](/quant/risk-parity/))

约束的隐性代价:影子成本要记账

每条约束都在放弃一点理论最优——拉格朗日乘子(影子价格)告诉你放弃了多少(见优化 ):行业约束的乘子 = 该约束每收紧 1% 损失多少期望收益。实务用法:

  • 乘子常年接近零的约束 → 过度保守,可放宽(释放被无谓束缚的 alpha);
  • 乘子巨大且波动 → 约束在深度绑定组合,检查是约束太紧还是信号有问题(信号若与约束激烈冲突,常常是信号本身带了不想要的风险暴露)。

季度评审一次「乘子报表」,是约束体系保持健康的最简单手段。

工程实现

Python 栈的务实选择:cvxpy(凸优化建模 DSL,接口直觉、solver 可换 ECOS/OSQP/Clarabel)是学术与中小机构标配。一个最小可用的均值-方差 + 约束样例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import cvxpy as cp

w = cp.Variable(n)
obj = cp.Maximize(mu @ w - lam * cp.quad_form(w, Sigma)
                  - cost * cp.norm1(w - w_prev))     # 含换手成本
cons = [cp.sum(w) == 1, w >= 0, w <= 0.05,
        industry_dev(w) <= 0.03,                     # 行业偏离(示意)
        cp.norm1(w - w_prev) <= 0.4]                 # 换手预算
prob = cp.Problem(obj, cons)
prob.solve(solver="CLARABEL")

线性/二次约束的组合优化是标准 QP,毫秒级可解;CVaR 约束可线性化;带整数约束(手数取整、最小手数)会失去凸性——工程上常用「连续解 + 后处理取整 + 再校验约束」的近似路线。开源参考实现见 PyPortfolioOpt/riskfolio-lib(见框架概览 )。

一条元原则收尾:约束的清单其实是你投资哲学的形式化——「不押单一行业」「不做高换手」「要能随时清仓」这些价值观,写成约束才不依赖盘中的意志力。约束不是 alpha 的敌人,而是让 alpha 可长期兑现的结构。

关联阅读:约束赖以工作的数学微积分与优化 ;暴露的监控闭环组合风险 ;换手成本的科学交易成本分解 。