DQN股票交易策略实战:环境搭建、网络训练与避坑指南
简介基于深度Q学习网络的股票交易策略设计源码是一个将强化学习与深度学习相结合的完整算法项目面向量化交易研究者、金融科技开发者和Python算法学习者用于解决股票市场中的自动交易决策问题。压缩包共24个文件主要包括Python脚本、CSV行情数据、深度Q网络检查点及说明文档整体体积12.21MB目录围绕数据预处理、环境模拟、模型训练与回测等关键环节组织便于直接进入开发流程。当前已有330人学习下载。资源提供DQN_trade.py核心训练脚本与test.py测试脚本附赠分钟和日级别历史K线数据并保存了多个训练好的网络模型检查点支持断点续训和策略回测。借助stock_env.py交易环境与prepare_data.py数据处理管线可以完整复现深度Q学习在股票交易中的应用还能在此基础上改进奖励函数或状态特征快速开展自己的量化策略实验。1. 深度Q学习网络做股票策略不预测涨跌直接学怎么交易先抛一个反直觉的结论用深度Q学习网络DQN做股票交易核心优势不在于“预测准”而在于“会做决策”。传统的价格预测模型给你一个涨跌概率但涨了买多少、跌了要不要止损、空仓时是等待还是进场——这些决策问题它不负责。DQN把整个交易过程当作马尔可夫决策链输入当前市场状态输出买、卖、持有三种动作然后根据账户收益给出奖励信号反复迭代直到模型学会在什么行情下做什么动作。本文围绕一套可运行的DQN股票交易策略源码把环境构建、网络设计、训练参数和踩坑点全部拆开讲。适合已经懂一些深度学习基础、但还没把强化学习完整跑通在金融数据上的读者——不需要你先精通RL数学跟着代码走一遍就明白七成。2. 搭建DQN交易环境状态、动作与奖励的设计顺序强化学习中环境决定策略上限。很多拿到DQN交易源码直接跑的人第一步就把环境写错了状态里塞了一堆没有对齐的特征奖励函数只写一行收益率。后面网络再高级都救不回来。这一章按“状态→动作→奖励”的顺序把环境拆开讲最后给一个可以直接跑的最小环境代码。2.1 状态空间模型看到的每一维特征都得多问一句“有没有用”状态是agent做决策的依据。常见做法是将两个维度拼起来第一维是过去N天的价格形态第二维是当前持仓状态。价格形态不是直接把原始收盘价喂进去而是用归一化的相对价格。为什么因为DQN对输入尺度很敏感股价从50涨到100和从500涨到550绝对幅度完全不同但相对涨幅都算“涨了10%”。归一化后模型才能学出可泛化的规律。具体来说我会用window_size30天的收盘价序列把窗口内每个价格除以窗口第一个价格再减1得到一个“相对基准涨跌幅”的序列。它反映的是最近30天是上涨趋势还是下跌趋势以及波动形态。然后拼一个持仓状态位0表示空仓1表示满仓。这个维度告诉模型“你现在手里有没有筹码”决定了买入动作的执行条件。这里有个容易忽略的边界状态不能只含价格不含成交量。很多公开源码里省略了成交量因为DQN训练时间会变长但至少把window_size里的均价或成交量变化率加进去否则遇到放量拉升的行情模型只看到价格涨就会在错误的位置追高。最小方案是加一个“最近5日成交量相对30日均量的倍数”。状态向量从window_size维变成window_size2维。2.2 动作空间与奖励函数买、卖、持有的取舍与收益计算动作空间先用离散三分类买入0、卖出1、持有2。为什么不用连续仓位因为连续动作空间需要DDPG或PPO这类policy gradient算法DQN直接输出离散Q值三分类最容易稳定收敛。很多策略源码在真实交易中会加一个“持仓比例0到100%”的连续动作本质上是把仓位离散化成5档甚至10档这也是可以的但训练难度会随动作数线性上升。第一次跑通就用3个动作。奖励函数是另一个关键。最简单的是每步计算账户净值的变化率当前总资产减去上一时刻总资产除以上一时刻总资产。这个公式本身没问题但如果忽略了交易成本模型会学出一个高频交易狂魔。奖励里要把两件事扣掉手续费和滑点。手续费在环境中模拟为买卖成交额的比例滑点模拟为成交价偏移。至少把手续费加进去否则回测曲线再好看也不可信。还有一个很实际的细节reward的数值量级。净值变化率的量级通常在0.001到0.01之间DQN的loss直接用MSE这种小数值会让loss看起来异常小但不代表学得好。我会把reward乘以100再给网络相当于用百分数做奖励训练曲线更好观察梯度也更稳定。这个改动不影响梯度方向只影响数值尺度和可视化时的直觉。2.3 环境代码骨架一份可以直接跑的TradingEnv把上面的设计落到代码就是下面这个类。我这里用pandas读数据字段至少包含date、close、volume建议也带上open/high/low用于更精细的价格模拟。这个环境跑在单股票的日线数据上每次reset后从window_size位置开始。import numpy as np import pandas as pd class TradingEnv: 最小可用的DQN交易环境 动作: 0卖出, 1持有, 2买入 状态: 最近window_size天的归一化收盘价 持仓标记 def __init__(self, df, window_size30, fee0.0005, slide0.0005): self.df df.reset_index(dropTrue) self.window_size window_size self.fee fee # 佣金比例, 双边各收一次 self.slide slide # 滑点比例, 双边各偏一次 self.reset() def reset(self): self.step self.window_size self.holding 0 # 0空仓, 1满仓 self.capital 100000.0 # 初始现金 self.shares 0 return self._get_state() def _get_state(self): close self.df[close].values[self.step - self.window_size: self.step] norm close / close[0] - 1.0 state np.append(norm, self.holding) return state.astype(np.float32) def step(self, action): price self.df[close].values[self.step] prev_value self.capital self.shares * price # 买入: 空仓 - 满仓, 扣佣金和滑点 if action 2 and self.holding 0: buy_price price * (1 self.slide) self.shares self.capital / (buy_price * (1 self.fee)) self.capital 0.0 self.holding 1 # 卖出: 满仓 - 空仓, 扣佣金和滑点 elif action 0 and self.holding 1: sell_price price * (1 - self.slide) self.capital self.shares * sell_price * (1 - self.fee) self.shares 0 self.holding 0 # 推进到下一个bar self.step 1 done self.step len(self.df) - 1 if done: next_price self.df[close].values[-1] else: next_price self.df[close].values[self.step] next_value self.capital self.shares * next_price # 用百分数变化做奖励, 便于观察 reward (next_value - prev_value) / prev_value * 100.0 return self._get_state(), reward, done代码核心逻辑有两个需要细看的点。第一买入时用了buy_price * (1 self.fee)意思是成交价按“滑点偏移后的价格”计算再按成交额收佣金卖出时反过来滑点往下偏佣金照扣。这样一套下来每轮完整的买卖要付出大约成交额0.2%左右的总成本接近A股双边费率的现实水平。第二_get_state里的close[0]是窗口内第一天的收盘价窗口是滑动的所以归一化基准也随每一步变化这保证输入特征的相对位置一致不会因为股价从50涨到100就改变特征幅度的量纲。注意这个环境里我只用了收盘价和持仓两个维度。你在自己的源码里扩展状态时直接在_get_state里追加np.append即可例如把“最近5日均量相对30日均量倍数”加进去。但要记住state_dim变了后面DQN网络的输入维度也要同步改否则会报维度不匹配的错误。3. 深度Q网络三大核心模块网络架构、经验回放与目标网络环境准备好之后剩下的就是DQN本身。这里拆开讲三个必须做对的东西网络结构、经验回放、目标网络。任何一个没处理好训练都会出问题——要么q值爆炸要么loss震荡不收敛要么模型只记住最近一段行情。3.1 网络结构三层全连接是最稳的起点别一上来就堆LSTMDQN网络做的事情很简单输入状态向量输出每个动作的Q值。Q值可以理解为“在这个状态下做这个动作未来收益的期望”。网络训练的目标就是让这个Q值逼近真实值。交易场景下状态是价格序列加持仓标记序列长度固定为window_size所以第一版用三层全连接完全够用。有人会问价格序列是不是得用LSTM或者Transformer才能学出时序规律我的建议是先把全连接跑通。全连接不是不能学时序而是它学的是“窗口内特征组合和动作价值之间的映射”对短窗口30天来说表达能力已经足够。LSTM确实能捕捉更长的依赖但训练不稳定、调参成本高而且稍不注意就过拟合。在日线级别的交易策略里30天均线级别的趋势足够支撑买、卖、持有决策没必要用序列模型把问题搞复杂。import torch import torch.nn as nn class DQN(nn.Module): def __init__(self, state_dim, action_dim, hidden256): super().__init__() self.net nn.Sequential( nn.Linear(state_dim, hidden), nn.ReLU(), nn.Linear(hidden, hidden), nn.ReLU(), nn.Linear(hidden, action_dim) # 输出每个动作的Q值 ) def forward(self, x): return self.net(x)这个网络输出维度是3正好对应买入、卖出、持有。hidden取256是经验值太小比如64拟合能力不够策略容易学成“一直持有”太大比如1024会增加训练时间和过拟合风险而且收益提升非常有限。如果你用的是分钟级高频数据可以适当加大到512但日线数据256足够。激活函数用ReLU最后一层不加激活因为Q值本身是有正有负的回归目标不能用sigmoid或tanh限制输出范围。3.2 经验回放打破时间序列相关性是DQN能work的前提经验回放replay buffer是DQN能稳定训练的基石。强化学习中agent每一步产生一条经验(s, a, r, s, done)如果每来一条就立刻更新网络相邻两步的状态和动作高度相关网络会被带偏。比如最近一段连续上涨模型就只会学“买入是对的”等行情转向它反应不过来。解决方案是先把这些经验存进一个固定容量的队列训练时从中随机抽一批出来更新。打乱了时间顺序打破了相关性网络学到的是不同市场形态下动作的期望价值而不是某一段特定行情的模式记忆。容量一般设10000到100000太小的话随机采样的多样性不足太大则可能采到很久以前和当前策略完全无关的旧数据更新效率变低。from collections import deque import random 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_size): batch random.sample(self.buffer, batch_size) s, a, r, ns, d zip(*batch) return (np.array(s), np.array(a), np.array(r), np.array(ns), np.array(d)) def __len__(self): return len(self.buffer)用deque(maxlencapacity)很关键它会在溢出时自动丢掉最老的经验不需要手动管理索引。采样时random.sample保证每条经验被抽中的概率均等。注意经验回放里存的是“当时的动作”和“当时的状态”不是当前策略重新评估的结果。无论之后策略怎么变这批经验都代表一次真实发生的环境交互这正是off-policy学习的核心。3.3 目标网络固定一个“慢速版本”来限制训练方差Q-learning有一个自举bootstrapping问题更新Q值时目标值里也包含Q值本身。如果你一边更新网络一边用这个网络算目标值目标会追着预测跑训练过程容易发散。DQN的做法是维护两个结构完全相同的网络策略网络policy net负责输出Q值并更新梯度目标网络target net负责计算目标Q值它不参加梯度更新只是每隔固定步数把策略网络的权重拷贝过来。class DQNAgent: def __init__(self, state_dim, action_dim, lr1e-3, gamma0.99): self.gamma gamma self.policy_net DQN(state_dim, action_dim) self.target_net DQN(state_dim, action_dim) self.target_net.load_state_dict(self.policy_net.state_dict()) self.optimizer torch.optim.Adam(self.policy_net.parameters(), lrlr) self.loss_fn nn.MSELoss() def act(self, state): with torch.no_grad(): q self.policy_net(torch.FloatTensor(state).unsqueeze(0)) return q.argmax().item() def update(self, s, a, r, ns, d): s torch.FloatTensor(s) a torch.LongTensor(a) r torch.FloatTensor(r) ns torch.FloatTensor(ns) d torch.FloatTensor(d) # 策略网络给出当前状态下所选动作的Q值 q_values self.policy_net(s).gather(1, a.unsqueeze(1)).squeeze(1) # 目标网络计算下一步的最大Q值 with torch.no_grad(): next_q self.target_net(ns).max(dim1)[0] target r self.gamma * next_q * (1 - d) loss self.loss_fn(q_values, target.detach()) self.optimizer.zero_grad() loss.backward() self.optimizer.step()这个代码里最值得注意的就是target.detach()。如果不做detachtarget里的next_q部分会参与梯度计算梯度顺着目标网络反向传到策略网络优化目标就变成“让目标值去迁就预测值”这会让loss下降为零但策略完全没学出来。detach之后目标被看作一个固定常数策略网络只负责往这个固定方向逼近训练才稳定。目标网络更新频率是一个重要超参数。我一般每100个episode拷贝一次权重。太频繁的话目标网络和策略网络几乎同步失去“限制方差”的意义太低的话目标长期不更新策略网络会被一个过时的目标带偏。用100到500这个范围都可以具体看震荡情况loss剧烈震荡就往大调loss死水一潭就往小调。4. 训练一个DQN交易策略主循环、超参数表与收敛判断环境、网络、回放、目标网络都就绪后下一步是把它们接起来跑训练。这一章给出一份完整的训练主循环代码然后是一张可以直接抄的超参数表最后讲清楚怎么判断模型是真的收敛了还是只是在过拟合。4.1 训练主循环从环境到agent的完整代码流程def train_dqn(env, agent, buffer, episodes500, batch_size64, gamma0.99, eps_start1.0, eps_end0.01, eps_decay0.995, target_update100): epsilon eps_start for ep in range(episodes): state env.reset() total_reward 0.0 while True: # epsilon-greedy: 以epsilon概率随机探索 if random.random() epsilon: action random.randint(0, 2) # 0卖出, 2买入, 1持有 else: action agent.act(state) next_state, reward, done env.step(action) buffer.push(state, action, reward, next_state, done) state next_state total_reward reward # 经验池攒够一个batch才开始训练 if len(buffer) batch_size: s, a, r, ns, d buffer.sample(batch_size) agent.update(s, a, r, ns, d, gamma) if done: break # 探索率衰减, 阶段性拷贝目标网络 epsilon max(eps_end, epsilon * eps_decay) if (ep 1) % target_update 0: agent.target_net.load_state_dict(agent.policy_net.state_dict()) if (ep 1) % 50 0: print(fepisode {ep1}, total_reward{total_reward:.2f}, epsilon{epsilon:.3f})训练循环的逻辑并不复杂核心是每个step先按epsilon-greedy策略选择动作执行动作拿到一个四元组经验存入buffer。经验池积累到batch_size之后才开始采样更新。这里有一个常见的误用在episode刚开始时buffer为空就强行采样会直接报random.sample的ValueError。我的判断条件是len(buffer) batch_size而不是“每个episode都训练”因为前几个episode确实攒不够一个batch。epsilon-greedy是DQN探索的核心机制。训练初期epsilon1.0模型基本在随机乱做动作这是好事它需要先广泛收集各种市场状态下的经验包括错误的。随着epsilon逐步衰减随机探索的比例降低模型越来越依赖策略网络预测的最优动作。eps_decay0.995意味着每个episode相乘一次500个episode后约等于0.08左右能够在一个训练周期内完成从探索到利用的过渡。4.2 超参数表window_size、学习率、gamma、epsilon的默认值下面是我在沪深300成分股日线数据上跑过比较可靠的默认参数。注意这些是在“日线、单股票、离散三动作”的前提下给出的你换数据频率或动作空间时需要重新调但初始值可以直接用这张表。参数默认值作用与调整方向window_size30状态回看窗口。日线用2060分钟线用60120hidden256隐藏层宽度。小数据集可以降到128buffer_capacity10000经验池容量。数据量大时可提到50000batch_size64每次训练的样本量。loss震荡时降到32gamma0.99折扣因子。接近1表示重视长期收益但不能等于1lr1e-3Adam学习率。loss发散时降到1e-4eps_start1.0初始探索率。固定从1开始eps_end0.01最小探索率。训练后期保留2%随机动作eps_decay0.995每episode衰减系数。衰减太快会过早收敛target_update100目标网络更新间隔。单位是episodegamma这个参数值得多说两句。gamma0.99的含义是“未来第N步的收益在当前的价值按0.99^N折扣”它让模型学会考虑长期结果而不是只看眼前一步。但如果gamma设成1.0所有未来收益权重完全相同Q值估计的方差会很大训练常出现数值发散如果设成0.9模型会短视只关心几天内的收益交易风格变得频繁换手。交易场景里0.99是比较合理的中间值。lr的调整优先级最高。损失函数出现NaN或Q值越训越大先看是不是lr太高。我遇到的情况大多是lr1e-2时训练100个episode后loss突然变NaN降到1e-3就正常了。如果你用的是不同优化器学习率的有效范围也不同Adam一般从1e-3开始SGD需要更低。4.3 收敛判断别只看loss净值曲线和策略行为才是重点DQN训练最常见的误区是拿loss曲线判断收敛看到loss下降就觉得策略在变好。问题在于Q-learning的loss是“预测Q值和目标Q值之间的误差”目标Q值本身也是网络预测出来的loss小只能说明网络已经自洽不代表策略真的赚到了钱。必须回到交易本身看三个东西训练集的净值曲线、测试集的净值曲线、策略实际做的动作序列。动作序列是很多人忽略的指标。一个好的DQN交易策略动作应该是“稀疏”的——大部分时间在持有或空仓只在关键位置买卖。如果你的动作序列是“买卖、买卖、买卖”高频切换说明奖励函数或状态定义有问题模型并没有从趋势里学到规律只是在用交易行为换微小的奖励波动。这时候即使累计收益为正实盘也会被滑点和流动性教育。下面是一个简单但有效的判断方式def evaluate(env, agent): 跑完整个环境, 返回累计收益和动作序列 state env.reset() done False actions [] values [] while not done: with torch.no_grad(): q agent.policy_net(torch.FloatTensor(state).unsqueeze(0)) action q.argmax().item() actions.append(action) state, _, done env.step(action) price env.df[close].values[min(env.step, len(env.df)-1)] values.append(env.capital env.shares * price) return values, actions你可以在训练每100个episode后调用这个函数把values画成净值曲线把actions画成散点图。净值曲线在训练集上稳定上升在测试集上能跑赢买入持有同时动作序列里持有/空仓占比高、买卖次数少这才叫收敛。如果净值曲线在训练集上猛涨但测试集上大幅回撤那就是过拟合后面要根据时间序列切数据重新训。5. DQN交易策略避坑指南五个让模型翻车的常见问题这一节写给已经在跑代码的人。下面五个问题我都在实际调试中遇到过每一条都按“现象→原因→解决”来讲能帮你少走很多弯路。5.1 状态里混了不同量纲的特征模型学了个寂寞现象训练loss下降得很漂亮但策略在测试集上一路亏损甚至净值曲线比买入持有差得多。原因状态向量里直接把原始收盘价比如3000元和持仓标记0或1拼在一起喂给网络。Adam虽然会自动调整学习率但输入特征之间量级相差上千倍梯度被价格主导持仓维度的贡献被完全稀释。模型本质上在“只看价格猜方向”持仓这个决策前提被忽略掉了。解决检查_get_state方法里每个特征的分布范围。价格序列必须做窗口内归一化持仓、成交量子信号单独映射到合理区间比如0到1或-1到1。一个简单的自检手段是打印一下第一个episode的前几个状态向量如果某一维永远在千位级其他维度在0点几说明归一化没做到位。5.2 奖励只算收益率模型学会了高频刷单现象回测日志显示整个测试周期内交易了200多次换手率奇高但扣除手续费后累计收益接近零。原因奖励函数只写了(new_value - prev_value) / prev_value没有把交易成本扣掉。模型发现每次买入后价格稍微上涨一点点就立即卖出能拿到正奖励于是不断重复这个操作。由于没有惩罚高频交易在训练中被认为是“最优策略”等实盘要付真实佣金时利润全被手续费吃掉了。解决把交易成本写进环境而不是在训练后再扣一次。前面TradingEnv的代码里已经演示了fee和slide的做法关键是从动作发生的这一刻就从账户里扣掉成本奖励函数随后自动反映这部分的损失。如果在已有源码上做修改先找step方法里有没有对capital或shares做手续费扣减没有就补上。5.3 训练集和测试集切分不做时序隔离未来函数泄漏现象训练集上准确率很高测试集上收益为负。更奇怪的是在测试集的K线图上买入点经常落在暴涨前一天卖出点恰好落在暴跌前一天。原因这是最严重的错误很多人把数据随机均匀切分成训练集和测试集。价格序列是时间相关的随机切分导致测试集里的某些样本其“未来几天的价格”在训练集里出现过。网络在训练时看到了未来数据策略实际上在作弊。解决严格按照时间顺序切分。前70%作为训练集后30%作为测试集中间不要做任何随机打散。更严谨的做法是walk-forward滚动回测在时间轴上滑动窗口每次用前60%的数据训练测试接下来10%然后窗口整体前移10%继续。这样能模拟策略在真实市场中的连续表现也会让验证更有说服力。5.4 epsilon衰减太快模型过早陷入局部最优现象前200个episode收益稳步上升之后无论怎么加长训练策略行为完全不变换一只股票跑也给出同样的买卖点。原因epsilon衰减系数设得太小比如每个episode直接乘以0.9到第30个episode时epsilon已经低于0.04。模型几乎没有随机探索的空间训练一开始遇到什么模式就锁死在那个模式上后续行情再变它也学不动。金融时序的非平稳性决定了DQN需要持续探索来应对模式切换。解决把epsilon衰减改成线性或更平缓的指数衰减。指数衰减系数调到0.995以上500个episode结束时epsilon还有大约0.08。同时设置一个下限0.01保证后期仍有少量随机动作帮助跳出局部最优。我在调参时还会观察测试集每个episode的动作分布如果某个动作占比超过90%基本可以断定探索不够。5.5 回测忽略滑点“回测赚实盘亏”的教科书级翻车现象回测年化收益30%夏普比率接近2实盘跑了一个月却亏了8%且每次下单的成交价都比回测时差了一截。原因回测里的成交价直接用了当天收盘价。真实交易中市价单在买单瞬间会有滑点特别是小市值股票或市场波动剧烈时你看到的收盘价不是你能成交的价格。如果源码里没有任何滑点模拟那回测结果里每一个盈利点都被乐观放大了。解决在TradingEnv的step方法里模拟滑点。买入时用price * (1 slide)作为成交价卖出时用price * (1 - slide)。slide的取值按流动性调大盘股可以取0.0002到0.0005小盘股可能需要0.001到0.002。更严谨的做法是用次日VWAP做成交基准在小时线或分钟线的源码里还可以把滑点模拟成“固定价格偏移成交量冲击项”。6. 验证与进阶回测指标、稳健性检验与多智能体方向6.1 回测指标怎么看夏普比率、最大回撤与换手率训练完一个策略不能只看累计收益率。三个指标必须一起看夏普比率衡量超额收益的稳定性最大回撤衡量风险承受能力换手率衡量交易成本敏感度。夏普比率大于1算基本及格大于2是优秀最大回撤如果超过20%策略在实盘里很难拿住换手率过高说明策略在频繁交易对滑点极其敏感前几章提到的奖励函数问题会从这里暴露出来。计算夏普比率时用日收益率序列而不是累计净值年化后再对比。import numpy as np def sharpe_ratio(daily_returns, periods252): daily_returns: 每日收益率序列 mean daily_returns.mean() std daily_returns.std() if std 0: return 0.0 return mean / std * np.sqrt(periods)6.2 稳健性检验的三种做法换股票、换时间段、加滑点一个只在一只股票上赚钱的策略很可能只是过拟合。三种检验方法建议都做一遍第一把训练好的模型拿到另一只股票上直接跑不重新训练看测试集收益是否还能跑赢买入持有第二把训练和测试的时间窗口整体前移或后移六个月观察收益是否大幅退化第三把回测里的滑点参数从万分之五改到千分之二看策略是否还能保持正收益。如果收益在三项检验中都有明显的“靠天吃饭”表现那问题不在参数而在环境设计或特征选择。6.3 从单智能体到多智能体一次一个策略还是组合管理源码跑通后进阶方向是多智能体组合管理。常见做法是给每一只股票单独训练一个DQN agent每个agent只决定自己这只股票买卖然后用一层归一化网络或简单线性加权把各个agent的仓位组合起来。这么做的好处是避开了“一个动作空间管所有股票”的状态爆炸问题又保留了组合层面的风险分散能力。缺点是训练成本随股票数量线性增加需要并行训练或者对每个agent做早停。我自己的习惯是在单股票验证通过后先拿一组行业不相关的5只股票做组合测试用等权重组合每个agent的持仓。这一阶段的主要目标是验证每个agent之间是否出现同质化交易行为如果它们在同一个时间段集体买入或卖出组合层面的回撤控制就失效了。到这一步你会发现DQN的交易策略源码跑通不难难的是让策略在真实市场环境里扛住成本、流动性和非平稳市场这三座大山。希望这些调试经验和参数边界能帮你在自己的实验里少走几步弯路。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →