Как подключить и использовать индикаторы в советнике на MQL5 (встроенные и пользовательские)

,

На чтение потребуется

5 минут

Индикаторы — это «мозг» вашего торгового робота. Без них советник не видит тренда, не распознаёт уровни и не замечает сигналов. В отличие от MQL4, в MQL5 работа с индикаторами построена на единой архитектуре хэндлов. В этой статье вы научитесь правильно подключать как стандартные индикаторы (скользящие средние, RSI), так и свои собственные — без ошибок и утечек памяти. Всё по шагам, с рабочими примерами.


Почему в MQL5 всё иначе?

В MQL4 можно было написать:

// MQL4 — устаревший подход (НЕ РАБОТАЕТ в MQL5!)
double ma = iMA(NULL, 0, 14, 0, MODE_SMA, PRICE_CLOSE, 0);

В MQL5 такой код не скомпилируется. Причина — асинхронная архитектура:

  1. Сначала создаётся хэндл (уникальный идентификатор индикатора в памяти терминала)
  2. Индикатор рассчитывается в фоне (требуется 1–2 тика)
  3. Данные копируются в массив через CopyBuffer()

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


Шаг 1. Создаём хэндл в OnInit()

Хэндл создаётся один раз при запуске советника. Для стандартных индикаторов используем специальные функции:

ИндикаторФункция создания хэндла
Скользящая средняяiMA()
RSIiRSI()
СтохастикiStochastic()
MACDiMACD()
Полосы БоллинджераiBands()
ATRiATR()
Пользовательский индикаторiCustom()

Пример: две скользящие средние

// Глобальные переменные для хэндлов
int handle_fast; // MA 5
int handle_slow; // MA 20

int OnInit()
  {
   // Быстрая MA (5 периодов)
   handle_fast = iMA(
      Symbol(),      // Символ
      Period(),      // Таймфрейм
      5,             // Период
      0,             // Сдвиг
      MODE_SMA,      // Метод
      PRICE_CLOSE    // Цена
   );

   // Медленная MA (20 периодов)
   handle_slow = iMA(
      Symbol(),
      Period(),
      20,
      0,
      MODE_SMA,
      PRICE_CLOSE
   );

   // Проверка ошибок
   if (handle_fast == INVALID_HANDLE || handle_slow == INVALID_HANDLE)
      {
       Print("Ошибка создания индикатора: ", GetLastError());
       return INIT_FAILED;
      }

   Print("Индикаторы успешно подключены");
   return INIT_SUCCEEDED;
  }

⚠️ Важно: Функции iMA(), iRSI() и другие возвращают хэндл, а не значение. Чтобы получить данные — нужен следующий шаг.


Шаг 2. Получаем данные через CopyBuffer() в OnTick()

void OnTick()
  {
   // Массивы для хранения значений
   double fast_ma[], slow_ma[];

   // Копируем последние 2 значения с каждой MA
   // Параметры: хэндл, номер буфера, стартовый бар, количество баров, массив
   if (CopyBuffer(handle_fast, 0, 0, 2, fast_ma) <= 0) return;
   if (CopyBuffer(handle_slow, 0, 0, 2, slow_ma) <= 0) return;

   // Теперь в массивах есть данные:
   // fast_ma[0] — текущее значение быстрой MA
   // fast_ma[1] — значение на предыдущем баре
   // slow_ma[0] — текущее значение медленной MA

   // Пример стратегии: пересечение MA
   if (fast_ma[1] <= slow_ma[1] && fast_ma[0] > slow_ma[0])
      {
       Print("Сигнал BUY: быстрая MA пересекла медленную снизу вверх");
       // Здесь можно добавить логику открытия ордера
      }

   if (fast_ma[1] >= slow_ma[1] && fast_ma[0] < slow_ma[0])
      {
       Print("Сигнал SELL: быстрая MA пересекла медленную сверху вниз");
      }
  }

Структура CopyBuffer():

CopyBuffer(
   handle,    // Хэндл индикатора
   buffer,    // Номер буфера (для простых индикаторов — 0)
   start,     // С какого бара копировать (0 = текущий)
   count,     // Сколько значений скопировать
   array      // Массив для результатов
);

💡 Совет: Всегда копируйте минимум 2 значения (count = 2), чтобы сравнивать текущий и предыдущий бары для обнаружения пересечений и разворотов.


Шаг 3. Подключаем пользовательский индикатор через iCustom()

Для своих индикаторов (например, Find_Limit_Levels.ex5) используем iCustom():

int handle_custom;

int OnInit()
  {
   handle_custom = iCustom(
      Symbol(),              // Символ
      Period(),              // Таймфрейм
      "Find_Limit_Levels",   // Имя файла БЕЗ .ex5
      14,                    // Параметр 1 (например, период)
      2.0                    // Параметр 2 (например, множитель)
      // ... остальные параметры в том же порядке, как в индикаторе
   );

   if (handle_custom == INVALID_HANDLE)
      {
       Print("Ошибка загрузки индикатора. Проверьте:");
       Print("- Файл лежит в папке /MQL5/Indicators/");
       Print("- Имя написано точно (регистр важен!)");
       Print("- Все параметры переданы в правильном порядке");
       Print("Код ошибки: ", GetLastError());
       return INIT_FAILED;
      }

   return INIT_SUCCEEDED;
  }

Как узнать параметры индикатора:

  1. Откройте .mq5 файл индикатора в MetaEditor
  2. Найдите все input-переменные — их порядок определяет порядок передачи в iCustom()
// Пример из кода индикатора
input int    PeriodMA = 14;    // Параметр 1
input double Deviation = 2.0;  // Параметр 2
input color  LineColor = clrRed; // Параметр 3

// В iCustom() передаём ТОЛЬКО первые два (цвет не нужен для расчётов):
handle = iCustom(Symbol(), Period(), "MyIndicator", 14, 2.0);

Шаг 4. Освобождаем ресурсы в OnDeinit()

Это критически важно — иначе будет утечка памяти:

void OnDeinit(const int reason)
  {
   if (handle_fast != INVALID_HANDLE)
      IndicatorRelease(handle_fast);

   if (handle_slow != INVALID_HANDLE)
      IndicatorRelease(handle_slow);

   if (handle_custom != INVALID_HANDLE)
      IndicatorRelease(handle_custom);

   Print("Индикаторы освобождены");
  }

Типичные ошибки новичков и как их избежать

ОшибкаПричинаРешение
4806 — индикатор не найденФайл не в папке /MQL5/Indicators/ или опечатка в имениПроверьте путь и регистр букв в имени
4807 — неверные параметрыПередано меньше/больше параметров, чем требуетсяСверьтесь с input-переменными в коде индикатора
Данные равны 0 или EMPTY_VALUEИндикатор ещё не рассчиталсяДобавьте проверку: if (CopyBuffer(...) < 2) return;
Терминал тормозитХэндл создаётся в OnTick() вместо OnInit()Создавайте хэндл только один раз при старте

Проверка готовности данных:

int copied = CopyBuffer(handle, 0, 0, 3, values);
if (copied < 0)
   {
    Print("Ошибка копирования: ", GetLastError());
    return;
   }
if (copied == 0)
   {
    // Индикатор ещё рассчитывается — ждём следующего тика
    return;
   }
// Данные готовы — работаем с ними

Полный рабочий пример: советник на пересечении двух MA

#property strict
#property version   "1.00"

int handle_fast; // MA 5
int handle_slow; // MA 20

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   handle_fast = iMA(Symbol(), Period(), 5, 0, MODE_SMA, PRICE_CLOSE);
   handle_slow = iMA(Symbol(), Period(), 20, 0, MODE_SMA, PRICE_CLOSE);

   if (handle_fast == INVALID_HANDLE || handle_slow == INVALID_HANDLE)
      {
       Print("Ошибка создания индикаторов: ", GetLastError());
       return INIT_FAILED;
      }

   Print("Советник запущен. Отслеживаем пересечение MA 5 и 20");
   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   double fast[], slow[];

   // Копируем данные
   if (CopyBuffer(handle_fast, 0, 0, 2, fast) < 2) return;
   if (CopyBuffer(handle_slow, 0, 0, 2, slow) < 2) return;

   // Сигнал на покупку
   if (fast[1] <= slow[1] && fast[0] > slow[0])
      {
       Print("BUY: цена ", DoubleToString(Close[0], _Digits),
             ", MA5=", DoubleToString(fast[0], _Digits),
             ", MA20=", DoubleToString(slow[0], _Digits));
      }

   // Сигнал на продажу
   if (fast[1] >= slow[1] && fast[0] < slow[0])
      {
       Print("SELL: цена ", DoubleToString(Close[0], _Digits),
             ", MA5=", DoubleToString(fast[0], _Digits),
             ", MA20=", DoubleToString(slow[0], _Digits));
      }
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if (handle_fast != INVALID_HANDLE) IndicatorRelease(handle_fast);
   if (handle_slow != INVALID_HANDLE) IndicatorRelease(handle_slow);
  }

Лучшие практики

Делайте так:

  • Создавайте хэндлы только в OnInit()
  • Всегда проверяйте INVALID_HANDLE после создания
  • Используйте CopyBuffer() с проверкой количества скопированных значений
  • Освобождайте хэндлы в OnDeinit() через IndicatorRelease()
  • Для отладки выводите значения через Print() с DoubleToString(value, _Digits)

Не делайте так:

  • Не создавайте хэндл в OnTick() — это вызовет утечку памяти и торможение терминала
  • Не игнорируйте возврат CopyBuffer() — данные могут быть ещё не готовы
  • Не пытайтесь использовать индикатор сразу после создания хэндла — дайте ему 1–2 тика на расчёт

Заключение

В MQL5 работа с индикаторами строится на трёх китах:

  1. Создание хэндла в OnInit() через iMA(), iRSI() или iCustom()
  2. Копирование данных в OnTick() через CopyBuffer()
  3. Освобождение ресурсов в OnDeinit() через IndicatorRelease()

Начните с простого — подключите к советнику из предыдущей статьи две скользящие средние и реализуйте стратегию пересечений. Когда заработает — добавьте фильтр по объёму или уровню поддержки/сопротивления. Так вы постепенно соберёте надёжного торгового робота.

Удачи в программировании! 📈

Поделись или сохрани ссылку

Автор статьи

Комментарии

Добавить комментарий

Вы добавили товары в корзину?

Мы можем сохранить вашу корзину и напомнить вам позже. Хотите скидку 3%?

Содержание