🚀 AI 一键生成 qmt 策略代码

如何在 QMT 中构建 VWAP 均价交易策略?(附完整 Python 源码)

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

问题描述

请帮我编写一个 VWAP 均价交易策略,当价格高于 VWAP 时做多,低于时做空。

解决方案

这是一个基于 QMT 平台的 VWAP(成交量加权平均价) 交易策略。

策略逻辑

  1. VWAP 计算:VWAP 通常是一个日内指标。公式为:$\text{VWAP} = \frac{\text{当日累计成交额}}{\text{当日累计成交量}}$。
  2. 交易信号
    • 做多:当当前收盘价 大于 当日 VWAP,且当前无持仓时,买入。
    • 做空/平仓:当当前收盘价 小于 当日 VWAP,且当前有持仓时,卖出(平仓)。
  3. 运行周期:建议在 分钟线(如 '1m', '3m', '5m')上运行,以便在日内捕捉价格相对于均价的波动。

策略代码

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

def init(ContextInfo):
    """
    初始化函数,策略启动时调用一次
    """
    # 设置要交易的股票代码,这里以浦发银行为例
    ContextInfo.stock_code = '600000.SH'
    
    # 设置股票池
    ContextInfo.set_universe([ContextInfo.stock_code])
    
    # 设置交易账号(请替换为您真实的资金账号)
    ContextInfo.account_id = 'YOUR_ACCOUNT_ID'
    ContextInfo.set_account(ContextInfo.account_id)
    
    # 定义一个全局变量记录持仓状态 (1: 持仓, 0: 空仓)
    # 注意:实盘中建议使用 get_trade_detail_data 获取真实持仓
    ContextInfo.holding = 0
    
    # 每次下单的数量(股)
    ContextInfo.trade_vol = 1000

def handlebar(ContextInfo):
    """
    行情事件函数,每根 K 线运行一次
    """
    # 获取当前 K 线的位置
    index = ContextInfo.barpos
    
    # 获取当前时间戳
    timetag = ContextInfo.get_bar_timetag(index)
    
    # 将时间戳转换为日期字符串 (格式 YYYYMMDD),用于判断是否为同一天
    # format参数根据需求设定,这里只需要日期部分
    current_date_str = timetag_to_datetime(timetag, '%Y%m%d')
    
    # 获取行情数据
    # 为了计算日内 VWAP,我们需要获取当天开盘以来的数据
    # 假设是分钟线,一天最多240根K线,取最近240根足以覆盖当天
    data_map = ContextInfo.get_market_data_ex(
        ['close', 'amount', 'volume'], 
        [ContextInfo.stock_code], 
        period=ContextInfo.period, 
        count=240, 
        dividend_type='follow'
    )
    
    if ContextInfo.stock_code not in data_map:
        return

    df = data_map[ContextInfo.stock_code]
    
    # 确保数据不为空
    if df.empty:
        return

    # --- 核心逻辑:计算日内 VWAP ---
    
    # 1. 将索引(时间字符串)转换为日期格式,以便筛选当天数据
    # 注意:get_market_data_ex 返回的 DataFrame index 是字符串格式的时间
    df['datetime_str'] = df.index
    # 提取日期部分 (假设索引格式为 YYYYMMDDHHMMSS)
    df['date'] = df['datetime_str'].apply(lambda x: x[0:8])
    
    # 2. 筛选出属于“今天”的数据
    today_df = df[df['date'] == current_date_str]
    
    if today_df.empty:
        return

    # 3. 计算累计成交额和累计成交量
    cumulative_amount = today_df['amount'].sum()
    cumulative_volume = today_df['volume'].sum()
    
    # 4. 计算 VWAP
    if cumulative_volume == 0:
        return
    
    vwap = cumulative_amount / cumulative_volume
    
    # 获取当前 K 线的收盘价
    current_close = today_df['close'].iloc[-1]
    
    # --- 交易逻辑 ---
    
    # 打印调试信息 (可在日志中查看)
    print(f"时间: {timetag_to_datetime(timetag, '%Y-%m-%d %H:%M:%S')}, 收盘价: {current_close:.2f}, VWAP: {vwap:.2f}")

    # 信号 1: 价格上穿 VWAP -> 买入
    if current_close > vwap and ContextInfo.holding == 0:
        print("信号触发: 价格高于 VWAP,买入")
        # 下单买入
        order_shares(ContextInfo.stock_code, ContextInfo.trade_vol, 'fix', current_close, ContextInfo, ContextInfo.account_id)
        ContextInfo.holding = 1
        
    # 信号 2: 价格下穿 VWAP -> 卖出 (平仓)
    elif current_close < vwap and ContextInfo.holding > 0:
        print("信号触发: 价格低于 VWAP,卖出")
        # 下单卖出 (负数代表卖出)
        order_shares(ContextInfo.stock_code, -ContextInfo.trade_vol, 'fix', current_close, ContextInfo, ContextInfo.account_id)
        ContextInfo.holding = 0

代码关键点说明

  1. 数据获取 (get_market_data_ex)

    • 我们获取了 amount (成交额) 和 volume (成交量)。
    • count=240:因为 A 股一天大约有 240 分钟的交易时间,获取最近 240 根分钟 K 线通常能覆盖当天的所有数据。
  2. 日内数据筛选

    • VWAP 是一个日内指标,每天开盘时需要重置。
    • 代码通过 timetag_to_datetime 获取当前 K 线的日期,并在 DataFrame 中筛选出 date 等于当前日期的所有行,从而实现仅基于当天数据计算 VWAP。
  3. VWAP 公式

    • 代码中使用了标准的计算方式:Sum(当日所有K线成交额) / Sum(当日所有K线成交量)
  4. 交易执行 (order_shares)

    • 使用 order_shares 进行指定股数下单。
    • 正数表示买入,负数表示卖出。
    • 'fix' 表示限价单(这里用的是当前收盘价作为委托价,回测中通常能成交,实盘中可能需要用对手价 'COMPETE' 或市价以保证成交)。

使用建议

  • 周期设置:在 QMT 界面运行或回测时,请将主图周期设置为 1分钟5分钟。如果设置为日线,VWAP 将等于当天的均价,策略意义不大。
  • 实盘账号:请务必在 init 函数中将 YOUR_ACCOUNT_ID 替换为您真实的资金账号。
  • 做空说明:上述代码在“低于 VWAP”时执行的是卖出平仓操作(适用于 A 股普通账户)。如果您是期货或融券账户想要进行真正的“开空单”,需要修改下单逻辑(例如使用 passorder 函数并指定开空操作码)。

Q&A

Q: 如何将此策略改为期货策略(支持开空单)?
A: 需要将 order_shares 替换为 buy_open (买入开仓), sell_close (卖出平仓), sell_open (卖出开仓), buy_close (买入平仓) 等期货专用函数,或者使用 passorder 函数配合期货的操作码(如 0:开多, 3:开空)。

Q: 为什么 VWAP 计算要筛选当天的日期?
A: VWAP 的定义是“成交量加权平均价”,通常指当日的均价。如果不重置,计算的就是过去 N 天的移动 VWAP,这与通用的 VWAP 交易逻辑不同。

Q: ContextInfo.holding 变量在实盘中可靠吗?
A: 不完全可靠。在实盘中,程序重启后变量会重置。建议在实盘中使用 get_trade_detail_data(ContextInfo.account_id, 'stock', 'position') 来查询真实的账户持仓情况。