针对初学者以 MQL5 编写EA 交易的分步指南(基础篇)
📘

针对初学者以 MQL5 编写EA 交易的分步指南(基础篇)

第 1/3 篇

「从零搭一个 MT5 EA 的路线」

想在 MT5 里写第一个 EA,最省事的入口就是 MetaEditor 里的向导:新建 → Expert Advisor (template),它会自动生成带 OnTick 和 OnInit 骨架的 .mq5 文件。这套官方模板在 2013 年那篇入门贴里被 85 578 次浏览过,说明新手踩坑点高度重合——多半卡在事件函数和订单发送逻辑。 先把 OnInit 当初始化区,放指标句柄和输入参数校验;真正下单动作全收进 OnTick,避免历史回测时重复触发。外汇和贵金属杠杆高,EA 未做手数校验就发单,可能在一根瞬时毛刺里把账户拖到强平。 验证方法很简单:写完先接 Strategy Tester 用 2020–2023 的 XAUUSD 一分钟数据跑一遍,看是否报‘trade context busy’或句柄为空的错,比直接上模拟盘更直观。

这套 EA 写给哪种人看

这一节面向的是还没碰过 MQL5 的新手——准确说,是那些想用这门语言写出第一个能跑的“EA 交易”的人。 我们不会一上来堆语法,而是先把 EA 要干什么说清楚,再讲它靠什么机制去干。对新手来说,先定功能、再找实现路径,比直接读代码更容易绕开“抄了跑不起来”的坑。 外汇和贵金属杠杆高、滑点跳空频繁,哪怕只是练习用 EA 在 MT5 模拟盘验证,也得先想明白它该对哪些行情条件反应,而不是盲目挂单。

◍ 用 MA-8 与 ADX 搭一套趋势过滤逻辑

先把 EA 要干的事想清楚:它只盯一个指标组合,条件齐了才动手,而不是全天候扫单。这里选时间周期 8 的移动平均线(MA-8)和同周期的平均方向性运动指标(ADX)做搭档。 具体规则很直接——MA-8 向上且收盘价站上 MA-8,触发买入倾向;MA-8 向下且收盘价跌破 MA-8,触发卖出倾向。但光有均线不够,得用 ADX 卡一道趋势门:只有 ADX 大于 22 且未走下坡时才允许开仓,ADX 小于 22 或虽大于 22 却在递减,即便均线信号给了也按兵不动。 风控参数固定为 30 点止损、100 点利润目标。EA 只在新 K 线形成时检查信号,且用持仓状态锁避免重复开仓:没多单才接多,没空单才接空。外汇与贵金属杠杆高,这套过滤只降频率不降风险,参数请上 MT5 用 EURUSD 的 M15 实测。

「从向导搭出可调参的 EA 骨架」

开 MT5 后直接进 MetaQuotes Language Editor 5,按 Ctrl+N 调出向导,选 Expert Advisor 一路 Next。在 Name 框填 My_First_EA,Author、Link 随意;关键是点 Add 把止损、获利、ADX 周期、均线周期塞进输入参数里,方便后面跑参数找组合。 向导里给四个参数都选了 int(4 字节,范围 -2,147,483,648 到 2,147,647,647)。其实按值的大小用 char(上限 127)或 uchar(上限 255)更省内存,但 int 可读性最好,新手不容易踩无符号类型存负数爆值的坑。 点 Finish 后编辑器吐出的框架分三块:头部的 #property 属性区、input 全局输入区、OnInit/OnDeinit/OnTick 函数体。以 # 开头的是预处理指令不加分号,比如 #define ABC 100 会把代码里 ABC 全替换成 100,编译期生效。 input 区声明的变量外部可改、函数内全可见;OnInit 只在加载时跑一次,适合做柱数检查和抓指标句柄;OnDeinit 卸载时释放句柄;真正跑策略逻辑全在 OnTick。注意客户端“Auto Trading”没点亮,EA 连单都下不出去。 写注释用 // 单行或 /* */ 多行,编译直接忽略。给 input 加 // 注释后,用户在 EA 属性框看到的是注释而不是变量名,这点对实盘易用性影响很大。 初始化里 iADX(NULL,0,Adx_Period) 抓 ADX 句柄,iMA(NULL,0,Ma_Period,0,MODE_SMA,PRICE_CLOSE) 抓均线句柄。若返回 INVALID_HANDLE,用 GetLastError 弹窗查因。STP/TKP 从只读 input 拷出来,遇 5 位或 3 位报价图表乘 10 对齐 Digits(),否则 brokers 点位偏差会让止损单无效。 OnTick 开头先 if(Bars(_Symbol,_Period)<60) 直接 Alert 退出,柱不够不动作。新柱识别用 static datetime Old_Time 存上根柱时间,和当前 New_Time[0] 比,配 bool IsNewBar 默认 false,只在换柱置 true——避免每跳都重判买卖,省 CPU 也防重复开仓。外汇与贵金属杠杆高,未对齐点差和手数前勿直接挂实盘。

把柱数据、报价和持仓状态搬进EA内存

新柱判定靠 CopyTime 把当前柱时间写进只有一个元素的数组 New_Time,和上次存的 Old_Time 比对,不等就置 IsNewBar=TRUE。IsNewBar 为 FALSE 时直接退出 OnTick,避免每个报价都重算。 OnInit 只在EA挂图表时跑一次,OnTick 每次来报价都触发,所以在 OnTick 里用局部变量 Mybars 重新数一遍可用柱数(用历史数据总柱数表达式填进去)更稳妥。局部变量只在声明它的函数内有效,和输入参数区的全局变量不同,出了函数就访问不到。 MQL5 内置结构帮我们省掉手写字段的麻烦:MqlTick 装最新买卖价和成交量,SymbolInfoTick() 一调就把值塞进 latest_price;MqlTradeRequest 是下单请求容器,我们命名为 mrequest;MqlTradeResult 接回执,命名为 mresult;MqlRates 装 OHLC+点差+成交量,我们用 mrate[] 存。 [CODE] struct MqlTick { datetime time; // 上次更新价格的时间 double bid; // 当前卖价 double ask; // 当前买价 double last; // 上次交易价格(最后价格) ulong volume; // 当前最后价格的交易量 }; [/CODE] 上面这段 MqlTick 逐行看:time 是价格刷新时间点;bid/ask 是当前买卖报价;last 是真实成交的最后一口价;volume 是那口价的成交量。 ArraySetAsSeries(mrate, true) 把数组倒序索引,保证 mrate[0] 永远是当前柱、mrate[1] 前一根,顺着写条件不易错。CopyRates(_symbol, _period, 0, 3, mrate) 从柱0起抓3根进数组;成功后 mrate[1].open 是前一根开盘、mrate[0].high 是当前最高。 指标值用 CopyBuffer 搬:ADX 句柄有3个缓冲区(0主线、1+DI、2-DI),均线只有0号主线;从柱0复制3根到 adxVal/plsDI/minDI/maVal。CopyBuffer 和 CopyRates 成功返复制条数、出错返 -1,所以错误检查写 <0 就够。 持仓唯一性靠 PositionSelect(_symbol) 判有无仓,再拿 PositionGetInteger(POSITION_TYPE) 区分 POSITION_TYPE_BUY / POSITION_TYPE_SELL,结果存进 Buy_opened / Sell_opened 两个 bool。后面下单前先瞅这俩变量,能挡掉同方向加仓。外汇和贵金属杠杆高,这类检查只是工程兜底,不预示胜率。 买入条件拆成四个 bool 子条件,任一条满足就记 TRUE,后面统一进下单判断。这样在 MT5 里单步调试时,哪一环没亮一目了然。

MQL5 / C++
class="kw">struct class="type">MqlTick
{
   class="type">class="kw">datetime time;           class=class="str">"cmt">// 上次更新价格的时间
   class="type">class="kw">double   bid;            class=class="str">"cmt">// 当前卖价
   class="type">class="kw">double   ask;            class=class="str">"cmt">// 当前买价
   class="type">class="kw">double   last;           class=class="str">"cmt">// 上次交易价格(最后价格)
   class="type">class="kw">ulong    volume;         class=class="str">"cmt">// 当前最后价格的交易量
};

常见问题

先用平台自带的EA向导生成空骨架,再往里填均线和持仓判断逻辑,别一上来就手敲完整文件。
适合。文中用MA-8加ADX做趋势过滤,逻辑轻量,可先当信号提醒用,再决定是否放权自动下单。
可以。小布能按你的品种和周期直接生成对应的趋势过滤诊断,打开品种页就能看信号,不用自己写EA。
用柱数据函数和报价函数分别取iOpen、Ask/Bid,存进变量即可在条件里实时比较。
文中以ADX<20视为趋势不足,此时过滤掉开仓信号;参数可在向导里调,建议先回测再定阈值。