尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Python实现股价预测:LR、LSTM、ARIMA、KNN模型对比与工程实践

发布时间:2026/9/24 1:03:59

资讯中心
01
ARTICLE

Python实现股价预测:LR、LSTM、ARIMA、KNN模型对比与工程实践

Python实现股价预测:LR、LSTM、ARIMA、KNN模型对比与工程实践
简介Python实现机器学习股价预测源码包集成线性回归LR、长短期记忆网络LSTM、ARIMA、KNN等多种主流模型覆盖数据预处理、模型训练、回测与可视化全流程代码含详细注释适合毕业设计、期末大作业及课程设计参考也便于新手快速上手。包内共31个文件包含Python脚本模型定义、预测、回测等、CSV数据、Excel表格、HTML交互图表、PNG可视化图片及依赖说明等压缩包仅1.45MB。其中PNG图覆盖趋势分解、移动平均、自相关、多种模型预测对比等HTML文件展示每日资金及持有期收益变化便于理解模型效果。已有372人学习下载属于导师认可的高分项目可直接作为毕设或大作业基础部署简单、扩展方便。1. 四个模型塞进同一个股价预测工程真正要解决的不是“预测”而是“对比”跑通一个股价预测模型五秒钟就能出图但要验证这个预测结果是真本事还是搭上了随机性可能需要你折腾一个完整的周末。Python 实现机器学习股价预测源码把 LR、LSTM、ARIMA、KNN 这几类模型装进同一个工程表面看是在做股价预测实际是在教你怎么做模型对比、参数调优和结果验证。刚入门的人在这里找到的是最短路径数据从哪拉、标签怎么打、时间序列为什么不能随机切分、ARIMA 和 LSTM 的预测输出为什么长得不一样。适合人群是在校生做课程设计、转行的数据分析师想接触量化以及在金融风控里摸爬滚打但没正经碰过时间序列预测的工程师。这篇笔记按数据准备、经典统计模型、深度与近邻模型、典型翻车现场、模型选型验证的顺序把我调参踩过的坑和能直接抄的代码都摆出来。2. 数据准备与特征工程预测值还没算出来别先被数据坑进去2.1 环境与数据源先把 Python 技术栈和日线数据备齐任何股价预测工程的第一步都不是建模型而是把 Python 环境、数据接口和回测数据集这三件事定下来。很多人在模型上花了大把时间最后发现数据有前视偏差或者复权方式不对全部白做。我常用的技术栈是 pandas 做数据处理numpy 做数值计算statsmodels 做 ARIMAscikit-learn 跑 LR 和 KNNPyTorch 搭 LSTM。如果你是第一次跑这类项目先确认这几个包都装好了建议用虚拟环境隔离依赖别和公司项目混在一起。数据方面常见做法是用公开的免费数据接口拉日线行情yfinance 可以拿美股tushare 和 akshare 能覆盖 A 股接口返回的字段基本都包含 open、high、low、close、volume 这五列。import pandas as pd import numpy as np import yfinance as yf df yf.download(AAPL, start2018-01-01, end2023-12-31, auto_adjustTrue) df df[[Open, High, Low, Close, Volume]].copy() df.dropna(inplaceTrue) df.sort_index(inplaceTrue) df df[~df.index.duplicated(keeplast)] print(df.head()) print(df.info())这段代码先把行情数据下载下来然后做了三件容易被忽略的事删掉缺失行、按时间升序排序、去掉重复索引。很多免费接口偶尔会返回重复的日期行如果不处理后面构造时序特征时索引会错位。auto_adjustTrue 表示拿到的价格是前复权价这对股价预测是必要的否则分红送股会导致价格断层模型会莫名其妙学到“假跌”。2.2 收益率先算对预测收益率比预测价格更符合金融直觉直接拿收盘价做预测是新手最容易踩的坑。股价是非平稳序列十年前 100 块、现在 200 块这种长期趋势会让模型倾向预测一个“惯性上涨”的值看似拟合得很好实际只是学到了价格的单调性一旦遇到市场风格切换立刻失效。我一般会先把价格转成收益率最常见的是简单收益率和对数收益率。对数收益率的好处是数学性质好它可以跨时间相加分布也更接近正态模型拟合时数值更稳定。代码里我用 pct_change 计算每日简单收益率然后向前移一位构造监督学习的标签——所谓监督学习是要让模型用今天以及之前的信息去预测“明天”这个未知量所以标签必须用未来数据生成但训练时不能让模型看到它。df[return] df[Close].pct_change() df[label_direction] (df[return].shift(-1) 0).astype(int) df[label_price] df[Close].shift(-1) for lag in [1, 2, 3, 5, 10]: df[freturn_lag_{lag}] df[return].shift(lag) feature_cols [return_lag_1, return_lag_2, return_lag_3, return_lag_5, return_lag_10] df.dropna(inplaceTrue) train df.loc[2018-01-01:2022-12-31] test df.loc[2023-01-01:2023-12-31] print(train.shape, test.shape)这段代码的核心是 shift 操作。label_direction 用 shift(-1) 把明天的涨跌方向拿到今天return_lag_1 用 shift(1) 把昨天的收益率拿到今天作为特征。方向搞反是最常见的错误逻辑上你只能用 t-1、t-2 去预测 t不能用 t 去预测 t更不能让 t 的信息出现在 t-1 的样本里。数据切分这里我按时间顺序训练集用前四年测试集用最后一年。这一点和图像分类、推荐系统不一样股价数据一旦 shuffle模型等于偷看了未来的答案。2.3 标准化与序列化LSTM 的数据格式和 LR/KNN 完全不同LR、KNN 这类模型接受的是二维表格每行是一个样本每列是一个特征LSTM 接受的是三维张量形状是样本数、时间步长、特征数。很多人拿着同一个 DataFrame 去喂所有模型到 LSTM 这里就报维度错误其实就是没做序列化。from sklearn.preprocessing import StandardScaler import torch scaler StandardScaler() train_scaled scaler.fit_transform(train[feature_cols]) test_scaled scaler.transform(test[feature_cols]) def create_sequences(data, label, lookback10): X, y [], [] for i in range(lookback, len(data)): X.append(data[i - lookback:i]) y.append(label[i]) return torch.tensor(np.array(X), dtypetorch.float32), torch.tensor(np.array(y), dtypetorch.float32) X_train_seq, y_train_seq create_sequences(train_scaled, train[label_direction].values, lookback10) X_test_seq, y_test_seq create_sequences(test_scaled, test[label_direction].values, lookback10) print(X_train_seq.shape, y_train_seq.shape)这里有两个细节值得圈出来。第一scaler 只能在训练集上 fit然后同时 transform 训练集和测试集。如果你拿全部数据做标准化均值方差里混入了测试期的信息这在时间序列预测里叫数据泄漏效果会被高估。第二lookback10 表示用过去 10 天的特征序列预测下一天这个窗口可以从 5 试到 30但不要贪大太长反而引入噪声。LSTM 的输入维度是 (样本数, 10, 特征数)如果你后面用 PyTorch这个 shape 直接对应网络输入的 (batch, seq_len, input_size)。3. ARIMA 与 LR统计基线模型怎么落地股价预测3.1 ARIMA 定阶逻辑p、d、q 三个参数到底怎么定ARIMA 是经典的时间序列统计模型全称是差分自回归移动平均模型。它的优势在于可解释性强参数有明确统计含义运行速度快不需要 GPU劣势在于它本质上假设序列是线性的而股价里大量非线性模式它学不到。所以我在实际项目中把 ARIMA 当作基线模型不是用来赚钱而是用来定义“一个线性模型能做到什么程度”LSTM 必须明显超过它才有存在价值。用 ARIMA 做股价预测第一个动作是判断平稳性。股价本身非平稳必须做差分差分阶数就是模型里的 d。一般用 ADF 检验来量化判断p 值小于 0.05 就认为序列平稳。p 是自回归阶数q 是移动平均阶数这两个参数可以直接看 ACF、PACF 图也可以用 AIC/BIC 准则自动搜索。from statsmodels.tsa.stattools import adfuller from statsmodels.graphics.tsaplots import plot_acf, plot_pacf import matplotlib.pyplot as plt from statsmodels.tsa.arima.model import ARIMA price_series df[Close].dropna() adf_result adfuller(price_series) print(fADF p-value: {adf_result[1]:.4f}) price_diff price_series.diff().dropna() adf_diff adfuller(price_diff) print(fADF p-value after diff: {adf_diff[1]:.4f}) model_arima ARIMA(price_series, order(2, 1, 2)) result model_arima.fit() print(result.summary()) forecast result.forecast(steps30) print(forecast.head())这段代码展示了最简路径先做 ADF 检验发现原始价格不平稳差分一次后 p 值显著小于 0.05因此 d 定为 1。p 和 q 我先用 (2, 1, 2) 试跑然后看 summary 里的 AIC 值再去试 (1, 1, 1) 或 (5, 1, 2)选 AIC 更小的配置。要注意的是ARIMA 预测股价原始价格时如果 d1模型实际预测的是差分值最终输出会累加回去。你可以加一个 for 循环把未来 30 天的预测值滚动回退成价格序列而不是只看 forecast 的原始输出否则会被“预测值接近常数”误导。3.2 LR 做股价预测的正确姿势预测涨跌方向而不是终点价格逻辑回归这个名字带“回归”但它实际是分类模型。在股价预测项目里我见过太多人拿 LinearRegression 拟合明天的收盘价结果精度一塌糊涂因为价格本身非平稳回归模型很难拟合这种带趋势的序列。常见做法是换个思路让模型预测明天是涨还是跌二分类。from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score, classification_report lr LogisticRegression(max_iter1000, C1.0, class_weightbalanced) lr.fit(train[feature_cols], train[label_direction]) pred lr.predict(test[feature_cols]) acc accuracy_score(test[label_direction], pred) print(fLR Accuracy: {acc:.4f}) print(classification_report(test[label_direction], pred))这里三个参数值得展开。max_iter1000 是因为逻辑回归用梯度下降求解特征量少时默认迭代次数够用但实战里保险起见我一般写到 1000 以上。C1.0 是正则化强度的倒数C 越小正则化越强越不容易过拟合。股票数据噪声大我建议 C 从 0.1 试到 10通过交叉验证选优。class_weightbalanced 是处理涨跌样本不均衡的常用做法如果数据集里上涨天数多模型会倾向全预测上涨这个参数能自动调节权重。LR 解释性很强你可以直接打印 lr.coef_ 看每个滞后收益率的权重系数负数表示该特征与次日上涨呈负相关。模型预测概率也有意义lr.predict_proba 输出的是上涨概率的估计你可以设一个阈值比如 0.6只有概率超过 0.6 才认为是看涨信号这比单纯看 0/1 分类结果更贴近真实交易决策。3.3 两个模型输出对比连续值、概率值和分类标签别混着看跑完 ARIMA 和 LR 之后你会得到两种完全不同的输出。ARIMA 给出的是连续价格预测线LR 给出的是 0/1 标签和上涨概率。很多初学者把它们画在同一个坐标系里对比这是不对的。正确做法是分别评估ARIMA 用均方根误差或平均绝对误差衡量预测价格与实际价格的偏差LR 用准确率、精确率和召回率衡量分类效果。from sklearn.metrics import mean_squared_error, mean_absolute_error forecast_index test.index[:len(forecast)] if len(forecast_index) 0: forecast_index pd.date_range(starttest.index[0], periodslen(forecast), freqB) rmse mean_squared_error(test[Close].values[:len(forecast)], forecast, squaredFalse) mae mean_absolute_error(test[Close].values[:len(forecast)], forecast) print(fARIMA RMSE: {rmse:.4f}, MAE: {mae:.4f})这里注意一个隐蔽问题forecast 的长度可能和 test 的交易日数不一致直接用 mean_squared_error 会报维度错误。我一般取两者长度的最小值做截断或者在训练 ARIMA 时显式传入 test 的长度这样预测区间就是确定的值。ARIMA 在价格水平上的评估指标比较直观但你要明确认识到价格预测的 RMSE 本质上包含价格绝对水平的影响股价 100 块的 RMSE 和 1000 块的 RMSE 不在一个尺度上横向对比模型要统一到收益率维度才公平。4. LSTM 与 KNN非线性模型能追到什么程度4.1 LSTM 输入格式与网络结构把二维表格变成三维序列张量LSTM 适合股价预测的原因很简单它有门控机制能记住长期依赖关系。比如某些股票在财报发布前后的走势模式可能和 30 天前的资金流向有关普通线性模型学不到这种跨度LSTM 理论上可以。但理论归理论落地时第一步就会卡住数据格式。我在 2.3 节已经构造好了 (样本数, lookback, 特征数) 的张量现在直接定义网络。注意 PyTorch 的 LSTM 层接收的输入形状是 (batch, seq_len, input_size)输出是 (batch, seq_len, hidden_size)我们通常只取最后一个时间步的隐藏状态送入全连接层。import torch.nn as nn class LSTMPredictor(nn.Module): def __init__(self, input_size5, hidden_size64, num_layers2, dropout0.2): super().__init__() self.lstm nn.LSTM(input_size, hidden_size, num_layers, batch_firstTrue, dropoutdropout) self.fc nn.Linear(hidden_size, 1) def forward(self, x): out, (h_n, c_n) self.lstm(x) last_hidden h_n[-1] return torch.sigmoid(self.fc(last_hidden))这里说几个参数的实际感受。hidden_size 是隐藏单元数64 是起步128 能捕捉更复杂模式但训练慢一倍对日线数据来说 64 通常足够。num_layers 是 LSTM 层数两层是常见上限再深容易梯度消失训练难度飙升。dropout0.2 只对多层 LSTM 的层间连接生效它的作用是随机屏蔽一部分神经元让网络不过度依赖某个特征的路径这对高噪声的股价数据几乎是必需品。训练时最关键的参数是学习率我一般从 0.001 开始如果 loss 震荡就降到 0.0003。复杂度再高一点的工程还会配合学习率衰减比如每训练十轮把学习率乘 0.9让模型在后期微调。4.2 训练循环与早停不要看到 loss 下降就高兴先看看是不是过拟合LSTM 训练是黑匣子你没法像 LR 那样直接解释每个系数。所以更依赖训练过程的监控训练集 loss 和验证集 loss 都要打印。如果训练 loss 持续下降而验证 loss 在第 20 个 epoch 就开始回升说明过拟合早停机制可以自动停在验证 loss 不再下降的节点。optimizer torch.optim.Adam(model.parameters(), lr0.001) loss_fn nn.BCELoss() best_val_loss float(inf) patience 0 max_patience 10 loss_fn nn.BCELoss() for epoch in range(100): model.train() optimizer.zero_grad() output model(X_train_seq) loss loss_fn(output, y_train_seq.unsqueeze(1).float()) loss.backward() nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step() model.eval() with torch.no_grad(): val_output model(X_test_seq) val_loss loss_fn(val_output, y_test_seq.unsqueeze(1).float()) if val_loss best_val_loss: best_val_loss val_loss patience 0 torch.save(model.state_dict(), best_model.pth) else: patience 1 if patience max_patience: print(fEarly stop at epoch {epoch}) break if epoch % 10 0: print(fEpoch {epoch}, Loss: {loss.item():.4f}, Val Loss: {val_loss.item():.4f})这段代码里有三个容易忽略的点。第一个是 nn.utils.clip_grad_norm_ 梯度裁剪它把所有参数的梯度范数限制在 1.0 以内防止训练到某一步 loss 突然变成 NaN。股价序列经常出现极端值梯度爆炸非常常见这个函数是后悔药。第二个是 BCELoss 配合 sigmoid 输出这是一个完整的二分类配置如果你把 sigmoid 去掉用 BCEWithLogitsLoss整体数值更稳定。第三个是 early stop 逻辑连续 10 轮验证 loss 没创新低就停并保存历史最优模型这个“最优模型”才是测试时要加载的权重。4.3 KNN 在股价场景下的另类用法找历史相似片段而不是直接回归KNN 在股价预测里的定位很微妙。传统用法是把过去 N 天的特征作为样本点用欧氏距离找当前状态最接近的历史交易日然后取这些邻近样本的平均涨跌作为预测。这不是真正的“预测未来”而是“历史会重复”的假设。实现上非常简单代码量比 LSTM 少了一个量级。from sklearn.neighbors import KNeighborsClassifier from sklearn.model_selection import cross_val_score knn KNeighborsClassifier(n_neighbors5, weightsdistance, metriceuclidean) knn.fit(train[feature_cols], train[label_direction]) scores cross_val_score(knn, train[feature_cols], train[label_direction], cv5) print(fKNN CV Accuracy: {scores.mean():.4f} ± {scores.std():.4f}) pred_knn knn.predict(test[feature_cols])n_neighbors 是邻居数这个值对结果影响巨大。K 太小比如 K1模型记住个别样本方差大K 太大比如 K30样本平均把信号磨平了又回到盲目预测。我一般先用交叉验证扫描比如每轮用训练数据做 5 折把 K 从 3 到 15 都跑一遍取平均准确率最高的值。weightsdistance 表示近的样本权重更大这在非平稳市场里更合理因为半个月前的相似行情应该比两年前的相似行情更有参考价值。KNN 的硬伤是计算复杂度预测时它要计算当前样本与所有历史样本的距离。如果全量历史是 20 年日线约 5000 个样本每次预测要扫一遍尚可忍受那如果换成分钟级数据或者全市场扫描KNN 会慢到让你怀疑人生。所以我把 KNN 定位为“轻量级基线模型”或者 LSTM 结果的交叉验证工具而不是生产主力。5. 股价预测的五个坑现象、原因和后悔药都在这里5.1 回测赢了实盘亏原因不是模型坏了是交易成本没算很多人第一次跑完整个 pipeline 兴奋得不行因为测试集准确率上了 60%于是喜滋滋拿去全市场验证结果资金曲线不是横盘就是向下。问题几乎都出在回测环境里没有交易成本。股价预测模型输出的是信号信号变成交易要付手续费、印花税和滑点A 股双边手续费加滑点大约千分之二到千分之三高频交易场景更高。解决方法不是在模型里调参而是在信号处理层加约束比如预测上涨概率低于 0.7 就不交易提高出手质量用交易次数换单笔胜率。还有一个常见问题是信号本身是“明日”的但实际交易要等开盘后才能执行如果你用明日开盘价作为成交价本身就隐含了未来信息。5.2 LSTM 在测试集上画出一条水平线越看越不像预测现象是 LSTM 训练 loss 正常下降验证集预测结果却是一条几乎平坦的线LSTM 输出的概率全部集中在一个均值附近。原因一般是标准化时把测试集和训练集一起 fit 了或者输入序列中包含了价格而非收益率导致模型学到的规律是均值回归。我排查的顺序是先检查数据切分确认 scaler 只用了 train 部分然后检查输入特征把价格类特征排除掉最后看网络结构hidden_size 过大而数据集太小模型直接记住了训练集测试集上退化到输出先验概率。我在 4.2 节故意加了早停逻辑就是为了避免这种记住训练集的情况。5.3 ARIMA 预测结果越来越平稳最后变成一条横线ARIMA 预测多步之后收敛到均值是正常现象这是模型结构决定的但收敛得太快就不正常。常见原因是差分阶数 d 设得太大你把序列差分到完全平稳但信息也被差没了模型只剩“下一步变化量与历史无关”的结论。解决办法是谨慎定 dADF 检验 p 值低于 0.05 就停止差分不要贪多。另一个原因是 p、q 阶数不足可以考虑用信息准则自动搜索而不是拍脑袋。ARIMA 天然擅长短期几步预测你让它预测 60 天它只能给你一个长期均衡值这不叫 bug叫模型边界。5.4 你已经用预测“过去”的方式偷看了“未来”数据泄漏在股价预测里有很多张脸最典型的就是随机打乱数据。很多从图像识别转过来的同学习惯 train_test_split 默认 shuffleTrue直接用在这里就是事故现场模型在训练阶段见过测试期间的行情准确率虚高到吓人。我在第二、三章的代码里都用切片方式切分数据就是在反复提醒这一点。另一种隐蔽的泄漏是特征构造时用了未来信息比如 shift 方向搞反或者标准化时拿全量数据拟合 scaler。检验泄漏的办法很简单把模型预测结果按时间排序画图如果测试集准确率异常高比如超过 70%大概率有泄漏真实股价方向预测能做到 55% 就已经值得认真对待了。5.5 数据源和复权方式不一致训练集和测试集对不上这个坑几乎没人提但遇到的概率极高。免费数据接口经常更新不同时间拉到的同一只股票历史数据复权因子可能不一样或者你训练用前复权预测时接口突然改了复权基准前复权价会整体平移模型特征全乱。解决方法是固定数据快照把原始数据存成 CSV 或 parquet 文件所有实验都从这份快照读取不要每次从接口现拉。如果必须增量更新至少要在日志里记录数据源、复权方式和拉取时间出了问题可以倒查。6. 滚动预测与模型选型矩阵用同一套规则验证四个模型前面已经把四个模型都跑通了最后一个建议是不要用“一次预测定胜负”而是用滚动预测的方式验证模型的稳定性。所谓滚动预测是在测试集上每次只预测下一天然后把真实值并入历史再预测再下一天整个测试集用 walk-forward 的方式跑完。这样模拟了真实交易场景也更容易暴露模型在行情切换时的表现差异。from sklearn.metrics import accuracy_score historical train.copy() test_size len(test) rolling_preds [] for i in range(test_size): current_row test.iloc[i] lr.fit(historical[feature_cols], historical[label_direction]) pred lr.predict(current_row[feature_cols].values.reshape(1, -1))[0] rolling_preds.append(pred) historical pd.concat([historical, current_row.to_frame().T]) rolling_acc accuracy_score(test[label_direction], rolling_preds) print(fRolling LR Accuracy: {rolling_acc:.4f})这段代码每次循环都在新增一个真实样本后重新训练 LR所以是严格无泄漏的评估方式。缺点是慢但结果可信度远高于一次性训练测试。对于 LSTM 和 ARIMA 道理都一样只是要额外按频率重新拟合模型比如 ARIMA 每周重估一次参数LSTM 每月重训一次这样能同时验证模型的时效性和计算代价。最后说下我的选型结论。数据量少、要求可解释、快速出结果选 ARIMA 或 LR数据量大、非线性关系明显、有 GPU 资源LSTM 是值得投入的方向KNN 适合做模型对比的锚点它会告诉你“历史重复”这个朴素假设到底有多强。这四个模型的预测逻辑完全不同对比它们的准确率和资金曲线核心收获不是找到一个必胜模型而是理解每个模型在什么行情下失效。我现在每次动手前都会先想清楚评估口径用滚动预测做验证再谈调参和上线。希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。