您应当知道的 MQL5 向导技术(第 14 部分):以 STF 进行多意向时间序列预测·进阶篇
用相关系数矩阵给训练集做互信息初筛
神经网络喂数据前,先算两组序列的相关系数矩阵再取行列式,能快速判断输入特征之间、以及输入与滞后目标之间是否存在可用线性结构。下面这段逻辑把 X、Y 各拆成两段(当前段与平移 m_train_set 段),分别填进 _x1/_x2/_y1/_y2 四个向量。 _m[0][0] = _x1.CorrCoef(_x2) 算 X 自身两段的相关;_m[0][1] = _x1.CorrCoef(_y2) 算 X 当前段与 Y 滞后段的相关;右下角 _m[1][1] 则是 Y 两段自相关。最后 return(_m.Det()) 返回矩阵行列式,若 det 接近 0,说明这几组序列线性独立程度低,特征冗余可能偏大。 Cnetwork 类把权重、偏置、各隐层输出都声明为矩阵/向量数组,激活函数选了 Softplus 而非 ReLU,好处是在 x 为负时也有平滑梯度,避免某些外汇分钟序列出现死神经元。构造函数接收 Settings[] 决定每层维度,InitialWeight 与 InitialBias 做全网初始化。 开 MT5 把这段嵌进 EA 的预筛选模块,先打印 Det() 数值:EURUSD 的 M15 上若 m_train_set=30 时 det 长期落在 0.02~0.15 区间,说明直接进网络前或许该先做去相关处理。外汇与贵金属杠杆高,任何特征工程都只是概率层面的滤波,实盘前务必用历史数据复验。
{
_x1[i] = X[i];
_x2[i] = X[i+m_train_set];
_y1[i] = Y[i];
_y2[i] = Y[i+m_train_set];
}
_m[class="num">0][class="num">0] = _x1.CorrCoef(_x2);
_m[class="num">0][class="num">1] = _x1.CorrCoef(_y2);
_m[class="num">1][class="num">0] = _y1.CorrCoef(_x2);
_m[class="num">1][class="num">1] = _y1.CorrCoef(_y2);
class="kw">return(_m.Det());
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| |
class=class="str">"cmt">//+------------------------------------------------------------------+
class Cnetwork
{
class="kw">protected:
matrix weights[];
vector biases[];
vector inputs;
vector hidden_outputs[];
vector target;
class="type">int hidden_layers;
class="type">class="kw">double Softplus(class="type">class="kw">double X);
class="type">class="kw">double SoftplusDerivative(class="type">class="kw">double X);
class="kw">public:
vector output;
class="type">void Get(vector &Target)
{ target.Copy(Target);
}
class="type">void Set(vector &Inputs)
{ inputs.Copy(Inputs);
}
class="type">bool Get(class="type">class="kw">string File, class="type">class="kw">double &Criteria, class="type">class="kw">datetime &Version);
class="type">bool Set(class="type">class="kw">string File, class="type">class="kw">double Criteria);
class="type">void Forward();
class="type">void Backward(class="type">class="kw">double LearningRate);
class="type">void Cnetwork(class="type">int &Settings[],class="type">class="kw">double InitialWeight,class="type">class="kw">double InitialBias)
{
...
...
};
class="type">void ~Cnetwork(class="type">void) { };
};
class="type">void Cnetwork(class="type">int &Settings[],class="type">class="kw">double InitialWeight,class="type">class="kw">double InitialBias)
{ class="type">int _size = ArraySize(Settings);◍ 神经网络结构初始化的边界与权重铺设
这段初始化逻辑决定了 MT5 里自定义神经网络信号能否正常跑起来。它先卡死结构合法性:层数 _size 必须在 2 到 USHORT_MAX(65535)之间,且各层节点数配置的最小值要大于 0、最大值小于 65535,否则直接 printf 报错并放弃构造。
满足条件后,代码按 _size 动态申请三组数组:权重矩阵比层数少 1,偏置也比层数少 1,隐层输出比层数少 2,同时把 hidden_layers 记为 _size - 2。这意味着一个 _size=4 的网络,实际是 1 输入层 + 2 隐层 + 1 输出层的四层形态。
逐层 Init 时,第 i 个权重块形状为 [Settings[i+1]][Settings[i]],即后层节点数 × 前层节点数,随后用 InitialWeight 填满;偏置按后层节点数 Init 并用 InitialBias 填满。只有 i 小于 _size-2 的层才建隐层输出缓冲并清 0,输出层和目标向量则单独按末层节点数 Init。
在 MT5 里验证时,把 Settings 数组设成例如 {5,8,6,3} 传入,就能直接看到 5→8→6→3 的权重组被铺开;若把某项写成 0 或 65535,终端日志会抛出 invalid network settings,网络不初始化。外汇与贵金属行情下用这类模型信号属高风险,过拟合可能导致实盘表现与回测偏离。
if(_size >= class="num">2 && _size <= USHORT_MAX && Settings[ArrayMinimum(Settings)] > class="num">0 && Settings[ArrayMaximum(Settings)] < USHORT_MAX) { ArrayResize(weights, _size - class="num">1); ArrayResize(biases, _size - class="num">1); ArrayResize(hidden_outputs, _size - class="num">2); hidden_layers = _size - class="num">2; for(class="type">int i = class="num">0; i < _size - class="num">1; i++) { weights[i].Init(Settings[i + class="num">1], Settings[i]); weights[i].Fill(InitialWeight); biases[i].Init(Settings[i + class="num">1]); biases[i].Fill(InitialBias); if(i < _size - class="num">2) { hidden_outputs[i].Init(Settings[i + class="num">1]); hidden_outputs[i].Fill(class="num">0.0); } } output.Init(Settings[_size - class="num">1]); target.Init(Settings[_size - class="num">1]); } else { printf(__FUNCSIG__ + " invalid network settings. "); class=class="str">"cmt">//~Cnetwork(class="type">void); }
「多空训练集的特征拼装逻辑」
这段循环在给多头与空头模型分别灌入训练样本。多头侧用了 3 个输入维度:时间、空间矩阵与需求差;空头侧多塞了一个供给差,共 4 维,对应 __SHORTS_INPUTS 的设定。 多头里 ii==1 的空间矩阵,用 CopyRates 拉取品种 m_symbol 在 m_period 下的 2 号与 4 号缓冲区(通常是最高价与开盘价),各取 2*m_train_set 根 K 线,交给 G() 算几何特征。空头侧把 2 与 4 号缓冲区的角色对调,但长度同样是 2*m_train_set,这是两组对称采样。 需求差写成 m_high - m_open,供给差写成 m_open - m_low,都是相邻 K 线的实体偏移量。标签 y 的赋值发生在 i>0 时,多头用前一根 high-open,空头用前一根 open-low,相当于把「下一根相对涨跌」作为分类目标。 开 MT5 把这段粘进 EA 的模型训练函数,改 m_train_set 从 50 调到 200,能看到空间矩阵向量长度翻倍,回测耗时可能明显拉长,但样本覆盖的波动区间更宽。外汇与贵金属杠杆高,这类特征工程只是前置步骤,信号失效概率不低,实盘前务必离线验证。
{ if(ii==class="num">0)class=class="str">"cmt">//time
{ m_model_longs.x[i][ii] = T(m_time.GetData(i));
}
else if(ii==class="num">1)class=class="str">"cmt">//spatial matrix
{ vector _x,_y;
_x.CopyRates(m_symbol.Name(),m_period,class="num">2,StartIndex() + ii + i,class="num">2*m_train_set);
_y.CopyRates(m_symbol.Name(),m_period,class="num">4,StartIndex() + ii + i,class="num">2*m_train_set);
m_model_longs.x[i][ii] = G(_x,_y);
}
else if(ii==class="num">2)class=class="str">"cmt">//demand
{ m_model_longs.x[i][ii] = (m_high.GetData(StartIndex() + ii + i) - m_open.GetData(StartIndex() + ii + i));
}
}
if(i > class="num">0) class=class="str">"cmt">//assign classifier
{ m_model_longs.y[i - class="num">1] = (m_high.GetData(StartIndex() + i - class="num">1) - m_open.GetData(StartIndex() + i - class="num">1));
}
}
for(class="type">int i = class="num">0; i < m_train_set; i++)
{ for(class="type">int ii = class="num">0; ii < __SHORTS_INPUTS; ii++)
{ if(ii==class="num">0)class=class="str">"cmt">//time
{ m_model_shorts.x[i][ii] = T(m_time.GetData(i));
}
else if(ii==class="num">1)class=class="str">"cmt">//spatial matrix
{ vector _x,_y;
_x.CopyRates(m_symbol.Name(),m_period,class="num">4,StartIndex() + ii + i,class="num">2*m_train_set);
_y.CopyRates(m_symbol.Name(),m_period,class="num">2,StartIndex() + ii + i,class="num">2*m_train_set);
m_model_shorts.x[i][ii] = G(_x,_y);
}
else if(ii==class="num">2)class=class="str">"cmt">//demand
{ m_model_shorts.x[i][ii] = (m_high.GetData(StartIndex() + ii + i) - m_open.GetData(StartIndex() + ii + i));
}
else if(ii==class="num">3)class=class="str">"cmt">//supply
{ m_model_shorts.x[i][ii] = (m_open.GetData(StartIndex() + ii + i) - m_low.GetData(StartIndex() + ii + i));
}
}
if(i > class="num">0) class=class="str">"cmt">//assign classifier
{ m_model_shorts.y[i - class="num">1] = (m_open.GetData(StartIndex() + i - class="num">1) - m_low.GetData(StartIndex() + i - class="num">1));
}
}EURUSD 日线神经网络跑出来的真实样子
用 2023 年 EURUSD 日线数据做了一次最简网络的测试运行。需求预测网络 3 层、规模为 3/5/1,供应预测网络因输入不同为 4/5/1,尾层皆为单输出。组装 EA 走的是 MQL5 向导,网络类上限能到 UCHAR_MAX 层与同量级层宽,但本次只用了最小配置。 测试不带止盈目标,信号逆转才平仓,偏向抓主要趋势。全年成交笔数很少,但单笔持仓久、回撤在周期内最小,品质算稳。不过横盘震荡里杠杆对微波动敏感,日线能忽略的噪音放到 4 小时甚至 1 小时会放大,计算量近似指数增长——这还是隐藏层仅 5 点的极简结构。 网络类自带权重与偏移的导入导出函数,本次没用,跨年扩展测试才需要。默认逻辑是“越大越好”写文件,若更在意回撤得改类内代码:仅当本次准则值小于前值才更新,且起始默认值要设 DBL_MAX 这类足够高的数,免得误写。外汇与贵金属属高风险,小周期验证前先估好自己的算力与回撤承受。
◍ 记住这一条就够了
STF 把时空双重预测揉进一个网络,思路不常见,但代价也直白:我们测试里用的 G 矩阵若拆成 n×n 输入,算力开销会随维度平方往上跳。 论文里用网络变换器的实现把这点放大得很清楚——神经网络跑 STF 本质是计算密集型,得喂相当大量的数据做交叉验证才靠得住。 MQL5 向导仍是快速原型验证的趁手工具,附带 Network.mqh(10.87 KB)和 nn_a.mq5(6.59 KB)可直接开 MT5 编译跑通。外汇与贵金属波动剧烈、杠杆高风险极高,任何模型都只是概率倾向,别当成确定性依据。