MQL5中的逐步特征选择·综合运用
🧮

MQL5中的逐步特征选择·综合运用

(3/3)·传统逐步法在外汇特征筛选里漏掉互补组合与吃噪声,这篇给完整解法

偏理论进阶 第 3/3 篇
做特征筛选时,很多人把单独不显著的指标直接丢掉,却没意识到它和特定互补变量组合后预测力会跳变。另一常见坑是把样本内拟合当标准,不断加指标直到模型吃进市场噪声。换一种能测组合贡献的思路,样本外稳健性才可能回来。

特征子集拷贝与迭代上限的取法

在变量选择流程里,每一轮迭代都要从全局索引池里截取一段塞进 selectedvars。下面这段逻辑用 ArrayCopy 按三角形数列偏移来搬数据:第 iteration_index 轮搬 iteration_index+1 个元素,起始位置是 int((iteration_index*(iteration_index+1))/2)。

MQL5 / C++
if(ArrayCopy(selectedvars,m_var_indices,class="num">0,class="type">int((iteration_index*(iteration_index+class="num">1))/class="num">2),class="type">int(iteration_index+class="num">1))>class="num">0)
   class="kw">return true;
else
   {
   Print(__FUNCTION__, " ArrayCopy error ", GetLastError());
   class="kw">return class="kw">false;
   }
}
class=class="str">"cmt">//+---------------------------------------------------------------------+
class=class="str">"cmt">//| get maximum number of iterations cycled through in selection process|
class=class="str">"cmt">//+---------------------------------------------------------------------+
class="type">class="kw">ulong            getNumFeatureSets(class="type">void)
  {
   class="kw">return m_decision_matrix.Rows();
  }
逐行拆解:第1行 ArrayCopy 把 m_var_indices 中从三角数偏移处开始的 iteration_index+1 个索引拷入 selectedvars,返回值大于0表示拷贝成功;第2行成功就返回 true。第4行起走 else 分支,第5行用 Print 打出函数名、错误提示和 GetLastError() 码便于排查,第6行返回 false 中断本轮。 getNumFeatureSets 直接返回 m_decision_matrix.Rows(),也就是决策矩阵行数,它等于选择过程已循环过的特征集总数。实盘前在 MT5 里打印这个函数返回值,就能确认特征选择跑了多大一轮、内存里的矩阵规模是否超出预期。外汇与贵金属波动剧烈,这类矩阵规模失控可能拖慢 EA 执行,属高风险操作环节。

MQL5 / C++
if(ArrayCopy(selectedvars,m_var_indices,class="num">0,class="type">int((iteration_index*(iteration_index+class="num">1))/class="num">2),class="type">int(iteration_index+class="num">1))>class="num">0)
   class="kw">return true;
else
   {
   Print(__FUNCTION__, " ArrayCopy error ", GetLastError());
   class="kw">return class="kw">false;
   }
}
class=class="str">"cmt">//+---------------------------------------------------------------------+
class=class="str">"cmt">//| get maximum number of iterations cycled through in selection process|
class=class="str">"cmt">//+---------------------------------------------------------------------+
class="type">class="kw">ulong            getNumFeatureSets(class="type">void)
  {
   class="kw">return m_decision_matrix.Rows();
  }

「用逐步选择给线性-二次回归挑特征」

线性-二次回归在普通线性回归基础上加了自变量的平方项,方程形如 y = β0 + β1·x + β2·x² + ε,靠 β2 决定曲线开口朝向(β2>0 向上、β2<0 向下)。它适合捕捉抛物线型关系,MT5 里用 OLS.mqh 的普通最小二乘类就能跑。 StepWiseFeatureSelection_Demo.mq5 脚本先造了一个 100×10 的随机矩阵(元素 0~1),把第 0、4、5、6 列求和作为目标向量,其余列当候选预测器。CStepwise 类继承基类,fitModel() 排除交叉验证区间构建设计矩阵,evalModel() 用留出样本算 SSE 衡量拟合。 默认参数首跑时没开 MCP 测试,日志只有一列 SSE 标准值,随特征加入从低走到最优值 1,算法在附加特征不再提升性能时终止。第二次跑加了 100 次排列的 MCP 测试,输出多列含 p 值,≤0.05 说明该特征不太可能靠偶然入选。 把 MinNumVars 提到 10,算法会搜更大特征组合,可能挖出小子集漏掉的非线性交互,但运行时间明显变长。代码里若 MinNumVars/MaxNumVars 超候选总数(本例 10)会自动截断防错配。MCP 排列数和交叉验证折数是拖慢速度的主因,每多一折就要多一轮全量拟合。 外汇与贵金属市场高波动、高杠杆,此类特征选择仅用于历史数据建模验证,实盘信号有效性须自行回测确认。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|    fit the OLS model                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">virtual class="type">bool      fitModel(class="type">class="kw">ulong num_preds,class="type">class="kw">ulong row_start,class="type">class="kw">ulong row_stop,vector& targets)
  {
   class="type">int k1,k2;
   class="type">class="kw">ulong nvars = num_preds + num_preds * (num_preds+class="num">1)/class="num">2;
   class="type">class="kw">ulong ntrain = m_samples - (row_stop - row_start);
   vector dependent(ntrain);
   matrix independent(ntrain,nvars+class="num">1);
   ntrain = class="num">0;
   for(class="type">class="kw">ulong sample=class="num">0; sample<m_samples; sample++)
     {
      if(sample>=row_start && sample<row_stop)
        class="kw">continue;
      k1=class="num">0;
      k2=class="num">1;
      for(class="type">class="kw">ulong var=class="num">0; var<nvars; var++)
        {
         if(var<num_preds)
           independent[ntrain][var]=m_data[sample][m_trial_vars[var]];

◍ 二阶特征构造与样本外校验

这段逻辑在训练集填充之外,还把每个 trial 变量的平方项和两两交叉项塞进 independent 矩阵:当 var 小于 num_preds 时是原始值,小于 num_preds*2 时取平方,其余位置由 k1、k2 双指针滑出 m_trial_vars[k1]*m_trial_vars[k2] 的乘积。 nvars 的总数被定为 num_preds + num_preds*(num_preds+1)/2,例如 5 个候选变量会展开成 5+15=20 维特征,交叉项循环里 k2 触到 num_preds 就推进 k1 并重置 k2=k1+1,保证不重复配对。 evalModel 先用 m_ols.Loglikelihood()==EMPTY_VALUE 拦掉拟合失败,再对 row_start 到 row_stop 的每一行重建同样的 20 维(以 5 变量为例)row 向量做预测,累加 error。样本外误差若明显大于训练期,说明二阶项过拟合,外汇与贵金属行情里这种非线性膨胀尤其要警惕——杠杆品种的高波动会让回测甜头迅速变实盘苦头。 把 evalModel 的 row 重建段直接抄进 MT5 脚本,改 num_preds 跑一遍 EURUSD 的 H1 样本,能立刻看出交叉项到底贡献了几成误差。

MQL5 / C++
class="kw">virtual class="type">class="kw">double evalModel(class="type">class="kw">ulong num_preds,class="type">class="kw">ulong row_start,class="type">class="kw">ulong row_stop,vector& targets)
  {
   if(m_ols.Loglikelihood()==EMPTY_VALUE)
     {
      Print(__FUNCTION__, " OLS error ");
      class="kw">return EMPTY_VALUE;
     }
   class="type">int k1,k2;
   class="type">class="kw">ulong nvars = num_preds + num_preds * (num_preds+class="num">1)/class="num">2;
   class="type">class="kw">double prediction;
   vector row(nvars);
   class="type">class="kw">double error = class="num">0.0;
   for(class="type">class="kw">ulong sample=row_start; sample<row_stop; sample++)
     {
      k1=class="num">0;
      k2=class="num">1;
      for(class="type">class="kw">ulong var=class="num">0; var<nvars; var++)
        {
         if(var<num_preds)
            row[var]=m_data[sample][m_trial_vars[var]];
         else
           {
            if(var<(num_preds*class="num">2))
              {
               row[var]=pow(m_data[sample][m_trial_vars[var-num_preds]],class="num">2.0);
              }
            else
              {
               row[var]=m_data[sample][m_trial_vars[k1]]*m_data[sample][m_trial_vars[k2]];
               ++k2;

逐步回归选变量的实跑日志

上面这段是逐步特征选择(stepwise selection)里交叉验证误差累积的收尾逻辑:当候选组合遍历完(k2 等于 num_preds),内层计数器 k1 自增并重置 k2,随后用 OLS 模型对样本行做 Predict,预测值为 EMPTY_VALUE 时直接打印函数名并退出,否则把 (预测值-真实值)^2 累加进 error。 真正跑起来时,先用 MathSrand(120) 固定随机种子,生成 100×10 的矩阵做 0.0~1.0 均匀随机填充,目标向量取第 0、4、5、6 列之和。参数设为每轮保留 1 个候选、10 折交叉验证、最少 3 个最多 5 个预测变量、复现 1 次。 在 BTCUSD 的 D1 周期上实测,日志显示 20:20:28 完成选择,21:05:21 打印参数:保留候选数 1、折数 10、最终最少选 3 个预测变量、最多 5 个。外汇与贵金属品种波动结构不同,这套选变量结果不能直接套用, BTCUSD 的高波动特性也意味着回测结论在外盘实盘可能倾向失效,需自行换品种验证。

MQL5 / C++
if(class="type">class="kw">ulong(k2)==num_preds)
  {
   ++k1;
   k2 = k1 + class="num">1;
  }
   }
  }
   prediction = m_ols.Predict(row);
   if(prediction == EMPTY_VALUE)
    {
     Print(__FUNCTION__, " OLS predict() error ");
     class="kw">return EMPTY_VALUE;
    }
   error+=pow(prediction-targets[sample],class="num">2.0);
  }
 class="kw">return error;
}
class=class="str">"cmt">//---
 MathSrand(class="num">120);
class=class="str">"cmt">//---
 matrix mat(class="num">100,class="num">10);
class=class="str">"cmt">//---
 mat.Random(class="num">0.0,class="num">1.0);
class=class="str">"cmt">//---
class=class="str">"cmt">//---
 vector target = mat.Col(class="num">0) + mat.Col(class="num">5) + mat.Col(class="num">4) + mat.Col(class="num">6);
class=class="str">"cmt">//---
class=class="str">"cmt">//---
 CStepwise stepwise;
 stepwise.setParams(NumKeptVars,NumFolds,MinNumVars,MaxNumVars,NumReplications,true);
 if(!stepwise.stepwiseSelection(mat,target))
   class="kw">return;
input class="type">class="kw">ulong NumKeptVars = class="num">1;
input class="type">class="kw">ulong NumFolds = class="num">10;
input class="type">class="kw">ulong MinNumVars = class="num">3;
input class="type">class="kw">ulong MaxNumVars = class="num">5;
input class="type">long NumReplications = class="num">1;

「逐步特征筛选在 BTCUSD 日线上的日志解读」

在 MT5 策略测试器里跑 StepWiseFeatureSelection_Demo(标的 BTCUSD,周期 D1),第一轮 MCP 检验只做了 1 次重复,日志先打印表头:Criterion 与 Column Indices 两列。随后逐行输出筛选轨迹——单变量 6 号列准则值 0.26300861,加入 4 号列后升到 0.57220902,再补 5 号列达 0.75419718,最后叠 0 号列直接满值 1.00000000。 值得注意的是,程序在 21:05:21.240 主动终止了 stepwise_search,原因是「再加入新变量反而导致性能退化」。这说明特征不是越多越好,过拟合边界在日志里写得明明白白。 第二轮参数明显加严:交叉验证折数 10,MCP 重复次数从 1 拉到 100,最终保留预测因子下限 3、上限 5。外汇与贵金属品种波动剧烈、杠杆风险高,这类筛选结果只代表历史样本上的统计倾向,实盘前务必自己重跑验证。 打开日志看 Criterion 后面的 pval 差分,能直接判断哪一根列「边际贡献」趋近于零——那是你删因子的首选候选。

◍ 逐步特征筛选在 BTCUSD 日线上的早停现象

在 BTCUSD 的 D1 周期上跑逐步特征选择,日志里能看到筛选过程不是一路加变量到上限,而是中途主动终止。RD 行先选入索引 6,交叉验证准则值 0.26300861;随后 EI、KM、FP 依次把 4、5 也纳入,准则值爬到 1.00000000。 FP 之后紧跟着 CI 行提示:stepwise_search 因为「再加入新变量会导致性能退化」而提前结束,并没有走到预设的最大预测变量数。这说明该数据集在日线尺度上,4/5/6 三个索引组合可能已经够用,硬加特征反而拖垮泛化。 另一段日志列出了筛选参数:每轮保留候选变量数 1、交叉验证折数 5、MCP 检验重复 100 次、最终选中的预测变量数上下限都锁在 10。但实跑中早停发生在远小于 10 个变量时,参数里的上限只是天花板而非必达目标。 外汇与贵金属品种波动结构不同,这套逐步筛选逻辑直接搬过去可能早停点完全偏移,属于高风险验证项,建议在 MT5 策略测试器里用自己品种重跑确认。

逐步特征筛选在 BTCUSD 日线上的日志轨迹

在 BTCUSD 的 D1 周期上跑逐步特征筛选,日志会按轮次吐出特征子集与对应权重。前几轮拟合优度偏低:首轮仅 0.568,入选特征编号 4 和 6;到第二轮升到 0.745,特征扩为 4、5、6。 从第三轮起拟合优度拉满到 1.000,但阈值列开始变化——第三轮第三列还是 0.01,第四轮变成 0.71,随后 0.85、0.94,最终稳定在 1.00。这说明筛选过程在后期主要调阈值而非换特征。 特征编号从 0 到 9 逐步补齐,第七轮就已集齐 0–7,最后两轮补齐 8 和 9,总耗时约 30 秒(07:22:48 到 07:23:18)。日志末行提示 Stepwise selection successfully completed,表示筛选正常结束。 这种日志可直接在 MT5 的「专家日志」里复现:挂上 StepWiseFeatureSelection_Demo 对 BTCUSD 日线运行,就能看到同样的特征递进结构。加密货币及外汇、贵金属杠杆交易高风险,拟合优度满值不代表样本外预测可靠。

MQL5 / C++
LJ       class="num">0    class="num">07:class="num">22:class="num">48.153   StepWiseFeatureSelection_Demo(BTCUSD,D1)    class="num">0.56836766 class="num">0.01000000 class="num">0.01000000  class="num">4 class="num">6
FH       class="num">0    class="num">07:class="num">22:class="num">49.518   StepWiseFeatureSelection_Demo(BTCUSD,D1)    class="num">0.74542884 class="num">0.01000000 class="num">0.01000000  class="num">4 class="num">5 class="num">6
NM       class="num">0    class="num">07:class="num">22:class="num">51.488   StepWiseFeatureSelection_Demo(BTCUSD,D1)    class="num">1.00000000 class="num">0.01000000 class="num">0.01000000  class="num">0 class="num">4 class="num">5 class="num">6
KQ       class="num">0    class="num">07:class="num">22:class="num">54.163   StepWiseFeatureSelection_Demo(BTCUSD,D1)    class="num">1.00000000 class="num">0.01000000 class="num">0.71000000  class="num">0 class="num">1 class="num">4 class="num">5 class="num">6
JF       class="num">0    class="num">07:class="num">22:class="num">57.793   StepWiseFeatureSelection_Demo(BTCUSD,D1)    class="num">1.00000000 class="num">0.01000000 class="num">0.85000000  class="num">0 class="num">1 class="num">2 class="num">4 class="num">5 class="num">6
DD       class="num">0    class="num">07:class="num">23:class="num">02.436   StepWiseFeatureSelection_Demo(BTCUSD,D1)    class="num">1.00000000 class="num">0.01000000 class="num">0.94000000  class="num">0 class="num">1 class="num">2 class="num">3 class="num">4 class="num">5 class="num">6
MK       class="num">0    class="num">07:class="num">23:class="num">08.007   StepWiseFeatureSelection_Demo(BTCUSD,D1)    class="num">1.00000000 class="num">0.01000000 class="num">1.00000000  class="num">0 class="num">1 class="num">2 class="num">3 class="num">4 class="num">5 class="num">6 class="num">7
DH       class="num">0    class="num">07:class="num">23:class="num">13.842   StepWiseFeatureSelection_Demo(BTCUSD,D1)    class="num">1.00000000 class="num">0.01000000 class="num">1.00000000  class="num">0 class="num">1 class="num">2 class="num">3 class="num">4 class="num">5 class="num">6 class="num">7 class="num">8
HO       class="num">0    class="num">07:class="num">23:class="num">18.949   StepWiseFeatureSelection_Demo(BTCUSD,D1)    class="num">1.00000000 class="num">0.01000000 class="num">1.00000000  class="num">0 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
PL       class="num">0    class="num">07:class="num">23:class="num">18.949   StepWiseFeatureSelection_Demo(BTCUSD,D1)      Stepwise selection successfully completed

「记住这一条就够了」

增强型逐步特征选择把交叉验证折数、MCP 排列次数、预测器数量上下限这几个旋钮交到你手里,跑一轮 demo 脚本(StepWiseFeatureSelection_Demo.mq5,5.37 KB)就能看见特征子集怎么在过拟合风险和预测力之间拉扯。 折数设太低跑得快但统计不可靠,排列数给少了显著性检验就是摆设;np.mqh 74.16 KB 那套矩阵工具决定了你能否在 MT5 里直接复算,而不是只看作者截图。 外汇与贵金属市场高杠杆、高波动,任何特征选择结论都只是概率倾向,拿去实盘前先在历史数据上重跑一遍再说话。

把组合筛选交给小布盯盘跑
这些诊断与小布盯盘的 AIGC 已内置,打开对应品种页即可看到候选特征的互补性评分,你专注判断哪组宏观与技术面配对值得留。

常见问题

它逐变量评估且难测多样组合贡献,单独无信息量的特征与互补特征组合后可能高度预测,传统法易漏掉这类交互。
基于回归的逐步法用p值评估系数为0的概率,低p值支持显著性,置信区间给出真实系数范围,但需防样本内过拟合误导。
它按Masters思路测有潜力组合而非穷举,并弱化单纯样本内拟合判断,倾向保留样本外更稳的变量子集。
能,小布盯盘的AIGC模块可对该品种候选特征做互补性诊断与噪声标记,省去手动跑筛选脚本的重复劳动。
基本面事件与技态互补时可捕获单独变量没有的预测结构,这是传统逐步法常忽略的组合效应。