数据处理的分组方法:在MQL5中实现组合算法·进阶篇
◍ 维度校验失败就直接退出拟合
在 GMDH 模型封装的 fit 入口里,第一步不是算系数,而是先卡维度。若特征矩阵 vars 与目标向量 targets 的维度对不上,代码会先 Print 出 ' vars dimensions donot correspond with targets ',随后 return false,整个拟合流程当场中止,不会往下走 splitData。 这段逻辑里有个容易被忽略的默认值设定:pAverage 写死为 1,limit 初始化成 0,kBest 直接等于 pAverage。也就是说在不传参干预时,模型默认只用一层滑动平均、不限制复杂度、选最优的 1 个候选。 调用 validateInputData(testsize, pAverage, limit, kBest) 若返回真,同样 return false。只有全部校验过了,才把切分后的训练集丢进 GmdhModel::gmdhFit。开 MT5 把 vars 行数故意改成和 targets 不一致,能复现那条维度报错并打印函数名。
Print(__FUNCTION__, " vars dimensions donot correspond with targets"); class="kw">return false; } SplittedData splited = splitData(vars,targets,testsize); Criterion criter(criterion); class="type">int pAverage = class="num">1; class="type">class="kw">double limit = class="num">0; class="type">int kBest = pAverage; if(validateInputData(testsize, pAverage, limit, kBest)) class="kw">return false; class="kw">return GmdhModel::gmdhFit(splited.xTrain, splited.yTrain, criter, kBest, testsize, pAverage, limit); }
COMBI 类怎么跑通一次拟合
把 COMBI 类接到数据集上的写法和前一篇的 MIA 类几乎一致:建实例、调 fit(),区别只在底层搜索逻辑。官方给的 COMBI_test.mq5 与 COMBI_Multivariable_test.mq5 两个脚本,分别用时间序列和 multivariable 数据演示了同一套调用流程。 在时间序列脚本输出里,COMBI 产出的多项式明显比 MIA 的更短,形状贴近序列本身,这是它线性特性的直接反映。多变量脚本让算法从「输入三个数、输出其和」的样例里反推规律,COMBI 成功推断出求和结构,而 MIA 模型相对臃肿。 穷举搜索是 COMBI 的双刃剑。正面看,遍历所有输入组合(候选数 = 2^m - 1,m 为输入变量数)更可能找到最优多项式;反面看,m 一大训练耗时就会飙升。实盘若拿 10 个因子进 COMBI,候选模型就是 1023 个,MT5 单核跑起来可能卡到没法忍。 遇到变量多的情况,后续的组合选择算法(Combinatorial Selective)就是为砍搜索空间而生的,思路留到下一节拆。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| COMBI_test.mq5 | class=class="str">"cmt">//| Copyright class="num">2024, MetaQuotes Ltd. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2024, MetaQuotes Ltd." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#class="kw">property script_show_inputs class="macro">#include <GMDH\combi.mqh> class="kw">input class="type">int NumLags = class="num">2; class="kw">input class="type">int NumPredictions = class="num">6; class="kw">input CriterionType critType = stab; class="kw">input class="type">class="kw">double DataSplitSize = class="num">0.33; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Script program start function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnStart() { class=class="str">"cmt">//--- vector tms = {class="num">1,class="num">2,class="num">3,class="num">4,class="num">5,class="num">6,class="num">7,class="num">8,class="num">9,class="num">10,class="num">11,class="num">12}; if(NumPredictions<class="num">1) { Alert("Invalid setting for NumPredictions, has to be larger than class="num">0"); class="kw">return; } COMBI combi; if(!combi.fit(tms,NumLags,DataSplitSize,critType)) class="kw">return; class="type">class="kw">string modelname = combi.getModelName()+"_"+EnumToString(critType)+"_"+class="type">class="kw">string(DataSplitSize); combi.save(modelname+".json"); vector in(class="type">ulong(NumLags),slice,tms,tms.Size()-class="type">ulong(NumLags)); vector out = combi.predict(in,NumPredictions); Print(modelname, " predictions ", out); Print(combi.getBestPolynomial()); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| COMBI_Multivariable_test.mq5 | class=class="str">"cmt">//| Copyright class="num">2024, MetaQuotes Ltd. |
「用组合算法在MT5里跑多变量拟合」
MT5 自带的 GMDH\combi.mqh 头文件提供了 COMBI 类,能在脚本里直接做多元多项式回归与模型选择。下面这段脚本把 6 行 3 列自变量和 6 个因变量喂进去,按 0.33 比例切分训练/验证集,用稳定准则 stab 拟合。 核心参数就两个:critType 控制模型选择准则(这里用 stab),DataSplitSize 设 0.33 意味着约三分之一样本留作验证。fit 失败直接 return,成功后会把模型存成 JSON 方便下次加载。 脚本末尾对 3 组未见过的输入做预测,日志里打出 inputs [8,6,4] prediction 18.00000000000001,说明拟合出的多项式倾向是 y = 1*x1 + 2,基本只认了第一列变量。外汇和贵金属市场噪声大、杠杆高,这类拟合在外盘实盘上过拟合概率不低,开 MT5 用自己的品种复跑才能看清泛化能力。 把代码贴进脚本文件,改 independent / dependent 为你自己的K线特征与标签,就能在策略测试器外快速验证一组因子的线性可解释性。
class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2024, MetaQuotes Ltd." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#class="kw">property script_show_inputs class="macro">#include <GMDH\combi.mqh> class="kw">input CriterionType critType = stab; class="kw">input class="type">class="kw">double DataSplitSize = class="num">0.33; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Script program start function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnStart() { class=class="str">"cmt">//--- matrix independent = {{class="num">1,class="num">2,class="num">3},{class="num">3,class="num">2,class="num">1},{class="num">1,class="num">4,class="num">2},{class="num">1,class="num">1,class="num">3},{class="num">5,class="num">3,class="num">1},{class="num">3,class="num">1,class="num">9}}; vector dependent = {class="num">6,class="num">6,class="num">7,class="num">5,class="num">9,class="num">13}; COMBI combi; if(!combi.fit(independent,dependent,DataSplitSize,critType)) class="kw">return; class="type">class="kw">string modelname = combi.getModelName()+"_"+EnumToString(critType)+"_"+class="type">class="kw">string(DataSplitSize)+"_multivars"; combi.save(modelname+".json"); matrix unseen = {{class="num">8,class="num">6,class="num">4},{class="num">1,class="num">5,class="num">3},{class="num">9,-class="num">5,class="num">3}}; for(class="type">ulong row = class="num">0; row<unseen.Rows(); row++) { vector in = unseen.Row(row); Print("inputs ", in, " prediction ", combi.predict(in,class="num">1)); } Print(combi.getBestPolynomial()); } class=class="str">"cmt">//+------------------------------------------------------------------+
◍ 回测日志里的多变量拟合残差
在 BTCUSD 的 D1 周期上跑 COMBI_Multivariable_test,输入 [1,5,3] 时模型给出预测值 9,而拟合式为 y = 1.000000e+00*x1 + 1.000000e+00*x2 + 1.000000e+00*x3 - 7.330836e-15,常数项几乎为零,说明该组变量近似纯线性叠加。 换 MIA_Multivariable_test 看同一品种,输入 [1,2,4] 预测 6.999999999999998,输入 [9,1,3] 预测 13.00000000000001,三个子模型 f1_1、f1_2、f1_3 的系数差异很大:f1_3 里 x1 和 x3 都是负向且倍数到 1.5,和 f1_1 的正向 x1 系数完全反向。这种分裂意味着单周期 D1 上不同变量组合对价格的解释力不稳定。 在 Step Index 500 的 M1 测试里,MIA_stab_0.33_1_0.0 输出了从 13.0 到 18.0 连续六步预测,对应拟合式 y = -9.34e-01*x1 + 1.93e+00*x2 + 3.87e-16*x1*x2 + 1.07e+00,交叉项系数小到可忽略,实质是 x2 主导。外汇和贵金属用这类多变量模型前,要先在 MT5 策略测试器里复跑这段日志对应的 EA,确认残差数量级;加密与贵金属均属高风险,拟合优不代表实盘倾向。
从日志看多层变量回归的展开过程
在 BTCUSD 的 D1 周期上跑多变量测试,同一时间戳 14:52:59.698 打出了一串变量代换记录,能直接看出输入特征怎样被逐层线性重组。 第一层里 f2_1 由 f1_1 乘 1.555556 减去 f1_3 乘 0.666667 再加 0.666667 得到,f2_2 则吃进 f1_2 的 1.620805 倍并扣除 f1_3 的 0.738255 倍。f2_3 的系数更极端,f1_1 拉到 3.019608 倍、f1_2 压到 -2.029412 倍,说明这一支对前两原始因子做了强对冲。 第二层 f3_1 几乎就是 f2_1 的原样拷贝,f2_3 的系数仅 -3.73e-15,浮点噪声级别可忽略;f3_2 保留 f2_2 的 0.834 倍并掺入 f2_3 的 0.171 倍。最后输出 y 等于 f3_1 乘 1.0,其余项都是 1e-15 量级的舍入残留。 这种日志适合在 MT5 策略测试器里核对自家回归链路有没有被优化器偷偷丢掉维度;crypto 与外汇贵金属皆属高波动品种,系数敏感,复盘时别把 1e-15 当有效信号。
IP class="num">0 class="num">14:class="num">52:class="num">59.698 MIA_Multivariable_test(BTCUSD,D1) f2_1 = class="num">1.555556e+00*f1_1 - class="num">6.666667e-01*f1_3 + class="num">6.666667e-01 ER class="num">0 class="num">14:class="num">52:class="num">59.698 MIA_Multivariable_test(BTCUSD,D1) f2_2 = class="num">1.620805e+00*f1_2 - class="num">7.382550e-01*f1_3 + class="num">7.046980e-01 ES class="num">0 class="num">14:class="num">52:class="num">59.698 MIA_Multivariable_test(BTCUSD,D1) f2_3 = class="num">3.019608e+00*f1_1 - class="num">2.029412e+00*f1_2 + class="num">5.882353e-02 NH class="num">0 class="num">14:class="num">52:class="num">59.698 MIA_Multivariable_test(BTCUSD,D1) CN class="num">0 class="num">14:class="num">52:class="num">59.698 MIA_Multivariable_test(BTCUSD,D1) f3_1 = class="num">1.000000e+00*f2_1 - class="num">3.731079e-15*f2_3 + class="num">1.155175e-14 GP class="num">0 class="num">14:class="num">52:class="num">59.698 MIA_Multivariable_test(BTCUSD,D1) f3_2 = class="num">8.342665e-01*f2_2 + class="num">1.713326e-01*f2_3 - class="num">3.359462e-02 DO class="num">0 class="num">14:class="num">52:class="num">59.698 MIA_Multivariable_test(BTCUSD,D1) OG class="num">0 class="num">14:class="num">52:class="num">59.698 MIA_Multivariable_test(BTCUSD,D1) y = class="num">1.000000e+00*f3_1 + class="num">3.122149e-16*f3_2 - class="num">1.899249e-15
「MULTI组合选择怎么绕过穷举」
MULTI 是对 COMBI 单层穷举思路的效率改进,借用了 GMDH 多层筛选的程序逻辑,不再把所有变量组合都跑一遍。它本质是把原本单层的 COMBI 改成了多层递进结构。 第一层先估算所有只含一个输入变量的模型,按外部准则挑出最优的送进下一层;后续每层再塞进不同的新变量试图改良候选模型。是否继续叠层,看输出精度有没有提升、以及还有没有未进模型的输入变量可用,所以理论最大层数等于输入变量总数。 这种跳着评估的方式大概率能躲开穷举,但代价是可能漏掉真正最优的多项式描述。外汇与贵金属数据噪声大、过拟合风险高,训练阶段就得主动放更多候选模型进去比。 每层评估的候选模型数量是个要手调的旋钮:调小了省时间但容易错失结构,调大了才更贴近最优多项式,但计算成本直线上升。