您应当知道的 MQL5 向导技术(第 23 部分):CNNs·综合运用
📘

您应当知道的 MQL5 向导技术(第 23 部分):CNNs·综合运用

第 3/3 篇

用价格差分矩阵给卷积打标签

卷积网络跑之前,得先有监督信号。这里把目标矩阵拆成三组:高点差分代理看涨强度,低点差分代理看跌强度,收盘差分幅度小则倾向震荡扫损行情。外汇与贵金属波动剧烈,这种标签只是概率映射,不是方向保证。 具体构造上,每一行对应不同价格加权:纯 High、 (H+H+C)/3、 (H+C)/2 三种;列方向则是 1 根、2 根、3 根 K 线的差分。比如 i==0 时,_targets[i][j][0] 直接算 _h[j] - _h[j+1],即单根新高落差,数值越大越可能暗示多头延续。 代码里用 ArrayResize 按核尺寸建矩阵,再双层循环填值。你可以把这段直接贴进 MT5 的 EA 初始化段,把 _h/_l/_c 换成你自己的价格序列缓冲,跑完 Print 一下 _targets 看前三根 bar 的差分符号分布。

MQL5 / C++
matrix _targets[];
ArrayResize(_targets, __KERNEL_SIZES.Size());
for(class="type">int i = class="num">0; i < class="type">int(__KERNEL_SIZES.Size()); i++)
{  _targets[i].Init(__KERNEL_SIZES[i], __KERNEL_SIZES[i]);
   for(class="type">int j = class="num">0; j < __KERNEL_SIZES[i]; j++)
   {  if(i == class="num">0)class=class="str">"cmt">// highs for &class="macro">#x27;bullishness&class="macro">#x27;
      {  _targets[i][j][class="num">0] = _h[j] - _h[j + class="num">1];
         _targets[i][j][class="num">1] = ((_h[j] + _h[j] + _c[j]) / class="num">3.0) - ((_h[j + class="num">1] + _h[j + class="num">1] + _c[j + class="num">1]) / class="num">3.0);
         _targets[i][j][class="num">2] = ((_h[j] + _c[j]) / class="num">2.0) - ((_h[j + class="num">1] + _c[j + class="num">1]) / class="num">2.0);
      }
      else if(i == class="num">1)class=class="str">"cmt">// lows for &class="macro">#x27;bearishness&class="macro">#x27;
      {  _targets[i][j][class="num">0] = _l[j] - _l[j + class="num">1];
         _targets[i][j][class="num">1] = ((_l[j] + _l[j] + _c[j]) / class="num">3.0) - ((_l[j + class="num">1] + _l[j + class="num">1] + _c[j + class="num">1]) / class="num">3.0);
         _targets[i][j][class="num">2] = ((_l[j] + _c[j]) / class="num">2.0) - ((_l[j + class="num">1] + _c[j + class="num">1]) / class="num">2.0);
      }

「CNN信号输出的多空权重归算」

上面这段是卷积网络推理收口的地方。GetOutput() 先通过 CopyRates 把最新 K线的高、低、收拷贝进 _h/_l/_c,长度取 __KERNEL+1 根,说明模型看的不是单根而是局部窗口。 输入张量由均线差填充:_inputs[i][j] = _c[__KERNEL] - _ma[__KERNEL],即用当前收盘价减对应均线值作为卷积核扫过的特征。随后走 Set→Pad→Convolve→Activate→Pool 标准前向流程。 输出层有三路:output[0] 取中位数大于0时记做多强度 _long,output[1] 中位数小于0时记做空强度 _short,output[2] 绝对值当中性权重 _neutral。 最终返回 (_long-_short)/(_long+_short+_neutral),区间理论在 [-1,1]。该比值越靠近1倾向多头信号越强,越靠近-1倾向空头信号越强;外汇与贵金属杠杆高,实盘使用前建议在 MT5 策略测试器用不同 __KERNEL 验证输出分布。

MQL5 / C++
class="type">class="kw">double CSignalCNN::GetOutput()
{  
   ...
   ...
   _index = class="num">0;
   _h.CopyRates(m_symbol.Name(), m_period, class="num">2, class="num">0, __KERNEL + class="num">1);
   _l.CopyRates(m_symbol.Name(), m_period, class="num">4, class="num">0, __KERNEL + class="num">1);
   _c.CopyRates(m_symbol.Name(), m_period, class="num">8, class="num">0, __KERNEL + class="num">1);
   for(class="type">int i = class="num">0; i < m_input_size; i++)
   {  for(class="type">int j = class="num">0; j < m_input_size; j++)
   {  if(_ma.CopyIndicatorBuffer(m_ma[_index].Handle(), class="num">0, class="num">0, __KERNEL + class="num">1))
      {  _inputs[i][j] = _c[__KERNEL] - _ma[__KERNEL];
         _index++;
      }
   }
   }
   CNN.Set(_inputs);
   CNN.Pad();
   CNN.Convolve();
   CNN.Activate();
   CNN.Pool();
   class="type">class="kw">double _long = class="num">0.0, _short = class="num">0.0;
   if(CNN.output[class="num">0].Median() > class="num">0.0)
   {  _long = fabs(CNN.output[class="num">0].Median());
   }
   if(CNN.output[class="num">1].Median() < class="num">0.0)
   {  _short = fabs(CNN.output[class="num">1].Median());
   }
   class="type">class="kw">double _neutral = fabs(CNN.output[class="num">2].Median());
   if(_long+_short+_neutral == class="num">0.0)
   {  class="kw">return(class="num">0.0);
   }
   class="kw">return((_long-_short)/(_long+_short+_neutral));
}

◍ EURJPY 日线回测暴露的阈值僵局

把信号类通过 MQL5 向导塞进 EA 后,按 EURJPY 2023 年日线数据跑策略测试器,多空信号输出只会出现 0 或 100 两种值。 根源在于网络输出没做归一化,导致开仓和平仓判定被卡在极端阈值上,调参空间实际被锁死。 若对网络结果做归一化,输出会连续分布,开仓/平仓阈值就能放开做优调,信号对价格变化的反应可能更灵敏。外汇与贵金属属高风险品种,此类回测结论仅代表历史样本表现,实盘存在显著失效概率。

把单层 CNN 类接着往上叠

前面几节把 CNN 的填充、前馈、激活、池化都塞进了一个独立的 MQL5 类文件,反向传播也顺着卷积在大小不等矩阵上的配对方式走通了。目前只实装了单层,cnn.mqh 这个头文件 14.55 KB,SignalWZ_23_.mqh 是 12.12 KB,都是可以直接在 MT5 里打开改的。 想验证的人,把 cnn.mq5(6.75 KB)拖进策略测试器跑一遍前馈,看卷积层输出维度是否和手算的填充后矩阵对得上;外汇和贵金属行情用这类模型自带过拟合风险,信号只作概率参考。 真要往下探,别只盯着图像输入,把不同品种 tick 序列喂进同一个单层类,再在外面套一层转换器做堆叠,未发掘的空间比换激活函数大得多。

常见问题

常用前后各 5~10 根 K 线做差分,窗口太短噪声大、太长吞掉局部形态;建议先按品种波动率用 EURJPY 日线回测调参。
把输出层多空节点做 softmax 后取差值,再乘仓位系数;阈值僵局说明单层网络区分度不够,别硬切 0.5。
小布可接入你的特征矩阵自动打标签并出 EURJPY 日线回测摘要,你只调阈值和层数即可,重复劳动交给它。
指固定信号阈值在样本内赚、样本外反复横跳,胜率掉到随机附近;根源多是单层 CNN 特征饱和,需叠层或换标签。
叠层后务必降学习率并加通道归一,否则日线小样本易过拟合;每层输出维度别超前层两倍,先叠两层验证。