☰
机器学习股票预测课设实战:从源码包到可复现预测流水线
2026/10/9 12:42:49 网站建设 项目流程

简介:这份资源是2024年课程设计级别的机器学习股票预测算法项目源码包,面向计算机、人工智能、通信工程、自动化等专业的高校学生与科研从业者,可用于毕业设计、课程设计、作业提交或项目初期立项演示,也适合具备一定基础的学习者进阶练手。压缩包共26个文件,约2.57MB,包含6个ipynb交互式笔记、5个py脚本、6个xml配置、3个csv数据文件,以及md说明、docx报告、txt依赖清单等,覆盖数据获取、特征工程、模型训练与回测的完整链路。内容涉及K线数据入库、小市值结合盈利指标过滤策略验证、FFT滤波、单票LSTM预测与sklearn回测等实践模块,并附有项目说明与参考报告,便于理解整体设计思路。目前已有53人学习下载,适合希望快速上手股票预测实战、借鉴代码结构或在此基础上二次开发的学习者参考使用。

1. 机器学习股票预测课设:从一份源码包到能跑通的预测流水线

很多人第一次拿到「机器学习股票预测算法源码项目说明+报告」这类课设压缩包时,第一反应是解压、找main.py、直接python main.py,然后被一堆ModuleNotFoundError和空白的收益曲线劝退。这个标题背后其实是一套完整的工程链路:数据获取与清洗、特征工程、标签构造、模型训练、回测评估,最后还要写一份能自圆其说的报告。它解决的不是「预测明天涨跌」这种玄学问题,而是让你在有限数据上跑通一条可复现、可解释、可写进报告的流水线。适合正在做课设的学生、想入门量化特征工程的开发者,以及需要一份能讲清楚「为什么这么做」的模板的人。下面我按自己踩过坑的顺序,把这条链路拆开讲。

2. 先搞清楚预测目标:回归、分类还是排序

2.1 三种目标定义决定了后面所有代码结构

股票预测听起来是一件事,落到代码里至少分三种目标。第一种是回归:预测未来 N 日收益率的具体数值,损失函数用 MSE 或 Huber,输出是一个连续值。第二种是分类:预测未来 N 日涨跌方向,二分类用 0/1,三分类加上震荡,损失函数用交叉熵。第三种是排序:在候选股票池里预测相对强弱,常用于多标的选股,损失函数用 pairwise ranking loss。

课设里最常见的是二分类,因为报告好写、指标直观(准确率、AUC、F1)。但这里有个血泪经验:股票涨跌本身噪声极大,二分类准确率能稳定在 53% 以上就已经不容易,别指望 80%。如果你的模型在测试集上准确率 90%,先检查是不是标签泄漏了。

我一般会先确定三件事:预测周期(1 日、5 日还是 20 日)、标签阈值(涨跌幅超过多少算正样本)、以及是否做中性化(减去行业或大盘收益)。这三件事写在报告的方法章节里,比模型结构更能体现你的思考。

2.2 标签构造的代码骨架与参数说明

下面这段代码是标签构造的最小实现,假设你已经有一个包含close列的 DataFrame,索引是交易日。

import pandas as pd import numpy as np def make_labels(df, horizon=5, threshold=0.02, price_col='close'): """ df: 含 close 列的行情数据,按日期升序 horizon: 预测未来多少个交易日 threshold: 涨跌幅阈值,超过该值记为正样本 """ # 未来 horizon 日的收益率 future_ret = df[price_col].shift(-horizon) / df[price_col] - 1 # 三分类:1 涨、0 震荡、-1 跌 labels = np.where(future_ret > threshold, 1, np.where(future_ret < -threshold, -1, 0)) # 最后 horizon 行没有未来数据,必须丢弃 df = df.iloc[:-horizon].copy() df['label'] = labels[:-horizon] df['future_ret'] = future_ret[:-horizon] return df

逻辑说明:shift(-horizon)把未来价格拉到当前行,这是标签构造的核心。参数horizon越大,标签噪声越低但样本越少;threshold越大,正负样本越不平衡。我通常先跑一遍df['label'].value_counts(),如果某一类占比低于 15%,就要考虑用class_weight='balanced'或者调整阈值。注意最后 horizon 行必须切掉,否则最后几行的标签是 NaN,训练时会报错或者被静默填充成 0,这就是典型的翻车点。

2.3 特征工程:别一上来就堆几百个因子

课设报告里常见「使用了 200 个技术指标」,但真正有效的往往不到 20 个。我建议按三类组织特征:价量特征(收益率、波动率、换手率)、趋势特征(均线偏离、MACD、RSI)、统计特征(过去 N 日收益的偏度、峰度)。每类先选 3 到 5 个,跑一遍特征重要性,再决定要不要加。

def add_features(df, windows=(5, 10, 20)): for w in windows: df[f'ret_{w}'] = df['close'].pct_change(w) df[f'vol_{w}'] = df['ret_1'].rolling(w).std() df[f'ma_dev_{w}'] = df['close'] / df['close'].rolling(w).mean() - 1 # RSI 简化版 delta = df['close'].diff() gain = delta.clip(lower=0).rolling(14).mean() loss = (-delta.clip(upper=0)).rolling(14).mean() df['rsi_14'] = 100 - 100 / (1 + gain / (loss + 1e-9)) return df.dropna()

参数说明:windows控制回看周期,短周期捕捉反转,长周期捕捉趋势。dropna()会丢掉前 20 行,这是正常的。注意loss + 1e-9是防止除零,这个细节在报告里可以提一句,体现工程严谨性。

3. 数据切分与模型训练:时间序列不能随机打乱

3.1 为什么随机切分会让你的报告直接失效

这是课设里最致命的错误。很多人习惯train_test_split(shuffle=True),但在时间序列上,这等于用未来数据预测过去,测试集准确率会虚高到离谱。正确做法是按时间切分:前 70% 训练,中间 15% 验证,最后 15% 测试。更严格一点用滚动窗口或扩展窗口交叉验证。

def time_split(df, train_ratio=0.7, val_ratio=0.15): n = len(df) train_end = int(n * train_ratio) val_end = int(n * (train_ratio + val_ratio)) train = df.iloc[:train_end] val = df.iloc[train_end:val_end] test = df.iloc[val_end:] return train, val, test

逻辑说明:iloc按位置切分,保证时间顺序。参数train_ratio和val_ratio根据样本量调整,样本少于 2000 行时验证集可以只留 10%。切分后检查一下三段的日期范围有没有重叠,我见过有人切完发现验证集日期在训练集之前,原因是原始数据没按日期排序。

3.2 树模型和线性模型的选型理由

课设里最稳的是 LightGBM 或 XGBoost,原因是能处理缺失值、对特征缩放不敏感、训练快。线性模型(逻辑回归、 Ridge)适合做 baseline,报告里放一个对比表格会加分。神经网络不是不能做,但样本量小的时候容易过拟合,调参成本高。

import lightgbm as lgb from sklearn.metrics import roc_auc_score, accuracy_score feature_cols = [c for c in train.columns if c not in ('label', 'future_ret')] X_train, y_train = train[feature_cols], (train['label'] == 1).astype(int) X_val, y_val = val[feature_cols], (val['label'] == 1).astype(int) model = lgb.LGBMClassifier( n_estimators=300, learning_rate=0.05, max_depth=5, num_leaves=31, subsample=0.8, colsample_bytree=0.8, class_weight='balanced', random_state=42 ) model.fit(X_train, y_train, eval_set=[(X_val, y_val)], eval_metric='auc', callbacks=[lgb.early_stopping(30)]) pred = model.predict_proba(X_val)[:, 1] print('AUC:', roc_auc_score(y_val, pred))

参数说明:n_estimators配合early_stopping用,防止过拟合;max_depth=5和num_leaves=31是控制复杂度的关键,股票数据信噪比低,树太深必然过拟合;class_weight='balanced'处理样本不平衡。early_stopping(30)表示验证集 AUC 30 轮不提升就停。跑完打印一下特征重要性,如果某个特征重要性异常高,检查它是不是未来函数。

3.3 评估指标不能只看准确率

准确率在样本不平衡时会骗人。假设正样本只占 20%,模型全预测负样本也有 80% 准确率。报告里至少放三个指标:AUC、F1、以及按预测概率分组的收益表现。更贴近实战的是看 Top 20% 预测概率的样本平均收益,如果这个值显著高于全样本平均,说明模型有区分能力。

def topk_metrics(y_true, pred_prob, future_ret, k=0.2): n = len(pred_prob) top_idx = np.argsort(pred_prob)[-int(n * k):] return { 'top_avg_ret': future_ret.iloc[top_idx].mean(), 'all_avg_ret': future_ret.mean(), 'hit_rate': y_true.iloc[top_idx].mean() }

逻辑说明:argsort取概率最高的 k 比例样本,计算平均未来收益。参数k一般取 0.1 到 0.3。如果top_avg_ret和all_avg_ret差不多,说明模型没有实际选股能力,报告里要如实写,别硬吹。

4. 避坑与排查:课设里最容易翻车的五个地方

4.1 标签泄漏:特征里混入了未来信息

现象:验证集 AUC 0.95 以上,测试集掉到 0.5。原因:某个特征在计算时用了未来数据,比如用全样本均值做标准化,或者用shift(-1)构造特征。解决:所有特征计算只允许用当前及历史数据,标准化参数只能在训练集上拟合。检查方法:把特征和标签的相关系数按时间画出来,如果某个特征在测试期相关性突然飙升,基本就是泄漏。

4.2 数据对齐错误:停牌和缺失值处理不当

现象:回测收益曲线出现不合理的跳空。原因:停牌日数据缺失,dropna()后日期不连续,收益率计算错位。解决:先按交易日历 reindex,停牌日用前值填充,但标签构造时要标记停牌期并剔除。我一般会在数据加载后先df.index = pd.to_datetime(df.index),再df = df.asfreq('B')对齐工作日。

4.3 过拟合:训练集完美,测试集崩盘

现象:训练集 AUC 0.99,验证集 0.55。原因:树太深、特征太多、样本太少。解决:限制max_depth不超过 6,min_child_samples设到 50 以上,特征数控制在样本数的十分之一以下。另外早停是必须的,别手动设n_estimators=1000跑满。

4.4 随机种子没固定:结果无法复现

现象:每次跑出来的 AUC 都不一样,报告里的数字对不上。原因:random_state没设,或者 LightGBM 的bagging_seed、feature_fraction_seed没设。解决:在模型初始化时把random_state、bagging_seed、feature_fraction_seed都固定成同一个值,数据切分也用固定种子。

4.5 报告和代码脱节:数字对不上

现象:报告里写准确率 65%,代码跑出来 58%。原因:报告里的数字是某次调参后的结果,但代码没保存那次配置。解决:每次实验把参数和指标写进一个experiment_log.csv,报告里的每个数字都能追溯到具体命令。这个习惯在课设答辩时能救命。

5. 把源码包变成可展示的成果:报告结构与复现技巧

课设的最终交付不只是代码,还有一份能讲清楚来龙去脉的报告。我一般按「问题定义 → 数据说明 → 特征工程 → 模型与参数 → 实验结果 → 局限性」六段写。其中「局限性」这段最容易被忽略,但恰恰是加分项:写清楚样本量小、未考虑交易成本、未做行业中性化,反而显得你懂行。

复现技巧上,建议在项目根目录放一个run.sh,把数据下载、特征生成、训练、评估串起来。这样别人拿到压缩包后一条命令就能跑通,而不是逐个文件找入口。

#!/bin/bash set -e python src/build_features.py --input data/raw.csv --output data/features.csv python src/train.py --data data/features.csv --model lgb --horizon 5 python src/evaluate.py --pred outputs/pred.csv --report reports/metrics.json

逻辑说明:set -e让脚本在任一步失败时停止,避免错误累积。参数--horizon和--model暴露出来,方便做对比实验。如果数据文件较大,可以在build_features.py里加缓存,避免重复计算。

最后一个具体技巧:在报告里放一张「特征重要性 Top 10」的表格,比放十张收益曲线更有说服力。因为收益曲线受市场行情影响大,而特征重要性反映的是模型逻辑。我习惯用lgb.plot_importance导出后手动整理成表格,标注每个特征的经济含义,比如ma_dev_20代表价格偏离 20 日均线程度,这样答辩时老师一看就懂。

我自己做这类课设最大的教训是:别在模型结构上炫技,把数据切分、标签构造、评估指标这三件事做扎实,比换十个模型都管用。每次跑实验前先问自己一句「这个特征在实盘里能不能实时拿到」,能过滤掉一大半未来函数。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询