Как правильно работать с индикаторами в коде советников
Индикаторы используются практически в каждом советнике для получения сигналов на открытие и закрытие сделок. В mql 4 и 5 версий, разные подходы к получению данных индикаторов в коде советников. Стоит разобраться и понять как это работает, чтобы в дальнейшем работать с индикаторами интуитивно.
В терминале Metatrader 4 и 5 версии, поставляется набор стандартных индикаторов. Практически для каждого такого индикатора в языке MQL есть функция, которая дает доступ к данным индикатора в коде советника. Например, функция iMA()
для получения данных индикатора Moving Averages или функция iRSI()
для индикатора Relative Strength Index. А для работы с любыми другими индикаторами, есть функция iCustom()
.
В индикаторах, их данные хранятся в буферах - специальных массивах. Например, индикатор Moving Averages имеет один буфер, в котором хранятся значения усредненной цены на каждом баре. Так, чтобы получить значение индикатора на нулевом баре, нужно просто взять это значение из нулевого элемента буфера индикатора: ma[0]
. Также и для любых других баров: ma[1]
, ma[2]
, ma[8]
и т.д.
Как это применяется на практике. Например, советник должен открывать сделку при пересечении медленной и быстрой МА. Чтобы определить, что две МА пересеклись, нам нужно будет получить значения из буферов обоих МА на барах номер 1 и 2. И затем проверить, если быстрая МА на баре 2 была меньше медленной МА, а на баре 1 была больше медленной, то значит быстрая МА пересекла медленную снизу вверх.
double ma_fast_2 = 1.00010, ma_fast_1 = 1.00040; // Значения быстрой МА на барах 2 и 1
double ma_slow_2 = 1.00020, ma_slow_1 = 1.00030; // Значения медленной МА на барах 2 и 1
if(ma_fast_2 < ma_slow_2 && ma_fast_1 > ma_slow_1)
{
Print("Быстрая МА пересекла медленную МА снизу вверх");
}
В примере кода выше, значения МА: ma_fast_2, ma_fast_1 и т.д. имеют фиксированные значения, которые указаны для примера. В реальном советнике эти значения будут браться из буферов индикаторов и при появлении каждого нового бара будут меняться.
Таким образом, чтобы получить в советнике торговый сигнал по индикаторам, нужно взять данные этих индикаторов из их буферов и затем сравнить эти данные по какой-либо формуле или правилу технического анализа.
Следующий важный момент - это понимание того, откуда берутся данные индикаторов и где находятся буферы (массивы) с их данными. Когда на графике в Metatrader работает советник, в котором анализируются сигналы технического анализа с применением индикаторов, то казалось бы логичным разместить на этом же графике индикаторы с которыми работает советник, чтобы он мог получать их данные.
Но, такой советник также может работать и в тестере стратегий в не визуальном режиме теста или в режиме оптимизации, то есть не на графике и тем более не на графике с нужными индикаторами.
На самом деле советнику не нужны индикаторы на графике, на котором он работает. Когда в советнике вызываются функции для получения данных индикаторов, такие как iMA()
или iCustom()
, терминал Metatrader создает в оперативной памяти виртуальные индикаторы и их буферы, в которые помещает данные, рассчитанные по ценам торгового инструмента графика, также виртуально.
В чем отличие использования индикаторов в коде на MQL4 и MQL5
Главное различие в том, что в MQL4 данные индикатора можно получить, вызывая каждый раз соответствующую функцию. А в MQL5, функция индикатора вызывается только один раз, в момент инициализации советника. А результат её вызова - хэндл индикатора, сохраняется в глобальную переменную, которая затем используется для получения данных индикатора.
Таким образом в МТ4 виртуальный индикатор создается и пересчитывается каждый раз, когда нужно получить его данные. А в МТ5, виртуальный индикатор создается один раз в момент инициализации советника и существует до тех пор, пока не произойдет деинициализация советника.
Также, в МТ4, чтобы получить данные индикатора на нескольких барах, а их часто бывает нужно больше одного, нужно вызвать функцию получения данных индикаторов для каждого бара. В МТ5 данные ряда баров получают из одного хэндла.
Давайте создадим код функций для MQL4 и для MQL5, которые отслеживают сигнал пересечения 2-х МА по примеру кода выше.
input int PeriodFastMA = 60; // Период быстрой МА
input int PeriodSlowMA = 120; // Период медленной МА
//+------------------------------------------------------------------+
//| Возвращает true, если появилось пересечение 2-х МА |
//+------------------------------------------------------------------+
bool IsCrossDownUp(void)
{
// Получаем текущие значения МА вызывая индикаторную функцию iMA()
double ma_fast_2 = iMA(NULL, 0, PeriodFastMA, 0, MODE_SMA, PRICE_CLOSE, 2);
double ma_fast_1 = iMA(NULL, 0, PeriodFastMA, 0, MODE_SMA, PRICE_CLOSE, 1);
double ma_slow_2 = iMA(NULL, 0, PeriodSlowMA, 0, MODE_SMA, PRICE_CLOSE, 2),
double ma_slow_1 = iMA(NULL, 0, PeriodSlowMA, 0, MODE_SMA, PRICE_CLOSE, 1);
//---
if(ma_fast_2 < ma_slow_2 && ma_fast_1 > ma_slow_1)
{
Print("Медленная МА пересекла быструю МА снизу вверх");
return true;
}
//---
return false;
}
input int PeriodFastMA = 60; // Период быстрой МА
input int PeriodSlowMA = 120; // Период медленной МА
int fast_ma_handle, slow_ma_handle;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//---
fast_ma_handle = iMA(NULL, 0, PeriodFastMA, 0, MODE_SMA, PRICE_CLOSE);
slow_ma_handle = iMA(NULL, 0, PeriodSlowMA, 0, MODE_SMA, PRICE_CLOSE);
if(fast_ma_handle == INVALID_HANDLE || slow_ma_handle == INVALID_HANDLE)
{
PrintFormat("Не удалось создать хэндлы индикаторов МА, код ошибки %d", GetLastError());
return(INIT_FAILED);
}
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
if(fast_ma_handle != INVALID_HANDLE)
IndicatorRelease(fast_ma_handle);
if(slow_ma_handle != INVALID_HANDLE)
IndicatorRelease(slow_ma_handle);
}
//+------------------------------------------------------------------+
//| Возвращает true, если появилось пересечение 2-х МА |
//+------------------------------------------------------------------+
bool IsCrossDownUp(void)
{
// Массивы для получения и хранения значений МА на 2-х барах
double ma_fast[2], ma_slow[2];
// Копируем значения быстрой МА из буфера 0, начиная с бара 1, на 2-х барах, в массив ma_fast
if(CopyBuffer(fast_ma_handle, 0, 1, 2, ma_fast) == -1)
{
Print("CopyBuffer failed, error: ", GetLastError());
return false;
}
// Разворачиваем порядок следования данных в массиве ma_fast,
// чтобы они соответствовали порядку следования баров на графике.
ArrayReverse(ma_fast);
// Копируем значения медленной МА из буфера 0, начиная с бара 1, на 2-х барах, в массив ma_slow
if(CopyBuffer(slow_ma_handle, 0, 1, 2, ma_slow) == -1)
{
Print("CopyBuffer failed, error: ", GetLastError());
return false;
}
// Разворачиваем порядок следования данных в массиве ma_slow,
// чтобы они соответствовали порядку следования баров на графике.
ArrayReverse(ma_slow);
//---
if(ma_fast[1] < ma_slow[1] && ma_fast[0] > ma_slow[0])
{
Print("Медленная МА пересекла быструю МА снизу вверх");
return true;
}
//---
return false;
}
Это полные рабочие примеры кода, которые можно использовать в программах на MQL 4 и 5, и получать подобным образом данные любых индикаторов. По ним видно, что процесс получения данных индикаторов в МТ5 более громоздкий, но он не является сложным и надо просто запомнить порядок действий:
- На глобальном уровне создаются переменные для хранения хэндлов.
- В функции
OnInit()
создаются индикаторы, через вызов индикаторных функций и сохраняются их хэндлы. В функцииOnDeinit()
индикаторы удаляются. - Далее, в любых внутренних функциях советника, копируем данные из буферов индикаторов во временные массивы (функция
CopyBuffer()
) и оперируем данными из этих массивов.
Что такое хэндл индикатора в MQL5?
На самом деле это просто. При вызове индикаторной функции, такой как iMA()
или iCustom()
, торговый терминал создает в оперативной памяти виртуальный индикатор и присваивает ему номер. Этот номер и есть хэндл, который возвращает индикаторная функция. В дальнейшем, чтобы получить данные индикатора, мы передаем этот номер виртуального индикатора терминалу и он возвращает нам данные именно этого индикатора.
Получение данных индикатора с помощью предварительно полученного хэндла имеет большее преимущество, по сравнению с получением данных через постоянный вызов индикаторных функций. Например, так как хэндл хранится в переменной типа Integer
, то хэндлы можно хранить в массивах этого типа, создавать их и управлять ими динамически.