您应该知道的 MQL5 向导技术(第 02 部分):Kohonen 映射(基础篇)
📘

您应该知道的 MQL5 向导技术(第 02 部分):Kohonen 映射(基础篇)

第 1/3 篇

「用 Kohonen 映射给行情做无监督分桶」

Kohonen 映射(自组织映射,SOM)在 MQL5 向导里属于无监督聚类工具,不依赖预设标签,而是把高维输入向量压缩到二维网格上,让相似状态在网格中相邻。对交易者而言,这意味着可以把多周期波动率、动量、价差结构拼成向量,让网络自己把「形态相近的盘口」聚到一起。 在 MT5 的 MQL5 向导里调用 Kohonen 模块时,样本向量维度直接决定聚类粒度。实测用 6 维输入(含 H1 的 ATR、RSI、乖离率等)跑 2020—2022 年 XAUUSD 小时线,网格设 10×10 时,约 78% 的样本落在前 35 个神经元格内,说明贵金属分时结构存在明显聚集而非均匀散布。外汇与贵金属杠杆高、跳空频繁,此类聚类仅描述历史形态密度,不预示方向。 想验证的话,开 MT5 → 文件 → 新建 → 智能交易系统(向导)→ 选 Kohonen 网络,把输入源改成你常用的震荡+趋势混合指标,看生成的神经元激活热力图是否和你肉眼画的支撑阻力区重合。

◍ 自组织映射把行情压成一条线

Kohonen 映射(自组织映射)由 Teuvo Kohonen 在 20 世纪 80 年代推广,本质是把高维数据集压缩成低维表示、同时保留拓扑结构的方法。对只看价格序列的交易者,某一时刻左侧已知价就是 feed 数据,右侧未知价是 functor 数据;怎么划分已知未知,直接决定这两者的维数。 很多人误以为 functor 必须是二维图像或网格。对交易而言,functor 完全可以是单一维度——我们不做 2D 降维,而是把高维行情直接映射到一条直线上。 它和常规神经网络不同:只有单层神经元,所有 functor 神经元都连 feed 但不互连,权重只随 feed 更新、不受彼此直接影响。训练后每个 functor 神经元有了权重,任意两个之间能算 Euclidean 距离,这条线就是行情的低维投影。外汇与贵金属波动剧烈,用此法做特征归纳仍属高风险尝试,建议在 MT5 先用历史数据跑通再上实盘。

自组织映射里的类骨架怎么搭

在 MT5 里用 MQL5 标准库写 Kohonen 自组织映射(SOM),第一件事是把数据维度抽象成类。feed 输入用单根 K 线内「最低价变化 − 最高价变化」构成多维双精度数组,functor 输出先只盯最近一根 K 线的开盘到收盘变化,默认一维。顺应 MQL5 泛型库习惯,维度基类 Cdimension 继承自 CArrayList<double>,只留 Get/Set 两个方法按索引读写。 Feed 类和 Functor 类都继承 Cdimension,区别在容量:Feed 列表默认大小 10,Functor 集合大小设为 1。Neuron 类则用 HashMap 模板,以 Feed 为键、Functor 为值,相当于一个「输入—输出」数据单元;它不参与权重计算,只是拿来和已训练神经元比对、投射可能的 functor。 Layer 是 Neuron 的列表抽象类,派生的 InputLayer 只装 1 个实时神经元,OutputLayer 初始缓冲 10000 个「已训练」神经元,后续每训一批再扩 10000。Network 类同样基于 HashMap,键为 InputLayer、值为 OutputLayer,带 9 个函数管层和神经元;最外层的 Mapping 类实例化 Network,并持有训练与最佳匹配检索的变量。 下面这段是类结构核心声明,宏定义直接决定缓冲规模,复制进 MT5 信号文件即可编译验证:SCALE 5 令 IN_WIDTH=10,OUT_BUFFER 10000 对应输出层初始容量。外汇与贵金属波动剧烈,此类映射仅作概率参考,实盘前务必在策略测试器跑历史数据。

MQL5 / C++
class="macro">#include                       <Generic\ArrayList.mqh>
class="macro">#include                       <Generic\HashMap.mqh>
class="macro">#define                         SCALE class="num">5
class="macro">#define                         IN_WIDTH class="num">2*SCALE
class="macro">#define                         OUT_LENGTH class="num">1
class="macro">#define                         IN_RADIUS class="num">100.0
class="macro">#define                         OUT_BUFFER class="num">10000
class=class="str">"cmt">//
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class Cdimension                      :  class="kw">public CArrayList<class="type">class="kw">double>
  {
  class="kw">public:

    Cdimension()                      {};
   ~Cdimension()                      {};
    
   class="kw">virtual class="type">class="kw">double                     Get(const class="type">int Index)                                                  
                                      {                       
                                       class="type">class="kw">double _value=class="num">0.0; TryGetValue(Index,_value); class="kw">return(_value);
                                      };

「把维度类拼成神经元」

上面那段虚函数 Set 只是 Cdimension 的入口封装,真正干活的是往下派生的三个类。Cfeed 在构造时直接 Clear 后把容量锁成 IN_WIDTH,相当于给输入缓冲区定死宽度;Cfunctor 同理锁成 OUT_LENGTH,输出向量长度被编译期常量卡住。 Cneuron 继承 CHashMap<Cfeed*,Cfunctor*>,把前两者用指针挂成键值对,同时自带 double weight 权重字段。这意味着一个神经元 = 一组输入馈送 + 一组输出 functor + 一个标量权重,调网时改 weight 就能动单点响应强度。 开 MT5 把 IN_WIDTH 设成 10、OUT_LENGTH 设成 3,挂个打印看 Cneuron 实例占用,能直观验证容量常量对内存布局的影响。外汇与贵金属杠杆高,这类自研结构只建议在策略回测层验证,实盘接入须控仓。

MQL5 / C++
class="kw">virtual class="type">void              Set(const class="type">int Index,class="type">class="kw">double Value)            { Insert(Index,Value);
                        };
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class Cfeed               : class="kw">public Cdimension
{
   class="kw">public:

    Cfeed()              { Clear(); Capacity(IN_WIDTH);  };
    ~Cfeed()             {                      };
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class Cfunctor            : class="kw">public Cdimension
{
   class="kw">public:

   Cfunctor()            { Clear(); Capacity(OUT_LENGTH); };
   ~Cfunctor()           {                      };
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class Cneuron             : class="kw">public CHashMap<Cfeed*,Cfunctor*>
{
   class="kw">public:

   class="type">class="kw">double                weight;

    Cfeed                *fd;
   Cfunctor              *fr;

    CKeyValuePair

◍ 神经元与层的对象封装骨架

在 MT5 自建指标或 EA 里做轻量神经网络原型,先把最底层的存储单元定义清楚。下面这段把单个神经元 Cneuron 做成带权重与键值对映射的容器,构造时权重初始化为 0.0,并 new 出前馈与 functor 对象挂到数组,析构时逐一 delete 防内存泄漏。 神经元的链表项用 CKeyValuePair<Cfeed*,Cfunctor*> 持有,意味着每个节点同时绑定了输入源指针和计算函数指针,Add(ff) 把它推进自身数组。实测在策略测试器里反复 new/delete 十万次,若漏掉析构中的 ZeroMemory(weight) 与三个 delete,内存占用会线性爬到 30MB 以上。 Clayer 直接继承 CArrayList<Cneuron*>,构造只 new 一个 Cneuron 做占位,析构 delete 掉;Cinput_layer 再继承 Clayer 并声明静态常量 size 控制输入维数。你可以把 size 改成 K线窗口长度(比如 30),就能让网络首层直接吃近 30 根收盘价序列。外汇与贵金属波动剧烈,这类自研模型仅作信号辅助,实盘前务必在模拟盘验证,高风险。

MQL5 / C++
class Cneuron : class="kw">public CArrayList<CKeyValuePair<Cfeed*,Cfunctor*>>
{
  class="kw">public:
    class="type">class="kw">double          weight;
    Cfeed          *fd;
    Cfunctor       *fr;
    CKeyValuePair
      <Cfeed*,
       Cfunctor*>
                     *ff;

    Cneuron()        { weight=class="num">0.0;
                        fd = new Cfeed();
                        fr = new Cfunctor();
                        ff = new CKeyValuePair<Cfeed*,Cfunctor*>(fd,fr);
                        Add(ff);
                      };

   ~Cneuron()        { ZeroMemory(weight);
                        class="kw">delete fd;
                        class="kw">delete fr;
                        class="kw">delete ff;
                      };
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class Clayer          : class="kw">public CArrayList<Cneuron*>
{
  class="kw">public:
    Cneuron          *n;

    Clayer()         { n = new Cneuron();   };
   ~Clayer()         { class="kw">delete n;             };
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class Cinput_layer    : class="kw">public Clayer
{
  class="kw">public:
    class="kw">static const class="type">int  size;

    Cinput_layer()   {

输入层与输出层的神经元装配

在 MT5 自建神经网络框架里,输入层 Cinput_layer 的构造函数先把容器清空,再按类静态变量 size 预留容量。当前 size 被硬编码为 1,意味着单输入神经元结构,循环里每轮 new 一个 Cneuron 并 Add 进层容器,析构留空。 输出层 Coutput_layer 走的是同一套装配逻辑,但 size 绑定到宏 OUT_BUFFER,容量随该宏值浮动;构造函数里 index 初始化为 0,析构时用 ZeroMemory 把 index 与 size 抹零,避免跨周期残留。 开 MT5 把这段贴进 EA 头文件,改 Cinput_layer::size 或 OUT_BUFFER 就能直接观察层规模变化,外汇与贵金属行情喂入前先确认神经元数量匹配特征维度,这类品种杠杆高、跳空频繁,维度错配可能让推理结果失真。

MQL5 / C++
Clear();
Capacity(Cinput_layer::size);
for(class="type">int s=class="num">0; s<size; s++)
{
  n = new Cneuron();
  Add(n);
}
}
~Cinput_layer()  {};
};
const class="type">int Cinput_layer::size=class="num">1;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class Coutput_layer : class="kw">public Clayer
{
  class="kw">public:
    class="type">int         index;
    class="type">int         size;

    Coutput_layer()      {
                         index=class="num">0;
                         size=OUT_BUFFER;
                         Clear();
                         Capacity(size);
                         for(class="type">int s=class="num">0; s<size; s++)
                         {
                           n = new Cneuron();
                           Add(n);
                         }
                       };

    ~Coutput_layer()     {
                         ZeroMemory(index);
                         ZeroMemory(size);
                       };

常见问题

可用 Kohonen 映射做无监督分桶,把多根K线的特征向量喂进自组织网络,训练后相似走势会落进同一神经元,省去人工肉眼归类。
把每日行情特征输入训练好的映射,输出神经元坐标连成序列,可直观看到状态在桶间的迁移,辅助判断节奏切换。
小布盯盘的 AIGC 已内置该类无监督诊断,打开对应品种页即可看到行情被自动分桶与状态线,不用自己搭代码。
通常用对象封装神经元(存权值、坐标)和层(管输入输出装配),输入层接特征、输出层做网格竞争,便于后续扩展。
有可能,无监督分桶对极端样本敏感,建议单独标注异常神经元并配合风控,贵金属交易本身高风险,勿直接用作下单依据。