🚀 AI 一键生成 qmt 策略代码

QMT 趋势策略:多空指数 (BBI) 的计算与 Python 实现

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

问题描述

帮我写一个 BBI 多空指数策略

解决方案

这是一个基于 BBI (Bull And Bear Index) 多空指数 的量化交易策略。

策略原理

BBI 多空指数是一种将不同天数的移动平均线加权平均之后的综合指标,属于均线型指标。它结合了短期、中期和长期均线的优势。

计算公式:
$$ BBI = \frac{MA(3) + MA(6) + MA(12) + MA(24)}{4} $$

交易逻辑:

  1. 买入信号:当收盘价 上穿 BBI 指标线时,视为多头市场,执行买入。
  2. 卖出信号:当收盘价 下穿 BBI 指标线时,视为空头市场,执行卖出(清仓)。

策略代码实现

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

def init(ContextInfo):
    """
    策略初始化函数
    """
    # 1. 设置股票池 (这里以 600000.SH 浦发银行为例,可根据需要修改)
    ContextInfo.stock_code = '600000.SH'
    ContextInfo.set_universe([ContextInfo.stock_code])
    
    # 2. 设置资金账号 (请替换为您真实的资金账号)
    ContextInfo.account_id = 'YOUR_ACCOUNT_ID' 
    ContextInfo.set_account(ContextInfo.account_id)
    
    # 3. 设置BBI计算参数 (3日, 6日, 12日, 24日均线)
    ContextInfo.ma_periods = [3, 6, 12, 24]
    
    # 4. 设置回测参数 (仅回测有效)
    ContextInfo.set_slippage(1, 0.002) # 设置滑点
    ContextInfo.set_commission(0, [0.0003, 0.0013, 0.0003, 0.0003, 0.0003, 5]) # 设置手续费

def handlebar(ContextInfo):
    """
    K线周期运行函数
    """
    # 获取当前正在处理的股票代码
    stock = ContextInfo.stock_code
    
    # 获取当前K线的时间
    index = ContextInfo.barpos
    timetag = ContextInfo.get_bar_timetag(index)
    date_str = timetag_to_datetime(timetag, '%Y-%m-%d %H:%M:%S')
    
    # 1. 获取历史行情数据
    # 我们需要足够的数据来计算 MA24,所以至少取 30 根K线
    # dividend_type='front' 使用前复权数据,保证信号准确
    data_map = ContextInfo.get_market_data_ex(
        ['close'], 
        [stock], 
        period='1d', 
        count=30, 
        dividend_type='front'
    )
    
    if stock not in data_map:
        return
        
    df = data_map[stock]
    
    # 数据长度校验,不足以计算MA24则跳过
    if len(df) < 24:
        return

    # 2. 计算 BBI 指标
    # 计算各周期均线
    ma3 = df['close'].rolling(window=3).mean()
    ma6 = df['close'].rolling(window=6).mean()
    ma12 = df['close'].rolling(window=12).mean()
    ma24 = df['close'].rolling(window=24).mean()
    
    # 计算 BBI
    bbi = (ma3 + ma6 + ma12 + ma24) / 4
    
    # 获取最新一根K线的收盘价和BBI值
    current_close = df['close'].iloc[-1]
    current_bbi = bbi.iloc[-1]
    
    # 获取前一根K线的收盘价和BBI值 (用于判断交叉)
    prev_close = df['close'].iloc[-2]
    prev_bbi = bbi.iloc[-2]
    
    # 3. 获取当前持仓
    positions = get_trade_detail_data(ContextInfo.account_id, 'stock', 'position')
    current_holding = 0
    for pos in positions:
        if pos.m_strInstrumentID + '.' + pos.m_strExchangeID == stock:
            current_holding = pos.m_nVolume
            break

    # 4. 生成交易信号
    # 信号逻辑:
    # 金叉:前一根K线收盘价 <= 前BBI,且 当前收盘价 > 当前BBI
    # 死叉:前一根K线收盘价 >= 前BBI,且 当前收盘价 < 当前BBI
    
    is_golden_cross = (prev_close <= prev_bbi) and (current_close > current_bbi)
    is_death_cross = (prev_close >= prev_bbi) and (current_close < current_bbi)
    
    # 5. 执行交易
    # 如果不是最后一根K线(回测中),或者在实盘的最新K线
    if ContextInfo.is_last_bar():
        print(f"时间: {date_str}, 收盘价: {current_close:.2f}, BBI: {current_bbi:.2f}")
    
    if is_golden_cross:
        if current_holding == 0:
            print(f"{date_str} 触发买入信号 (价格上穿BBI)")
            # 全仓买入 (目标仓位调整为 100%)
            order_target_percent(stock, 1.0, ContextInfo, ContextInfo.account_id)
            
    elif is_death_cross:
        if current_holding > 0:
            print(f"{date_str} 触发卖出信号 (价格下穿BBI)")
            # 清仓卖出 (目标仓位调整为 0%)
            order_target_percent(stock, 0.0, ContextInfo, ContextInfo.account_id)

代码说明

  1. 初始化 (init):

    • 设置了单一标的 600000.SH
    • 定义了 BBI 所需的均线周期列表 [3, 6, 12, 24]
    • 注意:请务必将 YOUR_ACCOUNT_ID 替换为您实际的 QMT 资金账号。
  2. 数据获取 (handlebar):

    • 使用 get_market_data_ex 获取前复权的日线数据。
    • 取最近 30 根 K 线以确保有足够的数据计算 24 日均线。
  3. 指标计算:

    • 利用 pandasrolling().mean() 快速计算各周期均线。
    • 根据公式计算 BBI 值。
  4. 信号判断:

    • 策略采用了交叉判断法:对比“当前时刻”和“上一时刻”的价格与 BBI 的关系。
    • 金叉:价格从下方穿过 BBI 线,视为买点。
    • 死叉:价格从上方穿过 BBI 线,视为卖点。
  5. 交易执行:

    • 使用 order_target_percent 函数。
    • 买入时设置为 1.0 (即 100% 仓位)。
    • 卖出时设置为 0.0 (即 0% 仓位,清仓)。

Q&A

Q: 为什么使用 get_market_data_ex 而不是 get_market_data?
A: get_market_data_ex 是 QMT 推荐的新版接口,返回的数据结构更清晰(字典包含 DataFrame),且支持更多参数配置(如复权方式、订阅开关等),处理效率更高。

Q: 如何在实盘中避免信号闪烁(Repainting)?
A: 在实盘中,当前 K 线尚未走完时,收盘价是变动的,可能导致 BBI 信号在盘中反复触发。

  • 解决方法 1:本代码逻辑是在 handlebar 中运行。如果是日线周期,盘中每个 tick 都会计算。如果希望只在收盘确认后交易,可以判断时间是否接近 14:55 或 15:00。
  • 解决方法 2:使用 ContextInfo.is_new_bar() 逻辑,仅在下一根 K 线开盘的瞬间,根据上一根已完成的 K 线信号进行下单(do_order 配合)。

Q: 这个策略适合什么周期?
A: BBI 是一个趋势指标。

  • 日线:适合捕捉中短期趋势,信号较稳。
  • 分钟线 (如 15m, 60m):信号会更频繁,但在震荡市中容易产生假信号导致频繁止损。建议配合其他过滤器(如成交量或 MACD)使用。