3. 历史波动率Python实现:数据获取、计算与可视化
好,咱们直接进入实战环节。
历史波动率这东西,说白了就是「过去的价格波动有多剧烈」。你想想看,如果一只股票过去一年上蹿下跳,那它的历史波动率肯定高;如果它像心电图一样平,那波动率就低。这个指标是期权定价、风险管理的基石,也是我们做量化交易绕不开的基本功。
我个人习惯用 yfinance 拿数据,用 numpy 算收益率,再用 matplotlib 画图。这一套组合拳打下来,你就能把历史波动率从原始数据变成可视化的曲线。嗯,咱们一步步来。
3.1 数据获取:用 yfinance 拉取行情
先装库。如果你还没装 yfinance,终端里跑一句:
pip install yfinance numpy pandas matplotlib
然后咱们写代码。我习惯把数据获取封装成一个函数,这样后面改标的、改时间范围都很方便。
import yfinance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
def get_stock_data(ticker, start='2023-01-01', end='2024-12-31'):
"""
获取股票历史日线数据
ticker: 股票代码,比如 'AAPL' 或 '600519.SS'
start, end: 起止日期
"""
df = yf.download(ticker, start=start, end=end, progress=False)
# 我只取收盘价,够用了
return df['Close']
# 拿苹果的数据试试
close_prices = get_stock_data('AAPL')
print(close_prices.head())
akshare、tushare 这些国内数据源。不过为了课程统一,咱们先用 yfinance。
3.2 对数收益率计算:为什么不用简单收益率?
拿到收盘价之后,下一步就是算收益率。这里有个关键选择:用简单收益率还是对数收益率?
我个人强烈推荐对数收益率。原因有三:
- 时间可加性:对数收益率可以直接相加得到多期总收益率,简单收益率不行。
- 分布更接近正态:虽然实际数据有厚尾,但比简单收益率好得多。
- 计算波动率更方便:对数收益率的标准差可以直接年化,简单收益率需要调整。
代码实现很简单:
# 计算对数收益率
log_returns = np.log(close_prices / close_prices.shift(1))
# 去掉第一行的 NaN
log_returns = log_returns.dropna()
print(log_returns.head())
pct_change() 算简单收益率,然后拿去算波动率,结果和期权市场报价对不上。后来才发现,对数收益率和简单收益率在小波动时差别不大,但遇到大跳空(比如财报日),差异就非常明显了。所以,做波动率分析,请用对数收益率。
3.3 年化波动率计算:把日波动率放大到年
算完日收益率,我们得到的是「日波动率」。但实际交易中,我们更关心年化波动率。怎么转换?
公式很简单:
σ年化 = σ日 × √252
为什么是252?因为一年大约有252个交易日。这个数字不是拍脑袋定的,是美国市场实际交易日统计出来的。A股大概243天左右,但为了和国际接轨,很多机构统一用252。
# 计算日波动率(标准差)
daily_vol = log_returns.std()
# 年化
annual_vol = daily_vol * np.sqrt(252)
print(f"日波动率: {daily_vol:.4f}")
print(f"年化波动率: {annual_vol:.4f}")
跑一下苹果的数据,你大概会得到年化波动率在0.2到0.3之间,也就是20%-30%。嗯,这个范围对于大盘股来说是正常的。
3.4 滚动窗口可视化:看波动率如何随时间变化
算一个整体的年化波动率太粗糙了。我们更想知道:波动率在过去一年里是怎么变化的?是平稳的,还是忽高忽低?
这时候就需要「滚动窗口」了。说白了,就是固定一个窗口长度(比如60天),每天往前滑动,算这个窗口内的波动率。这样就能得到一条波动率曲线。
# 设置滚动窗口大小(交易日)
window = 60
# 计算滚动年化波动率
rolling_vol = log_returns.rolling(window=window).std() * np.sqrt(252)
# 去掉前 window-1 天的 NaN
rolling_vol = rolling_vol.dropna()
# 可视化
plt.figure(figsize=(12, 6))
plt.plot(rolling_vol.index, rolling_vol, label=f'{window}日滚动年化波动率', color='#FF6B6B')
plt.title('AAPL 历史波动率(滚动窗口)')
plt.xlabel('日期')
plt.ylabel('年化波动率')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
跑完这段代码,你会看到一条上下起伏的曲线。比如2023年7月到10月,苹果的波动率明显上升,那是因为当时市场对iPhone销量有担忧。2024年初又降下来了。
3.5 本章知识体系总览
下面这张图,把咱们这一章的核心逻辑串起来了。从数据源到最终的可视化曲线,每一步都有它的意义。
3.6 完整代码整合
最后,我把上面所有代码整合成一个完整的函数。你以后做分析,直接复制这个函数,改一下参数就能用。
def historical_volatility(ticker, start='2023-01-01', end='2024-12-31', window=60):
"""
计算并可视化历史波动率
参数:
ticker: 股票代码
start, end: 日期范围
window: 滚动窗口大小(交易日)
返回:
rolling_vol: 滚动年化波动率序列
"""
# 1. 获取数据
close = yf.download(ticker, start=start, end=end, progress=False)['Close']
# 2. 对数收益率
log_ret = np.log(close / close.shift(1)).dropna()
# 3. 滚动年化波动率
rolling_vol = log_ret.rolling(window=window).std() * np.sqrt(252)
rolling_vol = rolling_vol.dropna()
# 4. 可视化
plt.figure(figsize=(12, 6))
plt.plot(rolling_vol.index, rolling_vol,
label=f'{window}日滚动年化波动率', color='#FF6B6B', linewidth=2)
plt.title(f'{ticker} 历史波动率')
plt.xlabel('日期')
plt.ylabel('年化波动率')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
return rolling_vol
# 使用示例
aapl_vol = historical_volatility('AAPL', window=60)
好了,这一章的内容就到这里。你跟着代码跑一遍,应该能拿到一条漂亮的波动率曲线。下一章咱们会深入探讨如何用这个波动率去做交易策略,敬请期待。
公众号:蓝海数据掘金营,微信deep3321