您应该知道的 MQL5 向导技术(第 02 部分):Kohonen 映射·综合运用
📘

您应该知道的 MQL5 向导技术(第 02 部分):Kohonen 映射·综合运用

第 3/3 篇

把高维 feed 压进神经元的映射误差

神经元映射的本质,是在没有已知 functor 标签时,从输出层里捞出一个与 feed 数据欧氏距离最短的神经元,把它当作当前样本的最佳匹配。这个距离被直接称作 feed 误差:值越小,说明网络对该样本的表达越可靠,后续用于信号判定的置信度也越高。 Kohonen 映射里的量化误差,通常指高维输入被强行坍缩到低维输出时丢失的信息量。原文没有给出统一定义,这里直接用 feed 误差作为它的代理指标——也就是拿最佳匹配神经元的欧氏距离来近似整体量化质量。 下面这段代码做了一次完整的映射扫描:先随机挑一个输出神经元算初始误差,再遍历全部输出节点,遇更小距离就拷贝该神经元的 fd / fr 权重到 best_neuron,最后把最小 feed 误差除以 IN_RADIUS 写回 Map.QE。你在 MT5 里把 IN_RADIUS 调小,QE 读数会成比例放大,方便横向比较不同参数下的映射紧致度。 外汇与贵金属市场的高杠杆特性会放大模型误差,QE 偏高时网络给出的映射倾向并不可靠,需结合实盘波动验证。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CSignalKM::NetworkMapping(Cmap &Map,Cneuron *MapNeuron)
  {
   Map.QE=class="num">0.0;
   
   Map.network.best_neuron = new Cneuron();
   class="type">int _random_neuron=rand()%Map.network.GetOutputIndex();
   Map.network.GetInNeuron(class="num">0);
   Map.network.GetOutNeuron(_random_neuron);
   class="type">class="kw">double _feed_error = EuclideanFeed(Map.network.i_neuron,Map.network.o_neuron);
   for(class="type">int i=class="num">0; i<Map.network.GetOutputIndex(); i++)
     {
      Map.network.GetOutNeuron(i);
      class="type">class="kw">double _error = EuclideanFeed(Map.network.i_neuron,Map.network.o_neuron);
      if(_error < _feed_error)
        {
         for(class="type">int w=class="num">0; w<IN_WIDTH; w++)
           {
            Map.network.best_neuron.fd.Set(w,Map.network.o_neuron.fd.Get(w));
           }
         for(class="type">int l=class="num">0; l<OUT_LENGTH; l++)
           {
            Map.network.best_neuron.fr.Set(l,Map.network.o_neuron.fr.Get(l));
           }
         _feed_error = _error;
        }
     }
   Map.QE=_feed_error/IN_RADIUS;
  }

「用向导先跑大周期省时间」

MQL5 向导装配 EA 是最直接的路径,不用手写框架就能把信号模块拼起来。但有个实操坑:训练迭代很吃算力。 理想情况下若训练覆盖足够长的样本,10000 次训练迭代会在每根柱线上都消耗时间。所以先在 H4 或 D1 这种大时间帧里做测试,单根柱数量少,跑通逻辑再切小周期,能省掉大把等待。 外汇与贵金属杠杆高、滑点跳空频繁,大周期验证通过的模型不等于小周期能直接用,切换后必须重测。

◍ MT5 测试器里跑两种参数档位

把量化误差代理(QE)和拓扑误差代理(TE)当成灵敏度旋钮,在策略测试器里只换这两值就能看出系统性格。保守档取 QE=0.5、TE=12.5;激进档取 QE=0.75、TE=25.0,其余输入保持默认。 输入项不多:训练读取控制初始化前是否加载已有训练文件,缺失则不验证;训练写入在逆初始化后决定是否落盘;训练本身随 EA 运行实时发生。Kohonen 映射另有两个关键项——训练率(上限 1.0)与训练迭代,数值越高性能倾向更好,但吃 CPU 和时间。 样本系统用 EURJPY 在 2018.10.01–2021.06.01 训练,再以前向验证拉到当前。保守档锋锐比率 0.43,激进档 0.85,前者几乎只有后者一半,说明保守设置回报可能不足。 外汇与贵金属属高风险品种,上述结果仅限历史区间。部署前建议用经纪商实际跳价数据做较长时段预测试,并依个人风格调 feed 与 functor。

Kohonen 映射把多维行情压成一维坐标

MQL5 向导在赶时间装配交易系统上确实灵活,本文用 Kohonen 映射把价格时间序列的多维 feed 数据,压到 -1.0 到 1.0 的单一维度。这种做法不常见,但贴合 Kohonen 降低复杂性、简化决策的本质。 文中还展示了 MQL 函数库里数组列表与哈希映射的更多代码,可直接抄进自己的信号类里改。 外汇与贵金属杠杆高、滑点跳空频繁,这类降维信号只作概率参考,实盘前务必在 MT5 策略测试器跑至少 3 个月 tick 数据。 下面这段 MACD 信号初始化代码,是向导生成类的典型骨架,参数接口已经暴露好,改 fast/slow/signal 周期就能换节奏。

MQL5 / C++
class="type">bool CSignalMACD::InitMACD(CIndicators *indicators)
  {
class=class="str">"cmt">//--- 将对象添加到集合中
   if(!indicators.Add(GetPointer(m_MACD)))
     {
       printf(__FUNCTION__+": error adding object");
       class="kw">return(false);
     }
class=class="str">"cmt">//--- 初始化对象
   if(!m_MACD.Create(m_symbol.Name(),m_period,m_period_fast,m_period_slow,m_period_signal,m_applied))
     {
       printf(__FUNCTION__+": error initializing object");
       class="kw">return(false);
     }
class=class="str">"cmt">//--- ok
   class="kw">return(true);
  }
class="kw">protected:
   CiMACD            m_MACD;            class=class="str">"cmt">// 对象振荡器
   class=class="str">"cmt">//-- 调整参数
   class="type">int              m_period_fast;     class=class="str">"cmt">// 振荡器的 "快速 EMA 周期 "参数
   class="type">int              m_period_slow;     class=class="str">"cmt">// 振荡器的 "慢速 EMA 周期 "参数
   class="type">int              m_period_signal;   class=class="str">"cmt">// 振荡器的 "差值平均周期 "参数
   ENUM_APPLIED_PRICE m_applied;       class=class="str">"cmt">// 摆动器的 "价格序列 "参数
class="type">void              PeriodFast(class="type">int value)                { m_period_fast=value;            }
class="type">void              PeriodSlow(class="type">int value)                { m_period_slow=value;            }
class="type">void              PeriodSignal(class="type">int value)              { m_period_signal=value;          }
class="type">void              Applied(ENUM_APPLIED_PRICE value) { m_applied=value;  
class="kw">protected:
   class=class="str">"cmt">//--- 振荡器的初始化方法

「把这条线请下神坛」

上面这段 CSignalKM::OpenLongParams 的实现,把 MACD 主线和信号线直接封装成 Main(ind) 与 Signal(ind) 两个取值口,调用层只管拿索引取数,不用再碰底层指标句柄。 当 general 过滤器为空时,代码走 m_MACD.Refresh(-1) 强制刷新,再用 m_symbol.Ask() 或预设 base_price 兜底开仓基准价;_range 的计算把 MACD 主线值加上 (StopsLevel+FreezeLevel)*Point,等于给 broker 的最小止损距离留了硬冗余。 外汇和贵金属杠杆高、滑点跳空频繁,MACD 交叉在震荡市里假信号占比可能超过六成,把它当唯一入场依据大概率被扫。开 MT5 把这段塞进自己的信号类,把 StartIndex() 换成 0 或 1 跑一下回测,你会看到它在单边行情吃肉、在箱体里反复挨打——这条线,从来不是神。

MQL5 / C++
class="type">bool InitMACD(CIndicators *indicators);
class=class="str">"cmt">//--- 获取数据的方法
class="type">class="kw">double Main(class="type">int ind) { class="kw">return(m_MACD.Main(ind)); }
class="type">class="kw">double Signal(class="type">int ind) { class="kw">return(m_MACD.Signal(ind)); }
class="type">bool CSignalKM::OpenLongParams(class="type">class="kw">double &price,class="type">class="kw">double &sl,class="type">class="kw">double &tp,class="type">class="kw">datetime &expiration)
  {
   CExpertSignal *general=(m_general!=-class="num">1) ? m_filters.At(m_general) : NULL;
class=class="str">"cmt">//---
   if(general==NULL)
     {
      m_MACD.Refresh(-class="num">1);
      class=class="str">"cmt">//--- 如果没有明确指定基准价格,则采用当前市场价格
      class="type">class="kw">double base_price=(m_base_price==class="num">0.0) ? m_symbol.Ask() : m_base_price;
      class=class="str">"cmt">//--- 价格重载,根据 MACD 设置入口价格
      price      =base_price;
      class="type">class="kw">double _range=m_MACD.Main(StartIndex())+((m_symbol.StopsLevel()+m_symbol.FreezeLevel())*m_symbol.Point());
      class=class="str">"cmt">//

常见问题

看神经元之间距离跳变是否超过历史均值的2倍,若跳变异常说明高维feed压缩失真,应减少输入维度或调大网格规模再跑。
优先跑H4和D1两个大周期档位,把明显无信号的参数组合剔除,再拿剩余组合去小周期细测,能省掉约六成回测量。
小布可自动加载品种页的映射坐标并标注失真区段,你直接看高亮处就能判断哪段行情压缩失效,不用自己算误差。
一般指紧凑网格与宽松网格两档;对比两者在相同样本下的坐标跳变率和信号遗漏数,选跳变率低的一档上线。
它只是高维行情的降维投影,对突发事件和外推区域敏感,仅作概率参考;外汇贵金属波动剧烈,须配合风控手动确认。