交易中的神经网络:使用小波变换和多任务注意力的模型(终篇)(基础篇)
◍ 把小波和多任务注意力塞进MT5的由头
这篇想解决一个老问题:金融时间序列里噪声盖过信号,普通MLP容易过拟合到随机波动。作者给出的路线是先用离散小波变换(DWT)把价格序列拆成不同频带,再接一个带多任务注意力机制的神经网络,让模型在同一前向过程里同时学趋势分类与波动率回归。 在 MetaTrader 5 环境落地这类模型,难点不在数学而在数据管道:你要把每根K线的OHLC连同小波系数一起喂给自定义指标,再用 EA 调用。原文发于 2025-10-27,截至发稿社区显示 633 次浏览、0 条评论,说明这套思路还少有人实盘验证。 对外汇与贵金属交易者来说,这类模型的高频重训成本不低,且样本外失效概率偏高,属于高风险探索。建议先开 MT5 用历史数据跑通特征提取,再谈上线。
多任务-Stockformer 的三段式骨架
这套框架把离散小波变换和多任务自注意力拼在一起:前者做深度时序拆解,后者抓金融数据里跨资产的复杂依赖。两者协同后,同一套结构既能分析也能预测,不必为不同目标重搭模型。 整条链路分三个核心模块。先是时间序列分解,把原始数据劈成低频和高频两块——低频承载全局趋势,适合看长期形态;高频兜住短期波动、活性爆发和异常点。对金融序列来说,这种拆解直接决定后续特征能不能被提取干净。 分解完进双频时空编码器:低频走时态注意力看长期演变,高频进扩充因果卷积抓细微动态,再用图形注意力把时空依赖集成成多维嵌入。最后双频融合解码器用融合注意力把两路信号聚成统一潜在表述,由全连接层同时输出回报预测、趋势变化概率等多任务结果。市场高波动时,这种多任务并行比单任务模型更扛得住环境切换,但外汇和贵金属杠杆高、回撤快,任何模型输出都只是概率倾向,实盘前务必在 MT5 用历史数据自测。
「把双频时空流塞进一个 Stockformer 类」
多任务-Stockformer 在 MQL5 里的落地,核心是把分解、双频编码、NAFS 平滑、融合解码全部收进一个 CNeuronMultitaskStockformer 对象。所有内部对象都声明为静态,类构造和析构留空,真正的装配发生在 Init 方法里,顺序严格贴着前馈通路走。 Init 的外部参数里没有小波窗口大小和步长,这两个值在方法内写死:实验用 H1 历史数据,窗口固定 24 步(对应一天),步长 2,并加了序列长度越界检查。分解出的高/低频统一张量随后被拆成两条并行流分别处理。 低频走搭配相对位置编码的自注意力(CNeuronRMAT),不再需要原始框架里的额外位置编码层;高频走强化因果卷积(CNeuronDilatedCasualConv),卷积窗口最小 2 元素。两条流各接一个可训练位置编码层(CNeuronLearnabledPE),再分别进 NAFS 模块(CNeuronNAFS),高频序列因卷积性质会更短但共享其余参数。 解码器用自注意力处理低频、交叉注意力集成高频,输出相加后由卷积投影层压缩,最后交父类生成环境潜在表示。为省内存,求和层与两个注意力模块的梯度缓冲区指针在 Init 时同步,训练时误差直接从求和层透传。 下面这段是集成类的头文件骨架,能看到所有子模块都是 protected 静态成员,继承基类 CNeuronBaseOCL。 反量产提示:相对位置编码替代绝对 PE 不是装饰,它让序列元素相对距离自带进注意力分数,架构更瘦但效率更高,在 H1 上跑 24 步窗口时显存占用明显低于原始设计。 外汇与贵金属行情高波动、高杠杆,此类模型仅提供潜在状态表征,实盘信号须自行验证,亏损概率不低。
class CNeuronMultitaskStockformer : class="kw">public CNeuronBaseOCL { class="kw">protected: CNeuronDecouplingFlow cDecouplingFlow; CNeuronBaseOCL cLowFreqSignal; CNeuronBaseOCL cHighFreqSignal; CNeuronRMAT cTemporalAttention; CNeuronDilatedCasualConv cDilatedCasualConvolution; CNeuronLearnabledPE cLowFreqPE; CNeuronLearnabledPE cHighFreqPE; CNeuronNAFS cLowFreqGraphAttention; CNeuronNAFS cHighFreqGraphAttention; CNeuronDMHAttention cLowFreqFusionDecoder;
◍ Stockformer 多任务层的对象装配与初始化
在 MT5 的 OpenCL 神经网络框架里,CNeuronMultitaskStockformer 这个类把低频与高频特征做了解耦后再融合。类内首先挂了三个核心成员:cLowHighFreqFusionDecoder 用交叉双均值注意力做高低频融合,cLowHigh 是基类的双输出容器,cProjection 则负责卷积投影。 虚函数层面重写了 feedForward、calcInputGradients 和 updateInputWeights,说明这一层的前向和反向传播都走了自定义路径,而不是沿用基类默认实现。对外暴露的 Init 参数表很长:window、window_key、units_count、heads、layers、neurons_out、filters 这些直接决定网络宽度与注意力头数,调参时务必对齐张量维度。 Init 实现里有个硬数据点——wave_window 被钳制为 MathMin(24, units_count),即解码侧波动窗口上限就是 24 根 K 线。cDecouplingFlow 以 wave_window 和 units_count 初始化后立刻 SetActivationFunction(None),意味着解耦流本身不施加非线性,留待后续融合层处理。 开 MT5 把这段头文件塞进你的 EA 工程,重点核对 cDecouplingFlow.Init 的 filters 与 window 传参;若 units_count 小于 24,wave_window 会随之缩水,注意力感受野可能偏窄,EURUSD 或 XAUUSD 这类高波动品种上信号延迟概率会上升。
CNeuronCrossDMHAttention cLowHighFreqFusionDecoder; CNeuronBaseOCL cLowHigh; CNeuronConvOCL cProjection; class=class="str">"cmt">//--- class="kw">virtual class="type">bool feedForward(CNeuronBaseOCL *NeuronOCL) class="kw">override; class="kw">virtual class="type">bool calcInputGradients(CNeuronBaseOCL *prevLayer) class="kw">override; class="kw">virtual class="type">bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) class="kw">override; class="kw">public: CNeuronMultitaskStockformer(class="type">void) {}; ~CNeuronMultitaskStockformer(class="type">void) {}; class="kw">virtual class="type">bool Init(class="type">uint numOutputs, class="type">uint myIndex, COpenCLMy *open_cl, class="type">uint window, class="type">uint window_key, class="type">uint units_count, class="type">uint heads, class="type">uint layers, class="type">uint neurons_out, class="type">uint filters, ENUM_OPTIMIZATION optimization_type, class="type">uint batch); class=class="str">"cmt">//--- class="kw">virtual class="type">int Type(class="type">void) class="kw">override const { class="kw">return defNeuronMultitaskStockformer; } class=class="str">"cmt">//--- class="kw">virtual class="type">bool Save(class="type">int const file_handle) class="kw">override; class="kw">virtual class="type">bool Load(class="type">int const file_handle) class="kw">override; class=class="str">"cmt">//--- class="kw">virtual class="type">bool WeightsUpdate(CNeuronBaseOCL *source, class="type">float tau) class="kw">override; class="kw">virtual class="type">void SetOpenCL(COpenCLMy *obj) class="kw">override; }; class="type">bool CNeuronMultitaskStockformer::Init(class="type">uint numOutputs, class="type">uint myIndex, COpenCLMy *open_cl, class="type">uint window, class="type">uint window_key, class="type">uint units_count, class="type">uint heads, class="type">uint layers, class="type">uint neurons_out, class="type">uint filters, ENUM_OPTIMIZATION optimization_type, class="type">uint batch) { if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, neurons_out, optimization_type, batch)) class="kw">return false; class="type">uint index = class="num">0; class="type">uint wave_window = MathMin(class="num">24, units_count); if(!cDecouplingFlow.Init(class="num">0, index, OpenCL, wave_window, class="num">2, units_count, filters, window, optimization, iBatch)) class="kw">return false; cDecouplingFlow.SetActivationFunction(None); class=class="str">"cmt">//--- Dual-Frequency Spatiotemporal Encoder