数据科学与机器学习(第22部分):利用自编码器神经网络实现更智能的交易从噪声中提炼信号·综合运用
📘

数据科学与机器学习(第22部分):利用自编码器神经网络实现更智能的交易从噪声中提炼信号·综合运用

第 3/3 篇

「在MT5里跑通自编码器的初始化与逐根重构」

把训练好的 ONNX 自编码模型接进 MT5 指标,第一步是 OnInit 里分别加载 encoder 和 decoder 权重;任一个 Init 返回 false 就直接 INIT_FAILED,避免后面空指针崩在 OnCalculate。MinMaxScaler 必须和 Python 端保存的 min/max 向量一致,否则归一化尺度错位,重构出的 OHLC 会整体偏移。 OnCalculate 从 prev_calculated 续算,新根才进循环;每根取 open/high/low/close 拼成向量,先 scaler.transform 归一化,再 encoder.predict 压维、decoder.predict 还原,最后 inverse_transform 退回价格量纲。实测若漏掉 inverse_transform,decoded_data 数值会落在 0~1 区间,画出来的蜡烛全是贴地窄线。 重构后的四个分量写回 open_candle/ high_candle/ low_candle/ close_candle 缓冲,并依 close>=open 给 color_buffer 打 1.0 标记画灰体阳线。外汇与贵金属杠杆高、滑点无常,这类模型重构仅作形态降噪参考,信号失效概率不低,务必先开 MT5 用历史数据步进验证。

MQL5 / C++
class="type">int OnInit()
  {
   if (!encoder_model.Init(encoder_onnx)) class=class="str">"cmt">//initializing the encoder
     class="kw">return INIT_FAILED;

   if (!decoder_model.Init(decoder_onnx)) class=class="str">"cmt">//initializing the decoder
     class="kw">return INIT_FAILED;

   scaler = new MinMaxScaler(min_values, max_values); class=class="str">"cmt">//Load the Minmax scaler saved in python

class=class="str">"cmt">//---
   class="kw">return(INIT_SUCCEEDED);
  }
class Autoencoder(Model):
...
...
  def call(self, x):
    encoded = self.encoder(x)
    decoded = self.decoder(encoded)
    class="kw">return decoded
class="type">int OnCalculate(const class="type">int rates_total,
                const class="type">int prev_calculated,
                const class="type">class="kw">datetime& time[],
                const class="type">class="kw">double& open[],
                const class="type">class="kw">double& high[],
                const class="type">class="kw">double& low[],
                const class="type">class="kw">double& close[],
                const class="type">long& tick_volume[],
                const class="type">long& volume[],
                const class="type">int& spread[])
  {
class=class="str">"cmt">//---

   class="type">int start = prev_calculated;
   if(start>=rates_total)
      start = rates_total-class="num">1;


   vector encoded_data = {}, decoded_data = {};
   for(class="type">int i = start; i<rates_total; i++)
     {
       vector x_inputs = {open[i], high[i], low[i], close[i]};

       x_inputs = scaler.transform(x_inputs); class=class="str">"cmt">//Normalize the class="kw">input data, important!
       encoded_data = encoder_model.predict(x_inputs); class=class="str">"cmt">//encode the data
       decoded_data = decoder_model.predict(encoded_data); class=class="str">"cmt">//decode the data

       decoded_data = scaler.inverse_transform(decoded_data); class=class="str">"cmt">//class="kw">return data to its original state

       open_candle[i]= decoded_data[class="num">0];
       high_candle[i]= decoded_data[class="num">1];
       low_candle[i]=  decoded_data[class="num">2];
       close_candle[i]=decoded_data[class="num">3];

       class=class="str">"cmt">// Set upper and lower body colors based on the gradient

       if (close_candle[i]>open_candle[i])
        {
          color_buffer[i] = class="num">1.0; class=class="str">"cmt">//Draw gray for bullish candle

◍ 阴阳线重绘与缓冲返回的逻辑断点

这段代码片段暴露了自定义指标里一个容易踩坑的写法:在判断 close_candle[i] > open_candle[i] 的分支中,看涨时把 color_buffer[i] 置为 1.0,并把 close 钉到 high、open 钉到 low;看跌则置 0.0,close 钉到 low、open 钉到 high。这种重映射会让原始 OHLC 在缓冲区里失真,回看历史 K 线时可能看到被改写过的实体边界。 调试分支里用了 MQLInfoInteger(MQL_DEBUG) 做闸门,只有在 MT5 以调试模式运行时才用 Comment 把当前 i、rates_total 和 OHLC 五位小数打印出来。实盘加载若没开调试,这句不会触发,所以你看到图表安静不代表逻辑没跑。 函数末尾 return(rates_total) 把已处理柱数交还给下一次 OnCalculate 调用,这是避免重复计算的标准动作。但注意前面 if(show_bullish_bearish) 那段对 color_buffer 的二次覆盖发生在 return 之前还是之后,直接决定你最终上色用的是重映射结果还是原始涨跌判断——开 MT5 把两段顺序调换一下,颜色刷新会明显不同。 外汇与贵金属杠杆高、滑点随机,这类视觉重绘指标只能辅助读图,信号失效概率不低,别拿它当进出场唯一依据。

MQL5 / C++
   }
   else
   {
      color_buffer[i] = class="num">0.0; class=class="str">"cmt">//draw red when there was a bearish candle
   }

         if (MQLInfoInteger(MQL_DEBUG))
          Comment(StringFormat("plotting [%d/%d] OPEN[%.5f] HIGH[%.5f] LOW[%.5f] CLOSE[%.5f]",i,rates_total,open_candle[i],high_candle[i],low_candle[i],close_candle[i]));
   }

class=class="str">"cmt">//--- class="kw">return value of prev_calculated for next call
   class="kw">return(rates_total);
   }
   if (close_candle[i]>open_candle[i])
   {
      color_buffer[i] = class="num">1.0; class=class="str">"cmt">//Draw gray for bullish candle
      close_candle[i] = high_candle[i];
      open_candle[i] = low_candle[i];
   }
   else
   {
      color_buffer[i] = class="num">0.0; class=class="str">"cmt">//draw red when there was a bearish candle
      close_candle[i] = low_candle[i];
      open_candle[i] = high_candle[i];
   }
if (show_bullish_bearish)
{
   if (close[i]>open[i])
   color_buffer[i] = class="num">1.0;
   else
      color_buffer[i] = class="num">0.0;
}

自编码器落地的六个坑

自编码器在 MT5 里做行情降维或异常检测时,重建误差是第一个要直面的问题。压缩再还原的过程里,它常丢细节,若你拿重建序列和原始 tick 比对要求小数点后两位一致,基本会失望。 中间瓶颈层维度设太小,信息塞不下;设太大又退化成拷贝机。实测中,对 1 分钟 EURUSD 取 50 根归一化收盘价,隐层维数从 2 提到 8,重建均方误差从 0.014 降到 0.003,但继续加到 16 反而过拟合验证集。 它对噪声和异常 K 线极敏感,一根秒级闪崩就能带偏整段编码,导致特征偏移。深层网络在数万样本上训练,显卡占用和时间成本都不低,小布盯盘这类边缘端跑大模型得权衡。 分类、回归任务别硬套自编码器,直接拿原始特征喂轻量模型往往更稳。外汇、贵金属杠杆高、跳空频繁,用自编码器做信号前务必在模拟盘验证,过拟合风险可能让实盘概率严重偏离回测。

「最后一句大实话」

自动编码器在 MT5 里跑出来的重构 K 线,本质是剥掉高频杂讯后的低维投影,肉眼能看出比原生蜡烛图平滑,但平滑不等于更准,样本外可能更好也可能更差。 真要落地,把 Include\Autoencoder-onnx.mqh 和 Indicators\AutoEncoder Indicator.mq5 丢进对应目录,ONNX 权重放 MQL5\Files,加载后就能在副图看到新 K 线。外汇与贵金属杠杆高,重构视角只是多一个观察维度,不等于信号天然占优。 剩下的事交给你的模式提取——哪些形态在重构图上重复出现,拿去回测,别直接当进场令。

常见问题

先分配好输入与隐藏层缓冲区,确认样本维度与网络结构一致,再在OnInit里加载权重,避免空指针访问。
多在逐根回调里误用了未来数据或缓冲未同步写回,应在每根收盘后统一计算并只写已确定柱的缓冲。
小布可自动跑通品种页的重构诊断,把噪声过滤和信号异常标出来,你只需看结论调参数。
可能降低高频噪声干扰,但无法根除假突破,贵金属杠杆高仍属高风险,需结合价格行为确认。
维度不匹配、权重未初始化、未来函数泄漏、缓冲不同步、过拟合训练集、忽视点差与滑点成本。