波动率计算基础:从历史数据中捕捉市场脉搏
做量化投资这些年,我越来越觉得波动率是个有意思的东西。它不像价格那样直白,也不像成交量那样热闹,但它就像市场的呼吸——你感受不到的时候,往往就是暴风雨前的宁静。
今天咱们聊聊波动率计算的基础。说白了,就是怎么从历史价格数据里,把市场的「脾气」给量化出来。
简单收益率 vs 对数收益率:选哪个?
先看一个最基础的问题:收益率怎么算?
简单收益率,公式很直观:
R_simple = (P_t - P_{t-1}) / P_{t-1}
比如昨天收盘100块,今天收盘105块,简单收益率就是5%。
对数收益率呢?
R_log = ln(P_t / P_{t-1})
同样是5%的涨幅,对数收益率算出来大概是4.88%。
你可能会问:差这么点,有必要纠结吗?
嗯,这里要注意。我在做高频策略回测时,曾经因为收益率计算方式选错,导致夏普比率差了0.3。0.3什么概念?在机构里,这足以让一个策略从「推荐」变成「待观察」。
我个人习惯用对数收益率,原因有三:
- 时间可加性:对数收益率可以直接相加得到多期总收益率,简单收益率不行
- 分布更接近正态:虽然实际中也不是完美的正态,但比简单收益率好得多
- 数值稳定性:价格波动大时,对数收益率不会出现负值过大导致的异常
我的小技巧:如果只是做日频以上的策略,两种收益率差别不大。但做高频或跨资产组合时,请务必用对数收益率。我曾经用简单收益率算跨市场相关性,结果出了个伪相关,排查了两天才发现是收益率计算方式的问题。
历史波动率:最朴素的波动度量
历史波动率,说白了就是收益率的标准差。但这里有个关键点:我们算的是「年化」波动率。
公式长这样:
σ = std(R) * sqrt(T)
其中T是每年的交易天数。A股一般用252天,美股用252或260天。
举个例子:
import numpy as np
import pandas as pd
# 模拟100天的日收益率
np.random.seed(42)
returns = np.random.normal(0.001, 0.02, 100)
# 计算日波动率
daily_vol = np.std(returns, ddof=1)
# 年化
annual_vol = daily_vol * np.sqrt(252)
print(f"日波动率: {daily_vol:.4f}")
print(f"年化波动率: {annual_vol:.4f}")
输出结果:
日波动率: 0.0198
年化波动率: 0.3142
31.42%的年化波动率,说明这只股票波动不小。A股里,茅台大概在25%左右,小盘股经常到40%以上。
避坑指南:我曾经犯过一个低级错误——直接用日收益率的标准差当波动率,忘了年化。结果给风控部门报了个2%的波动率,对方直接问我「你确定这是股票不是国债?」。嗯,从那以后我每次算波动率都会先确认:年化了吗?
滚动窗口波动率:动态捕捉市场变化
全样本的历史波动率有个问题:它把过去所有数据一视同仁。但市场是会变的啊!
想想看,2020年3月的美股波动率,和2021年3月的能一样吗?
所以我们需要滚动窗口波动率——只取最近N天的数据来算。
代码实现很简单:
def rolling_volatility(prices, window=20, annualize=True):
"""
计算滚动窗口波动率
Parameters:
-----------
prices : pd.Series
价格序列
window : int
滚动窗口大小(天数)
annualize : bool
是否年化
Returns:
--------
pd.Series : 滚动波动率序列
"""
# 计算对数收益率
log_returns = np.log(prices / prices.shift(1))
# 滚动计算标准差
rolling_std = log_returns.rolling(window=window).std()
if annualize:
rolling_std = rolling_std * np.sqrt(252)
return rolling_std
# 使用示例
prices = pd.Series(np.cumprod(1 + np.random.normal(0.001, 0.02, 500)))
vol_20d = rolling_volatility(prices, window=20)
vol_60d = rolling_volatility(prices, window=60)
窗口大小的选择,其实是个艺术活:
| 窗口大小 | 特点 | 适用场景 |
|---|---|---|
| 10-20天 | 反应快,但噪音大 | 短线交易、高频调仓 |
| 60-120天 | 平衡灵敏度和稳定性 | 中频策略、风险预算 |
| 252天 | 稳定,但滞后严重 | 长期资产配置、监管报告 |
核心要点:滚动窗口波动率不是越短越好。我见过有人用5天窗口做波动率控制,结果策略频繁调仓,交易成本把收益全吃掉了。建议至少用20天窗口,除非你做的是日内高频。
知识体系总览
下面这张图,把本章的核心逻辑串起来了:
几个实战中的坑
最后分享几个我踩过的坑:
- 数据频率要统一:别把日数据和周数据混在一起算波动率。我见过有人用日收益率算波动率,但年化时用了252,结果数据里混了周数据——波动率直接翻倍。
- 缺失值处理:停牌日、节假日会导致收益率缺失。直接用dropna()可能会丢失信息,建议用向前填充。
- 极端值影响:一个涨停板就能让20天波动率跳升30%。如果做波动率控制,建议对收益率做缩尾处理(winsorize)。
我的习惯:每次算波动率前,先画个收益率分布直方图。如果看到明显的尖峰或厚尾,我会考虑用稳健统计量(如MAD)代替标准差。这招在A股特别管用——你懂的,A股经常出现「异常波动」。
好了,波动率计算的基础就聊到这儿。记住一句话:波动率不是预测未来的水晶球,但它能告诉你市场现在有多「紧张」。下一节我们会聊更高级的波动率模型,但今天这些基础,才是你所有策略的根基。