某 H 股 + A 股双地区运营私募的首席风险官:周五下午三件事压在桌上。(1) 风控部要把当前持仓在「2015 年 A 股股灾再来一次」情景下重定价——CEO 周一下午开战略会要听数;(2) 监管要求按 2023 年新修《商业银行资本管理办法》算 FRTB 市场风险资本(基金有港股账户接入银行同业柜台,部分敞口需要并表);(3) 投委会要一张「一页式风险报告」(one-page risk summary),把 L1 协方差健康 + L2 跟踪误差归因 + L3 在险价值 + L4 压力测试 + 监管资本数全捏到一页 A4 上。这堂课讲的就是怎么把 L1 / L2 / L3 端到端串成生产风险体系,以及 2023 起 FRTB 监管框架的四大新规——这是 4.4.2 模块的资本课(capstone)。
L4 拿 L1 给的 、、、、因子收益时序,拿 L2 给的归因函数,拿 L3 给的 VaR/CVaR/ES 函数,不重新推导——本课的全部新增内容是四种压力测试 + Basel/FRTB 监管框架 + 一页式机构风险报告。
术语对齐表
本课新增的核心术语,与 data/glossaries/quant-glossary.yaml 标准译名一致:压力测试(stress test)、在险价值(value at risk, VaR)、条件在险价值(conditional VaR, CVaR;FRTB 称 97.5% expected shortfall, ES)、最大回撤(max drawdown,压力测试的核心读数)、跟踪误差(tracking error,L2 复用)、因子模型(factor model)、Barra 模型(Barra model)、协方差矩阵(covariance matrix)、对冲比率(hedge ratio,反向压力测试给出的隐含再平衡)。后续所有讨论用这些标准译名。
四种压力测试类型
下面是 Inline-code listing 中按固定顺序的四条:
named_scenario: apply historical factor moves f_t for t in {2008-GFC, 2020-COVID, 2015-SSE-crash, ...} to current portfolio exposures b_p = B^T w; report worst-day, worst-5d, peak-to-trough drawdown, cumulative P&L
historical_replay: for each business day t in lookback window, compute hypothetical PnL_t = b_p^T f_t * V_0; surface worst individual days rather than aggregating into a quantile
hypothetical_scenario: specify factor shock xi_k = c * sqrt(Sigma_F_kk) for c in {-3, +3}; conditional shock to other factors xi_{-k} = Sigma_F[-k, k] / Sigma_F[k, k] * xi_k; delta_PnL = -b_p^T xi * V_0
reverse_stress: given target loss L*, solve min xi^T Sigma_F^{-1} xi subject to -b_p^T xi = L*; closed-form xi* = -L* / (b_p^T Sigma_F b_p) * Sigma_F b_p; surfaces the MOST-LIKELY scenario that triggers L*
规则:named + historical answer WHAT IF; hypothetical answers WHAT IF FACTOR X MOVES; reverse answers WHAT SCENARIO TRIGGERS LOSS L*。
命名情景(named scenario)
修辞上最有冲击力——「如果 2008 再来一次,我们的账亏多少?」——也是投委会汇报的常规品。规范情景库(CN 版本):
- 2015-06 到 2015-08 沪深300 -45%(8 周内,触发于场外配资杠杆头寸的去化 + 监管收紧;2015-07-08 单日 -7.4% 是 CN 规范单日最差)
- 2015-08-11 RMB 贬值(USDCNY 3 日 +3.5%;股票市场后续 2 周 -10%)
- 2016-01 熔断债危(新规触发熔断,4 个交易日累计 -15% 后撤销)
- 2018全年去杠杆(沪深300 -25%;民营企业信用紧缩;中美贸易摩擦)
- 2020-02-03 COVID 单日 -7.9%(沪深300;2015 以来最大单日跌)
- 2021-02 茅指数解套(价值反弹,CN 史上最大单季动量崩盘,top-动量十分位 -25%)
- 2021-07 互联网监管(滴滴 IPO 整改触发恒生科技 6 个月 -50%)
- 2021-09 起 恒大 / 碧桂园 房地产信用(多年地产开发商信用解套)
- 2022-09 到 2022-10 二十大重定价(-15% 再 +20% 反弹)
压测引擎把当前账户按每个情景的日因子变动重定价,报累计 P&L。CN 监管要求 (CBIRC 商业银行资本管理办法 2023 修订) 的命名情景包括 2015 A 股崩盘和 COVID 冲击,作为 CN 域内银行交易账户的强制名单。
历史回放(historical replay)
最完整的变体:对回溯窗内每个交易日(典型 1 年或 5 年或「GFC-缩放」= 2007–2009),把那一天的实现单日因子变动套到当前组合上算假设单日 P&L。本质上是历史 VaR 的单情景透明化——监管和风险官能直接看「哪一个具体日子在当前账上造成最大单日亏损」,而不只是一个 99% 分位数。
假设情景(hypothetical scenario)
「如果这个因子动一下」层。指定单因子冲击 (典型 或 );为了尊重跨因子相关,其他因子的「条件冲击」用多元正态条件均值
这样一个规模因子(小盘走弱)的负向冲击会自动连带与之正相关的低波因子等。组合在该冲击下的 P&L 是 。规范库(CN):「SHIBOR +200bps」(央行加息)、「USDCNY +5%」(贬值)、「CN 信用利差 +100bps」(地产 / LGFV 压力)、「动量 -3 sigmas」(茅指数解套)、「小盘股 +3 sigmas」(2021 后小盘反弹)。
反向压力测试(reverse stress test)
倒着问:给定目标损失 (典型 15% V_0 或监管资本阈值),解逆向问题「找到最可能的情景 使 」。最可能的判据用 Mahalanobis 距离
拉格朗日闭式解:, 由约束定;最终 。Mahalanobis 距离 是「该情景距离因子分布均值多少个标准差」——越小越可能发生。反向压力测试报告:「触发 15% 组合损失最可能的情景是一个 2.3 sigma 冲击,由规模 -1.8 sigma + 动量 -1.4 sigma + 低波 +1.2 sigma 组合而成」——最可能的灾难情景从 L2 暴露中自动浮出,不需要风险官猜。
Basel 监管演进
下面是 Inline-code listing 中按固定顺序的五条:
Basel_I_1988: credit risk only; 8% capital ratio (Cooke ratio)
Basel_II_2004: operational + market risk via standardised approach (rule-based weights) or internal-models approach (10-day 99% VaR * 3-4x multiplier rising with back-test failures)
Basel_II_5_2009: post-GFC patch — stressed VaR (re-run VaR on stressed 1y window like 2007-09), Incremental Risk Charge (IRC) for default + credit-spread, Comprehensive Risk Measure (CRM) for correlation trading, simple sqrt(10) 1d-to-10d scaling
Basel_III_2010+: liquidity coverage + net stable funding + capital buffers (structural overhaul)
FRTB_BCBS_d457_2019: replaces VaR with 97.5% ES, risk-factor-specific liquidity horizons (10/20/40/60/120 days), NMRF separate stressed charge for factors with <24 real-price observations, desk-level P&L attribution test gating IMA vs SBA; effective EU/UK Jan 2023, US Jul 2025
规则:97.5% ES is the regulatory headline: matches 99% Gaussian VaR by construction but coherent for non-Gaussian。
Basel I(1988)——只有信用风险,8% 资本比例(Cooke 比率)。Basel II(2004)——加入操作风险 + 市场风险(标准法按规则给权重;内部模型法 10 日 99% VaR × 3–4 倍乘数,乘数随回溯检验失败上调)。Basel II.5(2009)——GFC 后补丁:压力 VaR(在 2007–2009 这样的应力一年期上重跑 VaR 引擎,与日常 VaR 相加进资本)、增量风险 IRC(交易账违约 + 信用利差风险)、综合风险 CRM(相关交易簿);简单的 把 1 日 VaR 扩到 10 日。Basel III(2010 起)——流动性覆盖 LCR、净稳定融资 NSFR、资本保留缓冲、逆周期缓冲;结构性大修,非市场风险专项。
FRTB 四大新规
下面是 Inline-code listing 中按固定顺序的四条:
ES_replaces_VaR: regulatory risk measure is 97.5% expected shortfall (not 99% VaR); 97.5% ES equals 99% VaR for Gaussian by phi(z_0.975)/(1-0.975) = z_0.99; ES is coherent for non-Gaussian where VaR is not
liquidity_horizons: replace sqrt(10) with risk-factor-specific LH; 10d major FX/large-cap equity; 20d small-cap equity/major credit-spread; 40d non-major credit-spread; 60-120d illiquid credit / structured / NMRFs
NMRF_charge: factors with fewer than 24 real (committed) price observations in lookback window get separate stressed-scenario capital charge with NO diversification benefit against IMA
pnl_attribution_test: daily desk-level reconciliation between risk-system P&L and front-office P&L; Spearman correlation > 0.8 over 250 days AND KS distance < 0.09; 2 consecutive failures = SBA for next year minimum (capital ~30-50% higher)
规则:IMA gives lower capital but requires desk-level back-test compliance — failure pushes desk to SBA。
(i) ES 替代 VaR:监管市场风险度量由 99% VaR 改为 97.5% ES;选 97.5% 这个水平是因为高斯下 ——97.5% 高斯 ES 在高斯下恰等于 99% 高斯 VaR;但 ES 在非高斯下保留一致性,VaR 不能。(ii) 流动性期限:简单 替换为按风险因子的流动性期限。10 天:主要外汇 + 大盘股权益;20 天:小盘股权益 + 主要信用利差;40 天:次要信用利差;60–120 天:非流动信用 / 结构化 / NMRF。资本数按 聚合。(iii) NMRF 收费:回溯窗内实成交价观测少于 24 个的风险因子归类为 NMRF,单独按压力情景计提资本,不享受与 IMA 的多元化抵扣。NMRF 典型来自非流动信用工具、奇异衍生品参数、结构化产品定价输入。(iv) 桌面级损益归因检验:每个交易桌每日提交风控 P&L(风险系统按因子变动算的)与前台 P&L(实际成交账)的对账。两个汇总统计:过去 250 天的 Spearman 相关 > 0.8;Kolmogorov-Smirnov 距离 < 0.09。失败两个月连续 = 该桌下一年最低跌入 SBA,资本约 +30%–50%。
实现:named_scenario_stress
import numpy as np
import pandas as pd
def named_scenario_stress(
weights: np.ndarray,
B: np.ndarray,
factor_returns_history: pd.DataFrame,
scenario_dates: list,
V_0: float = 1.0,
) -> dict:
# 组合层因子暴露
b_p = B.T @ weights
# 抽取情景窗的日因子收益
f_scenario = factor_returns_history.loc[scenario_dates].values
# 假设单日 P&L
daily_pnl = f_scenario @ b_p * V_0
worst_day_idx = int(np.argmin(daily_pnl))
worst_day_pnl = float(daily_pnl[worst_day_idx])
worst_day_date = scenario_dates[worst_day_idx]
# 滚动 5 日累计
rolling_5d = pd.Series(daily_pnl).rolling(5).sum()
worst_5d_pnl = float(rolling_5d.min())
# 峰谷回撤
cum_pnl = daily_pnl.cumsum()
running_max = np.maximum.accumulate(cum_pnl)
drawdown = cum_pnl - running_max
max_drawdown = float(drawdown.min())
total_pnl = float(daily_pnl.sum())
return {
"worst_day_pnl": worst_day_pnl,
"worst_day_date": worst_day_date,
"worst_5d_pnl": worst_5d_pnl,
"max_drawdown": max_drawdown,
"total_pnl": total_pnl,
"daily_pnl": daily_pnl,
"cum_pnl": cum_pnl,
}
签名(参数名 weights / B / factor_returns_history / scenario_dates / V_0、默认 V_0=1.0、返回字典键)与 US 版逐字节一致。
实现:reverse_stress_test
下面这段 Fenced Python 代码块封装反向压力测试:给定当前权重、L1 的因子载荷 B 与因子协方差 Sigma_F、目标损失阈值 target_loss(典型 15% × V_0),通过闭式解算出最可能的因子冲击向量 。返回的 mahalanobis 距离告诉你「这套灾难情景距离因子分布均值多少个标准差」——越小代表越可能发生,即风险越高。返回的 xi_star_normalised 用因子各自标准差作单位,可以直接读出「-1.8 sigma 的动量冲击」这种自然语言。反向压力测试是机构风控官最爱用的工具之一:不像命名情景需要找到「类似 2008」的历史模板,反向压测从当前持仓的因子暴露中自动浮出最危险情景——基金经理调仓后,最危险的情景也跟着变。
import numpy as np
def reverse_stress_test(
weights: np.ndarray,
B: np.ndarray,
Sigma_F: np.ndarray,
target_loss: float,
V_0: float = 1.0,
) -> dict:
# 组合层因子暴露
b_p = B.T @ weights
factor_variance = float(b_p @ Sigma_F @ b_p)
# 拉格朗日乘子(目标损失为正数表示亏损)
lam = -target_loss / (factor_variance * V_0)
# 最可能的因子冲击
xi_star = lam * Sigma_F @ b_p
# Mahalanobis 距离(以因子分布标准差为单位)
mahalanobis = float(np.sqrt(xi_star @ np.linalg.solve(Sigma_F, xi_star)))
# 按因子 sigma 单位的标准化冲击
xi_star_normalised = xi_star / np.sqrt(np.diag(Sigma_F))
realised_loss = float(-b_p @ xi_star * V_0)
top_3_factor_indices = np.argsort(np.abs(xi_star_normalised))[-3:][::-1]
return {
"xi_star": xi_star,
"xi_star_normalised": xi_star_normalised,
"mahalanobis": mahalanobis,
"target_loss": target_loss,
"realised_loss": realised_loss,
"top_3_factor_indices": top_3_factor_indices,
}
签名(参数名 weights / B / Sigma_F / target_loss / V_0、默认 V_0=1.0、返回字典键)与 US 版逐字节一致。
实现:hypothetical_single_factor_stress(辅助)
import numpy as np
def hypothetical_single_factor_stress(
weights: np.ndarray,
B: np.ndarray,
Sigma_F: np.ndarray,
factor_index: int,
shock_sigmas: float,
V_0: float = 1.0,
) -> dict:
# 在因子 k 上施加 c sigmas 冲击
K = Sigma_F.shape[0]
xi = np.zeros(K)
xi[factor_index] = shock_sigmas * np.sqrt(Sigma_F[factor_index, factor_index])
# 其他因子的条件均值冲击(尊重相关性)
for j in range(K):
if j == factor_index:
continue
xi[j] = Sigma_F[j, factor_index] / Sigma_F[factor_index, factor_index] * xi[factor_index]
b_p = B.T @ weights
delta_pnl = float(-b_p @ xi * V_0)
return {"delta_pnl": delta_pnl, "xi": xi}
一页式机构风险报告:capstone 实现
把 L1 / L2 / L3 / L4 的全部产出压成 5 段。下面是 CN 实例的产出结构(数字示意):
============================================================
Daily Risk Report — 2024-05-15 — 沪深300 指增 1 号
V_0 = 100,000,000 RMB
Section 1 — Covariance summary (L1)
cond(Sigma_BARRA) = 45 (健康, <100)
top-5 factor by Sigma_F diagonal: 新能源 / 电子 / 食品饮料 / 金融 / 医药
total Sigma factor/specific split: 84% / 16%
Section 2 — Risk attribution (L2)
annualised sigma_p = 18.2% annualised TE vs 沪深300 = 3.8%
TE 因子/特异: 75% / 25% sigma_p 因子/特异: 84% / 16%
top-5 PCTR stocks: 宁德时代 9.1%, 贵州茅台 8.3%, 招商银行 7.2%, 五粮液 6.5%, 比亚迪 6.1%
top-3 |b_a|: 价值 +0.6 sigma; 动量 +0.3 sigma; 规模 -0.4 sigma
Section 3 — Tail risk (L3, 1 日)
95% VaR: hist 3.1M param 2.7M MC-t 3.4M
99% VaR: hist 5.8M param 4.2M MC-t 6.5M
99% CVaR: hist 7.4M param 4.8M MC-t 8.7M
flag: param 与 MC-t 在 99% 差异 55%(超 50% 阈值)
Section 4 — Stress tests (L4)
2008-GFC replay (2008-08-01 to 2008-12-31):
worst day -7.2M (2008-09-29) | worst 5d -14.5M | max drawdown -28M | total -22M
2015-A 股 crash (2015-06-15 to 2015-08-26):
worst day -8.1M (2015-07-08) | worst 5d -19M | max drawdown -38M | total -34M
hypothetical SHIBOR +200bps: delta_PnL -3.2M
reverse stress for 15% loss (15M):
most-likely scenario: 2.3 sigma (规模 -1.8, 动量 -1.4, 低波 +1.2)
Section 5 — FRTB IMA capital
ES_97.5_today (parametric) = 4.6M
mean ES_97.5 over 60 days = 4.4M
Capital_FRTB = max(4.6, 4.4) * 3.0 * 1.0 = 13.8M (13.8% of V_0)
flag: 资本占比 < 20% 阈值(通过)
THRESHOLDS: TE 4% VaR_99 3% stress drawdown 30% FRTB capital 20%
FLAGS: Section 3 (parametric vs MC-t 99% disagreement)
ALL OTHERS: PASS
============================================================
权益单一账上 FRTB 流动性期限取 10 天,LH 缩放因子 = 1.0;若该账涉及非流动信用工具,需要按 (10 / 10 + 20/20 + 60/60 + 120/120) 等更复杂聚合。
可视化:压力测试瀑布图
Formula Explorer
x^2把命名情景的日 P&L 序列画成累计曲线;反向压力测试结果画成「因子冲击条形图」(每个因子的 );把每周 / 每月的 FRTB 资本时序画成红线对比内部资本 limit 的蓝线。投委会一眼看出哪些情景超 limit、哪些月份资本占比超阈值。
练习
Exercise
你是 50 只主动权益基金的首席风险官 (cn: 50 沪深300板块龙头, V_0 = 100,000,000 RMB)。给定 (i) weights(L2 主动组合), (ii) benchmark_weights(cn: 沪深300), (iii) L1 的 Sigma_BARRA、B、Sigma_F、D, (iv) factor_returns_history(L1 的 K 因子收益时序,2008-01 到 2023-12), (v) returns_history(L3 历史 VaR 用的资产收益时序)。产出一页机构风险报告,包含以下五段。(a) Section 1 — 协方差摘要:报告 cond(Sigma_BARRA)、Sigma_F 对角排前 5 的因子(波动率排名)、总组合方差 的因子-特异比。(b) Section 2 — 风险归因(复用 L2 辅助):报告年化 sigma_p、年化 TE、两者的因子-特异比、PCTR 前 5 资产、|b_a| 绝对值前 3。(c) Section 3 — 尾部风险(复用 L3 辅助):报告 1 日 95% / 99% VaR 与 CVaR 的三方法(历史 / 参数法 / MC-t with nu=5);任一对方法差异 > 50% 打 flag。(d) Section 4 — 压力测试:调 named_scenario_stress 两次:(i) 2008 GFC 窗 (2008-08-01 到 2008-12-31)、(ii) CN 地区危机 (2015-06-15 到 2015-08-26 A 股股灾);报告 worst_day_pnl、worst_day_date、worst_5d_pnl、max_drawdown、total_pnl。调 hypothetical_single_factor_stress 一次:(iii) 利率因子 SHIBOR +200bps。调 reverse_stress_test(weights, B, Sigma_F, target_loss=0.15 * V_0, V_0=V_0):(iv) 15% 组合损失;报告 mahalanobis、前 3 因子索引及其 xi_star_normalised;一句话写出最可能的灾难情景(比如「2.3 sigma 情景:动量 -1.8 + 规模 -1.4 + 低波 +1.2」)。(e) Section 5 — FRTB IMA 资本:在 Sigma_BARRA 上用参数高斯算过去 60 天滚动 97.5% ES(sigma_L * phi(z_0.975) / (1 - 0.975));算 Capital_FRTB ≈ max(ES_97_5_today, mean(ES_97_5_over_last_60_days)) * 3.0;报告 Capital_FRTB 占 V_0 百分比。把整套报告排成一张「Daily Risk Report — [date] — [fund name]」一页;任一段超过内部阈值 (TE > 4%, VaR_99 > 3% V_0, 压测 max drawdown > 30% V_0, FRTB 资本 > 20% V_0) 必须 flag。
提示
xi_star 是因子收益单位;转成「因子 sigma 单位」要除以 np.sqrt(np.diag(Sigma_F)),这样才能直观说「-1.8 sigma 的动量冲击」。提示
桥接到 4.4.3
4.4.2 到此结束:L1 给出结构化协方差(Barra 分解),L2 把它读成风险归因(MCTR / TE / b_a / IR),L3 切到分位数风险(三方法 VaR + CVaR + Artzner 一致性),L4 端到端串成机构生产风险体系(四种压力测试 + Basel/FRTB 监管框架 + 一页式报告)。模块 4.4.3《约束式组合优化》(Constrained Portfolio Optimisation)拿 L2 的 PCTR 当风险预算约束的输入,拿 L3 的 CVaR 当尾部风险约束的输入,拿 L4 的因子暴露当因子中性约束的输入,在 CVXPY 中显式求解带交易成本、换手率、行业 / 国家 / 因子上下限的实盘可行组合。简言之:4.4.1 给 MV 理论,4.4.2 给风险度量,4.4.3 给可执行优化——三者构成「组合构造与风险」(Subject 4.4)的完整三角。