如何用 Python 计算 VaR 和 CVaR(在险价值)?历史模拟法 + 正态法两种口径
如何用 Python 计算 VaR 和 CVaR(在险价值)?
一句话回答: VaR(Value at Risk,在险价值) 回答「在 95% 的置信度下,持有这只股票一天最多亏多少」;CVaR(条件在险价值,又叫 Expected Shortfall) 更狠,回答「一旦真的摔进最差的那 5%,平均亏多少」。用 AlphaFeed 取前复权日线算出日收益序列,用历史模拟法(直接取收益分布的分位)或参数法(假设正态)几行就能算出来——CVaR 永远 ≥ VaR,因为它盯的是更深的尾部。
为什么会有这个问题 / 传统做法的痛点
「这只票风险大不大」太模糊。标准差把涨和跌一视同仁,最大回撤只看历史最惨一次。VaR 把风险翻译成一句人话——「95% 的日子里,一天亏不超过 X%」,是银行、基金做风控的通用语言。但 VaR 有个著名缺陷:它只告诉你「亏不超过 X」,不告诉你突破这条线后会多惨。2008 年很多机构栽在这——于是 CVaR(尾部平均损失)成了更稳健的补充。
分步骤解决(每步配可运行代码)
第 1 步:取前复权日线,算日收益
from alphafeed import AlphaFeed
import numpy as np
af = AlphaFeed(api_key="your-api-key") # 或 export ALPHAFEED_API_KEY 后 AlphaFeed()
df = af.klines.get("600519.SH", period="1d", count=500,
adjust="forward", to_dataframe=True)
df = df.sort_values("trade_date").reset_index(drop=True)
ret = df["close"].pct_change().dropna() # 日收益率
必须前复权:除权缺口会被当成一次暴跌,污染收益分布与尾部。
第 2 步:历史模拟法 VaR / CVaR
alpha = 0.05 # 95% 置信度
var_hist = -np.percentile(ret, alpha * 100) # 收益 5% 分位的相反数=损失
cvar_hist = -ret[ret <= -var_hist].mean() # 比 VaR 更差那部分的平均损失
print("日 VaR95% =", round(var_hist * 100, 2), "%")
print("日 CVaR95% =", round(cvar_hist * 100, 2), "%")
历史模拟法不假设分布,直接用过去真实收益的 5% 分位当 VaR,再把「比 VaR 更惨的那些天」平均一下就是 CVaR。
实测(
tools/verify_new_2026-10-05.py,SDK 0.1.4):贵州茅台近 499 日 日 VaR95% = 2.08%、CVaR95% = 2.90%——即 95% 的日子里一天亏不超过 2.08%,但真摔进最差 5% 时平均亏 2.90%。CVaR > VaR,符合定义。
第 3 步:参数法(正态)VaR 做对比
z = 1.6448536269514722 # 95% 单尾分位
var_norm = -(ret.mean() - z * ret.std(ddof=1))
print("日 VaR95%(正态) =", round(var_norm * 100, 2), "%")
参数法假设收益服从正态分布,用均值和标准差直接套公式。实测茅台正态 VaR = 2.54%,比历史模拟的 2.08% 更大——因为正态法用标准差「摊平」了分布,对这只票的中段波动估计偏保守。两种口径常不一致,正是观察点。
第 4 步:换算到持仓金额 / 多日
position = 1_000_000 # 持仓 100 万
print("一天 95% 最大亏损 ≈", round(var_hist * position), "元")
# T 日 VaR 近似:单日 VaR × sqrt(T)(假设收益独立同分布)
print("10 日 VaR ≈", round(var_hist * np.sqrt(10) * 100, 2), "%")
参数与字段说明
| 参数/概念 | 含义 | 备注 |
|---|---|---|
adjust="forward" | 前复权 | 必须,除权缺口会污染尾部 |
alpha=0.05 | 显著性 | 95% 置信度;常用 99% 则 alpha=0.01 |
| 历史模拟 VaR | 收益分位 | 不假设分布,受样本区间影响 |
| 参数 VaR | 均值−z·标准差 | 假设正态,低估肥尾 |
| CVaR | 尾部平均损失 | 恒 ≥ VaR,更稳健 |
×sqrt(T) | 多日换算 | 仅在收益独立同分布时近似成立 |
关键坑与注意事项
- VaR 不是「最大损失」:它是「95% 情况下的下限」,剩下 5% 可能亏得多得多——这正是要配 CVaR 的原因。
- 正态法低估肥尾:A 股收益尖峰肥尾,正态假设会低估极端暴跌概率;历史模拟法更贴近现实,但依赖样本区间(含不含大跌差异很大)。
- 前复权:务必
adjust="forward"。 - 样本窗口:VaR 对回看长度敏感,500 日和 250 日结果会不同;比较时须统一。
- 历史不代表未来:VaR/CVaR 由历史分布估计,极端新事件(黑天鹅)无法被历史覆盖。
常见问题(FAQ)
Q:VaR 和 CVaR 到底该用哪个? A:VaR 是行业通用「一句话风险」,但对尾部不敏感;CVaR(Expected Shortfall)盯尾部平均损失,巴塞尔协议 III 已改用它。两个一起报最稳。
Q:历史模拟法和蒙特卡洛法区别? A:历史模拟直接用「真实发生过」的收益;蒙特卡洛用模型(如正态/GARCH)生成大量模拟路径。前者简单无偏,后者灵活但依赖模型假设。
Q:需要付费吗?
A:单只股票日线在免费额度内即可;若要对组合/全市场批量算 VaR 并聚合组合风险,需要 klines.batch + universes 标的池能力(Starter 及以上),详见官网定价页。
小结
VaR 把风险说成「95% 的日子一天最多亏多少」,CVaR 补上「真出事时平均多惨」。有了 AlphaFeed 的前复权日收益,历史模拟法和正态法各几行就能落地——记住 CVaR ≥ VaR、正态法会低估肥尾,这两点能让你的风控数字更诚实。
相关链接:
- AlphaFeed 官网:https://alphafeed.org/
- Python SDK 快速开始:https://docs.alphafeed.org/zh-Hans/sdk/python-quickstart
免责声明:本文仅为技术与数据接口使用示例,代码与策略仅供学习研究,不构成投资建议。市场有风险,历史数据不代表未来表现。