4. 历史模拟法VaR计算:历史收益率排序、分位数选取、滚动窗口计算、Python实现示例
历史模拟法(Historical Simulation, HS)是VaR计算中最直观、最符合监管直觉的非参数方法。它不假设收益率服从任何特定分布,而是直接使用过去一段时间的实际收益率数据,通过排序和分位数提取来估计未来风险。本章将系统讲解其核心步骤、关键参数选择以及Python实现。
4.1 历史模拟法核心流程
历史模拟法的计算逻辑可以概括为三个步骤:收集历史收益率 → 排序 → 取分位数。下面通过一个SVG流程图展示其完整架构。
4.2 历史收益率排序与分位数选取
在历史模拟法中,排序和分位数选取是两个最关键的数学操作。它们的准确性直接决定VaR的可靠性。
4.2.1 收益率排序
假设我们有一个包含N个历史收益率的样本,首先将这些收益率按从小到大(从最差到最好)进行排序:
- 排序后序列:\( r_{(1)} \leq r_{(2)} \leq \cdots \leq r_{(N)} \),其中 \( r_{(1)} \) 是最大亏损(最小收益率)。
- 位置索引:VaR对应的位置由分位数水平 \( \alpha \) 和样本量 \( N \) 共同决定。
4.2.2 分位数选取方法
对于给定的置信水平 \( c \)(如95%或99%),对应的损失分位数水平 \( \alpha = 1 - c \)。分位数的具体取值有两种常见处理方法:
| 方法 | 计算公式 | 说明 | 适用场景 |
|---|---|---|---|
| 位置索引法 | \( k = \lfloor \alpha \times N \rfloor \) | 取排序后第k个收益率作为VaR | 样本量较大时快速近似 |
| 线性插值法 | \( k = \alpha \times N \),在相邻两个收益率之间线性插值 | 更精确,避免离散误差 | 样本量较小或监管要求精确 |
💡 实践建议: 当滚动窗口长度为250天(约1个交易日年)时,95% VaR对应第12.5个最差收益率(250×5%)。使用线性插值法在排序后的第12和第13个收益率之间取值,结果更平滑。
4.3 滚动窗口计算机制
滚动窗口(Rolling Window)是历史模拟法在时间序列上的应用方式。它确保每次计算只使用最新的固定长度历史数据,随着时间推移不断更新样本。
4.3.1 滚动窗口参数
| 参数 | 典型值 | 影响 |
|---|---|---|
| 窗口长度 \( W \) | 250天(1年)、500天(2年) | 越长越稳定,但对市场变化反应越慢 |
| 滚动步长 \( S \) | 1天(每日滚动)、5天(周度滚动) | 步长越小,VaR序列越平滑 |
| 样本起始点 | 固定起点或滑动起点 | 固定起点用于回测,滑动起点用于实时监控 |
4.3.2 滚动计算过程
以下SVG图展示了滚动窗口在时间轴上的移动过程,帮助理解“窗口滑动、数据更新、VaR重算”的循环机制。
4.4 Python实现示例
下面给出一个完整的历史模拟法VaR计算Python实现。代码包含数据生成、滚动窗口计算、分位数选取和结果可视化。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# ==================== 1. 生成模拟收益率数据 ====================
np.random.seed(42)
n_days = 1000
returns = np.random.normal(loc=0.0005, scale=0.015, size=n_days) # 日收益率,均值0.05%,标准差1.5%
# ==================== 2. 历史模拟法VaR计算函数 ====================
def historical_var(returns, window=250, confidence=0.95):
"""
滚动窗口历史模拟法VaR计算
参数:
returns: 收益率序列(numpy数组)
window: 滚动窗口长度
confidence: 置信水平(如0.95表示95% VaR)
返回:
var_series: 每日VaR值序列(长度 = len(returns) - window + 1)
"""
alpha = 1 - confidence
n = len(returns)
var_series = []
for i in range(window, n + 1):
# 提取当前窗口数据
window_data = returns[i - window:i]
# 升序排序
sorted_returns = np.sort(window_data)
# 计算分位数位置(线性插值法)
k = alpha * window
k_low = int(np.floor(k))
k_high = int(np.ceil(k))
# 边界保护
if k_low < 0:
k_low = 0
if k_high >= window:
k_high = window - 1
# 线性插值
if k_low == k_high:
var_value = sorted_returns[k_low]
else:
frac = k - k_low
var_value = sorted_returns[k_low] * (1 - frac) + sorted_returns[k_high] * frac
var_series.append(var_value)
return np.array(var_series)
# ==================== 3. 执行计算 ====================
var_95 = historical_var(returns, window=250, confidence=0.95)
var_99 = historical_var(returns, window=250, confidence=0.99)
# ==================== 4. 结果展示 ====================
print("=" * 60)
print("历史模拟法VaR计算结果(滚动窗口=250天)")
print("=" * 60)
print(f"95% VaR(均值): {np.mean(var_95):.4f} ({np.mean(var_95)*100:.2f}%)")
print(f"95% VaR(标准差): {np.std(var_95):.4f}")
print(f"99% VaR(均值): {np.mean(var_99):.4f} ({np.mean(var_99)*100:.2f}%)")
print(f"99% VaR(标准差): {np.std(var_99):.4f}")
print(f"VaR序列长度: {len(var_95)} 天")
print("=" * 60)
# ==================== 5. 可视化 ====================
plt.figure(figsize=(12, 6))
plt.plot(var_95, label='95% VaR', color='#e74c3c', linewidth=1.5)
plt.plot(var_99, label='99% VaR', color='#9b59b6', linewidth=1.5)
plt.axhline(y=np.mean(var_95), color='#e74c3c', linestyle='--', alpha=0.5)
plt.axhline(y=np.mean(var_99), color='#9b59b6', linestyle='--', alpha=0.5)
plt.title('历史模拟法VaR滚动计算结果', fontsize=14, fontweight='bold')
plt.xlabel('时间(天)', fontsize=12)
plt.ylabel('VaR(日收益率)', fontsize=12)
plt.legend(loc='best')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
📊 代码输出解读:
- 95% VaR均值:约 -0.0245(-2.45%),表示在95%置信水平下,未来1天最大可能亏损不超过2.45%。
- 99% VaR均值:约 -0.0348(-3.48%),比95% VaR更保守,反映尾部风险更大。
- VaR序列波动:随着市场环境变化,VaR值会上下波动,体现风险水平的时变性。
4.5 关键参数对比与选择建议
| 参数 | 选项 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|---|
| 窗口长度 | 250天 vs 500天 | 250天更敏感;500天更稳定 | 250天噪声大;500天反应慢 | 日常风控用250天;监管报送用500天 |
| 分位数方法 | 位置索引 vs 线性插值 | 位置索引简单;插值更精确 | 索引法有离散误差;插值法计算略复杂 | 快速估算用索引;精确计算用插值 |
| 置信水平 | 95% vs 99% | 95%更稳定;99%更保守 | 95%低估尾部;99%数据需求更大 | 内部风控用95%;监管资本用99% |
⚠️ 历史模拟法局限性:
- 假设历史会重演:如果市场结构发生突变(如2008年金融危机),历史数据无法反映新风险。
- 窗口长度选择困境:窗口太短样本不足,太长则包含过时信息。
- 无法处理极端事件:如果历史样本中从未出现过极端损失,VaR会低估真实风险。
4.6 本章小结
- 历史模拟法的核心是“用过去N天的收益率分布代表未来1天的可能损益”。
- 排序+分位数是计算的关键步骤,线性插值法比分位索引法更精确。
- 滚动窗口机制确保VaR随市场变化动态更新,窗口长度通常设为250天。
- Python实现中,通过
np.sort()排序和线性插值公式即可高效计算。 - 该方法简单透明、易于监管沟通,但对市场突变不敏感,需配合其他方法使用。