От начального до среднего уровня: Индикатор (I)
В предыдущей статье "От начального до среднего уровня: События (II)", мы рассмотрели, как сохранить какую-либо информацию на более или менее постоянной основе между разными моментами использования платформы MetaTrader 5. Я знаю, что некоторые могут подумать, что такой материал не нужен. Но когда вам нужно будет сохранить какую-то информацию между вызовами, вы вспомните об этом и скажете: "Друг мой, спасибо, что дал мне этот совет. Большое спасибо". В любом случае,
мы только начинаем наше исследование. Поскольку у нас уже есть базовые понятия, которые позволяют создавать небольшие программы, и мы уже видели, как работает событийно-ориентированное программирование, пришло время разобраться с некоторыми базовыми понятиями, которые многие пытаются реализовать, хотя они уже придуманы и которые, по умолчанию, не нужно реализовывать, а только использовать при необходимости.
В данном случае я имею в виду вкладки, которые по умолчанию присутствуют почти во всех приложениях MetaTrader 5, хотя у нас их может быть как больше, так и меньше. Однако, если вы, будучи программистами, научитесь использовать данные вкладки, вы станете гораздо более наблюдательным и требовательным пользователем тех интерфейсов, которые вам предоставляют другие программисты.
Поэтому давайте сегодня заведем новую тему.
Вкладки по умолчанию
Любой код, созданный полностью на MQL5, может иметь окно базовой настройки. Это окно будет присутствовать не во всех приложениях; в таких приложениях, как скрипты и сервисы, оно может присутствовать или нет. Однако в индикаторах и советниках нам всегда будут представлены некоторые элементы или, скорее, вкладки по умолчанию. Они не создаются программистом, а определяются в среде MetaTrader 5, то есть у нас есть стандарт для использования и настройки любого индикатора или советника.
Но по разным причинам многие программисты часто добавляют элементы настройки, в которых нет необходимости. Это связано с тем, что такие элементы уже могут поместиться на вкладках, которые будут отображаться пользователю по умолчанию. Но вопрос в том, знаете ли вы как получить доступ к элементам, определенным на вкладках по умолчанию? Я задаю этот вопрос как программист, а не как пользователь.
Когда я говорю о вкладках по умолчанию, я имею в виду то, что мы видим на последовательности изображений ниже:

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

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

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

Изображение 04
Это вкладки индикатора, используемые по умолчанию. Поскольку мы сейчас сосредоточены на изучении работы событий в индикаторе, именно потому, что он проще, мы должны узнать о нем как можно больше, прежде чем переходить к более сложным вещам. Тем не менее, данная система вкладок по умолчанию довольно интересна и хорошо продумана, поскольку отлично подходит для самых разных случаев. На самом деле, в большинстве случаев нет необходимости реализовывать какие-либо дополнительные входные параметры. Но почему тогда многие программисты создают дополнительные входные параметры? Причина в том, что это становится необходимым, когда схема по умолчанию, предоставляемая MetaTrader 5, не может удовлетворить наши потребности. Однако прежде, чем начинать добавлять всякую ерунду, лучше сначала разобраться, как использовать данные вкладки, чтобы наше приложение не выходило за рамки стандарта MetaTrader 5.
По сути, единственная вкладка, которой нужно управлять, если мы считаем это необходимым, это вкладка из изображения 01. Это связано с тем, что на этой вкладке мы можем добавлять элементы, чтобы сделать вещи более показательными с точки зрения указания цели данного приложения. И сделать это очень просто. Однако, поскольку мы здесь и дальше будем сосредоточены только на образовательной части, мы не добавим элементы на эту вкладку. В любом случае, вам стоит подумать о том, как работать на этой первой вкладке, особенно если вы хотите сделать свои приложения доступными.
Чтобы добавить или изменить вкладку, показанную на рисунке 01, нам нужно указать компилятору, что в нее поместить. Для этого мы будем использовать директиву компиляции. Данная директива используется как #property. Эта директива позволяет нам определить множество вещей, от версии приложения до ссылки на определенное место в Интернете. Более подробную информацию можно найти в документации. Вы наверняка уже видели, как данная директива применяется во всех моих кодах из статей. По сути, единственное, что я определяю по умолчанию, - это авторские права. Давайте посмотрим, как мы можем определить еще одну, чтобы дать вам представление о том, что мы можем сделать. Для этого мы используем приведенный ниже код.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property version "1.0" 04. #property icon "Example.ico" 05. #property description "This is a demonstration application whose purpose is only didactic.\nIt is part of a sequence of articles published in the MQL5 community." 06. #property link "https://www.mql5.com/pt/articles/15794" 07. //+------------------------------------------------------------------+ 08. int OnInit() 09. { 10. return INIT_SUCCEEDED; 11. }; 12. //+------------------------------------------------------------------+ 13. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 14. { 15. return rates_total; 16. }; 17. //+------------------------------------------------------------------+
Код 01
Таким образом, при выполнении кода 01 мы увидим, что изображение 01 будет отображаться по-другому. Данный момент можно увидеть ниже:

Изображение 05
Прошу заметить, что теперь у нас есть иконка, которая отличается от стандартной иконки, а также описание назначения приложения. Всё это определено между строками 02 и 05. Обратите внимание, что нам нужно сообщить о каталоге и файле изображения, которые будем использовать в качестве иконки. В приложении я настраиваю всё так, чтобы вы могли поэкспериментировать и понять, как разместить нужную иконку. В этом случае, как видно из кода 01, файл иконки должен находиться в папке, где расположен код индикатора. Однако можно разместить его и в других местах; просто укажите место, и компилятор сможет найти нужный файл.
«Но подождите секундочку. Тут не хватает части информации: той, которую мы объявляем в строке 06 кода 01. Куда она делась?» На самом деле данная информация, которая раньше была видна на вкладке, теперь связана с другой информацией. Так, наведя курсор на то, что должно быть информацией об авторских правах, мы увидим показанное на рисунке ниже:

Изображение 06
Таким образом, мы можем убедиться, что вся информация присутствует, что можно увидеть на изображении 06. Если мы щелкнем по точке, из которой появилась ссылка, откроется браузер и направит нас в место, указанное в строке 06 из кода 01. Поскольку в большинстве случаев, как я полагаю, вы создаете что-то для личного пользования, можно игнорировать эти вещи, поскольку они не нуждаются в реализации. Хотя есть случаи, когда нам нужно указать компилятору, как должен быть сгенерирован код, или, скорее (поскольку эта фраза могла сбить с толку), - есть особые обстоятельства, когда даже при использовании кода для личного целей нам нужно задать эти свойства компиляции.
Один из таких случаев - это как раз создание индикатора, целью которого является построение чего-либо на графике. Однако вы можете пропустить объяснение других вкладок, о которых говорилось в начале этой темы. Я прошу вас набраться спокойствия и терпения. Мы вернемся к ним в ближайшее время. Но теперь нам нужно сделать кое-что ещё, чтобы эти вкладки приобрели смысл.
Базовый индикатор
Пока что всё, что мы сделали, - это сгенерировали много информации и распечатали ее на терминале, чтобы можно было вывести на экран. Наконец-то настал момент, когда можно увидеть, что на графике что-то нарисовано. Чтобы было приятнее и понятнее, мы будем использовать график в стандартном стиле MetaTrader 5. Я сделаю это только в демонстрационных целях, но можно использовать графику с той конфигурацией, которая вам больше всего подходит или которая вам больше всего нравится.
Хорошо. В предыдущих двух статьях мы объяснили, как работают два основных события, генерируемых MetaTrader 5: Deinit и Init. И то, и другое предназначено для того, чтобы указать нашему приложению, как себя вести. Однако все индикаторы нуждаются в двух событиях: одно из них - событие Init, о котором уже говорилось, а другое - событие Calculate.
Теперь будьте внимательны, дорогие читатели, потому что это очень важно. Функция OnCalculate должна захватывать событие Calculate, которое должно присутствовать в коде индикатора. Однако у нас есть две версии одной и той же функции, то есть, по умолчанию MQL5 предлагает возможность использовать перегруженные функции, одна из которых показана далее, а другая - ниже.
int OnCalculate( const int rates_total, // price[] array size const int prev_calculated, // number of handled bars at the previous call const int begin, // index number in the price[] array meaningful data starts from const double& price[] // array of values for calculation );
int OnCalculate( const int rates_total, // size of input time series const int prev_calculated, // number of handled bars at the previous call const datetime& time[], // Time array const double& open[], // Open array const double& high[], // High array const double& low[], // Low array const double& close[], // Close array const long& tick_volume[], // Tick Volume array const long& volume[], // Real Volume array const int& spread[] // Spread array );
Но почему разработчики MQL5 так поступили? Думаю, основная причина заключается в том, чтобы сделать работу индикатора более эффективной и стабильной. Дело в том, что существует несколько способов вычисления, особенно если учесть, что чаще всего индикатор вычисляет среднее значение. Поэтому разработчики, заметив, что могут улучшить производительность платформы MetaTrader 5, решили разрешить индикатору не выполнять некоторые из этих вычислений, так как платформа сама может сообщить нам результат заранее. Возможно, сейчас это не имеет для вас особого смысла, но я уверяю вас, что по мере того, как вы будете практиковаться и обучаться, вам станет понятно, что в этом есть большой смысл.
Но тогда какую перегруженную версию я должен использовать? Ответ: всё зависит от ситуации. Дело не в том, что вам нужно использовать одну версию в ущерб другой. Обе версии делают практически одно и то же, они получают событие Calculate. Вот и всё. Что будет дальше, зависит от того, что мы планируем и что нам нужно сделать.
Однако есть интересный момент, который, надо признать, стал удачной доработкой разработчиков платформы. В зависимости от того, какую версию OnCalculate использует пользователь, у него будет доступ к вкладке, показанной на изображении 02, или нет. Если поискать статьи с изображениями, анимацией или видео старых версий MetaTrader 5 в работе, то заметно, что вкладка на рисунке 02 появляется почти каждый раз, даже когда она совершенно не нужна.
Однако в какой-то момент всё это пропало. Если использовать версию с меньшим количеством аргументов, будет доступна вкладка, показанная на рисунке 02. Если использовать версию с большим количеством аргументов, эта вкладка будет недоступна. Именно поэтому я сказал, что всё будет объяснено в свое время. Если бы об этом было сказано в тот момент, это не имело бы никакого смысла. Но теперь смысл будет, и даже больше, когда мы внедрим какой-нибудь индикатор.
Для начала мы создадим очень простой вариант, в котором практически всё будет сделано и создано в MetaTrader 5. Мы, как программисты, сделаем минимум необходимого, чтобы она работала и была как можно более образовательной. И мы сделаем это поэтапно. Хотя сам код очень прост для понимания и выполнения, если идти небольшими шагами, то каждый, даже начинающий с базовыми знаниями, сможет следовать ему, если будет изучать эти статьи.
Наш код начинается таким образом:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. int OnInit() 05. { 06. return INIT_SUCCEEDED; 07. }; 08. //+------------------------------------------------------------------+ 09. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 10. { 11. return rates_total; 12. }; 13. //+------------------------------------------------------------------+
Код 02
Прошу заметить, что пока ничего не сделано. Код содержит только основы, чтобы компилятор знал, как создать желаемое приложение, то есть индикатор, который будет отображать все вкладки, как объяснялось в начале этой статьи. Далее мы опишем наш индикатор. Для этого мы должны указать компилятору, что нужно создать. Это можно сделать двумя способами: динамическим (который гораздо сложнее и он будет объяснен в другой момент) и статическим (который значительно проще и понятнее). Он идеально подходит для небольших кодов и ориентирован на тех, кто только начинает работать или просто хочет создать простой и быстрый индикатор. Для этого мы изменим код 02, чтобы получить эту версию:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_type1 DRAW_LINE 05. #property indicator_color1 clrBlack 06. //+------------------------------------------------------------------+ 07. int OnInit() 08. { 09. return INIT_SUCCEEDED; 10. }; 11. //+------------------------------------------------------------------+ 12. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 13. { 14. return rates_total; 15. }; 16. //+------------------------------------------------------------------+
Код 03
Это второй шаг, который нам необходимо сделать. Хотя заявление в строке 05 связано с небольшой ошибкой, которая всё ещё сохраняется при использовании стандартной системы MetaTrader 5. Без этой строки ВЫ НЕ СМОЖЕТЕ ИЗМЕНИТЬ ЦВЕТ рисования индикатора, даже если это очень простой индикатор. Однако самое важное заявление сделано в строке 04. В этой строке мы указываем, что именно мы хотим отобразить на графике. В данном случае это будет линия цены, но какой именно цены? Каковы будут определения и свойства линии, которую предстоит нарисовать? Спокойно, у нас все получится, наберитесь терпения.
После определения типа создаваемого индикатора необходимо определить ещё несколько его характеристик. Для этого нам нужно обратиться к документации MQL5, чтобы узнать, как объявить следующие пункты. Затем мы возвращаемся к документации и ищем термин #property, чтобы найти информацию, которая относится к тому, что объявлено в строке 04, то есть к типу индикатора. Когда мы ищем данную информацию, мы видим нечто, что выделяется на изображении ниже:

Изображение 07
Пункт, который показан на изображении 07, очень важен для нас, поскольку без него мы не сможем указать MetaTrader 5, как будет отображаться индикатор. Таким образом, мы ищем перечисление ENUM_DRAW_TYPE. Если посмотреть на это в документации, мы увидим таблицу, подобную приведенной ниже:

Изображение 08
Нужная нам информация - это как раз то, что выделено зеленым цветом на изображении 08, то есть нам нужен буфер для данных. Для каждого типа индикаторов нужны разные вещи. Поскольку создаваемая нами программа очень проста, нам понадобится всего лишь буфер. С этой информацией мы можем вернуться к коду 03 и добавить в него ещё несколько элементов. Однако сначала нам нужна дополнительная информация. Это связано с количеством элементов, которые индикатор должен будет отрисовать на экране. Чтобы найти упомянутую информацию, мы должны заглянуть внутрь свойства #property. Оно показано ниже.

Изображение 09
Чтобы получить вторую информацию, мы будем опираться на ту же таблицу. Теперь нам нужно использовать значение, равное сумме этих двух значений из изображении 10:

Изображение 10
Получив всю информацию, мы можем вернуться к коду 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. int OnInit() 11. { 12. return INIT_SUCCEEDED; 13. }; 14. //+------------------------------------------------------------------+ 15. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 16. { 17. return rates_total; 18. }; 19. //+------------------------------------------------------------------+
Код 04
Посмотрев на код 04, мы видим в строке 07 искомое объявление, которое мы нашли на изображении 08. В строке 08 указывается другая информация, которая равна сумме значений на изображении 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. 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. //+------------------------------------------------------------------+
Код 05
И вот, перед вами полноценный и полнофункциональный индикатор. Мы можем попросить его отобразить любую информацию, которую может предложить нам MetaTrader 5, и он сделает это безупречно. «Но подождите, как это возможно? Я уже видел коды других индикаторов, и они намного больше. Этого не может быть. Я уже пытался создать несколько индикаторов, и у всех есть гораздо больше кода чем то, что вы представляете. Я не думаю, что это сможет работать, хочу убедиться в его работоспособности».
Я понимаю ваше удивление, дорогие читатели. Это связано с тем, что любой изученный код индикатора будет содержать гораздо больше элементов, чем мы делаем здесь. Но чтобы показать вам, что данный код работает, давайте посмотрим на анимацию:

Анимация 01
В анимации 01 я показываю, как разместить индикатор, а затем изменить его цвет. Прошу заметить, что он довольно прост и интуитивно понятен.

Анимация 02
В анимации 02 мы покажем, как можно изменить стиль линии индикатора. Опять же, очень интуитивно.

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

Анимация 04
Наконец, в анимации 04 мы видим, как изменить тип вычислений, выполняемых для индикатора. Как видите, всё довольно просто и понятно, и не влияет на работу индикатора. Остается только догадываться, как работает вкладка из рисунка 04, но поскольку подобные вещи очень интуитивны и просты, нужно просто проявить любопытство и использовать данную вкладку.
Я не вижу необходимости объяснять это здесь и сейчас, поскольку эта вкладка используется только для настройки операционной среды. Редко кто из начинающих трейдеров будет пытаться манипулировать этой вкладкой. Но вам, как программистам, необходимо понять, как она работает и как её можно реализовать в коде. Да, можно настроить работу индикатора, не касаясь данной вкладки, для этого используется чистый MQL5, но об этом мы поговорим позже, в более подходящий момент.
Теперь, когда мы увидели, как работает код, давайте посмотрим, что добавилось на последнем этапе реализации. То, что здесь объясняется, будет рассмотрено в несколько более продвинутой форме позже. Однако вам нужно с самого начала понять, зачем и как нам нужны строки, реализованные в последнем шаге, начиная со строки 10.
Как уже объяснялось в первых статьях этой серии, обычно не рекомендуется использовать глобальные переменные. Однако переменная, объявленная в строке 10, - это особый случай: нам действительно нужна глобальная переменная, поскольку она необходима для реализации нескольких моментов, которые мы рассмотрим позже. Но сейчас вам нужно понять, что эта переменная будет содержать данные, которые будут нанесены на график, создавая тем самым индикатор, который можно видеть в анимации.
Для этого мы должны указать MetaTrader 5, КАКУЮ переменную использовать, так как в реальном коде может быть объявлено несколько переменных. Данный вид информации предоставляется с помощью того, что видно в строке 14. В принципе, это всё. После этого мы можем начать захват события Calculate и использовать данные, которые предоставит MetaTrader 5 для присвоения значений переменной, объявленной в строке 10.
Сделать это совсем несложно; на самом деле, в большинстве случаев это довольно просто. Всё, что нам нужно сделать, - это вычислить значение и присвоить его определенной позиции в массиве, объявленном в строке 10. А теперь обратите внимание. При объяснении темы массивов мы говорили, что они бывают двух типов: динамические и статические. Тем не менее, КАЖДЫЙ МАССИВ, который будет использоваться для хранения данных для построения графиков в индикаторе, должен быть динамическим. Однако вы НЕ ДОЛЖНЫ выделять под него память. Он будет назначен самим MetaTrader 5 по мере необходимости. Поэтому мы можем использовать код в строке 22. В строке 14 мы указываем, что эта переменная будет использоваться для хранения данных об индикаторе, который будет построен на графике. После этого MetaTrader 5 возьмет на себя полный контроль над памятью, которую необходимо выделить.
Но есть и другой важный аспект. В идеале любое приложение для работы на MetaTrader 5 должно быть как можно более эффективным. Это необходимо для того, чтобы не израсходовать много времени, а также для поддержания стабильности платформы. По этой причине обратите внимание на то, как создается этот цикл в строке 21. У вас может сложиться впечатление, что при каждом срабатывании события Calculate все данные в массиве price считываются от начала и до конца. Однако всё происходит не совсем так, по крайней мере, так не должно быть.
По сути, внутри MetaTrader 5 хранятся некоторые внутренние значения для управления действиями приложений. Тогда при первом захвате события Calculate нашим индикатором значение rates_total будет указывать на количество данных, присутствующих в массиве price. Значение prev_calculated может быть нулевым, а может и нет, но оно обязательно будет указывать на первую точку данных в серии. Это можно изменить, как мы увидим ниже. Пока что вам нужно просто понять то, о чем было рассказано: это условие будет выполнено только в первый раз, если ваш код правильно реализован и следует правилам.
При следующем вызове значение prev_calculated может быть равно значению rates_total, но может и не быть равен. Если он остался прежним, это означает, что существенных изменений не произошло. Если оно меньше, то выполняется цикл для обновления значения, которое надо нанести на график.
Поэтому, если мы правильно реализуем код, первое выполнение будет медленным. Однако во всех остальных захватах события Calculate будет выполнено минимальное количество сделок, поэтому платформа сможет быстро провести любой анализ и вывести результат на экран.
«Хорошо, но я не понял одного: как может произойти событие Calculate, если значения prev_calculated и rates_total одинаковы? Не будет ли это пустой тратой времени, ведь цикл в строке 21 не выполнит никакого действия?» Да, это правда. Однако MetaTrader 5 не будет запускать событие Calculate просто так. Он сработает только при изменении цены символа. Поэтому в периоды высокой волатильности на рынке будет происходить настоящая лавина событий. Если ваш код плохо оптимизирован, платформа со временем станет медленной, но вина лежит не на неё, а на плохо оптимизированном приложении, которое будет отнимать время и ресурсы.
Заключительные идеи
В этой статье мы создадим наш первый полностью функциональный и практичный индикатор. Цель не в том, чтобы показать, как создать приложение, а в том, чтобы помочь читателю понять, как можно развивать свои собственные идеи безопасным, простым и практичным способом. и дать вам возможность применить их на практике безопасным, простым и практичным способом.
Поскольку данная тема должна быть очень хорошо усвоена и изучена, я не буду продолжать больше необходимого, поскольку это сделает материал гораздо более плотным и сложным и не позволит спокойно изучать его. В приложении есть два кода из представленных здесь. С их помощью можно разобраться в пошаговых инструкциях, приведенных в статье.
В следующей статье мы продолжим сегодняшнюю, поскольку вторая форма объявления функции OnCalculate всё ещё нуждается в рассмотрении.
Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/15794
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Автоматизация торговых стратегий на MQL5 (Часть 10): Разработка стратегии Trend Flat Momentum
Нейросети в трейдинге: Обучение глубоких спайкинговых моделей (Интеграция спайков)
Алгоритм голубых обезьян — Blue Monkey (BM) Algorithm
От начального до среднего уровня: События (II)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования