Индикаторы — это «мозг» вашего торгового робота. Без них советник не видит тренда, не распознаёт уровни и не замечает сигналов. В отличие от MQL4, в MQL5 работа с индикаторами построена на единой архитектуре хэндлов. В этой статье вы научитесь правильно подключать как стандартные индикаторы (скользящие средние, RSI), так и свои собственные — без ошибок и утечек памяти. Всё по шагам, с рабочими примерами.
Почему в MQL5 всё иначе?
В MQL4 можно было написать:
// MQL4 — устаревший подход (НЕ РАБОТАЕТ в MQL5!)
double ma = iMA(NULL, 0, 14, 0, MODE_SMA, PRICE_CLOSE, 0);
В MQL5 такой код не скомпилируется. Причина — асинхронная архитектура:
- Сначала создаётся хэндл (уникальный идентификатор индикатора в памяти терминала)
- Индикатор рассчитывается в фоне (требуется 1–2 тика)
- Данные копируются в массив через
CopyBuffer()
Это сложнее, но даёт огромные преимущества: многопоточность, стабильность и возможность работать с сотнями индикаторов одновременно.
Шаг 1. Создаём хэндл в OnInit()
Хэндл создаётся один раз при запуске советника. Для стандартных индикаторов используем специальные функции:
| Индикатор | Функция создания хэндла |
|---|---|
| Скользящая средняя | iMA() |
| RSI | iRSI() |
| Стохастик | iStochastic() |
| MACD | iMACD() |
| Полосы Боллинджера | iBands() |
| ATR | iATR() |
| Пользовательский индикатор | 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;
}
Как узнать параметры индикатора:
- Откройте
.mq5файл индикатора в MetaEditor - Найдите все
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 работа с индикаторами строится на трёх китах:
- Создание хэндла в
OnInit()черезiMA(),iRSI()илиiCustom() - Копирование данных в
OnTick()черезCopyBuffer() - Освобождение ресурсов в
OnDeinit()черезIndicatorRelease()
Начните с простого — подключите к советнику из предыдущей статьи две скользящие средние и реализуйте стратегию пересечений. Когда заработает — добавьте фильтр по объёму или уровню поддержки/сопротивления. Так вы постепенно соберёте надёжного торгового робота.
Удачи в программировании! 📈

Добавить комментарий
Для отправки комментария вам необходимо авторизоваться.