在算法交易中 Kohonen 神经网络的实际应用。 第 I 部分 工具·综合运用
🧠

在算法交易中 Kohonen 神经网络的实际应用。 第 I 部分 工具·综合运用

(3/3)· 前两层铺垫完概念与工具,这一层把网络真正接进 EA 与行情预测闭环

含代码示例 第 3/3 篇

很多交易者把 Kohonen 网络当成优化结果的可视化摆设,靠肉眼从拓扑图里找参数,判断标准飘忽不定。本文收尾篇直接把网络接进自动决策:修掉索引与归一化旧坑,跑通测试、预测与参数优选。前面两篇没解决的“自动提取知识”,在这里闭环。

搭一个 SOM-Explorer 把网络跑起来

把 Kohonen 网络从纸面放到 MT5 里,最直接的方式是写一个通用 EA,命名为 SOM-Explorer,把封装好的 CSOM 类头文件包含进来,用输入参数控制网络结构与可视化。EA 会改当前图表设置,实操时开一张新图表专门挂它,避免把正在看的行情图搞乱。 核心输入分三组。网络结构里 CellsX/CellsY 默认都是 10,即 10×10 神经元网格;HexagonalCell 默认 true 用六边形拓扑,改 false 切矩形;EpochNumber 默认 100 个学习世代;ValidationSetPercent 默认 0 关验证,想用建议约 10%。可视化里 ImageW/ImageH 默认 500 像素,MaxPictures 默认 0 走连续滚动行,设大于 0 则把全部平面排成每行固定数的小图;ShowProgress 默认 true,学习时每秒刷一次网络映像。 模式由文件名切换:DataFileName 和 NetFileName 都空,EA 随机生成 3D 演示集并自教;NetFileName 填了 .som 就进测试模式,直接读已训网络;DataFileName 填 '?' 且网络有效时,EA 按网络里存的范围造随机测试样本。注意网络文件记了特征范围,若喂进去的数值超出原定义区间(比如回撤或成交数为负),不会报错但结果不可靠。 初始化别漏两件事:用 ChartSetInteger 开 CHART_EVENT_MOUSE_MOVE,否则鼠标悬停神经元看不到弹出提示;神经网络算法只靠 OnTimer 触发一次,进去先 EventKillTimer 停表,防止重复跑。下面这段就是 EA 初始化和事件转发的骨架。 用默认参数只把 ImageW/ImageH 降到 230、MaxPictures 设 3,能截到全部平面的图:随机 3D 矢量训出的 Kohonen 映像里,每个神经元标了服务数据,聚类也画了出来。日志会打印聚类信息(默认限 5 条)。之后把 NetFileName 指向刚存的 SOM-20181205-134437.som、DataFileName 写 '?',就能脱离学习集看随机样本的响应平面,此时把图放大、MaxPictures 归 0 更清楚。外汇与贵金属市场波动剧烈、杠杆风险高,这类自组织映射只做特征结构探索,不构成任何方向判定。

MQL5 / C++
CSOMDisplay KohonenMap;
class="type">void OnInit()
{
  ChartSetInteger(class="num">0, CHART_EVENT_MOUSE_MOVE, true);
  EventSetMillisecondTimer(class="num">1);
}
class="type">void OnChartEvent(class="kw">const class="type">int id, class="kw">const class="type">long &lparam, class="kw">const class="type">class="kw">double &dparam, class="kw">const class="type">class="kw">string &sparam)
{
  KohonenMap.OnChartEvent(id, lparam, dparam, sparam);
}
class="type">void OnTimer()
{
  EventKillTimer();
  
  MathSrand(RandomSeed);
  
  class="type">bool hasOneTestPattern = class="kw">false;
  
  if(NetFileName != "")
  {
    if(!KohonenMap.Load(NetFileName)) class="kw">return;
    KohonenMap.DisplayInit(ImageW, ImageH, MaxPictures, ColorScheme, ShowBorders, ShowTitles);
    Comment("Map ", NetFileName, " is loaded; size: ", KohonenMap.GetWidth(), "*", KohonenMap.GetHeight(), "; features: ", KohonenMap.GetFeatureCount());
    if(DataFileName != "")
    {
      if(!KohonenMap.LoadPatterns(DataFileName))
      {
        Print("Data loading error, file: ", DataFileName);
        class=class="str">"cmt">// 生成随机测试矢量
        class="type">int n = KohonenMap.GetFeatureCount();
        class="type">class="kw">double min, max;

「自造随机样本喂给 SOM 做匹配验证」

这段逻辑展示了在没有现成网络文件时,如何手动构造一组随机特征向量并投进 Kohonen 映射,观察最佳匹配节点(BMU)的输出与误差。外汇与贵金属行情的高维特征若直接套用此类随机初始化,极易因量纲差异导致聚类偏移,属高风险操作,仅适合在 MT5 策略测试器里先跑通流程。 下方代码先按特征数 n 动态扩数组 v,再对每个维度调用 GetFeatureBounds 取上下界,用 rand()/32767 做 [min,max] 均匀随机填充,最后 AddPattern 打上 RANDOM 标签。匹配阶段 GetBestMatchingFeatures 返回节点指针,可打印其坐标、命中次数与 MSE。 double v[]; ArrayResize(v, n); for(int i = 0; i < n; i++) { KohonenMap.GetFeatureBounds(i, min, max); v[i] = (max - min) * rand() / 32767 + min; } KohonenMap.AddPattern(v, "RANDOM"); Print("Random Input:"); ArrayPrint(v); double y[]; CSOMNode *node = KohonenMap.GetBestMatchingFeatures(v, y); Print("Matched Node Output (", node.GetX(), ",", node.GetY(), "); Hits:", node.GetHitsCount(), "; Error:", node.GetMSE(),"; Cluster N", node.GetCluster(), ":"); ArrayPrint(y); KohonenMap.CalculateOutput(v, true); hasOneTestPattern = true; 若连数据文件都没给,代码会生成 1000 组三维演示向量:x[0] 落在 [0,1000]、x[1] 落 [0,1]、x[2] 落 [-1,1],用来对比开/关归一化的映射差异。注意 x[0] 量纲是 x[1] 的千倍,未做 UseNormalization 时 SOM 会倾向被大数值维度主导。 训练分支里 ValidationSetPercent 限制在 1~49 才切分验证集,切分点按数据总量乘 (1-p/100) 取整;ClusterNumber>1 走指定簇数聚类,否则自动估算。开 MT5 把这段贴进 EA 的 OnStart,先试 n=3、CellsX=Y=10,看 BMU 的 MSE 是否随 EpochNumber 下降。

MQL5 / C++
class="type">class="kw">double v[];
ArrayResize(v, n);
for(class="type">int i = class="num">0; i < n; i++)
{
  KohonenMap.GetFeatureBounds(i, min, max);
  v[i] = (max - min) * rand() / class="num">32767 + min;
}
KohonenMap.AddPattern(v, "RANDOM");
Print("Random Input:");
ArrayPrint(v);
class="type">class="kw">double y[];
CSOMNode *node = KohonenMap.GetBestMatchingFeatures(v, y);
Print("Matched Node Output(", node.GetX(), ",", node.GetY(), "); Hits:", node.GetHitsCount(), "; Error:", node.GetMSE(),"; Cluster N", node.GetCluster(), ":");
ArrayPrint(y);
KohonenMap.CalculateOutput(v, true);
hasOneTestPattern = true;

◍ 自组织映射训练完怎么落盘和回检

Kohonen 网络跑完 1000 轮后,典型日志会停在 Overall NMSE=0.09420336270396877,说明映射对样本的总体归一化均方误差约 9.4%,在价格行为聚类里算可用但非极致紧凑。训练收尾时先 Render 进内部 BMP 缓冲,再按是否带测试样本决定画节点还是画全图案,ClusterNumber 非零才标聚类边界。 代码末段把映像存成 SOM-20181205-134437.som,若 NetFileName 为空就按 GetID 自动存盘;随后用随机输入 457.17510 / 0.29727 / 0.57621 做匹配,命中节点 (8,3)、Hits=5、Error=0.05246704285146882,回检误差比训练总体 NMSE 还低,说明该随机样本落进了高密度区。 注意 FileOpen error 5004 是 CSV 路径缺失导致数据装载失败,MT5 里跑这套时先把 ?.csv 换成真实路径,否则后续随机回检不会执行。外汇与贵金属行情高波动,SOM 聚类仅刻画历史形态分布,对未知行情只具概率倾向,不构成方向判定。

MQL5 / C++
  ArrayInitialize(vector, class="num">0);
  KohonenMap.CalculateOutput(vector);
  }
  KohonenMap.Render(); class=class="str">"cmt">// 将映像绘制到内部 BMP 缓冲区中
  if(hasOneTestPattern)
    KohonenMap.ShowAllPatterns();
  else
    KohonenMap.ShowAllNodes(); class=class="str">"cmt">// 在 BMP 缓冲区中绘制细胞内标签
  if(ClusterNumber != class="num">0)
  {
    KohonenMap.ShowClusters(); class=class="str">"cmt">// 标记聚类
  }
  KohonenMap.ShowBMP(SaveImages); class=class="str">"cmt">// 在图表上将文件显示为位图图像,可选择保存到文件中
  if(NetFileName == "")
  {
    KohonenMap.Save(KohonenMap.GetID());
  }
}
Pass class="num">0 from class="num">1000 class="num">0%
Pass class="num">78 from class="num">1000 class="num">7%
Pass class="num">157 from class="num">1000 class="num">15%
Pass class="num">232 from class="num">1000 class="num">23%
Pass class="num">310 from class="num">1000 class="num">31%
Pass class="num">389 from class="num">1000 class="num">38%
Pass class="num">468 from class="num">1000 class="num">46%
Pass class="num">550 from class="num">1000 class="num">55%
Pass class="num">631 from class="num">1000 class="num">63%
Pass class="num">710 from class="num">1000 class="num">71%
Pass class="num">790 from class="num">1000 class="num">79%
Pass class="num">870 from class="num">1000 class="num">87%
Pass class="num">951 from class="num">1000 class="num">95%
Overall NMSE=class="num">0.09420336270396877
Training completed at pass class="num">1000, NMSE=class="num">0.09420336270396877
Clusters [class="num">14]:
"R1000" "R1"    "R2"  
N0
class="num">754.83131   class="num">0.36778   class="num">0.25369
N1
class="num">341.39665   class="num">0.41402  -class="num">0.26702
N2
class="num">360.72925   class="num">0.86826  -class="num">0.69173
N3
class="num">798.15569   class="num">0.17846  -class="num">0.37911
N4
class="num">470.30648   class="num">0.52326   class="num">0.06442
Map file SOM-class="num">20181205-class="num">134437.som saved
FileOpen error ?.csv : class="num">5004
Data loading error, file: ?
Random Input:
class="num">457.17510   class="num">0.29727   class="num">0.57621
Matched Node Output(class="num">8,class="num">3); Hits:class="num">5; Error:class="num">0.05246704285146882; Cluster N0:
class="num">497.20453   class="num">0.28675   class="num">0.53213

把 Kohonen 类接进你自己的 EA

这几年 MT5 用户已经能拿到 Kohonen 神经网络的开放实现,我们修掉了一些原有 bug,补了配套工具,并用一个演示 EA 跑通了流程。源码直接可挂到你自己的任务里,下一步就是结合具体品种去验证聚类效果。 顺带一提,MQL5 自带的 ALGLIB 移植版已经支持层次聚类,路径在 MQL5/Include/Math/Alglib/dataanalysis.mqh 的 ClusterizerRunAHC,不想自己写 SOM 的可以用它做对照。外汇和贵金属波动受宏观事件驱动,聚类结果稳定性取决于数据分布是否延续,用验证抽样挑地图尺寸和训练时长更稳妥。 作者留了话头:后续会放具体用例,未完待续。你要是真想试,先把那 21.05 KB 的 Kohonen1MQL5.zip 下下来,在策略测试器里用演示 EA 跑一遍 EURUSD 的 H1,看权重图能不能分出趋势和盘整区。

把网络诊断交给小布盯盘
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到网络输出的特征聚类与异常提示,你只需在 EA 里调用结论,不必每次手跑训练脚本。

常见问题

旧实现用像素坐标索引神经元,映像尺寸变化会扰动计算过程;改为按输出层行列数字索引后,坐标与图像大小解耦,结果才可复现。
量纲大的分量会主导距离计算,网络学到的拓扑偏向该特征,预测与聚类的偏差概率显著上升,建议训练前做常规化。
两者倾向互补:网络预测给出状态分区,参数优选在分区内缩小搜索面,实盘里组合使用比单用一种更稳。
目前小布盯盘内置的是聚类与异常诊断视图,可读取导出的节点权重做对照;自动训练管道需你在 MT5 端跑完再喂数据,外汇贵金属波动剧烈仍属高风险。
关于网络层与神经元术语的完整讨论见《在算法交易中 Kohonen 神经网络的实际应用·基础篇》,代码修正细节见《在算法交易中 Kohonen 神经网络的实际应用·工具篇》。