问题描述
解决方案
这是一个非常好的量化实战需求。在 QMT 中结合 TensorFlow 构建深度学习模型(如 LSTM)进行股票预测是完全可行的。
前置准备:在 QMT 中安装 TensorFlow
由于 QMT 自带的 Python 环境可能没有预装 TensorFlow,你需要先手动安装。
- 找到 QMT 安装目录下的 Python 路径(通常在
bin.x64/下)。 - 打开命令行(CMD),运行类似以下的命令(请根据实际路径修改):
注意:QMT 的 Python 版本通常为 3.6.8,请确保安装兼容的 TensorFlow 版本(如 1.15.0 或 2.0.0-2.6.0 之间,建议先尝试D:\QMT\bin.x64\Python.exe -m pip install tensorflow scikit-learn pandas numpypip install tensorflow让其自动选择)。
策略逻辑概述
我们将构建一个基于 LSTM (长短期记忆网络) 的简单预测模型:
- 数据获取:获取目标股票过去 N 天的收盘价。
- 数据预处理:使用
MinMaxScaler将价格归一化到 0-1 之间(这对 LSTM 至关重要)。 - 模型训练:在
init初始化阶段,利用历史数据训练一个 LSTM 模型。 - 预测与交易:在
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("信号: 震荡 -> 保持观望")
代码关键点解析
-
数据获取 (
get_market_data_ex):- 我们使用了
get_market_data_ex接口,这是 QMT 推荐的获取行情数据的方式。 dividend_type='front':使用前复权数据对于深度学习模型非常重要,因为除权造成的股价跳空会干扰模型训练。
- 我们使用了
-
数据归一化 (
MinMaxScaler):- 神经网络(特别是 LSTM)对输入数据的尺度非常敏感。我们将股价缩放到
[0, 1]区间。 - 关键点:
scaler对象必须在init中保存到ContextInfo,因为在handlebar预测时,必须使用相同的参数对新数据进行归一化,并对预测结果进行反归一化。
- 神经网络(特别是 LSTM)对输入数据的尺度非常敏感。我们将股价缩放到
-
模型构建 (LSTM):
- 我们构建了一个包含两层 LSTM 和一层 Dense 输出层的简单模型。
input_shape=(x_train.shape[1], 1)对应(时间步长, 特征数)。这里时间步长是 60 天,特征数是 1(只有收盘价)。
-
实盘与回测的差异:
- 上述代码在
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 线上进行信号判断。