Сколько длится тренд?

Alexander Fedosov | 2 мая, 2017

Содержание

Введение

Определение состояний рынка в различные периоды времени — основа торговли. От того, насколько точен прогноз движения цены, зависит успех трейдера. На эту тему уже написан ряд статей: например, "Несколько способов определения тренда на MQL5". В ней были описаны способы определения трендового состояния рынка, написаны по ним индикаторы и торговые советники. Тренду была посвящена и одна из моих предыдущих статей "Сравнительный анализ 10 трендовых стратегий", в которой разрабатывались и тестировались трендовые стратегии. В этой статье мы тоже выберем несколько способов определения тренда, но только с целью определить его длительность по отношению к флэтовому состоянию рынка. Принято считать, что тренд соотносится с флэтом в пропорции 30% : 70%. Это мы и проверим.


Постановка задачи

Для проведения исследования определимся с задачами и их условиями.

  1. Выбрать способы определения тренда и флэта так, чтобы можно было количественно оценить их и привести к процентному соотношению. Для этого пригодятся только системы, которые могут показывать оба состояния рынка. Желательно, чтобы в них были встроены качественные показатели — сила тренда или ярко выраженное определение бокового состояния рынка.
  2. Иметь возможность определить и оценить соотношение на различных временных периодах, а также на разных рынках одного и разных типов, будь то валютные рынки, фондовые или фьючерсные.
  3. Разработать инструмент, который читатель мог бы использовать в самостоятельных исследованиях в интересующих его условиях.
  4. Провести сравнительный анализ на основе данных, полученных в различных условиях, поискать корреляцию.

Реализация

1. Выбор способов определения тренда.

1.1. Начнем исследование с классического индикатора силы тренда ADX. Оценкой присутствия тренда или флэта будет служить уровень TrendLevel. Считаем, что тренд есть, если основная линия поднимается выше этого уровня. На рис.1 представлен пример определения зоны тренда и зоны флета по этому способу. Подсчет состояния рынка будет производиться по количеству свечей, на которых значение ADX будет выше TrendLevel из заданной выборки.

Рис.1 Определение зоны тренда/флэта с помощью ADX

1.2. Следующим рассмотрим Индикатор силы и направления тренда, но под наши задачи выберем здесь только один индикатор Боллинджера и уменьшим количество цветов для отображения до двух (красного и зеленого). На рис.2 прекрасно видно, где происходят сильные движения рынка: свечи здесь окрашены в заданные цвета.

Рис.2 Определение зон тренда/флэта с помощью полос Боллинджера.

1.3. Третьим был рассмотрен Percentage of Trend, который тоже подвергся модификации: из него убран второй период и добавлена цветовая индикация тренда. Результат работы получившегося индикатора представлен на рис.3.

 

Рис.3 Определение зон тренда/флэта с помощью Percentage of Trend.

1.4. Еще один способ определения тренда/флэта — RSIFilter. Для удобства подсчета индикатор RSI отображается в виде гистограммы, где уход значений индикатора в заранее заданные зоны перепроданности/перекупленности отображается как столбец. Здесь тоже внесены изменения в оригинальный индикатор: состояние флэта не отображается, и буфер, отображающий значение высоты гистограммы в таком состоянии рынка, равен нулю. Сделано так, чтобы удобнее было определять наличие тренда, при котором значение буфера равно единице. Пример работы индикатора показан на рис.4.

Рис.4  Определение зон тренда/флэта с помощью RSIFilter.

1.5. И, наконец, рассмотрим способ из статьи "Несколько способов определения тренда на MQL5", а именно — определение состояний тренда/флэта с помощью индикатора ZigZagTrendDetector. В нем никаких изменений не проводилось. Работа его представлена на рис.5.

Рис.5  Определение зон тренда/флэта с помощью ZigZagTrendDetector.

2. Разработка и реализация инструмента для подсчета состояний рынка и отображение результатов.

Результат каждого способа определения тренда/флэта будет представлен в виде сводной таблицы по нескольким таймфреймам. Для наглядности отображения я воспользовался библиотекой EasyAndFastGUI на основе серии статей Графические интерфейсы. Был разработан специальный класс CTrendCountUI для визуализации результатов. Чтобы более четко представлять, как он будет выглядеть, на рис.6 представлен изначальный шаблон, в который будут записываться все подсчеты.

 

Рис.6 Шаблон отображения подсчета результатов тестирования.

Как видно на скриншоте, в первом столбце представлены способы подсчета тренда, а в первой строке расчет в мультитаймфреймовом варианте. Для экономии места и сохранения читабельности я не буду приводить полную реализацию интерфейса, а приведу лишь функцию, которая настраивает и визуализирует шаблон, представленный выше:

//+------------------------------------------------------------------+
//| Вывод и настройка информационной панели                          |
//+------------------------------------------------------------------+
void SetInfoPanel()
  {
//---

   UI.CreateMainPanel("Trend Counter");
   UI.CreateStatusBar(1,25);
   UI.m_status_bar.ValueToItem(0,"Enabled on "+Symbol());
   UI.CreateCanvasTable();
//---

   UI.m_canvas_table.SetValue(0,0,"Значение");
   UI.m_canvas_table.SetValue(0,1,"ADX");
   UI.m_canvas_table.SetValue(0,2,"BB");
   UI.m_canvas_table.SetValue(0,3,"PoT");
   UI.m_canvas_table.SetValue(0,4,"RSI");
   UI.m_canvas_table.SetValue(0,5,"ZZ");
//---

   UI.m_canvas_table.SetValue(1,0,"M1");
   UI.m_canvas_table.SetValue(2,0,"M5");
   UI.m_canvas_table.SetValue(3,0,"M15");
   UI.m_canvas_table.SetValue(4,0,"M30");
   UI.m_canvas_table.SetValue(5,0,"H1");
   UI.m_canvas_table.SetValue(6,0,"H4");
   UI.m_canvas_table.SetValue(7,0,"H6");
   UI.m_canvas_table.SetValue(8,0,"D1");
   UI.m_canvas_table.SetValue(9,0,"W1");

   UI.m_canvas_table.UpdateTable(true);
   UI.CreateLabel("Working...");
  }

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

Разберем первый описанный способ — ADX, функция GetADXCount().

//+------------------------------------------------------------------+
//| Функция подсчета состояний рынка с помощью ADX                   |
//+------------------------------------------------------------------+
bool GetADXCount()
  {
//--- Блок 1
   double adx[],result[],count;
   int to_copy,bars;
   int tf_size=ArraySize(Ind_Timeframe);
   ArrayResize(Ind_Handle,tf_size);
   ArrayResize(result,tf_size);
   ArrayInitialize(adx,0.0);
   ArrayInitialize(result,0.0);
   ArraySetAsSeries(adx,true);
//---

   for(int i=0;i<tf_size;i++)
     {
      count=0;
      Ind_Handle[i]=iADX(Symbol(),Ind_Timeframe[i],InpInd_ADXPeriod);
      if(Ind_Handle[i]==INVALID_HANDLE)
        {
         Print(" Failed to get the indicator handle");
         return(false);
        }
      //--- Блок 2

      bars=Bars(Symbol(),Ind_Timeframe[i]);
      to_copy=(bars<NumCandles)?bars:NumCandles;
      //---

      if(CopyBuffer(Ind_Handle[i],0,0,to_copy,adx)<=0)
         return(false);
      //--- Блок 3

      for(int j=0;j<to_copy;j++)
        {
         if(adx[j]>TrendLevel)
            count++;
        }
      result[i]=(count/to_copy)*100;
      IndicatorRelease(Ind_Handle[i]);
     }
//--- Блок 4

   for(int i=1;i<=tf_size;i++)
      UI.m_canvas_table.SetValue(i,1,DoubleToString(result[i-1],2));
   UI.m_canvas_table.UpdateTable(true);
   return(true);
  }

Рассмотрим обозначенные блоки кода из листинга выше.

  • Блок 1. Инициализация переменных и массивов, приведение их размера к количеству таймфреймов, на которых будут проводиться тесты.
  • Блок 2. Для тестирования и получения результата в процентах нужна выборка из определенного количества свечей. Надо учесть, что на больших таймфреймах или на инструментах с небольшой историей может не хватить количества баров для заданной выборки, поэтому нужно определить глубину истории и при необходимости корректировать объем запрошенных данных.
  • Блок 3. Для индикатора ADX мы будем увеличивать счетчик тогда, когда его значение для текущего бара будет выше TrendLevel. 
  • Блок 4. Заносим результаты тестирования в таблицу, подготовленную ранее в функции SetInfoPanel().

Далее будет меняться только Блок 3, то есть  условия подсчета.

Функция GetColorBBCount().

for(int j=0;j<to_copy;j++)
        {
         if(bb[j])
            count++;
        }
result[i]=(count/to_copy)*100;

Значение выбранного индикаторного буфера, отличное от нуля, говорит о том, что текущая в выборке свеча принадлежит тренду. Если иначе, то перед нами флэт.

Функция GetPoTCount().

Параметры индикатора Percentage of Trend имеют вид:

//--- Параметры Percentage of Trend

input int                  InpPeriodPoT=20;
input double               UpTrendLevel=0.8;
input double               DnTrendLevel=0.2;

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

 for(int j=0;j<to_copy;j++)
        {
         if(pot[j]>=UpTrendLevel || pot[j]<=DnTrendLevel)
            count++;
        }
 result[i]=(count/to_copy)*100;

Функция GetRSICount().

Так как она представлена в виде гистограммы, в которой наличие тренда обозначается как 1, а отсутствие — как 0, то условие будет идентично GetColorBBCount():

 for(int j=0;j<to_copy;j++)
        {
         if(rsi[j])
            count++;
        }
 result[i]=(count/to_copy)*100;

Функция GetZZCount().

Аналогична предыдущей — такой же гистограммный вид индикатора.

 for(int j=0;j<to_copy;j++)
        {
         if(zz[j])
            count++;
        }
 result[i]=(count/to_copy)*100;

Функция GetAverage().

Рассчитывает общий результат по всем способам и таймфреймам.

//+------------------------------------------------------------------+
//| Функция подсчета среднего значения по всем данным                |
//+------------------------------------------------------------------+
string GetAverage(double &arr[])
  {
   double sum=0.0;
   int size=ArraySize(arr);
   for(int i=0;i<size;i++)
      sum+=arr[i];
   sum/=size;
   return(DoubleToString(sum,2));
  }
//+------------------------------------------------------------------+

В итоге весь расчет и вывод информации выглядит таким компактным образом:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   SetInfoPanel();
   if(GetADXCount() && 
      GetColorBBCount() && 
      GetPoTCount() && 
      GetRSICount() && 
      GetZZCount()
      )
      UI.m_label1.LabelText("Done. Result for "+IntegerToString(NumCandles)+" bars. Average Trend Time - "+GetAverage(avr_result)+"%");
   else
     {
      UI.m_label1.LabelText("Error");
     }
//---
   return(INIT_SUCCEEDED);
  }

Тестирование

Для тестирования было решено взять несколько инструментов различных типов, чтобы максимально расширить условия исследования:

  • Валютные пары — EURUSD,USDCHF,USDJPY,GBPUSD;
  • Фьючерс — GOLD-6.17;
  • Рынок акций — #MCD;

Тестирование на всех таймфреймах будет проходить на выборке в 2000 баров. Ниже представлены настройки, одинаковые для всех инструментов.

//+------------------------------------------------------------------+
//| Входные параметры эксперта                                       |
//+------------------------------------------------------------------+

input int                  NumCandles=2000;              //Number of analyzed candles
//---- Параметры ADX
input string               com1="";                      //---- Параметры ADX
input int                  InpInd_ADXPeriod=14;          //ADX Period
input double               TrendLevel=20.0;
//--- Параметры ColorBBCandles
input string               com2="";                      //---- Параметры ColorBBCandles
input int                  BandsPeriod=20;               // Период усреднения BB
input double               BandsDeviation=1.0;           // Отклонение
input ENUM_MA_METHOD       MA_Method_=MODE_SMA;          //метод усреднения индикатора
input Applied_price_       IPC=PRICE_CLOSE_;             //ценовая константа
//--- Параметры Percentage of Trend
input string               com3="";                      //---- Параметры Percentage of Trend
input int                  InpPeriodPoT=20;
input double               UpTrendLevel=0.7;
input double               DnTrendLevel=0.3;
//--- Параметры RSIFilter
input string               com4="";                      //---- Параметры RSIFilter
input uint                 RSIPeriod=10;                 // Indicator period
input ENUM_APPLIED_PRICE   RSIPrice=PRICE_CLOSE;         // Price
input uint                 HighLevel=60;                 // Overbought level
input uint                 LowLevel=40;                  // Oversold level
//--- Параметры ZigZagTrendDetector 
input string               com5="";                      //---- Параметры ZigZagTrendDetector
input int                  ExtDepth=5;
input int                  ExtDeviation= 5;
input int                  ExtBackstep = 3;


1. Тестирование валютных пар.

По результатам тестирования четырех валютных пар выявилась тенденция среднего значения в 60% времени в состоянии тренда. При этом существенного отличия по различным способам и таймфреймам не наблюдается.

Рис.7 Результаты тестирования четырех валютных пар.

2. Тестирование фьючерса.

Тестирование фьючерса показало выбивающиеся из общей тенденции значения на старших таймфреймах. Это происходит потому, что изначально была задана выборка в 2000 баров, но на D1 и W1 для этого не хватает истории, поэтому расчет ведется по максимально доступной истории. 

Рис.7 Результаты тестирования фьючерса GOLD-6.17.

3. Тестирование акции.

Здесь также наблюдается общая тенденция, полученная на инструментах выше.

 

Рис.8 Результаты тестирования акции #MCD.

В процессе тестирования у меня возник вопрос по поводу справедливости выбора диапазона в 2000 свечей. Сохраняется ли среднее значение тренда для выборок других масштабов? И здесь возникает еще один интересный момент для проверки — как изменяется соотношение тренда/флэта в разрезе истории? Потому что исследовав динамику изменения этого соотношения, можно сделать выводы об изменении характера трендовых движений, длительности, частоты и т.д. Поэтому было решено исследовать зависимость значения выборки от среднего значения тренда для каждого из способов определения тренда. Это даст ответы на поставленные выше вопросы. Искать зависимости будем с помощью расчета коэффициента ранговой корреляции Спирмена. 

Нас интересует корреляция двух величин: количества баров для теста (X) и среднего времени тренда в процентах (Y). Для каждого X и Y присваиваются ранги. На основе полученных рангов высчитывается их разность d и вычисляется коэффициент по формуле:

где n — это число замеряемых пар X и Y. Значение коэффициента может меняться от -1 до 1. Положительное значение говорит о прямой зависимости исследуемых величин, отрицательное — об обратной. Нулевое значение говорит об отсутствии какой-либо зависимости.

В качестве примера подробно разберем один из способов определения тренда ADX на валютной паре EURUSD. Остальные будут рассчитываться аналогично, и их результаты будут сведены в таблицу для лучшей наглядности. В нашем случае замеров средних значений времени тренда будет n=7 и заранее выбрано количество баров для теста: это 100, 200, 300, 500, 1000, 1500, 2000. В нижеследующей таблице приведены результаты замера значений, их ранжирование, разность рангов и их квадрат для расчета коэффициента:

Rx X Y Ry D(Rx-Ry) D2
1  100 75.78 1 0 0
2  200 77.72 2 0 0
3  300 79.04 3 0 0
4  500 79.77 5 -1 1
5  1000 79.49 4 -1 1
6  1500 81.32 6 0 0
 2000 81.44 7 0 0

Подставляя полученные значения в формулу, получим коэффициент ρ=1-(6*2)/(7*(49-1)=0,96. 

Далее проверяем полученное значение по таблице критических значений коэффициентов, представленной на рис.8, для n=7 и делаем вывод, что в рамках данного тестирования присутствует сильная прямо пропорциональная зависимость среднего значения времени тренда от значения задаваемой выборки тестирования в барах.

Рис.8 Критические значения коэффициента корреляции рангов Спирмена.

Теперь рассчитаем коэффициент Спирмена для всех способов определения тренда для выбранной валютной пары EURUSD и выведем эти значения в таблицу.

Способ определения тренда Значение коэффициента Спирмена
 ADX 0.96
 ColorBBCount 0.89
 Percentage of Trend 0.68
 RSI Filter -0.68
 ZigZagTrendDetector -0.07 

О чем говорят полученные результаты? Первые три способа определения тренда дали прямую зависимость среднего значения тренда от глубины истории, RSI Filter — наоборот, продемонстрировал обратную зависимость, а определение с помощью индикатора ZigZag показало, что конечный результат не зависит от размера выборки. Рассчитаем коэффициенты для других участвующих в тестировании инструментов и также сведем полученные результаты в таблицу. 

Способ определения тренда EURUSD USDCHF USDJPY GBPUSD GOLD-6.17 #MCD
 ADX 0.96 -0.61 -0.75 0.96 -0.86 0.93
 ColorBBCount 0.89 0.46 -0.11 -0.14 -0.64 0.79
 Percentage of Trend 0.68 0.93 0.11 0.29 0.89 1
 RSI Filter -0.68 1 0.04 0.79 -1 0.89
 ZigZagTrendDetector -0.07 0 0.96 0.96 0.36 0.32

На основе полученных результатов можно сделать следующие выводы по каждому из торговых инструментов.

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

1. EURUSD

Рассмотрим рисунок, представленный ниже. На нем представлена валютная пара EURUSD с 1995 года по настоящее время. 

Рис.9 Недельный график EURUSD 1995-2017

На рисунке тремя областями отмечены характерные особенности рынка:

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


2. USDJPY

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


Рис.10 Недельный график USDJPY 1995-2017.

Поэтому в данном случае расчет коэффициентов корреляции Спирмена также верно характеризует отсутствие какой-либо зависимости между временем и соотношением тренда/флэта.


3. #MCD.

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

Рис.11 Акция #MCD 2003-2017.

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


Выводы

Для более наглядного сравнения всех тестов общие значения всех тестируемых инструментов были сведены в таблицу.

Тестируемый Инструмент Среднее значение времени тренда, %
 EURUSD  62.54
 USDCHF  62.27
 USDJPY  63.05
 GBPUSD  60.68
 GOLD-6.17  59.55
 #MCD  59.71

Как видно из сводной таблицы, по результатам тестирования можно сделать следующий вывод: для заданных условий тестирования среднее значение рынка в состоянии тренда составляет около 60%, при этом в разрезе истории, от прошлого к настоящему, соотношение тренд/флэт увеличивается в сторону флэта, постепенно приближаясь к изначальному предположению о соотношении 30:70.

О чем же говорят нам полученные результаты?

Заключение

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

Программы, используемые в статье:

#
 Имя
Тип
Описание
1
TrendCount.mq5 Эксперт
 Инструмент для проведения тестирования 
2
TrendCountUI.mqh Библиотека  Класс пользовательского интерфейса
3 ColorBBCandles.mq5 Индикатор  Индикатор силы и направления тренда на основе полос Боллинджера
4 percentageoftrend.mq5 Индикатор  Индикатор подсчета трендового/флетового состояния.
5 rsifilter.mq5 Индикатор  Индикатор RSI в виде гистограммы с заданными уровнями перекупленности/перепроданности.
6 ZigZagTrendDetector.mq5  Индикатор   Индикатор определения состояния рынка с помощью стандартного ZigZag.