简介:这是一套面向高校学生与机器学习入门者的股票预测实战项目,基于Python与sklearn构建,适合用作毕业设计、期末大作业或课程设计的高分参考方案。资源包共14个文件,包含4个py源码脚本、6个xml配置文件、1个csv数据集、1个txt依赖说明及gitignore、iml等工程文件,压缩包约24.25MB,结构清晰,部署简单。代码围绕随机森林等sklearn模型展开,从数据获取、特征处理到模型训练与预测均有完整实现,并配有详细注释,新手也能逐步理解。数据文件与依赖清单齐备,下载后可直接运行调试,省去自行搜集数据与配置环境的时间。目前已有245人学习下载,作者为qq_38140936。对于需要快速搭建股票预测demo、撰写论文实验章节或完成课程作业的读者,这份资源能提供可复用的代码框架与排错思路,帮助在有限时间内产出规范、可展示的成果。
1. 用 sklearn 做股票预测:一份能跑通的代码骨架长什么样
很多人第一次搜「python机器学习基于sklearn模型的股票预测代码+所需数据」,心里想的其实是:有没有一份能直接跑、数据也配齐的代码,让我先看到结果,再回头理解原理。这个诉求很实在,因为股票预测这个场景,光看算法推导容易飘,只有把数据喂进去、模型跑起来、指标打出来,才知道坑在哪。我一般会先搭一个最小可复现的骨架:用公开行情数据,构造几个常见特征,跑通线性回归、随机森林、梯度提升三类 sklearn 模型,最后用均方误差和方向准确率两个指标对比。这套骨架不追求实盘盈利,它的价值在于让你把「数据获取→特征工程→时序切分→训练→评估」整条链路走一遍,后面换数据、换特征、换模型都有地方下手。适合刚学完 sklearn 基础、想找一个完整项目练手的人,也适合做量化策略但想补机器学习环节的开发者。
2. 数据与特征:股票预测的输入到底怎么准备
2.1 行情数据从哪来、要哪些字段
股票预测代码能不能跑,第一道坎不是模型,是数据。常见做法是用公开的日线行情,字段至少包含日期、开盘价、最高价、最低价、收盘价、成交量。如果你手头没有现成 CSV,可以用 pandas 从本地行情文件读,或者用 akshare、tushare 这类库拉取。注意,这里不涉及任何交易接口的实盘对接,只是拿历史数据做离线训练。
我一般会把数据整理成一张宽表,索引是交易日,列是各字段。读进来之后先做三件事:按日期升序排列、检查缺失值、把日期列转成 datetime 类型。顺序错了,后面所有滚动特征都会错位,这是血泪经验。
import pandas as pd import numpy as np # 读取本地行情 CSV,假设列名为 date, open, high, low, close, volume df = pd.read_csv("stock_daily.csv", parse_dates=["date"]) df = df.sort_values("date").reset_index(drop=True) # 缺失值处理:行情数据一般用前向填充,避免直接删行导致时间不连续 df[["open", "high", "low", "close", "volume"]] = ( df[["open", "high", "low", "close", "volume"]].ffill() ) # 去掉仍然缺失的行(通常是数据开头) df = df.dropna().reset_index(drop=True) print(df.head()) print(df.shape)这段代码的逻辑很直白:先保证时间有序,再处理缺失。参数上,parse_dates把日期列解析成时间类型,ffill是前向填充,适合行情这种连续交易日场景。如果你用的是分钟级数据,缺失处理要更谨慎,因为停牌和休市含义不同。
2.2 特征工程:把价格变成模型能吃的数字
原始价格不能直接喂给模型,因为绝对价格没有可比性,而且不同股票价格量级差异大。常见做法是构造收益率、滚动均值、滚动波动率、成交量变化率这几类特征。收益率用pct_change(),滚动均值用rolling(window).mean(),波动率用滚动标准差。窗口参数我一般从 5、10、20 三个里选,对应一周、两周、一个月。
# 日收益率 df["ret_1"] = df["close"].pct_change() # 滚动均值与波动率 for w in [5, 10, 20]: df[f"ma_{w}"] = df["close"].rolling(w).mean() df[f"vol_{w}"] = df["ret_1"].rolling(w).std() # 成交量变化率 df["vol_chg"] = df["volume"].pct_change() # 预测目标:下一日收益率方向,涨为 1,跌为 0 df["target"] = (df["close"].shift(-1) > df["close"]).astype(int) # 去掉滚动窗口和 shift 产生的空值 df = df.dropna().reset_index(drop=True)这里有几个参数要留意。rolling(w)的窗口越大,特征越平滑,但滞后越明显;shift(-1)是把明天作为预测目标,训练时不能把今天之后的信息混进来。目标列用方向而不是具体价格,是因为价格回归在股票上极难做准,方向分类至少能看出模型有没有学到东西。
提示:特征构造完一定要检查有没有未来函数。比如用
close算的滚动均值,如果窗口包含了当天收盘,而预测目标又是下一日,那当天收盘是已知的,没问题;但如果用shift(-1)后的价格去算特征,就泄露了。
3. 用 sklearn 跑通三类模型:从线性回归到集成树
3.1 时序切分:为什么不能随机打乱
股票数据是典型的时间序列,随机切分会让模型在训练集里看到未来数据,评估结果虚高。正确做法是按时间顺序切,前 80% 做训练,后 20% 做测试。如果你要做交叉验证,也得用TimeSeriesSplit,不能用默认的 KFold。
from sklearn.model_selection import TimeSeriesSplit feature_cols = [c for c in df.columns if c not in ["date", "target"]] X = df[feature_cols].values y = df["target"].values # 按时间顺序切分 split_idx = int(len(df) * 0.8) X_train, X_test = X[:split_idx], X[split_idx:] y_train, y_test = y[:split_idx], y[split_idx:] # 时序交叉验证 tscv = TimeSeriesSplit(n_splits=5) print("训练集样本数:", X_train.shape, "测试集样本数:", X_test.shape)TimeSeriesSplit的n_splits决定切几折,每折训练集只包含测试集之前的数据。这个参数在样本量少的时候可以调小,比如 3 折,否则每折训练数据太短,模型学不到东西。
3.2 三个基线模型:线性回归、随机森林、梯度提升
sklearn 的好处是接口统一,换模型基本只改一行。我一般先跑逻辑回归做线性基线,再跑随机森林和梯度提升看非线性模型有没有提升。注意,这里说的是分类任务,所以用LogisticRegression而不是LinearRegression。如果你要做价格回归,把目标换成下一日收益率,模型换成Ridge或RandomForestRegressor。
from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline from sklearn.metrics import accuracy_score, f1_score models = { "逻辑回归": Pipeline([ ("scaler", StandardScaler()), ("clf", LogisticRegression(max_iter=1000)) ]), "随机森林": RandomForestClassifier( n_estimators=200, max_depth=5, random_state=42 ), "梯度提升": GradientBoostingClassifier( n_estimators=200, max_depth=3, learning_rate=0.05, random_state=42 ), } for name, model in models.items(): model.fit(X_train, y_train) pred = model.predict(X_test) print(f"{name} 准确率: {accuracy_score(y_test, pred):.4f} " f"F1: {f1_score(y_test, pred):.4f}")参数上,LogisticRegression的max_iter调大是为了防止不收敛;随机森林的n_estimators是树的数量,200 棵在几千条样本上够用,max_depth限制深度防止过拟合;梯度提升的learning_rate和n_estimators要配合调,学习率小就多跑几轮。标准化只对逻辑回归必要,树模型对量纲不敏感,但放进 Pipeline 里统一处理更省事。
3.3 评估指标:准确率会骗人,要看方向准确率和混淆矩阵
股票涨跌方向如果分布不均,准确率就没意义。比如 70% 的样本是涨,模型全猜涨也有 70% 准确率。所以要看 F1、混淆矩阵,以及单独算涨跌两个方向的表现。
from sklearn.metrics import confusion_matrix, classification_report best_model = models["梯度提升"] pred = best_model.predict(X_test) print(confusion_matrix(y_test, pred)) print(classification_report(y_test, pred, digits=4))classification_report会给出每个类别的精确率、召回率和 F1。如果跌的召回率很低,说明模型只会猜涨,这种模型实盘没法用。我一般要求两个方向的 F1 都不能太差,否则宁可不用。
4. 避坑与排查:股票预测代码最容易翻车的五个地方
4.1 未来函数:回测很美,实盘就废
现象:训练集和测试集指标都很高,换一段新数据就崩。原因:特征里混入了未来信息,比如用当天收盘算的指标去预测当天方向,或者用shift(-1)后的数据做滚动统计。解决:所有特征只能用当前及之前的数据,目标列才用未来。检查方法是把特征列逐个和日期对齐,看有没有用到shift(-n)且 n 为负的情况。
4.2 数据对齐错误:日期错位一天,结果全变
现象:模型表现忽好忽坏,换数据源就复现不了。原因:不同数据源的日期格式、时区、停牌处理不一致,合并时错位。解决:统一用交易日索引,合并前先sort_values和reset_index,关键步骤打印前后几行核对。我习惯在特征构造后随机抽几天,手工算一遍滚动均值,和代码结果对一下。
4.3 类别不平衡:模型只会猜多数类
现象:准确率看着不错,但混淆矩阵里一个类别几乎没预测对。原因:涨跌样本比例悬殊,模型偏向多数类。解决:用class_weight="balanced",或者对少数类过采样。注意过采样只能在训练集做,测试集保持原始分布。
4.4 过拟合:训练集 F1 0.9,测试集 0.5
现象:训练指标远高于测试指标。原因:树太深、特征太多、样本太少。解决:限制max_depth,减少n_estimators,加正则化,或者用TimeSeriesSplit做交叉验证看稳定性。我一般先跑一个很浅的模型做基线,再逐步加复杂度,每加一步都看测试指标有没有提升。
4.5 特征量纲与缺失值:标准化和填充顺序搞反
现象:逻辑回归不收敛,或者预测结果全是同一类。原因:先标准化再填充缺失值,导致填充值被缩放;或者树模型遇到 NaN 直接报错。解决:先填充再标准化,Pipeline 里把填充和缩放按顺序排好。sklearn 的SimpleImputer可以放进 Pipeline,但要注意strategy选median还是ffill取决于数据。
5. 把代码变成可复用的实验:参数调优与滚动验证
5.1 用 GridSearchCV 配合 TimeSeriesSplit 调参
单次切分评估波动大,调参要用时序交叉验证。GridSearchCV可以传cv=TimeSeriesSplit(n_splits=5),这样每组参数都在时间序列上验证。
from sklearn.model_selection import GridSearchCV param_grid = { "n_estimators": [100, 200, 300], "max_depth": [3, 5, 7], "learning_rate": [0.01, 0.05, 0.1], } gs = GridSearchCV( GradientBoostingClassifier(random_state=42), param_grid, cv=TimeSeriesSplit(n_splits=5), scoring="f1", n_jobs=-1, ) gs.fit(X_train, y_train) print("最佳参数:", gs.best_params_) print("最佳分数:", gs.best_score_)scoring="f1"比准确率更适合不平衡分类。n_jobs=-1用满 CPU,但数据量大时注意内存。调参范围不要一次铺太开,先粗后细,否则跑一晚上出不来结果。
5.2 滚动训练:模拟真实交易中的模型更新
实盘里模型不能一次训练用到底,要定期用新数据重新训练。滚动训练就是每过一段时间,把窗口往前滑,重新拟合。下面是一个简化版,每 60 个交易日重新训练一次,预测接下来 20 天。
def walk_forward(X, y, train_window=500, step=60): results = [] for start in range(0, len(X) - train_window - step, step): train_end = start + train_window test_end = train_end + step X_tr, y_tr = X[start:train_end], y[start:train_end] X_te, y_te = X[train_end:test_end], y[train_end:test_end] model = GradientBoostingClassifier( n_estimators=200, max_depth=3, learning_rate=0.05, random_state=42 ) model.fit(X_tr, y_tr) pred = model.predict(X_te) results.append(accuracy_score(y_te, pred)) return np.mean(results), np.std(results) mean_acc, std_acc = walk_forward(X, y) print(f"滚动验证准确率均值: {mean_acc:.4f} 标准差: {std_acc:.4f}")train_window是每次训练用的历史长度,step是往前滑的步长。窗口太短模型学不稳,太长又跟不上市场变化。我一般从 500 个交易日开始试,步长取 20 到 60。标准差比均值更重要,标准差大说明模型在不同时间段表现不稳定,实盘风险高。
5.3 特征重要性:看看模型到底在依赖什么
树模型可以输出特征重要性,这一步能帮你发现数据泄露和无效特征。
model = GradientBoostingClassifier( n_estimators=200, max_depth=3, learning_rate=0.05, random_state=42 ) model.fit(X_train, y_train) importances = pd.Series(model.feature_importances_, index=feature_cols) print(importances.sort_values(ascending=False).head(10))如果某个特征重要性异常高,先怀疑它是不是包含了未来信息。比如vol_chg如果用了当天收盘后的成交量,而预测目标是当天方向,那就是泄露。正常情况,收益率和滚动波动率应该排在前列,但不会一家独大。
5.4 一个具体技巧:用概率输出做阈值调整
分类模型默认阈值 0.5,但股票预测里你可以调阈值来平衡精确率和召回率。比如只在高概率时交易,减少交易次数但提高胜率。
proba = model.predict_proba(X_test)[:, 1] for thresh in [0.5, 0.55, 0.6, 0.65]: pred = (proba >= thresh).astype(int) print(f"阈值 {thresh} 准确率: {accuracy_score(y_test, pred):.4f} " f"F1: {f1_score(y_test, pred):.4f}")阈值调高,预测为涨的样本变少,精确率可能上升,但召回率下降。具体取多少要看你的策略能承受多少假信号。我一般会画一条阈值和 F1 的曲线,选 F1 最高附近的阈值,而不是死守 0.5。
这套代码骨架我前后改过很多版,最大的教训是:别一上来就堆模型,先把数据对齐和特征构造做扎实,再跑基线,最后才调参。股票预测没有后悔药,未来函数一旦混进去,回测再漂亮也是自欺欺人。希望帮到你。
本文还有配套的精品资源,点击获取