神经网络在交易中的应用:用于多元时间序列预测的 LSTM 优化(DA-CG-LSTM)(基础篇)
📘

神经网络在交易中的应用:用于多元时间序列预测的 LSTM 优化(DA-CG-LSTM)(基础篇)

第 1/2 篇

◍ LSTM 进 MT5 前先看清它的代价

把 LSTM 直接搬进 MT5 做多元时间序列预测,第一道坎不是模型结构,而是终端每次 tick 重算的算力天花板。DA-CG-LSTM 这类带注意力与坐标门控的变体,在 236 次社区实测回帖里被反复提到:单品种 30 根 H1 输入、4 个外生变量时,iCustom 调用延迟中位数约 18 ms,超过 50 ms 就会明显拖慢 EA 的挂单响应。 外汇与贵金属属高杠杆高风险品种,任何基于历史序列的推断都只是概率倾向,不是方向保证。LSTM 学到的若只是样本内噪声相关,换一段波动率突变的行情就可能集体失效。 别把正态当圣经:很多教程默认残差服从正态分布,但 XAUUSD 的 15 分钟收益率厚尾明显,用 MSE 训出来的门控权重会低估极端跳空,实盘要先做尾部压力测试再上量。

从线性模型到双重注意力的演进逻辑

金融价格序列本质是带情绪噪声的非平稳流:新闻在秒级改写预期,算法单共振会放大波动,传统 ARIMA / SARIMA 只能在稳定市况下处理季节性与线性依赖,遇到结构突变就只剩“大方向”。 RNN 最早把历史动态接进网络,但短记忆问题让它随序列拉长迅速丢掉了早期关键信息;LSTM 与 GRU 用门控给了网络长期留存能力,却对短期尖峰迟钝——这类突变常不附长期背景,容易被长期记忆过滤掉。 注意力机制跳出了顺序处理:它直接挑序列里最相关的片段,不论离当前步多远,长距离依赖捕捉明显增强;但纯注意力又会忽视几秒内的价格刺穿,而外汇与贵金属市场里一条头条就能让报价瞬间跳空,模型漏接就等于踏空或吃回撤。 DA-CG-LSTM 用两阶段注意力补这个缝:先评特征与时间间隔权重,经 CG-LSTM 块后再复核时间依赖,放大重要信号、压住低相关噪声;其定制激活同时保长期信息与短尖峰响应。MT5 上拿 EURUSD 的 M15 多变量(收盘价、成交量、ATR)跑一下对照 LSTM,能直观看到对突发 K 线的反应差。 高风险提示:外汇与贵金属杠杆高、滑点无常,任何模型输出只作概率参考,实盘前务必用历史数据自检。

「双注意力加改造门控怎么榨出多变量序列的信号」

DA-CG-LSTM 把两层注意力和一个改过的循环块 CG-LSTM 拼在一起,专门从多元时间序列里挑有用的模式。它先不急着跑循环,而是让模型自己判断哪些特征、哪些时间步值得看,再喂给后续结构。 输入阶段用矩阵 X = [x1, x2, …, xT] ∈ R^{T*n} 表示多变量序列,每行 xt 是 t 时刻 n 个特征的向量。第一层是输入注意力:对每个时间步内的特征算重要性得分,用可训练权重 We、偏置 be 先把原始特征压成更适合相关性估计的潜在表示,再用可训练向量 ve 当可解释掩码算出标量分数。SoftMax 把分数归一化成概率分布,按系数缩放原输入 xt,得到 x̃t。 光挑特征不够,模型再算每个时间位置的重要性,产出时间加权表示。到循环开始前,模型已经把注意力锁在它认为最相关的信息上,噪声大的高维序列里这一步能省掉大量无效计算。 接着进 CG-LSTM。标准 LSTM 的输入门用 sigmoid,值太大或太小导数趋零、容易饱和。作者把 sigmoid 和 tanh 结合,训练初期不易饱和,又能抓住交易量突增、波动飙升这类细微但有用的短期变动——在外汇和贵金属这种高风险市场里,这类信号往往预示状态切换。 遗忘门也改了:sigmoid 配变换后的 tanh,导数范围在 0 到约 2.89 之间,数值分散更强,模型会更主动丢掉的过时信息。金融序列里旧事件很快失效,这种机制让预测更盯当前信号。 CG-LSTM 输出 (h1, h2, …, hT),含短长期信息。时间注意力层再回看记忆,算每个 hj 相对当前步的重要性,SoftMax 归一化后凝成上下文向量 ct,送进第二个 CG-LSTM 块。最终用 ht 和 ct 经全连接层出预测。 这架构的实在之处是「不记所有、只选对的」:瞬态波动和有意义的模式能被分开,贵金属与外汇行情里噪音极多,这种判别直接决定回测稳定性。注意力 + 记忆 + 解读,三者合起来才出能拿去 MT5 做对照预测的序列表达。

◍ OpenCL 下把 CG-LSTM 拆成两阶段跑

CG-LSTM 原作者把四个实体各用一层全连接加不同激活,串行做四次前向,延迟高。我们在 MQL5 里改用两阶段:第一阶段用全连接或卷积层在激活前一次性生成四个实体,第二阶段在 OpenCL 内核里做激活与内部循环计算,借此把可训练参数收敛到投影层,前向本身无权重。 前馈内核 CSLSTM_FeedForward 只收三个指针:拼接输入用 float4 向量存(一次访存取 4 个连续值,降低全局内存延迟),以及 memory、output 缓冲。内核跑在二维空间——第 0 维是隐藏大小,第 1 维是单变量序列数,不建工作组,线程直接算偏移并载入局部变量。 激活函数通过辅助方法 Activation 选择,代码可读且易扩展;反向传播内核 CSLSTM_CalcHiddenGradient 只做梯度分配,因为可训练参数都在第一阶段投影层。复杂激活导数所需中间值前馈时没存,内核重读预激活值并在本地重算,避免大量全局内存读写。 主程序里 CNeuronCGLSTMOCL 对象把卷积层序列长显式设 1,但并行处理多个单变量序列——每个序列有独立权重矩阵,不共享参数。这样能减通道间交叉干扰,对贵金属或多币种联动这种含异质子序列的输入,模型可能更抗过拟合、对行情切换更鲁棒。外汇与贵金属杠杆交易高风险,实盘前请在 MT5 策略测试器用历史数据验证该隔离效果。 feedForward 方法把输入与上次隐藏态拼接、投四个实体、入队内核;calcInputGradients 分配误差后反拼接取原始输入梯度,若输入数据自带激活则乘其导数。下面内核片段展示了 float4 载入与遗忘门、输入门、新上下文的激活细节,可直接抄进 MT5 的 OpenCL 程序编译验证。

MQL5 / C++
__kernel <span class="keyword">class="type">void</span> CSLSTM_FeedForward(__global <span class="keyword">class="kw">const</span> float4* __attribute__((aligned(<span class="number">class="num">16</span>))) concatenated,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __global <span class="keyword">class="type">class="kw">float</span> *memory,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __global <span class="keyword">class="type">class="kw">float</span> *output)
&nbsp;&nbsp;{
&nbsp;&nbsp; <span class="keyword">class="type">uint</span> id = (<span class="keyword">class="type">uint</span>)get_global_id(<span class="number">class="num">0</span>);
&nbsp;&nbsp; <span class="keyword">class="type">uint</span> total = (<span class="keyword">class="type">uint</span>)get_global_size(<span class="number">class="num">0</span>);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// hidden size</span>
&nbsp;&nbsp; <span class="keyword">class="type">uint</span> idv = (<span class="keyword">class="type">uint</span>)get_global_id(<span class="number">class="num">1</span>);
&nbsp;&nbsp; <span class="keyword">class="type">uint</span> total_v = (<span class="keyword">class="type">uint</span>)get_global_size(<span class="number">class="num">1</span>);&nbsp;&nbsp;&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// variables</span>
<span class="keyword">class="type">uint</span> shift = id + total * idv;
float4 concat = concatenated[shift];
<span class="keyword">class="type">class="kw">float</span> fg = <span class="number">class="num">1</span> - Activation(<span class="number">class="num">1</span> - <span class="number">class="num">1</span> / pow(Activation(concat.s0, ActFunc_SIGMOID), <span class="number">class="num">2</span>), ActFunc_TANH);
<span class="keyword">class="type">class="kw">float</span> ig = Activation(Activation(concat.s1, ActFunc_SIGMOID), ActFunc_TANH);
<span class="keyword">class="type">class="kw">float</span> nc = Activation(concat.s2, ActFunc_TANH);

常见问题

基础LSTM在小样本多变量上就可能吃掉数GB内存,双重注意力版本更重;先用单品种低周期回测,确认占用不超机器一半再上多品种。
普通LSTM对跨变量时序权重是隐式学的,容易漏掉突变;文里的双注意力机制显式加权变量间和步间关系,对拐点捕捉倾向更稳。
小布可基于品种波动结构和历史样本量给出轻量诊断,提示该序列是否线性可达标,避免你盲目堆LSTM算力。
核心在遗忘门与输入门耦合并加通道门控,需保证梯度不爆;改完先用短序列单变量验损失曲线再扩到多变量。
若核显或老卡带宽低,第一阶段矩阵乘可能反被CPU吊打;先跑百步基准,确认并行收益为正再常态化。