机器学习股价预测源码:LR、LSTM、ARIMA、KNN四模型实现与回测

发布时间:2026/9/23 18:24:07
机器学习股价预测源码:LR、LSTM、ARIMA、KNN四模型实现与回测 简介Python实现机器学习股价预测源码集成LR、LSTM、ARIMA、KNN等主流模型针对股票行情数据完成数据预处理、特征分析与多模型训练预测的完整流程适用于毕业设计、期末大作业及课程设计场景也可帮助机器学习预测初学者快速上手。压缩包共31个文件以5个Python脚本为核心按功能拆分为模型定义、预测、回测、绘图及工具模块并配以17张结果图、2个基于ECharts的可视化HTML页面和2个CSV分析文件直观呈现趋势分解、季节性、移动平均、自相关、LSTM/KNN对比等分析结果另含Excel数据表与说明文档。整体仅1.45MB轻量易部署文件类型覆盖源码、图片、网页、表格等多种格式。项目由作者精心编写代码注释详尽新手也能看懂已经严格调试可正常运行界面美观、操作简单已有372人学习。下载后简单配置即可用于毕业设计或期末大作业且支持替换数据源做二次开发具有较高的实用价值。1. 别只盯着 LSTM这份能跑通四种模型的机器学习股价预测源码意味着什么做机器学习股价预测的毕业设计或期末大作业最怕的不是模型不够新而是代码和数据全是散的有模型没数据、有数据没可视化、跑通了一个 LSTM 却说不清它能干什么。这份用 LR、LSTM、ARIMA、KNN 多模型实现的机器学习股价预测源码刚好把闭环补齐——从 AMZN 历史行情到训练、预测、回测、资金曲线全覆盖代码带注释改改数据路径就能跑。它适合正在做毕设、期末大作业、课程设计的学生也适合想用完整案例理解主流时间序列模型差异的入门者。价值不在某个模型调得多极致而在于给你一条主线数据怎么进、模型怎么选、预测怎么评估、回测怎么验证。下面按这条线拆。2. 先拆文件再跑代码项目结构与数据流向拿到压缩包直接跑代码往往是翻车的第一步。这个项目文件命名有规律先花十分钟理清层次后面每个模型跑起来都能快速定位问题。我把整个项目分成三层来看数据与工具层、模型与业务层、可视化层。2.1 数据文件与工具函数AMZN.csv 和 myutils.py数据层的核心是 AMZN.csv一份亚马逊的日线历史行情。打开看一眼就知道结构日期、开盘、最高、最低、收盘、成交量之类的字段。预测股价大部分模型真正用得多的是收盘价但也有人把开盘价、最高最低价拼成特征一起喂进去这个后面讲 LSTM 时再说。myutils.py 是工具函数集合代码注释里把每个函数的职责写得很清楚。这类文件里通常包含三类函数读数据函数把 CSV 读成 DataFrame顺便做日期索引和缺失值处理画图函数把收盘价、预测值和真实值画到同一张图里评估函数计算 MAE、RMSE、MAPE 这些误差指标。它不承担模型逻辑却是所有模型脚本共用的底座。表格列一个文件与职责对照跑之前先对一遍文件职责关键点AMZN.csv日线行情数据日期索引、OHLCV 字段myutils.py通用工具函数读数据、画图、计算误差requirements.txt依赖清单一条命令装完环境models.py四种模型封装LR/ARIMA/KNN/LSTMforecast.py预测主流程调模型、出预测、存结果backtest.py回测逻辑资金曲线、持有期收益draw_line.py绘图脚本补充生成对比图注意源码里的图片、HTML 都是运行产物不是输入。你重新跑一遍会重新生成不需要手动改这些文件。2.2 模型层与业务层models.py、forecast.py、backtest.py 怎么分工models.py 把所有模型封装成统一接口这是这个项目最值得抄的地方。常见做法是每个模型写一个类或者一个函数输入训练数据和预测步长输出预测结果。这样 forecast.py 里不需要到处散落模型代码调用时传参就行。forecast.py 是预测主流程。它负责读数据、切分训练集和测试集、调用 models.py 里的模型、把预测结果反归一化、计算误差指标最后把真实值和预测值画在一起。你改模型参数主要改这个脚本顶部的配置区比如预测步长、窗口大小、LSTM 的 epochs。backtest.py 是回测脚本读模型生成的每日预测收益率模拟按信号买卖的账户变化输出每日资金情况图和持有期收益率情况图同时写一份个股持有情况分析.csv。回测的意义是回答“这个模型策略到底能不能赚钱”而不是只看预测曲线贴得好不好看。draw_line.py 是辅助绘图脚本。如果主流程生成的图不够用或者你想把某个模型的预测单独拉出来对比用它补图。2.3 可视化产物matplotlib 静态图与 ECharts 动态页面项目里 images 目录下有大量 png 图trend.png、seasonal.png、decompose.png、moving_average.png、autocorrelation.png、partial_auto.png、periodogram.png 这些是时间序列分析图LSTM.png、knn.png、arima.png、linearRegression.png 是四个模型的预测效果图prophet.png 和 prophet2.png 还做了 Prophet 的对比。从图名能看出作者当时把探索性分析做得很全。HTML 目录里是两份动态可视化每日资金情况图.html 和持有期收益率情况图.html基于 echarts.min.js 渲染。这两份是回测的可视化出口比 matplotlib 静态图更适合放进论文附录或答辩展示。打开 HTML 之前需要先跑完 forecast.py 和 backtest.py 生成结果数据因为页面里的曲线是动态读取脚本输出的。另外还有各月股票队列.xlsx 和个股持有情况分析.csv这两个是回测过程中的中间产物前者按月份整理了候选股票后者记录逐日持仓明细。跑回测前可以清空旧文件避免跟新结果混淆。3. 四种模型的选型逻辑与核心实现LR、ARIMA、KNN、LSTM这章是整个项目的重心也是答辩时导师大概率追问的地方。四种模型的数学原理在课本里都有但代码里怎么落地、参数怎么设才是源码里真正值钱的部分。我的建议是不要一上来就跑 LSTM按上面的顺序从简单模型开始每个模型跑通了再换下一个这样你对“哪个环节出了问题”会有更清晰的判断。3.1 LR线性回归不是玩具它是验证数据质量的探针线性回归在股价预测里常被当成最朴素 baseline。很多人瞧不上它但它有个独特价值如果连 LR 都跑不出一个合理的趋势方向说明数据清洗、特征构造、训练测试切分大概率出了问题。这个项目把 LR 放进来也是同样的用意。代码落地时LR 把所有历史时间步的数值当作特征预测下一个时间点。比如用前 60 天的收盘价预测第 61 天def build_lr_dataset(series, lookback60): X, y [], [] for i in range(lookback, len(series)): X.append(series[i - lookback:i]) y.append(series[i]) return np.array(X), np.array(y) # 按时间顺序切分严禁随机打乱 train_size int(len(series) * 0.8) X_train, X_test X[:train_size], X[train_size:] y_train, y_test y[:train_size], y[train_size:] from sklearn.linear_model import LinearRegression model LinearRegression() model.fit(X_train, y_train) y_pred model.predict(X_test)lookback 是核心参数取 60 天在股票日线上对应约一个季度的交易日。这个值决定模型能看到多长的历史太小会欠拟合太大特征维度变高、计算量变大。线性回归本身没有太多超参数要调真正要确认的是特征里不要混入未来数据——切分时一旦用了包含测试集标签的样本做训练评估指标会好看得骗人。3.2 ARIMA差分阶数 d 和 ACF/PACF 判读ARIMA 是统计模型全称自回归积分滑动平均三个参数 (p, d, q) 各管一段逻辑。d 是差分阶数让非平稳序列变平稳p 是自回归阶数看 PACF 图截尾确定q 是滑动平均阶数看 ACF 图截尾确定。项目里有一张 autocorrelation.png 和一张 partial_auto.png就是画这两个图。跑 ARIMA 之前先画图判阶而不是直接让函数自动搜参。自动搜参比如 pmdarima 的 auto_arima跑出来往往容易过拟合尤其在短序列上。from statsmodels.tsa.stattools import adfuller from statsmodels.graphics.tsaplots import plot_acf, plot_pacf from statsmodels.tsa.arima.model import ARIMA # 1. 先做 ADF 检验p 值大于 0.05 说明序列非平稳 adf_result adfuller(series) print(ADF p-value:, adf_result[1]) # 2. 如果不平稳做一阶差分 series_diff series.diff().dropna() # 3. 判阶后建模 model ARIMA(series, order(p, d, q)) fitted model.fit() # forecast.py 里一般把前向预测步长设置成 30 或 60 forecast fitted.forecast(steps30)d 的取值不能拍脑袋差分太多会丢失趋势信息一般股票收盘价一阶差分就够。p 和 q 的判读有经验成分看到 ACF 在滞后 1 到 2 阶截尾、PACF 拖尾就往 MA 方向想反向则往 AR 方向想。这个项目里 ARIMA 的主要坑不是收敛而是预测结果走平这个放到第 5 章展开。3.3 KNN高维相似性匹配与滑动窗口切分KNN 用于时间序列预测的思路是把最近 N 天的走势当成一个特征向量去历史数据里找最相似的 N 天走势然后取这些相似日下一日的实际值做平均或加权平均作为预测值。它不假设线性关系也不假设平稳性是一种纯粹的非参数做法。实现时要注意特征向量的长度和 K 值。特征长度就是滑动窗口K 值控制取多少个相似样本from sklearn.neighbors import KNeighborsRegressor def build_knn_dataset(series, window30): X, y [], [] for i in range(window, len(series) - 1): X.append(series[i - window:i]) y.append(series[i 1]) # 用前 window 天预测下一天 return np.array(X), np.array(y) knn KNeighborsRegressor(n_neighbors5, weightsdistance) knn.fit(X_train, y_train) y_pred knn.predict(X_test)window 取 30 表示用一个月的交易日形态做匹配K 取 5 是经验值太小对噪声敏感太大结果被平均得失去形态。weights‘distance’ 会让越相似的样本权重越高在股价这种噪声很强的序列上比默认的 uniform 更稳。KNN 的问题在于它天然有滞后性预测值会贴近最近的已知值导致曲线比真实值慢半拍。3.4 LSTM时间步长、归一化、训练轮数的组合拳LSTM 是这四种模型里唯一真正能学长短期依赖的也是答辩时最容易出彩也最容易翻车的部分。翻车多半不是因为模型原理不懂而是三个工程细节没做到位。第一是归一化。股价绝对值随行情变化LSTM 用 tanh 和 sigmoid 激活函数输入不归一化梯度容易出问题。常见做法是 MinMaxScaler 缩放到 0 到 1 区间。第二是时间步长。LSTM 输入形状是 (样本数, 时间步长, 特征数)把二维数据 reshape 成三维时维度写错是常态。第三是 epochs 和 batch_size。epochs 太小欠拟合太大过拟合到把训练集的噪声背下来。from sklearn.preprocessing import MinMaxScaler from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense scaler MinMaxScaler(feature_range(0, 1)) scaled scaler.fit_transform(series.reshape(-1, 1)) def build_lstm_dataset(scaled, lookback60): X, y [], [] for i in range(lookback, len(scaled)): X.append(scaled[i - lookback:i, 0]) y.append(scaled[i, 0]) return np.array(X), np.array(y) X, y build_lstm_dataset(scaled, lookback60) X X.reshape(X.shape[0], X.shape[1], 1) # (样本, 时间步, 特征数) model Sequential() model.add(LSTM(units50, return_sequencesTrue, input_shape(60, 1))) model.add(LSTM(units50)) model.add(Dense(1)) model.compile(optimizeradam, lossmse) model.fit(X_train, y_train, epochs50, batch_size32, validation_split0.1) y_pred model.predict(X_test) y_pred scaler.inverse_transform(y_pred) # 反归一化回真实价格units50 是第一层 LSTM 的记忆单元数回传序列给第二层 LSTM 用最后一层 Dense(1) 输出单值。epochs50 在股价数据上够用再高就要配合早停不然验证集 loss 曲线会在某个点开始反弹。模型预测完之后反归一化这步绝对不能漏漏了预测值全在 0 到 1 之间跟真实股价完全对不上。优化器用 adam 是大多数序列任务的安全选择学习率保持默认 0.001 就行。如果你发现 loss 下降特别慢优先检查数据顺序有没有在训练前被打乱——时序数据 shuffle 后 LSTM 就学不到时间依赖了。4. 从预测到回测跑通一版完整流程前面把模型和参数讲清楚了这一章走一遍完整流程环境准备、预测主流程、回测验证。全程跟着操作大概半小时能跑出第一版结果。4.1 环境准备与依赖安装项目自带 requirements.txt这是最省心的环境入口。先用 conda 或 venv 建一个干净环境避免把系统 Python 装乱然后一条命令装完python -m venv venv source venv/bin/activate # Windows 下是 venv\Scripts\activate pip install -r requirements.txt装完后建议手动确认一下关键包能不能 import这是最容易静默失败的一步。tensorflow 和 statsmodels 是两座大山前者版本冲突常见后者依赖 numpy 版本敏感。如果 LSTM 相关代码报错优先看是不是 tensorflow 版本跟 Python 版本不匹配。我的习惯是装完立刻跑一个最小导入测试import pandas, numpy, sklearn, statsmodels, tensorflow任何一行报错都先解决再往下走。4.2 预测主流程拆分、建模、反归一化、误差评估以 forecast.py 为例核心流程固定五步。第一步读入 AMZN.csv 并取收盘价列第二步按时间顺序切分训练集和测试集注意不能用随机切分第三步调用 models.py 里对应的模型第四步反归一化并计算误差第五步画出真实值与预测值对比图。# forecast.py 核心流程示意 import pandas as pd from myutils import load_data, evaluate_metrics, plot_compare df load_data(AMZN.csv) series df[close] train_size int(len(series) * 0.8) train, test series[:train_size], series[train_size:] # 以线性回归为例其余模型在 models.py 中按相同接口调用 from models import LinearModel model LinearModel(lookback60) pred model.fit_predict(train, test) rmse evaluate_metrics(test[60:], pred, metricrmse) plot_compare(test[60:], pred, save_pathimages/linearRegression.png)train_size 取 80% 是时间序列项目最常见的拆分比例。注意这里 test 里前 60 个点无法被预测因为模型需要 lookback60 的历史窗口所以误差评估和画图都必须从第 60 个点开始对齐。对不齐是新手最容易犯的错把预测序列和真实序列直接相减导致所有误差指标集体失真。4.3 回测与资金曲线backtest.py 的核心逻辑回测前要明确一个概念预测准确率和策略收益率不是一回事。预测曲线贴得好不代表按预测信号买卖能赚钱因为还有手续费、滑点、持有期这些因素。backtest.py 做的事是把预测结果翻译成一组交易信号再模拟账户资金变化。# backtest.py 核心逻辑示意 capital 100000 # 初始资金 position 0 # 持仓数量 for i in range(len(pred_signal)): if pred_signal[i] 0 and position 0: # 预测上涨且空仓买入 position capital / price[i] capital 0 elif pred_signal[i] 0 and position 0: # 预测下跌且有持仓卖出 capital position * price[i] position 0 total_value capital position * price[-1]这里最值得学的是将信号抽象为 pred_signal 这一层模型输出连续预测值回测脚本根据阈值把它离散成 1、0、-1 三个状态。阈值怎么定是回测调参的核心通道。信号生成方式也要记录形成文档答辩时说明白“为什么设这个阈值”比贴代码更有说服力。项目输出两份可视化和一份 CSV每日资金情况图.html 展示账户总价值的逐日变化持有期收益率情况图.html 展示每笔交易的持有期收益分布个股持有情况分析.csv 则记录了每日持仓明细。答辩时这套数据链讲下来比单张预测曲线有说服力得多。5. 避坑清单五个最容易翻车的细节与排查思路这个项目整体能跑但模型之间的差异非常大不同环境、不同数据、不同参数下翻车姿势各不相同。下面五条是我拆完代码后最想划重点的坑。5.1 LSTM 输出近乎一条直线现象训练 loss 正常下降但预测曲线一条水平线完全看不出涨跌。原因最常见的是数据顺序被 shuffle 破坏了。LSTM 依赖时间顺序如果 fit 时把训练集 shuffleTrue模型学到的几乎是噪声。另外反归一化丢失也会导致预测值被压缩在一个极小区间里肉眼看着像直线。解决训练时强制 shuffleFalse确认归一化和反归一化成对出现。还有一个隐性坑是时间步长太长、数据量太小LSTM 每一批样本高度相似模型退化成输出均值。把 lookback 从 60 降到 30 再试。5.2 ARIMA 预测结果永远走平现象ARIMA 前几个预测值贴近最后一个真实值之后逐渐趋于一条水平线。原因这是 ARIMA 在非平稳序列上做多步预测的正常行为。模型把序列当成随机游走未来所有值的最优估计就是最后一个已知值所以预测曲线会拉平。根源是差分阶数或模型阶数没选对序列中真正可学的自相关结构没有被捕获。解决先跑 ADF 检验确认差分后的平稳性再检查 ACF/PACF 判定的 p、q 是否合理。如果你要预测 60 天而模型 p、q 都很小拉平几乎不可避免。一个折中是缩短预测步长到 7 到 14 天ARIMA 在短周期上仍有参考价值。5.3 KNN 预测曲线整体滞后一天现象KNN 预测值和真实值走势一致但整条曲线沿着时间轴向右平移了一天。原因这是 KNN 匹配历史形态后的均值效应预测值天生偏向于最近见过的值。股价序列里当前价格本身就是最强的预测因子KNN 会高权重地贴近它于是产生了一天滞后。解决把预测目标从预测下一天改成预测未来第 N 天让模型学习更长跨度的映射关系滞后感会减小但误差会变大。或者对预测结果做一阶差分修正用预测变化量叠加到当前价格上。这是个取舍问题没有免费午餐。5.4 回测资金曲线和实际行情对不上现象单看模型预测曲线很准但回测资金曲线大幅亏损或与前一版运行结果对不上。原因一是预测是基于历史数据生成的回测时用到了未来函数——比如用全量数据归一化的 scaler 去处理训练集导致训练时已经看过测试集的信息。二是回测信号和实际价格序列没有严格对齐买卖发生在错误的时间点。解决归一化只在训练集上 fit测试集用 transform不能在整段数据上直接 fit。回测中平仓、开仓都基于当日收盘后信号、次日执行避免用当日未来信息下单。改完后再跑一遍资金曲线会立刻变得合理。5.5 依赖版本漂移导致结果不可复现现象同一份代码不同时间跑误差指标有差异换一台电脑跑完LSTM 预测曲线明显不同。原因LSTM 训练涉及随机初始化但更大的问题在于 numpy、scikit-learn 和 statsmodels 的版本差异会改变部分算法实现细节比如 ARIMA 的优化器迭代KNN 的默认距离计算逻辑在不同版本下的浮点精度不同。有项目因为 sklearn 版本升级KNN 的默认 metric 发生变化预测结果整体偏移。解决固定虚拟环境中的包版本。跑通后立刻执行pip freeze locked_requirements.txt把当前可复现环境的版本锁下来。答辩前用锁定的 requirements 重新部署一次确认能复现同一条曲线。要快速验证就看 random seed 有没有设置——LSTM 代码里没有固定 seed 的情况下结果波动是正常的加一句np.random.seed(42)和tf.random.set_seed(42)能压住大部分差异。6. 一个验证模型的硬习惯先画滞后图和残差分布模型跑完第一版之后不要急着看误差指标或调参。我会强迫自己先做一件事把预测值和真实值的滞后关系画出来再把残差分布画出来。这两张图能筛掉八成看起来不错但实际不可用的模型。滞后图的画法是横轴是真实值纵轴是预测值理想情况下所有点落在 yx 对角线上。如果点云整体偏在对角线下方说明模型系统性低估如果点云呈一团斜向带状围绕在“预测值 上一日真实值”的直线附近说明模型基本在复制上一天的价格属于滞后性主导这种事在 KNN 上尤其常见。把两条参考线画进同图一眼就能看出预测到底学到的是真实信号还是历史惯性。残差分布的做法是把“真实值 - 预测值”的序列画成直方图再算残差和滞后项的相关性import numpy as np import matplotlib.pyplot as plt residual y_test - y_pred fig, axes plt.subplots(1, 2, figsize(12, 4)) axes[0].scatter(y_test, y_pred, alpha0.5, s10) axes[0].plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], r--) axes[0].set_xlabel(真实值) axes[0].set_ylabel(预测值) axes[1].hist(residual, bins30, edgecolorwhite) axes[1].set_xlabel(残差) lag_corr np.corrcoef(y_pred, np.roll(y_test, 1))[0, 1] print(预测值与滞后一阶真实值相关系数:, lag_corr) plt.tight_layout() plt.show()正态性检验不是重点重点在两点残差直方图是否近似对称以及预测值和滞后一阶真实值的相关系数是不是高得离谱。相关系数大于 0.95模型基本没学到什么有效信息纯粹在复读昨天的价格。这种情况下 MAE 再低都没有意义因为换一个“预测值 昨天真实值”的傻瓜策略也能拿到同样低的 MAE。这个习惯救过我很多次从那以后我每次做时间序列项目无论用什么模型都会把这两张图作为强制关卡过了关才谈调参。完整的复现路径其实不复杂——按第 2 章理清文件关系按第 3 章理解模型参数按第 4 章跑通流程再按第 5 章排查遇坑最后用上面的方式验证模型真伪。希望这份源码能帮你少走一些弯路把时间留给真正有价值的模型分析希望帮到你。本文还有配套的精品资源点击获取