🚀 AI 一键生成 qmt 策略代码

如何在 QMT 中构建波动率锥(Volatility Cone)来判断当前引伸波幅的高低?

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

问题描述

我想写一个脚本来计算某个股票期权的隐含波动率和它过去30天、60天、90天的历史波动率,并比较它们,怎么做?

解决方案

这是一个非常实用的量化分析需求。在 QMT 中,我们可以通过以下步骤实现:

  1. 获取隐含波动率 (IV):直接调用 QMT 提供的 ContextInfo.get_option_iv() 接口获取期权的实时隐含波动率。
  2. 获取标的资产:通过 ContextInfo.get_option_detail_data() 查询期权对应的标的资产代码(如 ETF 或 股票)。
  3. 获取历史行情:使用 ContextInfo.get_market_data_ex() 获取标的资产的历史收盘价。
  4. 计算历史波动率 (HV):计算标的资产的对数收益率的标准差,并进行年化处理(通常乘以 $\sqrt{252}$)。

策略代码实现

以下是完整的 Python 策略代码。你可以将其复制到 QMT 的策略编辑器中运行。

# -*- coding: gbk -*-
import numpy as np
import pandas as pd
import math

def init(ContextInfo):
    # 在这里设置你想分析的期权代码
    # 例如:'10004366.SHO' (假设这是某个上证50ETF购X月X合约,请根据实际情况修改)
    # 注意:请确保该期权合约在当前回测或实盘时间段内是上市交易的
    ContextInfo.option_code = '10004366.SHO' 
    
    # 设置计算历史波动率的周期列表 (天)
    ContextInfo.periods = [30, 60, 90]
    
    # 设置运行周期,建议在日线级别运行
    ContextInfo.period = '1d'

def handlebar(ContextInfo):
    # 只在最后一根K线(最新时刻)进行计算和输出,避免历史回测时重复打印
    if not ContextInfo.is_last_bar():
        return

    option_code = ContextInfo.option_code
    
    # 1. 获取期权的隐含波动率 (IV)
    # get_option_iv 返回的是小数,例如 0.20 代表 20%
    iv = ContextInfo.get_option_iv(option_code)
    
    # 2. 获取期权详细信息,找到标的资产代码
    detail = ContextInfo.get_option_detail_data(option_code)
    if not detail:
        print(f"错误:无法获取期权 {option_code} 的详细信息")
        return
        
    # 拼接标的代码和市场,例如 '510050' 和 'SH' -> '510050.SH'
    underlying_code = detail['OptUndlCode'] + '.' + detail['OptUndlMarket']
    
    print("=" * 50)
    print(f"期权代码: {option_code}")
    print(f"标的代码: {underlying_code}")
    print(f"当前隐含波动率 (IV): {iv:.2%}")
    print("-" * 50)

    # 3. 获取标的资产的历史行情数据
    # 我们需要获取足够的数据来计算最长周期(90天)的波动率
    # 多取一些数据以防停牌等情况,这里取 120 个数据点
    count = max(ContextInfo.periods) + 20 
    
    # 获取收盘价
    market_data = ContextInfo.get_market_data_ex(
        fields=['close'], 
        stock_code=[underlying_code], 
        period='1d', 
        count=count,
        dividend_type='front' # 使用前复权数据计算收益率更准确
    )
    
    if underlying_code not in market_data or market_data[underlying_code].empty:
        print(f"错误:无法获取标的 {underlying_code} 的行情数据")
        return

    close_prices = market_data[underlying_code]['close']
    
    # 4. 计算历史波动率 (HV) 并比较
    # 计算对数收益率: ln(Pt / Pt-1)
    # np.log 是自然对数
    log_returns = np.log(close_prices / close_prices.shift(1)).dropna()
    
    print(f"{'周期(天)':<10} | {'历史波动率(HV)':<15} | {'IV - HV 差值':<15}")
    print("-" * 50)
    
    for days in ContextInfo.periods:
        if len(log_returns) < days:
            print(f"{days:<10} | 数据不足")
            continue
            
        # 取最近 N 天的收益率
        recent_returns = log_returns.tail(days)
        
        # 计算标准差 (std)
        std_dev = np.std(recent_returns)
        
        # 年化波动率 = 日波动率 * sqrt(252)
        hv = std_dev * math.sqrt(252)
        
        diff = iv - hv
        
        print(f"{days:<12} | {hv:.2%}           | {diff:.2%}")
        
    print("=" * 50)
    
    # 简单的分析逻辑
    hv_30 = np.std(log_returns.tail(30)) * math.sqrt(252)
    if iv > hv_30:
        print(f"结论: 当前IV ({iv:.2%}) 高于过去30天HV ({hv_30:.2%}),期权可能相对昂贵。")
    else:
        print(f"结论: 当前IV ({iv:.2%}) 低于过去30天HV ({hv_30:.2%}),期权可能相对便宜。")

代码核心逻辑解析

  1. ContextInfo.get_option_iv(option_code):

    • 这是 QMT 特有的接口,直接返回交易所或数据商计算好的隐含波动率。如果返回值为 0 或异常,可能是该合约流动性不足或数据缺失。
  2. ContextInfo.get_option_detail_data(option_code):

    • 期权分析必须基于标的资产。这个函数返回一个字典,其中 OptUndlCode 是标的代码(如 510050),OptUndlMarket 是市场代码(如 SH)。我们需要将它们拼接成 510050.SH 格式供后续行情接口使用。
  3. ContextInfo.get_market_data_ex(...):

    • 这是获取历史数据的核心函数。
    • dividend_type='front'非常重要。计算波动率时,必须使用复权价格(通常是前复权),否则分红除权造成的股价缺口会被误判为剧烈波动,导致 HV 计算虚高。
  4. 波动率计算公式:

    • 首先计算对数收益率:$R_t = \ln(\frac{P_t}{P_{t-1}})$。
    • 计算收益率的标准差 $\sigma$。
    • 年化处理:$HV = \sigma \times \sqrt{252}$。金融市场通常假设一年有 252 个交易日。

如何运行

  1. 打开 QMT 客户端,进入【模型研究】或【策略编辑器】。
  2. 新建一个 Python 策略。
  3. 将上述代码粘贴进去。
  4. 修改 init 函数中的 ContextInfo.option_code 为你想要观察的实际期权合约代码(例如当前活跃的 ETF 期权合约)。
  5. 点击【运行】。
  6. 在下方的【日志】窗口查看输出结果。

常见问题 Q&A

Q: 为什么计算出来的 HV 和某些看盘软件上显示的不一样?
A: 历史波动率的计算有多种口径,差异通常来自:

  1. 收益率算法:使用的是对数收益率还是简单收益率(差异通常很小)。
  2. 样本数量:比如计算30天波动率,是取过去30个交易日,还是过去30个自然日对应的交易日。
  3. 年化系数:有的软件使用 $\sqrt{250}$ 而不是 $\sqrt{252}$。
  4. 复权方式:是否使用了前复权数据。

Q: 如果 get_option_iv 返回 0 怎么办?
A: 这通常发生在期权合约流动性极差、刚刚上市或即将到期时。如果需要更精确的控制,可以使用 ContextInfo.bsm_iv 函数,自己传入标的价格、行权价、无风险利率等参数反推 IV。

Q: 这个脚本可以用于回测吗?
A: 可以。代码中使用了 ContextInfo.is_last_bar() 判断。如果在回测模式下,去掉这个判断,它就会在每一天的 K 线上计算当时的 IV 和当时的过去 N 天 HV,从而形成一条历史对比曲线。