3. 已实现波动率计算:5分钟抽样、已实现方差、已实现极差
好了,咱们进入正题。波动率这东西,在金融里就像空气——看不见摸不着,但谁都知道它有多重要。传统的方法用日收益率算波动率,说白了就是每天只看收盘价,中间发生了什么一概不管。这就像看一场足球赛只盯着终场比分,过程全丢了。
我个人习惯用高频数据来捕捉波动。为什么?因为信息都在交易过程中。今天咱们聊三个核心工具:5分钟抽样、已实现方差、已实现极差。这三个东西,是我做异质波动率因子时的基本功。
核心思想:用高频数据把「隐藏」的波动挖出来。低频数据只能看到结果,高频数据能看到过程。
3.1 为什么要用5分钟抽样?
你可能要问:为什么偏偏是5分钟?不是1分钟,不是10分钟?
嗯,这里有个权衡。采样太密,比如1分钟,会引入微观结构噪声——买卖价差、订单簿跳动这些乱七八糟的东西。采样太疏,比如30分钟,又会丢失太多信息。我在项目中试过各种频率,最后发现5分钟是个黄金点。
我记得有一次做A股的回测,用1分钟数据算出来的波动率,噪声大到因子失效。换成5分钟,效果立马不一样了。说白了,5分钟抽样是在「信息量」和「噪声」之间找了个平衡。
具体怎么做?很简单:
- 把一天的交易时间切成5分钟一段
- 取每个时间段的最后一笔成交价
- 如果某段没有交易,用前一段的价格填充(这叫前向填充)
举个例子,A股9:30开盘,那么9:30、9:35、9:40……一直到15:00,一共48个采样点。注意,开盘集合竞价和收盘集合竞价的数据要小心处理,我吃过这个亏。
避坑指南:我曾经在采样时没剔除午休时间(11:30-13:00),结果把两个不相干的时段连在一起算,波动率直接翻倍。后来加了个判断:如果两个采样点之间间隔超过10分钟,就跳过。
3.2 已实现方差:最直接的波动度量
有了5分钟的价格序列,已实现方差就顺理成章了。公式其实很简单:
RV = Σ (r_i)²
其中 r_i 是第 i 个5分钟区间的对数收益率。说白了,就是把每个小段的收益率平方,然后加总。
你想想看,这跟传统方差有什么区别?传统方差用日收益率,一天只有一个数。已实现方差用日内收益率,一天有几十个数。信息量完全不在一个量级。
我在做因子挖掘时,发现已实现方差对短期波动特别敏感。比如某天出了个突发消息,价格在10分钟内剧烈波动,传统方差根本捕捉不到,但已实现方差能立刻反映出来。
代码实现也很直接:
import pandas as pd
import numpy as np
def realized_variance(prices, freq='5T'):
"""
计算已实现方差
prices: DataFrame, 包含时间戳和价格
freq: 采样频率,默认5分钟
"""
# 重采样到5分钟
resampled = prices.resample(freq, label='right').last()
# 前向填充缺失值
resampled = resampled.fillna(method='ffill')
# 计算对数收益率
log_returns = np.log(resampled / resampled.shift(1))
# 去掉第一个NaN
log_returns = log_returns.dropna()
# 已实现方差 = 收益率平方和
rv = np.sum(log_returns ** 2)
return rv
小技巧:实际应用中,我习惯把已实现方差开根号,得到已实现波动率。这样单位跟收益率一致,更容易理解。比如RV=0.0004,开根号就是2%,意思是这天的波动率是2%。
3.3 已实现极差:用最高最低价说话
已实现方差虽然好,但它有个问题:只用了收盘价。你想想看,如果某天价格在5分钟内从100涨到110又跌回100,收盘价没变,但波动其实很大。已实现方差会漏掉这个信息。
这时候,已实现极差就派上用场了。它用的是每个5分钟区间内的最高价和最低价:
RR = Σ [ln(High_i / Low_i)]² / (4 * ln(2))
这个公式里的 4*ln(2) 是个调整因子,为了让极差估计跟方差估计在统计上一致。具体推导我就不展开了,你记住用法就行。
我个人习惯把已实现极差和已实现方差结合起来用。为什么?因为它们捕捉的是波动率的不同侧面。方差看的是「路径」,极差看的是「振幅」。两者互补,效果更好。
我记得在做一个多因子模型时,单独用已实现方差的IC(信息系数)只有0.03,加上已实现极差后,复合因子的IC提升到了0.06。虽然绝对值不高,但在高频因子里已经算不错了。
代码实现:
def realized_range(high, low, freq='5T'):
"""
计算已实现极差
high: 最高价序列
low: 最低价序列
"""
# 重采样到5分钟,取最高价和最低价
high_resampled = high.resample(freq, label='right').max()
low_resampled = low.resample(freq, label='right').min()
# 计算极差比率
range_ratio = np.log(high_resampled / low_resampled)
# 去掉无穷大或NaN
range_ratio = range_ratio.replace([np.inf, -np.inf], np.nan).dropna()
# 已实现极差
rr = np.sum(range_ratio ** 2) / (4 * np.log(2))
return rr
注意:如果某个5分钟区间内最高价等于最低价(比如没有交易),极差为0,取对数会出问题。我一般会加个判断:如果最高价等于最低价,直接跳过这个区间。
3.4 三种方法的对比
说了这么多,咱们来做个对比。我整理了一个表格,方便你直观理解:
| 方法 | 输入数据 | 优点 | 缺点 |
|---|---|---|---|
| 5分钟抽样 | 收盘价 | 简单、计算快、噪声小 | 丢失区间内波动信息 |
| 已实现方差 | 收盘价 | 理论成熟、易于扩展 | 对跳跃不敏感 |
| 已实现极差 | 最高价、最低价 | 捕捉振幅、信息量大 | 对极端值敏感 |
你可能会问:那我到底该用哪个?我的建议是:
- 如果数据质量一般(比如有大量缺失),用5分钟抽样+已实现方差
- 如果数据质量好,而且你想捕捉更精细的波动,加上已实现极差
- 如果做因子组合,把两者都放进去,让模型自己选
3.5 核心逻辑流程图
下面这张图,是我自己画的一个流程图,把整个计算过程串起来了。你看一眼就明白:
3.6 实战中的坑与经验
最后,分享几个我在实战中踩过的坑:
- 数据对齐问题:不同股票的交易时间不完全一致,尤其是小盘股。我建议统一用交易所时间,不要用本地时间。
- 隔夜跳空处理:今天的收盘价和明天的开盘价之间有个跳空,这个要不要算进波动率?我个人习惯是单独算,不混在日内波动里。
- 极端值处理:如果某天出现涨跌停,5分钟区间内价格不动,波动率为0。这时候要小心,别把异常值当正常数据用。
嗯,关于已实现波动率的计算,今天就聊到这儿。这三个工具——5分钟抽样、已实现方差、已实现极差——是高频波动率建模的基石。你先把它们跑通,后面讲异质波动率分解的时候,就能直接上手了。
一句话总结:5分钟抽样是基础,已实现方差看路径,已实现极差看振幅。三者结合,才能把波动率「看透」。
公众号:蓝海资料掘金营,微信deep3321