数据科学和机器学习(第 14 部分):运用 Kohonen 映射在市场中寻找出路(基础篇)
📘

数据科学和机器学习(第 14 部分):运用 Kohonen 映射在市场中寻找出路(基础篇)

第 1/3 篇

◍ 用 Kohonen 映射给行情做无监督分群

Kohonen 映射(自组织映射 / SOM)是一种无监督神经网络,能把高维市场状态压缩到二维网格上,让相似的价格结构落到相邻神经元。对交易者而言,它不预测方向,而是把当下行情归类到历史出现过的若干「形态簇」里,借此判断当前处于哪种已知环境。 在 MT5 里跑这套,核心是先构造输入向量:常用归一化后的多周期收益率、波动率与成交量偏离度。SOM 训练完成后,每个神经元对应一个权重向量,实时报价进来就找最佳匹配单元(BMU),从而定位当前市场所在的网格坐标。 需要注意的是,外汇与贵金属属高杠杆品种,SOM 给出的只是「历史相似度」而非胜率保证,簇内后续走势仍可能分化,实盘前务必在策略测试器用至少 3 年 Tick 数据回测。

「SOM 怎么把高维行情压成平面」

自组织映射(SOM)属于无监督学习,核心是把 p 个变量、n 条观测的高维数据,压成一张二维拓扑图,且近端样本比远端更相似。对交易者而言,这意味着多周期动量、波动率、相关性这类多维特征,能被聚成肉眼可辨的区块。 它最早由芬兰数学家 Teuvo Kohonen 在 1980 年代提出,并非为金融设计,但保留拓扑结构的特性,刚好适合把外汇或贵金属的多维状态做降维可视。 实际意义在于:当你把 EURUSD 的 H1 波动率、RSI、乖离率喂进去,相似行情会自然塌缩到相邻神经元。远端区块可能代表极端趋势,近端则是震荡收敛——这种聚类比肉眼翻指标更快定位当前所处状态。外汇与贵金属杠杆高、跳空频繁,任何降维都只是概率辅助,不能直接当方向依据。

Kohonen 网格怎么自组织出拓扑序

Kohonen 映射本质是一张神经元网格,每个节点都连着邻居。训练时把输入样本丢进网络,所有神经元各自算与输入的相似度,最像的那个就是获胜者,它的权重会被拉向该输入。 关键不在单点更新:获胜神经元周围的相邻节点权重也会跟着微调,逐渐向获胜者靠拢。多轮迭代后,整张网格自然浮现出拓扑顺序——空间上靠近的神经元,编码的输入也相似。 这种自组织不带监督标签,却能把高维金融序列压到低维平面,给可视化和聚类留出接口。MT5 里接 tick 或收盘序列跑一遍,你能直接看到不同波动状态在网格上的聚集区。外汇与贵金属波动带杠杆,网格聚类只揭示历史结构,不预示方向。

◍ 自组织映射里的学习逻辑

自组织映射(SOM)的学习目标,是让网络里不同区域对特定输入形态产生近似响应,这种激发方式模拟了人脑分区处理视觉、听觉等信号的机制。 它不像监督学习那样给标签,而是靠神经元之间的竞争与邻域更新,把高维输入压进低维拓扑网格。想在 MT5 里看效果,得先把算法落到 MQL5 的具体实现上。

「自组织映射落地的四个编码动作」

把自组织竞争网络写进 MT5 专家顾问,核心就是四步:先初始化权重和超参,再算输入到权重的欧氏距离,然后挑距离最小的神经元当胜者,最后用胜者邻域去拉权重。前两步若没写扎实,后面聚类会直接偏到随机噪声里。 初始化阶段把学习率、聚类数、迭代轮数都塞进类构造函数。下面这段代码默认 clusters=2、alpha=0.01、epochs=100,权重矩阵用 [0,1] 均匀随机生成,维度是 n×m(输入特征数 × 聚类数)。 欧氏距离函数逐行看:先比两个向量长度,不一致就 Print 报错;一致则对每一位差的平方求和再开方。测试集用了 6 行 2 列数据(如 1.2/2.3、5.2/6.7),日志里 w Matrix 输出的是 2×2 随机权重,例如 [[0.00534,0.01221],[0.54533,0.91726]],印证了 2 输入 2 输出的架构。 第三步找获胜单元,就是遍历所有聚类中心、取 D(j) 最小的索引 i。这一步是竞争学习的开关——只有胜者及其邻域才会在后续被更新,外汇小时线数据聚类时可能倾向把震荡段和趋势段分到不同单元,但 EURUSD 高频噪声大,结论仅具概率性。

MQL5 / C++
CKohonenMaps::CKohonenMaps(<span class="keyword">matrix</span> &amp;matrix_, <span class="keyword">class="type">bool</span> save_clusters=<span class="macro">true</span>, <span class="keyword">class="type">uint</span> clusters=<span class="number">class="num">2</span>, <span class="keyword">class="type">class="kw">double</span> alpha=<span class="number">class="num">0.01</span>, <span class="keyword">class="type">uint</span> epochs=<span class="number">class="num">100</span>)
{
&nbsp;&nbsp; Matrix = matrix_;
&nbsp;&nbsp;
&nbsp;&nbsp; n = (<span class="keyword">class="type">uint</span>)matrix_.Cols();
&nbsp;&nbsp; rows = matrix_.Rows();
&nbsp;&nbsp; m = clusters;
&nbsp;&nbsp;
&nbsp;&nbsp; cluster_tensor = <span class="keyword">new</span> CTensors(m);
&nbsp;&nbsp;
&nbsp;&nbsp; w_matrix =matrix_utils.Random(<span class="number">class="num">0.0</span>, <span class="number">class="num">1.0</span>, n, m, RANDOM_STATE);
}
<span class="keyword">class="type">class="kw">double</span> CKohonenMaps:: Euclidean_distance(<span class="keyword">class="kw">const</span> <span class="keyword">vector</span> &amp;v1, <span class="keyword">class="kw">const</span> <span class="keyword">vector</span> &amp;v2)
&nbsp;&nbsp;{
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> dist = <span class="number">class="num">0</span>;
&nbsp;&nbsp; <span class="keyword">if</span>(v1.Size() != v2.Size())
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">Print</span>(<span class="keyword">__FUNCTION__</span>, <span class="class="type">class="kw">string">" v1 and v2 not matching in size"</span>);
&nbsp;&nbsp; <span class="keyword">else</span>
&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> c = <span class="number">class="num">0</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span>(<span class="keyword">class="type">class="kw">ulong</span> i=<span class="number">class="num">0</span>; i&lt;v1.Size(); i++)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c += <span class="functions">MathPow</span>(v1[i] - v2[i], <span class="number">class="num">2</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dist = <span class="functions">MathSqrt</span>(c);
&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp; <span class="keyword">class="kw">return</span>(dist);
&nbsp;&nbsp;}
&nbsp;&nbsp; <span class="keyword">matrix</span> Matrix = {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<span class="number">class="num">1.2</span>, <span class="number">class="num">2.3</span>},
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<span class="number">class="num">0.7</span>, <span class="number">class="num">1.8</span>},
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<span class="number">class="num">3.6</span>, <span class="number">class="num">4.8</span>},
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<span class="number">class="num">2.8</span>, <span class="number">class="num">3.9</span>},
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<span class="number">class="num">5.2</span>, <span class="number">class="num">6.7</span>},
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<span class="number">class="num">4.8</span>, <span class="number">class="num">5.6</span>}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };
&nbsp;&nbsp;
&nbsp;&nbsp; maps = <span class="keyword">new</span> CKohonenMaps(Matrix); <span class="comment">class=class="str">"cmt">//Giving our kohonen maps class data</span>

日志里的 SOM 坐标怎么读

在 MT5 Experts 日志里,Self Organizing map 跑完 EURUSD H1 会吐出一行行坐标对,例如同一毫秒 15:52:27.572 连续打印了 [3.6,4.8]、[2.8,3.9]、[5.2,6.7]、[4.8,5.6]。这些二维数值是样本被映射到的获胜神经元位置,数值越接近,说明对应 K 线片段的形态聚类越相邻。 外汇与贵金属属高杠杆品种,这类坐标只反映历史形态分布,不预示后续方向,实盘使用前先在策略测试器用 2020—2023 年数据回放确认聚类稳定性。 权重初始化那行 w_matrix = matrix_utils.Random(0.0, 1.0, n, m, RANDOM_STATE); 决定映射起点,随机种子固定后,同样行情会得到同样拓扑,方便你对比不同参数下神经元排布差异。

MQL5 / C++
  w_matrix =matrix_utils.Random(class="num">0.0, class="num">1.0, n, m, RANDOM_STATE);

常见问题

可以。它把多维行情特征压成二维网格坐标,相同走势会聚到相邻神经元,看日志坐标就能判断行情聚类倾向。
不用每次手调,但初期学习率建议设 0.1 附近;过大易让网格震荡不收敛,小了则训练慢、拓扑序模糊。
能。小布内置了无监督聚类诊断,打开对应品种页即可看到行情自组织分群结果与坐标解读。
把它当行情指纹。连续几天落点靠近说明状态稳定,突然跳到远格大概率意味着节奏切换,可重点盯。
多因特征未归一化或迭代次数不够。先标准化输入,再把训练轮次提到 500 以上,拓扑通常会自然浮现。