第 10 章 · 06 强化学习交易 Agent DQN 本节摘要:本节是全章的收官,把深度学习与强化学习合二为一——训练一个深度 Q 网络(DQN)交易 Agent。Agent 不靠「预测收益再下单」,而是通过「试错」学习在什么市场状态下做什么动作(做空/持有/做多)能最大化长期奖励。本节讲清四件事:马尔可夫决策过程(MDP)与 Q-learning 的基本框架;DQN 三大创新(经验回放、目标网络、Double DQN)如何让 Q-learning 在连续状态下收敛;原项目 如何用三个类( / / )构造一个 OpenAI Gym 兼容的交易环境;以及奖励设计、动作空间、episode 长度等关键决策的含义。
本节摘要:本节是全章的收官,把深度学习与强化学习合二为一——训练一个深度 Q 网络(DQN)交易 Agent。Agent 不靠「预测收益再下单」,而是通过「试错」学习在什么市场状态下做什么动作(做空/持有/做多)能最大化长期奖励。本节讲清四件事:马尔可夫决策过程(MDP)与 Q-learning 的基本框架;DQN 三大创新(经验回放、目标网络、Double DQN)如何让 Q-learning 在连续状态下收敛;原项目
trading_env.py如何用三个类(DataSource/TradingSimulator/TradingEnvironment)构造一个 OpenAI Gym 兼容的交易环境;以及奖励设计、动作空间、episode 长度等关键决策的含义。读完本节,你能理解强化学习在交易中的完整建模思路,并跑通一个最简的 DQN 交易 Agent。
内容来源:原项目
22_deep_reinforcement_learning/02_gridworld_q_learning.ipynb、03_lunar_lander_deep_q_learning.ipynb、04_q_learning_for_trading.ipynb、trading_env.py,汉化并套用体系化模板。
⚠️ 学习提示:强化学习交易 Agent 是出了名的「难收敛」——奖励稀疏、市场非平稳、样本相关性强。本节的 Agent 在单只 AAPL 上能学会一些模式,但离实盘有巨大鸿沟,把它当作「RL 建模流程的入门示例」。
阅读完本节,你应当能够:
trading_env.py 的三类协作与 step()/reset() 接口。强化学习把「交易」建模成一个马尔可夫决策过程(MDP),五要素:
| 要素 | 含义 | 交易映射 |
|---|---|---|
| 状态 S | 环境的描述 | 收益、波动、技术指标 |
| 动作 A | Agent 可选的行为 | 做空 / 持有 / 做多 |
| 转移 P | 动作后的状态分布 | 市场本身的演化 |
| 奖励 R | 即时反馈 | 当日持仓收益 - 成本 |
| 折扣 \gamma | 未来奖励的权重 | 平衡眼前与长远 |
目标:学一个策略 \pi(状态→动作的映射),使累计折扣奖励最大化。与监督学习不同,这里没有「正确答案」标签,只有环境给的奖励信号——Agent 必须自己探索试错。
💡 核心心法:强化学习的本质是「用试错学决策」。监督学习告诉模型「这根 K 线该买」,强化学习只告诉模型「你做了这个动作,赚了多少」,让模型自己推断什么状态做什么动作最好。这套范式特别适合「没有标准答案,只有好结果与坏结果」的交易场景。
Q-learning 直接优化动作价值函数 Q(s, a)——在状态 s 做动作 a,之后按某策略走,期望能拿到的累计折扣奖励。更新规则:
直觉:用「实际拿到的奖励 r + 对下一步最优价值的估计 \gamma \max Q(s', a')」去修正旧的 Q(s,a)。\alpha 是学习率。
Q-learning 是离策略(off-policy)——它学的是最优 Q,但实际行为可以用任意策略。为兼顾「探索未知」与「利用已知」,常用 ε-greedy:以 ε 概率随机选动作(探索),否则选 Q 最大的动作(利用)。ε 通常随训练线性衰减,从「多探索」过渡到「多利用」。
notebook 02_gridworld_q_learning.ipynb 在 3×4 网格上实现了表格式 Q-learning——状态离散有限,直接用一个二维数组存 Q(s, a),是理解算法的最佳起点。
网格世界的状态是离散的,可以用表存。但交易的状态(收益、指标)是连续的,无法穷举。深度 Q 网络(DQN)(DeepMind, 2013)用神经网络近似 Q(s, a),把状态映射到各动作的价值。但「直接套神经网络到 Q-learning」会发散,三大创新让它稳定下来:
| 创新 | 解决的问题 |
|---|---|
| 经验回放 | 样本相关性高、分布非平稳 |
| 目标网络 | 训练目标自身在变,形成反馈循环 |
| Double DQN | \max 操作系统性高估动作价值 |
把每一步的 (s, a, r, s') 存进回放缓冲区,训练时从缓冲区随机采样小批量——打破样本时序相关性,提升样本效率,避免当前权重产生的训练样本导致局部最优。
用两个结构相同的网络:主网络(在线学习)与目标网络(缓慢更新)。训练目标里的 Q(s', a') 用目标网络算,主网络只负责预测当前 Q(s, a)。目标网络权重每隔若干步从主网络硬拷贝一次,这样训练目标不会随每次更新剧烈变化,反馈循环被削弱。
原 DQN 用同一个网络既选最优动作又估其价值,会系统性地高估(因为 \max 偏向噪声中的高估)。Double DQN 解耦:用主网络选最优动作,用目标网络估其价值,显著降低高估偏差。
notebook 03_lunar_lander_deep_q_learning.ipynb 在 OpenAI Gym 的 Lunar Lander 上实现了一个 DDQN,本节交易 Agent 复用同一架构。
OpenAI Gym 规定环境必须实现 step(action) 和 reset() 两个接口。原项目 trading_env.py 用三个协作类实现:
class DataSource: def __init__(self, trading_days=252, ticker='AAPL', normalize=True): ... self.data['returns'] = self.data.close.pct_change() self.data['rsi'] = talib.STOCHRSI(self.data.close)[1] self.data['macd'] = talib.MACD(self.data.close)[1] self.data['atr'] = talib.ATR(...) # ... 多种 TA-Lib 指标
每个 episode 从历史数据里随机起点抽 252 个交易日,逐日推进。返回当天特征作为 observation。
class TradingSimulator: def take_step(self, action, market_return): start_position = self.positions[max(0, self.step - 1)] end_position = action - 1 # 动作 0/1/2 → -1/0/+1 n_trades = end_position - start_position trade_costs = abs(n_trades) * self.trading_cost_bps time_cost = 0 if n_trades else self.time_cost_bps # 不交易也有成本 reward = start_position * market_return - (trade_costs + time_cost) ...
关键设计:
Discrete(3),0=做空,1=持有,2=做多;action - 1 映射到 -1/0/+1 持仓;class TradingEnvironment(gym.Env): def step(self, action): observation, done = self.data_source.take_step() reward, info = self.simulator.take_step(action=action, market_return=observation[0]) return observation, reward, done, info def reset(self): self.data_source.reset() self.simulator.reset() return self.data_source.take_step()[0]
NAV 从 1 开始,降到 0 算输,升到 2.0 算赢——这是 episode 的终止条件之一。同时记录了 buy-and-hold 基准做对比。
notebook 04_q_learning_for_trading.ipynb 复用 Lunar Lander 的 DDQN,在 AAPL 上训练。配置:探索 500000 步(约 2000 个 1 年 episode),ε 线性衰减到 0.1,之后指数衰减(因子 0.9999)。关键代码骨架:
import tensorflow as tf from tensorflow.keras import Sequential from tensorflow.keras.layers import Dense, Dropout # 主网络与目标网络同结构 def build_model(state_dim, action_dim): return Sequential([ Dense(64, activation='relu', input_dim=state_dim), Dropout(0.2), Dense(64, activation='relu'), Dense(action_dim) ]) # 训练循环(简化) for step in range(total_steps): action = epsilon_greedy(state, eps) next_state, reward, done, _ = env.step(action) replay_buffer.append((state, action, reward, next_state, done)) batch = sample(replay_buffer, batch_size) train_step(batch, main_net, target_net) # 经验回放训练 if step % update_every == 0: target_net.set_weights(main_net.get_weights()) # 周期性硬更新
⚠️ 强化学习交易的三大难:
- 奖励稀疏:单日收益噪声大,真实信号被淹没,Agent 很难从噪声里学到稳定模式;
- 市场非平稳:训练期学到的模式,测试期可能已失效,「分布漂移」是 RL 的天敌;
- 样本相关性强:连续交易日的状态高度相似,经验回放虽能缓解但无法根除。
务实态度:把 RL 交易 Agent 当「研究范式」而非「印钞机」。它能跑通、能学会简单模式,但稳定盈利需要更精细的奖励工程、更稳的算法(如 PPO/SAC)、更稳健的市场建模——这些都远超本节范围。
DataSource(数据+特征)、TradingSimulator(持仓+奖励)、TradingEnvironment(Gym 接口)。至此,第 10 章结束。从 PCA 提取隐性因子,到 HRP 稳健配置,到 FNN/LSTM 做收益预测,再到 DQN 训练交易 Agent——我们走过了金融机器学习的高阶全景。后续可进一步深入自编码器、GAN 合成数据、CNN 处理金融图表,以及 PPO/SAC 等更稳定的强化学习算法。