От начального до среднего уровня: Индикатор (II)
В предыдущей статье "От начального до среднего уровня: Индикатор (I)", мы начинали практический разговор о том, как можно создать очень простой и легкий индикатор, используя совсем немного знаний. Хотя многие могут подумать, что для достижения результата необходимо много знаний в области написания кода, оказалось, что даже новичок, приложив некоторые усилия, может создать нечто относительно простое и функциональное. Причем довольно элегантным способом, так как большая часть работы переносится в MetaTrader 5. Только от нас зависит, насколько правильно мы отреагируем на событие. Тогда MetaTrader 5 будет знать, что делать с остальной информацией, которую мы обрабатываем во время происходящих событий.
Вы, наверное, подумаете: «Но тот код, который мы смогли создать, не похож ни на один индикатор, который я когда-либо видел раньше. И хотя нам и удается построить что-то на графике, это не совсем то, что я ожидал, я надеялся, что смогу сделать гораздо больше».
Ладно, мой дорогой читатель, вам может показаться, что всё это выглядит не очень хорошо, но вы должны помнить, что знания не приобретаются в одночасье: их нужно культивировать. Цель данной серии статей заключается именно в этом: культивировать и распространять знания. Вопрос не в том, чтобы действовать по принципу: "Сделайте это, потому что это будет работать только так". Так что давайте посмотрим, как далеко мы продвинулись в предыдущей статье.
Как реализовать скользящую среднюю с периодом X
В предыдущей статье мы рассмотрели, как наносить информацию на график. Мы сделали это в простой для понимания форме. Однако целью статьи было показать, что есть способ лучше использовать ресурсы, доступные в MetaTrader 5, поэтому мы не стали слишком углубляться в определенные критерии или возможности.
Однако то, что было показано в предыдущей статье, можно сделать и другими способами. Но я думаю, что многие хотели бы увидеть, как может быть реализована более распространенная система скользящих средних. Другими словами, было бы интересно уметь создавать скользящие средние, похожие на, скажем, знаменитую девятипериодную экспоненциальную скользящую среднюю, известную именно потому, что она является очень прибыльной операционной моделью, хотя ей трудно следовать, несмотря на ее чрезвычайную простоту.
Итак, вы, возможно, думаете о том, как превратить скучный код, реализованный в предыдущей статье, в более интересный. Добиться этого может быть намного сложнее. Теперь, я думаю, вам придется создать что-то с большим количеством строк, а не то, что было показано в предыдущей статье.
Что ж, вызов принят: сегодня будет создан индикатор скользящей средней с минимальным количеством кода, - чем меньше кода, тем лучше. Но если для многих это вызов, то я нахожу это скучным и однообразным. Прежде, чем мы рассмотрим, как это сделать, давайте ознакомимся с кодом из предыдущей статьи:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_type1 DRAW_LINE 05. #property indicator_color1 clrBlack 06. //+----------------+ 07. #property indicator_buffers 1 08. #property indicator_plots 1 09. //+----------------+ 10. double gl_buffer[]; 11. //+------------------------------------------------------------------+ 12. int OnInit() 13. { 14. SetIndexBuffer(0, gl_buffer, INDICATOR_DATA); 15. 16. return INIT_SUCCEEDED; 17. }; 18. //+------------------------------------------------------------------+ 19. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 20. { 21. for (int c = prev_calculated; c < rates_total; c++) 22. gl_buffer[c] = price[c]; 23. 24. return rates_total; 25. }; 26. //+------------------------------------------------------------------+
Код 01
Я думаю, что вы уже попрактиковались с кодом 01, так как он очень простой и интересный, и позволяет нам понять вещи, которые иначе были бы трудны для понимания. Теперь к делу: как сделать так, чтобы этот код мог рассчитывать и строить скользящую среднюю? Чтобы добиться этого, мы должны сначала принять некоторые решения. Они не обязательны, но полезны, чтобы потренироваться думать о коде, который мы хотим реализовать.
Первое решение, которое мы должны принять, - это тип вычисления, который хотим использовать. Да, существуют различия между расчетами скользящей средней. Существуют взвешенные и невзвешенные скользящие средние. Данное взвешивание может осуществляться по объему, цене или времени. Понимание этого и умение выбирать очень важны. В случае с экспоненциальной скользящей средней она взвешивается по времени или по количеству периодов, которые присутствуют в ней. VWAP - это скользящая средняя значение цены, взвешенное по объему, или наоборот? Кто знает ответ? В любом случае, время не будет учитываться. В случае с арифметической скользящей средней у нас нет такого взвешивания, и значение рассчитывается с учетом только одних данных, а не его изменения по отношению к другим данным. Для простоты мы называем эти средние "экспоненциальной скользящей средней" и "простой скользящей средней".
Именно связь одной части информации с другой определяет весовые коэффициенты. Впрочем, мы не будем вдаваться в скучные математические подробности, поскольку в данном случае это не нужно. Но нам необходимо знать хотя бы формулу для того типа вычислений, которые мы собираемся реализовать. Иначе как бы мы могли реализовать что-то, без понимания того, как это вычисляется? Поскольку арифметическая скользящая средняя очень проста в расчетах (ведь нам нужно только складывать и вычитать значения в течение определенного количества периодов), мы рассмотрим немного более сложный случай: экспоненциальную среднюю, которую многие считают такой.
Формула, которая будет принята нами, приведена ниже:

Изображение 01
На изображении 01 показано выражение для вычисления среднего экспоненциального с любым количеством периодов. В этой формуле P представляет собой значение текущей цены. Значение M, - предыдущее значение экспоненциального среднего. N - это количество периодов, используемых для усреднения. Получив данную информацию, мы можем перейти к этапу реализации расчета. Сначала я уточню небольшую деталь, о которой недавно упоминал: среднее арифметическое вычисляется путем сложения X значений и деления полученного результата на используемое Х.
Но когда мы переходим к расчету X + 1, то есть следующего периода, то всё, что нам нужно сделать, - это вычесть из суммы значение первого входа и прибавить значение нового входа. Полученный результат мы делим на X. Вот почему я сказал, что среднее арифметическое - это скучно, ведь нам нужно только складывать и вычитать. Но считается, что экспоненциальное усреднение требует огромного объема вычислений. Однако вы заметите, что оно почти такое же, как и средняя арифметическая.
Давайте теперь посмотрим, что означает это выражение на картинке 01: чтобы вычислить текущее MME (экспоненциальное скользящая средняя), нам нужно предыдущее значение средней. Хм... похоже, у нас проблема, поскольку, когда мы начинаем вычисление, у нас ещё нет этого значения. Именно поэтому нам нужен небольшой математический ресурс. Устранение проблемы заключается в том, чтобы просто сделать M равным нулю. Теперь мы можем выполнить расчет. Для этого достаточно посмотреть на код 01 и найти, где его нужно изменить. В этом случае необходимо изменить только строку 22. Но, чтобы не возникло недоразумений и вы поняли, что усложнять код не стоит я покажу полный код:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_type1 DRAW_LINE 05. #property indicator_color1 clrBlack 06. //+----------------+ 07. #property indicator_buffers 1 08. #property indicator_plots 1 09. //+----------------+ 10. double gl_buffer[]; 11. //+------------------------------------------------------------------+ 12. int OnInit() 13. { 14. SetIndexBuffer(0, gl_buffer, INDICATOR_DATA); 15. 16. return INIT_SUCCEEDED; 17. }; 18. //+------------------------------------------------------------------+ 19. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 20. { 21. for (int c = prev_calculated; c < rates_total; c++) 22. gl_buffer[c] = (price[c] - (c ? gl_buffer[c - 1] : 0)) * (2. / (1.0 + 9)) + (c ? gl_buffer[c - 1] : 0); 23. 24. return rates_total; 25. }; 26. //+------------------------------------------------------------------+
Код 02
Прошу заметить, что единственное, что мы изменили в коде, - это строка 22. БОЛЬШЕ НИЧЕГО НЕ ИЗМЕНЯЛОСЬ. Однако при выполнении кода мы видим следующее:

Изображение 02
Вы, наверное, задаетесь вопросом: «Но действительно ли мы видим девятипериодную экспоненциальную скользящую среднюю на этом изображении 02? Трудно поверить, что для размещения экспоненциальной скользящей средней требуется так мало». Чтобы проверить это, мы воспользуемся индикатором, который по умолчанию поставляется с MetaTrader 5 и который позволит нам убедиться в том, что мы всё делаем правильно. Он показан в следующей анимации :

Анимация 01
На этой анимации мы видим на заднем плане среднее значение изображения 02, а на переднем плане - индикатор проверки. Обратите внимание на значения, которые мы используем. В соответствии с этими значениями индикатор будет размещен на графике, как показано на анимации. Всё идеально. На самом деле код 02 рассчитывает девятипериодное экспоненциальное среднее. Но как это возможно?
Если вы изучали понятия, представленные в статьях, вы поймете, что мы просто используем выражение, показанное на рисунке 01. Однако, как уже говорилось выше, необходимо внести небольшую поправку, так как нам нужно установить первое значение на ноль в цепочке вычислений. Поэтому на первый взгляд это выражение может показаться немного запутанным. Но при желании можно изменить код в строке 22 на более компактный. Таким образом, обработчик события Calculate будет выглядеть так:
. . . 18. //+------------------------------------------------------------------+ 19. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 20. { 21. #define def_N 9 22. 23. for (int c = prev_calculated; c < rates_total; c++) 24. { 25. double M = (c ? gl_buffer[c - 1] : 0); 26. 27. gl_buffer[c] = (price[c] - M) * (2. / (1.0 + def_N)) + M; 28. } 29. 30. return rates_total; 31. 32. #undef def_N 33. }; 34. //+------------------------------------------------------------------+
Фрагмент 01
Фрагмент 01 делает то же самое, что и код 02, только на несколько строк больше. Отлично, но прежде чем мы продолжим, я должен предупредить вас о том, что в этом коде есть небольшая проблема. Однако я не собираюсь объяснять, в чем заключается эта проблема, немного подогреем ваше любопытство. Таким образом, вы обязательно попытаетесь разгадать эту проблему и сможете составить более четкое мнение о некоторых аспектах программирования, например, о том, что не всегда следует поступать определенным образом только потому, что математическое выражение говорит нам, что нужно поступать именно так.
Чтобы устранить проблему, нужно изменить код на тот, который показан ниже:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_type1 DRAW_LINE 05. #property indicator_color1 clrBlack 06. //+----------------+ 07. #property indicator_buffers 1 08. #property indicator_plots 1 09. //+----------------+ 10. double gl_buffer[]; 11. //+------------------------------------------------------------------+ 12. int OnInit() 13. { 14. SetIndexBuffer(0, gl_buffer, INDICATOR_DATA); 15. 16. return INIT_SUCCEEDED; 17. }; 18. //+------------------------------------------------------------------+ 19. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 20. { 21. for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++) 22. gl_buffer[c] = (c ? ((price[c] - gl_buffer[c - 1]) * 2. / (1 + 9)) + gl_buffer[c - 1] : price[c] * 2. / (1 + 9)); 23. 24. return rates_total; 25. }; 26. //+------------------------------------------------------------------+
Код 03
В любом случае, не переживайте; в приложении вы найдете оба кода, чтобы вы могли поэкспериментировать и попытаться понять, в чем заключается проблема, которая, если посмотреть на код, кажется бессмысленной. В любом случае, теперь, я думаю, мы можем уже поговорить о другом. Я уверен, что вы подумаете: «Дорогой коллега, а если я захочу использовать экспоненциальную скользящую среднюю с другим количеством периодов, мне придется компилировать код каждый раз, когда я захочу использовать другое значение периода? Это выглядит не очень многообещающе». На самом деле, вид реализации, который мы видели до этого момента, отвечает только за очень специфическую модель. Однако, если не разобраться с каждым реализуемым пунктом, вам будет трудно понять новые появляющиеся понятия.
Но, учитывая всё, что было рассказано, я думаю, что вы готовы перейти на следующий уровень реализации. Однако, чтобы не смешивать всё в кучу, давайте сменим тему.
Взаимодействие с пользователем
Крайне редко кто-то захочет реализовывать код, подобный тому, что показан в предыдущей теме, именно из-за того, что мы не можем изменить количество периодов вычисления. Правда, есть случаи, например, при расчете VWAP, когда средняя, которая строится на графике, не зависит от времени. Однако это был бы довольно специфический случай, что не умаляет его достоинств и необходимости объяснить и понять то, о чем пойдет речь в данной теме.
Далее мы рассмотрим, как можно позволить пользователю изменять некоторые параметры вычислений без необходимости перекомпиляции кода. Для этого мы должны сообщить компилятору MQL5, что хотим разрешить пользователю или другим приложениям сообщать о внутренних значениях, реализуемых в нашем коде. Иначе говоря, у нас будет тип константы, которая для пользователя будет переменной. Однако с точки зрения нашего приложения то, что мы собираемся создать, будет считаться константой. Да, можно вносить эти изменения непосредственно через другие приложения, как мы увидим ниже, хотя, в принципе, такой ресурс призван обеспечить более прямое взаимодействие с пользователем.
Для начала нам нужно настроить код 03, потому что в нынешнем виде это приведет к некоторой путанице. Поэтому в дидактических целях (а не для повышения производительности) мы изменим код на тот, что показан ниже:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_type1 DRAW_LINE 05. #property indicator_color1 clrBlack 06. //+----------------+ 07. #property indicator_buffers 1 08. #property indicator_plots 1 09. //+----------------+ 10. double gl_buffer[]; 11. //+------------------------------------------------------------------+ 12. int OnInit() 13. { 14. SetIndexBuffer(0, gl_buffer, INDICATOR_DATA); 15. 16. return INIT_SUCCEEDED; 17. }; 18. //+------------------------------------------------------------------+ 19. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 20. { 21. double k = 2. / (1 + 9); 22. 23. for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++) 24. gl_buffer[c] = (c ? ((price[c] - gl_buffer[c - 1]) * k) + gl_buffer[c - 1] : price[c] * k); 25. 26. return rates_total; 27. }; 28. //+------------------------------------------------------------------+
Код 04
Прошу заметить, что изменения довольно просты, поскольку появилась только строка 21, которая создает константу для корректировки вычисления экспоненциальной средней. Теперь важный момент: значение, которое нам нужно изменить здесь, - это как раз та девятка, которую мы видим в строке 21 кода 04. Для этого просто добавьте новую строку в код 04. Таким образом, мы можем указать, какой период мы хотим использовать при расчете средней. Данное изменение можно увидеть чуть ниже:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_type1 DRAW_LINE 05. #property indicator_color1 clrBlack 06. //+----------------+ 07. #property indicator_buffers 1 08. #property indicator_plots 1 09. //+----------------+ 10. input uchar user01 = 9; 11. //+----------------+ 12. double gl_buffer[]; 13. //+------------------------------------------------------------------+ 14. int OnInit() 15. { 16. SetIndexBuffer(0, gl_buffer, INDICATOR_DATA); 17. 18. return INIT_SUCCEEDED; 19. }; 20. //+------------------------------------------------------------------+ 21. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 22. { 23. double k = 2. / (1 + user01); 24. 25. for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++) 26. gl_buffer[c] = (c ? ((price[c] - gl_buffer[c - 1]) * k) + gl_buffer[c - 1] : price[c] * k); 27. 28. return rates_total; 29. }; 30. //+------------------------------------------------------------------+
Код 05
И это всё. Теперь пользователь может указать период экспоненциальной скользящей средней, который будет использован. Однако нам еще предстоит объяснить один небольшой момент. Когда мы поместим этот индикатор на график, мы увидим следующее:

Изображение 03
Обратите внимание, что на этом изображении мы отмечаем новую вкладку. Раньше этой вкладки не существовало, но она была создана именно благодаря строке 10 из кода 05. То есть это точка взаимодействия с пользователем, которая позволяет ему сконфигурировать некоторые константы, которые мы будем использовать в коде. Пока всё замечательно. Однако при выборе новой вкладки мы сталкиваемся с изображением ниже:

Изображение 04
И в этом заключается проблема. Обратите внимание на информацию, выделенную на изображении 04. Для нас, разрабатывающих данное приложение, прекрасно понятно, что это значение будет делать. Однако, когда мы добавляем больше значений, всё становится немного запутанным, и даже человеку, который реализовал код, трудно понять, что представляет собой каждое значение. И здесь нам нужно обратить внимание на еще один маленький момент. Эта деталь будет передана компилятору довольно необычным способом. По крайней мере для меня сначала это показалось немного странным, хотя потом я привык.
Как видно на изображении 04, выделенная строка совпадает с той, которая дает имя константе, видимой в строке 10 из кода 05. И да, дорогой читатель, в строке 10 мы объявляем не переменную, а константу. В простых случаях мы можем дать представительное имя нашему коду и пользователю приложения. Но это не всегда применимо, так как в большинстве случаев отображение небольшого текста для пользователя является наиболее подходящим. Как же поместить текст на это видное место на изображении 04?
Это самая необычная часть. Для этого нам нужно будет добавить комментарий типа string. Поскольку комментарий такого типа обязательно должен располагаться после объявления константы, мы должны сделать нечто подобное следующему:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_type1 DRAW_LINE 05. #property indicator_color1 clrBlack 06. //+----------------+ 07. #property indicator_buffers 1 08. #property indicator_plots 1 09. //+----------------+ 10. input uchar user01 = 9; //Exponential average periods 11. //+----------------+ . . .
Код 06
Внимательно посмотрите на код 06, особенно на строку 10. То, что было добавлено к этой строке, мы называем строчным комментарием. Когда мы делаем это в константе, которая будет отображаться на вкладке на изображении 04, мы указываем компилятору использовать этот текст в качестве строки для отображения пользователю. Чтобы сделать это более понятным и чтобы вы действительно всё поняли, посмотрите, что произойдет, если мы увидим ту же вкладку, что показана на изображении 04, но используя то, что показано в коде 06. Это можно посмотреть на изображении ниже:

Изображение 05
Благодаря изображению 05 любой человек будет знать, что настраивается в приложении, и это значительно упрощает его использование.
Вы заметили, как легко создать индикатор? Нам нужно совсем немного, чтобы заставить всё работать и сделать элементы идеально подходящими для определенного типа интересов. Чтобы сделать индикатор более полезным и лучше интегрировать его в MetaTrader 5, а также использовать его в других целях, нужно сделать еще несколько вещей. Но мы будем рассматривать эти вещи постепенно, чтобы вы могли понять, что к чему с функциями стандартной библиотеки MQL5 в индикаторе. Как видите, для работы индикатора нам нужно несколько функций. Однако индикатор ещё не стал по-настоящему универсальным.
Хорошо, это была первая часть. Теперь мы посмотрим, как использовать вторую версию OnCalculate. Первая версия соответствует определенным критериям, но нам может понадобиться больше информации. Поэтому нам нужна другая версия. Однако, чтобы не смешивать всё в кучу, мы рассмотрим это в другой теме.
Как использовать вторую версию OnCalculate
В предыдущих статьях мы упоминали, что функция OnCalculate - единственная перегруженная функция в стандартной библиотеке MQL5. Это связано с тем, что в некоторых случаях мы можем использовать одну версию, а в других - другую. Однако выбор версии влияет не только на то, что отображается в пользовательском интерфейсе, но и на то, как вам придется подходить к реализации. Без помощи MetaTrader 5 мы должны быть более осторожными при реализации. Это связано с тем, что, в отличие от первой версии, во второй больше данных, которые можно использовать, и в зависимости от того, как их использовать, можно получить тот или иной результат.
В связи с тем, что вторая версия OnCalculate очень сложна, мы рассмотрим только то, как будет выглядеть код того же индикатора, но в этой улучшенной версии. Важно понимать, что есть разница между использованием одной или другой версии, но различия создаются вами, программистами. Итак, этот же код 06, рассмотренный в предыдущей теме, будет выглядеть так, если использовать вторую версию OnCalculate:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_type1 DRAW_LINE 05. #property indicator_color1 clrBlack 06. //+----------------+ 07. #property indicator_buffers 1 08. #property indicator_plots 1 09. //+----------------+ 10. input uchar user01 = 9; //Exponential average periods 11. //+----------------+ 12. double gl_buffer[]; 13. //+------------------------------------------------------------------+ 14. int OnInit() 15. { 16. SetIndexBuffer(0, gl_buffer, INDICATOR_DATA); 17. 18. return INIT_SUCCEEDED; 19. }; 20. //+------------------------------------------------------------------+ 21. 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[]) 22. { 23. double k = 2. / (1 + user01); 24. 25. for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++) 26. gl_buffer[c] = (c ? ((Close[c] - gl_buffer[c - 1]) * k) + gl_buffer[c - 1] : Close[c] * k); 27. 28. return rates_total; 29. }; 30. //+------------------------------------------------------------------+
Код 07
Обратите внимание на один момент. В отличие от предыдущего случая, когда мы могли выбрать, какой тип информации будет использоваться для построения среднего, в коде 07 это уже невозможно. Причина в том, что мы "фиксируем" расчет на одном из значений, в данном случае на значении закрытия бара. Вся остальная информация будет игнорирована, так как она не будет использоваться. Но изменилось не только это - помните, мы говорили, что изменится и пользовательский интерфейс? Когда мы поместим индикатор, представленный в коде 07, на график, мы заметим отсутствие одной из вкладок, как видно на изображении ниже:

Изображение 06
На изображении 06 видно, что у нас больше нет одной из вкладок, которые были раньше. Причина в том, что расчет индикатора фиксируется на определенном типе значения. Поэтому пользователь не сможет изменить происхождение, как это происходило раньше. Однако это не исключает использования других видов расчетов, мы можем предоставить пользователю возможность выбрать тип входных значений, который будет использоваться при вычислении. Для этого достаточно добавить новые input.
Эта часть очень интересна. Так как это довольно просто, в оставшейся части статьи мы расскажем, как это сделать. Тогда можно будет приступать к созданию различных решений интересным и креативным способом.
Чтобы предоставить пользователю доступ к другим типам входных значений, мы можем использовать несколько разных методов. Мне больше всего нравится использование перечислителей. Перечислители легко реализовать и модифицировать, поэтому они мне нравятся. По сути, нам нужна небольшая последовательность шагов, чтобы сделать всё достаточно красивым и интуитивно понятным как для вас, кто будет его реализовывать, так и для пользователей, которые будут использовать ваше приложение.
Первым шагом будет создание перечислителя. Для простоты понимания, мы создадим очень простой вариант. Поэтому код 07 изменим таким образом:
. . . 09. //+----------------+ 10. enum Enum_TypeInput { 11. HIGH, 12. OPEN, 13. CLOSE, 14. LOW 15. }; 16. //+----------------+ 17. input uchar user01 = 9; //Exponential average periods 18. input uchar user02 = HIGH; //Data type for calculation 19. //+----------------+ . . .
Код 08
Выполнив код 07 с изменениями, указанными в коде 08, мы увидим следующее:

Изображение 07
«Ух ты! Но это не то, что я хотел сделать. Я хотел показать пользователю данные или типы, определенные в перечислении в строке 10, чтобы он мог сделать выбор на основе этого типа перечисления. Почему это не сработало?» Что ж, причина в типе данных, ожидаемых в строке 18. Прошу заметить: мы объявили перечисление, и это нормально; проблема в том, что перечисление преобразуется в целочисленные значения. Компилятор не понял, что мы хотели сделать. Чтобы исправить это, просто изменим тип ожидаемого значения на константу. Мы также изменим тип HIGH на CLOSE, который будет наиболее распространенным в большинстве случаев. Таким образом, код 08 будет изменен на этот:
. . . 16. //+----------------+ 17. input uchar user01 = 9; //Exponential average periods 18. input Enum_TypeInput user02 = CLOSE; //Data type for calculation 19. //+----------------+ . . .
Код 09
После применения этой модификации с кода 09 на 08 мы можем увидеть следующий результат при выполнении кода в терминале, чуть ниже:

Изображение 08
Посмотрите, как это работает. Но речь не только об этом. Поскольку компилятор понял, что мы хотим сделать, мы также можем выбирать элементы с помощью того же текста, который находится в перечислении, объявленном в строке 10 из кода 08. Однако обратите внимание: в отличие от прежних лет, когда мы пользовались поддержкой MetaTrader 5, теперь мы сами по себе. Иными словами, недостаточно настроить всё так, как описано выше, чтобы всё сразу заработало. Нам нужно изменить обработчик события. В этом случае мы переходим к следующему шагу, который заключается в том, чтобы заставить наш код использовать выбор, сделанный пользователем. Для этого просто измените код, таким образом:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_type1 DRAW_LINE 05. #property indicator_color1 clrBlack 06. //+----------------+ 07. #property indicator_buffers 1 08. #property indicator_plots 1 09. //+----------------+ 10. enum Enum_TypeInput { 11. HIGH, 12. OPEN, 13. CLOSE, 14. LOW 15. }; 16. //+----------------+ 17. input uchar user01 = 9; //Exponential average periods 18. input Enum_TypeInput user02 = CLOSE; //Data type for calculation 19. //+----------------+ 20. double gl_buffer[]; 21. //+------------------------------------------------------------------+ 22. int OnInit() 23. { 24. SetIndexBuffer(0, gl_buffer, INDICATOR_DATA); 25. 26. return INIT_SUCCEEDED; 27. }; 28. //+------------------------------------------------------------------+ 29. 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[]) 30. { 31. double k = 2. / (1 + user01), 32. price = 0; 33. 34. for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++) 35. { 36. switch (user02) 37. { 38. case HIGH : 39. price = High[c]; 40. break; 41. case OPEN : 42. price = Open[c]; 43. break; 44. case CLOSE : 45. price = Close[c]; 46. break; 47. case LOW : 48. price = Low[c]; 49. break; 50. } 51. gl_buffer[c] = (c ? ((price - gl_buffer[c - 1]) * k) + gl_buffer[c - 1] : price * k); 52. } 53. 54. return rates_total; 55. }; 56. //+------------------------------------------------------------------+
Код 10
В этом коде мы видим, что в строке 32 мы добавили новую переменную. Её цель - облегчить нам управление всеми элементами. В строке 36 мы добавляем команду switch для определения значения, которое поместится в переменную price. При этом не нужно вносить много изменений в код расчета, так как необходимо только настроить строку расчета на использование переменной цены, как видно в строке 51. Отлично, не так ли? Но прежде, чем завершить эту статью, мы можем сделать еще одну вещь.
Обратите внимание: в перечислении, объявленном в строке 10, есть несколько значений, которые понятны и могут быть использованы непосредственно в интерфейсе. Однако можно предпочесть другой код. Как тогда мы можем прояснить ситуацию для будущих пользователей и для вас? Ведь таким образом, можно было бы легко понять, какое значение будет использоваться при расчете. Для этого мы делаем нечто подобное тому, что мы делаем в строках ввода, то есть мы добавим строчный комментарий. В результате получается такой код:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_type1 DRAW_LINE 05. #property indicator_color1 clrBlack 06. //+----------------+ 07. #property indicator_buffers 1 08. #property indicator_plots 1 09. //+----------------+ 10. enum Enum_TypeInput { 11. en_HIGH, //Use maximum prices 12. en_OPEN, //Use opening prices 13. en_CLOSE, //Use closing prices 14. en_LOW //Use minimum prices 15. }; 16. //+----------------+ 17. input uchar user01 = 9; //Exponential average periods 18. input Enum_TypeInput user02 = en_CLOSE; //Data type for calculation 19. //+----------------+ 20. double gl_buffer[]; 21. //+------------------------------------------------------------------+ 22. int OnInit() 23. { 24. SetIndexBuffer(0, gl_buffer, INDICATOR_DATA); 25. 26. return INIT_SUCCEEDED; 27. }; 28. //+------------------------------------------------------------------+ 29. 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[]) 30. { 31. double k = 2. / (1 + user01), 32. price = 0; 33. 34. for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++) 35. { 36. switch (user02) 37. { 38. case en_HIGH : 39. price = High[c]; 40. break; 41. case en_OPEN : 42. price = Open[c]; 43. break; 44. case en_CLOSE : 45. price = Close[c]; 46. break; 47. case en_LOW : 48. price = Low[c]; 49. break; 50. } 51. gl_buffer[c] = (c ? ((price - gl_buffer[c - 1]) * k) + gl_buffer[c - 1] : price * k); 52. } 53. 54. return rates_total; 55. }; 56. //+------------------------------------------------------------------+
Код 11
Посмотрите, что получилось в итоге. Это код будет в приложении, и даст вам возможность учиться и практиковаться. После его запуска вы получите в терминале MetaTrader 5 то, что показано ниже.

Рисунок 09
Как уже говорилось выше, мы просто скрыли от пользователя всю информацию, связанную с внутренней структурой кода. Для пользователя ничего не изменится. Однако для вас этот код будет немного сложнее реализовать, так как у нас много правил и меньше помощи от MetaTrader 5, чем раньше. Но поскольку каждый случай чем-то отличается, вы теперь знаете, как реализовать простой индикатор, который, однако, может научить вас многим другим вещам.
Заключительные идеи
В этой статье мы показали, как можно относительно легко реализовать простой индикатор, выполнив ряд шагов. Я не стал показывать такие вещи, как перемещение линии рисования, но поскольку это очень просто (достаточно добавить в код еще одну константу и использовать ее в функции события OnCalculate), мы не рассмотрим, как это делается. Оставим подобные вещи как задания для тех, кто хочет попрактиковаться и попытаться понять больше об индикаторах.
В следующей статье мы рассмотрим еще кое-что, также связанное с индикаторами. Так что, встретимся позже.
Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/15802
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Автоматизация торговых стратегий на MQL5 (Часть 11): Разработка многоуровневой системы сеточной торговли
Разрабатываем менеджер терминалов (Часть 1): Постановка задачи
Разработка инструментария для анализа движения цен (Часть 10): Внешние библиотеки (II) VWAP
Генеративно-состязательные сети (GAN) для синтетических данных в сфере финансового моделирования (Часть 2): Создание синтетического символа для тестирования
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования