简介这套基于深度Q学习网络的股票交易策略设计源码面向金融量化、深度学习及强化学习方向的开发者与研究者解决利用DQN进行行情数据处理、策略建模和交易决策的学习与验证问题。压缩包共24个文件大小12.21MB以Python脚本为主体辅以分钟/日级别CSV行情数据、TensorFlow模型检查点meta/index/data以及说明文档覆盖数据预处理、环境模拟、模型训练、策略回测等完整流程。已有330人学习下载。通过核心DQN训练脚本可直观理解动作价值网络与经验回放机制环境模拟与回测脚本则帮助在历史数据上检验策略收益与稳健性附带多种粒度的行情样本和预训练模型检查点便于断点续训和快速复现实验。对希望从理论走向实战、搭建可扩展交易策略原型的读者具有实用参考价值。1. 深度Q学习网络做股票交易这份源码能帮你少走多少弯路深度Q学习网络DQN拿来炒股票听起来像玄学但这份源码把自己的边界划得很清楚它不是预测明天涨跌的黑匣子而是一个在模拟环境里反复试错、学习买卖时机的强化学习智能体。整套代码基于 Python 和 TensorFlow 1.x 实现核心就三个文件——prepare_data.py 负责把行情数据切成模型能吃的状态序列stock_env.py 搭了一个带交易规则的模拟盘DQN_trade.py 把 Q 网络、经验回放和 epsilon 贪心策略串成完整的训练闭环。源码里还带了平安银行sz000001的日线和分钟线两份真实行情以及一组已经训练到第 9 轮的检查点文件。适合三类人想复现强化学习在金融场景落地的算法工程师、写课程设计需要完整可运行源码的学生、以及想验证 DQN 策略在历史数据上到底能不能赚到钱的研究者。下面按数据、环境、训练、避坑、回测的顺序拆开讲。2. 数据这关先过prepare_data.py 与三份 CSV 的选型和清洗很多人在 DQN 上翻车不是网络结构不对而是喂进去的数据本身就是脏的。这份源码里给了三份 CSV先弄清楚它们各自的作用再去跑 prepare_data.py否则后面训练出来的模型大概率是废的。2.1 三份 CSV 分别是什么data.csv、分钟线、日线的定位差异文件清单里 data.csv 排在最前面它通常是通用交易数据集字段基本是 date、open、high、low、close、volume 这类标准 OHLCV。注意 data.csv 里大概率没有代码列它是给那些不想绑定具体股票的人用的通用数据。sz000001_min.csv 是平安银行分钟线sz000001_day.csv 是平安银行日线。分钟线和日线的用途完全不同。日线数据样本量小一年只有 250 根 K 线训练 DQN 这种需要大量试错的算法很容易陷入欠拟合智能体还没学会交易规则数据就过完了。分钟线一年有几千根样本充足但噪音也大行情跳空、集合竞价、午盘休市都会带来异常值。我一般会这样选验证流程、跑通代码用日线因为训练快几分钟就能看到收敛趋势正式调参用分钟线但先做重采样和去噪。data.csv 如果字段和两者都不同先打印 head() 看列名别急着往下跑。2.2 prepare_data.py 的常见处理流程窗口、归一化、特征工程这份源码的 prepare_data.py 做的核心事情是把原始行情转换成强化学习里的状态state。常见做法是滑动窗口加技术指标下面这段代码是这类脚本最典型的处理逻辑import pandas as pd import numpy as np def prepare_data(csv_path, window10): df pd.read_csv(csv_path) df[date] pd.to_datetime(df[date]) df df.sort_values(date).reset_index(dropTrue) # 基础特征对数收益率消除价格绝对水平影响 df[ret] np.log(df[close] / df[close].shift(1)) # 技术指标MA5 和 MA10作为趋势特征 df[ma5] df[close].rolling(5).mean() df[ma10] df[close].rolling(10).mean() # 归一化只用历史窗口的均值方差避免未来数据泄漏 df[close_norm] (df[close] - df[close].rolling(20).mean()) / df[close].rolling(20).std() df df.dropna().reset_index(dropTrue) states [] for i in range(window, len(df)): # 每个状态是过去 window 天的特征矩阵 state df[[close_norm, ret, ma5, ma10]].iloc[i-window:i].values states.append(state) return np.array(states, dtypenp.float32), df这段代码里最关键的参数是 window也就是每个状态回看的K线根数。window 设太小比如3模型只能看到极短期的波动学不到趋势设太大比如60状态维度高了训练时间翻倍而且早期数据全被窗口吃掉。分钟线建议 window 取 20 到 30日线取 10 到 15。归一化那行用了 rolling(20) 的均值和标准差而不是全量数据的均值和标准差这是为了防止未来函数——如果用全量均值归一化等于是拿未来的统计量去处理过去的数据回测成绩会虚高实盘直接打回原形。2.3 特征列的选择别把成交量直接塞进去很多人在准备数据的阶段就埋雷。常见的错误是把成交量原始数值直接作为特征喂给网络但分钟线和日线的成交量尺度差几个数量级网络训练时 loss 容易震荡。源码里如果没做成交量处理我建议自己加一步成交量也做归一化或者用成交量变化率。另外一个容易忽略的点是特征列顺序。prepare_data.py 输出的 state列顺序必须和 stock_env.py 里 reset() 返回的状态顺序一致。这个顺序错了不会报错但模型学到的映射关系是乱的表现为训练 loss 能降、回测却稳定亏损。如果 run 起来有这样的现象先回来核对特征顺序。3. 交易环境与 DQN 主循环stock_env.py 和 DQN_trade.py 是如何咬合的数据准备好之后接下来是整个项目的核心交易环境怎么定义DQN 怎么在这个环境里学习和决策。这两个文件是配套的stock_env.py 定义「游戏规则」DQN_trade.py 定义「玩家大脑」。3.1 交易环境的三要素状态、动作、奖励强化学习环境的标准结构是 reset() 和 step() 两个方法。stock_env.py 里的模拟交易环境典型设计如下import numpy as np class StockEnv: def __init__(self, data, init_balance100000, fee_rate0.001): self.data data # shape: [样本数, window, features] self.init_balance init_balance self.fee_rate fee_rate # 单边手续费万五到千一常见 self.reset() def reset(self): self.idx 0 self.balance self.init_balance self.position 0 # 持仓股数0 表示空仓 self.total_steps len(self.data) return self.data[self.idx] def step(self, action): # action: 0持有, 1买入, 2卖出 current_price self.data[self.idx, -1, 0] # 取窗口最后一根K线的收盘价 if action 1 and self.position 0: # 全仓买入扣手续费 self.balance - self.balance * self.fee_rate self.position self.balance / current_price self.balance 0 elif action 2 and self.position 0: # 清仓卖出扣手续费 self.balance self.position * current_price self.balance - self.balance * self.fee_rate self.position 0 self.idx 1 done (self.idx self.total_steps - 1) # 奖励当前总资产相对上一步的变化 total_asset self.balance self.position * current_price if self.idx 0: prev_price self.data[self.idx - 1, -1, 0] prev_asset self.balance self.position * prev_price reward (total_asset - prev_asset) / self.init_balance else: reward 0 return self.data[self.idx], reward, done这份环境代码里有三个设计决策值得关注。第一动作空间只做了持有、买入、卖出三个离散动作没有做仓位比例控制这是 DQN 落地交易最常见的简化——连续仓位控制需要 DDPG 或 SACDQN 处理不了。第二 rewards 用的是总资产变化率除以初始资金把奖励尺度压到 0.001 这个量级避免极端数值把 Q 网络输出撑爆。第三手续费 fee_rate 设成了千分之一这是买入卖出都要扣的单边费率如果不设手续费模型会学到高频来回买卖套利实盘里这种策略必死。3.2 DQN 网络结构与超参数从文件命名反推训练配置saved_network 里有 network-dqn-5 到 network-dqn-9 共 5 组 checkpoint每组包含 .meta、.index、.data-00000-of-00001 三个文件。这说明代码里调用 saver.save() 时带了 global_step 参数每个 episode 存一次。DQN_trade.py 里最核心的深度Q网络部分常见实现是这样的import tensorflow as tf class DQN: def __init__(self, state_dim, action_dim3, hidden_dim64): self.state_dim state_dim self.action_dim action_dim self.hidden_dim hidden_dim # 在线网络每一步训练都更新 self.state_input tf.placeholder(tf.float32, [None, None, state_dim], namestate_input) flat tf.layers.flatten(self.state_input) h1 tf.layers.dense(flat, hidden_dim, activationtf.nn.relu) h2 tf.layers.dense(h1, hidden_dim, activationtf.nn.relu) self.q_values tf.layers.dense(h2, action_dim, nameq_values) # 目标网络每隔 N 步同步一次稳定训练 self.target_q_values tf.placeholder(tf.float32, [None, action_dim]) loss tf.reduce_mean(tf.square(self.target_q_values - self.q_values)) self.train_op tf.train.AdamOptimizer(learning_rate0.001).minimize(loss)这里有两个网络在线网络和目标网络。DQN 的一个经典问题是自举导致的训练发散——如果用同一个网络既预测又当标签更新会让 Q 值不断高估。目标网络的做法是用一份参数延迟同步的副本去算目标 Q 值每隔一定步数常见是 100 到 500 步把在线网络的参数复制过去。这份源码的 checkpoint 文件名里没有区分 target 网络说明目标网络参数是跟随主网络一起保存的恢复训练后需要重新走同步逻辑。超参数方面从常见实现和经验来看learning_rate 0.001 偏保守但稳定epsilon 从 1.0 开始衰减到 0.01衰减率设置在 0.995 到 0.999 之间。如果你发现训练时一开始 loss 很大、后面才降下来这是正常的因为前期的 epsilon 高模型在大量随机探索Q 值波动大。3.3 经验回放DQN 训练稳定性的另一根支柱stock_env.py 和 DQN_trade.py 的衔接点是经验回放缓冲区。step() 返回的 (state, action, reward, next_state, done) 不会立即用于训练而是先存进一个队列训练时随机采样一批。这个设计的目的是打断时序数据的相关性否则网络学到的只是最近几步的局部规律。代码里常见这样一段import random from collections import deque class ReplayBuffer: def __init__(self, capacity10000): self.buffer deque(maxlencapacity) def push(self, state, action, reward, next_state, done): self.buffer.append((state, action, reward, next_state, done)) def sample(self, batch_size32): batch random.sample(self.buffer, batch_size) states np.array([x[0] for x in batch]) actions np.array([x[1] for x in batch]) rewards np.array([x[2] for x in batch]) next_states np.array([x[3] for x in batch]) dones np.array([x[4] for x in batch]) return states, actions, rewards, next_states, dones缓冲区的 capacity 是个值得调的参数。10000 意味着模型能回看最近 10000 步的交易经验对分钟级数据来说大概是 20 个交易日够用。如果设太小比如 1000学到一段时间后会被近期的噪声经验冲刷掉设太大比如 100000老经验占内存大训练速度变慢而且市场特征早就变了旧经验的参考价值不大。4. 训练断电续跑saved_network 里 checkpoint 的正确读法训练 DQN 动辄几小时甚至一晚上中途断掉是常态。saved_network 目录里那几组文件就是为了解决「断电后从最近进度继续」的问题。但 checkpoint 文件的读法有讲究读错了要么加载失败要么加载了但 epsilon 等训练状态全丢了。4.1 checkpoint 三个文件各管什么meta、index、data-00000-of-00001TensorFlow 1.x 保存的 checkpoint 是分文件存储的。.meta 文件存的是计算图结构也就是网络层数、每层神经元数量、激活函数这些.index 和 .data-00000-of-00001 合起来存的是具体权重数值。这三个文件缺一不可但恢复时只需要指定前缀即可。network-dqn-9 这组代表第 9 个 episode 结束时保存的状态。从 network-dqn-5 到 9说明至少训练了 9 轮但注意这 9 轮不一定是 9 个完整 epoch可能是 9 次完整遍历数据也可能是一天训练里的 9 个阶段性保存。4.2 恢复训练的两种姿势import_meta_graph 与手动重建如果你要接着训第一种方式是直接加载图结构import tensorflow as tf sess tf.Session() saver tf.train.import_meta_graph(saved_network/network-dqn-9.meta) saver.restore(sess, tf.train.latest_checkpoint(saved_network)) # 之后通过 sess.graph.get_tensor_by_name(state_input:0) 取占位符继续训练第二种方式是代码里重新构建一遍网络结构然后用 Saver.restore 覆盖权重。这种方式更灵活因为你可以微调结构参数但同时要求新结构和保存时的结构完全一致否则会报 shape mismatch。这里有一个容易踩的坑TensorFlow 2.x 默认模式跑不动这份代码。项目文件的 checkpoint 格式是 TF 1.x 风格如果你机器上装的是 TF 2.x需要加这两行开启兼容模式import tensorflow.compat.v1 as tf tf.disable_v2_behavior()如果不加tf.placeholder、tf.layers 这些 API 直接报 AttributeError。4.3 继续训练时的 epsilon 重置一个细节定成败加载权重只是恢复了网络参数训练状态里还有两个东西不在 checkpoint 里epsilon 当前值和经验回放缓冲区的内容。如果从第 9 轮继续训练时把 epsilon 重置回 1.0模型会重新进入大量随机探索状态把已经学好的策略冲坏如果保持 0.01 不动模型又失去了继续探索的能力。我一般会在代码里维护一个变量保存当前 epsilon并把它写进文件名或者单独一个 json 配置文件里。从断点恢复时先读这个配置再决定是沿用旧值还是适当回升。如果不做这一步从 checkpoint 恢复的模型表现可能比不恢复还差这属于典型的隐蔽问题。5. 避坑清单五个让新手翻车的细节这部分是跑这个项目时最容易踩的坑每一条都有明确的现象、原因和解法。5.1 训练 loss 不降反升数值稳定飘高现象DQN_trade.py 跑起来后loss 不但没下降还在 10 以上反复横跳。原因奖励函数尺度设计不合理。如果 reward 直接用总资产的绝对变化量比如几百上千Q 网络的损失函数计算的是平方误差数值会被放大几个数量级梯度更新一步就冲飞了。解决把奖励除以 init_balance或者用对数收益率让奖励落在 0.01 量级。改完 loss 会明显稳定下来。5.2 回测赚钱实盘亏钱现象test.py 回测结果年化收益 30% 以上但实盘或模拟盘跑同样的策略持续亏损。原因数据泄漏。最常见的是 prepare_data.py 里归一化用了全量数据的均值方差或者特征里包含了未来 K 线的信息。另一个常见原因是回测没有扣手续费和滑点。解决归一化只允许用截至当前时刻的历史数据计算统计量。检查代码里有没有出现 shift(-1) 或者对全量数据调用 mean()、std()。手续费 fee_rate 至少设 0.001分钟线策略建议设 0.002 加滑点。5.3 加载 checkpoint 报 OutOfRangeError 或 shape mismatch现象restore 时直接崩报错信息里有 shape 或者 out of range 字样。原因网络结构和保存时不一致。你可能改了 hidden_dim、window 或者 feature 数量但 checkpoint 里存的还是旧结构的权重。解决想要加载就保持结构完全一致想改结构就把 checkpoint 留着重新训练。另外注意 import_meta_graph 加载的图结构和当前代码不一致时也会报错优先用 latest_checkpoint 配合手动重建网络的方式调参更自由。5.4 分钟线数据训练时偶尔出现 NaN loss现象训练跑到某个点loss 变成 NaN然后权重全部变成 NaN模型彻底废掉。原因数据里有 NaN 值或者归一化时标准差为 0某段时间价格横盘导致特征出现无穷大。分钟线数据里常见的是午盘休市时间戳缺失、成交量列为 0。解决prepare_data.py 里加一行 df df.replace([np.inf, -np.inf], np.nan).dropna()然后在归一化代码里对 std 加一个极小值 epsilon比如分母写成 std 1e-8。5.5 训练很久但策略一直是全仓买入不动现象episode 跑了很多轮epsilon 已经衰减到 0.02 了但智能体的策略始终是买入后一直持有从不卖出。原因奖励函数里没有对持仓周期做惩罚而股票长期趋势向上样本期内持有不动的期望收益高于来回交易模型学到了「躺赢」策略。这不算是 bug但说明策略没有学到择时能力。解决在奖励里加一个持仓惩罚项比如 reward - position * 0.0001让模型对持仓成本敏感。或者把奖励改成相对基准的超额收益即策略收益减去同期买入持有收益迫使模型产生主动择时。6. 回测验证与调参手感test.py 怎么用才不算自欺欺人到这一步模型训完了接下来是验证环节。test.py 的作用是加载训练好的 checkpoint在历史数据上模拟交易输出收益曲线、胜率等统计指标。但回测这件事同样的代码不同人跑能得出完全不同的结论关键在口径。6.1 两种回测跑法第一种是单次回测加载最新 checkpoint 后从测试集开头跑到结尾看累计收益。这个最简单但只有一次结果方差大运气成分高。第二种是滑动窗口回测把数据切成 N 段每段上分别测试最终统计平均收益和标准差。我一般跑后一种测试集分段不重叠每段至少包含一次明显的涨和一次明显的跌这样能看出策略在不同市场行情下的适应能力。6.2 看指标别只看收益率每次回测完我习惯记录四个数字累计收益率、最大回撤、夏普比率、交易次数。其中最大回撤比收益率更关键——一个回撤 40% 的策略收益率再高也没有实盘价值因为心理上扛不住资金上也可能触发风控。交易次数则用来判断策略是否过度交易次数太多说明模型在学高频噪声次数太少说明策略可能又退化成躺平。6.3 调参手感表参数欠拟合/不收敛过拟合/不稳学习率太小1e-4 以下训练极慢太大1e-2 以上loss 震荡epsilon 衰减衰减太快探索不足只会躺平衰减太慢一直随机买卖经验回放容量太小学完就忘太大旧经验干扰新趋势window 窗口太小看不到趋势太大特征冗余训练慢实际调参时不要同时动多个参数一次只改一个记录回测指标的变化。我发现这个项目里最敏感的参数是 epsilon 衰减率和手续费这两个直接决定了策略是偏向探索还是偏向利用。顺手记录一段经验那之后我每次跑完训练都会强制走一遍「加载第 N 轮 checkpoint → 跑测试集 → 记录四指标」的固定流程再决定要不要继续训练。不这样做你根本不知道第 7 轮和第 9 轮的模型差别到底在哪。希望这份拆解能帮你在复现这套源码时少走几步弯路。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
Ekko Agent 集成 Apple Reminders:基于 remindctl 的 macOS 提醒事项技能全解析 Ekko Agent 集成 Apple Reminders:基于 remindctl 的 macOS 提醒事项技能全解析 【免费下载链接】ekko-studio Ekko Studio is a local-first AI workspace for multi-agent chat, coding, and visual workflows, available on desktop and the web. 项目地址: ht… · 2026/9/23 18:13:12
3分钟搞定x线胸片代码逻辑,附完整示例 3分钟搞定x线胸片代码逻辑,附完整示例 翻遍官方文档,关于图像处理的API说明往往篇幅冗长,核心逻辑却藏在晦涩的函数定义里,让人抓不住重点。对于刚接手医疗影像项目的开发者,这种“文档太长抓不住重点”的困境尤为典型。今天直接上干货,通过拆解一… · 2026/9/23 18:13:12
Yii 2 Behaviors(行为)机制完全指南:定义、挂载、事件响应与内置行为实战 后端Web框架 【免费下载链接】yii2 Yii 2: The Fast, Secure and Professional PHP Framework 项目地址: https://gitcode.com/gh_mirrors/yi/yii2 点击查看 免费下载 Behaviors(行为,又称 mixin/混合)是 Yii 2 框架中用于在不改… · 2026/9/23 18:13:12
循环标记法:原地迭代的工程级数组处理范式 1. 为什么“循环标记法”不是教科书里的标准术语,却在真实工程中高频出现你翻遍《数据结构与算法分析》《C语言程序设计》甚至LeetCode官方题解,几乎找不到“循环标记法”这个名词。它既不在算法导论的索引里,也不在任何主流编程语言的API文档… · 2026/9/23 19:22:51
C# Math函数深度解析:精度陷阱、边界条件与高效实践 做C#开发这些年,Math类是那种看起来简单、用起来也简单,但真往深了挖全是坑的类型。很多人都觉得Math函数不就是Abs、Floor、Round这些吗,查个文档就完事了,但实际在项目里跑起来,精度问题、边界条件、性能损耗全冒出来… · 2026/9/23 19:22:45
自动驾驶多类别交通物体检测数据集:28类标注与YOLO训练实战 简介:这份自动驾驶多类别交通物体检测数据集面向从事目标检测算法研发的工程师、学生与科研人员,尤其适合使用YOLO系列(含YOLOv12)进行模型训练与验证的场景。数据集覆盖28类交通与道路相关目标,从行人、车辆、交通灯到… · 2026/9/23 19:22:45
Python岩石裂缝CT岩心语义分割源码与数据集:U-Net实战 简介:这份资源面向计算机视觉与地质工程方向的本科生、研究生及课程设计开发者,提供一套基于Python的CT岩芯与岩石裂缝语义分割完整方案,可用于期末大作业、课程设计或相关课题的快速复现与二次开发。压缩包共15个文件,约1.15MB&a… · 2026/9/23 19:22:45
摩尔投票法原理与高性能优化实践 1. 摩尔投票法基础原理摩尔投票法(Moore Voting Algorithm)是一种用于在数据流或数组中高效寻找多数元素的算法。我第一次接触这个算法是在处理一个实时日志分析系统时,需要快速识别出高频出现的错误类型。1.1 算法核心思想摩尔投票法的精妙之… · 2026/9/23 19:22:45
TensorRT-LLM部署Qwen1.5:从权重转换到引擎构建的完整指南 简介:面向大模型部署工程师与算法开发者的实战资源,聚焦TensorRT-LLM框架下部署Qwen1.5大语言模型的完整过程,针对推理时延高、显存占用大等常见难题,给出从模型转换到生产级部署的可行方案。压缩包共5个文件,包含4个P… · 2026/9/23 19:22:39
3招搞定手机怎么下载微信面试难题实战项目解析 3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29