🚀 AI 一键生成 qmt 策略代码

如何在 QMT 策略中集成 TensorFlow 深度学习模型?

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

问题描述

请用 TensorFlow 帮我构建一个股票预测模型

解决方案

这是一个非常好的量化实战需求。在 QMT 中结合 TensorFlow 构建深度学习模型(如 LSTM)进行股票预测是完全可行的。

前置准备:在 QMT 中安装 TensorFlow

由于 QMT 自带的 Python 环境可能没有预装 TensorFlow,你需要先手动安装。

  1. 找到 QMT 安装目录下的 Python 路径(通常在 bin.x64/ 下)。
  2. 打开命令行(CMD),运行类似以下的命令(请根据实际路径修改):
    D:\QMT\bin.x64\Python.exe -m pip install tensorflow scikit-learn pandas numpy
    
    注意:QMT 的 Python 版本通常为 3.6.8,请确保安装兼容的 TensorFlow 版本(如 1.15.0 或 2.0.0-2.6.0 之间,建议先尝试 pip install tensorflow 让其自动选择)。

策略逻辑概述

我们将构建一个基于 LSTM (长短期记忆网络) 的简单预测模型:

  1. 数据获取:获取目标股票过去 N 天的收盘价。
  2. 数据预处理:使用 MinMaxScaler 将价格归一化到 0-1 之间(这对 LSTM 至关重要)。
  3. 模型训练:在 init 初始化阶段,利用历史数据训练一个 LSTM 模型。
  4. 预测与交易:在 handlebar 中,获取最新的时间窗口数据,输入模型预测次日收盘价。
    • 如果 预测价格 > 当前价格 * (1 + 阈值) -> 买入
    • 如果 预测价格 < 当前价格 * (1 - 阈值) -> 卖出

QMT TensorFlow 股票预测策略代码

# -*- coding: gbk -*-
import pandas as pd
import numpy as np
import time
from sklearn.preprocessing import MinMaxScaler

# 尝试导入 TensorFlow,如果未安装则报错提示
try:
    import tensorflow as tf
    from tensorflow.keras.models import Sequential
    from tensorflow.keras.layers import LSTM, Dense
except ImportError:
    print("错误: 未检测到 TensorFlow。请在 QMT 的 Python 环境中执行 pip install tensorflow")

def init(ContextInfo):
    """
    初始化函数:设置参数、获取历史数据、训练模型
    """
    # 1. 策略参数设置
    ContextInfo.stock = '600000.SH'  # 目标股票:浦发银行
    ContextInfo.account_id = '600000248' # 请修改为您的资金账号
    ContextInfo.account_type = 'STOCK'
    ContextInfo.look_back = 60       # 回溯窗口:用过去60天预测下一天
    ContextInfo.threshold = 0.005    # 交易阈值:0.5%
    ContextInfo.train_days = 500     # 训练数据长度
    
    # 设置股票池
    ContextInfo.set_universe([ContextInfo.stock])
    ContextInfo.set_account(ContextInfo.account_id)
    
    print("正在初始化模型,请稍候...")
    
    # 2. 获取训练数据 (使用 get_market_data_ex)
    # 获取足够长的历史数据用于训练
    history_data = ContextInfo.get_market_data_ex(
        fields=['close'],
        stock_code=[ContextInfo.stock],
        period='1d',
        start_time='', 
        end_time='',
        count=ContextInfo.train_days + ContextInfo.look_back,
        dividend_type='front' # 前复权
    )
    
    if ContextInfo.stock not in history_data:
        print("未获取到历史数据")
        return

    df = history_data[ContextInfo.stock]
    
    # 3. 数据预处理
    # LSTM 对数据数值敏感,需要归一化
    ContextInfo.scaler = MinMaxScaler(feature_range=(0, 1))
    dataset = ContextInfo.scaler.fit_transform(df['close'].values.reshape(-1, 1))
    
    # 构建训练集 (X: 过去60天, Y: 第61天)
    x_train, y_train = [], []
    for i in range(ContextInfo.look_back, len(dataset)):
        x_train.append(dataset[i-ContextInfo.look_back:i, 0])
        y_train.append(dataset[i, 0])
        
    x_train, y_train = np.array(x_train), np.array(y_train)
    # LSTM 需要 3D 输入: [samples, time steps, features]
    x_train = np.reshape(x_train, (x_train.shape[0], x_train.shape[1], 1))
    
    # 4. 构建并训练 LSTM 模型
    # 注意:在实盘中,建议线下训练好模型保存为文件,在此处 load_model 读取,以节省启动时间
    model = Sequential()
    model.add(LSTM(units=50, return_sequences=True, input_shape=(x_train.shape[1], 1)))
    model.add(LSTM(units=50))
    model.add(Dense(1))
    
    model.compile(loss='mean_squared_error', optimizer='adam')
    
    # 训练模型 (epochs 和 batch_size 可根据需要调整)
    print("开始训练 TensorFlow 模型...")
    model.fit(x_train, y_train, epochs=5, batch_size=32, verbose=0)
    print("模型训练完成")
    
    # 将模型保存到 ContextInfo 以便在 handlebar 中使用
    ContextInfo.model = model

def handlebar(ContextInfo):
    """
    行情驱动函数:每根 K 线运行一次,进行预测和交易
    """
    # 确保是最后一根K线才交易(避免历史回测时的重复计算,或者根据需求调整)
    if not ContextInfo.is_last_bar():
        return

    stock = ContextInfo.stock
    look_back = ContextInfo.look_back
    
    # 1. 获取最近 N 天的数据用于预测
    # 我们需要过去 look_back 天的数据来预测明天
    recent_data = ContextInfo.get_market_data_ex(
        fields=['close'],
        stock_code=[stock],
        period='1d',
        start_time='',
        end_time='',
        count=look_back,
        dividend_type='front'
    )
    
    if stock not in recent_data or len(recent_data[stock]) < look_back:
        print("数据不足,无法预测")
        return
        
    df_recent = recent_data[stock]
    current_price = df_recent.iloc[-1]['close'] # 今日收盘价
    
    # 2. 数据预处理
    # 使用 init 中训练好的 scaler 进行转换
    inputs = df_recent['close'].values.reshape(-1, 1)
    inputs = ContextInfo.scaler.transform(inputs)
    
    # 重塑为 LSTM 输入格式 [1, look_back, 1]
    x_test = np.reshape(inputs, (1, look_back, 1))
    
    # 3. 模型预测
    predicted_scaled_price = ContextInfo.model.predict(x_test)
    # 反归一化,得到真实的预测价格
    predicted_price = ContextInfo.scaler.inverse_transform(predicted_scaled_price)[0][0]
    
    print(f"当前时间: {timetag_to_datetime(ContextInfo.get_bar_timetag(ContextInfo.barpos), '%Y-%m-%d')}")
    print(f"当前价格: {current_price:.2f}, 模型预测明日价格: {predicted_price:.2f}")
    
    # 4. 交易逻辑
    # 获取当前持仓
    positions = get_trade_detail_data(ContextInfo.account_id, ContextInfo.account_type, 'POSITION')
    has_position = False
    for pos in positions:
        if pos.m_strInstrumentID == stock:
            has_position = True
            break
            
    # 信号判断
    threshold = ContextInfo.threshold
    
    # 如果 预测价格 > 当前价格 * (1 + 阈值) -> 看涨 -> 买入
    if predicted_price > current_price * (1 + threshold):
        print("信号: 看涨 -> 执行买入")
        # 目标市值买入,例如调整仓位到 50%
        order_target_percent(stock, 0.5, 'FIX', current_price, ContextInfo, ContextInfo.account_id)
        
    # 如果 预测价格 < 当前价格 * (1 - 阈值) -> 看跌 -> 卖出
    elif predicted_price < current_price * (1 - threshold):
        print("信号: 看跌 -> 执行卖出")
        # 清仓
        order_target_percent(stock, 0.0, 'FIX', current_price, ContextInfo, ContextInfo.account_id)
    else:
        print("信号: 震荡 -> 保持观望")

代码关键点解析

  1. 数据获取 (get_market_data_ex):

    • 我们使用了 get_market_data_ex 接口,这是 QMT 推荐的获取行情数据的方式。
    • dividend_type='front':使用前复权数据对于深度学习模型非常重要,因为除权造成的股价跳空会干扰模型训练。
  2. 数据归一化 (MinMaxScaler):

    • 神经网络(特别是 LSTM)对输入数据的尺度非常敏感。我们将股价缩放到 [0, 1] 区间。
    • 关键点scaler 对象必须在 init 中保存到 ContextInfo,因为在 handlebar 预测时,必须使用相同的参数对新数据进行归一化,并对预测结果进行反归一化。
  3. 模型构建 (LSTM):

    • 我们构建了一个包含两层 LSTM 和一层 Dense 输出层的简单模型。
    • input_shape=(x_train.shape[1], 1) 对应 (时间步长, 特征数)。这里时间步长是 60 天,特征数是 1(只有收盘价)。
  4. 实盘与回测的差异:

    • 上述代码在 init 中进行训练。在回测模式下,这只会在开始时运行一次。
    • 在实盘模式下,每次启动策略都会重新训练。如果数据量很大,建议线下训练好模型保存为 .h5 文件,然后在 init 中使用 tf.keras.models.load_model 加载,以提高效率。

常见问题 (Q&A)

Q: 为什么运行代码时提示 ImportError: No module named 'tensorflow'?
A: QMT 自带的 Python 环境是独立的,不共享你电脑系统里安装的 Python 库。你需要按照文章开头的“前置准备”部分,使用 QMT 目录下的 pip.exe 安装 TensorFlow。

Q: 这个模型可以直接用于实盘盈利吗?
A: 不可以。 这是一个演示性的 "Hello World" 级别的模型。它只使用了单一的收盘价作为特征,且没有经过复杂的调参和验证。真实的量化模型需要加入更多因子(如成交量、技术指标、大盘指数等),并进行严格的交叉验证和风险控制。

Q: 如何让模型每天更新训练?
A: 目前的代码只在策略启动时训练一次。如果你希望每天收盘后更新模型,可以在 handlebar 中判断时间(例如 15:00 后),获取最新数据,调用 model.fit 进行增量训练(Incremental Learning)。

Q: handlebar 函数的执行频率是怎样的?
A: 在回测模式下,handlebar 会按照 K 线序列逐根执行。在实盘模式下,如果选择日线周期,它会在每天收盘或数据更新时执行;如果选择分钟线,则每分钟执行。代码中使用了 ContextInfo.is_last_bar() 确保只在最新的 K 线上进行信号判断。