您应该知道的 MQL5 向导技术(第 02 部分):Kohonen 映射(基础篇)
「用 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 对应输出层初始容量。外汇与贵金属波动剧烈,此类映射仅作概率参考,实盘前务必在策略测试器跑历史数据。
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 实例占用,能直观验证容量常量对内存布局的影响。外汇与贵金属杠杆高,这类自研结构只建议在策略回测层验证,实盘接入须控仓。
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 根收盘价序列。外汇与贵金属波动剧烈,这类自研模型仅作信号辅助,实盘前务必在模拟盘验证,高风险。
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 就能直接观察层规模变化,外汇与贵金属行情喂入前先确认神经元数量匹配特征维度,这类品种杠杆高、跳空频繁,维度错配可能让推理结果失真。
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); };