构建自动运行的 EA(第 09 部分):自动化(I)(基础篇)
「把EA交给MT5自己跑起来」
在 MT5 里写一个 EA,最基础的门槛不是算信号,而是让它在客户端关掉图表后还能活着干活。MQL5 的 CTrade 类封装了 OrderSend / PositionOpen 等底层调用,比直接拼交易请求结构体省掉一半出错概率。 真正跑自动化的第一步,是在 OnInit 里把交易对象实例化,并在 OnTick 里判断交易上下文是否允许下单。很多新手把下单逻辑直接堆在 OnTick,却忘了检查账户杠杆和品种点差,结果回测漂亮、实盘一口单都进不去。外汇与贵金属杠杆高、滑点突变频繁,这类故障在真实账户可能直接吞掉一周利润。 下面这段是最小可运行骨架:先在初始化时建一个 CTrade,之后每次报价进来先测环境再动作。你可以直接拷进 MetaEditor 编译,挂到 EURUSD 的 M1 上观察是否每根新蜡烛打印一次『tick』。
CTrade trade; class="type">int OnInit() { trade.SetDeviation(class="num">10); class="kw">return(INIT_SUCCEEDED); } class="type">void OnTick() { class="kw">static class="type">class="kw">datetime last_bar; class="type">class="kw">datetime now_bar = iTime(_Symbol, PERIOD_M1, class="num">0); if(now_bar != last_bar) { last_bar = now_bar; Print("tick"); } }
◍ 先搭不拖慢平台的 OCO 雏形
在 MT5 里做初级自动化,很多人担心 EA 或终端被事件机制拖垮。这里走一条更轻的路:用不令 EA 或平台超载的逻辑来挂单,普通净持结算账户能用,对冲账户也兼容。 第一步只做最基础的 OCO(一单成交另一单自动撤销)系统。它结构简单、资源占用低,适合先跑通流程。 后续会在这个雏形上扩展,做成更稳、更适合高波动行情的版本——那种行情里手动补单容易错过,自动化才有价值。外汇和贵金属杠杆高、滑点跳空频繁,任何自动挂单都只是降低遗漏概率,不消除风险。
用轻量触发器管住 OCO 的盈亏平衡与尾随
OCO(一笔订单取消另一笔)的逻辑是:挂单或持仓里的止盈止损,会随原单撤销或平仓一起消失。为不让 EA 代码膨胀,我们假定这些挂单只在发单时创建、平仓时自动清理,不动态增删。 盈亏平衡触发极简:先 PositionSelectByTicket 拉取持仓最新数据,再用 PositionGetDouble(POSITION_PROFIT) 取浮动盈亏,和构造函数里设的 m_Trigger 比。达到或超过就调 C_Orders 里的改价函数,把止损推到开仓价。这套判断不挑品种和图表类型,只看利润水平。 尾随停止比盈亏平衡多两层依赖:图表绘制模式(最后价还是买卖价)和仓位方向。若点差历史偏大,基于 SYMBOL_LAST 也可能把止损放错位置,所以得先抓准资产真实价格,再算它和当前 SL 的差值,转成点数后必须 ≥ 间隙值的两倍才移动。间隙固定,循环往复。 这种触发器像捕鼠夹——机制重了、执行慢了,行情早跑没影了。实测它极轻量、响应快,是能反复跑不拖慢 EA 的关键。注意别在 OnTick 里直接调,下文再说用什么事件更合适。
class="kw">inline class="type">void TriggerBreakeven(class="type">void) { if (PositionSelectByTicket(m_Position.Ticket)) if (PositionGetDouble(POSITION_PROFIT) >= m_Trigger) m_Position.EnableBreakEven = (ModifyPricePoints(m_Position.Ticket, m_Position.PriceOpen, m_Position.PriceOpen, m_Position.TP) ? class="kw">false : true); } class="kw">inline class="type">void TriggerTrailingStop(class="type">void) { class="type">class="kw">double price; if ((m_Position.Ticket == class="num">0) || (m_Position.SL == class="num">0)) class="kw">return; if (m_Position.EnableBreakEven) TriggerBreakeven(); else { price = SymbolInfoDouble(_Symbol, (GetTerminalInfos().ChartMode == SYMBOL_CHART_MODE_LAST ? SYMBOL_LAST : (m_Position.IsBuy ? SYMBOL_ASK : SYMBOL_BID))); if (MathAbs(price - m_Position.SL) >= (m_Position.Gap * class="num">2))
「把缺口幅度塞进移动止损」
上面这行调用把持仓的开仓价、止损、止盈一次性丢给 ModifyPricePoints。核心在止损重算:用原 SL 加上缺口 Gap 乘以方向系数,买仓乘 1、卖仓乘 -1,等于顺着缺口把止损推远一格。 外汇与贵金属跳空常见,尤其周末停盘后周一开盘。这套逻辑不保证避损,只是让止损位跟着缺口偏移,概率上降低被瞬时假突破扫掉的可能。 开 MT5 把这段接进你的持仓管理类,把 m_Position.Gap 的来源先打桩输出,确认它取的是真实跳空点数再实盘。高风险品种先跑模拟。
ModifyPricePoints(m_Position.Ticket, m_Position.PriceOpen, (m_Position.SL + (m_Position.Gap * (m_Position.IsBuy ? class="num">1 : -class="num">1))), m_Position.TP); }