Исследования технических фигур Меррилла

Alexander Fedosov | 19 июля, 2019

Содержание

Введение

Первая попытка создания системы ценовых фигур была сделана Робертом Леви в 1971 году. Он применял пятиконечные фигуры колебания цены и затем тестировал их на значимость. Значительных результатов он не добился, но его работы через 10 лет продолжил Артур Меррилл. 

Он разбил фигуры на две категории с очертаниями английских букв M и W. В каждой категории было по 16 фигур. В каждой категории были свои подкатегории. Также Меррилл выделил 6 подкатегорий:

Насколько актуальны технические фигуры Меррилла в сегодняшних условиях — нам и предстоит выяснить. Для этого разработано приложение, которое позволит проводить тестирование. Помимо этого было бы интересно применить данную модель к различным типам данных — таким как цена закрытия, ее максимумы и минимумы и индикаторы осцилляторного типа.

Теория и область применения

Для того чтобы было предельно ясно каким образом и для каких входных данных мы будем применять модель технических фигур Меррилла, необходимо разобраться что они из себя представляют. Основные две категории — это фигуры, которые выглядят в виде английских букв M и W. Они называются M-паттернами и W-паттернами. В каждой из этих категорий находиться по 16 фигур.

На рис.1 представлены 16 М-паттернов внешне, так или иначе, напоминающих букву М. Как видно из картинки, различие их состоит во взаимном расположении пяти точек, составляющих паттерн.  


Рис.1 Визуальное представление М-паттернов

Чуть ниже на рис.2 16 W-паттернов, напоминающих букву W. Общий набор из этих двух групп мы будем находить на графиках цены и индикаторах, а также исследовать, оценивать и искать возможные закономерности.

Рис.2 Визуальное представление М-паттернов

Ведь суть любых паттернов и состоит в том, чтобы при появлении той или иной формации ожидать с определенной долей вероятности цены в ожидаемую сторону и получение на этом прибыли.

Чтобы было максимально ясно в какой области и как будут исследоваться технические фигуры Меррилла, приведем несколько примеров. На рис.3 представлен обычный линейный график цен валютной пары USDCAD на часовом таймфрейме. Сейчас данный тип представления используется нечасто, потому как более популярными стали японские свечи или бары.

Рис.3 Линейный график по ценам закрытия валютной пары USDCAD на часовом таймфрейме

В таком виде даже невооруженным глазом можно идентифицировать уже несколько технических фигур, описанных выше. Это будет первая область исследования — применение к линейному графику по ценам закрытия. Также проверим линейные графики по ценам открытия, максимумы и минимумы. 

Второй областью исследования будут индикаторы осцилляторного типа, такие как:

В качестве способа оценки технических фигур, которые буду применяться как к цене, так и к описанным выше индикаторам осцилляторного типа, будет метод предложенный в моей статье  Исследование методов свечного анализа (Часть I): Проверка существующих паттернов. Суть его состоит достаточна проста:


Разработка инструмента для тестирования

Перед разработкой инструменты нужно определиться, какие настройки и установки он должен будет содержать. Состоять он будет из панели с двумя вкладками Анализ и Настройки. А также будут использоваться настройки из окна настроек эксперта. Итого будет три основных раздела, в которых будут свои инструменты для работы с техническими фигурами. Теперь опишем какие настройки будут в каждом из разделов.

Вкладка Анализ содержит:

  1. Два набора кнопок для выбора типов тестируемых технических фигур. Также имеются кнопки All M и All W для быстрого выбора/снятие группы фигур М и W типа.
  2. Набор кнопок для выбора тестируемых таймфреймов и кнопка ALL для выбора/снятия всей группы кнопок.
  3. Поле ввода "Пороговое значение тренда в пунктах". Это значение прибыли в пунктах, которую должна достичь цена максимум за три свечи после идентификации исследуемой фигуры Меррилла.
  4. Кнопка, открывающая диалоговое окно с выбором Начальной и Конечной даты и времени тестирования. 
  5. Поле ввода с чекбоксом и кнопкой представляет собой фильтр для поиска нужных торговых инструментов. Имеет один пресет — Major. Он показывает мажорные валютные пары. Чекбокс отключает фильтр и показывает все доступные торговые инструменты.
  6. Выбранные с помощью фильтра торговые инструменты в таблице, по выбору которых из списка происходит анализ технических фигур.
  7. Таблица результатов, состоящая из семи колонок: 
    • Имя фигуры. В столбце показывается имя исследуемой фигуры по Мерриллу. Например M10 или W12.
    • Найдено. Количество найденных фигур заданного типа на установленной выборке.
    • Таймфрейм. Показывает таймфрейм, на котором происходил анализ заданной фигуры.
    • P, Uptrend. Это вероятность движения цены на значение "Порогового тренда в пунктах" после появления технической фигуры в восходящем направлении.
    • P, Dntrend. Это вероятность движения цены на значение "Порогового тренда в пунктах" после появления технической фигуры в нисходящем направлении.
    • K, UpTrend/K, DnTrend. Это коэффициент, описанный в моей статье  Исследование методов свечного анализа (Часть I): Проверка существующих паттернов. Смысл его в том, что он оценивает насколько быстро цена достигает заданного профита после появления исследуемой технической фигуры в восходящем и нисходящем направлении тренда.

На рис.4 представлена визуальная реализация всех описанных выше инструментов и параметров.

Рис.4 Реализация вкладки Анализ

Теперь рассмотрим вторую вкладку, это Настройки:

  1. Используемый индикатор. Предоставляет выбор к какому из индикаторов будет применятся поиск и исследование технических фигур Меррилла.
  2. Весовые коэффициенты. Используются при расчете описанных выше коэффициентов K, UpTrend/DnTrend. 
  3. Язык интерфейса. Выпадающий список выбора языка интерфейса: русский или английский.

Внешний вид вкладки с настройками на рис.5 ниже:

Рис.5 Реализация вкладки Настройки

Последний раздел настроек использует окно "Свойств эксперта" (горячая клавиша F7) и там установлены настройки применяемых индикаторов, которые перечислены под заголовком Используемый индикатор. На рис.6 показано окно последнего раздела настроек.

Рис.6 Окно настроек используемых индикаторов

При определении настроек в этом окне нужно учитывать следующие особенности:

Рассмотрим теперь программную реализацию интерфейса,  описанного выше приложения, а также методику поиска и анализа технических фигур Меррилла.

Для создания графического интерфейса используется метод CreateGUI(), который состоит из методов создающих главное окно интерфейса CreateWindow() и диалоговой окно CreateWindowSetting1() выбора временного диапазона для исследования.

//+------------------------------------------------------------------+
//| Создаёт графический интерфейс программы                          |
//+------------------------------------------------------------------+
bool CProgram::CreateGUI(void)
  {
//--- Создание панели
   if(!CreateWindow("Merrill Patterns"))
      return(false);
//--- Создание диалогового окна
   if(!CreateWindowSetting1("Настройки диапазона дат"))
      return(false);
//--- Завершение создания GUI
   CWndEvents::CompletedGUI();
   return(true);
  }

Теперь посмотрим из чего состоят каждый из этих методов. Первым выберем главное окно интерфейса. Оно состоит из реализации вкладки Анализ, состоящей из элементов, описанных на рис.4.

//+------------------------------------------------------------------+
//| Вкладка Analyze                                                  |
//+------------------------------------------------------------------+
//--- Создание кнопок набора паттернов
   if(!CreatePatternSet(m_patterns,10,10))
      return(false);
//--- Заголовок таймфреймов
   if(!CreateTFLabel(m_text_labels[1],10,105,0))
      return(false);
//--- Создание кнопок набора таймфреймов
   if(!CreateTimeframeSet(m_timeframes,10,125,0))
      return(false);
//--- Поле поиска фильтра символом 
   if(!CreateSymbolsFilter(m_symb_filter1,m_request1,10,180,0))
      return(false);
//--- Создание кнопки выбора диапазона дат
   if(!CreateDateRange(m_request3,280,180,0))
      return(false);
//--- Создаёт поле ввода порогового значения прибыли
   if(!CreateThresholdValue(m_threshold1,400,180,100,0))
      return(false);
//--- Создание таблицы символов
   if(!CreateSymbTable(m_symb_table1,10,225,0))
      return(false);
//--- Создание таблицы результатов
   if(!CreateTable1(m_table1,120,225,0))
      return(false);

И из вкладки Настройки, описанной на рис.5

//+------------------------------------------------------------------+
//| Вкладка Settings                                                 |
//+------------------------------------------------------------------+
//---
   if(!CreateButtonsGroup1(10,50))
      return(false);
//--- Текстовые метки
   if(!CreateTextLabel(m_text_labels[0],10,100))
      return(false);
   if(!CreateTextLabel(m_text_labels[3],10,10))
      return(false);
//--- Поля ввода
   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);
//--- Статусная строка
   if(!CreateStatusBar(1,26))
      return(false);
//---
   return(true);
  }

Более подробную реализацию каждого из применяемых методов, реализующих элементы интерфейса, можно будет посмотреть в исходных кодах в конце статьи.

Метод, реализующий диалоговое окно установки временной выборки, выглядит так:

//+------------------------------------------------------------------+
//| Создаёт диалоговой окно выбора диапазона дат во вкладке Анализ   |
//+------------------------------------------------------------------+
bool CProgram::CreateWindowSetting1(const string caption_text)
  {
//--- Добавим указатель окна в массив окон
   CWndContainer::AddWindow(m_window[2]);
//--- Координаты
   int x=m_request3.X();
   int y=m_request3.Y()+m_request3.YSize();
//--- Свойства
   m_window[2].XSize(372);
   m_window[2].YSize(230);
   m_window[2].WindowType(W_DIALOG);

//--- Создание формы
   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,"Время",1))
      return(false);
   if(!CreateTimeEdit(m_time_edit2,m_window[2],200,200,"Время",1))
      return(false);
//---
   return(true);
  }

Теперь более подробно коснемся методик исследования технических фигур, их поиска и оценки. Для этого отследим всю последовательность действий этого алгоритма. Для начала рассмотрим файл MerrillPatterns.mq5 в котором стартует этот алгоритм.

//--- Подключение класса приложения
#include "Program.mqh"
CProgram program;
//+------------------------------------------------------------------+
//| Входные параметры эксперта                                       |
//+------------------------------------------------------------------+
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();
//--- Установим торговую панель
   if(!program.CreateGUI())
     {
      ::Print(__FUNCTION__," > Не удалось создать графический интерфейс!");
      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;
  }
//+-----------------------------------------------------------------

После этого приложение готово к использованию и все остальные настройки передаются созданному графическому интерфейсу. Чуть выше говорилось о том, что запуск расчета происходит по выбору валютного инструмента из таблицы символов(поз.6 рис.4), также это происходит по окончанию ввода "Порогового значение тренда в пунктах" (поз.3 рис.4). Оба этих события запускаю один и тот же метод ChangeSymbol1() для начала сбора установленных данных, чтобы подготовить их для начала анализа.

//+------------------------------------------------------------------+
//| Выбор символа во вкладке Анализ                                  |
//+------------------------------------------------------------------+
bool CProgram::ChangeSymbol1(const long id)
  {
//--- Проверка идентификатора элемента
   if(id!=m_symb_table1.Id())
      return(false);
//--- Выйти, если строка не выделена
   if(m_symb_table1.SelectedItem()==WRONG_VALUE)
     {
      //--- Показать полное описание символа в статусной строке
      m_status_bar.SetValue(0,"Не выбран символ для анализа");
      m_status_bar.GetItemPointer(0).Update(true);
      return(false);
     }
//--- Получим выбранный символ
   string symbol=m_symb_table1.GetValue(0,m_symb_table1.SelectedItem());
//--- Показать полное описание символа в статусной строке
   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(). Рассмотрим его более подробно, потому как вся основная работа происходит именно в этом методе.

//+------------------------------------------------------------------+
//| Обработка результатов поиска паттернов                           |
//+------------------------------------------------------------------+
bool CProgram::GetResult(const string symbol)
  {
//--- Структура для оценки эффективности фигур
   RATING_SET m_coef[];
//--- Типы фигур
   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);
     }
//--- Определяем выбранные таймфреймы
   GetTimeframes(m_timeframes,m_cur_timeframes);
   int total=ArraySize(m_cur_timeframes);
//--- Проверка на хотя бы один выбранный таймфрейм
   if(total<1)
     {
      if(m_lang_index==0)
         MessageBox("Вы не выбрали рабочий таймфрейм!","Ошибка",MB_OK);
      else if(m_lang_index==1)
         MessageBox("You have not selected a working timeframe!","Error",MB_OK);
      return(false);
     }
   int count=0;
   m_total_row=0;
//--- Удалить все строки
   m_table1.DeleteAllRows();
//--- Получение диапазона дат
   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");
//--- Проверка правильности установленных дат
   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;
      //--- Получение выбранных фигур для анализа
      if(m_patterns[k].IsPressed())
        {
         ArrayResize(m_m_total,total);
         ArrayResize(m_coef,total);
         ZeroMemory(m_m_total);
         ZeroMemory(m_coef);
         count++;
         //--- Расчет по таймфреймам
         for(int j=0;j<total;j++)
           {
            double arr[];
            //--- Получение данных для анализа
            int copied=GetData(m_buttons_group1.SelectedButtonIndex(),symbol,m_cur_timeframes[j],start,end,arr);
            //---
            if(copied<9)
               MessageBox("Недостаточно данных для анализа","Ошибка",MB_OK);
            for(int i=0;i<copied;i++)
              {
               if(i>copied-9)
                  continue;
               //--- Условие поиска паттерна
               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);
                 }
              }
            //--- Добавление результат в таблицу
            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);
      //--- Обновить таблицу
      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 и необходима для записи в них результатов о том, как часто после идентификации фигуры цена шла в заданном направлении и как быстро ее достигала. Например для восходящего тренда и установленного порогового значение тренда в пунктах в 100 пукнтов на 5-знаке при нахождении фигуры и преодолении цены этого значения за одну свечу в переменную a_uptrend запишется единица, если цена достигла 100 пунктов за 2 свечи, то в переменную b_uptrend. В нашем метода мы будем использовать массив таких структур m_coef[].

Второй тип переменной — PATTERN_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().

//+------------------------------------------------------------------+
//| Получение массива выбранных таймфреймов                          |
//+------------------------------------------------------------------+
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(), который определяет какие настройки вы задали в окне свойств эксперта, а также Используемый индикатор(поз.1 рис.5) в вкладке настройки приложения.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CProgram::GetData(int index,string symb,ENUM_TIMEFRAMES tf,datetime start,datetime end,double &arr[])
  {
//---
   int Handle=INVALID_HANDLE,copied;
//--- Цена закрытия
   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("Не удалось получить хендл индикатора");
      return(-1);
     }
   copied=CopyBuffer(Handle,0,start,end,arr);
   return(copied);
  }

После того, как данные для анализа получены, алгоритм переходит к методу GetPatternType() с помощью которого производится поиск всех заданных до этого паттернов на выбранных таймфреймах. 

//+------------------------------------------------------------------+
//| Распознавание паттернов                                          |
//+------------------------------------------------------------------+
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.

//+------------------------------------------------------------------+
//| Определение категорий прибыли                                    |
//+------------------------------------------------------------------+
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);
//--- Получаем данные предыдущих свечей
   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);

//--- Проверка на 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++;
     }

//--- Проверка на 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++;
  }

Для снятие возможных вопросов в использовании приложения в видео ниже приведены примеры расчета с различными настройками.


Рекомендации при тестировании технических фигур Меррилла:

Заключение

В конце статьи приложен архив со всеми перечисленными файлами, отсортированными по папкам. Поэтому для корректной работы достаточно положить папку  MQL5 в корень терминала. Для того чтобы найти корень терминала, в котором находится папка MQL5, нужно в MetaTrader 5 нажать комбинацию клавиш  Ctrl+Shift+D или воспользоваться контекстным меню, как показано на рис.14 ниже.


Рис.7 Поиск папки MQL5 в корне терминала MetaTrader 5