如何快速创建一个
📘

如何快速创建一个"2010 年自动交易锦标赛"专用"EA 交易"(基础篇)

第 1/2 篇

用 MT5 快速搭一个锦标赛风格 EA 骨架

想在 MT5 里复刻「2010 自动交易锦标赛」那种 EA 的起步结构,其实不需要从零写策略,先跑通一个最小可执行框架更实际。官方示例里这类 EA 通常挂在 OnTick 里做条件判断,再用 CTrade 下单,省去手动处理交易返回码。 根据站内示例存档,这篇 2013-11-08 发布的入门帖被查看 3411 次、收藏 7 次,说明即便多年后仍有交易者在照着搭基础框架。外汇与贵金属杠杆高、滑点跳空频繁,直接上实盘前务必在策略测试器用历史数据验证至少 3 个月。 最小骨架建议先写死一个均线穿越逻辑占位,确认编译通过、回测能出成交,再替换成你自己的信号。MT5 的高频 tick 回测对 EA 稳定性要求明显高于 MT4,早期就把日志打印开起来能少走弯路。

◍ 从现成模板起步写 EA

想参加 2010 自动交易锦标赛这类活动,没必要从零搭框架。直接用《交易机器人的原型》里给出的 EA 模板就行,它的策略基类、常用函数和整体结构都已经写好,新手 MQL5 程序员也接得住。 你真正要补的只是少量代码:把本人的交易想法翻译成开平仓逻辑。准备动作拆开看有六件——选策略、写 EA、回测、在策略测试器里做优化、调参优化、换不同周期再测一遍。 这类活儿的高风险在于:外汇和贵金属杠杆品种,回测漂亮不代表实盘能跑,过度拟合会让样本外表现塌房。建议每改一版参数,至少用 1 分钟和 1 小时两套周期各跑一次验证。

「顺着轨道线抢反弹的建仓逻辑」

不少老手默认随势交易比区间折腾更划算,而且盘中向通道边界的回弹,出现频率确实高于轨道线被直接击穿。基于这个观察,我们只在当前趋势方向上,等价格碰通道边界(轨道线)反弹时进场,而不是去赌边界突破。 平仓只看三件事:出现反向信号、打到 Stop Loss、或者触到 Take Profit,不额外加主观过滤。趋势判定上,日线 MACD 的连续变化是核心——连续两个柱增长视为买入信号,连续两个柱缩短视为卖出信号;具体下单则切到小时图,等价格真从轨道线弹起来再动手。 EURUSD 日线图里能直观看到,MACD 柱体连增两根的窗口,往往对应一波像样的向上反抽;而小时图上的轨道线边界,反弹触点比假突破多出不少。外汇与贵金属杠杆高,这种抢反弹手法在单边市里容易被止损,实盘前先用 MT5 回看近三个月 EURUSD 的轨道线反应再定参数。

把策略骨架编译成可测的 EA 类

EA 的躯干可以直接继承 CExpertAdvisor 基类,不用从零写订单与仓位管理。上面这段代码把外部输入和内部变量分成了两层:input 是你在 MT5 参数面板上能改的,protected 成员则是运行时真正被引用的副本。 注意指标句柄的创建位置——MACD 挂在 PERIOD_D1、轨道线挂在 PERIOD_H1,两套周期硬耦合在 Init() 里。若你想测 H4 共振,得改这两行的 PERIOD_ 常量,否则回测里信号源不会变。 析构函数里用 IndicatorRelease 释放 m_hmacd 与 m_henvel,避免重复加载指标拖慢策略测试器。编译通过后,把 ex5 丢进策略测试程序跑 2003—2023 的 XAUUSD 日线,才可能看出 SL=50 / TP=100 这组默认参数的盈亏分布倾向。外汇与贵金属杠杆高,回测盈利不代表实盘能复现,参数须自行承压验证。

MQL5 / C++
class="macro">#include <ExpertAdvisor.mqh>
class="kw">input class="type">int    SL        =  class="num">50; class=class="str">"cmt">// 止损距离
class="kw">input class="type">int    TP        = class="num">100; class=class="str">"cmt">// 获利距离
class="kw">input class="type">int    TS        =  class="num">50; class=class="str">"cmt">// 追踪止损距离
class="kw">input class="type">int    FastEMA   =  class="num">15; class=class="str">"cmt">// 快速 EMA
class="kw">input class="type">int    SlowEMA   =  class="num">26; class=class="str">"cmt">// 慢速 EMA
class="kw">input class="type">int    MACD_SMA  =   class="num">1; class=class="str">"cmt">// MACD 信号线
class="kw">input class="type">int    EnvelPer  =  class="num">20; class=class="str">"cmt">// 轨道线周期
class="kw">input class="type">class="kw">double EnvelDev  = class="num">0.4; class=class="str">"cmt">// 轨道线方差
class="kw">input class="type">class="kw">double Risk      = class="num">0.1; class=class="str">"cmt">// 风险
class CMyEA : class="kw">public CExpertAdvisor
{
class="kw">protected:
class="type">class="kw">double            m_risk;          class=class="str">"cmt">// 风险大小
class="type">int               m_sl;            class=class="str">"cmt">// 止损
class="type">int               m_tp;            class=class="str">"cmt">// 获利
class="type">int               m_ts;            class=class="str">"cmt">// 追踪止损
class="type">int               m_pFastEMA;      class=class="str">"cmt">// 快速 EMA
class="type">int               m_pSlowEMA;      class=class="str">"cmt">// 慢速 EMA
class="type">int               m_pMACD_SMA;     class=class="str">"cmt">/// MACD 信号线
class="type">int               m_EnvelPer;      class=class="str">"cmt">// 轨道线周期
class="type">class="kw">double            m_EnvelDev;      class=class="str">"cmt">// 轨道线方差
class="type">int               m_hmacd;         class=class="str">"cmt">// MACD指标句柄
class="type">int               m_henvel;        class=class="str">"cmt">// 轨道线指标句柄
class="kw">public:
class="type">void              CMyEA();
class="type">void             ~CMyEA();
class="kw">virtual class="type">bool      Init(class="type">class="kw">string smb,ENUM_TIMEFRAMES tf); class=class="str">"cmt">// 初始化
class="kw">virtual class="type">bool      Main();                           class=class="str">"cmt">// 主函数
class="kw">virtual class="type">void      OpenPosition(class="type">long dir);           class=class="str">"cmt">// 根据信号开仓
class="kw">virtual class="type">void      ClosePosition(class="type">long dir);          class=class="str">"cmt">// 根据信号平仓
class="kw">virtual class="type">long      CheckSignal(class="type">bool bEntry);         class=class="str">"cmt">// 检查信号
};
class=class="str">"cmt">//------------------------------------------------------------------
class=class="str">"cmt">//------------------------------------------------------------------    
class="type">void CMyEA::~CMyEA()
{
IndicatorRelease(m_hmacd);  class=class="str">"cmt">// 删除MACD指标
IndicatorRelease(m_henvel); class=class="str">"cmt">// 删除轨道线指标
}
class=class="str">"cmt">//------------------------------------------------------------------    
class=class="str">"cmt">//------------------------------------------------------------------    Init
class="type">bool CMyEA::Init(class="type">class="kw">string smb,ENUM_TIMEFRAMES tf)
{
if(!CExpertAdvisor::Init(class="num">0,smb,tf)) class="kw">return(class="kw">false);    class=class="str">"cmt">// initialize parent class
   class=class="str">"cmt">// 复制参数
m_risk=Risk;
m_tp=TP;
m_sl=SL;
m_ts=TS;
m_pFastEMA=FastEMA;
m_pSlowEMA=SlowEMA;
m_pMACD_SMA=MACD_SMA;
m_EnvelPer = EnvelPer;
m_EnvelDev = EnvelDev;
m_hmacd=iMACD(m_smb,PERIOD_D1,m_pFastEMA,m_pSlowEMA,m_pMACD_SMA,PRICE_CLOSE);      class=class="str">"cmt">// 创建MACD指标
m_henvel=iEnvelopes(m_smb,PERIOD_H1,m_EnvelPer,class="num">0,MODE_SMA,PRICE_CLOSE,m_EnvelDev);class=class="str">"cmt">// 创建轨道线指标
if(m_hmacd==INVALID_HANDLE ||m_henvel==INVALID_HANDLE ) class="kw">return(class="kw">false);            class=class="str">"cmt">//如果报错,则退出
m_bInit=true;
class="kw">return(true);                                                                  class=class="str">"cmt">// 允许交易
}
class=class="str">"cmt">//------------------------------------------------------------------    CheckSignal

常见问题

直接用平台自带的EA模板新建文件,改改入场条件和手数就能编译测试,不必从零写。
在价格触及下轨且未破位时发买单,上轨反向;用模板里的OrderSend封装好止损止盈即可。
可以,把EA源码贴给小布,它会标出订单校验缺失、止损为0等常见坑,并给修改建议。
注意函数名大小写、缺失OnTick入口、以及未声明的外汇品种点值变量,改完点编译看日志。
锦标赛偏单策略轻仓短持,别加马丁加仓;回测时固定点差更能接近比赛环境。