探索标准库的交易策略类 - 自定义策略

Harvester Trading | 19 三月, 2014

本文面对希望接触某种功能性自定义而无需从头编写 EA 的新手/初学者。

在 MetaTrader 5 中“EA 交易”具有极大的可能性,使我们凭借在编程语言和源代码方面极少的知识或零知识(和技巧)即可进行交易,这一切得益于一个 MetaEditor 功能:MQL5 向导。向导(我们不打算在本文中解释其详细工作情况)用于生成完成的程序(.mq5 和 .ex5 文件)、算法和代码。它受益于使用 MQL5 标准库及其交易策略类(这是很好的资源)。

探索标准库的交易策略类:自定义策略

实际上标准库中已有大量的交易策略类,它们之中的一部分已经非常完善,并或多或少来自于有关金融市场和盈利能力分析的著名研究。与 MetaTrader 5 一起提供的标准指标组中的每一个指标至少有一个策略。

为了从这些交易策略类建立交易信号,MQL5 向导使用一个机制调用指标以“交易模式”的形式编码的逻辑制成的行为。并且每一个特定生成 EA 调用指标(通过 #include 指令)及其模式组和交易决策,然后交易决策出于交易目的被导入 EA 核心。

MQL5 向导

第一步是使用 MQL5 向导创建一个“EA 交易”。要在 MetaEditor 中打开 MQL5 向导,从 "File"(文件)菜单选择 "New"(新建)或按 "New"(新建)按钮,然后选择 "Expert Advisor (generate)"(EA 交易 (生成))选项。

图 1. 新建文件(在向导中选择 "generate" (生成) 选项)

我们将 MQL5 向导生成的“EA 交易”命名为 "MyExpert"。

图 2. MQL5 向导生成的 EA 的名称和参数

然后,我们添加两个指标/信号以配合其使用(您可以从可用指标中选择所需数量的条件)。对于我们的示例,我们添加两个著名的指标:相对强弱指数 (RSI) 和移动平均线 (MA)。先添加 RSI 指标,然后添加 MA 指标。

图 3. 先选择 RSI,然后选择 MA

我们可以设置一些我们想要的参数,或在我们的示例中使用默认参数。

图 4. 信号的参数

单击 OK(确定)并继续向导,在下一窗口中我们将不选择(目前)任何追踪止损,但如果您愿意也可以添加:这不会影响本文的主题。在下一窗口中我们将选择 5.0 作为交易百分比以及 0.1 手,或任何其他您希望的参数:这同样不会影响本文的主题。

分析生成的代码

完成后您将得到 "MyExpert.mq5" 文件。我们来分析一下生成代码的要点。

//+------------------------------------------------------------------+
//|                                                     MyExpert.mq5 |
//|                                                        Harvester |
//|                        https://www.mql5.com/en/users/Harvester |
//+------------------------------------------------------------------+
#property copyright "Harvester"
#property link      "https://www.mql5.com/en/users/Harvester"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include                                                          |
//+------------------------------------------------------------------+
#include <Expert\Expert.mqh>
//--- available signals
#include <Expert\Signal\SignalRSI.mqh>
#include <Expert\Signal\SignalMA.mqh>
//--- available trailing
#include <Expert\Trailing\TrailingNone.mqh>
//--- available money management
#include <Expert\Money\MoneyFixedLot.mqh>

首先要注意的是,#include 文件通过向导添加至生成的代码。我们可以看到:

然后是代码的下述部分:

//--- Creating filter CSignalRSI
   CSignalRSI *filter0=new CSignalRSI;
   if(filter0==NULL)
     {
      //--- failed
      printf(__FUNCTION__+": error creating filter0");
      ExtExpert.Deinit();
      return(-3);
     }
   signal.AddFilter(filter0);

正如标题所示,“过滤器”将被应用至附加到图表或在策略测试程序中测试的生成 EA 的市场情形。而 filter0 是索引为 0 的第一个过滤器,对于此过滤器,我们在示例中选择 RSI。

CSignalRSI 表示类信号 RSI。此类用于调用 RSI 指标,并向其应用一些条件以通过使用向导的模式逻辑创建买入或卖出信号。RSI 是我们的第一个过滤器(过滤器编号 0)。

在代码接下来的部分中,有一些过滤器参数,然后是追踪止损部分(我们已选择无追踪),再往后是有关资金管理的代码部分。

接下来是:

//--- Tuning of all necessary indicators
   if(!ExtExpert.InitIndicators())
     {
      //--- failed
      printf(__FUNCTION__+": error initializing indicators");
      ExtExpert.Deinit();
      return(-10);
     }
//--- ok
   return(0);
  }

此部分属于 Expert.mqh 包含文件。这是关于“EA 交易”操作所需指标的初始化。

生成的 EA 代码的最后一部分是关于取消初始化和其他常见“EA 交易”事件:

//+------------------------------------------------------------------+
//| Deinitialization function of the expert                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ExtExpert.Deinit();
  }
//+------------------------------------------------------------------+
//| "Tick" event handler function                                    |
//+------------------------------------------------------------------+
void OnTick()
  {
   ExtExpert.OnTick();
  }
//+------------------------------------------------------------------+
//| "Trade" event handler function                                   |
//+------------------------------------------------------------------+
void OnTrade()
  {
   ExtExpert.OnTrade();
  }
//+------------------------------------------------------------------+
//| "Timer" event handler function                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
   ExtExpert.OnTimer();
  }
//+------------------------------------------------------------------+

实际上,此 EA 使用了两个指标(RSI 和 MA),通过利用“过滤器”和“权重”逻辑的交易类的标准库用于交易决策。您可以在“MQL5 参考”的交易信号模块部分找到更多相关信息。但我们的目的是将我们自己的交易策略用作新的过滤器。

因此,针对第一步(使用我们自己的交易策略),我们要对 MyExpert.mq5 稍事修改。首先,我们添加另一个过滤器。它将是 filter2,并且我们要将它放置在紧接着 filter1 代码部分的后面。

//--- Creating filter CSignalCCIxx
   CSignalCCIxx *filter2=new CSignalCCIxx;
   if(filter2==NULL)
     {
      //--- failed
      printf(__FUNCTION__+": error creating filter2");
      ExtExpert.Deinit();
      return(-4);
     }
   signal.AddFilter(filter2);
//--- Set filter parameters
   filter2.PeriodCCIxx(Signal_CCIxx_PeriodCCI);
   filter2.Applied(Signal_CCIxx_Applied);
   filter2.Weight(Signal_CCIxx_Weight);

让我们回到作为过滤器和市场决策核心的 #include 文件。第一个是 #include <Expert\Expert.mqh> 文件。此包含文件反过来包含其他文件:

这些包含文件分别是 EA、交易结构、信号、资金和追踪止损处理的主要结构。我们不打算深入分析这些文件或是对它们进行改动。我们的目的主要是通过使用来自 MetaTrader 5 标准指标组的现有指标添加我们自己的策略以及添加它们的包含文件。

在 MyExpert.mq5 代码中,我们有在本例中用作交易的市场决策的信号/过滤器的 RSI 和 MA 指标的 #include 文件。此时,让我们添加我们自己的自定义包含文件。为此,我们将使用属于 CCI 指标的修改(“改善”)版信号。

//+------------------------------------------------------------------+
//| Include                                                          |
//+------------------------------------------------------------------+
#include <Expert\Expert.mqh>
//--- available signals
#include <Expert\Signal\SignalRSI.mqh>
#include <Expert\Signal\SignalMA.mqh>

#include <Expert\Signal\SignalCCIxx.mqh>   // This is our own 'custom' indicator for custom Signal management of the EA

SignalCCIxx.mqh 文件应位于 \MQL5\Include\Expert\Signal\ 文件夹中,并且与向导生成 EA 的可集成性相一致,就像标准库的其他 #include 交易类一样 - 信号文件已经在此文件夹中(SignalRSI.mqh 和 SignalMA.mqh)。

对于此示例,我们将复制原始 CCI 文件,创建另一个名为 CCIxx 的文件并对代码稍事修改,然后将其用作 #include 文件。现在,为简单起见,我们只是使用标准库的 CCI 指标的复制版。

我们需要做的就是将 "\MQL5\Include\Expert\Signal\SignalCCI.mqh" 文件复制到 "\MQL5\Include\Expert\Signal\SignalCCIxx.mqh" 文件。这样做最简单的方法是在文件夹中制作文件的一个副本,然后重新命名它。

我们现在来看一看这个文件。在向导生成的 MyExpert.mq5 中整合此“自定义”方式即是已完成的文件。如上文所述,我们添加了 filter2 代码,现在我们将完成后续部分。因此我们将不再关注 MyExpert.mq5 文件,而是从现在开始关注 SignalCCIxx.mqh 文件 - 由于其 CCI 指标的 filter2 交易信号,它是 EA 的真正核心部分。

自定义策略

我们返回去添加我们称之为 CCIxx 的“半自定义”策略过滤器,它是 SignalCCI.mqh 的修改版。我将其定义为半自定义,因为事实上它不是一个全新的自定义信号,相反,它是来自随 MetaTrader 5 一起提供的标准指标组的 CCI 指标的重新设计版本。这样,即便是缺乏经验的用户和编程人员也能够使用大量的现有指标对 MQL5 向导生成的 EA 的模式和过滤器稍事修改。换句话说,您可以创建自己的过滤器和模式版本,用于生成买入和卖出市场信号。这仍然是使用策略的绝佳基础。

我们来看一看这个示例。这对于那些仅仅需要这个功能(将一些自定义模式添加至现有指标)的用户以及那些希望仅仅通过使用向导来快速创建具有某种自定义的功能完善(且有效)的 EA 参加自动交易锦标赛的用户而言十分有用。

这可以通过 1 小时的工作实现 - 创建一个“锦标赛友好”EA,它功能完善,具有追踪止损、资金管理以及竞争性交易所需的一切。再次将重心移至 EA 由向导生成的事实,正如我将其命名为“锦标赛友好”,这实际上意味着生成的代码没有错误,因此参赛者无需作出任何修改或担心出现漏洞或错误。

EA 将只是交易并且是完美地交易,至少对于那些希望参赛但不会编程也不想在工作服务(对于锦标赛参赛者而言是个不错的选择)中订购 EA 的参赛者而言是如此。有大量的输入参数可供指定,以使您的自动交易尽可能贴近于您脑海中的策略构思。

但实际上,您可以仅仅使用标准指标组以及 MetaQuotes 经由向导和交易策略类的标准库提供的标准过滤器/模式组。由于指标具有很多参数(时间框架、交易品种)以及指标自身的参数(例如,周期、应用的价格等),这就提供了大量的组合和成功交易的可能性。通过本文您将快速、轻松地学会如何为 MetaTrader 5 标准指标自定义和添加模式/过滤器。

我们继续来看 SignalCCIxx.mqh 文件,以自定义和修改其行为,制作我们自己的 CCI 信号交易模型 (CCIxx)。首先,让我们在 MyExpert.mq5 文件中为输入部分中的新代码添加新的变量,如下例所示(请参见高亮显示的代码):

//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
//--- inputs for expert
input string             Expert_Title         ="MyExpert";  // Document name
ulong                    Expert_MagicNumber   =26287;       // 
bool                     Expert_EveryTick     =false;       // 
//--- inputs for main signal
input int                Signal_ThresholdOpen =40;          // Signal threshold value to open [0...100]
input int                Signal_ThresholdClose=60;          // Signal threshold value to close [0...100]
input double             Signal_PriceLevel    =0.0;         // Price level to execute a deal
input double             Signal_StopLevel     =50.0;        // Stop Loss level (in points)
input double             Signal_TakeLevel     =50.0;        // Take Profit level (in points)
input int                Signal_Expiration    =4;           // Expiration of pending orders (in bars)
input int                Signal_RSI_PeriodRSI =8;           // Relative Strength Index(8,...) Period of calculation
input ENUM_APPLIED_PRICE Signal_RSI_Applied   =PRICE_CLOSE; // Relative Strength Index(8,...) Prices series
input double             Signal_RSI_Weight    =0.7;         // Relative Strength Index(8,...) Weight [0...1.0]
input int                Signal_MA_PeriodMA   =90;          // Moving Average(12,0,...) Period of averaging
input int                Signal_MA_Shift      =0;           // Moving Average(12,0,...) Time shift
input ENUM_MA_METHOD     Signal_MA_Method     =MODE_SMA;    // Moving Average(12,0,...) Method of averaging
input ENUM_APPLIED_PRICE Signal_MA_Applied    =PRICE_CLOSE; // Moving Average(12,0,...) Prices series
input double             Signal_MA_Weight     =0.6;         // Moving Average(12,0,...) Weight [0...1.0]

input int                Signal_CCIxx_PeriodCCI =8;            // Commodity Channel Index(8,...) Period of calculation
input ENUM_APPLIED_PRICE Signal_CCIxx_Applied   =PRICE_CLOSE;  // Commodity Channel Index(8,...) Prices series
input double             Signal_CCIxx_Weight    =0.8;          // Commodity Channel Index(8,...) Weight [0...1.0]

我们将 Signal_RSI_Weight 和 Signal_MA_Weight 变量的值从 1.0 分别更改为 0.7 和 0.6,并添加上述高亮显示的代码行。为了在交易策略类中正确使用属于 CCI 指标的模式的 CCIxx 修改版的输入参数,实际上我们从 SignalCCI.mqh 文件复制了这 3 行代码,并只是在 "CCI" 后面添加了后缀 "xx"。

在类声明的 "protected" 部分,有许多有趣的元素:

class CSignalCCI : public CExpertSignal
  {
protected:
   CiCCI             m_cci;            // object-oscillator
   //--- adjusted parameters
   int               m_periodCCI;      // the "period of calculation" parameter of the oscillator
   ENUM_APPLIED_PRICE m_applied;       // the "prices series" parameter of the oscillator
   //--- "weights" of market models (0-100)
   int               m_pattern_0;      // model 0 "the oscillator has required direction"
   int               m_pattern_1;      // model 1 "reverse behind the level of overbuying/overselling"
   int               m_pattern_2;      // model 2 "divergence of the oscillator and price"
   int               m_pattern_3;      // model 3 "double divergence of the oscillator and price"
   //--- variables
   double            m_extr_osc[10];   // array of values of extremums of the oscillator
   double            m_extr_pr[10];    // array of values of the corresponding extremums of price
   int               m_extr_pos[10];   // array of shifts of extremums (in bars)
   uint              m_extr_map;       // resulting bit-map of ratio of extremums of the oscillator and the price

看一看 int 类型的 m_pattern。这些变量从 0 到 3 递增编号,其中每一个都是一个“模式”,或换句话说,一个用于买入和卖出金融工具的市场决策条件的模型。

我们将添加 2 个自定义模式:m_pattern_4 和 m_pattern_5。这通过简单地添加两行代码、两个整数类型的变量来完成。

//--- "weights" of market models (0-100)
   int               m_pattern_0;      // model 0 "the oscillator has required direction"
   int               m_pattern_1;      // model 1 "reverse behind the level of overbuying/overselling"
   int               m_pattern_2;      // model 2 "divergence of the oscillator and price"
   int               m_pattern_3;      // model 3 "double divergence of the oscillator and price"

   int               m_pattern_4;      // model 4 "our own first new pattern: values cross the zero"
   int               m_pattern_5;      // model 5 "our own second new pattern: values bounce around the zero"

如果继续研读代码,您将理解买入和卖出逻辑以及所有一切。但在这里,我们将只关注如何添加我们自己的模式部分,因为我们不打算逐行解释那些包含文件(为此,读者可打开文件本身来研究,并且“MQL5 参考”可帮助理解)。

我们还希望:在 CSignalCCIxx.mqh 文件中按 CTRL+H,查找 "CCI" 并替换为 "CCIxx"。单击 "Replace All"(全部替换)- 应找到并替换 41 处。让我们转到文件的顶部:

//+------------------------------------------------------------------+
//| Class CSignalCCIxx.                                              |
//| Purpose: Class of generator of trade signals based on            |
//|          the 'Commodity Channel Index' oscillator.               |
//| Is derived from the CExpertSignal class.                         |
//+------------------------------------------------------------------+
class CSignalCCIxx : public CExpertSignal
  {
protected:
   CiCCIxx             m_CCIxx;            // object-oscillator

并将:

protected:
   CiCCIxx             m_CCIxx;            // object-oscillator

更改为如原始 SignalCCI.mqh 中一样:

protected:
   CiCCI             m_CCIxx;            // object-oscillator

我们这样做是因为 CiCCI 调用自其他包含文件,并且如果我们改变其名称,将出现多个明显错误。现在,我们可以编译 SignalCCIxx.mqh 文件,应没有错误和警告出现。如果出现错误和警告,您可能出了一些差错,应重复该过程。

现在,让我们转到添加我们自己的模式的核心部分。纯粹是一时意起,我们添加 2 个市场交易行为模式。总共我们将有 4 个新信号(模式),2 个买入型和 2 个卖出型。要更改的部分如下:

//+------------------------------------------------------------------+
//| Constructor CSignalCCIxx.                                        |
//| INPUT:  no.                                                      |
//| OUTPUT: no.                                                      |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
void CSignalCCIxx::CSignalCCIxx()
  {
//--- initialization of protected data
   m_used_series=USE_SERIES_HIGH+USE_SERIES_LOW;
//--- setting default values for the oscillator parameters
   m_periodCCIxx  =14;
//--- setting default "weights" of the market models
   m_pattern_0  =90;         // model 0 "the oscillator has required direction"
   m_pattern_1  =60;         // model 1 "reverse behind the level of overbuying/overselling"
   m_pattern_2  =100;        // model 2 "divergence of the oscillator and price"
   m_pattern_3  =50;         // model 3 "double divergence of the oscillator and price"
   m_pattern_4  =90;         // model 4 "our own first new pattern: "
   m_pattern_5  =90;         // model 5 "our own second new pattern: 
}

我们将值 90 分配至 m_pattern_4 和 m_pattern_5,但您应(必须)将它们更改为您自己的值:这些是您希望为您的新市场模型分配的权重,因为它们影响整个“EA 交易”交易行为。

出于一时的兴致,让我们添加两个新的市场模型。它们将十分简单 - 它们是未经测试的交易信号,仅供学习之用,因此请勿将它们用于交易。十字线将帮助我们在下面的图表中识别对应柱的 CCI 指标的值。

第一个模式

自下而上穿越零线

这是我们的第一个模式,用于:“判断价格将上涨”。

图 5. 我们的第一个模式,价格上涨 - 柱 1 处的 CCI   图 6. 我们的第一个模式,价格上涨 - 柱 2 处的 CCI

自上而下穿越零线

这是我们的第一个模式,用于:“判断价格将下跌”。

图 7. 我们的第一个模式,价格下跌 - 柱 1 处的 CCI   图 8. 我们的第一个模式,价格下跌 - 柱 2 处的 CCI

第二个模式

自上而下穿越零线并返回零线上方

这是我们的第二个模式,用于:“判断价格将上涨”。

图 9. 我们的第二个模式,价格上涨 - 柱 1 处的 CCI   图 10. 我们的第二个模式,价格上涨 - 柱 2 处的 CCI   图 11. 我们的第二个模式,价格上涨 - 柱 3 处的 CCI

自下而上穿越零线并返回零线下方

这是我们的第二个模式,用于:“判断价格将下跌”。

图 12. 我们的第二个模式,价格下跌 - 柱 1 处的 CCI   图 13. 我们的第二个模式,价格下跌 - 柱 2 处的 CCI   图 11. 我们的第二个模式,价格下跌 - 柱 3 处的 CCI

实现模式

为了实现这 4 种条件(每个模式两种),我们需要以这种方式修改下述代码部分。在底部,我们为“买入”条件添加高亮显示的代码行(请参见上面的注释:“判断”价格将上涨)。

//+------------------------------------------------------------------+
//| "Voting" that price will grow.                                   |
//| INPUT:  no.                                                      |
//| OUTPUT: number of "votes" that price will grow.                  |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
int CSignalCCIxx::LongCondition()
  {
   int result=0;
   int idx   =StartIndex();
//---
   if(Diff(idx)>0.0)
     {
      //--- the oscillator is directed upwards confirming the possibility of price growth
      if(IS_PATTERN_USAGE(0)) result=m_pattern_0;      // "confirming" signal number 0
      //--- if the model 1 is used, search for a reverse of the oscillator upwards behind the level of overselling
      if(IS_PATTERN_USAGE(1) && Diff(idx+1)<0.0 && CCIxx(idx+1)<-100.0)
         result=m_pattern_1;      // signal number 1
      //--- if the model 2 or 3 is used, perform the extended analysis of the oscillator state
      if(IS_PATTERN_USAGE(2) || IS_PATTERN_USAGE(3))
        {
         ExtState(idx);
         //--- if the model 2 is used, search for the "divergence" signal
         if(IS_PATTERN_USAGE(2) && CompareMaps(1,1))      // 00000001b
            result=m_pattern_2;   // signal number 2
         //--- if the model 3 is used, search for the "double divergence" signal
         if(IS_PATTERN_USAGE(3) && CompareMaps(0x11,2))   // 00010001b
            return(m_pattern_3);  // signal number 3
        }
      // if the model 4 is used, look for crossing of the zero line
      if(IS_PATTERN_USAGE(4) && CCIxx(idx+1)>0.0 && CCIxx(idx+2)<0.0)
         result=m_pattern_4;      // signal number 4 
      // if the model 5 is used, look for the bouncing around the zero line
      if(IS_PATTERN_USAGE(5) && CCIxx(idx+1)>0.0 && CCIxx(idx+2)<0.0 && CCIxx(idx+3)>0.0)
         result=m_pattern_5;      // signal number 5
     }
//--- return the result
   return(result);
  }

我们为“卖出”条件修改代码的相应部分。在底部,我们为“卖出”条件添加高亮显示的代码行(请参见上面的注释:“判断”价格将下跌)。

//+------------------------------------------------------------------+
//| "Voting" that price will fall.                                   |
//| INPUT:  no.                                                      |
//| OUTPUT: number of "votes" that price will fall.                  |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
int CSignalCCIxx::ShortCondition()
  {
   int result=0;
   int idx   =StartIndex();
//---
   if(Diff(idx)<0.0)
     {
      //--- the oscillator is directed downwards confirming the possibility of falling of price
      if(IS_PATTERN_USAGE(0)) result=m_pattern_0;      // "confirming" signal number 0
      //--- if the model 1 is used, search for a reverse of the oscillator downwards behind the level of overbuying
      if(IS_PATTERN_USAGE(1) && Diff(idx+1)>0.0 && CCIxx(idx+1)>100.0)
         result=m_pattern_1;      // signal number 1
      //--- if the model 2 or 3 is used, perform the extended analysis of the oscillator state
      if(IS_PATTERN_USAGE(2) || IS_PATTERN_USAGE(3))
        {
         ExtState(idx);
         //--- if the model 2 is used, search for the "divergence" signal
         if(IS_PATTERN_USAGE(2) && CompareMaps(1,1))      // 00000001b
            result=m_pattern_2;   // signal number 2
         //--- if the model 3 is used, search for the "double divergence" signal
         if(IS_PATTERN_USAGE(3) && CompareMaps(0x11,2))   // 00010001b
            return(m_pattern_3);  // signal number 3
        }
      if(IS_PATTERN_USAGE(4) && CCIxx(idx+1)<0.0 && CCIxx(idx+2)>0.0)
         result=m_pattern_4;      // signal number 4 
      if(IS_PATTERN_USAGE(5) && CCIxx(idx+1)<0.0 && CCIxx(idx+2)>0.0 && CCIxx(idx+3)<0.0)
         result=m_pattern_5;      // signal number 5  
     }
//--- return the result
   return(result);
  }

添加的最后几行代码中的 (idx+1) 或 (idx+2) ...(idx+n) 十分简单但十分重要:+1、+2、+3 等正是当前柱之前的柱数(当前柱是实际在使用中的“烛形”、第 0 个柱)。

图 15. 对应于代码中 (idx) 变量的柱(烛形)

因此,idx+N 越大,我们返回的柱越多。每个柱 (idx+n) 对应于在同一时间框架上处于相同“垂直”位置的指标值。

图 16. 每个柱 (idx) 对应于相对 CCI 值

在图 16 中,第 0 个柱(最右边第一个烛形,对应于代码中的 idx 或 (idx+0))具有低于 0.00 的对应 CCI 值。同样,第二个柱 (idx+1) 和第三个柱 (idx+2) 具有 0.00 线以下的值。我们没有用垂直箭头标注其他的柱,但如果您将鼠标悬停在向后的第 4 个柱 (idx+3),您可以看到它的对应 CCI 值高于 0.00。

对于大部分用户而言这一点显而易见,但对于新用户,最好知道价格图表的图形柱/烛形、CCI 指标的图形视图、(idx) 变量以及 CCIxx 指标的值如何相互对应。

这对于在图表上查看您选定的指标并尝试“可视化”(或发现)价格柱/烛形和选定指标的行为之间的对应很重要,尝试为策略作出推测,您可以使用柱索引 (idx) 和指标变量的值轻松编写代码。

在 SignalCCIxx.mqh 文件中,下述代码:

CCIxx(idx+1)>0.0 && CCIxx(idx+2)<0.0

用语言描述的含义是:

CCI Indicator value (one bar before, named idx+1) is above the zero line of CCI indicator
AND
CCI Indicator value (two bars before, named idx+2) is below the zero line of CCI indicator

这是如何简单地基于我们选择的指标值(在本例中 - CCI)添加两个自定义模式的最简单的示例。

“价格将上涨”或“价格将下跌”的条件已用这种方式编写和加入“模式”中,并且完全允许创建更复杂的条件。在最终测试前,我们来看一看建仓/平仓的机制。

相关机制和逻辑已在标准库的交易策略类部分的《MQL5 参考手册》中有完善的说明。

简单地说,我们在 MyExpert.mq5 文件中有 2 个输入参数(两个整数变量):

//--- inputs for main signal
input int                Signal_ThresholdOpen =40;          // Signal threshold value to open [0...100]
input int                Signal_ThresholdClose=60;          // Signal threshold value to close [0...100]

如果(根据我们的交易模型)交易建买入持仓或卖出持仓然后平仓,这些开仓和平仓的阈值是用于计算的两个值。阈值假设一个从 0 到 100 的整型数值。这些参数的含义是什么?

Signal_ThresholdOpen 是建买入持仓或卖出持仓的值,Signal_ThresholdClose 是平先前建立的仓位的值。这些值在紧附于向导生成的 EA 的整个逻辑的简单但聪明的机制的背景下进行计算。

正如我们之前所看到的细节,Signal__.mqh 文件中的每个信号(__ 代表使用的指标的名称,在我们的示例中为 - MA、RSI 和 CCIxx)都由模式组成。让我们在示例中再次看看它们。在 SignalMA.mqh 文件中,我们得到 4 个模式以及每个模式的相对“权重”:

//--- setting default "weights" of the market models
   m_pattern_0 =80;          // model 0 "price is on the necessary side from the indicator"
   m_pattern_1 =10;          // model 1 "price crossed the indicator with opposite direction"
   m_pattern_2 =60;          // model 2 "price crossed the indicator with the same direction"
   m_pattern_3 =60;          // model 3 "piercing"

而对于来自 SignalRSI.mqh 文件的 RSI,我们以同样的方式处理:

//--- setting default "weights" of the market models
   m_pattern_0  =70;         // model 0 "the oscillator has required direction"
   m_pattern_1  =100;        // model 1 "reverse behind the level of overbuying/overselling"
   m_pattern_2  =90;         // model 2 "failed swing"
   m_pattern_3  =80;         // model 3 "divergence of the oscillator and price"
   m_pattern_4  =100;        // model 4 "double divergence of the oscillator and price"
   m_pattern_5  =20;         // model 5 "head/shoulders"

在“我们自己的”SignalCCIxx.mqh(几乎完全是 SignalCCI.mqh 的副本)文件中,我们得到下面这些值:

//--- setting default "weights" of the market models
   m_pattern_0  =90;         // model 0 "the oscillator has required direction"
   m_pattern_1  =60;         // model 1 "reverse behind the level of overbuying/overselling"
   m_pattern_2  =100;        // model 3 "divergence of the oscillator and price"
   m_pattern_3  =50;         // model 4 "double divergence of the oscillator and price"
   m_pattern_4  =80;         // model 4 "our own first new pattern: "
   m_pattern_5  =90;         // model 5 "our own second new pattern: "

这些是标准 0、1、2、3 加上我们自己的具有最后两个值 80 和 90 的 4 和 5 模式。当我们将 MyExpert.ex5 附加至图表或在策略测试程序中对其进行测试时,我们选择的所有信号(RSI、MA 和 CCIxx)的模式将连续计算。

如果一个或多个模式的条件成功,该模式的信号将为下一个计算激活。例如,如果来自 SignalCCIxx.mqh 文件的 m_pattern_4 基于下述条件发生:

// if the model 4 is used, look for crossing of the zero line
       if(IS_PATTERN_USAGE(4) && CCIxx(idx+1)>0.0 && CCIxx(idx+2)<0.0)
          result=m_pattern_4;      // signal number 4 

它成为一个潜在交易信号。换言之,如图 5 和图 6 所示,如果柱 1 处的 CCI 值 > 0.0 同时柱 2 处的 CCI 值 < 0.0,条件发生且 m_pattern_4(信号 4)激活。

我们为我们的 CCIxx 策略的此信号设置的权重值等于 80 的绝对值,但它将假设 -80 发生在“判断价格将下跌”的情形中,而 80 发生在“判断价格将上涨”的情形中。“判断价格将下跌”只是将负号置于模式原始权重值之前。

假设 m_pattern_4 的条件成功,交易仅在以下情形得到满足时进行:

因此,我们可以将每个模式看作是 2 个派系的竞争者:牛市信号和熊市信号。当同一方向(“判断价格将上涨”)的这些模式/信号成功(激活),它们彼此相加,然后和值与 Signal_ThresholdOpen 值进行比较。如果没有建仓或和值与 Signal_ThresholdClose 值在之前相反仓位的情形下进行比较(在此例中为卖出持仓),SignalCCIxx.mqh 的 m_pattern_4 具有值:

我们假设所有信号(SignalRSI.mqh、SignalMA.mqh 和 SignalCCIxx.mqh 的 0、1、2、3 和 5 模式)的所有其他模式获得 0 值。这就好比令“竞争信号”全部出局,唯一的竞争者为两个 m_pattern_4 - 一个买入一个卖出。因此我们只有 m_pattern_4 工作,因为它具有不为 0 的值,即 80。

//--- setting default "weights" of the market models
   m_pattern_0 =0;          // model 0 "price is on the necessary side from the indicator"
   m_pattern_1 =0;          // model 1 "price crossed the indicator with opposite direction"
   m_pattern_2 =0;          // model 2 "price crossed the indicator with the same direction"
   m_pattern_3 =0;          // model 3 "piercing"

而对于来自 SignalRSI.mqh 文件的 RSI,我们以同样的方式处理:

//--- setting default "weights" of the market models
   m_pattern_0  =0;         // model 0 "the oscillator has required direction"
   m_pattern_1  =0;        // model 1 "reverse behind the level of overbuying/overselling"
   m_pattern_2  =0;        // model 2 "failed swing"
   m_pattern_3  =0;        // model 3 "divergence of the oscillator and price"
   m_pattern_4  =0;        // model 4 "double divergence of the oscillator and price"
   m_pattern_5  =0;        // model 5 "head/shoulders"

在“我们自己的”SignalCCIxx.mqh(几乎完全是 SignalCCI.mqh 的副本)文件中,我们具有下面这些值:

//--- setting default "weights" of the market models
   m_pattern_0  =0;        // model 0 "the oscillator has required direction"
   m_pattern_1  =0;        // model 1 "reverse behind the level of overbuying/overselling"
   m_pattern_2  =0;        // model 3 "divergence of the oscillator and price"
   m_pattern_3  =0;        // model 4 "double divergence of the oscillator and price"
   m_pattern_4  =80;       // model 4 "our own first new pattern: "
   m_pattern_5  =0;        // model 5 "our own second new pattern: "

在本文的开头,我们添加了下面这些代码行:

input int                Signal_CCIxx_PeriodCCI =8;            // Commodity Channel Index(8,...) Period of calculation
input ENUM_APPLIED_PRICE Signal_CCIxx_Applied   =PRICE_CLOSE;  // Commodity Channel Index(8,...) Prices series
input double             Signal_CCIxx_Weight    =0.8;          // Commodity Channel Index(8,...) Weight [0...1.0]

我们来关注一下值为 0.8 的 Signal_CCIxx_Weight 变量。当达到阈值时,达到(触发)Signal_ThresholdOpen。该值是这样计算的:

0.8 (Signal_CCIxx_Weight input parameter)
*
80 (m_pattern_4's weight value)
= 64 is the signal strength for the "voting that price will grow"

它是“判断价格将上涨”,因为算法获得一个“价格上涨”信号(SignalCCIxx 的 m_pattern_4),且值为 80。

如果假设它获得一个“判断价格将下跌”(SignalCCIxx 的 m_pattern_4),值为 -80。对于“价格下跌”,算法只是将一个负号置于模式值前。假设是“判断价格将下跌”的情形,计算如下所示:

0.8 (Signal_CCIxx_Weight input parameter)
*
-80 (m_pattern_4's weight value)
= -64 = the negative value is considered for short positions

-64 --> 64(绝对值)是“判断价格将下跌”的信号强度。信号强度始终以绝对值表示,同时在卖出持仓值前冠以减号,在买入持仓值前冠以加号。

让我们返回上面的一个示例,其中买入持仓具有达到值 64 和信号强度 64。如果没有其他相反(具有负号)信号(Signal__ 的 m_pattern_N)竞争,达到值为 40 的 Signal_ThresholdOpen,因为买入持仓信号的强度为 64,达到水平 40 的 Signal_ThresholdOpen 并超过 24 (40+24=64)。由于已达到 Signal_ThresholdOpen,一个买入持仓建立。

例如,如果我们将 Signal_CCIxx_Weight 的值设置为 0.4,没有买入持仓将被建立,因为:

0.4 (the Signal_CCIxx_Weight)
*
80(m_pattern_4)
= 32 (strength of "long signal")

且未达到水平 40 (Signal_ThresholdOpen),因为 32 < 40,因此无买入持仓建立。

上面值的示例组(所有值为 0,除了 SignalCCIxx.mqh 中 m_pattern_4 为 80)是违反常理的,是为了让我们理解向导蕴含的出色逻辑和权重及阈值系统。在正常编程中,您应为每个 Signal__ 的每个 m_pattern_N 分配一个优先的权重。如果您将值 0 分配给模式,这意味着此模式将不会被使用。

如果我们要更改上例中的其他值(所有参数设为 0,除了 SignalCCIxx.mqh 的 m_pattern_4),比方说将 SignalRSI.mqh 的 m_pattern_1 设为 100,计算结果改变,因此我们现在具有 4 个竞争者:

m_pattern_4 Bullish --> 0.8 * 80 = 64
m_pattern_2 Bullish --> 0.7 * 100 = 70
m_pattern_4 Bearish --> 0.8 * (-80) = -64
m_pattern_2 Bearish --> 0.7 * (-100) = -70

因此,我们将有 4 种可能的组合:

A) m_pattern_4 Bullish + m_pattern_2 Bullish = {[0.8 * (80)] + [0.7 * (100)]}/2 = [64 + (70)]/2 = 134/2 = 67
B) m_pattern_4 Bullish + m_pattern_2 Bearish = {[0.8 * (80)] + [0.7 * (-100)]}/2 = [64 + (-70)]/2 = -6/2 = -3
C) m_pattern_4 Bearish + m_pattern_2 Bullish = {[0.8 * (-80)] + [0.7 * (100)]}/2 = [(-64) + 70]/2 = 6/2 = 3
D) m_pattern_4 Bearish + m_pattern_2 Bearish = {[0.8 * (-80)] + [0.7 * (-100)]}/2 = [(-64) + (-70)]/2 = -134/2 = -67

情形 A
正值 67。买入持仓建立,因为达到并超过值为 40 的 Signal_ThresholdOpen。买入持仓随后在值为 60 的 Signal_ThresholdClose 被情形 D = -67 = |67|(绝对值)的绝对值达到并超过后平仓,因为情形 D 的强度的绝对值 67 > 60(这是 Signal_ThresholdClose 的阈值)。

情形 B
负值 -3。无卖出持仓建立,因为情形 B 的绝对值未达到和超过值为 40 的 Signal_ThresholdOpen:为计算“信号强度”,我们考虑它的绝对值,则 -3 变成 3,并且 3 < 40(建仓信号的值)。无卖出持仓建立,且明显没有卖出持仓平仓的计算。

情形 C
正值 3。无买入持仓建立,由于 3 < 40(建仓信号的值),情形 C 的值未达到和超过值为 40 的 Signal_ThresholdOpen。无买入持仓建立,且明显没有买入持仓平仓的计算。

情形 D
负值 -67。卖出持仓建立,因为信号强度达到并超过值为 40 的 Signal_ThresholdOpen,信号强度的值通过 -67 的绝对值等于 67 简单计算得出,且 67 > 40。卖出持仓随后在值为 60 的 Signal_ThresholdClose 被情形 A = 67 达到并超过后平仓,因为 67(情形 A 的强度)> 60(Signal_ThresholdClose 的阈值)。

换言之,对于建短仓,首先我们需要确认方向,因为信号的负值,然后将负值转变为其绝对值以计算信号强度,与 Signal_ThresholdOpen 值进行比较,看是否前者 >= 后者。

平长仓以类似的方式执行:首先我们考虑负值以平买入持仓(相反,平卖出持仓的值为正),然后此负值转变为其绝对值,与 Signal_ThresholdClose 值进行比较,看是否前者 >= 后者。

对于买入持仓和平短仓,计算使用正值进行(没有带减号的信号),因此无需考虑将绝对值用于计算。建买入持仓由信号强度的正值触发,而平卖出持仓同样由信号强度的正值触发。

首先考虑加号减号以分别建买入持仓建卖出持仓以及平卖出持仓平买入持仓。然后我们计算它们的绝对值,用于和始终使用正号(Signal_ThresholdOpen 和 Signal_ThresholdClose 无负号)计算的 Signal_ThresholdOpen 及 Signal_ThresholdClose 的阈值进行比较。

仓位细节

我们继续深入探究仓位的细节:

买入持仓建立,如果:

Open_long >= Signal_ThresholdOpen
如果 Signal_ThresholdClose <= Signal_ThresholdOpen
我们收到一个卖出信号,因此买入持仓将反向,如果:
Open_short > Signal_ThresholdClose AND Open_short > Signal_ThresholdOpen
我们收到一个卖出信号,因此买入持仓将平仓,如果:
Open_short > Signal_ThresholdClose AND Open_short < Signal_ThresholdOpen

如果 Signal_ThresholdClose >= Signal_ThresholdOpen
我们收到一个卖出信号,因此买入持仓将反向,如果:
Open_short > Signal_ThresholdClose OR Open_short > Signal_ThresholdOpen
我们收到一个卖出信号,因此买入持仓将平仓,如果:
Open_short > Signal_ThresholdClose OR Open_short < Signal_ThresholdOpen

如果 Signal_ThresholdClose >= Signal_ThresholdOpen,它是布尔值 "OR",因为 Signal_ThresholdClose >= Signal_ThresholdOpen 已经包含 Signal_ThresholdOpen 的值。因此,仓位将平仓并被 Signal_ThresholdClose >= Signal_ThresholdOpen 的值覆盖,无路如何它将反向为卖出持仓。

卖出持仓建立,如果:

Open_short >= Signal_ThresholdOpen.
Signal_ThresholdClose <= Signal_ThresholdOpen
我们收到一个买入信号,因此卖出持仓将反向,如果:
Open_long > Signal_ThresholdClose AND Open_long > Signal_ThresholdOpen
我们收到一个买入信号,因此卖出持仓将平仓,如果:
Open_long > Signal_ThresholdClose AND Open_long < Signal_ThresholdOpen

如果 Signal_ThresholdClose >= Signal_ThresholdOpen
我们收到一个买入信号,因此卖出持仓将反向,如果:
Open_long > Signal_ThresholdClose OR Open_long > Signal_ThresholdOpen
我们收到一个买入信号,因此卖出持仓将平仓,如果:
Open_long > Signal_ThresholdClose OR Open_long < Signal_ThresholdOpen

如果 Signal_ThresholdClose >= Signal_ThresholdOpen,它是布尔值 "OR",因为 Signal_ThresholdClose >= Signal_ThresholdOpen 已经包含 Signal_ThresholdOpen 的值。因此,仓位将平仓并被 Signal_ThresholdClose >= Signal_ThresholdOpen 的值覆盖,无路如何它将反向为买入持仓。

向导生成 EA 的建仓和平仓机制富于启发性和智能化,因为它基于权重、值和阈值系统。使用这种机制,仓位的管理将通过极好的方法进行且没有逻辑错误。

价格水平和信号过期

还有一个重要的变量:

input double             Signal_PriceLevel    =0.0;         // Price level to execute a deal

此变量对于向导生成 EA 机制的基本理解极其重要,并且它可以简化如下:

图 17. 止损订单和限价订单取决于 Signal_PriceLevel

例如:

EURUSD - 买入持仓

Signal_PriceLevel = -70(负 70)
,因此当激活建仓信号时(例如,当前价格 = 1.2500),
EA 将下达由 1.2500 + 70 = 1.2570
构成的买入止损订单(从牛市观点来看劣于当前价格)


Signal_PriceLevel = 60(正 60)
,因此当激活建仓信号时(例如,当前价格 = 1.2500),
EA 将下达由 1.2500 - 60 = 1.2440
构成的买入限价订单(从牛市观点来看优于当前价格)

EURUSD - 卖出持仓

Signal_PriceLevel = -70(负 70)
,因此当激活建仓信号时(例如,当前价格 = 1.2500),
EA 将下达由 1.2500 - 70 = 1.2430
构成的卖出止损订单(从熊市观点来看优于当前价格)


Signal_PriceLevel = 60(正 60)
,因此当激活建仓信号时(例如,当前价格 = 1.2500),
EA 将下达由 1.2500 + 60 = 1.2560
构成的卖出限价订单(从熊市观点来看劣于当前价格)

最后,输入变量

input int                Signal_Expiration    =4;           // Expiration of pending orders (in bars)

决定止损/限价订单将活动的次数(通过柱表示)。

流程图

为进一步理解,您可以参考此简化流程图,该图多多少少显示了向导生成 EA 如何工作的动态机制。

图 18. 订单和仓位工作的简化流程图

策略测试程序

现在,让我们回到我们的自定义策略背景下,编译 SignalCCIxx.mqh 文件。如果没有错误,则一切正常。好了,现在我们已添加了 2 个市场交易决策模型的新模式。每个模式具有买入和卖出条件,以及建仓和平仓条件。

现在,我们来编译 MyExpert.mq5 文件,如果一切正常,将不会出现错误和警告。好了,让我们在策略测试程序中对其进行测试。针对周期与上次2011 年自动交易锦标赛的周期相似,我在策略测试程序中对 EUR/USD 交易品种使用了一些参数。

图 19. 使用 MyExpert.mq5 模仿 ATC2011 的一些简单参数

虽然它取得好的成绩,以固定手数在不到 3 个月内使初始资金增加两倍多,但我不建议将此 EA 用于实际交易,相反,我支持您添加您自己的模式/模型并进行尝试,不断优化直到获得适合您的良好结果。

总之,在此我们的目的是要表明详述现有交易策略类工作的理念。

图 20. 使用 MyExpert.mq5 的伪 ATC2011 的结果

您可以创建新的模式/模型,并在 MQL5.community 上分享它们。使用“MQL5 向导”和本文讨论的这种简单方法,测试和尝试它们将十分容易。本文仅仅是如何探索标准库的交易策略类以及修改库以创建您自己的交易系统有多么简单的一个示例。

总结

我们轻松地添加了 2 个新的过滤器/模式至 CCI 信号。您可以对所有其他指标进行同样的操作,建立您自己的自定义信号群。如果您的操作经过深思熟虑且极具结构化,在交易时它将是一款十分强大的工具。

这是添加您自己的策略的有效且便利的方式,只需关注处理指标的策略核心。让“MQL5 向导”完成与 EA 的交易功能和操作有关的所有其他工作 - 这节省了时间,并保证了“EA 交易”的正确性。

您可以使用 MetaTrader 5 标准指标轻松地编写您自己的策略,并将它们放入 ATC 就绪的 EA 中。