MQL4通用EA模板拆解与实战
🤖

MQL4通用EA模板拆解与实战

从变量设计到风控函数的完整教学

MQL5 编程 难度 · 入门 2026-06-26 5 分钟阅读
#MQL4#EA模板#止损止盈#策略优化

为什么需要一个通用 EA 模板

许多交易者在迈出算法交易第一步时,往往不知道从何写起。是直接写买卖逻辑,还是先处理手数、止损、滑点?通用 EA 模板的价值在于:它把几乎所有自动交易系统都需要的公共部分——参数声明、初始化、仓位计算、订单管理、风控修改——预先结构化,你只需替换信号函数即可。这样不仅能减少重复劳动,还能保证在策略测试器中优化参数时行为一致。本文以 MetaQuotes 官方译介的通用模板为例,逐层拆解这个骨架。

需要强调的是,模板本身不包含圣杯策略,它提供的是工程规范。真实交易中,超过七成的 Bug 来自手数越界、止损小于平台最小止损位、追踪止损修改失败等基础问题,模板正是为规避这些而设计。

✦ 适用人群
如果你会用 MT4 但没自己写过 EA,或写过的 EA 混乱难维护,本模板是最佳起点。

外部变量:让策略可被优化

模板使用 extern 修饰符暴露参数,使它们在 EA 属性框和策略测试器优化中可见。关键设计是:用 0/1 整数而非 true/false 布尔,因为测试器对数值型扫描更友好。例如 Condition1=1 表示启用条件一,设 0 即关闭,优化时可遍历启用组合。

MaxLot 限制最大手数,防止过度下注;TakeProfit、StopLoss、TrailingStop 为 0 时代表该功能禁用;MinProfit 与 ProfitPoints 配合实现“回撤平仓”:价格触及 ProfitPoints 后回落至 MinProfit 就离场;LotSpliter 是仓位分割器,如 0.1 表示只用可用保证金的十分之一;CloseByOtherSideCondition 控制反向信号出现时是否平掉原单。

extern double MaxLot;
extern double TakeProfit;
extern double TrailingStop;
extern double StopLoss;
extern double MinProfit;
extern double ProfitPoints;
extern int    Slippage=0;
extern int Condition1=1;
extern double LotSpliter=1.0;
extern int CloseByOtherSideCondition;
⚠ 常见坑
止损若设为 0 代表不生效,但很多人误以为 0 是“默认平台值”。必须显式大于 0 才工作。

内部变量与初始化函数

内部变量如 Lot、PP(最高浮盈点记录)、slu/sld(上下轨差)等在 init() 中预赋初值。init() 仅启动一次,适合读取止损模板值 tp/sl,避免每次 tick 重算。注释用 Comment() 显示版本,\n 换行。

double Lot;
double PP=0;
double slu,sld,a,b;
double tp,sl;
int init()
  {
   tp=TakeProfit;
   sl=StopLoss;
   return(0);
  }
Comment("cloud trade \n v2.0.11");

preinit() 在每轮无单时调用,核心是根据余额、杠杆、ask 算 Lot,并受 MaxLot 与平台 MODE_MAXLOT 约束;同时把止损止盈拉升至不小于 MODE_STOPLEVEL,否则 OrderSend 会报错。

int preinit()
  {
   Lot=NormalizeDouble(MathFloor(LotSpliter*AccountBalance()*AccountLeverage()/Ask/MathPow(10,Digits+1)*10)/10,1);
   if(MaxLot>0 && Lot>MaxLot){Lot=MaxLot;}
   if(Lot>MarketInfo(Symbol(),MODE_MAXLOT)){Lot=MarketInfo(Symbol(),MODE_MAXLOT);}
   PP=0;
   StopLoss=sl;
   TakeProfit=tp;
   if(TakeProfit!=0 && TakeProfit<(MarketInfo(Symbol(),MODE_STOPLEVEL)))
     { TakeProfit=MarketInfo(Symbol(),MODE_STOPLEVEL); }
   if(StopLoss!=0 && StopLoss<(MarketInfo(Symbol(),MODE_STOPLEVEL)))
     { StopLoss=MarketInfo(Symbol(),MODE_STOPLEVEL); }
   return(0);
  }

主循环:无单与有单的分支

EA 核心逻辑极简:若 OrdersTotal()==0,调用 preinit 后由 U() 返回信号,1 买 2 卖;若有单,则选中管理。这种“只管一个单”的假设简化了代码,但实盘多单需改写。

if(OrdersTotal()==0)
   {   
      preinit();
      if(U()==1)
      { OrderBuy(); return(0); }
      if(U()==2)
      { OrderSell(); return(0); }
      return(0);
   }

有单时,先 SelectOnlyOrder 选位,再分配价格变量防止跳变。随后处理追踪止损、最小获利平仓、反向平单。注意模板用 PP 记录最高盈利点,实现 ProfitPoints 回撤逻辑。

if(OrderType()==OP_BUY)
  {
   if((slu)>PP) { PP=slu; }
   if(((slu)>0.001) && (OrderStopLoss()<(b-TrailingStop)) && (OrderOpenPrice()<(b-TrailingStop)) && (OrderProfit()>MathAbs(OrderSwap())))
     { if(TrailingStop!=0) { OrderModify(OrderTicket(), 0, b-TrailingStop, 0, 0, 0); } }
  }
⚠ 修改失败
OrderModify 失败常因新止损小于当前市价最小距离,实盘需检查返回值并刷新。

信号函数 U() 与条件封装

U() 汇总 U1()(随机指标交叉)与 U2()(分形趋势)信号,由 Condition1/2 开关。返回 0 无信号,1 买,2 卖。这种块化设计让策略拼接像搭积木。

int U()
  {
   if((U1()==2 && Condition1==1) || (U2()==2 && Condition2==1)){return(2);}
   if((U1()==1 && Condition1==1) || (U2()==1 && Condition2==1)){return(1);}
   return(0);
  }

U1 用 iStochastic 判断超买超卖与信号线主线交叉;U2 用 iFractals 数最近两个分形方向。你可以依样画葫芦加入均线、MACD 块,只需遵守返回 1/2/0 约定。

下单与平仓函数细节

OrderBuy/OrderSell 按止损止盈是否为零分四种 OrderSend 组合,并用 NormalizeDouble 规范精度。CloseOnlyOrder 封装 OrderClose,SelectOnlyOrder 封装 OrderSelect,降低主逻辑噪声。

bool OrderBuy()
  {
   bool res=false;
   if(StopLoss!=0 && TakeProfit!=0)
     { res=OrderSend(Symbol(), 0, NormalizeDouble(Lot,1), Ask, Slippage, NormalizeDouble(Ask-StopLoss,4), NormalizeDouble(Ask+TakeProfit,4), 0, 0, 0, 0); return(res); }
   if(StopLoss==0 && TakeProfit!=0)
     { res=OrderSend(Symbol(), 0, NormalizeDouble(Lot,1), Ask, Slippage, 0, NormalizeDouble(Ask+TakeProfit,4), 0, 0, 0, 0); return(res); }
   if(StopLoss==0 && TakeProfit==0)
     { res=OrderSend(Symbol(), 0, NormalizeDouble(Lot,1), Ask, Slippage, 0, 0, 0, 0, 0, 0); return(res); }
   if(StopLoss!=0 && TakeProfit==0)
     { res=OrderSend(Symbol(), 0, NormalizeDouble(Lot,1), Ask, Slippage, NormalizeDouble(Ask-StopLoss,4), 0, 0, 0, 0, 0); return(res); }
   return(res);
  }
✦ 编码建议
用 0/1 替代 true/false 便于优化;永远带止损测试;用函数块提升可读性。

模板的局限与进阶改造

该模板假设同时间仅一单、不区分魔术码、不处理多货币。实盘可加入 magic number 过滤、订单遍历池、部分平仓。但作为教学骨架,它完整示范了“参数—初始化—信号—执行—风控”的闭环。掌握后,你写任何 EA 都能以它为底座,效率倍增。

原文附 template.mq4 可于 https://www.mql5.com/zh/articles/1495 下载 ZIP 参考。注意材料版权归 MetaQuotes,禁止整段转载。

常见问题

代表该功能禁用,EA 不下止损单;必须显式设大于 0 才生效,且模板会自动拉升至不小于平台 MODE_STOPLEVEL。
因为策略测试器对数值参数优化更友好,可用 0/1 遍历启用组合,而布尔在优化器中不如整数灵活。
preinit() 中用 LotSpliter*余额*杠杆/Ask 估算,再受 MaxLot 与 MarketInfo MODE_MAXLOT 限制,最后 NormalizeDouble 规范。
价格浮盈达 ProfitPoints 后记录高点 PP,若回落至 MinProfit 则 CloseOnlyOrder 平仓,实现回撤止盈。
仿 U1/U2 写返回 1(买)/2(卖)/0(无) 的函数,在 U() 里用 ConditionX 开关汇总即可,不需改主循环。