ARTICLE DETAIL

资讯详情

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

A股机器学习实战:从数据落库到LSTM与sklearn单票回测全流程

A股机器学习实战:从数据落库到LSTM与sklearn单票回测全流程 简介这份资源面向具备一定Python与机器学习基础的金融量化爱好者、数据科学学习者及高校相关专业学生提供一套完整的A股股票走势预测系统源码帮助理解如何将算法模型落地到真实行情数据中。压缩包共12个文件约2.53MB包含6个ipynb交互式笔记、3个csv行情数据、2个py脚本及1个gitignore配置覆盖数据获取、特征工程、模型训练与回测等环节。内容涉及K线数据入库、小市值结合盈利指标过滤策略、FFT滤波、单层LSTM及sklearn单票回测等实践模块可据此复现从数据清洗、特征提取到模型评估的完整流程并借助均方误差、R²等指标检验预测效果。目前已有307人学习适合作为量化选股与时间序列预测的入门实战参考。1. 从一份 A 股机器学习源码包说起它到底能跑出什么打开这个压缩包第一眼看到的不是一篇论文而是一堆能直接跑的.ipynb和.py文件保存K线数据到数据库.ipynb、newfeature.py、singlelstm.ipynb、singlelstm2.ipynb、sklearn机器学习单票回测.ipynb、fft-filter.ipynb外加600256.csv、002475.csv、xmm.csv三份样本数据和getstockcsv.py。这套东西解决的不是“预测明天涨停”这种玄学问题而是把 A 股日线数据从获取、落库、特征工程、模型训练到单票回测的整条链路串起来让你能亲手验证一个机器学习模型在真实行情上到底是赚是亏。适合已经会点 Python、想拿 A 股数据练手机器学习完整流程的人也适合做课程设计或金融量化入门项目的人。它不承诺收益但能让你把“机器学习预测股票走势”从概念变成一条可复现的流水线。2. 数据管道与特征工程从 CSV 到模型能吃的矩阵2.1 先把数据落进数据库别每次重跑都读 CSV保存K线数据到数据库.ipynb和getstockcsv.py是整条链路的起点。常见做法是先用getstockcsv.py把 A 股日线行情拉成 CSV再用 notebook 写进 SQLite 或 MySQL。为什么强调落库因为后面newfeature.py做特征工程时需要反复按股票代码、日期区间查询直接读 CSV 每次都要全量解析几十只票还能忍上百只就明显拖慢迭代。我一般会建一张daily_kline表字段至少包含code、trade_date、open、high、low、close、volume、amount并在(code, trade_date)上建联合索引。import sqlite3 import pandas as pd # 读取单只股票的 CSV列名按实际文件调整 df pd.read_csv(600256.csv, parse_dates[trade_date]) df[code] 600256 conn sqlite3.connect(ashare.db) # 如果表已存在则追加不存在则创建 df.to_sql(daily_kline, conn, if_existsappend, indexFalse) conn.close()这段代码的逻辑很直白把 CSV 读成 DataFrame补一列股票代码然后整表写入 SQLite。参数上注意if_existsappend第一次跑会自动建表后续重复跑同一只票会插重复数据所以实际用的时候要么先DELETE对应代码的旧记录要么在写入前做一次去重。parse_dates把日期列转成 datetime不然后面按时间切片会报类型错误。数据库文件建议单独放别和 notebook 混在一个目录否则.gitignore里还得额外排除。2.2 newfeature.py 里到底算了哪些特征newfeature.py是这套源码里最值得逐行读的文件。它承担的是把原始 OHLCV 转成模型输入矩阵的任务。常见做法是算收益率、波动率、均线偏离、成交量变化率这几类。比如 5 日收益率、20 日波动率、收盘价相对 10 日均线的偏离度、成交量 5 日变化率。这些特征比直接拿收盘价喂模型更稳因为价格本身是非平稳的直接预测绝对价格容易过拟合。import pandas as pd import numpy as np def build_features(df): df df.sort_values(trade_date).copy() # 日收益率 df[ret_1] df[close].pct_change() # 5 日累计收益率 df[ret_5] df[close].pct_change(5) # 20 日波动率用滚动标准差衡量 df[vol_20] df[ret_1].rolling(20).std() # 收盘价相对 10 日均线偏离 ma10 df[close].rolling(10).mean() df[bias_10] (df[close] - ma10) / ma10 # 成交量 5 日变化率 df[vol_chg_5] df[volume].pct_change(5) # 标签下一日涨跌涨为 1 跌为 0 df[label] (df[close].shift(-1) df[close]).astype(int) return df.dropna() raw pd.read_csv(600256.csv, parse_dates[trade_date]) feat build_features(raw) print(feat[[trade_date, ret_1, vol_20, bias_10, label]].tail())逻辑说明所有特征都基于历史窗口计算shift(-1)生成标签时用的是“下一日收盘价”这意味着训练时不能把最后一行拿去预测因为它没有未来数据。参数上窗口长度 5、10、20 是日线级别的常用值改大改小会直接影响特征平滑程度。dropna()会丢掉前 20 行左右样本少的时候要留意剩余样本量。这里没有做标准化是因为树模型对量纲不敏感但如果后面接 LSTM 或 SVM必须对特征做StandardScaler否则梯度会被大数值特征主导。2.3 样本数据 600256.csv 和 002475.csv 怎么用包里的600256.csv和002475.csv是两只不同风格的票一只偏能源一只偏消费电子。它们的作用不是让你直接得出“哪只票能涨”而是让你对比同一套特征和模型在不同标的上的表现差异。常见做法是先用600256.csv跑通全流程确认代码没有报错再换002475.csv看模型指标是否稳定。如果换一只票准确率就从 60% 掉到 48%说明特征或标签设计对特定行情过拟合需要回头调整窗口参数或增加特征。xmm.csv看命名像是一只小市值票的样本可以拿来测试小市值策略下的表现但样本量通常偏少回测结果只能当参考不能当结论。3. 模型训练与单票回测LSTM 和 sklearn 两条路怎么选3.1 singlelstm.ipynb 与 singlelstm2.ipynb 的差异singlelstm.ipynb和singlelstm2.ipynb是这套源码里两个 LSTM 版本。从命名和常见实现习惯看第一个版本大概率是基础单层 LSTM第二个版本可能加了 Dropout、双向层或者调整了时间步长。LSTM 处理股票序列的优势在于它能记住跨时间步的模式比如连续几天缩量后突然放量。但坑也很明显A 股日线数据噪声极大LSTM 参数量一多就很容易记住训练集的噪声验证集损失不降反升。import numpy as np from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout # 假设 feat 是上一步 build_features 的输出 feature_cols [ret_1, ret_5, vol_20, bias_10, vol_chg_5] X feat[feature_cols].values y feat[label].values # 构造时间步为 10 的序列样本 def make_sequences(X, y, steps10): xs, ys [], [] for i in range(len(X) - steps): xs.append(X[i:isteps]) ys.append(y[isteps]) return np.array(xs), np.array(ys) X_seq, y_seq make_sequences(X, y, steps10) split int(len(X_seq) * 0.8) X_train, X_test X_seq[:split], X_seq[split:] y_train, y_test y_seq[:split], y_seq[split:] model Sequential([ LSTM(32, input_shape(10, len(feature_cols))), Dropout(0.3), Dense(1, activationsigmoid) ]) model.compile(optimizeradam, lossbinary_crossentropy, metrics[accuracy]) model.fit(X_train, y_train, epochs20, batch_size16, validation_split0.2)逻辑说明make_sequences把二维特征矩阵切成三维张量形状是(样本数, 时间步, 特征数)这是 LSTM 的标准输入格式。steps10表示用过去 10 天预测第 11 天涨跌。Dropout(0.3)是防过拟合的关键股票数据上通常设 0.2 到 0.5。validation_split0.2从训练集里再切一部分做验证但注意这不是时间序列验证会引入未来信息泄露严格做法应该用TimeSeriesSplit。epochs20和batch_size16是保守值实际跑的时候要看验证损失曲线如果训练损失降但验证损失升就提前停。3.2 sklearn机器学习单票回测.ipynb 的回测逻辑sklearn机器学习单票回测.ipynb走的是另一条路用 sklearn 里的随机森林或梯度提升树做分类然后按预测信号做单票回测。相比 LSTM树模型训练快、可解释性强适合快速验证特征有没有信号。回测部分通常包含信号生成、持仓计算、收益曲线绘制。from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import TimeSeriesSplit from sklearn.metrics import accuracy_score import pandas as pd feature_cols [ret_1, ret_5, vol_20, bias_10, vol_chg_5] X feat[feature_cols].values y feat[label].values tscv TimeSeriesSplit(n_splits5) accs [] for train_idx, test_idx in tscv.split(X): clf RandomForestClassifier(n_estimators100, max_depth5, random_state42) clf.fit(X[train_idx], y[train_idx]) pred clf.predict(X[test_idx]) accs.append(accuracy_score(y[test_idx], pred)) print(各折准确率:, accs) print(平均准确率:, sum(accs) / len(accs))逻辑说明TimeSeriesSplit保证训练集始终在测试集之前避免用未来数据训练。n_estimators100是随机森林的树数量max_depth5限制树深防过拟合。准确率只是参考股票涨跌分类里 50% 是基线55% 以上才有讨论价值但还要看回测收益曲线是否稳定。如果准确率 58% 但回测亏钱说明模型在上涨样本上预测对了但仓位管理有问题或者交易成本吃掉了利润。回测里常见做法是加千分之一到千分之三的单边手续费不加手续费的收益曲线基本没有参考意义。3.3 fft-filter.ipynb 的滤波思路值不值得用fft-filter.ipynb用傅里叶变换对价格序列做滤波试图分离趋势和噪声。这个思路在信号处理里很成熟但搬到股票上要小心。常见做法是对收盘价做 FFT去掉高频分量再逆变换得到平滑曲线然后用平滑曲线的斜率判断趋势。问题是股票价格不是平稳信号滤波会引入滞后而且去掉的高频里可能恰恰包含短期反转信号。我一般只把它当辅助特征比如把滤波后的趋势斜率作为一个额外输入而不是直接用滤波后的价格做预测。如果非要用窗口长度至少取 60 个交易日以上否则频率分辨率不够滤出来的东西没有统计意义。4. 避坑与排查这套源码跑不起来时先看这几条4.1 数据库写入重复导致样本翻倍现象跑完保存K线数据到数据库.ipynb后再跑一次特征工程发现样本量比预期多了一倍回测收益曲线出现重复日期。原因to_sql用了if_existsappend同一只票被插入了两次。解决写入前先执行DELETE FROM daily_kline WHERE code ?或者把if_exists改成replace但replace会清掉其他票的数据只适合单票调试。更稳的做法是建唯一索引UNIQUE(code, trade_date)插入时用INSERT OR IGNORE。4.2 LSTM 输入形状报错现象singlelstm.ipynb运行到model.fit时报ValueError: Input 0 of layer lstm is incompatible。原因特征矩阵没有做序列切分直接传了二维数组。解决确认make_sequences被调用且X_seq.shape是三维(样本数, 时间步, 特征数)。如果特征列数变了input_shape里的第二个维度也要同步改。另外注意steps不能大于样本量小样本票上把steps从 10 降到 5 试试。4.3 标签泄露导致准确率虚高现象模型在测试集上准确率 90% 以上但回测收益一塌糊涂。原因特征里混入了未来信息比如用了当日收盘价算的指标去预测当日涨跌或者标准化时用了全量数据的均值和方差。解决所有滚动计算只能用shift(1)之后的数据标准化要在训练集上fit再transform测试集。检查newfeature.py里有没有直接对全量 DataFrame 做rolling后又没shift的列。4.4 样本量太少导致交叉验证不稳定现象sklearn机器学习单票回测.ipynb里各折准确率从 40% 跳到 70%波动极大。原因单票日线数据通常只有几百到一千行去掉滚动窗口和标签对齐后剩更少五折之后每折测试集只有几十个样本。解决要么换用多票合并数据增加样本量要么把n_splits降到 3并且接受“单票回测结果只能定性参考”这个事实。想认真做至少拿 50 只票以上做横截面验证。4.5 手续费和滑点没算进去现象回测曲线一路向上实盘一跑就亏。原因回测里按收盘价成交且没扣手续费。解决在回测逻辑里加cost 0.001的单边成本买入和卖出各扣一次。如果策略换手率高成本影响会非常大。常见做法是统计每日换手率乘以成本再从收益里减掉。这一步不做回测结果就是自欺欺人。5. 把单票回测扩展成多票对比一个可复用的验证习惯单票回测最大的问题是运气成分。600256 上表现好不代表模型有泛化能力。我一般会写一个循环把包里的600256.csv、002475.csv、xmm.csv都跑一遍同一套特征和模型对比准确率和回测收益的离散程度。如果三只票里两只亏一只赚那这个模型基本不能信如果三只票收益方向一致且回撤可控才值得进一步调参。import glob import pandas as pd from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import TimeSeriesSplit from sklearn.metrics import accuracy_score def backtest_one(csv_path): raw pd.read_csv(csv_path, parse_dates[trade_date]) feat build_features(raw) feature_cols [ret_1, ret_5, vol_20, bias_10, vol_chg_5] X feat[feature_cols].values y feat[label].values tscv TimeSeriesSplit(n_splits3) accs [] for train_idx, test_idx in tscv.split(X): clf RandomForestClassifier(n_estimators100, max_depth5, random_state42) clf.fit(X[train_idx], y[train_idx]) accs.append(accuracy_score(y[test_idx], clf.predict(X[test_idx]))) return sum(accs) / len(accs) for path in glob.glob(*.csv): print(path, round(backtest_one(path), 4))这段代码把单票回测封装成函数然后遍历目录下所有 CSV。参数上n_splits3是因为单票样本少折数多了每折测试集太小。输出的是平均准确率你可以再加一段计算回测收益的代码把每日信号转成持仓乘以次日收益率累乘得到净值曲线。对比不同票的净值曲线比看单一准确率更有说服力。还有一个习惯每次改特征或模型参数都把结果记到一个 CSV 里字段包括日期、特征组合、模型类型、平均准确率、回测收益、最大回撤。跑上十几组之后你就能看出哪些特征真正有用哪些参数只是碰巧。这套源码包里的newfeature.py和几个 notebook 给了你起点但真正让模型可用的是这种反复对比、记录、淘汰的笨功夫。从那以后我每次拿到新的行情数据都强制先跑一遍多票对比再决定要不要继续调模型。希望帮到你。本文还有配套的精品资源点击获取
返回列表