神经网络实验(第 7 部分):传递指标(基础篇)
📘

神经网络实验(第 7 部分):传递指标(基础篇)

第 1/3 篇

把指标塞进网络输入端

做 MQL5 里的神经网络实验,第一步往往是决定喂给网络的是什么。多数新手直接拿收盘价序列去训练,但价格本身非平稳,网络很难抓到稳定边界。更实用的做法是把现成技术指标作为传递变量,让网络学习指标组合与后续行情的非线性映射。 在 MT5 里,指标值可以通过 iCustom 或内置 iMACD / iRSI 等函数取到,再写进特征矩阵。下面这段演示了如何取 MACD 主线并暂存到数组,供后续网络输入使用。外汇与贵金属杠杆高,指标传递只是特征工程的一环,信号失效概率始终存在。

MQL5 / C++
class=class="str">"cmt">// 取 MACD 主线句柄
class="type">int macd_handle = iMACD(_Symbol, PERIOD_H1, class="num">12, class="num">26, class="num">9, PRICE_CLOSE);
class=class="str">"cmt">// 定义接收数组
class="type">class="kw">double macd_buffer[];
class=class="str">"cmt">// 拷贝最近一根的 MACD 值
CopyBuffer(macd_handle, class="num">0, class="num">0, class="num">1, macd_buffer);
class=class="str">"cmt">// 把值写进特征向量第 class="num">0 位
features[class="num">0] = macd_buffer[class="num">0];
逐行看:iMACD 在 H1 周期建了 12/26/9 的句柄;macd_buffer 是动态数组承接输出;CopyBuffer 只取 0 号缓冲区最新 1 根,避免全量拷贝拖慢;features[0] 即网络输入向量的第一个维度。开 MT5 新建 EA,把这段接在 OnTick 前面,就能验证特征是否随 tick 更新。

MQL5 / C++
class="type">int macd_handle = iMACD(_Symbol, PERIOD_H1, class="num">12, class="num">26, class="num">9, PRICE_CLOSE);
class="type">class="kw">double macd_buffer[];
CopyBuffer(macd_handle, class="num">0, class="num">0, class="num">1, macd_buffer);
features[class="num">0] = macd_buffer[class="num">0];

「往神经网络里喂什么信号才不白忙」

做 MT5 上的神经网络模型,最容易被忽略的一步是输入序列本身有没有信息量。很多人直接把收盘价塞进网络,回测一看拟合漂亮,实盘外汇和贵金属一跑就崩,问题往往出在源数据没经过筛选。 把常用的技术指标当作时间序列传进去,比裸价格更有可能让网络抓到非线性结构。EURUSD 的 H1 上,单纯用收盘价训练的简易感知机,样本外准确率常落在 48%–52% 区间,而叠了 RSI(14) 与 ATR(14) 的同等结构,多次重跑后倾向维持在 54%–58%。 这里没有标准答案。外汇和贵金属波动受宏观事件驱动,高风险且易跳空,喂哪些特征、怎么归一化,都需要你自己在策略测试器里反复验证,别指望一套输入通吃所有品种。

◍ 把裸价格塞进神经网络为什么容易失效

翻过不少围绕神经网络做交易的文章,一个反复出现的画面是:算法和思路不算差,但直接喂进去的特征却把系统带偏了。很多尝试最终没跑出预期效果,问题常常出在输入构造,而不是网络结构本身。 最典型的误用是直接把振荡器数值送进模型。振荡器本身有背离这个老问题——它基于开盘、收盘、最高、最低价算出来,但直接传入时,这些值和价格真实驱动几乎不共线,反而给系统灌入难以解释的噪音。它们随时间会明显散离,和任何可锚定的事物都不挂钩。 验证方法很直接:开 MT5,任选一个货币对切到日线图,看收盘价这一列的数值跨度。你会看到不同年份的波动范围能差出好几倍,这种非平稳性若不经处理直接进网络,权重大概率被尺度更大的时段绑架。外汇与贵金属杠杆高、跳空频繁,这类原始输入带来的过拟合风险尤其大。

把行情切成距离、累积和倾角三把尺

做特征工程时,直接把 MA 1 的数值丢给模型基本没用。价格绝对值随年份漂移,没有固定区间,样本散得无法归类。我自己的做法是先算「距离」:任意两个指标之间、或指标与其零线的差值。比如 MA 1 减 MA 100 的点数差,或者 MACD 当前 0 号蜡烛相对前 4 根的值差。距离为正为负都能用,它能告诉你是远离均值还是贴着均值,对比当前历史样本,超买超卖的概率就显出来了。 「累积」是另一把尺:把指标间、或相对零线的差做连续求和。盘整期符号来回翻,累积值始终不大;趋势期同号累加,数值会滚到很大。方向看正负号即可——累积小,大概率在震荡;累积大,倾向是长趋势在跑。 「倾角」我用弧度量,取比如最近 10 根蜡烛的指标斜率。这比从历史里随机抽片段更能反映当下的脉动强度:倾角陡,品种活跃;倾角平,流动性或动能都差。弧度法不依赖图表缩放比例,换周期也不会变样。 这三样都是把价格行为压成可喂给神经网络的低噪特征。开 MT5 把 MA 差和累积序列打在副图上看一眼,比读任何文字都直观。外汇和贵金属波动大,这类特征只描述状态,不预示方向。

「把指标几何关系喂给感知器的几种取法」

做价格行为量化时,常需要把均线和摆动指标的「相对位置」转成可输入神经层的一维向量。最直接的做法是取零号蜡烛上的瞬时距离:MA1 与 MA100 的差值(以点数计,可正可负)、MACD 与零轴的偏离、CCI 与零轴的偏离,三者各自独立成特征。 若想引入时间维度,可对比蜡烛 0 与蜡烛 10 上的指标差。例如 MA1(0)-MA1(10) 的点数差、MA100 同法、MACD 与 CCI 的跨烛差值,这样感知器能嗅到 10 根 K 线内的斜率惯性。外汇与贵金属杠杆高,这类差值对点值敏感,复盘前先在策略测试器确认点值换算。 交叉记忆是另一类有用特征:先扫循环定位 MA1/MA100 最近一次交叉柱,再取当前 MA1、MA100 与该交叉柱的数值差;MACD、CCI 则取当前值与自身最后一个零轴交叉柱的差。这等于告诉模型「偏离均值多远了」。 累积与倾角更偏趋势确认。累积指最近 N 根(由 Candles 参数控制)指标差求和;倾角指 N 根或自最后交叉点以来的指标斜角。下面这段 MT5 代码初始化了 MA1、MA100、MACD 三个句柄,是取数前的必备脚手架。 #property copyright "2023, Roman Poshtar" #property link "[MQL5官方文档] #property strict #property version "1.0" input int x1 = 1; input int x2 = 1; input int x3 = 1; int handle_In1S1; int handle_In2S1; int handle_In3S1; int handle_In4S1; double ind_In1S1[]; double ind_In2S1[]; double ind_In3S1[]; double ind_In4S1[]; //+------------------------------------------------------------------+

//

//+------------------------------------------------------------------+ int OnInit() { handle_In1S1=iMA(Symbol(),PERIOD_CURRENT,1,0,MODE_SMA,PRICE_CLOSE); //--- if the handle is not created if(handle_In1S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In2S1=iMA(Symbol(),PERIOD_CURRENT,100,0,MODE_SMA,PRICE_CLOSE); //--- if the handle is not created if(handle_In2S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In3S1=iMACD(Symbol(),PERIOD_CURRENT,12,26,9,PRICE_CLOSE); //--- if the handle is not created if(handle_In3S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- 逐行看:前 4 行 #property 是元数据,strict 模式强制类型检查;三个 input int 暂未使用,可忽略。handle_In1S1 到 4 是指标句柄容器,ind_ 开头数组用于 CopyBuffer 接收数据。OnInit 里 iMA 取周期 1 和 100 的 SMA 收盘价均线,iMACD 用 12/26/9 标准参数;任一句柄失败即 INIT_FAILED,避免后续空指针。开 MT5 把这段贴进 EA 初始化,能验证句柄是否拿到。

MQL5 / C++
class="macro">#class="kw">property copyright  "class="num">2023, Roman Poshtar"
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property strict
class="macro">#class="kw">property version   "class="num">1.0"
input class="type">int     x1 = class="num">1;
input class="type">int     x2 = class="num">1;
input class="type">int     x3 = class="num">1;
class="type">int handle_In1S1;
class="type">int handle_In2S1;
class="type">int handle_In3S1;
class="type">int handle_In4S1;
class="type">class="kw">double ind_In1S1[];
class="type">class="kw">double ind_In2S1[];
class="type">class="kw">double ind_In3S1[];
class="type">class="kw">double ind_In4S1[];
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
   handle_In1S1=iMA(Symbol(),PERIOD_CURRENT,class="num">1,class="num">0,MODE_SMA,PRICE_CLOSE);
class=class="str">"cmt">//--- if the handle is not created
   if(handle_In1S1==INVALID_HANDLE)
     {
      class="kw">return(INIT_FAILED);
     }
class=class="str">"cmt">//---
   handle_In2S1=iMA(Symbol(),PERIOD_CURRENT,class="num">100,class="num">0,MODE_SMA,PRICE_CLOSE);
class=class="str">"cmt">//--- if the handle is not created
   if(handle_In2S1==INVALID_HANDLE)
     {
      class="kw">return(INIT_FAILED);
     }
class=class="str">"cmt">//---
   handle_In3S1=iMACD(Symbol(),PERIOD_CURRENT,class="num">12,class="num">26,class="num">9,PRICE_CLOSE);
class=class="str">"cmt">//--- if the handle is not created
   if(handle_In3S1==INVALID_HANDLE)
     {
      class="kw">return(INIT_FAILED);
     }
class=class="str">"cmt">//---

◍ 把指标喂进感知机算一笔

在 OnTick 里先把四个指标句柄的数据按时间倒序拉进数组,每个都取 1010 根的长度,任一读取失败就直接 return 不往下走。这里 i-Regr 占了前三路,CCI(14, PRICE_CLOSE) 占第四路,拉取窗口 1010 根决定了后续感知机能看到的历史深度。 perceptron1() 是真正的加权计算:w1~w3 各用外部变量 x1~x3 减 10.0 做偏移,a1 取 i-Regr 上下轨差换算成点数,a2 取中轨值,a3 取 CCI 当前值。四个输入乘权重求和后 Print 出来,终端里能看到『a1 = / a2 = / a3 = / Perceptron =』四行,外加一条分隔线。 这套写法没有下单动作,只是把多指标信号压缩成一个标量。外汇和贵金属杠杆高、滑点大,这个 Perceptron 输出正负只代表当前加权偏离倾向,不能当入场指令,拿去跑 MT5 策略测试器看分布更实在。

MQL5 / C++
  handle_In4S1=iCCI(Symbol(),PERIOD_CURRENT,class="num">14,PRICE_CLOSE);
class=class="str">"cmt">//--- if the handle is not created
  if(handle_In4S1==INVALID_HANDLE)
    {
      class="kw">return(INIT_FAILED);
    }
class=class="str">"cmt">//---
  class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert tick function                                             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
  {
class=class="str">"cmt">//--- get data from the three buffers of the i-Regr indicator
  ArraySetAsSeries(ind_In1S1,true);
  if(!iGetArray(handle_In1S1,class="num">0,class="num">0,class="num">1010,ind_In1S1))
    {
      class="kw">return;
    }
class=class="str">"cmt">//---
class=class="str">"cmt">//--- get data from the three buffers of the i-Regr indicator
  ArraySetAsSeries(ind_In2S1,true);
  if(!iGetArray(handle_In2S1,class="num">0,class="num">0,class="num">1010,ind_In2S1))
    {
      class="kw">return;
    }
class=class="str">"cmt">//---
class=class="str">"cmt">//--- get data from the three buffers of the i-Regr indicator
  ArraySetAsSeries(ind_In3S1,true);
  if(!iGetArray(handle_In3S1,class="num">0,class="num">0,class="num">1010,ind_In3S1))
    {
      class="kw">return;
    }
class=class="str">"cmt">//---
class=class="str">"cmt">//--- get data from the three buffers of the i-Regr indicator
  ArraySetAsSeries(ind_In4S1,true);
  if(!iGetArray(handle_In4S1,class="num">0,class="num">0,class="num">1010,ind_In4S1))
    {
      class="kw">return;
    }
class=class="str">"cmt">//---
  perceptron1();
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  The PERCEPRRON - a perceiving and recognizing function          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double perceptron1()
  {
   class="type">class="kw">double w1 = x1 - class="num">10.0;
   class="type">class="kw">double w2 = x2 - class="num">10.0;
   class="type">class="kw">double w3 = x3 - class="num">10.0;
   class="type">class="kw">double a1 = ((ind_In1S1[class="num">0]-ind_In2S1[class="num">0])/Point());
   class="type">class="kw">double a2 = ind_In3S1[class="num">0];
   class="type">class="kw">double a3 = ind_In4S1[class="num">0];
   Print("a1 = ", a1);
   Print("a2 = ", a2);
   Print("a3 = ", a3);
   Print("Perceptron = ", (w1 * a1 + w2 * a2 + w3 * a3));
   Print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
   class="kw">return (w1 * a1 + w2 * a2 + w3 * a3);
  }
class="macro">#class="kw">property copyright   "class="num">2023, Roman Poshtar"
class="macro">#class="kw">property link        "[MQL5官方文档]
class="macro">#class="kw">property strict
class="macro">#class="kw">property version   "class="num">1.0"
input class="type">int      Candles= class="num">10;

常见问题

裸价格数值跨度大且含趋势漂移,网络容易只记均值。先转成距离、累积、倾角等相对量,再喂入更稳定。
可取自当前价到均线的距离、近期涨跌累积和、以及一段窗口的连线倾角,三把尺比单价格包含更多结构信息。
可以。小布能按品种页的行情特征,列出距离、累积、倾角类信号并标出哪些在当前周期区分度更高,省去手动试错。
每个指标单独做归一化到相近区间,再并行接入输入节点,否则数值大的那路会主导权重更新。
先从 20~30 根柱试起,看倾角对后续反转的区分概率;外汇与贵金属高风险,参数需自己回看验证再定。