English Deutsch 日本語
preview
Создаем и оптимизируем торговую систему на основе волатильности с индикатором Чайкина

Создаем и оптимизируем торговую систему на основе волатильности с индикатором Чайкина

MetaTrader 5Трейдинг | 9 сентября 2024, 10:32
105 0
Mohamed Abdelmaaboud
Mohamed Abdelmaaboud

Введение

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

В этой статье мы рассмотрим один из индикаторов волатильности — Индикатор волатильности Чайкина (Chaikin Volatility, CHV). В статье мы поговорим о том, что можно извлечь пользу из индикатора Чайкина, как он рассчитывается и как использовать его для получения лучших результатов при торговле на финансовых рынках. Мы будем использовать индикатор на примере простых торговых стратегий (CHV Crossover и CHV and MA Crossover).

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

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

В статье рассмотрим следующие темы:

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

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



Волатильность Чайкина

В этой части мы подробно разберем сам индикатор волатильности Чайкина. Индикатор был создан Марком Чайкиным, который также создал множество других индикаторов, носящих его имя. Индикатор CHV используется для измерения волатильности движения финансовых рынков. Он может быть полезен для прогнозирования потенциальных разворотов рынка. Оценка волатильности может быть полезной для определения диапазона значений между максимумом и минимумом за определенный период времени. Так можно оценить потенциальные движения или колебания рынка в любом направлении. Индикатор CHV не учитывает разрывы, как мы увидим при его расчете. Важно отметить, что растущая волатильность может означать высокий риск или высокую доходность, и наоборот.

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

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

H-L (i) = HIGH (i) - LOW (i)

H-L (i - 10) = HIGH (i - 10) - LOW (i - 10)

CHV = (EMA (H-L (i), 10) - EMA (H-L (i - 10), 10)) / EMA (H-L (i - 10), 10) * 100

где:

  • HIGH (i) — цена максимума на текущей цене.
  • LOW (i) — цена минимума на текущей цене.
  • HIGH (i - 10) — цена максимума на свече десять свечей назад от текущей.
  • LOW (i - 10) — цена минимума на свече десять свечей назад от текущей.
  • H-L (i) — расстояние между максимумом и минимумом на текущей цене.
  • H-L (i - 10) — расстояние между максимумом и минимумом десять свечей назад.
  • EMA — экспоненциальное скользящее среднее.



Пользовательский индикатор волатильности Чайкина

В этом разделе напишем код пользовательского индикатора волатильности Чайкина с использованием MQL5. Создавая собственный код, мы можем настраивать индикатор так, как нужно нам. Рассмотрим по шагам, как написать код пользовательского индикатора волатильности Чайкина (CHV):

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

#include <MovingAverages.mqh>

Используем #property для указания дополнительных параметров, которые совпадают со следующими значениями:

  • description — краткое описание mql5-программы.
  • indicator_separate_window — размещение индикатора в отдельном окне.
  • indicator_buffers — количество буферов для расчета индикатора.
  • indicator_plots — количество графических серий в индикаторе.
  • indicator_type1 — тип графического построения, указывается значением перечисления ENUM_DRAW_TYPE. N — количество графических серий, начиная с 1.
  • indicator_color1 — цвет отображения линии N, N — номер графической серии, начиная с 1.
  • indicator_width1 — толщина линии индикатора, N — номер графической серии, начиная с 1.
#property description "Chaikin Volatility"
#property indicator_separate_window
#property indicator_buffers 3

#property indicator_plots   1
#property indicator_type1   DRAW_HISTOGRAM
#property indicator_color1  MediumBlue
#property indicator_width1  3

Используем ключевое слово enum для определения настроек для режима сглаживания скользящего среднего:

enum smoothMode
  {
   SMA=0,// Simple MA
   EMA=1 // Exponential MA
  };

Установим входные параметры для настроек индикатора с помощью ключевого слова input:

input int          smoothPeriodInp=10;  // Smoothing Period
input int          chvPeriodInp=10;     // Chaikin Volatility Period
input smoothMode   InpSmoothType=EMA;   // Smoothing Mode

Объявим три массивов буферов chv, hl и shl:

double             chvBuffer[];
double             hlBuffer[];
double             shlBuffer[];

Объявим две глобальные переменные для периода сглаживания и периода CHV:

int                smoothPeriod,chvPeriod;

В части OnInit() мы проверим и укажем переменные входных параметров.

Название скользящей средней. — после объявления maName программа проверяет, предназначены ли эти входные параметры для SMA, в этом случае имя будет SMA (простая скользящая средняя); если же они для EMA, имя будет, соответственно, EMA (экспоненциальная скользящая средняя).

   string maName;
   if(InpSmoothType==SMA)
      maName="SMA";
   else
      maName="EMA";

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

   if(smoothPeriodInp<=0)
     {
      smoothPeriod=10;
      printf("Incorrect value for Smoothing Period input = %d. Default value = %d.",smoothPeriodInp,smoothPeriod);
     }
   else smoothPeriod=smoothPeriodInp;

Период CHV — проверяем, если период CHV меньше или равен ли он нулю, по умолчанию будет установлено значение 10, и будет выведено сообщение. Если отличное от 0, будет указано в том виде, в котором оно было введено.

   if(chvPeriodInp<=0)
     {
      chvPeriod=10;
      printf("Incorrect value for Chaikin Volatility Period input = %d. Default value = %d.",chvPeriodInp,chvPeriod);
     }
   else chvPeriod=chvPeriodInp;

Определяет объявленные буферы с помощью ключевого слова SetIndexBuffer для возврата логического значения. Параметры:

  • index — номер буфера индикатора, начинается с нуля, меньше значения #property идентификатора indicator_buffers.
  • buffer[] — массив, объявленный в пользовательском индикаторе.
  • data_type — тип данных, хранящихся в массиве. Значение по умолчанию для chvBuffer — INDICATOR_DATA, а значение INDICATOR_CALCULATIONS мы укажем для использования в промежуточных расчетах, не в построении.
   SetIndexBuffer(0,chvBuffer);
   SetIndexBuffer(1,hlBuffer,INDICATOR_CALCULATIONS);
   SetIndexBuffer(2,shlBuffer,INDICATOR_CALCULATIONS);

Указываем настройки построения:

   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,smoothPeriod+chvPeriod-1);
   PlotIndexSetString(0,PLOT_LABEL,"CHV("+string(smoothPeriod)+","+maName+")");
   IndicatorSetString(INDICATOR_SHORTNAME,"Chaikin Volatility("+string(smoothPeriod)+","+maName+")");
   IndicatorSetInteger(INDICATOR_DIGITS,1);

В OnCalculate объявим три целочисленные переменные:

   int    i,pos,posCHV;

Определим переменную posCHV равной результату 2 - chvPeriod и smoothPeriod:

   posCHV=chvPeriod+smoothPeriod-2;

Чтобы проверить, меньше ли rateTotal значения posCHV, нужно вернуть нулевое значение:

   if(rates_total<posCHV)
      return(0);

Определим значение pos после проверки: если prev_calculated меньше 1, значение pos будет равно нулю, в противном случае pos будет равен prev_calculated -1.

   if(prev_calculated<1)
      pos=0;
   else pos=prev_calculated-1;

Определяем hlBuffer[i]

   for(i=pos;i<rates_total && !IsStopped();i++)
     {
      hlBuffer[i]=High[i]-Low[i];
     }

Определяем буфер smoothedhl (shl):

   if(pos<smoothPeriod-1)
     {
      pos=smoothPeriod-1;
      for(i=0;i<pos;i++)
        {
         shlBuffer[i]=0.0;
        }
     }

Определяем простую и экспоненциальную скользящую среднюю с помощью функций SimpleMAOnBuffer и ExponentialMAOnBuffer:

   if(InpSmoothType==SMA)
     {
      SimpleMAOnBuffer(rates_total,prev_calculated,0,smoothPeriod,hlBuffer,shlBuffer);
     }
   else
      ExponentialMAOnBuffer(rates_total,prev_calculated,0,smoothPeriod,hlBuffer,shlBuffer);

Обновляем pos после проверки: меньше ли pos, чем posCHV:

   if(pos<posCHV)
     {
      pos=posCHV;
     }

Определение буфера CHV:

   for(i=pos;i<rates_total && !IsStopped();i++)
     {
      if(shlBuffer[i-chvPeriod]!=0.0)
         chvBuffer[i]=100.0*(shlBuffer[i]-shlBuffer[i-chvPeriod])/shlBuffer[i-chvPeriod];
      else
         chvBuffer[i]=0.0;
     }

Возвращаем rates_total:

return(rates_total);

Ниже представлен полный код в одном блоке:

//+------------------------------------------------------------------+
//|                                           Chaikin Volatility.mq5 |
//+------------------------------------------------------------------+
#include <MovingAverages.mqh>
#property description "Chaikin Volatility"
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_plots   1
#property indicator_type1   DRAW_HISTOGRAM
#property indicator_color1  MediumBlue
#property indicator_width1  3
enum smoothMode
  {
   SMA=0,// Simple MA
   EMA=1 // Exponential MA
  };
input int          smoothPeriodInp=10;  // Smoothing Period
input int          chvPeriodInp=10;     // Chaikin Volatility Period
input smoothMode   InpSmoothType=EMA;   // Smoothing Mode
double             chvBuffer[];
double             hlBuffer[];
double             shlBuffer[];
int                smoothPeriod,chvPeriod;
void OnInit()
  {
   string maName;
   if(InpSmoothType==SMA)
      maName="SMA";
   else
      maName="EMA";
   if(smoothPeriodInp<=0)
     {
      smoothPeriod=10;
      printf("Incorrect value for Smoothing Period input = %d. Default value = %d.",smoothPeriodInp,smoothPeriod);
     }
   else
      smoothPeriod=smoothPeriodInp;
   if(chvPeriodInp<=0)
     {
      chvPeriod=10;
      printf("Incorrect value for Chaikin Volatility Period input = %d. Default value = %d.",chvPeriodInp,chvPeriod);
     }
   else
      chvPeriod=chvPeriodInp;
   SetIndexBuffer(0,chvBuffer);
   SetIndexBuffer(1,hlBuffer,INDICATOR_CALCULATIONS);
   SetIndexBuffer(2,shlBuffer,INDICATOR_CALCULATIONS);
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,smoothPeriod+chvPeriod-1);
   PlotIndexSetString(0,PLOT_LABEL,"CHV("+string(smoothPeriod)+","+maName+")");
   IndicatorSetString(INDICATOR_SHORTNAME,"Chaikin Volatility("+string(smoothPeriod)+","+maName+")");
   IndicatorSetInteger(INDICATOR_DIGITS,1);
  }
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,pos,posCHV;
   posCHV=chvPeriod+smoothPeriod-2;
   if(rates_total<posCHV)
      return(0);
   if(prev_calculated<1)
      pos=0;
   else
      pos=prev_calculated-1;
   for(i=pos;i<rates_total && !IsStopped();i++)
     {
      hlBuffer[i]=High[i]-Low[i];
     }
   if(pos<smoothPeriod-1)
     {
      pos=smoothPeriod-1;
      for(i=0;i<pos;i++)
        {
         shlBuffer[i]=0.0;
        }
     }
   if(InpSmoothType==SMA)
     {
      SimpleMAOnBuffer(rates_total,prev_calculated,0,smoothPeriod,hlBuffer,shlBuffer);
     }
   else
      ExponentialMAOnBuffer(rates_total,prev_calculated,0,smoothPeriod,hlBuffer,shlBuffer);
   if(pos<posCHV)
     {
      pos=posCHV;
     }
   for(i=pos;i<rates_total && !IsStopped();i++)
     {
      if(shlBuffer[i-chvPeriod]!=0.0)
         chvBuffer[i]=100.0*(shlBuffer[i]-shlBuffer[i-chvPeriod])/shlBuffer[i-chvPeriod];
      else
         chvBuffer[i]=0.0;
     }
   return(rates_total);
  }

После компиляции этого кода получаем индикатор, который запускаем на графике в терминале и видим такую картину:

CHVInda

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



Стратегии торговли по волатильности Чайкина

В этой части посмотрим, как можно использовать индикатор волатильности Чайкина для включения данных о волатильности в торговую стратегию. Мы будем использовать только сам индикатор Чайкина и принимать решения на основе его значений. Протестируем эту стратегию, чтобы понять, можно ли таким образом получить прибыльные результаты.

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

Итак, вот эти стратегии:

Пересечение индикатора Чайкина:

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

Схематично это можно представить так:

Значение CHV > 0 => позиция на покупку

Значение CHV < 0 => позиция на продажу

Пересечение CHV и MA:

Эта стратегия будет генерировать сигналы и размещать позиции на покупку и продажу на основе пересечения значения CHV и нулевого уровня, принимая при этом во внимание направление скользящей средней. Если значение Чайкина выше нуля, а скользящая средняя ниже цены закрытия, размещаем позицию на покупку. И наоборот, если индикатор Чайкина ниже нуля, а скользящая средняя выше цены закрытия, размещаем позицию на продажу.

Схематично это можно представить так:

CHV > 0 и цена Close > MA => позиция на покупку

CHV < 0 и цена Close < MA => позиция на продажу



Система по волатильности Чайкина

В этой части создадим на MQL5 торговые системы по каждой из рассмотренных стратегий. Также протестируем их и попробуем определить, можно ли их оптимизировать, чтобы улучшить показатели. Сначала создадим простой советник, который станет основой для торговых систем этих двух стратегий. Советник будет отображать значения CHV в качестве комментария на графике. Рассмотрим по шагам, как это сделать:

Объявляем входные параметры торговой системы на основе индикатора:

enum SmoothMethod
  {
   SMA=0,// Simple MA
   EMA=1 // Exponential MA
  };
input int          InpSmoothPeriod=10;  // Smoothing period
input int          InpCHVPeriod=10;     // Chaikin Volatility period
input SmoothMethod InpSmoothType=EMA;   // Smoothing method

Объявление целочисленной переменной chv для значений индикатора:

int chv;

В OnInit() советника определим переменную chv. Ее значение равно функции iCustom, которая возвращает дескриптор индикатора CHV. Параметры iCustom:

  • symbol — имя символа; у нас это _Symbol, то есть рассчитываем индикатор по символу текущего графика.
  • period - таймфрейм для расчета; значение PERIOD_CURRENT означает, что индикатор будет рассчитываться на текущем таймфрейме.
  • name — имя индикатора.
  • Указание входных параметров индикатора (smoothPeriodInp,chvPeriodInp и smoothTypeInp)
chv = iCustom(_Symbol,PERIOD_CURRENT,"Custom_CHV",smoothPeriodInp,chvPeriodInp, smoothTypeInp);

В OnDeinit() выведем сообщение при удалении советника:

Print("EA is removed");

В OnTick() объявляем массив chvInd:

double chvInd[];

Получаем указанные данные буфера индикатора CHV с помощью ключевого слова CopyBuffer. Параметры:

  • indicator_handle - хэндл индикатора, здесь - chv 
  • buffer_num - номер индикаторного буфера, в данном примере это 0.
  • start_pos - стартовая позиция равна 0.
  • count - копируемый объем, равен 3
  • buffer[] — целевой массив, который нам нужно скопировать, это chvInd
CopyBuffer(chv,0,0,3,chvInd);

Установим флаг AS_Series для массивов с помощью функции ArraySetAsSeries и ее параметров:

  • array[] - указать массив по ссылке, это chvInd 
  • flag — флаг в значении true, означает обратный порядок индексации 
ArraySetAsSeries(chvInd,true);

Объявляем и определяем chvVal для возврата текущего значения индикатора:

double chvVal = NormalizeDouble(chvInd[0], 1);

Выводим текущее значение в комментарии на графике:

Comment("CHV value = ",chvVal);

Вот такой блок кода у нас получился:

//+------------------------------------------------------------------+
//|                                                       chvVal.mq5 |
//+------------------------------------------------------------------+
enum SmoothMethod
  {
   SMA=0,// Simple MA
   EMA=1 // Exponential MA
  };
input int          smoothPeriodInp=10;  // Smoothing period
input int          chvPeriodInp=10;     // Chaikin Volatility period
input SmoothMethod smoothTypeInp=EMA;   // Smoothing method
int chv;
int OnInit()
  {
   chv = iCustom(_Symbol,PERIOD_CURRENT,"Custom_CHV",smoothPeriodInp,chvPeriodInp, smoothTypeInp);
   return(INIT_SUCCEEDED);
  }
void OnDeinit(const int reason)
  {
   Print("EA is removed");
  }
void OnTick()
  {
    double chvInd[];
    CopyBuffer(chv,0,0,3,chvInd);
    ArraySetAsSeries(chvInd,true);
    double chvVal = NormalizeDouble(chvInd[0], 1);
    Comment("CHV value = ",chvVal);
  }
//+------------------------------------------------------------------+

После компиляции кода и запуска программы текущее значение индикатора отобразится на графике:

chvVal

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

chvValSame

Как видите, текущее значение индикатора Чайкина, Выведенное в комментарии на графике, совпадает со значением запущенного индикатора. Теперь создадим нашу торговую систему по первой стратегии (CHV Crossover, пересечение с нулем).

Пересечение индикатора Чайкина:

Ниже показан полный код стратегии по пересечению индикатора CHV с уровнем нуля:

//+------------------------------------------------------------------+
//|                                                 chvCrossover.mq5 |
//+------------------------------------------------------------------+
#include <trade/trade.mqh>
enum smoothMode
  {
   SMA=0,// Simple MA
   EMA=1 // Exponential MA
  };
input int          smoothPeriodInp=10;  // Smoothing period
input int          chvPeriodInp=10;     // Chaikin Volatility period
input smoothMode   smoothTypeInp=EMA;   // Smoothing Mode
input double       lotSize=1;
input double slPips = 300;
input double tpPips = 600;

int chv;
int barsTotal;
CTrade trade;
int OnInit()
  {
   barsTotal=iBars(_Symbol,PERIOD_CURRENT);
   chv = iCustom(_Symbol,PERIOD_CURRENT,"Custom_CHV",smoothPeriodInp,chvPeriodInp, smoothTypeInp, lotSize, slPips, tpPips);
   return(INIT_SUCCEEDED);
  }
void OnDeinit(const int reason)
  {
   Print("EA is removed");
  }
void OnTick()
  {
   int bars=iBars(_Symbol,PERIOD_CURRENT);
   if(barsTotal < bars)
     {
      barsTotal=bars;
      double chvInd[];
      CopyBuffer(chv,0,0,3,chvInd);
      ArraySetAsSeries(chvInd,true);
      double chvVal = NormalizeDouble(chvInd[0], 1);
      double chvValPrev = NormalizeDouble(chvInd[1], 1);
      if(chvVal>0)
        {
         double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
         double slVal = ask - slPips*_Point;
         double tpVal = ask + tpPips*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }
      if(chvVal<0)
        {
         double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
         double slVal = bid + slPips*_Point;
         double tpVal = bid - tpPips*_Point;
         trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
        }
     }
  }
//+------------------------------------------------------------------+

Разберем этот код:

Подключаем файл торговых функций.

#include <trade/trade.mqh>

Объявляем входные параметры: lotSize, slPips и tpPips. Они указываются пользователем.

input double      lotSize=1;
input double slPips = 300;
input double tpPips = 600;

Объявление целочисленной переменной barsTotal, которая будет использоваться в качестве кода для каждого нового бара.

int barsTotal;

Объявляем объект торговой операции.

CTrade trade;

В OnInit() определяем переменную barsTotal.

barsTotal=iBars(_Symbol,PERIOD_CURRENT);

Добавляем три параметра: lotSize, slPips и tpPips для iCustom.

chv = iCustom(_Symbol,PERIOD_CURRENT,"Custom_CHV",smoothPeriodInp,chvPeriodInp, smoothTypeInp, lotSize, slPips, tpPips);

Объявление и определение целочисленной переменной bar.

int bars=iBars(_Symbol,PERIOD_CURRENT);

Проверяем, значение barsTotal меньше ли необходимого количества баров.

Для этого обновляем значение barsTotal равным значению bars.

barsTotal=bars;

Объявляем массив chvInd.

double chvInd[];

Извлекаем указанные данные буфера индикатора CHV, используем CopyBuffer.

CopyBuffer(chv,0,0,3,chvInd);

Установим флаг AS_SERIES для выбранного массива с ключевым словом ArraySetAsSeries, как показано ниже.

ArraySetAsSeries(chvInd,true);

Объявляем и определяем chvVal, чтобы вернуть текущее и предыдущее значения индикатора.

      double chvVal = NormalizeDouble(chvInd[0], 1);
      double chvValPrev = NormalizeDouble(chvInd[1], 1);

Установим условие позиции на покупку, чтобы советник автоматически размещал позицию на покупку. Для этого проверяем значение CHV, и если больше нуля, устанавливаем позицию на покупку.

      if(chvVal>0)
        {
         double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
         double slVal = ask - slPips*_Point;
         double tpVal = ask + tpPips*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }

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

      if(chvVal<0)
        {
         double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
         double slVal = bid + slPips*_Point;
         double tpVal = bid - tpPips*_Point;
         trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
        }

Компилируем код и запускаем советник. На графике ниже видно, что советник работает. Пример с открытием позиции на покупку:

chvCrossover_buySignal

А вот пример с позицией на продажу:

chvCrossover_sellSignal

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

  • Чистая прибыль (Net Profit) — рассчитывается путем вычитания валового убытка из валовой прибыли. Чем выше значение, тем лучше.
  • Относительная просадка по балансу (Balance DD relative) — максимальный убыток на счете во время работы. Чем ниже значение, тем лучше.
  • Профит-фактор (Profit factor) — отношение валовой прибыли к валовому убытку. Чем выше значение, тем лучше.
  • Матожидание выигрыша (Expected payoff) — средняя прибыль или убыток сделки. Чем выше значение, тем лучше.
  • Фактор восстановления (Recovery factor) — насколько хорошо протестированная стратегия восстанавливается после потерь. Чем выше значение, тем лучше.
  • Коэффициент Шарпа (Sharpe Ratio) — риск и стабильность тестируемой торговой системы путем сравнения доходности с безрисковой доходностью. Чем выше значение, тем лучше.

Результаты представлены на графиках ниже. Тестируем на периоде в один год (с 01.01.2023 по 31.12.2023) на символе EURUSD с часовым таймфреймом.

chvCrossover_result

chvCrossover_result2

chvCrossover_result1

По результатам тестирования получили следующие показатели эффективности:

  • Чистая прибыль: -35936.34 USD.
  • Относительная просадка по балансу: 48.12%.
  • Профит-фактор: 0,94.
  • Матожидание выигрыша: -6.03.
  • Фактор восстановления: -0.62.
  • Коэффициент Шарпа: -1.22.

Результаты, Мягко говоря, не очень хороший. Попробуем оптимизировать их, чтобы получить результаты получше. Для этого изменим саму стратегию.

Пересечение CHV и MA:

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

//+------------------------------------------------------------------+
//|                                             chv_MA_Crossover.mq5 |
//+------------------------------------------------------------------+
#include <trade/trade.mqh>
enum smoothMode
  {
   SMA=0,// Simple MA
   EMA=1 // Exponential MA
  };
input int          InpSmoothPeriod=10;  // Smoothing period
input int          InpCHVPeriod=10;     // Chaikin Volatility period
input smoothMode smoothTypeInp=EMA;   // Smoothing Mode
input int InpMAPeriod=10; //MA Period
input ENUM_MA_METHOD InpMAMode=MODE_EMA; // MA Mode
input double      lotSize=1;
input double slPips = 300;
input double tpPips = 600;
int chv;
int ma;
int barsTotal;
CTrade trade;
int OnInit()
  {
   barsTotal=iBars(_Symbol,PERIOD_CURRENT);
   chv = iCustom(_Symbol,PERIOD_CURRENT,"Custom_CHV",InpSmoothPeriod,InpCHVPeriod, smoothTypeInp, lotSize, slPips, tpPips);
   ma=iMA(_Symbol,PERIOD_CURRENT, InpMAPeriod, 0, InpMAMode, PRICE_CLOSE);
   return(INIT_SUCCEEDED);
  }
void OnDeinit(const int reason)
  {
   Print("EA is removed");
  }
void OnTick()
  {
   int bars=iBars(_Symbol,PERIOD_CURRENT);
   if(barsTotal < bars)
     {
      barsTotal=bars;
      double chvInd[];
      double maInd[];
      CopyBuffer(chv,0,0,3,chvInd);
      ArraySetAsSeries(chvInd,true);
      CopyBuffer(ma,0,0,3,maInd);
      ArraySetAsSeries(maInd,true);
      double chvVal = NormalizeDouble(chvInd[0], 1);
      double chvValPrev = NormalizeDouble(chvInd[1], 1);
      double maVal = NormalizeDouble(maInd[0], 5);
      double maValPrev = NormalizeDouble(maInd[1], 5);
      double lastClose=iClose(_Symbol,PERIOD_CURRENT,1);
      double prevLastClose=iClose(_Symbol,PERIOD_CURRENT,2);

      if(prevLastClose<maValPrev && lastClose>maVal && chvVal>0)
        {
         double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
         double slVal = ask - slPips*_Point;
         double tpVal = ask + tpPips*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }
      if(prevLastClose>maValPrev && lastClose<maVal && chvVal<0)
        {
         double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
         double slVal = bid + slPips*_Point;
         double tpVal = bid - tpPips*_Point;
         trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
        }
     }
  }
//+------------------------------------------------------------------+

Разберем этот код:

Объявляем целочисленную переменную ma для значений скользящей средней.

int ma;

В части OnInit() определим ma через значение iMA, которое возвращает дескриптор скользящей средней. Параметры:

  • symbol — имя символа; у нас это _Symbol, то есть рассчитываем индикатор по символу текущего графика. 
  • period — таймфрейм для расчета; значение PERIOD_CURRENT означает, что индикатор будет рассчитываться на текущем таймфрейме.
  • ma_period — период скользящей средней. Мы используем MAperiod.
  • ma_shift — сдвиг линии скользящей средней по горизонтали.
  • ma_method — метод сглаживания скользящей средней, MA mode 
  • applied_price — тип цены, используемой для расчета; используем цену закрытия
ma=iMA(_Symbol,PERIOD_CURRENT, InpMAPeriod, 0, InpMAMode, PRICE_CLOSE);

Проверяем наличие нового бара и выполняем следующий код:

Declaring maInd[] array.

double maInd[];

Получение данных указанного буфера скользящей средней через CopyBuffer и установка флага AS_SERIES для выбранного массива с помощью ArraySetAsSeries

      CopyBuffer(ma,0,0,3,maInd);
      ArraySetAsSeries(maInd,true);

Определение следующих значений:

Текущая скользящая средняя, предыдущая скользящая средняя, последнее закрытие и предыдущая цена последнего закрытия.

      double maVal = NormalizeDouble(maInd[0], 5);
      double maValPrev = NormalizeDouble(maInd[1], 5);
      double lastClose=iClose(_Symbol,PERIOD_CURRENT,1);
      double prevLastClose=iClose(_Symbol,PERIOD_CURRENT,2);

Задаем условия позиции на покупку: если предыдущее значение последней цены закрытия меньше предыдущего значения скользящей средней и при этом последняя цены закрытия больше текущей скользящей средней + текущее значение индикатора Чайкина больше нуля, советник разместит позицию на покупку.

      if(prevLastClose<maValPrev && lastClose>maVal && chvVal>0)
        {
         double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
         double slVal = ask - slPips*_Point;
         double tpVal = ask + tpPips*_Point;
         trade.Buy(lotSize,_Symbol,ask,slVal,tpVal);
        }

Задаем условия позиции на продажу: если предыдущее значение последней цены закрытия больше предыдущего значения скользящей средней и при этом последняя цены закрытия ниже текущей скользящей средней + текущее значение индикатора Чайкина ниже нуля, советник разместит позицию на продажу.

      if(prevLastClose>maValPrev && lastClose<maVal && chvVal<0)
        {
         double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
         double slVal = bid + slPips*_Point;
         double tpVal = bid - tpPips*_Point;
         trade.Sell(lotSize,_Symbol,bid,slVal,tpVal);
        }

Компилируем код и запускаем советник. На графике ниже видно, что советник работает. Пример с открытием позиции на покупку:

chvMACrossover_buySignal

А вот пример с позицией на продажу:

chvMACrossover_sellSignal

На тестировании этой стратегии в течение года (с 01.01.2023 по 31.12.2023) на валютной паре EURUSD и таймфрейме в 1 час получили следующие результаты.

chvMACrossover_result

chvMACrossover_result2

chvMACrossover_result1

По результатам тестирования получили следующие показатели эффективности:

  • Чистая прибыль: 20817.39 USD.
  • Относительная просадка по балансу: 9.62%.
  • Профит-фактор: 1.15.
  • Матожидание выигрыша: 29.28.
  • Фактор восстановления: 1.69.
  • Коэффициент Шарпа: 1.71.

На основании результатов предыдущих тестирований можно сделать вывод, что добавление еще одного технического инструмента, а именно скользящего среднего, позволило нам получить лучшие результаты. Итак, стратегия пересечения индикатора Чайкина и MA и часового таймфрейма показала:

  • Чистая прибыль — значение намного выше (20817.39 долларов США).
  • Относительная просадка по балансу — низкое значение (9.62%).
  • Профит-фактор — достаточно высокое значение (1.15).
  • Матожидание выигрыша — достаточно высокое значение (29.28).
  • Фактор восстановления — достаточно высокое значение (1.69).
  • Коэффициент Шарпа: — значение выше (1.71).



Заключение

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

Мы рассмотрели две простые стратегии на основе волатильности: чистую и в сочетании со скользящей средней. Мы увидели, как можно создать собственный индикатор Чайкина на MQL5 — такой инструмент можно легко подстраивать под собственные предпочтения и использовать без ограничений в торговых стратегиях. Рассмотрели две стратегии6

  • Пересечение индикатора Чайкина
  • Пересечение индикатора Чайкина и MA:

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

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

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

Прикрепленные файлы |
Custom_CHV.mq5 (3.24 KB)
chvVal.mq5 (0.97 KB)
chvCrossover.mq5 (1.8 KB)
Нейросети в трейдинге: Transformer для облака точек (Pointformer) Нейросети в трейдинге: Transformer для облака точек (Pointformer)
В данной статье мы поговорим об алгоритмах использования методов внимания при решении задач обнаружения объектов в облаке точек. Обнаружение объектов в облаках точек имеет важное значение для многих реальных приложений.
Разработка системы репликации (Parte 46): Проект Chart Trade (V) Разработка системы репликации (Parte 46): Проект Chart Trade (V)
Устали тратить время на поиск того самого файла, который необходим для работы вашего приложения? Как насчет того, чтобы включить все в исполняемый файл? Так вы больше не будете тратить время на поиск необходимого. Знаю, что многие пользуются именно такой формой распространения и хранения вещей, но есть гораздо более подходящий способ. По крайней мере, что касается распространения исполняемых файлов и их хранения. Метод, который будет здесь представлен, может оказаться очень полезным, так как в качестве отличного помощника вы сможете использовать сам MetaTrader 5, а также MQL5. И это не так уж трудно и сложно для понимания.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Оптимизация бактериальным хемотаксисом — Bacterial Chemotaxis Optimization (BCO) Оптимизация бактериальным хемотаксисом — Bacterial Chemotaxis Optimization (BCO)
В статье представлена оригинальная версия алгоритма бактериальной хемотаксисной оптимизации (BCO) и его модифицированный вариант. Мы подробно рассмотрим все отличия, уделяя особое внимание новой версии BCOm, которая упрощает механизм движения бактерий, снижает зависимость от истории изменений позиций и использует более простые математические операции по сравнению с перегруженной вычислениями оригинальной версией. Также будут проведены тесты и подведены итоги.