A股机器学习预测源码包实战:从数据落库到LSTM回测的完整流水线 简介这份资源面向对量化投资与机器学习感兴趣的开发者、金融数据分析学习者提供一套完整的A股股票走势预测系统源码帮助理解如何用算法从历史行情中挖掘价格变动规律。压缩包共12个文件约2.53MB包含6个ipynb交互式笔记、3个csv行情数据、2个py脚本及1个gitignore配置覆盖数据获取、特征工程、模型训练与回测等环节。笔记中涉及K线数据入库、小市值结合盈利指标过滤策略、FFT滤波、单层与改进LSTM建模以及sklearn单票回测等实践内容脚本则负责行情数据抓取与特征生成csv文件提供可直接运行的样本数据。目前已有307人学习下载适合希望从零搭建预测流程、对照代码复现实验并理解金融时序建模思路的读者参考。1. 从一份 A 股预测源码包说起它到底能不能跑出可用信号打开这份压缩包第一眼看到的不是一篇论文而是一堆散落的.ipynb和.py保存K线数据到数据库.ipynb、newfeature.py、验证小市值结合盈利指标过滤策略.ipynb、fft-filter.ipynb、singlelstm.ipynb、singlelstm2.ipynb、sklearn机器学习单票回测.ipynb外加600256.csv、002475.csv、xmm.csv三份样本数据和getstockcsv.py。这不是一个封装好的“一键预测涨停”软件而是一套从数据落库、特征工程、频域滤波到 LSTM 与 sklearn 回测的完整实验流水线。它真正能解决的问题是让你在一台普通笔记本上用真实 A 股日线数据把“机器学习算法预测股票走势”这件事从数据到回测跑通一遍而不是停留在调包跑个train_test_split就宣称能预测。适合谁适合已经会 Python、懂一点 pandas但没把金融时序的坑踩全的从业者也适合想拿一套可改可扩的骨架去验证自己因子想法的人。如果你指望它直接给出明天买哪只那这份源码会让你失望——它的价值在流程不在信号。2. 数据管道与特征工程从 K 线落库到 newfeature.py 的因子拼装2.1 保存K线数据到数据库为什么先落库再谈模型很多人拿到 CSV 就直接pd.read_csv然后喂模型跑得通但换一只票、换一个时间段就乱套。这份源码里第一个 notebook 叫保存K线数据到数据库.ipynb思路是对的先把原始行情固化到本地数据库后续所有实验都从同一份数据源取避免“这次用前复权、下次用后复权”导致的玄学差异。常见做法是用 SQLite零配置、单文件、够用。下面是我一般会写的落库骨架和源码里的思路一致只是补上了字段类型和索引。import sqlite3 import pandas as pd # 假设 getstockcsv.py 已经拉到了日线数据列包含 date, open, high, low, close, volume df pd.read_csv(600256.csv, parse_dates[date]) conn sqlite3.connect(astock.db) # 建表时把 date 和 code 做成联合主键防止重复插入 conn.execute( CREATE TABLE IF NOT EXISTS daily_kline ( code TEXT NOT NULL, date TEXT NOT NULL, open REAL, high REAL, low REAL, close REAL, volume REAL, PRIMARY KEY (code, date) ) ) # 用 INSERT OR REPLACE重复跑不会炸 df[code] 600256 df.to_sql(daily_kline, conn, if_existsappend, indexFalse, methodlambda table, conn, keys, data_iter: conn.executemany( fINSERT OR REPLACE INTO {table.name} ({,.join(keys)}) fVALUES ({,.join([?]*len(keys))}), data_iter)) conn.commit()逻辑说明parse_dates保证日期是时间类型而不是字符串否则后面按时间切片会得到错误结果联合主键(code, date)是血泪经验少了它同一只票重复拉取就会在回测里出现重复行收益率直接翻倍。参数上if_existsappend配合INSERT OR REPLACE才能做到幂等否则第二次跑就报主键冲突。getstockcsv.py负责从数据源拉取并生成 CSV具体数据源各人有各人的渠道源码里没有绑定某一家你替换成自己可用的接口即可。2.2 newfeature.py 里的特征拼装别把未来函数混进去newfeature.py是整份源码里最值得逐行读的文件因为它决定了模型能看到什么。金融时序特征工程最大的坑是未来函数——用当天收盘价算出来的指标如果被当成“预测当天”的输入回测收益会好到不真实。常见做法是所有特征在计算后统一shift(1)让第 t 行只包含 t-1 及之前的信息。下面这段是我在newfeature.py基础上整理出的最小可用特征集覆盖动量、波动和均线偏离。import pandas as pd import numpy as np def build_features(df): df df.sort_values(date).reset_index(dropTrue) # 收益率类过去 1/5/10 日收益 for n in [1, 5, 10]: df[fret_{n}] df[close].pct_change(n) # 波动类过去 5/20 日收益率标准差 df[vol_5] df[ret_1].rolling(5).std() df[vol_20] df[ret_1].rolling(20).std() # 均线偏离收盘价相对 5/20 日均线的偏离度 df[ma_5] df[close].rolling(5).mean() df[ma_20] df[close].rolling(20).mean() df[bias_5] (df[close] - df[ma_5]) / df[ma_5] df[bias_20] (df[close] - df[ma_20]) / df[ma_20] # 标签下一日涨跌1 为涨0 为跌 df[label] (df[close].shift(-1) df[close]).astype(int) # 关键一步所有特征整体下移一行杜绝未来函数 feature_cols [c for c in df.columns if c not in [date, label, code]] df[feature_cols] df[feature_cols].shift(1) return df.dropna().reset_index(dropTrue)逻辑说明pct_change(n)算的是 n 日累计收益rolling().std()算滚动波动bias衡量价格偏离均线的程度这三个维度基本覆盖了短线走势的主要信息。参数上窗口 5 和 20 是日线级别的常用值想更敏感就改小想更平滑就改大但别低于 3否则噪声占主导。最后整体shift(1)是保命操作少了它你的回测曲线会漂亮到让你怀疑人生实盘一上就翻车。fft-filter.ipynb则是在这基础上做频域滤波用 FFT 把价格序列的低频趋势和高频噪声分离只保留趋势分量作为额外特征适合想减少震荡假信号的人。3. 模型训练与回测singlelstm 与 sklearn 单票回测怎么落地3.1 singlelstm.ipynb 的序列构造与训练参数singlelstm.ipynb和singlelstm2.ipynb是两份 LSTM 实验区别通常在序列长度、层数或是否加入注意力。LSTM 用于股票走势预测的核心问题是它需要三维输入(样本数, 时间步, 特征数)而很多人直接把二维 DataFrame 塞进去报错后才开始翻文档。下面是我从源码里提炼出的序列构造和训练骨架用 Keras 写参数都是可改的。import numpy as np from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout def make_sequences(features, labels, time_steps20): X, y [], [] for i in range(time_steps, len(features)): X.append(features[i-time_steps:i]) y.append(labels[i]) return np.array(X), np.array(y) # features 是 build_features 输出的特征矩阵labels 是 label 列 X, y make_sequences(features.values, labels.values, time_steps20) split int(len(X) * 0.8) X_train, X_test X[:split], X[split:] y_train, y_test y[:split], y[split:] model Sequential([ LSTM(64, input_shape(X.shape[1], X.shape[2]), return_sequencesFalse), Dropout(0.3), Dense(1, activationsigmoid) ]) model.compile(optimizeradam, lossbinary_crossentropy, metrics[accuracy]) model.fit(X_train, y_train, epochs30, batch_size32, validation_split0.1, verbose1)逻辑说明time_steps20表示用过去 20 个交易日的数据预测下一天这个值在日线级别比较常见太短抓不到趋势太长则样本数骤减。LSTM(64)是隐藏单元数单票数据量通常只有几千行64 已经够用再大容易过拟合。Dropout(0.3)是正则化金融数据信噪比低不加 dropout 的 LSTM 几乎必然过拟合。validation_split0.1从训练集尾部切验证集注意这里没有打乱因为时序数据打乱会引入未来信息。singlelstm2.ipynb大概率是在这个基础上调了层数或加了第二层 LSTM你可以两份对比着看理解参数变化对验证集准确率的影响。3.2 sklearn机器学习单票回测.ipynb回测框架比模型更重要sklearn机器学习单票回测.ipynb用的是 sklearn 那套——随机森林、梯度提升之类配合单票回测。很多人模型训完只看 accuracy但股票预测里 accuracy 高不代表赚钱因为涨跌样本不平衡而且没有考虑交易成本。回测的核心是按时间顺序逐日推进用当天之前的数据训练预测下一天然后模拟买卖。下面是一个最小回测循环和源码思路一致。from sklearn.ensemble import RandomForestClassifier import numpy as np def backtest(features, labels, dates, train_window500): capital 1.0 position 0 equity_curve [] for i in range(train_window, len(features)): # 用过去 train_window 天训练预测第 i 天 X_train features[i-train_window:i] y_train labels[i-train_window:i] model RandomForestClassifier(n_estimators100, max_depth5, random_state42) model.fit(X_train, y_train) pred model.predict(features[i:i1])[0] # 简单策略预测涨则满仓预测跌则空仓 if pred 1 and position 0: position capital / dates[i] # 简化实际用收盘价 capital 0 elif pred 0 and position 0: capital position * dates[i] position 0 equity_curve.append(capital position * dates[i]) return equity_curve逻辑说明train_window500是滚动窗口约两年交易日保证模型只看到历史。RandomForestClassifier的max_depth5是刻意压低的金融数据上树太深必然过拟合n_estimators100是精度和速度的折中。回测里用dates[i]代替真实收盘价是简化写法实际应该传入价格序列。这个循环没有扣手续费和滑点真实回测要加上否则收益会被高估。验证小市值结合盈利指标过滤策略.ipynb则是在选股层面做过滤先按市值和盈利指标筛出股票池再在池子里跑预测这是把“机器学习算法”和“基本面过滤”结合起来的常见做法。4. 避坑与排查这份源码跑不起来时先看这几条4.1 数据对齐翻车日期错位导致回测虚高现象回测曲线平滑向上年化收益高得离谱但实盘完全不是那么回事。原因特征和标签没有对齐比如用当天收盘价算了 bias又用当天收盘价算 label模型实际上看到了未来。解决在build_features最后统一shift(1)并且回测循环里严格用features[i]预测labels[i]不要用labels[i-1]或任何未来行。检查方法很简单把特征矩阵和标签矩阵按行打印出来看第 i 行的特征是否只包含 i-1 及之前的信息。4.2 LSTM 输入维度报错三维张量不是可选项现象ValueError: Input 0 of layer lstm is incompatible with the layer: expected ndim3, found ndim2。原因直接把二维(样本数, 特征数)喂给了 LSTM而 LSTM 要求(样本数, 时间步, 特征数)。解决用make_sequences函数把数据切成滑动窗口time_steps设为 10 到 30 之间的值。注意切完之后样本数会减少time_steps行别再用原始长度去对标签。4.3 数据库重复插入主键缺失让同一根 K 线出现两次现象回测时发现某一天收益率异常检查数据发现同一日期有两行。原因建表时没设联合主键或者用了if_existsappend但没做去重。解决建表时加PRIMARY KEY (code, date)插入时用INSERT OR REPLACE。如果已经脏了用DELETE FROM daily_kline WHERE rowid NOT IN (SELECT MIN(rowid) FROM daily_kline GROUP BY code, date)清一遍。4.4 过拟合陷阱验证集准确率 90% 但回测亏钱现象LSTM 或随机森林在验证集上 accuracy 很高一回测就亏。原因金融数据信噪比极低模型记住了噪声或者验证集和训练集有重叠。解决降低模型复杂度LSTM 隐藏单元降到 32随机森林max_depth降到 3 到 5用滚动窗口回测代替单次 train/test split加入交易成本。如果准确率还在 70% 以上先怀疑数据泄漏而不是高兴。4.5 复权处理不一致前复权与后复权混用现象同一只票在不同 notebook 里跑出的价格不一样特征对不上。原因getstockcsv.py拉数据时复权方式不统一有的用前复权有的用后复权甚至不复权。解决在落库阶段就固定一种复权方式通常用前复权因为回测时当前价格是真实的。在getstockcsv.py里显式指定复权参数并在数据库里加一列adjust_type标记避免后续混用。5. 进阶技巧用 FFT 滤波和滚动回测把信号稳定性提上去fft-filter.ipynb是这份源码里容易被忽略但很有用的一环。股票价格序列可以看成低频趋势加高频噪声的叠加FFT 的作用是把序列变换到频域砍掉高频部分再逆变换回来得到一条平滑的趋势线。这条趋势线作为特征比原始价格更稳定能减少模型被短期噪声带偏的概率。具体做法是对收盘价序列做np.fft.fft把频率高于某个阈值的分量置零再np.fft.ifft取实部。阈值怎么定我一般保留前 10% 到 20% 的低频分量对应到日线级别就是周期在 20 到 50 个交易日以上的趋势。你可以把滤波后的序列和原始序列画在一起肉眼确认它没有滞后太多——滞后是 FFT 滤波的固有代价砍得越狠滞后越明显。另一个进阶点是滚动回测的窗口选择。sklearn机器学习单票回测.ipynb里用的是固定窗口 500 天但市场风格会切换固定窗口在风格突变时会失效。我一般会做两组对比一组固定 500 天一组用 250 天和 750 天双窗口看哪组在最近一年的表现更稳。注意这里说的“表现更稳”不是指收益更高而是指最大回撤更小、收益曲线更平滑。下面这个表格是我在几只样本票上跑出来的经验值供你调参时参考。参数常用值偏保守偏激进说明LSTM time_steps203010越长越平滑样本越少LSTM 隐藏单元6432128单票数据量小不建议超 128Dropout0.30.50.2金融数据信噪比低偏高更稳随机森林 max_depth538超过 8 几乎必然过拟合滚动窗口500750250窗口越短越敏感越长越稳FFT 保留低频比例15%10%20%越低越平滑滞后越大最后说一个验证方法把singlelstm.ipynb和sklearn机器学习单票回测.ipynb在同一只票、同一时间段上跑出来的收益曲线叠在一起。如果两条曲线相关性很高说明模型捕捉到的是同一类信号可以进一步做集成如果一条赚一条亏先别急着调参回去检查两份代码的特征工程是否一致——十有八九是某个shift漏了或者复权方式不同。从那以后我每次改完特征工程都强制走一遍“打印前 5 行特征和标签”的检查确认没有未来信息混进去再开始训练。希望帮到你。本文还有配套的精品资源点击获取