聚类分析(第一部分):精通指标线的斜率·综合运用
📘

聚类分析(第一部分):精通指标线的斜率·综合运用

第 3/3 篇

◍ 从日志打印里读出横盘与趋势的概率边界

指标大循环跑完且 prev_calculated < 1(即首遍计算)时,会把统计结果通过 prtStdDev() 打到 MT5 日志,同时停掉秒表并打印耗时。prtStdDev() 先输出 HeadLineIncrStat(pre) 表头,再对每条数值线(索引 iLine)逐行打印 14 个结果,每个都调用 retIncrStat()。其中 tot += (int)Cluster[iLne][i].n 汇总类别 4–13 的样本总量,作为这些类别的 100% 基准。 逐列看打印输出:第二列 ID 里 100nnn 是纯均值计算(末三位 100/25/50/75 代表学习率),400nnn–1300nnn 是聚类类别,末三位是 μ 的乘数。第四列给出各类别样本数与占比——以 GBPUSD D1 为例,Cat.#4 占 2409 根柱线即 66.0% 时间行情水平,说明约三分之二时段可能适合区间交易;而 #5 仅 112 个值(3.1%),#8–#10 局部峰值更多,这个断层可粗略划阈值:fabs(slope) < 0.5*μ 倾向横盘做区间,fabs(slope) > 1.0*μ 倾向趋势中顺势。外汇与贵金属杠杆高,这类划分仅基于历史分布,实战须自担风险。 μ 列(括号里是乘数)直接以点数为单位,日线 #4 均值从 100100 的 217.6 点降到 100075 的 182.1 点(降幅约 16%),说明 GBPUSD 波动在 2014.05.28 起样本里可能走弱。第六列 σ 及 σ/(max-min) 衡量离散度,正态下 68% 值落于 σ 内,σ 越小均值越准——前 4 行 100nnn 正是用来判断 μ 稳不稳,波动太大的 μ 尽量少用。 切到 M15 重跑,平均斜率从日线 217.6 点掉到 15 分钟内的 18.0 点,但横盘/趋势的 0.5μ、1.0μ 分界逻辑仍然成立。直接在 MT5 把图表周期从 D1 拨到 M15 就能复看这套打印,验证小周期是否重复大周期行为。

MQL5 / C++
  class=class="str">"cmt">//+------------------------------------------------------------------+
  class=class="str">"cmt">//|  added for cluster analysis                                      |
  class=class="str">"cmt">//+------------------------------------------------------------------+
  if (prev_calculated < class="num">1)
    {
      prtStdDev(_Symbol+" "+EnumToString(Period())+" "+ShortName, class="num">0, NumValues);
      if (StopWatch!=class="num">0) StopWatch = GetTickCount64()-StopWatch;
      Print("Time needed for ",rates_total," bars on a PC with ",TerminalInfoInteger(TERMINAL_CPU_CORES),
             " cores and ",TerminalInfoInteger(TERMINAL_MEMORY_PHYSICAL)," Ram: ",TimeToString(StopWatch/class="num">1000,TIME_SECONDS));
    }
  class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void prtStdDev(class="kw">const class="type">class="kw">string pre, class="type">int iLne, class="kw">const class="type">int NoVal)
  {
  if (NoVal <= class="num">0 ) class="kw">return;                                        class=class="str">"cmt">// if true no printing  
  if (Cluster[iLne][class="num">0].n==class="num">0 ) class="kw">return;                             class=class="str">"cmt">// no values entered for this &class="macro">#x27;line&class="macro">#x27;
  HeadLineIncrStat(pre);                                          class=class="str">"cmt">// print the headline
  class="type">int i,tot = class="num">0,sA=ArrayRange(Cluster,class="num">1),
      sC=ArraySize(CatCoeff);
  for(i=class="num">4; i<sA; i++)
      tot += (class="type">int)Cluster[iLne][i].n;                             class=class="str">"cmt">//  sum up the total volume of all but the first [class="num">0] category
      
  retIncrStat(Cluster[iLne][class="num">0].n, pre, "learn class="num">100% all["+(class="type">class="kw">string)sC+"]", Cluster[iLne][class="num">0], class="num">1, Cluster[iLne][class="num">0].µ); class=class="str">"cmt">// print the base the first category [class="num">0]

分位抽样下的聚类基准打印

这段逻辑在做一件事:把全样本和三个分位子集(25% / 50% / 75%)的聚类统计分别打出来,前三个 retIncrStat 调用固定输出以第一类 μ 为基准的相对值,方便横向比分布偏移。 循环从 i=4 开始扫到 sA-2,逐类打印中间分组;末尾再单独打印最后一类(系数大于 CatCoeff[sC-1] 的尾部)。这种写法让你在 MT5 Experts 日志里直接看到每层的样本数与离散度,不用事后导表。 GBPUSD 日线、HalfTrd=2 的实测里,100% 全样本 7266 根 K 线,均值 μ=217.6,σ 高达 1800(区间占比 1.21%),最大偏离 148850;而 25% 子集仅 5476 根,σ 降到 470.2(4.06%),均值 212.8 约为全样本 0.98 倍。外汇与贵金属属高风险品种,这类统计只描述历史分布,不预示后市方向。 想验证,把这段塞进你自己的聚类 EA 的尾段,把 sC 调到 9,跑 GBPUSD D1 看日志分层是否对齐上面的数字。

MQL5 / C++
  retIncrStat(Cluster[iLne][class="num">1].n, pre, "learn  class="num">25% all["+(class="type">class="kw">string)sC+"]", Cluster[iLne][class="num">1], class="num">1, Cluster[iLne][class="num">0].µ); class=class="str">"cmt">// print the base the first category [class="num">0]
  retIncrStat(Cluster[iLne][class="num">2].n, pre, "learn  class="num">50% all["+(class="type">class="kw">string)sC+"]", Cluster[iLne][class="num">2], class="num">1, Cluster[iLne][class="num">0].µ); class=class="str">"cmt">// print the base the first category [class="num">0]
  retIncrStat(Cluster[iLne][class="num">3].n, pre, "learn  class="num">75% all["+(class="type">class="kw">string)sC+"]", Cluster[iLne][class="num">3], class="num">1, Cluster[iLne][class="num">0].µ); class=class="str">"cmt">// print the base the first category [class="num">0]
  
  for(i=class="num">4; i<sA-class="num">1; i++)
    {
      retIncrStat(tot, pre,"Cluster["+(class="type">class="kw">string)(i)+"] (<="+_d22(CatCoeff[i-class="num">4])+")", Cluster[iLne][i], class="num">1, Cluster[iLne][class="num">0].µ);           class=class="str">"cmt">// print each category
    }
  retIncrStat(tot, pre,"Cluster["+(class="type">class="kw">string)i+"] (> "+_d22(CatCoeff[sC-class="num">1])+")", Cluster[iLne][i], class="num">1, Cluster[iLne][class="num">0].µ);           class=class="str">"cmt">// print the last category
}

「GBPUSD日线分簇的盈亏梯度」

在 GBPUSD 日线周期跑 HalfTrd 策略、参数 2 的全样本里,总交易 1825 次占 100%,均值收益 182.0(约 0.84 倍标准差),最大浮盈曾到 11574.0,说明基础分布下整体偏正但离散极大。 把样本按阈值切到 9 个簇后,Cluster[4](阈值 ≤0.00)有 2410 次却收益全 0,等于这批信号在该口径下完全失效,直接拉低了全样本效率。 从 Cluster[5] 到 Cluster[9],阈值从 ≤0.33 提到 ≤2.98,簇内次数从 112 爬到 189,均值收益从 37.9 一路抬到 566.3,且每簇均值都高于对应标准差倍数(0.17µ→2.60µ),高阈值簇的盈亏比明显更肥。 外汇与贵金属属高风险品种,以上为历史分簇统计,未来同分布不保证延续,开 MT5 把 HalfTrd 参数 2 套同样口径回测可复核对数值。

◍ 日线与十五分钟下的半仓分布差异

把 GBPUSD 在 D1 周期跑 HalfTrd 参数 2,聚类切到 Cluster[10]~[13] 能看到尾部厚度骤变:Cluster[10] 价格上限 4.21、样本 196 根(占 5.4%),均值 816.6 仅是 3.75 倍 µ;到 Cluster[13] 价格 >7.87,样本只剩 54 根(1.5%),但均值拉到 2707.3,是 12.44 倍 µ,极值上限 11574。这种右偏说明大波动日越来越少但单次幅度越来越夸张,外汇高杠杆下容易扫损后不回头。 同一品种切到 M15 周期,learn 100% 全样本 556389 根,µ=18.0、σ 区间占比仅 0.14%,max 却到 152900,几乎是被极个别异常棒撑开;learn 25% 截断后 max 收到 2971,σ 占比升到 1.76%。可见小周期全量统计会被少数离群棒污染,做特征工程时先截尾再算 µ/σ 更稳。

  • 根 D1 棒在 12 核机器上耗时 00:00:00:016,说明这类聚类扫描开销极低,值得在 MT5 里常开做基线监控。

HalfTrd 2 在英镑美刀 M15 上的分簇回报

把 GBPUSD 的 M15 周期、HalfTrd 参数设为 2 跑一遍,能看到学习集 50% 与 75% 两种切分下,样本覆盖都是 100.0%,平均单笔盈利约 15.9 与 15.7,波动以 0.88µ、0.87µ 计,最大回撤 45.0、46.1 点,对应 1.51%、1.55% 的账户暴露。这两行是整段的总览基线。 往下按盈利因子分簇,Cluster[4](盈利 ≤0.00)占了 69.4% 的样本量 193164,但平均盈利直接为 0、最大也 0,说明绝大多数单子落在盈亏临界点附近,策略在该区不产出。 真正有油水的是后面几个簇:Cluster[5](≤0.33)仅 3.8% 样本,平均盈利 3.3、胜率 33.57%;Cluster[6](≤0.76)4.6% 样本,平均 10.3、胜率 26.24%;Cluster[7](≤1.32)4.7% 样本,平均 19.6、胜率 25.90%;Cluster[8](≤2.04)4.5% 样本,平均 31.6、胜率 24.69%。样本越少、阈值越宽,单笔均值和 µ 倍数越往上走。 外汇与贵金属属高杠杆品种,上述分簇数据只是某次回测切片,换时间段可能倾向完全不同。开 MT5 把 HalfTrd 2 的 Cluster 阈值自己拉一遍,比盯总览数字更有用。

「GBPUSD 十五分钟半仓聚类的尾部膨胀」

在 GBPUSD 的 M15 周期上跑 HalfTrd 参数为 2 的聚类扫描,样本覆盖 556391 根 K 线,十二核机器耗时 140 毫秒,说明这类统计在本地 MT5 上几乎零成本,可随时复跑。 从 Cluster[9] 到 Cluster[13] 的分布看,阈值越放宽,样本数越少:Cluster[9](<=2.98)有 11067 次占比 4.0%,到 Cluster[12](<=7.87)只剩 4390 次占比 1.6%;而 Cluster[13](>7.87)有 5346 次,占比 1.9%,样本并未单调衰减。 偏差倍数(*µ)从 Cluster[9] 的 2.62 一路拉到 Cluster[13] 的 13.40,极端簇的平均耗时 241.8 远超前面各簇的 47~128,且最大观测到 2971.0——尾部风险不是线性外推,而是概率密度极薄但单笔代价极高。 做价格行为统计时,别只看中间簇的温和均值。外汇与贵金属属高风险品种,这种厚尾簇提示:止损要按 Cluster[13] 的量级预留,而非按 2~3 倍 µ 的常态设想。

MQL5 / C++
GBPUSD PERIOD_M15 HalfTrd class="num">2     class="num">900298    Cluster[class="num">9] (<=class="num">2.98)     class="num">11067 (  class="num">4.0%)      class="num">47.3  (class="num">2.62*µ)          class="num">5.5  (class="num">23.91%)          class="num">37.0 -       class="num">60.0
GBPUSD PERIOD_M15 HalfTrd class="num">2    class="num">1000421   Cluster[class="num">10] (<=class="num">4.21)      class="num">8931 (  class="num">3.2%)      class="num">67.6  (class="num">3.75*µ)          class="num">7.3  (class="num">23.59%)          class="num">54.0 -       class="num">85.0
GBPUSD PERIOD_M15 HalfTrd class="num">2    class="num">1100580   Cluster[class="num">11] (<=class="num">5.80)      class="num">6464 (  class="num">2.3%)      class="num">94.4  (class="num">5.23*µ)          class="num">9.7  (class="num">23.65%)          class="num">77.0 -      class="num">118.0
GBPUSD PERIOD_M15 HalfTrd class="num">2    class="num">1200787   Cluster[class="num">12] (<=class="num">7.87)      class="num">4390 (  class="num">1.6%)     class="num">128.4  (class="num">7.12*µ)         class="num">12.6  (class="num">22.94%)         class="num">105.0 -      class="num">160.0
GBPUSD PERIOD_M15 HalfTrd class="num">2    class="num">1300999   Cluster[class="num">13] (> class="num">7.87)      class="num">5346 (  class="num">1.9%)     class="num">241.8  (class="num">13.40*µ)        class="num">138.9  (class="num">4.91%)          class="num">143.0 -     class="num">2971.0
Time needed for class="num">556391 bars on a PC with class="num">12 cores and Ram: class="num">65482, Time: class="num">00:class="num">00:class="num">00:class="num">140

◍ 把聚类塞进指标主循环里跑

用 HalfTrend 做例子,能看出简单分类或聚类分析可能挖出指标行为里很有价值的信息,否则这类计算量会非常重。常规写法要把均值、方差放在单独循环算完,再开一个循环做聚类。 这里反而是把学习与应用全压进指标计算的大循环:前半段数据用来学分布,后半段直接套用,整套在 50 万条以上数据里跑完不到一秒,实时出结果对交易判断趋势或横盘有帮助。 代码结构设计得比较轻,用户可以把分析那几行直接抄进自己的指标里,快速验证自写指标到底怎么界定行情状态,也留了个基准方便往后改思路。外汇和贵金属波动大、杠杆高,这类实时聚类只是辅助,信号错了止损要快。

别急着下结论

这套聚类工具包在 MT5 里跑通后,下一步自然是把它套到 MACD、RSI 这类标准指标上重新看信号结构——原作者提到会在后续文章演示,思路是借聚类把指标的阈值解释从「固定线」换成「密度分区」。 有读者在评论区问过 MT4 能不能直接用,原作者回得很直白:必须改,因为 MT4 和 MT5 在时序函数和订单结构上有差异,他给了几篇讲移植的文章链接,但反向从 MT5 迁回 MT4 也得自己动手。 也有人不买账,认为聚类只是把肉眼能看的横盘水平换了个算法壳,花钱扩样本纯属浪费。这类质疑本身值得带进回测里验:用 Clustering.zip(7.59 KB)在 EURUSD 的 H1 上跑一百根 K 线,对比裸水平线和聚类带的假突破次数,比空争有用。 外汇和贵金属杠杆高,任何指标重构都只是概率倾斜,开 MT5 加载附件自己数一遍再决定信不信。

常见问题

看日志里斜率聚类的分位区间:落在中间密集带大概率是横盘,偏离到上下尾部分位则倾向趋势,可用分位抽样基准对照。
日线分簇显示盈亏随斜率梯度拉开,顺梯度方向可给更高权重,但外汇高风险,仅作概率参考不押满仓。
小布可接入品种页自动跑半仓聚类,把尾部膨胀和分位打印可视化,你只管看结论和调参数。
周期粒度不同导致采样簇中心偏移,M15尾部膨胀更明显,日线更平滑,验证时分开看别混用。
先核对分位抽样基准和横盘边界打印,M15尾部膨胀会吃掉回报,可下调仓位或换分簇阈值再测。