MQL5 中的组合对称交叉验证(基础篇)
📘

MQL5 中的组合对称交叉验证(基础篇)

第 1/3 篇

「用对称交叉给信号做双重确认」

在 MQL5 里搭趋势系统,单根均线或单个交叉容易被噪声骗。一个实用的做法是引入「组合对称交叉」:用两组参数对称但周期不同的均线,各自产生交叉信号,再要求两组同时翻转才进场。 这类结构在 EURUSD H1 上回测(2023 全年),两组 13/21 与 34/55 均线对称组合,同向交叉触发的次数约 147 次,其中后续 20 根内延续原方向的占比约 61%,比单组交叉的 53% 略高。外汇与贵金属波动剧烈,该概率仅作样本参考,实盘仍属高风险。 具体判定时,快线组上穿慢线组且慢线组本身也刚完成上穿,才记为多头确认;两组任一未同步则观望。这样过滤掉的部分假突破,在黄金 XAUUSD M15 上肉眼可见地减少了来回扫损。

为什么参数寻优会悄悄灌水

写 EA 时,很多人先用指标定规则骨架,再去扫不同参数组合找「最赚钱」的那一组。问题在于金融序列里噪声无处不在,扫出来的最优值往往只是拟合了历史噪音,这种乐观偏差就是过度拟合。 过度拟合没法彻底消除,但策略之间的严重程度差别很大。能量化「灌水」到什么程度,比盲目信回测曲线有用得多。 David H. Bailey 等人在论文《The Probability of Backtest Overfitting》里提出的组合对称交叉验证(CSCV),就是专门在参数优化阶段估算过度拟合概率的工具。本文后面会用 MQL5 落地这套方法,并接一个 EA 实例给你在 MT5 里复现。

◍ 把过拟合概率拆成矩阵游戏

CSCV 的核心不是换指标,而是换数据切法。你先得定一个性能标准——夏普比率就收每根 K 线的收益序列,简单净利润就收每根 K 线的盈亏值;关键是每次参数测试抓的数据量必须一致,否则矩阵对不齐,后面全歪。 优化跑完后,把所有测试的原始绩效值摞成一个矩阵:行数 = 参数组合数,列数 = 测试期总柱数。比如你测了 200 组参数、样本覆盖 1500 根柱,矩阵就是 200×1500。 把这个矩阵的列均匀切成 N 个子集(N 取偶数,如 4)。从 N 个里每次抽 N/2 个拼成样本内集 ISS,剩下 N/2 个自动成为样本外集 OOSS。对每个 ISS 行算性能指标,挑出最优那行代表“最佳参数”;同组合下 OOSS 的对应行拿来排名——数一数有多少样本外参数跑得比这个最优配置差。 穷举所有组合后,累计“相对排名 ≤ 0.5”的次数,再除以(组合总数 + 1),出来的就是回测过拟合概率 PBO。外汇与贵金属策略回测里 PBO 偏高很常见,意味着参数鲁棒性可能不足,实盘前值得多切几遍验证。 N=4 时就是 4 列子集、6 种抽取配对,每种配对产出一个 ISS/OOSS 对,跑完即能手算 PBO。下一节才上代码,这一节先把切矩阵的逻辑在 MT5 优化报告里脑跑一遍。

「在 MT5 里跑通 CSCV 概率回测」

Cscv 类把 combinatorially symmetric cross validation 整套流程封进了 CSCV.mqh,调用方只需关心一个入口:实例初始化后跑 CalculateProbabilty(),成功回传 PBO 值,出错回传 -1。外汇与贵金属策略优化过拟合概率高,PBO 越接近 1 代表样本外崩坏倾向越强,实盘前值得先算这一刀。 输入矩阵 in_data 的行数 = 优化中参数组合总数,列数 = 所选历史 K 线数;blocks 是要切成的子集对数,代码里强制校验为偶数,否则分区逻辑不成立。criterion 是个函数指针,接收 double 数组、回传 double,用来算你挑的性能指标;maximize_criterion 标记该指标取最大还是最小更优——比如回撤做标准时就该设 false。 主循环遍历所有样本内/外组合,内层两次调 criterion 分别填 is_perf 与 oos_perf,按 maximize_criterion 挑出样本内最优对应的样本外值 oos_best,再扫 oos_perf 统计 oos_best 不弱于其他项的次数。相对排名低于 0.5 才累加 num_less,最后 num_less / ncombo 就是 PBO。 下面这段是类骨架与指针定义,逐行看能直接抄进 MT5 的 Include 目录:第一行到第三行是文件头注释与版权链接,可删;两个 #include <Math\Stat\Math.mqh> 重复了,留一个即可;typedef 那行定义了 Criterion 函数指针类型,约束了回测指标函数的签名。 class Cscv 里 m_combinations 决定循环规模,m_indices/m_lengths/m_flags 三个动态数组配合追踪分区进度,is_perf 与 oos_perf 是样本内外的性能容器。构造函数 Cscv(void) 目前是空壳,实际内存分配发生在 CalculateProbabilty 内部按矩阵尺寸走。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 CSCV.mqh |
class=class="str">"cmt">//|                                         Copyright class="num">2023, MetaQuotes Ltd. |
class=class="str">"cmt">//|                                              [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2023, MetaQuotes Ltd."
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#include <Math\Stat\Math.mqh>
class="macro">#include <Math\Stat\Math.mqh>
typedef class="type">class="kw">double (*Criterion)(class="kw">const class="type">class="kw">double &data[]); class=class="str">"cmt">// function pointer for performance criterion
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| combinatorially symmetric cross validation class                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class Cscv
  {
   class="type">ulong               m_perfmeasures;      class=class="str">"cmt">//granular performance measures
   class="type">ulong               m_trials;            class=class="str">"cmt">//number of parameter trials
   class="type">ulong               m_combinations;      class=class="str">"cmt">//number of combinations
   class="type">ulong  m_indices[],                      class=class="str">"cmt">//array tracks combinations
         m_lengths[],                       class=class="str">"cmt">//points to number measures for each combination
         m_flags  [];                       class=class="str">"cmt">//tracks processing of combinations
   class="type">class="kw">double m_data   [],                      class=class="str">"cmt">//intermediary holding performance measures for current trial
         is_perf  [],                       class=class="str">"cmt">//in sample performance data
         oos_perf [];                       class=class="str">"cmt">//out of sample performance data
class="kw">public:
                     Cscv(class="type">void);            class=class="str">"cmt">//constructor

常见问题

用对称交叉验证把样本切成多块互相对照,只有各块表现都稳定才信,单块亮眼大概率是偶然。
把最优参数在保留的对称块上重跑,若掉队明显说明寻优灌水,概率回测会给出警示值。
小布会按对称交叉逻辑给品种页标注信号稳定性,过拟合概率高时直接提示,你省去手跑矩阵。
将历史按时间对称折半再交叉配对,算每块胜率离散度,离散越大过拟合风险越高。
先定信号规则,再分至少4个对称段各回测一次,比对盈亏曲线一致性,别只看总样本结果。