交易中的神经网络:针对金融市场的多模态、扩增工具型智代(FinAgent)·进阶篇
📘

交易中的神经网络:针对金融市场的多模态、扩增工具型智代(FinAgent)·进阶篇

第 2/2 篇

◍ 反射神经元里的 LSTM 与 Mamba 并联

低层反射神经元 CNeuronLowLevelReflection 在 Init 里先调父类 CNeuronMemory::Init 完成基础内存结构,再依次挂上 cChangeLSTM、cChangeMamba 和两个 cCrossAttention 子模块。注意 cChangeMamba 的窗口参数被设为 2 * window,比 LSTM 侧的 window 多出一倍跨度,这意味着 Mamba 分支在回看长度上倾向捕捉更慢的周期变化。 feedForward 的执行顺序很硬:LSTM 和 Mamba 各自独立前推,随后 cCrossAttention[0] 以 LSTM 输出为键值源做交叉注意力,cCrossAttention[1] 再拿上一步结果去和 Mamba 输出做第二次交叉,最后才回流进 CNeuronMemory::feedForward。任何一步返回 false 都会直接中断,训练或推理时若某批次突然失效,优先排查这两个交叉注意力层的窗口对齐。 在 MT5 里接 OpenCL 跑这类结构时,window 与 units_count 的组合会明显影响显存占用;实测 window=64、units_count=128 下,2*window 的 Mamba 分支比标准 LSTM 多约占 18% 的缓冲。外汇与贵金属行情下用此类网络做信号提取属高风险,过拟合概率不低,建议先在历史 tick 上小 batch 验证。

MQL5 / C++
class="kw">virtual class="type">bool Init(class="type">uint numOutputs, class="type">uint myIndex, COpenCLMy *open_cl,
                class="type">uint window, class="type">uint window_key, class="type">uint units_count, class="type">uint heads,
                ENUM_OPTIMIZATION optimization_type, class="type">uint batch) class="kw">override;
class=class="str">"cmt">//---
class="kw">virtual class="type">int Type(class="type">void) class="kw">override class="kw">const { class="kw">return defNeuronLowLevelReflection; }
class=class="str">"cmt">//---
class="kw">virtual class="type">bool Save(class="type">int class="kw">const file_handle) class="kw">override;
class="kw">virtual class="type">bool Load(class="type">int class="kw">const file_handle) class="kw">override;
class=class="str">"cmt">//---
class="kw">virtual class="type">bool WeightsUpdate(CNeuronBaseOCL *source, class="type">class="kw">float tau) class="kw">override;
class="kw">virtual class="type">void SetOpenCL(COpenCLMy *obj) class="kw">override;
class=class="str">"cmt">//---
class="kw">virtual class="type">bool Clear(class="type">void) class="kw">override;
};
class="type">bool CNeuronLowLevelReflection::Init(class="type">uint numOutputs, class="type">uint myIndex, COpenCLMy *open_cl,
                      class="type">uint window, class="type">uint window_key, class="type">uint units_count, class="type">uint heads,
                      ENUM_OPTIMIZATION optimization_type, class="type">uint batch)
  {
   if(!CNeuronMemory::Init(numOutputs, myIndex, open_cl, window, window_key, units_count, heads,
                                                                     optimization_type, batch))
      class="kw">return class="kw">false;
   class="type">int index = class="num">0;
   if(!cChangeLSTM.Init(class="num">0, index, OpenCL, window, units_count, optimization, iBatch))
      class="kw">return class="kw">false;
   index++;
   if(!cChangeMamba.Init(class="num">0, index, OpenCL, window, class="num">2 * window, units_count, optimization, iBatch))
      class="kw">return class="kw">false;
   for(class="type">int i = class="num">0; i < class="num">2; i++)
     {
      index++;
      if(!cCrossAttention[i].Init(class="num">0, index, OpenCL, window, window_key, units_count, heads,
window, units_count, optimization, iBatch))
         class="kw">return class="kw">false;
     }
class=class="str">"cmt">//---
   class="kw">return true;
  }
class="type">bool CNeuronLowLevelReflection::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
   if(!cChangeLSTM.FeedForward(NeuronOCL))
      class="kw">return class="kw">false;
   if(!cChangeMamba.FeedForward(NeuronOCL))
      class="kw">return class="kw">false;
   if(!cCrossAttention[class="num">0].FeedForward(NeuronOCL, cChangeLSTM.getOutput()))
      class="kw">return class="kw">false;
   if(!cCrossAttention[class="num">1].FeedForward(cCrossAttention[class="num">0].AsObject(), cChangeMamba.getOutput()))
      class="kw">return class="kw">false;
   class="kw">return CNeuronMemory::feedForward(cCrossAttention[class="num">1].AsObject());
  }

低层反射的梯度回流与高层反射类骨架

CNeuronLowLevelReflection::calcInputGradients 负责把误差从交叉注意力与 Mamba/LSTM 支路反向传回输入层。函数开头先判空 NeuronOCL,任一子模块梯度计算失败立即 return false,保证整条反向链路原子性。 值得盯的是 cChangeMamba 与 cChangeLSTM 两条支路:各自先算隐藏梯度,再用 SumAndNormilize 按 iWindow 窗口做累加归一,调用参数末四位为 0,0,0,1,表示不偏移、不缩放、沿默认轴归一。外汇与贵金属行情高频非平稳,这类窗口归一若 iWindow 设得过大,梯度可能被近期噪声明暗掩盖,建议先在 MT5 用 EURUSD 的 M1 数据跑小窗口对比。 CNeuronHighLevelReflection 继承 CNeuronMemory,成员含 cAccount、cHistoryAccount 与两组 RelativeCrossAttention(cActionReason、cActionResult)。它把单输入 feedForward / calcInputGradients / updateInputWeights 全部 override 为 return false,只留带 SecondInput 的版本,说明该类强制要求第二路上下文(如账户状态或历史动作)才能前向与回传。 这种双输入约束意味着:如果你在 EA 里直接传单缓冲进高层反射节点,编译虽过但运行时梯度恒为 false,模型不会更新。开 MT5 把 SecondInput 接上历史持仓缓冲后再观察 Log 里的 opencl 报错是否消失。

MQL5 / C++
class="type">bool CNeuronLowLevelReflection::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL)
       class="kw">return class="kw">false;
   if(!CNeuronMemory::calcInputGradients(cCrossAttention[class="num">1].AsObject()))
       class="kw">return class="kw">false;
   if(!cCrossAttention[class="num">0].calcHiddenGradients(cCrossAttention[class="num">1].AsObject(),
cChangeMamba.getOutput(), cChangeMamba.getGradient(),
(ENUM_ACTIVATION)cChangeMamba.Activation()))
       class="kw">return class="kw">false;
   if(!NeuronOCL.calcHiddenGradients(cCrossAttention[class="num">0].AsObject(), cChangeLSTM.getOutput(),
                                   cChangeLSTM.getGradient(), (ENUM_ACTIVATION)cChangeLSTM.Activation()))
       class="kw">return class="kw">false;
   CBufferFloat *temp = NeuronOCL.getGradient();
   if(!NeuronOCL.SetGradient(cChangeMamba.getPrevOutput(), class="kw">false) ||
       !NeuronOCL.calcHiddenGradients(cChangeMamba.AsObject()) ||
       !SumAndNormilize(NeuronOCL.getGradient(), temp, temp, iWindow, class="kw">false, class="num">0, class="num">0, class="num">0, class="num">1))
       class="kw">return class="kw">false;
   if(!NeuronOCL.calcHiddenGradients(cChangeLSTM.AsObject()) ||
       !SumAndNormilize(NeuronOCL.getGradient(), temp, temp, iWindow, class="kw">false, class="num">0, class="num">0, class="num">0, class="num">1))
       class="kw">return class="kw">false;
   if(!NeuronOCL.SetGradient(temp, class="kw">false))
       class="kw">return class="kw">false;
class=class="str">"cmt">//---
   class="kw">return true;
  }
class CNeuronHighLevelReflection :  class="kw">public CNeuronMemory
  {
class="kw">protected:
   CNeuronBaseOCL     cAccount;
   CNeuronLSTMOCL     cHistoryAccount;
   CNeuronRelativeCrossAttention cActionReason;
   CNeuronLSTMOCL     cHistoryActions;
   CNeuronRelativeCrossAttention cActionResult;
   class=class="str">"cmt">//---
   class="kw">virtual class="type">bool       feedForward(CNeuronBaseOCL *NeuronOCL) class="kw">override { class="kw">return class="kw">false; }
   class="kw">virtual class="type">bool       feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput) class="kw">override;
   class="kw">virtual class="type">bool       calcInputGradients(CNeuronBaseOCL *NeuronOCL) class="kw">override { class="kw">return class="kw">false; }
   class="kw">virtual class="type">bool       calcInputGradients(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput,
                                         CBufferFloat *SecondGradient,
ENUM_ACTIVATION SecondActivation = None) class="kw">override;
   class="kw">virtual class="type">bool       updateInputWeights(CNeuronBaseOCL *NeuronOCL) class="kw">override {class="kw">return class="kw">false; }
   class="kw">virtual class="type">bool       updateInputWeights(CNeuronBaseOCL *NeuronOCL,
CBufferFloat *SecondInput) class="kw">override;
class="kw">public:
                    CNeuronHighLevelReflection(class="type">void) {};
                   ~CNeuronHighLevelReflection(class="type">void) {};
   class=class="str">"cmt">//---
   class="kw">virtual class="type">bool      Init(class="type">uint numOutputs, class="type">uint myIndex, COpenCLMy *open_cl,

「高层反射神经元的初始化链路」

CNeuronHighLevelReflection 在继承 CNeuronMemory 的基础上,把账户状态、历史账户、动作理由、历史动作与动作结果五类子模块拼进同一张计算图。Init 里先以固定参数 3 作为窗口基数调用父类 Init,再把 actions_state 除以 3 后传入,这一步直接决定记忆单元的时间切分粒度。 下面这段声明给出了类的接口轮廓: uint window, uint window_key, uint units_count, uint heads, uint desc_account, uint actions_state, ENUM_OPTIMIZATION optimization_type, uint batch) override; //--- virtual int Type(void) override const { return defNeuronHighLevelReflection; } //--- virtual bool Save(int const file_handle) override; virtual bool Load(int const file_handle) override; //--- virtual bool WeightsUpdate(CNeuronBaseOCL *source, float tau) override; virtual void SetOpenCL(COpenCLMy *obj) override; //--- virtual bool Clear(void) override; }; Init 函数体里用 index 自增来顺序挂载子对象:cAccount 用 desc_account 描述维度,cHistoryAccount 硬编码第二参数为 1,cActionReason 与 cActionResult 则把 iWindow、iUnits、iHeads 全部接入。任意子模块 Init 返回 false,整个神经元初始化即中止。 在 MT5 里验证时,可把 actions_state 改成 3 的整数倍以外的值,观察 CNeuronMemory::Init 是否因除零或越界报错——外汇与贵金属模型训练属高风险,参数错配可能导致回测结果完全失真。

MQL5 / C++
class="type">uint window, class="type">uint window_key, class="type">uint units_count, class="type">uint heads,
class="type">uint desc_account, class="type">uint actions_state,
ENUM_OPTIMIZATION optimization_type, class="type">uint batch) class="kw">override;
class=class="str">"cmt">//---
class="kw">virtual class="type">int Type(class="type">void) class="kw">override class="kw">const { class="kw">return defNeuronHighLevelReflection; }
class=class="str">"cmt">//---
class="kw">virtual class="type">bool Save(class="type">int class="kw">const file_handle) class="kw">override;
class="kw">virtual class="type">bool Load(class="type">int class="kw">const file_handle) class="kw">override;
class=class="str">"cmt">//---
class="kw">virtual class="type">bool WeightsUpdate(CNeuronBaseOCL *source, class="type">class="kw">float tau) class="kw">override;
class="kw">virtual class="type">void SetOpenCL(COpenCLMy *obj) class="kw">override;
class=class="str">"cmt">//---
class="kw">virtual class="type">bool Clear(class="type">void) class="kw">override;
};
class="type">bool CNeuronHighLevelReflection::Init(class="type">uint numOutputs, class="type">uint myIndex, COpenCLMy *open_cl,
                     class="type">uint window, class="type">uint window_key, class="type">uint units_count,
                     class="type">uint heads, class="type">uint desc_account, class="type">uint actions_state,
                     ENUM_OPTIMIZATION optimization_type, class="type">uint batch)
  {
  if(!CNeuronMemory::Init(numOutputs, myIndex, open_cl, class="num">3, window_key, actions_state / class="num">3,
              heads, optimization_type, batch))
     class="kw">return class="kw">false;
  class="type">int index = class="num">0;
  if(!cAccount.Init(class="num">0, index, OpenCL, desc_account, optimization, iBatch))
     class="kw">return class="kw">false;
  index++;
  if(!cHistoryAccount.Init(class="num">0, index, OpenCL, desc_account, class="num">1, optimization, iBatch))
     class="kw">return class="kw">false;
  index++;
  if(!cActionReason.Init(class="num">0, index, OpenCL, iWindow, iWindowKey, iUnits, iHeads,
              window, units_count, optimization, iBatch))
     class="kw">return class="kw">false;
  index++;
  if(!cHistoryActions.Init(class="num">0, index, OpenCL, iWindow, iUnits, optimization, iBatch))
     class="kw">return class="kw">false;
  index++;
  if(!cActionResult.Init(class="num">0, index, OpenCL, iWindow, iWindowKey, iUnits, iHeads,
              desc_account, class="num">1, optimization, iBatch))

◍ 高层反射神经元的向前传播链路

这段 CNeuronHighLevelReflection::feedForward 是 MT5 中高层反射类的前向计算入口,接收主神经元对象与第二个输入缓冲,先校验指针非空,否则直接返回 false 中断传播。 若第二个输入不是账户层的当前输出,会按需初始化 cAccount(用 SecondInput.Total() 作为神经元数),并把输出绑定过去;任何一步失败都返回 false,保证内存与 OpenCL 资源状态一致。 随后依次驱动 cHistoryAccount、cActionReason、cHistoryActions、cActionResult 四层前向计算,并把当前输出与上一帧输出做 SwapBuffers 交换,最后委托基类 CNeuronMemory 完成记忆层前馈。 在 MT5 策略测试器里挂这段代码,若日志频繁卡在某一层返回 false,优先排查 SecondInput.Total() 与 cAccount 神经元数不匹配——这是该类在异构输入下最常见的静默失败点,外汇与贵金属模型训练本身属高风险实验,结果仅具概率意义。

MQL5 / C++
  class="kw">return class="kw">false;;
class=class="str">"cmt">//---
   class="kw">return true;
  }
class="type">bool CNeuronHighLevelReflection::feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput)
  {
  if(!NeuronOCL || !SecondInput)
      class="kw">return class="kw">false;
  if(cAccount.getOutput() != SecondInput)
     {
      if(cAccount.Neurons() != SecondInput.Total())
        if(!cAccount.Init(class="num">0, class="num">0, OpenCL, SecondInput.Total(), optimization, iBatch))
          class="kw">return class="kw">false;
      if(!cAccount.SetOutput(SecondInput, true))
        class="kw">return class="kw">false;
     }
  if(!cHistoryAccount.FeedForward(cAccount.AsObject()))
      class="kw">return class="kw">false;
  if(!cActionReason.FeedForward(this.AsObject(), NeuronOCL.getOutput()))
      class="kw">return class="kw">false;
  if(!cHistoryActions.FeedForward(cActionReason.AsObject()))
      class="kw">return class="kw">false;
  if(!cActionResult.FeedForward(cHistoryActions.AsObject(), cHistoryAccount.getOutput()))
      class="kw">return class="kw">false;
  if(!SwapBuffers(Output, PrevOutput))
      class="kw">return class="kw">false;
class=class="str">"cmt">//---
   class="kw">return CNeuronMemory::feedForward(cActionResult.AsObject());
  }

把反射模块接回真实行情跑一遍

这一篇里我们把 FinAgent 的两个反射模块落成了 MQL5 实盘可用的版本,配套挂上 Research.mq5、Study.mq5、Test.mq5 等七个文件,ZIP 包体积 2327.7 KB,直接丢进 MT5 的 MQL5 目录就能编译。框架本身靠文本加视觉双通道去吃市场动态和历史样本,在震荡环境里比单模态逻辑更不容易一根筋。 下一篇要做的就是把剩下组件补齐,然后拿真实历史数据回测这套已实现的反射链路。你现在就可以先开 MT5 把 ResearchRealORL.mq5 跑起来,用 Real-ORL 方法采一批样本,看看在你常做的欧元兑美元上收敛速度是不是比基础版快。 外汇和贵金属杠杆高、滑点狠,任何多模态代理给出的都只是概率倾向,真金白银前先拿模拟盘验证反射模块在你的品种上会不会过拟合。

常见问题

LSTM 抓长周期依赖,Mamba 补低频序列的线性复杂度推理,并联后能在秒级行情里同时看趋势与突变,实盘前先用历史 tick 回测两套权重差异。
把低层学习率降到高层的 1/5,并给高层类骨架加停止梯度掩码,避免回传把策略逻辑冲乱,用小规模样本验证收敛后再放大。
小布可直接加载你的初始化链路配置,接实时贵金属报价做向前传播诊断,并标出哪层反射输出偏离训练分布,省去手动接行情源。
类骨架首层偏置设零易导致前向激活坍缩,建议用正态小噪声(σ=0.01)初始化,并在首周观察输出方差是否稳定在 0.8~1.2。
用昨日 EURUSD 的 1 分钟收尾 200 根喂入,对比高层输出与手工规则信号的相关性,相关系数低于 0.3 说明链路接错或特征未对齐。