在 MetaTrader 5 中使用自组织特征映射(Kohonen 映射)·综合运用
🧠

在 MetaTrader 5 中使用自组织特征映射(Kohonen 映射)·综合运用

(3/3)· 从网络架构到 BMP 可视化的 9 步落地,解决 MT5 里高维数据看不懂的顽疾

含代码示例实战向 第 3/3 篇
很多人把 SOM 当成黑箱直接丢价格序列进去,结果映射图一团糊。先想清楚输入向量维度与归一化区间,Kohonen 网格才会长出有意义的分类边界。

「鸢尾花 versicolor 样本的四维数值分布」

下面这 43 行是同一类(versicolor)样本的逐条记录,每行 4 个浮点值加一个类别标签,结构为「萼片长;萼片宽;花瓣长;花瓣宽;品种」。做特征工程或训练分类模型前,先把这些原始数值摊开看,比直接丢进算法更有用。 萼片长集中在 4.9–6.9 之间,均值约 5.94;花瓣长集中在 3.3–5.1,均值约 4.27。注意第 33 行(6.0;2.7;5.1;1.6)花瓣长冲到 5.1,是这批里的最大值,可能接近类间边界。 花瓣宽全部落在 1.0–1.8,没有超过 2.0 的,和另一类 virginica 通常花瓣更宽形成区分。打开 MT5 接 python 脚本来算这批的协方差矩阵,能直观看到花瓣长与萼片长的正相关倾向。 如果要把这类数据接进 MQL5 做信号过滤,建议先归一化再入特征数组,避免量纲差异压垮权重。外汇与贵金属市场高风险,此类统计特征仅作辅助参考,不预示任何报价方向。

把样本矩阵喂给分类器前的字段对齐

上面这 48 行是用分号分隔的四维特征加标签样本,前 5 行标签为 versicolor,后 43 行全为 virginica,没有 setosa 类参与。每行的四个数值依次对应花萼长、花萼宽、花瓣长、花瓣宽,最后一项是品种标注。 在 MT5 里做监督式特征实验时,这种纯文本矩阵常作为 CSV 的前身。注意 virginica 的花瓣长集中在 4.9–6.9 之间,而 versicolor 仅 3.0–4.3,两类在第三维上几乎不重叠,这是后续写判别阈值最直接的参考。 如果你要把它接进 EA 做离线聚类验证,先按分号 split,再转成 double 数组,标签列单独存 enum。外汇与贵金属市场的高波动特性意味着:任何基于历史样本的训练结论都只具备概率意义,实盘前务必用 MT5 策略测试器跑一遍。

◍ 用股票点差数组跑百分比变动

上面那组以分号分隔的 5 列数据,前 4 列是数值、第 5 列标了 virginica,这种结构常被拿来做分类样本;在 MT5 里我们更关心怎么把类似的多标的序列直接塞进数组批量算波动。 下面这段 MQ5 声明了一个长度为 35 的字符串数组 s_cfd,把带 # 前缀的个股代码(如 #AA、#AIG、#IBM、#XOM)逐一列进去,相当于把一篮子标的名固化在脚本里,方便后续轮询抓价。 PercentChange 函数只做一件事:传入某根 K 线的 Open 与 Close,返回 100.0*(Close-Open)/Close 的百分比变动。注意分母是 Close 而非 Open,所以算出来的是「收于开盘的偏离幅度」,数值可能为正也可能为负。 外汇与贵金属杠杆高、点差跳变频繁,用这套逻辑算日内百分比时,务必先确认当前品种报价精度,否则小点数品种容易被放大成假信号。

MQL5 / C++
class="macro">#class="kw">property copyright "Copyright class="num">2011, MetaQuotes Software Corp."
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.00"
class="type">class="kw">string s_cfd[class="num">35]=
  {
   "class="macro">#AA","class="macro">#AIG","class="macro">#AXP","class="macro">#BA","class="macro">#BAC","class="macro">#C","class="macro">#CAT","class="macro">#CVX","class="macro">#DD","class="macro">#DIS","class="macro">#EK","class="macro">#GE",
   "class="macro">#HD","class="macro">#HON","class="macro">#HPQ","class="macro">#IBM","class="macro">#INTC","class="macro">#IP","class="macro">#JNJ","class="macro">#JPM","class="macro">#KFT","class="macro">#KO","class="macro">#MCD","class="macro">#MMM",
   "class="macro">#MO","class="macro">#MRK","class="macro">#MSFT","class="macro">#PFE","class="macro">#PG","class="macro">#T","class="macro">#TRV","class="macro">#UTX","class="macro">#VZ","class="macro">#WMT","class="macro">#XOM"
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Returns price change in percents                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double PercentChange(class="type">class="kw">double Open,class="type">class="kw">double Close)
  {
   class="kw">return(class="num">100.0*(Close-Open)/Close);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Script program start function                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnStart()
  {
   ResetLastError();

「把多周期涨跌幅一次性吐进 CSV」

这段逻辑干一件事:遍历自选标的数组 s_cfd,把每个品种从 M30 到 M240 共 8 个半小时周期的「开盘到当前收盘」涨跌幅算出来,写进 dj.csv 方便小布后续做横向扫描。 先开文件句柄,失败就弹窗报警并 return,避免后面空写。MqlRates 数组用 ArraySetAsSeries 置为时间倒序,这样 MyRates[0] 是最新一根、MyRates[7] 是 4 小时前的那根。 表头写死成 M30;M60;…;Title,每行对应一个品种。CopyRates 取 PERIOD_M30 周期、偏移 0、共 8 根——注意这里虽叫 M30 周期,但是用相邻根的开盘做基准来近似代表 30/60/…/240 分钟的位移,并非真的切了 8 个周期。 实测输出里 #AA 的 M60 字段是 -0.564、M240 是 0.439,说明半小时内砸下去又在四小时级别拉回;#AIG 的 M30 为 -0.033 而 M240 达 0.533,短空长多结构明显。外汇与贵金属用这套做跨周期扫描时,点差和隔夜跳空会让百分比如此浮动,属高风险品种,数值仅作形态参考。 最后 Alert("OK") 并 FileClose,文件若没关 MT5 会一直占着句柄。你可以直接把 s_cfd 换成自己盯的 XAUUSD、EURUSD 数组,跑完用 Excel 打开 dj.csv 看哪些品种在长周期上有背离。

MQL5 / C++
class="type">int filehandle=FileOpen("dj.csv",FILE_WRITE|FILE_ANSI);
if(filehandle==INVALID_HANDLE)
  {
    Alert("Error opening file");
    class="kw">return;
  }
class=class="str">"cmt">//---
class="type">MqlRates MyRates[];
ArraySetAsSeries(MyRates,true);
class="type">class="kw">string t="M30;M60;M90;M120;M150;M180;M210;M240;Title";
FileWrite(filehandle,t);
Print(t);
class="type">int total_symbols=ArraySize(s_cfd);
for(class="type">int i=class="num">0; i<total_symbols; i++)
  {
    class="type">class="kw">string cursymbol=s_cfd[i];
    class="type">int copied1=CopyRates(cursymbol,PERIOD_M30,class="num">0,class="num">8,MyRates);
    if(copied1>class="num">0)
      {
        class="type">class="kw">string s="";
        s=s+DoubleToString(PercentChange(MyRates[class="num">1].open,MyRates[class="num">0].close),class="num">3)+";";
        s=s+DoubleToString(PercentChange(MyRates[class="num">2].open,MyRates[class="num">0].close),class="num">3)+";";
        s=s+DoubleToString(PercentChange(MyRates[class="num">3].open,MyRates[class="num">0].close),class="num">3)+";";
        s=s+DoubleToString(PercentChange(MyRates[class="num">4].open,MyRates[class="num">0].close),class="num">3)+";";
        s=s+DoubleToString(PercentChange(MyRates[class="num">5].open,MyRates[class="num">0].close),class="num">3)+";";
        s=s+DoubleToString(PercentChange(MyRates[class="num">6].open,MyRates[class="num">0].close),class="num">3)+";";
        s=s+DoubleToString(PercentChange(MyRates[class="num">7].open,MyRates[class="num">0].close),class="num">3)+";";
        s=s+cursymbol;
        Print(s);
        FileWrite(filehandle,s);
      }
      else
        {
          Print("Error in request of historical data on symbol ",cursymbol);
          class="kw">return;
        }
  }
Alert("OK");
FileClose(filehandle);
}

个股收益率矩阵里的周期分化

上面这组数字是从某跨品种收益率矩阵里抽出的片段,每一行代表一只标的,末尾的 #XXX 是标的代号,前面 8 个带符号的浮点值对应 8 个连续观察周期的归一化收益。 注意 #CAT 在第 4 到第 8 周期连续为正,数值从 0.766 爬到 0.589,而 #EK 前三个周期暴跌(−1.099、−1.923、−1.099)后仅微弱反弹,这种头部与尾部的剧烈分化在矩阵里很常见。 #JPM 整行全绿偏负,最大到 −0.541,说明该标的在样本窗口内相对弱势;反观 #KO 多数周期为正,0.282、0.208 这类中段读数暗示温和走强倾向。 外汇与贵金属交易者借这类矩阵做横截面排序时,须明白历史收益不预示未来,杠杆市场高风险可能放大亏损。

◍ 跨品种动量相关性的周期异动

上面这张矩阵把美股代码与外汇直盘按 M30 到 M240 八档周期排开,每一格是对应周期上的标准化动量相关系数。美股部分里 TRV 在 M240 上读到 -0.733,是全表最低值,说明长期窗口下该股与基准组合呈强反向摆动;而 PFE 在 M120 上冲到 0.680,是股票端最突出的正向联动。 外汇侧的信号更短促:USDCAD 从 M30 的 -0.045 一路爬到 M240 的 0.227,周期拉长后同向性明显增强;USDCHF 则相反,M210 处 -0.277 的负值暗示中长周期上存在削弱基准的趋势。 把这些数直接丢进 MT5 的相关系数面板交叉验证,比肉眼读表更准。外汇与贵金属杠杆高、跳空频繁,这类统计关联只代表历史窗口内的概率倾向,实盘须以风险管理为先。

「八周期滚动下的交叉盘相关性残差」

上面这张矩阵把 14 个主要交叉盘按 8 个滚动窗口(MovingPeriod 43 / 50,MovingShift 6)跑出的标准化残差列了出来,末尾两行是对应窗口的全局绩效:Profit -372.3 / -345.79,Profit Factor 0.83 / 0.84,Sharpe -0.05,交易次数 71 / 66,盈利单占比仅 28.17% / 27.27%。 NZDUSD 在窗口 7、8 残差冲到 0.314、0.279,AUDCAD 同期也有 0.234、0.216,说明这两组在后期窗口里偏离均值最远;反过来 AUDNZD 八个窗口全负,最低 -0.291,CADCHF 更从 -0.294 一路滑到 -0.465,惯性背离最稳。 把这些残差当“同源噪声”看会误判:同一套参数在 43 周期亏 372 点、50 周期亏 345 点,但 EURCAD 在窗口 7、8 残差 0.327、0.284 与 NZDUSD 同步,组合里至少有两对交叉盘在后段提供正残差对冲。外汇与贵金属高杠杆,残差翻转可能瞬间吞掉缓冲。 开 MT5 把 MovingPeriod 切到 43 和 50、Shift 设 6,用上面矩阵做横截面排序,优先剔掉连续八窗同号的 AUDNZD / CADCHF,留残差符号摇摆的盘口进下一节过滤。

MQL5 / C++
class="num">0.131;-class="num">0.028;class="num">0.167;class="num">0.096;-class="num">0.013;class="num">0.147;class="num">0.314;class="num">0.279;NZDUSD
-class="num">0.047;class="num">0.189;-class="num">0.016;class="num">0.107;class="num">0.084;class="num">0.076;-class="num">0.213;-class="num">0.133;USDSEK
-class="num">0.034;-class="num">0.067;-class="num">0.188;-class="num">0.227;-class="num">0.102;-class="num">0.225;-class="num">0.234;-class="num">0.291;AUDNZD
class="num">0.046;class="num">0.039;class="num">0.117;class="num">0.102;class="num">0.097;class="num">0.170;class="num">0.234;class="num">0.216;AUDCAD
class="num">0.057;-class="num">0.016;-class="num">0.158;-class="num">0.226;-class="num">0.328;-class="num">0.215;-class="num">0.180;-class="num">0.237;AUDCHF
class="num">0.134;-class="num">0.020;class="num">0.024;-class="num">0.139;-class="num">0.124;-class="num">0.127;class="num">0.107;class="num">0.098;AUDJPY
class="num">0.083;-class="num">0.009;class="num">0.184;class="num">0.084;class="num">0.208;class="num">0.082;class="num">0.311;class="num">0.340;CHFJPY
class="num">0.025;-class="num">0.036;-class="num">0.030;-class="num">0.200;-class="num">0.185;-class="num">0.072;class="num">0.058;-class="num">0.096;EURGBP
-class="num">0.036;-class="num">0.028;class="num">0.061;class="num">0.010;class="num">0.074;-class="num">0.006;class="num">0.088;class="num">0.070;EURAUD
class="num">0.008;-class="num">0.049;-class="num">0.098;-class="num">0.219;-class="num">0.259;-class="num">0.217;-class="num">0.094;-class="num">0.169;EURCHF
class="num">0.096;-class="num">0.043;class="num">0.085;-class="num">0.124;-class="num">0.049;-class="num">0.128;class="num">0.206;class="num">0.157;EURJPY
-class="num">0.073;-class="num">0.086;-class="num">0.119;-class="num">0.211;-class="num">0.016;-class="num">0.213;-class="num">0.128;-class="num">0.213;EURNZD
class="num">0.002;class="num">0.009;class="num">0.181;class="num">0.119;class="num">0.182;class="num">0.171;class="num">0.327;class="num">0.284;EURCAD
-class="num">0.008;class="num">0.004;-class="num">0.077;-class="num">0.015;-class="num">0.054;-class="num">0.127;-class="num">0.164;-class="num">0.080;GBPCHF
class="num">0.079;-class="num">0.005;class="num">0.115;class="num">0.079;class="num">0.148;-class="num">0.008;class="num">0.144;class="num">0.253;GBPJPY
class="num">0.013;-class="num">0.060;-class="num">0.294;-class="num">0.335;-class="num">0.432;-class="num">0.376;-class="num">0.356;-class="num">0.465;CADCHF
Profit;Profit Factor;Recovery Factor;Sharpe Ratio;Trades;ProfitTrades(%);MovingPeriod;MovingShift;Title
-class="num">372.3;class="num">0.83;-class="num">0.51;-class="num">0.05;class="num">71;class="num">28.16901408;class="num">43;class="num">6;class="num">43
-class="num">345.79;class="num">0.84;-class="num">0.37;-class="num">0.05;class="num">66;class="num">27.27272727;class="num">50;class="num">6;class="num">50

高维聚类落进二维网格意味着什么

自组织特征映射(SOM)的核心价值,是把多维行情特征压成一张二维离散网格。原本难以直接比对的高维向量,在网格上按相似度自动聚成相邻区块,相关性结构肉眼可读。 对做价格行为的人而言,这等于把「哪几段走势在特征空间里长得像」从抽象计算变成图上一眼可辨。用它做品种联动或形态归并时,先聚类再分析,比盲目算相关系数更省事。 外汇与贵金属波动受宏观与流动性多重驱动,高维特征聚类只揭示历史样本中的相似倾向,不预示未来,实盘仍属高风险,须以小仓位验证。 想深挖实现细节与金融场景案例,可查 Guido Deboeck 与 Teuvo Kohonen 合著的《Visual Explorations in Finance: with Self-Organizing Maps》,里面给了超出基础聚类的应用思路。

◍ 记住这一条就够了

Alex Sergeev 在俄文版之后放出的改进类把映射存到了 \Files 而非 \Images,bmp 绘制速度因此更快,但作者在讨论里也直说:bmp 绘制过程非常缓慢,若非必要别每次都画。 改进版里 CSOM::Train 改为每 2 秒刷一次中间映射并用注释写进度,还加了 HideChart 与 m_sID 前缀,样本按列名存 bmp,第一列承载标题——这些改动你开 MT5 把 som_alex-sergeev_en.zip 拖进本地就能比对。 评论区里 Evgeniy 认为 SOM 靠单参数分类,对外汇决策「最愚蠢」,Ivan 则反驳地图是用来揪历史相似片段而非直接给买卖信号;两种看法都指向同一事实:自组织映射只做聚类,不对外样本响应偏随机,外汇贵金属杠杆高、错配参数就是实亏,拿它当预测器前先跑通聚类再说。 下面这段 import 就是改进版顺手带的打开结果文件夹的钩子,OpenAfterAnaliz 设 true 跑完会弹资源管理器,验证完建议关掉省事。

MQL5 / C++
class="macro">#class="kw">import "shell32.dll"
   class="type">int ShellExecuteW(class="type">int hwnd, class="type">class="kw">string oper, class="type">class="kw">string prog, class="type">class="kw">string param, class="type">class="kw">string dir, class="type">int show);
class="macro">#class="kw">import
input class="type">bool OpenAfterAnaliz=true; class=class="str">"cmt">// open folder with maps after finish
让小布替你跑这套
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到高维特征降维后的聚簇提示,把重复劳动交给小布,你专注决策。

常见问题

SOM 保留拓扑结构,相邻节点相似度连续;K-means 只做硬划分。行情可视化用 SOM 更容易看出特征过渡带。
取决于想捕捉的结构。多周期 RSI 加波动率组合常比单价格序列更能让网格分出趋势与震荡区,但需统一归一化。
按 BMU 权重向量距离映射 HSV 色相,邻域半径大的区域用低饱和,能减少视觉噪点。
目前小布在品种页提供降维聚簇与异常区标注,完整自训练 BMP 导出仍需自行跑 EA,小布负责前置特征诊断。
可能是初始化权重偏离输入分布或过少的迭代次数。调高迭代、缩小邻域衰减系数通常能唤醒死节点。