第 33 章/ 45
风险管理(Risk Management)
主线:把策略持仓变成带方向、单位、期限与条件的损失分布,找出损失由什么暴露产生、在什么情景下放大,并把风险约束传回下一次可执行的目标权重。 先修:第 15 章因子模型、第 18 章波动率、第 21 章组合暴露、第 29 章账本、第 30 章偏差、第 31 章验证、第 32 章推断;完成后你能定义 VaR 与 ES、复算离散尾部、区分不同风险来源、重估真实账本持仓、设置流动性与保证金约束,并正确解释一次 VaR 回测。
1. 本章目标
完成本章后,你应能从一个明确的持仓和收益频率出发,写出损失变量,并始终同时标注置信水平与持有期限;按分位数函数定义风险价值(Value-at-Risk,VaR),按分位积分和阈值原子概率定义预期尾部损失(Expected Shortfall,ES;原稿亦称预期损失,本章专指尾部均值,区别于信用风险的Expected Loss);说明何时 ES 不等于严格尾部或包含阈值的条件均值;在同一组合上比较历史、正态、Student-t 与 Monte Carlo 估计;分开报告净暴露、总暴露、杠杆、集中度、因子暴露、可清算天数和保证金;将情景冲击施加到第 29 章的实际持仓;用训练窗之外的数据统计 VaR 超越并解释 Kupiec 无条件覆盖检验的能力边界;最后把风险限额转成第 29 章可以执行的下一期目标权重。
本章的完成标准不是记住一个“安全数字”。你应能从输入复算输出,指出参数来自何处,区分构造情景和经验数据,检查时间可得性,并说出指标不能回答的问题。正文算例数字都可以在 CLI 产物 ch33_results.json 中找到;每组算例后列出对应 JSON 键,便于逐项对账。
2. 为什么 Quant 需要它
第 29 章已经把目标权重转换成带有信号日期、延迟成交、费用、现金和持仓的净值账本。第 30 章说明,时间戳和成交假设写错时,净值可以在没有程序报错的情况下被抬高。第 31 章要求把模型选择和最终评估分开,第 32 章提醒我们不能把一条表现好的收益曲线直接当作未来表现的证据。本章承接这些结果,回答一个更具体的问题:已经生成的持仓,在一个明确的时间跨度内会损失多少?损失可能来自方向、集中、共同因子、尾部、流动性,还是融资约束?
研究者容易把风险简化成波动率,随后再乘以一个经验系数。这样做可能遗漏偏度、厚尾、跳跃、持仓集中、相关性变化、跳空、交易冲击和保证金。一个模型给出低 VaR,并不意味着极端损失轻微;两个组合有相同方差,也不意味着它们的 ES 或清算路径相同。风险管理也不能只留在报告里:如果压力情景损失超过限额,目标仓位要下降;而目标权重只能在信号产生后、允许的成交时点执行,不能回写到已经发生的价格上。
本章使用三类证据,不能混为一类。概率分布和参数模型是模型假设;固定输入和随机种子得到的模拟是可复现的教学构造;冻结行业收益是实际发布过的历史序列,但属于修订后回溯数据。本章没有把任何一种证据包装成完整风险答案。
3. 直觉
先只看一个离散损失变量。亏损记为正数,收益记为负损失。可能的结果是损失 −10、0、5、20,相应概率为 0.50、0.30、0.15、0.05。置信水平
ES 要回答更难的问题:最差的尾部区间平均损失有多大?尾部概率为 1−
这个例子先给出本章的核心:VaR 是一个分位点,ES 是一段分位区间上的平均;分布在阈值处有原子概率时,必须只取阈值质量中构成尾部所需要的那一部分。CLI 把这组输入展开为 20 个等权观察,保存累积分布值、VaR、ES、严格阈值均值和包含阈值均值。完整的分位数阶梯与尾部面积见图 ch33_discrete_quantile_es.png。
JSON 对照:pinned_discrete_case.loss_values、probabilities、alpha、scenario_count、cdf_at_var、var、es、strict_tail_conditional_mean、inclusive_tail_conditional_mean。
4. 数学基础
令
因此
本章计算都要同时注明
4.1 风险输入的量纲账
| 对象 | 单位或频率 | 代入前要固定的条件 |
|---|---|---|
| 账户货币,同一h | 正数为亏损,估值对象和持有期限相同 | |
| 无量纲,同一h | 分母NAV明确;不能与货币值直接比较 | |
| 无量纲置信水平 | 与h一起报告;99%日频不等于95%月频 | |
| 股、货币/股、无量纲 | ||
| 无量纲 | 同一时点NAV归一,现金单列,不再除一次NAV | |
| 股/交易日、无量纲、交易日 |
在NAV正且没有外部现金流的同一估值区间,
记损失的累积分布函数为
5. 正式定义
置信水平
这是一个损失阈值。它不描述阈值之后损失有多严重,也不表示这个损失“绝不会超过”。若某日损失超过 VaR,则发生一次超越;超越率用于回测时,只有在相应预测分布连续或对阈值原子采用了明确一致的随机化规则时,理论概率才直接等于
本章的 ES 定义为分位数函数在右尾置信区间上的平均:
如果
右尾 ES 需要
6. 推导
从分位积分看离散例子,分位数函数
再从概率质量看。严格超过阈值
对等权样本
数学中的上取整是在精确实数下定义的。实现时不要直接依赖浮点乘积 alpha * n 的上取整:0.14 * 100 会略大于14,而经验累积概率 14 / 100 已达到0.14。核心函数直接寻找首个满足 k / n >= alpha 的阶次,回归测试还检查紧邻0.14上方的概率应选择下一阶次。
这相当于对阈值处的样本质量作分数加权。实现时应先验证输入是非空有限实数向量,并验证
6.1 连续模型的尾积分
对连续的
正态标准密度满足
若
若改用位置0、尺度1(并非单位方差)的 Student-t 随机变量
写
当nu>1,g在正无穷趋于0,故
代入连续尾积分即得上式;nu>1保证尾部均值可积。只有把输入sigma解释为标准差并匹配方差时才需nu>2,不能混同这两个条件。
Student-t 的均值和 ES 在自由度大于 1 时存在;方差仅在自由度大于 2 时有限。本章传入的是均值和标准差,因此实现要求
6.2 Kupiec无条件覆盖:从似然到检验
令
对0<x<n,
用
7. 金融解释
组合风险不是一个数。令
对单个市场因子,组合 beta 可写为
杠杆还要连同融资规则读。现金收益、借券费、返佣、融资利率、抵押折扣、交易冲击和保证金会改变现金路径与可持有规模。示例账本明确把现金每期利率设为 0;借券费、返佣和超出现金利率的保证金融资没有建模。这个简化不能解释成真实融资成本为零。初始保证金是开仓时要求的资金,维持保证金是持仓继续存在时不能跌破的资金水平;参数若是构造值,必须明确标识,不能冒充券商规则。
压力情景是“如果指定条件发生,账面结果怎样”,不是“这种情况有多大概率”。对数量
流动性可以先用一个透明近似:
8. Numerical Example
9. Python Implementation
核心函数在 risk.py,按用途分为:
- 分布与尾部:
empirical_var_es、normal_var_es、student_t_var_es、simulate_student_t_losses。 - 重估与验证:
scenario_pnl、lagged_rolling_var、kupiec_pof。 - 退出与融资:
liquidation_days、margin_requirements。
本章 CLI 在 ch33_risk_management.py,复用第29章账本、现有暴露报告函数和冻结French数据读取器。
默认读者从接受I3包根目录运行:
python run_notebook.py notebooks/ch33_risk_management.ipynb
I3的 analyze(include_french=False) 默认跳过French;Notebook fresh计算3000个构造历史观察与50000个Monte Carlo损失,写结果JSON并生成4图。它创建唯一 runs/ch33_risk_management_<uuid>/;I3 CLI使用自己的 runs/cli_ch33_<uuid>/。runner还保存执行副本、execution.json和失败状态到 runs/execution_<stem>_<uuid>/。因此不能把该Notebook称为不写实验文件的静态回放。本批只静态核入口,不执行完整Notebook/CLI。工作源 code/ch33_risk_management.py 的 analyze() 仍无include_french参数并总调用French,与I3包装不同;不能将两入口混称已完全同步。英语Notebook、装配和冷启动留BIL23。若另获运行授权,须指定新输出目录,不能覆盖冻结qa或图。
核心输入按函数分别检查。scenario_pnl(简单收益情景重估)拒绝非正价格和低于−100%的简单收益;经验损失向量和无界Student-t参数损失允许大于1,属于另一模型域,不对其硬裁剪。Student-t API按标准差输入要求nu>2。损失货币/比例、股数、USD/股、ADV股/交易日及持有期均需明确。相关测试入口是 tests/test_ch33_risk.py 和 tests/test_ch33_workflow.py;本批仅执行新显示小例的局部核验,历史完整workflow不重跑。
10. Visualization
本章 CLI 生成四张图,文件名由结果 JSON 的 artifacts.figure_files 给出。
离散分位数与 ES 尾部面积

横轴是无量纲分位水平
同组合风险方法比较

横轴是组合每月简单收益的负值,纵轴是每单位损失的密度。样本包括构造历史损失和按拟合 Student-t 参数生成的 Monte Carlo 损失;曲线为同一组合拟合的正态与 Student-t 密度,竖虚线为四种方法的 VaR。观察尾部位置时要同时看 ES 表,密度图的横向范围不能证明模型外的尾部一定如何。
账本隔夜压力贡献

横轴为第 29 章教学账本资产,纵轴为 USD 情景 PnL,正数为收益、负数为损失。图内相关性文字是构造状态假设,不是事件概率。
分窗 VaR 回测

横轴为检验窗内月度观察序号,纵轴为 NAV 比例损失。VaR 水平只用前一段训练窗估计,红点显示严格超过阈值的观察。图注中的样本数、超越数与 p 值均由同一 JSON 结果生成。
图不能替代参数、样本范围、费用和时间戳说明;若需要改动输入,应使用新输出目录重新运行,避免静默覆盖已有图和数值。
11. Real Market Example
除构造样本外,CLI 读取项目中冻结的 Kenneth R. French Data Library 十行业月度价值加权组合。输入快照日期为 2026-10-05,样本从 1926-07 到 2026-08,共 1202 个月、十个行业组合;每个月对十个发布组合等权,使用简单月收益。第一个 120 个月(1926-07 至 1936-06)作为估计窗,余下 1082 个月(1936-07 至 2026-08)作为检验窗。训练期 95% VaR 为 0.14302,ES 为 0.20204833;检验期间有 5 次超过,Kupiec LR 统计量为 76.69493441,渐近 p 值为 1.995144691255254e−18。可复算字段见 JSON 的 french_real_example。
这是实际发布过、但现在按当前版本重修订的行业组合时间序列。它没有 point-in-time 版本、历史接收时间戳或投资者可交易成分组合的真实成交;月标签表示被衡量的日历区间,不代表当月收益在月末之前可知。数据也不是某个策略的交易账本。本例只能用于说明一套历史窗口 VaR 与检验窗超越计数在此序列上的行为,不能据此推导实时策略表现、交易能力或可交易回测结论。原始、抽取、处理行业数据以及 FF3 数据的六项 SHA-256 在 french_real_example.sha256,下载来源和转换记录在 data/manifests/french_industries_2026-10-05.json 与 FF3 manifest。下载和使用仍须遵守数据提供方条款;本章不把公开可下载等同于任意再分发许可。
12. Common Mistakes
把正收益和正损失写反。 组合收益为正意味着盈利,但本章的 scenario_pnl 返回的是货币 PnL,不是损失。
把 VaR 当成最大损失,或把 ES 写成阈值条件均值。 VaR 不是保证上限。分布在阈值处有概率质量时,严格超过 VaR 和大于等于 VaR 的条件均值都不等于分位积分 ES。以下代码从结果 JSON 自动构造同一经验样本,打印正确值和两个错误替代值;它能从仓库根目录运行:
import json
import sys
from pathlib import Path
import numpy as np
ROOT = Path.cwd()
sys.path.insert(0, str(ROOT / "src"))
from quant_course.risk import empirical_var_es
result = json.loads((ROOT / "qa/a6/codex_resume/ch33/results/ch33_results.json").read_text())
case = result["pinned_discrete_case"]
counts = np.rint(np.asarray(case["probabilities"]) * case["scenario_count"]).astype(int)
losses = np.repeat(case["loss_values"], counts)
var, es = empirical_var_es(losses, case["alpha"])
print(var, es, losses[losses > var].mean(), losses[losses >= var].mean())
输出分别是 VaR、分位积分 ES、严格尾均值和包含阈值的均值;它们的逐项结果在 pinned_discrete_case。实现错误通常不是排序本身,而是忽略了阈值点中只能取一部分概率质量。
把高波动等同于厚尾。 增加正态标准差会抬高所有尾部损失,但形状仍是正态。正文的方差匹配反例显示,即便把正态 VaR 拉到 Student-t VaR,正态 ES 仍偏低。报告模型时要写明分布、参数口径、估计窗和频率,不能只写“采用保守波动率”。
把美元中性当成无风险,或把 gross 与 net 混成一个比例。 多空市值可能抵消,但总敞口、beta、因子、行业、借券成本和挤兑风险仍存在。空头与多头在同一行业的净和为零,并不说明个别证券容易平仓。应同时报告有符号净额和绝对总量,并说明各自分母。
把情景损失写成概率,把 ADV 公式写成执行保证。 固定收益冲击只描述条件结果。相关性假设、跳空幅度和流动性折扣是构造输入;若未指定校准模型和事件频率,就不能声称“概率为某值”。
把同一观察既用于估 VaR 又用于检查超越。 这会让阈值适应已发生损失,产生向内看的偏差。固定历史 VaR 必须先用训练窗估值,再在后续独立检验窗数超越;滚动 VaR 在目标期
把 POF 的不拒绝说成模型已被证明正确。 Kupiec 检验只检验无条件超越率是否与
13. Quant Research Application
把风险工作放在研究流程里,首先冻结风险问题,而非先选统计包。写清研究组合、币种、损失符号、置信水平
其次检查收益输入和账本一致。可得时点应早于信号形成,信号形成早于成交;市场价格、公司行为、现金、费用、持仓数量和收益频率都要对齐。第 29 章账本的目标权重不等于当前实际权重,压力重估应取正确日期的实际股数和标记价。若组合有期权或非线性头寸,
然后安排互补但不同的问题:用净/总暴露、beta 和因子暴露定位风险源;用 VaR 报告给定置信水平的阈值;用 ES 观察尾部严重度;用集中度识别单一头寸支配;用 ADV 粗筛退出时间;用联合压力情景衡量条件下的账面损失;用保证金和融资假设检查现金约束。方法间差异不是让分析者挑最好看的结果,而是暴露模型风险的线索。厚尾参数模型更高的 ES,不等于它就是真实数据生成过程;历史样本给出的阈值,也不会自动覆盖从未出现的事件。
对模型回测,先分开估计窗和检验窗。示例用一段构造的月收益估 VaR,然后在不重叠的后续窗口统计严格超过次数。结果有 74 次超越,相对于原假设期望数 75 次,LR 统计量为 0.01409458、渐近 p 值为 0.9054967146341367。这只说明该特定检验窗内超越总数未提供强烈的无条件覆盖反证;它不证明尾部金额、独立性和稳定性正确。Kupiec 原文指出尾部验证功效有限;本章只采用 Bernoulli 次数似然,不照搬其关于检验性质的旧措辞。若业务关注成簇超越,需另设独立性检验并先核读相应原文和有限样本性质。
风险限额要落到下一期动作。示例中条件损失上限为当前 NAV 的 0.08,压力损失超过上限,故把当前仓位按线性情景损失比例缩小,再把目标送入账本按下一交易日价格成交。执行有一个账本间隔,并存在费用和价格差异;收盘实际权重不等于信号目标。研究报告应留存限额触发值、输入持仓、缩放系数、成交数量、费用和交易后复评结果。若成交成本使目标无法达到,或清算天数超过退出约束,需采用更保守的可成交目标,而不是只调整文字说明。
历史数据例使用 French 十行业发布组合,只用于方法演示。当前修订版的行业数据没有历史“何时收到”记录,训练与检验分窗也不能倒推投资者在早期实际能拿到现有版本。研究要作实时可交易结论时,需要 point-in-time 数据、资产构成和交易成本证据;这部分当前示例不提供。
13.1 零超越:点概率与检验p值分开
33-L04另设99%一日VaR与250个事先预测日。在独立且每天超越概率0.01的原假设下,
14. Chapter Summary
风险报告应把“持仓是什么”“损失如何定义”“用什么信息估计”“在什么情景下重估”“阈值如何影响下一次交易”连成一条可复核链。先给方向、单位、置信水平和期限;再报告 VaR 与 ES,并在离散分布中保留阈值处的分数尾部权重;再拆分净/总杠杆、beta、因子、行业、集中、流动性、融资和保证金;随后用实际持仓而非理想目标做条件压力;接着用隔离的检验窗评估超越率;最后将限额转为可按信号与成交时间执行的目标,并按实际成交重评。
一个可读的风险句子至少包括:报告对象和时点、损失符号与单位、置信水平与期限、方法与训练数据、VaR 和 ES、主要因子与集中度、压力情景和其条件性质、流动性及保证金假设、样本外超越数和检验限制、限额触发后的目标与实际成交。只呈现 VaR 一个数,或只报告一个历史回测 p 值,均不足以描述策略可以承受的损失。
15. Exercises
33-E01(离散阈值) 使用 JSON 的 pinned_discrete_case。写出 VaR 所在损失点的累积分布,手算 ES 的两个尾部贡献,并解释为什么严格超越均值与包含阈值均值不是 ES。
33-E02(分布形状) 使用 same_portfolio_risk_methods.variance_matched_tail_counterexample。说明比较正态与 Student-t 时保持了什么,改变了什么;解释为什么提高正态标准差可以匹配 VaR,却仍无法匹配 ES。
33-E03(暴露) 使用 exposures。分别说明净杠杆、总杠杆、现金权重、Market 因子暴露、Value 因子暴露和 HHI 的含义。再用 neutrality_counterexample 说明净美元中性与 beta 中性、行业中性之间的区别。
33-E04(压力与退出) 使用 ledger_stress_and_limit。从股数和价格复算两个资产的情景 PnL;比较正常与压力 ADV 下的退出天数;解释保证金余量为正与风险限额已经触发并不矛盾。
33-E05(覆盖检验) 使用 risk_model_backtest。列出估计窗与检验窗边界,复述检验窗超越数、LR 和 p 值。说明不能由“未拒绝”推出的至少两项结论,并解释滚动预测第一期为何只能用其之前的观察。
16. Programming Exercises
33-P01(分数尾部实现) 从结果 JSON 读取离散输入,调用 empirical_var_es,动态重算 VaR、ES 和两个错误替代均值。用断言验证结果键。测试 NaN 与非法置信水平必须触发输入错误。
33-P02(暴露复核) 从 CLI 中读取 teaching_inputs.ledger_stress 和 JSON 输出,调用现有 exposure_report。复核净暴露、总暴露与 beta,并验证美元中性反例仍有非零 beta 和总暴露。
33-P03(限额到成交) 从 JSON 读取真实账本信号日股数、标记价、压力收益、NAV 和上限,调用 scenario_pnl 重算压力损失及限额缩放系数。复核 CLI 的执行卖单符号,并指出目标权重和成交后权重为何可以不同。
33-P04(Kupiec 边界) 从 risk_model_backtest 读取 kupiec_pof 重算已观测结果,并复算零次与全体超越的边界似然。所有统计量应有限;解释边界的
33-P05(滚动无泄漏) 使用第18节给定的8项确定性小损失序列(不调用完整CLI或analyze),调用 lagged_rolling_var。核验第一项预测只接收紧邻目标期之前的窗口、预测长度正确,且任何预测都没有包含其目标期损失。
17. Challenge Problems
33-C01(原子质量与风险测度) 对 pinned_discrete_case 写出分位数阶梯函数并证明分数阈值公式等于分位积分。然后讨论 ES 的结论如何依赖损失正部可积。说明若把数据取负改成利润符号,哪个尾部积分和符号必须相应调整。
33-C02(无条件覆盖的遗漏) 以 risk_model_backtest.test_window 的超越数与样本量构造两种不同排序的超越序列,使其具有相同样本超越比例但呈现不同聚集程度。证明 Kupiec POF 对二者给出同一似然比统计量,并提出需要额外核读和实现的检验问题。
33-C03(真实序列的可用时点) 使用 french_real_example 写一个最小研究复核计划,分别标注现有数据可以支持和不能支持的结论。提出能够使历史模拟具有 point-in-time 解释所需的数据字段与成交证据,并给出继续、暂停或拒绝策略结论的标准。
18. Solutions / Solution Hints
33-E01 解:累积概率在损失 −10、0、5、20 处依次为 0.50、0.80、0.95、1.00,故达到 0.90 的最小损失点是 VaR=5。尾部区间长度为 0.10;阈值 5 的质量有 0.15,但在该尾部内只需取 0.05。超越阈值的 20 也有 0.05。因此 ES=(5×0.05+20×0.05)/0.10=12.5。严格大于 VaR 的条件均值只剩 20;大于等于 VaR 的均值把 0.15 的阈值质量全部纳入,得到 8.75。JSON 核验键是 pinned_discrete_case。
33-E02 解:比较维持了同一组合、同一损失均值和同一标准差,把尾部形状从正态换成自由度 5 的 Student-t。正态 VaR/ES 分别为 0.09291625 和 0.10669587,t 模型为 0.10430684 和 0.13856101。把正态标准差增至 0.04556023,可匹配 t 模型 VaR;相应正态 ES 只到 0.11974567,仍小于 t ES。原因是正态尾部衰减形状不变,单一尺度只能沿同一族分布伸缩,不能复制更厚尾分布的尾部形状。此反例不能证明所有样本都由 t 分布生成,构造规则和种子见 teaching_inputs.portfolio_dgp。
33-E03 解:目标风险权重净和与绝对值和均为 0.80,所以净杠杆和总杠杆分别为 0.80,现金权重为 0.20。Market 暴露计算为
33-E04 解:PnL 公式为 ledger_stress_and_limit.liquidity;ADV 收缩会让退出周期按比例拉长。维持保证金余量为正是一个以构造比率和情景净值计算的局部结果,而损失上限单独比较条件损失和 NAV 比例;一个指标未超并不能抵消另一个限额已触发。此例维持保证金余量为正 381.3898 USD,风险限额仍被突破。
33-E05 解:训练窗索引为 [0,1500),检验窗为 [1500,3000),没有重叠。检验窗有 74 次超越;LR 为 0.01409458,p 值为 0.9054967146341367。非拒绝不证明尾部严重程度准确、不证明超越独立,也不证明分布稳定或极端压力已覆盖。滚动窗口目标索引为 250 时,允许的信息只有索引 0 到 249;将目标损失放进其自己的阈值估计会让预测利用未来结果,构成时间泄漏。所有字段位于 risk_model_backtest。
33-P01 参考实现(从仓库根目录执行):
import json
import sys
from pathlib import Path
import numpy as np
ROOT = Path.cwd()
sys.path.insert(0, str(ROOT / "src"))
from quant_course.risk import empirical_var_es
result = json.loads((ROOT / "qa/a6/codex_resume/ch33/results/ch33_results.json").read_text())
case = result["pinned_discrete_case"]
counts = np.rint(np.asarray(case["probabilities"]) * case["scenario_count"]).astype(int)
losses = np.repeat(case["loss_values"], counts)
var, es = empirical_var_es(losses, case["alpha"])
assert np.isclose(var, case["var"]) and np.isclose(es, case["es"])
assert losses[losses > var].mean() == case["strict_tail_conditional_mean"]
assert losses[losses >= var].mean() == case["inclusive_tail_conditional_mean"]
for bad_values, bad_alpha in (([np.nan], case["alpha"]), (losses, np.nan), (losses, 0.0), (losses, 1.0)):
try:
empirical_var_es(bad_values, bad_alpha)
except (TypeError, ValueError):
pass
else:
raise AssertionError("non-finite input must be rejected")
print(var, es)
断言覆盖原子阈值修正。测试另外验证 NaN 和
33-P02 参考实现(从仓库根目录执行):
import importlib.util
import json
from pathlib import Path
import numpy as np
ROOT = Path.cwd()
spec = importlib.util.spec_from_file_location("ch33", ROOT / "code/ch33_risk_management.py")
ch33 = importlib.util.module_from_spec(spec)
spec.loader.exec_module(ch33)
from quant_course.portfolio_construction import exposure_report
result = json.loads((ROOT / "qa/a6/codex_resume/ch33/results/ch33_results.json").read_text())
inputs = ch33.INPUTS["ledger_stress"]
weights = np.asarray(inputs["initial_target_weights"])
report = exposure_report(weights, inputs["betas"], inputs["sectors"])
saved = result["exposures"]
assert report.net == saved["net_exposure"]
assert report.gross == saved["gross_exposure"]
assert report.beta == saved["market_beta"]
neutral = saved["neutrality_counterexample"]
nw = np.asarray(inputs["neutrality_example_weights"], dtype=float)
nb = np.asarray(inputs["neutrality_example_betas"], dtype=float)
assert np.array_equal(nw, [0.5, -0.5]) and np.array_equal(nb, [1.5, 0.3])
net, gross, beta = float(nw.sum()), float(np.abs(nw).sum()), float(nw @ nb)
assert net == 0 and gross == 1 and np.isclose(beta, 0.6)
assert np.isclose(net, neutral["net_leverage_on_nav"])
assert np.isclose(gross, neutral["gross_leverage_on_nav"])
assert np.isclose(beta, neutral["beta"])
print(saved["factor_exposures"], saved["concentration_hhi_within_gross_risk_assets"])
此复核从已存在的暴露接口计算,不新增引擎;行业、beta 和 HHI 仍须分别解释。
33-P03 参考实现(从仓库根目录执行):
import json
import sys
from pathlib import Path
ROOT = Path.cwd()
sys.path.insert(0, str(ROOT / "src"))
from quant_course.risk import scenario_pnl
result = json.loads((ROOT / "qa/a6/codex_resume/ch33/results/ch33_results.json").read_text())
case = result["ledger_stress_and_limit"]
names = list(case["positions_at_signal_shares"])
pnl = scenario_pnl(
[case["positions_at_signal_shares"][a] for a in names],
[case["mark_prices_at_signal_currency_per_share"][a] for a in names],
[case["overnight_gap_simple_returns"][a] for a in names],
)
loss = -float(pnl.sum())
risk = case["risk_limit"]
scale = risk["loss_cap_currency"] / loss
assert round(loss, 4) == case["conditional_stress_loss_currency"]
assert round(scale, 7) == risk["scale_factor"]
assert all(fill["quantity"] < 0 for fill in risk["actual_execution_fills"])
print(loss, scale, risk["target_weights_after_limit"], risk["actual_close_weights_after_execution"])
卖单数量为负。先按信号日的实际持仓股数、当前标记价和给定收益冲击计算当前压力损失,再用损失上限除以该损失求缩放系数,并据此生成新目标权重。随后成交的股数、执行价格、费用与时间间隔共同影响交易后权重,因此成交后权重可能偏离信号目标。
33-P04 参考实现(从仓库根目录执行):
import json
import sys
from pathlib import Path
ROOT = Path.cwd()
sys.path.insert(0, str(ROOT / "src"))
from quant_course.risk import kupiec_pof
result = json.loads((ROOT / "qa/a6/codex_resume/ch33/results/ch33_results.json").read_text())
backtest = result["risk_model_backtest"]
test = backtest["test_window"]
calculated = kupiec_pof(test["realized_exceedances"], test["observations"], backtest["alpha"])
for count in backtest["boundary_count_checks"].values():
if isinstance(count, dict):
edge = kupiec_pof(count["exceedances"], count["n_obs"], backtest["alpha"])
assert edge["lr_uc"] == count["lr_uc"]
assert edge["p_value"] == count["p_value"]
assert abs(calculated["lr_uc"] - test["lr_uc"]) < 1e-8
assert calculated["p_value"] == test["p_value"]
print(calculated)
kupiec_pof 对
33-P05 参考实现(局部确定性输入,不调用完整analyze):
import numpy as np
from quant_course.risk import lagged_rolling_var
# 定义小构造器,替代完整3000观察/MC/French研究入口。
def constructed_losses():
return np.array([0.0, 0.2, -0.1, 0.4, 0.1, 0.3, -0.2, 0.5])
losses = constructed_losses()
window, alpha = 3, 0.75
actual = lagged_rolling_var(losses, window, alpha)
expected = []
for t in range(window, len(losses)):
past = sorted(float(v) for v in losses[t-window:t])
# 独立次序统计量选择,不调用empirical_var_es重复实现。
k = next(k for k in range(1, window+1) if k/window >= alpha)
expected.append(past[k-1])
changed = losses.copy()
changed[t:] += 10.0
altered = lagged_rolling_var(changed, window, alpha)
np.testing.assert_array_equal(altered[:t-window+1], actual[:t-window+1])
np.testing.assert_allclose(actual, expected, rtol=0, atol=1e-14)
np.testing.assert_allclose(actual, [0.2, 0.4, 0.4, 0.4, 0.3], rtol=0, atol=1e-14)
assert len(actual) == len(losses)-window
assert expected[0] == max(losses[:3])
print(actual)
返回第j项对应目标位置t=window+j;每次独立只用[t−window,t)窗口。改变t及其后的损失不改变到t为止的预测。本例覆盖明确有限窗口,不证明任意数据/特征通道无泄漏;真实缺月日历仍须另定义合法月历。原题保留“确定性构造输入”目的,此处提供显式最小输入,避免调用会触发French与Monte Carlo的完整CLI。
33-C01 参考论证:经验分布的分位函数在每个概率质量区间保持常数。尾部积分把严格阈值上方的每段矩形面积相加,再加上阈值点右侧所覆盖的那一段面积;用阈值点值乘
原子端点处,lower-quantile取负需要对应一侧约定,不能逐点直接等同;积分端点的零测度不改变这个ES等式。参考 Acerbi–Tasche 的 Definition 2.6、Proposition 3.2 和 Proposition 4.1(已读范围见来源说明)。
33-C02 参考论证:从检验窗构造两个长度为 JSON 中 test_window.observations 的 0/1 序列;二者都含 test_window.realized_exceedances 个 1,一个把 1 分散在全窗,一个把 1 连续放在一起。两序列的样本比例
33-C03 参考论证:现有数据可支持对当前快照所含的发布月度行业回报进行样本切分、组合化和超越计数;不能支持历史时点的可得性、当时成分、执行价格、容量、费用、可交易策略收益或未来风险。point-in-time 复核至少需要每一版本的发布日期与接收时间、修订前后值、字段口径、发布日期时区、历史行业成员和权重、信号可得时点、下单/成交记录以及费用和市场容量证据。若版本不可追溯,应暂停实时结论;若仍以当前修订值回填信号,则拒绝把它称为无泄漏历史部署证据。此题不要求研究结论一定为正;评分应奖励对“描述历史序列”和“可交易部署”两种问题的明确区分。
19. Further Reading
Must Know:Acerbi 与 Tasche,On the Coherence of Expected Shortfall,历史作者记录阅读 Definition 2.6、Proposition 3.2、式 (3.3) 和 Proposition 4.1 相关页。用途是理解分位积分和离散阈值处的 ES 定义;阅读范围不是全文。Should Know:Kupiec,Techniques for Verifying the Accuracy of Risk Measurement Models,FEDS 95-24,历史作者记录阅读单日监测、失败计数似然、二项似然以及历史模拟估计误差所对应的选页。用途是理解无条件覆盖检验的设定和尾部验证功效边界;应牢记这是 FEDS 讨论材料,所读不是期刊论文全文。- Advanced(未核读):Christoffersen 条件覆盖与超越独立性方法。正式使用其检验前,应核读原文并说明模型假设、校准和边界计数处理。
Optional:SciPy scipy.stats.t 官方文档。用途是查 ppf、pdf、rvs、loc 和 scale 的接口语义;它不是 ES 理论推导来源。- Basel 市场风险资本规则与监管 backtesting 指引在本次写作中未核读,正文不提出资本合规或监管阈值结论。
20. References
[AT2002-v5]Carlo Acerbi and Dirk Tasche, “On the Coherence of Expected Shortfall,” arXiv:cond-mat/0104295v5, 2002. https://arxiv.org/pdf/cond-mat/0104295v5. 本章引用范围:离散尾部原子修正、分位积分、尾部样本平均的相关定义与论证;局部核读,具体页码见 source_notes.md。[KUPIEC1995-FEDS95-24]Paul H. Kupiec, “Techniques for Verifying the Accuracy of Risk Measurement Models,” Finance and Economics Discussion Series 95-24, Federal Reserve Board, cover May 1995; interior title page April 1995. https://fraser.stlouisfed.org/files/docs/publications/bog_feds/feds_1995-024.pdf. 本章只采用所读选页中的监测设定和计数 likelihood,不把原文所有论断照搬为一般结论。[SCIPY-T]SciPy v1.18.0 Manual, scipy.stats.t. https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.t.html. 本章只据此确认 Student-t 分布 API 与位置/尺度参数化。[FRENCH-FROZEN]Kenneth R. French Data Library, 10 Industry Portfolios, value-weighted monthly block; FF3 risk-free series. Industry data: https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/ftp/10_Industry_Portfolios_CSV.zip; details: https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/Data_Library/det_10_ind_port.html. 课程冻结快照、下载时间、授权说明、处理字段和校验值见对应 data/manifests JSON;其历史值为当前修订版本。[A6-33-PLAN]项目本地冻结计划qa/a6/claude/plan.md与A6_CONTRACT.md的第 33 章合同条款。用于课程接口、符号和教学边界,不作为外部实证证据。
以上每个公式或经验结果对应的实际读取范围、方法用途与限制集中记录在 来源说明。