ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

LSTM股票价格预测源码实践:数据处理、参数调优与回测避坑指南

LSTM股票价格预测源码实践:数据处理、参数调优与回测避坑指南 简介基于长短期记忆网络的股票价格预测Python实现源码包面向高校数据科学与金融工程方向的课程设计、期末大作业及毕业设计场景提供完整可运行的预测项目适合作为深度学习与量化交易练习的入门范例。资源共89个文件压缩包约8.89MB核心包含24个Python脚本、13个说明文档、若干HTML/CSS/JS前端页面及SQLite数据库文件既能支撑LSTM模型训练与预测流程也可用于结果可视化与交互展示。已有738人学习下载。项目中不仅含可直接运行的Django代码与核心预测脚本还附带使用说明文档、运行截图及README文件覆盖数据预处理、模型构建、训练评估到结果输出全过程目录结构清晰便于快速定位和理解模块关系。整个项目达到高分课程设计要求下载后无需额外修改即可复现演示适合作为期末大作业或课程设计的参考蓝本。1. 拿到基于LSTM的股票价格预测源码包先别急着跑你要解决的不只是模型看到基于LSTM的股票价格预测 python实现 源码使用说明文档.zip这个标题时我猜你和我一样第一反应是下载下来解压再说。等你真正跑起来会发现这个包解决的并不是预测明天的涨跌而是用 Python 把 LSTM 模型应用到股票时间序列上的完整流程数据读取、窗口构造、模型训练、结果预测外加一份使用说明文档解释每个文件是干嘛的。我建议你在敲下python train.py之前先弄清楚三个问题模型输入是什么、标签怎么构造、验证集有没有偷看未来。很多源码包能跑通但预测曲线是一条水平直线问题往往不在 LSTM而在数据处理。我会从解压 zip 开始把模型选型、参数调整、常见翻车现场和滚动回测完整过一遍让你拿到手不是只会跑而是能改、能说清楚、能换到自己的数据上。2. 拆开这个 zip 之前LSTM 为什么被用在股票价格预测上以及它的三个硬边界2.1 LSTM 在时间序列里的角色记忆门控和长期依赖的通俗账股票价格数据本质上是一条时间序列今天的价格和十几天前的价格之间可能存在关联但中间又夹杂了大量无关波动。传统 RNN 在反向传播时梯度会随着时间步反复相乘超过一定长度就消失或爆炸于是模型只能记住最近几步学不到十天前的放量这种长距离信息。LSTM 引入遗忘门、输入门、输出门三个门控把信息更新变成逐元素的加法和乘法让梯度有一条能穿越时间步的公路。通俗地说遗忘门决定要不要清掉旧状态输入门决定新信息怎么写进去输出门决定当前隐藏状态给谁看。在股票预测这种信噪比很低的场景里LSTM 并不保证比线性模型强但门控结构让它至少能处理变长窗口并有机会记住当前趋势可能要反转的隐状态。很多开源 LSTM 模型代码正是把它当作默认模型。你需要注意的是房价、天气、交通流量这类序列也可以复用同一套 python 实现只要把输入特征和归一化方式换掉。这也是这类源码包价值所在模型本身是通用的股票数据只是载体。2.2 股票价格预测任务的建模选择单变量、多变量、还是多步预测打开源码包里的数据处理脚本你会发现建模方式通常分成三类。单变量只取收盘价 close构造成[t-lookback1, ..., t]预测t1的价格。多变量则把 open、high、low、close、volume 甚至 MACD、RSI 一起作为输入。多步预测是在输出端一次预测未来 N 天或者用滚动方式迭代预测 N 次。三种组合差别很大源码包里默认的往往是单变量单步因为它最容易在课程设计和毕业设计里跑通。模型类型输入特征输出目标适合场景常见坑单变量单步收盘价序列次日收盘价入门、基线归一化泄漏多变量单步OHLCV指标次日收盘价/涨跌幅有一定特征工程经验特征过多过拟合单变量多步收盘价序列未来 N 天价格观察中期趋势误差逐日累积多变量多步OHLCV指标未来 N 天价格研究型项目数据对齐复杂我建议你拿到源码后先跑通单变量单步把预测曲线稳定下来再加成交量之类的特征。不要一上来就塞十几个指标否则你很难判断 LSTM 学到的是市场规律还是噪声。使用说明文档里如果给了参数表通常也只会覆盖这个默认组合所以先把基础流程摸熟再往多变量方向扩。关于选型常见的问题还有为什么不直接用 ARIMA 或者 Transformer。ARIMA 对线性关系更擅长但对多变量、非线性交互无能为力Transformer 需要大量数据且计算注意力矩阵时会把时间顺序打散得更彻底需要额外的位置编码和 mask。LSTM 模型代码在几千条日线数据上就能训练而且社区里的 python 源码包大多围绕它实现调试成本最低。所以不管行业里怎么争论在课程设计、毕业设计和量化入门阶段LSTM 是这个标题下性价比最稳的选择。2.3 硬边界一收益率的不可预测部分硬边界二滑点与手续费硬边界三数据穿越先说边界免得你对 LSTM 抱有不切实际的期望。股票收益率里大部分是不可预测的LSTM 能学到的主要是短期的动量效应、均值回归和波动聚集。源码包在测试集上展示的 MSE 很低不一定是因为它看懂了市场而是因为它在预测一条缓慢上涨的曲线涨跌方向的胜率可能和抛硬币差不多。所以评估模型时不要只看 Loss要看方向准确率、回撤和扣掉交易成本后的净收益。第二道边界是滑点与手续费。很多源码包回测时假设成交价等于收盘价也不扣手续费于是策略在纸面上年化收益很高。你把预测结果变成交易信号之前至少按万分之二的手续费加千分之五的滑点压测一次很多高频信号会直接变成负收益。这也是我拿到任何预测源码都会先问一句它的回测模块有没有把交易成本算进去。第三道边界是数据穿越这是源码包最容易埋雷的地方。典型写法是把全部历史价格拿去算均值方差再做归一化然后用前 80% 训练、后 20% 测试。看似没问题但归一化的统计量已经偷看了测试集。更隐蔽的是某些包用shift(-1)把明天的收盘价搬到今天的特征里。遇到这种情况训练出来的模型准确率高得离谱一到真实环境立刻失效。第 5 章我会专门讲怎么排查这类问题。说这三个边界不是劝退而是告诉你这类源码包的适用位置它最适合用在学习、研究和策略探索上而不是作为自动印钞机。想用 LSTM 做股票价格预测先接受大部分收益不可预测再把精力放在数据质量、特征构造和回测框架上这才是源码包真正值得投入的地方。3. 从 zip 到第一个预测结果源码包的目录结构、数据准备和最小跑通命令3.1 解压后的常见目录划分与入口脚本说明这类源码包解压后通常不是一个大杂烩而是按功能分好目录。我经手过的 LSTM 股票预测 python 项目最常见骨架是一个data/目录放原始 CSV一个models/或utils/放网络结构和通用函数根目录放 train.py 和 predict.py另有 requirements.txt 和使用说明文档。你第一件事不是跑 train.py而是打开使用说明文档确认 Python 版本和依赖。文件/目录作用你需要改什么data/xxx.csv历史行情数据换成自己的股票数据train.py训练主入口数据路径、模型参数predict.py加载模型并预测窗口大小、预测长度utils/数据读取、归一化检查 scaler 是否被重复 fitrequirements.txt依赖声明按实际环境固定版本使用说明文档参数说明和示例命令对照本文查漏有些压缩包会把使用说明文档放在根目录命名为 README.md 或者 使用说明.docx/pdf。如果里面连环境要求都没写你就要小心这份源码可能从未在干净环境跑通过。3.2 安装依赖Python 版本、TensorFlow/PyTorch 的选择以及 requirements.txt 的处理先把环境隔离出来。项目默认可能是 TensorFlow 的 Keras 实现也可能是 PyTorch两者代码风格完全不同。建议你先看使用说明文档里写的是哪个框架。我一般会用 Python 3.8 以上的版本建一个虚拟环境再按 requirements.txt 装依赖避免把全局环境搞乱。# 进入解压目录 cd lstm_stock_project # 创建并激活 Python 3 虚拟环境 python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate # 安装依赖如果 requirements.txt 缺失可手动安装核心库 pip install -r requirements.txt这里的source venv/bin/activate是 Linux/macOS 的激活命令Windows 上换成venv\Scripts\activate。如果包是 PyTorch 版本建议到官网按显卡型号安装对应 CUDA 版本只跑 CPU 的话默认包也能跑通只是慢一些。requirements.txt 里的版本不要照单全收TensorFlow 2.x 在不同小版本间 API 差异明显如果使用说明文档没有强制版本我建议选一个稳定的 TensorFlow 2.x 组合或者干脆换成 PyTorch 2.x 的对应实现。装完依赖后先跑python -c import tensorflow as tf; print(tf.__version__)确认导入不报错再继续。3.3 原始数据长什么样CSV列名、时间格式、除权除息与停牌的预处理最常见的行情 CSV 长这样第一行是列名后面按时间升序排列列名通常是 date、open、high、low、close、volume。但不同数据源的列名可能不同有的用日期时间戳有的是字符串有的直接把股票代码放在第一列。拿到数据后先别急着训练先做一次体检。import pandas as pd # 读取原始数据date 列先解析成 datetime df pd.read_csv(data/000001.csv, parse_dates[date]) # 看缺失值和每一列的类型 print(df.info()) print(df.head()) # 统一按日期排序确保时间升序 df df.sort_values(date).reset_index(dropTrue) # 删除空值缺口太多就重新取数 df df.dropna() # 对停牌造成的缺失价格做前向填充但前提是知道停牌原因 df[close] df[close].ffill()parse_dates会把日期列直接解析成 datetime 类型省去后面手动格式转换。排序是时间序列任务的第一原则否则滑动窗口会切乱。缺失值处理上插值适合短暂缺口连续停牌超过 5 个交易日我一般直接剔除或者重新下载复权数据。复权特别重要除权除息当天价格会跳空不处理会让 LSTM 学出一个假跳变预测结果自然南辕北辙。所以如果源码包没有做复权处理你要在预处理阶段补上。这里要注意很多使用说明文档只写数据格式为 CSV但不会告诉你应该用前复权还是后复权。对 LSTM 这类按价格绝对数值学习的模型我建议用后复权数据因为它不会因为最新价格变化而重写历史序列。3.4 最小跑通命令训练脚本 预测脚本从命令行参数到输出文件训练入口脚本的骨架通常是这样读数据、构造窗口、训练、保存模型。下面是一个可运行的最小版本你可以对照源码包里的 train.py 检查流程是否完整。# train.py import numpy as np import pandas as pd from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from sklearn.preprocessing import MinMaxScaler def create_sequences(data, lookback): 把连续序列切成 (lookback, 1) 的输入窗口 X, y [], [] for i in range(lookback, len(data)): X.append(data[i-lookback:i]) y.append(data[i]) # 用前 lookback 天预测第 i 天 return np.array(X), np.array(y) df pd.read_csv(data/000001.csv, parse_dates[date]) df df.sort_values(date) close df[close].values.reshape(-1, 1) # 归一化只用训练段 fit测试段用同一个 scaler scaler MinMaxScaler(feature_range(0, 1)) scaled scaler.fit_transform(close) lookback 30 X, y create_sequences(scaled, lookback) split int(len(X) * 0.7) # 按时间顺序前 70% 训练 X_train, X_test X[:split], X[split:] y_train, y_test y[:split], y[split:] model Sequential([ LSTM(64, return_sequencesTrue, input_shape(lookback, 1)), Dropout(0.2), LSTM(32), Dropout(0.2), Dense(1) ]) model.compile(optimizeradam, lossmse) model.fit(X_train, y_train, validation_data(X_test, y_test), epochs30, batch_size32, verbose1) model.save(lstm_stock.h5)这里create_sequences函数把长度为 lookback 的历史窗口映射到下一个时间点的价格。注意我没有随机打乱样本因为时间序列一旦打乱就破坏了顺序。归一化只对训练段调用fit_transform测试段复用同一个 scaler这是防止数据穿越的关键。模型用了两层 LSTM第一层return_sequencesTrue保留每个时间步输出才能拼第二层。参数上lookback 取 30 是日线比较常用的经验值神经元从 64 开始先看 loss 趋势再调整。训练轮数 30 轮如果训练集 loss 还在降再加 epochs。显卡内存不足时把 batch_size 调小到 16 或 8两个 LSTM 层对显存消耗不算小。predict.py 要做的是加载保存的模型用最后 lookback 个交易日的数据预测下一个交易日再把归一化数值还原成原始价格。# predict.py import numpy as np import pandas as pd from tensorflow.keras.models import load_model from sklearn.preprocessing import MinMaxScaler import joblib df pd.read_csv(data/000001.csv, parse_dates[date]).sort_values(date) close df[close].values.reshape(-1, 1) # 注意正式项目这里应加载训练时保存的 scaler scaler MinMaxScaler() scaled scaler.fit_transform(close) lookback 30 last_seq scaled[-lookback:].reshape(1, lookback, 1) model load_model(lstm_stock.h5) pred_scaled model.predict(last_seq)[0, 0] pred_price scaler.inverse_transform([[pred_scaled]])[0, 0] print(fnext close: {pred_price:.2f})predict.py 里如果重新fit_transform全部数据scaler 的 min/max 就包含未来信息相当于偷看答案。正确做法是在训练结束后用joblib.dump(scaler, scaler.pkl)保存 scalerpredict.py 里只joblib.load不做新的 fit。源码包里如果没这么做你要自己补上。这也是为什么我说这份使用说明文档不能只教命令还得教会你保存状态。4. 把源码调成你自己的数据LSTM 时间序列预测的 6 个必调参数与预处理细节4.1 滑动窗口 lookback窗口太短学不到趋势太长把序列拉成白噪声lookback 是第一个要调的参数。对日线数据常见范围是 10 到 60。窗口太短模型只能看到最近几天的波动捉不到慢变量窗口太长序列里大部分信息冗余LSTM 反而更难提取有用信号。一个经验做法是先画收盘价的自相关图看多少阶之后自相关衰减到 0再在那个附近选窗口。from statsmodels.graphics.tsaplots import plot_acf import matplotlib.pyplot as plt plot_acf(df[close], lags60) plt.show()自相关只代表线性相关性LSTM 还能捕捉非线性关系所以它是个起点而不是硬指标。我一般会先跑 20、30、45 三组窗口做对比看验证集 RMSE 的变化。注意每次换窗口都要重新训练否则对比没有意义。另外 lookback 变了输入维度也跟着变model summary 里确认一下input_shape是否同步修改。4.2 归一化方式MinMaxScaler、StandardScaler以及预测值还原的正确姿势价格序列几乎总是用 MinMaxScaler 缩放到 [0,1]因为 LSTM 的激活函数在中间区间敏感、在两端饱和。收益序列或者技术指标可以另行处理。关键不是选哪个 scaler而是训练和预测必须共用同一套统计量。from sklearn.preprocessing import MinMaxScaler import joblib # 训练阶段只对训练段做 fit scaler MinMaxScaler() scaled_train scaler.fit_transform(train_close) # 保存 scaler joblib.dump(scaler, scaler.pkl) # 预测阶段直接加载不做新的 fit scaler joblib.load(scaler.pkl) scaled_test scaler.transform(test_close)fit_transform和transform的区别一定要分清。很多人图省事在预测脚本里重新fit_transform全部数据scaler 的 min/max 变成全量统计量测试集信息就穿到了训练过程。我还见过一种做法把价格除以一个常数比如 1 万来归一化常数来自未来最高价这同样是数据穿越。记住归一化统计量一旦沾到未来后面的模型评估全部作废。4.3 训练/验证/测试集的切分按时间切不能随机打乱很多表格型任务的代码习惯是train_test_split(X, y, test_size0.2, random_state42)但股票预测不能这样用。时间序列一旦随机打乱昨天和明天的样本出现在同一个批次里验证集的 loss 毫无意义。正确做法是直接用索引切。split int(len(X) * 0.7) # 前 70% 训练 val int(len(X) * 0.9) # 再 20% 验证最后 10% 测试 X_train, y_train X[:split], y[:split] X_val, y_val X[split:val], y[split:val] X_test, y_test X[val:], y[val:] # 如果要数据增强只在训练段内部打乱 idx np.random.permutation(split) X_train, y_train X_train[idx], y_train[idx]这里补充一个很多人容易误踩的点训练集内部可以随机打乱因为样本之间的顺序不影响单个样本的时间结构验证集和测试集必须保持原始顺序因为它们要模拟真实的逐日预测。如果不打乱训练集LSTM 会学到训练段的整体趋势验证 loss 可能异常高也可能异常低。源码包如果给出了 shuffle 参数默认开在训练阶段是合理的。4.4 神经元数、层数、dropout、batch_size、epochs 的参考区间LSTM 模型代码里的超参数直接影响欠拟合还是过拟合。下面这组参数来自我调 LSTM 设备的通用经验也适用于股票日线参数参考范围说明lookback10~60日线常用 20/30LSTM 神经元32~128第一层可稍大层数1~3超过 3 层需要大量数据dropout0.1~0.3防止过拟合batch_size16~128显存允许时取大值epochs20~100配合 EarlyStopping从 1 层 32 个神经元开始如果验证集 loss 不降再往上加。我见过很多人上来就是三层 256 神经元结果在几千条日线数据上严重过拟合训练集 loss 接近 0预测曲线几乎不动。宁可让模型欠拟合也不要把它做成记忆器。epochs 最好配合 EarlyStopping。from tensorflow.keras.callbacks import EarlyStopping early_stop EarlyStopping(monitorval_loss, patience5, restore_best_weightsTrue) model.fit(X_train, y_train, validation_data(X_val, y_val), epochs50, batch_size32, callbacks[early_stop])patience5表示验证集 loss 连续 5 个 epoch 不下降就停止restore_best_weights会把权重恢复到验证集最优的那一步这是防止过拟合的后悔药。4.5 损失函数与评估指标回归问题用 MSE/MAE不要拿 Accuracy 说事LSTM 的最后一层是 Dense(1)本质是回归。有人习惯在model.evaluate里看准确率但回归任务没有 accuracy你看到的准确率其实是 Keras 按 0.5 阈值硬切的假指标。应该看 MSE、MAE、RMSE 或 MAPE。MAPE 是百分比更适合汇报如果预测价格和真实价格平均差 2%说明模型对幅度有基本把握。from sklearn.metrics import mean_absolute_error, mean_squared_error y_pred model.predict(X_test) mae mean_absolute_error(y_test, y_pred) rmse np.sqrt(mean_squared_error(y_test, y_pred)) # 方向准确率预测涨跌方向和真实涨跌方向是否一致 direction np.mean(np.sign(y_test[1:] - y_test[:-1]) np.sign(y_pred[1:] - y_pred[:-1]))方向准确率比数值误差重要因为投资决策真正关心的是方向。但注意方向准确率也要在扣掉手续费后才能算收益不要被 55% 的准确率冲昏头脑。源码包如果只打印 MSE我建议你自己补上方向准确率。4.6 多步预测的两种实现方式递归滚动与直接多输出如果你想预测未来 5 天源码包可能用的是递归滚动用 t 预测 t1把预测值拼回去再预测 t2。这种方式实现简单但误差会逐日累积。另一种是直接多输出把最后一层改成 Dense(5)损失是 5 个步长的平均误差训练更稳但会假设 5 个输出相互独立。# 递归滚动预测 def recursive_forecast(model, last_seq, steps): preds [] current last_seq.copy() for _ in range(steps): p model.predict(current, verbose0)[0, 0] preds.append(p) current np.concatenate( [current[:, 1:, :], np.array([[[p]]])], axis1 ) return np.array(preds) # 直接多输出只需把最后一层换成 Dense(5) # model.add(Dense(5))我建议在源码包里优先用直接多输出因为每个输出节点在训练时都能得到梯度而递归滚动在第二步之后全部依赖模型自己的输出误差放大速度非常快。如果使用说明文档只讲了单步预测你就先预测一天把单步做稳再做 5 天。多步预测的评估要用预测起点的对齐比如第 1 天预测未来 5 天第 2 天预测未来 5 天不能把两条预测序列简单拼接否则你会在同一时间点上叠加多个不同起点的预测人为平滑掉误差。5. 避坑清单从 zip 解压到预测曲线最常见的 5 个翻车现场拿到源码包到真正跑通大多数人都会在上面这些问题里至少翻车一次。下面按现象 → 原因 → 解决的框架记录你可以直接跳到自己遇到的那条。5.1 现象loss 正常下降但预测曲线是一条水平直线这是最常见的翻车现场。训练过程中 loss 从 0.1 降到 0.001看起来很漂亮但把预测值和真实价格画在一起发现预测值是一条贴着均值走平的线。原因通常是目标值在样本中变化太小模型退化成预测下一个点等于上一个点的常数估计另一种原因是最后一层用了 Tanh 激活把输出钳制在固定区间而股票价格归一化后分布很不均匀。解决方法是先看model.summary()确认最后一层是Dense(1)不带激活再看训练集标签的标准差如果小到接近 0需要换一个更长的时间窗口或改用收益率作为预测目标。还有一个容易忽视的原因训练集和验证集整体趋势一致模型只需要记住今天的价格大概率是明天的价格就能得到很低的 MSE于是它学成了复制器。所以你看到水平直线不一定是模型坏了可能是任务本身太容易模型不值得学更多。解决思路是把标签从价格改成涨跌方向或者未来 N 日收益率强迫模型去学变化。5.2 现象用今天的全部数据预测明天结果看起来超级准预测曲线几乎完全贴合真实值很多人以为 LSTM 通灵了实际上十有八九是数据穿越。常见的偷看方式有三种一是归一化时用全量数据 fitscaler 的 min/max 里带着未来信息二是构造特征时用了shift(-1)把明天的价格挪到今天的特征里三是切分前先做了差分差分的统计量跨了训练和测试期。排查办法是打开数据处理脚本搜索shift(和fit_transform确认所有统计量都只来自训练段。另一个粗暴的验证方法训练完把模型权重随机初始化再预测如果预测精度仍然很高说明你的测试集里确实藏了训练信息。或者把时间标签随机打乱再训练如果测试集指标几乎不变这套流程就有问题。数据穿越是 LSTM 时间序列预测里最隐蔽的坑也是使用说明文档一般不会告诉你的部分。5.3 现象验证集 Loss 比训练集低一大截怀疑模型作弊这种情况往往不是模型作弊而是验证集本身太简单。比如你按时间顺序切分前 70% 是 2015 到 2021 年的震荡行情后 30% 是 2021 到 2023 年的单边上涨验证集里昨天的价格就是很好的预测器MSE 天然就小。解决方法是不要只切一次改用滚动验证每训练一次就验证一段最后取所有验证段的平均指标。滚动验证会明显增加训练时间但对时间序列来说是必要的交学费。如果验证集 Loss 反而低得异常还有一种原因是训练阶段没有做随机打乱模型把训练段尾部当成了趋势放大器验证时遇到新趋势就失灵。所以先检查训练代码里有没有 shuffle 训练集再检查验证集的行情结构。不要被一个看起来漂亮的验证数字影响判断。5.4 现象把股票代码的字符串列直接喂给模型训练直接崩溃如果原始表里有code、name这样的字符串列而源码包又偷懒直接把它读进 DataFrame 当特征你会看到类似cannot convert string to float的报错或者模型训练起来 loss 非常不稳定。有些人的第一反应是删掉这一列这是对的。但如果你想保留股票代码这个类别信息应该用LabelEncoder编码后再作为普通特征或者 Embedding 输入。对于单只股票的预测这列没有任何信息量删掉最干净。这里有个更隐蔽的变体有些数据源会把date列也读成 string然后被当成特征送进模型模型训练也能跑但日期字符串被转成整数后会产生无穷大的虚假时间趋势。解决办法是训练前显式df[date] pd.to_datetime(df[date])并且只保留数值列作为模型输入。5.5 现象保存的模型文件在换机器后无法加载或者加载后预测结果错乱用model.save(lstm_stock.h5)保存的模型换到没有安装同样版本库的机器上经常报错。Keras 的 h5 文件把网络结构、权重和优化器状态打包在一起TensorFlow 版本一换就拒载。更隐蔽的问题是训练时设置了随机种子但预测脚本没设结果每次预测值略有差异。解决方法是保存时用model.export(saved_model)导出 SavedModel 格式另外把预测脚本里的随机种子固定成同一个值。如果 predict.py 里重新实现了预处理逻辑记得把训练阶段的 scaler 一起复制过去。还有一类问题是模型能加载但预测结果全是 NaN。这通常是因为训练时输入里有 NaN或者归一化时除零。Keras 在训练时不报错会把 NaN 写进权重预测自然出错。所以预处理阶段的dropna()和replace([np.inf, -np.inf], np.nan)要写进数据检查里强制要求数据质量合格再开始训练。6. 从能跑通到敢实盘先用滚动回测验证源码再谈参数寻优当模型能稳定输出预测价格后我不建议直接把它接到交易 API 上。我会先写一个滚动回测脚本把源码包里的单次训练/预测改成每 20 个交易日重新训练一次预测未来 5 天然后把结果拼接成一条资金曲线。这样做能同时检验模型的稳定性、参数敏感度和交易成本的影响。def rolling_backtest(df_scaled, lookback, step20, horizon5): preds, reals [], [] start lookback end start step while end horizon len(df_scaled): # 每次只用 [:end] 训练确保不偷看未来 X, y create_sequences(df_scaled[:end], lookback) model build_lstm(lookback) # 重新建模型 model.fit(X, y, epochs30, batch_size32, verbose0) # 从 end-lookback 开始递归预测 horizon 天 cur df_scaled[end-lookback:end].reshape(1, lookback, 1) for _ in range(horizon): p model.predict(cur, verbose0)[0, 0] preds.append(p) cur np.concatenate([cur[:, 1:, :], np.array([[[p]]])], axis1) reals.extend(df_scaled[end:endhorizon].flatten()) end step return np.array(preds), np.array(reals)这段代码的核心是每次只使用 end 之前的数据保证预测时看不到未来。step 控制重训练频率20 个交易日相当于每月更新一次。回测完成后至少计算三个指标方向准确率、累计收益率、最大回撤。方向准确率低于 52% 基本覆盖不了手续费。参数寻优方面使用说明文档一般只给一组默认参数我会用 Optuna 做贝叶斯搜索搜索空间限定在 lookback、神经元数、dropout、batch_size 四个维度每个组合用滚动回测的验证段来评分而不是用最后的测试段。这里有个血泪教训如果直接对测试段调参数调出来的结果只对那一段历史有效换一只股票就翻车。我过去就这么干过回测曲线漂亮得像印钞机实盘一上去就偃旗息鼓。后来学乖了先锁死一组保守参数跑多只股票看参数在不同标的上是不是都稳定再去微调。所以拿到这个 zip真正值钱的不是 LSTM 本身而是那条从数据准备、建模、回测到参数寻优的流水线。把滚动回测写进你的日常流程比盯着一时的预测准确率有用得多。希望帮到你。本文还有配套的精品资源点击获取
返回列表