神经网络在交易中的应用:用于多元时间序列预测的 LSTM 优化(终篇)(基础篇)
📘

神经网络在交易中的应用:用于多元时间序列预测的 LSTM 优化(终篇)(基础篇)

第 1/2 篇

◍ LSTM 进 MT5 前先弄清它在算什么

很多交易者把 LSTM 当成黑箱预测器直接丢进 EA,但它在 MT5 里真正做的是对多元时间序列做长程依赖建模。外汇与贵金属市场高杠杆、高波动,用这类模型做信号生成前,必须明白它拟合的是条件概率而非确定性方向。 以 EURUSD 为例,若把近 200 根 H1 的 open/high/low/close/volume 作为多元输入,LSTM 倾向于捕捉跨变量的滞后协同,而非单根 K 线的形态。这种结构对突发流动性枯竭事件概率上不敏感,实盘前需用历史样本外数据验证。 开 MT5 用以下片段可先确认终端是否具备张量计算所需的矩阵接口,避免后续训练脚本在 OnInit 阶段静默失败。

「为什么传统时序模型在盘面上会失灵」

现代外汇与贵金属盘面是个多因子纠缠的动态系统:宏观数据、利率预期、新闻脉冲、主力订单流持续互扰,价格与成交量序列只是这些隐性变量的投影。传统时序方法在波动放大时容易丢失焦点,对关键特征和时间窗口的选择性注意力不足,预测质量随噪声抬升而下滑。 DA-CG-LSTM 的思路是把双重注意力机制嵌进改进的循环块:第一层注意力筛特征,判断哪一维输入(如利差、波动率、成交量异动)当下权重最高;第二层注意力筛时间区间,挑出对后市最有信息量的历史片段。两层过滤后,再交给 CG-LSTM 做带控制门的记忆聚合。 该结构对噪声的容忍度来自 CG-LSTM 的自适应滤波:随机跳动若不属于稳定价格行为模式,其权重会被动态压低。作者原框架在长序列上靠早期注意力过滤加专用记忆结构规避梯度消失,因此既能跟长期趋势,也不漏掉日内波动率飙升这类短期刺激。 落地到 MT5 侧,我们下一篇会用 MQL5 自行实现这套架构,重点先放在模型模块设计而非调参。外汇与贵金属为高杠杆高风险品种,任何模型输出只代表概率倾向,需以实盘历史回测验证。

把 DA-CG-LSTM 拆成可训练的交易骨架

DA-CG-LSTM 框架由两个基本件撑起:线性注意力模块和改进的 CG-LSTM 循环模块。前者抓特征间与时间序列的依赖,后者做噪声过滤、内部状态管理和多尺度聚合,在高波动外汇/贵金属行情里,这种组合对系统可靠性倾向是刚需,而非锦上添花。 我们在 MQL5 里直接复用此前 Hidformer 开发的 CNeuronLinearAttention,不做额外修改,显著缩短了开发周期,也压住了引入未验证模块的回撤风险。可训练模型用 CreateDescriptions 方法搭起来,它接收 6 个动态数组指针,分别对应编码器、底层控制器、演员、概率趋势预测、导演、评论家。 环境状态编码器是第一道关:原始报价、成交量、情绪指标直接进全连接层,不做外部预处理。我们把带可控噪声注入的批归一化 CNeuronBatchNormWithNoise 塞进编码器内部,统一多模态量纲,少量噪声增强让有限数据集上的过拟合概率下降。 注意力分两遍走。第一遍用共享参数矩阵分析每个时间步的特征互依,参数总量比逐时刻独立训练少一截,有限数据下泛化更稳;第二遍先转置张量,把每个特征当成单变量序列,给每条序列配独立参数矩阵,捕捉不同模态对同一事件的不同反应速度和方向偏差——这在贵金属受基本面突刺时尤其明显。 随后数据进 CG-LSTM,每个单变量序列独立过循环单元,隐藏维度等于全局技能向量大小,输出固定长度表示。我们故意丢掉原版解码器深度结构,改用两层卷积极简解码,避免扭曲全局技能,最后逆变换恢复特征分布。 底层控制器复制编码器结构生成本地技能,再用两层交叉注意力以本地技能为查询、全局技能为键值做融合:第一阶段标准交叉注意力提取相关片段,第二阶段加一层挖短中长期关系。融合后送两个卷积层并行生成各智能体动作张量。演员/导演/评论家架构继承自前作,不再重述。 开 MT5 把下面代码段补进你的 EA 骨架,先确认 6 个指针数组的校验逻辑跑通,再逐步接注意力层。外汇和贵金属杠杆高、滑点狠,任何模型信号都只是概率参考,实盘前必须用历史 Tick 验证分布一致性。

MQL5 / C++
class="type">bool CreateDescriptions(CArrayObj *&encoder,
                       CArrayObj *&task,
                       CArrayObj *&actor,
                       CArrayObj *&probability,
                       CArrayObj *&director,
                       CArrayObj *&critic)

◍ 编码器各层的对象装配细节

这段逻辑先做六个容器对象的惰性初始化:encoder、task、actor、probability、director、critic 若为空就 new 一个 CArrayObj,任一分配失败直接 return false。MT5 里跑神经网络框架时,这种写法能避免重复构造,但也意味着首次调用会一次性吃掉六块堆内存。 初始化完成后 encoder.Clear() 清空旧结构,开始逐层塞 CLayerDescription。输入层用 defNeuronBaseOCL,节点数 = HistoryBars * BarDescr,激活函数 None,优化器 ADAM;prev_count 被记下来供后续层引用。 第二层是 defNeuronBatchNormWithNoise,节点数沿用 prev_count,batch 设成 1e4(即 10000),不做激活。第三层换 defNeuronLinerAttention,节点数缩到 HistoryBars,window=BarDescr、layers=1、window_out=32,这一步把每根 bar 的描述维度压到 32 维特征。 第四层 defNeuronTransposeOCL 做维度转置,count 和 window 又回到 HistoryBars 与 BarDescr。每层 Add 失败都会 delete descr 并返 false,所以你在 MT5 导航里若看到某次模型构建中断,优先查是哪一层的 descr 没进数组。

MQL5 / C++
   )
  {
class=class="str">"cmt">//---
   CLayerDescription *descr;
class=class="str">"cmt">//---
   if(!encoder)
     {
       encoder = new CArrayObj();
       if(!encoder)
          class="kw">return false;
     }
   if(!task)
     {
       task = new CArrayObj();
       if(!task)
          class="kw">return false;
     }
   if(!actor)
     {
       actor = new CArrayObj();
       if(!actor)
          class="kw">return false;
     }
   if(!probability)
     {
       probability = new CArrayObj();
       if(!probability)
          class="kw">return false;
     }
   if(!director)
     {
       director = new CArrayObj();
       if(!director)
          class="kw">return false;
     }
   if(!critic)
     {
       critic = new CArrayObj();
       if(!critic)
          class="kw">return false;
     } 
class=class="str">"cmt">//--- Encoder
   encoder.Clear();
class=class="str">"cmt">//--- Input layer
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronBaseOCL;
   class="type">int prev_count = descr.count = (HistoryBars * BarDescr);
   descr.activation = None;
   descr.optimization = ADAM;
   if(!encoder.Add(descr))
     {
       class="kw">delete descr;
       class="kw">return false;
     }
class=class="str">"cmt">//--- layer class="num">1
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronBatchNormWithNoise;
   descr.count = prev_count;
   descr.batch = class="num">1e4;
   descr.activation = None;
   descr.optimization = ADAM;
   if(!encoder.Add(descr))
     {
       class="kw">delete descr;
       class="kw">return false;
     }
class=class="str">"cmt">//--- layer class="num">2
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronLinerAttention;
   prev_count = descr.count = HistoryBars;
   descr.window = BarDescr;
   descr.layers = class="num">1;
   descr.window_out = class="num">32;
   descr.batch = class="num">1e4;
   descr.optimization = ADAM;
   descr.activation = None;
   if(!encoder.Add(descr))
     {
       class="kw">delete descr;
       class="kw">return false;
     }
class=class="str">"cmt">//--- layer class="num">3
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronTransposeOCL;
   descr.count = HistoryBars;
   descr.window = BarDescr;
   descr.batch = class="num">1e4;
   descr.optimization = ADAM;
   descr.activation = None;
   if(!encoder.Add(descr))
     {
       class="kw">delete descr;
       class="kw">return false;
     }

常见问题

盘口存在突变跳空、波动聚集和非平稳结构,传统线性时序假设稳态分布,容易在重大数据行情后给出滞后且偏离的推断,建议先检验序列平稳性再决定模型。
至少分清输入归一化层、遗忘门控的序列编码器、以及输出回归头;先把这三层对象装配清楚,再谈调超参,否则只是黑箱调包。
小布可以接入你的品种页,对比训练窗与 unseen 行情的残差分布,标出疑似记忆噪声的层,并提示是否该减隐藏单元或加 dropout。
容易漏掉时间轴对齐对象和特征缩放器的一致保存;推理时必须复用训练期的同款缩放对象,否则多元输入量纲错位会直接扭曲门控输出。
基础篇实测 32–64 单元在小时线噪声下泛化较好,超过 128 易在样本外走高方差;外汇贵金属属高风险,务必做滚动窗口验证再上实仓。