- Описание ресурсов с помощью директивы #resource
- Разделяемое использование ресурсов разных MQL-программ
- Ресурсные переменные
- Подключение пользовательских индикаторов как ресурсов
- Динамическое создание ресурсов: ResourceCreate
- Удаление динамических ресурсов: ResourceFree
- Чтение и модификация данных ресурса: ResourceReadImage
- Сохранение изображений в файл: ResourceSave
- Шрифты и вывод текста в графические ресурсы
- Прикладное применение графических ресурсов в трейдинге
Подключение пользовательских индикаторов как ресурсов
Для работы MQL-программ может потребоваться один или несколько пользовательских индикаторов. Все они могут быть включены в исполняемый ex5-файл как ресурсы, что упрощает распространение и установку.
Директива #resource с описанием вложенного индикатора имеет следующий формат:
#resource "путь_имя_индикатора.ex5" |
Правила задания и поиска указанного файла те же самые, что и для всех ресурсов в целом.
Мы уже использовали данную возможность в большом примера эксперта, в финальной версии UnityMartingale.mq5.
#resource "\\Indicators\\MQL5Book\\p6\\UnityPercentEvent.ex5" |
Далее этот ресурс передавался в функцию iCustom вместо имени индикатора: "::Indicators\\MQL5Book\\p6\\UnityPercentEvent.ex5".
Случай, когда пользовательский индикатор создает в функции OnInit одну или несколько копий себя, требует отдельного рассмотрения (если само это техническое решение кажется странным, мы приведем практический пример после вводных примеров).
Как мы знаем, для использования ресурса из MQL-программы его необходимо указывать в виде: "путь_имя_файла.ex5::имя_ресурса". Например, если индикатор EmbeddedIndicator.ex5 включается в качестве ресурса в другой индикатор MainIndicator.mq5 (а точнее, в его двоичный образ MainIndicator.ex5), то имя, указываемое при вызове самого себя через iCustom, уже не может быть кратким, без пути, а путь должен включать расположение "родительского" индикатора внутри папки MQL5. В противном случае система не сможет найти вложенный индикатор.
Действительно, в обычных обстоятельствах индикатор может вызвать самого себя, например, с помощью оператора iCustom(_Symbol, _Period, myself,...), где myself — это строка, равная либо MQLInfoString(MQL_PROGRAM_NAME), либо названию, которое было предварительно назначено в коде свойству INDICATOR_SHORTNAME. Но когда индикатор находится внутри другой MQL-программы как ресурс, имя уже не ссылается на соответствующий файл — ведь файл, который послужил прообразом для ресурса, остался на том компьютере, где производилась компиляция, а на компьютере пользователя есть только файл MainIndicator.ex5. Здесь потребуется некоторый анализ программного окружения при запуске программы.
Рассмотрим это на практике.
Создадим для начала индикатор NonEmbeddedIndicator.mq5. Важно отметить, что он расположен в папке MQL5/Indicators/MQL5Book/p7/SubFolder/, то есть в подпапке SubFolder относительно папки p7, выделенной для всех индикаторов данной Части книги. Это сделано намеренно, чтобы эмулировать ситуацию, когда откомпилированный файл отсутствует на компьютере пользователя. Как это работает (а точнее — демонстрирует проблему), мы сейчас увидим.
Индикатор имеет единственный входной параметр Reference, назначение которого — подсчет количества копий самого себя: при первом создании в параметре будет 0, и индикатор создаст свою копию со значением параметра 1. Вторая копия, "увидев" значение 1, уже не станет создавать еще одну копию (иначе мы быстро исчерпали бы ресурсы без пограничного условия остановки размножения).
input int Reference = 0; |
Для дескриптора индикатора-копии зарезервирована переменная handle.
int handle = 0; |
В обработчике OnInit мы для наглядности прежде всего выводим имя и путь MQL-программы.
int OnInit()
|
Далее идет код, подходящий для самозапуска обособленного индикатора (существующего в виде привычного файла NonEmbeddedIndicator.ex5).
if(Reference == 0)
|
Такой индикатор мы могли бы успешно разместить на графике и получили бы в журнале записи такого рода (актуальные пути файловой системы будут у вас свои):
0 Name: NonEmbeddedIndicator Full path: C:\Program Files\MT5East\MQL5\Indicators\MQL5Book\p7\SubFolder\NonEmbeddedIndicator.ex5 Success 1 Name: NonEmbeddedIndicator Full path: C:\Program Files\MT5East\MQL5\Indicators\MQL5Book\p7\SubFolder\NonEmbeddedIndicator.ex5 Success |
Таким образом, копия запустилась успешно просто по имени "NonEmbeddedIndicator".
Оставим пока данный индикатор и создадим второй — FaultyIndicator.mq5, в который подключим первый индикатор как ресурс (обратите внимание на указание подпапки SubFolder в относительном пути ресурса — это нужно, поскольку индикатор FaultyIndicator.mq5 находится в папке на уровень выше: MQL5/Indicators/MQL5Book/p7/).
// FaultyIndicator.mq5
|
Если попытаться запустить откомпилированный FaultyIndicator.ex5, возникнет ошибка:
0 Name: NonEmbeddedIndicator Full path: C:\Program Files\MT5East\MQL5\Indicators\MQL5Book\p7\FaultyIndicator.ex5 » » ::SubFolder\NonEmbeddedIndicator.ex5 cannot load custom indicator 'NonEmbeddedIndicator' [4802] |
Во время запуска копии вложенного индикатора он ищется в папке основного индикатора, в котором описан ресурс. Но там файла NonEmbeddedIndicator.ex5 нет, поскольку требуемый ресурс находится внутри FaultyIndicator.ex5.
Чтобы решить проблему модифицируем NonEmbeddedIndicator.mq5. Прежде всего, дадим ему другое, более правильное имя EmbeddedIndicator.mq5. В исходном коде нам потребуется добавить вспомогательную функцию GetMQL5Path, которая из общего пути запускаемой MQL-программы умеет вычленить относительную часть внутри папки MQL5 (в этой части будет находиться и название ресурса, если индикатор запускается из ресурса).
// EmbeddedIndicator.mq5
|
С учетом новой функции изменим вызов iCustom в обработчике OnInit.
int OnInit()
|
Убедимся, что данная правка не сломала запуск индикатора самого по себе. Наложение на график приводит к появлению в журнале ожидаемых строк:
0 Name: EmbeddedIndicator Full path: C:\Program Files\MT5East\MQL5\Indicators\MQL5Book\p7\SubFolder\EmbeddedIndicator.ex5 Location in MQL5:\Indicators\MQL5Book\p7\SubFolder\EmbeddedIndicator.ex5 Success 1 Name: EmbeddedIndicator Full path: C:\Program Files\MT5East\MQL5\Indicators\MQL5Book\p7\SubFolder\EmbeddedIndicator.ex5 Location in MQL5:\Indicators\MQL5Book\p7\SubFolder\EmbeddedIndicator.ex5 Success |
Здесь добавился отладочный вывод относительного пути, который получила функция GetMQL5Path. Именно эта строка теперь используется в iCustom, и она работает в данном режиме — копия создалась.
Теперь встроим этот индикатор как ресурс в другой индикатор в папке MQL5Book/p7 с именем MainIndicator.mq5. Примечательно, что MainIndicator.mq5 полностью идентичен FaultyIndicator.mq5 за исключением лишь подключаемого ресурса.
// MainIndicator.mq5
|
Откомпилируем и запустим его. В журнале появятся записи с новым относительным путем, включающим вложенный ресурс.
0 Name: EmbeddedIndicator Full path: C:\Program Files\MT5East\MQL5\Indicators\MQL5Book\p7\MainIndicator.ex5 » » ::SubFolder\EmbeddedIndicator.ex5 Location in MQL5:\Indicators\MQL5Book\p7\MainIndicator.ex5::SubFolder\EmbeddedIndicator.ex5 Success 1 Name: EmbeddedIndicator Full path: C:\Program Files\MT5East\MQL5\Indicators\MQL5Book\p7\MainIndicator.ex5 » » ::SubFolder\EmbeddedIndicator.ex5 Location in MQL5:\Indicators\MQL5Book\p7\MainIndicator.ex5::SubFolder\EmbeddedIndicator.ex5 Success |
Как мы видим, на этот раз вложенный индикатор успешно создал копию самого себя, так как использовал квалифицированное название с относительным путем и именем ресурса "\\Indicators\\MQL5Book\\p7\\MainIndicator.ex5::SubFolder\\EmbeddedIndicator.ex5".
Во время многократных экспериментов с запуском данного индикатора обратите внимание, что вложенные копии не сразу выгружаются с графика после удаления главного индикатора. Поэтому повторные запуски следует производить, только дождавшись выгрузки: в противном случае еще выполняющиеся копии будут использованы повторно, и в журнале не появятся вышеприведенные строки инициализации. Для контролирования выгрузки в код добавлена распечатка значения Reference в обработчике OnDeinit.
Мы обещали показать, что создание индикатором своей копии не является чем-то экстраординарным. В качестве прикладной демонстрации данного приема возьмем индикатор DeltaPrice.mq5, который рассчитывает разницу приращений цен заданного порядка. Порядок 0 означает отсутствие дифференцирования (только для проверки исходного временного ряда), 1 — однократное дифференцирование, 2 — двукратное и так далее.
Порядок задается во входном параметре Differencing.
input int Differencing = 1; |
Разностный ряд будет отображаться в единственном буфере в подокне.
#property indicator_separate_window
|
В обработчике OnInit мы не только настраиваем буфер, но и создаем тот же индикатор, передавая во входном параметре уменьшенную на 1 величину.
#include <MQL5Book/AppliedTo.mqh> // APPLIED_TO_STR macro
|
Чтобы избежать потенциальных проблем со встраиванием индикатора в качестве ресурса, мы используем уже проверенную функцию GetMQL5Path.
В функции OnCalculate выполняем операцию вычитания соседних значений временного ряда. Когда Differencing равно 1, операндами выступают элементы массива price. При большем значении Differencing, мы читаем буфер копии индикатора, созданной для предыдущего порядка.
int OnCalculate(const int rates_total,
|
Исходный тип дифференцируемой цены задается в диалоге настроек индикатора в выпадающем списке Применить к. По умолчанию это цена Close.
Вот как на графике выглядят несколько копий индикатора с разными порядками дифференцирования.
Разница цен Close различных порядков