Español Português
preview
От начального до среднего уровня: Индикатор (V)

От начального до среднего уровня: Индикатор (V)

MetaTrader 5Примеры |
109 0
CODE X
CODE X

Введение

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

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

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

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


Разница между статическим и динамическим индикатором

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

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

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_type1       DRAW_COLOR_CANDLES
05. #property indicator_color1      clrRed, clrRoyalBlue, clrGreen
06. //+----------------+
07. #property indicator_buffers     5
08. #property indicator_plots       1
09. //+----------------+
10. double  gl_Buff_High[],
11.         gl_Buff_Open[],
12.         gl_Buff_Close[],
13.         gl_Buff_Low[],
14.         gl_Buff_Color[];
15. //+------------------------------------------------------------------+
16. int OnInit()
17. {
18.    SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA);
19.    SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA);
20.    SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA);
21.    SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA);
22.    SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX);
23. 
24.    return INIT_SUCCEEDED;
25. };
26. //+------------------------------------------------------------------+
27. 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[])
28. {
29.    static double  high = DBL_MIN,
30.                   low = DBL_MAX;
31. 
32.    for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++)
33.    {
34.       gl_Buff_High[c] = High[c];
35.       gl_Buff_Open[c] = Open[c];
36.       gl_Buff_Close[c] = Close[c];
37.       gl_Buff_Low[c] = Low[c];
38. 
39.       gl_Buff_Color[c] = (Open[c] > Close[c] ? 0 : 2);
40.       if ((c - 1) > 0)
41.       {
42.          high = (High[c - 1] > high ? High[c - 1] : high);
43.          low = (Low[c - 1] < low ? Low[c - 1] : low);
44.          gl_Buff_Color[c] = ((high > High[c]) && (low < Low[c]) ? 1 : gl_Buff_Color[c]);
45.          if (gl_Buff_Color[c] != 1)
46.          {
47.             high = DBL_MIN;
48.             low = DBL_MAX;
49.          }
50.       }
51.    }
52. 
53.    return rates_total;
54. };
55. //+------------------------------------------------------------------+

Код 01

При выполнении код 01 преобразует график в индикатор. В данном случае это индикатор Inside Bar, как можно видеть на изображении ниже.

Изображение 01

Итак, мы знаем, как работает код 01, по крайней мере, в его базовой части, именно потому, что при его введении объяснялось, как объявлять те или иные параметры. Однако данный индикатор, обозначенный кодом 01, насколько я понимаю, является статическим индикатором. И причина кроется именно в объявлениях, сделанных в строках 04 и 05. И пожалуйста, уважаемые читатели, не поймите меня неправильно. Я утверждаю, что индикатор в коде 01 является статическим, потому что после компиляции мы не можем изменить сам индикатор, так как он был объявлен с использованием директив компиляции MQL5, которые как раз и содержат упомянутые строки.

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

Как работает динамический индикатор? Каковы преимущества его внедрения? Давайте двигаться шаг за шагом. Во-первых, динамический индикатор не должен содержать в своем коде строки 04 и 05, которые видны в коде 01. Это первое и самое существенное различие. Преимущество заключалось бы именно в отсутствии этих двух строк. Возможно, это покажется странным, но вы лучше поймете подобные вещи позже, когда мы перейдем к более сложным моделям.

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

А теперь будьте внимательны. То, что мы здесь сделаем, не изменит принцип работы индикатора из кода 01. Мы просто создадим способ реализации этих действий с помощью кода. Иными словами, часть действий, которые обычно выполняются посредством директив, будет осуществляться с помощью вызовов стандартной библиотеки MQL5. Первым делом нужно удалить строки, которые превращают индикатор из кода 01 в статический индикатор. Удалив статическую часть, мы получим следующее:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_buffers     5
05. #property indicator_plots       1
06. //+----------------+
07. double  gl_Buff_High[],
08.         gl_Buff_Open[],
09.         gl_Buff_Close[],
10.         gl_Buff_Low[],
11.         gl_Buff_Color[];
12. //+------------------------------------------------------------------+
13. int OnInit()
14. {
15.    SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA);
16.    SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA);
17.    SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA);
18.    SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA);
19.    SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX);
20. 
21.    return INIT_SUCCEEDED;
22. };
23. //+------------------------------------------------------------------+
24. 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[])
25. {
26.    static double  high = DBL_MIN,
27.                   low = DBL_MAX;
28. 
29.    for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++)
30.    {
31.       gl_Buff_High[c] = High[c];
32.       gl_Buff_Open[c] = Open[c];
33.       gl_Buff_Close[c] = Close[c];
34.       gl_Buff_Low[c] = Low[c];
35. 
36.       gl_Buff_Color[c] = (Open[c] > Close[c] ? 0 : 2);
37.       if ((c - 1) > 0)
38.       {
39.          high = (High[c - 1] > high ? High[c - 1] : high);
40.          low = (Low[c - 1] < low ? Low[c - 1] : low);
41.          gl_Buff_Color[c] = ((high > High[c]) && (low < Low[c]) ? 1 : gl_Buff_Color[c]);
42.          if (gl_Buff_Color[c] != 1)
43.          {
44.             high = DBL_MIN;
45.             low = DBL_MAX;
46.          }
47.       }
48.    }
49. 
50.    return rates_total;
51. };
52. //+------------------------------------------------------------------+

Код 02

Код 02, хотя и компилируется, НЕ РАБОТАЕТ. По крайней мере, это никак не повлияет на график, на котором он размещен. Однако это на самом деле не проблема. Так произошло, потому что мы удалили ту часть, которая сообщает MetaTrader 5, как именно будет использоваться индикатор. Таким образом, MetaTrader 5 знает лишь то, что у нас есть четыре буфера данных и один буфер цвета, и что будет построен только один график. Но MetaTrader 5 НЕ ЗНАЕТ, как использовать эти буферы, хотя они и настроены для отображения некоторой информации. Поэтому на графике ничего не происходит.

Хорошо, как нам решить эту проблему? Каким образом указать MetaTrader 5, как использовать буферы, объявленные в коде 02? Что ж, вот это и есть самая интересная часть, потому что в данный момент мы будем динамически указывать MetaTrader 5, как следует использовать буферы. Это достигается добавлением нескольких новых строк в код, как можно видеть ниже.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_buffers     5
05. #property indicator_plots       1
06. //+----------------+
07. double  gl_Buff_High[],
08.         gl_Buff_Open[],
09.         gl_Buff_Close[],
10.         gl_Buff_Low[],
11.         gl_Buff_Color[];
12. //+------------------------------------------------------------------+
13. int OnInit()
14. {
15.    const color cor[] = {clrRed, clrRoyalBlue, clrGreen};
16. 
17.    PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES);   
18.    PlotIndexSetInteger(0, PLOT_COLOR_INDEXES, cor.Size());
19.    for (uint c = 0; c < cor.Size(); c++)
20.       PlotIndexSetInteger(c, PLOT_LINE_COLOR, c, cor[c]);
21. 
22.    SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA);
23.    SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA);
24.    SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA);
25.    SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA);
26.    SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX);
27. 
28.    return INIT_SUCCEEDED;
29. };
30. //+------------------------------------------------------------------+
                   .
                   .
                   .

Код 03

Поскольку конечный результат совпадает с тем, что показываем на изображении 01, я не буду его повторять. Однако при использовании динамического индикатора, который мы видим во фрагменте кода 03, всё будет работать несколько иначе, чем при использовании статического индикатора в коде 01.

Но прежде, чем продолжить, давайте разберемся, что было сделано во фрагменте кода 03, где показана только та часть, которая нас интересует. Напоминаем, что полный код будет находиться в приложении. Обратите внимание: в строке 15 определен массив постоянных цветов. Последовательность цветов та же, что и в коде 01, в строке 05. Важно, чтобы вы это понимали, чтобы разобраться, как на самом деле работает код. Теперь, когда в коде 03 будет выполнена строка 17, мы получим тот же эффект, что и при просмотре компилятором строки 04 из кода 01. Обратите внимание на едва заметную разницу, которая здесь присутствует. В отличие от кода 01, где во время компиляции мы указывали, как всё будет выглядеть, здесь мы этого не делаем. Мы просто говорим компилятору: создай код и используй это значение. Но это имеет серьезные последствия, как мы увидим позже.

Сразу после этого, в строке 18 из кода 03, мы сообщаем MetaTrader 5, сколько цветов находится в буфере цвета. Примечание: указание количества цветов в строке 18 не говорит нам о том, какие значения будет иметь каждый цвет. Таким образом, если мы их не инициализируем, MetaTrader 5 сделает это каким-то образом, и цветовая схема может оказаться совершенно неупорядоченной. Вот почему мы используем цикл в строке 19, чтобы инициализировать цвета, вызвав функцию в строке 20. Всё это эквивалентно строке 05 из кода 01.

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

Изображение 02

«Эй! Подождите минутку. Где цвета, которые будут применены к индикатору?» Думаю, что и вас, и пользователя, увидевшего изображение 02, это сильно удивит, а сам пользователь, увидев это изображение, окажется в полном замешательстве. Но давайте посмотрим, что произойдет, если мы применим данный индикатор к графику. Это можно увидеть в анимации ниже.

Анимация 01

«Какая пугающая вещь. Я не понимаю. Почему, несмотря на невозможность регулировки цветов, как показано на изображении 02, индикатор всё равно выдал тот же результат, что и на изображении 01? Это довольно странно».

На самом деле, такое поведение не кажется таким уж странным. Это происходит так, потому что мы определяем параметры динамически. А поскольку MetaTrader 5 пока не знает, как создавать цвета, он не может их использовать, или, скорее, не может представить их для настройки, так как ещё не знает, сколько цветов и какие именно цвета будут использоваться. Это действительно довольно интересно, и в будущем мы сможем изучить данный вопрос разными способами. Давайте пока сосредоточимся на основных моментах.

Учитывая, что цветовая схема совпадала с той, которую мы видели на изображении 01, мы знаем, что индикатор работает. А ещё мы можем показать пользователю, какой шаблон мы определили на этапе написания кода. «Но разве нельзя это изменить, позволив пользователю выбирать желаемые цвета?» Что касается этого, уважаемый читатель, то здесь, по сути, нет никаких проблем, поскольку, как только индикатор появится на графике, пользователь сможет открыть его свойства и изменить их. В этом случае посмотрите, что происходит в анимации 02.

Анимация 02

Хм, интригующе, теперь мы можем увидеть, какие цвета определены для использования в индикаторе. «Но подождите секунду. Вы сказали, что мы можем изменить цвета. Однако я кое-что вспомнил. В строке 15 фрагмента из кода 03 мы определяем именно те цвета, которые видны в анимации 02, и все же они определены как константы. Кажется, есть какое-то противоречие. Если они определены как константы, как пользователь сможет изменять цветовую схему?»

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

Анимация 03

Обратите внимание, что это действительно работает. Однако у такого подхода есть небольшой минус. Когда пользователь удаляет индикатор с графика, или, точнее, когда он просит MetaTrader 5 перестроить график, то значения, объявленные в строке 15, будут использованы повторно, в результате чего любые изменения конфигурации, внесенные пользователем, будут потеряны, и придется вносить их заново.

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

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

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

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


Индикатор Inside Bar на различных типах графиков

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

Изображение 03

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

Изображение 04

Здесь мы имеем дело с проблемой, очень похожей на проблему из изображения 03. Однако в случае с изображением 04 мы просим использовать линейный график, а также индикатор Inside Bar. Возможно, для большинства людей в действительности, такая ситуация не имеет смысла, поскольку, используя линейный график, мы стремимся получить информацию только из одного из значений OHCL, и почти всегда для нас важна будет конечная величина. Но это не так. Проблема в том, что даже если мы попросим показать график в другом формате, мы всё равно увидим представленные свечи. И это необходимо исправить.

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

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_color1      clrRed, clrRoyalBlue, clrGreen
05. //+----------------+
06. #property indicator_buffers     5
07. #property indicator_plots       1
08. //+----------------+
09. double  gl_Buff_High[],
10.         gl_Buff_Open[],
11.         gl_Buff_Close[],
12.         gl_Buff_Low[],
13.         gl_Buff_Color[];
14. //+------------------------------------------------------------------+
15. int OnInit()
16. {
17.    PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES);   
18. 
19.    SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA);
20.    SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA);
21.    SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA);
22.    SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA);
23.    SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX);
24. 
25.    return INIT_SUCCEEDED;
26. };
27. //+------------------------------------------------------------------+
28. 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[])
29. {
30.    static double  high = DBL_MIN,
31.                   low = DBL_MAX;
32. 
33.    for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++)
34.    {
35.       gl_Buff_High[c] = High[c];
36.       gl_Buff_Open[c] = Open[c];
37.       gl_Buff_Close[c] = Close[c];
38.       gl_Buff_Low[c] = Low[c];
39. 
40.       gl_Buff_Color[c] = (Open[c] > Close[c] ? 0 : 2);
41.       if ((c - 1) > 0)
42.       {
43.          high = (High[c - 1] > high ? High[c - 1] : high);
44.          low = (Low[c - 1] < low ? Low[c - 1] : low);
45.          gl_Buff_Color[c] = ((high > High[c]) && (low < Low[c]) ? 1 : gl_Buff_Color[c]);
46.          if (gl_Buff_Color[c] != 1)
47.          {
48.             high = DBL_MIN;
49.             low = DBL_MAX;
50.          }
51.       }
52.    }
53. 
54.    return rates_total;
55. };
56. //+------------------------------------------------------------------+

Код 04

Теперь код 04 работает более удобным для любого пользователя индикатора способом, поскольку мы можем задавать цвета с самого начала. И всё это так, потому что в строке 04 из кода 04 мы определяем свойство индикатора. Обратите внимание, что поэтому мы удалили часть кода, отвечающую за загрузку цветов из блока обработки события OnInit. Таким образом, мы получаем базовый индикатор, которым нам нужно будет манипулировать.

Теперь подходим к самой интересной части: Если этот индикатор, который мы видим в коде 04, работает динамически и одновременно имеет определенную статическую часть, позволяющую отображать цветовой шаблон, как заставить его работать и адаптироваться к типу просматриваемого графика?

Вот тут у многих, кто смотрит на мой код, обычно бегут мурашки по коже. Прошу заметить, что в строке 17 мы указываем, что система построения графика индикатора будет свечного типа. Однако при преобразовании графика на график баров возникнут проблемы, как показано на предыдущих изображениях. Но если изучить документацию, то мы обнаружим довольно интересную особенность стандартной библиотеки. Это позволяет нам узнать, какой тип графика используется в данный момент. Речь идёт о функции ChartGetInteger. Когда мы запрашиваем у этой функции информацию о типе используемого графика, она возвращает перечисление, в данном случае ENUM_CHART_MODE.

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

                   .
                   .
                   .
14. //+------------------------------------------------------------------+
15. #define PrintX(X) Print(__FUNCTION__, " => ",#X, " :: ",X);
16. //+------------------------------------------------------------------+
17. int OnInit()
18. {
19.     PrintX(EnumToString((ENUM_CHART_MODE)ChartGetInteger(0, CHART_MODE)));
20.     PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES);   
21. 
22.     SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA);
23.     SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA);
24.     SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA);
25.     SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA);
26.     SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX);
27. 
28.     return INIT_SUCCEEDED;
29. };
30. //+------------------------------------------------------------------+
                   .
                   .
                   .

Код 05

Теперь будьте внимательны, иначе вы запутаетесь в описании того, что будет сделано. В этом фрагменте кода 05 показаны изменения, которые пришлось внести в код 04, чтобы определить, какой тип графика используется в данный момент времени. Затем, в строке 19, мы делаем вызов, который в конечном итоге, согласно определению, приведенному в строке 15, преобразуется в информацию, которая отобразится в терминале MetaTrader 5. Данную информацию можно увидеть на изображениях ниже.

Изображение 05

На изображении 05 видно, что мы используем график CHART_CANDLES.

Изображение 06

На изображении 06 видим отчетливо, что график называется CHART_BARS.

Изображение 07

И наконец, на изображении 07 мы видим, что график называется CHART_LINE. Хорошо, но как мы можем использовать это в своих интересах? Что ж, всё очень просто, за исключением графика CHART_LINE, решение которого мы рассмотрим позже. Чтобы индикатор использовал правильный режим построения графика, нам всего лишь нужно, чтобы вызов функции в строке 20 (она была показана в этом фрагменте кода 05) указал тип DRAW_COLOR_CANDLES, когда мы находимся на свечном графике, и DRAW_COLOR_BARS, когда мы находимся на графике баров.

Остальное — уже история. Это очень просто сделать любому, и это прекрасно работает, если MetaTrader 5 применяет индикатор из события Init. Другими словами, если мы хотим использовать его самым простым способом, код должен выглядеть так, как показано ниже.

                   .
                   .
                   .
14. //+------------------------------------------------------------------+
15. int OnInit()
16. {
17.     switch ((ENUM_CHART_MODE)ChartGetInteger(0, CHART_MODE))
18.     {
19.         case CHART_BARS:
20.             PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_BARS);
21.             break;
22.         case CHART_CANDLES:
23.             PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES);   
24.             break;
25.         case CHART_LINE:
26.             break;
27.     }   
28. 
29.     SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA);
30.     SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA);
31.     SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA);
32.     SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA);
33.     SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX);
34. 
35.     return INIT_SUCCEEDED;
36. };
37. //+------------------------------------------------------------------+
                   .
                   .
                   .

Код 06

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

Анимация 04

На анимации 04 мы видим, что при переключении графика на другой режим построения индикатор перестаёт успевать за действиями MetaTrader 5. Многие бы сдались, заявив, что создать индикатор правильным образом невозможно. Однако они не знают, как на самом деле работает MetaTrader 5. Всякий раз, когда что-то происходит, и как правило, на графике, MetaTrader 5 запускает очень специфическое событие, которое содержит некоторую информацию, которая помогает нам определить, что произошло. Некоторые события можно включить или выключить, а другие — нет. Каждое событие, которое мы активируем или деактивируем, влияет на общую производительность MetaTrader 5, поскольку система оказывается перегружена и вынуждена обрабатывать события, которые она обычно не отслеживает.

А для тех, кто ищет высокопроизводительную систему, это тривиально. Многие трейдеры, собираясь использовать MetaTrader 5, вообще ничего с ним не используют, например, индикаторы или советники. Это происходит так, потому что зачастую данные приложения замедляют работу MetaTrader 5 в периоды высокой волатильности. Именно в такие моменты эти трейдеры хотят извлечь выгоду; поэтому они используют базовую версию MetaTrader 5, только её и ничего лишнего.

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

Событие, которое мы хотим зафиксировать, — это ChartEvent. В нашем коде он обрабатывается процедурой OnChartEvent. А теперь будьте внимательны, дорогие читатели. Событие ChartEvent происходит всякий раз, когда на графике что-либо случается. Однако мы можем включать и выключать определенные параметры, чтобы не фиксировать каждое отдельное изменение, внесенное на график. Тем не менее, при добавлении индикатора на график, MetaTrader 5 запускает два события одно за другим. В некоторых случаях срабатывает и третий, но мы сосредоточимся только на первых двух.

Первым событием, которое срабатывает, является инициализация (Init), которая перехватывается и обрабатывается функцией OnInit, которую мы рассматриваем с самого начала изучения темы, посвященной индикаторам. Сразу после обработки события Init запускается новое событие, даже если мы ещё ничего не сделали. Это событие ChartEvent, и именно в данный момент индикатор фактически размещается на графике.

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

void OnChartEvent(const int id,         // Event ID
                  const long& lparam,   // Parameter of type long event
                  const double& dparam, // Parameter of type double event
                  const string& sparam  // Parameter of type string events
  );

Код 07

Теперь, если посмотреть документацию по OnChartEvent, мы увидим небольшую таблицу. Из этой таблицы нас интересует следующее значение: CHARTEVENT_CHART_CHANGE — событие, которое происходит при изменении графика. Таким образом, именно это значение и появляется при первом срабатывании события ChartEvent. Теперь, зная это, мы можем сделать кое-что довольно интересное в нашем коде. Итак, после обновления изображение будет выглядеть так, как показано ниже.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_color1      clrRed, clrRoyalBlue, clrGreen
05. //+----------------+
06. #property indicator_buffers     5
07. #property indicator_plots       1
08. //+----------------+
09. double  gl_Buff_High[],
10.         gl_Buff_Open[],
11.         gl_Buff_Close[],
12.         gl_Buff_Low[],
13.         gl_Buff_Color[];
14. //+------------------------------------------------------------------+
15. int OnInit()
16. {
17.     SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA);
18.     SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA);
19.     SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA);
20.     SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA);
21.     SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX);
22. 
23.     return INIT_SUCCEEDED;
24. };
25. //+------------------------------------------------------------------+
26. 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[])
27. {
28.    static double  high = DBL_MIN,
29.                   low = DBL_MAX;
30. 
31.    for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++)
32.    {
33.       gl_Buff_High[c] = High[c];
34.       gl_Buff_Open[c] = Open[c];
35.       gl_Buff_Close[c] = Close[c];
36.       gl_Buff_Low[c] = Low[c];
37. 
38.       gl_Buff_Color[c] = (Open[c] > Close[c] ? 0 : 2);
39.       if ((c - 1) > 0)
40.       {
41.          high = (High[c - 1] > high ? High[c - 1] : high);
42.          low = (Low[c - 1] < low ? Low[c - 1] : low);
43.          gl_Buff_Color[c] = ((high > High[c]) && (low < Low[c]) ? 1 : gl_Buff_Color[c]);
44.          if (gl_Buff_Color[c] != 1)
45.          {
46.             high = DBL_MIN;
47.             low = DBL_MAX;
48.          }
49.       }
50.    }
51. 
52.    return rates_total;
53. };
54. //+------------------------------------------------------------------+
55. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
56. {
57.     switch (id)
58.     {
59.         case CHARTEVENT_CHART_CHANGE:
60.             switch ((ENUM_CHART_MODE)ChartGetInteger(0, CHART_MODE))
61.             {
62.                 case CHART_BARS:
63.                     PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_BARS);
64.                     break;
65.                 case CHART_CANDLES:
66.                     PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES);   
67.                     break;
68.                 case CHART_LINE:
69.                     break;
70.             }
71.             break;
72.     }
73.     ChartRedraw();
74. };
75. //+------------------------------------------------------------------+

Код 08

Гм. «Не знаю. Я думаю, этот код не сработает, потому что мы не указываем MetaTrader 5, какую систему построения графиков использовать при выполнении функции OnInit. Не знаю. Я почти уверен, что результат будет таким же, как и при ошибке с кодом 02».

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

Анимация 05

Прошу заметить, что единственное, что не соответствует действиям MetaTrader 5 на графике, — это переключение в режим CHART_LINE. В данном случае наш индикатор, не используя модель построения графиков, не успевает за действиями пользователя в MetaTrader 5.


Заключительные идеи

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

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

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

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

Прикрепленные файлы |
Anexo.zip (2.69 KB)
Алгоритм Стрекозы — Dragonfly Algorithm (DA) Алгоритм Стрекозы — Dragonfly Algorithm (DA)
В данной статье рассмотрим алгоритм стрекозы (Dragonfly Algorithm, DA), вдохновлённый коллективным поведением стрекоз в природе — их способностью координировать полёт в стае, избегая столкновений, следуя за добычей и уклоняясь от хищников. Разберём, как пять простых поведенческих правил и адаптивный механизм перехода от исследования к эксплуатации реализуются на MQL5, и проверим алгоритм на нашем тестовом стенде.
Нейросети в трейдинге: Единая архитектура взаимодействия рыночных признаков и торгового контекста (Основные компоненты) Нейросети в трейдинге: Единая архитектура взаимодействия рыночных признаков и торгового контекста (Основные компоненты)
Рассматривается реализация OneTrans для задач трейдинга на MQL5: FlashAttention на OpenCL, модуль многоголового кросс‑внимания, смешанный Feed‑Forward и объект верхнего уровня. Поясняется адаптация к финансовым данным, кэширование Key/Value и формирование стека токенов. Читатель получит рабочий каркас и примеры соединения компонентов в согласованный вычислительный граф.
Создание самооптимизирующихся советников на MQL5 (Часть 8): Анализ нескольких стратегий Создание самооптимизирующихся советников на MQL5 (Часть 8): Анализ нескольких стратегий
Как лучше всего объединить несколько стратегий для создания мощной ансамблевой стратегии? Мы рассмотрим, как объединить три различные стратегии в нашем торговом приложении. Трейдеры часто используют специализированные стратегии для открытия и закрытия позиций, и мы хотим узнать, могут ли машины выполнять эту задачу лучше. В начале нашего обсуждения мы ознакомимся с возможностями тестера стратегий и принципами объектно-ориентированного программирования, которые нам понадобятся для решения этой задачи.
Машинное обучение и Data Science (Часть 42): Прогнозирование временных рядов на форексе с ARIMA и Python Машинное обучение и Data Science (Часть 42): Прогнозирование временных рядов на форексе с ARIMA и Python
ARIMA (сокращение от Auto Regressive Integrated Moving Average, авторегрессионная интегрированная скользящая средняя) — это традиционная модель прогнозирования временных рядов. Благодаря способности обнаруживать всплески и колебания в данных временного ряда, эта модель может делать точные прогнозы относительно следующих значений. В этой статье мы разберемся, что это такое, как это работает, можно ли это использовать для точного прогнозирования будущих цен на рынке и многое другое.