Книга Продвинутое использование торговой платформы MetaTrader 5. Создание индикаторов и торговых роботов на MQL5 и Python. Издание 3-е, исправленное и дополненное - читать онлайн бесплатно, автор Тимур Машнин. Cтраница 2
bannerbanner
Вы не авторизовались
Войти
Зарегистрироваться
Продвинутое использование торговой платформы MetaTrader 5. Создание индикаторов и торговых роботов на MQL5 и Python. Издание 3-е, исправленное и дополненное
Продвинутое использование торговой платформы MetaTrader 5. Создание индикаторов и торговых роботов на MQL5 и Python. Издание 3-е, исправленное и дополненное
Добавить В библиотекуАвторизуйтесь, чтобы добавить
Оценить:

Рейтинг: 0

Добавить отзывДобавить цитату

Продвинутое использование торговой платформы MetaTrader 5. Создание индикаторов и торговых роботов на MQL5 и Python. Издание 3-е, исправленное и дополненное

В данном случае, это 0, 1 и 2.

Теперь при отрисовке диаграммы индикатора, из буфера берется значение диаграммы, по позиции значения оно сопоставляется со значением буфера цвета, и элемент диаграммы становится цветным.



Вместо свойства #property indicator_color, цвета графического построения можно задать программным способом:

Задаем количество индексов цветов для графического построения с помощью функции:

PlotIndexSetInteger(0,PLOT_COLOR_INDEXES,3);

И задаем цвет для каждого индекса с помощью функции:

PlotIndexSetInteger(0,PLOT_LINE_COLOR,0,Red);

Где первый параметр – индекс графического построения, соответственно первое графическое построение имеет индекс 0.

Это идентично объявлению:

#property indicator_color1 Red,Green,Blue



Давайте продолжим рассмотрение свойств индикатора.

Толщина линии диаграммы индикатора задается свойством indicator_widthN, где N – номер графического построения, например:

#property indicator_width1 1

Также можно задать стиль линии диаграммы индикатора – сплошная линия, прерывистая, пунктирная, штрих-пунктирная, штрих – с помощью свойства indicator_styleN, где N – номер графического построения, например:

#property indicator_style1 STYLE_SOLID

И, наконец, свойство indicator_labelN указывает метки диаграмм индикатора в DataWindow или Окно данных, например:

#property indicator_label1 "ADX"

#property indicator_label2 "+DI"

#property indicator_label3 "-DI"



Другие свойства индикатора можно посмотреть в справочнике.

Правда можно отметить еще одну группу свойств, которая позволяет нарисовать горизонтальный уровень индикатора в отдельном окне, например:



#property indicator_level1 30.0

#property indicator_levelcolor Red

#property indicator_levelstyle STYLE_SOLID

#property indicator_levelwidth 2

В результате добавления этих строк кода в индикатор ADX, у него появится горизонтальный уровень.



Теперь, на примере индикатора ADX, при присоединении индикатора к графику в MetaTrader 5, во-первых, откроется диалоговое окно индикатора, которое во вкладке Общие отобразит значения свойств copyright, link и description.



А во вкладке Цвета отобразятся значения свойств indicator_label, indicator_color, indicator_width, indicator_style.

Само же название индикатора определяется именем файла индикатора.

К слову сказать, диалоговое окно индикатора можно открыть и после присоединения индикатора к графику, с помощью контекстного меню, щелкнув правой кнопкой мышки на индикаторе и выбрав свойства индикатора.



При наведении курсора на название индикатора в окне Navigator терминала всплывает подсказка, отображающая свойство copyright.



После присоединения индикатора свойство:

#property indicator_label1 "ADX"

работать не будет, так как в функции OnInit() с помощью вызова функции:

string short_name="ADX("+string(ExtADXPeriod)+")";

IndicatorSetString(INDICATOR_SHORTNAME,short_name);

изменена подпись индикатора на ADX(14) – период индикатора.



А вызовом функции:

PlotIndexSetString(0,PLOT_LABEL,short_name);

изменена метка индикатора в окне Окно Данных, которое открывается в меню Вид терминала.

Значения же свойств:

#property indicator_label2 "+DI"

#property indicator_label3 "-DI"

отображаются, как и было определено, во всплывающих подсказках к диаграммам индикатора и отображаются в окне Окно Данных.



В коде индикатора ADX объявленное количество буферов индикатора больше, чем количество графических построений.

Свойство indicator_buffers равно 6

А свойство indicator_plots равно 3

Сделано это для того, чтобы использовать три буфера индикатора для промежуточных расчетов.

Это массивы ExtPDBuffer, ExtNDBuffer и ExtTmpBuffer.

В функции OnCalculate индикатора, значения массивов ExtPDBuffer, ExtNDBuffer, ExtTmpBuffer рассчитываются на основе загруженной ценовой истории, а затем уже на их основе рассчитываются значения массивов ExtADXBuffer, ExtPDIBuffer, ExtNDIBuffer, которые используются для отрисовки диаграмм индикатора.

Как уже было сказано, буферы индикатора для промежуточных вычислений здесь объявляются с константой INDICATOR_CALCULATIONS, так как заранее неизвестен размер загружаемой ценовой истории.



Теперь, в описании индикатора ADX сказано, что:

Сигнал на покупку формируется тогда, когда +DI поднимается выше – DI и при этом сам ADX растет.

В момент, когда +DI расположен выше – DI, но сам ADX начинает снижаться, индикатор подает сигнал о том, что рынок «перегрет» и пришло время фиксировать прибыль.

Сигнал на продажу формируется тогда, когда +DI опускается ниже – DI и при этом ADX растет.

В момент, когда +DI расположен ниже – DI, но сам ADX начинает снижаться, индикатор подает сигнал о том, что рынок «перегрет» и пришло время фиксировать прибыль.

Давайте, модифицируем код индикатора ADX таким образом, чтобы раскрасить диаграмму ADX в четыре цвета, которые соответствуют описанным выше четырем торговым сигналам.



В качестве первого шага изменим свойство indicator_type1 на DRAW_COLOR_LINE.

Далее увеличим на единицу значение свойства indicator_buffers на значение 7.

Объявим массив для буфера цвета ExtColorsBuffer.



И в функции OnInit() свяжем объявленный массив с буфером цвета с помощью функции SetIndexBuffer.

Тут есть хитрость – индекс буфера цвета должен следовать за индексом буфера значений индикатора.

Если, например, связать массив ExtColorsBuffer с буфером с индексом 6, тогда индикатор не будет корректно отрисовываться.



В свойство indicator_color1 добавим цветов.

И увеличим толщину линии с помощью свойства indicator_width1.



В функции OnCalculate в конце перед закрывающей скобкой цикла for добавим код заполнения буфера цвета значениями согласно описанной нами стратегии.



Откомпилируем код и получим индикатор с визуальным отображением сигналов на покупку и продажу:



В редакторе MQL5 откроем другой индикатор из папки Examples – RSI.

Данный индикатор имеет два ключевых уровня, которые определяют области перекупленности и перепроданности.

В коде индикатора эти уровни определены как свойства:

#property indicator_level1 30

#property indicator_level2 70

Давайте улучшим отображение этих уровней, добавив им цвета и стиля.



Для этого добавим свойства:

#property indicator_levelcolor Red

#property indicator_levelstyle STYLE_SOLID

#property indicator_levelwidth 1

Теперь индикатор будет выглядеть следующим образом.



Параметры ввода и переменные индикатора

Параметры ввода – это те параметры индикатора, которые отображаются пользователю перед присоединением индикатора к графику во вкладке Входные параметры диалогового окна.



Например, для индикатора MACD – это периоды скользящих средних и тип применяемой цены.

Здесь пользователь может поменять параметры индикатора по умолчанию, и индикатор присоединится к графику с уже измененными параметрами.

Также пользователь может поменять параметры индикатора после присоединения индикатора к графику, щелкнув правой кнопкой мышки на индикаторе и выбрав свойства индикатора.



В коде индикатора такие параметры задаются input переменными с модификатором input, который указывается перед типом данных. Как правило, input переменные объявляются сразу после свойств индикатора.

Например, для индикатора MACD – это периоды для

экспоненциальной скользящей средней с коротким периодом от цены, экспоненциальной скользящей средней с длинным периодом от цены, сглаживающей скользящей средней с коротким периодом от разницы двух остальных скользящих, и тип применяемой цены.

Здесь надо отметить то, что в диалоговом окне присоединения индикатора к графику отображаются не имена переменных, а комментарии к ним.

Если убрать комментарии, входные параметры отобразятся следующим образом.



Здесь уже отображаются имена переменных.

Как вы сами, наверное, уже догадались, комментарии используются для отображения, чтобы облегчить пользователю понимание их предназначения.

Здесь также видно, что входными параметрами могут быть не только отдельные переменные, но и перечисления, которые отображаются в виде выпадающих списков.



Для индикатора MACD используется встроенное перечисление ENUM_APPLIED_PRICE, но можно также определить и свое перечисление.

В справочнике приводится соответствующий пример.



В этом примере команда #property script_show_inputs используется для скриптов, для индикаторов ее можно опустить.

Основное отличие input переменных от других типов переменных состоит в том, что изменить их значение может только пользователь в диалоговом окне индикатора.

Если в коде индикатора попытаться изменить значение входного параметра, при компиляции возникнет ошибка.



Поэтому, если вы хотите при расчетах использовать измененное значение входного параметра, нужно использовать промежуточную переменную.



Помимо input переменных MQL5-код использует локальные переменные, статические переменные, глобальные переменные и extern переменные.



С локальными переменными в принципе все понятно, они объявляются в блоке кода, например, в цикле или функции, там же инициализируются, и, после выполнения блока кода, память, выделенная под локальные переменные в программном стеке, освобождается.

Тут особо надо отметить, что для локальных объектов, созданных с помощью оператора new, в конце блока кода нужно применить оператор delete для освобождения памяти.

Глобальные переменные, как правило, объявляются после свойств индикатора, входных параметров и массивов буферов индикатора, перед функциями.

Глобальные переменные видны в пределах всей программы, их значение может быть изменено в любом месте программы и память, выделяемая под глобальные переменные вне программного стека, освобождается при выгрузке программы.

Здесь видно, что input переменные – это те же глобальные переменные, за исключением опции – их значение не может быть изменено в любом месте программы.

Если глобальную или локальную переменную объявить со спецификатором const – это так же не позволит изменять значение этой переменной в процессе выполнения программы.

Статические переменные определяются модификатором static, который указывается перед типом данных.

Со статическими переменными все немного сложнее, но легче всего их понять, сравнивая статические переменные с локальными и глобальными переменными.

В принципе, статическая переменная, объявленная там же, где и глобальная переменная, ничем не отличается от глобальной переменной.

Хитрость начинается, если локальную переменную объявить с модификатором static.

В этом случае, после выполнения блока кода, память, выделенная под статическую переменную, не освобождается. И при следующем выполнении того же блока кода, предыдущее значение статической переменной можно использовать.

Хотя область видимости такой статической переменной ограничивается те же самым блоком кода, в котором она была объявлена.

extern переменные это аналог статических глобальных переменных. Нельзя объявить локальную переменную с модификатором extern.

Отличие extern переменных от статических глобальных переменных проще всего продемонстрировать на индикаторе MACD.



Индикатор MACD имеет включаемый файл MovingAverages, обозначенный с помощью директивы #include и расположенный в папке Include.

Если в файле MovingAverages и файле MACD одновременно объявить extern-переменную:

extern int a=0;

то при компиляции обоих файлов все пройдет удачно, и переменную можно будет использовать.

Если же в файле MovingAverages и файле MACD одновременно объявить статическую глобальную переменную:

static int a=0;

тогда при компиляции обоих файлов возникнет ошибка.



Помимо команды #include полезной является также директива #define, которая позволяет делать подстановку выражения вместо идентификатора, например:



#define PI 3.14

Хэндл индикатора

Начнем с цитаты:

HANDLE идентифицирует объект, которым Вы можете манипулировать. Джеффри РИХТЕР "Windows для профессионалов".



Переменные типа handle представляют собой указатель на некоторую системную структуру или индекс в некоторой системной таблице, которая содержит адрес структуры.

Таким образом, получив хэндл некоторого индикатора, мы можем использовать его данные для построения своего индикатора.

Хэндл индикатора представляет собой переменную типа int и объявляется, как правило, после объявления массивов буферов индикатора, вместе с глобальными переменными, например в индикаторе MACD:



Объявляются два хэндла – int ExtFastMaHandle и int ExtSlowMaHandle.

Здесь хэндлы индикаторов – это указатели на индикатор скользящего среднего с разными периодами 12 и 26.

Объявив эти переменные, мы, естественно, реально ничего не получаем, так как объекта индикатора, данные которого мы хотим использовать, еще не существует.

Создать в глобальном кеше клиентского терминала копию соответствующего технического индикатора и получить ссылку на нее можно несколькими способами.

Если это стандартный индикатор, проще всего получить его хэндл можно с помощью стандартной функции для работы с техническими индикаторами.



Стандартная функция для индикатора скользящего среднего это функция iMA.

И в индикаторе MACD хэндлы индикатора скользящего среднего получаются с помощью вызова функции iMA в функции OnInit().



где используются свойства индикатора – InpFastEMA, InpSlowEMA и InpAppliedPrice.

Предположим, что мы хотим использовать не стандартный, а пользовательский индикатор.



В папке Indicators/Examples редактора MQL5 есть нужный нам индикатор – это файл Custom Moving Average.mq5.

Для вызова того индикатора воспользуемся функцией iCustom.



В функции OnInit() индикатора MACD изменим код, где для получения хэндлов вместо стандартной функции, используем функцию iCustom.



После компиляции индикатора мы увидим, что его отображение никак не изменилось.



Еще один способ получить хэндл пользовательского индикатора, это использовать функцию IndicatorCreate.



В функции OnInit() индикатора MACD изменим код, где для получения хэндлов используем функцию IndicatorCreate.



После компиляции индикатора мы опять увидим, что его отображение никак не изменилось.

После получения хэндла индикатора, если он используется в коде один раз, для экономии памяти неплохо использовать функцию IndicatorRelease.



Которая удаляет хэндл индикатора и освобождает расчетную часть индикатора.

Хорошо, хэндл индикатора мы получили. Как же теперь извлечь его данные?

Делается это в функции OnCalculate с помощью функции CopyBuffer.



При этом функция CopyBuffer() распределяет размер принимающего массива под размер копируемых данных.

Напомним, что это работает, если принимающий массив является просто динамическим массивом.

Если же принимающий массив связан с буфером индикатора, тогда клиентский терминал сам заботится о том, чтобы размер такого массива соответствовал количеству баров, доступных индикатору для расчета.

В индикаторе MACD именно такая ситуация.



Промежуточные массивы ExtFastMaBuffer и ExtSlowMaBuffer привязаны к буферам индикатора с помощью функции SetIndexBuffer.



И в эти массивы производится копирование буфера индикатора Moving Average на основе его хэндлов с помощью функции CopyBuffer.



Если убрать привязку массивов ExtFastMaBuffer и ExtSlowMaBuffer к буферам индикатора, тогда клиентский терминал выдаст ошибку.

Происходит это потому, что при загрузке индикатора значение to_copy равно размеру ценовой истории, а дальше to_copy=1 и производится частичное копирование в массивы ExtFastMaBuffer и ExtSlowMaBuffer, при этом их размеры становятся равны 1.

В этом случае применением функции ArrayResize проблему не решить, так как функция CopyBuffer все равно будет уменьшать размер массива до 1.

Можно, конечно, использовать еще один массив-посредник, в который копировать один элемент. И уже из этого массива-посредника производить копирование в промежуточный массив, но проще всего, конечно, просто привязать промежуточный массив к буферу индикатора.

Функция OnInit

Как уже говорилось, функции OnInit(), OnDeinit(), OnCalculate() вызываются клиентским терминалом при наступлении определенных событий.



Функция OnInit() вызывается сразу после загрузки индикатора и соответственно используется для его инициализации.

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

Давайте разберем некоторые из этих пунктов более подробно.

Как уже было показано, привязка массивов к буферам индикатора осуществляется с помощью функции SetIndexBuffer.



Где data_type может быть INDICATOR_DATA (данные индикатора для отрисовки, по умолчанию, можно не указывать), INDICATOR_COLOR_INDEX (цвет индикатора), INDICATOR_CALCULATIONS (буфер промежуточных расчетов индикатора).

После применения функции SetIndexBuffer к динамическому массиву, его размер автоматически поддерживается равным количеству баров, доступных индикатору для расчета.

Каждый индекс массива типа INDICATOR_COLOR_INDEX соответствует индексу массива типа INDICATOR_DATA, а значение индекса массива типа INDICATOR_COLOR_INDEX определяет цвет отображения индекса массива типа INDICATOR_DATA.

Значение индекса массива типа INDICATOR_COLOR_INDEX, при его установке, берется из свойства #property indicator_colorN как индекс цвета в строке.

Индекс буфера типа INDICATOR_COLOR_INDEX должен следовать за индексом буфера типа INDICATOR_DATA.

После привязки динамического массива к буферу индикатора можно поменять порядок доступа к массиву от конца к началу, т.е. значение массива с индексом 0 будет соответствовать последнему полученному значению индикатора. Сделать это можно с помощью функции ArraySetAsSeries.



При применении функции ArraySetAsSeries физическое хранение данных массива не меняется, в памяти, массив, как и прежде, хранится в порядке от первого значения до последнего значения.

Функция ArraySetAsSeries меняет лишь программный доступ к элементам массива – от последнего элемента массива к первому элементу массива.

В функции OnInit() также может осуществляться проверка входных параметров на корректность, так как пользователь может ввести все, что угодно.

При этом значение входного параметра переназначается с помощью глобальной переменной, и далее в расчетах используется уже значение глобальной переменной.

Например, для индикатора ADX это выглядит так:



здесь ExtADXPeriod – глобальная переменная, а InpPeriodADX – входной параметр.

При использовании хэндлов индикатора, можно указывать символ (финансовый инструмент), для которого индикатор будет создаваться.

При этом такой символ может определяться пользователем.

В функции OnInit() также полезно проверить этот входной параметр на корректность.



Например, в коде индикатора MACD пусть определен входной параметр:

input string symbol=" ";

Объявим глобальную переменную:

string name=symbol;

И в функции OnInit() произведем проверку – удалим пробелы слева и справа с помощью функции StringTrimRight, и если после этого длина строки name нулевая, возьмем символ с графика, на котором запущен индикатор.



Программная установка свойств индикатора осуществляется с помощью функций IndicatorSetDouble, IndicatorSetInteger, IndicatorSetString, PlotIndexSetDouble, PlotIndexSetInteger, PlotIndexSetString.



Функция IndicatorSetDouble позволяет программным способом определять такие свойства индикатора как indicator_minimum, indicator_maximum и indicator_levelN, например:

IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, 50)

является аналогом:

property indicator_level1 50



Функция IndicatorSetInteger позволяет программным способом определять такие свойства индикатора как indicator_height, indicator_levelcolor, indicator_levelwidth, indicator_levelstyle.