简介毕业设计主题的量化选股项目以LSTM模型对财务因子进行预测并生成选股信号同时提供BP神经网络作为对照方案。面向计算机、人工智能、金融工程等方向学生及科研人员适用于课程设计、毕业设计、论文实验和项目演示等场景。资源共14个文件以7个Python脚本为核心包含模型构建、训练流程、MACD与RSI指标计算及选股逻辑此外还有训练好的模型权重、配置文件、项目介绍和运行说明便于快速理解与复现。压缩包大小仅1.19MB轻量简洁运行门槛低。目前已有40人学习下载。通过这份资料可获得完整可运行的模型源码、预训练权重、配套文档与运行指引既可直接用作毕业设计成果展示也可基于现有代码扩充财务因子、调整网络结构或接入实盘数据适合有Python基础、希望深入学习机器学习选股的读者。1. LSTM财务因子预测选股模型一套能跑通的量化选股基线源码做量化选股的人大多有过这种经历花两周读了十几篇LSTM预测股价的论文动手时却卡在数据对齐和模型收敛上项目从“预测股价”默默改成“预测涨跌方向”最后连方向都预测不准。这套LSTM财务因子预测选股模型源码难得的地方在于它把整个选股流程完整落地了——从财务因子提取、数据预处理到LSTM和BP两条模型路线训练再到MACD_RSI技术指标叠加过滤最后输出可执行的选股结果。它不是论文里的伪代码而是能在本地跑通、能看着loss下降、能输出候选股列表的完整工程。你拿到手的是一个Zip包解压后是Python工程核心文件包括mindgo_lstm.py聚宽平台回测入口、LSTM_class.py模型定义、BP.py和BP_class.py对照组模型、mindgo_LSTM_single_model.py单模型预测版以及训练好的checkpoint权重文件。适合三类人做毕业设计需要“深度学习金融”交叉题目的学生想快速验证LSTM在A股财务选股上是否有效的从业者以及想在同一份数据上对比BP和LSTM效果的量化入门者。2. 拆解源码结构两条模型路线和一个回测入口2.1 文件清单与技术栈定位解压后的文件不多但分工明确。我建议你按“数据→模型→回测”的顺序去读代码而不是按文件名猜。下面是完整的文件清单和职责划分文件/目录职责关键依赖mindgo_lstm.py聚宽平台回测主入口数据获取、因子计算、模型调用pandas, numpy, jqdatasdkLSTM_class.pyLSTM模型定义含网络结构和训练逻辑tensorflow (1.x checkpoint)mindgo_LSTM_single_model.py单模型预测版直接用训练好的权重做推理tensorflowBP.py/BP_class.py三层BP神经网络对照组numpy 手写实现MACD_RSI.py技术指标过滤模块对模型候选股做二次筛选pandas, talibmodel/checkpoint/训练好的TensorFlow权重文件single_model.ckpt.*运行说明-仅供参考学习.txt手把手的运行指引无项目介绍.md设计文档写毕业论文可参考无这里要特别说明LSTM_class.py里用的是TensorFlow 1.x风格的tf.compat.v1接口和checkpoint保存方式这是很多人在运行阶段第一个翻车点——装了个TensorFlow 2.x就直接跑single_model.ckpt.meta会报错或者压根加载不上。后面第4章会专门说这个问题。2.2 为什么同时提供LSTM和BP两条路线这是这套源码设计上最聪明的地方。很多毕业设计只做一个模型答辩时老师问“为什么选LSTM而不选别的”答不上来。这里直接给你一个对照组BP.py是用numpy从零手写的三层神经网络LSTM_class.py是TensorFlow实现的LSTM。两条路线共享同一套财务因子输入你可以在同一批数据上对比两者效果。以我的经验在财务因子这种“截面数据”上BP往往不比LSTM差多少——因为财务因子是季度级别的低频数据样本量小、时间依赖性弱LSTM的优势本来就不明显。这个结论写进论文里比单纯说“LSTM效果更好”要有说服力得多。具体到代码逻辑mindgo_lstm.py里的流程是先通过聚宽API拉取股票财务数据计算因子比如市盈率、市净率、ROE、营收增长率等然后按时间序列切分成训练集和测试集喂给模型。模型输出的是每只股票的“预测收益排名”排名靠前的进入候选池最后再用MACD和RSI做技术面过滤。2.3 模型checkpoint里藏了什么model/checkpoint目录下有四个文件single_model.ckpt.index、single_model.ckpt.meta、single_model.ckpt.data-00000-of-00001和checkpoint。这是TensorFlow 1.x的经典保存格式。.meta存的是计算图结构.index和.data-*存的是变量取值。加载时用tf.train.import_meta_graph恢复图结构再用saver.restore恢复权重。因为LSTM的图结构里包含BasicLSTMCell的变量名如果加载时变量名对不上会报类似Key xxx not found in checkpoint的错误。源码里专门写了变量名映射的逻辑你要是想改网络结构这里几乎必然要动。3. 环境搭建与依赖处理TensorFlow 1.x和Python版本适配3.1 依赖清单与版本建议这套源码的依赖不算复杂但版本敏感。核心依赖是TensorFlow、pandas、numpy以及聚宽的数据接口jqdatasdk。我实测下来一个能跑的版本组合是pip install tensorflow1.14.0 pip install pandas0.25.3 pip install numpy1.16.5 pip install jqdatasdk pip install talib逻辑说明TensorFlow 1.14是1.x系列的最后一个稳定版本对tf.compat.v1接口支持最完整。pandas和numpy的版本要配合1.14的TensorFlow——如果numpy版本太高比如1.20运行时会报np.float属性缺失的错误这是TensorFlow 1.x老代码的经典问题。参数说明.vscode/settings.json里其实已经配置了Python解释器路径和linting选项说明原开发者在VS Code里调试过。你如果也用VS Code可以参照这个文件的配置来避免语法检查误报——比如python.linting.enabled设为false否则pylint会对未使用的import报一堆警告干扰你读代码。3.2 聚宽平台接口本地跑VS平台跑mindgo_lstm.py是给聚宽JoinQuant研究环境设计的但里面的数据处理逻辑可以剥离出来本地跑。这里有个常见的认知误区以为必须在聚宽平台上才能运行。实际上聚宽的数据接口jqdatasdk是支持本地调用的你需要自己注册一个聚宽账号然后在代码里填入你的账号密码import jqdatasdk as jq jq.auth(你的用户名, 你的密码)逻辑说明调用jq.auth后会建立会话之后就能用jq.get_fundamentals拉财务数据、用jq.get_price拉行情数据。这个是聚宽官方支持的本地调用方式配额限制是每分钟调用次数有上限。参数说明聚宽免费版有数据调用频率限制我一般把拉数逻辑放在循环外一次性拉完存成csv缓存。不建议直接在回测循环里高频调用聚宽接口一个是慢另一个是容易触发限流导致数据中断。跑起来之后你会看到模型开始训练loss逐步下降。这时候我建议你打开任务管理器盯着内存——LSTM在财务因子这种小数据集上训练显存和内存占用都不大但如果你数据预处理时用了pandas.concat把全市场数据拼在一起内存峰值也不小。3.3 没有聚宽账号怎么办如果你注册不了聚宽或者想完全本地跑通需要把数据源换掉。我在实际调试中常用的替代方案是用akshare拉免费财务数据import akshare as ak # 拉取全部A股股票列表 stock_info ak.stock_info_a_code_name() # 拉取单只股票的财务指标 financial_df ak.stock_financial_abstract(symbol000001)逻辑说明akshare是纯免费的数据源不需要账号但字段命名和聚宽不一样需要做字段映射。比如聚宽的pe_ratio_lyr对应到akshare里可能是市盈率(动)你得在mindgo_lstm.py里的calculate_factors函数里改字段名。参数说明如果你用akshare数据注意财务数据的发布季度。比如2025年6月的时候你只能拿到2025年一季度的财报数据用未来数据回测是大忌——源码里本来就有防止未来函数的处理逻辑换数据源时务必检查这一点。4. 核心代码实战LSTM模型的数据处理、训练与推理4.1 特征工程财务因子怎么变成模型输入打开mindgo_lstm.py最先看到的应该是因子计算函数。它把你从聚宽拉到的原始财务数据转成模型能用的特征矩阵。这是整个项目里最值得抠代码的部分因为选股模型的效果上限在因子不在模型。def calculate_factors(q_financial_data): # 原始字段pe_ratio(市盈率), pb_ratio(市净率), roe(净资产收益率) factors pd.DataFrame() factors[pe_ttm] q_financial_data[pe_ratio_lyr] factors[pb] q_financial_data[pb_ratio] factors[roe] q_financial_data[roe] # 处理无穷值和缺失值市盈率可能出现负值或极端值 factors factors.replace([np.inf, -np.inf], np.nan) factors factors.fillna(factors.median()) # 截面标准化按日期分组做z-score factors factors.groupby(leveldate).transform( lambda x: (x - x.mean()) / x.std() ) return factors.values逻辑说明这里的核心操作有两个。第一个是处理无穷值和缺失值replace([np.inf, -np.inf], np.nan)把市盈率为负或极端异常的样本清掉再用fillna(factors.median())填充——之所以用中位数而不是均值是因为财务因子分布偏态严重均值容易被极端值拉偏。第二个是按日期分组做z-score标准化这一步很关键因为不同季度的市场整体估值水平不一样你要的是横截面上“哪只股票相对便宜”而不是拿今天的PE和两年前的PE比。参数说明groupby(leveldate)要求你的DataFrame索引必须是[date, code]的MultiIndex如果数据预处理时索引顺序搞反了这里会直接报KeyError。我在第一次跑的时候就是在这里翻的车报错信息还特别不直观得自己打印索引确认。4.2 时间序列切分防止未来函数泄漏LSTM选股的另一个关键是样本的组织方式。它的输入不是单条截面而是一个连续时间窗口的因子序列。源码里用滑窗方式构造样本def create_sequences(factor_data, seq_len5): x_data, y_data [], [] for i in range(len(factor_data) - seq_len): x_seq factor_data[i:iseq_len] # 过去5个季度的因子 y_label factor_data[iseq_len] # 下一个季度的因子 x_data.append(x_seq) y_data.append(y_label) return np.array(x_data), np.array(y_data)逻辑说明seq_len5表示用过去5个季度的财务因子预测下一个季度的情况。这个“5”不是随便定的财务数据是季度频率5个季度刚好覆盖一年多一点既能让模型看到周期的变化又不至于输入太长导致样本量不够。源码里这个参数是可以在mindgo_lstm.py顶部通过变量调整的建议你试的时候顺便跑一组seq_len3和seq_len8的对比。参数说明预测的目标y_label是“下一个季度的因子”而不是“下一期的涨跌幅”这意味着模型学的是因子本身的延续性不是直接预测收益。在mindgo_lstm.py后续逻辑里会把预测的因子值转成排名再映射到预期收益上。这种间接预测的好处是稳健——直接预测收益的噪声太大LSTM很容易过拟合。4.3 训练参数与LSTM网络结构看LSTM_class.py核心网络结构是两层LSTM加全连接层。这里贴一段训练入口的代码对应运行说明-仅供参考学习.txt里“训练模型”的步骤# 在 mindgo_LSTM_single_model.py 中 from LSTM_class import LSTMModel model LSTMModel( input_dim3, # 特征维度PE, PB, ROE三个因子 hidden_dim32, # LSTM隐层维度 layer_num2, # LSTM层数 output_dim3, # 输出维度与输入一致 learning_rate0.001 ) model.train(train_x, train_y, epochs100, batch_size32)逻辑说明hidden_dim32和layer_num2是最常见的取法。财务因子数据量小特征维度才3个不需要很大的网络。如果你把hidden_dim提到128训练会变慢而且大概率过拟合——验证集loss会在某个epoch之后不降反升。learning_rate0.001是Adam优化器比较安全的初始值。参数说明epochs100在财务数据这种小样本上是够用的我跑的时候到第60个epoch左右loss就基本不再下降。如果你发现loss一直在0.7左右不动先检查标准化有没有生效——未标准化的原始因子直接喂进LSTM梯度很容易消失。4.4 推理阶段加载checkpoint做预测训练完成后model/checkpoint里保存了权重。推理代码单独放在mindgo_LSTM_single_model.py里保证“训练”和“上线”分离import tensorflow as tf tf.reset_default_graph() with tf.Session() as sess: # 从meta文件恢复计算图 saver tf.train.import_meta_graph(model/checkpoint/single_model.ckpt.meta) saver.restore(sess, tf.train.latest_checkpoint(model/checkpoint/)) # 从图中取出输入输出tensor graph tf.get_default_graph() x_input graph.get_tensor_by_name(input_x:0) y_output graph.get_tensor_by_name(output_y:0) # 预测输入最新5个季度的因子序列 pred sess.run(y_output, feed_dict{x_input: latest_sequence})逻辑说明这段代码的关键在import_meta_graph和get_tensor_by_name。因为你恢复的是别人训练好的图必须知道图上各节点的名字才能取出输入输出。源码里定义模型时给tensor命名是input_x和output_y如果你自己改了网络结构这里的名字也要同步改。参数说明tf.train.latest_checkpoint(model/checkpoint/)会自动找目录下最新的checkpoint返回的是model/checkpoint/single_model.ckpt。这段代码依赖TensorFlow 1.x的运行方式在2.x下要加tf.compat.v1.disable_eager_execution()才能跑。5. 避坑清单运行时最容易翻车的五个问题5.1 现象module tensorflow has no attribute reset_default_graph这个报错出现在运行mindgo_LSTM_single_model.py时。原因是你装的是TensorFlow 2.xreset_default_graph被移到tf.compat.v1命名空间下了。解决方法是把文件开头的import改掉# 错误写法TensorFlow 2.x下直接报错 import tensorflow as tf tf.reset_default_graph() # 正确写法 import tensorflow.compat.v1 as tf tf.disable_v2_behavior()我在调试时试过最好用的方式是干脆在项目里建一个tf_compat.py公共模块所有用到TensorFlow 1.x接口的文件统一从那里导入这样改一处全局生效。5.2 现象numpy.floatobject has no attributedtype或类似报错这是TensorFlow 1.x配新版本numpy的经典冲突。TensorFlow 1.14源码里大量使用np.float、np.int这种老别名numpy从1.20开始移除了这些属性。解决方法是装回numpy 1.16.5或者做一个兼容层# 在导入tensorflow之前执行 import numpy as np if not hasattr(np, float): np.float float np.int int注意这个代码必须放在import tensorflow之前因为tensorflow内部import numpy时就会触发错误。如果你用VS Code调试.vscode/settings.json里的python.terminal.executeInFileDir配置可能影响相对路径排查时留意一下。5.3 现象加载checkpoint时报Key input_x not found in checkpoint这个错误最迷惑人因为你的代码里明明定义了input_x。原因在于checkpoint里的变量名带前缀——保存图结构时可能在variable_scope下或者模型定义里包了名字空间。解决方法是先打印checkpoint里实际存在的变量saver tf.train.import_meta_graph(model/checkpoint/single_model.ckpt.meta) for var in tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES): print(var.name)然后根据打印出的变量名调整get_tensor_by_name的参数。我见过的情况是变量名实际叫lstm/input_x:0而不是input_x:0多了一个lstm/前缀。5.4 现象回测结果里有“未来函数”特征的异常高收益如果你把mindgo_lstm.py的数据处理逻辑改成用akshare数据很容易引入未来函数。原因是聚宽的财务数据接口默认返回“实际披露日期”而akshare的某些接口直接返回“报告期”的数据也就是在报告期之前就能看到数据。解决方法是严格对齐“数据可用时间”用季报数据做预测时因子值必须对应其披露日期一般滞后报告期1-4个月而不是报告期本身。源码里的get_fundamentals查询里已经处理了这个问题——它加了date参数来指定“在这个日期时点能看到的数据”——但你换成akshare之后必须自己保证这一点。5.5 现象模型loss正常下降但候选股票池和随机选股没有明显区别这是选股模型最常见的隐性翻车。loss正常不代表模型学到了有效选股信息——如果因子本身没有预测力或者切分样本时把未来数据混进去了模型学到的是“平均值”。排查思路按顺序来先检查标准化是否按截面日期进行而不是全部数据统一标准化——后者会让模型学到的“高ROE”其实是“历史高ROE”对未来没有预测力。然后检查训练集和测试集的时间切分必须保证测试集的时间晚于训练集不能随机打乱切分。时间序列数据的分割不能用train_test_split(random_state42)这种随机切法必须按时间先后切。源码里用的是前80%时间段的样本训练、后20%测试这个顺序不要改。6. 进阶用法从复现到改进把基线模型变成你自己的选题当你把源码跑通之后下一步就该考虑怎么把“别人的毕业设计”变成“自己的毕业设计”。我的建议是不要大改模型结构而是从数据、特征、验证三个角度分别做增量实验。第一个值得动手的方向是扩展因子库。calculate_factors里现在只有PE、PB、ROE三个核心因子你可以加上营收增长率、毛利率、资产负债率、经营现金流等。操作方式是在聚宽的财务数据接口里多取几个字段然后在calculate_factors函数里加相应的标准化逻辑。注意保持特征维度的一致——LSTM_class.py里的input_dim3要改成你实际的因子数量否则训练时直接报维度不匹配的错误。第二个方向是加一个简单的模型评估模块。源码里没有写回测的绩效统计我自己习惯把MCAD_RSI.py输出的候选股票池接一段评价代码计算累计收益率和沪深300做对比import pandas as pd def evaluate_pool(stock_pool_codes, start_date, end_date): # stock_pool_codes: 模型选出的股票代码列表 # 这里拉取组合的日收益并计算累计净值 returns [] for code in stock_pool_codes: df jq.get_price(code, start_datestart_date, end_dateend_date, frequencydaily, fields[close]) df[ret] df[close].pct_change() returns.append(df[ret].mean()) # 简单等权组合年化收益 avg_daily_ret sum(returns) / len(returns) annual_ret (1 avg_daily_ret) ** 252 - 1 return annual_ret这段代码实现了等权组合的年化收益估算。注意聚宽的get_price接口有调用频率限制如果股票池里有50只股票循环拉日线数据容易超限建议一次性把数据拉全存在本地再算。第三个方向是参数敏感性分析。seq_len、hidden_dim、learning_rate这三个参数每组参数跑一遍记录测试集上的排名ICInformation Coefficient。所谓IC是预测排名和实际收益排名的Spearman相关系数IC大于0.05就说明模型有微弱的选股能力大于0.1算不错。这个指标写进论文里比“准确率85%”要专业得多。从我拆过这么多量化类毕业设计的经验来看这套源码最大的价值在于它把“选股预测”这件事做成了可复现的闭环——你有数据入口、有模型、有对比基准、有技术面过滤任何一个环节都能单独拎出来做深入分析。这也正是它适合作为毕业设计的原因不是简单跑通一个模型而是有足够多的扩展点让你做出自己的增量贡献。运行这套源码之前我强烈建议你先把运行说明-仅供参考学习.txt完整读一遍那里有原作者踩过坑之后的运行顺序。从那以后我每次跑类似的LSTM时间序列项目都强制自己先确认TensorFlow版本、numpy版本、数据时间切分顺序这三件事再动手能省下至少半天排错时间。希望帮到你。本文还有配套的精品资源点击获取