Русский

Учебник по MQL4  Создание обычной программы  Функция слежения за событиями


Функция слежения за событиями


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

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

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

Пользовательская функция Events()

int Events()

Функция вычисляет изменение минимальной дистанции для установки ордеров и их стоп-приказов, а также изменения в составе рыночных и отложенных ордеров. Для исполнения функции требуется применение в программе функции учёта ордеров Terminal(). Используются значения глобальных массивов:

  • Mas_Ord_New - массив характеристик ордеров на момент последнего исполнения функции Terminal();
  • Mas_Ord_Old - массив характеристик ордеров на момент предыдущего исполнения функции Terminal().

Используются значения глобальных переменных:
- Level_new - текущее значение минимальной дистанции;
- Level_old - предыдущее значение минимальной дистанции.

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

Функция слежения за событиями Events() оформлена в виде включаемого файла Events.mqh:

//--------------------------------------------------------------------
// Events.mqh
// Предназначен для использования в качестве примера в учебнике MQL4.
//--------------------------------------------------------------- 1 --
// Функция слежения за событиями.
// Глобальные переменные:
// Level_new Новое значение минимальной дистанции
// Level_old Предыдущее значение минимальной дистанции
// Mas_Ord_New[31][9] Массив ордеров последний известный
// Mas_Ord_Old[31][9] Массив ордеров предыдущий (старый)
//--------------------------------------------------------------- 2 --
int Events() // Пользовательская функция
{
bool Conc_Nom_Ord; // Совпадение ордеров в ..
//.. старом и новом массивах
//--------------------------------------------------------------- 3 --
Level_new=MarketInfo(Symbol(),MODE_STOPLEVEL );// Последн.известное
if (Level_old!=Level_new) // Новое не равно старому..
{ // значит изменились условия
Level_old=Level_new; // Новое "старое значение"
Inform(10,Level_new); // Сообщение: новая дистанц.
}
//--------------------------------------------------------------- 4 --
// Поиск пропавших, поменявших тип, частично закрытых и переоткрытых
for(int old=1;old<=Mas_Ord_Old[0][0];old++)// По массиву старых
{ // Исходим из того, что..
Conc_Nom_Ord=false; // ..ордера не совпадают
//--------------------------------------------------------- 5 --
for(int new=1;new<=Mas_Ord_New[0][0];new++)//Цикл по массиву ..
{ //..новых ордеров
//------------------------------------------------------ 6 --
if (Mas_Ord_Old[old][4]==Mas_Ord_New[new][4])// Совпал номер
{ // Тип ордера стал ..
if (Mas_Ord_New[new][6]!=Mas_Ord_Old[old][6])//.. другим
Inform(7,Mas_Ord_New[new][4]);// Сообщение: преобраз.:)
Conc_Nom_Ord=true; // Ордер найден, ..
break; // ..значит выходим из ..
} // .. внутреннего цикла
//------------------------------------------------------ 7 --
// Не совпал номер ордера
if (Mas_Ord_Old[old][7]>0 && // MagicNumber есть, совпал
Mas_Ord_Old[old][7]==Mas_Ord_New[new][7])//.. со старым
{ //значит он переоткрыт или частично закрыт
// Если лоты совпадают,..
if (Mas_Ord_Old[old][5]==Mas_Ord_New[new][5])
Inform(8,Mas_Ord_Old[old][4]);// ..то переоткрытие
else // А иначе это было..
Inform(9,Mas_Ord_Old[old][4]);// ..частичное закрытие
Conc_Nom_Ord=true; // Ордер найден, ..
break; // ..значит выходим из ..
} // .. внутреннего цикла
}
//--------------------------------------------------------- 8 --
if (Conc_Nom_Ord==false) // Если мы сюда дошли,..
{ // ..то ордера нет:(
if (Mas_Ord_Old[old][6]==0)
Inform(1, Mas_Ord_Old[old][4]); // Ордер Buy закрыт
if (Mas_Ord_Old[old][6]==1)
Inform(2, Mas_Ord_Old[old][4]); // Ордер Sell закрыт
if (Mas_Ord_Old[old][6]> 1)
Inform(3, Mas_Ord_Old[old][4]); // Отложен. ордер удалён
}
}
//--------------------------------------------------------------- 9 --
// Поиск новых ордеров
for(new=1; new<=Mas_Ord_New[0][0]; new++)// По массиву новых орд.
{
if (Mas_Ord_New[new][8]>0) //Это не новый,а переоткр
continue; //..или частично закрытый
Conc_Nom_Ord=false; // Пока совпадения нет
for(old=1; old<=Mas_Ord_Old[0][0]; old++)// Поищем этот ордерок
{ // ..в массиве старых
if (Mas_Ord_New[new][4]==Mas_Ord_Old[old][4])//Совпал номер..
{ //.. ордера
Conc_Nom_Ord=true; // Ордер найден, ..
break; // ..значит выходим из ..
} // .. внутреннего цикла
}
if (Conc_Nom_Ord==false) // Если совпадения нет,..
{ // ..то ордер новый :)
if (Mas_Ord_New[new][6]==0)
Inform(4, Mas_Ord_New[new][4]); // Ордер Buy открыт
if (Mas_Ord_New[new][6]==1)
Inform(5, Mas_Ord_New[new][4]); // Ордер Sell открыт
if (Mas_Ord_New[new][6]> 1)
Inform(6, Mas_Ord_New[new][4]); // Установлен отлож.ордер
}
}
//-------------------------------------------------------------- 10 --
return;
}
//-------------------------------------------------------------- 11 --

В блоке 1-2 описаны глобальные массивы и переменные, необходимые для исполнения функции. В блоке 2-3 открыта переменная Conc_Nom_Ord, используемая в дальнейшем коде для анализа состава ордеров.

Функция отслеживает изменение минимальной дистанции для установки ордеров и стоп-приказов. Для этого при каждом исполнении функции (блок 3-4) сначала вычисляется текущее значение минимальной дистанции Level_new, а затем оно сравнивается с предыдущим значением минимальной дистанции Level_old (полученным в период предыдущего исполнения функции). Если значения этих переменных не равны, значит незадолго до последнего исполнения функции минимальная дистанция была изменена дилинговым центром. В этом случае переменной Level_old присваивается текущее значение минимальной дистанции (чтобы учесть при последующих исполнениях функции) и исполняется функция Inform() с целью вывести необходимое сообщение.

В общем случае аналогичным способом возможно выявить и другие события, например, изменение спреда, разрешение торгов по указанному инструменту (идентификатор MODE_TRADEALLOWED в функции MarketInfo()), факт образования нового бара (см. Задачу 27), факт пересечения индикаторных линий (см. Рис. 107), факт достижения некоторого заданного времени и пр. Некоторые события можно отслеживать с целью использования полученных значений в эксперте, другие - для того, чтобы информировать пользователя.

В блоках 4-10 выполняется анализ состояния рыночных и отложенных ордеров. Информация о большинстве событий, связанных с изменениями ордеров, доводится до сведения пользователя. Анализ выполняется в два этапа. На первом этапе выявляются изменения, касающиеся пропавших (закрытых или удалённых), поменявших тип, частично закрытых и переоткрытых ордеров (блок 4-9). На втором этапе (блок 9-10) осуществляется поиск новых ордеров.

В блоке 4-9 производится анализ ордеров, учтённых в массиве Mas_Ord_Old. Количество итераций во внешнем цикл for устанавливается в соответствии с общим количеством ордеров в этом массиве (элемент массива Mas_Ord_Old[0][0]). Для того чтобы определить, сохранился ли ордер на текущий момент, необходимо найти аналогичный ордер в массиве ордеров Mas_Ord_New. Этот поиск выполняется во внутреннем цикле for (блок 6-8), количество итераций которого равно количеству ордеров в массиве (элемент массива Mas_Ord_New[0][0]). В дальнейшем изложении массив Mas_Ord_Old именуется старым массивом, а Mas_Ord_New - новым.

В блоках 6-8 выполняется поиск только тех ордеров, характеристики которых отличаются. Например, в блоке 6-7 производится проверка ордера по его номеру (см. соответствие индексов массивов характеристикам ордеров в Таблице 4). Если исследуемый ордер из старого массива совпал по номеру с одним из ордеров нового массива, то этот ордер, по крайней мере не закрыт (или удалён). При этом необходимо выяснить также, не поменялся ли тип ордера. И если тип ордера изменился, значит отложенный ордер преобразовался в рыночный. В этом случае с помощью функции Inform() выводится необходимое сообщение. Независимо от факта изменения (или сохранения) типа ордера дальнейшие исследования этого ордера прекращаются: осуществляется выход из внутреннего цикла и, в конечном счёте, начало новой итерации внешнего цикла.

Если при исполнении блока 6-7 обнаружено, что исследуемый ордер из старого массива не совпал по номеру ни с одним из ордеров нового массива, то управление передаётся в блок 7-8. Здесь выясняется, есть ли у текущего ордера из нового массива ненулевой MagicNumber (все ордера, открываемые и устанавливаемые экспертом, имеют ненулевой MagicNumber). Если MagicNumber есть и он совпадает с MagicNumber исследуемого ордера из старого массива, значит исследуемый ордер присутствует в торговле, но претерпел какие-то преобразования. Преобразования, связанные с изменением номера ордера, возникают в двух случаях.

Случай 1. Если ордер частично закрыт. Частичное закрытие рыночного ордера (для отложенных ордеров не предусмотрено) в соответствии с технологией, принятой в среде МТ4, выполняется в два этапа. На первом этапе полностью закрывается исходный ордер в полном объёме. В этот же момент открывается новый рыночный ордер в объёме остаточной стоимости, с тем же курсом открытия и заявленными ценами стоп-приказов, что и у исходного. Этот новый ордер получает свой уникальный номер ордера, не совпадающий с номером исходного частично закрываемого ордера.

Случай 2. Если ордер переоткрыт дилинговым центром. Некоторые банки (в связи со специфическим порядком внутреннего учёта) в конце каждого рабочего дня принудительно закрывают все рыночные ордера и в тот же момент открывают рыночные ордера того же типа на то же количество лотов, но уже по текущему курсу за вычетом свопа. Это событие не оказывает никакого влияния на экономический результат по игровому счёту. Каждый из вновь открытых ордеров также получает свой уникальный номер, не совпадающий с номерами закрытых ордеров.

Разница между рассмотренными случаями заключается в новом количестве лотов для новых ордеров - в первом случае количество лотов уменьшается, во втором случае не изменяется. Это различие используется в блоке 7-8 для того, чтобы отличать причину преобразования ордера. В том и другом случае выводится соответствующее сообщение (ордер частично закрыт или ордер переоткрыт).

Если к моменту полного окончания внутреннего цикла не удалось выявить признаки совпадения (блок 6-7) или наследования (блок 7-8) ордера в новом массиве, значит исследуемый ордер из старого массива ордеров закрыт или удалён. В этом случае управление передаётся в блок 8-9, где в зависимости от типа ордера выводится то или иное сообщение. В данном примере реализовано три варианта сообщений - для ордера Buy, ордера Sell и отложенных ордеров независимо от их типа. В общем случае этот порядок может быть несколько изменён (дополнен) - возможно создание группы соответствующих сообщений для каждого из типов отложенных ордеров.

На втором этапе принимаются к рассмотрению ордера из нового массива ордеров (блок 9-10). Это делается для того, чтобы выявить вновь открытые и установленные ордера. Во внешнем цикле for выполняется перебор всех ордеров, сведения о которых содержатся в массиве новых ордеров. Для идентификации переоткрытых или частично закрытых ордеров используется простой признак - наличие комментария. При частичном закрытии или переоткрытии в комментарий ордера сервером автоматически заносится запись, в которой указывается номер исходного ордера. Рассматриваемый эксперт не использует комментарий, поэтому наличие комментария свидетельствует о том, что рассматриваемый ордер не новый.

Если ордер не содержит комментарий, то выполняется поиск ордера с таким же номером в массиве старых ордеров. В случае, если во внутреннем цикле for среди старых ордеров обнаружен ордер с таким номером, значит он не новый, а был открыт ранее. Если же номер исследуемого ордера из нового массива не совпадает ни с одним из номеров ордеров в старом массиве, значит этот ордер - открытый рыночный или установленный отложенный. В нижней части блока 9-10, в зависимости от типа ордера, выполняется обращение к функции Inform() для вывода необходимого сообщения.

Использование рассматриваемой функции Events() на практике оказывается очень полезным. Однажды применив функцию в советнике, программист обычно использует её и в дальнейшем. Отдельно нужно заметить, что функции Events() и Terminal() тесно связаны между собой. Если предполагается вносить изменения в одну из этих функций (например, использовать другие названия глобальных массивов), то соответствующие изменения необходимы и в другой функции. Если для реализации торговой стратегии используются комментарии в ордерах, то следует иначе обрабатывать признак наследования ордера (блок 9-10), а именно, с помощью строковых функций проанализировать текст комментария к ордеру.

Количество событий, рассматриваемых в функции Events(), может быть существенно увеличено. Например, для того чтобы избыточно полно отразить все события, связанные с ордерами, необходимо добавить анализ характеристик ордеров - изменение заявленных цен стоп-приказов и заявленных цен открытия отложенных ордеров, а также метод закрытия ордеров (закрыты ли ордера встречно или каждый в отдельности) и причину закрытия или удаления ордеров (при достижении ценой уровня заявленной цены стоп-приказа, по инициативе трейдера и т.д.).