6、期权组合风险度量:基于曲面的VaR与CVaR计算
聊到期权组合的风险度量,很多人第一反应就是看希腊字母。Delta、Gamma、Vega……这些当然重要。但说实话,它们只能告诉你「某个风险因子动一下,组合会怎么变」。真正到了实战中,我们需要回答的问题是:「明天我的组合最大可能亏多少钱?」
这就是VaR和CVaR要干的事。我个人习惯把VaR叫做「最坏情况下的底线」,而CVaR则是「底线被击穿后的平均损失」。今天我们就结合波动率曲面,把这两个指标讲透。
为什么普通VaR对期权不够用?
传统的VaR计算,通常假设资产收益率服从正态分布。嗯,这个假设对股票现货可能勉强凑合,但对期权组合来说,简直就是灾难。
为什么?因为期权是非线性产品。你想想看,一个深度虚值期权,它的价格变化和标的物价格变化根本不是线性关系。再加上波动率曲面本身会扭曲、会微笑、会偏斜……用传统方法算VaR,结果基本是错的。
我在项目中遇到过一位交易员,他用普通参数法算出的VaR只有实际亏损的一半。那次教训挺深刻的——期权组合的风险度量,必须把波动率曲面的动态变化考虑进去。
基于曲面的VaR计算框架
我个人建议采用以下三步走的方法:
- 场景生成:基于波动率曲面,模拟标的资产价格和波动率曲面的联合变化
- 组合重定价:在每个场景下,用曲面重新计算所有期权的价格
- 损益统计:汇总损益分布,计算VaR和CVaR
下面这张图展示了整个流程:
实战代码:蒙特卡洛模拟
下面这段代码是我在实际项目中用过的框架。它把波动率曲面和标的资产价格联合模拟,然后计算VaR和CVaR。
import numpy as np
import pandas as pd
from scipy.interpolate import RectBivariateSpline
class SurfaceVaRCalculator:
"""基于波动率曲面的VaR/CVaR计算器"""
def __init__(self, surface_model, portfolio):
"""
surface_model: 波动率曲面模型(包含插值函数)
portfolio: 期权组合(包含每个期权的合约信息)
"""
self.surface = surface_model
self.portfolio = portfolio
self.n_scenarios = 10000
def generate_scenarios(self, S0, T, sigma_spot=0.02):
"""生成标的资产和曲面参数的联合场景"""
np.random.seed(42)
# 标的资产价格场景(几何布朗运动)
dt = T / 252 # 假设T天
z1 = np.random.normal(0, 1, self.n_scenarios)
S_t = S0 * np.exp((0 - 0.5 * sigma_spot**2) * dt + sigma_spot * np.sqrt(dt) * z1)
# 波动率曲面扰动(PCA方法)
# 假设曲面由3个主成分驱动
pca_scores = np.random.multivariate_normal(
mean=[0, 0, 0],
cov=np.diag([0.15, 0.05, 0.02]), # 主成分方差递减
size=self.n_scenarios
)
return S_t, pca_scores
def reprice_portfolio(self, S_t, pca_scores):
"""在每个场景下重新定价组合"""
pnl = np.zeros(self.n_scenarios)
for i in range(self.n_scenarios):
# 更新曲面
self.surface.perturb(pca_scores[i])
# 对每个期权重新定价
total_pnl = 0
for option in self.portfolio:
# 从曲面获取隐含波动率
iv = self.surface.get_iv(
strike=option.strike,
expiry=option.expiry
)
# 计算新价格
new_price = option.price(S_t[i], iv)
# 损益 = 新价格 - 当前价格
total_pnl += (new_price - option.current_price) * option.position
pnl[i] = total_pnl
return pnl
def calculate_var_cvar(self, pnl, alpha=0.95):
"""计算VaR和CVaR"""
sorted_pnl = np.sort(pnl)
n = len(sorted_pnl)
# VaR:第(1-alpha)百分位
var_index = int(n * (1 - alpha))
var = sorted_pnl[var_index]
# CVaR:低于VaR的损益均值
tail_losses = sorted_pnl[:var_index]
cvar = np.mean(tail_losses) if len(tail_losses) > 0 else var
return var, cvar
def run(self, S0, T):
"""完整运行流程"""
S_t, pca_scores = self.generate_scenarios(S0, T)
pnl = self.reprice_portfolio(S_t, pca_scores)
var, cvar = self.calculate_var_cvar(pnl)
return {
'VaR_95': var,
'CVaR_95': cvar,
'pnl_distribution': pnl
}
关键点:这段代码的核心在于generate_scenarios方法。它同时模拟了标的资产价格和波动率曲面的变化,并且通过PCA方法保留了曲面各期限、各执行价之间的相关性。这才是「基于曲面」的真正含义。
CVaR为什么比VaR更实用?
VaR有个臭名昭著的缺陷:它只告诉你「最坏情况下的边界」,但边界之外的事情它不管。比如,两个组合的VaR可能都是-100万,但一个组合的尾部损失可能只到-110万,另一个可能到-500万。VaR完全区分不了。
CVaR就不一样了。它计算的是「一旦损失超过VaR,平均会亏多少」。说白了,CVaR关注的是尾部风险的严重程度。
我曾经用CVaR帮一个客户发现了组合中的「隐形炸弹」——一个深度虚值期权组合,VaR看起来很正常,但CVaR高得吓人。后来果然出了事,那个组合在极端行情下亏得一塌糊涂。
实战建议:我个人习惯同时看VaR和CVaR。如果两者差距很大(比如CVaR是VaR的2倍以上),说明组合存在严重的尾部风险,需要警惕。
曲面参数扰动:PCA降维
波动率曲面是一个二维结构(执行价 × 期限),直接模拟所有点是不现实的。我常用的方法是PCA降维。
具体来说:
- 把曲面展平成一个高维向量
- 计算历史曲面的协方差矩阵
- 提取前3-5个主成分(通常能解释90%以上的方差)
- 只对这些主成分的得分进行随机模拟
这样做的好处很明显:既保留了曲面各点之间的相关性,又大幅降低了计算量。
| 主成分 | 解释方差比例 | 物理含义 |
|---|---|---|
| PC1 | 65-75% | 曲面整体平移(波动率水平变化) |
| PC2 | 15-20% | 曲面倾斜(偏斜程度变化) |
| PC3 | 5-10% | 曲面曲率(微笑程度变化) |
注意:PCA方法依赖于历史数据的稳定性。如果市场结构发生了根本性变化(比如从低波动环境切换到高波动环境),历史协方差矩阵可能失效。我建议定期重新估计PCA参数,至少每季度一次。
回测验证:你的VaR准不准?
算完VaR不是终点,你得验证它准不准。常用的方法是回测检验:
- 记录每天的实际损益
- 检查实际亏损超过VaR的天数比例
- 这个比例应该接近(1 - 置信水平)
比如,95%的VaR,理论上100天里应该有5天实际亏损超过VaR。如果超过的天数太多,说明你的VaR模型太乐观了;如果太少,说明模型太保守。
我记得有一次回测,发现基于曲面的VaR比传统方法准确得多。传统方法的失败率是8%(太乐观),而曲面方法的失败率是5.3%,非常接近理论值。这就是曲面建模的价值所在。
小结
基于波动率曲面的VaR和CVaR计算,说白了就是把「曲面动态」和「风险度量」结合起来。它比传统方法更准确,但也更复杂。核心要点就三个:
- 场景生成要联合:标的资产和曲面参数一起模拟
- 重定价要精确:每个场景下用曲面重新定价
- 尾部风险要看CVaR:别被VaR的假象骗了
嗯,今天就聊到这里。这些方法我在实盘中用了好几年,效果还不错。希望对你也有帮助。
公众号:蓝海资料掘金营,微信deep3321