🚀 AI 一键生成 qmt 策略代码

如何在 QMT 中使用资金流量指标 (MFI) 判断超买超卖?

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

问题描述

请帮我编写一个使用 MFI 指标的交易策略

解决方案

这是一个基于 MFI (Money Flow Index, 资金流量指标) 的 QMT 量化交易策略。

策略逻辑说明

MFI 指标结合了价格和成交量,用于衡量市场的买卖压力。它通常在 0 到 100 之间波动。

  1. 买入信号(超卖):当 MFI 值低于 20 时,认为市场处于超卖状态,资金可能开始回流,产生买入信号。
  2. 卖出信号(超买):当 MFI 值高于 80 时,认为市场处于超买状态,资金可能流出,产生卖出信号。
  3. 持仓管理
    • 触发买入信号且当前无持仓时,全仓买入。
    • 触发卖出信号且当前有持仓时,清仓卖出。

策略代码

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

def init(ContextInfo):
    """
    初始化函数,策略启动时调用一次
    """
    # 1. 设置策略参数
    ContextInfo.mfi_period = 14       # MFI 计算周期
    ContextInfo.buy_threshold = 20    # 买入阈值 (超卖)
    ContextInfo.sell_threshold = 80   # 卖出阈值 (超买)
    
    # 2. 设置股票池 (示例:浦发银行,平安银行)
    # 在实际运行时,可以通过界面设置股票池,也可以在这里代码指定
    ContextInfo.stock_list = ['600000.SH', '000001.SZ']
    ContextInfo.set_universe(ContextInfo.stock_list)
    
    # 3. 设置资金账号 (请替换为您真实的资金账号)
    ContextInfo.account_id = 'YOUR_ACCOUNT_ID' 
    ContextInfo.set_account(ContextInfo.account_id)
    
    # 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线或每个Tick调用一次
    """
    # 获取当前设置的股票池
    stock_list = ContextInfo.get_universe()
    
    # 获取当前周期 (例如 '1d')
    period = ContextInfo.period
    
    # 确保是最后一根K线才下单 (避免历史K线重复下单,实盘中通常需要此判断)
    # 如果是回测模式,平台会自动处理,但加上此判断逻辑更严谨
    if not ContextInfo.is_last_bar():
        # 回测模式下,如果不是最后一根bar,通常由平台驱动循环,这里直接return可能导致回测不执行
        # 注意:QMT回测机制中,handlebar会在每一根历史K线上执行。
        # 实盘中,通常只在最新的K线(is_last_bar)处理交易。
        # 为了兼容回测和实盘,这里不做 return,而是依赖 QMT 的机制。
        pass

    # 获取历史行情数据
    # 我们需要 High, Low, Close, Volume 来计算 MFI
    # 多取一些数据以保证指标计算的稳定性,例如取 period * 3
    count = ContextInfo.mfi_period * 3
    
    # 使用 get_market_data_ex 获取数据 (推荐使用 ex 接口)
    # 返回格式: {code: DataFrame}
    market_data = ContextInfo.get_market_data_ex(
        ['high', 'low', 'close', 'volume'], 
        stock_list, 
        period=period, 
        count=count, 
        dividend_type='front' # 前复权
    )

    for stock in stock_list:
        # 获取该股票的 DataFrame 数据
        if stock not in market_data:
            continue
            
        df = market_data[stock]
        
        # 数据长度不足,无法计算指标,跳过
        if len(df) < ContextInfo.mfi_period + 1:
            continue
            
        # 提取 numpy 数组供 talib 使用
        # 注意:talib 需要 float 类型
        high_prices = df['high'].values.astype(float)
        low_prices = df['low'].values.astype(float)
        close_prices = df['close'].values.astype(float)
        volumes = df['volume'].values.astype(float)
        
        # --- 计算 MFI 指标 ---
        try:
            mfi_values = talib.MFI(high_prices, low_prices, close_prices, volumes, timeperiod=ContextInfo.mfi_period)
        except Exception as e:
            print(f"计算 {stock} MFI 出错: {e}")
            continue
            
        # 获取最新的 MFI 值 (数组的最后一个)
        # 注意:如果数据包含 NaN,需要处理
        current_mfi = mfi_values[-1]
        
        if np.isnan(current_mfi):
            continue
            
        # --- 交易逻辑 ---
        
        # 打印日志方便调试
        # print(f"代码: {stock}, 时间: {df.index[-1]}, MFI: {current_mfi:.2f}")

        # 1. 卖出逻辑:MFI > 80 (超买)
        if current_mfi > ContextInfo.sell_threshold:
            # 使用 order_target_percent 调整仓位到 0 (即清仓)
            # 这种方式比 passorder 更方便,不用自己计算持仓量
            order_target_percent(stock, 0, ContextInfo, ContextInfo.account_id)
            # print(f"{stock} 触发卖出信号,MFI={current_mfi}")

        # 2. 买入逻辑:MFI < 20 (超卖)
        elif current_mfi < ContextInfo.buy_threshold:
            # 使用 order_target_percent 调整仓位到 10% (示例:每只股票买入总资产的 10%)
            # 注意:实际使用时请根据资金管理需求调整比例
            target_percent = 0.1 
            order_target_percent(stock, target_percent, ContextInfo, ContextInfo.account_id)
            # print(f"{stock} 触发买入信号,MFI={current_mfi}")

代码关键点解析

  1. # -*- coding: gbk -*-:

    • QMT 的 Python 编辑器默认编码通常是 GBK,加上这行可以防止中文注释乱码。
  2. 数据获取 (get_market_data_ex):

    • 我们使用了 get_market_data_ex 接口,这是 QMT 推荐的高效数据接口。
    • 我们需要 high, low, close, volume 四个字段来计算 MFI。
    • count 设置为 mfi_period * 3 是为了确保有足够的数据让 talib 计算出有效的指标值(去除前期的 NaN 值)。
  3. 指标计算 (talib.MFI):

    • QMT 内置了 talib 库,直接调用 talib.MFI 比手动编写公式计算效率更高且更准确。
    • 输入数据必须转换为 float 类型的 numpy 数组。
  4. 交易执行 (order_target_percent):

    • order_target_percent(stock, percent, ...) 是一个非常方便的高级函数。
    • 当信号为买入时,我们设置目标仓位为 0.1 (即 10%)。如果当前没仓位,它会自动买入;如果已有仓位不足 10%,它会补仓。
    • 当信号为卖出时,我们设置目标仓位为 0。如果当前有仓位,它会自动卖出平仓。
    • 注意:在实盘中,请务必在 init 中将 ContextInfo.account_id 替换为您真实的资金账号。

如何在 QMT 中运行

  1. 打开 QMT 客户端,进入【模型研究】或【策略交易】界面。
  2. 新建一个 Python 策略。
  3. 将上述代码完整复制粘贴到编辑器中。
  4. 修改 init 函数中的 ContextInfo.account_id 为您的实际账号。
  5. 在右侧参数面板或代码中设置想要交易的股票池。
  6. 点击【编译】检查是否有语法错误。
  7. 点击【回测】查看历史表现,或在实盘模式下点击【运行】。

Q&A

Q: 为什么使用 order_target_percent 而不是 passorder?
A: passorder 是最底层的下单函数,需要手动计算买卖数量(例如:当前持仓多少?需要卖多少才能清仓?)。而 order_target_percent 封装了这些逻辑,你只需要告诉它“我希望这只股票占我总资产的百分之几”,系统会自动计算并下单,非常适合这种趋势或反转策略。

Q: 如何调整 MFI 的灵敏度?
A: 修改 init 函数中的 ContextInfo.mfi_period

  • 减小周期(如 10):指标更灵敏,信号更多,但假信号也可能增加。
  • 增大周期(如 20):指标更平滑,信号更少,但更可靠。

Q: 实盘交易需要注意什么?
A: 实盘时建议在 handlebar 开头增加 if not ContextInfo.is_last_bar(): return 的判断,确保只在最新的行情数据到来时才进行计算和下单,避免对历史 K 线重复触发交易。同时,请确保已在 QMT 界面登录了交易账号。