您应当知道的 MQL5 向导技术(第 07 部分):树状图·进阶篇
📘

您应当知道的 MQL5 向导技术(第 07 部分):树状图·进阶篇

第 2/3 篇

◍ 聚类模型的内部数据载体

这段代码展示了一个用于价格行为聚类的 C++ 风格类骨架,核心在于把特征矩阵与标签行分开存放。外汇与贵金属行情的高波动特性,使得这类聚类结构在样本外容易漂移,使用时需警惕过拟合风险。 struct Sdata 是承载训练数据的容器:x 为 CMatrixDouble 类型,存多列特征值(如点位偏离、振幅等);y 为 CRowDouble 类型,存对应输出标签。二者分离便于后续调用 CClustering 做无监督分组。 类内还声明了 m_clustering_index 与 m_clustering_z 两个整型行向量,分别记录样本所属簇编号与标准化后的簇映射。开 MT5 把这段声明塞进你的 EA 头文件,先跑一轮 EURUSD 的 M15 数据,看 m_clustering_index 的分布是否集中在 3~5 个簇内,再决定阈值。

MQL5 / C++
class="type">void PointFeatures(class="type">int value) { m_point_featues=value; }
class="kw">protected:
  class="type">class="kw">double Optimize(class="type">class="kw">double lots);
  class="type">class="kw">double GetOutput();
  CClusterizerState m_state;
  CAHCReport m_report;
  class="kw">struct Sdata
    {
      CMatrixDouble x;
      CRowDouble  y;
      Sdata(){};
      ~Sdata(){};
    };
  Sdata m_data;
  CClustering m_clustering;
  CRowInt m_clustering_index;
  CRowInt m_clustering_z;
};

用最近 N 根柱线范围喂训练矩阵

新柱成形那一刻,先把最新柱线的高低差算出来,再填进自定义结构里。训练点数量(m_training_points)直接决定数据集规模,属于可外部调的输入参数;而每个数据点的特征数(m_point_featues)就是它的维度,默认 4 代表取最后 4 根价格柱的范围来描述一个样本,本质上是个向量。 下面这段代码在每根新柱触发时重算并填充。x 矩阵被Resize成 m_training_points 行、m_point_featues 列;y 少一行是因为标签用的是下一段偏移。 m_high.Refresh(-1) 与 m_low.Refresh(-1) 拉取最新高低价缓存。双层循环里,i 走训练点、ii 走特征维度,用 m_high.GetData(StartIndex()+ii+i) 减 m_low.GetData(同位置) 得到该柱范围写进 x。 开 MT5 把 m_point_featues 改成 8,能看到样本维度扩张、历史范围窗口拉长,模型输入也随之变化;外汇与贵金属波动剧烈,此类特征工程仅辅助判断,不改变高风险本质。

MQL5 / C++
    m_data.x.Resize(m_training_points,m_point_featues);
    m_data.y.Resize(m_training_points-class="num">1);
    
    m_high.Refresh(-class="num">1);
    m_low.Refresh(-class="num">1);
    
    for(class="type">int i=class="num">0;i<m_training_points;i++)
    {
      for(class="type">int ii=class="num">0;ii<m_point_featues;ii++)
      {
        m_data.x.Set(i,ii,m_high.GetData(StartIndex()+ii+i)-m_low.GetData(StartIndex()+ii+i));
      }
    }

「两步跑通 AlgLib 的 AHC 聚类」

把训练数据塞进自定义结构后,真正建模只需两步:先初始化模型点,再跑 AHC 生成器。初始化对应代码里的 ClusterizerSetPoints,最后那个常量 20 是特征维度上限相关的设定项,需与你实际特征数匹配,否则建模会直接报错。 跑模型靠 ClusterizerRunAHC,它先做简短预处理,再调受保护的 ClusterizerRunAHCInternal 干苦活。源代码在 include\math\AlgLib\dataanalysis.mqh 第 22463 行附近,核心产出是 cidx 输出数组——它用单个数组压缩了完整树状图:前 N 个值(N 为训练点数)是每点的聚类归属,后续索引记录聚类逐级合并关系。 距离类型有 9 种可选,从切比雪夫、欧几里得到长矛手等级相关,各自有索引,在设定点函数里指定。选错距离类型会让聚类形态大变;用欧几里得(索引 2)配合 Ward 方法最稳,因为 Ward 和其他距离类型的计算逻辑不通用。外汇与贵金属市场波动剧烈、杠杆风险高,聚类仅作概率性结构参考,不能直接当方向依据。

MQL5 / C++
m_clustering.ClusterizerSetPoints(m_state, m_data.x, m_training_points, m_point_featues, class="num">20);
m_clustering.ClusterizerRunAHC(m_state, m_report);

◍ 从 AHC 报告里抠出聚类数组

把训练集跑完 AHC 之后,真正能拿来画树状图、定边界的东西,藏在生成函数的输出报告里。调一句 ClusterizerGetKClusters 就能把聚类索引和聚类 z 两个数组抽出来,后面怎么切分样本、怎么分层合并全靠它们。 本例只把数据压成 3 个聚类,所以树状图的合并层级不会超过三层,结构一眼能看穿。若把聚类数提到 n,合并层级会涨到 n-1 级,图会明显变乱,回测时先想清楚要不要这么多类。 外汇与贵金属行情高波动,聚类只是刻画历史样本分布的工具,对后续信号只能给概率倾向,不能直接当方向结论。

MQL5 / C++
m_clustering.ClusterizerGetKClusters(m_report, m_clusters, m_clustering_index, m_clustering_z);

给训练点打上后续波动标签

做标记不是为了给数据分个类,而是要让模型知道每个样本后面到底走出了多大行情。这里用的标签,是训练点之后那根柱线的最终高低差——也就是真实波幅范围。 实时取数时,每来一根新柱线就补一个含当前点的数据集,但当前点的最终波动此时还看不见。所以标记阶段必须跳过索引 0,只给已经走完的柱子贴标签,逻辑上和图里展示的一致。 如果想看远一点,也可以不盯下一根,而是取之后 5 根或 10 根的整体范围当 y 值。这样数值更稳,若标签改成收盘价方向(涨跌),同样能往前多推几根来预测。代价是要跳过 n 根柱子(n 即前瞻根数),滞后明显变大;不过大滞后反而方便和投影结果做安全比对,因为距目标 y 值只差一根。外汇与贵金属波动剧烈,这种滞后在实盘里可能让你晚进场,高风险需自担。 下面这段是标记循环的核心:从 0 扫到训练点总数,i>0 才赋值,把第 i-1 个点的 y 设成对应柱线的高减低。

MQL5 / C++
      for(class="type">int i=class="num">0;i<m_training_points;i++)
      {
         if(i>class="num">0)class=class="str">"cmt">//assign classifier only for data points for which eventual bar range is known
         {
            m_data.y.Set(i-class="num">1,m_high.GetData(StartIndex()+i-class="num">1)-m_low.GetData(StartIndex()+i-class="num">1));
         }
      }

「从聚类匹配推算当前波动幅度」

标记完训练集后,下一步是判定当前数据点落入哪个已定义聚类。做法很直接:遍历建模报告输出的聚类索引数组,把当前点的索引和训练样本逐一比对,索引相同即归为同一类。 归好类后,只从该聚类内部的其它训练点取 Y 值来估算。最粗的办法是算平均值,但中位数或众数也行,逻辑一致——只用同簇样本,不掺外部数据。均值法在样本少时偏差可能偏大,实战中可先跑均值看分布再换中位数验证。 下面这段 MT5 代码演示了匹配簇内平均目标区间的求法。注意它先判断终止类型是否为 1(即建模正常结束),否则不进计算。 别把均值当唯一真理 外汇与贵金属波动聚类常受跳空干扰,均值易被极端 K 线拉偏;同一簇内建议同时输出中位数,比较两者差离再决定信号置信度。 if(m_report.m_terminationtype==1) // 建模正常终止才继续 { int _clusters_by_index[]; // 存放各点聚类索引 if(m_clustering_index.ToArray(_clusters_by_index)) // 转成数组 { int _output_count=0; // 匹配计数 for(int i=1;i<m_training_points;i++) // 遍历训练点 { //get mean target bar range of matching cluster if(_clusters_by_index[0]==_clusters_by_index[i]) // 当前点与样本同簇 { _output+=(m_data.y[i-1]); // 累加同簇 Y 值 _output_count++; // 计数加一 } } // if(_output_count>0){ _output/=_output_count; } // 求平均 } } 在 MT5 里把这段接进你的聚类 EA,把 m_data.y 换成真实 K 线区间(如 high-low),跑 EURUSD 的 M15 数据,同簇样本数低于 5 时平均值的参考概率会明显下降,这时该倾向用中位数。

MQL5 / C++
  if(m_report.m_terminationtype==class="num">1)
  {
     class="type">int _clusters_by_index[];
     if(m_clustering_index.ToArray(_clusters_by_index))
     {
      class="type">int _output_count=class="num">0;
      for(class="type">int i=class="num">1;i<m_training_points;i++)
      {
         class=class="str">"cmt">//get mean target bar range of matching cluster
         if(_clusters_by_index[class="num">0]==_clusters_by_index[i])
         {
            _output+=(m_data.y[i-class="num">1]);
            _output_count++;
         }
      }
      class=class="str">"cmt">//
      if(_output_count>class="num">0){ _output/=_output_count; } 
     }
  }

常见问题

通常用二维数组或结构体数组保存每个样本的特征向量与标签,便于后续按索引取用。
实盘常见取 50~200 根,N 太小噪声大、太大滞后,建议先用 100 根回测观察稳定性。
可以,小布能按你设定的 N 根范围自动生成训练矩阵并输出聚类结果,省去手写循环。
解析报告中的合并顺序与距离阈值,按cut-off切分后得到每类的样本索引数组即可。
定位当前样本所属聚类,取该聚类内历史点的后续波动均值作为当前幅度参考,属概率估计。