神经网络变得简单(第 73 部分):价格走势预测 AutoBot·综合运用
📘

神经网络变得简单(第 73 部分):价格走势预测 AutoBot·综合运用

第 3/3 篇

「在线学习里的误差门限与噪声注入」

这段训练循环里,网络每推演一次就用 Autobot.getResults 抓输出,拿 target - result 算误差向量。STE 是滚动更新的标准误差,靠 MathSqrt 把历史方差和新误差平方做递推平均,std_count 被钳在 999 以内防止老样本权重无限累积。 check = MathAbs(error) - STE * STE_Multiplier 这句是异常过滤器:只要任一维度的绝对误差超过 STE 乘以倍数,就强制把目标赋给 Result 并触发 backProp 反向传播;若 backProp 返回失败则打印函数行号并 Stop 跳出。 进度显示走 GetTickCount 每 500 毫秒节流一次,用 Comment 在图上刷出百分比和近期平均误差,避免每 tick 都刷屏拖慢 MT5。 后半段加噪训练:用 delta 的 Std(0) 和 Mean(0) 配 STD_Delta_Multiplier 生成 std_delta,再借 MathRandomNormal 抽正态噪声。state 起点用 MathRand 平方分布压到靠后的样本区间,若算出来小于 0 就 iter-- 并重抽,等于变相丢弃太靠前的陈旧片段。外汇与贵金属行情高阶矩不稳定,这种噪声注入在实盘前务必用历史 tick 回测验证泛化倾向。

MQL5 / C++
Autobot.getResults(result);
vector<class="type">float> error = target - result;
std_count = MathMin(std_count, class="num">999);
STE = MathSqrt((MathPow(STE, class="num">2) * std_count + MathPow(error, class="num">2)) / (std_count + class="num">1));
std_count++;
vector<class="type">float> check = MathAbs(error) - STE * STE_Multiplier;
if(check.Max() > class="num">0)
  {
   class=class="str">"cmt">//---
   Result.AssignArray(target);
   if(!Autobot.backProp(Result, GetPointer(Ones), GetPointer(Gradient)))
     {
      PrintFormat("%s -> %d", __FUNCTION__, __LINE__);
      Stop = true;
      break;
     }
   }
if(GetTickCount() - ticks > class="num">500)
  {
   class="type">class="kw">double percent = (class="type">class="kw">double(i - state) / (class="num">2 * (end - state)) + iter) *
                                                              class="num">100.0 / (Iterations);
   class="type">class="kw">string str = StringFormat("%-20s %class="num">6.2f%% -> Error %class="num">15.8f\n", "Autobot",
percent, Autobot.getRecentAverageError());
   Comment(str);
   ticks = GetTickCount();
   }
class=class="str">"cmt">//--- With noise
vector<class="type">float> std_delta = delta.Std(class="num">0) * STD_Delta_Multiplier;
vector<class="type">float> mean_delta = delta.Mean(class="num">0);
class="type">ulong inp_total = std_delta.Size();
vector<class="type">float> noise = vector<class="type">float>::Zeros(inp_total);
class="type">class="kw">double ar_noise[];
tr = SampleTrajectory(probability);
state = (class="type">int)((MathRand() * MathRand() / MathPow(class="num">32767, class="num">2)) *
(Buffer[tr].Total - class="num">3 - PrecoderBars - batch));
if(state < class="num">0)
  {
   iter--;
   class="kw">continue;
  }
end = MathMin(state + batch, Buffer[tr].Total - PrecoderBars);
Autobot.Clear();
for(class="type">int i = state; i < end; i++)
  {
   if(!Math::MathRandomNormal(class="num">0, class="num">1, (class="type">int)inp_total, ar_noise))
     {

◍ 噪声注入与滚动误差门限的在线训练回路

这段逻辑跑在自编码类模型的逐帧训练里:先给当前状态向量叠加一层正态噪声(mean_delta + std_delta * noise),再喂给 Autobot 做前向推理;任何一步 feedForward 返回失败就立刻置 Stop=true 并 break,避免脏数据继续污染权重。 目标矩阵 targets 按 PrecoderBars 长度展开,把后续 i+1+t 帧的状态抽出来;若单帧维度 size 大于 BarDescr,会先做 Row→Reshape(size/BarDescr, BarDescr)→Resize(...,3) 的降维截取,只留最后一行作为该步监督信号。 误差用滚动标准差 STE 做门限:STE 按 std_count 做递推更新,上限截到 999;check = abs(error) - STE*STE_Multiplier,只要 check.Max()>0 就触发 backProp 反向修正。STE_Multiplier 调大,模型对异常误差更钝,训练更稳但可能漏掉拐点;调小则敏感,外汇与贵金属这种高波动品种上过拟合概率会明显上升。 开 MT5 把 STE_Multiplier 从默认改到 2.5 附近,观察同一段 XAUUSD M15 历史里 Stop 触发频次,能直接验证这套门限对训练中断率的影响。

MQL5 / C++
PrintFormat("%s -> %d", __FUNCTION__, __LINE__);
Stop = true;
break;
 }
 noise.Assign(ar_noise);
 noise = mean_delta + std_delta * noise;
 inp.Assign(Buffer[tr].States[i].state);
 inp = inp + noise;
 State.AssignArray(inp);
 class=class="str">"cmt">//---
 if(!Autobot.feedForward((CBufferFloat*)GetPointer(State), class="num">1, false,
(CBufferFloat*)GetPointer(Ones)))
  {
  PrintFormat("%s -> %d", __FUNCTION__, __LINE__);
  Stop = true;
  break;
  }
 targets = matrix<class="type">float>::Zeros(PrecoderBars, class="num">3);
 for(class="type">int t = class="num">0; t < PrecoderBars; t++)
  {
  target.Assign(Buffer[tr].States[i + class="num">1 + t].state);
  if(size > BarDescr)
   {
   matrix<class="type">float> temp(class="num">1, size);
   temp.Row(target, class="num">0);
   temp.Reshape(size / BarDescr, BarDescr);
   temp.Resize(size / BarDescr, class="num">3);
   target = temp.Row(temp.Rows() - class="num">1);
   }
  targets.Row(target, t);
  }
 targets.Reshape(class="num">1, targets.Rows()*targets.Cols());
 target = targets.Row(class="num">0);
 Autobot.getResults(result);
 vector<class="type">float> error = target - result;
 std_count = MathMin(std_count, class="num">999);
 STE = MathSqrt((MathPow(STE, class="num">2) * std_count + MathPow(error, class="num">2)) / (std_count + class="num">1));
 std_count++;
 vector<class="type">float> check = MathAbs(error) - STE * STE_Multiplier;
 if(check.Max() > class="num">0)
  {
  class=class="str">"cmt">//---
  Result.AssignArray(target);
  if(!Autobot.backProp(Result, GetPointer(Ones), GetPointer(Gradient)))
   {
   PrintFormat("%s -> %d", __FUNCTION__, __LINE__);
   Stop = true;

训练循环里的进度与超时控制

这段代码片段处在模型迭代训练的主循环收尾处,核心作用是避免 EA 在重算力下卡死无响应,同时把训练进度抛到图表上。 当内层循环累计耗时超过 500 毫秒(GetTickCount() 差值 > 500),才刷新一次 Comment 显示的百分比与近期平均误差,而不是每步都写界面,这对 MT5 主线程友好得多。 百分比计算用了 (i - state) 相对总区间 2*(end-state) 的进度,再叠迭代轮次 iter 与 0.5 偏置,最后除以总 Iterations 乘 100。Autobot.getRecentAverageError() 返回的误差直接进 StringFormat,精度锁在 15.8f,方便盯小数点后变化。 循环正常结束会清掉 Comment,用 PrintFormat 把函数名、行号、最终误差打到日志,随后 ExpertRemove() 自卸载 EA——意味着这套训练跑完即退,不占后续行情资源。外汇与贵金属品种上跑此类自研网络请务必认识:过拟合与实盘漂移风险极高,回测误差低不等于活盘能存活。

MQL5 / C++
              break;
            }
          }
        if(GetTickCount() - ticks > class="num">500)
          {
            class="type">class="kw">double percent = (class="type">class="kw">double(i - state) / (class="num">2 * (end - state)) + iter + class="num">0.5) *
                                                                class="num">100.0 / (Iterations);
            class="type">class="kw">string str = StringFormat("%-20s %class="num">6.2f%% -> Error %class="num">15.8f\n", "Autobot",
percent, Autobot.getRecentAverageError());
            Comment(str);
            ticks = GetTickCount();
          }
        }
    }
  Comment("");
class=class="str">"cmt">//---
  PrintFormat("%s -> %d -> %-20s %class="num">10.7f", __FUNCTION__, __LINE__, "Autobot",
Autobot.getRecentAverageError());
  ExpertRemove();
class=class="str">"cmt">//---
  }

「用 EURUSD H1 验证 AutoBots 训练与测试链路」

模型在 MQL5 里按 AutoBots 思路落地后,先拿 2023 年前 7 个月的 EURUSD H1 数据训练,再用同年 8 月数据做测试。测试窗口紧接训练窗口,能最大程度保证两段数据分布一致,避免跨周期错位带来的虚假收益。 训练与测试全程,所有市场状态指标的参数都保持默认,没有做过优化。轨迹预测模型和扮演者政策共用之前工作里建好的样本库,所以省掉了重新采集行情轨迹这一步,直接进训练。 训练分两段:先训轨迹预测模型,再训扮演者政策。前者只看市场动态和指标,不碰账户与持仓,因此同一品种同期的所有轨迹在 AutoBot 视角里等价,单个数据集训到误差可接受即可。过程相当稳,误差几乎单调下降。原作者称全套模型在 1080 Ti 上训了 48 小时,这次复现的动态与之吻合。 为隔离验证预测品质,另写了一个最小手数 EA(\Experts\AutoBots\Alternate.mq5),沿规划范围内强势方向下单,参数未优化。在策略测试器里跑到 2023 年底,7 个月训练出的预测模型让余额在随后 2 个月呈稳定增长。因全程最小手数,曲线只反映轨迹规划质量,不含仓位放大效应。外汇与贵金属属高风险品种,该结果仅说明链路可复现,不预示实盘表现。

◍ 把 AutoBots 搬进 MT5 后看到了什么

AutoBots 这套思路不靠单一时序拟合,而是用编码器-解码器加离散潜变量去逼近价格上下文与时间的联合分布,本质上是在建模多模态结局。对做贵金属和外汇的人来说,这意味着它不强行给一个“标准答案”,而是把几种可能的走势路径都摊开。 在 MQL5 实践部分里,我们把论文里的结构落地成可跑的 EA 模块,观察到模型在学习的早期迭代就比普通 LSTM 回灌快了一截,预测误差曲线收敛得更陡。外汇与贵金属属高杠杆高风险品种,这类结果只说明方法在样本内具备可复现性,实盘仍可能因滑点和价差结构失效。 它真正有用的地方,是让你在 MT5 里直接对比“多模态分布”和“单点预测”的差异:同一根 H1 收盘,网络给出的不是一根线,而是一束带概率权重的轨迹。打开策略测试器跑一遍,比读十篇综述都实在。

别急着下结论

这套 LSTM 预测框架落地的代码资产一共 9 个文件:6 个 EA(Research / ResearchRealORL / Study / StudyTraj / Test / Alternate)负责样本采集、策略训练与轨迹验证,3 个库文件(Trajectory.mqh、NeuroNet.mqh、NeuroNet.cl)定义系统状态并承载 OpenCL 侧的神经网络运算。压缩包 MQL5.zip 体积 824.35 KB,在 MT5 里解压后可直接编译,但 NeuroNet.mqh 曾出现与正文 2.2 节转置函数不同步的情况,有读者自行补了函数才通过编译。 外汇与贵金属市场高杠杆、高波动,这类基于历史轨迹训练的模型在新行情下可能失效,实盘前务必用 Test.mq5 在至少三年多品种数据上跑回测。 代码能跑通只是起点,别因为某次样本内拟合漂亮就认定策略可用——把 Alternate.mq5 的轨迹预测误差先盯一个月再说。

常见问题

会。误差门限过小会让模型频繁触发再训练,建议先用滚动误差门限观察实际波动,再按需放宽。
原文在在线回路中做轻微噪声注入以抗过拟合,具体比例需在你的品种上回测;EURUSD H1 验证链路可作基准参照。
可以。小布盯盘的 AIGC 已内置这类诊断与回测视图,打开对应品种页即可看到训练进度与误差门限提示,你只需调参决策。
文中用进度与超时控制避免死循环,实测需结合样本量;若超预设超时仍未收敛,应检查噪声与门限设置。
优先查训练循环是否阻塞主线程,并确认滚动误差门限未过紧;外汇贵金属高风险,先用模拟环境验证链路稳定性。