Python线性回归股票预测源码实战:从特征工程到批量回测的避坑指南
发布时间:2026/10/2 3:26:48来源:尧图网络
简介这份资源面向具备一定Python基础、希望入门数据挖掘与机器学习实战的学习者聚焦线性回归在股票预测中的完整应用。内容围绕数据获取与清洗、特征工程、模型训练、预测与评估展开涉及pandas数据处理、NumPy特征计算以及移动平均、MACD、RSI等技术指标的构建思路并借助sklearn线性回归完成拟合通过MSE、RMSE与R²衡量预测效果同时点明线性假设的局限与组合其他模型的改进方向。压缩包共2个文件包含1个py源码与1个pdf讲解文档源码对应完整实现流程文档用于辅助理解原理与步骤整体约2.34MB结构精简便于快速上手。目前已有4741人学习下载适合作为课程设计、项目练手或金融预测入门的参考案例帮助读者把数据预处理、特征选择、模型训练与结果评估串联成一条可复用的实践路径。1. 一份能跑通的线性回归股票预测源码到底值不值得拆很多人第一次接触量化或者金融数据分析都是从「用线性回归预测股票价格」这个题目开始的。原因很直接线性回归是最容易上手的机器学习算法股票数据又是最容易获取的时间序列数据两者一结合看起来就是一个完美的入门项目。但真正动手写过的人都知道这件事的坑远比想象中多——用收盘价预测收盘价R² 能到 0.99但模型毫无意义用当天数据预测当天涨跌准确率看着不错实盘一跑就废。这份《Python数据挖掘与机器学习开发实战_基于线性回归的股票预测_优秀案例实例源代码源码.zip》就是围绕这个经典命题展开的一套完整代码包覆盖了从数据获取、特征工程、模型训练到结果可视化的全流程。它适合两类人一是正在做课程设计或毕业设计、需要一份能跑通且逻辑完整的参考实现的学生二是想快速了解 Python 数据挖掘与机器学习在金融场景下怎么落地、但不想从零搭框架的开发者。下面我按实际拆包和复现的顺序把这份资源里的技术点、参数设置和踩坑经验一条条讲清楚。2. 拆开源码包先看什么目录结构与数据流设计拿到一个源码包我一般不会急着跑main.py而是先把目录结构过一遍搞清楚数据从哪来、经过哪些模块、最终输出什么。这份资源的组织方式比较典型属于「教学友好型」——每个环节都有独立脚本方便单独调试也方便你替换成自己的数据。2.1 典型目录布局与各文件职责解压后常见的结构大致如下不同版本可能略有差异但核心模块不会少stock_prediction/ ├── data/ │ ├── raw/ # 原始行情数据通常是 csv │ └── processed/ # 清洗和特征工程后的数据 ├── src/ │ ├── data_loader.py # 数据读取与初筛 │ ├── feature_engineer.py # 技术指标计算、滞后特征构造 │ ├── model_train.py # 线性回归模型训练与评估 │ └── visualize.py # 预测结果绘图 ├── config.py # 路径、参数、股票代码等配置 ├── main.py # 主流程入口 └── requirements.txt # 依赖清单这个布局的好处是职责清晰data_loader.py只管把数据读进来并做基本清洗feature_engineer.py负责把原始 OHLCV 数据转成模型能用的特征矩阵model_train.py专注模型本身visualize.py处理输出。你如果想换成自己的股票池或者调整预测目标只需要改config.py和对应的特征工程逻辑不用动模型训练部分。config.py里通常会有几个关键配置项我一般会先检查这几个# config.py 典型内容 STOCK_CODE 600519 # 股票代码默认可能是茅台或平安 START_DATE 2018-01-01 # 数据起始日期 END_DATE 2023-12-31 # 数据结束日期 PREDICT_DAYS 5 # 预测未来几天 FEATURE_COLS [open, high, low, close, volume, ma5, ma10, ma20, rsi, macd] TEST_SIZE 0.2 # 测试集比例 RANDOM_STATE 42 # 随机种子保证可复现这里PREDICT_DAYS和FEATURE_COLS是最需要关注的两个参数。PREDICT_DAYS决定了你是做「预测明天收盘价」还是「预测未来一周走势」前者更容易过拟合后者更接近实际需求但误差会放大。FEATURE_COLS则直接决定模型输入维度后面讲特征工程时会展开。2.2 数据获取方式与依赖安装这类项目的数据来源通常有两种一是通过tushare、akshare、baostock等接口在线拉取二是直接附带一份历史行情 csv 文件。如果源码包里data/raw/下已经有 csv那说明作者已经帮你把数据固化下来了省去了接口不稳定的麻烦。如果没有就需要自己装数据接口库。依赖安装这一步看着简单但实际翻车率不低。常见做法是# 创建虚拟环境推荐避免污染全局包 python -m venv venv source venv/bin/activate # Linux/Mac # venv\Scripts\activate # Windows # 安装依赖 pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simplerequirements.txt里一般会包含pandas、numpy、scikit-learn、matplotlib、seaborn如果是在线拉数据还会加上tushare或akshare。这里有个血泪经验tushare的老版本和新版本接口差异很大老代码里用的ts.get_hist_data()在新版里已经废弃需要改成pro.daily()并传入 token。如果你跑的时候报AttributeError先检查tushare版本别急着怀疑代码逻辑。提示如果requirements.txt里没有锁定版本号建议手动给scikit-learn和pandas加上版本约束比如scikit-learn1.3.0、pandas2.0.3否则不同时间安装可能因为 API 变动导致fit()或DataFrame.append()报错。数据加载部分的代码逻辑通常是这样的# src/data_loader.py 核心逻辑 import pandas as pd def load_stock_data(filepath): df pd.read_csv(filepath, parse_dates[date]) df df.sort_values(date).reset_index(dropTrue) # 处理缺失值前向填充避免直接 drop 导致时间序列断裂 df df.fillna(methodffill) # 过滤掉成交量异常为 0 的停牌日 df df[df[volume] 0] return df这段代码里parse_dates确保日期列被解析成datetime类型sort_values保证时间顺序正确——时间序列数据如果顺序乱了后面构造滞后特征会全错。fillna(methodffill)用前值填充是金融数据的常见做法因为停牌或节假日没有交易直接删除会破坏时间连续性。最后过滤volume 0是为了排除停牌日这些日子的价格数据没有参考价值。3. 特征工程怎么做才不白费从 OHLCV 到模型输入矩阵线性回归对特征质量极其敏感。如果你直接把open、high、low、close、volume五列丢进去预测close模型会告诉你close的系数接近 1其他系数接近 0R² 高得离谱但这本质上是让模型抄答案。真正有意义的做法是构造滞后特征和技术指标让模型用「过去的信息」预测「未来的价格」。3.1 滞后特征与滚动窗口的构造方法滞后特征的核心思想是今天之前 N 天的价格、成交量、指标才可能对明天的价格有预测价值。常见做法是构造close_lag1、close_lag2、close_lag5等列分别代表前 1 天、前 2 天、前 5 天的收盘价。# src/feature_engineer.py 滞后特征构造 import pandas as pd def add_lag_features(df, lag_list[1, 2, 3, 5, 10]): for lag in lag_list: df[fclose_lag{lag}] df[close].shift(lag) df[fvolume_lag{lag}] df[volume].shift(lag) # 构造预测目标未来第 N 天的收盘价 df[target] df[close].shift(-5) # 去掉因为 shift 产生的 NaN 行 df df.dropna().reset_index(dropTrue) return dfshift(lag)把数据向下移动 lag 行shift(-5)则是向上移动用来构造「未来第 5 天收盘价」作为预测目标。dropna()会删掉开头和结尾因为移位产生的空值行这一步不能省否则sklearn的fit()会直接报错说输入包含 NaN。滚动窗口特征则是计算过去 N 天的均值、标准差等统计量def add_rolling_features(df, windows[5, 10, 20]): for w in windows: df[fma{w}] df[close].rolling(windoww).mean() df[fstd{w}] df[close].rolling(windoww).std() df[fvol_ma{w}] df[volume].rolling(windoww).mean() df df.dropna().reset_index(dropTrue) return dfrolling(windoww).mean()就是常说的 MA5、MA10、MA20 均线std()是波动率指标。这些特征在金融分析里很常用但要注意滚动窗口计算同样会产生前w-1行的 NaN必须再次dropna()。3.2 技术指标计算与特征筛选除了均线RSI 和 MACD 也是这类项目里高频出现的技术指标。RSI 衡量超买超卖MACD 捕捉趋势变化两者都能用pandas手动实现不需要额外装TA-Lib。def add_rsi(df, period14): delta df[close].diff() gain delta.where(delta 0, 0).rolling(windowperiod).mean() loss (-delta.where(delta 0, 0)).rolling(windowperiod).mean() rs gain / loss df[frsi{period}] 100 - (100 / (1 rs)) return df def add_macd(df, fast12, slow26, signal9): ema_fast df[close].ewm(spanfast, adjustFalse).mean() ema_slow df[close].ewm(spanslow, adjustFalse).mean() df[macd] ema_fast - ema_slow df[macd_signal] df[macd].ewm(spansignal, adjustFalse).mean() df[macd_hist] df[macd] - df[macd_signal] return dfRSI 的period默认 14 天MACD 的fast12、slow26、signal9是行业标准参数一般不需要改。ewm(adjustFalse)表示指数加权移动平均adjustFalse让计算结果与常见交易软件一致如果设成True前期数据会有偏差。特征构造完之后不是所有列都适合丢进模型。我一般会做两步筛选先去掉与目标相关性极低的列再检查特征之间的共线性。close_lag1和ma5相关性往往很高同时保留会让线性回归系数不稳定。# 特征筛选示例 corr_matrix df[feature_cols [target]].corr() # 找出与 target 相关性绝对值小于 0.1 的特征 low_corr corr_matrix[target][abs(corr_matrix[target]) 0.1].index.tolist() # 找出特征之间相关性大于 0.95 的列保留其中一个这一步没有固定标准但经验值是与目标相关性低于 0.1 的特征基本可以丢掉特征之间相关性高于 0.95 的保留一个即可。特征太多不仅训练慢还容易过拟合。4. 模型训练与评估线性回归的参数、划分与指标解读特征矩阵准备好之后模型训练本身反而简单——sklearn的LinearRegression只有几个参数真正需要花心思的是数据划分方式和评估指标的选择。4.1 时间序列划分与防止数据泄露普通机器学习用train_test_split随机划分就行但时间序列绝对不能随机打乱。你今天的数据不能用来预测昨天的价格这是基本逻辑。常见做法是按时间顺序切分# src/model_train.py 时间序列划分 import numpy as np from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score def train_model(df, feature_cols, target_coltarget, test_size0.2): # 按时间顺序划分前 80% 训练后 20% 测试 split_idx int(len(df) * (1 - test_size)) train df.iloc[:split_idx] test df.iloc[split_idx:] X_train train[feature_cols].values y_train train[target_col].values X_test test[feature_cols].values y_test test[target_col].values model LinearRegression(fit_interceptTrue) model.fit(X_train, y_train) y_pred model.predict(X_test) mse mean_squared_error(y_test, y_pred) mae mean_absolute_error(y_test, y_pred) r2 r2_score(y_test, y_pred) print(fMSE: {mse:.4f}, MAE: {mae:.4f}, R2: {r2:.4f}) return model, y_test, y_predfit_interceptTrue是默认值表示模型会计算截距项。如果你的特征已经做过标准化截距项仍然需要保留因为目标值不一定在零附近。test_size0.2表示最后 20% 的数据用于测试这个比例可以根据数据量调整——数据少于 500 条时建议用 0.3数据多的时候 0.1 到 0.2 都行。评估指标里MSE 对异常值敏感MAE 更稳健R² 表示模型解释了目标变量多少方差。但要注意股票价格预测的 R² 高不代表模型有用因为价格本身有强趋势性模型可能只是学会了「明天价格约等于今天价格」。真正有参考价值的是 MAE 相对于股价的比例比如股价 100 元MAE 是 2 元那误差约 2%还算能接受如果 MAE 是 10 元那模型基本没有实用价值。4.2 系数解读与模型诊断线性回归的好处是可解释性强训练完之后可以看每个特征的系数# 输出特征系数 coef_df pd.DataFrame({ feature: feature_cols, coef: model.coef_ }).sort_values(coef, keyabs, ascendingFalse) print(coef_df)系数绝对值越大说明该特征对预测结果影响越大。正系数表示特征增加会推高预测价格负系数则相反。如果发现close_lag1的系数接近 1而其他特征系数都很小说明模型基本在抄最近一天的价格这时候需要考虑去掉close_lag1或者改用「预测涨跌幅」而不是「预测价格」。残差分析也是必要的诊断步骤。如果残差图呈现明显的规律性比如随时间递增或递减说明模型遗漏了某些重要特征或者目标变量和特征之间不是线性关系。import matplotlib.pyplot as plt residuals y_test - y_pred plt.figure(figsize(10, 4)) plt.subplot(1, 2, 1) plt.scatter(y_pred, residuals, alpha0.5) plt.axhline(y0, colorr, linestyle--) plt.xlabel(Predicted) plt.ylabel(Residuals) plt.subplot(1, 2, 2) plt.hist(residuals, bins30) plt.xlabel(Residual) plt.show()理想情况下残差应该随机分布在零线两侧直方图接近正态分布。如果残差随着预测值增大而增大说明存在异方差性可以考虑对目标变量取对数后再建模。5. 避坑与常见问题跑不通、结果差、结果每次不一样这一章集中说几个高频翻车点。这些问题在课程设计答辩和实际项目里都经常遇到提前知道能省不少时间。5.1 现象代码报错KeyError或ValueError原因通常是列名不匹配或数据里包含 NaN。比如config.py里写的FEATURE_COLS包含rsi但feature_engineer.py里生成的列名是rsi14df[feature_cols]就会直接报KeyError。另一种情况是特征工程做完忘了dropna()sklearn的fit()遇到 NaN 会抛ValueError: Input contains NaN。解决办法在model_train.py开头加一行print(df.columns.tolist())确认实际列名在特征工程每个步骤后加print(df.isnull().sum())定位是哪一步产生了空值。养成「先看列名和空值再跑模型」的习惯能省掉大量调试时间。5.2 现象R² 很高但预测结果明显滞后于真实价格这是最典型的「抄答案」问题。模型学到的本质是target ≈ close_lag1因为target是未来第 5 天收盘价而close_lag1是昨天收盘价两者在短期内高度相关。结果就是预测曲线整体比真实曲线向右平移了几天看起来拟合很好实际上没有任何预测能力。解决办法把预测目标从「未来价格」改成「未来涨跌幅」即target (close.shift(-5) - close) / close。这样模型必须真正从特征里学习涨跌信号而不是简单复制最近价格。改完之后 R² 通常会大幅下降但模型才有实际意义。5.3 现象每次运行结果不一样原因通常是随机种子没有固定。train_test_split的random_state参数、LinearRegression本身没有随机性但如果代码里用了sklearn的StandardScaler或者其他带随机性的预处理没设random_state就会导致结果波动。另外如果数据是从在线接口实时拉取的每次拉到的数据可能略有差异也会导致结果不同。解决办法在config.py里统一定义RANDOM_STATE 42所有涉及随机的地方都传入这个参数。数据尽量固化成 csv 文件避免每次运行都重新拉取。如果必须在线拉取在拉取后立刻保存一份本地副本后续调试用本地文件。5.4 现象MAE 很小但实盘完全不能用MAE 小可能是因为股价本身波动小比如银行股每天涨跌不到 1%模型预测「明天价格等于今天价格」MAE 自然很小。但这不代表模型有预测能力。评估时要看 MAE 相对于股价的比例以及模型在涨跌方向上的准确率而不是只看绝对误差。解决办法增加方向准确率指标即预测涨跌方向与真实方向一致的比例。如果方向准确率接近 50%说明模型和抛硬币差不多MAE 再小也没有实战价值。5.5 现象换了股票代码后模型效果急剧下降不同股票的波动特性差异很大用茅台数据训练出来的模型直接套用到小盘股上效果通常很差。线性回归的系数是针对特定数据分布学出来的换股票意味着数据分布变了系数不再适用。解决办法每只股票单独训练模型或者把股票代码作为类别特征加入模型需要做 one-hot 编码。更稳妥的做法是先用一只股票把流程跑通确认每个环节都正确再扩展到多只股票。6. 从单只股票到批量验证一个可复用的回测小技巧把单只股票的流程跑通之后下一步自然是验证这套方法在多只股票上是否稳定。我一般会写一个批量回测脚本把股票池里的每只股票都跑一遍记录 MAE、方向准确率和训练时间然后按方向准确率排序。这样能快速看出哪些股票适合线性回归、哪些不适合。# batch_backtest.py 批量回测核心逻辑 import pandas as pd from src.data_loader import load_stock_data from src.feature_engineer import add_lag_features, add_rolling_features, add_rsi, add_macd from src.model_train import train_model stock_list [600519, 000858, 601318, 000333, 600036] results [] for code in stock_list: try: df load_stock_data(fdata/raw/{code}.csv) df add_lag_features(df) df add_rolling_features(df) df add_rsi(df) df add_macd(df) feature_cols [c for c in df.columns if c not in [date, target]] model, y_test, y_pred train_model(df, feature_cols) # 计算方向准确率 direction_acc ((y_pred - y_test.shift(1)) * (y_test - y_test.shift(1)) 0).mean() results.append({code: code, direction_acc: direction_acc}) except Exception as e: print(f{code} failed: {e}) result_df pd.DataFrame(results).sort_values(direction_acc, ascendingFalse) print(result_df)这段脚本的关键在于direction_acc的计算比较预测变化方向和真实变化方向是否一致。y_test.shift(1)是前一天的收盘价y_pred - y_test.shift(1)是预测的涨跌幅度y_test - y_test.shift(1)是真实涨跌幅度两者相乘大于零说明方向一致。这个指标比 MAE 更能反映模型的实战价值。跑完批量回测后我一般会挑方向准确率最高的 2 到 3 只股票再单独看它们的残差图和预测曲线确认不是偶然结果。如果某只股票方向准确率超过 60%那说明线性回归在这只股票上确实捕捉到了一些规律值得进一步优化特征。如果所有股票都在 50% 附近那就要考虑换模型了——线性回归的假设太强可能不适合这个场景。从那以后我每次拿到新的股票预测代码都会先跑一遍批量回测用方向准确率而不是 R² 来判断模型是否值得继续投入时间。这个习惯帮我避开了很多「看起来很美」的坑。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网