交易中的神经网络:基于双注意力的趋势预测模型·综合运用
(3/3)·从分段线性到 LSTM 双注意力,把论文模型真正跑成 MT5 可读信号的收尾实战
「编码器后段层定义与训练入口」
上面这段是编码器从第 3 层到第 7 层的堆叠逻辑,每一层都用 CLayerDescription 描述后通过 encoder.Add 挂进网络。第 3、4 层是潜变量层,节点数由 LatentCount 控制,激活分别用 LReLU 和 SIGMOID;第 5 层输出维度为 BarDescr * NForecast,用 TANH 把值压到 [-1,1] 区间,适合价格归一化特征。 第 6 层是反归一化层(defNeuronRevInDenormOCL),layers 显式设为 1,不做激活;第 7 层是频域预测层(defNeuronFreDFOCL),window 等于 BarDescr,count 为 NForecast,probability 写死 0.7f,意味着该层以 70% 概率参与前向。任何一层 new 失败或 Add 返回 false 都会 delete 描述符并回 false,避免野指针。 Train 函数是训练触发点:先用 GetProbTrajectories(Buffer, 0.9) 拿到轨迹采样概率向量,Batch 固定 100,用 GetTickCount 记时。循环里 SampleTrajectory 选轨迹,再用 MathRand 平方分布挑起始偏移 st,若 st<=0 就回退 Batch 重采。外汇与贵金属行情下用这类自编码结构做预测属高概率试探,实盘前务必在 MT5 策略测试器跑通后再调 LatentCount 与 probability。
class=class="str">"cmt">//--- layer class="num">3 if(!(descr = new CLayerDescription())) class="kw">return false; descr.type = defNeuronBaseOCL; descr.count = LatentCount; descr.activation = LReLU; descr.optimization = ADAM; if(!encoder.Add(descr)) { class="kw">delete descr; class="kw">return false; } class=class="str">"cmt">//--- layer class="num">4 if(!(descr = new CLayerDescription())) class="kw">return false; descr.type = defNeuronBaseOCL; descr.count = LatentCount; descr.optimization = ADAM; descr.activation = SIGMOID; if(!encoder.Add(descr)) { class="kw">delete descr; class="kw">return false; } class=class="str">"cmt">//--- layer class="num">5 if(!(descr = new CLayerDescription())) class="kw">return false; descr.type = defNeuronBaseOCL; descr.count = BarDescr * NForecast; descr.optimization = ADAM; descr.activation = TANH; if(!encoder.Add(descr)) { class="kw">delete descr; class="kw">return false; } class=class="str">"cmt">//--- layer class="num">6 if(!(descr = new CLayerDescription())) class="kw">return false; descr.type = defNeuronRevInDenormOCL; descr.count = BarDescr * NForecast; descr.activation = None; descr.optimization = ADAM; descr.layers = class="num">1; if(!encoder.Add(descr)) { class="kw">delete descr; class="kw">return false; } class=class="str">"cmt">//--- layer class="num">7 if(!(descr = new CLayerDescription())) class="kw">return false; descr.type = defNeuronFreDFOCL; descr.window = BarDescr; descr.count = NForecast; descr.step = class="type">int(true); descr.probability = class="num">0.7f; descr.activation = None; descr.optimization = ADAM; if(!encoder.Add(descr)) { class="kw">delete descr; class="kw">return false; } class=class="str">"cmt">//--- class="kw">return true; } class="type">void Train(class="type">void) { class=class="str">"cmt">//--- vector<class="type">float> probability = GetProbTrajectories(Buffer, class="num">0.9); vector<class="type">float> result, target, state; class="type">bool Stop = false; class="type">int Batch = class="num">100; class="type">uint ticks = GetTickCount(); class=class="str">"cmt">//--- for(class="type">int iter = class="num">0; (iter < Iterations && !IsStopped() && !Stop); iter += Batch) { class="type">int tr = SampleTrajectory(probability); class="type">int st = (class="type">int)((MathRand() * MathRand() / MathPow(class="num">32767, class="num">2)) * (Buffer[tr].Total - class="num">2 - NForecast)); if(st <= class="num">0) { iter -= Batch; class="kw">continue; } Encoder.Clear();
◍ 状态编码器的批量训练循环
这段循环是编码器离线训练的核心:每次从样本缓冲里取一批(Batch)状态向量喂给神经网络,用未来 NForecast 根 K 线的状态作监督目标做反向传播。循环上限由 MathMin(st+Batch, 总样本数-NForecast) 卡住,同时监听 IsStopped() 与自定义 Stop 标志,避免 MT5 终端人工停止或出错时死跑。 当某条状态向量的绝对值之和为零(MathAbs(state).Sum()==0),说明是空状态,直接把 iter 补偿后 break,跳过无效样本。这一判断能省掉约数万次无意义前向计算,具体占比取决于你的 Buffer 里 padding 比例。 每过 500 毫秒(GetTickCount 差值)才刷新一次 Comment 进度,显示 Encoder 的近期平均误差与百分比,避免每根样本都刷屏拖慢回测。训练结束无论成功与否都会 Print 最终误差并调用 ExpertRemove() 把 EA 从图表卸掉——这是离线训练脚本的典型收尾,不是实盘逻辑。 让小布替你跑这套 把 Batch 调到 256、NForecast 设为 5,在 EURUSD 的 M15 上跑一遍,观察 Comment 里 Error 是否从 0.1 量级降到 0.01 以下;外汇与贵金属杠杆品种波动剧烈,离线误差低不代表实盘预测概率高,需自行验证。
for(class="type">int i = st; (i < MathMin(st + Batch, Buffer[tr].Total - NForecast) && !IsStopped() && !Stop); i++) { state.Assign(Buffer[tr].States[i].state); if(MathAbs(state).Sum() == class="num">0) { iter += i - st - Batch; class="kw">break; } bState.AssignArray(state); class=class="str">"cmt">//--- State Encoder if(!Encoder.feedForward((CBufferFloat*)GetPointer(bState), class="num">1, false, (CBufferFloat*)NULL)) { PrintFormat("%s -> %d", __FUNCTION__, __LINE__); Stop = true; class="kw">break; } class=class="str">"cmt">//--- Collect target data if(!Result.AssignArray(Buffer[tr].States[i + NForecast].state)) class="kw">continue; if(!Result.Resize(BarDescr * NForecast)) class="kw">continue; if(!Encoder.backProp(Result, (CBufferFloat*)NULL)) { PrintFormat("%s -> %d", __FUNCTION__, __LINE__); Stop = true; class="kw">break; } if(GetTickCount() - ticks > class="num">500) { class="type">class="kw">double percent = class="type">class="kw">double(iter + i - st) * class="num">100.0 / (Iterations); class="type">class="kw">string str = StringFormat("%-14s %class="num">6.2f%% -> Error %class="num">15.8f\n", "Encoder", percent, Encoder.getRecentAverageError()); Comment(str); ticks = GetTickCount(); } } } Comment(""); class=class="str">"cmt">//--- PrintFormat("%s -> %d -> %-15s %class="num">10.7f", __FUNCTION__, __LINE__, "Encoder", Encoder.getRecentAverageError()); ExpertRemove(); class=class="str">"cmt">//--- }
EURUSD 上跑通的两阶段验证
用 TPM 预测轨迹的思路落地后,先拿 EURUSD 2023 年 H1 的 H1 周期历史数据训环境编码器。它只吃历史价格走势、不评估参与者动作,所以能在初始集上一次训透,不用频繁更新,速度也快。 编码器训完,预测线和实际价贴得很紧,只是预测轨更平滑——这种平滑在后续训参与者时可能让策略更新更稳。接着进第二阶段:参与者政策迭代训练。因为动作会漂出原数据边界,经验回放缓冲区得定期用当前政策跑出的状态与奖励去补。 多轮参与者—评价者交替训完,训练集内政策是盈利的。换 2024 年 1 月同条件外推测试:共 26 笔交易,11 笔盈利,胜率刚过 42%;但平均和最大盈利都高于对应亏损,盈利因子 1.12,整体仍净赚。 风险点在月末前段——余额图出现大幅回撤,外汇/贵金属这类高杠杆品种上这种回撤很要命。模型能赚,但离直接上实盘还差一轮优调。
「模型跑通了但还没调顺」
把卷积抓短期依赖、PLR 拆长期趋势这两件事拼到一起,TPM 的思路在 MT5 里确实能落地。我们用 MQL5 把整套流程写进 EA,训完模型丢到训练集之外的行情里跑,账户权益在样本外数据上整体是正向的,说明泛化没完全失效。 不过别急着上实盘。余额曲线没有走出那种干净的单向爬坡,中间回撤肉眼可见,训练集里没见过的波动段它会犹豫甚至反向。外汇和贵金属这种高杠杆品种,回撤放大就是爆仓前奏,样本外盈利不等于能扛住极端价差。 当前这版模型只是证明路子的潜力,参数和结构都还粗。想复用的人直接把下面训练入口拷进策略测试器,先拿 EURUSD 的 H1 跑一轮,看看你机器上回撤比我们测的更深还是更浅。
◍ 把这条线请下神坛
整套 LSTM 优化方案落地到 MT5,真正跑起来的不是某个黑箱,而是一组可查文件的分工:Research.mq5 与 ResearchRealORL.mq5 负责样本采集,Study.mq5 与 StudyEncoder.mq5 做模型与编码器训练,Test.mq5 回测验证,Trajectory.mqh、NeuroNet.mqh、NeuroNet.cl 则提供状态结构与 OpenCL 加速类库,压缩包 MQL5.zip 约 1446.2 KB。 这些 mq5 与 mqh 文件直接拖进 MT5 的 MQL5 目录就能编译,但外汇与贵金属市场高杠杆、高波动,神经网络预测只代表概率倾向,不代表下一根 K 线必然重复历史轨迹。 真正该做的,是开 MT5 把 Test.mq5 接上自己的品种数据跑一遍,看回测曲线和样本外误差,再决定要不要把这条线请下神坛、放进实盘观察列表。