构建K线图趋势约束模型(第8部分):EA的开发(一)·综合运用
🤖

构建K线图趋势约束模型(第8部分):EA的开发(一)·综合运用

(3/3)·把5000根K线压到1000根、拆出独立脚本后,EA如何把手动信号变成可跑的自动策略

实战向 第 3/3 篇

很多交易者把指标塞进EA时,直接照搬原缓冲区逻辑,结果图表卡死、矩形不显示却查不出原因。历史K线默认拉到5000根、多缓冲区叠加,终端悄无声息地拖慢了整个分析流程。本篇接上两篇的约束模型,收尾讲清怎么用脚本分流再合回EA。

用 MQL5 交易请求结构下多单与空单

当指标缓冲 Buffer7[0] 不是 EMPTY_VALUE 时,视为多单触发信号;Buffer6[0] 非空则对应空单信号。这两段逻辑都依赖外部传入的 StopLoss、TakeProfit(以点数计)、Lots、Slippage、MagicNumber 等变量,实盘前需在输入参数里给明确数值,外汇与贵金属杠杆高,止损点数设错可能瞬间放大回撤。 多单部分先取 SYMBOL_ASK 作为入场价,SL 用 ask 减 StopLoss*_Point 并 NormalizeDouble 到品种精度,TP 则加 TakeProfit*_Point;空单反过来用 SYMBOL_BID,SL 在 bid 之上、TP 在 bid 之下。两者都走 MqlTradeRequest + OrderSend 的市价单路径,返回 retcode 不为 0 就 Print 错误码,便于在 MT5 Experts 日志里抓失效原因。 下面这段是原文多单与空单的下单核心,可直接贴进 EA 的 OnTick 信号判断之后验证:

MQL5 / C++
  class=class="str">"cmt">//--- Check for a buy signal
  if (Buffer7[class="num">0] != EMPTY_VALUE)
  {
    class="type">class="kw">double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
    class="type">class="kw">double sl = NormalizeDouble(ask - StopLoss * _Point, _Digits);
    class="type">class="kw">double tp = NormalizeDouble(ask + TakeProfit * _Point, _Digits);
    class=class="str">"cmt">//--- Prepare the buy order request
    class="type">MqlTradeRequest request;
    class="type">MqlTradeResult result;
    ZeroMemory(request);
    ZeroMemory(result);
    request.action = TRADE_ACTION_DEAL;
    request.symbol = Symbol();
    request.volume = Lots;
    request.type = ORDER_TYPE_BUY;
    request.price = ask;
    request.sl = sl;
    request.tp = tp;
    request.deviation = Slippage;
    request.magic = MagicNumber;
    request.comment = "Buy Order";
    class=class="str">"cmt">//--- Send the buy order
    if (!OrderSend(request, result))
      {
       Print("Error opening buy order: ", result.retcode);
      }
    else
      {
       Print("Buy order opened successfully! Ticket: ", result.order);
      }
   }
  class=class="str">"cmt">//--- Check for a sell signal
  if (Buffer6[class="num">0] != EMPTY_VALUE)
  {
    class="type">class="kw">double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
    class="type">class="kw">double sl = NormalizeDouble(bid + StopLoss * _Point, _Digits);
    class="type">class="kw">double tp = NormalizeDouble(bid - TakeProfit * _Point, _Digits);
    class=class="str">"cmt">//--- Prepare the sell order request
    class="type">MqlTradeRequest request;
    class="type">MqlTradeResult result;
    ZeroMemory(request);
    ZeroMemory(result);
    request.action = TRADE_ACTION_DEAL;
    request.symbol = Symbol();
    request.volume = Lots;
    request.type = ORDER_TYPE_SELL;
    request.price = bid;
    request.sl = sl;
    request.tp = tp;
    request.deviation = Slippage;
    request.magic = MagicNumber;
    request.comment = "Sell Order";
    class=class="str">"cmt">//--- Send the sell order
    if (!OrderSend(request, result))
      {
       Print("Error opening sell order: ", result.retcode);
      }
    else
      {
       Print("Sell order opened successfully! Ticket: ", result.order);
      }
   }

◍ 回测与图表事件钩子的空壳写法

EA 框架里除了 OnInit/OnTick,还有一组专门对接交易动作、策略测试器和图表交互的系统函数,上面这段代码把它们的骨架都列出来了。 OnTrade 在每笔成交或订单状态变化时被触发,这里留空代表暂不处理实时交易事件;OnTester 必须返回 double,是优化时自定义评分的出口,现在写死返回 0.0 等于没给测试器任何额外判据。 OnTesterInit 与 OnTesterDeinit 分别在跑回测前和跑完后调用,适合做计数器清零或文件收尾;OnTesterPass 则在每一组参数优化跑完时执行,可以用来把中间结果落盘。 OnChartEvent 接收 id、lparam、dparam、sparam 四个参数,覆盖点击、按键、对象拖拽等界面动作。把这些函数原样塞进 MT5 的 EA 模板,编译能通过但不会产生任何交易行为,仅作为后续填充逻辑的占位。外汇与贵金属品种波动剧烈,用此类空壳做实盘前务必接真实风控。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Trade function                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTrade()
  {
   class=class="str">"cmt">//--- Handle trade events if necessary
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Tester function                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double OnTester()
  {
   class="type">class="kw">double ret = class="num">0.0;
   class=class="str">"cmt">//--- Custom calculations for strategy tester
   class="kw">return(ret);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| TesterInit function                                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTesterInit()
  {
   class=class="str">"cmt">//--- Initialization for the strategy tester
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| TesterPass function                                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTesterPass()
  {
   class=class="str">"cmt">//--- Code executed after each pass in optimization
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| TesterDeinit function                                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTesterDeinit()
  {
   class=class="str">"cmt">//--- Cleanup after tester runs
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| ChartEvent function                                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnChartEvent(const class="type">int id,
                  const class="type">long &lparam,
                  const class="type">class="kw">double &dparam,
                  const class="type">class="kw">string &sparam)
  {
   class=class="str">"cmt">//--- Handle chart events here
  }
class=class="str">"cmt">//+------------------------------------------------------------------+

「在策略测试器里跑通趋势约束EA」

在 MetaEditor 里点「编译」或「运行」,先把程序编过再丢进策略测试器。本次编译无报错,直接对 Boom 500 指数做回测。 从导航栏调出策略测试器面板,正式跑之前能改参数。EA 里默认手数是 0.1 手,但 Boom 500 指数的点值结构不同,实测至少得调到 0.2 手才不会因合约规格被拒单或信号失真。 点右下角「开始」后,系统在测试期内走出一条平滑上行的净值曲线,回测可视化里趋势约束逻辑没有明显反指。外汇与指数类品种杠杆高、跳空频繁,回测顺滑不代表实盘同概率盈利,上 MT5 用同样参数自己跑一遍最实在。

把工具请下神坛

风险回报矩形把未平仓的潜在盈亏边界直接画在图上,在贵金属与外汇这种秒级跳动的盘口里,交易者少算一次心算、就少一次被情绪带偏的可能。它本质只是个视觉锚点,逼你盯住原计划,而不是替你预测方向。 Trend Constraint V1.09 指标负责标趋势和反转区,EA 接信号下单并管风险,两者打通后策略更成体系;但外汇/贵金属高杠杆,协同再顺也只提高概率,不消除爆仓风险。 三个文件各司其职:指标源码 20.95 KB、矩形脚本 7.94 KB、EA 6.79 KB。装 EA 前先确认指标已载入且放行了 DLL 依赖,否则会报 4802 无法创建自定义指标。工具跑通了,剩下的纪律归你自己。

把重复劳动交给小布
这些诊断——K线负载过高、缓冲区卡顿、信号脚本是否持续运行——小布盯盘的AIGC已内置,打开对应品种页即可看到负荷与信号标记,你专注决策就好。

常见问题

默认历史范围被设为5000根K线,数据量过大且多缓冲区计算拖慢窗口绘制;将回看周期降到1000根左右、并把矩形绘制拆成独立脚本可解决。
小布盯盘内置了AIGC诊断与信号标记,可呈现类似移动平均交叉与风险回报区域,但是否转成EA自动下单仍由你在本机终端配置,外汇贵金属属高风险品类,信号仅作概率参考。
脚本只做一次性任务、不持续运行,需手动拖入图表;EA随行情实时计算并可持续重绘,适合把交叉信号接成自动下单流程。
先确认iMA取价模式与缓冲同步,再检查脚本阶段遗留的删除旧对象逻辑是否在EA里误清了实时图形,最后看最大处理K线数是否拖慢循环。