MQL5 向导:如何创建交易信号模块(基础篇)
用向导搭一个信号模块骨架
MT5 自带的 MQL5 向导能直接生成交易信号模块的 C++ 风格骨架,省去从零写类继承的麻烦。打开 MetaEditor 选「新建→通过向导创建→交易信号」,勾选你想要的指标源,向导会吐出一个继承自 CExpertSignal 的子类文件。 这套机制在 2013 年底随 MT5 交易系统文档公开,社区浏览量一度到 7707 次、收藏 77 次,说明手动写信号类的人不少都在找捷径。外汇与贵金属杠杆高,生成的模块只是决策部件,实盘前务必在策略测试器跑历史数据。 向导产出的代码默认留了 LongCondition() 和 ShortCondition() 两个虚函数待填,真正的信号逻辑得自己往里塞。先跑通空壳确认编译无误,再逐根均线或 RSI 条件补进去,比纯手写更不容易漏掉头文件。
◍ 向导式EA背后的四个基类
MT5 的 MQL5 向导自带交易策略生成器,能在不手写全部代码的前提下产出 EA 框架。它之所以可扩展,是因为信号、资金、追踪三类逻辑都以开放类的形式挂在标准结构下,你自己写的模块能直接塞进对应插槽。 用向导生成的机器人本质上围绕 CExpert 这一个“引擎”类运转。CExpert 实例内部固定持有三个派生类副本:CExpertSignal 负责给 EA 提供准入可能、准入价位和保护性订单的算法信息;CExpertMoney 只算仓位和挂单体量;CExpertTrailing 提示何时该移动止损。注意,这三者都只给建议,最终发单与否仍是 EA 自己拍板。 除三大件外,CExpert 还内嵌了六个常用实例:CExpertTrade 管交易动作,CIndicator 控指标与序列,CSymbolInfo 和 CAccountInfo 分别取品种与账户状态,CPositionInfo、COrderInfo 拉取持仓和挂单数据。外汇与贵金属杠杆高,回测通过不代表实盘稳,任何信号类模块都先用策略测试器跑历史段验证。 下文所有“EA”均指 CExpert 或其子类实例,类间调用关系会在后续章节拆开讲。
「信号基类的虚拟接口清单」
CExpertSignal 是 MT5 中交易信号生成器的根类,本身不产信号,只定义了一套 public virtual 方法,让派生类去填具体逻辑。外界(EA 或 CExpert)只跟这些虚函数对话,不关心指标怎么算。 它把生命周期拆成三块:初始化、持仓信号、挂单管理。初始化侧有 Init(同步模块与 EA 数据)、ValidationSettings(校验参数)、InitIndicator(建指标和时间序列)。这步没跑通,后面所有 Check 都不会被调用。 持仓信号是核心,六个虚函数覆盖开平反三个动作的多空两侧:CheckOpenLong / CheckOpenShort 负责开仓并挂保护性订单,CheckCloseLong / CheckCloseShort 给退出水平,CheckReverseLong / CheckReverseShort 做反手。挂单方面用 CheckTrailingOrderLong / CheckTrailingOrderShort 去改买入卖出挂单的价格。 做自定义信号类时,至少重写 InitIndicator 和六个 Check 方法;只写开仓不写平仓,回测里会出现仓位没人管的现象。外汇与贵金属杠杆高,信号类任何改动都先在策略测试器跑历史再上实盘。
信号类生命周期与钩子方法怎么接
写 MT5 信号模块时,CExpertSignal 派生类有一套固定调用顺序:实例挂进 EA 后先跑 Init(),这个不用你重写;参数填完 EA 会调 ValidationSettings(),只要你类里带了可调参数就必须覆盖,全合法回 true 否则 false。基类因为没参数,永远直接回 true。 指标和时间序列的创建放在 InitIndicators() 里,它在参数校验通过后由 EA 调用。用标准库类建指标,指针要塞进 EA 的指标集;任一指标初始化失败就回 false,成功才回 true。基类不碰指标,所以恒回 true。 开仓、平仓、反向、改挂单是四类决策点。CheckOpenLong / CheckOpenShort 负责多空开仓,满足条件要给 price、sl、tp、expiration 引用赋值再回 true;基类无内置算法,恒回 false。CheckCloseLong / CheckCloseShort 只回填 price 控制平多平空。 反向信号里基类其实写了默认逻辑:CheckReverseLong 先查平多再查开空,两者都过且收盘价与开盘价匹配才填值回 true;CheckReverseShort 对称处理。挂单追踪 CheckTrailingOrderLong / Short 只回填新 price,基类同样恒 false。 下面这段是信号类虚方法声明原型,开 MT5 新建派生类时照着签名覆写就不会漏掉生命周期钩子。
<span class="keyword">class="kw">virtual</span> <span class="keyword">class="type">bool</span> Init(CSymbolInfo* symbol, <span class="keyword">ENUM_TIMEFRAMES</span> period, <span class="keyword">class="type">class="kw">double</span> adjusted_point); <span class="keyword">class="kw">virtual</span> <span class="keyword">class="type">bool</span> ValidationSettings(); <span class="keyword">class="kw">virtual</span> <span class="keyword">class="type">bool</span> InitIndicators(CIndicators* indicators); <span class="keyword">class="kw">virtual</span> <span class="keyword">class="type">bool</span> CheckOpenLong(<span class="keyword">class="type">class="kw">double</span>& price, <span class="keyword">class="type">class="kw">double</span>& sl, <span class="keyword">class="type">class="kw">double</span>& tp, <span class="keyword">class="type">class="kw">datetime</span>& expiration); <span class="keyword">class="kw">virtual</span> <span class="keyword">class="type">bool</span> CheckOpenShort(<span class="keyword">class="type">class="kw">double</span>& price, <span class="keyword">class="type">class="kw">double</span>& sl, <span class="keyword">class="type">class="kw">double</span>& tp, <span class="keyword">class="type">class="kw">datetime</span>& expiration); <span class="keyword">class="kw">virtual</span> <span class="keyword">class="type">bool</span> CheckCloseLong(<span class="keyword">class="type">class="kw">double</span>& price); <span class="keyword">class="kw">virtual</span> <span class="keyword">class="type">bool</span> CheckCloseShort(<span class="keyword">class="type">class="kw">double</span>& price); <span class="keyword">class="kw">virtual</span> <span class="keyword">class="type">bool</span> CheckReverseLong(<span class="keyword">class="type">class="kw">double</span>& price, <span class="keyword">class="type">class="kw">double</span>& sl, <span class="keyword">class="type">class="kw">double</span>& tp, <span class="keyword">class="type">class="kw">datetime</span>& expiration); <span class="keyword">class="kw">virtual</span> <span class="keyword">class="type">bool</span> CheckReverseShort(<span class="keyword">class="type">class="kw">double</span>& price, <span class="keyword">class="type">class="kw">double</span>& sl, <span class="keyword">class="type">class="kw">double</span>& tp, <span class="keyword">class="type">class="kw">datetime</span>& expiration); <span class="keyword">class="kw">virtual</span> <span class="keyword">class="type">bool</span> CheckTrailingOrderLong(COrderInfo* order, <span class="keyword">class="type">class="kw">double</span>& price) <span class="keyword">class="kw">virtual</span> <span class="keyword">class="type">bool</span> CheckTrailingOrderShort(COrderInfo* order, <span class="keyword">class="type">class="kw">double</span>& price)
◍ 从零搭一个穿越均线的信号类
想在 MT5 里写自己的信号模块,核心思路就一条:建一个派生于 CExpertSignal 的子类,把虚方法覆盖掉,塞进你自己的算法。本文以「价格上穿均线后回踩再进场」为例,所有逻辑都落在上一根柱的开盘、收盘与同柱均线值上。 文件必须放在 Include\Expert\Signal\MySignals\ 下,取名 SampleSignal.mqh,MQL5 向导才扫得到。建好空壳后,先引标准库 ExpertSignal.mqh,再把 CiOpen、CiClose、CiMA 三个类作为受保护成员挂进去——它们帮你省掉建指标句柄、管缓冲、释放句柄的脏活,EA 跑起来会自动刷新数据。 参数全收进受保护数据:均线周期、偏移、均值法、均价对象,再加准入水平、保护性挂单水平、挂单寿命。构造函数给默认值,ValidationSettings() 照基类规范覆写做参数校验。InitIndicators 里初始化三个 Ci 类对象,这一步不做后面取数全是空。 信号规则很直白:买条件是柱开低于均线、收高于均线且均线向上;卖条件反过来。覆写 CheckOpenLong / CheckOpenShort 发挂单,不覆写平仓方法,止损止盈交给订单本身。挂单用 CheckTrailingOrderLong / CheckTrailingOrderShort 沿均线距离动态改价。外汇与贵金属波动剧烈,这套逻辑实盘前务必在策略测试器跑参数敏感性。
| 类写完了还不算完,MQL5 向导要靠特定注释块认人。文件头加 //+------------------------------------------------------------------+ 起头的描述符:Title 是下拉里看到的名,Type=signal,Name 是全局变量短名,Class 必须和类名一字不差,参数行按 // | Parameter=方法名,类型,默认值 | 列全。改完重启 MetaEditor,向导启动时才重新扫 Include\Expert 目录,不然看不见新模块。 |
|---|
重启后就能在向导里选到这个信号,输入参数面板直接暴露你定义的那些字段。策略测试程序可以扫出较优的周期与水平组合,但回测拟合不等于实盘概率,贵金属隔夜跳空可能直接吞掉挂单空间。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| SampleSignal.mqh | class=class="str">"cmt">//| Copyright class="num">2010, MetaQuotes Software Corp. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2010, MetaQuotes Software Corp." class="macro">#class="kw">property link "[MQL5官方文档] class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| defines | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">// class="macro">#define MacrosHello "Hello, world!" class=class="str">"cmt">// class="macro">#define MacrosYear class="num">2010 class=class="str">"cmt">//+------------------------------------------------------------------+
「信号类骨架怎么挂外部库」
写自定义信号模块时,先得把外部依赖声明清楚。DLL 导入分两块:系统级 user32.dll 拿窗口消息控制,自己编译的 my_expert.dll 做重算逻辑,两者都用 #import 显式绑定函数签名。 EX5 侧则常引 stdlib.ex5 里的 ErrorDescription,把错误码转成可读串,省得调试时对着数字猜。下面这段是信号类的最小壳:从 CExpertSignal 派生出 CSampleSignal,先空着方法,后续填指标逻辑。 #include <Expert\ExpertSignal.mqh> class CSampleSignal : public CExpertSignal { }; 外汇与贵金属波动大、杠杆高,这类信号类若接了 DLL,先在策略测试器跑空壳确认无加载报错,再逐步加逻辑,避免实盘崩在依赖层。
class=class="str">"cmt">// class="macro">#class="kw">import "user32.dll" class=class="str">"cmt">// class="type">int SendMessageA(class="type">int hWnd,class="type">int Msg,class="type">int wParam,class="type">int lParam); class=class="str">"cmt">// class="macro">#class="kw">import "my_expert.dll" class=class="str">"cmt">// class="type">int ExpertRecalculate(class="type">int wParam,class="type">int lParam); class=class="str">"cmt">// class="macro">#class="kw">import class=class="str">"cmt">// class="macro">#class="kw">import "stdlib.ex5" class=class="str">"cmt">// class="type">class="kw">string ErrorDescription(class="type">int error_code); class=class="str">"cmt">// class="macro">#class="kw">import class="macro">#include <Expert\ExpertSignal.mqh> class CSampleSignal : class="kw">public CExpertSignal { };