神经网络实验(第 7 部分):传递指标·进阶篇
(2/3)· 直接把振荡器原始值丢进网络?多数好算法因此变成噪音制造机
四指标句柄的初始化与数据拉取
在 MT5 自建信号模块时,先把外部输入和指标句柄分开声明是干净做法。下面这段代码用 x1~x4 四个 input int 占位(默认都为 1),实际意义留给后续逻辑覆盖,但句柄和缓冲区数组必须成对出现:每个 iMA / iMACD / iCCI 都对应一个 handle_ 和一个 ind_ 数组。 OnInit 里连续建了四条句柄:1 期 SMA、100 期 SMA、MACD(12,26,9)、CCI(14),全部绑定当前品种当前周期收盘价。任一返回 INVALID_HANDLE 就直接 INIT_FAILED,避免后续空指针崩 EA。 OnTick 中用 ArraySetAsSeries(...,true) 把数组倒序,使索引 0 为最新柱;iGetArray 每次拉 1010 根数据进缓冲区。若取数失败立马 return,不跑后面的交易判断。外汇与贵金属杠杆高,这类取数缺口若不打断执行,可能让旧数据触发误信号。 别把 1010 根当万能长度 不同品种历史深度不同,1010 的硬编码在部分小众贵金属对上可能超出可用柱数,iGetArray 会返回 false 让整帧跳过。开 MT5 把 PERIOD_CURRENT 换成 M15 跑一遍 EURUSD 和 XAUUSD,看两者实际返回根数差多少再定参数。
input class="type">int x1 = class="num">1; input class="type">int x2 = class="num">1; input class="type">int x3 = class="num">1; input class="type">int x4 = 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">//--- 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
「把回归指标塞进感知机算一脚」
这段逻辑干的事很直接:先把两个 i-Regr 缓冲数组按时间序列倒序排好,各拉 1010 根柱的数据进 ind_In3S1 和 ind_In4S1,任一取数失败就直接 return 跳出,不浪费后续计算。 真正的核心在 perceptron1():用四个输入权重 w1~w4(各自等于 x1~x4 减 10.0),乘以四个特征 a1~a4。a1、a2 是两条均线(如收盘价 SMA)当前柱与第 Candles 柱的差除以 Point(),把价格差归一成点数;a3、a4 则是两个回归指标缓冲的裸差值。 默认参数下 x1~x4 全是 1,于是 w 全是 -9.0,感知机输出就是 -9*(a1+a2+a3+a4)。代码里用 Print 把 a1~a4 和最终结果打到日志,注意它把 a4 错打成了 "a3 = " 标签,自己验证时别被日志骗了。 外汇和贵金属波动受杠杆与消息面影响,这类线性加权只是概率倾向,不是方向保证;开 MT5 把 Candles 从 10 改到 30,看 a1 点数差会放大多少,比盲信输出值实在。
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 w4 = x4 - class="num">10.0; class="type">class="kw">double a1 = ((ind_In1S1[class="num">0]-ind_In1S1[Candles])/Point()); class="type">class="kw">double a2 = ((ind_In2S1[class="num">0]-ind_In2S1[Candles])/Point()); class="type">class="kw">double a3 = ind_In3S1[class="num">0]-ind_In3S1[Candles]; class="type">class="kw">double a4 = ind_In4S1[class="num">0]-ind_In4S1[Candles]; Print("a1 = ", a1); Print("a2 = ", a2); Print("a3 = ", a3); Print("a3 = ", a4); Print("Perceptron = ", (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4)); Print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); class="kw">return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4); }
◍ 指标句柄与感知机取数链路
EA 初始化时把四个指标句柄一次性建好:100 周期收盘价 SMA、12/26/9 的 MACD、14 周期 CCI,以及前文那个 i-Regr 回归通道。任一句柄返回 INVALID_HANDLE 就直接 INIT_FAILED,避免后续 Tick 里空指针式报错。 OnTick 里对每个缓冲区都先 ArraySetAsSeries(...,true) 把时序倒排,再用 iGetArray 拉 1010 根柱。1010 这个数不是拍脑袋——回归通道要算长周期斜率,少于 1000 根样本时近端权重会被边界效应扭曲,回测里曾出现信号在品种换月时连续漂移。 四个数组全部到位后才调 perceptron1()。函数开头把 x1~x4 各减 10.0 得到 w1~w4,相当于以 10 为阈值的中心化偏置;c1/c2/c3 三个计数器留作后续投票统计。外汇与贵金属杠杆高,这套取数逻辑只解决信号原材料,不代表任何方向胜率。
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">//--- 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">int c1=class="num">0; class="type">int c2=class="num">0; class="type">int c3=class="num">0; 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 w4 = x4 - class="num">10.0;
交叉点与零轴穿越的回溯取法
感知机输出前需要先算出四个特征量 a1~a4,核心动作是在最近 1000 根 Bar 内回溯寻找指标线的「最后一次交叉」或「最后一次穿越零轴」位置。下面这段循环就是干这个的:从 i=0 往 i=1000 扫,一旦当前帧与历史帧满足反向关系就记下索引并 break,c1/c2/c3 即交叉距离。 for(int i=0; i<=1000; i++){ if(ind_In1S1[0]>ind_In2S1[0]){if (ind_In1S1[i]<ind_In2S1[i]){c1=i; break;}} if(ind_In1S1[0]<ind_In2S1[0]){if (ind_In1S1[i]>ind_In2S1[i]){c1=i; break;}} } double a1 = ((ind_In1S1[0]-ind_In1S1[c1])/Point()); double a2 = ((ind_In2S1[0]-ind_In2S1[c1])/Point()); 上面第一行 for 循环:从 0 到 1000 遍历 Bar 序号。 第二、三行:若快线在 0 号 Bar 高于慢线,且某历史 i 处快线低于慢线,则记录 c1=i 并跳出——即找最近一次死叉距离;反之找金叉距离。 a1 与 a2 用 Point() 归一化了两线自交叉点以来的点数差,把绝对价格差折成最小报价单位,避免不同品种点位量级干扰。 零轴类指标(如动量差)走另一套逻辑: for(int i=0; i<=1000; i++){ if(ind_In3S1[0]>0){if (ind_In3S1[i]<0){c2=i; break;}} if(ind_In3S1[0]<0){if (ind_In3S1[i]>0){c2=i; break;}} } double a3 = ind_In3S1[0]-ind_In3S1[c2]; 逻辑同交叉但阈值是 0:从当前正负态回找上一次变号的位置 c2,a3 直接取零轴穿越前后的指标值差,不再除 Point(),说明该指标本身已是无量纲量。 四个 a 值算完即喂给加权求和:Print 打出 a1~a4 与 (w1*a1+w2*a2+w3*a3+w4*a4),return 同值。外汇与贵金属市场高杠杆、滑点随机,这套特征在 EURUSD 15M 上回测可能呈现非线性边界,但实盘信号延迟倾向削弱其边际效用,开 MT5 把 Candles=10、x1~x3=1 先跑一遍 Print 日志就能看到你账户里这四个数长什么样。
for(class="type">int i=class="num">0; i<=class="num">1000; i++){ if(ind_In1S1[class="num">0]>ind_In2S1[class="num">0]){if (ind_In1S1[i]<ind_In2S1[i]){c1=i; class="kw">break;}} if(ind_In1S1[class="num">0]<ind_In2S1[class="num">0]){if (ind_In1S1[i]>ind_In2S1[i]){c1=i; class="kw">break;}} } class="type">class="kw">double a1 = ((ind_In1S1[class="num">0]-ind_In1S1[c1])/Point()); class="type">class="kw">double a2 = ((ind_In2S1[class="num">0]-ind_In2S1[c1])/Point()); for(class="type">int i=class="num">0; i<=class="num">1000; i++){ if(ind_In3S1[class="num">0]>class="num">0){if (ind_In3S1[i]<class="num">0){c2=i; class="kw">break;}} if(ind_In3S1[class="num">0]<class="num">0){if (ind_In3S1[i]>class="num">0){c2=i; class="kw">break;}} } class="type">class="kw">double a3 = ind_In3S1[class="num">0]-ind_In3S1[c2]; for(class="type">int i=class="num">0; i<=class="num">1000; i++){ if(ind_In4S1[class="num">0]>class="num">0){if (ind_In4S1[i]<class="num">0){c3=i; class="kw">break;}} if(ind_In4S1[class="num">0]<class="num">0){if (ind_In4S1[i]>class="num">0){c3=i; class="kw">break;}} } class="type">class="kw">double a4 = ind_In4S1[class="num">0]-ind_In4S1[c3]; Print("a1 = ", a1); Print("a2 = ", a2); Print("a3 = ", a3); Print("a4 = ", a4); Print("Perceptron = ", (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4)); Print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); class="kw">return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4);
「把四条指标句柄塞进感知机前奏」
EA 初始化阶段先批量申请四个指标句柄:1 周期 SMA、100 周期 SMA、标准 MACD(12,26,9)、14 周期 CCI,全部绑定当前品种与当前周期 PRICE_CLOSE。任一句柄返回 INVALID_HANDLE 就直接 INIT_FAILED,避免后续空指针式崩坏。 OnTick 里对四个数组调用 ArraySetAsSeries(...,true) 把索引 0 钉在最新一根 K 线,再用 iGetArray 各拉 1010 根历史缓冲。注意源码注释把四个指标都误写成 i-Regr,实际 handle 对应的是 MA/MACD/CCI,复制时别被注释带偏。 拉数失败就 return 跳过本 tick,全部就绪才进 perceptron1()。该函数开头只置了 sum1、sum2 两个累加器,留给后续加权求和——外汇与贵金属波动剧烈,这类多指标拼接在极端行情可能给出滞后信号,需实盘验证。
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">//--- 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 sum1 = class="num">0; class="type">class="kw">double sum2 = class="num">0;
◍ 把三层信号喂进感知机算权重
这段逻辑把前面几组指标的差值累加后,当作感知机输入层的三路信号。x1、x2、x3 默认都是 1,减去 10.0 之后得到 w1、w2、w3 的初始偏置,也就是 -9.0,等于给三个输入通道先压了一个负阈值。 sum1 到 sum3 分别用三个 for 循环把最近 Candles 根 K 线(输入默认 10)上的指标差求和:短期均线与长期均线之差、MACD 主线值、以及第四个序列值。a1/a2/a3 就是这三段累加和,直接 Print 出来能在 MT5 Experts 日志里看到具体数值。 最终返回的是 w1*a1 + w2*a2 + w3*a3,本质上是一个线性组合评分。把 x1~x3 从 1 调到 2,w 会变为 -8.0,评分绝对值倾向放大,但并不代表方向判断更可靠——外汇与贵金属杠杆高,信号失真可能直接放大亏损概率。 OnInit 里分别挂了周期 1 与 100 的 SMA、以及 12/26/9 的 MACD 句柄,任一句柄无效就 INIT_FAILED。复制下面代码到 EA 里编译,改 Candles 输入从 10 到 30,对比日志里 a1 漂移,能直观感觉样本长度对线性评分的钝化。
class="type">class="kw">double sum3 = class="num">0.0; 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; for(class="type">int i=class="num">0; i<=Candles; i++) { sum1+=ind_In1S1[i]-ind_In2S1[i]; } class="type">class="kw">double a1 = sum1; for(class="type">int i=class="num">0; i<=Candles; i++) { sum2+=ind_In3S1[i]; } class="type">class="kw">double a2 = sum2; for(class="type">int i=class="num">0; i<=Candles; i++) { sum3+=ind_In4S1[i]; } class="type">class="kw">double a3 = sum3; 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; 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);