风险预算数学原理:等风险贡献模型、凸优化问题、KKT条件、拉格朗日乘子法
各位同学,今天我们进入风险预算最核心的数学部分。说实话,这部分内容我当年自学时也绕了不少弯路。等风险贡献模型、凸优化、KKT条件、拉格朗日乘子法——这几个概念串起来,就是风险预算的数学骨架。我个人习惯把数学原理讲透,因为只有理解了底层逻辑,你写代码时才不会心虚。
1. 等风险贡献模型:直觉与定义
先问大家一个问题:你手上有两个资产,一个波动大,一个波动小。怎么分配资金才算「公平」?
传统做法是按市值等权,或者按风险平价(Risk Parity)让每个资产对组合的风险贡献相等。但风险预算更灵活——你可以指定每个资产承担的风险比例,比如资产A承担40%的风险,资产B承担60%。
等风险贡献(Equal Risk Contribution, ERC)是风险预算的一个特例。它要求每个资产对组合总风险的贡献完全相等。数学上,组合风险通常用方差表示:
σ²_p = w^T Σ w
其中w是权重向量,Σ是协方差矩阵。第i个资产的边际风险贡献(Marginal Risk Contribution, MRC)是:
MRC_i = ∂σ_p / ∂w_i = (Σ w)_i / σ_p
那么第i个资产的总风险贡献(Total Risk Contribution, TRC)就是:
TRC_i = w_i × MRC_i = w_i × (Σ w)_i / σ_p
等风险贡献模型要求所有资产的TRC相等:
TRC_1 = TRC_2 = ... = TRC_n
嗯,这里要注意:TRC之和等于组合总风险σ_p。所以每个资产的TRC都等于σ_p / n。
核心要点:等风险贡献不是等权重,也不是等波动率。它追求的是每个资产对组合风险的「贡献度」相同。我在项目中遇到过不少新手,以为等风险贡献就是让每个资产的波动率一样,结果算出来的权重完全不对。
2. 凸优化问题:为什么是凸的?
等风险贡献模型本质上是一个优化问题。我们要找一组权重w,使得所有资产的TRC尽可能相等。这可以写成:
minimize Σ (TRC_i - TRC_j)²
subject to Σ w_i = 1, w_i ≥ 0
或者更常见的写法是:
minimize Σ (w_i × (Σ w)_i - w_j × (Σ w)_j)²
subject to 1^T w = 1, w ≥ 0
为什么说这是个凸优化问题?因为目标函数是二次型,约束是线性的。协方差矩阵Σ是半正定的,所以整个问题是个凸二次规划。凸优化有个好处——局部最优就是全局最优。你想想看,做投资最怕什么?最怕找到的「最优解」其实是个局部陷阱。
我建议在实际操作中,直接用凸优化求解器,比如cvxopt或scipy.optimize。别自己手写梯度下降,除非你想练手。
避坑指南:我曾经在项目中直接用scipy.optimize.minimize,没指定method='SLSQP',结果默认用了BFGS,收敛到非可行解。后来我养成了习惯——凸优化问题一定要用支持约束的求解器,并且检查KKT条件是否满足。
3. KKT条件:最优解的必要条件
KKT条件(Karush-Kuhn-Tucker条件)是判断一个点是否为最优解的必要条件。对于凸优化问题,它也是充分条件。说白了,KKT条件就是带约束的拉格朗日乘子法。
对于等风险贡献问题,我们引入拉格朗日函数:
L(w, λ, μ) = f(w) + λ(1^T w - 1) - Σ μ_i w_i
其中f(w)是目标函数,λ是等式约束的乘子,μ_i是不等式约束的乘子(w_i ≥ 0)。
KKT条件包括:
- 稳定性条件:∇f(w) + λ·1 - μ = 0
- 原始可行性:1^T w = 1, w ≥ 0
- 对偶可行性:μ_i ≥ 0
- 互补松弛:μ_i × w_i = 0
互补松弛条件很有意思——它告诉我们,如果某个资产权重w_i > 0,那么对应的μ_i必须为0。换句话说,只有权重为0的资产才可能有非零的乘子。这在实际中意味着:如果某个资产被排除在外(权重为0),那一定是因为它「不划算」。
个人经验:我记得有一次做多资产风险预算,发现某个债券的权重总是被优化到0。检查KKT条件后发现,它的边际风险贡献太高,导致拉格朗日乘子μ_i > 0。说白了,这个资产的风险收益特征太差,被优化器「淘汰」了。
4. 拉格朗日乘子法:从等式约束到不等式约束
拉格朗日乘子法大家应该熟悉。对于等式约束问题:
minimize f(w)
subject to h(w) = 0
我们构造拉格朗日函数L(w, λ) = f(w) + λ h(w),然后令梯度为0求解。
对于不等式约束,比如w_i ≥ 0,我们需要引入额外的乘子μ_i,并且要求μ_i ≥ 0和μ_i w_i = 0。这就是KKT条件的由来。
在实际求解中,我们通常用数值方法。下面是一个简单的Python实现,用scipy求解等风险贡献模型:
import numpy as np
from scipy.optimize import minimize
def risk_contribution(w, cov):
"""计算每个资产的风险贡献"""
port_var = w @ cov @ w
mrc = cov @ w / np.sqrt(port_var)
trc = w * mrc
return trc
def objective(w, cov, n):
"""目标函数:各资产风险贡献的方差最小化"""
trc = risk_contribution(w, cov)
return np.var(trc) * n # 乘以n是为了数值稳定性
def equal_risk_contribution(cov):
n = cov.shape[0]
# 初始权重:等权重
w0 = np.ones(n) / n
constraints = ({'type': 'eq', 'fun': lambda w: np.sum(w) - 1})
bounds = [(0, 1) for _ in range(n)]
result = minimize(objective, w0, args=(cov, n),
method='SLSQP',
constraints=constraints,
bounds=bounds)
return result.x
# 示例:3个资产
cov = np.array([[0.1, 0.02, 0.01],
[0.02, 0.2, 0.03],
[0.01, 0.03, 0.3]])
weights = equal_risk_contribution(cov)
print("等风险贡献权重:", weights)
print("各资产风险贡献:", risk_contribution(weights, cov))
运行这段代码,你会发现三个资产的风险贡献几乎相等。这就是等风险贡献模型的魅力——它不关心你投了多少钱,只关心你承担了多少风险。
注意事项:协方差矩阵的估计质量直接影响结果。我见过有人直接用历史数据算协方差,结果因为样本量不足,矩阵是奇异的。建议至少用60个月的数据,或者使用收缩估计(Shrinkage Estimator)。
5. 知识体系结构图
下面我用一张SVG图来总结本章的知识体系。这张图展示了从风险预算到等风险贡献模型,再到凸优化和KKT条件的逻辑链条。
这张图把本章的核心逻辑串起来了。从风险预算出发,我们定义了等风险贡献模型,然后把它转化为凸优化问题,最后用KKT条件和拉格朗日乘子法来求解。每一步都有明确的数学支撑。
我的建议:如果你刚开始接触这些数学概念,别急着啃教材。先跑通上面的代码,看看权重和风险贡献是怎么算出来的。有了感性认识,再回头理解KKT条件,会轻松很多。
好了,这一章的内容就到这里。数学原理是枯燥的,但它是量化投资的基石。你想想看,如果没有这些数学工具,我们凭什么说某个投资组合是「最优」的?凭感觉吗?不,凭数学。
公众号:蓝海数据掘金营,微信deep3321