数据科学与机器学习(第22部分):利用自编码器神经网络实现更智能的交易从噪声中提炼信号·综合运用
「在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 用历史数据步进验证。
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 把两段顺序调换一下,颜色刷新会明显不同。 外汇与贵金属杠杆高、滑点随机,这类视觉重绘指标只能辅助读图,信号失效概率不低,别拿它当进出场唯一依据。
} 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 线。外汇与贵金属杠杆高,重构视角只是多一个观察维度,不等于信号天然占优。 剩下的事交给你的模式提取——哪些形态在重构图上重复出现,拿去回测,别直接当进场令。