简介:这份资源面向计算机、金融等专业的学生与机器学习入门者,提供一套基于sklearn的股票预测完整代码与配套数据,可用于毕业设计、期末大作业或课程设计场景,帮助解决从数据获取到模型训练预测的全流程实现问题。压缩包共14个文件,约24.25MB,以4个Python脚本为核心,涵盖随机森林等模型的训练与预测逻辑,另含csv数据集、txt依赖说明、xml与iml等IDE配置文件,结构清晰、便于直接部署运行。代码附有详细注释,新手也能理解特征构建、模型拟合与结果评估的关键环节。目前已有245人学习下载,适合希望快速复现股票预测实验、对照调参并整理成文的中初级学习者参考借鉴。

1. 从一份 sklearn 股票预测代码说起:它到底能预测什么

很多人第一次搜「python机器学习基于sklearn模型的股票预测代码」,心里想的其实是「给我一份能跑的代码,最好明天就能告诉我买哪只」。我先把预期摁住:这份代码能做的,是用历史行情数据训练一个监督学习模型,去预测「下一个交易日涨还是跌」或者「下一日收盘价大概落在哪个区间」,它输出的是一个概率或数值,不是发财指令。真正有价值的地方在于,你能借它把机器学习的完整链路——数据获取、特征工程、标签构造、模型训练、回测评估——在本地跑通一遍,理解每一步在干什么、哪里最容易骗自己。

适合谁?会一点 python、装过 numpy 和 pandas、想用 sklearn 做第一个金融方向项目的同学,以及做量化策略想加一层机器学习信号的人。不适合指望它直接实盘赚钱的人。下面我按「数据怎么来 → 特征怎么造 → 模型怎么训 → 怎么评估不翻车」的顺序讲透,代码都能直接抄。

2. 数据与标签:股票预测代码的第一道分水岭

2.1 数据从哪来,用什么格式存

股票预测代码跑不跑得起来,八成取决于数据这一环。常见做法有三类:一是用 yfinance 、 tushare 、 akshare 这类库直接拉 A 股或美股日线;二是券商或数据商导出的 CSV;三是本地已有的行情文件。我一般先用 CSV 落地,因为可复现、可版本管理,不依赖网络。

一份够用的日线数据至少要有这些列:日期、开盘、最高、最低、收盘、成交量。下面这段代码把 CSV 读进来并做基础清洗,注意日期要转成 datetime 并设为索引,否则后面做滚动特征会错位。

import pandas as pd
import numpy as np

# 读取本地行情 CSV,列名按你的数据源调整
df = pd.read_csv("stock_daily.csv")
df.columns = [c.lower() for c in df.columns]  # 统一小写,避免大小写踩坑

# 日期解析 + 排序,顺序错了滚动窗口全废
df["date"] = pd.to_datetime(df["date"])
df = df.sort_values("date").reset_index(drop=True)
df = df.set_index("date")

# 只保留需要的列,缺失值先看再决定怎么处理
df = df[["open", "high", "low", "close", "volume"]].astype(float)
print(df.isna().sum())          # 先看缺失分布,别急着 dropna
df = df.dropna()                # 日线数据缺失一般直接删,量不大
print(df.tail())

逻辑说明: sort_values 是必须的,很多数据源导出的顺序是乱的,滚动均值一旦顺序错,特征就是噪声。 dropna 放在最后,是因为有些指标计算会产生 NaN,先算完再删比一开始删更合理。参数上, astype(float) 防止成交量被读成字符串导致后续运算报错。

2.2 标签怎么定义,决定了模型在预测什么

这是最容易被忽略、也最影响结果的一步。标签(label)就是你让模型去拟合的「答案」。股票预测里常见三种:

标签类型 定义方式 适用场景
分类涨跌 明日收盘 > 今日收盘 记为 1,否则 0 方向判断,最常用
回归收益率 明日收益率 = (明日收盘-今日收盘)/今日收盘 预测幅度
三分类 涨超 1%、跌超 1%、其余 过滤震荡

我一般先用二分类涨跌跑通,因为它对模型和评估都最友好。构造标签时务必用 shift(-1) ,让今天的特征对应明天的结果,绝不能把当天收盘价泄漏进特征。

# 二分类标签:明日涨为 1,跌或平为 0
df["label"] = (df["close"].shift(-1) > df["close"]).astype(int)

# 最后一行没有“明天”,标签是 NaN,必须删掉
df = df.dropna(subset=["label"])
print(df["label"].value_counts(normalize=True))  # 看类别是否均衡

逻辑说明: shift(-1) 把下一行数据上移,实现「今天看明天」。 value_counts(normalize=True) 打印涨跌比例,如果一边倒到 90% 以上,说明这段行情单边严重,模型很容易靠「永远猜涨」刷高准确率,后面评估要特别小心。参数上,如果你做的是回归,把标签换成 df["close"].shift(-1) / df["close"] - 1 即可。

提示:标签定义一旦确定,整个项目从特征到评估都要围绕它,中途改标签等于重做,别在训练到一半时换。

3. 特征工程:把 K 线变成 sklearn 能吃的数字

3.1 用滚动窗口造出「过去的信息」

sklearn 的模型不认识「昨天涨了」这种话,它只吃数值矩阵。特征工程就是把行情翻译成一组数字。最常用的是滚动统计量:过去 N 天的收益率均值、波动率、成交量变化等。这些特征只用到当前及之前的数据,天然不会泄漏未来。

# 基础收益率
df["ret"] = df["close"].pct_change()

# 滚动特征:窗口 5 和 10,代表短中期
for w in [5, 10]:
    df[f"ma_{w}"] = df["close"].rolling(w).mean() / df["close"] - 1  # 偏离均线
    df[f"vol_{w}"] = df["ret"].rolling(w).std()                       # 滚动波动率
    df[f"volchg_{w}"] = df["volume"].rolling(w).mean() / df["volume"] - 1

# 高低价差,衡量当日振幅
df["hl_range"] = (df["high"] - df["low"]) / df["close"]

df = df.dropna()  # 滚动窗口前几行是 NaN
print(df.filter(like="ma_").describe())

逻辑说明: rolling(w).mean() 是滚动均值,除以收盘价得到「偏离度」,比原始均线更能反映相对位置。 pct_change() 算日收益率。 describe() 用来检查特征量纲,如果某个特征标准差特别大,后面标准化时要留意。参数上,窗口 5 和 10 是日线常用值,做周线可以换成 4 和 12。

3.2 特征标准化与训练集切分

sklearn 里像逻辑回归、SVM 这类模型对量纲敏感,必须标准化;树模型(随机森林、梯度提升)不敏感,但标准化也无害。关键是:标准化器只能在训练集上 fit,再 transform 测试集,否则测试集信息泄漏进训练,评估结果虚高。

from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

feature_cols = [c for c in df.columns if c not in ["label", "open", "high", "low", "close", "volume"]]
X = df[feature_cols].values
y = df["label"].values

# 按时间顺序切分,不能随机打乱!这是时间序列的铁律
split = int(len(X) * 0.8)
X_train, X_test = X[:split], X[split:]
y_train, y_test = y[:split], y[split:]

scaler = StandardScaler().fit(X_train)   # 只在训练集 fit
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)
print("train:", X_train.shape, "test:", X_test.shape)

逻辑说明: train_test_split 默认随机打乱,对时间序列是灾难——它会让模型用「未来」预测「过去」。所以这里手动按 80/20 顺序切。 StandardScaler().fit(X_train) 只学训练集的均值和方差,是防泄漏的关键动作。参数上,80/20 是常见比例,样本少时可以 70/30。

注意:如果你看到有人用 train_test_split(shuffle=True) 做股票预测还宣称准确率 90%,基本可以判定结果不可信。

4. 模型训练与评估:sklearn 里选哪个、怎么调

4.1 三个基线模型先跑通

不要一上来就上复杂模型。我一般先跑三个基线:逻辑回归(线性、可解释)、随机森林(非线性、抗过拟合)、梯度提升(通常最强)。用同一套特征和切分对比,才知道数据本身有多少信息量。

from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.metrics import accuracy_score, roc_auc_score, classification_report

models = {
    "logistic": LogisticRegression(max_iter=1000),
    "rf": RandomForestClassifier(n_estimators=200, max_depth=5, random_state=42),
    "gbdt": GradientBoostingClassifier(n_estimators=200, max_depth=3, random_state=42),
}

for name, model in models.items():
    model.fit(X_train, y_train)
    pred = model.predict(X_test)
    prob = model.predict_proba(X_test)[:, 1]
    print(f"\n{name}")
    print("acc:", round(accuracy_score(y_test, pred), 4))
    print("auc:", round(roc_auc_score(y_test, prob), 4))
    print(classification_report(y_test, pred))

逻辑说明: max_iter=1000 防止逻辑回归不收敛报警。随机森林 max_depth=5 是刻意压低的,股票数据噪声大,树太深必然过拟合。 predict_proba 给出概率,AUC 比准确率更能反映排序能力。参数上, n_estimators 200 是性价比起点, random_state 固定保证可复现。

4.2 评估指标别只看准确率

股票涨跌天然不均衡,准确率会骗人。假设某段行情 70% 是涨,模型全猜涨就有 70% 准确率,但它毫无用处。要看 AUC、看分类报告里的 precision/recall,还要看「如果按模型信号交易,收益曲线长什么样」。

# 简单策略回测:模型说涨就持有,说跌就空仓
import numpy as np

signal = models["rf"].predict(X_test)          # 1 持有,0 空仓
ret_test = df["ret"].iloc[split:].values        # 测试期真实日收益
strategy_ret = signal * ret_test                # 信号滞后一天的问题见避坑章

cum_strategy = np.cumprod(1 + strategy_ret)
cum_buyhold = np.cumprod(1 + ret_test)
print("策略累计:", round(cum_strategy[-1], 4))
print("买入持有:", round(cum_buyhold[-1], 4))

逻辑说明: signal * ret_test 是最朴素的策略收益, cumprod 算累计净值。对比买入持有,能直观看出模型有没有创造超额。参数上,这里没扣手续费和滑点,真实回测必须扣,否则高估收益。

提示:如果策略累计收益和买入持有差不多,说明模型没提供有效信号,别急着上实盘。

5. 避坑与排查:股票预测代码最容易翻车的 5 个地方

5.1 现象:测试集准确率 85%,实盘一塌糊涂

原因:最常见的是数据泄漏。要么标准化在全集上 fit,要么特征里混进了未来数据(比如用了 shift(1) 方向搞反),要么随机打乱了时间顺序。解决:逐行检查每个特征的计算是否只依赖当前及历史行,标准化只在训练集 fit,切分严格按时间。

5.2 现象:模型永远预测同一个类别

原因:标签不均衡 + 模型没设 class_weight 。某段行情 90% 上涨,模型学个「全猜涨」就收敛了。解决: LogisticRegression(class_weight="balanced") ,或对少数类过采样,或改用 AUC 作为选型指标而不是准确率。

5.3 现象:回测收益高得离谱

原因:信号和收益没对齐。今天收盘后才知道今天的信号,却用今天的收益去乘,等于用了未来信息。解决:信号必须滞后一天, strategy_ret = signal[:-1] * ret_test[1:] ,确保「昨天信号吃今天收益」。

5.4 现象:换一段数据结果完全崩掉

原因:过拟合。特征太多、树太深、样本太少,模型把训练集的噪声背下来了。解决:减少特征数量,限制树深,加正则化,用滚动窗口做多段样本外验证,而不是只切一次。

5.5 现象: ValueError: Input contains NaN

原因:滚动特征产生 NaN 后没清理干净,或某列本身有缺失。解决:在 X = df[feature_cols].values 之前统一 df = df.dropna() ,并打印 df.isna().sum() 确认。别用 fillna(0) 糊弄,0 在收益率里是有意义的数值,会污染特征。

6. 让这份代码真正可用:滚动验证与特征重要性排查

跑通一次切分只是开始,真正决定这份代码值不值得投入的,是它能不能在不同时间段稳定。我一般会做两件事:滚动窗口验证和特征重要性检查。

滚动验证的思路是:用前 3 年训练、第 4 年测试,然后窗口整体后移一年,重复多次,看每段测试的 AUC 是否稳定。稳定在 0.52~0.58 之间就算有微弱信号,低于 0.5 说明模型在瞎猜。

from sklearn.metrics import roc_auc_score

def walk_forward(X, y, train_size=750, test_size=250, step=250):
    scores = []
    start = 0
    while start + train_size + test_size <= len(X):
        tr = slice(start, start + train_size)
        te = slice(start + train_size, start + train_size + test_size)
        model = RandomForestClassifier(n_estimators=200, max_depth=5, random_state=42)
        model.fit(X[tr], y[tr])
        prob = model.predict_proba(X[te])[:, 1]
        scores.append(roc_auc_score(y[te], prob))
        start += step
    return scores

scores = walk_forward(X, y)
print("各段 AUC:", [round(s, 4) for s in scores])
print("均值:", round(np.mean(scores), 4), "标准差:", round(np.std(scores), 4))

逻辑说明: train_size=750 约三年日线, test_size=250 约一年, step=250 每次后移一年。打印均值和标准差,均值看有没有信号,标准差看稳不稳定。参数上,样本少可以把窗口缩小到 500/125。

第二件事是看特征重要性,判断模型是不是靠某个可疑特征在撑。

rf = RandomForestClassifier(n_estimators=200, max_depth=5, random_state=42).fit(X_train, y_train)
imp = pd.Series(rf.feature_importances_, index=feature_cols).sort_values(ascending=False)
print(imp.head(10))

如果排名第一的特征是某个你事后才构造、逻辑上说不通的列,八成是泄漏,回去查它的计算。我自己的习惯是:任何一份股票预测代码,先跑滚动验证看 AUC 均值,再看特征重要性有没有异常,这两关过了才考虑加策略逻辑。这套流程帮我省下过很多次「以为发现了圣杯、其实是数据泄漏」的后悔药。希望帮到你。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

更多推荐