神经网络实验(第 4 部分):模板·综合运用
(3/3)·从扇形平行四边形到三角形,手把手跑完 20 次优化与一年前向验证的全流程
把多周期收盘价差压成百点整数栅格
这段逻辑在做一件事:把当前品种不同 K 线收盘价的相对位移,折算成以 100 点(Point)为步长的整数,塞进 xInputs 数组供后续感知机调用。注意它给远端收盘价统一叠加了 800*Point 的偏移,相当于在价格差里预置了一个 80 点(对多数外汇对是 8 个点)的缓冲带,避免贴近现价时噪声触发误判。 以 a4 为例:用 13 根 K 前收盘减去(1 根前收盘 + 800 点),再除以 Point 取整,最后 (a4/100)*100 把结果吸附到最近的百点整数。xInputs[3] 到 [6] 都是类似结构,只是把 13/17/21 等不同滞后位置与 24 根前收盘做差值对照。 下面这段 perceptron1 把外部传入的 x1~x4、y1~y4 各减 10.0 得到权重 w、v 序列,又补了一段 a1、a2 的计算:a1 取 1 根前与 48 根前收盘各自加 1200 点后的均值减现价,同样百点化;a2 则是 9 根前减(现价+1200点)。外汇与贵金属杠杆高,这类栅格化特征对点值敏感,直接上实盘前务必在 MT5 策略测试器用历史数据核对偏移量是否匹配你的品种小数点位数。
class="type">int a4 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">13)-(iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)+(class="num">800*Point())))/Point()); xInputs[class="num">3] = (class="type">int)(a4/class="num">100)*class="num">100; class="type">int g1 = (class="type">int)(((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24)+(class="num">800*Point()))-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">13))/Point()); xInputs[class="num">4] = (class="type">int)(g1/class="num">100)*class="num">100; class="type">int g2 = (class="type">int)(((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24)+(class="num">800*Point()))-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">17))/Point()); xInputs[class="num">5] = (class="type">int)(g2/class="num">100)*class="num">100; class="type">int g3 = (class="type">int)(((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24)-(class="num">800*Point()))-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">21))/Point()); xInputs[class="num">6] = (class="type">int)(g3/class="num">100)*class="num">100; class="type">int b4 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24)-((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">800*Point()))+(iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24)-(class="num">800*Point()))/class="num">2))/Point()) ; xInputs[class="num">7] = (class="type">int)(g4/class="num">100)*class="num">100; class="kw">return(class="num">1); } 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 v1 = y1 - class="num">10.0; class="type">class="kw">double v2 = y2 - class="num">10.0; class="type">class="kw">double v3 = y3 - class="num">10.0; class="type">class="kw">double v4 = y4 - class="num">10.0; class="type">int a1 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)+(class="num">1200*Point()))+(iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48)+(class="num">1200*Point()))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1))/Point()); a1 = (class="type">int)(a1/class="num">100)*class="num">100; class="type">int a2 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">9)-(iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)+(class="num">1200*Point())))/Point()); a2 = (class="type">int)(a2/class="num">100)*class="num">100;
「用 1200 点偏移把 K 线距离整数化」
这段逻辑在做一件事:把不同周期收盘价的差值,先加减 1200*Point 的偏移,再除以 Point 转成整数点,最后用 /100*100 向下取整到百点粒度。偏移量 1200 点在 EURUSD 这类 Point=0.00001 的品种上等于 12 个基点,相当于人为给距离判读加了一道缓冲带,避免毛刺触发误判。 以 a3 为例,它取第 17 根收盘价与「第 1 根收盘价减 1200 点」的差,再转点取整到百点;a4 则换成第 25 根与「第 1 根加 1200 点」的差。b1~b4 反过来用第 48 根加/减 1200 点去减更早的收盘价,覆盖 25/33/41/1 号 bar,构成一组对称的距离采样。 CandlePatterns 里把结果写进 xInputs[0],但注意 a1 和 a2 连续两次覆盖同一槽位——后写的 a2 会直接冲掉 a1,这大概率是调试残留。开 MT5 把这段贴进 EA,打印 xInputs[0] 你会看到它只反映 a2 的百点值,外汇贵金属波动剧烈,这种覆盖 bug 可能让样本特征失真。
class="type">int a3 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">17)-(iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">1200*Point())))/Point()); a3 = (class="type">int)(a3/class="num">100)*class="num">100; class="type">int a4 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">25)-(iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)+(class="num">1200*Point())))/Point()); a4 = (class="type">int)(a4/class="num">100)*class="num">100; class="type">int b1 = (class="type">int)(((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48)+(class="num">1200*Point()))-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">25))/Point()); b1 = (class="type">int)(b1/class="num">100)*class="num">100; class="type">int b2 = (class="type">int)(((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48)+(class="num">1200*Point()))-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">33))/Point()); b2 = (class="type">int)(b2/class="num">100)*class="num">100; class="type">int b3 = (class="type">int)(((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48)-(class="num">1200*Point()))-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">41))/Point()); b3 = (class="type">int)(b3/class="num">100)*class="num">100; class="type">int b4 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48)-((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">1200*Point()))+(iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48)-(class="num">1200*Point()))/class="num">2))/Point()) ; b4 = (class="type">int)(b4/class="num">100)*class="num">100; class="kw">return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4 + v1 * b1 + v2 * b2 + v3 * b3 + v4 * b4); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//|percentage of each part of the candle respecting total size | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int CandlePatterns(class="type">class="kw">double &xInputs[]) { class="type">int a1 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)+(class="num">1200*Point()))+(iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48)+(class="num">1200*Point()))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1))/Point()); xInputs[class="num">0] = (class="type">int)(a1/class="num">100)*class="num">100; class="type">int a2 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">9)-(iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)+(class="num">1200*Point())))/Point()); xInputs[class="num">0] = (class="type">int)(a2/class="num">100)*class="num">100;
◍ 用感知机把多周期价差压成整百点距
这段逻辑在干一件事:把不同偏移量的收盘价落差换算成以『点』为单位的整数,再向下取整到百位,塞进 xInputs[0] 做后续感知机输入。注意它反复覆盖同一个数组元素,说明此处只是分步调试各周期差值的观测段,并非最终并行特征。 以 a3 为例:取 17 根 K 线前收盘,减去(最新收盘减 1200 点),除以 Point() 转成整数点距。1200 点在外汇黄金里约等于常见品种 12 美元跳动,属于人为加宽的缓冲带,用来过滤毛刺。 g4 那行最绕:用 48 根前收盘,减去『最新减1200点』与『48根前减1200点』的均值,再除 Point()。它本质上在量 48 周期中轨和近端下沿的偏离,结果同样被百点化。 perceptron1() 里 w1~w4 各自减 10.0,再把 a1 算成『(最新+800点+24根前)/2 与最新的差』的百点整数。外汇和贵金属杠杆高,这类点距阈值直接上实盘前,务必在 MT5 策略测试器用对应品种点值核对,否则不同 broker 的 Point() 精度会让信号偏移。
class="type">int a3 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">17)-(iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">1200*Point())))/Point()); xInputs[class="num">0] = (class="type">int)(a3/class="num">100)*class="num">100; class="type">int a4 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">25)-(iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)+(class="num">1200*Point())))/Point()); xInputs[class="num">0] = (class="type">int)(a4/class="num">100)*class="num">100; class="type">int g1 = (class="type">int)(((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48)+(class="num">1200*Point()))-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">25))/Point()); xInputs[class="num">0] = (class="type">int)(g1/class="num">100)*class="num">100; class="type">int g2 = (class="type">int)(((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48)+(class="num">1200*Point()))-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">33))/Point()); xInputs[class="num">0] = (class="type">int)(g2/class="num">100)*class="num">100; class="type">int g3 = (class="type">int)(((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48)-(class="num">1200*Point()))-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">41))/Point()); xInputs[class="num">0] = (class="type">int)(g3/class="num">100)*class="num">100; class="type">int g4 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48)-((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">1200*Point()))+(iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48)-(class="num">1200*Point()))/class="num">2))/Point()) ; xInputs[class="num">0] = (class="type">int)(g4/class="num">100)*class="num">100; class="kw">return(class="num">1); } 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">int a1 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)+(class="num">800*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1))/Point()); a1 = (class="type">int)(a1/class="num">100)*class="num">100;
把蜡烛拆成四个偏移量喂给感知机
这段逻辑干的事很直接:用不同 bar 的收盘价差值,换算成以点为单位的整数偏移,再统一向下取整到百点整数倍。a1 到 a4 分别抓取了近一根、第 7 根、第 13 根、第 19 根与第 24 根收盘之间的相对距离,其中 a2 还叠了一个 800 点的平移补偿。 CandlePatterns 函数把同样的计算结果写进 xInputs 数组,下标 0~3 对应那四个偏移量,方便后续模块直接读取。注意它永远 return 1,真正的信号权重在另一个 perceptron1 里用 w1=x1-10 这类减法做偏移修正。 外汇与贵金属市场杠杆高、滑点随机,这套特征构造在 EURUSD 这类点值为 0.0001 的品种上,800 点平移相当于 0.08 价格单位,回测时若换到 XAUUSD 必须重算 Point 量级,否则特征会整体失真。
class="type">int a2 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">800*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">7))/Point()); a2 = (class="type">int)(a2/class="num">100)*class="num">100; class="type">int a3 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">13))/Point()); a3 = (class="type">int)(a3/class="num">100)*class="num">100; class="type">int a4 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">19))/Point()); a4 = (class="type">int)(a4/class="num">100)*class="num">100; class="kw">return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//|percentage of each part of the candle respecting total size | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int CandlePatterns(class="type">class="kw">double &xInputs[]) { class="type">int a1 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)+(class="num">800*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1))/Point()); xInputs[class="num">0] = (class="type">int)(a1/class="num">100)*class="num">100; class="type">int a2 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">800*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">7))/Point()); xInputs[class="num">1] = (class="type">int)(a2/class="num">100)*class="num">100; class="type">int a3 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">13))/Point()); xInputs[class="num">2] = (class="type">int)(a3/class="num">100)*class="num">100; class="type">int a4 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">19))/Point()); xInputs[class="num">3] = (class="type">int)(a4/class="num">100)*class="num">100; class="kw">return(class="num">1); } 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;
「把K线距离折算成整百点距」
这段逻辑干的事很直白:用当前价和若干根历史收盘价的差,除以 Point() 得到以点为单位的数值,再向下取整到百点整数倍。a1 取的是「(现价+1200点+48根前收盘)/2」与现价的差,a2 换成减 1200 点后和 13 根前收盘比,a3、a4 则纯看 48 根前收盘相对 25、37 根前收盘的落点。 注意 1200*Point() 这个偏移量,在常规外汇品种上约等于 1200 点(若是五位数报价就是 120 点,需按品种确认),它把中枢线人为外推,避免和现价贴太死。除以 Point() 转成整数点距后,(int)(x/100)*100 把噪声削到百点粒度——你开 MT5 把这段贴进 EA,改 100 为 50 就能拿到更细的档位。 CandlePatterns 函数把同样的四个量写进 xInputs 数组而非直接返回加权值,调用方便但容易让人忽略:a1~a4 的计算式和前一个函数完全一致,只是出口不同。外汇与贵金属波动剧烈,这类点距特征仅描述形态偏离,不预示方向,实盘须自担高风险。
class="type">int a1 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)+(class="num">1200*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1))/Point()); a1 = (class="type">int)(a1/class="num">100)*class="num">100; class="type">int a2 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">1200*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">13))/Point()); a2 = (class="type">int)(a2/class="num">100)*class="num">100; class="type">int a3 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">25))/Point()); a3 = (class="type">int)(a3/class="num">100)*class="num">100; class="type">int a4 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">37))/Point()); a4 = (class="type">int)(a4/class="num">100)*class="num">100; class="kw">return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//|percentage of each part of the candle respecting total size | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int CandlePatterns(class="type">class="kw">double &xInputs[]) { class="type">int a1 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)+(class="num">1200*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1))/Point()); xInputs[class="num">0] = (class="type">int)(a1/class="num">100)*class="num">100; class="type">int a2 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">1200*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">13))/Point()); xInputs[class="num">1] = (class="type">int)(a2/class="num">100)*class="num">100; class="type">int a3 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">25))/Point()); xInputs[class="num">2] = (class="type">int)(a3/class="num">100)*class="num">100; class="type">int a4 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">37))/Point()); xInputs[class="num">3] = (class="type">int)(a4/class="num">100)*class="num">100; class="kw">return(class="num">1); } class=class="str">"cmt">//+------------------------------------------------------------------+
◍ 感知机里的偏移与价格区间离散化
这段函数把感知机思路搬进了 MT5 指标计算:先用 x1~x4、y1~y4 各自减去常数 10.0 得到权重偏移 w1~w4、v1~v4,相当于给输入特征做固定去均值处理,避免后续距离计算被绝对价位带偏。 真正的信号特征来自 a1~a4 与 b1~b3 这几组整数化差值。以 a1 为例,它取当前收盘价加 800 点、与 24 根 K 线前收盘价的均值,再减当前收盘价,除以 Point 后取整并百点对齐——把「近端价格相对中段均值的偏离」压成每 100 点一档的离散量。a2 则用减 800 点替代加 800 点,捕捉下方偏移。 a3、a4 更直接:分别是 24 周期与 13、19 周期收盘价的差值,同样百点取整。b 组换用了 4、10、16 周期作参照,构成另一套偏移剖面。外汇与贵金属杠杆高,这类离散特征只描述价格结构,不预示方向。 开 MT5 把下面代码贴进自定义指标,改 PERIOD_CURRENT 为具体周期,观察 a1 在 EURUSD 的 H1 上是否常在 ±800 点边界处跳档,就能验证这套离散化对区间突破的灵敏度。
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 v1 = y1 - class="num">10.0; class="type">class="kw">double v2 = y2 - class="num">10.0; class="type">class="kw">double v3 = y3 - class="num">10.0; class="type">class="kw">double v4 = y4 - class="num">10.0; class="type">int a1 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)+(class="num">800*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1))/Point()); a1 = (class="type">int)(a1/class="num">100)*class="num">100; class="type">int a2 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">800*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">7))/Point()); a2 = (class="type">int)(a2/class="num">100)*class="num">100; class="type">int a3 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">13))/Point()); a3 = (class="type">int)(a3/class="num">100)*class="num">100; class="type">int a4 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">19))/Point()); a4 = (class="type">int)(a4/class="num">100)*class="num">100; class="type">int b1 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)+(class="num">800*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">4))/Point()); b1 = (class="type">int)(b1/class="num">100)*class="num">100; class="type">int b2 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">800*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">10))/Point()); b2 = (class="type">int)(b2/class="num">100)*class="num">100; class="type">int b3 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">800*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">16))/Point()); b3 = (class="type">int)(b3/class="num">100)*class="num">100;
把 K 线各段占比量化成整百点值
这段逻辑在算当前品种 K 线内部不同价格段相对整根实体的点数差,并统一向下取整到 100 点整数倍,方便后续当离散特征喂给模型。 以 a1 为例:取前 1 根收盘与 24 根收盘的平均值(上方偏移 800 点)再减前 1 根收盘,除以 Point() 转成整数点。xInputs[0] 存的是 (a1/100)*100,也就是丢掉了不足 100 点的零头。 a3、a4 则直接拿 24 根与 13、19 根收盘做差,不叠加 800 点偏移,只看中段趋势的位移点数。g1、g2 又回到带 800 点偏移的算法,分别对照 4 根和 10 根收盘。 在 MT5 里把 symbolS1 换成你自己盯的黄金或欧美品种,打印 xInputs 数组,能直接看到每根 K 线被压成 6 个整百点特征。外汇和贵金属杠杆高,这类特征只描述形态结构,不预示方向。
class="type">int CandlePatterns(class="type">class="kw">double &xInputs[]) { class="type">int a1 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)+(class="num">800*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1))/Point()); xInputs[class="num">0] = (class="type">int)(a1/class="num">100)*class="num">100; class="type">int a2 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">800*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">7))/Point()); xInputs[class="num">1] = (class="type">int)(a2/class="num">100)*class="num">100; class="type">int a3 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">13))/Point()); xInputs[class="num">2] = (class="type">int)(a3/class="num">100)*class="num">100; class="type">int a4 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">19))/Point()); xInputs[class="num">3] = (class="type">int)(a4/class="num">100)*class="num">100; class="type">int g1 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)+(class="num">800*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">4))/Point()); xInputs[class="num">4] = (class="type">int)(g1/class="num">100)*class="num">100; class="type">int g2 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">800*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">10))/Point()); xInputs[class="num">5] = (class="type">int)(g2/class="num">100)*class="num">100;
「感知机里的价格偏移量化」
下面这段 MQL5 片段把多个收盘价组合成整数偏移量,再塞进 xInputs 数组,供后续感知机函数调用。注意所有偏移都除以 Point() 转成点数,并用 (int)(x/100)*100 做了百点取整——这意味着小于 100 点的噪声被直接抹平。 perceptron1() 里先把 x1~x4、y1~y4 各减 10.0 得到 w、v 系列权重偏移,随后用 iClose 取当前周期 1、48、13、25、37 等柱的收盘价。a1 用 (收盘1 + 1200点 + 收盘48)/2 再减收盘1,等价于 600点 + (收盘48-收盘1)/2,百点取整后反映长周期与即时价的半差。 外汇与贵金属杠杆高,这类基于历史偏移的算法信号仅代表概率倾向,实盘前务必在 MT5 策略测试器用对应品种点值验证。把下面代码直接挂到 EA 里打印 xInputs[6]、[7] 与 a1~a4,能看到真实百点档位分布。
class="type">int g3 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">800*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">16))/Point()); xInputs[class="num">6] = (class="type">int)(g3/class="num">100)*class="num">100; class="type">int g4 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">800*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">24))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">22))/Point()); xInputs[class="num">7] = (class="type">int)(g4/class="num">100)*class="num">100; class="kw">return(class="num">1); } 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 v1 = y1 - class="num">10.0; class="type">class="kw">double v2 = y2 - class="num">10.0; class="type">class="kw">double v3 = y3 - class="num">10.0; class="type">class="kw">double v4 = y4 - class="num">10.0; class="type">int a1 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)+(class="num">1200*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1))/Point()); a1 = (class="type">int)(a1/class="num">100)*class="num">100; class="type">int a2 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">1200*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">13))/Point()); a2 = (class="type">int)(a2/class="num">100)*class="num">100; class="type">int a3 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">25))/Point()); a3 = (class="type">int)(a3/class="num">100)*class="num">100; class="type">int a4 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">37))/Point()); a4 = (class="type">int)(a4/class="num">100)*class="num">100; class="type">int b1 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)+(class="num">1200*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">6))/Point());
◍ 用百点取整压缩蜡烛形态特征
这段逻辑把多个偏移收盘价之差换算成 Point 单位的整数,再统一除以 100 后乘 100,等于把特征量按月百点粒度量化。b1 到 b4 分别以第 1、18、31、43 根 K 线收盘价为基准,对照「近 48 根中点下移 1200 点」的参考线,抓的是中长周期偏离结构。 CandlePatterns 函数把同类计算写进 xInputs 数组:a1 取当前收盘相对上偏移中点的差,a3、a4 直接算第 48 根与第 25、37 根的差,全部丢弃个位百点以下的噪音。回测中这种取整能让样本空间从几千维降到百点档位,聚类稳定性倾向提升。 别把取整当无损压缩 百点截断会吃掉小周期刺透形态,欧元美元这种平均日波 80~120 点的品种可能漏掉关键反转。开 MT5 把 /100*100 改成 /50*50,对比信号触发频次再决定粒度。
b1 = (class="type">int)(b1/class="num">100)*class="num">100; class="type">int b2 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">1200*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">18))/Point()); b2 = (class="type">int)(b2/class="num">100)*class="num">100; class="type">int b3 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">1200*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">31))/Point()); b3 = (class="type">int)(b3/class="num">100)*class="num">100; class="type">int b4 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">1200*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">43))/Point()); b4 = (class="type">int)(b4/class="num">100)*class="num">100; class="kw">return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4 + v1 * b1 + v2 * b2 + v3 * b3 + v4 * b4); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//|percentage of each part of the candle respecting total size | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int CandlePatterns(class="type">class="kw">double &xInputs[]) { class="type">int a1 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)+(class="num">1200*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1))/Point()); xInputs[class="num">0] = (class="type">int)(a1/class="num">100)*class="num">100; class="type">int a2 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">1200*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">13))/Point()); xInputs[class="num">1] = (class="type">int)(a2/class="num">100)*class="num">100; class="type">int a3 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">25))/Point()); xInputs[class="num">2] = (class="type">int)(a3/class="num">100)*class="num">100; class="type">int a4 = (class="type">int)((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">37))/Point()); xInputs[class="num">3] = (class="type">int)(a4/class="num">100)*class="num">100;
用偏移中枢量化多周期收盘落差
这段逻辑把「近48根收盘均值上移1200点」当作参考中枢,再分别减去第6、18、31、43根收盘价,把价格落差折算成点数后做百点取整,塞进 xInputs[4]~[7]。 具体看 g1:中枢 = (最新收盘 + 1200*Point + 48根前收盘) / 2,减去第6根收盘,除以 Point 得到整数点差,再 (g1/100)*100 把尾数抹掉。g2~g4 只是把加号换成减号、把减的棒线换成 18、31、43,其余完全同构。 落差是带符号的:若近期收盘相对旧收盘偏高,xInputs 会落为负数,模型可能把它读成上方空间受压;反之正数倾向代表低位偏离。外汇与贵金属杠杆高,这套特征只是原始输入,实盘信号概率不等于确定性。 直接把下面代码贴进 MT5 的自定义指标或EA里,把 symbolS1 换成你要测的品种,改 1200 那个偏移量就能看不同阈值下特征分布怎么变。
class="type">int g1 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)+(class="num">1200*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">6))/Point()); xInputs[class="num">4] = (class="type">int)(g1/class="num">100)*class="num">100; class="type">int g2 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">1200*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">18))/Point()); xInputs[class="num">5] = (class="type">int)(g2/class="num">100)*class="num">100; class="type">int g3 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">1200*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">31))/Point()); xInputs[class="num">6] = (class="type">int)(g3/class="num">100)*class="num">100; class="type">int g4 = (class="type">int)((((iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">1)-(class="num">1200*Point())+iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">48))/class="num">2)-iClose(symbolS1.Name(),PERIOD_CURRENT,class="num">43))/Point()); xInputs[class="num">7] = (class="type">int)(g4/class="num">100)*class="num">100; class="kw">return(class="num">1); }
「神经网络EA的前向验证实况」
把 DeepNeuralNetwork.mqh 那套 4-4-3 和 8-4-3 逻辑直接塞进扇形模板做前向验证,取优化期「最大复杂准则」排名前 40 的结果跑样本外,能看清一件事:优化盈利因子和实盘前向表现经常脱节。 4-4-3 扇形 4 参数、24 根蜡烛、止盈 60 / 止损 600 时,优化盈利因子约 20,前向里靠大量小止盈扛住密集止损;但同样结构换 48 根蜡烛,优化因子掉到约 14,前向仍无正期望。8-4-3 扇形 8 参数、24 根蜡烛、止盈 60 / 止损 600 时优化因子飙到约 29,前向全程仅 1 次止损却仍显随机——高因子不等于样本外能赚。 止盈 430 / 止损 200(二比一)在感知器 48 根蜡烛版里前六个月稳步增长、优化因子约 1.6,算少数站住的组合;但 8-4-3 同比例下优化因子 3 却前向亏损,倾向是神经网络对大止盈预测力不足。外汇与贵金属杠杆高,这类前向回撤随时可能放大,验证前请先算好自己的仓位风险。 想复现就开 MT5 把现成模板代码替进文末 EA,自己把优化迭代拉到 20 次以上——原文那点验算次数对多参数网络明显不够,加跑迭代才可能碰到更稳的局部最优。
◍ 画得少,看得清
这套基于感知器和浅层神经网络的实验系统,伸缩性比预想中更灵活:模板和传参几乎可以无限叠加,但真正往前向验证时,少而精的入口反而更经得起推敲。把输入值做四舍五入处理后,正向结果数量大约翻了一倍,说明噪声压缩比堆参数更重要。 算力是实打实的瓶颈。策略测试器只吃物理核心不吃线程,双 CPU 凑到 16 核及以上才像样,接 MQL5 云网络能明显把搜索效率拉起来。下一步值得试的是多感知器结构,以及把指标在一段区间内游走、背离这类现象喂进去。 附件里从 DeepNeuralNetwork 原始库到 4-4-3 / 8-4-3 扇形 EA 的 opt 与 trade 版本都齐了,直接丢进 MT5 就能复跑。外汇和贵金属波动剧烈、杠杆风险高,任何信号都只是概率倾向,实盘前先用历史数据自己跑一遍前向测试。