← 返回模块
4.4.2.2beta 可读 · 未来付费校验通过内容版本 2026-05-28

事前跟踪误差与组合风险归因

4.4.2 · 风险模型与风险管理 · 量化全流程

某上海公募的沪深300指增基金:基金经理周一早盘想加 3% 宁德时代,通过减 3% 招商银行融资。​​90 秒之内​​,风控要回答三个问题——加完之后(1)对沪深300的事前跟踪误差是不是还在 4% 预算之内?(2)哪个因子吃掉了最多的跟踪误差预算?(3)哪几只股票贡献了最多的组合风险?这堂课讲的就是这套​​事前组合风险归因​​(ex-ante portfolio risk attribution)算法,以及它如何在 L1 给出的 ΣBARRA\Sigma_{\text{BARRA}} 之上做交易前合规检查。Grinold-Kahn(1999)《Active Portfolio Management》第 17 章是规范参考。

L2 拿 L1 的 ΣBARRA\Sigma_{\text{BARRA}}BBΣF\Sigma_FDD 当成给定输入;L2 自己不构造协方差,只用归因代数读取风险。

术语对齐表

本课新增的核心术语,与 data/glossaries/quant-glossary.yaml 标准译名一致:​​跟踪误差​​(tracking error, TE)、​​因子模型​​(factor model)、​​因子暴露​​(factor loading)、​​Barra 模型​​(Barra model)、​​协方差矩阵​​(covariance matrix)、​​夏普比率​​(Sharpe ratio,主动收益的「同款」就是 IR)、​​贝塔​​(beta,系统性风险暴露的最简单代表)、​​风险平价​​(risk parity,与 PCTR 同质的「按风险等量分配」理念)。后续所有讨论用这些标准译名。

事前组合风险的因子-特异分解

下面是 ​Inline-code listing​ 中按固定顺序的五条:

total_variance:    sigma_p^2 = w^T * Sigma * w
factor_variance:   w^T * B * Sigma_F * B^T * w = b_p^T * Sigma_F * b_p with b_p = B^T * w (portfolio factor exposures)
specific_variance: w^T * D * w = sum_i w_i^2 * D_ii
total_decomposition: sigma_p^2 = factor_variance + specific_variance
empirical_split:   factor ~70-90% for typical active equity fund; ~95% for cap-weighted index; ~50% for concentrated stock-picker book

规则:most equity portfolios are factor-risk dominated; specific risk grows in importance for concentrated books

给定 L1 的 Barra 协方差 Σ=BΣFB+D\Sigma = B \Sigma_F B^{\top} + D 与组合权重 ww(NN 维,加和为 1),事前(ex-ante)组合方差是

σp2=wΣw=wBΣFBw+wDw.\sigma_p^2 = w^{\top} \Sigma w = w^{\top} B \Sigma_F B^{\top} w + w^{\top} D w.

第一项是​​因子风险​​——由组合对 KK 个公共因子暴露贡献的方差;第二项是​​特异风险​​——由资产特有残差贡献的方差。两者的比例是任何机构风险报告的第一行头条数:沪深300完全复制(全市值加权)组合的因子比例约 95%(指数权重本身就承袭了跨股共动结构);典型 100–200 只股票的主动权益基金落在 70–90%;集中 10–20 只的选股师组合可能降到 50%(多个独立特异押注的多元化抵消了大量特异风险,残余的因子暴露反而成主要贡献)。

组合层因子暴露与每因子风险归因

定义​​组合层因子暴露​ bp=Bwb_p = B^{\top} w(KK 维)。这是 L2 最核心的一个产出:bp,kb_{p, k} 是组合在因子 kk 上的暴露,单位是​​横截面标准差​​(因为风格因子已被 z-score)。例如 bp,规模=0.3b_{p, \text{规模}} = -0.3 表示组合相对市场偏向小市值 0.3 sigma;bp,动量=+0.5b_{p, \text{动量}} = +0.5 表示偏向高动量 0.5 sigma。

因子风险项展开得很干净:

wBΣFBw=bpΣFbp=klbp,kΣF,klbp,l.w^{\top} B \Sigma_F B^{\top} w = b_p^{\top} \Sigma_F b_p = \sum_k \sum_l b_{p, k}\, \Sigma_{F, kl}\, b_{p, l}.

每因子对​​因子方差​​的贡献可拆为对角项 bp,k2ΣF,kkb_{p, k}^2 \cdot \Sigma_{F, kk}(其它因子归零时的边际贡献)加上交叉项 2bp,klkΣF,klbp,l2 b_{p, k} \sum_{l \neq k} \Sigma_{F, kl} b_{p, l}(与其它因子暴露的协同)。每因子​​百分比贡献​​是

pct_factork=bp,k(ΣFbp)kbpΣFbp,\text{pct\_factor}_k = \frac{b_{p, k} \cdot (\Sigma_F b_p)_k}{b_p^{\top} \Sigma_F b_p},

加和等于 100%。这就是风险归因报告中「因子归因」那一行的产出方式。

MCTR / CCTR / PCTR:资产层归因

下面是 ​Inline-code listing​ 中按固定顺序的五条:

marginal_contribution:  MCTR_i = (Sigma * w)_i / sigma_p
component_contribution: CCTR_i = w_i * MCTR_i
percent_contribution:   PCTR_i = CCTR_i / sigma_p
euler_identity:         sum_i CCTR_i = sigma_p; sum_i PCTR_i = 1.0
actionable_insight:     rank assets by PCTR; top-PCTR positions are the actionable risk levers — often NOT the same as top-weight positions

规则:PCTR sums to 1 across assets and identifies the actionable risk-reduction levers

每只资产 ii 的​​边际风险贡献​​(marginal contribution to risk, MCTR)是偏导

MCTRi=σpwi=(Σw)iσp.\text{MCTR}_i = \frac{\partial \sigma_p}{\partial w_i} = \frac{(\Sigma w)_i}{\sigma_p}.

回答的是「如果把 wiw_i 增加一个 dw\mathrm{d}w,总风险变化多少?」。​​成分风险贡献​​(component contribution to risk, CCTR)是 CCTRi=wiMCTRi\text{CCTR}_i = w_i \cdot \text{MCTR}_i;按 Euler 齐次函数定理,iCCTRi=σp\sum_i \text{CCTR}_i = \sigma_p。​​百分比风险贡献​​(percent contribution to risk, PCTR)是 PCTRi=CCTRi/σp\text{PCTR}_i = \text{CCTR}_i / \sigma_p,加和为 1。

PCTR 是资产层归因的核心输出:按 PCTR 排序就指出了​​风险贡献前五​​的资产,而这往往​​不是权重前五​​。一只 5% 权重、与大盘高度相关的茅指数白马股可能贡献 12% 风险(PCTR = 0.12);一只 10% 权重、与大盘相关性低的多元化品种可能只贡献 4% 风险(PCTR = 0.04)。基金经理调风险时盯的就是 PCTR——「把 PCTR 排第一的位置减半,跟踪误差能下来多少?」

事前跟踪误差

下面是 ​Inline-code listing​ 中按固定顺序的七条:

active_weights:         w_a = w - w_B
tracking_error:         TE = sqrt(w_a^T * Sigma * w_a)
active_factor_exposure: b_a = B^T * w_a
factor_TE:              sqrt(b_a^T * Sigma_F * b_a)
specific_TE:            sqrt(sum_i w_a_i^2 * D_ii)
decomposition_identity: TE^2 = factor_TE^2 + specific_TE^2
information_ratio:      IR = alpha / TE = expected_active_return / tracking_error

规则:long-only funds typically have factor_TE > specific_TE; market-neutral funds typically have specific_TE > factor_TE

主动组合(active portfolio)是 wa=wwBw_a = w - w_B,wBw_B 是基准权重(典型基准:沪深300指增产品基准就是沪深300 ETF 510300 持仓;中证500 / 中证1000 同理)。​​跟踪误差​​是主动收益 ra=warr_a = w_a^{\top} r 的标准差:

TE=waΣwa.\text{TE} = \sqrt{w_a^{\top} \Sigma w_a}.

主动基金按​​跟踪误差预算​​运作:产品合同典型规定「相对基准预期跟踪误差 3%–5%」作为风险护栏;风控系统每笔交易前算事前 TE 检查是否在预算内。TE 按 L1 的因子-特异分解同款拆解:

TE2=waBΣFBwa+waDwa=baΣFba+iwa,i2Dii,\text{TE}^2 = w_a^{\top} B \Sigma_F B^{\top} w_a + w_a^{\top} D w_a = b_a^{\top} \Sigma_F b_a + \sum_i w_{a, i}^2 D_{ii},

其中 ba=Bwab_a = B^{\top} w_a 是​​主动因子暴露​​——风险报告里「相对基准在哪些因子上偏了多少」的核心读数。因子跟踪误差 baΣFba\sqrt{b_a^{\top} \Sigma_F b_a} 和特异跟踪误差 waDwa\sqrt{w_a^{\top} D w_a} 都非负,总 TE 是二者平方和的平方根(交叉项按 ΣF\Sigma_FDD 的正交性恰好为零)。

经验签名:长期跟基准的多头基金通常 factor_TE > specific_TE(主动权重被「不许做空」约束,大头分散到多只跟基准结构相近的票);市场中性多空基金通常 specific_TE >> factor_TE(整套机制就是把因子暴露中性化,赚特异 alpha)。

信息比率与主动管理基本法则

​信息比率​​(information ratio, IR)是主动收益的夏普比率:

IR=αTE=E[ra]TE=waμTE.\text{IR} = \frac{\alpha}{\text{TE}} = \frac{\mathbb{E}[r_a]}{\text{TE}} = \frac{w_a^{\top} \mu}{\text{TE}}.

CN 公募沪深300指增头部产品净 IR 约 0.6–1.0(费后);量化 500 指增因为残余 alpha 更多通常 IR 更高(头部能到 1.2+);最强的私募中性产品 IR 可以稳定 2.0+。​​主动管理基本法则​​(Grinold 1989 JPM):

IRICBR,\text{IR} \approx \text{IC} \cdot \sqrt{\text{BR}},

其中 IC 是​​信息系数​​(每笔押注的预测 alpha 与实现收益的相关系数,4.2.3 L1 全推),BR 是​​广度​​(每年独立押注的数量)。结论:IC 翻倍 -> IR 翻倍;广度从 50 只扩到 200 只(假设独立)-> IR 乘 4=2\sqrt{4} = 2。​​对风险预算分配的含义​​:4% 跟踪误差预算最好分摊到尽量多的独立押注上;把 TE 集中在 1–2 个押注上,即使 IC 相同也是统计学上的浪费。这就是为什么国内量化指增普遍跑 200–500 只票而非 30 只——不是基本面研究能力不够,是 IR-广度的法则把分散写在了数学上。

实践工作流:交易前合规

Step 1: 基金经理提议交易 delta_w(权重变化)
Step 2: 风控系统计算新组合 w_new = w + delta_w
Step 3: 用 L1 的 Sigma_BARRA + L2 归因算 sigma_p_new、TE_new、因子-特异分解、b_a_new、PCTR 前五、因子 TE 贡献前五
Step 4: 风控经理对照预算,过则放行;超则要求缩规模或重构
Step 5: 实盘成交后系统记录新归因作为下一笔交易前检查的基线

每个交易日、每只受监管主动权益基金都在跑这套流。下面给出可直接套用的两个函数。

实现:risk_attribution

import numpy as np

def risk_attribution(
    weights: np.ndarray,
    benchmark_weights: np.ndarray,
    Sigma_BARRA: np.ndarray,
    B: np.ndarray,
    Sigma_F: np.ndarray,
    D: np.ndarray,
) -> dict:
    # 总风险与因子-特异分解
    sigma_p = float(np.sqrt(weights @ Sigma_BARRA @ weights))
    b_p = B.T @ weights
    factor_var = float(weights @ B @ Sigma_F @ B.T @ weights)
    specific_var = float(weights @ np.diag(D) @ weights)
    factor_var_pct = factor_var / (factor_var + specific_var)
    specific_var_pct = specific_var / (factor_var + specific_var)
    # 资产层 MCTR / CCTR / PCTR
    mctr = Sigma_BARRA @ weights / sigma_p
    cctr = weights * mctr
    pctr = cctr / sigma_p
    # 主动组合与跟踪误差
    w_a = weights - benchmark_weights
    TE = float(np.sqrt(w_a @ Sigma_BARRA @ w_a))
    b_a = B.T @ w_a
    factor_TE = float(np.sqrt(b_a @ Sigma_F @ b_a))
    specific_TE = float(np.sqrt(w_a @ np.diag(D) @ w_a))
    return {
        "sigma_p": sigma_p,
        "factor_var_pct": factor_var_pct,
        "specific_var_pct": specific_var_pct,
        "b_p": b_p,
        "MCTR": mctr,
        "CCTR": cctr,
        "PCTR": pctr,
        "TE": TE,
        "factor_TE": factor_TE,
        "specific_TE": specific_TE,
        "b_a": b_a,
    }

签名(参数名 weights / benchmark_weights / Sigma_BARRA / B / Sigma_F / D、返回字典键)与 US 版逐字节一致。

实现:pre_trade_check

下面这段 Fenced Python 代码块封装了​​交易前合规检查​​:把当前权重、拟交易增量、基准、L1 协方差全部送进去,一次性返回拟成交后的总跟踪误差、是否在预算内、PCTR 排第一的资产索引、贡献最多 TE 的因子索引。这是风控经理收到基金经理交易请求后真正调用的函数——它把 L2 的归因代数封成单一接口,90 秒之内给出 yes/no 决策外加可执行的归因报告。每家受监管主动权益基金都在跑这套同样形态的接口,只是变量命名不同。

import numpy as np

def pre_trade_check(
    current_weights: np.ndarray,
    delta_weights: np.ndarray,
    benchmark_weights: np.ndarray,
    Sigma_BARRA: np.ndarray,
    B: np.ndarray,
    Sigma_F: np.ndarray,
    D: np.ndarray,
    te_budget: float = 0.04,
) -> dict:
    # 拟成交后的组合
    new_weights = current_weights + delta_weights
    current_attr = risk_attribution(current_weights, benchmark_weights, Sigma_BARRA, B, Sigma_F, D)
    new_attr = risk_attribution(new_weights, benchmark_weights, Sigma_BARRA, B, Sigma_F, D)
    passes_budget = new_attr["TE"] < te_budget
    te_change = new_attr["TE"] - current_attr["TE"]
    top_pctr_asset_index = int(np.argmax(new_attr["PCTR"]))
    # 每因子 TE 贡献
    b_a = new_attr["b_a"]
    factor_te_sq = b_a @ Sigma_F @ b_a
    if factor_te_sq > 0:
        b_a_pcd = b_a * (Sigma_F @ b_a) / factor_te_sq
    else:
        b_a_pcd = np.zeros_like(b_a)
    top_factor_te_contributor_index = int(np.argmax(np.abs(b_a_pcd)))
    return {
        "passes_budget": passes_budget,
        "current_TE": current_attr["TE"],
        "new_TE": new_attr["TE"],
        "te_change": te_change,
        "top_pctr_asset_index": top_pctr_asset_index,
        "top_factor_te_contributor_index": top_factor_te_contributor_index,
        "current_attr": current_attr,
        "new_attr": new_attr,
    }

签名(参数名、默认 te_budget=0.04、返回字典键)与 US 版逐字节一致。

可视化:PCTR 条形图

Formula Explorer

x^2

把 PCTR 按降序条形图画出来,再叠加权重直方图。健康签名:前五 PCTR 加和 30%–50%(分散组合);如果前五 PCTR 加和超过 70%,说明组合过分集中,基金经理需要分散或减仓最高 PCTR 头寸。

练习

Exercise

你是 50 只大盘主动权益基金的风险官 (cn: 50 沪深300板块龙头, V_0 = 100,000,000 RMB),对沪深300 ETF 510300 跟踪误差预算 4%。给定 (i) 当前权重 current_weights, (ii) 基准权重 benchmark_weights, (iii) L1 的 Barra 协方差 Sigma_BARRABSigma_FD, (iv) 拟交易 delta_weights(+3% 加最大市值科技股,-3% 减最大市值金融股融资):(a) 调 current_attr = risk_attribution(current_weights, benchmark_weights, Sigma_BARRA, B, Sigma_F, D),报告 sigma_pTEfactor_var_pctspecific_var_pct;验证因子比例落在典型 70%–90% 区间。(b) 调 pre_trade_check(current_weights, delta_weights, benchmark_weights, Sigma_BARRA, B, Sigma_F, D, te_budget=0.04);报告 passes_budgetcurrent_TEnew_TEte_change。(c) 报告新组合下 b_a 绝对值前三(相对基准最偏的三个因子)。(d) 按 PCTR 排序,报告新组合前五资产名 + PCTR 值;评论最高 PCTR 资产是否也是权重最大的资产(通常不是——最高 PCTR 通常是中等权重的高相关科技茅指数)。(e) 计算每因子 TE 贡献 pct_factor_k = b_a_k * (Sigma_F @ b_a)_k / (b_a^T @ Sigma_F @ b_a);报告绝对值前三的因子;指认吃掉最多预算的那一个因子。(f) 验证如果新组合超过 4% 预算,把交易缩 50%(delta_weights * 0.5)能否拉回预算内;否则一句话推荐应该减哪一项因子暴露。

提示
主动权重 w_a = w - w_B 加和应严格等于 0(都是 1 - 1);如果加和不为 0,说明基准或当前权重未归一化。
提示
每因子 TE 贡献加和等于因子 TE 的平方;特异 TE 与因子 TE 通过 ΣF\Sigma_FDD 的正交性独立计算,总 TE = factor_TE2+specific_TE2\sqrt{\text{factor\_TE}^2 + \text{specific\_TE}^2}

实战注脚:CN 主动权益基金的归因报告

国内公募指数增强(沪深300指增 / 中证500指增 / 中证1000指增)产品每天发的归因报告基本就是 L2 的产出加上一段择时叙事。中信、中金、兴业证券的卖方风险研究每年也会发布行业级 PCTR 排行——某季度新能源板块在主动基金中的 PCTR 占比、消费板块的因子 TE 贡献等——是基金经理调仓的参考。L2 的算法本身十年没变,变的是用什么 Sigma_BARRA 来算:从早年自建小因子模型,到 Barra CNE6 商业授权,再到近年 因子投资(石川 等)开源复刻 + 自建增强因子的混合栈。这一层的工程量在风控团队的日常 80% 用在数据管线、20% 用在新增因子的归因展示——L2 的数学是稳定的,工程是活的。私募方面,千象 / 幻方 / 鸣石等头部量化的 沪深300指增 / 中证500指增 产品都跑这套同型归因,但用的因子库更深(可能 100+ 因子)、ΣBARRA\Sigma_{\text{BARRA}} 估计更频繁(可能日更而非月更)、合规人对 PCTR 阈值要求更严(top-1 PCTR 不超过总风险 5%)。掌握 L2 的算法就掌握了和这些团队对话的共同语言。

桥接到 L3

L1 给协方差,L2 给方差归因(MCTR / CCTR / PCTR / TE / b_a / IR),L3 上​​分位数风险​​——把目光从「典型一天波动多少」(variance-based)切到「最坏的一天能亏多少」(tail-risk)。​​在险价值​​(VaR)是损失分布的分位数;​​条件在险价值​​(CVaR / 期望损失)是超过 VaR 的平均损失,在 Artzner 等(1999)的一致性公理下比 VaR 更稳。L3 用 L1 的 ΣBARRA\Sigma_{\text{BARRA}} 算参数法 VaR、用历史 P&L 算历史法 VaR、用 ΣF\Sigma_F + DD 蒙特卡洛模拟多元 t 分布算 MC VaR;然后讲为什么 2023 起的 FRTB 用 97.5% ES 替代 99% VaR 作为监管资本头条数。