您应当知道的 MQL5 向导技术(第 57 部分):搭配移动平均和随机振荡器的监督训练·进阶篇
🧠

您应当知道的 MQL5 向导技术(第 57 部分):搭配移动平均和随机振荡器的监督训练·进阶篇

(2/3)· 手工写EA太慢?向导+Python训好的ONNX网络,前向漫游测2024年EURJPY日线

实战向 第 2/3 篇

不少人以为向导拼出的智能系统只是玩具,跑两天就丢进回收站。其实只要把MA和随机振荡器拆成独立神经网络监督训练,再丢回MT5做前向漫游,就能用极少代码筛出真正能看的信号组合。本篇接着上篇,把这套流水线在日线EURJPY上跑通。

◍ 反向传播那段到底在跑什么

上面这段是 PyTorch 训练循环里最容易被忽略的几行。loss.backward() 负责把误差沿网络反向传,算出每个参数的梯度;optimizer.step() 紧接着按设定好的学习率去更新权重,两步顺序不能反。 train_loss += loss.item() 把当前 batch 的标量损失累加,循环结束再用 train_loss /= len(train_loader) 求整轮平均损失。打印出来能看到第 10 轮训练损失压到 0.2491,测试损失 0.2592,说明模型在样本外没明显恶化。 你在 MT5 里接小布 AIGC 模块做特征预测时,若自己用 Python 侧训轻量网络,这几行就是最小可跑骨架。外汇与贵金属波动高危,任何模型输出只作概率参考,别当确定性信号。

MQL5 / C++
    loss = loss_fn(output, target)

    # Step class="num">4: Backpropagation
    loss.backward()
    optimizer.step()
    train_loss += loss.item()
    train_loss /= len(train_loader)
    print(f"Epoch {epoch + class="num">1}/{epochs}, Train Loss: {train_loss:.4f}")
Epoch class="num">10/class="num">10, Train Loss: class="num">0.2491
Test Loss: class="num">0.2592

均线斜率搭随机指标做趋势动量确认

这套组合用移动平均线斜率锁趋势方向,再用随机振荡器的 %K 缓冲区确认动量强弱。买入逻辑是均线向上倾斜、%K 站上 50 且继续上行;卖出则镜像处理:均线向下、%K 跌破 50 且继续下行。相比等均线交叉的老办法,这里省掉了交叉等待,滞后明显降低,对趋势加速段更敏感。 在 EURJPY 上,我们用 2023 年数据跑 Python 训练,再用 2024 年数据做 MT5 策略测试器的前向测试。报告里形态只开了多头,说明偏侧问题存在——更大的训练集或能缓解,但本质上它是个适合区间波动过滤的组件:斜率保证趋势明显才下手,%K 相对 50 的位置验证动量成色。 弱点是反转反应慢。均线斜率要等几根 K 线才调头,随机指标在转折时也会延迟,真等确认可能错过早期进场;窄幅横盘直接失效。加 ADX 确认趋势强度,或叠价格行为突破、回调过滤,准确率会好一些。外汇和贵金属波动剧烈、杠杆风险高,实盘前务必在 MT5 用历史数据自测。 下面这段是形态索引为 2 时的 MQL5 取数逻辑,以及对应的 Python Test 函数骨架,逐行看清楚条件怎么落地的: else if(Index == 2) { if(C.GetData(T, 2, _c) >= 2 && CopyBuffer(M.Handle(), 0, T, 2, _ma) >= 2 && CopyBuffer(S.Handle(), 0, T, 2, _sto_k) >= 2) { _v[0] = ((_ma[1] < _ma[0]) ? 1.0f : 0.0f); _v[1] = ((_sto_k[1] < _sto_k[0] && _sto_k[1] >= 50.0) ? 1.0f : 0.0f); _v[2] = ((_sto_k[1] > _sto_k[0] && _sto_k[1] <= 50.0) ? 1.0f : 0.0f); _v[3] = ((_ma[1] > _ma[0]) ? 1.0f : 0.0f); } } # Test function # 定义 Test:把模型丢到 cuda 或 cpu,关梯度算测试损失 # 循环 test_loader,data reshape 成 (批,1,特征) 再送设备 # 前向算 output,和 target 算 loss,累加后除以批数得平均 Test Loss _v[0] 记均线上升(前根小于当前),_v[3] 记下降;_v[1] 是 %K 高于 50 且还在涨,_v[2] 是低于 50 且还在跌。CopyBuffer 取均线主缓冲和随机 %K 缓冲各两根,比较相邻值得出斜率与动量方向。Python 侧 Test 不更新权重,只报平均损失,用来和训练损失比对看是否过拟合。

MQL5 / C++
else if(Index == class="num">2)
{  if(C.GetData(T, class="num">2, _c) >= class="num">2 && CopyBuffer(M.Handle(), class="num">0, T, class="num">2, _ma) >= class="num">2 && CopyBuffer(S.Handle(), class="num">0, T, class="num">2, _sto_k) >= class="num">2)
   { _v[class="num">0] = ((_ma[class="num">1] < _ma[class="num">0]) ? class="num">1.0f : class="num">0.0f);
     _v[class="num">1] = ((_sto_k[class="num">1] < _sto_k[class="num">0] && _sto_k[class="num">1] >= class="num">50.0) ? class="num">1.0f : class="num">0.0f);
     _v[class="num">2] = ((_sto_k[class="num">1] > _sto_k[class="num">0] && _sto_k[class="num">1] <= class="num">50.0) ? class="num">1.0f : class="num">0.0f);
     _v[class="num">3] = ((_ma[class="num">1] > _ma[class="num">0]) ? class="num">1.0f : class="num">0.0f);
   }
}
# Test function
def Test(model, test_loader, optimizer, loss_fn, epochs):
    device = T.device(&class="macro">#x27;cuda:class="num">0&class="macro">#x27; if T.cuda.is_available() else &class="macro">#x27;cpu&class="macro">#x27;)
    model.to(device)
    
    with T.no_grad():
        test_loss = class="num">0.0
        
        for batch_idx, (data, target) in enumerate(tqdm(test_loader, desc=f"Loss at {test_loss} in(Test)")):
            # Step class="num">1: Ensure proper tensor dimensions
            data = data.view(-class="num">1, class="num">1, feature_size)
            
            ...
            # Step class="num">3: Move to device and forward pass
            data, target = data.to(device), target.to(device)
            optimizer.zero_grad()
            output = model(data)
            target = target.view(-class="num">1, class="num">1, class="num">1)
            class="macro">#print(f"target: {target.shape}, plus output: {output.shape}")
            loss = loss_fn(output, target)
            test_loss += loss.item()
        test_loss /= len(test_loader)
        print(f"Test Loss: {test_loss:.4f}")

「均线反弹叠背离的早期逆转捕法」

把向上倾斜的均线当支撑、向下倾斜的均线当阻力,再叠一层随机振荡器的动量背离,能在逆转发生前先抓到苗头。多头设定是价格从上行均线弹起,同时随机指标出现更低低点对应更高低点的底背离;空头则是价格碰下行均线就垮,指标更高高点配更低高点顶背离。

  • 年数据训练后,在 2024 年数据上前向漫游测试并不平衡,多空开仓姿态偏斜,但形态-3 对早期逆转的检测还算可靠——背离往往比价格反应先走一步。它顺带避开了追趋势的被动,让你贴着动态支撑阻力位进场,不用等行情跑远。

弱点也直白:强趋势里假信号多,单靠背离 often 掀不动趋势。均线本身有滞后,反弹和背离时序未必严丝合缝,得等确认。实战里拿针棒、吞噬棒这类烛条形态去配均线层,或拿成交量激刺补一刀背离确认,设定会更硬。 下面这段 MQL5 是形态-3 的网络输入构造,Index==3 时取近 3 根bar的收盘价、均线和 Stochastic 主线缓冲,逐位填特征向量 _v: _v[0] 判多头刺透——前两根走低、最近一根反抽且仍低于前高、均线在谷底 bar 上方;_v[1] 要随机指标先抑后扬且谷值过 40;_v[2]/_v[3] 分别记均线向上/向下倾斜;_v[4] 为空头背离段随机指标过 60 的峰;_v[5] 是空头刺透配均线在峰下。跑前记得把 M、S 句柄和 C 数据接好,否则 CopyBuffer 长度不够会直接跳过填充。

MQL5 / C++
else if(Index == class="num">3)
{
  _v.Init(class="num">6);
  _v.Fill(class="num">0.0);
  if(C.GetData(T, class="num">3, _c) >= class="num">3 && CopyBuffer(M.Handle(), class="num">0, T, class="num">3, _ma) >= class="num">3 && CopyBuffer(S.Handle(), class="num">0, T, class="num">3, _sto_k) >= class="num">3)
  {
    _v[class="num">0] = ((_c[class="num">2] > _c[class="num">1] && _c[class="num">1] < _c[class="num">0] &&  _c[class="num">0] < _c[class="num">2] && _ma[class="num">1] >= _c[class="num">1]) ? class="num">1.0f : class="num">0.0f);
    _v[class="num">1] = ((_sto_k[class="num">2] > _sto_k[class="num">1] && _sto_k[class="num">1] < _sto_k[class="num">0] && _sto_k[class="num">1] >= class="num">40.0) ? class="num">1.0f : class="num">0.0f);
    _v[class="num">2] = ((_ma[class="num">2] > _ma[class="num">0]) ? class="num">1.0f : class="num">0.0f);
    _v[class="num">3] = ((_ma[class="num">2] < _ma[class="num">0]) ? class="num">1.0f : class="num">0.0f);
    _v[class="num">4] = ((_sto_k[class="num">2] < _sto_k[class="num">1] && _sto_k[class="num">1] > _sto_k[class="num">0] && _sto_k[class="num">1] <= class="num">60.0) ? class="num">1.0f : class="num">0.0f);
    _v[class="num">5] = ((_c[class="num">2] < _c[class="num">1] && _c[class="num">1] > _c[class="num">0] &&  _c[class="num">0] > _c[class="num">2] && _ma[class="num">1] <= _c[class="num">1]) ? class="num">1.0f : class="num">0.0f);
  }
}

◍ 均线与随机交叉的双条件过滤

把向上倾斜的均线当动态支撑、向下倾斜的当动态阻力,再叠加随机振荡器 %K 与 %D 的交叉,这套组合能压住一部分假突破。价格在 MA 上方且 %K 上穿 %D 才视为多头触发;反之下方且 %K 下穿 %D 为空头触发。 提前进场的坑在于:光看价格触碰均线,动量可能还没转。随机交叉相当于一道确认闸,过滤掉过早抄底摸顶的单子,假信号概率会降,但代价是入场偏晚,早期突破容易踏空。 外汇与贵金属波动剧烈,尤其空头段往往比多头段更急,震荡市里价格可能先刺穿均线再续跌,这时候双条件反而会连续吃亏。下面的 MQL5 片段把形态-4 拆成四维向量:_v[0] 记金叉、_v[1] 记价格与上行 MA 关系、_v[2] 记价格与下行 MA 关系、_v[3] 记死叉,训练网络时只喂这一形态。 前向游走测试在趋势市表现还行,区间反弹场景必然吃力;报告里只跑了多头,更大样本下多空双边都得验,别直接当通用信号搬。

MQL5 / C++
else if(Index == class="num">4)
{  if(C.GetData(T, class="num">2, _c) >= class="num">2 && CopyBuffer(M.Handle(), class="num">0, T, class="num">2, _ma) >= class="num">2 && CopyBuffer(S.Handle(), class="num">0, T, class="num">2, _sto_k) >= class="num">2 && CopyBuffer(S.Handle(), class="num">1, T, class="num">2, _sto_d) >= class="num">2)
   {  _v[class="num">0] = ((_sto_k[class="num">1] < _sto_d[class="num">1] && _sto_k[class="num">0] > _sto_d[class="num">0]) ? class="num">1.0f : class="num">0.0f);
      _v[class="num">1] = ((_ma[class="num">1] < _c[class="num">1] && _ma[class="num">0] < _c[class="num">0] && _ma[class="num">1] < _ma[class="num">0]) ? class="num">1.0f : class="num">0.0f);
      _v[class="num">2] = ((_ma[class="num">1] > _c[class="num">1] && _ma[class="num">0] > _c[class="num">0] && _ma[class="num">1] > _ma[class="num">0]) ? class="num">1.0f : class="num">0.0f);
      _v[class="num">3] = ((_sto_k[class="num">1] > _sto_d[class="num">1] && _sto_k[class="num">0] < _sto_d[class="num">0]) ? class="num">1.0f : class="num">0.0f);
   }
}

均线托底叠加随机反转的入场过滤

把移动平均线当成动态支撑或阻力,再叠随机振荡器穿越 20 / 80 阈值,能滤掉一批假突破。多头侧看的是:价格回踩均线不破、且随机 K 线从 20 下方上穿,说明下跌动量衰竭、买方可能接手;空头侧反过来,价格碰向下倾斜的均线、随机 K 从 80 上方下穿,多头动量减弱、卖方倾向进场。 这套双指标组合的核心价值是推迟信号——避免在趋势中途的弱回调里过早动手。但在强势单边里也有代价:价格可能在均线处短暂停留再破位,导致多头离场偏晚或错失一段点数,外汇与贵金属品种上这种高风险情形尤其要留意。 下面这段 MQL5 把形态条件写成 0/1 输入向量,多空索引分开,技术上不可能同时填满 1。训练用 2023 年数据、前向测试放 2024 年,实测只出了多头开仓——小样本训练让网络输出偏多,换更大数据集才有机会修正。 代码逐行拆解: else if(Index == 5) —— 当前形态编号等于 5 才进入本分支。 if(C.GetData(T, 3, _c) >= 3 && CopyBuffer(M.Handle(), 0, T, 3, _ma) >= 3 && CopyBuffer(S.Handle(), 0, T, 2, _sto_k) >= 2) —— 取近 3 根收盘价、3 根均线值、2 根随机 K 值,缓冲区足够才继续。 _v[0] = ((_sto_k[1] < 20.0 && _sto_k[0] > 20.0) ? 1.0f : 0.0f); —— 上一根 K 低于 20 且本根上穿 20,标记多头动量反转。 _v[1] = ((_c[2] > _c[1] && _c[1] < _c[0] && _c[1] >= _ma[1]) ? 1.0f : 0.0f); —— 中间那根收阴且为局部低点、并站在均线之上,确认均线托底。 _v[2] = ((_c[2] < _c[1] && _c[1] > _c[0] && _c[1] <= _ma[1]) ? 1.0f : 0.0f); —— 镜像条件,中间阳线为局部高点且受压于均线。 _v[3] = ((_sto_k[1] > 80.0 && _sto_k[0] < 80.0) ? 1.0f : 0.0f); —— 上一根 K 高于 80 且本根下穿 80,标记空头动量反转。

MQL5 / C++
else if(Index == class="num">5)
{  if(C.GetData(T, class="num">3, _c) >= class="num">3 && CopyBuffer(M.Handle(), class="num">0, T, class="num">3, _ma) >= class="num">3 && CopyBuffer(S.Handle(), class="num">0, T, class="num">2, _sto_k) >= class="num">2)
   {  _v[class="num">0] = ((_sto_k[class="num">1] < class="num">20.0 && _sto_k[class="num">0] > class="num">20.0) ? class="num">1.0f : class="num">0.0f);
      _v[class="num">1] = ((_c[class="num">2] > _c[class="num">1] && _c[class="num">1] < _c[class="num">0] && _c[class="num">1] >= _ma[class="num">1]) ? class="num">1.0f : class="num">0.0f);
      _v[class="num">2] = ((_c[class="num">2] < _c[class="num">1] && _c[class="num">1] > _c[class="num">0] && _c[class="num">1] <= _ma[class="num">1]) ? class="num">1.0f : class="num">0.0f);
      _v[class="num">3] = ((_sto_k[class="num">1] > class="num">80.0 && _sto_k[class="num">0] < class="num">80.0) ? class="num">1.0f : class="num">0.0f);
   }
}

「均线交叉叠随机滤波的实盘验证落点」

把短周期均线压到 8、长周期翻倍到 16,再让随机振荡器的 D 线站上 50 且 K 线上穿 D,才视作买入触发;死叉则反过来要求 D 在 50 下方且 K 下穿 D。这套组合在 2023 年样本内训练完,放到 2024 年前向漫游,盈利端和多空对称性都没站住。 代码里 Index==6 的分支先批量取两段缓冲:_ma_lag 与 _ma 各取 2 根,_sto_k 与 _sto_d 也各取 2 根,任一 CopyBuffer 不足 2 就跳过。随后 _v[0] 和 _v[3] 都是判短均由下穿上长均(前一根 lag>ma、当前 lag<ma 给 1.0),_v[1] 是 D≥50 且 K>D 的偏多确认,_v[2] 是 D≤50 且 K<D 的偏空确认。 别把漫游失效当死刑。外汇与贵金属属高杠杆品种,这套形态若换周期或重训参数,概率上仍可能改善,但务必先在 MT5 用 2024 年数据重跑一遍上面的句柄逻辑再下结论。

MQL5 / C++
else if(Index == class="num">6)
{  if(CopyBuffer(M.Handle(), class="num">0, T, class="num">2, _ma_lag) >= class="num">2 && CopyBuffer(M.Handle(), class="num">0, T, class="num">2, _ma) >= class="num">2 && CopyBuffer(S.Handle(), class="num">0, T, class="num">2, _sto_k) >= class="num">2 && CopyBuffer(S.Handle(), class="num">0, T, class="num">2, _sto_d) >= class="num">2)
   {  _v[class="num">0] = ((_ma_lag[class="num">1] > _ma[class="num">1] && _ma_lag[class="num">0] < _ma[class="num">0]) ? class="num">1.0f : class="num">0.0f);
      _v[class="num">1] = ((class="num">50.0 <= _sto_d[class="num">0] && _sto_k[class="num">0] > _sto_d[class="num">0]) ? class="num">1.0f : class="num">0.0f);
      _v[class="num">2] = ((class="num">50.0 >= _sto_d[class="num">0] && _sto_k[class="num">0] < _sto_d[class="num">0]) ? class="num">1.0f : class="num">0.0f);
      _v[class="num">3] = ((_ma_lag[class="num">1] > _ma[class="num">1] && _ma_lag[class="num">0] < _ma[class="num">0]) ? class="num">1.0f : class="num">0.0f);
   }
}
把交叉验证交给小布盯盘
这些诊断小布盯盘的AIGC已内置,打开对应品种页即可看到训练集与前向年度的盈亏分布,你专注判断信号形态是否过拟合。

常见问题

监督阶段用标注历史数据训出权重偏差,强化近似前向漫游里的反向传播调优,两者在MT5策略测试器里分段跑更清楚。
Python训练效率和交叉验证支持远好于MQL5,训完导出ONNX进策略测试器前向漫游,省下的时间够多测几个品种。
本篇仅列10个顶级形态,如斜率确认、反弹背离、动态支撑交叉等,具体组合概率随品种和年份浮动。
目前小布内置的是诊断与分布视图,ONNX前向测试需在MT5完成;把结果页喂给小布可自动标出疑似过拟合年份。
EURJPY 2024波动结构可能异于2023训练集,日线信号失效概率偏高,外汇贵金属本身高风险,别把漫游盈利率当实盘预期。