От начального к среднему уровню: Подокна (IV)
Введение
В предыдущей статье, От начального к среднему уровню: Подокна (III), мы объясняли, как можно работать с определёнными ситуациями, связанными с подокнами. Основная идея той статьи связана со свойствами, которые мы можем назначить определённому индикатору. Эти свойства позволяют указать компилятору, как именно должно создаваться — или не создаваться — определённое подокно.
Как объяснялось в предыдущей статье, бывают случаи, когда МЫ НЕ ХОТИМ, чтобы у нашего индикатора был второй экземпляр. Этот тип ситуации будет более подробно рассмотрен в другой статье позже, когда мы начнём использовать определённые свойства, чтобы лучше контролировать то, что MetaTrader 5 действительно должен делать.
Поэтому нам ещё многое предстоит рассмотреть из того, что я считаю базовыми знаниями по MQL5. Хотя MetaTrader 5 в первую очередь задумывался как торговая платформа, мы можем многое в ней изменить так, чтобы в итоге она служила совсем иной цели.
Помните, что мы можем заставить MetaTrader 5 делать что угодно. Во многих случаях нам придётся прибегать к средствам, выходящим за рамки того, что MQL5 предлагает по умолчанию. В таких случаях нам придётся перейти к C или C++ и использовать какой-либо способ связать код MQL5 с C или C++. Сделав это, перед вами откроются врата рая — или, возможно, ада.
Эта серия статей не будет заходить на эту территорию, поскольку я считаю, что использование других языков, таких как C и C++, для ещё большего расширения контроля MQL5 над MetaTrader 5 — это действительно продвинутая тема. И это здесь рассматриваться не будет.
Подокна (IV)
В предыдущей статье мы рассмотрели то, что позволило бы нам создать код, показанный ниже.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property description "DEMO Indicator" 05. //+------------------------------------------------------------------+ 06. #property indicator_separate_window 07. #property indicator_height 50 08. #property indicator_plots 0 09. //+------------------------------------------------------------------+ 10. int OnInit(void) 11. { 12. IndicatorSetString(INDICATOR_SHORTNAME, "Test 123."); 13. 14. return INIT_SUCCEEDED; 15. }; 16. //+------------------------------------------------------------------+ 17. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 18. { 19. return rates_total; 20. }; 21. //+------------------------------------------------------------------+
Код 01
И хотя все коды, рассмотренные в предыдущей статье, работоспособны, то есть могут выполняться в MetaTrader 5, при компиляции некоторые из них могут вызывать предупреждение компилятора. Это можно исправить, добавив то, что показано в восьмой строке этого кода 01. Поскольку этот показанный выше индикатор я считаю «пустым», он идеально подходит для демонстрации и изучения всех свойств индикаторов. Когда я говорю, что индикатор пустой, я имею в виду, что он абсолютно ничего не делает и не имеет никакой практической функциональности при размещении на графике. Поскольку при использовании этого кода 01 мы не будем отображать на графике ни линий, ни кривых, мы указываем, что количество построений индикатора равно нулю, чтобы компилятор не выдавал никаких предупреждений.
Хорошо, как мы видели в предыдущей статье, шестая строка этого кода 01 сообщит MetaTrader 5, что при попытке использовать этот индикатор на графике его следует разместить в подокне. Оно может быть создано или нет, как было показано в предыдущих статьях. Седьмая строка укажет размер этого подокна, если возникнет необходимость его создать. Что касается этих свойств индикатора, думаю, ни у кого из вас не должно возникнуть сомнений, поскольку всё это довольно просто, понятно и прямолинейно.
Мне нужно, чтобы вы поняли: этот код 01 служит основой для другого подхода, который мы можем использовать. Хотя поначалу это может показаться несколько запутанным, такой подход позволяет нам гораздо лучше контролировать происходящее в определённых ситуациях, которыми в противном случае было бы гораздо сложнее управлять.
Чтобы вы поняли, какого рода проблему мы можем решить, глубоко разобравшись в том, что делает этот код 01, необходимо, чтобы вы усвоили материал, показанный в последних статьях этой серии. Там было показано, что MetaTrader 5 может не допустить появления более одного экземпляра индикатора на одном графике. Это позволяет избежать ненужной траты ресурсов. То же самое уже не применимо, когда мы начинаем использовать свойство, указывающее на необходимость подокна. В этом случае MetaTrader 5 игнорирует наличие уже открытого экземпляра и создаёт новые экземпляры по мере того, как пользователь или трейдер снова добавляет тот же индикатор на график. Всё это подробно объяснялось в предыдущих статьях. Тем не менее существует способ избежать этого и, по крайней мере в общем случае, не допустить, чтобы новый экземпляр определённого реализованного нами индикатора размещался на графике, когда мы работаем с подокнами.
Чтобы понять, как мы можем это сделать, нужно обратиться к документации MQL5, поскольку именно там объясняется, как это сделать. Если вы внимательно присмотритесь, то найдёте функцию ChartIndicatorAdd. Эта функция очень интересна, хотя, если судить только по документации, она может показаться самой обычной. То, что вы, возможно, не представляете себе иных способов использования этой функции, кроме предусмотренных, сильно ограничивает ваши возможности и нередко даже мешает понять, почему код ведёт себя определённым образом. Прежде чем разбирать это, я хочу, чтобы вы обратили внимание на одно замечание из самой документации. Чтобы облегчить понимание дальнейшего объяснения, ниже мы полностью приведём важный фрагмент документации об этой функции, который вскоре нам понадобится.
Примечание
Если индикатор должен отображаться в отдельном подокне (например, при создании iMACD или пользовательского индикатора с указанным свойством #property indicator_separate_window) и применяется к главному окну графика, он может быть невидим, хотя по-прежнему будет отображаться в списке индикаторов. Это означает, что шкала индикатора отличается от шкалы ценового графика и что значение применённого индикатора не попадает в видимый диапазон ценового графика. В этом случае функция GetLastError() возвращает код 0, что указывает на отсутствие ошибок. Значения этого «невидимого» индикатора можно увидеть в окне данных (Data Window) и получить из других приложений MQL5.
Если кратко, документация говорит, что в главное окно можно загрузить индикатор, предназначенный для использования в подокне. Он не будет виден и не создаст подокно для своего отображения, если это не будет правильно указано при вызове функции ChartIndicatorAdd.
То, что вы не сразу понимаете объяснение в документации, совершенно нормально. Многие хорошие программисты поначалу сталкиваются с этой трудностью. Это происходит потому, что документация призвана в упрощённой форме объяснить, какого поведения следует ожидать, а не описывать то, что вы, возможно, захотите сделать, но что не было учтено при создании системы. И это применимо к любому языку программирования. Для тех, кто только начинает, значительная часть информации, представленной в документации, может показаться довольно запутанной. С опытом вы научитесь извлекать из неё ценную информацию для создания нестандартных решений. Именно потому, что я знаю об этой трудности и понимаю, насколько многим бывает сложно пользоваться документацией, я время от времени связываю объяснение в статье с тем, что можно увидеть в ней. Так со временем вы сможете понять любое объяснение, которое там встретится, и обходиться своими силами. По этой причине мы можем немного глубже разобраться в том, что уже было сказано, чтобы вы действительно поняли, что именно говорится в документации.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property description "DEMO Indicator" 05. //+------------------------------------------------------------------+ 06. #property indicator_chart_window 07. #property indicator_plots 0 08. //+------------------------------------------------------------------+ 09. int OnInit(void) 10. { 11. IndicatorSetString(INDICATOR_SHORTNAME, "Test 456S."); 12. 13. return INIT_SUCCEEDED; 14. }; 15. //+------------------------------------------------------------------+ 16. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 17. { 18. return rates_total; 19. }; 20. //+------------------------------------------------------------------+
Код 02
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property description "DEMO Indicator" 05. //+------------------------------------------------------------------+ 06. #property indicator_chart_window 07. #property indicator_plots 0 08. //+------------------------------------------------------------------+ 09. int OnInit(void) 10. { 11. IndicatorSetString(INDICATOR_SHORTNAME, "Test 456."); 12. ChartIndicatorAdd(0, 0, iCustom(NULL, NULL, "Tutor\\Code 01")); 13. 14. return INIT_SUCCEEDED; 15. }; 16. //+------------------------------------------------------------------+ 17. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 18. { 19. return rates_total; 20. }; 21. //+------------------------------------------------------------------+
Код 03
Хорошо, теперь обратите внимание на некоторые детали этого кода 03. Обратите внимание, что в двенадцатой строке мы используем функцию ChartIndicatorAdd. Здесь мы не выполняем никакую проверку ошибок времени выполнения (run-time). Именно поэтому следует соблюдать осторожность при попытке запустить этот код 03 локально. Во-первых, убедитесь, что код 01 был правильно скомпилирован. Таким образом, при выполнении двенадцатой строки этого кода 03 MetaTrader 5 получит доступ к исполняемому файлу индикатора 01. Во-вторых, можно заметить, что внутри функции ChartIndicatorAdd я использую вызов iCustom. Цель этого вызова — создать ссылку на пользовательский индикатор. То есть это позволяет нам расширить возможности или, точнее, использовать индикаторы, кроме тех, которые MetaTrader 5 уже предоставляет средствами MQL5.
В статье "От начального к среднему уровню: Индикатор (V)" мы начали говорить об этих индикаторах, то есть о так называемых технических индикаторах. Здесь мы продолжаем развивать эту тему. Хорошо, на этом этапе необходимо убедиться, что и каталог, и имя индикатора, который будет загружен с помощью iCustom, указаны правильно. В приложенном файле необходимая структура каталогов уже будет настроена правильно. Поэтому, чтобы протестировать этот код 03, не изменяйте структуру, если только вы действительно не понимаете, что делаете. В противном случае конечный результат будет отличаться от того, который будет показан здесь.

Анимация 01
Что это было? Я этого не понимаю. Разве индикатор из кода 03 не добавил индикатор 01 на график? Давайте проверим, что произошло. Для этого достаточно открыть список индикаторов, присутствующих на графике, и проверить, какие из них там отображаются. Это можно увидеть на следующем изображении.

Изображение 01
Как странно. Индикатор 03 действительно смог загрузить индикатор 01 на график. Индикатор 01 не создал подокно, как ожидалось. Или, точнее, я предполагал, что при выполнении индикатора 01 должно было создаться подокно. Хм, подождите-ка минутку. Действительно ли выполняется этот индикатор 01? Потому что, на мой взгляд, произошедшее только что не имеет никакого смысла. Честно говоря, я немного растерян.
Поэтому важно, чтобы вы старались применять все знания, изложенные в этих статьях, дорогой читатель, и всегда проводили собственные локальные тесты, чтобы понять детали. Любые вопросы, которые вы не проясните на этом этапе обучения, позже обязательно дадут о себе знать. В любом случае, анализируя происходящее здесь, вы заметите, что индикатор 01 не только загружается, но и выполняется. И вот что важно: он не отображается на графике. Это происходит потому, что в коде 03 мы не указываем, что индикатор должен отображаться. Именно это и объясняется в тексте документации.

Изображение 02
Область, выделенная на изображении 02, — именно та часть, которая нас интересует. Обратите внимание на следующее: здесь указано, что при добавлении индикатора с помощью функции ChartIndicatorAdd мы должны указать подокно, в которое его следует добавить. Обратите самое пристальное внимание на эту информацию, мой дорогой читатель, потому что в зависимости от того, что именно вы планируете делать с помощью кода, автоматизируя тем самым ту или иную стратегию, чёткое и объективное понимание того, что выделено на изображении 02, может определить, будет ли ваш код работать так, как задумано.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property description "DEMO Indicator" 05. //+------------------------------------------------------------------+ 06. #property indicator_chart_window 07. #property indicator_plots 0 08. //+------------------------------------------------------------------+ 09. int OnInit(void) 10. { 11. IndicatorSetString(INDICATOR_SHORTNAME, "Test 456."); 12. ChartIndicatorAdd(0, (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), iCustom(NULL, NULL, "Tutor\\Code 01")); 13. 14. return INIT_SUCCEEDED; 15. }; 16. //+------------------------------------------------------------------+ 17. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 18. { 19. return rates_total; 20. }; 21. //+------------------------------------------------------------------+
Код 04
А теперь — максимальное внимание к тому, что будет объяснено, потому что это крайне важно для понимания того, что мы будем делать дальше. Обратите внимание, что единственное различие между кодом 03 и этим кодом 04 заключается именно во втором аргументе функции ChartIndicatorAdd, который находится в двенадцатой строке обоих кодов. И вот что важно: этого простого различия более чем достаточно, чтобы исполняемый файл индикатора вёл себя совершенно иначе, чем в анимации 01.

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

Изображение 03
Отлично. Вопрос в том: почему сейчас это работает, а раньше — нет? Ответ заключается в том, что, если вы посмотрите код 03, то увидите: второй аргумент, передаваемый в функцию ChartIndicatorAdd, указывает MetaTrader 5 добавить индикатор в главное окно. Поскольку индикатор из кода 01 ОЖИДАЕТ И ТРЕБУЕТ подокно, он не отображается на экране. Даже если бы он отображал важную информацию, ОН НЕ ОТОБРАЖАЛСЯ БЫ и оставался бы невидимым для пользователя или оператора, наблюдающего за графиком.
В коде 04, напротив, в том же аргументе, который раньше указывал на главное окно, мы теперь просим MetaTrader 5 определить и сообщить нам количество подокон, имеющихся на графике, а затем добавить индикатор, указанный в третьем аргументе, в следующее подокно. По этой причине код 04 позволяет коду 01 создать новое подокно. Это очень полезно и позволяет нам делать много интересных вещей, которые мы рассмотрим и изучим в другой раз.
Прежде чем завершить эту статью, я хочу объяснить вам ещё одну вещь о функции ChartIndicatorAdd. Считайте это небольшим бонусом. Если вы внимательно следили за тем, что мы делали до сих пор, то наверняка заметили кое-что. Во всех случаях мы можем получить довольно интересные результаты очень простым способом и практически без усилий. В одном случае мы указали, что добавляемый индикатор должен быть размещён в главном окне, а в другом — что должно быть создано новое подокно. Вопрос: что произойдёт, если на графике уже есть подокно, а мы загрузим индикатор и разместим его в нём? "Подождите немного. К чему вы ведёте этой гипотезой?".
Итак, идея, которую я хочу, чтобы вы поняли, уже рассматривалась в другой статье из этой же серии. Речь идёт о статье "От начального к среднему уровню: Подокна (II)". В той статье было показано, что мы можем заставить один индикатор использовать значения, рассчитанные другим индикатором, для создания третьего индикатора. Сама по себе идея довольно проста и интересна. Чтобы это происходило автоматически сразу после добавления индикатора на график, необходимо точно понимать, что именно вы реализуете.

Анимация 03
Теперь, когда это сделано, давайте создадим новый индикатор, и вы сразу поймёте, зачем это нужно. Его код приведён ниже.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property description "DEMO Indicator" 05. //+------------------------------------------------------------------+ 06. #property indicator_chart_window 07. #property indicator_plots 0 08. //+------------------------------------------------------------------+ 09. int OnInit(void) 10. { 11. for (int c0 = 0; c0 < (int)(ChartGetInteger(0, CHART_WINDOWS_TOTAL)); c0++) 12. for (int c1 = (ChartIndicatorsTotal(0, c0) - 1); c1 >= 0; c1--) 13. PrintFormat("In the sub window [%d] was found this indicator >>%s<<", c0, ChartIndicatorName(0, c0, c1)); 14. 15. return INIT_SUCCEEDED; 16. }; 17. //+------------------------------------------------------------------+ 18. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 19. { 20. return rates_total; 21. }; 22. //+------------------------------------------------------------------+
Код 05
Цель этого кода 05 заключается просто в том, чтобы показать названия индикаторов, присутствующих на графике. Об этом уже говорилось в предыдущих статьях; здесь мы пойдём ещё дальше. Итак, при выполнении этого кода 05 мы получим результат, показанный на следующем изображении.

Изображение 04
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property description "DEMO Indicator" 05. //+------------------------------------------------------------------+ 06. #property indicator_chart_window 07. #property indicator_plots 0 08. //+------------------------------------------------------------------+ 09. int OnInit(void) 10. { 11. int index = -1; 12. 13. for (int c0 = 0; (index < 0) && (c0 < (int)(ChartGetInteger(0, CHART_WINDOWS_TOTAL))); c0++) 14. index = (ChartIndicatorName(0, c0, 0) == "StdDev(20)" ? c0 : index); 15. 16. IndicatorSetString(INDICATOR_SHORTNAME, "Demo 123."); 17. if (index >= 0) 18. ChartIndicatorAdd(0, index, iCustom(NULL, NULL, "Examples\\Custom Moving Average", 20, 0, 0, PRICE_CLOSE)); 19. 20. return (index < 0 ? INIT_FAILED : INIT_SUCCEEDED); 21. }; 22. //+------------------------------------------------------------------+ 23. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 24. { 25. return rates_total; 26. }; 27. //+------------------------------------------------------------------+
Код 06
Итак, прежде чем посмотреть результат выполнения этого кода 06, я должен пояснить один момент. Обратите внимание на то, какой индикатор мы используем в строке 18. В данном случае я использую индикатор, встроенный в сам MQL5. Это значительно упрощает дело и существенно сокращает количество моментов, которые пришлось бы здесь объяснять. Однако это не решает ситуацию полностью и не делает код на 100 % работоспособным. Причина проста. Видите последний аргумент в вызове iCustom? Итак, этот аргумент, PRICE_CLOSE, приведёт к тому, что добавленный индикатор будет рассчитывать данные на основе котировочной информации основного графика. Чтобы использовать значения, рассчитанные индикатором, добавленным в анимации 03, необходимо, чтобы этот аргумент был другим. Недостаточно просто изменить его; нам нужно пойти другим путём. Об этом пути мы поговорим позже, поскольку, чтобы понять причину, сначала нужно объяснить ещё один вопрос, который в этой серии статей пока не был должным образом освещён. Так что наберитесь терпения.

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

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

Анимация 05
Заключительные замечания
В этой статье было показано, как работать несколько более продвинутым образом со всем, что связано с использованием подокон. Хотя рассмотренное здесь — лишь часть того, что я считаю базой для гораздо более глубокого изучения, понимание этого поможет вам более правильно использовать ресурсы, доступные в MetaTrader 5. Чтобы этого добиться, нам нужно использовать MQL5, чтобы лучше контролировать то, что будет отображаться.
| Файл MQ5 | Описание |
|---|---|
| Indicators\Code 01 | Базовая демонстрация |
| Indicators\Code 02 | Базовая демонстрация |
| Indicators\Code 03 | Базовая демонстрация |
| Indicators\Code 04 | Базовая демонстрация |
| Indicators\Code 05 | Базовая демонстрация |
Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/17152
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Особенности написания Пользовательских Индикаторов
Стресс-тестирование последовательностей сделок методом Монте-Карло в MQL5
Нейронная сеть на практике: График функции ReLU
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Write an article on how to create two subwindows in one indicator; that would be progress!
Напишите статью, как сделать в одном индикаторе два подокна, вот это будет прогресс!