Пользовательские индикаторы и скрипты на MQL4: примеры кода

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

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

Буфферы

Буферы — это массивы, в которых хранятся значения индикаторов и расчеты. Пользовательский индикатор может иметь до 8 буферов. Буферы используют индексы, как и массивы, и находятся в диапазоне от 0 до 7. Когда вы вызываете пользовательский индикатор в советнике с помощью функции iCustom(), последним последним параметром в функции является буфер индикатора.

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

Создание кастомного индикатора

Давайте создадим пользовательский индикатор, используя два встроенных индикатора MetaTrader для расчета наших линий. Мы собираемся построить модифицированный индикатор полос Боллинджера. Полосы Боллинджера состоят из 3 линий — центральной линии, которая представляет собой простую скользящяую среднюю, наряду с верхней и нижней линиями, значение которых определяется стандартным отклонением.

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

Начнем с использования мастера для создания нашего файла индикатора. Выберите «Создать» в меню «Файл» или на панели инструментов, чтобы открыть мастер и создать собственный индикатор. Заполните имя индикатора и добавьте параметры, если хотите. На последней странице мы добавили три индикаторные линии одного цвета. Вот результат работы мастера. На данный момент мы исключили функцию start():

//+------------------------------------------------------------------+
//| EMA Bollinger.mq4
//| Александр Паркер
//| https://traderblog.net
+------------------------------------------------------------------+ 
#property copyright "Александр Паркер"
#property link "http://www.easyexpertforex.com"
#property indicator_chart_window 
#property indicator_buffers 3 
#property indicator_color1 DeepSkyBlue 
#property indicator_color2 DeepSkyBlue 
#property indicator_color3 DeepSkyBlue 
//---- buffers
double ExtMapBuffer1[];
double ExtMapBuffer2[];
double ExtMapBuffer3[]; 
//+------------------------------------------------------------------+ 
//| Инициализация | 
//+------------------------------------------------------------------+ 
int init()
{
//---- indicators
       SetIndexStyle(0,DRAW_LINE);
       SetIndexBuffer(0,ExtMapBuffer1);
       SetIndexStyle(1,DRAW_LINE);
       SetIndexBuffer(1,ExtMapBuffer2);
       SetIndexStyle(2,DRAW_LINE);
       SetIndexBuffer(2,ExtMapBuffer3);
//---- return(0);
}

Давайте обратим наше внимание на элементы, перечисленные жирным шрифтом. Объявления #property устанавливают параметры для наших индикаторных буферов. Свойство Indicator_chart_window рисует наш индикатор в главном окне графика. Если бы мы создавали осциллятор и хотели нарисовать индикатор в отдельном окне, мы бы использовали вместо этого свойство Indicator_separate_window.

Свойство Indicator_buffers устанавливает количество буферов для нашего индикатора. В этом случае мы используем три буфера. Свойства цвета устанавливает цвет всех трех линий на DeepSkyBlue.

Далее идут объявления для наших буферных массивов. У нас есть три буфера с именем ExtMapBuffer (1-3). Скоро мы изменим эти идентификаторы массива на что-то более описательное.

Функция init() — это то, где мы устанавливаем свойства для наших индикаторных буферов. SetIndexBuffer() связывает буферный массив с индексом буфера. Индекс буфера — это то, к чему мы обращаемся, когда устанавливаем свойства для линии индикатора, а также когда мы вызываем линию индикатора из советника с помощью функции iCustom(). Первый параметр — это целое число от 0 до 7, а второй параметр — это имя буферного массива.

Отрисовка свойств

Функция SetIndexStyle() устанавливает тип линии для рисования, а также свойства этой линии. Каждая строка индикатора будет иметь соответствующую функцию SetIndexStyle(). Вот ее синтаксис:

void SetIndexStyle(int BufferIndex, int LineType, int LineStyle = EMPTY, int LineWidth = EMPTY, color LineColor = CLR_NONE)
  • BufferIndex — Индекс буфера, от 0 до 7.
  • LineType — устанавливает тип линии для рисования. DRAW_LINE рисует одну линию, DRAW_HISTOGRAM рисует вертикальную гистограмму, DRAW_ARROW рисует символ, а DRAW_NONE не рисует линию.
  • LineStyle — необязательный параметр, указывающий стиль рисования. Используется в основном для строк типа DRAW_LINE. По умолчанию сплошная линия рисуется (STYLE_SOLID). Вы также можете рисовать пунктирные (STYLE_DASH) и пунктирные (STYLE_DOT) линии.
  • LineWidth — необязательный параметр, указывающий ширину линии в пикселях. Значением по умолчанию является 1.
  • LineColor — необязательный параметр, указывающий цвет линии. Если вы используете мастер, цвет устанавливается с помощью объявлений #property, но вы также можете установить цвет и здесь.

Если вы используете DRAW_ARROW в качестве LineType, функция SetArrow() позволяет вам установить символ шрифта Wingdings для рисования на графике. Первый параметр — это индекс буфера, а второй — целочисленная константа, представляющая символ для рисования.

Вы можете добавить описание для линий индикатора, которые будут отображаться во всплывающей подсказке или в окне данных. Для этого используйте функцию SetIndexLabel(). Первый параметр — это индекс буфера, а второй параметр — текстовое описание. Мы добавим их в наш индикатор в ближайшее время.

Если ваш индикатор отображается в отдельном окне (например, осциллятор), и вы хотите добавить уровни, чтобы указать уровни перекупленности или перепроданности (например, в индикаторах Стохастик или RSI), или нулевой уровень (например, в Индикатор CCI), вы можете использовать функции SetLevelStyle() и SetLevelValue().

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

Использование описательных буфферных имен

Вот наш обновленный код индикатора. Обратите внимание, что мы переименовали буферные массивы, чтобы они стали более наглядными относительно их действительной функции. Мы изменили второй параметр функций SetIndexBuffer(), чтобы отразить новые имена буферов. Мы также добавили SetIndexLabel() для каждой строки для отображения описательных имен в окне данных.

//---- buffers
double EMA[];
double UpperBand[];
double LowerBand[]; 
//+------------------------------------------------------------------+ 
//| Custom indicator initialization function | 
//+------------------------------------------------------------------+ 
int init()
{
//---- индикаторы
SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,EMA);
SetIndexLabel(0,"EMA");
SetIndexStyle(1,DRAW_LINE); SetIndexBuffer(1,UpperBand); SetIndexLabel(1,"UpperBand");
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,LowerBand); SetIndexLabel(2,"LowerBand");
//---- return(0);
}

Мы переименовали наши буферные массивы из имен по умолчанию (ExtMapBuffer) в более описательные. EMA[] будет нашим буфером для центральной линии, а UpperBand[] и LowerBand[] будут верхней и нижней полосами соответственно.

Функции SetIndexBuffer() связывает буферные индексы с нашими буферными массивами. EMA равно 0, UpperBand равно 1, а LowerBand равно 2. Обратите внимание, что фигурные скобки исключены из имени идентификатора массива для второго параметра SetIndexBuffer().

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

Функция start() для индикаторов

Мастер вставляет только одно выражение в функцию start():

int counted_bars = IndicatorCounted();

IndicatorCounting() возвращает количество баров на графике, которое уже рассчитал индикатор. При первом запуске советника это значение будет равно 0. Индикатор будет рассчитываться для каждого бара на графике. На последующих барах мы проверим функцию IndicatorCounting(), чтобы увидеть, сколько баров уже рассчитано, поэтому мы точно будем знать, сколько новых баров нам нужно рассчитать.

Наши расчеты индикатора будут происходить внутри цикла for. Начальной точкой будет первый невычисленный бар, а конечной точкой будет текущий бар. Мы сравним значение IndicatorCounting() с предопределенной переменной Bars, которая возвращает количество баров на текущем графике. Это определит нашу отправную точку. Вот код для цикла for:

int counted_bars = IndicatorCounted(); 
if(counted_bars > 0) counted_bars--; 
int CalculateBars = Bars - counted_bars;
for(int Count = CalculateBars; Count >= 0; Count--) {
}

Первый оператор if будет уменьшать значение countted_bars на 1 при расчете новых баров. Мы всегда будем рассчитывать как минимум два предыдущих бара. Это связано с тем, что в некоторых случаях последний тик бара может не рассчитываться. Затем мы определяем количество баров для вычисления, вычитая countted_bars из предопределенной переменной Bars. Это значение хранится в переменной CalculateBars.

В нашем цикле for инкрементная переменная Count устанавливается равной значению CalculateBars, условием завершения является случай, когда Count меньше 0 и переменная Count уменьшается на каждой итерации. Это будет рассчитывать каждый бар на графике слева направо.

Вот код для расчета наших полос Боллинджера. Мы объявим внешнюю переменную BandsPeriod в начале файла.

// Внешние параметры
 extern int BandsPeriod = 20;
 
// start() function
for(int Count = CalculateBars; Count >= 0; Count--)
{
EMA[Count] = iMA(NULL,0,BandsPeriod,0,MODE_EMA,0,Count);
double StdDev = iStdDev(NULL,0,BandsPeriod,0,MODE_EMA,0,Count);
UpperBand[Count] = EMA[Count] + StdDev;
LowerBand[Count] = EMA[Count] - StdDev;
}

Сначала мы вызываем встроенный индикатор Moving Average с помощью функции iMA() и присваиваем возвращаемое значение EMA [Count]. Обратите внимание, что индекс массива и параметр Shift для индикатора скользящей средней используют текущее значение Count.

Далее мы вызываем индикатор стандартного отклонения с помощью iStdDev(). Чтобы вычислить верхнюю полосу, нам нужно добавить стандартное отклонение к линии скользящей средней. Это значение хранится в буферном массиве UpperBand[]. Чтобы вычислить LowerBand[], мы вычтем стандартное отклонение от скользящей средней.

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

// Внешние параметры
extern int BandsPeriod = 20; 
extern int BandsShift = 0; 
extern int BandsMethod = 1; 
extern int BandsPrice = 0; 
extern int Deviations = 1;
 
// start() function
for(int Count = CalculateBars; Count >= 0; Count--) {
EMA[Count] = iMA(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice,Count);
double StdDev = iStdDev(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice,Count);
UpperBand[Count] = EMA[Count] + (StdDev * Deviations);
LowerBand[Count] = EMA[Count] - (StdDev * Deviations); 
}

Мы добавили внешние переменные, чтобы настроить остальные параметры для функций iMA() и iStdDev(). Мы также добавили параметр для настройки количества стандартных отклонений. Чтобы вычислить это, мы просто умножаем StdDev на отклонения. Теперь у нас есть полностью настраиваемый индикатор полос Боллинджера, который является более гибким, чем стандартный индикатор MetaTrader.

Полный код индикатора:

#property copyright "Александр Паркер"
 
#property indicator_chart_window
#property indicator_buffers 3
#property indicator_color1 DeepSkyBlue
#property indicator_color2 DeepSkyBlue
#property indicator_color3 DeepSkyBlue
 
// Внешние переменные
extern int BandsPeriod = 20;
extern int BandsShift = 0;
extern int BandsMethod = 1;
extern int BandsPrice = 0;
extern int Deviations = 1;
 
// Буфферы
double EMA[];
double UpperBand[];
double LowerBand[];
 
// Init
int init()
{
SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,EMA); SetIndexLabel(0,"EMA");
SetIndexStyle(1,DRAW_LINE); SetIndexBuffer(1,UpperBand); SetIndexLabel(1,"UpperBand");
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,LowerBand); SetIndexLabel(2,"LowerBand");
return(0); 
}
 
// Start
int start()
{
int counted_bars = IndicatorCounted();
int CalculateBars = Bars - counted_bars;
for(int Count = CalculateBars; Count >= 0; Count--) {
EMA[Count] = iMA(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice,Count);
double StdDev = iStdDev(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice, Count);
UpperBand[Count] = EMA[Count] + (StdDev * Deviations);
LowerBand[Count] = EMA[Count] - (StdDev * Deviations); }
return(0); 
}

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

Скрипты

Скрипт — это MQL-программа, которая запускается только один раз, когда она впервые присоединяется к графику. Скрипты можно использовать для автоматизации ряда торговых действий. К примеру, закрытие всех ордеров на графике или отправка отложенных ордеров. Некоторые сценарии, например сценарий period_converter, поставляемый с MetaTrader, могут перерисовывать график на основе произвольного периода времени.

Файл исходного кода скрипта должен иметь директиву свойства show_confirm или show_inputs. Свойство show_confirm предлагает пользователю подтвердить работу скрипта, а show_inputs отображает диалог свойств скрипта.

 #property show_confirm // диалог подтверждения
 #property show_inputs  // диалог свойств

Если в вашем скрипте есть параметры, которые необходимо настроить, используйте свойство show_inputs. В противном случае используйте show_confirm.

Как и советники и индикаторы, скрипты используют функции init(), deinit() и start(). Помните, что каждая функция будет запускаться только один раз — init() и start() при запуске сценария и deinit() при его удалении. Вы можете иметь только один скрипт, прикрепленный к графику за раз.

MetaTrader поставляется с несколькими примерами скриптов. Все скрипты сохраняются в каталоге \experts\scripts.