问题描述
解决方案
在 QMT (Quant Trader) 平台中,虽然 API 提供了计算期权理论价格 (bsm_price) 和隐含波动率 (bsm_iv) 的函数,但没有直接提供计算 Delta 等希腊字母(Greeks)的原生接口。
因此,我们需要基于 Black-Scholes-Merton (BSM) 模型,利用 Python 的数学库(如 scipy 和 numpy)自行编写计算逻辑。
计算 Delta 的核心逻辑
Delta ($\Delta$) 衡量的是期权价格相对于标的资产价格变动的敏感度。基于 BSM 模型,其计算公式如下:
-
计算 $d_1$:
$$d_1 = \frac{\ln(S / K) + (r + \sigma^2 / 2)T}{\sigma \sqrt{T}}$$ -
计算 Delta:
- 认购期权 (Call): $\Delta = N(d_1)$
- 认沽期权 (Put): $\Delta = N(d_1) - 1$
其中:
- $S$: 标的资产当前价格
- $K$: 行权价
- $r$: 无风险利率
- $\sigma$: 波动率(通常使用隐含波动率 IV)
- $T$: 距离到期时间(年化)
- $N(x)$: 标准正态分布的累积分布函数
QMT 策略代码实现
以下是一个完整的 Python 策略示例,展示了如何获取 QMT 的行情数据、期权详情、隐含波动率,并计算 Delta 值。
# -*- coding: gbk -*-
import numpy as np
from scipy.stats import norm
import datetime
def init(ContextInfo):
# 设置要计算 Delta 的期权代码 (示例:沪深300ETF期权)
# 请确保在“数据管理”中下载了对应的期权和标的数据
ContextInfo.option_code = '10006234.SHO'
# 设置标的代码 (示例:华泰柏瑞沪深300ETF)
ContextInfo.underlying_code = '510300.SH'
# 订阅行情
ContextInfo.subscribe_quote(ContextInfo.option_code, 'tick', 'follow')
ContextInfo.subscribe_quote(ContextInfo.underlying_code, 'tick', 'follow')
def handlebar(ContextInfo):
# 仅在最后一根K线(实时行情)计算,避免回测时重复大量计算
if not ContextInfo.is_last_bar():
return
# 1. 获取期权合约详细信息 (行权价 K, 到期日等)
detail = ContextInfo.get_option_detail_data(ContextInfo.option_code)
if not detail:
print(f"未找到合约 {ContextInfo.option_code} 的详细信息")
return
strike_price = detail['OptExercisePrice'] # 行权价 K
expire_date_str = str(detail['ExpireDate']) # 到期日 YYYYMMDD
option_type = detail['optType'] # 'CALL' 或 'PUT'
# 2. 获取标的资产当前价格 S
# 使用 get_market_data_ex 获取最新 tick 数据
tick_data = ContextInfo.get_market_data_ex(
[],
[ContextInfo.underlying_code],
period='tick',
count=1,
subscribe=True
)
if ContextInfo.underlying_code not in tick_data or tick_data[ContextInfo.underlying_code].empty:
return
# 获取标的最新价
S = tick_data[ContextInfo.underlying_code].iloc[-1]['lastPrice']
# 3. 获取无风险利率 r
# QMT 提供 get_risk_free_rate,通常返回百分比(如 3.0),需要除以 100
# 这里取当前 bar 对应的利率
r = ContextInfo.get_risk_free_rate(ContextInfo.barpos) / 100.0
# 4. 获取隐含波动率 sigma
# QMT 提供 get_option_iv 接口
iv = ContextInfo.get_option_iv(ContextInfo.option_code)
# 如果 IV 获取失败或为 0,可以使用历史波动率或固定值代替,这里做简单处理
if iv <= 0.0001:
# print("隐含波动率无效,跳过计算")
return
sigma = iv
# 5. 计算剩余期限 T (年化)
# 获取当前时间
current_timetag = ContextInfo.get_tick_timetag()
current_dt = datetime.datetime.fromtimestamp(current_timetag / 1000)
# 解析到期日
expire_dt = datetime.datetime.strptime(expire_date_str, '%Y%m%d')
# 将到期日设置为当天的 15:00:00 收盘
expire_dt = expire_dt.replace(hour=15, minute=0, second=0)
# 计算剩余秒数并转换为年
delta_seconds = (expire_dt - current_dt).total_seconds()
if delta_seconds <= 0:
print("期权已到期")
return
T = delta_seconds / (365 * 24 * 3600)
# 6. 调用函数计算 Delta
delta_val = calculate_bsm_delta(S, strike_price, T, r, sigma, option_type)
print(f"时间: {current_dt} | 代码: {ContextInfo.option_code} | 类型: {option_type}")
print(f"标的价(S): {S:.4f} | 行权价(K): {strike_price} | 剩余年(T): {T:.4f}")
print(f"波动率(IV): {sigma:.4f} | 利率(r): {r:.4f}")
print(f"==> Delta: {delta_val:.4f}")
def calculate_bsm_delta(S, K, T, r, sigma, option_type):
"""
计算 BSM 模型下的 Delta 值
"""
try:
d1 = (np.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
if option_type.upper() == 'CALL':
delta = norm.cdf(d1)
elif option_type.upper() == 'PUT':
delta = norm.cdf(d1) - 1
else:
delta = 0
return delta
except Exception as e:
print(f"计算 Delta 出错: {e}")
return 0
代码关键点解析
-
数据获取 (
handlebar函数内):- 标的价格 ($S$): 使用
ContextInfo.get_market_data_ex获取标的(如 510300.SH)的最新 Tick 数据。 - 行权价 ($K$) & 到期日: 使用
ContextInfo.get_option_detail_data获取合约的静态数据。 - 隐含波动率 ($\sigma$): 直接调用 QMT 的
ContextInfo.get_option_iv。这是计算 Delta 最关键的动态参数。 - 无风险利率 ($r$): 使用
ContextInfo.get_risk_free_rate,注意单位转换。
- 标的价格 ($S$): 使用
-
时间处理 ($T$):
- BSM 模型中的 $T$ 单位为“年”。
- 代码中通过
(到期时间 - 当前时间).total_seconds() / (365 * 24 * 3600)将剩余时间精确转换为年化数值。
-
数学计算 (
calculate_bsm_delta函数):- 使用
numpy进行对数和平方根运算。 - 使用
scipy.stats.norm.cdf计算标准正态分布的累积概率 $N(x)$。
- 使用
注意事项
- 第三方库: 代码依赖
scipy库。QMT 内置的 Python 环境通常包含此库。如果报错提示缺少库,请参考 QMT 文档中关于“用户自行安装 Python 三方库”的章节进行安装(通常使用pip install scipy)。 - 数据补充: 在运行策略前,请务必在 QMT 客户端的【数据管理】中补充相关的期权和标的的历史数据,以确保
get_option_iv等函数能正常返回数据。 - 异常处理: 期权临近到期或深度虚值/实值时,IV 可能计算异常或缺失,代码中应加入适当的判断逻辑(如示例中的
if iv <= 0.0001)。
Q&A
Q: 为什么 QMT 不直接提供 Delta 接口?
A: QMT 的 API 设计更侧重于交易执行和基础数据获取。Greeks(希腊字母)属于衍生品分析的高级指标,通常由量化开发者根据自己的定价模型(如 BSM、二叉树等)自行计算,以便拥有更高的灵活性。
Q: 代码中的 ContextInfo.get_option_iv 返回的是什么?
A: 它返回的是交易所或数据商提供的实时隐含波动率。如果该数据缺失,你可以使用标的资产的历史波动率作为替代,或者自行编写算法反推 IV。
Q: 如何计算 Gamma 或 Vega?
A: 逻辑与 Delta 类似,只需将 calculate_bsm_delta 函数中的公式替换为 Gamma 或 Vega 的 BSM 公式即可。所有需要的基础输入参数(S, K, T, r, sigma)在上述代码中都已经获取到了。