构建自动运行的 EA(第 14 部分):自动化(VI)·进阶篇
🤖

构建自动运行的 EA(第 14 部分):自动化(VI)·进阶篇

(2/3)· 手动智能EA如何平滑转为自动运行,监管边界与最小改动原则一次讲清

偏理论进阶 第 2/3 篇
不少交易者一上来就把整段EA代码重写以求“全自动”,结果旧逻辑被改崩,连手动都回不去。其实自动化只需在定时器里挂一个新函数,其余结构保持原样才稳妥。

「MQL5 代码块的注释边界」

MQL5 源码里常看到以 //+--- 开头的连续横线,这并非语法要素,只是用来在编辑器里划分逻辑区的视觉注释。 MT5 的 MetaEditor 不会编译这些符号,它们对 EA 运行零影响;但若你复制代码到第三方工具,个别解析器可能把整段当注释吞掉。 验证方式很简单:在 MT5 新建脚本,贴入下方片段,编译通过后看左侧导航是否仍识别函数——能识别就说明注释边界无害。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+

◍ 拆开 C_Automaton 的三个核心方法

C_Automaton 类无论挂在哪种策略上,骨架都只有三块:构造函数、被 EA 时间事件驱动的主函数、以及类内部私有的辅助函数。其余方法都是为这三块服务的,本质上跑不掉这个结构。 作者演示用的 EA 是基于 9 周期指数移动平均线,在每个新 K 线开盘时入场。上一篇文章已经讲过这套系统的搭建,这里重点不在策略逻辑,而在类怎么把建仓、管理、平仓接起来。 进代码前先记一个事实:演示视频约 25 分钟,里面跑的是纯自动模型,任何系统都不可避免碰上亏损,只是多少问题。看懂视频再读下面的代码示例,才知道类里每个方法在管哪一环。

共用骨架与依赖部件怎么切分

写全自动 EA 时先分清楚两类代码:所有模型都复用的骨架,以及只属于你那套策略的依赖部件。骨架不动,依赖部件决定模型独一无二的行为,别在骨架里硬塞特殊逻辑,否则后续扩展会互相污染。 C_Automaton 类公开继承 C_Manager,因此能直接调 C_Manager 的初始化与控件过程。类里受保护部分声明了 eTrigger 枚举(TRIGGER_NONE / BUY / SELL),虽外部用不到,但必须放在受保护段才能初始化静态类变量。 变量区几乎不用改:一个 st00 结构存 Shift、nBars、OverBought、OverSold,覆盖多数指标场景;另有一维缓冲区 m_Buff[] 接收指标值、m_TF 定时间帧、m_LastTrigger 存上次触发态。静态变量要在类外初始化,且必须用 TRIGGER_NONE 起步,避免 EA 挂图瞬间乱触发。 私密函数 GetTrigger 默认返空触发,只在新柱且句柄有效时才计算,这条规则锁死能少踩很多坑。构造函数把 EA 传来的图表周期写死给自动化系统用——若交易者中途切周期,EA 仍按原 TF 工作,不会误触发,这是很多开源 EA 翻车的高频点。 OnTime 里那段不是 100% 终版:它用记忆变量禁了同信号加仓,有意不让人轻易改;没把握就别动,否则可能几秒亏光余额。过程声明成原样是为防继承链里被覆写,MQL5 的 final 类机制帮我们锁住模块化安全。 外汇与贵金属自动交易属高风险,没有系统保盈利,只自动化你真懂的模型,别拼凑策略。下面这段是骨架原码,逐行拆完你就能照着加自己的依赖部件。

MQL5 / C++
class="macro">#class="kw">property copyright "Daniel Jose"
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include "C_Manager.mqh"
class=class="str">"cmt">//+------------------------------------------------------------------+
class C_Automaton : class="kw">public C_Manager
{
				class="kw">protected:
					enum eTrigger {TRIGGER_NONE, TRIGGER_BUY, TRIGGER_SELL};
			class="kw">private :
				class="kw">struct st00
				{
						class="type">int		 Shift,
							nBars;
						class="type">class="kw">double	OverBought,
							OverSold;
					}m_Infos;
				class="type">class="kw">double	m_Buff[];

「状态机里的触发器与构造入参」

这段代码片段展示了一个基于状态机(Automaton)的 EA 骨架,核心是先声明私有成员与静态记忆变量,再在构造函数里固化交易参数。 私有区里 m_nBars 记录当前周期已加载 K 线数,m_Handle 存指标句柄,m_TF 锁定时间框架;静态变量 m_Memory 用来跨 tick 保留上一次触发状态,避免重复信号。 CheckTrigger 方法只写了一半:当 iBars(NULL,m_TF) 大于 m_nBars 且句柄有效时进入空分支,否则直接返回 TRIGGER_NONE,说明实际信号逻辑要后续填充。 构造函数 C_Automaton 把 magic、止损止盈金额、杠杆、是否日内、追踪止损、周期、超买 70 / 超卖 30、偏移 1 根作为默认入参,并调用父类 C_Manager 初始化,同时将缓冲数组设为时间序列(ArraySetAsSeries(m_Buff,true))。 开 MT5 把这段粘进类定义,先补全 CheckTrigger 里的空分支,把 m_nBars 初始值改成 iBars 返回值,就能跑通基础框架;外汇与贵金属杠杆交易高风险,参数未经实盘验证,信号可能失效。

MQL5 / C++
class="type">int    m_nBars,
                  m_Handle;
ENUM_TIMEFRAMES m_TF;
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">static eTrigger m_Memory;
class=class="str">"cmt">//+------------------------------------------------------------------+  
class="kw">inline eTrigger CheckTrigger(class="type">void)
                  {
                     class="type">int iRet;
                     if (((iRet = iBars(NULL, m_TF)) > m_nBars) && (m_Handle != INVALID_HANDLE))
	       {
	       }
                     class="kw">return TRIGGER_NONE;
                  }
class=class="str">"cmt">//+------------------------------------------------------------------+  
      class="kw">public  :
class=class="str">"cmt">//+------------------------------------------------------------------+
                  C_Automaton(class="kw">const class="type">ulong magic, class="type">class="kw">double FinanceStop, class="type">class="kw">double FinanceTake, class="type">uint Leverage,
                              class="type">bool IsDayTrade, class="type">class="kw">double Trailing, class="kw">const ENUM_TIMEFRAMES iPeriod,
                              class="kw">const class="type">class="kw">double OverBought = class="num">70, class="kw">const class="type">class="kw">double OverSold = class="num">30, class="kw">const class="type">int iShift = class="num">1)
                  :C_Manager(magic, FinanceStop, FinanceTake, Leverage, IsDayTrade, Trailing, true, class="num">10),
                   m_TF(iPeriod),
                   m_Handle(INVALID_HANDLE)
                  {
                     m_Infos.Shift      = iShift;
                     m_Infos.OverBought = OverBought;
                     m_Infos.OverSold   = OverSold;
                     ArraySetAsSeries(m_Buff, true);
                     m_nBars = iBars(NULL, m_TF);
                  }

◍ 触发器的状态记忆与反手逻辑

上面这段是自动机里 Triggers 方法的最终实现,用 final 锁死不允许子类重写,核心就干一件事:按时间过滤和触发信号决定平仓还是反向进场。 先看入口判断:CtrlTimeIsPassed() 返回 false 说明没到允许交易的时间窗,直接 ClosePosition() 清掉当前仓;过了时间窗才进 switch(CheckTrigger()) 分买卖触发。 买卖分支都用了 m_Memory 做状态记忆。以 TRIGGER_BUY 为例:若上一次记忆是 TRIGGER_SELL,说明持仓方向相反,先 ClosePosition() 平掉空单;若记忆不是 TRIGGER_BUY,才 ToMarket(ORDER_TYPE_BUY) 开多,随后把 m_Memory 刷成 TRIGGER_BUY。卖分支完全对称。 类外那行 static C_Automaton::eTrigger C_Automaton::m_Memory = TRIGGER_NONE; 是静态成员初始化,保证 EA 加载时状态机从“无触发”起步,不会误读上一次 MT5 终端残留值。 在 MT5 里把这段代码塞进你的 C_Automaton 类,跑 EURUSD 的 M15 复盘,重点观察 m_Memory 在震荡市里频繁在 BUY/SELL 间翻转时平仓次数是否过多——外汇和贵金属杠杆高,这种反手频率会显著放大点差和佣金磨损。

MQL5 / C++
class="kw">inline class="kw">virtual class="type">void Triggers(class="type">void) final
  {
  if (!CtrlTimeIsPassed()) ClosePosition(); else class="kw">switch (CheckTrigger())
    {
    case TRIGGER_BUY:
      if (m_Memory == TRIGGER_SELL) ClosePosition();
      if (m_Memory != TRIGGER_BUY) ToMarket(ORDER_TYPE_BUY);
      m_Memory = TRIGGER_BUY;
      class="kw">break;
    case TRIGGER_SELL:
      if (m_Memory == TRIGGER_BUY) ClosePosition();
      if (m_Memory != TRIGGER_SELL) ToMarket(ORDER_TYPE_SELL);
      m_Memory = TRIGGER_SELL;
      class="kw">break;
    }
  };
class=class="str">"cmt">//+------------------------------------------------------------------+  
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">static C_Automaton::eTrigger C_Automaton::m_Memory = TRIGGER_NONE;
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include "C_Manager.mqh"
class=class="str">"cmt">//+------------------------------------------------------------------+
class C_Automaton : class="kw">public C_Manager
{
  class="kw">protected:
    enum eTrigger {TRIGGER_NONE, TRIGGER_BUY, TRIGGER_SELL};
  class="kw">private :
把监管动作交给小布盯盘
这些诊断小布盯盘的AIGC已内置,打开对应品种页即可看到EA自动下单后的持仓异动与异常挂单提醒,你只管决定是否介入。

常见问题

它代替交易者在订单簿中开仓、平仓或下挂单,原本由C_Mouse接收手动事件的部分转由该类按规则驱动,但监管者角色仍由人承担。
小布盯盘不托管EA运行,但可同步读取账户持仓与挂单流,对自动策略的异常撤单、滑点放大做实时标注,降低无人监督的盲区。
手动模式依赖图表事件捕捉点击与拖拽,自动模式由定时器触发决策,该处理程序失去用途,移除可避免冗余事件干扰执行路径。
应先移除C_Automaton类还原C_Mouse,在手动模式下测透修改点,确认稳健后再加回自动化类,切忌边自动跑边改核心逻辑。
不能。这类品种高杠杆高波动,自动化系统绝不可无人监督,人为干预虽是最后手段,但持续盯盘与熔断准备是前提。