尧图精选

机器学习预测股票价格趋势:Python仿真全流程与随机森林实战

🕒 发布时间:2026/10/1 19:08:27 📁 来源:尧图网络
简介一套面向毕业设计、期末大作业与课程设计场景的机器学习股价预测Python仿真项目完整提供从数据解析、特征数据集构建、LSTM模型定义到训练评估的端到端实现。压缩包共13个文件其中5个Python脚本为程序主体涵盖数据读取、数据预处理、模型配置、训练与效果评估等模块另含csv格式的原始行情数据、pkl格式的已训练模型、png可视化结果图表及md格式说明文档数据、代码、结果与文档齐备。资源仅356KB部署简单代码内附详细注释能够降低新手理解门槛。该项目源自个人手打高分作品据称导师认可度较高适合需要参考完整项目结构、学习时间序列预测建模流程的学生直接使用。目前已有303人学习下载。1. 机器学习预测股票价格趋势为什么先预测方向而不是预测价格第一次拿到“基于机器学习实现预测股票价格趋势的Python仿真”这类题目的人多半会先去找一份现成的股票价格预测代码然后发现两个极端要么跑出来分数虚高换一段行情就漏气要么整个工程只有训练和预测没有说明文档和数据答辩时根本讲不清楚自己在做什么。这篇笔记想把这套方案拆开——数据从哪来、怎么洗、怎么造特征、用什么模型、简单到什么程度还能出活、哪些坑会让人辛辛苦苦做出一个自欺欺人的结果。适合正在做课程设计、毕业设计或准备量化比赛的新手老手可以直接看第4章和第5章那里是我调这类仿真时翻过车以后才总结出来的边界。2. 用Python做股票趋势预测前先把数据从CSV处理成样本2.1 数据源选型自带CSV、Tushare还是AkShare预测股票价格趋势的前提是把行情数据读成一张能喂进机器学习模型的表。常见做法有三个直接读题目自带的数据文件、用Tushare按token拉数据、用AkShare免token抓数据。对课程设计和仿真练习来说我的建议顺序是自带CSV最优先其次是AkShare最后才考虑Tushare。原因很简单自带数据已经把格式定好了你不需要在反爬和接口参数上浪费时间AkShare不用注册就能拉数据适合临时补数据Tushare虽然字段规范且稳定但要先去官网注册拿token而且它有积分限制某些接口新账号调用不了。不管数据从哪个来源来第一步都是先看数据长什么样。拿一份常见的日线CSV举例它通常至少包含日期、开盘价、最高价、最低价、收盘价和成交量这六列。先把文件读进来确认列名和类型再做后续清洗。import pandas as pd # 读取日线行情数据请把路径换成自己手头的文件 df pd.read_csv(stock_daily.csv, encodinggbk) print(df.head()) # 先看前几行确认列名和数值是不是正常 print(df.dtypes) # 检查数据类型日期列必须是 datetime价格列必须是数值 print(df.isnull().sum()) # 统计缺失值决定后续怎么处理代码的逻辑分三步head看数据长相、dtypes看类型、isnull看缺失。很多新手拿到CSV直接就开始算特征结果日期列是字符串、价格列里面混着逗号或者空行后面全部报错。encoding参数也要留意国产金融数据CSV常常用GBK编码直接read_csv读会乱码这时把encoding改成gbk或gb18030。2.2 时间对齐与缺失值处理停牌日和字符串日期怎么洗行情数据的清洗有两个固定动作把日期规范化以及把缺失值处理干净。日期列如果不是datetime类型后面所有按时间切片、对齐合并的操作都会出问题。规范的写法是先转类型、再排序、再设索引。这三步顺手做完后面按时间切片才不会乱套。缺失值在股票数据里很常见停牌日没有交易、某些指标数据上线晚、数据供应商对个别股票的天数不足。处理缺失值没有标准答案我的习惯是区分对待——如果是构造特征时产生的缺失比如滞后第10天收益率前面9天天然没有直接保留NaN模型能处理如果是因为停牌导致的整行缺失用dropna删掉因为你无法用一行不存在的行情去预测未来。df[date] pd.to_datetime(df[date]) # 字符串转日期类型 df df.sort_values(date).reset_index(dropTrue) # 按日期升序排好 df df.drop_duplicates(subsetdate) # 同一个交易日只留一条 df df.dropna(subset[close, volume]) # 核心行情缺失的直接删除这一步看起来不起眼但它决定了后面所有特征计算的准确性。drop_duplicates是针对同一个日期出现多行记录的情况数据源拼接时常出这种问题dropna只针对核心价格和成交量字段不是对全表删空因为后面构造的滞后特征本来就会有NaN。如果你把全表dropna样本量可能少掉一半得不偿失。2.3 把行情变成特征滞后期、涨跌幅、波动率避开标签泄露预测股票价格趋势本质上是把行情序列转换成“特征-标签”对。特征是用历史已知信息构造的数值表标签是你想预测的未来结果。最常见的做法是预测未来N天的涨跌方向也就是分类问题。为什么不直接预测价格因为价格序列是非平稳的直接对价格做回归很容易出现“预测值永远等于最近收盘价”这种没有任何交易价值的模型而预测方向把问题简化成“涨/跌/平”模型学的是规律不是死记价格数字。构造特征要严格遵守一条铁律特征里只能包含T时刻及T时刻以前的信息标签必须是T时刻以后的信息。稍微松懈一点就会造成未来函数泄露——也就是你的模型在训练时偷看到了答案。具体实现上最核心的手段就是shift。# 基础特征过去 N 天的收益率、振幅、成交量变化 df[ret_1] df[close].pct_change(1) # 前一日收益率 df[ret_5] df[close].pct_change(5) # 前5日累计收益率 df[ret_10] df[close].pct_change(10) # 前10日累计收益率 df[amp] (df[high] - df[low]) / df[close] # 当日振幅 df[vol_change] df[volume].pct_change(1) # 成交量变化率 # 特征统一滞后一天用昨天及之前的数据预测今天的下一个方向 feature_cols [ret_1, ret_5, ret_10, amp, vol_change] for col in feature_cols: df[col] df[col].shift(1) # 标签未来5个交易日涨跌方向1 表示涨0 表示跌 df[label] (df[close].shift(-5) df[close]).astype(int) df df.dropna()这里的逻辑是先把收益率、振幅、量比算出来这是基于当前及历史行情的信息随后用shift(1)把所有特征整体往后挪一行意思是“今天开盘前我能看到的所有信息截止到昨天收盘”标签则用shift(-5)取未来第5天的收盘价和今天比较1表示未来5天是涨的。shift的方向很多人搞反我当年也在这里栽过——特征用shift是为了防止用当天的信息预测当天标签用shift(-5)是为了把答案挪到未来。最后统一dropna把前10个没有完整历史的交易日清理掉。特征工程的深度可以无限加MACD、RSI、布林带位置、均线偏离度都有人做但对课程设计这个体量的仿真来说收益率加振幅加量比已经能说明完整流程了。真正影响评分的是这段代码有没有讲清楚“为什么特征要滞后、标签为什么要前置”而不是特征的数量。3. 在Python里跑通股票趋势预测的第一版仿真从读数据到出预测3.1 环境依赖把Python和必需的库装好别一上来就上深度学习趋势预测仿真对机器要求不高Anaconda自带的那套环境基本够用。需要确认的是pandas、numpy、scikit-learn、matplotlib这四个库已经装好。深度学习在这个场景里不是必须的像LSTM、Transformer这类模型参数多、训练慢、可解释性差课程设计里如果用不好反而会让答辩变成事故现场。scikit-learn里的随机森林和逻辑回归跑得快、解释清楚、参数少足够撑起一个完整的高分仿真。# 在命令行里安装所需依赖 pip install pandas numpy scikit-learn matplotlib如果你是在做比赛或者毕设强烈建议把环境依赖单独写一个requirements.txt放进项目里让答辩老师知道你的工程是可复现的。这一条看似不起眼但评分的“完整度”很大程度体现在这种地方。安装时如果遇到scikit-learn和numpy版本冲突通常是网络源或Python版本太老导致的换个Python 3.9以上的环境就好不要在旧版本上死磕。3.2 按时间顺序切分训练集和测试集为什么不能shuffle做完特征工程后接下来要面对一个很容易犯错的环节数据切分。用分类问题的通用写法train_test_split时shuffle默认是True也就是先把样本随机打乱再切。这个操作对普通机器学习任务没问题但对股票时间序列是致命的——它会让训练集里混进未来的样本测试集里也能看到过去的信息模型在训练时等于提前读到了未来测试分数虚高得离谱。正确做法是按时间顺序切分前70%或80%做训练后30%或20%做测试。这只是一种基础切法真实世界里还有滚动预测、扩窗训练这些进阶玩法留到第5章再展开。第一版仿真先按时间切出干净的训练和测试集。3.3 完整训练与预测脚本随机森林怎么调才不出虚高的分数随机森林是这里最稳的模型选择。它属于集成学习对特征尺度不敏感不需要标准化容忍噪声和缺失值而且可以直接输出每个特征的重要程度。周志华《机器学习》里讲集成学习时提到随机森林通过多棵决策树投票来降低方差这正是股票这种高噪声数据需要的特性。import pandas as pd from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score, classification_report # 假设 df 经过了上一章的特征处理包含 feature_cols 和 label X df[feature_cols].values y df[label].values # 按时间顺序切分前80%训练后20%测试 split int(len(df) * 0.8) X_train, X_test X[:split], X[split:] y_train, y_test y[:split], y[split:] # 随机森林控制树的数量和深度固定随机种子保证可复现 model RandomForestClassifier( n_estimators200, # 树的数量越多越稳但越慢 max_depth6, # 限制深度防止单棵树记住噪声 min_samples_leaf10, # 叶子节点最少样本数太少了容易过拟合 random_state42, # 固定种子别人复现结果一致 class_weightbalanced # 自动平衡涨跌样本数量 ) model.fit(X_train, y_train) # 在测试集上预测并输出评估结果 y_pred model.predict(X_test) print(测试集准确率:, accuracy_score(y_test, y_pred)) print(classification_report(y_test, y_pred))代码的核心在三个参数max_depth6限制每棵树的深度避免树把训练样本死记下来min_samples_leaf10强制叶子节点至少覆盖10个样本进一步抑制过拟合class_weightbalanced解决涨跌样本不均衡问题后面第4章会展开讲。这三个参数是股票预测这种低信噪比场景的合理起点不要一上来就抄鸢尾花分类的参数那种默认设置在金融数据上会直接过拟合。训练完成后预测方向只是第一步还要把它转成可展示的结果。很多课程设计只输出一个准确率数字然后就没了这不够。至少要把测试集的预测涨跌画成曲线与真实涨跌放在一起对比让老师一眼看到模型在哪些时段跟上了行情、哪些时段是完全反向的。import matplotlib.pyplot as plt test_df df.iloc[split:].copy() test_df[pred_direction] y_pred # 画真实收盘价曲线再把预测为涨的点标红、预测为跌的点标绿 plt.figure(figsize(12, 5)) plt.plot(test_df[date], test_df[close], colorgray, linewidth2) plt.scatter(test_df.loc[test_df[pred_direction]1, date], test_df.loc[test_df[pred_direction]1, close], colorred, s8, labelpredict up) plt.scatter(test_df.loc[test_df[pred_direction]0, date], test_df.loc[test_df[pred_direction]0, close], colorgreen, s8, labelpredict down) plt.legend() plt.show()这段代码的作用是把预测方向叠加到价格曲线上红色是模型判断未来5天要涨的交易日绿色是判断要跌的。如果红色密集出现在上涨区间、绿色密集出现在下跌区间说明模型学到了行情结构如果颜色随机散布甚至反向说明特征和模型都有问题需要回去检查数据清洗。完整的仿真还要包含一份说明文档里面至少写清楚三件事数据集来源与预处理规则、特征含义、模型选择理由与参数说明。这一套做下来从数据到特征、从模型到展示才是标题里“完整代码说明文档数据”真正对应的高分结构。4. 预测股票价格趋势的5个高频翻车点从未来函数到虚高准确率4.1 未来函数特征和标签用了同一天数据现象训练集准确率98%测试集准确率也有90%以上模型表现好得不像话但把预测结果画到K线图上发现模型给出的买点和卖点都完美踩在转折点上像开了透视。原因构造特征时没有做滞后直接用当天的收益率、振幅作为特征去预测当天的涨跌方向。当天涨跌幅本身就能直接决定方向模型当然“聪明”。解决所有特征必须shift(1)保证模型用的信息截止到昨天收盘标签用shift(-n)指向未来第n天。判断有没有踩这个坑最简单的自查方法就是看特征里有没有当天收盘价直接算出来的收益。4.2 shuffle毁掉的时间序列训练集里混进了未来现象用train_test_split(X, y, test_size0.2)跑出来的准确率比按时间切分高出一大截但同样一组参数放到新数据上就哑火。原因train_test_split默认shuffleTrue随机打乱后训练集里有未来样本模型实际上站在未来回顾过去。解决按时间顺序手动切片或者用sklearn.model_selection.TimeSeriesSplit做交叉验证。这个坑最隐蔽因为代码不报错分数也不难看。4.3 复权价没处理除权除息让模型学到一根假K线现象某个交易日的收盘价突然从20元跳成15元看起来像是暴跌模型据此输出强烈卖出信号但真实原因是股票除权除息不是行情崩了。原因数据源没有做前复权或后复权处理直接把原始价格拿来算收益率。解决在做特征工程前把价格列统一换成后复权价如果数据源本身已经做了前复权要确认整个文件口径一致不要前半段是复权价、后半段是原始价。这类问题在带成交量特征的模型里更隐蔽因为除权日成交量通常也会异常放大两个假信号叠加预测结果完全失真。4.4 涨跌样本不均衡90%准确率只是瞎蒙对了方向现象测试集准确率60%看起来还行但仔细看分类报告涨类召回率只有20%跌类召回率90%也就是说模型大部分时候只会预测“跌”。原因样本里跌的样本远多于涨的样本模型学到的策略是“全猜跌也能拿高分”。解决在分类模型里设置class_weightbalanced或者对训练集中跌的样本抽样降权评估时同时看准确率、精确率、召回率和F1分数而不是只盯着准确率一个指标。对于股票趋势预测一个合理的基准是测试集准确率稳定高于55%且涨跌两类的召回率没有明显偏科。4.5 训练分数高、持续亏损过拟合在金融数据上的真实表现现象训练集F1在0.7以上测试集掉到0.5以下随机抽几段行情验证预测结果和抛硬币差不多。原因树模型在训练时把历史上偶尔出现的巧合当成规律死记了下来金融数据噪声极大特征里真正有效的信息占比非常低。解决先把特征砍到最核心的5个以内然后调大min_samples_leaf把叶子节点样本数从1提到10、20甚至30。另一个有效手段是对特征做重要性排序筛选出模型认为重要的前3个特征重新训练通常比堆一大把特征更稳。5. 做一次真正能信的验证把预测趋势变成回测收益曲线5.1 先做前向验证滚动训练模拟真实交易节奏测试集上一锤子买卖预测完就结束这在课程设计答辩里够用但如果你想验证这个模型值不值得继续投入得做一次前向验证。方法很简单从测试集的起点开始每次只用截止当日的数据训练预测未来5天方向然后往前走5天再用扩充后的数据重新训练。这个过程模拟的是真实操作中“每天收盘后更新模型、第二天按信号下单”的节奏。predictions [] dates df[date].iloc[split:].tolist() for i in range(split, len(df) - 5, 5): # 截至第 i 天为止的历史数据 train_data df.iloc[:i] X_train train_data[feature_cols].values y_train train_data[label].values # 重新训练一个随机森林参数与主模型一致 model RandomForestClassifier( n_estimators200, max_depth6, min_samples_leaf10, random_state42, class_weightbalanced ) model.fit(X_train, y_train) # 预测第 i 天之后未来5天的方向 X_new df.iloc[i][feature_cols].values.reshape(1, -1) pred model.predict(X_new)[0] predictions.append((dates[i], pred)) print(前向验证样本数:, len(predictions))这段代码的成本比一次性训练高但它给你的信息量大得多如果滚动训练出来的预测准确率还稳定保持在高位说明模型学到的规律是延续的如果一路下滑说明特征已经失效得回炉重造。注意range的步长设成5和标签的预测周期保持一致避免同一个样本被重复预测。5.2 把预测结果变成模拟交易资金曲线准确率只是过程指标最终要看的是资金曲线。写一个简单的模拟器预测“涨”就全仓买入预测“跌”就空仓等待每次交易扣除手续费和滑点画出账户净值曲线。# 资金曲线模拟初始资金10万元每次预测正确买入持有5天 initial_cash 100000.0 cash initial_cash hold 0 # 持仓市值 stats [] for date, pred in predictions: if pred 1 and hold 0: # 买入按当日收盘价全仓 position cash / df_close_at(date) cash 0 hold position * df_close_at(date) * (1 - 0.001) # 0.1%手续费 elif pred 0 and hold 0: # 卖出按当日收盘价清仓 cash hold * (1 - 0.001) hold 0 stats.append(cash hold)真实的回测引擎还要考虑涨跌停无法买入、滑点、印花税等问题但作为仿真练习这个简化版本已经够验证一个朴素策略的盈利潜力。跑完后把净值曲线和基准指数画在同一张图里如果模型连随机买入持有的收益率都跑不过那说明它的“预测”没有转化为“策略收益”这才是判断一个模型能否落地的最硬标准。5.3 养成一个习惯把每次预测的置信度存下来我现在的习惯是在前向验证时顺手把predict_proba输出的正类概率一起存下来。概率高但方向错误和概率低但方向正确背后的含义完全不同每次只存0/1方向复盘时什么都看不到。这个习惯帮我在做特征迭代时省掉很多无效劳动——一次建模后回看置信度分布如果发现高置信度样本的命中率和低置信度没什么差别那说明模型输出的概率是虚的比逻辑回归都差。希望这个技巧对你也有用少走一段我走过的弯路。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →