在 MetaTrader 5 中使用自组织特征映射(Kohonen 映射)·综合运用
(3/3)· 从网络架构到 BMP 可视化的 9 步落地,解决 MT5 里高维数据看不懂的顽疾
「鸢尾花 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,所以算出来的是「收于开盘的偏离幅度」,数值可能为正也可能为负。 外汇与贵金属杠杆高、点差跳变频繁,用这套逻辑算日内百分比时,务必先确认当前品种报价精度,否则小点数品种容易被放大成假信号。
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 看哪些品种在长周期上有背离。
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,留残差符号摇摆的盘口进下一节过滤。
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 跑完会弹资源管理器,验证完建议关掉省事。
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