🚀 AI 一键生成 qmt 策略代码

QMT 期权教程:如何利用波动率微笑进行套利?

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

问题描述

如何利用期权的波动率微笑现象进行套利?

解决方案

如何利用期权的波动率微笑现象进行套利

波动率微笑(Volatility Smile)是指具有相同到期日但行权价不同的期权,其隐含波动率(Implied Volatility, IV)并不相同,而是呈现出一种中间低、两边高的微笑形状(或偏斜形状,称为Volatility Skew)。

利用波动率微笑进行套利(或更准确地说是相对价值交易),核心思想是做空高波动率的合约,做多低波动率的合约,同时保持Delta中性或构建特定的价差组合,等待波动率回归或通过时间价值获利。

在 QMT 平台中,我们可以利用内置的 BSM 模型函数计算隐含波动率,并构建相应的策略。

1. 常见的波动率微笑套利策略

A. 垂直价差套利 (Vertical Spread Arbitrage)

这是最直接利用波动率偏斜(Skew)的策略。

  • 原理:如果虚值(OTM)期权的隐含波动率显著高于平值(ATM)期权(即微笑曲线陡峭),可以卖出高 IV 的 OTM 期权,买入低 IV 的 ATM 期权。
  • 操作:例如 牛市看涨价差 (Bull Call Spread)熊市看跌价差 (Bear Put Spread)

B. 日历价差套利 (Calendar Spread)

利用不同到期日的波动率结构(期限结构)。

  • 原理:通常近期期权的波动率变化比远期更剧烈。如果近期 IV 远高于远期 IV,可以卖出近期、买入远期。

C. 蝶式套利 (Butterfly Spread)

利用微笑曲线的曲率。

  • 原理:如果两端的 IV 极高,中间的 IV 较低,可以卖出两端(高IV),买入中间(低IV)。

2. QMT 策略实现思路

在 QMT 中实现该策略的步骤如下:

  1. 获取期权链:使用 get_option_list 获取标的对应的所有期权合约。
  2. 获取数据:使用 get_market_data_ex 获取标的价格和期权价格。
  3. 计算隐含波动率 (IV):使用 ContextInfo.bsm_iv 计算每个合约的 IV。
  4. 发现交易机会:比较不同行权价的 IV,寻找价差过大的机会。
  5. 执行交易:使用 passorder 构建价差组合。

3. QMT 代码示例:基于波动率观测的垂直价差策略

以下代码演示了如何在 QMT 中计算 510300 (沪深300 ETF) 期权的隐含波动率,并构建一个简单的牛市看涨价差 (Bull Call Spread)。该策略假设虚值期权 IV 过高,因此卖出虚值 Call,买入平值 Call。

# -*- coding: gbk -*-
import pandas as pd
import numpy as np
import time
from datetime import datetime

def init(ContextInfo):
    # 设置账号
    ContextInfo.accid = '您的资金账号'
    ContextInfo.set_account(ContextInfo.accid)
    
    # 策略参数
    ContextInfo.underlying = '510300.SH'  # 标的:300ETF
    ContextInfo.trade_unit = 1            # 交易手数
    ContextInfo.rf = 0.025                # 无风险利率 (假设2.5%)
    ContextInfo.dividend = 0.0            # 分红率
    
    # 设定定时运行,例如每天14:30运行一次
    ContextInfo.run_time("my_strategy_logic", "1d", "2023-01-01 14:30:00", "SH")

def my_strategy_logic(ContextInfo):
    """
    核心策略逻辑:计算IV,构建牛市看涨价差
    """
    # 1. 获取标的当前价格
    last_tick = ContextInfo.get_full_tick([ContextInfo.underlying])
    if not last_tick:
        print("无法获取标的行情")
        return
    
    S = last_tick[ContextInfo.underlying]['lastPrice'] # 标的现价
    current_date = datetime.now().strftime('%Y%m%d')
    
    # 2. 获取当月到期的认购期权列表
    # 获取期权链,这里简化处理,直接获取当前可交易的期权
    # 实际生产中应根据到期日筛选"当月"合约
    option_list = ContextInfo.get_option_list(ContextInfo.underlying, current_date, "CALL", True)
    
    if not option_list:
        print("未获取到期权合约列表")
        return

    # 3. 获取期权详细信息(行权价、到期日)和行情
    iv_data = []
    
    # 批量获取期权行情
    opt_ticks = ContextInfo.get_full_tick(option_list)
    
    for opt_code in option_list:
        detail = ContextInfo.get_instrumentdetail(opt_code)
        tick = opt_ticks.get(opt_code)
        
        if not detail or not tick:
            continue
            
        K = detail['OptExercisePrice']      # 行权价
        price = tick['lastPrice']           # 期权现价
        expire_date = detail['ExpireDate']  # 到期日 (int, e.g., 20231227)
        
        # 计算剩余天数
        days = days_between(current_date, str(expire_date))
        
        if days <= 0 or price <= 0.0001:
            continue
            
        # 4. 计算隐含波动率 (IV)
        # bsm_iv(optionType, objectPrices, strikePrice, optionPrice, riskFree, days, dividend)
        iv = ContextInfo.bsm_iv('C', S, K, price, ContextInfo.rf, days, ContextInfo.dividend)
        
        if np.isnan(iv) or iv == 0:
            continue
            
        iv_data.append({
            'code': opt_code,
            'strike': K,
            'price': price,
            'iv': iv,
            'days': days
        })
    
    # 转换为DataFrame方便处理
    df = pd.DataFrame(iv_data)
    if df.empty:
        return
        
    # 按行权价排序
    df = df.sort_values('strike')
    
    # 打印波动率微笑曲线数据
    print("="*30)
    print(f"标的价格: {S}")
    print(df[['code', 'strike', 'price', 'iv']])
    print("="*30)
    
    # 5. 策略逻辑:构建牛市看涨价差 (Bull Call Spread)
    # 假设我们观察到深度虚值(OTM)的IV异常高,我们想卖出它赚取波动率溢价
    # 同时买入平值(ATM)或轻度实值(ITM)期权作为保护和方向性赌注
    
    # 找到平值合约 (行权价最接近标的价)
    df['diff'] = abs(df['strike'] - S)
    atm_opt = df.loc[df['diff'].idxmin()]
    
    # 找到虚值合约 (行权价 > 标的价,且IV较高)
    # 这里简单选取行权价高于标的价2档的合约作为卖出对象
    otm_candidates = df[df['strike'] > S].sort_values('strike')
    
    if len(otm_candidates) >= 2:
        otm_opt = otm_candidates.iloc[1] # 选取第二档虚值
        
        # 简单的交易信号:如果OTM的IV比ATM的IV高出一定阈值(例如2%),则开仓
        # 注意:实际市场中Call通常呈现偏斜(低行权价IV高),这里仅为演示代码逻辑
        iv_spread = otm_opt['iv'] - atm_opt['iv']
        print(f"ATM IV: {atm_opt['iv']:.4f}, OTM IV: {otm_opt['iv']:.4f}, Spread: {iv_spread:.4f}")
        
        # 执行交易 (示例:买入ATM Call,卖出OTM Call)
        # 实际交易需检查持仓、资金等
        print(f"交易信号: 买入 {atm_opt['code']} (K={atm_opt['strike']}), 卖出 {otm_opt['code']} (K={otm_opt['strike']})")
        
        # 买入平值认购
        passorder(23, 1101, ContextInfo.accid, atm_opt['code'], 5, -1, ContextInfo.trade_unit, ContextInfo)
        # 卖出虚值认购
        passorder(24, 1101, ContextInfo.accid, otm_opt['code'], 5, -1, ContextInfo.trade_unit, ContextInfo)

def days_between(d1, d2):
    """计算两个日期字符串(YYYYMMDD)之间的天数"""
    d1 = datetime.strptime(d1, "%Y%m%d")
    d2 = datetime.strptime(d2, "%Y%m%d")
    return (d2 - d1).days

def handlebar(ContextInfo):
    # 实时行情驱动,此处留空,逻辑在 run_time 中定时触发
    pass

4. 代码关键点解析

  1. ContextInfo.bsm_iv: 这是 QMT 提供的核心函数,用于反推隐含波动率。

    • 参数:optionType ('C'/'P'), objectPrices (标的价), strikePrice (行权价), optionPrice (期权价), riskFree (无风险利率), days (剩余天数), dividend (分红率)。
    • 注意:days 参数通常需要是剩余天数(整数或浮点数),QMT 内部会将其转换为年化时间(days/365)。
  2. 数据获取:

    • get_option_list: 能够根据标的和月份快速筛选合约。
    • get_full_tick: 获取最新的盘口价格,用于计算实时的 IV。
  3. 策略逻辑:

    • 代码首先计算了所有 Call 的 IV 并打印,这让你能直观地在日志中看到“微笑”或“偏斜”的形状。
    • 构建了 Bull Call Spread:买入低行权价(ATM),卖出高行权价(OTM)。如果 OTM 的 IV 显著高于 ATM(即市场过度定价了尾部风险),卖出 OTM 可以获得理论上的波动率溢价。

5. 风险提示

  1. 模型风险:BSM 模型假设波动率是常数,而我们正是利用波动率不为常数(微笑)来套利,这本身存在理论悖论。实际操作中 IV 只是一个报价单位。
  2. 流动性风险:深度虚值期权流动性可能很差,买卖价差(Bid-Ask Spread)可能很大,这会吞噬掉套利利润。建议在代码中加入对 bidPriceaskPrice 的检查。
  3. 保证金风险:作为期权卖方(卖出 OTM Call),需要缴纳保证金。垂直价差策略通常可以减少保证金占用,但需确保账户资金充足。
  4. Delta 风险:上述策略并非 Delta 中性。如果标的价格大幅下跌,Bull Call Spread 会亏损。纯粹的波动率套利通常需要用期货进行 Delta 对冲。

Q&A

Q: 如何在 QMT 中进行 Delta 对冲以实现纯波动率套利?
A: 您可以使用 ContextInfo.bsm_price 或相关的希腊字母计算公式计算持仓期权的 Delta 值,然后计算整个组合的 Net Delta。如果 Net Delta 不为 0,则通过买卖标的(如 300ETF 或 股指期货 IF)来将 Delta 归零。这需要在 handlebar 中高频监控并动态调整。

Q: 为什么计算出的 IV 有时是 NaN?
A: 如果期权价格偏离理论价格太远(例如深度实值期权价格低于内涵价值,或者价格为0),BSM 公式可能无法收敛求出解。代码中应加入 np.isnan 判断进行过滤。

Q: get_market_data_exget_full_tick 哪个更适合期权?
A: 对于实时监控和 IV 计算,推荐使用 get_full_tick,因为它提供最新的买一卖一价和最新成交价。get_market_data_ex 更适合获取历史序列数据进行回测分析。