MQL5 简介(第 8 部分):初学者构建 EA 交易系统指南(二)(基础篇)
「EA 到底跑在哪一层」
MQL5 写的自动交易系统(EA)不是独立程序,而是寄生在 MetaTrader 5 客户端里的执行体。你写的策略逻辑编译成 ex5,挂到图表上才生效,行情与下单通道全走 MT5 终端,脱离平台单独跑不了。 这套架构意味着:回测用的是什么点差、什么滑点模型,实盘就可能偏离多少。2025 年 3 月 6 日发布的初学者指南原文里提到,该篇在发布初期就有 1860 次浏览,说明新手对「EA 怎么嵌进终端」这件事的认知缺口很大。 想验证也很直接:开 MT5,Ctrl+N 调出导航器,把任意一个自带 EA 拖到 EURUSD 的 M1 图,立刻能在「专家」标签页看到初始化日志。这一眼就能确认——EA 的生命周期绑定图表周期,换周期不重新加载就不会重算。
◍ 用前一天日K定方向、首根小时K验证的EA思路
学完 MQL5 基础后,真正能落地的第一步是写一个有明确规则的 EA,而不是再啃语法。本篇用基于项目的方式,带你从零搭一个按烛形形态自动决策的机器人,避免论坛里「给了片段却拼不出完整程序」的常见困境。 这个 EA 的逻辑很直接:读前一天的日线烛形——若收阴,当天只找卖点;若收阳,当天只找买点。再用当天第一根 1 小时烛形的收盘价做信号确认,避免裸方向进场。 风控上它很克制:任意时刻只允许 1 个未平仓位,每天最多 2 笔交易;交易时段锁在周一至周三的指定时间,其余时间不盯盘。外汇与贵金属杠杆高、滑点猛,这种硬限制能帮新手压住过度交易的概率。 初学者常卡住的几个点——怎么下买卖单、怎么取 OHLC、怎么不被每个 tick 触发、怎么限单数、怎么设交易时段与星期过滤、怎么给损益封顶——都会在这套代码里逐一现形,你拷过去改几个参数就能在 MT5 策略测试器里跑。
先画逻辑草图再挂交易库
动手写 EA 前,用伪代码把交易逻辑钉死比直接敲代码更省时间。以「前一日收阴则卖、收阳则买」为例,规则要落到具体约束:只在周一至周四跑、单日最多两笔、同时刻仅一个未平头寸、收市平仓。这类清单越具体,后面 MQL5 实现越不容易返工。 MQL5 的库相当于现成的工具书,Trade.mqh 就是管开仓平仓的那一本。你不必重写订单发送细节,只要把它请进项目,再实例化一个 CTrade 对象就能调用。下面这段代码把「引入库—建实例—设幻数」三步一次做完,幻数 103432 用于区分本 EA 与其他来源的下单。 别把正态当圣经:幻数只是标识,不是盈利保证。外汇与贵金属杠杆高、滑点跳空频繁,挂上 CTrade 后仍需在策略测试器里用真实点差回测,确认每日两笔限制真的生效。 代码里 OnInit 中调用 trade.SetExpertMagicNumber(MagicNumber),意味着此后该实例发出的每笔订单都带 103432 标签,便于在终端「交易」标签里筛选自查。
class="macro">#include <Trade/Trade.mqh> class=class="str">"cmt">// Include the trade library for trading functions class="macro">#include <Trade/Trade.mqh> class=class="str">"cmt">// Include the trade library for trading functions class=class="str">"cmt">// Create an instance of the CTrade class for trading operations CTrade trade; class=class="str">"cmt">//magic number class="type">int MagicNumber = class="num">103432; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">// Set the magic number for the EA&class="macro">#x27;s trades trade.SetExpertMagicNumber(MagicNumber); class=class="str">"cmt">// Return initialization success class="kw">return(INIT_SUCCEEDED); } CTrade trade; class="type">int MagicNumber = class="num">103432; trade.SetExpertMagicNumber(MagicNumber);
「用 CopyClose 和 CopyOpen 抓取烛形开收价」
判断一根 H1 或 D1 烛形是看涨还是看跌,核心就看收盘价是否高于开盘价。MT5 里不需要自己循环去读缓冲,直接调 CopyClose 与 CopyOpen 就能把指定品种、指定周期的开盘与收盘序列搬进数组。 这两个函数有三种调用形态。第一种按起始位置加数量:start_pos=2、count=5 表示从第 3 根烛形开始连续取 5 根,索引 0 是最旧那根(若未反转序列)。第二种按开始日期加数量:传入 datetime 型的 start_date,如 D'2023.06.01',再给 count=5,就能拿到 6 月 1 日起的 5 根日线收口价。第三种按起止日期:start_date 到 stop_time(如 D'2023.06.05')之间闭区间覆盖的所有烛形被一次性复制。 做日内 EA 时每天手改日期不现实。把 "00:00"、"20:00" 这类字符串用 StringToTime 转成 datetime,它自动绑到当天,无需每日重写。配合 ArraySetAsSeries 把数组设成右到左(最新在索引 0),再用 CopyClose(_Symbol, PERIOD_D1, 2, 5, close_prices) 取最近第 3~7 根日线,就能比价定多空倾向。外汇与贵金属杠杆高,信号仅代表概率倾向,实盘前请在 MT5 策略测试器核对数组索引。
CopyClose(symbol_name, timeframe, start_pos, count, close_array); CopyOpen(symbol_name, timeframe, start_pos, count, open_array); class="type">class="kw">double close_prices[]; class="type">class="kw">double open_prices[]; CopyClose(_Symbol, PERIOD_D1, class="num">2, class="num">5, close_prices); class=class="str">"cmt">// Copy the close prices of class="num">5 daily candlesticks starting from the 3rd candlestick CopyOpen(_Symbol, PERIOD_D1, class="num">2, class="num">5, open_prices); class=class="str">"cmt">// Copy the open prices of class="num">5 daily candlesticks starting from the 3rd candlestick CopyClose(symbol_name, timeframe, timeframe, count, close_array[]); CopyOpen(symbol_name, timeframe, timeframe, count, open_array[]); close_prices[]; class="type">class="kw">double open_prices[]; class="type">class="kw">datetime start_date = D&class="macro">#x27;class="num">2023.06.class="num">01 class="num">00:class="num">00&class="macro">#x27;; class=class="str">"cmt">// Starting from June 1st, class="num">2023 class=class="str">"cmt">// Copy the close prices of class="num">5 daily candlesticks starting from June 1st, class="num">2023 CopyClose(_Symbol, PERIOD_D1, start_date, class="num">5, close_prices); class=class="str">"cmt">// Copy the open prices of class="num">5 daily candlesticks starting from June 1st, class="num">2023 CopyOpen(_Symbol, PERIOD_D1, start_date, class="num">5, open_prices); CopyClose( symbol_name, timeframe, start_time, stop_time, close_array[]); CopyOpen(symbol_name, timeframe, start_time, stop_time, open_array[]); class="type">class="kw">double close_prices[]; class="type">class="kw">double open_prices[]; class="type">class="kw">datetime start_date = D&class="macro">#x27;class="num">2023.06.class="num">01 class="num">00:class="num">00&class="macro">#x27;; class=class="str">"cmt">// Starting from June 1st, class="num">2023 class="type">class="kw">datetime end_date = D&class="macro">#x27;class="num">2023.06.class="num">05 class="num">00:class="num">00&class="macro">#x27;; class=class="str">"cmt">// Ending on June 5th, class="num">2023 class=class="str">"cmt">// Copy the close prices from June 1st to June 5th CopyClose(_Symbol, PERIOD_D1, start_date, end_date, close_prices); class=class="str">"cmt">// Copy the open prices from June 1st to June 5th