Принцип замены времени в интрадей-торговле

kamal | 5 июля, 2007

Введение

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

Возьму на себя смелость заявить, что мало кто из разработчиков систем – новичков, а также некоторых «опытных», задумывается о том, что даже самые простые индикаторы типа скользящего среднего, будучи связанными со временем, представляют из себя фактически разный агрегат в разное время суток. Безусловно, есть и системы, формулирующиеся в терминах цены, но не времени. Типичный пример – системы по renko и kagi методикам, но таких меньшинство. Большинство же, повторюсь, «завязаны» на время чаще всего опосредованно через индикаторы.

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


Теория

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

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

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


Статистические данные

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

Кроме того, те же эффекты наблюдаются и на широко распространенных парах. Это вызывает интерес к более детальному изучению поведения объемов и волатильности. Ясное дело, что критически важным для понимания колебаний внутридневной волатильности является поведение объемов, то есть тиков на бар. Однако объем сам по себе является величиной случайной, что заставляет нас обращаться к историческому среднему. Такое обращение вполне может оказаться не вполне легальным, если статистическое «сырье» будет вести себя «плохо».

С целью проверки этих гипотез напишем простенький индикатор – ExpectedVolume (ожидаемый объем), который найдет историческое среднее количества тиков в час за histSteps шагов назад, каждый шаг длиной span дней. Типичное значение этих параметров – 100 и 1 соответственно. Все тестирование проводится на таймфрейме H1, на других внутридневных таймфеймах параметры следует соответственно менять. Приведем код индикатора:

//+------------------------------------------------------------------+
//|                                             Expected Volumes.mq4 |
//|                                     Copyright © 2007, Amir Aliev |
//|                                       http://finmat.blogspot.com/ |
//+------------------------------------------------------------------+
#property  copyright "Copyright © 2007, Amir Aliev"
#property  link      "http://finmat.blogspot.com/"
//---- 
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_color1 Blue
//---- input parameters
extern int hist_steps = 100;      // Number of observations
extern int span = 1;              // Days to step back each time 
//---- buffers
double ExtMapBuffer1[];
int sum;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
   string short_name;
//---- indicators
   SetIndexStyle(0, DRAW_HISTOGRAM);
   SetIndexBuffer(0,ExtMapBuffer1);
//----
   short_name = "Expected volumes(" + hist_steps + ")";
   IndicatorShortName(short_name);
   SetIndexLabel(0, short_name);
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
  {
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
   int counted_bars = IndicatorCounted();
   int rest = Bars - counted_bars;
   int j, k, u;
//----     
   while(rest >= 0)
     {
      if(Bars - rest < span * 23 * hist_steps) 
        {
         ExtMapBuffer1[rest] = 0;     
         rest--; 
         continue;                                   
        }
      sum = 0;
      j = 0;
      k = 0;
      u = 0;
      while(j < hist_steps && k < Bars) 
        {
         if(TimeHour(Time[rest+k]) == TimeHour(Time[rest]))
           {
            u++;
            if(u == span)
              {
               u = 0;
               j++;
               sum += Volume[rest + k]; 
              }
            k += 23;
           }
         k++;
        }
      ExtMapBuffer1[rest] = sum / hist_steps;     
      rest--;                                    
     }
//----
   return(0);
  }
//+------------------------------------------------------------------+

Однако для проверки статистической однородности необходимо сделать наблюдения независимыми, например, разбить по дням недели. Для этого положим span = 5. Имеем следующую картинку:

Соседние горбы почти совпадают. Это означает что волатильность, которую мы измеряем тиками в час, статистически однородна. Кроме того, понять структуру этой волатильности можно, внимательнее взглянув на картинки (слева - EURUSD, справа - USDJPY):

На них ясно видны три пика активности сессий – Азиатской, Европейской и Американской. Отметим отдельно, что деление именно на эти сессии не является общепринятым - иногда выделяют и другие. В принципе, можно увидеть и некоторые особенности, например, характер активности в американскую сессию (повторяется на обоих графиках).


Изменение индикаторов

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

В конечном итоге наиболее разумным было бы все же введение «операционного» таймфрейма, а пока покажем, как поменять несколько простых индикаторов. Самый примитивный из них - скорректированные объемы строится делением реальных объемов на ожидаемые. Таким образов, отклонение в ту или иную сторону от единицы этого индикатора отражает повышенную/пониженную активность на рынке. Код этого индикатора в силу крайней простоты в тексте статьи приводить не будем. Он есть в приложденных файлах.

Следующий пример - среднее. Фактически следует просто взвешивать характеристику бара, по которой мы строим среднее (в примере берется open), количеством тиков внутри бара. Полученное число не равно строго сумме значений цены по всем тикам. Для более точной оценки надо брать не open, a средневзвешенное по бару. Индикатор сделан «в лоб» и из-за этого расчет его требует значительных и по сути излишних вычислительных затрат. Из-за этого введен дополнительный параметр – число баров из прошлого, которое индикатор будет отрисовываться, по умолчанию равно 500. Кроме того, период средней задается теперь не в барах, а в количестве тиков. Итак, код:

//+------------------------------------------------------------------+
//|                                                Corrected SMA.mq4 |
//|                                      Copyright © 2007, Amir Aliev |
//|                                    http://finmat.blogspot.com/   |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, Amir Aliev"
#property link      "http://finmat.blogspot.com/"
//----
#property indicator_chart_window
#property indicator_color1 Red
//---- input parameters
extern int MA_Ticks = 10000;
extern int MA_Shift = 0;
extern int MA_Start = 500;
//---- indicator buffers
double ExtMapBuffer[];
double ExpVolBuffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
//----
   SetIndexStyle(0, DRAW_LINE);
   SetIndexShift(0, MA_Shift);
   IndicatorBuffers(2);
//---- indicator buffers mapping
   SetIndexBuffer(0, ExtMapBuffer);
   SetIndexBuffer(1, ExpVolBuffer);
   SetIndexDrawBegin(0, 0);  
//---- initialization done
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
   int counted_bars = IndicatorCounted();
   int rest  = Bars - counted_bars;
   int restt = Bars - counted_bars;
   double sum;                               
   int ts;                                   
   int evol;                                 
   int volsum;
   int j;
//----
   while(restt >= 0)
     {
       volsum = 0;
       for(int k = 0; k < 30; k++) 
           volsum += iVolume(NULL, 0, restt + k*24); 
       ExpVolBuffer[restt] = volsum / 30;
       restt--;
     }
//----
   while(ExpVolBuffer[rest] == 0 && rest >= 0) 
       rest--;
   rest -= MA_Ticks / 200;
   if(rest > MA_Start) 
       rest = MA_Start;  
//----
   while(rest >= 0)
     {
       sum = 0;
       ts = 0;
       j = rest;
       while(ts < MA_Ticks)
         {
           evol = ExpVolBuffer[j];
           Print("Evol = ", evol);
           if(ts + evol < MA_Ticks)
             {
               sum += evol * Open[j];
               ts += evol;
             }
           else
             {
               sum += (MA_Ticks - ts) * Open[j];
               ts = MA_Ticks;
             }
           j++;
         }
       ExtMapBuffer[rest] = sum / MA_Ticks;
       rest--;
     }   
//----
   return(0);
  }
//+------------------------------------------------------------------+

После того, как построены более простые индикаторы, переделать сложные не составит труда, так, например, в коде MACD следует просто использовать вместо обычной скользящей средней скорректированную. Соотвествующий код также отнесен в приложение.

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

Казалось бы, нет никакого смысла усреднять объемы за прошлые периоды: достаточно просто в расчете среднего использовать имеющиеся объемы как коэффициенты. Соответствующий код средней приведем и мы, но отметим, что по техническим причинам лучше всего использовать такую среднюю на маленьких таймфреймах, вроде М1-М5:

//+------------------------------------------------------------------+
//|                                             Corrected SMA II.mq4 |
//|                                     Copyright © 2007, Amir Aliev |
//|                                       http://finmat.blogspot.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, Amir Aliev"
#property link      "http://finmat.blogspot.com/"
 
#property indicator_chart_window
#property indicator_color1 Red
//---- input parameters
extern int MA_Ticks = 1000;
//---- indicator buffers
double sum = 0;                               
int ticks = 0;
bool collected = false;
bool started = false;
int fbar = 0;
double ExtMapBuffer[];
int oldRange = 0;
int lbarVol = 0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
//----
   SetIndexStyle(0, DRAW_LINE);
//---- indicator buffers mapping
   SetIndexBuffer(0, ExtMapBuffer);
//---- initialization done
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
   int rest = Bars - IndicatorCounted();
   if(! rest) 
       return (0);
   Print("Ticks = ", ticks);
   Print("Rest = ", rest);
   Print("fbar = ", fbar);  
   rest--;
   fbar += rest;
   while(!collected && (rest >= 0))
     {
      if(ticks + Volume[rest] < MA_Ticks)
        {
         ticks += Volume[rest];
         sum += Volume[rest] * Open[rest];
         if(!started)
           {
            fbar = rest;
            started = true;
           }
         rest--;
         continue;
        } 
      collected = true;
     }
   if(! collected) 
       return (0);
 
   ticks += (Volume[rest] - lbarVol);
   sum += (Volume[rest] - lbarVol) * Open[rest];
   lbarVol = Volume[rest];
   while(ticks > MA_Ticks)
     {
       Print("fbar-- because bar ticks reaches 1000");
       ticks -= Volume[fbar];
       sum -= Volume[fbar] * Open[fbar];
       fbar--;
     }
   ExtMapBuffer[rest] = sum / ticks;
   rest--;
   while(rest >= 0)
     {
      ticks += Volume[rest];
      sum += Volume[rest] * Open[rest];
      lbarVol = Volume[rest];
      while(ticks > MA_Ticks)
        {
         Print("fbar-- because of new bar ");
         ticks -= Volume[fbar];
         sum -= Volume[fbar] * Open[fbar];
         fbar--;
        }
      ExtMapBuffer[rest] = sum / ticks;
      rest--;
     } 
//----
   return(0);
  }
//+------------------------------------------------------------------+

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

Каждый из представленных индикаторов может быть доработан под конкретные задачи, например, изменение средней для учета суммы значений внутри бара или изменение параметров расчета средней. Напомним, что мы считали через open, что визуально вызывает ощущение запаздывания - концепция замены времени, деволатилизации, допускает широкое толкование, в том числе и не затронутые в статье применения такие, как сезонная волатильность.


Заключение

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

Сама концепция операционного времени поэтому представляется чрезвычайно полезной и естественной с позиции технического анализа. Безусловно, тот факт, что, например, какие-то ключевые новости по состоянию экономик выходят в определенное время, нарушает однородность достаточно серьезно и с трудом поддается «лечению». Однако во многих случаях применение замены времени позволяет получать более стабильные результаты и увеличивать прибыльность торговой стратегии.