请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  量化策略 帖子:3364704 新帖:25

MQL5 向导:如何创建交易信号模块

美联储发表于:4 月 17 日 17:05回复(1)

简介

MetaTrader 5 提供了一款强大的工具用于快速检验交易理念。这就是 MQL5 向导的交易策略生成器。使用 MQL5 向导自动创建 EA 交易代码的做法已在《MQL5 向导:无需编程即可创建 EA 交易》一文中进行了介绍。由于代码生成系统的开放性,您能够将自己的交易信号类、资金管理系统和追踪模块添加至标准类、系统和模块。

本文介绍了交易信号模块的编写原则,以便您在通过 MQL5 向导创建 EA 交易时能用到这些原则。

通过 MQL5 向导创建的 EA 交易基于四大支柱,即四个基本类:

图 1. CExpert 基类的结构

图 1. CExpert 基类的结构

CExpert 类(或其子类)是交易机器人的主要“引擎”。CExpert 的实例包含下列每个类的一个副本:CExpertSignal、CExpertMoney 和 CExpertTrailing(或其子类):

  1. CExpertSignal 构成了交易信号生成器的基础。CExpert 中包含的一个 CExpertSignal 派生类实例提供了 EA 交易以及市场准入可能性、准入水平和基于内置算法下达保护性订单方面的相关信息。交易操作执行的最终决策由 EA 交易作出。
  2. CExpertMoney 构成了资金和风险管理系统的基础。CExpertMoney 派生类的一个实例计算了持仓和下挂单的交易量。交易量的最终决策由 EA 交易作出。
  3. CExpertTrailing 构成了开仓支持模块的基础。CExpertTrailing 派生类的一个实例通知了 EA 交易修改持仓保护性订单的必要性。订单修改的最终决策由 EA 交易作出。

此外,CExpert 类的成员也是以下类的实例:

  • CExpertTrade(用于交易)
  • CIndicator(用于控制 EA 交易中涉及的指标和时间序列)
  • CSymbolInfo(用于获取工具的信息)
  • CAccountInfo(用于获取交易账户状态的相关信息)
  • CPositionInfo(用于获取持仓的相关信息)
  • COrderInfo (用于获取挂单的相关信息)

在下文中,EA 交易指的是 CExpert 或其子类的实例。

CExpert 及其使用的更多详情将在各个章节中进行介绍。


1. CExpertSignal 基类

CExpertSignal 构成了交易信号生成器的基础。CExpertSignal 采用了一套公共虚拟方法,用于同“外界”进行通信:

初始化

 说明

虚拟 Init

类实例的初始化可实现模块数据和 EA 交易数据的同步

虚拟 ValidationSettings

参数设定验证

虚拟 InitIndicator

创建并初始化操作交易信号生成器所需的所有指标和时间序列

开仓/反向持仓/平仓信号

 

虚拟 CheckOpenLong

生成买入持仓开仓信号,定义准入水平并下达保护性订单

虚拟 CheckOpenShort

生成卖出持仓开仓信号,定义准入水平并下达保护性订单

虚拟 CheckCloseLong

生成买入持仓平仓信号并定义退出水平

虚拟 CheckCloseShort

生成卖出持仓平仓信号并定义退出水平

虚拟 CheckReverseLong

生成买入持仓反向信号,定义反向水平并下达保护性订单

虚拟 CheckReverseShort

生成卖出持仓反向信号,定义反向水平并下达保护性订单

管理挂单

 

虚拟 CheckTrailingOrderLong

生成买入挂单修改信号并定义新订单价格

虚拟 CheckTrailingOrderShort

生成卖出挂单修改信号并定义新订单价格

方法说明

1.1. 初始化方法:

1.1.1 Init

Init() 方法在类实例添加至 EA 交易后会立即自动调用。无需覆盖该方法。

virtual bool Init(CSymbolInfo* symbol, ENUM_TIMEFRAMES period, double adjusted_point);

1.1.2 ValidationSettings

ValidationSettings() 方法在所有参数设定后从 EA 交易中直接调用。如果存在任何设定参数,您都必须覆盖该方法。

virtual bool ValidationSettings();

如果所有选项都有效(可用),则覆盖方法必须返回 true。如果至少有一个参数不正确(导致无法进一步操作),则必须返回 false。

由于基类 CExpertSignal 不具备可调参数,因此基类方法会始终返回 ture 而不执行任何检查。

1.1.3 InitIndicators

InitIndicators () 方法用于创建和初始化所有必要的指标和时间序列。在设置完所有参数且成功验证其正确性后,该方法会从 EA 交易中调用。 如果交易信号生成器使用了至少一个指标或时间序列,则应覆盖该方法。

virtual bool InitIndicators(CIndicators* indicators);

应通过标准库的适当类来使用指标和/或时间序列。所有指标和/或时间序列的指针均应添加至 EA 交易的指标集(指向其的指针作为参数传递)。

如果对指标和/或时间序列的所有操作均已成功(表明其适合使用),则覆盖方法必须返回 true。如果对指标和/或时间序列的操作至少有一次失败(导致无法进一步操作),则覆盖方法必须返回 false。

由于基类 CExpertSignal 不使用指标或时间序列,因此基类方法始终返回 ture 而不执行任何操作。


1.2. 检查开仓信号的方法:

1.2.1 CheckOpenLong

CheckOpenLong() 方法会生成买入持仓开仓信号,同时定义准入水平和保护性订单下达水平。该方法通过 EA 交易调用,以确定是否有必要建立买入持仓。如果希望生成买入持仓开仓信号,则必须覆盖该方法。

virtual bool CheckOpenLong(double& price, double& sl, double& tp, datetime& expiration);

使用该方法时必须实现检查买入持仓开仓条件的算法。如果满足条件,则必须为变量 price、sl、tp 和 expiration(对其的引用作为参数传递)赋予相应的值,且该方法应返回 true。如果不满足条件,则该方法必须返回 false。

基类 CExpertSignal 没有内置的算法用于生成买入持仓开仓信号,因此基类方法会始终返回 false。

1.2.2 CheckOpenShort

CheckOpenShort() 方法会生成卖出持仓开仓信号,同时定义准入水平和保护性订单下达水平。该方法通过 EA 交易调用,以确定是否有必要建立卖出持仓。如果希望生成卖出持仓开仓信号,则必须覆盖该方法。

virtual bool CheckOpenShort(double& price, double& sl, double& tp, datetime& expiration);

该方法必须实现检查卖出持仓开仓条件的算法。如果满足条件,则必须为变量 price、sl、tp 和 expiration(对其的引用作为参数传递)赋予相应的值,且该方法应返回 true。如果不满足条件,则该方法必须返回 false。

基类 CExpertSignal 没有内置的算法用于生成卖出持仓开仓信号,因此基类方法会始终返回 false。


1.3. 检查平仓信号的方法:

1.3.1 CheckCloseLong

CheckCloseLong() 方法会生成买入持仓平仓信号,同时定义退出水平。它通过 EA 交易调用,以确定是否有必要平买入持仓。如果希望生成买入持仓平仓信号,则必须覆盖该方法。

virtual bool CheckCloseLong(double& price);

该方法必须实现检查买入持仓平仓条件的算法。如果满足条件,则必须为变量 price(对其的引用作为参数传递)赋予相应的值,且该方法应返回 true。如果不满足条件,则该方法必须返回 false。

基类 CExpertSignal 没有内置的算法用于生成买入持仓平仓信号,因此基类方法会始终返回 false。

1.3.2 CheckCloseShort

CheckCloseShort() 方法会生成卖出持仓平仓信号,同时定义退出水平。该方法通过 EA 交易调用,以确定是否有必要平卖出持仓。如果希望生成卖出持仓平仓信号,则必须覆盖该方法。

virtual bool CheckCloseShort(double& price);

该方法必须实现检查卖出持仓平仓条件的算法。如果满足条件,则必须为变量 price(对其的引用作为参数传递)赋予相应的值,且该方法应返回 true。如果不满足条件,则该方法必须返回 false。

基类 CExpertSignal 没有内置的算法用于生成卖出持仓平仓信号,因此基类方法会始终返回 false。


1.4. 检查反向持仓信号的方法:

1.4.1 CheckReverseLong

CheckReverseLong 方法会生成买入持仓反向信号,同时定义反向水平和保护性订单下达水平。该方法通过 EA 交易调用,以确定是否有必要反向买入持仓。如果希望生成买入持仓反向信号,则必须覆盖该方法。

virtual bool CheckReverseLong(double& price, double& sl, double& tp, datetime& expiration);

该方法必须实现检查买入持仓反向条件的算法。如果满足条件,则必须为变量 price、sl、tp 和 expiration(对其的引用作为参数传递)赋予相应的值,且该方法应返回 true。如果不满足条件,则该方法必须返回 false。

CExpertSignal 基类中实现了以下算法,用于生成买入持仓反向信号:

  1. 检查信号以平买入持仓。
  2. 检查信号以建立卖出持仓。
  3. 如果两个信号均有效(满足条件)且收盘价和开盘价相匹配,则变量 price、sl、tp 和 expiration(对其的引用作为参数传递)被赋予相应的值且该方法返回 ture。
如果不满足条件,则该方法返回 false。

1.4.2 CheckReverseShort

CheckReverseShort 方法会生成卖出持仓反向信号,同时定义反向水平和保护性订单下达水平。该方法通过 EA 交易调用,以确定是否有必要反向卖出持仓。如果希望根据一定的算法(该算法不同于基类中实现的算法)生成买入持仓反向信号,则必须覆盖该方法。

virtual bool CheckReverseShort(double& price, double& sl, double& tp, datetime& expiration);

该方法必须实现检查卖出持仓反向条件的算法。如果满足条件,则必须为变量 price、sl、tp 和 expiration(对其的引用作为参数传递)赋予相应的值,且该方法应返回 true。如果不满足条件,则该方法必须返回 false。

CExpertSignal 基类中实现了以下算法,用于生成卖出持仓反向信号:

  1. 检查信号以平卖出持仓。
  2. 检查信号以建立买入持仓。
  3. 如果两个信号均有效(满足条件)且收盘价和开盘价相匹配,则变量 price、sl、tp 和 expiration(对其的引用作为参数传递)被赋予相应的值且该方法返回 ture。

如果不满足条件,则该方法返回 false。


1.5. 检查挂单修改信号的算法:

1.5.1 CheckTrailingOrderLong

CheckTrailingOrderLong() 方法会生成买入挂单修改信号,同时定义新订单价格。该方法通过 EA 交易调用,以确定是否有必要买入挂单。如果希望生成买入挂单修改信号,则必须覆盖该方法。

virtual bool CheckTrailingOrderLong(COrderInfo* order, double& price)

该方法必须实现检查买入挂单修改条件的算法。如果满足条件,则必须为变量 price(对其的引用作为参数传递)赋予相应的值,且该方法应返回 true。如果不满足条件,则该方法必须返回 false。

基类 CExpertSignal 没有内置的算法用于生成买入挂单修改信号,因此基类方法会始终返回 false。

1.5.2 CheckTrailingOrderShort

CheckTrailingOrderShort() 方法会生成卖出挂单修改信号,同时定义新订单价格。该方法通过 EA 交易调用,以确定是否有必要修改卖出挂单。如果希望生成卖出挂单修改信号,则必须覆盖该方法。

virtual bool CheckTrailingOrderShort(COrderInfo* order, double& price)

该方法必须实现检查卖出挂单修改条件的算法。如果满足条件,则必须为变量 price(对其的引用作为参数传递)赋予相应的值,且该方法应返回 true。如果不满足条件,则该方法必须返回 false。

基类 CExpertSignal 没有内置的算法用于生成卖出挂单修改信号,因此基类方法始终返回 false。


2. 开发自己的交易信号生成器

了解 CExpertSignal 基类的结构后,现在您就可开始创建自己的交易信号生成器了!

如上所述,CExpertSignal 类是一套公共虚拟方法,EA 交易通过其了解交易信号生成器在有关市场准入方向方面的“意见”。

因此,我们的主要目标是创建自己的交易信号生成器类,使其派生于 CExpertSignal 类并覆盖相应的虚拟方法,同时实现所需算法。

我们的第二个问题(具有同等重要性)是让我们的类对于 MQL5 向导呈“可视”状态。先说最重要的吧。

2.1. 创建交易信号生成器类

我们开始吧。

首先创建(例如,使用同一 MQL5 向导创建)一个具有 mqh 扩展名的包含文件。

在 File(文件)菜单中选择 Create(创建)(或按 Ctrl+N 组合键),并指明创建一个包含文件:

图 2. 使用 MQL5 向导创建一个包含文件。

图 2. 使用 MQL5 向导创建一个包含文件

应该注意的是,为方便 MQL5 向导以后将文件识别为信号生成器,应在文件夹 Include\Expert\Signal\ 中创建该文件。

为了避免与标准库发生冲突,创建自己的文件夹 Include\Expert\Signal\MySignals,然后在其中创建文件 SampleSignal.mqh,并在 MQL5 向导中指定这些参数:

图 3. 设置包含文件的位置

图 3. 设置包含文件的位置

MQL5 向导的运行结果显示如下情形:

//+------------------------------------------------------------------+
//|                                                 SampleSignal.mqh |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
// #define MacrosHello   "Hello, world!"
// #define MacrosYear    2010
//+------------------------------------------------------------------+
//| DLL imports                                                      |
//+------------------------------------------------------------------+
// #import "user32.dll"
//   int      SendMessageA(int hWnd,int Msg,int wParam,int lParam);
// #import "my_expert.dll"
//   int      ExpertRecalculate(int wParam,int lParam);
// #import
//+------------------------------------------------------------------+
//| EX5 imports                                                      |
//+------------------------------------------------------------------+
// #import "stdlib.ex5"
//   string ErrorDescription(int error_code);
// #import
//+------------------------------------------------------------------+

接下来完全是“手工”操作。删除不必要部分,并加入所需部分(即标准库的包含文件 ExpertSignal.mqh 和目前为空的类说明)。

//+------------------------------------------------------------------+
//|                                                 SampleSignal.mqh |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
//+------------------------------------------------------------------+
//| include files                                                    |
//+------------------------------------------------------------------+
#include <Expert\ExpertSignal.mqh>
//+------------------------------------------------------------------+
//| The CSampleSignal class.                                         |
//| Purpose: Class of trading signal generator.                      |
//|          It is derived from the CExpertSignal class.             |
//+------------------------------------------------------------------+
class CSampleSignal : public CExpertSignal
  {
  };
//+------------------------------------------------------------------+

现在有必要选择算法了。

作为交易信号生成器的基础,我们采用了广泛应用的模型“价格穿越移动平均线”。但我们增加了一个假设:“在穿越移动平均线后,价格会返回,直到那时才进入正确方向。”这反映在我们的文件中。

通常来说,在编写代码时不要省略注释。在一段时间后重读仔细注释过的代码是很惬意的事。

//+------------------------------------------------------------------+
//|                                                 SampleSignal.mqh |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
//+------------------------------------------------------------------+
//| include files                                                    |
//+------------------------------------------------------------------+
#include <Expert\ExpertSignal.mqh>
//+------------------------------------------------------------------+
//| Class CSampleSignal.                                             |
//| Purpose: Class of trading signal generator when price            |
//|          crosses moving average,                                 |
//|          entering on the subsequent back movement.               |
//|          It is derived from the CExpertSignal class.             |
//+------------------------------------------------------------------+
class CSampleSignal : public CExpertSignal
  {
  };
//+------------------------------------------------------------------+

现在,我们来详细说明确定所生成的交易信号需要用到哪些数据。在本例中,这些数据包括上一个柱的开盘价和收盘价,以及同一个柱上移动平均线的值。

为了访问这些数据,我们使用了标准库类 CiOpen、CiClose 和 CiMA。稍后我们将探讨指标和时间序列。

与此同时,让我们为生成器定义一个设置列表吧。首先,我们需要设置移动平均线。这些参数包括周期、沿时间轴偏移、均值法以及均值对象。其次,我们需要设定准入水平和保护性订单下达水平,以及挂单的生命周期,因为我们将用到挂单。

生成器的所有设置都将存储在类的受保护数据成员中。将通过相应的公共方法实现对设置的访问。

让我们将这些更改包含在文件中:

//+------------------------------------------------------------------+
//|                                                 SampleSignal.mqh |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
//+------------------------------------------------------------------+
//| include files                                                    |
//+------------------------------------------------------------------+
#include <Expert\ExpertSignal.mqh>
//+------------------------------------------------------------------+
//| The CSampleSignal class.                                         |
//| Purpose: Class of trading signal generator when price            |
//|             crosses moving average,                              |
//|             entering on the subsequent back movement.            |
//|             It is derived from the CExpertSignal class.          |
//+------------------------------------------------------------------+
class CSampleSignal : public CExpertSignal
  {
protected:
   //--- Setup parameters
   int                m_period_ma;       // averaging period of the MA
   int                m_shift_ma;        // shift of the MA along the time axis
   ENUM_MA_METHOD     m_method_ma;       // averaging method of the MA
   ENUM_APPLIED_PRICE m_applied_ma;      // averaging object of the MA
   double             m_limit;           // level to place a pending order relative to the MA
   double             m_stop_loss;       // level to place a stop loss order relative to the open price
   double             m_take_profit;     // level to place a take profit order relative to the open price
   int                m_expiration;      // lifetime of a pending order in bars

public:
   //--- Methods to set the parameters
   void               PeriodMA(int value)                 { m_period_ma=value;   }
   void               ShiftMA(int value)                  { m_shift_ma=value;    }
   void               MethodMA(ENUM_MA_METHOD value)      { m_method_ma=value;   }
   void               AppliedMA(ENUM_APPLIED_PRICE value) { m_applied_ma=value;  }
   void               Limit(double value)                 { m_limit=value;       }
   void               StopLoss(double value)              { m_stop_loss=value;   }
   void               TakeProfit(double value)            { m_take_profit=value; }
   void               Expiration(int value)               { m_expiration=value;  }
  };
//+------------------------------------------------------------------+

由于我们使用的是受保护数据成员,需要添加一个类构造函数,以便通过使用默认值来初始化这些数据。

要检查这些参数,可根据基类说明来覆盖虚拟方法 ValidationSettings。

类的说明:

class CSampleSignal : public CExpertSignal
  {
protected:
   //--- Setup parameters
   int                m_period_ma;       // averaging period of the MA
   int                m_shift_ma;        // shift of the MA along the time axis
   ENUM_MA_METHOD     m_method_ma;       // averaging method of the MA
   ENUM_APPLIED_PRICE m_applied_ma;      // averaging object of the MA
   double             m_limit;            // level to place a pending order relative to the MA
   double             m_stop_loss;        // level to place a stop loss order relative to the open price
   double             m_take_profit;      // level to place a take profit order relative to the open price
   int                m_expiration;       // lifetime of a pending order in bars

public:
                      CSampleSignal();
   //--- Methods to set the parameters
   void               PeriodMA(int value)                 { m_period_ma=value;   }
   void               ShiftMA(int value)                  { m_shift_ma=value;    }
   void               MethodMA(ENUM_MA_METHOD value)      { m_method_ma=value;   }
   void               AppliedMA(ENUM_APPLIED_PRICE value) { m_applied_ma=value;  }
   void               Limit(double value)                 { m_limit=value;       }
   void               StopLoss(double value)              { m_stop_loss=value;   }
   void               TakeProfit(double value)            { m_take_profit=value; }
   void               Expiration(int value)               { m_expiration=value;  }
   //--- Methods to validate the parameters
   virtual bool       ValidationSettings();
  };

ValidationSettings() 方法的实现:


//+------------------------------------------------------------------+
//| Validation of the setup parameters.                              |
//| INPUT:  No.                                                      |
//| OUTPUT: true if the settings are correct, otherwise false.       |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::ValidationSettings()
  {
//--- Validation of parameters
   if(m_period_ma<=0)
     {
      printf(__FUNCTION__+": the MA period must be greater than zero");
      return(false);
     }
//--- ok
   return(true);
  }
//+------------------------------------------------------------------+

至此我们已完成了大部分准备工作,我们将以更多篇幅来讨论指标和时间序列。

指标和时间序列是决策的主要信息源(当然,您也可以通过猜硬币或观月相的方式进行决策,但这些方法难以做到规范化)。

正如上文所述,进行决策时需要以下信息:上一个柱的开盘价和收盘价,以及同一个柱的移动平均线值。

为了访问这些数据,我们将使用标准库的以下类:

  • CiOpen - 访问上一个柱的开盘价;
  • CiClose - 访问上一个柱的收盘价;
  • CiMA - 访问上一个柱上的移动平均线值。

读者也许会问:“为什么要使用封装在类中的指标或时间序列,而这样做只是为了获得几个数字?”

这其中包含了隐含的意义,我们现在来将其揭晓。

如何使用指标或时间序列的数据?

首先,我们需要创建一个指标。

其次,我们需要将必要数量的数据复制到中间缓冲区。

再次,我们需要检查复制是否已完成。

只有经过这些步骤,您才能使用数据。

通过使用标准库的类,您无需再创建指标、关注中间缓冲区的可用性以及数据加载或句柄释放。相应类的对象将为您执行上述这些步骤。所需的一切指标都将在初始化阶段通过信号生成器生成,且所有指标都将获得必要的临时缓冲区。此外,一旦我们在集(特殊类对象)中添加了指标或时间序列对象,您便无需再关注数据的相关性(EA 交易将自动更新数据)。

我们将这些类的对象置于受保护数据成员中。我们为每个对象创建了初始化方法和数据访问方法。

让我们根据基类说明来覆盖虚拟方法 InitIndicators 吧。

类的说明:

class CSampleSignal : public CExpertSignal
  {
protected:
   CiMA               m_MA;              // object to access the values om the moving average
   CiOpen             m_open;            // object to access the bar open prices
   CiClose            m_close;           // object to access the bar close prices
   //--- Setup parameters
   int                m_period_ma;       // averaging period of the MA
   int                m_shift_ma;        // shift of the MA along the time axis
   ENUM_MA_METHOD     m_method_ma;       // averaging method of the MA
   ENUM_APPLIED_PRICE m_applied_ma;      // averaging object of the MA
   double             m_limit;            // level to place a pending order relative to the MA
   double             m_stop_loss;        // level to place a stop loss order relative to the open price
   double             m_take_profit;      // level to place a take profit order relative to the open price
   int                m_expiration;      // lifetime of a pending order in bars

public:
                      CSampleSignal();
   //--- Methods to set the parameters
   void               PeriodMA(int value)                 { m_period_ma=value;              }
   void               ShiftMA(int value)                  { m_shift_ma=value;               }
   void               MethodMA(ENUM_MA_METHOD value)      { m_method_ma=value;              }
   void               AppliedMA(ENUM_APPLIED_PRICE value) { m_applied_ma=value;             }
   void               Limit(double value)                 { m_limit=value;                  }
   void               StopLoss(double value)              { m_stop_loss=value;              }
   void               TakeProfit(double value)            { m_take_profit=value;            }
   void               Expiration(int value)               { m_expiration=value;             }
   //--- Method to validate the parameters
   virtual bool       ValidationSettings();
   //--- Method to validate the parameters
   virtual bool       InitIndicators(CIndicators* indicators);

protected:
   //--- Object initialization method
   bool               InitMA(CIndicators* indicators);
   bool               InitOpen(CIndicators* indicators);
   bool               InitClose(CIndicators* indicators);
   //--- Methods to access object data
   double             MA(int index)                       { return(m_MA.Main(index));       }
   double             Open(int index)                     { return(m_open.GetData(index));  }
   double             Close(int index)                    { return(m_close.GetData(index)); }
  };

InitIndicators、InitMA、InitOpen、InitClose 方法的实现:

//+------------------------------------------------------------------+
//| Initialization of indicators and timeseries.                     |
//| INPUT:  indicators - pointer to the object - collection of       |
//|                      indicators and timeseries.                  |
//| OUTPUT: true in case of success, otherwise false.                |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::InitIndicators(CIndicators* indicators)
  {
//--- Validation of the pointer
   if(indicators==NULL)       return(false);
//--- Initialization of the moving average
   if(!InitMA(indicators))    return(false);
//--- Initialization of the timeseries of open prices
   if(!InitOpen(indicators))  return(false);
//--- Initialization of the timeseries of close prices
   if(!InitClose(indicators)) return(false);
//--- Successful completion
   return(true);
  }
//+------------------------------------------------------------------+
//| Initialization of the moving average                             |
//| INPUT:  indicators - pointer to the object - collection of       |
//|                      indicators and timeseries.                  |
//| OUTPUT: true in case of success, otherwise false.                |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::InitMA(CIndicators* indicators)
  {
//--- Initialization of the MA object
   if(!m_MA.Create(m_symbol.Name(),m_period,m_period_ma,m_shift_ma,m_method_ma,m_applied_ma))
     {
      printf(__FUNCTION__+": object initialization error");
      return(false);
     }
   m_MA.BufferResize(3+m_shift_ma);
//--- Adding an object to the collection
   if(!indicators.Add(GetPointer(m_MA)))
     {
      printf(__FUNCTION__+": object adding error");
      return(false);
     }
//--- Successful completion
   return(true);
  }
//+------------------------------------------------------------------+
//| Initialization of the timeseries of open prices.                 |
//| INPUT:  indicators - pointer to the object - collection of       |
//|                      indicators and timeseries.                  |
//| OUTPUT: true in case of success, otherwise false.                |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::InitOpen(CIndicators* indicators)
  {
//--- Initialization of the timeseries object
   if(!m_open.Create(m_symbol.Name(),m_period))
     {
      printf(__FUNCTION__+": object initialization error");
      return(false);
     }
//--- Adding an object to the collection
   if(!indicators.Add(GetPointer(m_open)))
     {
      printf(__FUNCTION__+": object adding error");
      return(false);
     }
//--- Successful completion
   return(true);
  }
//+------------------------------------------------------------------+
//| Initialization of the timeseries of close prices.                |
//| INPUT:  indicators - pointer to the object - collection of       |
//|                      indicators and timeseries.                  |
//| OUTPUT: true in case of success, otherwise false.                |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::InitClose(CIndicators* indicators)
  {
//--- Initialization of the timeseries object
   if(!m_close.Create(m_symbol.Name(),m_period))
     {
      printf(__FUNCTION__+": object initialization error");
      return(false);
     }
//--- Adding an object to the collection
   if(!indicators.Add(GetPointer(m_close)))
     {
      printf(__FUNCTION__+": object adding error");
      return(false);
     }
//--- Successful completion
   return(true);
  }
//+------------------------------------------------------------------+

所有准备工作均已就绪。如您所见,我们的类有了显著增长。

我们现在已准备好生成交易信号了。

图 4. 价格穿越移动平均线的交易信号

图 4. 价格穿越移动平均线的交易信号

让我们再仔细考虑下这些算法。

1. 当上一个柱满足以下条件时,会出现买入信号:

  • 柱的开盘价低于移动平均线值;
  • 柱的收盘价高于移动平均线值;
  • 移动平均线渐增。

在这种情况下,我们提出买入挂单,其参数可通过设置进行定义。为此,我们覆盖了虚拟方法 CheckOpenLong 并赋予其相应功能。

2. 当上一个柱满足以下条件时,会出现卖出信号:

  • 柱的开盘价高于移动平均线值;
  • 柱的收盘价低于移动平均线值;
  • 移动平均线正在逐渐下降。

在这种情况下,我们提出卖出挂单,其参数可通过设置进行定义。为此,我们覆盖了虚拟方法 CheckOpenShort 并赋予其相应功能。

3. 我们将不会生成平仓信号。平仓通过使用止损/获利订单完成。

因此,我们不会覆盖 CheckCloseLong 和 CheckCloseShort 方法。

4. 我们建议按照设置指定的“距离”沿移动平均线修改挂单。

为此,我们覆盖了虚拟方法 CheckTrailingOrderLong 和 CheckTrailingOrderShort 并赋予其相应功能。

类的说明:

class CSampleSignal : public CExpertSignal
  {
protected:
   CiMA               m_MA;              // object to access the values of the moving average
   CiOpen             m_open;            // object to access the bar open prices
   CiClose            m_close;           // object to access the bar close prices
   //--- Setup parameters
   int                m_period_ma;       // averaging period of the MA
   int                m_shift_ma;        // shift of the MA along the time axis
   ENUM_MA_METHOD     m_method_ma;       // averaging method of the MA
   ENUM_APPLIED_PRICE m_applied_ma;      // averaging object of the MA
   double             m_limit;            // level to place a pending order relative to the MA
   double             m_stop_loss;        // level to place a stop loss order relative to the open price
   double             m_take_profit;      // level to place a take profit order relative to the open price
   int                m_expiration;       // lifetime of a pending order in bars

public:
                      CSampleSignal();
   //--- Methods to set the parameters

   void               PeriodMA(int value)                 { m_period_ma=value;              }
   void               ShiftMA(int value)                  { m_shift_ma=value;               }
   void               MethodMA(ENUM_MA_METHOD value)      { m_method_ma=value;              }
   void               AppliedMA(ENUM_APPLIED_PRICE value) { m_applied_ma=value;             }
   void               Limit(double value)                 { m_limit=value;                  }
   void               StopLoss(double value)              { m_stop_loss=value;              }
   void               TakeProfit(double value)            { m_take_profit=value;            }
   void               Expiration(int value)               { m_expiration=value;             }
   //--- Method to validate the parameters
   virtual bool       ValidationSettings();
   //--- Method to validate the parameters
   virtual bool       InitIndicators(CIndicators* indicators);
   //--- Methods to generate signals to enter the market
   virtual bool      CheckOpenLong(double& price,double& sl,double& tp,datetime& expiration);
   virtual bool      CheckOpenShort(double& price,double& sl,double& tp,datetime& expiration);
   //--- Methods to generate signals of pending order modification
   virtual bool      CheckTrailingOrderLong(COrderInfo* order,double& price);
   virtual bool      CheckTrailingOrderShort(COrderInfo* order,double& price);

protected:
   //--- Object initialization method
   bool               InitMA(CIndicators* indicators);
   bool               InitOpen(CIndicators* indicators);
   bool               InitClose(CIndicators* indicators);
   //--- Methods to access object data
   double             MA(int index)                       { return(m_MA.Main(index));       }
   double             Open(int index)                     { return(m_open.GetData(index));  }
   double             Close(int index)                    { return(m_close.GetData(index)); }
  };

CheckOpenLong、CheckOpenShort、CheckTrailingOrderLong、CheckTrailingOrderShort 方法的实现:

//+------------------------------------------------------------------+
//| Check whether a Buy condition is fulfilled                       |
//| INPUT:  price      - variable for open price                     |
//|         sl         - variable for stop loss price,               |
//|         tp         - variable for take profit price              |
//|         expiration - variable for expiration time.               |
//| OUTPUT: true if the condition is fulfilled, otherwise false.     |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::CheckOpenLong(double& price,double& sl,double& tp,datetime& expiration)
  {
//--- Preparing the data
   double spread=m_symbol.Ask()-m_symbol.Bid();
   double ma    =MA(1);
   double unit  =PriceLevelUnit();
//--- Checking the condition
   if(Open(1)<ma && Close(1)>ma && ma>MA(2))
     {
      price=m_symbol.NormalizePrice(ma-m_limit*unit+spread);
      sl   =m_symbol.NormalizePrice(price-m_stop_loss*unit);
      tp   =m_symbol.NormalizePrice(price+m_take_profit*unit);
      expiration+=m_expiration*PeriodSeconds(m_period);
      //--- Condition is fulfilled
      return(true);
     }
//--- Condition is not fulfilled
   return(false);
  }
//+------------------------------------------------------------------+
//| Check whether a Sell condition is fulfilled.                     |
//| INPUT:  price      - variable for open price,                    |
//|         sl         - variable for stop loss,                     |
//|         tp         - variable for take profit                    |
//|         expiration - variable for expiration time.               |
//| OUTPUT: true if the condition is fulfilled, otherwise false.     |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::CheckOpenShort(double& price,double& sl,double& tp,datetime& expiration)
  {
//--- Preparing the data
   double ma  =MA(1);
   double unit=PriceLevelUnit();
//--- Checking the condition
   if(Open(1)>ma && Close(1)<ma && ma<MA(2))
     {
      price=m_symbol.NormalizePrice(ma+m_limit*unit);
      sl   =m_symbol.NormalizePrice(price+m_stop_loss*unit);
      tp   =m_symbol.NormalizePrice(price-m_take_profit*unit);
      expiration+=m_expiration*PeriodSeconds(m_period);
      //--- Condition is fulfilled
      return(true);
     }
//--- Condition is not fulfilled
   return(false);
  }
//+------------------------------------------------------------------+
//| Check whether the condition of modification                      |
//|  of a Buy order is fulfilled.                                    |
//| INPUT:  order - pointer at the object-order,                     |
//|         price - a variable for the new open price.               |
//| OUTPUT: true if the condition is fulfilled, otherwise false.     |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::CheckTrailingOrderLong(COrderInfo* order,double& price)
  {
//--- Checking the pointer
   if(order==NULL) return(false);
//--- Preparing the data
   double spread   =m_symbol.Ask()-m_symbol.Bid();
   double ma       =MA(1);
   double unit     =PriceLevelUnit();
   double new_price=m_symbol.NormalizePrice(ma-m_limit*unit+spread);
//--- Checking the condition
   if(order.PriceOpen()==new_price) return(false);
   price=new_price;
//--- Condition is fulfilled
   return(true);
  }
//+------------------------------------------------------------------+
//| Check whether the condition of modification                      |
//| of a Sell order is fulfilled.                                    |
//| INPUT:  order - pointer at the object-order,                     |
//|         price - a variable for the new open price.               |
//| OUTPUT: true if the condition is fulfilled, otherwise false.     |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::CheckTrailingOrderShort(COrderInfo* order,double& price)
  {
//--- Checking the pointer
   if(order==NULL) return(false);
//--- Preparing the data
   double ma  =MA(1);
   double unit=PriceLevelUnit();
   double new_price=m_symbol.NormalizePrice(ma+m_limit*unit);
//--- Checking the condition
   if(order.PriceOpen()==new_price) return(false);
   price=new_price;
//--- Condition is fulfilled
   return(true);
  }
//+------------------------------------------------------------------+

至此,我们已解决了第一个问题。上述代码是交易信号生成器的类的源代码,可满足我们主要任务的要求。


2.2. 为 MQL5 向导准备所创建交易信号类的说明

我们现在开始解决第二个问题。我们的信号应能被 MQL5 向导的交易策略生成器所识别。

我们已完成了第一个必要条件:我们将文件放置在 MQL5 向导能够找到的位置。但这还不够。MQL5 向导不仅要能找到文件,还要能识别它。 为此,我们必须根据 MQL5 向导的要求将类描述符添加至原始文本。

类描述符是根据一定规则构成的注释块。

我们来看一下这些规则:

1. 注释块应以下面的代码行开头:

// wizard description start
//+------------------------------------------------------------------+
//| Description of the class                                         |

2. 下一行是一个文本描述符(我们会在 MQL5 向导中选择信号时见到),格式为 "//| Title=<Text> |"。如果代码文本过多而无法显示在一行,您可以在后面再添加一行代码(但不能超过此数)。

在本例中,即下述代码行:

//| Title=Signal on the crossing of a price and the MA               |
//| entering on its back movement                                    |

3. 接下来是以格式 "//| Type=<Type> |" 指定类的类型的代码行。<Type> 字段必须具有信号值(除了信号,MQL5 向导也能识别其他类的类型)。

输入:

//| Type=Signal                                                      |

4. 接下来一行呈 "//| Name=<Name> |" 格式的代码是信号的简称(MQL5 向导用其来生成 EA 交易全局变量的名称)。

我们得到下述内容:

//| Name=Sample                                                      |

5. 类的名称是说明中的一个重要要素。在呈 "//| Class=<ClassNameа> |" 格式的代码行中,<ClassName> 参数必须与类的名称相匹配:

//| Class=CSampleSignal                                              |

6. 我们不会在此行填入任何内容,但该行必须存在(这是到语言参考部分的链接):

//| Page=                                                            |

7. 此外还有关于信号设置参数的说明。

这是一组代码行(行数等于参数的个数)。

每一行的格式均为 "//| Parameter=<NameOfMethod>,<TypeOfParameter>,<DefaultValue> |"。

下面是我们的参数集:

//| Parameter=PeriodMA,int,12                                        |
//| Parameter=ShiftMA,int,0                                          |
//| Parameter=MethodMA,ENUM_MA_METHOD,MODE_EMA                       |
//| Parameter=AppliedMA,ENUM_APPLIED_PRICE,PRICE_CLOSE               |
//| Parameter=Limit,double,0.0                                       |
//| Parameter=StopLoss,double,50.0                                   |
//| Parameter=TakeProfit,double,50.0                                 |
//| Parameter=Expiration,int,10                                      |

8. 注释块应以下面的代码行结束:

//+------------------------------------------------------------------+
// wizard description end

让我们将描述符添加到源代码。

//+------------------------------------------------------------------+
//|                                                 SampleSignal.mqh |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
//+------------------------------------------------------------------+
//| include files                                                    |
//+------------------------------------------------------------------+
#include <Expert\ExpertSignal.mqh>
// wizard description start
//+------------------------------------------------------------------+
//| Description of the class                                         |
//| Title=Signal on crossing of the price and the MA                 |
//| entering on the back movement                                    |
//| Type=Signal                                                      |
//| Name=Sample                                                      |
//| Class=CSampleSignal                                              |
//| Page=                                                            |
//| Parameter=PeriodMA,int,12                                        |
//| Parameter=ShiftMA,int,0                                          |
//| Parameter=MethodMA,ENUM_MA_METHOD,MODE_EMA                       |
//| Parameter=AppliedMA,ENUM_APPLIED_PRICE,PRICE_CLOSE               |
//| Parameter=Limit,double,0.0                                       |
//| Parameter=StopLoss,double,50.0                                   |
//| Parameter=TakeProfit,double,50.0                                 |
//| Parameter=Expiration,int,10                                      |
//+------------------------------------------------------------------+
// wizard description end
//+------------------------------------------------------------------+
//| CSampleSignal class.                                             |
//| Purpose: Class of trading signal generator when price            |
//|             crosses moving average,                              |
//|             entering on the subsequent back movement.            |
//|             It is derived from the CExpertSignal class.          |
//+------------------------------------------------------------------+
class CSampleSignal : public CExpertSignal
  {
protected:
   CiMA               m_MA;               // object to access the values of the moving average
   CiOpen             m_open;             // object to access the bar open prices
   CiClose            m_close;            // object to access the bar close prices
   //--- Setup parameters
   int                m_period_ma;        // averaging period of the MA
   int                m_shift_ma;         // shift of the MA along the time axis
   ENUM_MA_METHOD     m_method_ma;        // averaging method of the MA
   ENUM_APPLIED_PRICE m_applied_ma;       // averaging object of the MA
   double             m_limit;            // level to place a pending order relative to the MA
   double             m_stop_loss;        // level to place a stop loss order relative to the open price
   double             m_take_profit;      // level to place a take profit order relative to the open price
   int                m_expiration;       // lifetime of a pending order in bars

public:
                      CSampleSignal();
   //--- Methods to set the parameters
   void               PeriodMA(int value)                 { m_period_ma=value;              }
   void               ShiftMA(int value)                  { m_shift_ma=value;               }
   void               MethodMA(ENUM_MA_METHOD value)      { m_method_ma=value;              }
   void               AppliedMA(ENUM_APPLIED_PRICE value) { m_applied_ma=value;             }
   void               Limit(double value)                 { m_limit=value;                  }
   void               StopLoss(double value)              { m_stop_loss=value;              }
   void               TakeProfit(double value)            { m_take_profit=value;            }
   void               Expiration(int value)               { m_expiration=value;             }
   //---Method to validate the parameters
   virtual bool       ValidationSettings();
   //--- Method to validate the parameters
   virtual bool       InitIndicators(CIndicators* indicators);
   //--- Methods to generate signals to enter the market
   virtual bool      CheckOpenLong(double& price,double& sl,double& tp,datetime& expiration);
   virtual bool      CheckOpenShort(double& price,double& sl,double& tp,datetime& expiration);
   //--- Methods to generate signals of pending order modification
   virtual bool      CheckTrailingOrderLong(COrderInfo* order,double& price);
   virtual bool      CheckTrailingOrderShort(COrderInfo* order,double& price);

protected:
   //--- Object initialization method
   bool               InitMA(CIndicators* indicators);
   bool               InitOpen(CIndicators* indicators);
   bool               InitClose(CIndicators* indicators);
   //--- Methods to access object data
   double             MA(int index)                       { return(m_MA.Main(index));       }
   double             Open(int index)                     { return(m_open.GetData(index));  }
   double             Close(int index)                    { return(m_close.GetData(index)); }
  };
//+------------------------------------------------------------------+
//| CSampleSignal Constructor.                                       |
//| INPUT:  No.                                                      |
//| OUTPUT: No.                                                      |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
void CSampleSignal::CSampleSignal()
  {
//--- Setting the default values
   m_period_ma  =12;
   m_shift_ma   =0;
   m_method_ma  =MODE_EMA;
   m_applied_ma =PRICE_CLOSE;
   m_limit      =0.0;
   m_stop_loss  =50.0;
   m_take_profit=50.0;
   m_expiration =10;
  }
//+------------------------------------------------------------------+
//| Validation of parameters.                                        |
//| INPUT:  No.                                                      |
//| OUTPUT: true if the settings are correct, otherwise false.       |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::ValidationSettings()
  {
//--- Validation of parameters
   if(m_period_ma<=0)
     {
      printf(__FUNCTION__+": the MA period must be greater than zero");
      return(false);
     }
//--- Successful completion
   return(true);
  }
//+------------------------------------------------------------------+
//| Initialization of indicators and timeseries.                     |
//| INPUT:  indicators - pointer to the object - collection of       |
//|                      indicators and timeseries.                  |
//| OUTPUT: true in case of success, otherwise false.                |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::InitIndicators(CIndicators* indicators)
  {
//--- Validation of the pointer
   if(indicators==NULL)       return(false);
//--- Initialization of the moving average
   if(!InitMA(indicators))    return(false);
//--- Initialization of the timeseries of open prices
   if(!InitOpen(indicators))  return(false);
//--- Initialization of the timeseries of close prices
   if(!InitClose(indicators)) return(false);
//--- Successful completion
   return(true);
  }
//+------------------------------------------------------------------+
//| Initialization of the moving average                             |
//| INPUT:  indicators - pointer to the object - collection of       |
//|                      indicators and timeseries.                  |
//| OUTPUT: true in case of success, otherwise false.                |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::InitMA(CIndicators* indicators)
  {
//--- Initialization of the MA object
   if(!m_MA.Create(m_symbol.Name(),m_period,m_period_ma,m_shift_ma,m_method_ma,m_applied_ma))
     {
      printf(__FUNCTION__+": object initialization error");
      return(false);
     }
   m_MA.BufferResize(3+m_shift_ma);
//--- Adding an object to the collection
   if(!indicators.Add(GetPointer(m_MA)))
     {
      printf(__FUNCTION__+": object adding error");
      return(false);
     }
//--- Successful completion
   return(true);
  }
//+------------------------------------------------------------------+
//| Initialization of the timeseries of open prices.                 |
//| INPUT:  indicators - pointer to the object - collection of       |
//|                      indicators and timeseries.                  |
//| OUTPUT: true in case of success, otherwise false.                |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::InitOpen(CIndicators* indicators)
  {
//--- Initialization of the timeseries object
   if(!m_open.Create(m_symbol.Name(),m_period))
     {
      printf(__FUNCTION__+": object initialization error");
      return(false);
     }
//--- Adding an object to the collection
   if(!indicators.Add(GetPointer(m_open)))
     {
      printf(__FUNCTION__+": object adding error");
      return(false);
     }
//--- Successful completion
   return(true);
  }
//+------------------------------------------------------------------+
//| Initialization of the timeseries of close prices.                |
//| INPUT:  indicators - pointer to the object - collection of       |
//|                      indicators and timeseries.                  |
//| OUTPUT: true in case of success, otherwise false.                |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::InitClose(CIndicators* indicators)
  {
//--- Initialization of the timeseries object
   if(!m_close.Create(m_symbol.Name(),m_period))
     {
      printf(__FUNCTION__+": object initialization error");
      return(false);
     }
//--- Adding an object to the collection
   if(!indicators.Add(GetPointer(m_close)))
     {
      printf(__FUNCTION__+": object adding error");
      return(false);
     }
//--- Successful completion
   return(true);
  }
//+------------------------------------------------------------------+
//| Check whether a Buy condition is fulfilled                       |
//| INPUT:  price      - variable for open price                     |
//|         sl         - variable for stop loss price,               |
//|         tp         - variable for take profit price              |
//|         expiration - variable for expiration time.               |
//| OUTPUT: true if the condition is fulfilled, otherwise false.     |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::CheckOpenLong(double& price,double& sl,double& tp,datetime& expiration)
  {
//--- Preparing the data
   double spread=m_symbol.Ask()-m_symbol.Bid();
   double ma    =MA(1);
   double unit  =PriceLevelUnit();
//--- Checking the condition
   if(Open(1)<ma && Close(1)>ma && ma>MA(2))
     {
      price=m_symbol.NormalizePrice(ma-m_limit*unit+spread);
      sl   =m_symbol.NormalizePrice(price-m_stop_loss*unit);
      tp   =m_symbol.NormalizePrice(price+m_take_profit*unit);
      expiration+=m_expiration*PeriodSeconds(m_period);
      //--- Condition is fulfilled
      return(true);
     }
//--- Condition is not fulfilled
   return(false);
  }
//+------------------------------------------------------------------+
//| Check whether a Sell condition is fulfilled.                     |
//| INPUT:  price      - variable for open price,                    |
//|         sl         - variable for stop loss,                     |
//|         tp         - variable for take profit                    |
//|         expiration - variable for expiration time.               |
//| OUTPUT: true if the condition is fulfilled, otherwise false.     |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::CheckOpenShort(double& price,double& sl,double& tp,datetime& expiration)
  {
//--- Preparing the data
   double ma  =MA(1);
   double unit=PriceLevelUnit();
//--- Checking the condition
   if(Open(1)>ma && Close(1)<ma && ma<MA(2))
     {
      price=m_symbol.NormalizePrice(ma+m_limit*unit);
      sl   =m_symbol.NormalizePrice(price+m_stop_loss*unit);
      tp   =m_symbol.NormalizePrice(price-m_take_profit*unit);
      expiration+=m_expiration*PeriodSeconds(m_period);
      //--- Condition is fulfilled
      return(true);
     }
//--- Condition is not fulfilled
   return(false);
  }
//+------------------------------------------------------------------+
//| Check whether the condition of modification                      |
//|  of a Buy order is fulfilled.                                    |
//| INPUT:  order - pointer at the object-order,                     |
//|         price - a variable for the new open price.               |
//| OUTPUT: true if the condition is fulfilled, otherwise false.     |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::CheckTrailingOrderLong(COrderInfo* order,double& price)
  {
//--- Checking the pointer
   if(order==NULL) return(false);
//--- Preparing the data
   double spread   =m_symbol.Ask()-m_symbol.Bid();
   double ma       =MA(1);
   double unit     =PriceLevelUnit();
   double new_price=m_symbol.NormalizePrice(ma-m_limit*unit+spread);
//--- Checking the condition
   if(order.PriceOpen()==new_price) return(false);
   price=new_price;
//--- Condition is fulfilled
   return(true);
  }
//+------------------------------------------------------------------+
//| Check whether the condition of modification                      |
//| of a Sell order is fulfilled.                                    |
//| INPUT:  order - pointer at the object-order,                     |
//|         price - a variable for the new open price.               |
//| OUTPUT: true if the condition is fulfilled, otherwise false.     |
//| REMARK: No.                                                      |
//+------------------------------------------------------------------+
bool CSampleSignal::CheckTrailingOrderShort(COrderInfo* order,double& price)
  {
//--- Checking the pointer
   if(order==NULL) return(false);
//--- Preparing the data
   double ma  =MA(1);
   double unit=PriceLevelUnit();
   double new_price=m_symbol.NormalizePrice(ma+m_limit*unit);
//--- Checking the condition
   if(order.PriceOpen()==new_price) return(false);
   price=new_price;
//--- Condition is fulfilled
   return(true);
  }
//+------------------------------------------------------------------+

以上便是全部内容。信号已经可以使用了。

为使 MQL5 向导的交易策略生成器能够使用我们的信号,我们应重启 MetaEditor(MQL5 向导仅在启动时才会扫描文件夹 Include\Expert)。

重启 MetaEditor 后,创建的交易信号模块就可以在 MQL5 向导中使用了:

图 5. MQL5 向导中创建的交易信号生成器

图 5. MQL5 向导中创建的交易信号生成器

现在可以使用交易信号生成器参数说明部分指定的输入参数了:

图 6. MQL5 向导中创建的交易信号生成器的输入参数

图 6. MQL5 向导中创建的交易信号生成器的输入参数

可使用 MetaTrader 5 终端的策略测试程序找出所实现交易策略的输入参数的最佳值。


总结

MQL5 向导的交易策略生成器极大简化了交易理念的检验过程。所生成 EA 交易的代码基于标准库的交易策略类,用于实现某些交易信号类、资金和风险管理类以及持仓支持类。

本文介绍了如何通过在穿越价格和移动平均线时构成信号这一方法来编写自己的交易信号类,以及如何将其包含在 MQL5 向导的交易策略生成器中,还介绍了用于 MQL5 向导的生成类的说明的结构和格式。

全部回复

0/140

量化课程

    移动端课程