
简介这份资源是面向高校学生与量化投资初学者的深度学习实战项目包可作为毕业设计、期末大作业或人工智能课程的参考案例帮助读者理解如何将神经网络应用于股票价格预测与交易策略开发。压缩包共46个文件约216KB以23个Python源码文件为核心辅以xml配置、csv策略数据、sqlite与db数据库文件、md说明文档及txt依赖清单覆盖数据处理、模型构建、策略执行与模拟交易等模块。项目围绕数据预处理、RNN/LSTM/CNN等模型搭建、训练调优以及历史回测评估展开代码结构清晰便于按模块阅读与二次开发。目前已有133人学习下载适合希望打通深度学习与量化投资交叉知识、积累完整项目经验的读者参考借鉴。1. 从一份 600519 的 CSV 说起这个深度学习量化策略包到底能跑出什么很多人第一次接触量化投资都是从一份贵州茅台的日线数据开始的。这个压缩包里就躺着一份600519.SH.CSV旁边还有600519.db、600519.sqlite两个同源数据文件以及data.sqlite这个总库。它不是一个只放论文和 PPT 的毕业设计壳子而是一套能真正跑起来的 Python 工程stock.py负责读行情ML.py搭深度学习网络main.py串起训练和预测simulate_RUN.py做模拟撮合strategy/目录下还有一套策略字典和默认参数表。如果你正在找一份能改、能跑、能写进毕业设计或期末大作业的深度学习量化项目这份代码的完整度是够的——数据、模型、回测、单元测试、依赖清单都在。它适合两类人一类是想把 LSTM、CNN 这些模型真正落到时间序列上的学生另一类是做过传统均线策略、想看看深度学习能不能在 A 股单票上跑出点东西的开发者。下面我按自己拆包的顺序把这份资源从结构到跑通、再到避坑完整过一遍。2. 拆包先看骨架目录结构、数据流与三个入口文件2.1 目录分层与模块职责拿到压缩包别急着pip install先把目录树看一遍。这份工程的层次感比很多同类毕设强它把「数据」「模型」「策略」「回测」拆成了独立目录而不是全塞进一个main.py。路径职责关键文件根目录入口与配置main.py、ML.py、requirements.txt、README.mddata/行情数据与数据库600519.SH.CSV、600519.db、600519.sqlite、data.sqliteprocess/数据清洗与入库Process.py、SQLFrame.py、csv2sqlite.pyDL/深度学习网络network.py、GPU.pystrategy/策略定义与参数Strategy.py、chost.py、strategyDic/default.strategy.csvanalysis/分析与绘图draw.py、error.py、stock.pysimulink/模拟交易simulink.pytests/单元测试test_frame.py、test_data_frame.py、test.py这个分层透露出的设计意图很明确数据从 CSV 进经process/清洗后落到 SQLiteDL/network.py从库里取序列做训练strategy/Strategy.py把预测信号翻译成买卖动作最后simulate_RUN.py跑一遍历史回测。整条链路是通的不是各写各的。2.2 数据流从 CSV 到 SQLite 再到模型输入process/csv2sqlite.py是数据入口的第一站。它的作用是把600519.SH.CSV这种带表头的行情文件写进600519.db后续所有模块都从数据库读避免每次训练都去解析 CSV。常见做法是用 pandas 读 CSV再用 sqlite3 的to_sql落库字段一般包括日期、开盘、最高、最低、收盘、成交量。# process/csv2sqlite.py 的核心逻辑按常见实现还原 import pandas as pd import sqlite3 def csv_to_sqlite(csv_path, db_path, table_name): # 读行情 CSVparse_dates 把日期列转成 datetime方便后续按时间切片 df pd.read_csv(csv_path, parse_dates[date]) # 按日期升序时间序列模型对顺序极度敏感乱序会直接毁掉训练 df df.sort_values(date).reset_index(dropTrue) conn sqlite3.connect(db_path) # if_existsreplace 保证重复执行不会叠加脏数据 df.to_sql(table_name, conn, if_existsreplace, indexFalse) conn.close() return df.shape if __name__ __main__: print(csv_to_sqlite(data/600519.SH.CSV, data/600519.db, stock_600519))这段代码有三个参数要盯住parse_dates决定日期列能不能被正确识别漏了它后面按时间窗口切序列会报类型错误sort_values(date)是时间序列的生命线我见过有人跳过这步模型在训练集上 loss 降得漂亮一回测就崩if_existsreplace决定重复跑脚本时是覆盖还是追加调试阶段用 replace正式入库前建议先备份。2.3 三个入口文件的分工main.py、ML.py、simulate_RUN.py是三个不同层级的入口。ML.py偏模型层负责定义网络结构、训练循环、保存权重main.py偏调度层把数据加载、模型训练、信号生成串起来simulate_RUN.py偏回测层拿预测结果去模拟买卖。很多人跑不通是因为直接执行main.py却发现它依赖ML.py里尚未初始化的全局配置。稳妥的顺序是先跑csv2sqlite.py确认数据入库再单独跑ML.py确认模型能训练最后才跑main.py和simulate_RUN.py。提示requirements.txt里通常锁定了 tensorflow 或 pytorch 的版本先看它再建虚拟环境能省掉一半的依赖冲突。3. 把模型跑起来LSTM 网络结构、训练参数与 GPU 开关3.1 为什么这类项目默认选 LSTM 而不是普通 RNNDL/network.py里搭的是时间序列模型。量化行情是典型的长依赖序列普通 RNN 在反向传播时梯度容易消失几十个交易日前的信息传不回来LSTM 靠输入门、遗忘门、输出门三个结构把长期信息留在细胞状态里对「过去 60 天走势影响今天」这种模式更友好。这也是深度学习做量化的入门标配动手深度学习那类教材里讲序列模型时也是拿它当主线。网络输入一般是滑动窗口切出来的三维张量[样本数, 时间步长, 特征数]。时间步长常见取 20、30、60特征数取决于你喂了几列——只用收盘价就是 1加上成交量、换手率就是 3 到 5。输出层通常接一个全连接回归任务输出下一日收益率分类任务输出涨跌概率。# DL/network.py 的典型 LSTM 结构按常见实现还原 import torch import torch.nn as nn class LSTMModel(nn.Module): def __init__(self, input_size1, hidden_size64, num_layers2, output_size1): super().__init__() # batch_firstTrue 让输入维度是 [batch, seq, feature]符合 pandas 切窗习惯 self.lstm nn.LSTM(input_size, hidden_size, num_layers, batch_firstTrue) # 全连接把最后一个时间步的隐状态映射成预测值 self.fc nn.Linear(hidden_size, output_size) def forward(self, x): # out 形状 [batch, seq, hidden]取最后一个时间步代表整段序列 out, _ self.lstm(x) return self.fc(out[:, -1, :])hidden_size是模型容量的核心旋钮64 到 128 是单票日线数据的常见区间再大就容易过拟合num_layers2表示堆两层 LSTM层数越多越能拟合复杂模式但训练时间和过拟合风险同步上升。batch_firstTrue这个参数如果漏了输入维度会被当成[seq, batch, feature]报错信息往往很隐晦是新手翻车高发点。3.2 训练参数怎么设损失函数、优化器与早停ML.py里的训练循环决定了模型能不能收敛。回归任务常用 MSE 或 HuberLossHuberLoss 对收益率里的极端值更稳优化器 Adam 是默认选择学习率从 1e-3 起步配合ReduceLROnPlateau在验证 loss 不降时自动衰减。批大小 32 或 64训练轮数设大一点但一定要加早停否则模型会把训练集背下来。# ML.py 训练循环的关键片段按常见实现还原 import torch from torch import optim model LSTMModel(input_size1, hidden_size64, num_layers2) criterion torch.nn.HuberLoss() # 对收益率异常值更鲁棒 optimizer optim.Adam(model.parameters(), lr1e-3) scheduler optim.lr_scheduler.ReduceLROnPlateau(optimizer, patience5) best_val float(inf) for epoch in range(200): model.train() for xb, yb in train_loader: optimizer.zero_grad() loss criterion(model(xb), yb) loss.backward() optimizer.step() # 验证阶段必须 model.eval()否则 dropout/bn 会污染评估 model.eval() with torch.no_grad(): val_loss sum(criterion(model(xb), yb).item() for xb, yb in val_loader) scheduler.step(val_loss) if val_loss best_val: best_val val_loss torch.save(model.state_dict(), best.pth) # 只存最优权重这里每个参数都有讲究HuberLoss的 delta 默认 1.0收益率量级小的时候可以调小patience5表示验证 loss 连续 5 轮不降就降学习率torch.save只存state_dict而不是整个模型对象换环境加载时更不容易出兼容问题。model.eval()和torch.no_grad()这两行如果漏写验证 loss 会虚高早停会误判。3.3 GPU 开关与显存边界DL/GPU.py是设备管理模块负责判断有没有可用显卡并把模型和数据搬过去。单票日线数据量不大CPU 也能跑但如果你把时间步长拉到 120、特征堆到十几列GPU 能省不少时间。# DL/GPU.py 的设备选择逻辑按常见实现还原 import torch def get_device(): if torch.cuda.is_available(): # 打印显卡型号方便确认没跑在集显上 print(using gpu:, torch.cuda.get_device_name(0)) return torch.device(cuda) return torch.device(cpu) device get_device() model model.to(device) # 数据也要同步搬到同一设备否则会报 tensor 不在同一 device xb, yb xb.to(device), yb.to(device)常见坑是模型搬了、数据没搬报错信息是设备不匹配另一个坑是显存不够时 batch 太大直接 OOM把 batch 从 64 降到 16 通常能救回来。如果torch.cuda.is_available()返回 False先确认装的是 GPU 版而不是 CPU 版框架这个在深度学习环境配置里是高频问题。4. 策略与回测从预测信号到模拟撮合的完整链路4.1 策略字典与默认参数表strategy/strategyDic/default.strategy.csv是这个项目的策略配置中心。它把阈值、持仓比例、止损线这些参数外置成 CSV改策略不用动代码这对做对比实验特别友好。Strategy.py读这张表chost.py负责信号到订单的翻译。参数名含义常见取值buy_threshold预测收益率超过该值买入0.005 ~ 0.01sell_threshold预测收益率低于该值卖出-0.01 ~ -0.005position_ratio单次建仓占总资金比例0.3 ~ 0.5stop_loss单笔止损线-0.05window_size模型输入时间步长20 / 30 / 60阈值设得太低交易频率飙升手续费会把利润吃光设得太高信号稀疏一年可能就几笔。我一般先用 0.005 跑一遍看信号分布再根据换手率调。4.2 回测执行simulate_RUN.py 在做什么simulate_RUN.py是回测主循环。它按交易日推进每天拿模型对最近 window_size 天的数据做一次预测根据阈值决定买、卖还是持有然后按当日收盘价或次日开盘价撮合记录净值和持仓。# simulate_RUN.py 的回测主循环按常见实现还原 import pandas as pd def run_backtest(df, model, cfg, device): cash, shares 1.0, 0.0 # 初始资金归一化为 1 nav [] # 每日净值曲线 for i in range(cfg[window_size], len(df)): window df.iloc[i-cfg[window_size]:i][[close]].values pred model_predict(model, window, device) # 模型预测下一日收益 price df.iloc[i][close] if pred cfg[buy_threshold] and cash 0: shares cash * cfg[position_ratio] / price cash - shares * price elif pred cfg[sell_threshold] and shares 0: cash shares * price shares 0 nav.append(cash shares * price) return pd.Series(nav, indexdf.index[cfg[window_size]:])这段逻辑里cash和shares的更新顺序不能反先算买入再扣现金否则会出现资金为负的假象。nav记录的是每日总资产最后用analysis/draw.py画净值曲线和买入持有基准对比。注意这里没有扣手续费和滑点真实回测要加上否则收益会被高估。4.3 单元测试与数据校验tests/目录下的test_frame.py、test_data_frame.py是很多人会忽略的部分但它恰恰是这份工程比同类毕设靠谱的地方。测试覆盖了数据帧的列名、类型、缺失值处理跑一遍能提前发现数据格式问题。# 在项目根目录执行测试 python -m pytest tests/ -v如果测试报KeyError: close说明 CSV 列名和代码里约定的不一致去process/Process.py里改映射如果报日期解析失败检查 CSV 的日期格式是不是YYYY-MM-DD。先让测试全绿再跑回测能省掉大量「模型没问题但结果不对」的排查时间。5. 避坑与排查这份代码最容易翻车的五个地方5.1 数据泄漏归一化用了全量数据现象是回测净值曲线漂亮得不像话年化动辄翻倍。原因是归一化时用了整段数据的均值和方差等于把未来信息泄漏给了训练集。正确做法是只用训练集统计量再应用到验证和测试集或者用滚动窗口归一化。这个坑在深度学习量化项目里几乎人人踩过一次。5.2 时间序列乱序sort_values 漏写现象是模型训练 loss 正常下降但预测结果和价格走势完全对不上。原因是 CSV 读进来没按日期排序滑动窗口切出来的序列是乱的。解决就是在csv2sqlite.py和任何读数据的地方都加sort_values(date).reset_index(dropTrue)并在测试里断言日期单调递增。5.3 设备不匹配模型在 GPU、数据在 CPU现象是运行时报Expected all tensors to be on the same device。原因是model.to(device)之后忘了把每个 batch 的数据也搬过去。解决是在训练循环里统一xb, yb xb.to(device), yb.to(device)或者封装一个to_device辅助函数。如果压根没有 GPU确认torch.cuda.is_available()的返回值别在 CPU 版框架上找显卡。5.4 回测未扣成本手续费和滑点被忽略现象是策略换手率高但收益仍然为正实盘根本做不到。原因是simulate_RUN.py里买卖按收盘价全额成交没有手续费、印花税和滑点。解决是在每次买卖时扣掉固定比例成本A 股常见按单边万分之几到千分之几估算滑点按一个最小变动价位加。加完之后很多「高收益」策略会现原形。5.5 依赖版本冲突框架版本与代码不匹配现象是import torch成功但一调用 LSTM 就报参数错误或者pandas.to_sql报 API 变更。原因是requirements.txt里的版本和你环境里的不一致。解决是严格按requirements.txt建虚拟环境pip install -r requirements.txt不要图省事用全局环境。如果文件里没锁死版本至少确认框架大版本和代码写法对得上。6. 进阶玩法把单票策略扩成多票轮动与参数扫描跑通单票只是起点。这份工程的strategyDic设计天然支持参数扫描——把default.strategy.csv复制几份改不同阈值组合写个循环批量跑simulate_RUN.py最后对比净值曲线挑最优参数。但要注意参数在单票上扫出来的最优值往往过拟合换一只票就失效所以更稳的做法是拿一批股票一起扫看参数在横截面上的稳定性。再往上一层是多票轮动。data/目录现在只有 600519 一份数据但csv2sqlite.py是通用的把其他票的 CSV 按同样格式丢进去改一下表名就能建出多票库。然后每天对所有票做预测按预测收益率排序买最高的前 N 只这就是最朴素的深度学习选股轮动。这一步的坑在于不同票的价格量级不同归一化必须逐票做不能混在一起。# 多票参数扫描的骨架按常见实现还原 import itertools, pandas as pd results {} for buy_t, sell_t in itertools.product([0.003, 0.005, 0.008], [-0.008, -0.005, -0.003]): cfg {buy_threshold: buy_t, sell_threshold: sell_t, position_ratio: 0.4, window_size: 30} nav run_backtest(df, model, cfg, device) # 用夏普比率而不是总收益做筛选避免选中高波动参数 results[(buy_t, sell_t)] nav.pct_change().mean() / nav.pct_change().std() best max(results, keyresults.get) print(best params:, best, sharpe:, results[best])这段扫描用夏普比率而不是总收益做筛选标准是因为总收益容易被一两笔极端交易拉高夏普更能反映风险调整后的表现。itertools.product生成参数组合每个组合跑一遍回测最后取夏普最高的。实际跑的时候建议把结果存成 CSV方便后面画热力图看参数敏感性。验证方法上我习惯把数据按时间切成三段前 60% 训练中间 20% 验证调参最后 20% 完全不碰只在最终确认时跑一次。如果最后 20% 的表现和验证集差距巨大说明参数过拟合了得退回去简化模型或加正则。这个习惯是被坑出来的——早年我拿全量数据调参回测夏普 2.0实盘一上就亏从那以后每次调参都强制留一段「没见过」的数据做最终检验。希望这份拆解能帮你少走点弯路把这份资源真正跑出自己的结果。本文还有配套的精品资源点击获取