Скачать MetaTrader 5

Теория адаптивных индикаторов и ее реализация в MQL5

1 июня 2011, 19:00
investeo
9
7 293

Введение

Материалы этой статьи основаны на двух отличных книгах Джона Элерса "Rocket Science for Traders" и "Cybernetic Analysis for Stock and Futures".

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

В этой статье будут описаны основы теории адаптивных индикаторов и ее реализация на языке MQL5. Кроме того, мы сравним адаптивные индикаторы с их неадаптивными аналогами.


Использование комплексных чисел и фазовых векторов для измерения рыночных циклов

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

Фазовый вектор

Фазовый вектор (Phase Vector) - это вектор, показывающий амплитуду и фазу периодического процесса (цикла). Согласно формуле Эйлера комплексное число (состоящее из действительной и мнимой части) можно представить в показательной форме, где в аргументом в фазе является угол. Это позволяет наглядно иллюстрировать периодические процессы.

На рисунке ниже приведено видео, иллюстрирующее вращение фазового вектора синусоиды.

Phasor 


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

Я представляю это следующим образом: полный оборот фазового вектора на 360 градусов (или радиан) является одинаковым для всего цикла. Текущий угол фазового вектора показывает, в какой части цикла (фазы) мы находимся в данный момент. Ось Y отображает текущее значение амплитуды периодического процесса.

Фазовый вектор может быть разложен на 2 компоненты: InPhase (cosine) и Quadrature (sine). Подробное объяснение получения этих компонент приведено в разделе 6 "Преобразования Гильберта" книги "Rocket Science for Traders". Этот раздел я рекомендую всем, кто хочет разобраться в деталях.

Сейчас нам важно лишь то, что для вычисления адаптивных индикаторов нам нужно преобразовать сигнал (waveform) в комплексный сигнал, состоящий из двух компонент. Как мы достигнем этого? Я упоминал о преобразовании Гильберта? Именно это оно и способно сделать.


Измерение периода цикла

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

Выражение для компоненты Quadrature выглядит следующим образом:

Quadrature component equation 

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

InPhase component equation 

Вычислив компоненты InPhase и Quadrature (напомним, это действительная и мнимая части комплексного числа), можно вычислить сдвиг фазы между текущим и предыдущим барами. Для текущего бара фаза равна Phase of the current bar, для предыдущего бара фаза равнаArctan previous bar.

Воспользовавшись тригонометрическим тождеством:

Arctan deltaphase 

мы получаем выражение для дифференциальной фазы DeltaPhase.

На величину DeltaPhase Джон Элерс наложил дополнительные ограничения: она не может быть отрицательной и ограничена интервалом <0.1, 1.1> (длина цикла от 6 до 63 баров). Оказалось, что для реальных данных DeltaPhase не выглядит гладкой, поэтому необходимо сглаживание.

Наилучшим способом сглаживания для данных с выбросами (spiky data) является медианный фильтр, поэтому применяется медианное сглаживание пяти данных DeltaPhase, результат помещается в переменную MedianDelta. Значение MedianDelta, деленное на далее используется для вычисления периода основного цикла (Dominant Cycle), который является нашей целью.

В процессе тестирования выяснилось, что при измерении существует сдвиг примерно 0.5, который нужно убирать, поэтому был добавлен соответствующий член в расчетной формуле. Наконец, вычисленное значение Dominant Cycle дважды сглаживается при помощи EMA со значениями alpha, равными 0.33 and 0.1 соответственно. Рекомендую посмотреть книгу "Rocket Science for Traders", в которой приведена иллюстрация робастности алгоритма на примере синусоподобного сигнала, период которого постепенно увеличивался с 6 до 40.

Вооружившись теоретическими знаниями, теперь мы готовы к тому, чтобы реализовать индикатор CyclePeriod на MQL5.


Индикатор периода цикла (Cycle Period indicator)

Индикатор Cycle Period состоит из двух линий: линия cycle, показывающая период цикла и сигнальной линии (trigger line), в качестве которой обычно берется линия cycle, запаздывающая на 1 бар.

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

//+------------------------------------------------------------------+
//|                                                  CyclePeriod.mq5 |
//|                                      Copyright 2011, Investeo.pl |
//|                                               http://Investeo.pl |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, Investeo.pl"
#property link      "http://Investeo.pl"
#property version   "1.00"
#property indicator_separate_window

#property description "Индикатор CyclePeriod - предложен Джоном Элерсом (John F. Ehlers)"
#property description "в книге \"Cybernetic Analysis for Stocks and Futures\""

#property indicator_buffers 2
#property indicator_plots 2
#property indicator_width1 1
#property indicator_width2 1
#property indicator_type1   DRAW_LINE
#property indicator_type2   DRAW_LINE
#property indicator_color1  Green
#property indicator_color2  Red
#property indicator_label1  "Cycle"
#property indicator_label2  "Trigger Line"

#define Price(i) ((high[i]+low[i])/2.0)

double Smooth[];
double Cycle[];
double Trigger[];
double Q1[]; // компонента Quadrature
double I1[]; // компонента InPhase
double DeltaPhase[];
double InstPeriod[];
double CyclePeriod[];


input double InpAlpha=0.07; // alpha
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- индикаторные буферы 
   ArraySetAsSeries(Cycle,true);
   ArraySetAsSeries(CyclePeriod,true);
   ArraySetAsSeries(Trigger,true); 
   ArraySetAsSeries(Smooth,true);
   
   SetIndexBuffer(0,CyclePeriod,INDICATOR_DATA);
   SetIndexBuffer(1,Trigger,INDICATOR_DATA);
   
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0.0);

   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---   
   long tickCnt[1];
   int i;
   int ticks=CopyTickVolume(Symbol(), 0, 0, 1, tickCnt);
   if(ticks!=1) return(rates_total);
   double DC, MedianDelta;

   Comment(tickCnt[0]);

   if(prev_calculated==0 || tickCnt[0]==1)
     {
      //--- последний рассчитанный бар будет пересчитан
      int nLimit=rates_total-prev_calculated-1; // начальный индекс для расчетов

      ArraySetAsSeries(high,true);
      ArraySetAsSeries(low,true);
      
      ArrayResize(Smooth,Bars(_Symbol,_Period));
      ArrayResize(Cycle,Bars(_Symbol,_Period));
      //ArrayResize(Price,Bars(_Symbol,_Period));
      ArrayResize(CyclePeriod,Bars(_Symbol,_Period));
      ArrayResize(InstPeriod,Bars(_Symbol,_Period));
      ArrayResize(Q1,Bars(_Symbol,_Period));
      ArrayResize(I1,Bars(_Symbol,_Period));
      ArrayResize(DeltaPhase,Bars(_Symbol,_Period));
      
      if (nLimit>rates_total-7) // последние бары
         nLimit=rates_total-7;   
      
      for(i=nLimit;i>=0 && !IsStopped();i--)   
      {
         Smooth[i] = (Price(i)+2*Price(i+1)+2*Price(i+2)+Price(i+3))/6.0;
   
         if (i<rates_total-7)
         {
            Cycle[i] = (1.0-0.5*InpAlpha) * (1.0-0.5*InpAlpha) * (Smooth[i]-2.0*Smooth[i+1]+Smooth[i+2])
                      +2.0*(1.0-InpAlpha)*Cycle[i+1]-(1.0-InpAlpha)*(1.0-InpAlpha)*Cycle[i+2];
                   
         } else         
         {
            Cycle[i]=(Price(i)-2.0*Price(i+1)+Price(i+2))/4.0;
         }
         
         Q1[i] = (0.0962*Cycle[i]+0.5769*Cycle[i+2]-0.5769*Cycle[i+4]-0.0962*Cycle[i+6])*(0.5+0.08*InstPeriod[i+1]);
         I1[i] = Cycle[i+3];
         
         if (Q1[i]!=0.0 && Q1[i+1]!=0.0) 
            DeltaPhase[i] = (I1[i]/Q1[i]-I1[i+1]/Q1[i+1])/(1.0+I1[i]*I1[i+1]/(Q1[i]*Q1[i+1]));
         if (DeltaPhase[i] < 0.1)
            DeltaPhase[i] = 0.1;
         if (DeltaPhase[i] > 0.9)
            DeltaPhase[i] = 0.9;
        
         MedianDelta = Median(DeltaPhase, i, 5);
         
         if (MedianDelta == 0.0)
            DC = 15.0;
         else
            DC = (6.28318/MedianDelta) + 0.5;
        
         InstPeriod[i] = 0.33 * DC + 0.67 * InstPeriod[i+1];
         CyclePeriod[i] = 0.15 * InstPeriod[i] + 0.85 * CyclePeriod[i+1];
         Trigger[i] = CyclePeriod[i+1];
      }
     }
//--- возвращаем значение prev_calculated для следующего вызова
   return(rates_total);
  }
//+------------------------------------------------------------------+

double Median(double& arr[], int idx, int m_len)
{
   double MedianArr[];
   int copied;
   double result = 0.0;
   
   ArraySetAsSeries(MedianArr, true);
   ArrayResize(MedianArr, m_len);
   
   copied = ArrayCopy(MedianArr, arr, 0, idx, m_len);
   if (copied == m_len)
   {
      ArraySort(MedianArr);
      if (m_len %2 == 0) 
            result = (MedianArr[m_len/2] + MedianArr[(m_len/2)+1])/2.0;
      else
            result = MedianArr[m_len / 2];
      
   }
   else Print(__FILE__+__FUNCTION__+" ошибка в функции median - неверное количество скопированных данных."); 
   return result; 
}

Мы можем проверить его, присоединив к любому графику - он будет работать на любом инструменте и любом таймфрейме (см. рис).

CyclePeriod indicator 

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


Индикатор Cyber Cycle

Индикатор Cyber Cycle представляет собой высокочастотный фильтр из книги "Cybernetic analysis for stocks and futures".

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

Код этого и других индикаторов статьи переписан на MQL5 на основе индикаторов на языке EFL (Tradestation), приведенных в книге.

//+------------------------------------------------------------------+
//|                                                   CyberCycle.mq5 |
//|                                      Copyright 2011, Investeo.pl |
//|                                               http://Investeo.pl |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, Investeo.pl"
#property link      "http://Investeo.pl"
#property version   "1.00"
#property indicator_separate_window

#property description "Индикатор CyberCycle - предложен Джоном Элерсом (John F. Ehlers)"
#property description "в книге \"Cybernetic Analysis for Stocks and Futures\""

#property indicator_buffers 2
#property indicator_plots 2
#property indicator_width1 1
#property indicator_width2 1
#property indicator_type1   DRAW_LINE
#property indicator_type2   DRAW_LINE
#property indicator_color1  Green
#property indicator_color2  Red
#property indicator_label1  "Cycle"
#property indicator_label2  "Trigger Line"

#define Price(i) ((high[i]+low[i])/2.0)

double Smooth[];
double Cycle[];
double Trigger[];

input double InpAlpha=0.07; // alpha
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- индикаторные буферы
   ArraySetAsSeries(Cycle,true);
   ArraySetAsSeries(Trigger,true);
   ArraySetAsSeries(Smooth,true);

   SetIndexBuffer(0,Cycle,INDICATOR_DATA);
   SetIndexBuffer(1,Trigger,INDICATOR_DATA);

   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0.0);

   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---   
   long tickCnt[1];
   int i;
   int ticks=CopyTickVolume(Symbol(), 0, 0, 1, tickCnt);
   if(ticks!=1) return(rates_total);

   Comment(tickCnt[0]);

   if(prev_calculated==0 || tickCnt[0]==1)
     {
      //--- последний рассчитанный бар будет пересчитан
      int nLimit=rates_total-prev_calculated-1; // индекс начального бара для расчетов

      ArraySetAsSeries(high,true);
      ArraySetAsSeries(low,true);

      ArrayResize(Smooth,Bars(_Symbol,_Period));
      ArrayResize(Cycle,Bars(_Symbol,_Period));
      
      if(nLimit>rates_total-4) // adjust for last bars
         nLimit=rates_total-4;

      for(i=nLimit;i>=0 && !IsStopped();i--)
        {
         Smooth[i]=(Price(i)+2*Price(i+1)+2*Price(i+2)+Price(i+3))/6.0;

         if(i<rates_total-5)
           {
            Cycle[i]=(1.0-0.5*InpAlpha) *(1.0-0.5*InpAlpha) *(Smooth[i]-2.0*Smooth[i+1]+Smooth[i+2])
                     +2.0*(1.0-InpAlpha)*Cycle[i+1]-(1.0-InpAlpha)*(1.0-InpAlpha)*Cycle[i+2];
           }
         else
           {
            Cycle[i]=(Price(i)-2.0*Price(i+1)+Price(i+2))/4.0;
           }

         //Print(__FILE__+__FUNCTION__+" получено значений: ",rCnt);
         Trigger[i]=Cycle[i+1];
        }
     }
//--- возвращаем значение prev_calculated для следующего вызова
   return(rates_total);
  }
//+------------------------------------------------------------------+

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

CyberCycle indicator 

Алгоритм торговли по этому индикатору простой: покупать, если линия cycle пересекла снизу вверх сигнальную линию. Продавать следует, если линия cycle пересекла сигнальную линию сверху вниз. Предлагаю вам самостоятельно реализовать стратегию на базе этого индикатора в советнике или модуле торговых сигналов.


Адаптивный индикатор Cyber Cycle

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

Сначала нам нужно получить хэндл индикатора:

hCyclePeriod=iCustom(NULL,0,"CyclePeriod",InpAlpha);
   if(hCyclePeriod==INVALID_HANDLE)
     {
      Print("Индикатор CyclePeriod не найден!");
      return(-1);
     }

а затем запросить данные индикаторного буфера в функции OnCalculate():

int copied=CopyBuffer(hCyclePeriod,0,i,1,CyclePeriod);
    if(copied<=0)
      {
       Print("Ошибка: Ошибка получения значений индикатора CyclePeriod.");
       return -1;
      }
alpha1 = 2.0/(CyclePeriod[0]+1.0);

Значение alpha в экспоненциальном сглаживании связано с периодом простой скользящей средней Length соотношением alpha, при вычислении alpha в индикаторе адаптивного Cyber Cycle Джон Элерс использует период Dominant Cycle в качестве значения Length.

Полный код индикатора приведен ниже:

//+------------------------------------------------------------------+
//|                                           AdaptiveCyberCycle.mq5 |
//|                                      Copyright 2011, Investeo.pl |
//|                                               http://Investeo.pl |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, Investeo.pl"
#property link      "http://Investeo.pl"
#property version   "1.00"
#property indicator_separate_window

#property description "Адаптивная версия индикатора CyberCycle - предложен Джоном Элерсом (John F. Ehlers)"
#property description "в книге \"Cybernetic Analysis for Stocks and Futures\""

#property indicator_buffers 2
#property indicator_plots 2
#property indicator_width1 1
#property indicator_width2 1
#property indicator_type1   DRAW_LINE
#property indicator_type2   DRAW_LINE
#property indicator_color1  Green
#property indicator_color2  Red
#property indicator_label1  "Cycle"
#property indicator_label2  "Trigger Line"

#define Price(i) ((high[i]+low[i])/2.0)

double Smooth[];
double Cycle[];
double Trigger[];

int hCyclePeriod;
 
input double InpAlpha=0.07; // значение alpha для периода Cycle
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- индикаторные буферы 
   ArraySetAsSeries(Cycle,true);
   ArraySetAsSeries(Trigger,true);
   ArraySetAsSeries(Smooth,true);

   SetIndexBuffer(0,Cycle,INDICATOR_DATA);
   SetIndexBuffer(1,Trigger,INDICATOR_DATA);

   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0.0);

   hCyclePeriod=iCustom(NULL,0,"CyclePeriod",InpAlpha);
   if(hCyclePeriod==INVALID_HANDLE)
     {
      Print("Индикатор CyclePeriod не найден!");
      return(-1);
     }
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---   
   long tickCnt[1];
   int i;
   int ticks=CopyTickVolume(Symbol(), 0, 0, 1, tickCnt);
   if(ticks!=1) return(rates_total);
   double CyclePeriod[1],alpha1;

   Comment(tickCnt[0]);

   if(prev_calculated==0 || tickCnt[0]==1)
     {
      //--- последний рассчитанный бар будет пересчитан
      int nLimit=rates_total-prev_calculated-1; // начальный индекс для расчетов

      ArraySetAsSeries(high,true);
      ArraySetAsSeries(low,true);

      ArrayResize(Smooth,Bars(_Symbol,_Period));
      ArrayResize(Cycle,Bars(_Symbol,_Period));
      
      if(nLimit>rates_total-4) // для последних баров
         nLimit=rates_total-4;

      for(i=nLimit;i>=0 && !IsStopped();i--)
        {
         Smooth[i]=(Price(i)+2*Price(i+1)+2*Price(i+2)+Price(i+3))/6.0;
         int copied=CopyBuffer(hCyclePeriod,0,i,1,CyclePeriod);

         if(copied<=0)
           {
            Print("Ошибка: Ошибка получения значений индикатора CyclePeriod.");
            return -1;
           }
         alpha1 = 2.0/(CyclePeriod[0]+1.0);
         //Print(alpha1);
         //Print(CyclePeriod[0]);
         if(i>=0)
           {
            Cycle[i]=(1.0-0.5*alpha1) *(1.0-0.5*alpha1) *(Smooth[i]-2.0*Smooth[i+1]+Smooth[i+2])
                     +2.0*(1.0-alpha1)*Cycle[i+1]-(1.0-alpha1)*(1.0-alpha1)*Cycle[i+2];

            //Print("Smooth["+IntegerToString(i)+"]="+DoubleToString(Smooth[i])+" Cycle["+IntegerToString(i)+"]="+DoubleToString(Cycle[i]));
           }
         else
           {
            Cycle[i]=(Price(i)-2.0*Price(i+1)+Price(i+2))/4.0;
           }

         //Print(__FILE__+__FUNCTION__+" получено значений: ",rCnt);
         Trigger[i]=Cycle[i+1];
        }
     }
//--- возвращаем значение prev_calculated для следующего вызова
   return(rates_total);
  }
//+------------------------------------------------------------------+

Индикатор приведен на рисунке:

Adaptive CyberCycle indicator 

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

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


Индикатор Center of Gravity

Под центром тяжести (center of gravity) физических объектов подразумевают точку равновесия. Идея заключается в том, чтобы ввести это понятие в трейдинг, основываясь на связи величины запаздывания (lags) различных фильтров с их коэффицинтами.

Для простой скользящей средней (SMA, Simple Moving Average) все коэффициенты одинаковы, центр тяжести находится посередине.

Для взвешенной скользящей средней (WMA, Weighted Moving Average) последние цены являются более важными, чем старые. Более точно, коэффициенты WMA описывают контур треугольника.

Общее выражение для вычисления центра тяжести при заданном окне наблюдения выглядит следующим образом:

CenterOfGravity 

Положение точки равновесия получается суммированием произведения положения цены в окне на соответствующую цену (+1 введено из-за того, что нумерация производится от 0 до N, а не от 1 до N)

Главной особенностью индикатора Center of gravity является то, что он увеличивается и уменьшается вдоль колебаний и по сути является осциллятором без запаздывания (zero-lag oscillator).

Исходный код приведен ниже:

//+------------------------------------------------------------------+
//|                                              CenterOfGravity.mq5 |
//|                                      Copyright 2011, Investeo.pl |
//|                                               http://Investeo.pl |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, Investeo.pl"
#property link      "http://Investeo.pl"
#property version   "1.00"
#property indicator_separate_window

#property description "Индикатор CG - предложен Джоном Элерсом (John F. Ehlers)"
#property description "в книге \"Cybernetic Analysis for Stocks and Futures\""

#property indicator_buffers 2
#property indicator_plots 2
#property indicator_width1 1
#property indicator_width2 1
#property indicator_type1   DRAW_LINE
#property indicator_type2   DRAW_LINE
#property indicator_color1  Green
#property indicator_color2  Red
#property indicator_label1  "Cycle"
#property indicator_label2  "Trigger Line"

#define Price(i) ((high[i]+low[i])/2.0)

double Smooth[];
double Cycle[];
double Trigger[];

input double InpAlpha=0.07; // alpha
input int InpCGLength=10;   // размер окна CG

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- индикаторные буферы 
   ArraySetAsSeries(Cycle,true);
   ArraySetAsSeries(Trigger,true);
   ArraySetAsSeries(Smooth,true);
   
   SetIndexBuffer(0,Cycle,INDICATOR_DATA);
   SetIndexBuffer(1,Trigger,INDICATOR_DATA);

   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0.0);

   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---   
   long tickCnt[1];
   int i;
   double Num, Denom; // числитель и знаменатель для CG
   int ticks=CopyTickVolume(Symbol(), 0, 0, 1, tickCnt);
   if(ticks!=1) return(rates_total);

   Comment(tickCnt[0]);

   if(prev_calculated==0 || tickCnt[0]==1)
     {
      //--- последний рассчитанный бар будет пересчитан
      int nLimit=rates_total-prev_calculated-1; // начальный индекс для расчета

      ArraySetAsSeries(high,true);
      ArraySetAsSeries(low,true);

      ArrayResize(Smooth,Bars(_Symbol,_Period));
      ArrayResize(Cycle,Bars(_Symbol,_Period));

      if(nLimit>rates_total-InpCGLength) // для последних баров
         nLimit=rates_total-InpCGLength;

      for(i=nLimit;i>=0 && !IsStopped();i--)
        {
         Num = 0.0;
         Denom = 0.0;
         for (int count=0; count<InpCGLength; count++)
            {
               Num += (1.0+count)*Price(i+count);
               Denom += Price(i+count);
            }
         if (Denom != 0.0)
            Cycle[i] = -Num/Denom+(InpCGLength+1.0)/2.0;
         else
            Cycle[i] = 0.0;
         
         //Print(__FILE__+__FUNCTION__+" получено значений: ",rCnt);
         Trigger[i]=Cycle[i+1];
        }
     }
//--- возвращаем значение prev_calculated для следующего вызова
   return(rates_total);
  }
//+------------------------------------------------------------------+ 

Скриншот приведен ниже. Обратите внимание на небольшое запаздывание (lag).

  CenterOfGravity indicator


Адаптивная версия индикатора Center of Gravity

Осциллятор Center of Gravity определяет "центр тяжести" данных окна фиксированной длины. Адаптивный осциллятор Center of Gravity в качестве длины динамического окна использует период преобладающего цикла (Dominant Cycle). 

Для нахождения периода преобладающего цикла используется следующий код:

copied=CopyBuffer(hCyclePeriod,0,i,1,CyclePeriod);

if(copied<=0)
  {
   Print("Ошибка: Ошибка получения значений индикатора CyclePeriod.");
   return -1;
  }
CG_len = floor(CyclePeriod[0]/2.0);

Полный исходный код индикатора приведен ниже, сравните его с неадаптивной версией (см. также с индикатор Adaptive Cyber Cycle).

//+------------------------------------------------------------------+
//|                                      AdaptiveCenterOfGravity.mq5 |
//|                                      Copyright 2011, Investeo.pl |
//|                                               http://Investeo.pl |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, Investeo.pl"
#property link      "http://Investeo.pl"
#property version   "1.00"
#property indicator_separate_window

#property description "Адаптивная версия индикатора CG - предложен Джоном Элерсом (John F. Ehlers)"
#property description "в книге \"Cybernetic Analysis for Stocks and Futures\""

#property indicator_buffers 2
#property indicator_plots 2
#property indicator_width1 1
#property indicator_width2 1
#property indicator_type1   DRAW_LINE
#property indicator_type2   DRAW_LINE
#property indicator_color1  Green
#property indicator_color2  Red
#property indicator_label1  "Cycle"
#property indicator_label2  "Trigger Line"

#define Price(i) ((high[i]+low[i])/2.0)

double Smooth[];
double Cycle[];
double Trigger[];

int hCyclePeriod;

input double InpAlpha=0.07; // alpha
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- индикаторные буферы 
   ArraySetAsSeries(Cycle,true);
   ArraySetAsSeries(Trigger,true);
   ArraySetAsSeries(Smooth,true);

   SetIndexBuffer(0,Cycle,INDICATOR_DATA);
   SetIndexBuffer(1,Trigger,INDICATOR_DATA);

   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0.0);

   hCyclePeriod=iCustom(NULL,0,"CyclePeriod",InpAlpha);
   if(hCyclePeriod==INVALID_HANDLE)
     {
      Print("Индикатор CyclePeriod не найден!");
      return(-1);
     }

   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---   
   long tickCnt[1];
   int i, copied;
   double Num,Denom; // Числитель и знаменатель индикатора CG
   double CG_len;
   int ticks=CopyTickVolume(Symbol(), 0, 0, 1, tickCnt);
   if(ticks!=1) return(rates_total);
   double CyclePeriod[1];

   Comment(tickCnt[0]);

   if(prev_calculated==0 || tickCnt[0]==1)
     {
      //--- последний рассчитанный бар будет пересчитан
      int nLimit=rates_total-prev_calculated-1; // начальный индекс для расчета

      ArraySetAsSeries(high,true);
      ArraySetAsSeries(low,true);

      ArrayResize(Smooth,Bars(_Symbol,_Period));
      ArrayResize(Cycle,Bars(_Symbol,_Period));
      
      copied=CopyBuffer(hCyclePeriod,0,0,1,CyclePeriod);

      if(copied<=0)
        {
         Print("Ошибка: Ошибка получения значений индикатора CyclePeriod.");
         return -1;
        }

      if(nLimit>rates_total-int(CyclePeriod[0])-2) // для последних баров
         nLimit=rates_total-int(CyclePeriod[0])-2;


      for(i=nLimit;i>=0 && !IsStopped();i--)
        {
         copied=CopyBuffer(hCyclePeriod,0,i,1,CyclePeriod);

         if(copied<=0)
           {
            Print("Ошибка: Ошибка получения значений индикатора CyclePeriod.");
            return -1;
           }
         CG_len = floor(CyclePeriod[0]/2.0);
         //Print("CG_len="+DoubleToString(CG_len));
         
         Num=0.0;
         Denom=0.0;
         for(int count=0; count<int(CG_len); count++)
           {
            Num+=(1.0+count)*Price(i+count);
            Denom+=Price(i+count);
           }
         if(Denom!=0.0)
            Cycle[i]=-Num/Denom+(CG_len+1.0)/2.0;
         else
            Cycle[i]=0.0;

         //Print(__FILE__+__FUNCTION__+" получено значений: ",rCnt);
         Trigger[i]=Cycle[i+1];
        }
     }
//--- возвращаем значение prev_calculated для следующего вызова
   return(rates_total);
  }
//+------------------------------------------------------------------+

Рисунок индикатора AdaptiveCG приведен на рисунке:

 Adaptive CenterOfGravity screenshot


Индикатор RVI

RVI - это индикатор Relative Vigor Index, который основан на том факте, что для бычьих рынков цена закрытия обычно выше цены открытия, а для медвежьих рынков цена закрытия, как правило, ниже цены открытия.

Сила (vigor) движения измеряется как отношение разности цен закрытия и открытия к торговому диапазону.

RVI 

Этот индикатор хорошо известен многим пользователям терминала MetaTrader, теперь он включен в стандартную поставку.

Тем не менее, я приведу здесь его код:

//+------------------------------------------------------------------+
//|                                                          RVI.mq5 |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright   "2009, MetaQuotes Software Corp."
#property link        "http://www.mql5.com"
#property description "Relative Vigor Index"
//--- настройки индикатора
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
#property indicator_type1   DRAW_LINE
#property indicator_type2   DRAW_LINE
#property indicator_color1  Green
#property indicator_color2  Red
#property indicator_label1  "RVI"
#property indicator_label2  "Signal"
//--- входные параметры
input int InpRVIPeriod=10; // Период
//--- индикаторные буферы
double    ExtRVIBuffer[];
double    ExtSignalBuffer[];
//---
#define TRIANGLE_PERIOD  3
#define AVERAGE_PERIOD   (TRIANGLE_PERIOD*2)
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
void OnInit()
  {
//--- индикаторные буферы
   SetIndexBuffer(0,ExtRVIBuffer,INDICATOR_DATA);
   SetIndexBuffer(1,ExtSignalBuffer,INDICATOR_DATA);
   IndicatorSetInteger(INDICATOR_DIGITS,3);
//--- установим начало отрисовки
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,(InpRVIPeriod-1)+TRIANGLE_PERIOD);
   PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,(InpRVIPeriod-1)+AVERAGE_PERIOD);
//--- установка наименований линий, которые будут показываться в окне DataWindow
   IndicatorSetString(INDICATOR_SHORTNAME,"RVI("+string(InpRVIPeriod)+")");
   PlotIndexSetString(0,PLOT_LABEL,"RVI("+string(InpRVIPeriod)+")");
   PlotIndexSetString(1,PLOT_LABEL,"Signal("+string(InpRVIPeriod)+")");
//--- инициализация завершена
  }
//+------------------------------------------------------------------+
//| Relative Vigor Index                                             |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,const int prev_calculated,
                const datetime &Time[],
                const double &Open[],
                const double &High[],
                const double &Low[],
                const double &Close[],
                const long &TickVolume[],
                const long &Volume[],
                const int &Spread[])
  {
   int    i,j,nLimit;
   double dValueUp,dValueDown,dNum,dDeNum;
//--- проверка количества баров
   if(rates_total<=InpRVIPeriod+AVERAGE_PERIOD+2) return(0); // выходим с кодом возврата 0
//--- проверка возможных ошибок
   if(prev_calculated<0) return(0); // выходим с кодом возврата 0

//--- последний рассчитанный бар будет пересчитан
   nLimit=InpRVIPeriod+2;
   if(prev_calculated>InpRVIPeriod+TRIANGLE_PERIOD+2)
      nLimit=prev_calculated-1;
//--- установка empty value для нерасчитанных баров
   if(prev_calculated==0)
     {
      for(i=0;i<InpRVIPeriod+TRIANGLE_PERIOD;i++) ExtRVIBuffer[i]=0.0;
      for(i=0;i<InpRVIPeriod+AVERAGE_PERIOD;i++)  ExtSignalBuffer[i]=0.0;
     }
//--- RVI вычисляется в 1-м буфере
   for(i=nLimit;i<rates_total && !IsStopped();i++)
     {
      dNum=0.0;
      dDeNum=0.0;
      for(j=i;j>i-InpRVIPeriod;j--)
        {
         dValueUp=Close[j]-Open[j]+2*(Close[j-1]-Open[j-1])+2*(Close[j-2]-Open[j-2])+Close[j-3]-Open[j-3];
         dValueDown=High[j]-Low[j]+2*(High[j-1]-Low[j-1])+2*(High[j-2]-Low[j-2])+High[j-3]-Low[j-3];
         dNum+=dValueUp;
         dDeNum+=dValueDown;
        }
      if(dDeNum!=0.0)
         ExtRVIBuffer[i]=dNum/dDeNum;
      else
         ExtRVIBuffer[i]=dNum;
     }
//--- сигнальная линия вычилсяется во 2-м буфере
   nLimit=InpRVIPeriod+TRIANGLE_PERIOD+2;
   if(prev_calculated>InpRVIPeriod+AVERAGE_PERIOD+2)
      nLimit=prev_calculated-1;
   for(i=nLimit;i<rates_total && !IsStopped();i++) 
      ExtSignalBuffer[i]=(ExtRVIBuffer[i]+2*ExtRVIBuffer[i-1]+2*ExtRVIBuffer[i-2]+ExtRVIBuffer[i-3])/AVERAGE_PERIOD;

//--- Завершение OnCalculate. Возвращаем новое значение prev_calculated.
   return(rates_total);
  }
//+------------------------------------------------------------------+

На рисунке ниже приведен скриншот стандартного индикатора RVI с периодом 10 по умолчанию:

RVI indicator 

 

Адаптивная версия индикатора RVI

Как и для предыдущих индикаторов (точнее, их адаптивных версий), нам нужно получить значение Dominant Cycle из индикатора CyclePeriod и применить его к периоду индикатора RVI. Переменная Length вычисляется как взвешенное значение скользящей средней периода CyclePeriod, усредненное по 4 последним барам.

copied=CopyBuffer(hCyclePeriod,0,0,4,CyclePeriod);

if(copied<=0)
  {
   Print("Ошибка: Ошибка получения значений индикатора CyclePeriod.");
   return -1;
  }
AdaptiveRVIPeriod = int(floor((4*CyclePeriod[0]+3*CyclePeriod[1]+2*CyclePeriod[2]+CyclePeriod[3])/20.0));

Ниже приведен полный исходный код адаптивной версии индикатора RVI.

//+------------------------------------------------------------------+
//|                                                 Adaptive RVI.mq5 |
//|                        Based on RVI by MetaQuotes Software Corp. |
//|                        Copyright 2009, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright   "2009, MetaQuotes Software Corp."
#property copyright   "2011, Adaptive version Investeo.pl"
#property link        "http://www.mql5.com"
#property description "Adaptive Relative Vigor Index"
//--- настройки индикатора
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
#property indicator_type1   DRAW_LINE
#property indicator_type2   DRAW_LINE
#property indicator_color1  Green
#property indicator_color2  Red
#property indicator_label1  "AdaptiveRVI"
#property indicator_label2  "Signal"

#define Price(i) ((high[i]+low[i])/2.0)

//--- входные параметры
input int InpRVIPeriod=10; // Начальное значение периода RVI
//--- индикаторные буферы
double    ExtRVIBuffer[];
double    ExtSignalBuffer[];
//---
int hCyclePeriod; 
input double InpAlpha=0.07; // alpha для Cycle Period
int AdaptiveRVIPeriod;

#define TRIANGLE_PERIOD  3
#define AVERAGE_PERIOD   (TRIANGLE_PERIOD*2)
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- индикаторные буферы
   SetIndexBuffer(0,ExtRVIBuffer,INDICATOR_DATA);
   SetIndexBuffer(1,ExtSignalBuffer,INDICATOR_DATA);
   IndicatorSetInteger(INDICATOR_DIGITS,3);
   hCyclePeriod=iCustom(NULL,0,"CyclePeriod",InpAlpha);
   if(hCyclePeriod==INVALID_HANDLE)
     {
      Print("Не найден индикатор CyclePeriod!");
      return(-1);
     }
//--- установка начального бара для отображения
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,(InpRVIPeriod-1)+TRIANGLE_PERIOD);
   PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,(InpRVIPeriod-1)+AVERAGE_PERIOD);
//--- установка наименований линий, которые будут показываться в окне DataWindow
   IndicatorSetString(INDICATOR_SHORTNAME,"AdaptiveRVI");
   PlotIndexSetString(0,PLOT_LABEL,"AdaptiveRVI");
   PlotIndexSetString(1,PLOT_LABEL,"Signal");
//--- инициализация завершена
  return 0;
  }
//+------------------------------------------------------------------+
//| Relative Vigor Index                                             |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,const int prev_calculated,
                const datetime &Time[],
                const double &Open[],
                const double &High[],
                const double &Low[],
                const double &Close[],
                const long &TickVolume[],
                const long &Volume[],
                const int &Spread[])
  {
   int    i,j,nLimit;
   double dValueUp,dValueDown,dNum,dDeNum;
   double CyclePeriod[4];
   int copied;
   
   copied=CopyBuffer(hCyclePeriod,0,0,4,CyclePeriod);

   if(copied<=0)
     {
       Print("Ошибка: Ошибка получения значений индикатора CyclePeriod.");
       return -1;
     }
   AdaptiveRVIPeriod = int(floor((4*CyclePeriod[0]+3*CyclePeriod[1]+2*CyclePeriod[2]+CyclePeriod[3])/20.0));
//--- проверка количества баров
   if(rates_total<=AdaptiveRVIPeriod+AVERAGE_PERIOD+2) return(0); // возвращаем 0
//--- проверка возможных ошибок
   if(prev_calculated<0) return(0); // возвращаем 0
//--- последний рассчитанный бар будет пересчитан
   nLimit=AdaptiveRVIPeriod+2;
   if(prev_calculated>AdaptiveRVIPeriod+TRIANGLE_PERIOD+2)
      nLimit=prev_calculated-1;
//--- установка empty value для нерасчитанных баров
   if(prev_calculated==0)
     {
      for(i=0;i<AdaptiveRVIPeriod+TRIANGLE_PERIOD;i++) ExtRVIBuffer[i]=0.0;
      for(i=0;i<AdaptiveRVIPeriod+AVERAGE_PERIOD;i++)  ExtSignalBuffer[i]=0.0;
     }
//--- RVI вычисляется в 1-м буфере
   for(i=nLimit;i<rates_total && !IsStopped();i++)
     {
      copied=CopyBuffer(hCyclePeriod,0,rates_total-i-1,4,CyclePeriod);

         if(copied<=0)
           {
            Print("FAILURE: Could not get values from CyclePeriod indicator.");
            return -1;
           }
      AdaptiveRVIPeriod = int(floor((4*CyclePeriod[0]+3*CyclePeriod[1]+2*CyclePeriod[2]+CyclePeriod[3])/20.0));
      dNum=0.0;
      dDeNum=0.0;
      for(j=i;j>MathMax(i-AdaptiveRVIPeriod, 3);j--)
        {
         //Print("rates_total="+IntegerToString(rates_total)+" nLimit="+IntegerToString(nLimit)+
         //      " AdaptiveRVIPeriod="+IntegerToString(AdaptiveRVIPeriod)+" j="+IntegerToString(j));
         dValueUp=Close[j]-Open[j]+2*(Close[j-1]-Open[j-1])+2*(Close[j-2]-Open[j-2])+Close[j-3]-Open[j-3];
         dValueDown=High[j]-Low[j]+2*(High[j-1]-Low[j-1])+2*(High[j-2]-Low[j-2])+High[j-3]-Low[j-3];
         dNum+=dValueUp;
         dDeNum+=dValueDown;
        }
      if(dDeNum!=0.0)
         ExtRVIBuffer[i]=dNum/dDeNum;
      else
         ExtRVIBuffer[i]=dNum;
     }
//--- сигнальная линия вычисляется во 2-м буфере
   nLimit=AdaptiveRVIPeriod+TRIANGLE_PERIOD+2;
   if(prev_calculated>AdaptiveRVIPeriod+AVERAGE_PERIOD+2)
      nLimit=prev_calculated-1;
   for(i=nLimit;i<rates_total && !IsStopped();i++)
    ExtSignalBuffer[i]=(ExtRVIBuffer[i]+2*ExtRVIBuffer[i-1]+2*ExtRVIBuffer[i-2]+ExtRVIBuffer[i-3])/AVERAGE_PERIOD;

//--- Завершение OnCalculate. Возвратим новое значение prev_calculated.
   return(rates_total);
  }
//+------------------------------------------------------------------+ 

Скриншот адаптивного индикатора RVI с динамической шириной окна:

AdaptiveRVI indicator 


Выводы

В статье рассмотрены адаптивные версии трех технических индикаторов индикаторов и представлена их реализация на MQL5.

После чтения статьи, я надеюсь, теперь вам понятен механизм написания адаптивных индикаторов.

Автор призывает к экспериментам и созданию адаптивных версий других индикаторов.


Перевод с английского произведен MetaQuotes Software Corp.
Оригинальная статья: https://www.mql5.com/en/articles/288

Прикрепленные файлы |
adaptive_rvi.mq5 (5.66 KB)
cybercycle.mq5 (3.71 KB)
cycleperiod.mq5 (5.57 KB)
Rashid Umarov
Rashid Umarov | 12 авг 2013 в 08:33
Argo:
Статья интересная , только вот почему то я так и не смог посмотреть в деле не одного адаптированного индюка . Все которые были предложены на скачивание не захотели работать. Для меня MQL5 язык новый и разбираться самому сложновато. Или может я что не так сделал?
Что имеется в виду - не работают индкитаоры, приложенные к статье?
Automated-Trading
Automated-Trading | 12 авг 2013 в 12:46
Статья интересная , только вот почему то я так и не смог посмотреть в деле не одного адаптированного индюка . Все которые были предложены на скачивание не захотели работать. Для меня MQL5 язык новый и разбираться самому сложновато. Или может я что не так сделал?

Индикаторы CenterOfGravity, CyberCycle, CyclePeriod работают исправно.

Индикаторы AdaptiveRVI, AdaptiveCenterOfGravity, AdaptiveCyberCycle используют в своем коде индикатор CyberCycle. Имя индикатора указано явно.


Скорее всего Вы положили индикаторы в поддиректорию, в следствии чего AdaptiveRVI, AdaptiveCenterOfGravity и AdaptiveCyberCycle не запустились. Проблема решаема 2мя путями:

1. Разместить индикатор CyberCycle в корне.

2. В коде индикаторов AdaptiveRVI, AdaptiveCenterOfGravity, AdaptiveCyberCycle прописать путь до индикатора CyberCycl (например: Examples\\CyberCycle)

Aleksey Rodionov
Aleksey Rodionov | 12 авг 2013 в 16:49
Заставили задуматься.
Дмитрий Ушаков
Дмитрий Ушаков | 10 дек 2015 в 16:46
Automated-Trading:

Индикаторы CenterOfGravity, CyberCycle, CyclePeriod работают исправно.

Индикаторы AdaptiveRVI, AdaptiveCenterOfGravity, AdaptiveCyberCycle используют в своем коде индикатор CyberCycle. Имя индикатора указано явно.


Скорее всего Вы положили индикаторы в поддиректорию, в следствии чего AdaptiveRVI, AdaptiveCenterOfGravity и AdaptiveCyberCycle не запустились. Проблема решаема 2мя путями:

1. Разместить индикатор CyberCycle в корне.

2. В коде индикаторов AdaptiveRVI, AdaptiveCenterOfGravity, AdaptiveCyberCycle прописать путь до индикатора CyberCycl (например: Examples\\CyberCycle)

Разместил все индикаторы в одной папке Examples, но все Adaptive так и не заработали.

Что делать? 

Дмитрий Ушаков
Дмитрий Ушаков | 10 дек 2015 в 23:53
Скачал похожие все заработало. 
Переход на новые рельсы: пользовательские индикаторы в MQL5 Переход на новые рельсы: пользовательские индикаторы в MQL5

Я не буду перечислять все новые возможности и особенности нового терминала и языка. Их действительно много, и некоторые новинки вполне достойны освещения в отдельной статье. Вы не увидите здесь кода, написанного по принципам объектно-ориентированного программирования — это слишком серьезная тема для того, чтобы просто быть упомянутой в контексте как дополнительная вкусность для кодописателей. В этой статье остановимся подробней на индикаторах, их строении, отображении, видах, а также особенностях их написания по сравнению с MQL4.

Вот мы и получили долгожданные MetaTrader 5 и MQL5 Вот мы и получили долгожданные MetaTrader 5 и MQL5

Это очень краткий обзор MetaTrader 5. Я не могу описать все новшества системы за столь короткий период времени - тестирование стартовало 09-09-2009. Это символическая дата, и я уверен, что это будет счастливым числом. Всего несколько дней у меня на руках бета-версия терминала MetaTrader 5 и MQL5. Я не успел опробовать все, что в нем есть нового, но то, что есть, уже впечатляет.

Портфельная торговля в MetaTrader 4 Портфельная торговля в MetaTrader 4

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

Работа с корзинами валютных пар на рынке Форекс Работа с корзинами валютных пар на рынке Форекс

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