神经网络变得轻松(第十七部分):降低维度·综合运用
「信号函数收尾的布尔返回」
MQL5 里很多自定义指标或 EA 的辅助函数,最后一步都是返回一个布尔值来标记执行状态。上面这段截取的收尾代码,直接 return true 表示本次调用逻辑已正常走完。 在 MT5 里如果你自己写信号判断函数,记得收口处明确返回 true/false,否则编译器可能给到 'not all control paths return a value' 的警告。打开 MetaEditor 随便建个脚本粘进去编译,能立刻验证这个返回结构。 外汇与贵金属品种波动大、杠杆高,这类底层函数返回异常可能让策略在极端行情下漏单,实盘前务必用策略测试器跑几组历史数据确认。
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。
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 调用后不再接等号。
外汇与贵金属市场杠杆高、滑点凶,这类无监督模型在实盘前务必用策略测试器跑够样本,任何信号都只是概率倾向,不是确定性结论。
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); }