ARTICLE DETAIL

资讯详情

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

sklearn股票预测实战:从特征工程到滚动回测的工程路径

sklearn股票预测实战:从特征工程到滚动回测的工程路径 简介这份资源是一套基于sklearn实现股票预测的机器学习项目源码面向计算机、人工智能、金融商贸等方向的在校学生与自学者可用于毕业设计、课程设计、作业或项目初期立项演示帮助读者理解如何用经典机器学习模型处理金融时序数据。压缩包共15个文件约24.25MB以4个Python脚本为核心配合1个CSV数据集、1个TXT依赖说明及若干XML、MD等配置与说明文件结构清晰便于直接运行与二次修改。项目围绕随机森林等模型展开包含数据获取、特征处理、模型训练与预测等环节代码均经过测试运行成功答辩评审平均分达到96分。目前已有287人学习下载适合希望快速上手sklearn金融预测实战、并在此基础上扩展功能的读者参考学习。1. 用 sklearn 做股票预测一条能跑通但别指望暴富的工程路径很多人第一次听到「基于 sklearn 模型的机器学习股票预测」脑子里浮现的是训练一个模型然后躺着数钱。我当年也这么想过直到把回测曲线和实盘曲线摆在一起才发现中间隔着一整个太平洋。这个方向真正能交付的东西不是一台印钞机而是一套把行情数据变成可验证信号的工程流程用 pandas 清洗量价数据、用 sklearn 构建回归或分类模型、用时间序列切分做无泄漏回测、用一套固定的评估口径判断这个信号到底有没有边际价值。它适合两类人一类是想把机器学习入门知识落到真实数据上的工程师sklearn 安装、机器学习线性回归实验这些入门动作你已经熟了缺的是一个不那么玩具的项目另一类是量化方向的从业者想快速验证某个因子或特征组合是否值得投入更多算力。不适合指望它直接产出交易策略的人——预测涨跌和赚钱之间还差着仓位管理、交易成本和滑点三座大山。下面我把这条路径从数据到评估完整拆一遍参数怎么设、哪里会翻车都讲清楚。2. 数据与标签股票预测的输入到底长什么样2.1 为什么不能直接用收盘价当标签新手最容易犯的错是把「明天的收盘价」直接当 y然后拿今天的收盘价当特征去回归。这样训练出来的模型 R² 能到 0.99看起来完美实际上模型只学会了一件事明天的价格约等于今天的价格。这就是典型的标签泄漏式自欺欺人。股票预测里更合理的做法是预测收益率而不是价格。收益率有两种常见定义一种是简单收益率(close[t1] - close[t]) / close[t]另一种是对数收益率log(close[t1] / close[t])。对数收益率在跨期累加和统计分布上更友好我一般优先用它。如果要做分类任务就把收益率二值化大于 0 记 1否则记 0或者设一个阈值过滤掉微小波动比如只有涨跌超过 0.5% 才给标签其余样本丢弃。这一步能显著降低噪声标签的比例。标签的预测周期也要想清楚。预测未来 1 天噪声极大信噪比很低预测未来 5 天或 10 天的累计收益信号更稳但样本重叠更严重。常见做法是先用 5 日累计对数收益率做标签配合后面要讲的 purge 切分来缓解重叠问题。2.2 特征工程从量价数据里榨出可用信号原始数据通常就是 OHLCV 五列开盘、最高、最低、收盘、成交量。直接把这五列丢给模型效果很差因为它们是绝对水平值不同股票、不同时期不可比。必须做特征变换构造出有经济含义的相对量。我常用的特征分四类。第一类是动量类过去 N 日的累计收益率、收盘价相对 N 日均线的偏离度。第二类是波动类过去 N 日收益率的标准差、最高最低价之差占收盘价的比例振幅。第三类是量能类成交量相对 N 日均量的比值、量价相关性。第四类是均线类短均线与长均线之差除以长均线也就是 MACD 那种思路的简化版。import numpy as np import pandas as pd def build_features(df, windows(5, 10, 20)): df 需包含列: open, high, low, close, volume索引为日期 返回带特征和标签的 DataFrame out pd.DataFrame(indexdf.index) ret np.log(df[close] / df[close].shift(1)) out[ret_1] ret for w in windows: # 动量过去 w 日累计对数收益 out[fmom_{w}] ret.rolling(w).sum() # 波动过去 w 日收益标准差 out[fvol_{w}] ret.rolling(w).std() # 均线偏离收盘价相对 w 日均线 ma df[close].rolling(w).mean() out[fma_dev_{w}] (df[close] - ma) / ma # 量能比当日量相对 w 日均量 out[fvol_ratio_{w}] df[volume] / df[volume].rolling(w).mean() # 振幅 out[amplitude] (df[high] - df[low]) / df[close] # 标签未来 5 日累计对数收益 out[label] ret.shift(-1).rolling(5).sum().shift(-4) return out.dropna()这段代码里windows控制回看窗口5/10/20 是日线级别的常用组合对应一周、两周、一个月。mom捕捉趋势vol捕捉风险ma_dev捕捉超买超卖vol_ratio捕捉资金异动。标签用ret.shift(-1).rolling(5).sum().shift(-4)构造未来 5 日累计收益注意两次 shift 的方向写反了就是未来函数。dropna()会砍掉前 20 行和后 5 行这是正常损耗。提示所有 rolling 和 shift 操作都要在脑子里过一遍「这个时点用到的数据在真实交易时是否已经可得」。任何用到未来数据的特征回测再漂亮都是假的。3. 模型选型与训练sklearn 里哪些模型真的能用3.1 从线性回归到梯度提升的取舍sklearn 里能拿来做股票预测的模型不少但适合的不多。线性回归和 Ridge 是最稳的基线优点是训练快、可解释、不容易过拟合缺点是只能捕捉线性关系。股票数据里非线性关系确实存在但线性模型作为 baseline 必须先跑一遍如果线性模型都跑不出正收益复杂模型大概率是在拟合噪声。随机森林和梯度提升树GradientBoostingRegressor、HistGradientBoostingRegressor是主力。它们能自动捕捉特征交互对缺失值和异常值相对鲁棒。HistGradientBoostingRegressor 在大样本上比传统 GBDT 快很多特征几百个、样本几十万行时优势明显。支持向量机在股票预测里我不推荐核函数调参成本高样本量大时训练慢收益还不一定比树模型好。选型顺序我一般是这样Ridge 打底随机森林看特征重要性HistGradientBoosting 冲效果最后用集成或简单平均把几个模型合起来。不要一上来就上深度学习sklearn 这套工具在中小规模结构化数据上完全够用把特征和验证做扎实比换模型重要得多。3.2 时间序列切分别用 train_test_split这是股票预测里翻车率最高的地方。train_test_split默认随机打乱用在时间序列上就是灾难训练集里混进了未来样本模型在回测时表现优异实盘一塌糊涂。正确做法是按时间顺序切分训练集在前验证集在后。from sklearn.ensemble import HistGradientBoostingRegressor from sklearn.preprocessing import StandardScaler from sklearn.metrics import mean_squared_error, r2_score import numpy as np def time_split(features, train_ratio0.7): n len(features) split int(n * train_ratio) train features.iloc[:split] test features.iloc[split:] return train, test def train_model(features): train, test time_split(features) feat_cols [c for c in features.columns if c ! label] scaler StandardScaler() X_train scaler.fit_transform(train[feat_cols]) X_test scaler.transform(test[feat_cols]) y_train train[label].values y_test test[label].values model HistGradientBoostingRegressor( max_iter300, learning_rate0.05, max_depth4, min_samples_leaf50, l2_regularization1.0, random_state42 ) model.fit(X_train, y_train) pred model.predict(X_test) print(MSE:, mean_squared_error(y_test, pred)) print(R2 :, r2_score(y_test, pred)) # 方向准确率预测涨跌方向对不对 hit np.mean((pred 0) (y_test 0)) print(方向准确率:, hit) return model, scalermax_iter是提升轮数300 轮配合 0.05 学习率是常见起点轮数太多容易过拟合。max_depth4限制单棵树深度股票数据噪声大树太深必然记住噪声。min_samples_leaf50保证每个叶子节点有足够样本这个值在日线数据上我一般设 30 到 100。l2_regularization1.0是叶节点权重的 L2 惩罚能压住极端预测值。评估指标里 MSE 和 R² 看回归拟合质量但真正要盯的是方向准确率。股票预测里 R² 能到 0.01 就算有信号了别拿房价预测那种 0.8 的标准来要求。方向准确率能稳定在 52% 到 55% 之间配合合理的盈亏比就已经有研究价值。注意StandardScaler 必须用训练集的均值和方差去 transform 测试集不能对全量数据 fit。上面代码里先 fit_transform 训练集、再 transform 测试集顺序不能反。4. 回测与评估怎么判断模型是真有用还是过拟合4.1 滚动前向验证比单次切分可靠单次时间切分只得到一个测试结果运气成分大。更稳的做法是滚动前向验证walk-forward validation用前一段训练、后一段测试然后窗口整体后移重复多次把每次的测试结果汇总。这样能看到模型在不同市场阶段的稳定性。def walk_forward(features, n_splits5, train_ratio0.6): feat_cols [c for c in features.columns if c ! label] n len(features) step (n - int(n * train_ratio)) // n_splits results [] for i in range(n_splits): start i * step split start int(n * train_ratio) end min(split step, n) if end split: break train features.iloc[start:split] test features.iloc[split:end] model HistGradientBoostingRegressor( max_iter200, learning_rate0.05, max_depth4, min_samples_leaf50, l2_regularization1.0, random_state42 ) model.fit(train[feat_cols], train[label]) pred model.predict(test[feat_cols]) hit np.mean((pred 0) (test[label].values 0)) results.append(hit) print(各折方向准确率:, [round(r, 4) for r in results]) print(均值:, round(np.mean(results), 4), 标准差:, round(np.std(results), 4)) return resultsn_splits控制折数5 折能看出大概的稳定性。重点看两个数均值和标准差。均值高但标准差大说明模型在某些市场阶段失效实盘风险高。均值一般但标准差小反而更值得继续研究。如果各折准确率在 0.45 到 0.55 之间乱跳基本可以判定模型没学到东西。4.2 交易成本与滑点回测里最容易被忽略的杀手很多回测报告收益率惊人一加上交易成本就归零甚至转负。股票交易有佣金、印花税、过户费还有滑点。日频策略如果每天都调仓成本能吃掉大部分利润。评估时必须把成本算进去。一个粗略但有效的做法每次换手按单边 0.1% 到 0.2% 扣减收益。如果你的策略年化换手率是 50 倍光成本就是 5% 到 10%很多所谓的高频信号根本扛不住。我一般会在回测里加一个cost_per_turnover参数把预测信号转成仓位变化再按换手扣成本看净值曲线还剩多少。提示回测里还有一个隐蔽的坑是幸存者偏差。如果你用的股票池是「今天还在交易的股票」那些退市的、被并购的都没进来回测收益会被系统性高估。严谨做法是用历史成分股但数据获取成本高至少要在报告里说明这个局限。5. 避坑与排查股票预测里最常见的五个翻车点5.1 现象回测 R² 很高但实盘完全无效原因标签泄漏。最常见的是特征里混入了未来信息比如用了当日收盘后才公布的财务数据或者 rolling 窗口的 shift 方向写反。另一个来源是标准化时对全量数据 fit测试集的统计量泄漏进了训练。解决逐个特征检查时间戳确认每个值在预测时点已经可得。标准化严格用训练集 fit。写一个单元测试把数据整体后移一天如果模型效果断崖式下跌说明有泄漏。5.2 现象模型在训练集上表现完美测试集一塌糊涂原因过拟合。树太深、迭代轮数太多、特征数量远超样本量都会导致模型记住噪声。股票数据信噪比极低过拟合几乎是默认状态。解决限制max_depth在 3 到 5min_samples_leaf不低于 30加 L2 正则。用早停early_stoppingTrue让验证集损失不再下降时停止。特征数量控制在样本量的十分之一以下。5.3 现象方向准确率在 50% 上下随机波动原因特征没有预测力或者标签噪声太大。日频涨跌本身接近随机如果特征只是简单的价格变换模型学不到东西很正常。解决换预测周期从 1 日改成 5 日或 10 日。引入外部特征比如行业指数收益、市场整体波动率、资金流向。如果加了特征还是 50%说明这个标的在这个周期上就是不可预测的换标的比调模型有用。5.4 现象训练时报错Input contains NaN或inf原因收益率计算里出现除零或者 rolling 窗口内数据不足产生 NaN。对数收益率在价格连续停牌时也可能出问题。解决在build_features末尾统一replace([np.inf, -np.inf], np.nan).dropna()。检查原始数据里有没有价格为 0 或负数的异常行。停牌期间的数据要么前向填充要么直接剔除。5.5 现象不同随机种子下结果差异巨大原因模型对初始化敏感或者样本量太小。股票数据如果只用了两三年日线样本也就几百行随机性影响很大。解决固定random_state跑多个种子取平均。扩大样本用多只股票的面板数据或者拉长历史区间。如果多种子下方向准确率标准差超过 0.03说明结论不可靠别急着下判断。6. 把预测信号变成可验证的研究结论走到这一步你手里应该有一个能跑通的流程和一组滚动验证结果。但「模型能预测」和「这个方向值得投入」之间还差最后一步把预测信号转成可比较的研究结论。我一般会做三件事。第一做特征重要性分析。HistGradientBoostingRegressor没有直接的feature_importances_但可以用permutation_importance来算。把每个特征打乱后看误差上升多少上升越多说明越重要。如果重要性全集中在某一两个特征上说明模型很脆弱换个市场阶段可能就失效。from sklearn.inspection import permutation_importance def check_importance(model, X_test, y_test, feat_cols): result permutation_importance( model, X_test, y_test, n_repeats10, random_state42, scoringneg_mean_squared_error ) imp pd.Series(result.importances_mean, indexfeat_cols) print(imp.sort_values(ascendingFalse).head(10))n_repeats10表示每个特征重复打乱 10 次取平均次数越多越稳但越慢。看排名前 10 的特征是否符合经济直觉如果排第一的是某个莫名其妙的组合要警惕。第二做分层评估。把测试集按市场状态分组上涨期、下跌期、震荡期分别看方向准确率。一个只在上涨期有效的模型本质上是 beta 暴露不是 alpha。真正有用的信号应该在下跌期也有一定表现哪怕准确率低一些。第三和基线对比。最简单的基线是「永远预测上涨」在牛市里这个基线准确率能到 55% 以上。你的模型必须显著超过这个基线否则没有存在价值。另一个基线是「用过去 5 日收益预测未来 5 日收益」如果复杂模型跑不赢这个简单规则说明特征工程没做到位。评估维度及格线值得继续研究说明方向准确率 51% 53%滚动验证均值非单次多种子标准差 0.03 0.02越小越稳扣除成本后收益 0 年化 5%按单边 0.15% 估算特征重要性集中度前 3 特征 70%前 3 特征 50%越分散越鲁棒这张表是我自己用的筛选口径不是行业标准但能帮你快速判断一个方向要不要继续投入。达不到及格线的先回去查数据泄漏和过拟合别急着换模型。最后说个我踩过的坑早期我花了两周调模型参数把方向准确率从 51% 调到 53%兴奋得不行。后来发现那 2 个百分点完全来自数据里一个不起眼的复权处理差异换个数据源就没了。从那以后我养成了一个习惯任何效果提升先问一句「这个提升在另一份数据上还在吗」。股票预测这个方向验证比建模重要十倍把评估做扎实比追新模型有用得多。希望帮到你。本文还有配套的精品资源点击获取
返回列表