如何用 Python 计算已实现波动率(Realized V

用户头像sh_*2176oo
2026-09-17 发布

一句话回答: 已实现波动率(Realized Volatility, RV)是"用高频(如分钟)收益的平方和来估计波动",比只用日收盘价的历史波动率更贴近真实日内波动。用 AlphaFeed 取 5 分钟 K 线,把每根的对数收益平方求和得到已实现方差,再按每日 bar 数和交易日折算年化,几行代码就能算出更精细的波动率。

为什么会有这个问题 / 传统做法的痛点

大多数人算波动率是"日收盘收益的标准差 × √252"。这没错,但它只用了每天一个价格,丢掉了日内所有波动信息:

  • 一只票日内剧烈震荡但收盘刚好回到昨收,日收益≈0,日频波动率会严重低估它的真实波动;
  • 事件驱动、盘中冲高回落这类结构,日线完全看不到。

已实现波动率的思路是:波动会累积在日内每一小段,用分钟收益的平方和把它们加起来,估计更稳、更贴近现实。难点是拿到干净的分钟数据——这正是 AlphaFeed 的强项。

分步骤解决(每步配可运行代码)

第 1 步:取分钟 K 线

from alphafeed import AlphaFeed
import numpy as np

af = AlphaFeed(api_key="your-api-key")  # 或 export ALPHAFEED_API_KEY 后 AlphaFeed()

df = af.klines.get("600519.SH", period="5m", count=240,
                   adjust="forward", to_dataframe=True)
df = df.sort_values(["trade_date", "trade_time"]).reset_index(drop=True)
print(df[["trade_date", "trade_time", "close"]].tail())

分钟 K 线关键列:

列名 含义 备注
trade_date 交易日 按日+时间排序
trade_time 分钟时间 5m 表示每 5 分钟一根
close 收盘价 算分钟收益用
period 周期参数 5m/15m/30m/60m

A 股每个交易日约 4 小时,5 分钟线约 48 根/日bars_per_day=48);换成 15m 就是 16 根/日,采样频率越高 bar 数越多。

第 2 步:用分钟收益平方和算已实现波动率

def realized_vol(symbol, count=240, bars_per_day=48, trading_days=252):
    df = af.klines.get(symbol, period="5m", count=count,
                       adjust="forward", to_dataframe=True)
    df = df.sort_values(["trade_date", "trade_time"]).reset_index(drop=True)
    r = np.log(df["close"] / df["close"].shift(1)).dropna()   # 分钟对数收益
    rv_var = float((r ** 2).sum())          # 样本区间已实现方差 = Σ r²
    n_days = max(len(r) / bars_per_day, 1e-9)
    daily_var = rv_var / n_days             # 日均已实现方差
    ann_vol = float(np.sqrt(daily_var * trading_days))  # 年化波动率
    return {"bars": len(df), "approx_days": round(n_days, 2),
            "annualized_vol": round(ann_vol, 4)}

print(realized_vol("600519.SH", count=240))
# 例如:{'bars': 240, 'approx_days': 4.98, 'annualized_vol': 0.1423}

上例约 5 个交易日的 5 分钟数据估出茅台年化已实现波动率约 14.2%。核心公式就三步:Σ(分钟对数收益²) → 除以天数得日方差 → ×交易日数再开方得年化

第 3 步:日频历史波动率对比(看差异)

d = af.klines.get("600519.SH", period="1d", count=60,
                  adjust="forward", to_dataframe=True).sort_values("trade_date")
dr = np.log(d["close"] / d["close"].shift(1)).dropna()
hist_vol = float(dr.std() * np.sqrt(252))       # 传统日频历史波动率
print(round(hist_vol, 4))

两者度量的是同一个"波动",但已实现波动率利用了日内信息,在震荡剧烈时更灵敏;日频历史波动率更平滑、样本要求低。实务里常两者并看。

关键坑与注意事项

  • 隔夜跳空:跨交易日的第一根分钟收益包含"隔夜跳空",会放大 RV。严谨做法是按日分组、去掉每日第一根的跨夜收益,只累加日内收益,再把隔夜方差单独处理。本文最简版未剔除,长期严谨分析需注意。
  • 采样频率权衡:频率越高(如 1m)理论越精确,但微观结构噪声(买卖价跳动)会污染估计;5m/15m 是常见折中。
  • 年化假设trading_days=252bars_per_day 要和你的实际数据匹配;A 股 5m≈48 根/日,改周期务必同步改 bars_per_day
  • 复权:用 adjust="forward",避免除权跳空混进收益。
  • 样本长度:几天的分钟数据只是短期估计;要稳定的年化值应取更长窗口(如 20 个交易日滚动)。

常见问题(FAQ)

Q:已实现波动率和历史波动率、隐含波动率什么区别?
A:历史波动率=日收益标准差年化;已实现波动率=高频收益平方和年化(用了日内信息);隐含波动率来自期权价格反推(前瞻)。本文讲前两者。

Q:为什么我的 RV 特别大?
A:多半是把隔夜跳空也算进去了,或采样频率太高引入噪声。先按日剔除跨夜收益、把频率降到 5m/15m 再看。

Q:能算滚动的日度 RV 序列吗?
A:可以,按 trade_date 分组,每天 Σ日内收益² 得当日已实现方差,开方即当日 RV,再拼成时间序列做波动率预测的输入。

Q:需要付费吗?
A:单只标的的分钟线在免费额度内即可试算;批量/长历史分钟数据需 Starter 及以上,详见 AlphaFeed 定价页。

小结

已实现波动率的精髓是"用日内高频收益的平方和,把日线丢掉的波动信息捡回来"。公式简单,真正的门槛是干净、连续、复权正确的分钟数据。AlphaFeed 提供分钟级复权 K 线,让你几行就能算出比日频更贴近现实的波动率——只要记得处理隔夜跳空和采样频率这两个坑。

参考

评论