
简介这是一份面向计算机相关专业毕业设计、课程设计与期末大作业的深度学习实战项目围绕股票价格预测与量化策略展开包含 Python 源码、文档说明和答辩报告 PPT。项目由大四学生完成经导师指导并获 99 分好评代码完整可运行适合需要快速上手完整项目流程的学习者也适合作为毕业设计模板或实训案例。资源共 1409 个文件压缩包约 16.2MB。其中以 Python 的 py/pyc 源码为主辅以可执行文件、配置说明、CSV 数据文件和 PPT 报告并包含虚拟环境相关配置便于还原运行环境、查看预测结果与策略回测过程。文件类型覆盖源码、数据、说明文档和展示课件结构清晰可按模块检索学习。目前已有 247 人学习下载。通过这份资源使用者可以获得一套完整的股票数据预处理、深度学习建模、价格预测和量化回测的实现思路以及毕设文档和演示 PPT 的撰写参考既能用于实战练习也可为同类课题提供直接借鉴。1. 股票预测项目拿到 99 分的底气不在模型而在闭环深度学习做股票价格预测最常见的翻车不是网络不收敛而是用 LSTM 预测收盘价时几乎原样复制前一天价格预测曲线相比真实值整体滞后一根 K 线。这个“滞后复制”问题如果在答辩 PPT 里被当场指出来项目基本就没有高分可能。这套基于深度学习的股票价格预测与量化策略研究 Python 源码能拿到 99 分靠的不是把网络层数堆到多深而是把数据清洗、序列建模、信号生成和量化回测整理成了一个能自证逻辑的闭环文档说明和报告 PPT 也都围绕这一闭环做了展示。对正在做毕业设计的计算机专业学生以及想用 Python 做量化入门实战的人来说需要复现的正是这个闭环而不是单独跑一个 LSTM。2. LSTM 与 GRU 选型从时间步建模到预测头设计2.1 为什么普通 RNN 在日线行情上会失效直接拿三层 RNN 预测日线收盘价很容易发现验证集 loss 在前几个 epoch 仍然抖动不降。这个现象并不一定来自学习率更多是梯度路径出了问题当序列长度超过 30 步RNN 的隐藏状态在反向传播时经过多轮连乘较早时间步的梯度要么消失要么爆炸而 A 股日线数据里值得记忆的形态往往散布在近 5 到 20 根 K 线中普通 RNN 很难把这种跨时间的依赖保存下来。LSTM 用输入门、遗忘门、输出门维护一条细胞状态通路梯度沿这条通路回传时受到稳定控制GRU 把门控简化成两个门参数更少在小数据集上收敛更快。我在做这类毕设时会同时保留 LSTM 和 GRU 两个等价模块先跑 100 个 epoch 对比验证集 loss再选默认实现。源码工程里同时看到两种结构并不奇怪关键是要知道默认参数并不是最优。2.2 用 PyTorch 搭一个可训练的 LSTMPredictor下面给出一个能直接进入训练循环的预测网络。注意这里的输入 shape 使用 batch_first方便和 DataLoader 返回的维度对齐。import torch.nn as nn class LSTMPredictor(nn.Module): 将 LSTM 最后一个时间步的隐藏状态映射成未来收益预测。 def __init__(self, n_features, n_hidden64, n_layers2, dropout0.2): super().__init__() self.lstm nn.LSTM( input_sizen_features, hidden_sizen_hidden, num_layersn_layers, batch_firstTrue, dropoutdropout, ) self.head nn.Sequential( nn.Linear(n_hidden, 16), nn.ReLU(), nn.Linear(16, 1), ) def forward(self, x): out, _ self.lstm(x) # out: [batch, seq_len, n_hidden] last out[:, -1, :] # 只取最后一个时间步的特征 return self.head(last)这里的 n_features 取决于第 3 章里构造的特征宽度不是只用收盘价。n_hidden 我一般从 64 起调样本量只有几千条时超过 128 很容易过拟合n_layers 设置为 2 是为了让模型能拟合到部分中长期依赖3 层以上在日线场景下收益非常有限。head 部分先用一个 16 维全连接加 ReLU再压缩到 1 个输出比直接 Linear(n_hidden, 1) 的拟合能力更稳。如果希望同时预测未来 5 日最高价和最低价把最后一个 Linear 的输出维度改成 2 或 3并配合多任务损失但这会明显加大调参量。2.3 损失函数与评价指标回归误差小不等于策略能赚钱模型训练时最常用 MSE但股票预测项目不能只看 MSE。下面这个表格可以帮你决定在不同阶段看哪个指标。指标计算口径在毕设里的用途MSEmean((y - y_hat)^2)训练主损失放大大误差样本MAEmean(abs(y - y_hat))报告常用单位直观MAPEmean(abs((y - y_hat) / y))无量纲对比但接近 0 时会异常放大方向准确率mean(sign(y_diff) sign(y_hat_diff))评估交易信号的核心指标在训练日志里我会同时记录 MSE 和方向准确率如果方向准确率长期低于 52%说明模型没有学到比随机略好的规律此时调大 lookback 或调整特征往往比加深网络更有效。MSE 优化出的模型会倾向于输出靠近均值的预测天然容易把极端行情识别得不够充分因此后续量化策略里对预测值做阈值切割时不能直接套用回归评价结果。真正影响资金曲线的不是回归精度而是信号切换频率和持仓周期这一点在量化回测章节会更明显。3. 数据预处理与滑动窗口让模型“看见”可用的行情结构3.1 前复权与特征拼装别把除权跳空当成暴跌很多数据接口默认返回未复权价格。未复权价格在分红送股当天会出现一个向下的价格跳空模型会把它当成真实跌幅从而学习到错误标签。在毕设里要干脆统一使用前复权价格保证历史价格连续可回测。用 akshare 拉日线时可以通过 adjust 参数控制复权方式import akshare as ak df ak.stock_zh_a_hist( symbol000001, perioddaily, start_date20180101, end_date20231231, adjustqfq, ) print(df.columns)不同版本的 akshare 返回列名可能不同通常是中文列名。拿到原始数据后把日期解析成 datetime 类型并排成升序再用pd.to_datetime做索引。特征工程方面可以加入过去 5 日收益率、成交量 5 日均线与 20 日均线的比值、最高最低价振幅、换手率这些特征都是同一时刻能拿到的信息不会引入未来值。对于常见做法特征数量控制在 6 到 10 个即可太多特征在几千条样本下会放大过拟合。3.2 滑动窗口构造与标签设计预测未来 K 根 K 线有了特征表后需要把二维行情数据切成模型能吃的三维序列。下面这个函数按时间顺序生成样本标签是窗口结束后 horizon 天的累计收益率import numpy as np def make_sequences(data, feature_cols, label_col0, lookback30, horizon5): X, y [], [] for i in range(len(data) - lookback - horizon 1): t i lookback - 1 # 当前窗口最后一根K线的索引 X.append(data[i: t 1, feature_cols]) future_ret data[t horizon, label_col] / data[t, label_col] - 1.0 y.append(future_ret) return np.array(X, dtypenp.float32), np.array(y, dtypenp.float32)这里 lookback 表示使用的历史长度horizon 表示未来多少天。horizon 必须和策略的持仓周期保持一致如果策略每 5 天调一次仓就用 horizon5如果只是做次日涨跌判断就设 horizon1。标签使用未来第 horizon 天与当前窗口最后一天收盘价的比值减去 1是为了避免模型预测绝对价格带来的量纲不稳定。注意最后 horizon 行的样本不存在完整标签直接从训练集中丢弃这是正确做法。窗口重叠是允许的但会带来样本自相关性论文里最好说明这一点我通常把步长设为 horizon减少重叠测试集表现会稍微难看一点但回测更诚实。3.3 标准化与数据划分防止未来函数泄漏很多新手会把所有样本先算 mean/std 再做切分然后在测试集上获得不错的表现其实测试集的分布信息已经悄悄进入了标准化参数。正确做法是先按时间顺序切出训练集、验证集、测试集再用训练集的数据去 fit 标准化器最后 transform 三个集合。代码示例如下from sklearn.preprocessing import StandardScaler N, T, F X_train.shape scaler StandardScaler() scaler.fit(X_train.reshape(-1, F)) X_train scaler.transform(X_train.reshape(-1, F)).reshape(N, T, F) # 验证集和测试集沿用同一 scaler不能重新 fit提示标准化器只能 fit 一次切分顺序出错时回测会自动“偷看”未来导致策略绩效虚高。注意不能在整个数据集上按随机比例切分时间序列必须保证验证集晚于训练集。下面是两种常见错误与正确做法的对比做法是否可用原因全量数据标准化后切分不可用测试集信息泄漏到训练数据随机 shuffle 后切分不可用破坏时间依赖产生未来函数泄漏按时间顺序切分后再 fit scaler可用符合真实交易时只能看到历史信息用滚动窗口重训模型推荐模拟真实市场代价是训练成本上升数据泄露会在回测阶段表现为“预测曲线和真实走势高度重合”但实盘完全失效。后面第 5 章会专门介绍一种诊断这种假学习的检查脚本现在只需要记住标准化器只能 fit 一次时间顺序不能破坏。4. 模型输出到量化策略信号生成、回测引擎与绩效指标4.1 连续预测值如何变成交易信号阈值、差分与持仓周期模型输出的是未来 5 日累计收益率不能简单拿大于 0 就开多小于 0 就开空。因为预测误差大靠近 0 的信号频繁翻转会吃满手续费。常见做法是设置一个阈值只有预测收益超过阈值才建仓低于负阈值才反向。阈值可以取交易成本的 2 到 3 倍。信号生成代码可以写成这样cost 0.001 # 单边手续费加滑点 threshold cost * 2 # 0.002约 20 个基点 pred pd.Series(...) # 模型输出的未来5日累计收益率 target pd.Series(0, indexpred.index) target[pred threshold] 1 target[pred -threshold] -1 signal target.shift(1) # 信号在次日开盘执行避免用当天收盘价偷看未来这里信号序列 target 是在收盘后计算的所以执行时点必须向后平移一天。如果预测周期是 5 天那么 signal 应该保持 5 天不变而不是每天都按当日预测结果调仓一个简单实现是对 target 进行reindex().ffill(limithorizon-1)或者直接对每日收益率向量按持仓周期采样。A 股还有 T1 限制这里只做简化演示论文里要单独说明。4.2 向量化回测引擎计算资金曲线与换手成本回测引擎不推荐用 for 循环逐日买卖用 pandas 向量化更高效且不易出错import pandas as pd close df[close] daily_ret close.pct_change().fillna(0.0) position signal.fillna(0.0) turnover position.diff().abs().fillna(0.0) strategy_ret daily_ret * position - turnover * cost equity (1 strategy_ret).cumprod()position.diff()统计的是相邻两天持仓变化变化一次就计一次单边交易成本。这里的成本是简化模型实际回测还可以按成交价滑点、印花税分别建模。要注意 position 必须在前一天收盘时已经确定也就是说daily_ret和position在索引上要对齐但执行上错位 1 天上面的target.shift(1)已经处理了这个错位。如果回测资金曲线出现 45 度上行后断崖回落一般就是没有考虑换手成本或信号前视。4.3 绩效归因夏普比率、最大回撤与卡玛比率给出一套基础绩效函数def sharpe_ratio(returns, rf0.0, periods252): return np.sqrt(periods) * (returns.mean() - rf) / (returns.std() 1e-8) def max_drawdown(equity): peak equity.cummax() return ((peak - equity) / peak).max()sharpe_ratio 中加 1e-8 是防止 std 为 0 导致除零max_drawdown 返回的是回撤比例数值越小越好。在报告 PPT 中建议用下面这个标准化表格模板对齐策略表现指标公式高分毕设里应呈现的水平建议年化收益率所有交易日的累计收益折算年化结合样本区间说明不要单独写年化波动率日收益标准差乘 sqrt(252)与基准比较夏普比率(年化收益-无风险)/年化波动1.2 以上算合格最大回撤峰值到谷底的亏损幅度尽量控制 15% 以内卡玛比率年化收益 / 最大回撤与夏普配合使用另外项目压缩包里出现的 fitness_dll.dll、oputils.dll从文件命名看估计是策略参数寻优时使用的动态库。常见做法是回测中把适应度函数用 C/C 封装成 DLL再通过 ctypes 在 Python 中调用批量参数扫描可以提速几倍到几十倍如果直接在 Python 里写 for 循环跑网格搜索几千组参数可能要等半小时。这一层的优化不影响策略逻辑但能把答辩现场演示做得更流畅。5. 验证 y_hat 是否为“滞后复制”种子固定与结果重放的三个技巧5.1 用相关关系定位“假学习”模型模型训练完成后通常会导出 y_hat.csv、y_hat2.csv 这类预测结果。我拿到后第一件事不是看 loss而是算两个相关系数。如果模型在复制上一天价格那么预测序列与真实序列的同步相关系数可能很高但和真实序列滞后一日数据的相关系数更高。下面用 pandas 快速检查import pandas as pd pred pd.read_csv(y_hat.csv, index_col0)[pred] actual pd.read_csv(y_true.csv, index_col0)[close] sync_corr pred.corr(actual) lag_corr pred.corr(actual.shift(-1)) print(fsync{sync_corr:.3f}, lag{lag_corr:.3f})当lag_corr明显高于sync_corr说明模型学到的是“今天等于昨天”的退化解。正确模型应该同步相关更高且滞后相关接近同步相关或更低。如果预测目标是收益率把这个检查应用到收益率序列上同样有效。这个诊断技巧在导师提问“你怎么证明模型没有未来函数”时可以直接回答。5.2 固定随机种子与模型保存确保答辩可复现神经网络每次运行结果不同评阅人复现时如果跑出差异很容易被认为结论不稳定。训练入口处统一固定三个随机源import random import numpy as np import torch def set_seed(seed42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) torch.backends.cudnn.deterministic True set_seed(42) model LSTMPredictor(n_featuresF) torch.save(model.state_dict(), best_model.pt)开了 cudnn.deterministic 后 PyTorch 的卷积和 LSTM 计算会切换到确定算法运行变慢但结果可复现。预测完成后把结果写回 y_hat.csv同真实标签一起画图放进 PPT。最容易打动答辩老师的图组合是训练集与验证集 loss 曲线、真实收盘价与预测收益率的对比曲线、回测资金曲线和回撤区间标注。不要只写模型准确率写“方向准确率 56%、夏普 1.3、最大回撤小于 15%”更能经受连续追问。如果换机器复现项目里自带的 activate.bat、pyvenv.cfg 是 venv 生成的虚拟环境配套文件先检查 requirements.txt 是否完整再在 Python 安装目录下用python -m venv venv重建环境即可。本文还有配套的精品资源点击获取