第1章:VaR与CVaR——风险度量中的两把尺子
做轮动策略这些年,我踩过最大的坑,就是只看收益不看风险。有一回,一个策略回测年化30%,我兴冲冲上了实盘,结果一个月回撤15%。后来复盘才发现——我根本没搞清楚“最坏情况下会亏多少”。
嗯,今天我们就来聊聊这个问题。VaR和CVaR,就是用来回答“最坏情况”的。
1.1 Value at Risk:到底什么是VaR?
VaR,全称Value at Risk,中文叫“风险价值”。说白了就是:在给定的置信水平下,未来一段时间内,你的组合最多可能亏多少钱。
举个例子:
- 你有一个100万的组合
- 95%置信水平下,日VaR是2万
- 意思就是:100天里,有95天亏损不超过2万
- 剩下5天,亏损可能超过2万——具体多少,VaR不告诉你
我个人习惯用95%或99%的置信水平。为什么?因为太低的置信水平(比如90%)其实没啥意义,你想想看,10天里就有1天超限,这风险控制等于没做。
核心公式:
VaR(α) = inf{ l ∈ R : P(L > l) ≤ 1-α }
其中α是置信水平,L是损失变量。
VaR(α) = inf{ l ∈ R : P(L > l) ≤ 1-α }
其中α是置信水平,L是损失变量。
1.2 历史模拟法:最简单粗暴的VaR计算
历史模拟法,是我刚入行时最先学会的方法。它的逻辑特别朴素:
“过去发生过的最坏情况,未来也可能发生。”
具体做法:
- 取过去N天的收益率数据(比如500天)
- 按收益率从小到大排序
- 取第(1-α)×N个位置的收益率
- 乘以当前组合市值,就是VaR
举个例子,500天数据,95%置信水平:
- 排序后,取第25小的收益率(因为500×5%=25)
- 假设这个收益率是-3.2%
- 组合市值100万,VaR就是3.2万
import numpy as np
import pandas as pd
def historical_var(returns, confidence=0.95):
"""
历史模拟法计算VaR
returns: 收益率序列(一维数组或Series)
confidence: 置信水平,默认95%
"""
sorted_returns = np.sort(returns)
index = int((1 - confidence) * len(sorted_returns))
var = -sorted_returns[index] # 取正值表示损失
return var
# 示例:假设我们有500天的日收益率数据
np.random.seed(42)
daily_returns = np.random.normal(0.001, 0.02, 500) # 模拟数据
var_95 = historical_var(daily_returns, 0.95)
print(f"95%置信水平下的日VaR: {var_95:.4f} 即 {var_95*100:.2f}%")
我的经验:历史模拟法虽然简单,但有个致命问题——它假设历史会重演。我在2020年3月那次美股熔断中吃过亏,历史数据里根本没出现过那种极端情况,VaR完全失效了。
1.3 蒙特卡洛模拟:自己造数据算VaR
历史模拟法依赖过去的数据,那如果历史数据不够用呢?蒙特卡洛模拟就是干这个的。
它的思路是:
- 假设收益率服从某个分布(比如正态分布)
- 用计算机生成成千上万条可能的收益率路径
- 从这些模拟数据中计算VaR
说白了,就是“自己造数据,造得够多,就能逼近真实分布”。
def monte_carlo_var(mean, std, days=500, simulations=10000, confidence=0.95):
"""
蒙特卡洛模拟计算VaR
mean: 日收益率均值
std: 日收益率标准差
days: 模拟天数
simulations: 模拟次数
confidence: 置信水平
"""
# 生成模拟收益率矩阵
simulated_returns = np.random.normal(mean, std, (simulations, days))
# 计算每个模拟路径的累计收益率
cumulative_returns = np.cumprod(1 + simulated_returns, axis=1)
# 取最后一天的收益率
final_returns = cumulative_returns[:, -1] / cumulative_returns[:, 0] - 1
# 计算VaR
var = -np.percentile(final_returns, (1 - confidence) * 100)
return var
# 示例
mean_return = 0.001
std_return = 0.02
var_mc = monte_carlo_var(mean_return, std_return)
print(f"蒙特卡洛模拟95% VaR: {var_mc:.4f} 即 {var_mc*100:.2f}%")
注意:蒙特卡洛模拟的精度取决于你假设的分布是否正确。我曾经用正态分布模拟过加密货币的VaR,结果严重低估了风险——因为加密货币的尾部比正态分布厚得多。后来改用t分布,效果才好一些。
1.4 CVaR:比VaR更狠的风险度量
VaR有个大问题:它只告诉你“最坏情况下亏多少”,但不告诉你“亏超了会亏多少”。
举个例子:
- 策略A:95% VaR是2万,但剩下5%可能亏20万
- 策略B:95% VaR也是2万,但剩下5%只亏3万
VaR看两个策略一样,但实际风险天差地别。这时候就需要CVaR出场了。
CVaR,也叫条件VaR或期望损失。它计算的是:当损失超过VaR时,平均会亏多少。
CVaR公式:
CVaR(α) = E[ L | L > VaR(α) ]
即:在损失超过VaR的条件下,损失的期望值。
CVaR(α) = E[ L | L > VaR(α) ]
即:在损失超过VaR的条件下,损失的期望值。
def cvar_from_returns(returns, confidence=0.95):
"""
从收益率序列计算CVaR
"""
sorted_returns = np.sort(returns)
index = int((1 - confidence) * len(sorted_returns))
# 取尾部所有损失的平均值
tail_losses = -sorted_returns[:index]
cvar = np.mean(tail_losses)
return cvar
# 对比VaR和CVaR
var_95 = historical_var(daily_returns, 0.95)
cvar_95 = cvar_from_returns(daily_returns, 0.95)
print(f"VaR(95%): {var_95:.4f}")
print(f"CVaR(95%): {cvar_95:.4f}")
print(f"CVaR是VaR的 {cvar_95/var_95:.2f} 倍")
1.5 知识体系总览
下面这张图,是我自己梳理的VaR与CVaR知识框架。你看一眼,就能明白这章讲了什么。
1.6 实战中的选择建议
说了这么多,到底该用VaR还是CVaR?我个人的经验是:
| 场景 | 推荐指标 | 原因 |
|---|---|---|
| 日常风控监控 | VaR | 计算快,容易理解,适合高频监控 |
| 策略优化目标 | CVaR | 能捕捉尾部风险,优化结果更稳健 |
| 监管合规报告 | VaR | 监管机构认这个,没办法 |
| 极端行情压力测试 | CVaR | VaR在极端行情下会失效,CVaR更靠谱 |
我的建议:如果你刚开始做轮动策略,先用VaR把风控框架搭起来。等策略跑顺了,再引入CVaR做精细化优化。别一上来就搞太复杂,容易把自己绕晕。
好了,这一章的内容就到这里。VaR和CVaR是风险度量的基本功,后面讲轮动策略的止损、仓位管理时,还会反复用到这两个概念。先把它们吃透,后面的路就好走了。