第4章:VaR风险度量:历史模拟法、参数法、蒙特卡洛模拟法计算VaR
说到风险度量,VaR(Value at Risk)绝对是绕不开的核心指标。很多刚入行的朋友问我:「VaR到底是个啥?」说白了,它回答了一个很朴素的问题:在给定的置信水平下,我的持仓在未来一段时间内,最大可能亏多少钱?
我最早接触VaR是在2015年,当时帮一家自营团队搭建风控系统。那会儿市场波动大,老板每天都要看「今天最多能亏多少」。嗯,VaR就是用来回答这个问题的。今天咱们就把三种主流方法——历史模拟法、参数法、蒙特卡洛模拟法,掰开揉碎了讲清楚。
4.1 什么是VaR?
VaR的定义其实很简单。假设你持有一个投资组合,在95%的置信水平下,一天的VaR是100万。这意味着:在正常的市场条件下,你有95%的把握,一天内的损失不会超过100万。反过来,有5%的可能性,损失会超过100万。
核心公式:
P(ΔP ≤ -VaR) = 1 - α
其中α是置信水平,ΔP是组合的价值变化。
这里有个坑,我一开始也踩过——VaR不告诉你超过阈值后会亏多少。它只告诉你「最坏情况的下限」,而不是「最坏情况本身」。所以后来业界又搞了个CVaR(条件VaR),专门算尾部损失的期望值。这个咱们后面章节会细讲。
4.2 历史模拟法
历史模拟法,说白了就是「过去怎么跌,未来就可能怎么跌」。它不做任何分布假设,直接用历史收益率数据来模拟未来的可能损失。
我个人习惯用这种方法做快速验证,因为它直观、好解释。跟老板汇报的时候,你直接说「根据过去500天的数据,最差的5%情况是这样的」,老板一听就懂。
4.2.1 计算步骤
- 收集历史价格数据,计算每日收益率
- 将收益率从小到大排序
- 找到对应分位数的收益率值
- 乘以当前持仓市值,得到VaR
4.2.2 Python实现
import numpy as np
import pandas as pd
def historical_var(returns, confidence_level=0.95):
"""
历史模拟法计算VaR
returns: 历史收益率序列
confidence_level: 置信水平,默认95%
"""
sorted_returns = np.sort(returns)
index = int((1 - confidence_level) * len(sorted_returns))
var = -sorted_returns[index]
return var
# 示例:假设我们有某股票500天的日收益率
np.random.seed(42)
returns = np.random.normal(0.001, 0.02, 500)
var_95 = historical_var(returns, 0.95)
print(f"95%置信水平下的日VaR: {var_95:.4f}")
# 输出:95%置信水平下的日VaR: 0.0328
经验之谈:历史模拟法的窗口期选择很关键。我一般用250个交易日(约1年),太短了样本不够,太长了市场结构可能已经变了。曾经有个项目,用了3年数据,结果市场风格切换,VaR严重低估——嗯,那次的教训挺深刻的。
4.3 参数法(方差-协方差法)
参数法假设收益率服从正态分布。你想想看,如果收益率是正态的,那VaR的计算就变成了一个简单的公式问题。
为什么叫参数法?因为它只需要估计两个参数:均值和标准差。说白了,就是假设未来收益率是均值为μ、标准差为σ的正态分布。
4.3.1 计算公式
VaR = -(μ + z_α × σ) × P
其中z_α是标准正态分布的分位数,95%置信水平下z_α = -1.645,99%下z_α = -2.326。
4.3.2 Python实现
from scipy.stats import norm
def parametric_var(returns, confidence_level=0.95, portfolio_value=1.0):
"""
参数法计算VaR
"""
mu = np.mean(returns)
sigma = np.std(returns)
z_score = norm.ppf(1 - confidence_level)
var = -(mu + z_score * sigma) * portfolio_value
return var
# 示例
var_param = parametric_var(returns, 0.95, 1000000)
print(f"参数法95% VaR(持仓100万): {var_param:.2f} 元")
# 输出:参数法95% VaR(持仓100万): 31890.23 元
注意:参数法最大的问题是「肥尾」效应。真实市场的收益率分布往往有厚尾特征,极端事件发生的概率比正态分布假设的要高。我曾经在2018年用参数法给一个期权组合算VaR,结果第二天市场暴跌,实际损失是VaR预测的3倍多。从那以后,我对参数法就多留了个心眼。
4.4 蒙特卡洛模拟法
蒙特卡洛模拟法,我个人觉得是最灵活、也最强大的方法。它不假设收益率服从特定分布,而是通过随机模拟生成大量可能的价格路径,然后统计损失分布。
为什么叫蒙特卡洛?因为像赌场里的轮盘一样,靠随机数来模拟。你想想看,如果我们模拟10万次,每次都是一条不同的价格路径,那最终的损失分布就非常接近真实情况了。
4.4.1 计算步骤
- 估计收益率的分布参数(均值、标准差、偏度等)
- 生成N条随机价格路径(通常N≥10000)
- 计算每条路径下的组合损益
- 对损益排序,找到对应分位数的VaR
4.4.2 Python实现
def monte_carlo_var(returns, confidence_level=0.95,
n_simulations=100000, days=1,
portfolio_value=1.0):
"""
蒙特卡洛模拟法计算VaR
"""
mu = np.mean(returns)
sigma = np.std(returns)
# 模拟价格路径
random_returns = np.random.normal(mu, sigma,
(n_simulations, days))
cumulative_returns = np.sum(random_returns, axis=1)
# 计算损益
pnl = cumulative_returns * portfolio_value
# 排序并找到VaR
sorted_pnl = np.sort(pnl)
index = int((1 - confidence_level) * n_simulations)
var = -sorted_pnl[index]
return var
# 示例
var_mc = monte_carlo_var(returns, 0.95, 100000, 1, 1000000)
print(f"蒙特卡洛法95% VaR(持仓100万): {var_mc:.2f} 元")
# 输出:蒙特卡洛法95% VaR(持仓100万): 31950.67 元
我的建议:蒙特卡洛模拟的次数不要少于5万次。太少的话,尾部估计不稳定。我一般用10万次,既保证精度,计算时间也能接受。另外,如果组合包含期权等非线性产品,蒙特卡洛几乎是唯一的选择——参数法和历史模拟法都搞不定。
4.5 三种方法对比
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 历史模拟法 | 无需分布假设,直观易懂 | 依赖历史数据,对极端事件估计不足 | 线性产品、快速估算 |
| 参数法 | 计算简单,速度快 | 正态假设不现实,忽略肥尾 | 大型组合的快速风控 |
| 蒙特卡洛法 | 灵活,可处理复杂产品 | 计算量大,依赖随机数质量 | 期权组合、非线性产品 |
4.6 实战中的避坑指南
做VaR计算这么多年,我踩过的坑不少,分享几个最关键的:
- 数据频率问题:用日收益率算出来的VaR是日VaR,别跟周VaR搞混了。我曾经见过有人用日数据算,然后跟老板说「这是周度的风险」——嗯,后果你懂的。
- 置信水平的选择:95%和99%差别很大。99%的VaR对尾部更敏感,需要的样本量也更大。我一般风控用95%,监管报送用99%。
- 回测验证:算完VaR一定要做回测。看看实际损失超过VaR的次数,是不是真的接近5%(95%置信水平下)。如果偏差太大,说明模型有问题。
核心要点总结:
- VaR回答的是「给定置信水平下,最大可能损失是多少」
- 历史模拟法:用过去的数据说话,简单直观
- 参数法:快,但别太相信正态假设
- 蒙特卡洛法:灵活强大,但计算成本高
- 三种方法结合使用,互相验证,才是实战中的正确姿势
好了,这一章的内容就到这里。下一章咱们聊聊VaR的回测和压力测试——这才是真正检验模型好坏的环节。到时候我会分享一个我亲身经历的「VaR模型翻车」案例,保证让你印象深刻。