神经网络实验(第 1 部分):重温几何学(基础篇)
◍ 用几何直觉拆开神经网络的第一层
很多交易者一听到神经网络就觉得是黑箱,其实最浅的那一层本质就是高维空间里的几何变换。把输入特征看成坐标点,权重矩阵就是一次旋转加拉伸,偏置项把整个点云平移。理解这一步,后面反向传播的数学才不会悬空。 在 MT5 里跑一个最小感知机做验证,比看公式直观得多。下面这段用矩阵乘法算单层输出,输入是三根均线的归一化差值,输出一个原始信号强度。 外汇与贵金属杠杆高、滑点随机,信号强度只是概率倾向,不是方向保证。建议先用 EURUSD 的 M1 历史跑一遍,观察权重在 200 根 K 线内的漂移,再决定要不要接激活函数。
class="type">class="kw">double Input[class="num">3] = {class="num">0.2, -class="num">0.1, class="num">0.05}; class=class="str">"cmt">// 三根均线归一化差值作为输入 class="type">class="kw">double Weight[class="num">3] = {class="num">0.6, -class="num">0.3, class="num">0.8}; class=class="str">"cmt">// 对应权重 class="type">class="kw">double Bias = class="num">0.05; class=class="str">"cmt">// 偏置项 class="type">class="kw">double Output = Bias; class=class="str">"cmt">// 初始化输出为偏置 for(class="type">int i=class="num">0; i<class="num">3; i++) class=class="str">"cmt">// 遍历三个输入 Output += Input[i] * Weight[i]; class=class="str">"cmt">// 加权累加 class=class="str">"cmt">// 此时 Output 即单层线性变换后的原始信号
「为什么现成神经网络素材落不了地」
MQL5 社区里关于神经网络的文章、函数库和源码数量已经不少,光是公开可参考的 EA 框架就有几十套,但翻完一圈会发现一个共性问题:理论模块齐全,却没人把「输入特征→训练→实盘信号」串成一条能跑通的逻辑链。 多数示例停在回测曲线漂亮这一步,没有给出样本外验证和品种适配办法,直接搬去 EURUSD 或 XAUUSD 实盘,胜率往往会从回测的 70% 掉到 50% 边缘,外汇和贵金属的高杠杆会迅速放大这种偏差。 这篇里我把自己作为非专家、也给初学者做的实验过程摊开:不堆公式,只说怎么让网络输出变成下单依据,以及哪些坑是 MT5 里亲手跑过才发现的。
从几何形态到均线的网络输入
神经网络擅长抓形态,但喂进去的训练数据决定上限。我默认这个前提成立,并打算把几何形状直接映射进网络结构。 最初参考了 MetaTrader 4 的一个智能系统样本(MTC Combo),其底层是一个常规感知器。我在 MT5 里复现测试时,弃用了振荡器,改用均线作为主输入——回测里振荡器方案几乎不出有效信号。 大家都知道价格涨、 oscillator 跌的背离,但振荡器离价格本体太远。均线参数紧贴报价,作为几何形态的量化代理更稳,后续网络也更容易学到边界。
◍ 把均线关系捏成可喂模型的几何形状
基准用两条均线搭出来:周期 1 和 24、简单平均、收盘价。思路不是只给网络当前均线位置,而是把「当前之前的状态」也编码进去。直接把价格丢给神经网络基本是走弯路——价格可能在十年区间里乱晃,范围根本不固定。 正确做法是传价值点:所有数值压进一个固定区间,超不出边界。构建形状时,指标参数数量可多可少,形状能简能繁。下面几种是实测可用的结构:形状1取已收盘蜡烛 1、4、7、10 距 MA1 与 MA24 差值的点数;形状2取 1-4、4-7、7-10 三段距 MA1 的点数;形状3同理换成 MA24。 稍微复杂些的:形状4叫蝴蝶,用蜡烛 1—10 距 MA1、距 MA24 的点数,再加蜡烛1的 MA1 与蜡烛10的 MA24 互距、蜡烛1的 MA24 与蜡烛10的 MA1 互距,四条线一交就是蝶形。形状5是四边形,取两端蜡烛距双指标差、以及 MA1/MA24 各自 1—10 的跨度。形状6把前述全拼一起,喂给一个复杂形状。 代码里 perceptron1 到 perceptron5 就是这些形状的量化实现。以 perceptron1 为例:w1~w4 是权重偏置(各减 100.0),a1~a4 算的是 ind_In1 与 ind_In2 在柱 1/4/7/10 的差除以 Point() 转点数,返回加权求和。perceptron4 的 a3、a4 交叉取了不同均线的首尾柱距,对应蝴蝶的对角翼。 外汇和贵金属杠杆高、跳空频繁,这种形状构造只解决特征表达,不预示方向。开 MT5 把 ind_In1/ind_In2 接成你的 MA(1)/MA(24),改 perceptron 里的柱位就能验证哪类形状对当前品种更敏感。
class="type">class="kw">double perceptron1() { 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_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="kw">return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4); } class="type">class="kw">double perceptron2() { class="type">class="kw">double w1 = y1 - class="num">100.0; class="type">class="kw">double w2 = y2 - class="num">100.0; class="type">class="kw">double w3 = y3 - class="num">100.0; class="type">class="kw">double a1 = (ind_In1[class="num">1]-ind_In1[class="num">4])/Point(); class="type">class="kw">double a2 = (ind_In1[class="num">4]-ind_In1[class="num">7])/Point(); class="type">class="kw">double a3 = (ind_In1[class="num">7]-ind_In1[class="num">10])/Point(); class="kw">return (w1 * a1 + w2 * a2 + w3 * a3); } class="type">class="kw">double perceptron3() { class="type">class="kw">double w1 = z1 - class="num">100.0; class="type">class="kw">double w2 = z2 - class="num">100.0; class="type">class="kw">double w3 = z3 - class="num">100.0; class="type">class="kw">double a1 = (ind_In2[class="num">1]-ind_In2[class="num">4])/Point(); class="type">class="kw">double a2 = (ind_In2[class="num">4]-ind_In2[class="num">7])/Point(); class="type">class="kw">double a3 = (ind_In2[class="num">7]-ind_In2[class="num">10])/Point(); class="kw">return (w1 * a1 + w2 * a2 + w3 * a3); } class="type">class="kw">double perceptron4() { class="type">class="kw">double w1 = f1 - class="num">100.0; class="type">class="kw">double w2 = f2 - class="num">100.0; class="type">class="kw">double w3 = f3 - class="num">100.0; class="type">class="kw">double w4 = f4 - class="num">100.0; class="type">class="kw">double a1 = (ind_In1[class="num">1]-ind_In1[class="num">10])/Point(); class="type">class="kw">double a2 = (ind_In2[class="num">1]-ind_In2[class="num">10])/Point(); class="type">class="kw">double a3 = (ind_In1[class="num">1]-ind_In2[class="num">10])/Point(); class="type">class="kw">double a4 = (ind_In2[class="num">1]-ind_In1[class="num">10])/Point(); class="kw">return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4); } class="type">class="kw">double perceptron5() { class="type">class="kw">double w1 = c1 - class="num">100.0; class="type">class="kw">double w2 = c2 - class="num">100.0; class="type">class="kw">double w3 = c3 - class="num">100.0; class="type">class="kw">double w4 = c4 - class="num">100.0; class="type">class="kw">double a1 = (ind_In1[class="num">1]-ind_In1[class="num">10])/Point(); class="type">class="kw">double a2 = (ind_In2[class="num">1]-ind_In2[class="num">10])/Point();