第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是损失变量。

1.2 历史模拟法:最简单粗暴的VaR计算

历史模拟法,是我刚入行时最先学会的方法。它的逻辑特别朴素:

“过去发生过的最坏情况,未来也可能发生。”

具体做法:

  1. 取过去N天的收益率数据(比如500天)
  2. 按收益率从小到大排序
  3. 取第(1-α)×N个位置的收益率
  4. 乘以当前组合市值,就是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

历史模拟法依赖过去的数据,那如果历史数据不够用呢?蒙特卡洛模拟就是干这个的。

它的思路是:

  1. 假设收益率服从某个分布(比如正态分布)
  2. 用计算机生成成千上万条可能的收益率路径
  3. 从这些模拟数据中计算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的条件下,损失的期望值。
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知识框架。你看一眼,就能明白这章讲了什么。

VaR与CVaR知识体系 风险度量方法 VaR(风险价值) CVaR(条件VaR) 历史模拟法 蒙特卡洛模拟 尾部期望损失 风险补充度量 VaR:最坏情况下的最大损失 CVaR:最坏情况下的平均损失 核心区别:VaR看“门槛”,CVaR看“门槛后的深渊” 应用场景:轮动策略止损线设置 | 组合优化目标函数 | 压力测试基准

1.6 实战中的选择建议

说了这么多,到底该用VaR还是CVaR?我个人的经验是:

场景 推荐指标 原因
日常风控监控 VaR 计算快,容易理解,适合高频监控
策略优化目标 CVaR 能捕捉尾部风险,优化结果更稳健
监管合规报告 VaR 监管机构认这个,没办法
极端行情压力测试 CVaR VaR在极端行情下会失效,CVaR更靠谱
我的建议:如果你刚开始做轮动策略,先用VaR把风控框架搭起来。等策略跑顺了,再引入CVaR做精细化优化。别一上来就搞太复杂,容易把自己绕晕。

好了,这一章的内容就到这里。VaR和CVaR是风险度量的基本功,后面讲轮动策略的止损、仓位管理时,还会反复用到这两个概念。先把它们吃透,后面的路就好走了。


专注资料整理