跳至正文
MODULE 06风险模型与组合优化4 讲 · PORTFOLIO CONSTRUCTION
LECTURE 23共 31 讲预计阅读 60 分钟

第 23 讲:Barra 结构化风险模型数学解构 ​

本讲核心目标

在现代买方量化资管机构中,“控制风险与追求超额同等重要”。如果直接使用 A 股全市场 5,000 余只股票的历史收益率计算样本协方差矩阵,会立刻遭遇毁灭性的高维维度灾难(Curse of Dimensionality)与随机矩阵噪声污染,导致优化器把虚假统计套利机会放大为致命巨亏。

本讲融合全球量化标杆 MSCI Barra CNE5/CNE6 实证白皮书(Empirical Notes)、BARRA US-E3 模型手册(Risk Model Handbook) 以及国内顶尖卖方工程实践 华泰证券多因子体系(系列之一),对结构化风险模型进行彻底的数学解构:

  1. 深入剖析 Barra CNE5/CNE6 十大风格因子的全部细分描述因子(Descriptors)标准公式与精确组合权重;
  2. 推导残差波动率(Residual Volatility)对 Beta/Size、流动性(Liquidity)对 Size、以及非线性市值(Non-linear Size)的回归正交化清洗算法;
  3. 掌握 Barra CNE5 针对优化器低估风险提出的四大前沿创新:优化偏差调整(Optimization Bias Adjustment / Eigenfactor Adjustment)、波动率机制调整(Volatility Regime Adjustment, VRA)、国家因子与纯行业中性分离、以及按市值分位数的贝叶斯特异性风险收缩(Bayesian Shrinkage);
  4. 依托工业级可运行代码完成全流程闭环验证。

1. 经典文献与工业界理论基石 ​

在深入推导前,建议研读奠定现代多因子风险管理基石的经典文献与行业权威白皮书:

  1. Orr, D. J., Mashtaler, Igor, & Nagy, Adam (2012). "The Barra China Equity Model (CNE5) Empirical Notes", MSCI Research.

    专门针对中国 A 股市场微观结构定制的经典实证白皮书,详述了 CNE5 十大风格因子、细分描述子构成、特征值优化偏差修正与波动率机制调整。

  2. BARRA Inc. (1998). United States Equity Version 3 (US-E3) Risk Model Handbook. BARRA Research.

    奠定了 Barra 结构化风险模型的工业规范,详述了多因子模型数据流、描述因子标准化、非线性市值(Size Nonlinearity)、特异方差两级时序-截面建模框架以及四维风险分解魔方。

  3. 华泰证券研究所 (林晓明、陈烨, 2016). 《华泰多因子模型体系初探——华泰多因子系列之一》.

    国内本土量化多因子体系的开山之作,系统阐释了因子提纯、Breusch-Pagan / White 异方差检验、加权最小二乘法(WLS)、因子协方差与因子边际风险贡献(FMCAR)的工程落地。

  4. Rosenberg, Barr (1974). "Extra-Market Components of Covariance in Security Returns", Journal of Financial and Quantitative Analysis, 9(2): 263-274.

    首次提出将资产收益率解构为行业哑变量与连续风格因子的结构化多因子协方差模型。

  5. Menchero, Jose, Wang, Jun, & Orr, D. J. (2011). "Eigen-Adjusted Covariance Matrices", MSCI Research Insight.

    揭示了样本协方差在小特征值方向系统性低估风险的机理,推导了蒙特卡洛模拟特征因子(Eigenfactors)风险校准算法。

  6. Shepard, Peter (2009). "Second Order Risk", Working Paper, arXiv:0908.2455.

    解析推导了二次规划优化器在有限样本下必然低估组合预测风险的数学内涵。


2. 高维样本协方差的“维度灾难”与随机矩阵理论 ​

在马科维茨(Markowitz, 1952)均值-方差现代投资组合理论(MPT)中,投资组合的预期方差由股票权重向量 w∈RN 与股票收益协方差矩阵 Σ∈RN×N 决定:

σp2=wTΣw

若直接采用全市场股票历史收益率的无偏样本协方差估计:

Σsample=1T−1∑t=1T(rt−r¯)(rt−r¯)T

在当前 A 股市场(股票数量 N≈5,000 只),直接使用样本协方差矩阵将面临无法逾越的数学困境:

课程图示
可放大查看细节,点击百分比恢复原始大小。

根据**随机矩阵理论(Random Matrix Theory, RMT)**中的 Marchenko-Pastur 定理(1967):当样本维度比率 γ=N/T>0 时,纯白噪声矩阵的特征值分布将发散在区间 [λ−,λ+] 内:

λ±=σ2(1±γ)2

当 N=5,000,T=250 时,γ=20,最大特征值被夸大几十倍,最小特征值挤压至 0 附近。优化器将错误地认为最小特征向量方向拥有“零风险超额”,进而将全部杠杆倾泻于此,实盘运行必然惨败。


3. Barra 结构化风险模型核心数学架构 ​

为了彻底粉碎维度灾难,Barra 结构化风险模型提出:资产收益率的大部分波动来源于少数可识别的宏观、行业与风格共同因子,剩余极小部分由个股特异性事件支配。

3.1 横截面多因子收益线性分解 ​

在每个交易日 t,截面上 N 只股票的收益率向量可分解为:

ri,t=∑k=1KXi,k,tfk,t+ui,t

以矩阵紧凑形式表达:

rt=Xtft+ut

其中:

  • rt∈RN:全市场 N 只股票在交易日 t 的收益率向量;
  • Xt∈RN×K:因子暴露矩阵(Factor Exposure Matrix),每行代表单只股票在 K 个风险因子上的暴露(包括行业哑变量与标准化风格因子,通常 K≈40∼60≪N);
  • ft∈RK:公共因子收益率向量(Factor Returns);
  • ut∈RN:个股特异性残差收益率向量(Specific / Idiosyncratic Returns)。

3.2 结构化模型的核心统计假设 ​

Barra 模型依赖两条经典正交性假设:

  1. 因子与特异残差不相关:Cov(ft,ut)=0∈RK×N;
  2. 个股特异残差互不相关:即跨股票特异残差协方差为零,Cov(ui,t,uj,t)=0,∀i≠j。因此,个股特异性风险矩阵为一个严格的对角矩阵:Δt=diag(σu,12,σu,22,…,σu,N2)∈RN×N

3.3 协方差矩阵重构方程 ​

根据协方差的双线性性质:

Σ=Cov(r)=Cov(Xf+u)=XCov(f)XT+Cov(u)Σ=XFXT+Δ

其中:

  • F=Cov(f)∈RK×K:因子协方差矩阵(Factor Covariance Matrix);
  • Δ∈RN×N:特异性方差对角矩阵(Specific Variance Matrix)。
课程图示
可放大查看细节,点击百分比恢复原始大小。

4. MSCI Barra CNE5/CNE6 十大风格因子与细分描述子(Descriptors) ​

根据 MSCI Barra CNE5 官方实证手册(Appendix A)与 US-E3 体系,每个风格大类并非单一指标,而是由多个具有深厚财务与交易学内涵的底层细分描述子(Descriptors)加权合成,并经过严格的去极值、标准化和正交化清洗:

4.1 十大风格因子构成规范与数学公式 ​

课程图示
可放大查看细节,点击百分比恢复原始大小。

1. Size (规模因子) ​

  • 定义公式:1.0×LNCAP
  • 公式解析:个股全部发行在外的股票总市值(Price × Total Shares)取自然对数:LNCAP=ln⁡(MarketCap)
  • 经济内涵:解释资产收益截面方差最强的主因子,区分大盘蓝筹与中小盘溢价。

2. Beta (贝塔因子) ​

  • 定义公式:1.0×BETA
  • 公式解析:个股过去 252 个交易日超额收益率 ri,t−rf,t 对全市场市值加权基准超额收益率 Rt 进行 EWMA 指数衰减时序回归(半衰期设为 63 个交易日):ri,t−rf,t=αi+βiRt+ei,t回归斜率 βi 即为该股票的 Beta 暴露。

3. Momentum (动量因子) ​

  • 定义公式:1.0×RSTR (Relative Strength)
  • 公式解析:过去 T=504 个交易日(2 年)内,滞后 L=21 个交易日的指数加权对数超额收益率之和(半衰期设为 126 个交易日):RSTR=∑t=LT+Lwt[ln⁡(1+ri,t)−ln⁡(1+rf,t)]
  • 关键设计:强制排除最近 21 个交易日(1 个月),以规避 A 股极其强烈的短期流动性反转效应干扰。

4. Residual Volatility (残差波动率因子) ​

  • 合成权重:0.74×DASTD+0.16×CMRA+0.10×HSIGMA
  • 细分描述子定义:
    1. DASTD (日收益波动率):过去 252 个交易日超额收益率的 EWMA 加权标准差(半衰期 42 个交易日):DASTD=∑t=1252wt(ri,t−r¯i)2
    2. CMRA (累计收益极差):过去 12 个月(每 21 天为一个复合月度 τ)累计对数超额收益序列的最大波动极差:Z(T)=∑τ=1T[ln⁡(1+ri,τ)−ln⁡(1+rf,τ)],T=1,…,12CMRA=ln⁡(1+Zmax)−ln⁡(1+Zmin)
    3. HSIGMA (历史残差标准差):上述 Beta 估计方程中残差项 ei,t 的 EWMA 加权标准差(半衰期 63 个交易日):HSIGMA=std(ei,t)。
  • 正交化铁律:合成为原始残差波动率后,必须对 Beta 因子和 Size 因子执行截面加权最小二乘正交化,取其纯净残差,防止与贝塔和规模因子产生高多重共线性(VIF 爆炸)。

5. Non-linear Size (非线性市值因子) ​

  • 定义公式:1.0×NLSIZE
  • 公式解析:将已经标准化的规模因子暴露 Xsize 进行立方运算:Yi=(Xi,size)3将 Y 对 Xsize 在截面上执行以市值平方根为权重的回归:Yi=a+bXi,size+ϵi取回归残差 ϵi 并重新极值截断和标准化,即为最终的 NLSIZE。
  • 经济内涵:Barra 发现纯线性 Size 模型对于前 500 大股票预测极好,但在全市场小盘股与微盘股上系统性低估风险。非线性市值因子刻画了“哑铃型投资组合(长仓中盘股、短仓大盘与微盘)”的曲率风险。

6. Book-to-Price (账面市值比 / 价值因子) ​

  • 定义公式:1.0×BTOP
  • 公式解析:最新可获得的 Point-in-Time 财报归属于母公司普通股权益账面价值除以当前总市值:BTOP=Common EquityTotal MarketCap

7. Liquidity (流动性因子) ​

  • 合成权重:0.35×STOM+0.35×STOQ+0.30×STOA
  • 细分描述子定义:
    1. STOM (单月换手率对数):过去 21 个交易日日均换手率之和的对数:STOM=ln⁡(∑t=121VolumetSharesOutstandingt)
    2. STOQ (季均换手率对数):过去 3 个月换手率的指数平均对数:STOQ=ln⁡(13∑τ=13exp⁡(STOMτ))
    3. STOA (年均换手率对数):过去 12 个月换手率的指数平均对数:STOA=ln⁡(112∑τ=112exp⁡(STOMτ))
  • 正交化铁律:合成后必须对 Size 因子进行截面正交化。

8. Earnings Yield (盈利收益因子) ​

  • 合成权重:0.68×EPFWD+0.21×CETOP+0.11×ETOP
  • 细分描述子定义:
    1. EPFWD (一致预期前瞻市盈率倒数):分析师一致预期未来 12 个月前瞻净利润除以总市值(权重高达 68%);
    2. CETOP (现金盈利价格比):过去 12 个月经营活动现金流净额除以总市值;
    3. ETOP (历史市盈率倒数):过去 12 个月 TTM 归母净利润除以总市值。

9. Growth (成长因子) ​

  • 合成权重:0.18×EGRLF+0.11×EGRSF+0.24×EGRO+0.47×SGRO
  • 细分描述子定义:
    1. EGRLF:分析师一致预期长期(3~5年)净利润复合增长率;
    2. EGRSF:分析师一致预期短期(1年)净利润增速;
    3. EGRO:过去 5 年年报 EPS 对时间进行线性回归的斜率除以 5 年均值;
    4. SGRO:过去 5 年年报每股营业收入对时间回归的斜率除以 5 年均值(权重最高,达 47%)。

10. Leverage (杠杆因子) ​

  • 合成权重:0.38×MLEV+0.35×DTOA+0.27×BLEV
  • 细分描述子定义:
    1. MLEV (市场杠杆):(ME+PE+LD)/ME,其中 ME 为权益市值,PE 为优先股,LD 为长期负债账面值;
    2. DTOA (资产负债率):(LD+DCL)/TA,其中 DCL 为流动负债,TA 为总资产;
    3. BLEV (账面杠杆):(BE+PE+LD)/BE,其中 BE 为普通股权益账面价值。

5. 异方差检验与受限加权最小二乘法(Restricted WLS) ​

每日已知收益率 rt 与暴露矩阵 Xt 后,因子收益率 ft 需通过截面回归提取。

5.1 异方差检验(Breusch-Pagan 与 White 检验) ​

华泰证券研究表明,普通 OLS 回归在大盘股与微盘股混杂时,随机误差项 u 存在严重的异方差性(Heteroscedasticity):

Var(ui∣X)=σi2≠σ2

异方差会导致 OLS 参数估计量丧失有效性,标准误低估,t 统计量失效。通过 Breusch-Pagan 辅助回归:

ui2=α0+∑k=1KαkXi,k+vi

构造拉格朗日乘数统计量 LM=nRu2∼χ2(K),经验检验在 A 股日频截面上拒绝同方差假设的比率高达 95% 以上!

因此,Barra 统一采用加权最小二乘法(WLS),权重矩阵 W=diag(w1,…,wN),取个股流通市值的平方根 wi=FloatCapi(或 US-E3 中采用的 hsigmai−2 倒数),彻底抚平微盘股异常波动。

5.2 国家因子、行业分离与受限回归数学方程 ​

在 CNE5 中,模型引入显式**国家因子(Country Factor)**以分离全市场共性走势。 为避免虚拟变量陷阱,施加行业中性受限条件:全市场行业因子收益率的市值加权和恒等于 0:

∑j=1Jcjfjind=0,cj=∑i∈IndjFloatCapi∑i=1NFloatCapi

引入拉格朗日乘子 λ,建立 KKT 鞍点求解方程组:

(XtTWtXtCTC0)(ftλ)=(XtTWtrt0)
  • 纯行业收益的优越性:解出的纯行业因子收益代表该行业相对于大盘全市场基准的净超额收益(Dollar-Neutral Industry Return),使得行业暴露为 0 的纯多头组合在业绩归因时不产生伪行业偏离(Davis & Menchero, 2011)。

6. Barra CNE5 四大前沿方法论突破(Methodology Highlights) ​

相比于老一代 CHE2 或朴素因子模型,MSCI Barra CNE5 在风险预测精度上做出了四大里程碑式改进:

课程图示
可放大查看细节,点击百分比恢复原始大小。

6.1 优化偏差调整(Optimization Bias Adjustment / Eigenfactor Adjustment) ​

  • 问题本质:Muller (1993) 与 Shepard (2009) 证明,投资组合优化器是“风险低估狂魔”。优化器在最小化 wTΣw 时,会无孔不入地寻找协方差矩阵中由于抽样误差而产生的“虚假对冲机会(Spurious Hedges)”,使得最优化组合的样本外实现波动率远高于事前预测波动率。
  • 特征因子(Eigenfactors)解耦: 设因子样本协方差矩阵谱分解为 F=UΛUT。其中正交特征向量矩阵 U 的每一列定义了一个特征因子组合(Eigenfactor Portfolio),其方差对应特征值 λk。
  • 偏差机理:Menchero, Wang & Orr (2011) 证明,低波动特征因子的方差被系统性低估,高波动特征因子的方差被系统性高估。
  • 蒙特卡洛调整法:通过设定真实现金流生成过程进行 Monte Carlo 模拟,量化每一阶特征因子的系统性低估比率,直接修正特征值矩阵 Λ~=DΛD,在不破坏纯因子经济学含义的前提下,彻底治愈优化器低估风险的绝症!

6.2 波动率机制调整(Volatility Regime Adjustment, VRA) ​

金融时间序列具备非平稳性(Non-stationarity)。在牛熊剧烈转换或突发黑天鹅时,依赖过去 1 年平稳数据的 EWMA 会发生严重时滞:

  • 在波动率飙升期(如 2008 年金融危机、2015 年 A 股异常波动),模型预测风险滞后严重低估;
  • 在波动率回落期,模型预测风险过度高估。

CNE5 引入每日截面偏差统计量(Cross-Sectional Bias Statistic):

Bt=1N∑i=1N(ui,tσ^u,i,t)2

通过在合适的时间窗口计算 Bt 的指数加权均值作为即时调整因子,动态缩放因子协方差与特异性风险矩阵,使风险预测能够“秒级自适应”当前宏观波动环境。

6.3 特异性风险的市值分位数贝叶斯收缩(Bayesian Shrinkage) ​

纯时序估计的个股特异性方差存在严重回归均值倾向(Mean Reversion):原本极低波动的股票未来特异风险常大幅上升,而极高波动的股票未来特异风险大幅回落。

CNE5 的处理流程:

  1. 将全市场股票按流通市值大小划分为 10 个等份分位数桶(Size Deciles);
  2. 在每个市值桶内,计算时序特异波动率预测值的均值 μd 与横截面标准差 σd;
  3. 将单只股票的时序特异波动率 σu,i 向其所在市值桶的均值 μd 进行收缩:σu,i∗=(1−vi)σu,i+viμd其中收缩强度 vi∈[0,1] 随着该个股特异波动率偏离均值的标准差倍数 |σu,i−μd|/σd 增加而单调增大。

7. 预测精度评估基准:偏差统计量(Bias Statistic)与 MRAD ​

在 CNE5 报告第 5 节与 Appendix C 中,MSCI 详细公开了买方评估风险模型优劣的工业金标准指标:

7.1 标准化收益与单窗口偏差统计量 ​

对于任意投资组合 n(如纯因子组合、随机主动组合、长多因子倾斜组合、最小方差优化组合),其在第 t 期的标准化收益率为:

bnt=Rntσnt

其中 Rnt 为实现主动收益,σnt 为期初事前预测风险。

在长度为 T 的评估窗口内,**偏差统计量(Bias Statistic)**定义为标准化收益的实现样本标准差:

Bn=1T−1∑t=1T(bnt−b¯n)2
  • 理想基准:完美的风险预测模型应使得 Bn≡1.0;
  • 95% 理论置信区间:在收益正态分布假设下,当窗口期为 T 时:Bn∈[1−2T,1+2T]
  • 肥尾修正(Kurtosis Impact):金融收益存在显著超额峰度(Fat Tails)。当峰度 k=4 时,95% 置信区间的下限会由 0.66 下移至 0.61,上限由 1.34 上移至 1.40。

7.2 滚动 12 个月平均绝对偏差(MRAD) ​

为防止长期正负误差冲抵,CNE5 重点考察覆盖 N 个测试组合的滚动 12 期 平均绝对偏差(Mean Rolling Absolute Deviation, MRAD):

MRADτ=1N∑n=1N|Bnτ−1|
  • 在正态分布且预测完美时,E[MRAD]≈0.17;在峰度为 3.5∼4.0 时,E[MRAD]≈0.19。MRAD 越小,说明风险模型的绝对测算精度越高!

8. 工业级 Barra 结构化风险引擎实现 ​

在 tutorial/examples/lecture_23_barra_risk_model_architecture.py 中,我们构建了生产级 AshareBarraRiskEngine,严格实现了:

  1. 逐日对齐的 (T, N, K) 历史暴露与市值权重 WLS 回归;
  2. EWMA 因子协方差矩阵 F 估计;
  3. 特异性方差残差中心化与经验贝叶斯平滑收缩;
  4. 组合年化波动率与跟踪误差(TE)精确分解接口。

可在终端直接运行验证:

bash
python3 -m examples.lecture_23_barra_risk_model_architecture
py
"""
教学用 Barra 形式 结构化风险模型引擎 (AshareBarraRiskEngine)
实现:Σ = X F X^T + Δ 的精确重构、EWMA 因子协方差与特异性方差固定强度收缩
"""

from dataclasses import dataclass
from typing import Dict, List, Optional, Tuple
import numpy as np
import polars as pl


@dataclass
class BarraRiskMatrices:
    """Barra 风险模型输出矩阵容器"""
    exposure_matrix: np.ndarray        # X: (N, K)
    factor_covariance: np.ndarray      # F: (K, K)
    specific_variance: np.ndarray      # Δ: (N,) 对角线元素
    factor_names: List[str]
    symbols: List[str]

    def compute_portfolio_variance(self, weights: np.ndarray) -> Tuple[float, float, float]:
        """
        计算指定权重向量的总年化方差、因子风险方差与特异性风险方差
        返回: (总年化方差, 因子年化方差, 特异性年化方差)
        """
        # 因子暴露: beta = X^T @ w (K,)
        factor_exp = self.exposure_matrix.T @ weights
        
        # 因子风险: w^T (X F X^T) w = beta^T F beta
        factor_var = float(factor_exp.T @ self.factor_covariance @ factor_exp)
        
        # 特异性风险: w^T Δ w = sum(w_i^2 * delta_i)
        specific_var = float(np.sum((weights ** 2) * self.specific_variance))
        
        total_var = factor_var + specific_var
        return total_var, factor_var, specific_var


class AshareBarraRiskEngine:
    """
    Barra 多因子结构化风险模型估计器
    """

    def __init__(
        self,
        half_life_days: int = 60,
        annual_factor: float = 250.0,
        shrinkage_intensity: float = 0.30
    ):
        if half_life_days <= 0 or annual_factor <= 0 or not 0 <= shrinkage_intensity <= 1:
            raise ValueError("半衰期/年化系数须为正,收缩强度须在 [0,1]")
        self.half_life = half_life_days
        self.annual_factor = annual_factor
        self.shrinkage = shrinkage_intensity

    def _ewma_weights(self, T: int) -> np.ndarray:
        """生成指数加权衰减权重向量 (归一化使其和为 1)"""
        decay_factor = 2.0 ** (-1.0 / self.half_life)
        powers = np.arange(T - 1, -1, -1)
        w = decay_factor ** powers
        return w / np.sum(w)

    def estimate_risk_model(
        self,
        historical_returns: np.ndarray, # (T, N) 历史收益率时序
        exposure_matrix: np.ndarray,    # (T, N, K),每期收益开始前已知的历史暴露
        market_caps: np.ndarray,        # (T, N),与历史暴露同一时点的市值
        factor_names: List[str],
        symbols: List[str],
        current_exposure_matrix: Optional[np.ndarray] = None,
        assume_static: bool = False
    ) -> BarraRiskMatrices:
        """
        核心模型估计流程
        """
        historical_returns = np.asarray(historical_returns, dtype=float)
        exposures = np.asarray(exposure_matrix, dtype=float)
        caps = np.asarray(market_caps, dtype=float)
        if historical_returns.ndim != 2 or historical_returns.shape[0] < 2:
            raise ValueError("收益必须为至少两期的 (T,N) 矩阵")
        T, N = historical_returns.shape
        if exposures.ndim == 2:
            if not assume_static:
                raise ValueError("固定暴露仅用于显式 assume_static=True 的合成实验")
            exposures = np.broadcast_to(exposures, (T, *exposures.shape))
            caps = np.broadcast_to(caps, (T, N))
            if current_exposure_matrix is None:
                current_exposure_matrix = exposures[-1]
        if exposures.ndim != 3 or exposures.shape[:2] != (T, N) or caps.shape != (T, N):
            raise ValueError("历史暴露/市值须按收益行对齐为 (T,N,K)/(T,N)")
        K = exposures.shape[2]
        current = np.asarray(current_exposure_matrix, dtype=float)
        if current.shape != (N, K) or len(factor_names) != K or len(symbols) != N:
            raise ValueError("必须提供预测时点暴露 (N,K),并对齐因子和证券名称")
        if not all(np.isfinite(v).all() for v in (historical_returns, exposures, caps, current)) or np.any(caps <= 0):
            raise ValueError("收益/暴露须有限,市值须为正")
        factor_returns = np.zeros((T, K))
        specific_residuals = np.zeros((T, N))
        for t in range(T):
            X = exposures[t]
            # WLS 目标权重 sqrt(cap);对设计矩阵/收益乘 sqrt(W)。
            sqrt_w = caps[t] ** .25
            f_t = np.linalg.lstsq(X * sqrt_w[:, None], historical_returns[t] * sqrt_w, rcond=None)[0]
            factor_returns[t] = f_t
            specific_residuals[t] = historical_returns[t] - X @ f_t

        # 2. 指数加权移动平均 (EWMA) 计算因子协方差矩阵 F (K, K)
        ewma_w = self._ewma_weights(T).reshape(-1, 1) # (T, 1)
        centered_f = factor_returns - np.sum(factor_returns * ewma_w, axis=0)
        
        # F = (centered_f * w)^T @ centered_f * annual_factor
        weighted_f = centered_f * np.sqrt(ewma_w)
        F_matrix = (weighted_f.T @ weighted_f) * self.annual_factor

        # 3. 中心化残差,计算特异性方差并作固定强度收缩
        centered_u = specific_residuals - np.sum(specific_residuals * ewma_w, axis=0)
        weighted_u = centered_u * np.sqrt(ewma_w)
        raw_specific_var = np.sum(weighted_u ** 2, axis=0) * self.annual_factor

        # 教学简化:固定强度向全市场方差中位数收缩;未实现分组自适应贝叶斯估计
        prior_mean_var = np.median(raw_specific_var)
        shrunk_specific_var = (
            (1.0 - self.shrinkage) * raw_specific_var + 
            self.shrinkage * prior_mean_var
        )

        return BarraRiskMatrices(
            exposure_matrix=current,
            factor_covariance=F_matrix,
            specific_variance=shrunk_specific_var,
            factor_names=factor_names,
            symbols=symbols
        )


# ================= 单元测试与风险分解演示 =================
if __name__ == "__main__":
    np.random.seed(42)
    n_stocks = 500  # 类似中证 500 样本
    t_history = 250 # 1 年历史交易日
    n_factors = 10  # 6 个行业因子 + 4 个风格因子 (Size, Value, Momentum, Vol)

    factor_names = [f"industry_{i}" for i in range(6)] + ["size", "value", "momentum", "volatility"]
    symbols = [f"{s:06d}.SH" for s in range(n_stocks)]

    # 1. 模拟因子暴露矩阵 X
    # 前 6 列为行业独热编码 (One-Hot),后 4 列为标准化风格特征
    X_mat = np.zeros((n_stocks, n_factors))
    ind_assign = np.random.choice(6, n_stocks)
    for s_idx in range(n_stocks):
        X_mat[s_idx, ind_assign[s_idx]] = 1.0
    X_mat[:, 6:] = np.random.normal(0, 1.0, (n_stocks, 4))

    # 模拟市值
    market_caps = np.exp(np.random.uniform(np.log(50), np.log(3000), n_stocks))

    # 2. 模拟真实时序收益率
    true_f_cov = np.eye(n_factors) * 0.04 / 250
    f_returns_sim = np.random.multivariate_normal(np.zeros(n_factors), true_f_cov, t_history)
    u_sim = np.random.normal(0, 0.02, (t_history, n_stocks))
    # 历史暴露在对应收益开始前可知;预测时点另提供 X_mat。
    X_history = np.broadcast_to(X_mat, (t_history, n_stocks, n_factors)).copy()
    X_history[:, :, 6:] += np.random.normal(0, .1, (t_history, n_stocks, 4))
    R_sim = np.einsum('tnk,tk->tn', X_history, f_returns_sim) + u_sim

    # 3. 运行 Barra 风险引擎
    engine = AshareBarraRiskEngine(half_life_days=60, shrinkage_intensity=0.25)
    risk_model = engine.estimate_risk_model(
        historical_returns=R_sim,
        exposure_matrix=X_history,
        current_exposure_matrix=X_mat,
        market_caps=np.broadcast_to(market_caps, (t_history, n_stocks)),
        factor_names=factor_names,
        symbols=symbols
    )

    print(">>> Barra 风险模型估计成功!")
    print(f"  • 因子协方差矩阵 F 形状 : {risk_model.factor_covariance.shape}")
    print(f"  • 特异性风险对角矩阵形状: {risk_model.specific_variance.shape}")

    # 4. 模拟一个等权指数基金组合 vs 主动增强偏离组合
    benchmark_weights = np.ones(n_stocks) / n_stocks
    active_weights = benchmark_weights.copy()
    # 主动超配前 50 支股票,低配后 50 支
    active_weights[:50] += 0.001
    active_weights[-50:] -= 0.001
    tracking_diff_weights = active_weights - benchmark_weights

    # 计算主动跟踪误差 (Tracking Error)
    te_var, te_factor_var, te_spec_var = risk_model.compute_portfolio_variance(tracking_diff_weights)
    annual_te = np.sqrt(te_var) * 100.0

    print("\n>>> 指数增强组合主动风险 (Tracking Error) 分解报告:")
    print(f"  • 年化总跟踪误差 (Annualized TE)  : {annual_te:6.2f}%")
    print(f"  • 公共因子风险占比 (Factor Risk) : {te_factor_var / te_var * 100:6.2f}%")
    print(f"  • 个股特异风险占比 (Specific Risk): {te_spec_var / te_var * 100:6.2f}%")

9. 本讲小结与核心自测 ​

核心考点工业标准回答要点常见工程/学术陷阱
为什么动量因子(RSTR)必须排除最近 21 天?A 股短期具有强烈的散户流动性反转效应;若包含最近 1 个月,短期反转会严重对冲抵消中长期动量,导致因子失效。简单把过去 12 个月全周期累加,忽视了短期反转噪声的污染。
残差波动率与流动性因子为什么必须对 Size 正交化?小微盘股票天然流动性低、特异波动大,若不正交化,残差波动率与流动性因子实际上是“规模因子的变种翻版”,引发共线性灾难。未做正交化直接投入回归,导致因子方差膨胀因子(VIF)超过 5,参数估计彻底失真。
什么是优化偏差(Optimization Bias)?如何修正?优化器在最小化组合方差时会过拟合样本协方差的小特征值方向,使得样本外真实风险远超预测。Barra 通过特征因子(Eigenfactors)模拟调整矩阵谱进行修正。误以为只要均值-方差优化求解出 optimal,实盘就必然拥有最小波动率。

在下一讲中,我们将把本讲产出的结构化风险协方差矩阵 Σ 与前序模块沉淀的 Alpha 预期超额收益向量 α 融合,正式进入现代量化资产管理的核心引擎——第 24 讲:凸优化求解器(cvxpy)实战!

QUANTML · Quantitative Research Handbook