神经网络变得轻松(第十七部分):降低维度·综合运用
📘

神经网络变得轻松(第十七部分):降低维度·综合运用

第 3/3 篇

「信号函数收尾的布尔返回」

MQL5 里很多自定义指标或 EA 的辅助函数,最后一步都是返回一个布尔值来标记执行状态。上面这段截取的收尾代码,直接 return true 表示本次调用逻辑已正常走完。 在 MT5 里如果你自己写信号判断函数,记得收口处明确返回 true/false,否则编译器可能给到 'not all control paths return a value' 的警告。打开 MetaEditor 随便建个脚本粘进去编译,能立刻验证这个返回结构。 外汇与贵金属品种波动大、杠杆高,这类底层函数返回异常可能让策略在极端行情下漏单,实盘前务必用策略测试器跑几组历史数据确认。

MQL5 / C++
  class="kw">return true;
}

◍ PCA 降维训练的两个阶段怎么跑

主成分分析在 MT5 里分两步验证。第一步先训模型:基于前文 kmeans 的 EA 框架改出 pca.mq5,只动模型对象和 Train 函数,其余结构沿用。 判定训练区间起点后,把 RSI、CCI、ATR、MACD 的报价和指标值拉进矩阵,调用 PCA.Study 训练,成功就写进 pca.net 文件并退出 EA。用过去 15 年历史跑下来,原始维度从 160 个元素压到 68 个,数据量砍掉近 2.4 倍,信息损失约 1%。 第二阶段换 pca_net.mq5,把降维后的特征喂给全连接感知器,感知器用近两年历史训。图上能看到压缩数据上误差收敛很稳,55 个 epoch 还没躺平,继续训大概率还能往下压。外汇和贵金属波动剧烈,这种降维方案仅降低过拟合概率,实盘仍属高风险。 下面这段 Train 是降维训练核心,逐行拆一下逻辑:void Train 无参入口;用 TimeCurrent 取当前时间,年份减 StudyPeriod 定起点,防年份越界回退 1900;CopyRates 按符号周期拉数据到 Rates 数组。 RSI/CCI/ATR/MACD 四个指标 BufferResize 到 bars 长度,失败直接 return;ArraySetAsSeries 倒序排列方便按 bar 索引。Refresh 刷新指标缓冲,total 去掉末尾 HistoryBars 根避免未来函数。 data 矩阵初始化为 total 行、8*HistoryBars 列(每根 bar 存 open-low、high-open、close-open 加 5 个指标共 8 维),失败则 ExpertRemove 退出。双层循环把每根 bar 的 8 维特征填进矩阵对应偏移。 PCA.Study(data) 执行降维训练,失败打印错误码返回;FileOpen 写二进制 pca.net,PCA.Save 存模型后关句柄,清 Comment 并 ExpertRemove 结束 EA。

MQL5 / C++
class="type">void Train(class="type">void)
  {
class=class="str">"cmt">//---
   class="type">MqlDateTime start_time;
   TimeCurrent(start_time);
   start_time.year -= StudyPeriod;
   if(start_time.year <= class="num">0)
       start_time.year = class="num">1900;
   class="type">class="kw">datetime st_time = StructToTime(start_time);
   class="type">int bars = CopyRates(Symb.Name(), TimeFrame, st_time, TimeCurrent(), Rates);
   if(!RSI.BufferResize(bars) || !CCI.BufferResize(bars) || !ATR.BufferResize(bars) || !MACD.BufferResize(bars))
       class="kw">return;
   if(!ArraySetAsSeries(Rates, true))
       class="kw">return;
class=class="str">"cmt">//---
   RSI.Refresh();
   CCI.Refresh();
   ATR.Refresh();
   MACD.Refresh();
   class="type">int total = bars - (class="type">int)HistoryBars;
   matrix data;
   if(!data.Init(total, class="num">8 * HistoryBars))
    {
       ExpertRemove();
       class="kw">return;
    }
class=class="str">"cmt">//---
   for(class="type">int i = class="num">0; i < total; i++)
    {
       Comment(StringFormat("Create data: %d of %d", i, total));
       for(class="type">int b = class="num">0; b < (class="type">int)HistoryBars; b++)
         {
          class="type">int bar = i + b;
          class="type">int shift = b * class="num">8;
          class="type">class="kw">double open = Rates[bar]
               .open;
          data[i, shift] = open - Rates[bar].low;
          data[i, shift + class="num">1] = Rates[bar].high - open;
          data[i, shift + class="num">2] = Rates[bar].close - open;
          data[i, shift + class="num">3] = RSI.GetData(MAIN_LINE, bar);
          data[i, shift + class="num">4] = CCI.GetData(MAIN_LINE, bar);
          data[i, shift + class="num">5] = ATR.GetData(MAIN_LINE, bar);
          data[i, shift + class="num">6] = MACD.GetData(MAIN_LINE, bar);
          data[i, shift + class="num">7] = MACD.GetData(SIGNAL_LINE, bar);
         }
    }
   ResetLastError();
   if(!PCA.Study(data))
    {
       printf("Runtime error %d", GetLastError());
       class="kw">return;
    }
   class="type">int handl = FileOpen("pca.net", FILE_WRITE | FILE_BIN);
   if(handl != INVALID_HANDLE)
    {
       PCA.Save(handl);
       FileClose(handl);
    }
class=class="str">"cmt">//---
   Comment("");
   ExpertRemove();
   }

降维压缩的边界与误用风险

我们用 CPCA 类做主成分分析,把原始数据压到约 2.4 倍压缩率时只丢了 1% 信息,这种可量化的失真阈值对训练效率有帮助。正交矩阵把压缩后特征间相关性降到近乎 0,二次测试也印证了这点。 外汇和贵金属市场高波动,用压缩数据跑模型前先在 MT5 用样本外数据验一遍失真是否可接受。 别拿主成分法去压过拟合,那是正则化该干的事;只有别的训练路子出不来预期结果,才值得碰降维。 MQL5 原生矩阵运算让这类建模代码轻很多,写 AI 相关逻辑时直接调就行。

「随文附带的工程文件与编译坑」

这套无监督学习方案在 MT5 里落地,依赖一组分工明确的文件:pca.mq5 负责训练主成分模型,pca_net.mq5 把训练好的数据喂给第二套模型做测试;pca.mqh 与 kmeans.mqh 是两个核心类库,分别实现 PCA 和 k-均值聚类,unsupervised.cl 和 NeuroNet.cl 则是 OpenCL 核,把聚类与神经网络运算丢给显卡跑。 实际拉进 MT5 编译时,老版本里常见一个报错:cannot convert type 'bool' to type 'matrix'。问题出在 X = X.Row(temp, i); 这种写法——新版本 MQL5 的 matrix::Row 不再返回矩阵,而是直接把向量写进矩阵并返回 bool 状态。 作者在原讨论里给的修法是删掉赋值,改成判断写入是否成功。下面这段就是修正后的写法,注意 Row 调用后不再接等号。 外汇与贵金属市场杠杆高、滑点凶,这类无监督模型在实盘前务必用策略测试器跑够样本,任何信号都只是概率倾向,不是确定性结论。

MQL5 / C++
if(!X.Row(temp, i))
   class="kw">return false;
for(class="type">class="kw">ulong r = class="num">0; r < total; r++)
   {
      vectorf temp = data.Row(r) - v_Means;
      temp /= v_STDs;
      result.Row(temp, r);
   }

常见问题

布尔返回相当于给模型一个明确的是/否信号,能避免中间数值被后续误当成强度用;收尾时只输出 true/false 更利于策略做二分类决策。
第一阶段用历史样本算投影矩阵并压缩特征,第二阶段拿压缩后的低维数据去训练预测模型;两阶段分开存文件,别混在同一轮里跑。
可以,小布能按你给的工程文件顺序调用降维与训练流程,并提示编译报错点,你把文件丢进去就能看中间结果。
不是,压太狠会丢掉关键非线性结构,贵金属和外币对波动里常有这类信息;建议保留累计解释方差 85% 以上的维度。
多数卡在依赖库路径没设、或旧版编译器不认新语法;检查 include 目录并换较新编译环境通常就能过。