神经网络变得简单(第 60 部分):在线决策转换器(ODT)·综合运用
🧠

神经网络变得简单(第 60 部分):在线决策转换器(ODT)·综合运用

(3/3)·离线 DT 跑久了就亏,本篇拆解在线决策转换器如何边交互边微调挽回衰减

进阶 第 3/3 篇
离线训练好的决策转换器前几根 K 线还能盈利,往后越跑越亏是常态。多数人以为加数据重训就行,结果模型越复杂越难维护。在线优调不补这一步,你的 EA 大概率在实盘默默回吐利润。

◍ 把账户快照压成强化学习的状态向量

这段逻辑干的事很直接:把每一笔交易状态里的账户数据,换算成强化学习网络能吃的归一化特征。state 为 0 时取当前态的余额和净值作基准,否则拿上一态的 account[0]、account[1] 当 prevBalance、prevEquity,避免除零只能靠前置状态守卫。 前 9 个 Add 覆盖了余额变化率、净值对余额比、净值变化率,以及 account[2]~[6] 里除余额的若干比值。这里 account[7] 是时间戳,代码用 2024.01.01 减 2023.01.01 的秒数(约 31536000 秒)把年周期归一,再塞进 sin(2πx);月、周、日周期则分别拿 PeriodSeconds(PERIOD_MN1/W1/D1) 做分母,喂 cos 或 sin。周期编码让网络可能感知季节性资金节律。 特征尾部把 Actions 数组和上一步 rewards 接上,构成完整 state。feedForward 一旦失败就置 StopFlag 并 break,backProp 同理——训练中断不留脏数据。最后把下一态 rewards 再 Append 进 bState,供策略网络前向推演。外汇与贵金属波动剧烈,这类RL特征若直接上实盘,回撤可能超出手工风控阈值,建议先在 MT5 策略测试器跑年回测。

MQL5 / C++
class="type">float prevBalance = (state == class="num">0 ? Buffer[tr].States[state].account[class="num">0] : Buffer[tr].States[state - class="num">1].account[class="num">0]);
class="type">float prevEquity = (state == class="num">0 ? Buffer[tr].States[state].account[class="num">1] : Buffer[tr].States[state - class="num">1].account[class="num">1]);
bState.Add((Buffer[tr].States[state].account[class="num">0] - prevBalance) / prevBalance);
bState.Add(Buffer[tr].States[state].account[class="num">1] / prevBalance);
bState.Add((Buffer[tr].States[state].account[class="num">1] - prevEquity) / prevEquity);
bState.Add(Buffer[tr].States[state].account[class="num">2]);
bState.Add(Buffer[tr].States[state].account[class="num">3]);
bState.Add(Buffer[tr].States[state].account[class="num">4] / prevBalance);
bState.Add(Buffer[tr].States[state].account[class="num">5] / prevBalance);
bState.Add(Buffer[tr].States[state].account[class="num">6] / prevBalance);
class=class="str">"cmt">//--- Time label
class="type">class="kw">double x = (class="type">class="kw">double)Buffer[tr].States[state].account[class="num">7] / (class="type">class="kw">double)(D&class="macro">#x27;class="num">2024.01.class="num">01&class="macro">#x27; - D&class="macro">#x27;class="num">2023.01.class="num">01&class="macro">#x27;);
bState.Add((class="type">float)MathSin(class="num">2.0 * M_PI * x));
x = (class="type">class="kw">double)Buffer[tr].States[state].account[class="num">7] / (class="type">class="kw">double)PeriodSeconds(PERIOD_MN1);
bState.Add((class="type">float)MathCos(class="num">2.0 * M_PI * x));
x = (class="type">class="kw">double)Buffer[tr].States[state].account[class="num">7] / (class="type">class="kw">double)PeriodSeconds(PERIOD_W1);
bState.Add((class="type">float)MathSin(class="num">2.0 * M_PI * x));
x = (class="type">class="kw">double)Buffer[tr].States[state].account[class="num">7] / (class="type">class="kw">double)PeriodSeconds(PERIOD_D1);
bState.Add((class="type">float)MathSin(class="num">2.0 * M_PI * x));
class=class="str">"cmt">//--- Prev action
bState.AddArray(Actions);
class=class="str">"cmt">//--- Return to go
if(!RTGStudy.feedForward(GetPointer(bState)))
  {
   PrintFormat("%s -> %d", __FUNCTION__, __LINE__);
   StopFlag = true;
   break;
  }
Result.AssignArray(Buffer[tr].States[state + class="num">1].rewards);
if(!RTGStudy.backProp(Result))
  {
   PrintFormat("%s -> %d", __FUNCTION__, __LINE__);
   StopFlag = true;
   break;
  }
class=class="str">"cmt">//--- Policy Feed Forward
bState.AddArray(Buffer[tr].States[state + class="num">1].rewards);

「策略网络的前向回传与权重落地」

这段片段是强化学习智能体在单步训练里的核心闭环:先让 AgentStudy 做前向推理,再把策略梯度算出来反向传播,最后把两套网络的权重同步回去。 前向阶段调用 feedForward 时传入状态缓冲指针、步数 1、训练标志 false,以及空的结果缓冲;若返回失败就打印函数名与行号、置 StopFlag 并 break,避免脏权重写回。 策略侧先把上一步动作 Actions 赋值,再从 AgentStudy 取 result 向量,用 CAGrad(Actions - result) + result 合成梯度目标 Result,交给 backProp 做误差回传;同样失败即中断。 收尾处 Agent.WeightsUpdate 与 RTG.WeightsUpdate 均以学习率 1.0f 把训练后的 Study 权重拷回在线网络。外汇与贵金属行情噪声大,这类全量更新若不加折扣因子,策略可能过拟合到近期波动,实盘前建议在 MT5 用历史数据改 1.0f 为 0.01~0.1 区间做敏感性验证。

MQL5 / C++
if(!AgentStudy.feedForward(GetPointer(bState), class="num">1, false, (CBufferFloat*)NULL))
  {
    PrintFormat("%s -> %d", __FUNCTION__, __LINE__);
    StopFlag = true;
    break;
  }
class=class="str">"cmt">//--- Policy study
Actions.Assign(Buffer[tr].States[state].action);
vector<class="type">float> result;
AgentStudy.getResults(result);
Result.AssignArray(CAGrad(Actions - result) + result);
if(!AgentStudy.backProp(Result, (CBufferFloat*)NULL))
  {
    PrintFormat("%s -> %d", __FUNCTION__, __LINE__);
    StopFlag = true;
    break;
  }
Comment("");
class=class="str">"cmt">//---
Agent.WeightsUpdate(GetPointer(AgentStudy), class="num">1.0f);
RTG.WeightsUpdate(GetPointer(RTGStudy), class="num">1.0f);
class=class="str">"cmt">//---
}

用 8 月样本验 ODT 优调成色

上一篇文章里,模型离线训练吃的是 2023 年前 7 个月的历史数据,这次优调也锁在同一区间,避免未来函数污染。优调后的 ODT 版本丢进策略测试器,按训练数据历史跑单次循环,不做样本外乱推。 直接拿 2023 年 8 月这段没参与训练的样本跑,模型交出约 10% 的盈利。收益率曲线不算漂亮,但上行的趋势已经能肉眼分辨,不是纯噪声。 测试期共成交 271 笔,128 笔获利了结,胜率刚过 47%,比亏损笔数略低。关键在赔率:最大盈利单比最大亏损单高 26%,平均盈利单比平均亏损单高出 20% 以上。两项叠加把盈利因子顶到 1.10。 外汇与贵金属市场高杠杆、高波动,这类基于历史样本的方法在实盘可能失效,任何验证结论都只是概率倾向,开 MT5 用自己的品种重跑一遍才算数。

◍ ODT 在线训练没改架构也跑通了

这一节把前面离线训好的决策转换器直接接上进在线训练(ODT)模式,没有动任何模型结构,只加了交互式微调这一段。结果是在不变更架构的前提下,模型效率出现可观察提升,这本身就说明 ODT 那套在线优调算法不是空谈。 我们在 MQL5 里复用了上一篇文章的离线权重,边和环境交互边更新策略。对做价格行为研究的人来说,重点不是它多神,而是验证了一条路径:旧模型不重写也能持续适应行情漂移。 必须泼盆冷水——文里所有程序只是技术演示,外汇和贵金属杠杆高、滑点诡异,直接挂实盘等于裸奔。真要试,先在 MT5 策略测试器里用历史 tick 跑通在线循环再说。

「把决策建模拆成两条线」

原文把几篇关联文章列成索引:在线决策转换器(Online DT)、控制二分法(DoC)、完全参数化分位数函数,以及神经网络系列第 34、58、59 部分。对 MT5 交易者而言,关键不在追新名词,而在分清「策略里哪些由模型参数决定、哪些由行情随机性决定」。 控制二分法(DoC)的核心是把可干预项(仓位、止损距离、特征窗口)与不可干预项(点差跳空、宏观冲击)显式切开,避免把样本内拟合误当成可控收益。 决策转换器走序列建模路线,把状态-动作-回报当成 token 序列做回归,而非传统 Q 学习那样逐步反推。想在 MT5 验证,可先读神经网络第 58 篇的 DT 实现,用 EURUSD 15 分钟历史跑离线回测,观察分位数头输出的回报分布偏移——外汇与贵金属杠杆高,序列过拟合会导致实盘回撤显著放大,仅作方法验证。

别急着下结论

这套 LSTM 预测框架落到 MT5 里,靠的是七个文件分工:Research.mq5 负责采样,Study.mq5 与 OnlineStudy.mq5 管离线和在线训练,Test.mq5 做模型验证,Trajectory.mqh、NeuroNet.mqh、NeuroNet.cl 则分别定义状态结构、封装网络类、承载 OpenCL 核函数。压缩包 MQL5.zip 约 530.55 KB,直接丢进 MT5 的 MQL5 目录就能编译跑通。 外汇与贵金属市场高杠杆、高波动,神经网络的预测倾向只是概率优势,实盘前务必用 Test.mq5 在多点差时段回测,别把样本内拟合当真。 真要上手,先只改 Research.mq5 的采集周期参数,看轨迹文件维度是否和你品种节奏对得上,再谈训练。

把在线训练监控交给小布
ODT 的在线回报衰减和微调触发点,小布盯盘的 AIGC 已内置诊断,打开对应品种页就能看到上下文窗口的实时表现,你只管判断要不要干预。

常见问题

经典 DT 只用离线轨迹训练固定策略,上下文长度固定且不能从环境反馈里更新;ODT 在离线预训练后接在线交互优调,用随机政策最大化期望回报,缓解分布覆盖不足导致的次优。
离线数据只覆盖状态动作空间有限部分,训练集外分布偏移后模型策略退化,生成动作偏离盈利区,亏损可能超过前期利润。
可以。训练期用较长 K 捕捉序列依赖,操作期上下文可短于训练值,但要注意过短会丢失回报梯度信息导致动作漂移。
小布目前内置的是 ODT 类模型的在线回报衰减与上下文诊断视图,模型训练推理仍需你在 MT5 端部署,它帮你省去重复观测盘口的小事。
原论文实验显示 ODT 绝对性能可与离线 RL 领先者竞争,且优调阶段提升更明显,不过外汇贵金属高风险,迁移到实盘仍要自验。