🚀 AI 一键生成 joinquant 策略代码

JoinQuant 期权数据 API 实战:如何获取期权数据并计算隐含波动率?

joinquant | 发布时间: 2025/12/16
以下内容由 EasyQuant 生成。

问题描述

如何获取期权价格数据,并计算它的隐含波动率?

解决方案

在聚宽(JoinQuant)平台上,获取期权价格数据主要使用 get_priceget_current_data API。而计算隐含波动率(Implied Volatility, IV)通常需要结合期权的市场价格、标的价格、行权价、剩余期限和无风险利率,通过 Black-Scholes (BS) 模型 反推得出。

以下是具体的步骤和完整的代码实现。

核心步骤说明

  1. 获取期权合约信息:使用 get_security_info 获取行权价(Strike Price)和到期日(Expiration Date)。
  2. 获取市场数据:使用 get_price 获取期权及其标的资产(如 50ETF)的当前价格。
  3. 定义 BS 模型:编写 Black-Scholes 定价公式。
  4. 数值求解 IV:由于 BS 公式无法直接解析出波动率,需要使用数值方法(如二分法或牛顿法,这里使用 scipy.optimize 库)来求解使模型价格等于市场价格的波动率。

完整代码实现

以下代码可以在聚宽的 研究环境(Research)回测环境 中运行。

# -*- coding: utf-8 -*-
import numpy as np
import pandas as pd
from scipy.stats import norm
from scipy.optimize import brentq
from jqdata import *
import datetime

def d1(S, K, T, r, sigma):
    """计算BS模型中的d1"""
    return (np.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))

def d2(S, K, T, r, sigma):
    """计算BS模型中的d2"""
    return d1(S, K, T, r, sigma) - sigma * np.sqrt(T)

def bs_price(S, K, T, r, sigma, option_type='call'):
    """
    根据BS公式计算期权理论价格
    S: 标的资产价格
    K: 行权价
    T: 剩余期限(年)
    r: 无风险利率
    sigma: 波动率
    option_type: 'call' 或 'put'
    """
    if T <= 0:
        return max(0, S - K) if option_type == 'call' else max(0, K - S)
    
    if option_type == 'call':
        price = S * norm.cdf(d1(S, K, T, r, sigma)) - K * np.exp(-r * T) * norm.cdf(d2(S, K, T, r, sigma))
    else:
        price = K * np.exp(-r * T) * norm.cdf(-d2(S, K, T, r, sigma)) - S * norm.cdf(-d1(S, K, T, r, sigma))
    return price

def calculate_implied_volatility(option_price, S, K, T, r, option_type='call'):
    """
    计算隐含波动率
    """
    if option_price <= 0:
        return np.nan
        
    # 定义目标函数:BS理论价格 - 市场价格 = 0
    def objective_function(sigma):
        return bs_price(S, K, T, r, sigma, option_type) - option_price

    # 使用Brent方法求解根(隐含波动率),假设波动率在 0.1% 到 500% 之间
    try:
        iv = brentq(objective_function, 0.001, 5.0)
        return iv
    except Exception as e:
        # 如果求解失败(例如价格深度实值或虚值导致无法收敛),返回NaN
        return np.nan

# --- 示例调用逻辑 ---

# 1. 设定参数
# 假设我们计算 50ETF购12月3000 (示例代码,请替换为当前存在的真实合约代码)
# 注意:实际使用时请在聚宽查询最新的期权代码
option_code = '10002063.XSHG'  # 示例:50ETF购11月3000
date = '2019-11-01'            # 计算日期

# 2. 获取合约基本信息
info = get_security_info(option_code)
strike_price = info.high_limit # 注意:聚宽info中没有直接的strike_price字段,通常需要从合约命名或扩展数据获取
# 在聚宽中,期权行权价通常建议通过 get_option_contracts 或外部数据表获取
# 这里为了演示,我们假设已知行权价,或者解析 display_name
# 实际更严谨的方式是查询 opt_contract_info 表
# 这里简化处理,假设行权价为 3.000
K = 3.000 

# 获取标的代码 (50ETF)
underlying_symbol = '510050.XSHG'

# 获取到期日
end_date = info.end_date
# 计算剩余期限 T (年化)
days_to_maturity = (end_date - datetime.datetime.strptime(date, "%Y-%m-%d").date()).days
T = days_to_maturity / 365.0

# 3. 获取价格数据
# 获取期权收盘价
opt_data = get_price(option_code, start_date=date, end_date=date, fields=['close'])
option_market_price = opt_data['close'][0]

# 获取标的收盘价
stock_data = get_price(underlying_symbol, start_date=date, end_date=date, fields=['close'])
S = stock_data['close'][0]

# 4. 设定无风险利率 (通常取国债收益率,这里简化为 3%)
r = 0.03

# 5. 判断期权类型 (Call/Put)
# 通过代码或名称判断,这里假设是 Call
option_type = 'call' 
if '沽' in info.display_name or 'Put' in info.display_name:
    option_type = 'put'

# 6. 计算隐含波动率
iv = calculate_implied_volatility(option_market_price, S, K, T, r, option_type)

print(f"计算日期: {date}")
print(f"期权代码: {option_code} ({info.display_name})")
print(f"标的价格(S): {S}")
print(f"行权价格(K): {K}")
print(f"期权价格(Price): {option_market_price}")
print(f"剩余年限(T): {T:.4f}")
print(f"隐含波动率(IV): {iv:.4f} ({iv*100:.2f}%)")

代码关键点解析

  1. 数据获取 (get_price):
    • 使用 get_price 获取指定日期的 close 价格。如果是盘中实时计算,可以使用 get_current_data()[code].last_price
  2. 剩余期限 ($T$):
    • 必须将天数转换为年(通常除以 365 或 252)。如果 $T$ 接近 0,BS 公式中的分母会趋向无穷大,代码中需要做边界处理。
  3. 数值求解 (brentq):
    • BS 公式是一个关于波动率 $\sigma$ 的单调函数,因此可以使用二分法或 Brent 方法(scipy.optimize.brentq)高效求解。
    • 设定了波动率的搜索范围为 [0.001, 5.0](即 0.1% 到 500%),这覆盖了绝大多数市场情况。
  4. 行权价获取:
    • 在聚宽 API 中,get_security_info 返回的对象主要包含上市退市时间等,不直接包含行权价。在实际策略中,建议通过查询 opt_contract_info 数据库表或解析期权名称来准确获取行权价。

常见问题 (Q&A)

Q1: 为什么计算出的隐含波动率是 NaN?
A1: 这通常发生在以下几种情况:

  1. 期权价格异常(例如价格为0或负数)。
  2. 期权处于深度实值或深度虚值,导致理论价格即使在波动率极大的情况下也无法触及市场价格(通常是因为流动性差导致的市场定价偏差)。
  3. 剩余期限 $T$ 为 0 或负数(已到期)。

Q2: 无风险利率 $r$ 应该填多少?
A2: 严谨的做法是使用对应期限的国债收益率或 SHIBOR 利率。在简化计算中,通常使用 3% (0.03) 或 2.5% 作为近似值。

Q3: 聚宽是否有直接获取 IV 的 API?
A3: 聚宽的 jqdata 库中提供了衍生品数据表(如 opt_risk_indicator),其中可能包含交易所或聚宽预计算的希腊字母和隐含波动率。如果拥有相应的数据权限,可以直接查询该表,无需手动计算。上述代码适用于需要自行实时计算或回测中动态计算的场景。