第1章:实盘数据获取——用Python抓取期权链与计算隐含波动率
做期权交易,最怕什么?
我个人觉得,最怕的是「闭着眼睛做」。你连当前市场的隐含波动率是多少都不知道,那跟赌博有什么区别?
这一章,咱们就来解决这个最基础、也最关键的问题——怎么用Python拿到实盘期权数据,并算出隐含波动率。
1.1 数据源的选择:别在第一步就踩坑
先说说数据源。我刚开始做量化的时候,也踩过不少坑。有的数据源免费但延迟高,有的收费但接口不稳定。
目前国内比较靠谱的期权数据源,我列一下:
| 数据源 | 特点 | 费用 | 我个人的建议 |
|---|---|---|---|
| Wind | 数据全,接口稳定 | 贵(机构用) | 机构首选,个人不推荐 |
| 聚宽/JQData | 有免费额度,适合学习 | 部分免费 | 初学者可以先用这个 |
| 东方财富Choice | 数据质量不错 | 中等 | 性价比还行 |
| 新浪/腾讯财经API | 免费,但需要自己解析 | 免费 | 适合练手,别用于实盘 |
小提示: 我个人习惯先用聚宽做策略回测,实盘时再切换到Wind。别在数据源上省钱,数据不准,策略再好也是白搭。
1.2 抓取期权链数据:代码来了
好,咱们直接上代码。这里我用的是聚宽的数据接口,因为免费且容易上手。
import pandas as pd
import numpy as np
from jqdatasdk import *
import datetime
# 登录聚宽(需要注册账号)
auth('你的账号', '你的密码')
# 获取50ETF期权链
def get_option_chain(date, etf_code='510050.XSHG'):
"""
获取指定日期的期权链数据
"""
# 获取所有期权合约
all_options = get_all_securities(['option'], date=date)
# 筛选出50ETF期权
etf_options = all_options[all_options['display_name'].str.contains('50ETF')]
# 获取合约信息
option_data = []
for code in etf_options.index:
# 获取合约基本信息
info = get_security_info(code)
# 获取当日行情
day_data = get_price(code, start_date=date, end_date=date,
fields=['open', 'close', 'high', 'low', 'volume', 'money'])
if not day_data.empty:
option_data.append({
'code': code,
'name': info.display_name,
'type': 'call' if '购' in info.display_name else 'put',
'strike': info.strike_price,
'expire_date': info.end_date,
'close': day_data['close'].values[0],
'volume': day_data['volume'].values[0]
})
return pd.DataFrame(option_data)
# 获取今天的数据
today = datetime.date.today()
option_chain = get_option_chain(today)
print(f"共获取到 {len(option_chain)} 个期权合约")
print(option_chain.head())
重点: 这段代码的核心是get_all_securities和get_price两个函数。前者拿到所有期权合约列表,后者获取具体行情。我在项目中遇到过一个问题——有些合约当天没有交易,成交量是0,这种合约的隐含波动率算出来也没意义,记得过滤掉。
1.3 计算隐含波动率:这才是硬核部分
拿到期权价格了,怎么算隐含波动率?
说白了,隐含波动率就是「把市场价格代入BS公式,反推出来的波动率」。但BS公式没法直接求逆,得用数值方法。
我常用的方法是牛顿迭代法。为什么?因为它收敛快,一般迭代5-10次就能得到精确结果。
from scipy.stats import norm
from scipy.optimize import brentq
def bs_price(S, K, T, r, sigma, option_type='call'):
"""
Black-Scholes定价公式
S: 标的价格
K: 行权价
T: 剩余时间(年)
r: 无风险利率
sigma: 波动率
"""
d1 = (np.log(S/K) + (r + 0.5*sigma**2)*T) / (sigma*np.sqrt(T))
d2 = d1 - sigma*np.sqrt(T)
if option_type == 'call':
price = S*norm.cdf(d1) - K*np.exp(-r*T)*norm.cdf(d2)
else:
price = K*np.exp(-r*T)*norm.cdf(-d2) - S*norm.cdf(-d1)
return price
def implied_volatility(market_price, S, K, T, r, option_type='call'):
"""
使用牛顿迭代法计算隐含波动率
"""
# 初始猜测
sigma = 0.3
# 牛顿迭代
for i in range(100):
price = bs_price(S, K, T, r, sigma, option_type)
vega = S * norm.pdf(d1(S, K, T, r, sigma)) * np.sqrt(T)
diff = price - market_price
if abs(diff) < 1e-6:
break
sigma = sigma - diff / vega
# 防止波动率变成负数
if sigma < 0.01:
sigma = 0.01
return sigma
def d1(S, K, T, r, sigma):
return (np.log(S/K) + (r + 0.5*sigma**2)*T) / (sigma*np.sqrt(T))
# 计算整个期权链的隐含波动率
def calculate_chain_iv(option_chain, S, r=0.03):
"""
计算期权链上所有合约的隐含波动率
"""
results = []
for _, row in option_chain.iterrows():
# 计算剩余时间(年)
T = (row['expire_date'] - today).days / 365.0
if T <= 0 or row['close'] <= 0:
continue
try:
iv = implied_volatility(
row['close'], S, row['strike'], T, r,
'call' if '购' in row['name'] else 'put'
)
results.append({
'strike': row['strike'],
'type': row['type'],
'iv': iv,
'volume': row['volume']
})
except:
# 有些合约可能算不出来,跳过
continue
return pd.DataFrame(results)
# 假设50ETF当前价格是2.800
S = 2.800
iv_data = calculate_chain_iv(option_chain, S)
print("隐含波动率计算结果:")
print(iv_data.head())
注意: 我曾经在实盘中遇到过一个问题——临近到期的期权,时间价值很小,隐含波动率计算容易发散。我的经验是:剩余时间少于7天的合约,直接过滤掉,算出来的值也不可靠。
1.4 可视化:看一眼波动率微笑
数据算出来了,光看数字不够直观。咱们画个图,看看波动率微笑长什么样。
import matplotlib.pyplot as plt
def plot_volatility_smile(iv_data):
"""
绘制波动率微笑曲线
"""
plt.figure(figsize=(12, 6))
# 分别画看涨和看跌
for opt_type in ['call', 'put']:
subset = iv_data[iv_data['type'] == opt_type]
plt.plot(subset['strike'], subset['iv'],
'o-', label=f'{opt_type}期权', linewidth=2)
plt.xlabel('行权价')
plt.ylabel('隐含波动率')
plt.title('50ETF期权波动率微笑')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
plot_volatility_smile(iv_data)
你想想看,如果画出来的曲线是平的,说明市场认为未来波动不大。如果左边翘起来(虚值看跌期权IV高),说明市场在担心下跌——这就是偏斜。
1.5 知识体系总览
这一章的内容,我画了个图帮你理清思路:
1.6 避坑指南:我踩过的那些坑
- 数据延迟问题: 我曾经用免费API做日内交易,结果数据延迟了3秒,策略直接亏了2%。后来我学乖了——做日内交易必须用Level-2数据。
- 合约过滤: 有些期权合约流动性极差,一天就成交几手。这种合约的隐含波动率算出来也没参考价值。我一般会过滤掉成交量低于100手的合约。
- 除权除息: 50ETF分红的时候,期权合约会调整。如果不做复权处理,算出来的隐含波动率会突变。嗯,这个坑我踩过两次才记住。
- 无风险利率: 别用固定的3%!我建议用SHIBOR 1个月利率,或者国债收益率。利率差0.5%,隐含波动率能差出1-2个点。
我的习惯: 每天开盘前,我会先跑一遍这个脚本,看看当前市场的波动率结构。如果发现某个行权价的IV异常高,我会特别关注——往往意味着有大资金在押注某个方向。
好了,这一章的内容就到这里。数据拿到了,隐含波动率也算出来了,下一章咱们就可以开始分析波动率微笑的形态了。