神经网络实验(第 1 部分):重温几何学·进阶篇
「感知机里的价差与权重偏移写法」
这段 MT5 自定义函数把两条指标序列 ind_In1 与 ind_In2 在 1、4、7、10 号偏移上的差值,统一除以 Point() 转成整数化点差,再和一组权重做线性组合返回。注意 a3、a4 在前后两个函数里都用了 1 与 10 号偏移,但中间采样点从 4、10 换成了 4、7、10,特征密度明显提高了。 perceptron6() 里把所有权重 w1~w14 都先减掉 100.0,相当于给神经元输入做了一次固定偏置平移;这种写法在调参时若直接改 x1、y1 等外部变量,实际生效权重会整体偏移 100 个基点量级。 特征构造上除了跨指标价差,还加了 ind_In1 自身 1-4-7-10 的三段落差、ind_In2 同理,以及两条序列首尾交叉比对(如 a13 用 In1[1] 减 In2[10])。回测时若发现权重 w11~w14 长期贴近 0,说明首尾交叉项在该品种上区分度弱,可优先剔除降维。 外汇与贵金属杠杆高、点差跳变频繁,这类基于固定偏移的线性特征在极端行情可能失效,实盘前务必在 MT5 策略测试器用对应品种真实tick重跑。
class="type">class="kw">double a3 = (ind_In1[class="num">1]-ind_In2[class="num">1])/Point(); class="type">class="kw">double a4 = (ind_In1[class="num">10]-ind_In2[class="num">10])/Point(); class="kw">return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4); } class="type">class="kw">double perceptron6() { class="type">class="kw">double w1 = x1 - class="num">100.0; class="type">class="kw">double w2 = x2 - class="num">100.0; class="type">class="kw">double w3 = x3 - class="num">100.0; class="type">class="kw">double w4 = x4 - class="num">100.0; class="type">class="kw">double w5 = y1 - class="num">100.0; class="type">class="kw">double w6 = y2 - class="num">100.0; class="type">class="kw">double w7 = y3 - class="num">100.0; class="type">class="kw">double w8 = z1 - class="num">100.0; class="type">class="kw">double w9 = z2 - class="num">100.0; class="type">class="kw">double w10 = z3 - class="num">100.0; class="type">class="kw">double w11 = f1 - class="num">100.0; class="type">class="kw">double w12 = f2 - class="num">100.0; class="type">class="kw">double w13 = f3 - class="num">100.0; class="type">class="kw">double w14 = f4 - class="num">100.0; class="type">class="kw">double a1 = (ind_In1[class="num">1]-ind_In2[class="num">1])/Point(); class="type">class="kw">double a2 = (ind_In1[class="num">4]-ind_In2[class="num">4])/Point(); class="type">class="kw">double a3 = (ind_In1[class="num">7]-ind_In2[class="num">7])/Point(); class="type">class="kw">double a4 = (ind_In1[class="num">10]-ind_In2[class="num">10])/Point(); class="type">class="kw">double a5 = (ind_In1[class="num">1]-ind_In1[class="num">4])/Point(); class="type">class="kw">double a6 = (ind_In1[class="num">4]-ind_In1[class="num">7])/Point(); class="type">class="kw">double a7 = (ind_In1[class="num">7]-ind_In1[class="num">10])/Point(); class="type">class="kw">double a8 = (ind_In2[class="num">1]-ind_In2[class="num">4])/Point(); class="type">class="kw">double a9 = (ind_In2[class="num">4]-ind_In2[class="num">7])/Point(); class="type">class="kw">double a10 = (ind_In2[class="num">7]-ind_In2[class="num">10])/Point(); class="type">class="kw">double a11 = (ind_In1[class="num">1]-ind_In1[class="num">10])/Point(); class="type">class="kw">double a12 = (ind_In2[class="num">1]-ind_In2[class="num">10])/Point(); class="type">class="kw">double a13 = (ind_In1[class="num">1]-ind_In2[class="num">10])/Point(); class="type">class="kw">double a14 = (ind_In2[class="num">1]-ind_In1[class="num">10])/Point(); class="kw">return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4 + w5 * a5 + w6 * a6 + w7 * a7 + w8 * a8 + w9 * a9 + w10 * a10 + w11 * a11 + w12 * a12 + w13 * a13 + w14 * a14); }
◍ 用点数比柱数算指标倾角
把价格喂给感知器时,除了形状和线条,指标倾角也是一类天然受限的输入——它落在固定区间内,和前面用模板的思路一致。 很多人习惯直接读图表上的切线角度,但那玩意随坐标轴缩放乱跳。更稳的做法是用「点数差 ÷ 柱线数」代替屏幕角度:tg(α) 就是一段走势的竖向点数(支撑腿 a)与横向柱数(支撑腿 b)之比,不依赖显示比例。 上面四个感知器演示了不同组合。perceptront1 只看一条 MA(ind_In1),取 1-4、1-7、1-10 柱三段坡度;perceptront2 引入第二条 MA(ind_In2),各取 1-5、1-10 两段坡度共四个输入;perceptront3 和 4 把两条均线的交叉坡度揉在一起,结构更复杂,但本质还是「(当前值-过去值)/间隔柱数」的坡度向量。 外汇和贵金属杠杆高、跳空频发,坡度阈值在不同品种上要重测,别直接搬参数。 代码里 w1~w4 都是 x1~x4 减 100.0 后的权重偏移,a1~a4 是各类坡度:例如 a1=(ind_In1[1]-ind_In1[4])/4 表示最近 4 根柱 MA1 的平均每柱涨跌点数;return 行把权重和坡度做点积输出。复制进 MT5 把 ind_In1/ind_In2 绑到 MA1/MA24,改除数为 5、10、20 就能看坡度敏感性变化。
class="type">class="kw">double perceptront1() { class="type">class="kw">double w1 = x1 - class="num">100.0; class="type">class="kw">double w2 = x2 - class="num">100.0; class="type">class="kw">double w3 = x3 - class="num">100.0; class="type">class="kw">double a1 = (ind_In1[class="num">1]-ind_In1[class="num">4])/class="num">4; class="type">class="kw">double a2 = (ind_In1[class="num">1]-ind_In1[class="num">7])/class="num">7; class="type">class="kw">double a3 = (ind_In1[class="num">1]-ind_In1[class="num">10])/class="num">10; class="kw">return (w1 * a1 + w2 * a2 + w3 * a3); } class="type">class="kw">double perceptront2() { class="type">class="kw">double w1 = x1 - class="num">100.0; class="type">class="kw">double w2 = x2 - class="num">100.0; class="type">class="kw">double w3 = x3 - class="num">100.0; class="type">class="kw">double w4 = x4 - class="num">100.0; class="type">class="kw">double a1 = (ind_In1[class="num">1]-ind_In1[class="num">5])/class="num">5; class="type">class="kw">double a2 = (ind_In1[class="num">1]-ind_In1[class="num">10])/class="num">10; class="type">class="kw">double a3 = (ind_In2[class="num">1]-ind_In2[class="num">5])/class="num">5; class="type">class="kw">double a4 = (ind_In2[class="num">1]-ind_In2[class="num">10])/class="num">10; class="kw">return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4); } class="type">class="kw">double perceptront3() { class="type">class="kw">double w1 = x1 - class="num">100.0; class="type">class="kw">double w2 = x2 - class="num">100.0; class="type">class="kw">double w3 = x3 - class="num">100.0; class="type">class="kw">double w4 = x4 - class="num">100.0; class="type">class="kw">double a1 = (ind_In1[class="num">1]-ind_In1[class="num">10])/class="num">10; class="type">class="kw">double a2 = (ind_In2[class="num">1]-ind_In1[class="num">4])/class="num">4; class="type">class="kw">double a3 = (ind_In2[class="num">1]-ind_In1[class="num">7])/class="num">7; class="type">class="kw">double a4 = (ind_In2[class="num">1]-ind_In1[class="num">10])/class="num">10; class="kw">return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4); } class="type">class="kw">double perceptront4() { class="type">class="kw">double w1 = x1 - class="num">100.0; class="type">class="kw">double w2 = x2 - class="num">100.0; class="type">class="kw">double w3 = x3 - class="num">100.0; class="type">class="kw">double w4 = x4 - class="num">100.0; class="type">class="kw">double a1 = (ind_In1[class="num">1]-ind_In1[class="num">10])/class="num">10; class="type">class="kw">double a2 = (ind_In2[class="num">1]-ind_In1[class="num">10])/class="num">10; class="type">class="kw">double a3 = (ind_In1[class="num">1]-ind_In1[class="num">10])/class="num">10; class="type">class="kw">double a4 = (ind_In2[class="num">1]-ind_In2[class="num">10])/class="num">10; class="kw">return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4); }
逆势过滤下的感知机下单逻辑
训练时我直接把策略写成逆势趋势:卖单要求前一根蜡烛 MA1 高于 MA24,买单反之,这样买卖信号在特征层就被硬性区隔。你也可以反过来做顺势,或把均线换成 TEMA 这类指标,输入维度自己定。 五位小数品种想预测 400 点波动不现实,行情方向没人能提前锁定。测试里我给这类品种钉死 600 点止损、60 点止盈,盈亏比约 1:10,纯为验证模型而非实盘参数。 下方代码是 EA 的开仓判断核心:无同魔术码持仓、均线叉条件、感知机输出符号、点差上限四重过滤才放行。
class=class="str">"cmt">//SELL++++++++++++++++++++++++++++++++++++++++++++++++ if ((CalculatePositions(symbolS1.Name(), Magic, POSITION_TYPE_SELL, EAComment)==class="num">0) && (ind_In1[class="num">1]>ind_In2[class="num">1]) && (perceptron1()<class="num">0) &&(SpreadS1<=MaxSpread)){class=class="str">"cmt">//v1 OpenSell(symbolS1.Name(), LotsXSell, TakeProfit, StopLoss, EAComment); } class=class="str">"cmt">//BUY++++++++++++++++++++++++++++++++++++++++++++++++ if ((CalculatePositions(symbolS1.Name(), Magic, POSITION_TYPE_BUY, EAComment)==class="num">0) && (ind_In1[class="num">1]<ind_In2[class="num">1]) && (perceptron1()>class="num">0) && (SpreadS1<=MaxSpread)){class=class="str">"cmt">//v1 OpenBuy(symbolS1.Name(), LotsXBuy, TakeProfit, StopLoss, EAComment); }
「感知器权重优化的算力与样本现实」
神经网络类 EA 的优化极其吃算力。实测中每个 EA 连续跑 10 轮遗传优化,单轮通过的结果量约 10000–15000 组,通过次数越多,命中可用权重比的概率才越高;因此策略测试器务必开「仅开盘价」模式,并显式在代码里取收盘价,否则本地基本跑不动,建议直接挂云网络。 权重步长选 5 而非 1 不是随手定的。实验里对比过,步长为 1 时权重比收敛过窄,而 5 步让感知器权重分布更散,对最终形态识别反而更有利。 具体落地的样本:EA1 单感知器在 2010.5.31–2021.5.30、H1、固定 0.01 手、TP60/SL600 下,x1–x4 权重 0–200 以 5 步进优化,最佳结果仅 0.87,前向验证无意义;EA4 四感知器同区间、TP200/SL200、复杂准则最大化时最佳 32,前向 2021.5.31–2022.5.30 应挑利润因子最大且复杂值超 40–50 的组。外汇与贵金属杠杆品种波动剧烈,这类回测不代表实盘倾向,高风险自担。 两个绕不开的工程问题:多权重比参数必须能在 EA 代码内灵活迁移;优化出的参数族需要落库,考虑用 .CSV 在 EA 启动时批量读入同仓交易。下面这段开仓判定是四感知器同向过滤的骨架。
class=class="str">"cmt">//SELL++++++++++++++++++++++++++++++++++++++++++++++++ if ((CalculatePositions(symbolS1.Name(), Magic, POSITION_TYPE_SELL, EAComment)==class="num">0) && (ind_In1[class="num">1]>ind_In2[class="num">1]) && (perceptron1()<class="num">0) && (perceptron2()<class="num">0) && (perceptron3()<class="num">0) && (perceptron4()<class="num">0) && (SpreadS1<=MaxSpread)){class=class="str">"cmt">//v1 OpenSell(symbolS1.Name(), LotsXSell, TakeProfit, StopLoss, EAComment); } class=class="str">"cmt">//BUY++++++++++++++++++++++++++++++++++++++++++++++++ if ((CalculatePositions(symbolS1.Name(), Magic, POSITION_TYPE_BUY, EAComment)==class="num">0) && (ind_In1[class="num">1]<ind_In2[class="num">1]) && (perceptron1()>class="num">0) && (perceptron2()>class="num">0) && (perceptron3()>class="num">0) && (perceptron4()>class="num">0) && (SpreadS1<=MaxSpread)){class=class="str">"cmt">//v1 OpenBuy(symbolS1.Name(), LotsXBuy, TakeProfit, StopLoss, EAComment); }
◍ 别急着下结论
这套前向验证跑下来,作者只敢说「部分达成盈利目标」,附带的 EA.zip 实测体积 181.54 KB,说明策略离直接实盘还有距离。外汇与贵金属自带高杠杆高风险,任何前向结果都只是概率倾向,不是通关证。 下一阶段要转向更复杂的系统,但已验证的输入正规化思路可以先在 MT5 里复跑——把你自己的一组通道参数丢进优化器,看是否也会撞上 10K 上限。 实验第二部分才会聊「如何更多利用已有经验」,在那之前,先把手上这份 EA 的前向曲线拆清楚,比追新模型更实在。