
Несколько индикаторов на графике (Часть 04): Начинаем работу с советником
Введение
В предыдущих статьях я рассказывал, как создать индикатор с несколькими подокнами — такая возможность становится интересной при использовании пользовательских индикаторов. Это было довольно просто сделать, но когда мы переходим к реализации в программе советника, всё начинает становиться немного сложнее, поскольку у нас нет тех же инструментов, которые были доступны в пользовательском индикаторе. В этот момент программирование становится необходимым: умение писать правильный код для создания подокон имеет первостепенное значение. Не смотря на то, что эта задача не такая уж простая, знание того, как поместить подокно в советник, не подразумевает большого кода. Понадобятся лишь некоторые знания о том, как работает MQL5.
Планирование
У нас уже работает пользовательский индикатор, то есть наш объектный класс уже функционирует. Поскольку это объектный класс, мы можем легко перенести его в другие модели. Однако простое объявление и попытка использовать класс в советнике не заставит работать вещи так же, как в нашем пользовательском индикаторе. Все потому, что в советнике нет возможности такой возможности как подокно. А потом подумалось: "А что если использовать уже скомпилированный и работающий пользовательский индикатор и вызвать его из советника с помощью команды iCustom? Что ж, это действительно может сработать, поскольку в подокне нет необходимости, а команда будет выглядеть следующим образом:
#property copyright "Daniel Jose" //+------------------------------------------------------------------+ input string user01 = ""; //Используемые индикаторы input string user02 = ""; //Сопровождаемые активы //+------------------------------------------------------------------+ int OnInit() { int m_handleSub; //... Код советника ... if ((m_handleSub = iCustom(NULL, 0, "Chart In SubWindows\\Chart In SubWindow.ex5", user01, user02)) == INVALID_HANDLE) return INIT_FAILED; if (!ChartIndicatorAdd(ChartID(), 0, m_handleSub)) return INIT_FAILED; //... Код советника ... ChartRedraw(); return(INIT_SUCCEEDED); } //...Остальной код советника ...
Этот простой фрагмент кода способен загрузить наш пользовательский индикатор, хотя он не будет работать должным образом, потому что нет подокна. В этом случае, когда код выполняется в советнике, он применит наш индикатор непосредственно в главном окне. В этом случае график будет скрыт шаблонами, загруженными индикатором, а это определенно не то, что мы ищем.
Поэтому наша настоящая и главная проблема заключается в том, чтобы создать подокно, которым можно воспользоваться, чтобы можно было использовать наш уже функционирующий индикатор. Но зачем создавать подокно для последующего запуска нашего индикатора? Это не имеет смысла, лучше добавить функциональные возможности непосредственно в наш советник и таким образом преодолеть любые ограничения, которые могут возникнуть.
Исходя из этого, нам необходимо выполнить несколько задач:
Задание | Цель |
---|---|
1 => Создать индикатор общего назначения, т.е. общий. | Иметь возможность создавать и использовать команду iCustom, совершенно не загрязняя при этом график. |
2 => Включить этот индикатор в советник каким-либо образом. | Это позволит вам без проблем перенести советник с полной функциональностью |
3 => Создавать общий класс объекта для подокна | Позволяет добавлять подокна через наш советник |
4 => Добиться привязки нашего класса C_TemplateChart к классу окна. | Это позволит управлять содержимым подокон, ничего не меняя в уже работающем коде. |
Хотя это может показаться трудоемким, трудности решаются довольно просто. Итак, давайте разберемся с каждым из пунктов.
Реализация: Создание индикатора общего назначения
Эта часть может быть решена через создание полностью чистого, но функционального кода пользовательского индикатора. В этом случае код будет выглядеть следующим образом:
#property copyright "Daniel Jose" #property version "1.00" #property description "Этот файл служит только для поддержки индикаторов в SubWin." #property indicator_chart_window #property indicator_plots 0 //+------------------------------------------------------------------+ int OnInit() { return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { return rates_total; } //+------------------------------------------------------------------+
Только это и ничего больше. Сохраним этот файл как SubSupport.mq5, только он не будет находиться вместе с другими индикаторами, а перенесем его в директорию RESOURCE нашего советника, тогда структура файла будет выглядеть как на картинке ниже:
Реализация: Включение общего показателя в советник
Для этого нам нужно добавить следующий код в начало нашего советника.
//+------------------------------------------------------------------+ #define def_Resource "Resources\\SubSupport.ex5" //+------------------------------------------------------------------+ #resource def_Resource //+------------------------------------------------------------------+
Это включит скомпилированный код общего индикатора в наш советник, после этого общий индикатор может удалить файл .ex5, так как в нем нет необходимости. Теперь следует иметь в виду, что если во время компиляции кода советника не будет найден исполняемый файл SubSupport.ex5, то компилятор автоматически скомпилирует код нашего общего индикатора SubSupport.mq5 и добавит этот новый скомпилированный исполняемый файл в наш советник, т.е. если мы по какой-либо причине изменим файл SubSupport.mq5 и захотим добавить изменения в советник, нужно удалить файл SubSupport.ex5, иначе изменения не будут добавлены.
Очень важно отметить эту деталь, потому что в некоторых случаях вам нужно знать, как добавить новые изменения в ресурс.
Хорошо, общий индикатор теперь является частью нашего советника, поэтому перейдем к следующей задаче.
Реализация: Создание класса объекта подокна.
Эта часть также проста, хотя нам нужно понять некоторые моменты перед кодированием, а именно: какие функции нам действительно понадобятся в этом классе? Изначально я решил использовать следующее:
Функция | Описание |
---|---|
Init | Позволяет добавлять подокна через наш советник |
Close | Позволяет добавлять подокна через наш советник |
Эти функции не будут тестироваться, поэтому я предполагаю, что они будут вызваны только один раз за все время существования советника. Но поскольку наш советник растет, хорошо бы подумать о том, чтобы сделать его еще более практичным в будущем, поэтому давайте создадим новый класс объектов, который будет называться C_Terminal, этот класс будет поддерживать несколько вещей, связанных с графическим терминалом. Но пока не будем углубляться в это. Давайте посмотрим на последнюю задачу, поскольку нет возможности реализовать решение частично.
Реализация: Наследование класса C_TemplateChart
Когда я решил создавать что-то новое, используя ООП (Объектно-ориентированное программирование), я сделал это, потому что уже знал, что есть большие преимущества в использовании такого подхода, среди которых безопасность и наследование, хотя у нас также есть полиморфизм, но мы будем использовать его позже, когда будем создавать систему кросс-ордеров, а здесь мы будем использовать одно из преимуществ ООП — наследование. Наш класс C_TemplateChart уже полностью функционален, и, видя это, мы не хотим перепрограммировать его заново или рисковать, добавляя код в класс, поотому что этот код не позволит использовать класс в других местах. Решением является использование наследования, что позволяет добавлять новый код или функции, никак не изменяя исходный код.
Использование наследования имеет ряд преимуществ, в том числе следующие: уже протестированный код остается протестированным; сложность растет без равного роста объема кода; только новые функции действительно нуждаются в тестировании; то, что не меняется, просто наследуется, обеспечивая стабильность. Иными словами, всё улучшается с минимальными усилиями, но с максимальной безопасностьюю Чтобы понять это, давайте посмотрим на схему ниже.
Класс-прародитель — это самый базовый класс, где мы имеем меньший уровень манипуляции данными, но когда родительский класс наследует что-то от прародительского класса, все вещи, объявленные как public в прародителе, могут быть видны и использованы родителем. Но при этом также можно добавлять новые вещи в родительский класс, и это не повлияете на то, что наследуется и поддерживается при наследовании. Если родительский класс уже закончен и работает, а нам нужно расширить его, не меняя ничего в классах ниже, то мы создаем класс-наследник, и он будет обладать всеми возможностями предыдущих классов. Также можно изменить операцию, и это интересная вещь о наследовании, потому что делая эти изменения, другие классы не будут затронуты. Однако здесь есть ограничение, в отличие от C++, который позволяет множественное наследование. Если ребенок может наследовать функции как от отцовской, так и от материнской стороны, в MQL5 это невозможно, поэтому структура всегда немного лагает, но вы все равно получаете некоторую выгоду от наследования. Пример множественного наследования можно увидеть ниже:
Хорошо, но как же это сделать в MQL5? Как объявить наследование, чтобы можно было воспользоваться им? Самый точный способ понять это — прочитать содержание объектно-ориентированного программирования (ООП), но здесь мы сразу перейдем к делу. Наследование будет осуществляться через следующие строки:
#include "C_TemplateChart.mqh" //+------------------------------------------------------------------+ class C_SubWindow : public C_TemplateChart { // ... Код класса };
Видите, что класс C_SubWindow будет публично наследовать класс C_TemplateChart, поэтому теперь мы можем использовать класс C_SubWindow для доступа к функциональности класса C_TemplateChart.
В приведенном выше фрагменте кода я выделил одну вещь, обратите внимание, что она находится в кавычках ( " ), а не как обычно используется ( < > ). Так почему же я это сделал? Как и в языке C++, в MQL5 есть несколько очень интересных вещей, но некоторые сбивают с толку тех, кто только начинает изучать искусство программирования. Когда мы помещаем заголовочный файл внутри угловых скобок ( < > ), мы имеем в виду абсолютный путь, то есть компилятор будет следовать точно по указанному нами пути, но когда мы используем кавычки (как мы уже сделали), то компилятор будет использовать относительный путь, или, чтобы вы поняли это лучше, он сначала начнет с текущего каталога, где находится рабочий файл. Это может показаться странным, но бывают случаи, когда мы имеем одно и то же имя для файлов, содержимое которых отличается, и они находятся в разных каталогах, но мы все равно хотим ссылаться на текущий каталог, поэтому мы используем кавычки для этого.
Две функции, которые мы думали использовать раньше, INIT и CLOSE, показаны ниже:
//+------------------------------------------------------------------+ bool Init(void) { if (m_handleSub != INVALID_HANDLE) return true; if ((m_handleSub = iCustom(NULL, 0, "::" + def_Resource)) == INVALID_HANDLE) return false; m_IdSub = (int) ChartGetInteger(Terminal.Get_ID(), CHART_WINDOWS_TOTAL); if (!ChartIndicatorAdd(Terminal.Get_ID(), m_IdSub, m_handleSub)) return false; return true; } //+------------------------------------------------------------------+ void Close(void) { ClearTemplateChart(); if (m_handleSub == INVALID_HANDLE) return; IndicatorRelease(m_IdSub); ChartIndicatorDelete(Terminal.Get_ID(), m_IdSub, ChartIndicatorName(Terminal.Get_ID(), m_IdSub, 0)); ChartRedraw(); m_handleSub = INVALID_HANDLE; } //+------------------------------------------------------------------+
Видите, код очень простой и короткий, но есть кое-что, с чем мы должны быть осторожны, обратите внимание на выделенную часть. Вы должны быть внимательны, чтобы не ошибиться при добавлении этой части, потому что если вы не оставите всё именно так, исполняемый файл SubSupport.ex5, который мы попросили добавить в советник, будет виден не внутри советника, а вне его. Можно прочитать о Ресурсах, чтобы понять это, но в основном это выглядит следующим образом: если использовать ( :: ), это укажет, что советник должен использовать внутренний ресурс, присутствующий в нем, но если просто назвать ресурс, то советник будет искать его внутри каталога MQL5, поэтому если файл не существует в указанном месте, функция завершится неудачей, даже если файл был добавлен как ресурс советника.
Затем, когда ресурс загружен, мы проверяем количество присутствующих подокон и добавляем индикатор в это подокно.
Что на самом деле делает этот код, можно увидеть ниже:
input string user01 = ""; //Используемые индикаторы input string user02 = ""; //Сопровождаемые активы //+------------------------------------------------------------------+ int OnInit() { int m_handleSub; //... if ((m_handleSub = iCustom(NULL, 0, "Chart In SubWindows\\Chart In SubWindow.ex5", user01, user02)) == INVALID_HANDLE) return INIT_FAILED; if (!ChartIndicatorAdd(ChartID(), (int) ChartGetInteger(ChartID(), CHART_WINDOWS_TOTAL), m_handleSub)) return INIT_FAILED; //... ChartRedraw(); return(INIT_SUCCEEDED); } //...Остальной код советника ...
Оба кода будут работать одинаково, но версия класса объекта позволит нам добавить больше вещей со временем, однако, версия, показанная выше, является консолидированной версией и не будет меняться. Обе версии делают одно и то же, они создают подокно из советника и помещают в это подокно все пользовательские индикаторы, созданные ранее. Обратите внимание, что код был изменен по сравнению с кодом, приведенным в начале статьи, и выделен цветом.
Заключение:
Очень интересно и любопытно, как мы решаем идти по пути достижения наших целей, несколько раз мы сталкиваемся и представляем, что достичь поставленных целей сложно, но с небольшим терпением и самоотдачей мы можем преодолеть препятствия, которые поначалу казались непреодолимыми. В этой статье я демонстрирую, как можно расширить функциональность класса без необходимости его модификации благодаря наследованию, и в то же время показываю, как можно добавить индикаторы к графикам, чтобы они работали так, как уже протестировано. Мы добавляем программу ex5 внутрь нашего советника и используем ее без необходимости переносить оригинальный ex5, просто загрузив советник.
В прикрепленном файле содержатся все улучшения, разработанные на данный момент, но скоро в этом коде будет еще больше интересных вещей. 😁👍
Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/10241
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Опубликована новая статья Множественные индикаторы на графике (часть 04): начало работы с советником:
Автор: Даниэль Хосе
Здравствуйте Даниил, я слежу за вашими статьями, но что насчет части 03?
Была небольшая проблема в то время, когда просили выпустить для публикации, но уже разрешили публикацию части 03, скоро она тоже будет доступна, эта проблема была вызвана скорее количеством статей, которые я уже отправил ... В настоящее время у них есть еще 15 статей для анализа, все они связаны с разработкой этого советника, и в каждой из них все сложнее .... но спасибо, что следите за серией ... ожидайте огромных новостей от статьи 05, с этого момента вещь действительно будет стоить того, потому что она станет чем-то большим для людей, эти первые просто представляют то, что будет впереди ...😁👍
Привет, Даниель,
У меня возникли проблемы с разноцветными индикаторами, и ваши статьи, которые мне очень нравятся, указывают на то, что вы можете знать их решение.
Я хочу создать функцию, которая устанавливает все атрибуты индикатора без использования опций #property, например, #property indicator_color1 clrCrimson, clrWhite, clrLime.
В приведенной ниже тестовой программе я обнаружил, что при включенном #property indicator_color1 clrCrimson,clrWhite,clrLime программа работает правильно, тогда как если я его закомментирую, то программа работает некорректно.В этом случае кажется, что программа строит график только по некоторым точкам данных, как будто она использует таймфрейм "выше?" или пропускает несколько точек данных. Я подозреваю, что директива property color устанавливает больше атрибутов, которые я не определил, когда указано несколько цветов.
Вторая проблема заключается в том, что я, очевидно, не понимаю деталей и требований использования многоцветных графиков. Я просмотрел документацию и не нашел ни одной статьи, в которой бы давался обзор использования многоцветных индикаторов. В то время как первый график правильно меняет цвет, второй, построенный по минимумам, не меняет цвета в соответствии с моим вызовом функции. Для правильной работы этого графика требуются все три свойства: type2, color2 и width2. Я также недоумеваю, почему в объявлениях свойств используется 2, а не 3. Я использовал индикатор Laguerre Adaptive Filter Младена, чтобы определить, что индикаторы свойств используют номер графика 2, а не номер индикатора 3 для правильного отображения.
Любые предложения, ссылки или помощь будут высоко оценены.
С уважением, CapeCoddah
Любые предложения, ссылки или помощь будут высоко оценены.
Искренне, CapeCoddah
Вы немного запутались, я могу понять... но вся путаница в том, что вы на самом деле не смотрите на детали в своем коде. Я попытаюсь объяснить некоторые детали, которые есть в комментарии, а затем немного расскажу о вашем коде.....
Первый момент заключается в том, что вы можете сделать многоцветный индикатор и без использования #property indicator_colorN, но для пользователя и даже для вас это практичнее, безопаснее и проще в понимании и модификации кода, потому что все, что вам нужно сделать, это зайти в свойства и изменить присутствующие там цвета, а для пользователя это проще, потому что ему нужно будет только выбрать цвет, который нужно изменить, и сделать изменение, это в стандартном окне, которое MT5 создает для индикаторов. Вы как-то уже правильно поступаете, когда используете команду PlotIndexSetInteger для генерации изменений цвета, и так оно и есть, когда мы не используем #property indicator_colorN, но когда мы используем свойства, часто не имеет смысла использовать команду PlotIndexSetInteger для установки других цветов, Это связано с тем, что это может быть более сложным в обслуживании и более запутанным для пользователя, так как он может не понимать, что пытается сказать стандарт цвета, даже если код ваш и вы будете единственным, кто будет использовать индикатор, это не имеет большого смысла, за исключением редких случаев, когда вы создаете динамический цветовой шаблон.
Теперь о втором моменте: Проблема в том, что вы путаете количество объектов, которые будет строить индикатор (2 для 2 линий) со свойствами объекта (в данном случае линии), а для того, чтобы линия была построена, вам нужно объявить как минимум 3 части информации, а именно TypeN, ColorN, WidthN, где N означает номер объекта, с практикой и временем вы поймете эти небольшие различия между свойствами индикатора и свойствами объектов, которые он использует... Не сдавайтесь... продолжайте учиться и скоро все станет понятнее... 😁👍
Теперь давайте посмотрим на ваш код... Я не буду показывать вам, как именно его исправить (если я это сделаю, будет неинтересно... .... 😁👍✌ ) Я хочу, чтобы вы обратили внимание на следующий факт, и это важно:
Обратите внимание, что я отметил две вещи в вашем коде... теперь давайте посмотрим, что происходит, когда это воспроизводится на графике.....
Видите, что только одна из меток отображается так, как вы объявили в коде, только HIGH .... , а LOW ?!!! где она ?!!! это первый пункт, который вы должны исправить, потому что тот факт, что метка LOW не отображается, указывает на то, что используется цветовая схема, которая объявлена в #property indicator_color2, то есть у вас сбой в этой точке, если вы попытаетесь удалить свойства индикатора 2, которые фактически создают линию LOW, даже сохранив остальную часть кода, линия HIGH будет построена, но линия LOW не будет....почему?!!! потому что на самом деле вы не определяете информацию, необходимую для построения линии LOW, это происходит динамически через использование вызова PlotIndexSetInteger... это кажется странным ... но это то, что происходит .....
Когда вам удастся это исправить, если вы действительно хотите использовать динамический способ объявления данных объекта строки с помощью PlotIndexSetInteger, вы сможете удалить команды компиляции #property indicator_color из сцены, так как необходимые данные будут устанавливаться динамически, но если вы не хотите прилагать такие усилия, то это нормально.....
Теперь я хочу, чтобы вы посмотрели на изображение выше и сравнили его с цветами, которые вы используете в #property indicator_color ... смотрите на эти цвета очень внимательно .... если вы сделаете это, то заметите там что-то странное .... опять же не буду говорить, чтобы не потерять удовольствие, но попробуйте использовать разные цвета, не повторяя ни один из них... когда вы повторяете их, становится сложнее понять, где ошибка... 😁👍
Теперь последняя деталь: То, что вам кажется, что он прокладывает только одни точки и пропускает другие, может быть по двум причинам: Цветовой шаблон не контрастирует с фоном графика, попробуйте использовать цвета, контрастирующие с фоном графика, и второе, но я не верю, что это действительно так, это то, что может быть сбой в событии OnCalcule, вы возвращаете -1 или значение i, правильнее было бы возвращать rates_total, так что измените это в коде, чтобы избежать проблем с футурами...
Привет, Дэниел,
Я запутался. Я думал, что характеристики рисунка определяются с помощью спецификации буфера, как в MQ4, тогда как, по крайней мере, для спецификаций DRAW_COLOR... характеристики рисунка определяются с помощью идентификатора последовательного графика. Я не выяснил, требуют ли спецификации сюжета также для DRAW_LINE и т. д. Более того, свойство indicator_colorX фактически имеет две функции: сначала подсчитывает и устанавливает количество цветов, а затем устанавливает каждый указанный цвет в соответствующую позицию массива.Прилагаю два файла: Color Test, который сейчас работает корректно, хотя и требует доработки, и немного измененный MACD Original2_1 Младена. Программа Младена интересна тем, что он определил два графика, но использует только один цветовой индексный буфер.
Спасибо за помощь