4、VaR计算方法(一):历史模拟法——原理、步骤、优缺点、Python实现

做市交易里,风险度量是个绕不开的话题。今天咱们聊聊VaR计算里最直观、也最容易被新手接受的方法——历史模拟法

说实话,我刚入行那会儿,第一次接触VaR就是用的历史模拟法。为什么?因为它简单、透明,你一看就懂它在干什么。不像那些蒙特卡洛模拟,搞一堆随机数,看着就头大。

4.1 历史模拟法的核心思想

历史模拟法的原理,说白了就一句话:过去发生过的,未来也可能发生

我们不去假设收益率服从什么正态分布、t分布,也不去估计什么均值方差。我们直接拿历史数据说话——把过去N天的收益率排序,找到第5%分位数,那就是95%置信水平下的VaR。

举个例子:你手里有1000万的头寸,过去500天的日收益率数据摆在那。你把收益率从小到大排个序,找到第25个(500×5%)最差的收益率,假设是-2.3%。那你的VaR就是:1000万 × 2.3% = 23万。

这意味着什么?根据历史数据,你有95%的把握,明天亏损不会超过23万。

核心公式:
VaR95% = -Percentile(R历史, 5%) × 当前头寸

4.2 具体步骤:一步一步来

我在做市团队里带新人时,经常让他们手算一遍历史模拟法。为什么?因为只有亲手算过,才能真正理解。步骤其实就五步:

  1. 收集历史价格数据——一般取过去250到500个交易日的数据。我个人习惯用500天,样本量够大,尾部事件也能覆盖一些。
  2. 计算每日收益率——用对数收益率还是简单收益率?我建议用对数收益率,因为它具有可加性,而且更接近正态分布。公式:rt = ln(Pt / Pt-1)
  3. 对收益率排序——从小到大排,最差的在最前面。
  4. 找到分位点——95%置信水平就看第5%分位数,99%就看第1%分位数。
  5. 乘以当前头寸——得到VaR的金额。

嗯,这里要注意:分位点的计算有几种方法,比如线性插值法、最近邻法。我一般用线性插值,结果更平滑一些。

4.3 优缺点分析

优点 缺点
  • 无需分布假设——不依赖正态分布,能捕捉厚尾特征
  • 直观易懂——给老板解释时,一句话就能说清楚
  • 实现简单——几行Python代码就能搞定
  • 完全可复现——同样的数据,谁算结果都一样
  • 依赖历史数据——如果历史没发生过极端事件,VaR会偏小
  • 等权重问题——100天前的数据和昨天的数据权重一样,不合理
  • 样本量敏感——数据太少,尾部估计不准;数据太多,可能包含过时的市场结构
  • 无法预测结构性变化——市场规则变了,历史就没参考价值了
避坑指南:我曾经在2015年股灾前用历史模拟法算VaR,结果严重低估了风险。为什么?因为过去一年都没发生过单日跌5%以上的情况,历史数据里根本没有这个样本。从那以后,我每次用历史模拟法,都会额外做一次压力测试,看看如果发生历史上最极端的情况,我的头寸会怎样。

4.4 Python实现:从数据到VaR

代码其实很简单,我直接贴一个我常用的版本。这个函数我用了好几年,改过几版,现在这个版本比较稳定。

import numpy as np
import pandas as pd

def historical_var(prices, confidence_level=0.95):
    """
    历史模拟法计算VaR
    
    参数:
        prices: 价格序列,pandas Series或numpy array
        confidence_level: 置信水平,默认95%
    
    返回:
        VaR值(百分比形式)
    """
    # 计算对数收益率
    returns = np.log(prices[1:] / prices[:-1])
    
    # 排序
    sorted_returns = np.sort(returns)
    
    # 找到分位点
    index = int((1 - confidence_level) * len(sorted_returns))
    
    # 线性插值,更精确
    frac = (1 - confidence_level) * len(sorted_returns) - index
    if frac > 0 and index + 1 < len(sorted_returns):
        var = sorted_returns[index] * (1 - frac) + sorted_returns[index + 1] * frac
    else:
        var = sorted_returns[index]
    
    return -var  # 返回正值

# 示例:用模拟数据测试
np.random.seed(42)
# 生成1000个价格数据,模拟做市交易中的高频价格
prices = 100 * np.exp(np.cumsum(np.random.normal(0, 0.01, 1000)))

var_95 = historical_var(prices, 0.95)
var_99 = historical_var(prices, 0.99)

print(f"95% VaR: {var_95:.4f} ({var_95*100:.2f}%)")
print(f"99% VaR: {var_99:.4f} ({var_99*100:.2f}%)")
个人经验:实际做市交易中,我一般会同时算三个时间窗口:250天(约1年)、500天(约2年)、1000天(约4年)。然后取三个VaR的最大值作为风控参考。为什么?因为不同窗口捕捉到的市场状态不同,取最大值相当于做了个保守估计。

4.5 知识体系框架

下面这张图是我自己整理的,把历史模拟法的核心逻辑串起来了。你看一遍,应该就能记住整个流程。

历史模拟法知识体系 历史价格数据 步骤1:计算每日对数收益率 步骤2:收益率从小到大排序 步骤3:找到第5%分位点 VaR值 关键参数 • 置信水平:95% 或 99% • 历史窗口:250-500天 • 收益率类型:对数收益率 • 分位点计算:线性插值 • 输出:百分比或金额 核心思想:过去发生过的,未来也可能发生

4.6 实战中的注意事项

做市交易和普通投资不太一样。我们每天交易量很大,持仓时间短,对VaR的时效性要求很高。我分享几个实战中踩过的坑:

  • 数据频率要匹配——如果你是高频做市,用日收益率算VaR可能不够。我一般会同时算日VaR和小时VaR,两个都监控。
  • 注意价格跳跃——做市交易中经常遇到跳空,比如开盘直接低开2%。历史模拟法对这种跳跃的捕捉能力有限,因为历史数据里可能没发生过。
  • 不要只看一个置信水平——我习惯同时看95%和99%两个水平。如果两者差距很大,说明尾部风险很重,要小心。
  • 定期更新数据——每天收盘后更新历史数据,剔除最旧的一天,加入最新的一天。保持窗口长度不变。
一句话总结:历史模拟法是最直观的VaR计算方法,适合做市交易的日常风控。但它不是万能的,尤其当市场发生结构性变化时,历史数据会失效。所以,我每次用历史模拟法算完VaR,都会问自己一个问题:如果明天发生历史上从未发生过的事情,我的头寸扛得住吗?

好了,历史模拟法就聊到这儿。下一章咱们聊聊参数法,也就是那个假设收益率服从正态分布的方法。到时候你会发现,参数法虽然数学上更漂亮,但坑也不少。


公众号:蓝海资料掘金营,微信deep3321