🚀 AI 一键生成 qmt 策略代码

QMT API 教程:如何利用内外盘数据 get_bvol 和 get_svol 构建市场情绪指标?

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

问题描述

请帮我编写一个基于内外盘成交量数据的交易策略。

解决方案

这是一个基于 QMT 平台 API 编写的内外盘成交量比率交易策略

策略逻辑说明

该策略的核心逻辑是利用**外盘(主动买入)内盘(主动卖出)**的成交量差异来判断市场情绪:

  1. 数据获取:使用 ContextInfo.get_bvol() 获取外盘成交量,使用 ContextInfo.get_svol() 获取内盘成交量。
  2. 买入信号:当 外盘成交量 > 内盘成交量 $\times$ (1 + 阈值) 时,认为买盘强劲,执行买入。
  3. 卖出信号:当 内盘成交量 > 外盘成交量 $\times$ (1 + 阈值) 时,认为抛压沉重,执行卖出(平仓)。
  4. 风控与过滤:设置了 threshold(阈值)参数,防止在多空力量均衡时频繁交易。

策略代码

# -*- coding: gbk -*-
import time

def init(ContextInfo):
    """
    策略初始化函数
    """
    # 1. 设置资金账号 (请替换为您真实的资金账号)
    ContextInfo.account_id = 'YOUR_ACCOUNT_ID'
    ContextInfo.account_type = 'STOCK' # 账号类型:'STOCK'股票, 'FUTURE'期货
    ContextInfo.set_account(ContextInfo.account_id)
    
    # 2. 设置股票池 (示例:平安银行)
    ContextInfo.stock_code = '000001.SZ'
    ContextInfo.set_universe([ContextInfo.stock_code])
    
    # 3. 策略参数设置
    ContextInfo.threshold = 0.1  # 阈值 10%,即外盘比内盘多10%才买入
    ContextInfo.trade_vol = 100  # 每次交易股数
    
    print("策略初始化完成,监控标的: {}".format(ContextInfo.stock_code))

def get_position(ContextInfo, stock_code):
    """
    获取指定股票的持仓数量
    """
    try:
        # 获取持仓对象列表
        pos_list = get_trade_detail_data(ContextInfo.account_id, ContextInfo.account_type, 'POSITION')
        for pos in pos_list:
            if pos.m_strInstrumentID + '.' + pos.m_strExchangeID == stock_code:
                return pos.m_nVolume
        return 0
    except Exception as e:
        print("获取持仓失败: {}".format(e))
        return 0

def handlebar(ContextInfo):
    """
    K线/行情驱动函数
    """
    # 仅在最后一根K线(实时行情)或回测的当前步运行逻辑
    # 注意:get_bvol/get_svol 通常在实时行情中有效,回测历史数据可能需要特定数据源支持
    if not ContextInfo.is_last_bar():
        return

    stock_code = ContextInfo.stock_code
    
    # 1. 获取内外盘数据
    # 注意:API返回的是字符串,需要转换为浮点数
    try:
        bvol_str = ContextInfo.get_bvol(stock_code) # 外盘(主动买入)
        svol_str = ContextInfo.get_svol(stock_code) # 内盘(主动卖出)
        
        # 数据有效性检查
        if bvol_str == '' or svol_str == '':
            return
            
        bvol = float(bvol_str)
        svol = float(svol_str)
        
        if svol == 0: # 防止除以0
            return
            
    except Exception as e:
        print("获取内外盘数据异常: {}".format(e))
        return

    # 2. 获取当前持仓
    current_pos = get_position(ContextInfo, stock_code)
    
    # 3. 计算多空比率
    # 避免除零错误,如果内盘极小,视为极端多头
    ratio = bvol / svol if svol > 0 else 999 
    
    # 4. 交易逻辑
    
    # --- 买入逻辑 ---
    # 外盘明显大于内盘 (例如:外盘是内盘的 1.1 倍以上)
    if bvol > svol * (1 + ContextInfo.threshold):
        if current_pos == 0:
            print("【买入信号】外盘: {:.0f}, 内盘: {:.0f}, 比例: {:.2f}".format(bvol, svol, ratio))
            # 使用 order_shares 指定股数下单
            order_shares(stock_code, ContextInfo.trade_vol, 'fix', -1, ContextInfo, ContextInfo.account_id)
            # 'fix', -1 代表使用最新价下单 (具体行为取决于交易设置,通常等同于对手价或最新价)

    # --- 卖出逻辑 ---
    # 内盘明显大于外盘 (例如:内盘是外盘的 1.1 倍以上)
    elif svol > bvol * (1 + ContextInfo.threshold):
        if current_pos > 0:
            print("【卖出信号】外盘: {:.0f}, 内盘: {:.0f}, 比例: {:.2f}".format(bvol, svol, ratio))
            # 卖出全部持仓或指定数量
            order_shares(stock_code, -current_pos, 'fix', -1, ContextInfo, ContextInfo.account_id)

    # 打印调试信息 (可选)
    # print("监控中 - {} - 外盘:{:.0f} 内盘:{:.0f}".format(stock_code, bvol, svol))

关键点说明

  1. 数据类型转换:QMT 的 get_bvolget_svol 接口返回的是 字符串 (String) 类型,直接比较会报错,必须使用 float() 进行转换。
  2. 运行模式
    • 内外盘数据(Bvol/Svol)通常是当日累计数据。
    • 此策略最适合在实盘运行模拟交易模式下使用。
    • 如果在回测模式下使用,请确保您下载的历史数据中包含分笔或Tick级别的统计数据,否则这两个函数可能返回空值或0。
  3. 下单价格:示例中使用了 'fix', -1,这在 QMT 中通常表示以最新价或对手价下单(取决于客户端的交易设置)。如果需要更激进的成交,可以使用 'COMPETE'(对手价)或 'MARKET'(市价)。
  4. 持仓查询:使用了 get_trade_detail_data 来获取真实的账户持仓,这是比维护本地变量更稳健的方法。

Q&A

Q: 为什么 get_bvol 返回的是字符串?
A: 这是 QMT 底层 C++ 接口透传的设计,为了保证大数值的精度或兼容性,Python 层接收到的是字符串,使用前务必转换。

Q: 这个策略适合什么周期运行?
A: 建议在 分时 (Tick)1分钟/5分钟 周期下运行。因为内外盘数据是盘中实时变化的,高频监控能更敏锐地捕捉资金流向。

Q: 如何增加止损逻辑?
A: 可以在 handlebar 中获取持仓成本 (pos.m_dOpenPrice) 和最新价 (ContextInfo.get_market_data(['close']...)),计算浮动盈亏比例,当亏损超过一定比例(如 -5%)时强制触发卖出逻辑。