研究烛条分析技术(第三部分):用于形态操作的函数库

Alexander Fedosov | 24 四月, 2019

内容目录

概述

我们已研究过烛条分析技术:在第一篇文章中检查当前市场条件下的形态实际情况,并在第二篇文章中尝试拓展这些研究。 使用开发评估标准,我们研究、测试并比较了各种可能的形态组合。 为此目的,我们开发了一个自定义形态分析器应用程序,其中包含大量用于研究形态的设置。 然而,理论和研究只能提供信息和结论。 任务的逻辑延续是在实际条件下运用它们。

所以,本文的目的是创建一个自定义工具,可令用户能够接收和使用前面所讨论形态的整体信息数组。 我们将创建一个可令您在自己的指标、交易面板、智能交易系统中使用的函数库。    


函数库结构

在继续创建函数库结构、类和连接之前,我们先来定义将要用到的数据。 也就是说,我们需要分离负责输入数据和提供结果的方法。 通用函数库结构将基于前面文章中开发的可视化解决方案 — 形态分析器。 

我们从应用程序输入数据开始,这些数据会在测试形态时影响结果。

图例 1 在“设置”选项卡中输入参数。

区块 1. 此区块包括烛条类型列表,由现有和生成的形态构成。 每种类型都有其设置,您可以通过单击烛条可视化页面右上角的齿轮图标来查看。 烛条类型 1-5 只有一个设置,而锤子有两个。 

区块 2. 权重系数。 有三个参数 К1,К2,К3 影响形态效率评估结果。 

区块 3. 以点数为单位的趋势阈值。 

区块 4. 测试所生成形态时用到的烛条。 在此处,我们需要顺序号或烛条索引。 使用这些数据,我们将能够获得高达三根烛条的任何尺寸、任何形态的信息。

区块 5. 形态中的烛条数量。 此设置仅适用于自定义形态。 

然后我们查看“分析”选项卡和其中包含的输入参数。

图例 2 “分析”选项卡中的输入参数。

区块 6. 此区块包含用于形态分析的当前时间帧和数据样本范围的设置。 

区块 7. 现有形态的名称。 它还有一个输入无法从应用程序编辑,但在访问形态并获取有关信息时则必需用到它。

我们在此列举可以从形态分析中获得的数据。 这是在类中创建正确的方法结构所必需的。

函数库开发

确定基本点后,我们继续创建函数库。 我们从创建一个包含所需枚举的文件 Enums.mqh 开始。

//+------------------------------------------------------------------+
//|                                                        Enums.mqh |
//|                                 版权所有 2018, MetaQuotes 软件公司 |
//|                           https://www.mql5.com/zh/users/alex2356 |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 烛条类型                                                         |
//+------------------------------------------------------------------+
enum TYPE_CANDLESTICK
  {
   CAND_NONE,           // 未定义
   CAND_MARIBOZU,       // Marubozu
   CAND_DOJI,           // 十字星
   CAND_SPIN_TOP,       // 尖顶
   CAND_HAMMER,         // 锤子
   CAND_INVERT_HAMMER,  // 倒锤子
   CAND_LONG,           // 长体
   CAND_SHORT           // 短体
  };
//+------------------------------------------------------------------+
//| 形态类型                                                         |
//+------------------------------------------------------------------+
enum TYPE_PATTERN
  {
   NONE,
   HUMMER,
   INVERT_HUMMER,
   HANDING_MAN,
   SHOOTING_STAR,
   ENGULFING_BULL,
   ENGULFING_BEAR,
   HARAMI_BULL,
   HARAMI_BEAR,
   HARAMI_CROSS_BULL,
   HARAMI_CROSS_BEAR,
   DOJI_STAR_BULL,
   DOJI_STAR_BEAR,
   PIERCING_LINE,
   DARK_CLOUD_COVER
  };
//+------------------------------------------------------------------+
//| 趋势类型                                                         |
//+------------------------------------------------------------------+
enum TYPE_TREND
  {
   UPPER,               //上行趋势
   DOWN,                //下行趋势
   FLAT                 //横盘
  };
//+------------------------------------------------------------------+

在此,我们将检测用到的简单烛条类型列表,现有形态的类型以及趋势类型 - 数据是识别图表上现有形态所必需的。

之后我们要创建 Pattern.mqh 文件。 将在其中创建 CPattern 类,在其私有部分中,我们将声明上一节中所提参数的变量。 我们还需要 将文件与枚举联系起来

//+------------------------------------------------------------------+
//|                                                      Pattern.mqh |
//|                                 版权所有 2018, MetaQuotes 软件公司 |
//|                           https://www.mql5.com/zh/users/alex2356 |
//+------------------------------------------------------------------+
#include "Enums.mqh"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CPattern
  {
private:
   //--- 权重
   double            m_k1;
   double            m_k2;
   double            m_k3;
   //--- 以点数为单位的趋势阈值
   int               m_threshold_value;
   //--- 长体烛条设置系数
   double            m_long_coef;
   //--- 短体烛条设置系数
   double            m_short_coef;
   //--- 十字星烛条设置系数
   double            m_doji_coef;
   //--- Marubozu 烛条设置系数
   double            m_maribozu_coef;
   //--- 尖顶烛条设置系数
   double            m_spin_coef;
   //--- 锤子烛条设置系数
   double            m_hummer_coef1;
   double            m_hummer_coef2;
   //--- 预设形态的采样范围
   int               m_range_total;
   //--- 判定趋势的周期
   int               m_trend_period;
   //--- 已发现形态
   int               m_found;
   //--- 形态发生
   double            m_coincidence;
   //--- 上行或下行走势的概率
   double            m_probability1;
   double            m_probability2;
   //--- 效率
   double            m_efficiency1;
   double            m_efficiency2;
   //--- 简单的烛条形态
   struct CANDLE_STRUCTURE
     {
      double            m_open;
      double            m_high;
      double            m_low;
      double            m_close;                      // OHLC
      TYPE_TREND        m_trend;                      // 趋势
      bool              m_bull;                       // 涨势烛条
      double            m_bodysize;                   // 实体大小
      TYPE_CANDLESTICK  m_type;                       // 烛条类型
     };
   //--- 形态效率评估属性
   struct RATING_SET
     {
      int               m_a_uptrend;
      int               m_b_uptrend;
      int               m_c_uptrend;
      int               m_a_dntrend;
      int               m_b_dntrend;
      int               m_c_dntrend;
     };

正如上面的代码所示,我们的程序中加入了两个结构。 第一个结构 CANDLE_STRUCTURE 是判断图表上烛条类型所必需的。 请注意,此结构中使用了两种类型的趋势枚举:来自 Enums.mqh 文件的 TYPE_TREND 和 TYPE_CANDLESTICK,这在之前研究过,并为此结构而创建。 第二种结构 RATING_SET 存储形态出现后的价格走势的评估记录。 有关更多详细信息,请参阅第一篇文章

现在考察该类的公有部分,它描述了自定义和检索输入参数值的方法,这些方法在函数库结构章节中已描述过。

public:
                     CPattern(void);
                    ~CPattern(void);
   //--- 设置并返回权重系数
   void              K1(const double k1)                             { m_k1=k1;                       }
   double            K1(void)                                        { return(m_k1);                  }
   void              K2(const double k2)                             { m_k2=k2;                       }
   double            K2(void)                                        { return(m_k2);                  }
   void              K3(const double k3)                             { m_k3=k3;                       }
   double            K3(void)                                        { return(m_k3);                  }
   //--- 设置并返回趋势阈值
   void              Threshold(const int threshold)                  { m_threshold_value=threshold;   }
   int               Threshold(void)                                 { return(m_threshold_value);     }
   //--- 设置并返回长体烛条设置系数
   void              Long_coef(const double long_coef)               { m_long_coef=long_coef;         }
   double            Long_coef(void)                                 { return(m_long_coef);           }
   //--- 设置并返回短体烛条设置系数
   void              Short_coef(const double short_coef)             { m_short_coef=short_coef;       }
   double            Short_coef(void)                                { return(m_short_coef);          }
   //--- 设置并返回十字星烛条设置系数
   void              Doji_coef(const double doji_coef)               { m_doji_coef=doji_coef;         }
   double            Doji_coef(void)                                 { return(m_doji_coef);           }
   //--- 设置并返回 marubozu 烛条设置系数
   void              Maribozu_coef(const double maribozu_coef)       { m_maribozu_coef=maribozu_coef; }
   double            Maribozu_coef(void)                             { return(m_maribozu_coef);       }
   //--- 设置并返回尖顶烛条设置系数
   void              Spin_coef(const double spin_coef)               { m_spin_coef=spin_coef;         }
   double            Spin_coef(void)                                 { return(m_spin_coef);           }
   //--- 设置并返回锤子烛条设置系数
   void              Hummer_coef1(const double hummer_coef1)         { m_hummer_coef1=hummer_coef1;   }
   void              Hummer_coef2(const double hummer_coef2)         { m_hummer_coef2=hummer_coef2;   }
   double            Hummer_coef1(void)                              { return(m_hummer_coef1);        }
   double            Hummer_coef2(void)                              { return(m_hummer_coef2);        }
   //--- 设置并返回预设参数的采样范围
   void              Range(const int range_total)                    { m_range_total=range_total;     }
   int               Range(void)                                     { return(m_range_total);         }
   //--- 设置并返回计算趋势所需的烛条数量  
   void              TrendPeriod(const int period)                   { m_trend_period=period;         }
   int               TrendPeriod(void)                               { return(m_trend_period);        }

在类构造函数中,我们将在“设置”选项卡中描述应用程序中指定的默认参数。

//+------------------------------------------------------------------+
//| 构造函数                                                         |
//+------------------------------------------------------------------+
CPattern::CPattern(void) : m_k1(1),
                           m_k2(0.5),
                           m_k3(0.25),
                           m_threshold_value(100),
                           m_long_coef(1.3),
                           m_short_coef(0.5),
                           m_doji_coef(0.04),
                           m_maribozu_coef(0.01),
                           m_spin_coef(1),
                           m_hummer_coef1(0.1),
                           m_hummer_coef2(2),
                           m_range_total(8000),
                           m_trend_period(5)
  {
  }

CPattern 类公有部分的第二部分提供了处理所声明输入参数以及获取形态属性和特征的方法的描述。

我们依次详细了解。 了解操作算法非常重要,以便在创建指标、交易面板或智能交易系统时能够有效地运用它们。

CandleType

从 TYPE_CANDLESTICK 中列出的现有形态返回所选蜡烛的类型。
TYPE_CANDLESTICK  CandleType(const string symbol,const ENUM_TIMEFRAMES timeframe,const int shift);

参数

返回值

TYPE_CANDLESTICK 枚举中选定的烛条类型。

//+------------------------------------------------------------------+
//| 烛条类型                                                         |
//+------------------------------------------------------------------+
enum TYPE_CANDLESTICK
  {
   CAND_NONE,           // 未定义
   CAND_MARIBOZU,       // Marubozu
   CAND_DOJI,           // 十字星
   CAND_SPIN_TOP,       // 尖顶
   CAND_HAMMER,         // 锤子
   CAND_INVERT_HAMMER,  // 倒锤子
   CAND_LONG,           // 长体
   CAND_SHORT           // 短体
  };

实现

//+------------------------------------------------------------------+
//| 返回所选烛条的类型                                                  |
//+------------------------------------------------------------------+
TYPE_CANDLESTICK CPattern::CandleType(const string symbol,const ENUM_TIMEFRAMES timeframe,const int shift)
  {
   CANDLE_STRUCTURE res;
   if(GetCandleType(symbol,timeframe,res,shift))
      return(res.m_type);
   return(CAND_NONE);
  }
//+------------------------------------------------------------------+
//| 烛条类型识别                                                       |
//+------------------------------------------------------------------+
bool CPattern::GetCandleType(const string symbol,const ENUM_TIMEFRAMES timeframe,CANDLE_STRUCTURE &res,const int shift)
  {
   MqlRates rt[];
   int aver_period=m_trend_period;
   double aver=0;
   SymbolSelect(symbol,true);
   int copied=CopyRates(symbol,timeframe,shift,aver_period+1,rt);
//--- 获取前一根烛条的详细信息
   if(copied<aver_period)
      return(false);
//---
   res.m_open=rt[aver_period].open;
   res.m_high=rt[aver_period].high;
   res.m_low=rt[aver_period].low;
   res.m_close=rt[aver_period].close;

//--- 判断趋势方向
   for(int i=0;i<aver_period;i++)
      aver+=rt[i].close;
   aver/=aver_period;

   if(aver<res.m_close)
      res.m_trend=UPPER;
   if(aver>res.m_close)
      res.m_trend=DOWN;
   if(aver==res.m_close)
      res.m_trend=FLAT;
//--- 判断它是涨势还是跌势烛条
   res.m_bull=res.m_open<res.m_close;
//--- 获得烛条实体的绝对大小
   res.m_bodysize=MathAbs(res.m_open-res.m_close);
//--- 获取阴影的大小
   double shade_low=res.m_close-res.m_low;
   double shade_high=res.m_high-res.m_open;
   if(res.m_bull)
     {
      shade_low=res.m_open-res.m_low;
      shade_high=res.m_high-res.m_close;
     }
   double HL=res.m_high-res.m_low;
//--- 计算之前烛条的平均实体大小
   double sum=0;
   for(int i=1; i<=aver_period; i++)
      sum=sum+MathAbs(rt[i].open-rt[i].close);
   sum=sum/aver_period;

//--- 判断烛条类型   
   res.m_type=CAND_NONE;
//--- 长体 
   if(res.m_bodysize>sum*m_long_coef)
      res.m_type=CAND_LONG;
//--- 短体 
   if(res.m_bodysize<sum*m_short_coef)
      res.m_type=CAND_SHORT;
//--- 十字星
   if(res.m_bodysize<HL*m_doji_coef)
      res.m_type=CAND_DOJI;
//--- marubozu
   if((shade_low<res.m_bodysize*m_maribozu_coef || shade_high<res.m_bodysize*m_maribozu_coef) && res.m_bodysize>0)
      res.m_type=CAND_MARIBOZU;
//--- 锤子
   if(shade_low>res.m_bodysize*m_hummer_coef2 && shade_high<res.m_bodysize*m_hummer_coef1)
      res.m_type=CAND_HAMMER;
//--- 倒锤子
   if(shade_low<res.m_bodysize*m_hummer_coef1 && shade_high>res.m_bodysize*m_hummer_coef2)
      res.m_type=CAND_INVERT_HAMMER;
//--- 尖顶
   if(res.m_type==CAND_SHORT && shade_low>res.m_bodysize*m_spin_coef && shade_high>res.m_bodysize*m_spin_coef)
      res.m_type=CAND_SPIN_TOP;
//---
   ArrayFree(rt);
   return(true);
  }

烛条识别方法 GetCandleType() 在公有 GetCandleType() 方法里使用。 GetCandleType() 是私有方法。 它的参数包括当前品种、时间帧和烛条编号,以及指向结构的指针。 基于这些参数执行形态计算和识别。

PatternType

利用所选蜡烛识别形态类型。 它有 5 个重载方法,因为参数可以是 TYPE_PATTERN 枚举中的现有形态,也可以是由一根、两根或三根烛条组成的生成形态。 参数也可以是 TYPE_PATTERN 枚举中的形态数组。

   //--- 识别形态类型
   bool              PatternType(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern,int shift);
   bool              PatternType(const string symbol,const ENUM_TIMEFRAMES timeframe,int index,const int shift);
   bool              PatternType(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,const int shift);
   bool              PatternType(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int index3,const int shift);
   //--- 识别到形态集合
   bool              PatternType(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN &pattern[],int shift);

参数

指向 TYPE_PATTERN 列表中现有形态数组的指针。

//+------------------------------------------------------------------+
//| 形态类型                                                         |
//+------------------------------------------------------------------+
enum TYPE_PATTERN
  {
   NONE,
   HUMMER,
   INVERT_HUMMER,
   HANDING_MAN,
   SHOOTING_STAR,
   ENGULFING_BULL,
   ENGULFING_BEAR,
   HARAMI_BULL,
   HARAMI_BEAR,
   HARAMI_CROSS_BULL,
   HARAMI_CROSS_BEAR,
   DOJI_STAR_BULL,
   DOJI_STAR_BEAR,
   PIERCING_LINE,
   DARK_CLOUD_COVER
  };

返回值

一个布尔值。

实现

由于 PatternType 方法有 5 种类型的实现,我们将使用各种参数分别对其进行分析。 第一种从 TYPE_PATTERN 枚举中搜索现有形态。 如下所示,这是利用私有方法 CheckPattern 完成的。

//+------------------------------------------------------------------+
//| 识别预定义的形态                                                   |
//+------------------------------------------------------------------+
bool CPattern::PatternType(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern,const int shift)
  {
   if(CheckPattern(symbol,timeframe,shift)==pattern)
      return(true);
   return(false);
  }
//+------------------------------------------------------------------+
//| 检查并返回形态类型                                                  |
//+------------------------------------------------------------------+
TYPE_PATTERN CPattern::CheckPattern(const string symbol,const ENUM_TIMEFRAMES timeframe,int shift)
  {
   CANDLE_STRUCTURE cand1,cand2;
   TYPE_PATTERN pattern=NONE;
   ZeroMemory(cand1);
   ZeroMemory(cand2);
   GetCandleType(symbol,timeframe,cand2,shift);                                           // 之前的烛条
   GetCandleType(symbol,timeframe,cand1,shift-1);                                         // 当前烛条
//--- 倒锤子,涨势模型
   if(cand2.m_trend==DOWN &&                                                              // 检查趋势方向
      cand2.m_type==CAND_INVERT_HAMMER)                                                   // 检查“倒锤子”
      pattern=INVERT_HUMMER;
//--- 上吊线,跌势
   else if(cand2.m_trend==UPPER && // 检查趋势方向
      cand2.m_type==CAND_HAMMER) // 检查”锤子“
      pattern=HANDING_MAN;
//--- 锤子,涨势模型
   else if(cand2.m_trend==DOWN && // 检查趋势方向
      cand2.m_type==CAND_HAMMER) // 检查”锤子“
      pattern=HUMMER;
//--- 射击之星,跌势模型
   else if(cand1.m_trend==UPPER && cand2.m_trend==UPPER && // 检查趋势方向
      cand2.m_type==CAND_INVERT_HAMMER && cand1.m_close<=cand2.m_open) // 检查 "倒锤子"
      pattern=SHOOTING_STAR;
//--- 吞噬,涨势模型
   else if(cand1.m_trend==DOWN && cand1.m_bull && cand2.m_trend==DOWN && !cand2.m_bull && // 检查趋势方向和烛条方向
      cand1.m_bodysize>cand2.m_bodysize &&
      cand1.m_close>=cand2.m_open && cand1.m_open<cand2.m_close)
      pattern=ENGULFING_BULL;
//--- 吞噬,跌势模型
   else if(cand1.m_trend==UPPER && cand1.m_bull && cand2.m_trend==UPPER && !cand2.m_bull && // 检查趋势方向和烛台方向
      cand1.m_bodysize<cand2.m_bodysize &&
      cand1.m_close<=cand2.m_open && cand1.m_open>cand2.m_close)
      pattern=ENGULFING_BEAR;
//--- Harami 交叉,涨势
   else if(cand2.m_trend==DOWN && !cand2.m_bull && // 检查趋势方向和烛条方向
      (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && cand1.m_type==CAND_DOJI && // 检查“长体”的第一根烛条和十字星烛条
      cand1.m_close<cand2.m_open && cand1.m_open>=cand2.m_close) // 十字星在第一根烛条实体内
      pattern=HARAMI_CROSS_BULL;
//--- Harami 交叉,跌势模型
   else if(cand2.m_trend==UPPER && cand2.m_bull && // 检查趋势方向和烛条方向
      (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && cand1.m_type==CAND_DOJI && // 检查“长体”烛条和十字星烛条
      cand1.m_close>cand2.m_open && cand1.m_open<=cand2.m_close) // 十字星在第一根烛条实体内
      pattern=HARAMI_CROSS_BEAR;
//--- Harami,涨势
   else if(cand1.m_trend==DOWN && cand1.m_bull && !cand2.m_bull && // 检查趋势方向和烛条方向
      (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && // 检查“长体”的第一根烛条
      cand1.m_type!=CAND_DOJI && cand1.m_bodysize<cand2.m_bodysize && // 第二根蜡烛不是十字星,第一根蜡烛实体大于第二根蜡烛实体
                    cand1.m_close<cand2.m_open && cand1.m_open>=cand2.m_close) // 第二根烛条实体位于第一根烛条实体内
                    pattern=HARAMI_BULL;
//--- Harami, 跌势
   else if(cand1.m_trend==UPPER && !cand1.m_bull && cand2.m_bull && // 检查趋势方向和烛条方向
      (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && // 检查“长体”的第一根烛条

      cand1.m_type!=CAND_DOJI && cand1.m_bodysize<cand2.m_bodysize && // 第二根蜡烛不是十字星,第一根蜡烛实体大于第二根蜡烛实体
                    cand1.m_close>cand2.m_open && cand1.m_open<=cand2.m_close) // 第二根烛条实体位于第一根烛条实体内
                    pattern=HARAMI_BEAR;
//--- 十字星,涨势
   else if(cand1.m_trend==DOWN && !cand2.m_bull && // 检查趋势方向和烛条方向
      (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && cand1.m_type==CAND_DOJI && // 检查“长体”烛条和第二根十字星
      cand1.m_close<=cand2.m_open) // 十字星开盘价低于或等于第一根蜡烛收盘价
      pattern=DOJI_STAR_BULL;
//--- 十字星,跌势
   else if(cand1.m_trend==UPPER && cand2.m_bull && // 检查趋势方向和烛条方向
      (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && cand1.m_type==CAND_DOJI && // 检查“长体”烛条和第二根十字星
      cand1.m_open>=cand2.m_close) //十字星开盘价高于或等于第一根蜡烛收盘价
      pattern=DOJI_STAR_BEAR;
//--- 穿孔,涨势模型
   else if(cand1.m_trend==DOWN && cand1.m_bull && !cand2.m_bull && // 检查趋势方向和烛条方向
      (cand1.m_type==CAND_LONG || cand1.m_type==CAND_MARIBOZU) && (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && // 检查“长体”烛条
      cand1.m_close>(cand2.m_close+cand2.m_open)/2 && // 第二根蜡烛收盘价位于第一根烛条的中间位置
      cand2.m_open>cand1.m_close && cand2.m_close>=cand1.m_open)
      pattern=PIERCING_LINE;
//--- 乌云盖顶,跌势
   else if(cand1.m_trend==UPPER && !cand1.m_bull && cand2.m_bull && // 检查趋势方向和烛条方向
      (cand1.m_type==CAND_LONG || cand1.m_type==CAND_MARIBOZU) && (cand2.m_type==CAND_LONG || cand2.m_type==CAND_MARIBOZU) && // 检查“长体”烛条
      cand1.m_close<(cand2.m_close+cand2.m_open)/2 && // 第二根蜡烛收盘价低于第一根烛条实体的中间位置
      cand1.m_close<cand2.m_open && cand2.m_close<=cand1.m_open)
      pattern=DARK_CLOUD_COVER;
   return(pattern);
  }
//+------------------------------------------------------------------+

下一种 PatternType 方法与前一种方法的不同之处在于,所传递参数由搜索形态的数组替代了形态类型:

//+------------------------------------------------------------------+
//| 识别形态数组                                                       |
//+------------------------------------------------------------------+
bool CPattern::PatternType(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN &pattern[],int shift)
  {
   for(int i=0;i<ArraySize(pattern);i++)
     {
      if(CheckPattern(symbol,timeframe,shift)==pattern[i])
         return(true);
     }
   return(false);
  }

接下来,我们研究 PatternType 方法的实现,处理由简单烛条类型生成的形态。 这些形态有三种类型:由一个根、两根或三根烛条组成。 我们逐一考察它们:

//+------------------------------------------------------------------+
//| 按照烛条索引识别形态                                               |
//+------------------------------------------------------------------+
bool CPattern::PatternType(const string symbol,const ENUM_TIMEFRAMES timeframe,int index,int shift)
  {
//--- 验证烛条索引
   if(index<0 || index>11)
      return(false);
//---
   CANDLE_STRUCTURE cand,cur_cand;
   RATING_SET ratings;
   ZeroMemory(cand);
   IndexToPatternType(cand,index);
//--- 获取当前的烛条类型
   GetCandleType(symbol,timeframe,cur_cand,shift);                                // 当前烛条
//---
   if(cur_cand.m_type==cand.m_type && cur_cand.m_bull==cand.m_bull)
      return(true);
   return(false);
  }

实现搜索一根烛条形态的方法,这与 CandleType() 方法非常相似,尽管传递的参数类型和范围有所不同。 注意以前没有使用过的私有方法 IndextoPatternType()

   //--- 将烛条索引转换为其类型
   void              IndexToPatternType(CANDLE_STRUCTURE &res,int index);

它将简单类型烛条的索引转换为其类型,并将其传递给指定的结构。

以下是两根烛条形态的方法:

//+------------------------------------------------------------------+
//| 按照烛条索引识别形态                                               |
//+------------------------------------------------------------------+
bool CPattern::PatternType(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int shift)
  {
//--- 验证烛条索引
   if(index1<0 || index1>11 || index2<0 || index2>11)
      return(false);
//---
   CANDLE_STRUCTURE cand1,cand2,cand3,cur_cand,prev_cand;
   RATING_SET ratings;
   ZeroMemory(cand1);
   ZeroMemory(cand2);
   IndexToPatternType(cand1,index1);
   IndexToPatternType(cand2,index2);
//--- 获取当前的烛条类型
   GetCandleType(symbol,timeframe,prev_cand,shift+1);                             // 之前的烛条
   GetCandleType(symbol,timeframe,cur_cand,shift);                                // 当前烛条
//---
   if(cur_cand.m_type==cand1.m_type && cur_cand.m_bull==cand1.m_bull && 
      prev_cand.m_type==cand2.m_type && prev_cand.m_bull==cand2.m_bull)
      return(true);
   return(false);
  }

代码实现与前一个非常相似。 不过,请注意在选择烛条索引进行分析时应考虑以下特征:由于该形态由两根烛条组成,因此烛条 'index1' 将按 'shift' 偏移,而对于 index2 - 则按 'shift+1'。 同样的特性涉及三根烛条形态的方法实现:

//+------------------------------------------------------------------+
//| 按照烛条索引识别形态                                               |
//+------------------------------------------------------------------+
bool CPattern::PatternType(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int index3,int shift)
  {
   CANDLE_STRUCTURE cand1,cand2,cand3,cur_cand,prev_cand,prev_cand2;
   RATING_SET ratings;
//---
   ZeroMemory(cand1);
   ZeroMemory(cand2);
   ZeroMemory(cand3);
//---
   IndexToPatternType(cand1,index1);
   IndexToPatternType(cand2,index2);
   IndexToPatternType(cand3,index3);

//--- 获取当前的烛条类型
   GetCandleType(symbol,timeframe,prev_cand2,shift+2);                            // 之前的烛条
   GetCandleType(symbol,timeframe,prev_cand,shift+1);                             // 之前的烛条
   GetCandleType(symbol,timeframe,cur_cand,shift);                                // 当前烛条
//---
   if(cur_cand.m_type==cand1.m_type && cur_cand.m_bull==cand1.m_bull && 
      prev_cand.m_type==cand2.m_type && prev_cand.m_bull==cand2.m_bull && 
      prev_cand2.m_type==cand3.m_type && prev_cand2.m_bull==cand3.m_bull)
      return(true);
   return(false);
  }

Found

返回指定类型的已发现形态的数量。 对于现有形态 TYPE_PATTERN,它有 3 个方法重载,以及由 1-3 个简单烛条类型组成的生成形态。

   //--- 返回指定类型的已发现形态的数量
   int               Found(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern);
   int               Found(const string symbol,const ENUM_TIMEFRAMES timeframe,int index);
   int               Found(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2);
   int               Found(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int index3);

参数

  • symbol  选择用于搜索的品种。
  • timeframe  选定的时间帧。
  • patternTYPE_PATTERN 中的现有形态类型。
  • index,index1,index2,index3 简单类型烛条的索引(图例 1 中的区块 4)。

返回值

已发现的指定类型的形态数量。

实现

这种方法的实现非常简单。 私有方法 PatternStat() 负责收集统计信息相关的主要操作。

//+------------------------------------------------------------------+
//| 返回指定类型的已发现形态的数量                                        |
//+------------------------------------------------------------------+
int CPattern::Found(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern)
  {
   PatternStat(symbol,timeframe,pattern);
   return(m_found);
  }
//+------------------------------------------------------------------+
//| 返回指定类型的已发现形态的数量                                        |
//+------------------------------------------------------------------+
int CPattern::Found(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1)
  {
   PatternStat(symbol,timeframe,index1);
   return(m_found);
  }
//+------------------------------------------------------------------+
//| 返回指定类型的已发现形态的数量                                        |
//+------------------------------------------------------------------+
int CPattern::Found(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2)
  {
   PatternStat(symbol,timeframe,index1,index2);
   return(m_found);
  }
//+------------------------------------------------------------------+
//| 返回指定类型的已发现形态的数量                                        |
//+------------------------------------------------------------------+
int CPattern::Found(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int index3)
  {
   PatternStat(symbol,timeframe,index1,index2,index3);
   return(m_found);
  }

PatternStat() 方法有两种类型:用于现有形态,和生成形态。 我们来详细研究它们。 第一个用于 TYPE_PATTERN 枚举中的形态:

//+------------------------------------------------------------------+
//| 获取有关给定形态的统计信息                                          |
//+------------------------------------------------------------------+
CPattern::PatternStat(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern)
  {
//---
   int pattern_counter=0;
//---
   RATING_SET pattern_coef={0,0,0,0,0,0};
//---
   for(int i=m_range_total;i>4;i--)
     {
      if(CheckPattern(symbol,timeframe,i)==pattern)
        {
         pattern_counter++;
         if(pattern==HUMMER || pattern==INVERT_HUMMER || pattern==HANDING_MAN)
            GetCategory(symbol,timeframe,pattern_coef,i-3);
         else
            GetCategory(symbol,timeframe,pattern_coef,i-4);
        }
     }
//---
   CoefCalculation(pattern_coef,pattern_counter);
  }

第二个用于由简单烛条类型的索引组成的生成形态。

//+------------------------------------------------------------------+
//| 获取有关给定形态的统计信息                                          |
//+------------------------------------------------------------------+
void CPattern::PatternStat(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2=0,int index3=0)
  {
   CANDLE_STRUCTURE cand1,cand2,cand3,cur_cand,prev_cand,prev_cand2;
   RATING_SET rating={0,0,0,0,0,0};
   int pattern_total=0,pattern_size=1;
//---
   ZeroMemory(cand1);
   ZeroMemory(cand2);
   ZeroMemory(cand3);
   ZeroMemory(cur_cand);
   ZeroMemory(prev_cand);
   ZeroMemory(prev_cand2);
//---
   if(index2>0)
      pattern_size=2;
   if(index3>0)
      pattern_size=3;
//---
   if(pattern_size==1)
      IndexToPatternType(cand1,index1);
   else if(pattern_size==2)
     {
      IndexToPatternType(cand1,index1);
      IndexToPatternType(cand2,index2);
     }
   else if(pattern_size==3)
     {
      IndexToPatternType(cand1,index1);
      IndexToPatternType(cand2,index2);
      IndexToPatternType(cand3,index3);
     }
//---
   for(int i=m_range_total;i>5;i--)
     {
      if(pattern_size==1)
        {
         //--- 获取当前的烛条类型
         GetCandleType(symbol,timeframe,cur_cand,i);                                       // 当前烛条
         //---
         if(cur_cand.m_type==cand1.m_type && cur_cand.m_bull==cand1.m_bull)
           {
            pattern_total++;
            GetCategory(symbol,timeframe,rating,i-3);
           }
        }
      else if(pattern_size==2)
        {
         //--- 获取当前的烛条类型
         GetCandleType(symbol,timeframe,prev_cand,i);                                        // 之前的烛条
         GetCandleType(symbol,timeframe,cur_cand,i-1);                                       // 当前烛条
         //---

         if(cur_cand.m_type==cand1.m_type && cur_cand.m_bull==cand1.m_bull && 
            prev_cand.m_type==cand2.m_type && prev_cand.m_bull==cand2.m_bull)
           {
            pattern_total++;
            GetCategory(symbol,timeframe,rating,i-4);
           }
        }
      else if(pattern_size==3)
        {
         //--- 获取当前的烛条类型
         GetCandleType(symbol,timeframe,prev_cand2,i);                                       // 之前的烛条
         GetCandleType(symbol,timeframe,prev_cand,i-1);                                      // 之前的烛条
         GetCandleType(symbol,timeframe,cur_cand,i-2);                                       // 当前烛条
         //---
         if(cur_cand.m_type==cand1.m_type && cur_cand.m_bull==cand1.m_bull && 
            prev_cand.m_type==cand2.m_type && prev_cand.m_bull==cand2.m_bull && 
            prev_cand2.m_type==cand3.m_type && prev_cand2.m_bull==cand3.m_bull)
           {
            pattern_total++;
            GetCategory(symbol,timeframe,rating,i-5);
           }
        }
     }
//---
   CoefCalculation(rating,pattern_total);
  }

这两个方法的实现均包含一个新方法。 这是私有方法 CoefCalculation():

   //--- 计算系数 
   bool              CoefCalculation(RATING_SET &rate,int found);

它处理搜索和测试形态时获得的所有结果。 它的参数包含一个指向 RATING_SET 结构的指针,该结构负责收集有关形态效率测试结果,和已发现形态数量的数据。 所分析形态的所有其他参数和属性在 CoefCalculation() 方法中计算。

//+------------------------------------------------------------------+
//| 计算效率评估系数                                                   |
//+------------------------------------------------------------------+
bool CPattern::CoefCalculation(RATING_SET &rate,int found)
  {
   int sum1=0,sum2=0;
   sum1=rate.m_a_uptrend+rate.m_b_uptrend+rate.m_c_uptrend;
   sum2=rate.m_a_dntrend+rate.m_b_dntrend+rate.m_c_dntrend;
//---
   m_probability1=(found>0)?NormalizeDouble((double)sum1/found*100,2):0;
   m_probability2=(found>0)?NormalizeDouble((double)sum2/found*100,2):0;
   m_efficiency1=(found>0)?NormalizeDouble((m_k1*rate.m_a_uptrend+m_k2*rate.m_b_uptrend+m_k3*rate.m_c_uptrend)/found,3):0;
   m_efficiency2=(found>0)?NormalizeDouble((m_k1*rate.m_a_dntrend+m_k2*rate.m_b_dntrend+m_k3*rate.m_c_dntrend)/found,3):0;
   m_found=found;
   m_coincidence=((double)found/m_range_total*100);
   return(true);
  }

Coincidence

返回形态发生的频率。 对于现有形态 TYPE_PATTERN,它有 3 个方法重载,以及由 1-3 个简单烛条类型组成的生成形态。

   //--- 返回形态出现的频率
   double            Coincidence(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern);
   double            Coincidence(const string symbol,const ENUM_TIMEFRAMES timeframe,int index);
   double            Coincidence(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2);
   double            Coincidence(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int index3);

参数

  • symbol选择用于搜索的品种。
  • timeframe选定的时间帧。
  • patternTYPE_PATTERN 中的现有形态类型。
  • index,index1,index2,index3简单类型烛条的索引(图例 1 中的区块 4)。

返回值

以百分比表示的形态出现频率。

实现

Found() 方法类似。 唯一的区别是它返回 m_coincidence 变量的值。

//+------------------------------------------------------------------+
//| 返回形态出现的频率                                                |
//+------------------------------------------------------------------+
double CPattern::Coincidence(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern)
  {
   PatternStat(symbol,timeframe,pattern);
   return(m_coincidence);
  }
//+------------------------------------------------------------------+
//| 返回形态出现的频率                                                |
//+------------------------------------------------------------------+
double CPattern::Coincidence(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1)
  {
   PatternStat(symbol,timeframe,index1);
   return(m_coincidence);
  }
//+------------------------------------------------------------------+
//| 返回形态出现的频率                                                |
//+------------------------------------------------------------------+
double CPattern::Coincidence(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2)
  {
   PatternStat(symbol,timeframe,index1,index2);
   return(m_coincidence);
  }
//+------------------------------------------------------------------+
//| 返回形态出现的频率                                                |
//+------------------------------------------------------------------+
double CPattern::Coincidence(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int index3)
  {
   PatternStat(symbol,timeframe,index1,index2,index3);
   return(m_coincidence);
  }

Probability

返回给定形态出现后的走势概率百分比。 对于现有形态 TYPE_PATTERN,它有 3 个方法重载,以及由 1-3 个简单烛条类型组成的生成形态。

   //--- 返回给定形态出现后的走势概率 
   double            Probability(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern,TYPE_TREND trend);
   double            Probability(const string symbol,const ENUM_TIMEFRAMES timeframe,int index,TYPE_TREND trend);
   double            Probability(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,TYPE_TREND trend);
   double            Probability(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int index3,TYPE_TREND trend);

参数

  • symbol选择用于搜索的品种。
  • timeframe选定的时间帧。
  • patternTYPE_PATTERN 中的现有形态类型。
  • index,index1,index2,index 3简单类型烛条的索引(图例 1 中的区块 4)。

  • trendTYPE_TREND
//+------------------------------------------------------------------+
//| 趋势类型                                                         |
//+------------------------------------------------------------------+
enum TYPE_TREND
  {
   UPPER,               //上行趋势
   DOWN,                //下行趋势
   FLAT                 //横盘
  };
//+------------------------------------------------------------------+

返回值

给定形态出现后的走势概率百分比。

实现

Found() 方法类似。 唯一的区别是它返回 m_probability1 或 m_probability2 变量的值,具体取决于所选的趋势类型。

//+------------------------------------------------------------------+
//| 返回给定形态出现后的走势概率                                         |
//+------------------------------------------------------------------+
double CPattern::Probability(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,pattern);
   if(trend==UPPER)
      return(m_probability1);
   if(trend==DOWN)
      return(m_probability2);
   return(0);
  }
//+------------------------------------------------------------------+
//| 返回给定形态出现后的走势概率                                         |
//+------------------------------------------------------------------+
double CPattern::Probability(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,index1);
   if(trend==UPPER)
      return(m_probability1);
   if(trend==DOWN)
      return(m_probability2);
   return(0);
  }
//+------------------------------------------------------------------+
//| 返回给定形态出现后的走势概率                                         |
//+------------------------------------------------------------------+
double CPattern::Probability(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,index1,index2);
   if(trend==UPPER)
      return(m_probability1);
   if(trend==DOWN)
      return(m_probability2);
   return(0);
  }
//+------------------------------------------------------------------+
//| 返回给定形态出现后的走势概率                                         |
//+------------------------------------------------------------------+
double CPattern::Probability(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int index3,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,index1,index2,index3);
   if(trend==UPPER)
      return(m_probability1);
   if(trend==DOWN)
      return(m_probability2);
   return(0);
  }

Efficiency

返回形态效率系数。 对于现有形态 TYPE_PATTERN,它有 3 个方法重载,以及由 1-3 个简单烛条类型组成的生成形态。

   //--- 返回形态效率系数 
   double            Efficiency(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern,TYPE_TREND trend);
   double            Efficiency(const string symbol,const ENUM_TIMEFRAMES timeframe,int index,TYPE_TREND trend);
   double            Efficiency(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,TYPE_TREND trend);
   double            Efficiency(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int index3,TYPE_TREND trend);

参数

  • symbol选择用于搜索的品种。
  • timeframe选定的时间帧。
  • patternTYPE_PATTERN 中的现有形态类型。
  • index,index1,index2,index3简单类型烛条的索引(图例 1 中的区块 4)。
  • trendtrend type, TYPE_TREND
//+------------------------------------------------------------------+
//| 趋势类型                                                         |
//+------------------------------------------------------------------+
enum TYPE_TREND
  {
   UPPER,               //上行趋势
   DOWN,                //下行趋势
   FLAT                 //横盘
  };
//+------------------------------------------------------------------+

返回值

形态效率系数。

实现

Found() 方法类似。 唯一的区别是它返回 m_efficiency1 或 m_efficiency2 变量的值,具体取决于所选的趋势类型。

//+------------------------------------------------------------------+
//| 返回给定形态出现后的走势概率                                         |
//+------------------------------------------------------------------+
double CPattern::Efficiency(const string symbol,const ENUM_TIMEFRAMES timeframe,TYPE_PATTERN pattern,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,pattern);
   if(trend==UPPER)
      return(m_efficiency1);
   if(trend==DOWN)
      return(m_efficiency2);
   return(0);
  }
//+------------------------------------------------------------------+
//| 返回给定形态出现后的走势概率                                         |
//+------------------------------------------------------------------+
double CPattern::Efficiency(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,index1);
   if(trend==UPPER)
      return(m_efficiency1);
   if(trend==DOWN)
      return(m_efficiency2);
   return(0);
  }
//+------------------------------------------------------------------+
//| 返回给定形态出现后的走势概率                                         |
//+------------------------------------------------------------------+
double CPattern::Efficiency(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,index1,index2);
   if(trend==UPPER)
      return(m_efficiency1);
   if(trend==DOWN)
      return(m_efficiency2);
   return(0);
  }
//+------------------------------------------------------------------+
//| 返回给定形态出现后的走势概率                                         |
//+------------------------------------------------------------------+
double CPattern::Efficiency(const string symbol,const ENUM_TIMEFRAMES timeframe,int index1,int index2,int index3,TYPE_TREND trend)
  {
   PatternStat(symbol,timeframe,index1,index2,index3);
   if(trend==UPPER)
      return(m_efficiency1);
   if(trend==DOWN)
      return(m_efficiency2);
   return(0);
  }

实际使用

在考察了处理形态的工具之后,我们实现两个指标和一个智能交易系统来演示函数库的使用示例。

CandleDetector

我们从指标开始,该指标在图表上显示 TYPE_CANDLESTICK 枚举中所选类型的烛条。 在 Indicators 文件夹中创建 Pattern 文件夹。 在此文件夹中,创建指标文件 CandleDetector.mq5。 我们关联 Pattern.mqh 函数库,用于处理形态操作并设置未来指标的初始属性:

//+------------------------------------------------------------------+
//|                                               CandleDetector.mq5 |
//|                                 版权所有 2019, MetaQuotes 软件公司 |
//|                           https://www.mql5.com/zh/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "版权所有 2019, MetaQuotes 软件公司"
#property link      "https://www.mql5.com/zh/users/alex2356"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
#include <Pattern/Pattern.mqh>
//+----------------------------------------------+
//|  指标绘图参数                                  |
//+----------------------------------------------+
//---- 将指标绘制为标签
#property indicator_type1   DRAW_ARROW
//---- 指标线宽度
#property indicator_width1  1

The next step is to determine key settings which will affect the display of this or that candlestick type. 

//+----------------------------------------------+
//| 指标输入参数                                   |
//+----------------------------------------------+
input TYPE_CANDLESTICK  CandleType=1;                 // 烛条类型
input color             LabelColor=clrCrimson;
input double            LongCoef=1.3;
input double            ShortCoef=0.5;
input double            DojiCoef=0.04;
input double            MaribozuCoef=0.01;
input double            SpinCoef=1;
input double            HummerCoef1=0.1;
input double            HummerCoef2=2;
input int               TrendPeriod=5; 

接下来,在初始化中,配置指标外观,并确定输入参数中用于搜索的所有数值。

//+------------------------------------------------------------------+
//| 自定义指标初始化函数                                               |
//+------------------------------------------------------------------+
int OnInit()
  {
//---- 初始化计算开始的数据变量
   min_rates_total=TrendPeriod+1;
//---- 定义指标数值的显示精度
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits);

//---- 将动态数组 Signal[] 设置为指标缓冲区
   SetIndexBuffer(0,Signal,INDICATOR_DATA);
//---- 指标 1 的画图开始偏移
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,min_rates_total);
//---- 按时间顺序设置缓冲区中元素的索引   
   ArraySetAsSeries(Signal,true);
//---- 设置在图表上不可见的指标值
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE);
//---- 指标品种
   PlotIndexSetInteger(0,PLOT_ARROW,108);
//---
   PlotIndexSetInteger(0,PLOT_LINE_COLOR,LabelColor);
//----
   Pat.Long_coef(LongCoef);
   Pat.Short_coef(ShortCoef);
   Pat.Doji_coef(DojiCoef);
   Pat.Maribozu_coef(MaribozuCoef);
   Pat.Spin_coef(SpinCoef);
   Pat.Hummer_coef1(HummerCoef1);
   Pat.Hummer_coef2(HummerCoef2);
   Pat.TrendPeriod(TrendPeriod);
   return(INIT_SUCCEEDED);
  }

注意 CandleType() 方法 — 它用于在图表上搜索选定的烛条类型。

//+------------------------------------------------------------------+
//| 自定义指标迭代函数                                                  |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---- 检查计算是否有足够的柱线
   if(rates_total<min_rates_total)
      return(0);
//---- 声明局部变量 
   int limit,bar;
//---- 按时间序列设置数组中元素的索引  
   ArraySetAsSeries(low,true);
//---- 重新计算周期自 “第一根” 柱线编号开始计算
   if(prev_calculated>rates_total || prev_calculated<=0)          // 检查指标是否为首次计算
      limit=rates_total-min_rates_total;                          // 计算所有装修按的起始索引
   else
      limit=rates_total-prev_calculated;                          // 计算新柱线的起始索引
//---- 指标的主要计算循环
   for(bar=limit; bar>=0; bar--)
     {
      Signal[bar]=0.0;
      if(Pat.CandleType(Symbol(),PERIOD_CURRENT,bar)==CandleType)
         Signal[bar]=low[bar]-200*_Point;
     }
   return(rates_total);
  }
//+------------------------------------------------------------------+

指标操作示例如图例 3 所示(搜索长体烛条)。

图例 3 CandleDetector 指标操作示例。

PatternDetector

第二个指标将从 TYPE_PATTERN 枚举中搜索指定的形态。 实现与前一个非常相似。 此代码的不同之处在于 计算部分里使用的方法一个输入参数

//+----------------------------------------------+
//| 指标输入参数                                   |
//+----------------------------------------------+
input TYPE_PATTERN      PatternType=1;                // 形态类型
input color             LabelColor=clrCrimson;
input double            LongCoef=1.3;
input double            ShortCoef=0.5;
input double            DojiCoef=0.04;
input double            MaribozuCoef=0.01;
input double            SpinCoef=1;
input double            HummerCoef1=0.1;
input double            HummerCoef2=2;
input int               TrendPeriod=5;
//---
CPattern Pat;
double Signal[];
//---- 为数据计算开始声明整数变量
int min_rates_total;
//+------------------------------------------------------------------+
//| 自定义指标初始化函数                                               |
//+------------------------------------------------------------------+  
void OnInit()
  {
//---- 初始化计算开始的数据变量
   min_rates_total=TrendPeriod+2;
//---- 定义指标数值的显示精度
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits);

//---- 将 SignUp[] 动态数组设置为指标缓冲区
   SetIndexBuffer(0,Signal,INDICATOR_DATA);
//---- 指标 1 的画图开始偏移
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,min_rates_total);
//---- 按时间顺序设置缓冲区中元素的索引   
   ArraySetAsSeries(Signal,true);
//---- 设置在图表上不可见的指标值
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE);
//---- 指标品种
   PlotIndexSetInteger(0,PLOT_ARROW,108);
//---
   PlotIndexSetInteger(0,PLOT_LINE_COLOR,LabelColor);
//----
   Pat.Long_coef(LongCoef);
   Pat.Short_coef(ShortCoef);
   Pat.Doji_coef(DojiCoef);
   Pat.Maribozu_coef(MaribozuCoef);
   Pat.Spin_coef(SpinCoef);
   Pat.Hummer_coef1(HummerCoef1);
   Pat.Hummer_coef2(HummerCoef2);
   Pat.TrendPeriod(TrendPeriod);
  }
//+------------------------------------------------------------------+
//| 自定义指标迭代函数                                                  |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---- 检查计算是否有足够的柱线
   if(rates_total<min_rates_total)
      return(0);
//---- 声明局部变量 
   int limit,bar;
//---- 按时间序列设置数组中元素的索引  
   ArraySetAsSeries(low,true);
//---- 重新计算周期自 “第一根” 柱线编号开始计算
   if(prev_calculated>rates_total || prev_calculated<=0)       // 检查指标首次计算
      limit=rates_total-min_rates_total;                       // 计算所有柱线的起始索引
   else
      limit=rates_total-prev_calculated;                       // 计算新柱线的起始索引
//---- 指标的主要计算循环
   for(bar=limit; bar>0; bar--)
     {
      Signal[bar]=0.0;
      if(Pat.PatternType(_Symbol,_Period,PatternType,bar))
         Signal[bar]=low[bar]-200*_Point;
     }
   return(rates_total);
  }
//+------------------------------------------------------------------+

PatternDetector 操作结果如图例 4 所示(搜索吞噬 - 涨势形态)。

图例 4 PatternDetector 指标操作示例。

现在我们创建一个智能交易系统,它可以在图表上找到形态并根据所选形态开仓。 每种成交类型都可以使用独立的形态类型。 附加模式允许在现有形态和利用简单烛条类型生成的形态之间进行选择。

我们在 Experts 文件夹中创建 Pattern 文件夹,然后添加 PatternExpert.mq5,其内容是编写的 EA 代码。 在第一阶段,关联 Pattern.mqh 函数库以便处理形态,以及用于交易操作的 Trade.mqh 函数库。 声明类实例并引入 PATTERN_MODE 枚举,该枚举允许在现有形态和生成形态之间进行切换。

//+------------------------------------------------------------------+
//|                                                PatternExpert.mq5 |
//|                                 版权所有 2019, MetaQuotes 软件公司 |
//|                           https://www.mql5.com/zh/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "版权所有 2018, MetaQuotes 软件公司"
#property link      "https://www.mql5.com/zh/users/alex2356"
#property version   "1.00"
#include <Pattern/Pattern.mqh>
#include "Trade.mqh" 
CTradeBase Trade;
CPattern Pat;
//+------------------------------------------------------------------+
//| 形态搜索模式                                                       |
//+------------------------------------------------------------------+
enum PATTERN_MODE
  {
   EXISTING,
   GENERATED
  };

现在定义智能交易系统的输入参数。 EA 参数在第一个模块中提供:

//+------------------------------------------------------------------+
//| 智能交易系统输入参数                                               |
//+------------------------------------------------------------------+
input    string               Inp_EaComment="Pattern Strategy";         // EA 注释
input    double               Inp_Lot=0.01;                             // 手数
input    MarginMode           Inp_MMode=LOT;                            // 资金管理

//--- EA 参数
input    string               Inp_Str_label="===EA parameters===";      // 标签
input    int                  Inp_MagicNum=1111;                        // 魔幻数字
input    int                  Inp_StopLoss=40;                          // 止损(点数)
input    int                  Inp_TakeProfit=30;                        // 止盈(点数)

第二部分包含设置和交易参数。 

//--- 交易参数
input ENUM_TIMEFRAMES         Timeframe=PERIOD_CURRENT;                 // 当前时间帧
input PATTERN_MODE            PatternMode=0;                            // 形态模式
input TYPE_PATTERN            BuyPatternType=ENGULFING_BULL;            // 买入形态类型
input TYPE_PATTERN            SellPatternType=ENGULFING_BEAR;           // 卖出形态类型
input uint                    BuyIndex1=1;                              // 简单蜡烛1的买入索引
input uint                    BuyIndex2=0;                              // 简单蜡烛2的买入索引
input uint                    BuyIndex3=0;                              // 简单蜡烛3的买入索引
input uint                    SellIndex1=1;                             // 简单蜡烛1的卖出索引
input uint                    SellIndex2=0;                             // 简单蜡烛2的卖出索引
input uint                    SellIndex3=0;                             // 简单蜡烛3的卖出索引
input double                  LongCoef=1.3;                             // 长体蜡烛系数
input double                  ShortCoef=0.5;                            // 短体蜡烛系数
input double                  DojiCoef=0.04;                            // 十字星蜡烛系数
input double                  MaribozuCoef=0.01;                        // Maribozu 蜡烛系数
input double                  SpinCoef=1;                               // 尖顶蜡烛系数
input double                  HummerCoef1=0.1;                          // 锤子蜡烛系数1
input double                  HummerCoef2=2;                            // 锤子蜡烛系数2
input int                     TrendPeriod=5;                            // 趋势周期

我们更详细地考察其中一些参数:

  • Current Timeframe — 选择的操作时间帧。 在优化期间启用时间帧选择。 默认情况下会选择当前图表时间帧。
  • Pattern Mode — 形态选择模式。 EXISTING — 现有形态; 两种设置买入形态类型和卖出形态类型适用于此模式。GENERATED - 生成的形态。 在此模式下,将忽略 "Buy Pattern Type/ Sell Pattern Type" 设定,而用 BuyIndex1-3 和 SellIndex1-3 替代。
  • Buy Pattern Type/ Sell Pattern Type — 选择形态所对应的交易操作。 
  • BuyIndex1-3/SellIndex1-3 — 选择由简单烛条类型生成的形态(图例 1 中的区块 4),在其出现时将打开多头/空头仓位。 

其他参数类似于上述考察的指标。 除了检查之外,还在初始化模块中设置趋势周期值,这会影响图表的形态检测。

//+------------------------------------------------------------------+
//| 智能系统初始化函数                                                  |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- 检查与交易服务器的连接
   if(!TerminalInfoInteger(TERMINAL_CONNECTED))
     {
      Print(Inp_EaComment,": No Connection!");
      return(INIT_FAILED);
     }
//--- 检查自动交易许可
   if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
     {
      Print(Inp_EaComment,": Trade is not allowed!");
      return(INIT_FAILED);
     }
//---
   Pat.TrendPeriod(TrendPeriod);
//---
   return(INIT_SUCCEEDED);
  }

计算部分易于理解。 我们考察 买入和卖出 信号搜索函数。

//+------------------------------------------------------------------+
//| 智能系统即时报价函数                                               |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(!Trade.IsOpenedByMagic(Inp_MagicNum))
     {
      //--- 如果有买入信号,则开单
      if(BuySignal())
         Trade.BuyPositionOpen(Symbol(),Inp_Lot,Inp_StopLoss,Inp_TakeProfit,Inp_MagicNum,Inp_EaComment);
      //--- 如果有卖出信号,则开单
      if(SellSignal())
         Trade.SellPositionOpen(Symbol(),Inp_Lot,Inp_StopLoss,Inp_TakeProfit,Inp_MagicNum,Inp_EaComment);
     }
  }

这些函数类似,因此我们仅考察其中一个 BuySignal(),它搜索买入信号。

//+------------------------------------------------------------------+
//| 买入条件                                                         |
//+------------------------------------------------------------------+
bool BuySignal()
  {
   if(PatternMode==0)
     {
      if(BuyPatternType==NONE)
         return(false);
      if(Pat.PatternType(_Symbol,Timeframe,BuyPatternType,1))
         return(true);
     }
   else if(PatternMode==1)
     {
      if(BuyIndex1>0 && BuyIndex2==0 && BuyIndex3==0)
        {
         if(Pat.PatternType(_Symbol,Timeframe,BuyIndex1,1))
            return(true);
        }
      else if(BuyIndex1>0 && BuyIndex2>0 && BuyIndex3==0)
        {
         if(Pat.PatternType(_Symbol,Timeframe,BuyIndex1,BuyIndex2,1))
            return(true);
        }
      else if(BuyIndex1>0 && BuyIndex2>0 && BuyIndex3>0)
        {
         if(Pat.PatternType(_Symbol,Timeframe,BuyIndex1,BuyIndex2,BuyIndex3,1))
            return(true);
        }
     }
   return(false);
  }

该函数包含对当前所选模式、现有形态或生成形态 的检查。 相应地选择输入参数:TYPE_PATTERN 或生成形态的一组索引

我们以两种模式测试和优化所生成智能交易系统:使用 TYPE_PATTERN 枚举中的现有形态,以及由图例 1 中的区块 4 所示的简单烛条类型组成的生成形态。

将使用以下参数测试智能交易系统:

  • 间隔:对于上行趋势模式 2018.01.01 — 2018.03.15
  • 当前货币对: EURUSD
  • 交易模式: 无延迟。这些不是高频交易策略,因此延迟的影响非常小。
  • 测试: 1 分钟 OHLC。 
  • 初始资金: 1000 USD
  • 杠杆: 1:500
  • 服务器: MetaQuotes-Demo
  • 报价: 5-位小数

生成形态模式。

我们确定要测试和优化的参数。

图例 5 在生成形态模式下设置优化参数

图例 5 显示了测试和优化条件。 测试结果获得的最佳参数显示在第二个数值列中。 回测结果和图表如下面的图例 6 所示。 

图例 6 生成形态模式的最佳参数测试结果。

现有形态模式。

设置测试和优化的参数。

图例 7 在现有形态模式下设置优化参数  

同样,最佳优化结果的参数显示在第二列中。 现在我们进行单次测试。 结果如下图例 8 所示。

图例 8 现有形态模式下的最佳参数测试结果。


结束语

下面附带的存档包含所有描述文件,并在文件夹中正确安置。 若要正确操作,应将 MQL5 文件夹保存到终端的根目录下。 若要查找 MQL5 文件夹所在的根文件夹,请在 MetaTrader 5 中按 Ctrl+Shift+D 或使用关联菜单,如图例 9 所示。

图例 9 如何在 MetaTrader 5 根目录中找到 MQL5 文件夹。

本文中使用的程序

#
 名称
类型
描述
1
Pattern.mqh 函数库  用于处理形态的函数库
2 CandleDetector.mq5 指标  烛条搜索指示器
3 PatternDetector.mq5 指标  形态搜索指标
4 PatternExpert.mq5  智能交易系统   利用形态操作的智能交易系统
5 Trade.mqh 函数库  交易函数的类