风险模型回答一个核心问题:一个组合的风险到底来自哪里。表面上看,你持有了 500 只股票,风险似乎被分散了;但如果这 500 只都是小盘成长股,那么它们会同时下跌——真正的风险是「小盘成长」这一个因子,而不是 500 只个股。
工程上真正难的地方在于:风险模型要同时做到「稳定」和「敏感」。太敏感,协方差矩阵噪声大、优化器会追逐噪声;太稳定,又无法及时反映市场结构的变化。Barra 式的结构化模型之所以成为行业标准,就是因为它在这两者之间找到了一个可调的结构。
本文聚焦风险模型本身:怎么估计、怎么分解、怎么归因、怎么压力测试。从风险模型到最优权重的优化过程,在 组合优化与风险模型 中讨论,本文提供的是那一步的输入与解释框架。
目录
- 风险模型的定位与结构
- 因子暴露的估计
- 因子收益与因子协方差
- 特质风险的估计
- 组合风险的分解
- 风险归因:因子与选股
- 边际风险贡献与风险预算
- 压力测试与情景分析
- 与约束优化的衔接
1. 风险模型的定位与结构
风险模型的基本形式是把个股收益拆成「因子部分 + 特质部分」:
r = X·f + u
r:N 只股票的收益向量
X:N×K 的因子暴露矩阵
f:K 维因子收益
u:特质收益(假设互不相关)
在这个结构下,组合协方差变成:
Σ = X·Σf·Xᵀ + Δ
Σf:K×K 的因子协方差(K 通常 30~60)
Δ:N×N 的对角矩阵(特质风险)
这个结构的核心价值是降维。N = 5000 只股票,直接估计协方差需要 1250 万个参数;用 50 个因子,只需要 1275 个因子协方差参数 + 5000 个特质方差。参数的减少直接带来估计的稳定。
风险模型按因子来源分三类:
| 类型 | 因子来源 | 优点 | 缺点 |
|---|---|---|---|
| 基本面 | 行业 + 风格(Barra) | 可解释、稳定 | 需要维护暴露数据 |
| 统计 | PCA / 因子分析 | 无需外部数据 | 因子含义不明确 |
| 宏观 | 利率、汇率、商品 | 与宏观联动 | 解释力弱、频率低 |
基本面模型是主流,因为它的因子有明确的经济含义,能支持归因和约束。统计模型(PCA)在纯预测场景有优势,但无法回答「我为什么承担了这个风险」。
2. 因子暴露的估计
因子暴露矩阵 X 的构造分两类:
行业因子用哑变量,一只股票属于一个行业,对应位置为 1,其余为 0。A 股通常用申万一级(31 个)或中信一级(30 个)行业。
风格因子用标准化后的指标。Barra 的经典风格因子:
| 风格因子 | 主要指标 |
|---|---|
| Size | 对数总市值 |
| Beta | 对市场的历史回归斜率 |
| Momentum | 过去 12 个月(剔除最近 1 月)收益 |
| Value | 账面市值比、盈利收益率 |
| Volatility | 历史波动率、日收益标准差 |
| Liquidity | 换手率 |
| Growth | 营收/盈利增速 |
标准化必须用市值加权,而不是等权。因为组合的收益是按市值加权的,暴露的基准也应该一致:
def standardize_exposure(raw, mktcap):
# 市值加权均值与标准差
w = mktcap / mktcap.sum()
mean = (raw * w).sum()
std = np.sqrt((w * (raw - mean) ** 2).sum())
return (raw - mean) / std # 市值加权的 z-score
暴露的时效性很关键:市值每天变、财报每季度更新。基本面数据必须用公告日对齐,且要处理「报告期与公告日之间」的时间差,否则就是前视偏差。暴露数据的更新与存储可以参考 数据库 里时序数据的建模方式。
3. 因子收益与因子协方差
因子收益 f 无法直接观测,需要用横截面回归从个股收益反推。每个调仓期做一次回归:
import statsmodels.api as sm
def estimate_factor_returns(returns, X, weights=None):
# 每期横截面回归 r = X·f + u,加权最小二乘
Xw = sm.add_constant(X) # 加截距(可吸收市场均值)
model = sm.WLS(returns, Xw, weights=weights).fit()
return model.params, model.resid # 因子收益与特质收益
加权最小二乘(WLS) 比普通最小二乘更合适:用市值平方根或特异度倒数加权,降低小盘股噪声的影响。A 股的横截面回归尤其要注意极端值——涨跌停、停牌、次新股都会污染回归。
因子协方差 Σf 的估计有几个关键处理:
| 处理 | 目的 | 方法 |
|---|---|---|
| 指数加权 | 近期数据权重更高 | EWMA,半衰期 90~180 天 |
| 自相关调整 | 修正序列相关 | Newey-West |
| 波动率偏误调整 | 修正高波动期低估 | 波动率状态调整 |
| 特征值调整 | 修正系统性低估 | 特征值去偏 |
波动率偏误调整是 Barra 模型的一个精妙之处:波动率有均值回归特性,高波动期之后波动率往往下降,低波动期之后往往上升。如果直接用历史数据,会高估当前高波动期的未来波动、低估低波动期的未来波动。调整方法是把预测波动率向长期均值拉回一部分。
def volatility_regime_adjust(cov, current_vol, long_run_vol, beta=0.5):
# 把当前协方差按波动率偏离长期均值的程度做修正
scale = (long_run_vol / current_vol) ** beta
return cov * scale
4. 特质风险的估计
特质风险 Δ 是残差收益的方差。它看似简单(对残差求方差),但直接估计会系统性低估,因为:
- 样本期内恰好没有大波动的股票,方差被低估。
- 停牌、次新股的样本量不足。
- 残差的厚尾使样本方差低估尾部风险。
贝叶斯收缩是标准解法:把每只股票的特质方差向「同组股票的先验」收缩:
def shrink_specific_var(sample_var, prior_var, n_obs, prior_weight=50):
# 向先验收缩,样本量越少收缩越强
lam = prior_weight / (prior_weight + n_obs)
return lam * prior_var + (1 - lam) * sample_var
先验可以按「行业 × 市值组」计算:
小盘股的特质风险天然高于大盘股,先验应该分组而非全局
特质风险也要做自相关调整。残差收益往往有正的序列相关(动量、流动性),这会让「T 天的方差」比「单日方差的 T 倍」更大。Newey-West 调整能修正这个偏差。
5. 组合风险的分解
有了 Σ = XΣfXᵀ + Δ,组合风险可以精确分解成因子部分与特质部分:
def decompose_risk(w, X, Sigma_f, Delta):
total_var = w @ (X @ Sigma_f @ X.T + Delta) @ w
factor_var = w @ X @ Sigma_f @ X.T @ w # 因子贡献
specific_var = w @ Delta @ w # 特质贡献
return {
'total_vol': np.sqrt(total_var) * np.sqrt(252),
'factor_vol': np.sqrt(factor_var) * np.sqrt(252),
'specific_vol': np.sqrt(specific_var) * np.sqrt(252),
'factor_ratio': factor_var / total_var, # 因子风险占比
}
「因子风险占比」是最有诊断价值的指标。一个分散良好的主动组合,因子风险占比通常在 80%~95%——因为分散化消灭了特质风险,剩下的主要是因子暴露。如果因子占比低于 60%,说明组合的特质风险过高,可能是持仓太集中(个股集中)而不是因子暴露问题。
进一步的分解是按单个因子:
def factor_risk_contribution(w, X, Sigma_f):
expo = X.T @ w # 组合的因子暴露(K 维)
factor_var = expo @ Sigma_f @ expo
contrib = expo * (Sigma_f @ expo) / factor_var # 各因子的风险贡献,和为 1
return dict(zip(FACTOR_NAMES, contrib))
这个分解告诉你:组合的风险里,多少来自行业暴露、多少来自市值暴露、多少来自动量暴露。如果风险集中在某一个因子上,这个因子反转时组合就会巨亏。
6. 风险归因:因子与选股
风险分解是「事前」的(基于模型预测),归因是「事后」的(基于实际收益)。两者要分开看。
Brinson 归因把主动收益拆成三部分:
主动收益 = 配置效应 + 选股效应 + 交互效应
配置效应:行业权重的偏离 × 行业基准收益
选股效应:行业内的选股 × 行业权重
交互效应:配置与选股的交叉项
def brinson_attribution(w_p, w_b, r_p, r_b, sectors):
alloc = ((w_p - w_b) * r_b).sum() # 配置效应
select = (w_b * (r_p - r_b)).sum() # 选股效应
interact = ((w_p - w_b) * (r_p - r_b)).sum() # 交互效应
return {'allocation': alloc, 'selection': select, 'interaction': interact}
因子归因是更现代的视角:把收益拆成「因子暴露 × 因子收益 + 特质 alpha」:
def factor_attribution(portfolio_returns, exposures, factor_returns):
explained = (exposures * factor_returns).sum(axis=1) # 因子解释的收益
alpha = portfolio_returns - explained # 特质 alpha
return {
'factor_return': explained.sum(),
'alpha': alpha.sum(),
'alpha_ir': alpha.mean() / alpha.std() * np.sqrt(252), # alpha 的信息比率
}
如果 alpha 项接近 0 而因子项很高,说明你的策略只是在承担因子风险。2021 年初「抱团股」崩盘时大量量化产品巨亏,根因就是它们的收益主要来自「大盘成长」这一个因子暴露,而非真正的选股能力。
7. 边际风险贡献与风险预算
边际风险贡献(Marginal Risk Contribution, MRC) 是组合波动对某个持仓权重变化的敏感度:
MRC_i = ∂σ_p / ∂w_i = (Σw)_i / σ_p
风险贡献(RC)= w_i · MRC_i
组合波动 = Σ RC_i (欧拉分解,贡献之和等于总风险)
def risk_contributions(w, cov):
port_var = w @ cov @ w
port_vol = np.sqrt(port_var)
mrc = (cov @ w) / port_vol # 边际贡献
rc = w * mrc # 总贡献
return rc, rc / port_vol # 绝对贡献与占比
欧拉分解的关键性质是「贡献之和等于总风险」,这让「谁的贡献最大」成为一个有明确定义的问题。这个性质只在「波动率」这个风险度量下成立——用 VaR 或 CVaR 时,分解不再可加(除非假设正态)。
风险预算(risk budgeting) 就是给每个资产/因子分配一个风险贡献目标,然后求解满足这个目标的权重。它比「分配资金」更符合直觉:给低波动资产更多资金、高波动资产更少资金,让每份风险贡献相等。
from scipy.optimize import minimize
def risk_budget(cov, budget):
# budget: 各资产的目标风险贡献占比,和为 1
n = len(budget)
def objective(w):
rc = w * (cov @ w)
rc_frac = rc / rc.sum()
return ((rc_frac - budget) ** 2).sum()
cons = [{'type': 'eq', 'fun': lambda w: w.sum() - 1}]
res = minimize(objective, np.ones(n) / n, bounds=[(1e-6, 1)] * n, constraints=cons)
return res.x
风险预算与组合优化里的风险平价是同一族方法:风险平价是「预算相等」的特例,风险预算是它的推广。
8. 压力测试与情景分析
风险模型基于历史协方差,无法预测从未发生过的极端场景。压力测试弥补这个盲区。
| 方法 | 做法 | 优点 | 缺点 |
|---|---|---|---|
| 历史情景 | 用历史极端日的实际收益 | 真实、含相关性 | 只有发生过的 |
| 假设情景 | 手工设定因子冲击 | 灵活、可极端 | 相关性靠假设 |
| 因子冲击 | 对因子施加冲击后传导 | 结构化 | 依赖模型 |
| 反向压力 | 求解「什么情景会让我爆仓」 | 揭示脆弱点 | 计算复杂 |
假设情景是最实用的:直接对因子施加冲击,看组合损失:
def scenario_loss(w, X, factor_shock, specific_shock=0.0):
# factor_shock: K 维的因子冲击(如「小盘 -10%、成长 -8%」)
factor_pnl = (X @ factor_shock) @ w
specific_pnl = specific_shock * (w ** 2).sum() ** 0.5
return factor_pnl + specific_pnl
反向压力测试(reverse stress testing) 最有启发性:不问「这个情景下我会亏多少」,而问「什么情景会让我亏损超过 20%」。它把问题反过来,揭示组合最脆弱的暴露方向。
def reverse_stress(w, X, max_loss):
# 找到让组合损失达到 max_loss 的最小因子冲击方向
exposure = X.T @ w # 组合的因子暴露
# 最脆弱的方向 = 与暴露方向相反的冲击
direction = -exposure / np.linalg.norm(exposure)
magnitude = max_loss / np.linalg.norm(exposure)
return direction, magnitude
压力测试的结果应该定期审查并转化为约束。如果压力测试显示组合在「小盘股下跌 15%」时会亏损 12%,而你的风控上限是 10%,那么这个暴露就应该被限制。风控侧的限额设计在 交易风控与实时限额 中有详细讨论。
9. 与约束优化的衔接
风险模型是组合优化的输入,它同时提供目标函数(最小化 wᵀΣw)和约束(限制因子暴露):
import cvxpy as cp
def optimize_with_risk_model(mu, w, X, Sigma_f, Delta, max_factor_expo=0.05):
n, k = X.shape
cov = X @ Sigma_f @ X.T + Delta # 由风险模型构造协方差
objective = cp.Maximize(mu @ w - 5.0 / 2 * cp.quad_form(w, cov))
constraints = [
cp.sum(w) == 1,
w <= 0.03,
cp.abs(X.T @ w) <= max_factor_expo, # 限制因子暴露
]
prob = cp.Problem(objective, constraints)
prob.solve(solver=cp.OSQP)
return w.value
用结构化风险模型而非样本协方差做优化,本身就是一种正则化。样本协方差含大量噪声,优化器会利用这些噪声(在「假低相关」的股票对上下重注);因子模型把协方差约束在低维结构上,天然抑制了这种噪声利用。
风险模型还支持因子中性约束:强制 Xᵀw = 0(或接近 0),使组合对某些因子免疫。这是「指数增强」产品的标准做法——不做行业与风格暴露,只赚选股 alpha。
优化之外,风险模型还要服务于实时监控:组合上线后,需要持续计算实时的因子暴露与风险分解,与限额对比。这类监控指标的实时计算与告警,与 实盘运维与策略监控 中的健康度监控是同一套体系。
权衡取舍
| 维度 | 样本协方差 | 因子模型 | 收缩估计 |
|---|---|---|---|
| 参数数量 | N(N+1)/2 | K(K+1)/2 + N | N(N+1)/2 |
| 稳定性 | 差(N 大时) | 好 | 中 |
| 可解释性 | 无 | 强 | 无 |
| 支持归因 | 否 | 是 | 否 |
| 适用规模 | 小组合 | 大组合 | 中等 |
股票数越多,越应该用因子模型:N < 100 时样本协方差够用;N > 500 时因子模型是唯一可行的选择;中间区间可以用收缩估计过渡。这个选择不是偏好问题,而是参数估计的统计必要性。
因子数量的选择也有取舍:因子太少,模型解释力不足、特质风险过高;因子太多,因子协方差本身的估计噪声上升,且因子之间多重共线。30~60 个因子是实践中的合理区间,再多就需要更强的正则化。
常见坑清单
- 用等权标准化暴露:暴露基准应与组合加权一致,用市值加权。
- 基本面数据用报告期对齐:必须用公告日,否则前视偏差。
- 因子收益用 OLS 估计:小盘股噪声大,应使用 WLS。
- 特质方差直接估计:系统性低估,需贝叶斯收缩 + 自相关调整。
- 忽略波动率均值回归:高波动期高估未来风险,需向长期均值调整。
- 只看总风险不看分解:不知道风险来自哪个因子,无法针对性控制。
- 归因时 alpha 与因子不分开:把因子暴露的收益当成选股能力。
- 风险贡献用 VaR 分解:欧拉分解只对波动率可加,VaR 不可加。
- 只做历史压力测试:无法覆盖未发生过的极端场景。
- 压力测试结果不转化为约束:发现问题却不限制暴露,等于没测。
小结
风险模型的核心是用低维结构刻画高维风险。它把「5000 只股票的协方差」压缩成「50 个因子的协方差 + 特质风险」,既解决了参数估计的统计问题,又让风险变得可分解、可归因、可约束。Barra 式模型之所以流行几十年,就是因为这个结构在稳定性、可解释性与灵活性之间取得了很好的平衡。
判断一个风险模型是否可用,看三个问题:它的风险分解是否可加(因子 + 特质 = 总风险)?它的归因能否把 alpha 与因子收益分开?它的压力测试结果有没有转化为实际约束? 三个都做到,风险模型才不只是报表,而是真正的决策工具。
下一步建议阅读组合优化与风险模型,看风险模型如何与收益预测一起生成最优权重;如果你关心实盘中的风险监控,可以回顾交易风控与实时限额。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。