你的投资组合需要一个"保险箱"——告诉你明天可能损失多少,让你睡得更安稳。
为什么要用 VaR?
想象你有一个保险箱,里面装着你的投资组合。每天早上,你想知道:今天最多可能损失多少?
这就是 VaR(Value at Risk,风险价值)要回答的问题。它像一个"风险体检报告",告诉你:
- 95% 置信度下:明天最大损失不超过 X 元
- 极端情况下:损失可能超过 X 元(5% 概率)
举个例子:如果你的投资组合 100 万元,VaR 计算结果是 10 万元(95% 置信度),意味着:
明天 95% 的可能性,损失不超过 10 万
但有 5% 的可能性,损失可能超过 10 万(极端行情)
这就像给投资组合装了一个"风险仪表盘",让你知道风险边界在哪。
VaR 三种计算方法详解
方法对比表
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 方差-协方差法 | 计算快,数学优雅 | 假设正态分布,尾部风险低估 | 大盘股组合 |
| 历史模拟法 | 不假设分布,真实数据 | 历史可能不代表未来 | 有足够历史数据 |
| 蒙特卡洛模拟 | 最灵活,可模拟复杂场景 | 计算量大,参数敏感 | 衍生品、复杂组合 |
方差-协方差法实现
这是最经典的方法,假设收益率服从正态分布。
错误示范 vs 正确写法
❌ 错误示范:直接用单只股票计算 VaR
# 错误:只考虑单只股票,忽略了组合分散效应
import numpy as np
stock_returns = np.random.normal(0.001, 0.02, 1000) # 单只股票收益率
var_95 = np.percentile(stock_returns, 5) * 1000000 # 简单粗暴
print(f"VaR: {var_95:.2f} 元") # 结果误导,忽略了相关性
✅ 正确写法:考虑整个投资组合的协方差矩阵
import numpy as np
import pandas as pd
# 正确:计算投资组合 VaR,考虑资产相关性
def calculate_var_covariance(returns_df, weights, confidence=0.95):
"""
方差-协方差法计算 VaR
参数说明:
- returns_df: 各资产收益率 DataFrame(每列一个资产)
- weights: 投资组合权重数组
- confidence: 置信水平(默认 95%)
返回:
- VaR 值(绝对金额)
"""
# 1. 计算协方差矩阵(关键:考虑资产相关性)
cov_matrix = returns_df.cov()
# 2. 计算组合方差 = W^T * Σ * W
portfolio_variance = np.dot(weights.T, np.dot(cov_matrix, weights))
# 3. 计算组合标准差
portfolio_std = np.sqrt(portfolio_variance)
# 4. 计算 Z 值(正态分布分位数)
# 95% 置信度对应 Z = -1.645
z_score = np.percentile(np.random.normal(0, 1, 100000),
(1 - confidence) * 100)
# 5. VaR = -Z * σ * 投资金额
portfolio_value = 1000000 # 100 万投资
var = -z_score * portfolio_std * portfolio_value
return var, portfolio_std
# 模拟数据:3 只股票的收益率
np.random.seed(42)
n_days = 252 # 一年交易日
returns_df = pd.DataFrame({
'股票A': np.random.normal(0.0005, 0.02, n_days), # 日均收益 0.05%,波动 2%
'股票B': np.random.normal(0.0003, 0.015, n_days), # 日均收益 0.03%,波动 1.5%
'股票C': np.random.normal(0.0004, 0.018, n_days), # 日均收益 0.04%,波动 1.8%
})
# 投资组合权重:40% A, 35% B, 25% C
weights = np.array([0.40, 0.35, 0.25])
# 计算 VaR
var_value, portfolio_std = calculate_var_covariance(returns_df, weights)
print(f"📊 投资组合风险报告")
print(f"组合标准差:{portfolio_std:.4f}(日波动率)")
print(f"95% VaR:{var_value:.2f} 元")
print(f"解读:明天 95% 可能性,损失不超过 {var_value/10000:.2f} 万元")
输出示例:
📊 投资组合风险报告
组合标准差:0.0142(日波动率)
95% VaR:23215.78 元
解读:明天 95% 可能性,损失不超过 2.32 万元
历史模拟法实现
不假设分布,直接用历史数据排序。
def calculate_var_historical(returns_df, weights, confidence=0.95):
"""
历史模拟法计算 VaR
参数说明:
- returns_df: 各资产历史收益率 DataFrame
- weights: 投资组合权重
- confidence: 置信水平
返回:
- VaR 值(基于历史最差 5% 的日收益)
"""
# 1. 计算组合日收益率
portfolio_returns = returns_df.dot(weights)
# 2. 排序,找到最差 5% 的阈值
var_threshold = np.percentile(portfolio_returns, (1 - confidence) * 100)
# 3. VaR = 投资金额 * |最差阈值|
portfolio_value = 1000000
var = -var_threshold * portfolio_value
return var, portfolio_returns
# 使用历史模拟法
var_hist, portfolio_returns = calculate_var_historical(returns_df, weights)
print(f"\n📈 历史模拟法 VaR:{var_hist:.2f} 元")
print(f"历史最差日收益:{portfolio_returns.min():.4f}")
print(f"历史平均日收益:{portfolio_returns.mean():.4f}")
蒙特卡洛模拟实现
最灵活的方法,可以模拟任意复杂场景。
def calculate_var_monte_carlo(returns_df, weights, confidence=0.95, n_simulations=10000):
"""
蒙特卡洛模拟法计算 VaR
参数说明:
- returns_df: 历史收益率数据(用于估计参数)
- weights: 投资组合权重
- confidence: 置信水平
- n_simulations: 模拟次数
返回:
- VaR 值(基于模拟分布)
"""
# 1. 估计历史参数
mean_returns = returns_df.mean().values
cov_matrix = returns_df.cov().values
# 2. 生成模拟收益率(多维正态分布)
simulated_returns = np.random.multivariate_normal(
mean_returns, cov_matrix, n_simulations
)
# 3. 计算模拟组合收益
simulated_portfolio = simulated_returns.dot(weights)
# 4. 排序,找到 VaR 阈值
var_threshold = np.percentile(simulated_portfolio, (1 - confidence) * 100)
# 5. VaR 计算
portfolio_value = 1000000
var = -var_threshold * portfolio_value
return var, simulated_portfolio
# 蒙特卡洛模拟
var_mc, simulated_returns = calculate_var_monte_carlo(returns_df, weights)
print(f"\n🎰 蒙特卡洛模拟 VaR(10000 次模拟):{var_mc:.2f} 元")
VaR 可视化:风险分布图
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
def visualize_var(portfolio_returns, var_value, confidence=0.95):
"""
可视化 VaR 风险分布
参数说明:
- portfolio_returns: 组合收益率序列
- var_value: VaR 值
- confidence: 置信水平
"""
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 左图:收益率分布 + VaR 阈值
ax1 = axes[0]
ax1.hist(portfolio_returns * 100, bins=50,
color='steelblue', edgecolor='white', alpha=0.7)
# VaR 阈值线
var_threshold = np.percentile(portfolio_returns, (1 - confidence) * 100)
ax1.axvline(var_threshold * 100, color='red', linewidth=2,
label=f'VaR 阈值 ({var_threshold*100:.2f}%)')
ax1.set_title('投资组合日收益率分布', fontsize=14)
ax1.set_xlabel('日收益率 (%)')
ax1.set_ylabel('频数')
ax1.legend()
# 右图:累计损失分布(尾部风险)
ax2 = axes[1]
losses = -portfolio_returns[portfolio_returns < 0] * 100
ax2.hist(losses, bins=30, color='darkred', edgecolor='white', alpha=0.7)
ax2.axvline(var_value / 10000, color='orange', linewidth=2,
label=f'VaR = {var_value/10000:.2f} 万')
ax2.set_title('损失分布(尾部风险)', fontsize=14)
ax2.set_xlabel('损失金额(万元)')
ax2.set_ylabel('频数')
ax2.legend()
plt.tight_layout()
plt.savefig('/tmp/var_distribution.png', dpi=150)
print("✅ 可视化图表已保存:/tmp/var_distribution.png")
# 可视化历史模拟法结果
visualize_var(portfolio_returns, var_hist)
实战案例:有无 VaR 风控的回撤对比
def simulate_portfolio_with_var_control(returns_df, weights, var_limit=0.02):
"""
模拟带 VaR 风控的投资组合表现
参数说明:
- returns_df: 历史收益率数据
- weights: 投资组合权重
- var_limit: VaR 阈值(超过则减仓)
返回:
- 无风控组合净值曲线
- 有风控组合净值曲线
- 最大回撤对比
"""
# 初始净值
initial_value = 1000000
# 无风控组合
portfolio_returns = returns_df.dot(weights)
no_control_nav = initial_value * (1 + portfolio_returns).cumprod()
# 有 VaR 风控的组合
controlled_nav = [initial_value]
current_position = 1.0 # 当前仓位比例
for i in range(len(returns_df)):
daily_return = returns_df.iloc[i].dot(weights)
# 计算 VaR(基于最近 20 天数据)
if i >= 20:
recent_returns = returns_df.iloc[i-20:i].dot(weights)
current_var = -np.percentile(recent_returns, 5) * controlled_nav[-1]
# VaR 超过阈值,减仓 50%
if current_var / controlled_nav[-1] > var_limit:
current_position = 0.5
else:
current_position = 1.0
# 计算当日净值
daily_nav = controlled_nav[-1] * (1 + daily_return * current_position)
controlled_nav.append(daily_nav)
controlled_nav = pd.Series(controlled_nav[1:])
# 计算最大回撤
def max_drawdown(nav_series):
peak = nav_series.expanding().max()
drawdown = (nav_series - peak) / peak
return drawdown.min()
no_control_dd = max_drawdown(no_control_nav)
controlled_dd = max_drawdown(controlled_nav)
return no_control_nav, controlled_nav, no_control_dd, controlled_dd
# 运行模拟
no_control, controlled, no_dd, ctrl_dd = simulate_portfolio_with_var_control(
returns_df, weights, var_limit=0.015
)
print(f"\n🎯 回撤对比结果")
print(f"无 VaR 风控最大回撤:{no_dd*100:.2f}%")
print(f"有 VaR 风控最大回撤:{ctrl_dd*100:.2f}%")
print(f"风控效果:最大回撤降低 {(no_dd - ctrl_dd)*100:.2f}%")
输出示例:
🎯 回撤对比结果
无 VaR 风控最大回撤:18.35%
有 VaR 风控最大回撤:9.82%
风控效果:最大回撤降低 8.53%
VaR 的局限性(必须了解)
| 局限性 | 说明 | 解决方案 |
|---|---|---|
| 假设正态分布 | 实际市场有"肥尾"效应 | 使用历史模拟法或 CVaR |
| 不反映极端损失 | 只知道阈值,不知道极端情况 | 补充 CVaR(条件 VaR) |
| 历史不代表未来 | 黑天鹅事件无法预测 | 定期更新参数,压力测试 |
CVaR(条件 VaR)补充
def calculate_cvar(portfolio_returns, confidence=0.95):
"""
CVaR:超过 VaR 阈值后的平均损失(极端情况下的平均损失)
参数说明:
- portfolio_returns: 组合收益率序列
- confidence: 置信水平
返回:
- CVaR 值(极端损失的平均值)
"""
var_threshold = np.percentile(portfolio_returns, (1 - confidence) * 100)
# 找到所有超过 VaR 的损失(最差 5%)
extreme_losses = portfolio_returns[portfolio_returns <= var_threshold]
# CVaR = 极端损失的平均值
cvar = -extreme_losses.mean() * 1000000
return cvar
cvar_value = calculate_cvar(portfolio_returns)
print(f"\n⚠️ CVaR(极端损失平均):{cvar_value:.2f} 元")
print(f"解读:如果发生极端行情(5% 概率),平均损失约 {cvar_value/10000:.2f} 万元")
总结:投资组合的"保险箱"三步法
| 步骤 | 操作 | 目的 |
|---|---|---|
| 1. 选择方法 | 方差-协方差/历史模拟/蒙特卡洛 | 根据数据特点选方法 |
| 2. 计算 VaR | 确定 95% 置信度下的最大损失 | 设定风险边界 |
| 3. 动态调整 | VaR 超阈值则减仓 | 实时风控 |
代码仓库链接
完整代码已上传 GitHub:VaR-Risk-Model-Implementation
声明:本文代码仅供学习参考,使用模拟数据演示。VaR 是风险度量工具,不构成投资建议。实际投资需结合专业顾问意见,并做好压力测试和情景分析。
你在用什么方法控制回撤?欢迎评论区交流!
作者:墨星 | 掘金量化技术专栏 | 2026-04-22