美林(Merrill)形态

3 十月 2019, 07:53
Alexander Fedosov
0
2 097

内容

概述

1971 年,罗伯特·利维(Robert Levy)首次尝试建立了价格形态系统。 他应用了五点价格波动形态,然后检查它们是否有意义。 他没有达成任何显著绩效,但是 10 年后,亚瑟·美林(Arthur Merrill)继承了他的工作。 

他用 M 和 W 字母将形态分为两类。 每个类别包含 16 个形态,以及自己的子类别。 美林着重强调了 6 个子类别:

  • 上行趋势
  • 下行趋势
  • 三角形
  • 扩展
  • 头顶肩
  • 逆头顶肩

我们将利用应用程序测试所定义的美林形态与当前行情的相关性。 此外,将这种形态应用于各种类型的数据会很有趣 — 例如收盘价、最高价和最低价,以及振荡器。

定义与应用

为了阐明我们在应用美林形态时如何以及该使用哪些数据,我们需要了解它们的实际含义。 主要的两个类别是类似于字母 M 和 W 的图案。它们被称为 M 和 W 形态。 每个类别包含 16 种形态。

图例 1、示意 16 个 M 形态。 我们可以看到,区别在于构成形态的五个点的相互排列。  


图例 1、 М 形态的直观展示

图例 2、 16 个 W 形态的展示。 我们将从价格图表和指标上搜索这两组形态,并研究、评估和寻找可能的规律性。

图例 2、 W 形态的直观展示

任何形态背后的思想都可以归结为以下事实:当出现特定形态时,我们可以预期价格朝某个方向移动,并能从中获利。

为了尽可能清晰地阐明在哪些区域,以及如何研究美林形态,下面列举一些示例。 图例 3、显示一幅通常的线性 USDCAD H1 价格图表。 由于蜡烛和柱线变得越来越流行,因此很少使用这种表现形式。

图例 3、 基于收盘价的线性 USDCAD H1 图表

在此,我们已经可以看到上述几种形态。 这将是第一个要研究的区域 — 应用在基于收盘价的线性图表。 另外,我们将根据开盘价、最高价和最低价排查线性图表。 

第二个要研究的区域将由振荡器组成,例如:

我会用到在研究烛台分析技术(第一部分):检查现有形态一文中建议的方法来评估基于价格和上述振荡器的形态。 其背后的思路很简单:

  • 在指定样本部分识别所分析形态。
  • 识别后再分析价格走势。
  • 收集数据并计算形态的效能。


开发测试工具

在起手开发之前,我们需要定义应当包含的设置。 该工具应由 分析(Analysis)设置(Settings) 选卡面板组成。 还要用到 EA 设置窗口中的参数。 总之,我们将其划分为三个部分,作为处理形态的工具。 现在,我们来描述每个部分中的设置。

分析(Analysis) 选卡包括:

  1. 两组按钮,用于选择测试形态的类型。 还有 All MAll W 按钮,用来快速选择/取消选择 М 和 W 形态。
  2. 用于选择测试时间帧的按钮组,和用于选择/取消选择整个按钮组的 ALL 按钮。
  3. “趋势阈值(点数)”输入字段。 这是在识别出所分析的美林形态后,价格应在最多三根蜡烛内达到的盈利点数。
  4. 打开对话框窗口的按钮,用于选择开始和结束日期,以及测试时间。 
  5. 带有复选框和按钮的输入字段则是用来搜索必要交易品种的过滤器。 它已有个预设 — Major(主要)。 它显示主要货币对。 复选框则禁用过滤器,并显示所有可用的交易品种。
  6. 在表格中配合过滤器选择交易代码。 从列表中选出它们之后,将执行形态分析。
  7. 结果表格由七列组成: 
    • 图形名称。 该列显示所分析的美林形态名称,例如 M10 或 W12。
    • 发现。 按所选样本检测到的指定类型的形态数量。
    • 时间帧。 在时间帧内分析指定形态。
    • P, 上行趋势。 形态出现后,价格可能向上移动的“趋势阈值(点数)”值。
    • P, 下行趋势。 形态出现后,价格可能向下移动的“趋势阈值(点数)”值。
    • K, 上行趋势/K, 上行趋势。 这个比率在我的文章研究烛台分析技术(第一部分):检查现有形态中已有所阐述。 它评估所分析形态在趋势向上或向下方向出现之后,价格多快达到指定的利润。

图例 4、提供上述所有品种和参数的直观实现。


图例 4、 分析(Analysis)选卡

现在我们研究设置(Settings)选卡:

  1. 用到的指标。 选择一个指标,搜索并分析所应用的美林形态。
  2. 在计算上述 K,上行趋势/下行趋势的比率时用到的权重系数。 
  3. 界面语言。 选择界面语言的下拉列表:英语或俄语。

设置选卡外观显示在下面的图例 5 当中:


图例 5、 设置(Settings)选卡

最后一个板块应用“ EA 设置”窗口(F7 热键),并提供“用到的指标”中列出的已应用指标的设置。 图例 6 显示最后部分的设置窗口。


图例 6、 所用指标的设置窗口

在窗口中定义设置时,我们应考虑以下细微差别:

  • 第一个(“应用价格”)应用 ENUM_APPLIED_PRICE 枚举类型变量,该变量有七个值:开盘价,收盘价,最高价和最低价,以及中间价,典型价格和加权平均价格。 在基于图表价格执行分析时,应使用前四个值,因为后三个是为指标计算而设计的。
  • 如果要在分析形态时使用指标,某些指标在计算时会将“应用价格”设置为 ENUM_APPLIED_PRICE 类型变量,即:ATR,CCI 和 RSI。

现在,我们研究一下应用程序界面的实现,以及搜索和分析美林形态的方法。

为了开发 GUI,我们使用由 CreateWindow() 方法组成的 CreateGUI() 方法来创建界面主窗口,并调用 CreateWindowSetting1() 对话框窗口选择一个研究时间帧。

//+------------------------------------------------------------------+
//| Create the program GUI                                           |
//+------------------------------------------------------------------+
bool CProgram::CreateGUI(void)
  {
//--- Create the panel
   if(!CreateWindow("Merrill Patterns"))
      return(false);
//--- Create the dialog window
   if(!CreateWindowSetting1("Setting dates"))
      return(false);
//--- Complete GUI creation
   CWndEvents::CompletedGUI();
   return(true);
  }

现在,我们看看每种方法的组成。 我们首先将注意力集中在界面主窗口上。 它的构成包括“分析(Analysis)”选卡的实现,该选卡由图例 4 中描述的元素组成。

//+------------------------------------------------------------------+
//| Analyze tab                                                      |
//+------------------------------------------------------------------+
//--- Create the pattern set buttons
   if(!CreatePatternSet(m_patterns,10,10))
      return(false);
//--- Timeframe header
   if(!CreateTFLabel(m_text_labels[1],10,105,0))
      return(false);
//--- Create the timeframe set buttons
   if(!CreateTimeframeSet(m_timeframes,10,125,0))
      return(false);
//--- Field for searching the symbol filter 
   if(!CreateSymbolsFilter(m_symb_filter1,m_request1,10,180,0))
      return(false);
//--- Create the button for selecting a date range
   if(!CreateDateRange(m_request3,280,180,0))
      return(false);
//--- Create the field for entering the profit threshold value
   if(!CreateThresholdValue(m_threshold1,400,180,100,0))
      return(false);
//--- Create the symbol table
   if(!CreateSymbTable(m_symb_table1,10,225,0))
      return(false);
//--- Create the result table
   if(!CreateTable1(m_table1,120,225,0))
      return(false);

以及图例 5 中描述的“设置(Settings)”选卡。

//+------------------------------------------------------------------+
//| Settings tab                                                     |
//+------------------------------------------------------------------+
//---
   if(!CreateButtonsGroup1(10,50))
      return(false);
//--- Text labels
   if(!CreateTextLabel(m_text_labels[0],10,100))
      return(false);
   if(!CreateTextLabel(m_text_labels[3],10,10))
      return(false);
//--- Input fields
   if(!CreateCoef(m_coef1,10,140,"K1",1))
      return(false);
   if(!CreateCoef(m_coef2,100,140,"K2",0.5))
      return(false);
   if(!CreateCoef(m_coef3,200,140,"K3",0.25))
      return(false);
   if(!CreateLanguageSetting(m_lang_setting,10,180,1))
      return(false);
//--- Status bar
   if(!CreateStatusBar(1,26))
      return(false);
//---
   return(true);
  }

可以在随附的源代码中找到每种应用方法添加接口元素的更详细实现。

实现对话框窗口以便设置临时样本的方法如下所示:

//+---------------------------------------------------------------------------------+
//| Create the dialog window for selecting the range of dates in the Analysis tab   |
//+---------------------------------------------------------------------------------+
bool CProgram::CreateWindowSetting1(const string caption_text)
  {
//--- Add the window pointer to the window array
   CWndContainer::AddWindow(m_window[2]);
//--- Coordinates
   int x=m_request3.X();
   int y=m_request3.Y()+m_request3.YSize();
//--- Properties
   m_window[2].XSize(372);
   m_window[2].YSize(230);
   m_window[2].WindowType(W_DIALOG);

//--- Create the form
   if(!m_window[2].CreateWindow(m_chart_id,m_subwin,caption_text,x,y))
      return(false);
//---
   if(!CreateCalendar(m_calendar1,m_window[2],10,25,D'01.01.2019',1))
      return(false);
   if(!CreateCalendar(m_calendar2,m_window[2],201,25,m_calendar2.Today(),1))
      return(false);
//---
   if(!CreateTimeEdit(m_time_edit1,m_window[2],10,200,"Time",1))
      return(false);
   if(!CreateTimeEdit(m_time_edit2,m_window[2],200,200,"Time",1))
      return(false);
//---
   return(true);
  }

现在,我们将注意力转向搜索、研究和评估形态的方法。 为达此目标,我们需要跟踪算法动作的整个序列。 首先,查看此算法开始的 MerrillPatterns.mq5 文件。

//--- Include the application class
#include "Program.mqh"
CProgram program;
//+------------------------------------------------------------------+
//| EA inputs                                                        |
//+------------------------------------------------------------------+
input ENUM_APPLIED_PRICE   Inp_Price1              =  PRICE_CLOSE;   // Applied price
input int                  Inp_ATR_Peroid          =  5;             // ATR Period
input int                  Inp_CCI_Peroid          =  5;             // CCI Period
input int                  Inp_DeM_Peroid          =  5;             // DeMarker Period
input int                  Inp_ForcePeriod         =  13;            // ForceIndex Period
input ENUM_MA_METHOD       Inp_ForceMAMethod       =  MODE_SMA;      // ForceIndex MA method
input ENUM_APPLIED_PRICE   Inp_ForceAppliedPrice   =  PRICE_CLOSE;   // ForceIndex Applied price
input ENUM_APPLIED_VOLUME  Inp_ForceAppliedVolume  =  VOLUME_TICK;   // ForceIndex Volumes
input int                  Inp_WPR_Period          =  5;             // WPR Period
input int                  Inp_RSI_Period          =  5;             // RSI Period
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(void)
  {
//---
   program.OnInitEvent();
//--- Set the trading panel
   if(!program.CreateGUI())
     {
      ::Print(__FUNCTION__," > Failed to create GUI!");
      return(INIT_FAILED);
     }
//---
   program.InitializePrice(Inp_Price1);
   program.InitializeATR(Inp_ATR_Peroid);
   program.InitializeCCI(Inp_CCI_Peroid);
   program.InitializeDeM(Inp_DeM_Peroid);
   program.InitializeForce(Inp_ForcePeriod,Inp_ForceMAMethod,Inp_ForceAppliedPrice,Inp_ForceAppliedVolume);
   program.InitializeWPR(Inp_WPR_Period);
   program.InitializeRSI(Inp_RSI_Period);
   return(INIT_SUCCEEDED);
  }

除了指标输入之外,在 OnInit() 部分中进行搜索图形外壳,然后在“属性”窗口中初始化数据集。所有方法都会将外部设置传递给内部变量。

//---
   void              InitializePrice(ENUM_APPLIED_PRICE price)    { m_applied_price=price;        }
   void              InitializeATR(int period)                    { m_atr_period=period;          }
   void              InitializeCCI(int period)                    { m_cci_period=period;          }
   void              InitializeDeM(int period)                    { m_dem_period=period;          }
   void              InitializeWPR(int period)                    { m_wpr_period=period;          }
   void              InitializeRSI(int period)                    { m_rsi_period=period;          }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::InitializeForce(int period,ENUM_MA_METHOD ma_method,ENUM_APPLIED_PRICE price,ENUM_APPLIED_VOLUME volume)
  {
   m_force_period=period;
   m_force_ma_method=ma_method;
   m_force_applied_price=price;
   m_force_applied_volume=volume;
  }
//+-----------------------------------------------------------------

之后,该应用程序准备就绪,而其余设置会被传递到所创建的图形界面。 我已经提到过,所执行计算会按从品种表中选择的货币品种来启动 (图例 4 位置 6)。 设置“趋势阈值”后也执行此操作 (图例 4 位置 3)。 这两个事件都会启动 ChangeSymbol1() 方法,以便开始收集检测到的数据,并准备进行分析。

//+------------------------------------------------------------------+
//| Select a symbol in the Analysis tab                              |
//+------------------------------------------------------------------+
bool CProgram::ChangeSymbol1(const long id)
  {
//--- Check the element ID
   if(id!=m_symb_table1.Id())
      return(false);
//--- Exit if the string is not highlighted
   if(m_symb_table1.SelectedItem()==WRONG_VALUE)
     {
      //--- Show full description of a symbol in the status bar
      m_status_bar.SetValue(0,"Symbol for analysis not selected");
      m_status_bar.GetItemPointer(0).Update(true);
      return(false);
     }
//--- Get a selected symbol
   string symbol=m_symb_table1.GetValue(0,m_symb_table1.SelectedItem());
//--- Show the full symbol description in the status bar
   string val=(m_lang_index==0)?"Выбранный символ: ":"Selected symbol: ";
   m_status_bar.SetValue(0,val+::SymbolInfoString(symbol,SYMBOL_DESCRIPTION));
   m_status_bar.GetItemPointer(0).Update(true);
//---
   GetResult(symbol);
   return(true);
  }

它的工作实质是从交易品种表中定义一个选定的交易品种,并将其值传递给状态栏和 GetResult() 方法。 我们来更深入地考察该方法,因为所有主要操作i都在其中进行。

//+------------------------------------------------------------------+
//| Handle pattern search results                                    |
//+------------------------------------------------------------------+
bool CProgram::GetResult(const string symbol)
  {
//--- Structure for evaluating pattern efficiency
   RATING_SET m_coef[];
//--- Figure types
   PATTERN_TYPE pattern_types[];
//---
   ArrayResize(pattern_types,33);
   for(int i=0;i<33;i++)
     {
      if(i==16)
         pattern_types[i]=-1;
      if(i<16)
         pattern_types[i]=PATTERN_TYPE(i);
      if(i>16)
         pattern_types[i]=PATTERN_TYPE(i-1);
     }
//--- Define selected timeframes
   GetTimeframes(m_timeframes,m_cur_timeframes);
   int total=ArraySize(m_cur_timeframes);
//--- Check for at least one selected timeframe
   if(total<1)
     {
      if(m_lang_index==0)
         MessageBox("Вы не выбрали рабочий таймфрейм!","Ошибка",MB_OK);
      else if(m_lang_index==1)
         MessageBox("You have not selected working timeframe!","Error",MB_OK);
      return(false);
     }
   int count=0;
   m_total_row=0;
//--- Remove all strings
   m_table1.DeleteAllRows();
//--- Get date range
   datetime start=StringToTime(TimeToString(m_calendar1.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit1.GetHours()+":"+(string)m_time_edit1.GetMinutes()+":00");
   datetime end=StringToTime(TimeToString(m_calendar2.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit2.GetHours()+":"+(string)m_time_edit2.GetMinutes()+":00");
//--- Check selected dates
   if(start>end || end>TimeCurrent())
     {
      if(m_lang_index==0)
         MessageBox("Неправильно выбран диапазон дат!","Ошибка",MB_OK);
      else if(m_lang_index==1)
         MessageBox("Incorrect date range selected!","Error",MB_OK);
      return(false);
     }
//--- 
   for(int k=0;k<33;k++)
     {
      if(k==16)
         continue;
      //--- Get selected patterns for analysis
      if(m_patterns[k].IsPressed())
        {
         ArrayResize(m_m_total,total);
         ArrayResize(m_coef,total);
         ZeroMemory(m_m_total);
         ZeroMemory(m_coef);
         count++;
         //--- Calculate by timeframes
         for(int j=0;j<total;j++)
           {
            double arr[];
            //--- Get data for analysis
            int copied=GetData(m_buttons_group1.SelectedButtonIndex(),symbol,m_cur_timeframes[j],start,end,arr);
            //---
            if(copied<9)
               MessageBox("Insufficient data for analysis","Error",MB_OK);
            for(int i=0;i<copied;i++)
              {
               if(i>copied-9)
                  continue;
               //--- Pattern search condition
               double A=arr[i];
               double B=arr[i+1];
               double C=arr[i+2];
               double D=arr[i+3];
               double E=arr[i+4];
               if(GetPatternType(A,B,C,D,E)==pattern_types[k])
                 {
                  m_m_total[j]++;
                  GetCategory(symbol,i+5,m_coef[j],m_cur_timeframes[j],m_threshold_value1);
                 }
              }
            //--- Add the result to the table
            AddRow(m_table1,m_patterns[k].LabelText(),m_coef[j],m_m_total[j],m_cur_timeframes[j]);
           }
        }
     }
//---
   if(count>0)
     {
      //---
      m_table1.DeleteRow(m_total_row);
      //--- Update the table
      m_table1.Update(true);
      m_table1.GetScrollVPointer().Update(true);
     }
   else
     {
      if(m_lang_index==0)
         MessageBox("Вы не выбрали паттерн!","Ошибка",MB_OK);
      else if(m_lang_index==1)
         MessageBox("You have not chosen a pattern!","Error",MB_OK);
     }
   return(true);
  }

首先,我需要在方法的开头解释输入的变量类型。第一个是 RATING_SET 结构。

struct RATING_SET
  {
   int               a_uptrend;
   int               b_uptrend;
   int               c_uptrend;
   int               a_dntrend;
   int               b_dntrend;
   int               c_dntrend;
  };

它包含 6 个 int 类型变量,这是必需要添加的数据,用来衡量确定形态后,价格移向指定方向的概率,以及价格会多快触及。 例如,假设我们有一个上行趋势,并且将趋势阈值按 5 位小数设置为 100 点,而价格在单个蜡烛中覆盖了该值。 在此情况下,a_uptrend 变量将接到数值 1。 如果价格在 2 根蜡烛内达到 100 点,则将该值传递给 b_uptrend 变量。 我们将在自己的方法中使用 m_coef[] 结构数组。

第二个变量类型是 PATTERN_TYPE。 这是一个收集所有美林形态类型的枚举。

//+------------------------------------------------------------------+
//| Figure type                                                      |
//+------------------------------------------------------------------+
enum PATTERN_TYPE
  {
   M1,M2,M3,M4,M5,M6,M7,M8,
   M9,M10,M11,M12,M13,M14,M15,M16,
   W1,W2,W3,W4,W5,W6,W7,W8,
   W9,W10,W11,W12,W13,W14,W15,W16
  };

方法中用到了 pattern_types[] 枚举数组。 接着进行检查 — 应用程序中选择哪个时间帧来操作。 此数据由 GetTimeframes() 方法处理。

//+------------------------------------------------------------------+
//| Get the array of selected timeframes                             |
//+------------------------------------------------------------------+
void  CProgram::GetTimeframes(CButton &buttons[],ENUM_TIMEFRAMES &timeframe[])
  {
   string tf[22]=
     {
      "M1","M2","M3","M4","M5","M6","M10","M12","M15","M20","M30",
      "H1","H2","H3","H4","H6","H8","H12","D1","W1","MN"
     };
   int j=0;
   ArrayResize(timeframe,22);
   for(int i=0;i<22;i++)
     {
      if(buttons[i].IsPressed())
        {
         timeframe[j]=StringToTimeframe(tf[i]);
         j++;
        }
     }
   ArrayResize(timeframe,j);
  }

该方法将其写入预先设置的 m_cur_timeframes[] 时间帧数组。 接着,获取操作时间范围。

在第一次循环中,我们开始检查是否按下了负责形态类型选定的按钮,并定义一组被研究形态。 在下一次循环中,于此前所选时间帧内研究每种形态。 在此阶段,出现了一个问题,应为形态和时间帧预先设置将哪些数据。 GetData() 方法为此负责,因为它定义了您在 EA 属性窗口中设置的设定,以及应用程序设置选项卡中的已用到指标(图例 5 位置 1)。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CProgram::GetData(int index,string symb,ENUM_TIMEFRAMES tf,datetime start,datetime end,double &arr[])
  {
//---
   int Handle=INVALID_HANDLE,copied;
//--- Close price
   if(index==0)
     {
      MqlRates rt[];
      ZeroMemory(rt);
      copied=CopyRates(symb,tf,start,end,rt);
      ArrayResize(arr,copied);
      for(int i=0;i<copied;i++)
        {
         arr[i]=rt[i].close;
         if(m_applied_price==PRICE_OPEN)
            arr[i]=rt[i].open;
         else if(m_applied_price==PRICE_CLOSE)
            arr[i]=rt[i].close;
         else if(m_applied_price==PRICE_HIGH)
            arr[i]=rt[i].high;
         else if(m_applied_price==PRICE_LOW)
            arr[i]=rt[i].low;
        }
      return(copied);
     }
//--- ATR
   if(index==1)
      Handle=iATR(symb,tf,m_atr_period,m_applied_price);
//--- CCI
   if(index==2)
      Handle=iCCI(symb,tf,m_cci_period,m_applied_price);
//--- DeMarker
   if(index==3)
      Handle=iDeMarker(symb,tf,m_dem_period);
//--- Force Index
   if(index==4)
      Handle=iForce(symb,tf,m_force_period,m_force_ma_method,m_force_applied_volume);
//--- WPR
   if(index==5)
      Handle=iWPR(symb,tf,m_wpr_period);
//--- RSI
   if(index==6)
      Handle=iRSI(symb,tf,m_rsi_period,m_applied_price);
//---
   if(Handle==INVALID_HANDLE)
     {
      Print("Failed to get indicator handle");
      return(-1);
     }
   copied=CopyBuffer(Handle,0,start,end,arr);
   return(copied);
  }

接收用于分析的数据之后,该算法将继续调用 GetPatternType() 方法,该方法用于在选定的时间帧内搜索所有先前设置的形态。 

//+------------------------------------------------------------------+
//| Define the patterns                                              |
//+------------------------------------------------------------------+
PATTERN_TYPE CProgram::GetPatternType(double A,double B,double C,double D,double E)
  {
//--- M1
   if(B>A && A>D && D>C && C>E)
      return(M1);
//--- M2
   if(B>A && A>D && D>E && E>C)
      return(M2);
//--- M3
   if(B>D && D>A && A>C && C>E)
      return(M3);
//--- M4
   if(B>D && D>A && A>E && E>C)
      return(M4);
//--- M5
   if(D>B && B>A && A>C && C>E)
      return(M5);
//--- M6
   if(D>B && B>A && A>E && E>C)
      return(M6);
//--- M7
   if(B>D && D>C && C>A && A>E)
      return(M7);
//--- M8
   if(B>D && D>E && E>A && A>C)
      return(M8);
//--- M9
   if(D>B && B>C && C>A && A>E)
      return(M9);
//--- M10
   if(D>B && B>E && E>A && A>C)
      return(M10);
//--- M11
   if(D>E && E>B && B>A && A>C)
      return(M11);
//--- M12
   if(B>D && D>C && C>E && E>A)
      return(M12);
//--- M13
   if(B>D && D>E && E>C && C>A)
      return(M13);
//--- M14
   if(D>B && B>C && C>E && E>A)
      return(M14);
//--- M15
   if(D>B && B>E && E>C && C>A)
      return(M15);
//--- M16
   if(D>E && E>B && B>C && C>A)
      return(M16);
//--- W1
   if(A>C && C>B && B>E && E>D)
      return(W1);
//--- W2
   if(A>C && C>E && E>B && B>D)
      return(W2);
//--- W3
   if(A>E && E>C && C>B && B>D)
      return(W3);
//--- W4
   if(A>C && C>E && E>D && D>B)
      return(W4);
//--- W5
   if(A>E && E>C && C>D && D>B)
      return(W5);
//--- W6
   if(C>A && A>B && B>E && E>D)
      return(W6);
//--- W7
   if(C>A && A>E && E>B && B>D)
      return(W7);
//--- W8
   if(E>A && A>C && C>B && B>D)
      return(W8);
//--- W9
   if(C>A && A>E && E>D && D>B)
      return(W9);
//--- W10
   if(E>A && A>C && C>D && D>B)
      return(W10);
//--- W11
   if(C>E && E>A && A>B && B>D)
      return(W11);
//--- W12
   if(E>C && C>A && A>B && B>D)
      return(W12);
//--- W13
   if(C>E && E>A && A>D && D>B)
      return(W13);
//--- W14
   if(E>C && C>A && A>D && D>B)
      return(W14);
//--- W15
   if(C>E && E>D && D>A && A>B)
      return(W15);
//--- W16
   if(E>C && C>D && D>A && A>B)
      return(W16);
   return(-1);
  }

在检测到形态时,利用 GetCategory() 方法对其进行评估。 在此使用先前定义的 RATING_SET 类型结构数组。

//+------------------------------------------------------------------+
//| Define the profit categories                                     |
//+------------------------------------------------------------------+
bool CProgram::GetCategory(const string symbol,const int shift,RATING_SET &rate,ENUM_TIMEFRAMES timeframe,int threshold)
  {
   MqlRates rt[];
   datetime start=StringToTime(TimeToString(m_calendar1.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit1.GetHours()+":"+(string)m_time_edit1.GetMinutes()+":00");
   start+=PeriodSeconds(timeframe)*shift;
   int copied=CopyRates(symbol,timeframe,start,4,rt);
//--- Get the data of previous candles
   if(copied<4)
      return(false);
   double high1,high2,high3,low1,low2,low3,close0,point;
   close0=rt[0].close;
   high1=rt[1].high;
   high2=rt[2].high;
   high3=rt[3].high;
   low1=rt[1].low;
   low2=rt[2].low;
   low3=rt[3].low;
   if(!SymbolInfoDouble(symbol,SYMBOL_POINT,point))
      return(false);

//--- Check for Uptrend
   if((int)((high1-close0)/point)>=threshold)
     {
      rate.a_uptrend++;
     }
   else if((int)((high2-close0)/point)>=threshold)
     {
      rate.b_uptrend++;
     }
   else if((int)((high3-close0)/point)>=threshold)
     {
      rate.c_uptrend++;
     }

//--- Check for Downtrend
   if((int)((close0-low1)/point)>=threshold)
     {
      rate.a_dntrend++;
     }
   else if((int)((close0-low2)/point)>=threshold)
     {
      rate.b_dntrend++;
     }
   else if((int)((close0-low3)/point)>=threshold)
     {
      rate.c_dntrend++;
     }
   return(true);
  }

处理完毕的评估数据将传递给 AddRow() 方法,该方法将计算概率值和绩效,并将其添加到结果表格中。

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::AddRow(CTable &table,string pattern_name,RATING_SET &rate,int found,ENUM_TIMEFRAMES timeframe)
  {
   int row=m_total_row;
   double p1,p2,k1,k2;
   int sum1=0,sum2=0;
   sum1=rate.a_uptrend+rate.b_uptrend+rate.c_uptrend;
   sum2=rate.a_dntrend+rate.b_dntrend+rate.c_dntrend;
//---
   p1=(found>0)?(double)sum1/found*100:0;
   p2=(found>0)?(double)sum2/found*100:0;
   k1=(found>0)?(m_k1*rate.a_uptrend+m_k2*rate.b_uptrend+m_k3*rate.c_uptrend)/found:0;
   k2=(found>0)?(m_k1*rate.a_dntrend+m_k2*rate.b_dntrend+m_k3*rate.c_dntrend)/found:0;
//---
   table.AddRow(row);
   table.SetValue(0,row,pattern_name);
   table.SetValue(1,row,(string)found);
   table.SetValue(2,row,TimeframeToString(timeframe));
   table.SetValue(3,row,DoubleToString(p1,2),2);
   table.SetValue(4,row,DoubleToString(p2,2),2);
   table.SetValue(5,row,DoubleToString(k1,2),2);
   table.SetValue(6,row,DoubleToString(k2,2),2);
   ZeroMemory(rate);
   m_total_row++;
  }

为了消除调用该应用程序会导致的有关潜在问题,以下视频显示了具有不同设置的计算示例。


测试美林形态的建议:

  • 为了令应用程序正常工作,我们需要下载指定测试交易品种的历史数据。
  • 不建议同时下载所有形态和所有时间帧的数据,因为处理结果可能需要很长时间。
  • 可能会引起困难的最常见情景都配发了提示。 这些情景包括不设置时间帧或形态,以及无效的日期。
  • 设置 EA 属性时要小心(图例 6)。 如果设置不清楚,请重新阅读本文。
  • 本文已两次涵盖了形态效率计算方法的主题。 指向该主题文章的链接。 请记住,您需要清楚了解设置选卡中的加权系数如何影响形态评估。

结束语

下面随附的文档包含所有论述的文件,这些文件应安置在相应的文件夹中。 为了正确操作,请将 MQL5 文件夹放置在终端的根目录中。 若要打开 MQL5 文件夹所在的终端根目录,请在 MetaTrader 5 终端中按下 Ctrl+Shift+D 组合键,或使用关联菜单,如下面的图例 7 中所示。


图例 7、 在 MetaTrader 5 终端根目录中打开 MQL5 文件夹


本文译自 MetaQuotes Software Corp. 撰写的俄文原文
原文地址: https://www.mql5.com/ru/articles/7022

附加的文件 |
MQL5.zip (1736.93 KB)
轻松快捷开发 MetaTrader 程序的函数库(第十四部分):品种对象 轻松快捷开发 MetaTrader 程序的函数库(第十四部分):品种对象

在本文中,我们将创建品种对象类,该类将成为创建品种集合的基本对象。 该类可令我们获取必要品种的数据,以便进一步进行分析和比较。

轻松快捷开发 MetaTrader 程序的函数库(第十三部分):帐户对象事件 轻松快捷开发 MetaTrader 程序的函数库(第十三部分):帐户对象事件

本文研究如何处理帐户事件,跟踪影响自动交易的重要帐户属性变化。 在开发帐户对象集合时,我们已在上一篇文章中实现了一些跟踪帐户事件的功能。

优化管理(第二部分):创建按键对象和附加逻辑 优化管理(第二部分):创建按键对象和附加逻辑

这篇文章是之前发表的关于创建优化管理图形界面的延续,本文探讨了附加组件的逻辑,将为 MetaTrader 5 终端创建一个包装器:它将使附加组件通过C#作为一个托管进程运行。此外,本文还探讨了对配置文件和安装文件的操作。应用逻辑分为两部分:第一部分描述了按下特定按键后调用的方法,第二部分描述了优化启动和管理。

MQL5.community - 用户手册 MQL5.community - 用户手册

如果你已经在本社区成功注册,那么你很可能会问:怎样在我发送的消息中插入图片?怎样格式化MQL5源代码?我的私信保存在哪?诸如此类的很多问题。本文我们为您准备了一些实用技巧,帮助你熟悉MQL5.community,并充分利用其提供的功能。