数据处理的分组方法:在MQL5中实现组合算法·综合运用
◍ MULTI组合选择类的超参陷阱
multi.mqh里给出了组合选择算法的具体实现,核心是一个继承自LinearModel的MULTI类。它的对外接口和老牌的COMBI类很像,都靠fit()把模型塞进数据集,但fit()里挂的超参数明显更多,调起来更费手。 实操里最该盯住的是kBest和pAverage这两个量。kBest控制保留多少候选组合,pAverage决定用多少历史窗口做平均,乱填可能让拟合结果偏得离谱。 MULTI_test.mq5和MULTI_Multivariable_test.mq5两个脚本演示了完整拟合流程。跑完能发现:在简单数据集上,MULTI吐出的多项式和应用COMBI算法得到的那根曲线完全一致——说明底层数学没变,只是组合筛选路径不同。 下面这段是MULTI类的头文件代码,重点看它对bestCombinations的裁剪逻辑: //+------------------------------------------------------------------+
| // | multi.mqh |
|---|---|
| // | Copyright 2024, MetaQuotes Ltd. |
| // | [MQL5官方文档] |
//+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "[MQL5官方文档] #include "linearmodel.mqh" //+------------------------------------------------------------------+
| // | Class implementing combinatorial selection MULTI algorithm |
|---|
//+------------------------------------------------------------------+ class MULTI : public LinearModel { protected: virtual void removeExtraCombinations(void) override { CVector2d realBestCombinations; CVector n; n.push_back(bestCombinations[0][0]); realBestCombinations.push_back(n); bestCombinations = realBestCombinations; } virtual bool preparations(SplittedData &data, CVector &_bestCombinations) override { return (bestCombinations.setAt(0,_bestCombinations) && ulong(level+1) < data.xTrain.Cols()); } void generateCombinations(int n_cols,vector &out[]) override { if(level == 1) { nChooseK(n_cols,level,out); return; } for(int i = 0; i<bestCombinations[0].size(); i++) { for(int z = 0; z<n_cols; z++) { vector comb = bestCombinations[0][i].combination(); double array[]; vecToArray(comb,array); int found = ArrayBsearch(array,double(z)); if(int(array[found])!=z) { array.Push(double(z)); ArraySort(array); comb.Assign(array); 逐行拆解:
#include "linearmodel.mqh":把线性模型基类拉进来,MULTI复用它的训练框架。removeExtraCombinations():只留bestCombinations[0][0]这一组,等于强制剪枝到单一组合。preparations():把传入的_bestCombinations放到第0位,并校验level+1小于训练集列数,否则不跑。generateCombinations():level为1时直接nChooseK生成;否则在已有组合上逐个追加z列,用ArrayBsearch查重,无重复才Push并排序后写回comb。
开MT5把kBest从默认改到3、pAverage降到0.5,跑一遍MULTI_test能直观看到候选集厚度变化。外汇与贵金属市场波动剧烈,此类模型仅作概率参考,实盘高风险。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| multi.mqh | 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">#include "linearmodel.mqh" class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Class implementing combinatorial selection MULTI algorithm | class=class="str">"cmt">//+------------------------------------------------------------------+ class MULTI : class="kw">public LinearModel { class="kw">protected: class="kw">virtual class="type">void removeExtraCombinations(class="type">void) class="kw">override { CVector2d realBestCombinations; CVector n; n.push_back(bestCombinations[class="num">0][class="num">0]); realBestCombinations.push_back(n); bestCombinations = realBestCombinations; } class="kw">virtual class="type">bool preparations(SplittedData &data, CVector &_bestCombinations) class="kw">override { class="kw">return (bestCombinations.setAt(class="num">0,_bestCombinations) && class="type">ulong(level+class="num">1) < data.xTrain.Cols()); } class="type">void generateCombinations(class="type">int n_cols,vector &out[]) class="kw">override { if(level == class="num">1) { nChooseK(n_cols,level,out); class="kw">return; } for(class="type">int i = class="num">0; i<bestCombinations[class="num">0].size(); i++) { for(class="type">int z = class="num">0; z<n_cols; z++) { vector comb = bestCombinations[class="num">0][i].combination(); class="type">class="kw">double array[]; vecToArray(comb,array); class="type">int found = ArrayBsearch(array,class="type">class="kw">double(z)); if(class="type">int(array[found])!=z) { array.Push(class="type">class="kw">double(z)); ArraySort(array); comb.Assign(array);
「组合去重与多变量拟合入口」
这段逻辑先解决 GMDH 多层迭代里的组合复用问题:用 ulong dif=1 做标记,逐行拿 comb.Compare 跟已存结果比对,阈值取 1e0。只要 dif 为 0 就 break,说明该组合已存在,不再重复入栈。 若循环跑完 dif 仍非零,才用 ArrayResize 把 out 扩容 1 个、保留 100 的储备空间,把新 comb 塞进末尾。这一步直接决定候选模型规模,外汇与贵金属序列里滞后阶一多,组合爆炸会拖垮 MT5 回测速度,属于高风险调参区。 MULTI 类的 fit 有两套重载:单序列版本要求 lags>=1,否则 Print 报错并返回 false;多变量版本额外校验 vars.Cols()>=1 且行数必须等于 targets.Size(),维度不对直接退出。默认 testsize=0.5、kBest=3、pAverage=1,意味着训练测试五五开、每层只留 3 个最优。 下面给出原文里的核心片段,可在 MT5 里直接对照跑通:
class="type">ulong dif = class="num">1; for(class="type">uint row = class="num">0; row<out.Size(); row++) { dif = comb.Compare(out[row],class="num">1e0); if(!dif) class="kw">break; } if(dif) { ArrayResize(out,out.Size()+class="num">1,class="num">100); out[out.Size()-class="num">1] = comb; } } } } } class="kw">public: MULTI(class="type">void):LinearModel() { CVector members; bestCombinations.push_back(members); modelName = "MULTI"; } class="type">bool fit(vector &time_series,class="type">int lags,class="type">class="kw">double testsize=class="num">0.5,CriterionType criterion=stab,class="type">int kBest = class="num">3,class="type">int pAverage = class="num">1,class="type">class="kw">double limit = class="num">0.0) { if(lags < class="num">1) { Print(__FUNCTION__," lags must be >= class="num">1"); class="kw">return false; } PairMVXd transformed = timeSeriesTransformation(time_series,lags); SplittedData splited = splitData(transformed.first,transformed.second,testsize); Criterion criter(criterion); if(validateInputData(testsize, pAverage, limit, kBest)) class="kw">return false; class="kw">return GmdhModel::gmdhFit(splited.xTrain, splited.yTrain, criter, kBest, testsize, pAverage, limit); } class="type">bool fit(matrix &vars,vector &targets,class="type">class="kw">double testsize=class="num">0.5,CriterionType criterion=stab,class="type">int kBest = class="num">3,class="type">int pAverage = class="num">1,class="type">class="kw">double limit = class="num">0.0) { if(vars.Cols() < class="num">1) { Print(__FUNCTION__," columns in vars must be >= class="num">1"); class="kw">return false; } if(vars.Rows() != targets.Size()) { Print(__FUNCTION__, " vars dimensions donot correspond with targets"); class="kw">return false; } SplittedData splited = splitData(vars,targets,testsize);
把 GMDH 多变量拟合跑成一条脚本
上面这段脚本把多变量 GMDH 封装类 MULTI 直接挂到 OnStart 里,用 12 个递增整数当样本,验证拟合与预测链路是否通。外汇与贵金属市场波动剧烈、杠杆高风险极大,这类合成序列仅用于跑通接口,实盘替换成真实报价前务必自测。 输入参数里 NumLags=2 表示用过去 2 期作滞后特征,NumPredictions=6 要求向外推 6 步,DataSplitSize=0.33 代表 33% 数据留作测试集。critType 选了 stab 准则,NumBest=3 保留每层筛选出的 3 个最优模型,Average=1 不做多次平均。 核心调用是先 multi.fit 用尾段 2 维切片作输入向量,再 multi.predict 生成 6 步外推,最后 Print 出预测值与最优多项式。若 NumPredictions 小于 1 会弹 Alert 并退出,这是脚本里唯一的硬性守卫。 开 MT5 新建脚本,把 #include <GMDH\multi.mqh> 和这段逻辑贴进去,把 tms 换成你自己的收盘价向量,就能在日志里看到预测序列与多项式表达式,据此判断该算法对当前品种的记忆长度是否够用。
Criterion criter(criterion); if(validateInputData(testsize, pAverage, limit, kBest)) class="kw">return false; class="kw">return GmdhModel::gmdhFit(splited.xTrain, splited.yTrain, criter, kBest, testsize, pAverage, limit); } }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+----------------------------------------------------------------------+ class=class="str">"cmt">//| MULTI_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\multi.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">int Average = class="num">1; class="kw">input class="type">int NumBest = class="num">3; class="kw">input class="type">class="kw">double DataSplitSize = class="num">0.33; class="kw">input class="type">class="kw">double critLimit = class="num">0; 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; } MULTI multi; if(!multi.fit(tms,NumLags,DataSplitSize,critType,NumBest,Average,critLimit)) class="kw">return; vector in(class="type">ulong(NumLags),slice,tms,tms.Size()-class="type">ulong(NumLags)); vector out = multi.predict(in,NumPredictions); Print(" predictions ", out); Print(multi.getBestPolynomial()); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| MULTI_Mulitivariable_test.mq5 | class=class="str">"cmt">//| Copyright class="num">2024, MetaQuotes Ltd. |
◍ 用 GMDH 多变量脚本跑通一次预测
MT5 里做多变量非线性拟合,可以借 GMDH 库的 multi.mqh 直接写脚本,不必自己造轮。下面这段脚本把 6 行已知样本丢进 MULTI 类做训练,再用 3 行未见数据验证外推效果。 参数上 DataSplitSize=0.33 表示随机留 33% 样本做测试集,NumBest=3 保留每层筛选出的前 3 个最优模型,critType=stab 选用稳定性准则。Average=1 与 critLimit=0 则保持默认不平滑、不卡阈值。 实跑日志里,输入 [1,2,4] 给出预测 7.000000000000002,[1,5,3] 给 9,[9,1,3] 给 13.00000000000001,与训练集输出量级一致,说明该组多项式在外推上未明显失真。外汇与贵金属品种上套用同类脚本,因跳空与杠杆效应,样本外预测可能偏离较大,属高风险验证行为。 [CODE] 标记内的代码逐行拆解: #property script_show_inputs 让输入参数在运行前弹窗可改; #include <GMDH\multi.mqh> 引入多变量 GMDH 实现; input CriterionType critType = stab 选定模型筛选准则为稳定性; input double DataSplitSize = 0.33 设定测试集占比; input int Average = 1 指定对后代模型不做平均; input int NumBest = 3 每层保留最优 3 个; input double critLimit = 0 不限制准则下限; matrix independent 是 6×3 的自变量样本; vector dependent 是 6 个因变量观测; multi.fit(...) 执行训练,失败直接 return; matrix unseen 是 3×3 的待预测样本; 循环里 unseen.Row(row) 取单行,multi.predict(in,1) 输出单步预测; Print(multi.getBestPolynomial()) 打印最优多项式结构。
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\multi.mqh> class="kw">input CriterionType critType = stab; class="kw">input class="type">class="kw">double DataSplitSize = class="num">0.33; class="kw">input class="type">int Average = class="num">1; class="kw">input class="type">int NumBest = class="num">3; class="kw">input class="type">class="kw">double critLimit = class="num">0; 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}; MULTI multi; if(!multi.fit(independent,dependent,DataSplitSize,critType,NumBest,Average,critLimit)) class="kw">return; matrix unseen = {{class="num">1,class="num">2,class="num">4},{class="num">1,class="num">5,class="num">3},{class="num">9,class="num">1,class="num">3}}; for(class="type">ulong row = class="num">0; row<unseen.Rows(); row++) { vector in = unseen.Row(row); Print("inputs ", in, " prediction ", multi.predict(in,class="num">1)); } Print(multi.getBestPolynomial()); }
「多元回归在 BTCUSD 日线上的实测输出」
把三个自变量丢进日线 BTCUSD 做多元拟合,系数直接被优化器压成 1.000000e+00 的整型权重,截距项 -7.330836e-15 基本等于 0,说明模型在这组样本里倾向于把 x1、x2、x3 等权相加。 单变量对照测试 MULTI_test 吐出一串预测值 [13,14,15,16,17,18.00000000000001],最后一位浮点误差来自双精度累加,实战里可忽略。 外汇与贵金属以外的加密品种波动更极端,这类线性外推在高波动区容易失效,上 MT5 跑同款 EA 前建议先缩样本周期验证过拟合。
PP class="num">0 class="num">18:class="num">24:class="num">28.812 MULTI_Mulitivariable_test(BTCUSD,D1) y= class="num">1.000000e+00*x1 + class="num">1.000000e+00*x2 + class="num">1.000000e+00*x3 - class="num">7.330836e-15 DP class="num">0 class="num">18:class="num">25:class="num">04.454 MULTI_test(BTCUSD,D1) predictions [class="num">13,class="num">14,class="num">15,class="num">16,class="num">17,class="num">18.00000000000001] MH class="num">0 class="num">18:class="num">25:class="num">04.454 MULTI_test(BTCUSD,D1) y= class="num">1.000000e+00*x1 + class="num">2.000000e+00
拿比特币日线跑一遍GMDH预测脚本
把 GMDH 用于比特币每日收盘价的预测,核心不是算法本身,而是那支 GMDH_Price_Model 脚本怎么喂数据。脚本不挑品种,BTC 只是演示,换个图表品种把 SetSymbol 留空也能跑,时间周期由 tf 控制,默认就是日线 PERIOD_D1。 脚本开头用 iBarShift 把训练起止和样本外起止换算成 bar 偏移,任何一次返回负值就直接 Print 错误并 return,避免后面数组越界。训练集长度由 (trainstart - trainstop) + 1 算出,样本外同理,两块任一小于等于 0 就报 Invalid inputs 退出。 参数里几个值直接影响模型形态:Numlags=3 表示用前 3 根收盘做滞后特征;modelType 可在 Combi 与 MIA 间切,MIA 下 polyType 决定新变量怎么构造;critLimit=0 代表外部标准只要不退化就继续叠层。DataSplitSize=0.2 意味着 20% 输入数据拿去评估候选模型。 下载训练数据用 prices.CopyRates 带重试,最多 try=10 次,还失败就放弃。跑完用 NumTestSamplesPlot=20 把最近 20 个样本外点画成预测 vs 实际的对照,肉眼看 Combi 和 MIA 两张图的偏离程度,判断哪类外部标准更贴合 BTC 的波动结构。加密货币高波动,样本外吻合不代表后续概率不变,开 MT5 加载脚本改改日期就能验证。
class=class="str">"cmt">//--- class="kw">input parameters class="kw">input class="type">class="kw">string SetSymbol=""; class="kw">input ENUM_GMDH_MODEL modelType = Combi; class="kw">input class="type">class="kw">datetime TrainingSampleStartDate=D&class="macro">#x27;class="num">2019.12.class="num">31&class="macro">#x27;; class="kw">input class="type">class="kw">datetime TrainingSampleStopDate=D&class="macro">#x27;class="num">2022.12.class="num">31&class="macro">#x27;; class="kw">input class="type">class="kw">datetime TestSampleStartDate = D&class="macro">#x27;class="num">2023.01.class="num">01&class="macro">#x27;; class="kw">input class="type">class="kw">datetime TestSampleStopDate = D&class="macro">#x27;class="num">2023.12.class="num">31&class="macro">#x27;; class="kw">input ENUM_TIMEFRAMES tf=PERIOD_D1; class=class="str">"cmt">//time frame class="kw">input class="type">int Numlags = class="num">3; class="kw">input CriterionType critType = stab; class="kw">input PolynomialType polyType = linear_cov; class="kw">input class="type">int Average = class="num">10; class="kw">input class="type">int NumBest = class="num">10; class="kw">input class="type">class="kw">double DataSplitSize = class="num">0.2; class="kw">input class="type">class="kw">double critLimit = class="num">0; class="kw">input class="type">ulong NumTestSamplesPlot = class="num">20; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Script program start function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnStart() { class=class="str">"cmt">//get relative shift of IS and OOS sets class="type">int trainstart,trainstop, teststart, teststop; trainstart=iBarShift(SetSymbol!=""?SetSymbol:NULL,tf,TrainingSampleStartDate); trainstop=iBarShift(SetSymbol!=""?SetSymbol:NULL,tf,TrainingSampleStopDate); teststart=iBarShift(SetSymbol!=""?SetSymbol:NULL,tf,TestSampleStartDate); teststop=iBarShift(SetSymbol!=""?SetSymbol:NULL,tf,TestSampleStopDate); class=class="str">"cmt">//check for errors from ibarshift calls if(trainstart<class="num">0 || trainstop<class="num">0 || teststart<class="num">0 || teststop<class="num">0) { Print(ErrorDescription(GetLastError())); class="kw">return; } class=class="str">"cmt">//---set the size of the sample sets size_observations=(trainstart - trainstop) + class="num">1 ; size_outsample = (teststart - teststop) + class="num">1; class=class="str">"cmt">//---check for class="kw">input errors if(size_observations <= class="num">0 || size_outsample<=class="num">0) { Print("Invalid inputs "); class="kw">return; } class=class="str">"cmt">//---download insample prices for training class="type">int try = class="num">10; while(!prices.CopyRates(SetSymbol,tf,COPY_RATES_CLOSE,TrainingSampleStartDate,TrainingSampleStopDate) && try) { try --; if(!try)
◍ 样本外回测与三类 GMDH 模型切换
这段逻辑紧接在样本内价格拉取之后,先以 try=10 为上限循环拷贝样本外行情:若 CopyRates 对 testprices 失败则 Sleep(5000) 重试,耗尽 10 次仍取不到就 Print 错误并 return,避免脏数据进模型。 模型分支用 switch(modelType) 处理三种 GMDH 变体——Combi、Mia、Multi,各自先 fit 训练,失败即退出,成功则 Print 最优多项式并调用 MakePredictions 生成 predictions。默认分支直接报「Invalid GMDH model type」后返回。 绘图样本量由 NumTestSamplesPlot 控制,若未设或越界则回退到 20 或 testprices.Rows()-Numlags;随后用 vector slice 从 testprices 与 predictions 切出 testsample、testpredictions、dates 三段,长度上限 100。外汇与贵金属行情跳空频繁,样本外复制失败率可能偏高,开 MT5 把 try 上限调到 20 观察重试日志更稳。
{
Print("error copying to prices ",GetLastError());
class="kw">return;
}
Sleep(class="num">5000);
class=class="str">"cmt">//---download out of sample prices testing
try
= class="num">10;
while(!testprices.CopyRates(SetSymbol,tf,COPY_RATES_CLOSE|COPY_RATES_TIME|COPY_RATES_VERTICAL,TestSampleStartDate,TestSampleStopDate) && try)
{
try
--;
if(!try)
{
Print("error copying to testprices ",GetLastError());
class="kw">return;
}
Sleep(class="num">5000);
}
class=class="str">"cmt">//--- train and make predictions
class="kw">switch(modelType)
{
case Combi:
{
COMBI combi;
if(!combi.fit(prices,Numlags,DataSplitSize,critType))
class="kw">return;
Print("Model ", combi.getBestPolynomial());
MakePredictions(combi,testprices.Col(class="num">0),predictions);
}
class="kw">break;
case Mia:
{
MIA mia;
if(!mia.fit(prices,Numlags,DataSplitSize,polyType,critType,NumBest,Average,critLimit))
class="kw">return;
Print("Model ", mia.getBestPolynomial());
MakePredictions(mia,testprices.Col(class="num">0),predictions);
}
class="kw">break;
case Multi:
{
MULTI multi;
if(!multi.fit(prices,Numlags,DataSplitSize,critType,NumBest,Average,critLimit))
class="kw">return;
Print("Model ", multi.getBestPolynomial());
MakePredictions(multi,testprices.Col(class="num">0),predictions);
}
class="kw">break;
class="kw">default:
Print("Invalid GMDH model type ");
class="kw">return;
}
class=class="str">"cmt">//---
class=class="str">"cmt">//---
class="type">ulong TestSamplesPlot = (NumTestSamplesPlot>class="num">0)?NumTestSamplesPlot:class="num">20;
class=class="str">"cmt">//---
if(NumTestSamplesPlot>=testprices.Rows())
TestSamplesPlot = testprices.Rows()-Numlags;
class=class="str">"cmt">//---
vector testsample(class="num">100,slice,testprices.Col(class="num">0),Numlags,Numlags+TestSamplesPlot-class="num">1);
vector testpredictions(class="num">100,slice,predictions,class="num">0,TestSamplesPlot-class="num">1);
vector dates(class="num">100,slice,testprices.Col(class="num">1),Numlags,Numlags+TestSamplesPlot-class="num">1);
class=class="str">"cmt">//---
class=class="str">"cmt">//Print(testpredictions.Size(), ":", testsample.Size());
class=class="str">"cmt">//---
class="type">class="kw">double y[], y_hat[];
class=class="str">"cmt">//---「把预测结果画上图才算跑通」
模型算完不是终点,得把测试集预测值和真实值同时落到图表上,肉眼看偏离才有意义。下面这段就是收尾时把三个向量转成数组并触发绘制的逻辑。
if(vecToArray(testpredictions,y_hat) && vecToArray(testsample,y) && vecToArray(dates,xaxis)) { PlotPrices(y_hat,y); } class=class="str">"cmt">//--- ChartRedraw(); }
if(vecToArray(testpredictions,y_hat) && vecToArray(testsample,y) && vecToArray(dates,xaxis)) { PlotPrices(y_hat,y); } class=class="str">"cmt">//--- ChartRedraw(); }
GMDH 工具箱的落地路径
GMDH 的组合算法给复杂系统建模提供了一套数据驱动的归纳框架,在金融时间序列里确有可挖的潜力。但原生实现碰到大型数据集就掉速,组合选择算法只缓解一部分,加可调参数能提速却把调优负担转嫁回使用者。
附件里 Mql5\include\GMDH\ 下的头文件是可直接挂载的:gmdh.mqh 定义基类,linearmodel.mqh 是 COMBI 与 MULTI 的共同底座,combi.mqh 和 multi.mqh 各自实现两类模型,mia.mqh 跑多层迭代。脚本侧 COMBI_test.mq5 仅 1.59 KB,能最快验证单变量时序建模。
外汇与贵金属行情高波动、高杠杆,用这套做预测仅作概率参考,实盘前务必在 MT5 用 GMDH_Price_Model.mqh(8.41 KB)跑一遍自己的品种,看回测漂移再决定参数粒度。