4. 风险指标计算:VaR、CVaR、最大回撤、夏普比率
风险指标这东西,说白了就是给投资组合做「体检报告」。我做了这么多年风控,见过太多人只看收益率,结果一把亏光。今天咱们就把几个核心指标掰开揉碎讲清楚。
核心观点:风险不是亏钱,而是不确定性。指标是工具,关键看你怎么用。
4.1 VaR:在险价值
VaR 是风控领域最常用的指标。它的意思很简单:在给定置信水平下,未来一段时间内最大可能损失是多少。比如 95% VaR = 100 万,意思是有 95% 的把握,最大亏损不超过 100 万。
计算 VaR 有三种主流方法,我挨个讲。
4.1.1 历史模拟法
这个方法最直观。说白了就是「过去怎么亏,未来就可能怎么亏」。把历史收益率排序,取第 5% 分位数(95% 置信水平)就行。
import numpy as np
import pandas as pd
def historical_var(returns, confidence=0.95):
"""
历史模拟法计算 VaR
returns: 收益率序列
confidence: 置信水平,默认 95%
"""
sorted_returns = np.sort(returns)
index = int((1 - confidence) * len(sorted_returns))
var = -sorted_returns[index]
return var
# 示例
np.random.seed(42)
returns = np.random.normal(0.001, 0.02, 1000)
var_95 = historical_var(returns, 0.95)
print(f"95% VaR (历史模拟法): {var_95:.4f}")
我的经验:历史模拟法简单,但有个坑——如果历史数据不够长,或者市场结构变了,结果会失真。我曾在 2015 年股灾前用这个方法算 VaR,结果严重低估了风险。后来我加了个「压力测试」做补充。
4.1.2 参数法(方差-协方差法)
这个方法假设收益率服从正态分布。嗯,现实中收益率往往有「肥尾」特征,但参数法胜在计算快。
def parametric_var(returns, confidence=0.95):
"""
参数法计算 VaR
假设收益率服从正态分布
"""
mu = np.mean(returns)
sigma = np.std(returns)
from scipy.stats import norm
z = norm.ppf(1 - confidence)
var = -(mu + z * sigma)
return var
var_95_param = parametric_var(returns, 0.95)
print(f"95% VaR (参数法): {var_95_param:.4f}")
注意:参数法对极端事件几乎「失明」。2008 年金融危机时,很多银行用参数法算 VaR,结果模型显示风险很低,实际却爆了。我个人建议:参数法只适合做快速估算,别用它做最终决策。
4.1.3 蒙特卡洛模拟
这个方法最灵活,也最耗计算资源。它通过随机生成大量路径来模拟未来收益分布。
def monte_carlo_var(returns, confidence=0.95, n_simulations=10000, horizon=1):
"""
蒙特卡洛模拟计算 VaR
"""
mu = np.mean(returns)
sigma = np.std(returns)
# 模拟未来收益率
simulated_returns = np.random.normal(mu, sigma, (n_simulations, horizon))
portfolio_returns = np.sum(simulated_returns, axis=1)
var = -np.percentile(portfolio_returns, (1 - confidence) * 100)
return var
var_95_mc = monte_carlo_var(returns, 0.95, 10000, 1)
print(f"95% VaR (蒙特卡洛): {var_95_mc:.4f}")
避坑指南:我曾经用蒙特卡洛模拟一个包含期权组合的 VaR,结果跑了 10 万次模拟,算出来结果和参数法差不多。后来发现是随机数生成器没设置好种子,导致路径相关性出了问题。记住:随机数质量直接影响结果。
4.2 CVaR:条件在险价值
VaR 有个致命缺陷:它只告诉你「最坏情况下的损失边界」,但没告诉你「一旦突破边界,会亏多少」。CVaR 就是来解决这个问题的。它计算的是「超过 VaR 的那些损失的平均值」。
def cvar(returns, confidence=0.95):
"""
计算 CVaR
"""
sorted_returns = np.sort(returns)
index = int((1 - confidence) * len(sorted_returns))
var = -sorted_returns[index]
# 取所有超过 VaR 的损失的平均值
tail_losses = -sorted_returns[:index]
cvar_value = np.mean(tail_losses)
return cvar_value
cvar_95 = cvar(returns, 0.95)
print(f"95% CVaR: {cvar_95:.4f}")
为什么 CVaR 更靠谱?因为它是「尾部风险」的度量。你想想看,真正让你破产的不是「95% 的情况」,而是那「5% 的极端情况」。CVaR 就是盯着这 5% 不放。
4.3 最大回撤
最大回撤是投资者最直观的感受。它衡量的是「从最高点跌到最低点,最多亏了多少」。我见过很多量化策略,年化收益 30%,但最大回撤 40%,这种策略根本拿不住。
def max_drawdown(price_series):
"""
计算最大回撤
price_series: 价格序列
"""
peak = np.maximum.accumulate(price_series)
drawdown = (price_series - peak) / peak
max_dd = np.min(drawdown)
return max_dd
# 示例
prices = 100 * np.exp(np.cumsum(returns))
max_dd = max_drawdown(prices)
print(f"最大回撤: {max_dd:.2%}")
我的习惯:看策略好不好,先看最大回撤。如果回撤超过 20%,我会直接毙掉。别问我为什么,这是血的教训——2018 年我有个策略回撤 25%,结果客户直接撤资了。
4.4 夏普比率
夏普比率是「收益/风险」的比值。它告诉你:每承担一单位风险,能换来多少超额收益。一般认为夏普比率大于 1 算不错,大于 2 算优秀。
def sharpe_ratio(returns, risk_free_rate=0.02):
"""
计算夏普比率
risk_free_rate: 无风险利率,默认 2%
"""
excess_returns = returns - risk_free_rate / 252 # 日化
sharpe = np.sqrt(252) * np.mean(excess_returns) / np.std(returns)
return sharpe
sharpe = sharpe_ratio(returns, 0.02)
print(f"夏普比率: {sharpe:.2f}")
注意:夏普比率有个大坑——它假设收益率正态分布。如果策略有「肥尾」特征,夏普比率会虚高。我见过一个高频策略,夏普比率 3.5,但实际最大回撤 30%。后来发现是尾部风险没暴露。
4.5 知识体系总览
下面这张图把本章的核心逻辑串起来了。你仔细看,会发现所有指标都围绕「风险度量」和「收益调整」两个维度展开。
4.6 指标对比与选择建议
| 指标 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| VaR | 直观、通用 | 忽略尾部风险 | 日常风控报告 |
| CVaR | 关注极端损失 | 计算复杂 | 压力测试、极端行情 |
| 最大回撤 | 投资者感受最直接 | 只反映历史 | 策略评估、产品设计 |
| 夏普比率 | 收益风险综合 | 假设正态分布 | 策略对比、绩效归因 |
我的建议:别只看一个指标。我习惯把 VaR、CVaR、最大回撤、夏普比率放在一起看。如果四个指标都指向同一个方向,那决策就稳了。如果出现矛盾——比如夏普比率高但最大回撤也高——那就要警惕了,说明策略可能「赌性」太重。
好了,这一章的内容就到这儿。记住:指标是死的,人是活的。别被数字骗了,多想想数字背后的逻辑。
公众号:蓝海资料掘金营,微信deep3321