自定义指标交易信号生成器实战
从MACD改写到挂单辅助模型0的完整MQL5教程
一、为什么需要基于自定义指标写信号生成器
MetaTrader 5 标准库提供了大量基于内置指标(如均线、包络线、MACD)的交易信号类,但实战中我们常需要使用自己编写的或第三方下载的自定义指标来决策。标准库并没有为每一个自定义指标准备好现成的信号类,这时就必须从 CExpertSignal 基类派生出自己的信号生成器。CExpertSignal 是 MQL5 标准库专家系统框架中的核心基类之一,它定义了一系列公共方法,允许 EA 通过“投票”机制获取进场方向信号。理解这一点,是后续所有代码改写的基础。
所谓“市场模型”,是指标数值与价格之间的一种特定组合。比如 MACD 官方指标自带主线与信号线交叉、零轴穿越、背离等模型。但你可能不满足于这些,想要定义自己的模型:例如“MACD低于零轴且数值递增=预测上涨”。本文正是以这种新模型为需求,演示如何把自定义逻辑塞进信号类里。
二、创建信号类文件与目录结构
默认情况下,交易信号生成器头文件应放在 MQL5\Include\Expert\Signal 目录下。但为了避免污染标准库,推荐在 MQL5\Include\Expert 下新建一个文件夹,例如命名为 MySignals。随后在 MetaEditor 中使用 File -> New -> Include File (*.mqh) 向导,指定路径为 Include\Expert\MySignals\MySignal.mqh,类名设为 MySignal。
向导生成的只是空模板。内部而言,标准库所有信号类的结构几乎一致,区别仅在于判断市场模型的算法。因此最省事的做法是:复制标准库 Signal 文件夹下任意一个现有文件(本文选了 SignalEnvelopes.mqh)的全部内容,粘贴进你的空模板,然后做针对性修改。这样能保留向导描述块、构造函数、虚函数重写等标准骨架。
- 新建 MySignals 文件夹隔离自定义代码
- 用 MQL5 向导生成 .mqh 包含文件骨架
- 从 SignalEnvelopes.mqh 复制内容作为模板
- 全局替换 CSignalEnvelopes 为自定义类名如 CSignalMyCustInd
三、Wizard描述块与参数映射
信号类顶部有一段被 // wizard description start 和 end 包裹的特殊注释,它决定了 MQL5 向导能否识别并列出你的信号。你需要修改 Title、Name、ShortName、Class、Page 以及 Parameter 行。Class 必须和代码中的类名一致,Parameter 行则要把指标的真实输入参数暴露给向导。
以 MACD 为例,其指标输入为 InpFastEMA、InpSlowEMA、InpSignalSMA、InpAppliedPrice。我们在信号类中声明对应的受保护变量 m_period_fast、m_period_slow、m_period_signal、m_applied,并在描述块中写:Parameter=PeriodFast,int,12,Period of fast EMA 等。这样 EA 向导就能让用户可视化调整这些参数,并传递给信号类。
//| Title=Signal of the 'Custom Indicator' indicator //| Type=SignalAdvanced //| Name=MyCustomIndicator //| ShortName=MyCustomIndicator //| Class=CSignalMyCustInd //| Page=signal_envelopes //| Parameter=PeriodFast,int,12,Period of fast EMA //| Parameter=PeriodSlow,int,24,Period of slow EMA //| Parameter=PeriodSignal,int,9,Period of averaging of difference //| Parameter=Applied,ENUM_APPLIED_PRICE,PRICE_CLOSE,Prices series
四、CiCustom类与指标对象声明
标准库为内置指标提供了 CiEnvelopes、CiMACD 等封装类,但自定义指标没有现成类,必须使用通用容器 CiCustom。CiCustom 专门用于处理自定义指标,可实现自定义指标数据的创建、设置和访问。我们在信号类的 protected 区域声明 CiCustom m_mci 对象,取代模板里的 CiEnvelopes m_env。
同时,我们需要四个变量来接收前面描述的四个参数:m_period_fast、m_period_slow、m_period_signal、m_applied。这些变量会在构造函数中赋默认初值,并通过 PeriodFast() 等公共方法允许外部设置。
class CSignalMyCustInd : public CExpertSignal
{
protected:
CiCustom m_mci; // indicator object "MyCustomIndicator"
//--- adjustable parameters
int m_period_fast; // "fast EMA period"
int m_period_slow; // "slow EMA period"
int m_period_signal; // "difference averaging period"
ENUM_APPLIED_PRICE m_applied; // "price type"
五、模型0的妙用:挂单跟随价格
本文开头只定义了“新模型”(模型1:MACD低于零轴且递增则买;高于零轴且递减则卖)。但代码里出现了模型0,它是必不可少的辅助模型。模型0的核心作用是:当主模型条件未完全触发时,若基础条件(如MACD低于零轴)满足,仍允许挂单存在并随价格移动。
具体场景:我们在距离柱开盘价50点处挂 Buy Stop。第1根柱 MACD低于零轴且蓄势,符合买入信号,挂单。第2根柱 MACD低于零轴但下跌,按主模型无买卖条件,CExpertSignal 逻辑会删除所有挂单,可能错失后续突发行情。而模型0因 MACD 仍低于零轴而成立,挂单不被删,且随价格下移,保住了进场机会。
六、核心方法重写:验证、初始化与条件判断
ValidationSettings 虚函数负责检查参数合理性,例如快线周期必须小于慢线周期,否则打印错误并返回 false,阻止 EA 启动。InitIndicators 则负责把自定义指标加入 CIndicators 集合并初始化。CIndicators 是管理所有指标实例的容器类,其 Create 方法可按参数构造指标。
在 InitMyCustomIndicator 中,我们通过 MqlParam 数组向 CiCustom::Create 传递指标路径(Examples\MACD.ex5)和三个周期参数,再设 NumBuffers(4)。注意:自定义指标必须已编译为 ex5 且路径正确,否则运行时报错。
bool CSignalMyCustInd::InitMyCustomIndicator(CIndicators *indicators)
{
if(!indicators.Add(GetPointer(m_mci))) return(false);
MqlParam parameters[4];
parameters[0].type=TYPE_STRING;
parameters[0].string_value="Examples\\MACD.ex5";
parameters[1].type=TYPE_INT; parameters[1].integer_value=m_period_fast;
parameters[2].type=TYPE_INT; parameters[2].integer_value=m_period_slow;
parameters[3].type=TYPE_INT; parameters[3].integer_value=m_period_signal;
if(!m_mci.Create(m_symbol.Name(),0,IND_CUSTOM,4,parameters)) return(false);
if(!m_mci.NumBuffers(4)) return(false);
return(true);
}
LongCondition 和 ShortCondition 是信号“投票”的核心。以模型1为例:若 DiffMain(idx)>0(主线递增)且 IS_PATTERN_USAGE(1),则返回 m_pattern_1 作为看涨权重;模型0则作为确认信号。DiffMain 通过计算 Main(ind)-Main(ind+1) 得到指标斜率。
int CSignalMyCustInd::LongCondition(void)
{
int result=0;
int idx=StartIndex();
if(DiffMain(idx)>0.0)
{
if(IS_PATTERN_USAGE(0)) result=m_pattern_0;
if(IS_PATTERN_USAGE(1) && DiffMain(idx+1)<0.0) result=m_pattern_1;
}
return(result);
}
七、与其他方案的对比及实操建议
对比直接使用 iCustom 在 EA 中裸写逻辑,继承 CExpertSignal 的方案更贴合 MQL5 向导生态:信号可拖拽组合、权重可调、能与其它信号类投票融合。缺点是需理解标准库骨架,前期模板改动繁琐。另一种方案是改写现有类似信号类(如 CSignalMACD)而非 Envelopes,可少改部分缓冲访问代码,但本文方法更具通用性。
实操步骤总结:1)建目录与mqh;2)抄模板改类名与描述块;3)换 CiCustom 并声明参数变量;4)改构造函数默认序列为 HIGH+LOW;5)写 Validation 与 InitIndicators;6)在 Condition 方法中用 IS_PATTERN_USAGE 实现模型0/1。编译后,在 EA 向导的 Signals 里即可看到 MyCustomIndicator。