问题描述
解决方案
在 QMT 策略中计算期权的 BSM(Black-Scholes-Merton)理论价格,可以直接使用平台内置的 ContextInfo.bsm_price() 函数。
该函数基于 BSM 模型,通过输入标的价格、行权价、无风险利率、波动率、剩余天数等参数,快速计算出期权的理论价格。
核心函数说明
函数名: ContextInfo.bsm_price(optionType, objectPrices, strikePrice, riskFree, sigma, days, dividend)
参数详解:
- optionType (str): 期权类型。认购期权为
'C',认沽期权为'P'。 - objectPrices (float/list): 期权标的资产的当前价格。
- strikePrice (float): 期权的行权价。
- riskFree (float): 无风险利率(年化,例如 3% 写作 0.03)。
- sigma (float): 标的资产的年化波动率(例如 20% 写作 0.2)。
- days (float): 距离到期日的剩余天数。
- dividend (float): 标的分红率(通常填 0)。
完整策略代码示例
以下代码展示了如何获取期权的基础信息(行权价、到期日、标的代码),获取标的实时价格,计算剩余天数,并最终调用 BSM 公式计算理论价格。
# -*- coding: gbk -*-
import datetime
import numpy as np
def init(ContextInfo):
# 设置要计算的期权代码 (此处以某上证50ETF期权为例,实际使用请替换为有效合约)
# 注意:请确保该合约在回测或实盘的时间段内是存在的
ContextInfo.option_code = '10005264.SHO'
# 设置无风险利率 (例如 2.5%)
ContextInfo.risk_free_rate = 0.025
# 设置预估波动率 (例如 20%)
# 注意:实际策略中,波动率通常需要根据历史数据计算(历史波动率)或使用隐含波动率
ContextInfo.sigma = 0.20
def handlebar(ContextInfo):
# 仅在最后一根K线(实时行情或回测当前步)计算,避免重复打印
if not ContextInfo.is_last_bar():
return
# 1. 获取期权详细信息 (行权价、到期日、标的代码等)
# get_option_detail_data 返回字典,包含 OptExercisePrice, ExpireDate, OptUndlCode 等
detail = ContextInfo.get_option_detail_data(ContextInfo.option_code)
if not detail:
print(f"未获取到合约 {ContextInfo.option_code} 的详细信息")
return
strike_price = detail['OptExercisePrice'] # 行权价
expire_date_str = str(detail['ExpireDate']) # 到期日 (格式 YYYYMMDD)
undl_code = detail['OptUndlCode'] + '.' + detail['OptUndlMarket'] # 标的代码
opt_type_raw = detail['optType'] # 类型 CALL 或 PUT
# 转换期权类型格式为 BSM 函数需要的 'C' 或 'P'
bsm_opt_type = 'C' if opt_type_raw == 'CALL' else 'P'
# 2. 获取标的资产最新价格
# 使用 get_market_data_ex 获取标的最新收盘价
market_data = ContextInfo.get_market_data_ex(
['close'],
[undl_code],
period='1d',
count=1,
subscribe=True
)
if undl_code not in market_data or market_data[undl_code].empty:
print(f"未获取到标的 {undl_code} 的行情数据")
return
undl_price = market_data[undl_code].iloc[-1]['close']
# 3. 计算剩余天数 (Days)
# 获取当前回测或实盘的时间
current_timetag = ContextInfo.get_bar_timetag(ContextInfo.barpos)
current_date = datetime.datetime.fromtimestamp(current_timetag / 1000)
# 解析到期日
expire_date = datetime.datetime.strptime(expire_date_str, '%Y%m%d')
# 计算差值
delta = expire_date - current_date
days_remaining = delta.days + (delta.seconds / 86400.0)
# 如果已经过期,不再计算
if days_remaining <= 0:
print(f"合约 {ContextInfo.option_code} 已过期")
return
# 4. 调用 BSM 公式计算理论价格
# 参数:类型, 标的价格, 行权价, 无风险利率, 波动率, 剩余天数, 分红率
theoretical_price = ContextInfo.bsm_price(
bsm_opt_type,
undl_price,
strike_price,
ContextInfo.risk_free_rate,
ContextInfo.sigma,
days_remaining,
0 # 分红率设为0
)
print("=" * 30)
print(f"期权代码: {ContextInfo.option_code}")
print(f"当前时间: {current_date}")
print(f"标的价格: {undl_price:.4f}")
print(f"行权价格: {strike_price:.4f}")
print(f"剩余天数: {days_remaining:.2f} 天")
print(f"BSM理论价: {theoretical_price:.4f}")
print("=" * 30)
关键点解析
-
数据获取 (
get_option_detail_data):- 这是计算 BSM 的前提,必须通过此接口获取期权的行权价 (
OptExercisePrice) 和 到期日 (ExpireDate)。 - 同时需要获取 标的代码 (
OptUndlCode) 以便查询标的现价。
- 这是计算 BSM 的前提,必须通过此接口获取期权的行权价 (
-
剩余天数计算:
- BSM 模型中的
days参数通常指剩余的自然日天数。 - 代码中通过
ContextInfo.get_bar_timetag获取当前策略运行的时间,并与到期日进行减法运算得出。
- BSM 模型中的
-
波动率 (
sigma):- 在上述示例中,波动率被硬编码为
0.20(20%)。 - 在实际量化策略中,你通常需要编写额外的逻辑来计算历史波动率(使用标的历史收盘价计算标准差),或者使用
ContextInfo.get_option_iv(optioncode)获取市场的隐含波动率作为参考。
- 在上述示例中,波动率被硬编码为
-
无风险利率 (
riskFree):- 可以使用固定值(如国债收益率),也可以通过
ContextInfo.get_risk_free_rate(index)动态获取。
- 可以使用固定值(如国债收益率),也可以通过
Q&A
Q: 如何获取期权的实时隐含波动率而不是自己设定波动率?
A: 可以使用 ContextInfo.get_option_iv(optioncode) 接口。例如:iv = ContextInfo.get_option_iv(ContextInfo.option_code),然后将 iv 传入 bsm_price 函数的 sigma 参数中。
Q: bsm_price 函数计算出的价格单位是什么?
A: 计算出的价格单位与标的资产价格单位一致(通常是元)。对于 ETF 期权,通常精确到小数点后 4 位。
Q: 如果我想反推隐含波动率怎么办?
A: QMT 提供了 ContextInfo.bsm_iv(optionType, objectPrices, strikePrice, optionPrice, riskFree, days, dividend) 函数,输入期权的当前市场价格 (optionPrice) 即可反推隐含波动率。