English 中文 Español Deutsch 日本語 Português
Метод площадей

Метод площадей

MetaTrader 5Торговые системы | 26 февраля 2016, 15:02
6 407 10
Vladimir Karputov
Vladimir Karputov
главление


Введение

Как правильно установить индикаторы и советники из архивов в конце статьи: архивы "Indicators.zip" и "Experts.zip" нужно разархивировать в <каталог данных>\MQL5\

Описание метода площадей было впервые опубликовано в 2004 году [1]. Метод интересен необычным взглядом на данные индикатора RSI: в нем предлагается оценивать площадь, которую осциллятор рисует выше/ниже линии 50 с момента последнего ее пересечения. С 2004 года рынки сильно изменились, был создан язык MQL5 – а значит, пришло время проверить стратегию на языке MQL5 и на современном рынке.


1. Общепринятая методика оценки показателей индикатора RSI

Обычный метод торговли по сигналам индикатора RSI – это оценка показателей индикатора на предмет перекупленности/перепроданности, поиск дивергенции между показаниями индикатора и ценой, разворот после посещения индикатором зон перекупленности/перепроданности, неудавшийся размах. Таким образом, для технического анализа осциллятора RSI применяется минимум четыре сигнала, и это усложняет систему принятия решений.

При этом мы знаем, что индикатор RSI не может находиться в зоне перекупленности (выше линии 70)/перепроданности (ниже линии 30) очень долго – он обязательно вернётся и пересечёт среднюю линию 50:

RSI

Рис. 1. Осциллятор RSI всегда возвращается из зон перекупленности/перепроданности

На рисунке 1 видно, что суммарное время нахождения осциллятора в зонах перепроданности/перекупленности, по сравнению с остальным временем, очень мало. Также после посещения зон перекупленности/перепроданности RSI пересекает среднюю линию 50. Именно знание того, что осциллятор RSI обязательно возвращается и пересекает линию 50, а также необходимость упрощения технического анализа показаний осциллятора RSI стали основой для разработки метода площадей.


2. Метод площадей

Метод площадей предлагает оценивать показания осциллятора RSI по одному показателю: площади, которая образована осциллятором выше/ниже линии 50. Эта величина и будет характеризовать степень перекупенности/перепроданности:

area_figure

Рис. 2. Метод площадей — оценка площади над/под линией 50

Сигнал к открытию позиции в этом случае — величина площади над/под линией 50 с момента последнего ее пересечения индикатором RSI.   

  • При долгом нахождении RSI над линией 50 и после преодоления определённой величины площади (пусть это будет значение площади, равное 300) будет открыта позиция SELL: 

SELL signal 

Рис. 3. Сигнал на открытие SELL позиции, как только площадь стала равна 300

  • Соответственно, при долгом нахождении RSI под линией 50 и после преодоления определённой величины площади будет открыта позиция BUY.

Сигналом к закрытию позиции служит пересечение осциллятором RSI линии 50 с последующим образованием локального максимума/минимума и откат от него на величину 4% шкалы.

  • Например, при долгом нахождении над линией 50 в какой-то момент мы имеем открытую позицию SELL. Затем значение индикатора начинает уменьшаться и достигает, допустим линии 40, после чего значение индикатора начинает увеличиваться (то есть, образуется локальный минимум). Когда значение индикатора достигнет линии 44, это и станет сигналом к закрытию позиции: 

Close SELL signal 

Рис. 4. Сигнал на закрытие SELL позиции после образования локального минимума и последующего отката на 4%

  • Аналогичная логика возникает при долгом нахождении индикатора под линией 50, только в этом случае мы ждем образования локального максимума.

Для визуализации площади над/под линией 50 нам поможет индикатор RSIAreaIndicator.  


3. Индикатор RSIAreaIndicator_v1

Индикатор RSIAreaIndicator построен на базе осциллятора RSI. Основное отличие состоит в том, что индикатор RSIAreaIndicator имеет два буфера. Один буфер имеет стиль построения DRAW_HISTOGRAM, а второй — DRAW_LINE. Значения буферов получают по формуле

formula RSIAreaIndicaor

Вид индикатора RSIAreaIndicator version 1.00:

RSIAreaIndicator 

Рис. 5. Индикатор RSIAreaIndicator _v1

 

3.1. Создаём заготовку индикатора 

Созданные вами пользовательские индикаторы я рекомендую размещать в отдельной папке. В моем случае такая папка называется "MyInd". Чтобы начать писать индикатор, нужно создать его заготовку в редакторе кода MetaEditor с использованием помощника MQL5 Wizard. Первые шаги по созданию заготовки индикатора я собрал в этом видео:


Полученную заготовку вы можете просмотреть в конце статьи — индикатор сохранён под именем "RSIAreaIndicatorStep1.mq5". 

3.2. Заполнение "шапки" индикатора

Следующий шаг —  это добавление описания индикатора. Впоследствии его всегда можно будет увидеть в свойствах индикатора во вкладке "Общие". Весь добавленный код в статье будет выделяться цветом для визуального восприятия:

#property version   "1.00"
#property description   "The indicator displays area RSI over/under line 50"
#property indicator_separate_window

Как мы помним, индикатор RSIAreaIndicator имеет два индикаторных буфера. Кроме них, нам понадобится еще один, вспомогательный. Таким образом, всего в индикаторе будет использовано три буфера. Начнём редактирование кода с "шапки" индикатора:

#property indicator_separate_window
#property indicator_buffers 3
#property indicator_plots   2
#property indicator_type1   DRAW_HISTOGRAM
#property indicator_type2   DRAW_LINE
#property indicator_color1  clrGray
#property indicator_color2  clrGray
//--- input parameters
input int      ExtRSIPeriod=13;

Теперь нужно объявить три массива, в которых будут храниться значения индикаторных и вспомогательного буферов:

//--- input parameters
input int      ExtRSIPeriod=13;
//---- buffers
double ExtMapBuffer1[];
double ExtMapBuffer2[];
double ExtMapBuffer3[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |

Далее, так как наш индикатор RSIAreaIndicator рассчитывается на базе стандартного индикатора RSI и нам нужно будет получать значения индикатора, потребуется переменная, в которой будет храниться хэндл индикатора Relative Strength Index:

double ExtMapBuffer3[];
//--- variable for storing the handle of the iRSI indicator 
int    handle;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |

Осталось объявить в "шапке" три служебных переменных. В переменной name будет храниться имя символа, на котором запущен индикатор, в переменной short_name — короткое имя индикатора, а в переменной bars_calculated — количество посчитанных баров в индикаторе RSI:

int    handle;
//--- variable for storing 
string name=Symbol();
//--- name of the indicator on a chart 
string short_name;
//--- we will keep the number of values in the Relative Strength Index indicator 
int    bars_calculated=0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |

"Шапка" индикатора заполнена, теперь можно приступить к редактированию функции OnInit().

3.3. Редактирование функции OnInit() индикатора 

Так как мы пишем индикатор, то нам нужно связать наши индикаторные буферы с объявленными ранее динамическими массивами типа double:

int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,ExtMapBuffer1,INDICATOR_DATA);
   SetIndexBuffer(1,ExtMapBuffer2,INDICATOR_DATA);
   SetIndexBuffer(2,ExtMapBuffer3,INDICATOR_CALCULATIONS);
//---
   return(INIT_SUCCEEDED);

Затем, сразу же после связывания буферов и массивов, установим индексацию элементов массива как в таймсериях (рекомендую просмотреть хороший пример справки по  ArraySetAsSeries). Самый правый элемент массивов будет иметь индекс "0":

   SetIndexBuffer(1,ExtMapBuffer2,INDICATOR_DATA);
   SetIndexBuffer(2,ExtMapBuffer3,INDICATOR_CALCULATIONS);
   ArraySetAsSeries(ExtMapBuffer1,true);
   ArraySetAsSeries(ExtMapBuffer2,true);
   ArraySetAsSeries(ExtMapBuffer3,true);
//---
   return(INIT_SUCCEEDED);

Теперь установим точность отображения — индикатор будет отображаться с двумя знаками после запятой:

   ArraySetAsSeries(ExtMapBuffer3,true);
//--- set accuracy 
   IndicatorSetInteger(INDICATOR_DIGITS,2);
//---
   return(INIT_SUCCEEDED);

 В функции OnInit() осталось ещё получить хэндл индикатора RSIndex, заполнить переменную short_name и присвоить нашему индикатору короткое имя:

   ArraySetAsSeries(ExtMapBuffer2,true);
   ArraySetAsSeries(ExtMapBuffer3,true);
//--- set accuracy 
   IndicatorSetInteger(INDICATOR_DIGITS,2);
   handle=iRSI(name,0,ExtRSIPeriod,PRICE_CLOSE);
//--- if the handle is not created 
   if(handle==INVALID_HANDLE)
     {
      //---  notify about failure and output error code
      PrintFormat("Failed to create handle of the iRSI indicator for the symbol %s/%s, error code %d",
                  name,
                  EnumToString(PERIOD_CURRENT),
                  GetLastError());
      //--- the indicator is stopped early 
      return(INIT_FAILED);
     }
//--- show the symbol/timeframe the RSI Area Indicator is calculated for 
   short_name=StringFormat("RSIArea(%d)",ExtRSIPeriod);
   IndicatorSetString(INDICATOR_SHORTNAME,short_name);
//--- normal initialization of the indicator 
   return(INIT_SUCCEEDED);

Итак "шапку" индикатора и  функцию OnInit() мы заполнили. Отредактированный код Вы можете просмотреть в конце статьи — индикатор сохранён под именем "RSIAreaIndicatorStep2.mq5".  

3.4. Создание вспомогательной функции индикатора 

Для работы индикатора RSIAreaIndicator нужно при каждом заходе в функцию OnCalculate() получать данные индикатора RSI. Не менее важно обеспечить удобство чтения кода и разделить функционал программы. Поэтому вспомогательный код получения значений RSI и копирование этих значений в один из буферов RSIAreaIndicator вынесены в отдельную функцию FillArrayFromBuffer(). Ее мы разместим после OnCalculate(). Значения копируются при помощи функции CopyBuffer.

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+ 
//| Filling indicator buffers from the iRSI indicator                | 
//+------------------------------------------------------------------+ 
bool FillArrayFromBuffer(double &rsi_buffer[],  // indicator buffer of Relative Strength Index values 
                         int ind_handle,        // handle of the iRSI indicator 
                         int amount             // number of copied values 
                         )
  {
//--- reset error code 
   ResetLastError();
//--- fill a part of the iRSIBuffer array with values from the indicator buffer that has 0 index 
   if(CopyBuffer(ind_handle,0,0,amount,rsi_buffer)<0)
     {
      //--- output error code if copying fails 
      PrintFormat("Failed to copy data from the iRSI indicator, error code %d",GetLastError());
      //--- quit with zero result - it means that the indicator is considered as not calculated 
      return(false);
     }
//--- all in order
   return(true);
  }
//+------------------------------------------------------------------+

3.5. Создание основного рабочего кода индикатора 

Основной рабочий код (или логика) индикатора RSIAreaIndicator расположен в функции OnCalculate(). Здесь объявляется главная переменная — values_to_copy. Впоследствии переменная values_to_copy будет хранить количество значений, которое нужно копировать из индикатора RSI.

                const int &spread[])
  {
//--- number of values copied from the iRSI indicator 
   int values_to_copy;
//--- determine the number of values calculated in the indicator 
   int calculated=BarsCalculated(handle);
   if(calculated<=0)
     {
      PrintFormat("BarsCalculated() returned %d, error code %d",calculated,GetLastError());
      return(0);
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }

Расчёт значения values_to_copy:

      PrintFormat("BarsCalculated() returned %d, error code %d",calculated,GetLastError());
      return(0);
     }
//--- if it is the first calculation of the indicator or if the number of values in the iRSI indicator changed 
//---calculate 
   if(prev_calculated==0 || calculated!=bars_calculated || rates_total>prev_calculated+1)
     {
      //--- if the iRSIBuffer array is greater than the number of values in the iRSI indicator for symbol/period, then we don't copy everything  
      //--- otherwise, we copy less than the size of indicator buffers 
      if(calculated>rates_total) values_to_copy=rates_total;
      else                       values_to_copy=calculated;
     }
   else
     {
      //--- it means that the indicator is calculated not for the first time, but since the last call of OnCalculate()  
      //--- not more than one bar is added for calculation 
      values_to_copy=(rates_total-prev_calculated)+1;
     }
//--- return value of prev_calculated for next call
   return(rates_total);

Почему переменная values_to_copy рассчитывается именно так? В MQL5 индикаторе элементы массивов (time[], open[], high[], low[], close[], tick_volume[], volume[] и spread[]), передаваемые в функцию OnCalculate(), имеют индексацию от начала массива к концу. Вот как это выглядит на примере графика:

Array Not Series

Рис. 6. Индексация элементов массива, если массив не таймсерия 

То есть в массиве, не являющемся таймсерией, самый правый элемент будет иметь максимальный индекс. Это нужно учитывать при математических вычислениях.

Теперь, когда значение переменной values_to_copy рассчитано, можно вызывать вспомогательную функцию FillArrayFromBuffer() и заполнить значениями индикаторные буферы ExtMapBuffer1[] и ExtMapBuffer2[]:

      //--- for calculation not more than one bar is added 
      values_to_copy=(rates_total-prev_calculated)+1;
     }
//--- fill the array with values of the iRSI indicator 
//--- not ready 
   if(!FillArrayFromBuffer(ExtMapBuffer3,handle,values_to_copy)) return(0);
//---
   for(int i=0;i<values_to_copy;i++)
     {
      ExtMapBuffer1[i]=ExtMapBuffer2[i]=ExtMapBuffer3[i]-50.0;
     }
//--- memorize the number of values in the Relative Strength Index indicator 
   bars_calculated=calculated;
//--- return value of prev_calculated for next call
   return(rates_total);

Индикатор RSIAreaIndicator version 1.00 готов. Его можно скачать в конце статьи под именем "RSIAreaIndicatorv1.mq5". Теперь можно приступить к написанию советника RSIAreaEA version 1.00, который будет торговать по методу площадей.


4. Советник RSIAreaExpert version 1.00

Как и в случае с индикаторами, я рекомендую созданные советники размещать в отдельной папке. Например, моя папка для советников называется "MyExp". По аналогии с индикатором создадим заготовку советника RSIAreaExpert_v1. Важное уточнение: на одном из шагов нужно снять все галочки:

Setting Expert 

 Рис. 7. Настройки при создании советника

Полученную заготовку советника вы можете просмотреть в конце статьи — советник сохранён под именем "RSIAreaExpert_v1_Step1.mq5". 

4.1. Редактирование "шапки" советника

Добавим описание советника. Оно будет видно во вкладке "Общие" советника:

#property version   "1.00"
#property description "EA trades on \"Method areas\""
//+------------------------------------------------------------------+
//| Expert initialization function                                   |

Описание не всегда хочется сразу заполнять, однако впоследствии вам очень поможет выработанное раз и навсегда правило: "При создании программы первое действие — это создание описания". 

Советник будет использовать стандартную библиотеку — класс CTrade для выполнения торговых операций. Для этого нужно будет подключить класс CTrade и объявить переменную my_trade:

#property description "EA trades on \"Method areas\""
#include <Trade\Trade.mqh>
//--- global variables
CTrade      my_trade;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |

Также добавим три переменных (для хранения хэндла индикатора Relative Strength Index, для хранения текущей рассчитанной площади и одну вспомогательную):

//--- global variables
CTrade      my_trade;
int         handle;     // variable for storing the handle of the iRSI indicator 
double      RSIArea;    // the calculated area
double      RSIOpen;    // the auxiliary variable
//+------------------------------------------------------------------+
//| Expert initialization function                                   |

И последний шаг редактирования "шапки" советника — добавление входных параметров:

double      RSIOpen;    // the auxiliary variable
//--- input parametres
input int   ExtRSIPeriod=13;    // period of RSI
input int   AreaCondition=300;  // area
input ENUM_TIMEFRAMES period=PERIOD_M15;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |

Переменная period введена исключительно для удобства тестирования в тестере стратегий — таким образом можно задавать разные диапазоны тестируемых периодов:

variable period

Рис. 8. Переменная period позволяет тестировать советник в широком диапазоне периодов 

"Шапка" советника заполнена, теперь очередь функции OnInit(). В OnInit() будет только одна операция — получение хэндла индикатора на текущем символе (Symbol()) и на заданном периоде (period):

int OnInit()
  {
//---
   handle=iRSI(Symbol(),period,ExtRSIPeriod,PRICE_CLOSE);
//--- if the handle is not created 
   if(handle==INVALID_HANDLE)
     {
      //--- tell about the failure and output the error code 
      PrintFormat("Failed to create handle of the iRSI indicator for the symbol %s/%s, error code %d",
                  Symbol(),
                  EnumToString(period),
                  GetLastError());
      //--- the indicator is stopped early 
      return(INIT_FAILED);
     }
//---
   return(INIT_SUCCEEDED);
  }

Все эти изменения в процессе редактирования советника можно увидеть в файле "RSIAreaExpert_v1_Step2.mq5".

4.2. Вспомогательная функция RSIAreaFunc 

Функция определения площади RSIAreaFunc() состоит из нескольких функциональных частей. Будем добавлять функционал постепенно. Первый блок (пояснения к нему — после кода):

void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+
//| Area calculation                                                 |
//+------------------------------------------------------------------+
double RSIAreaFunc(int &RSIAreaShift,int BeginShift)
  {
   int    shift,limit;
   double rsivalue,result;
//--- get current RSI 
   limit=Bars(Symbol(),period)-ExtRSIPeriod;
   if(limit>100)
      limit=100;
   double   arr_rsi[];
   ArrayResize(arr_rsi,limit);
   ArraySetAsSeries(arr_rsi,true);
   if(CopyBuffer(handle,0,0,limit,arr_rsi)==-1)
     {
      Print("CopyBuffer from iRSI failed, no data");
      return(0);
     }
   return(result);
  }

Переменная limit отвечает за то, сколько значений индикатора iRSI мы будем копировать в массив arr_rsi[] при помощи CopyBuffer. Ограничим переменную limit значением "100" — то есть, всегда будем копировать последние 100 значений индикатора iRSI. Эти изменения редактирования советника можно увидеть в файле "RSIAreaExpert_v1_Step3.mq5".

4.3 Проверочный код работы функции CopyBuffer 

Если Вам не совсем понятно, как работает функция CopyBuffer и какие значения содержатся в массиве под индексом "0", то можно написать простой проверочный код: в функции OnTick() напишем вызов вспомогательной функции RSIAreaFunc().

void OnTick()
  {
//---
   static int RSIAreaShift=0;
   RSIAreaFunc(RSIAreaShift,0);
  }
//+------------------------------------------------------------------+
//| Area calculation                                                 |
//+------------------------------------------------------------------+
double RSIAreaFunc(int &RSIAreaShift,int BeginShift)

В конце первого блока функции RSIAreaFunc() допишем вывод комментария — значения начального и конечного элементов массива arr_rsi[]:

   if(CopyBuffer(handle,0,0,limit,arr_rsi)==-1)
     {
      Print("CopyBuffer from iRSI failed, no data");
      return(0);
     }
//---
   Comment("arr_rsi[",limit-1,"]=",DoubleToString(arr_rsi[limit-1],2),
           "; arr_rsi[0]=",DoubleToString(arr_rsi[0],2));
   return(result);
  }

Это проверочный код, он внесен только в файл RSIAreaExpert_v1_Step3_check.mq5, и его не будет в основном эксперте. Для проверки выполним следующее:

  • скомпилировать (если это ещё не сделано ранее) файл советника RSIAreaExpert_v1_Step3_check.mq5;
  • открыть новый график любого инструмента и поменять для него таймфрейм на M15 (так как по умолчанию во входных параметрах переменная period=PERIOD_M15);
  • вставить на график индикатор RSI (меню "Вставка" -> "Индикаторы" -> "Осцилляторы" ->  "Relative Strength Index" с такими настройками: "Период" 13 и "Применить к" Close);
  • присоединить к графику советник RSIAreaExpert_v1_Step3_check.mq5.

На графике сразу станет видно, что значение элемента с индексом "0" в массиве arr_rsi  соответствует значению индикатора RSI на самом правом баре:

  check function CopyBuffer 

Рис. 9. Проверка работы функции CopyBuffer  

4.4. Продолжение редактирования вспомогательной функции

Следующий блок функции RSIAreaFunc():

   if(CopyBuffer(handle,0,0,limit,arr_rsi)==-1)
     {
      Print("CopyBuffer from iRSI failed, no data");
      return(0);
     }

   result=arr_rsi[0]-50.0; // values from the bar that has 0 index

   for(shift=BeginShift+1;shift<limit;shift++)
     {
      rsivalue=arr_rsi[shift]-50;
      if((result>0 && rsivalue<-3) || (result<0 && rsivalue>3))
        {
         RSIAreaShift=shift;
         break;
        }
      result+=rsivalue;
     }
   return(result);
  }

Сначала переменной result присваивается значение индикатора RSI на самом правом баре минус 50. Затем идёт цикл по массиву arr_rsi, начиная с элемента с индексом "1" и до элемента с индексом limit-1. В этом цикле проверяется условие: "Было ли пересечение нулевой линии". Если пересечение было, то индекс бара (считаем справа налево) запоминается в переменную RSIAreaShift.

4.5. Функция OnTick() советника

Мы закончили редактировать вспомогательную функцию RSIAreaFunc(). Теперь очередь главной торговой функции OnTick(). Добавим следующий код в OnTick(): 

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   static int RSIAreaShift=0;
   int      shift;
   double   RSICurrent,RSILocalMin,RSILocalMax,value;
   double   arr_rsi[1],rsi;
   MqlTick  last_tick;
//---
   if(CopyBuffer(handle,0,0,1,arr_rsi)==-1)
     {
      Print("CopyBuffer from iRSI failed, no data");
      return;
     }
   rsi=arr_rsi[0];
//--- 
   if(!SymbolInfoTick(Symbol(),last_tick))
      Print("SymbolInfoTick() failed, error = ",GetLastError());
//---
  }

При помощи уже знакомой нам функции CopyBuffer получаем одно значение индикатора RSI на самом правом баре и далее присваиваем это значение переменной rsi -. Дальше в коде мы будем не раз обращаться к этой переменной. Затем получаем текущие цены по данному символу и храним эти цены в переменной last_tick.

Следующий блок кода обрабатывается при условии, что у нас есть открытая позиция по данному инструменту:

   if(!SymbolInfoTick(Symbol(),last_tick))
      Print("SymbolInfoTick() failed, error = ",GetLastError());
//--- check the conditions for opening a position
   if(!PositionSelect(Symbol()))
     {
      RSIArea=RSIAreaFunc(RSIAreaShift,0);
      //--- check for a chance to take a long position
      if(RSIArea<-AreaCondition)
        {
         my_trade.Buy(1.0,NULL,last_tick.ask,0.0,0.0,NULL);
         RSIOpen=rsi;
         return;
        }
      //---check for a chance to take a short position
      if(RSIArea>AreaCondition)
        {
         my_trade.Sell(1.0,NULL,last_tick.bid,0.0,0.0,NULL);
         RSIOpen=rsi;
         return;
        }
      RSIAreaShift=0;
     }
//---
  }
//+------------------------------------------------------------------+
//| Area calculation                                                 |

В коде проверяются условия на открытие позиции: если рассчитанная на данный момент площадь (переменная RSIArea) меньше/больше входного параметра (AreaCondition), то, соответственно, будет открыта позиция Buy/Sell.

Дальше переменной RSICurrent  присваивается значение переменной rsi  (напомню, что в ней хранится одно значение индикатора RSI на самом правом баре) и проверяется условие выхода из функции OnTick():

  • если позиция была открыта выше линии "50" (RSIOpen>50) и мы на данный момент находимся выше линии "50" (RSICurrent>50);
  • если позиция была открыта ниже линии "50" (RSIOpen<50) и мы на данный момент находимся ниже линии "50" (RSICurrent<50):

      RSIAreaShift=0;
     }
   RSICurrent=rsi;
   if(RSIOpen>50 && RSICurrent>50) return;
   if(RSIOpen<50 && RSICurrent<50) return;

   RSILocalMin = RSICurrent;
   RSILocalMax = RSICurrent;

//---
  }
//+------------------------------------------------------------------+
//| Area calculation                                                 |

Следующий блок кода находит локальные минимумы/максимумы и присваивает эти значения переменным RSILocalMin и RSILocalMax:

   RSILocalMin = RSICurrent;
   RSILocalMax = RSICurrent;
   
//--- search local minimum/maximum
   if(RSIAreaShift>1)
     {
      double   arr_rsi_1[];
      ArrayResize(arr_rsi_1,RSIAreaShift);
      ArraySetAsSeries(arr_rsi_1,true);
      if(CopyBuffer(handle,0,0,RSIAreaShift,arr_rsi_1)==-1)
        {
         Print("CopyBuffer from iRSI failed, no data");
         return;
        }
      for(shift=1; shift<RSIAreaShift; shift++)
        {
         value=arr_rsi_1[shift];
         if(value<RSILocalMin && RSIArea>0) RSILocalMin=value;
         if(value>RSILocalMax && RSIArea<0) RSILocalMax=value;
        }
     }
//---
  }
//+------------------------------------------------------------------+
//| Area calculation                                                 |

И, наконец, последний блок кода:

         if(value>RSILocalMax && RSIArea<0) RSILocalMax=value;
        }
     }

//--- check for rollback
   if(PositionSelect(Symbol()))
     {
      if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
        {
         //--- сheck if it is time for closing
         if(RSILocalMax>=RSICurrent+4 && RSILocalMax>50)
            my_trade.PositionClose(Symbol(),20);
        }
      if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
        {
         //--- check, can it is time already be closing?
         if(RSILocalMin<=RSICurrent-4 && RSILocalMin<50)
            my_trade.PositionClose(Symbol(),20);
        }
     }
//---
   return;
  }
//+------------------------------------------------------------------+
//| Area calculation                                                 |

Здесь (при наличии открытой позиции) проверяется условие закрытия позиции по правилу:

Пересечение осциллятором RSI линии 50 с последующим образованием локального максимума/минимума и откат от него на величину 4% шкалы.
Например, при долгом нахождении над линией 50 в какой-то момент имеем открытую позицию SELL. Затем значение индикатора начинает уменьшаться и достигает, допустим линии 40, после чего значение индикатора начинает увеличиваться (то есть, образуется локальный минимум). Сигналом к закрытию позиции будет момент, когда значение индикатора достигнет линии 44.

На этом создание советника RSIAreaExpert_v1 закончено. Файл "RSIAreaExpert_v1.mq5" можно скачать в конце статьи.


5. Тестирование советника RSIAreaExpert version 1.00 на разных периодах и символах

Тестирование советника RSIAreaExpert изначально проводилось на периоде графика H1 [1], но с 2004 года рынки сильно изменились, стали более волатильными, и поэтому для проверки работоспособности метода площадей тестирование решено было провести на большом диапазоне периодов: от M10 до H6. Также был существенно расширен диапазон площадей для тестирования: от 100 до 800. Временной период тестирования — с 2015.01.05 по 2016.01.05.

Итак, результаты тестирования советника RSIAreaExpert version 1 для символа AUDCAD:

  RSIAreaExpert version 1 AreaCondition to Profit AUDCAD

Рис. 10.  Результаты тестирования советника RSIAreaExpert version 1. Символ AUDCAD. Интервал площадей 100-800. Интервал периодов M10-H6 

На периоде H2 мы видим хорошую плотность результатов. Можно ещё взять в расчёт период H3. Теперь посмотрим на рисунок ниже и оценим, сколько было сделок за год на символе AUDCAD при тестировании советника RSIAreaExpert version 1:

RSIAreaExpert version 1 Trades to Profit AUDCAD

Рис. 11.  Результаты тестирования советника RSIAreaExpert version 1. Символ AUDCAD. Интервал площадей 100-800. Интервал периодов M10-H6 

На периодах H2 и H3 количество сделок за год колеблется в пределах 50. Это немного, а погрешность высока. Делаем вывод, что на символе AUDCAD стратегия метода площадей работает плохо.

Результаты тестирования советника RSIAreaExpert version 1 для символа AUDUSD

RSIAreaExpert version 1 AreaCondition to Profit AUDUSD 

 Рис. 12.  Результаты тестирования советника RSIAreaExpert version 1. Символ AUDUSD. Интервал площадей 100-800. Интервал периодов M10-H6 

Если рассматривать прибыльность стратегии на символе AUDUSD, то можно рассматривать торговлю на периодах H2 и H3. На этих периодах параметр AreaCondition колеблется в пределах от 250 до 400. Для подтверждения обоснования торговли на периодах H2 и H3 нужно просмотреть количество трейдов за год на этих периодах:

RSIAreaExpert version 1 Trades to Profit AUDUSD 

 Рис. 13.  Результаты тестирования советника RSIAreaExpert version 1. Символ AUDUSD. Интервал площадей 100-800. Интервал периодов M10-H6 

Как видим, оно катастрофически мало. Значит, и на AUDUSD по методу площадей торговать не рекомендуется.

Результаты тестирования советника RSIAreaExpert version 1 для символа EURUSD:

RSIAreaExpert version 1 AreaCondition to Profit EURUSD

 Рис. 14. Результаты тестирования советника RSIAreaExpert. Интервал площадей 100-800. Интервал периодов M10-H6  

На рисунке 4 видно, что для периода M10 наблюдается хорошая плотность результатов прибыли для диапазона площадей от 400 до 550, для периода M12 — от 300 до 400 и для периода M15 — от 300 до 400. Более высокие периоды не рассматриваем, так как количество сделок для них за год слишком мало (см. рис. 5).

На рисунке 5 представлен график зависимостей количества сделок и прибыли для символа EURUSD:

RSIAreaExpert version 1 Trades to Profit EURUSD

Рис. 15. Результаты тестирования советника RSIAreaExpert. Интервал площадей 100-800. Интервал периодов M10-H6 

Здесь хорошо видно, что на высоких периодах (от H1 до H6) количество сделок мизерное, что ставит под сомнение оправданность применения метода площадей на таких периодах. А вот на периодах M10, M12 и M15 количество сделок достаточно для подтверждения прибыльности метода площадей на этих таймфреймах. Определённо, символ EURUSD подходит для торговли по методу площадей.

Результаты тестирования советника RSIAreaExpert version 1 для символа GBPUSD:

RSIAreaExpert version 1 AreaCondition to Profit GBPUSD 

 Рис. 16.  Результаты тестирования советника RSIAreaExpert version 1. Символ GBPUSD. Интервал площадей 100-800. Интервал периодов M10-H6 

На символе GBPUSD хорошая плотность положительной прибыли для периода M20. Разброс параметра AreaCondition от 300 до 500.

RSIAreaExpert version 1 Trades to Profit GBPUSD 

Рис. 17.  Результаты тестирования советника RSIAreaExpert version 1. Символ GBPUSD. Интервал площадей 100-800. Интервал периодов M10-H6  

Для символа GBPUSD и на периоде M20 количество сделок за год в пределах от 140 до 250. Это, конечно, не фантастический показатель, но, тем не менее, его можно взять на заметку. Иными словами, торговля на символе GBPUSD методом площадей — занятие на любителя. 

Результаты тестирования советника RSIAreaExpert version 1 для символа USDCAD:

 

Рис. 18.  Результаты тестирования советника RSIAreaExpert version 1. Символ USDCAD. Интервал площадей 100-800. Интервал периодов M10-H6  

На символе USDCAD я бы рассматривал только период M30, поскольку лишь на нем отмечается хорошая плотность положительной прибыли. При этом параметр AreaCondition изменяется в диапазоне от 280 до 550.

RSIAreaExpert version 1 Trades to Profit USDCAD 

 Рис. 19.  Результаты тестирования советника RSIAreaExpert version 1. Символ USDCAD. Интервал площадей 100-800. Интервал периодов M10-H6  

А вот количество сделок за год на этой валютной паре по таймфрейму M30 колеблется от 90 до 200. Это не очень много, поэтому метод площадей для  символа USDCAD я бы не рекомендовал.

Результаты тестирования советника RSIAreaExpert version 1 для символа USDJPY:

RSIAreaExpert version 1 AreaCondition to Profit USDJPY 

Рис. 20.  Результаты тестирования советника RSIAreaExpert version 1. Символ USDJPY. Интервал площадей 100-800. Интервал периодов M10-H6  

На символе USDJPY выделяются два периода: M10 и M30. Параметр AreaCondition для периода M10 находится в пределах от 320 до 650, для периода M30 — в пределах от 550 до 600.

RSIAreaExpert version 1 Trades to Profit USDJPY 

Рис. 21.  Результаты тестирования советника RSIAreaExpert version 1. Символ USDJPY. Интервал площадей 100-800. Интервал периодов M10-H6  

Для символа USDJPY количество сделок за год по методу площадей для периода M10 в пределах от 150 до 200, а для периода M30 в пределах 50 — 150. Таким образом, мы видим, что и здесь рекомендации по торговле весьма расплывчатые.


Заключение

Торговлю по методу площадей оказалось рано списывать со счетов. Правда, при текущих условиях на рынках система стала показывать убытки на периоде H1, хотя раньше [1] именно на нем отмечалась основная прибыль. На современном рынке самой прибыльной и результативной оказалась торговля по методу площадей на символе EURUSD на периодах M10, M12 и M15. Именно на этой валютной паре и на этих таймфреймах тестирование показало достаточное количество сделок в течение года.

 

Список использованной литературы

  1. Морозов И. В., Фатхуллин Р.Р. FOREX: от простого к сложному. Новые возможности с клиентским терминалом "MetaTrader". - М: ООО "Телетрейд", 2004. - 448 с.
Как правильно установить индикаторы и советники из архивов в конце статьи: архивы "Indicators.zip" и "Experts.zip" нужно разархивировать в <каталог данных>\MQL5\

Прикрепленные файлы |
Indicators.zip (3.91 KB)
Experts.zip (5.37 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (10)
Aleksey Vyazmikin
Aleksey Vyazmikin | 22 мар. 2016 в 15:50

Не понял из статьи, как именно рассчитывается площадь? Можно код и математическую формулу увидеть?

Я может и пропустил что-то, извиняюсь, если так....

Vladimir Karputov
Vladimir Karputov | 22 мар. 2016 в 16:03
-Aleks-:

Не понял из статьи, как именно рассчитывается площадь? Можно код и математическую формулу увидеть?

Я может и пропустил что-то, извиняюсь, если так....

Площадь вычисляется приближённо, методом прямоугольников. Но при одном условии - ширина такого прямоугольника равна "1".
Aleksey Vyazmikin
Aleksey Vyazmikin | 22 мар. 2016 в 19:55
Karputov Vladimir:
Площадь вычисляется приближённо, методом прямоугольников. Но при одном условии - ширина такого прямоугольника равна "1".
Спасибо - идея ясна. А алгоритм можете показать с пошаговым описанием?
Vladimir Karputov
Vladimir Karputov | 22 мар. 2016 в 20:02
-Aleks-:
Спасибо - идея ясна. А алгоритм можете показать с пошаговым описанием?

Для советника начиная с 4.2. Вспомогательная функция RSIAreaFunc

Aleksey Vyazmikin
Aleksey Vyazmikin | 22 мар. 2016 в 23:01
Karputov Vladimir:

Для советника начиная с 4.2. Вспомогательная функция RSIAreaFunc

Если честно то, не вижу этого в коде - вижу поиск экстремумов, но не площади... 

Графические интерфейсы II: Настройка обработчиков событий библиотеки (Глава 3) Графические интерфейсы II: Настройка обработчиков событий библиотеки (Глава 3)
В предыдущих статьях были реализованы классы для создания всех составных частей главного меню. Теперь же настало время познакомиться с обработчиками событий в главных базовых классах и в классах созданных элементов управления. Отдельное внимание уделено управлению состоянием графика в зависимости от того, где находится курсор мыши.
Графические интерфейсы II: Элементы "Разделительная линия" и "Контекстное меню" (Глава 2) Графические интерфейсы II: Элементы "Разделительная линия" и "Контекстное меню" (Глава 2)
В этой статье мы создадим элемент «Разделительная линия». Его тоже можно будет использовать не только как независимый элемент интерфейса, но и как часть многих других элементов. После этого у нас будет всё необходимое для разработки класса контекстного меню, которое тоже будет подробно рассмотрено в этой статье. Кроме этого, вносятся необходимые дополнения в класс, который является базой для хранения указателей на все элементы графического интерфейса приложения.
Вклад Томаса Демарка в технический анализ Вклад Томаса Демарка в технический анализ
В статье описаны изобретенные Томасом Демарком TD-точки и TD-линии. Показано их применение на практике. Также продемонстрирован процесс написания трех индикаторов и двух экспертов с использованием идей Томаса Демарка.
Как быстро добавить панель управления к индикатору и советнику Как быстро добавить панель управления к индикатору и советнику
Вы хотите добавить к своему индикатору или советнику графическую панельку для удобного и быстрого управления, но не знаете, как это сделать? В этой статье шаг за шагом я покажу как "прикрутить" панель диалога со входными параметрами к вашей MQL4/MQL5-программе.