使用带 ENCOG 机器学习框架的 MetaTrader 5 指标进行时间序列预测·进阶篇
(2/3)· 接基础篇的环境搭建,本篇拆解训练数据导出与神经元数量设定的实战坑点
不少交易者把 MT5 的历史数据直接丢进神经网络,却忽略时间窗口与归一化口径,导致 ENCOG 训出的模型在样本外迅速失效。还有人盲目堆神经元层数,以为越深越准,实则陷入过拟合却毫无察觉。
「三层前馈网络里神经元怎么数」
刚接触神经网络的交易者最容易卡在架构选择上。本文把前馈网络限定为三层:输入、隐藏、输出,各层神经元数量里只有输入和输出能精确算,隐藏层只能靠训练表现倒推。 输入层直接按“指标数 × 回看柱数”定。比如拿 3 个指标、回看 6 根柱去预测下一根,输入层就是 3×6=18 个神经元,数据须先做时间定量预处理再喂进去。 输出层等于要预测的柱数。本例只预测未来 1 根柱,所以输出层就 1 个神经元。隐藏层没有现成数学公式,得用前向选择算法边训边砍错,或者试 ENCOG 的修剪法——它删掉权重为零的隐藏节点。隐藏和输出神经元越多,训练耗时越长,外汇与贵金属行情高波动高风险,过拟合会放大实盘亏损概率。 除了试错,Heaton Research 的前向选择思路值得照搬:先小网络起步,逐步加隐藏神经元直到验证集误差不再降,就停。
把 MT5 指标导成 ENCOG 能吃的 CSV
ENCOG 做神经网络训练只认 CSV,首行必须是逗号分隔的表头:日期、时间、收盘价打头,后面跟指标名。数据行里指标值要用科学计数法,例如 -7.8970208860e-002,否则读进去会错位。 下面这段脚本把 BullsPower 导出来。extern 参数里 trainSize=400 代表取最近 400 根 K 线,maPeriod=210 是 BullsPower 的均线周期,跑完在终端根目录生成 mt5export.csv。 只导一个指标时,表头写 DATE,TIME,CLOSE,BullsPower;若像原文那样叠加随机指标和威廉指标,就手动在 FileWriteString 里加列名,并多开几个 CopyBuffer 把缓存塞进文件。改第二个示例就能拼出你自己的多指标训练集,外汇和贵金属波动大、滑点凶,导出的样本仅供回测参考,实盘信号概率不等于确定性。
DATE,TIME,CLOSE,Indicator_Name1,Indicator_Name2,Indicator_Name3 class="num">20110103,class="num">0000,class="num">0.93377000,-class="num">7.8970208860e-002 class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| ExportToEncog.mq5 | class=class="str">"cmt">//| Copyright class="num">2011, Investeo.pl | class=class="str">"cmt">//| http:/Investeo.pl | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2011, Investeo.pl" class="macro">#class="kw">property link "http:/Investeo.pl" class="macro">#class="kw">property version "class="num">1.00" class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Script program start function | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">// Export Indicator values for NN training by ENCOG class="kw">extern class="type">class="kw">string IndExportFileName = "mt5export.csv"; class="kw">extern class="type">int trainSize = class="num">400; class="kw">extern class="type">int maPeriod = class="num">210; class="type">MqlRates srcArr[]; class="type">class="kw">double expBullsArr[]; class="type">void OnStart() { class=class="str">"cmt">//--- ArraySetAsSeries(srcArr, true); ArraySetAsSeries(expBullsArr, true); class="type">int copied = CopyRates(Symbol(), Period(), class="num">0, trainSize, srcArr); if (copied!=trainSize) { Print("Not enough data for " + Symbol()); class="kw">return; } class="type">int hBullsPower = iBullsPower(Symbol(), Period(), maPeriod); CopyBuffer(hBullsPower, class="num">0, class="num">0, trainSize, expBullsArr); class="type">int hFile = FileOpen(IndExportFileName, FILE_CSV | FILE_ANSI | FILE_WRITE | FILE_REWRITE, ",", CP_ACP); FileWriteString(hFile, "DATE,TIME,CLOSE,BullsPower\n"); Print("Exporting indicator data to " + IndExportFileName); for (class="type">int i=trainSize-class="num">1; i>=class="num">0; i--) { class="type">class="kw">string candleDate = TimeToString(srcArr[i].time, TIME_DATE); StringReplace(candleDate,".",""); class="type">class="kw">string candleTime = TimeToString(srcArr[i].time, TIME_MINUTES); StringReplace(candleTime,":","");
◍ 导出的 BullsPower 数据长什么样
把指标序列写进 CSV 后,文件头固定是 DATE,TIME,CLOSE,BullsPower 四列,每行对应一根日线。下面这段落盘代码负责把日期、时间、收盘价和牛力值按列写入,牛力值用 DoubleToString 保留 10 位小数精度,避免浮点截断。 FileWrite(hFile, candleDate, candleTime, DoubleToString(srcArr[i].close), DoubleToString(expBullsArr[i], -10)); FileClose(hFile); Print("Indicator data exported."); 以 EURUSD 日线为例,2011 年 1 月 3 日收盘 0.93377,BullsPower 为 -0.07897;到 1 月 11 日收盘涨到 0.97359,牛力值仅回升到 -0.03603,负值持续收窄但全程未翻正。这种「价升而牛力仍负」的背离,在外汇高杠杆环境里往往暗示反弹力度偏弱,后续回撤概率偏高。 直接把上面 20 余行样本贴进 MT5 的 File 文件夹做比对,或改 expBullsArr 的周期参数重跑导出,能快速验证你本地指标计算结果是否与样本一致。
FileWrite(hFile, candleDate, candleTime, DoubleToString(srcArr[i].close), DoubleToString(expBullsArr[i], -class="num">10)); } FileClose(hFile); Print("Indicator data exported."); }
「把指标序列导成 CSV 喂给神经网络」
做价格行为之外的量化尝试时,先把 MT5 里的裸数据抓出来比自己手算指标靠谱。下面这段脚本的思路很直接:在当前图表品种与周期上,取最近 2000 根 K 线,顺手把随机指标和威廉指标一并拷出来,写进一个逗号分隔的文件。 外汇与贵金属杠杆高、跳空频繁,2000 根样本在 M15 上大约只覆盖 20 多个交易日,用来训练任何模型都只能算试探性样本,过拟合概率偏大。 脚本头部用 extern 暴露了两个入口:IndExportFileName 默认叫 mt5export.csv,trainSize 写死 2000。Stochastic 用了 (8,5,5,EMA,LOWHIGH) 这组参数,Williams %R 周期是 21,都是偏短线的配置。 循环里从最旧一根写到最后一根,日期时间里的点号和冒号被替换掉,方便后续 ENCOG 这类 Java 训练框架直接读。开 MT5 把代码挂到任意图表跑一次,终端目录里就能拿到带 CLOSE、StochK、StochD、WilliamsR 四列的 csv。
class=class="str">"cmt">//| Copyright class="num">2011, Investeo.pl | class=class="str">"cmt">//| http:/Investeo.pl | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2011, Investeo.pl" class="macro">#class="kw">property link "http:/Investeo.pl" class="macro">#class="kw">property version "class="num">1.00" class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Script program start function | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">// Export Indicator values for NN training by ENCOG class="kw">extern class="type">class="kw">string IndExportFileName = "mt5export.csv"; class="kw">extern class="type">int trainSize = class="num">2000; class="type">MqlRates srcArr[]; class="type">class="kw">double StochKArr[], StochDArr[], WilliamsRArr[]; class="type">void OnStart() { class=class="str">"cmt">//--- ArraySetAsSeries(srcArr, true); ArraySetAsSeries(StochKArr, true); ArraySetAsSeries(StochDArr, true); ArraySetAsSeries(WilliamsRArr, true); class="type">int copied = CopyRates(Symbol(), Period(), class="num">0, trainSize, srcArr); if (copied!=trainSize) { Print("Not enough data for " + Symbol()); class="kw">return; } class="type">int hStochastic = iStochastic(Symbol(), Period(), class="num">8, class="num">5, class="num">5, MODE_EMA, STO_LOWHIGH); class="type">int hWilliamsR = iWPR(Symbol(), Period(), class="num">21); CopyBuffer(hStochastic, class="num">0, class="num">0, trainSize, StochKArr); CopyBuffer(hStochastic, class="num">1, class="num">0, trainSize, StochDArr); CopyBuffer(hWilliamsR, class="num">0, class="num">0, trainSize, WilliamsRArr); class="type">int hFile = FileOpen(IndExportFileName, FILE_CSV | FILE_ANSI | FILE_WRITE | FILE_REWRITE, ",", CP_ACP); FileWriteString(hFile, "DATE,TIME,CLOSE,StochK,StochD,WilliamsR\n"); Print("Exporting indicator data to " + IndExportFileName); for (class="type">int i=trainSize-class="num">1; i>=class="num">0; i--) { class="type">class="kw">string candleDate = TimeToString(srcArr[i].time, TIME_DATE); StringReplace(candleDate,".",""); class="type">class="kw">string candleTime = TimeToString(srcArr[i].time, TIME_MINUTES); StringReplace(candleTime,":",""); FileWrite(hFile, candleDate, candleTime, DoubleToString(srcArr[i].close),
导出后的指标数据长这样
上面那段循环把 Stochastic 的 K、D 两线与 Williams %R 通过 DoubleToString 转成 10 位小数精度的字符串,塞进 CSV 行里,最后 FileClose 收尾并在日志打印 Indicator data exported.。这套写法直接把 MT5 内置指标序列落盘,方便你拿去外部做回归或喂给小布的分析模块。 导出的前 12 行样本(EURUSD 日线,2003 年 7 月)里,CLOSE 从 1.3737 滑到 1.3613,StochK 从 71.74 跌到 29.04,WilliamsR 从 -0.62 一路压到 -45.19。注意 20030721 那天 StochK 跌破 50 中线到 43.50,价格同步破 1.36,属于典型的动能转弱信号,但外汇高风险,仅凭这列数字不能断言趋势反转。 你可在 MT5 里把这段逻辑挂到 OnTick 或独立脚本跑一遍,把 hFile 路径改成你终端的 Files 目录,导出来用 Excel 打开核对:若 StochK 与 StochD 差值持续 >5 且 WilliamsR 深于 -80,后续几天价格倾向继续探底,概率随样本拉长会变化,自己验证最实在。
DoubleToString(StochKArr[i], -class="num">10), DoubleToString(StochDArr[i], -class="num">10), DoubleToString(WilliamsRArr[i], -class="num">10) ); } FileClose(hFile); Print("Indicator data exported."); } class=class="str">"cmt">//+------------------------------------------------------------------+ DATE,TIME,CLOSE,StochK,StochD,WilliamsR class="num">20030707,class="num">0000,class="num">1.37370000,class="num">7.1743119266e+001,class="num">7.2390220187e+001,-class="num">6.2189054726e-001 class="num">20030708,class="num">0000,class="num">1.36870000,class="num">7.5140977444e+001,class="num">7.3307139273e+001,-class="num">1.2500000000e+001 class="num">20030709,class="num">0000,class="num">1.35990000,class="num">7.3831775701e+001,class="num">7.3482018082e+001,-class="num">2.2780373832e+001 class="num">20030710,class="num">0000,class="num">1.36100000,class="num">7.1421933086e+001,class="num">7.2795323083e+001,-class="num">2.1495327103e+001 class="num">20030711,class="num">0000,class="num">1.37600000,class="num">7.5398313027e+001,class="num">7.3662986398e+001,-class="num">3.9719626168e+000 class="num">20030714,class="num">0000,class="num">1.37370000,class="num">7.0955352856e+001,class="num">7.2760441884e+001,-class="num">9.6153846154e+000 class="num">20030715,class="num">0000,class="num">1.38560000,class="num">7.4975891996e+001,class="num">7.3498925255e+001,-class="num">2.3890784983e+000 class="num">20030716,class="num">0000,class="num">1.37530000,class="num">7.5354107649e+001,class="num">7.4117319386e+001,-class="num">2.2322435175e+001 class="num">20030717,class="num">0000,class="num">1.36960000,class="num">7.1775345074e+001,class="num">7.3336661282e+001,-class="num">3.0429594272e+001 class="num">20030718,class="num">0000,class="num">1.36280000,class="num">5.8474576271e+001,class="num">6.8382632945e+001,-class="num">3.9778325123e+001 class="num">20030721,class="num">0000,class="num">1.35400000,class="num">4.3498596819e+001,class="num">6.0087954237e+001,-class="num">5.4946524064e+001 class="num">20030722,class="num">0000,class="num">1.36130000,class="num">2.9036761284e+001,class="num">4.9737556586e+001,-class="num">4.5187165775e+001
◍ EURUSD 日线样本里的持仓结构读数
下面这组成交快照覆盖 2003-07-23 至 2003-08-15 的每日 00:00 报价,第三列是 EURUSD 中间价,从 1.3464 一路走到 1.3715,约 250 点上行。外汇与贵金属杠杆品种波动剧烈,这类历史切片仅用于方法验证,实盘须自担高风险。 第四、五、六列可理解为某种多空持仓或强度派生值(科学计数法)。以 07-23 为例:4 列为 16.979、5 列为 38.818、6 列为 -65.989;到 08-15 变为 69.668、56.267、-21.739。负向列绝对值从 65.99 收敛到 21.74,同时价格抬升,说明空头挤压倾向在减弱。 把 08-04 与 08-05 放一起看更有意思:价格几乎持平(1.3478 / 1.3477),但 4 列从 57.07 掉到 53.51,6 列维持在 -81 附近,这种背离可能预示后续 08-06 的反弹(1.3535)。开 MT5 把这段 CSV 喂进自定义指标,逐日打印这三列差值,比肉眼扫 K 线更快抓到结构变化。