Русский

Учебник по MQL4  Торговые операции  Модификация ордеров

Модификация ордеров


Язык MQL4 предоставляет возможность модификации рыночных и отложенных ордеров. Модификация ордеров производится в соответствии с правилами, описанными в разделе Характеристики ордеров и Приложении 3.

Функция OrderModify()


Торговые приказы для модификации рыночных и отложенных ордеров формируются с помощью функции OrderModify().

bool OrderModify(int ticket, double price, double stoploss, double takeprofit, datetime expiration, color arrow_color=CLR_NONE)

Функция изменяет параметры рыночных и отложенных ордеров. Функция возвращает TRUE при успешном исполнении торговой операции и FALSE при неудачном.

Параметры:

ticket - уникальный порядковый номер ордера.

price - новая заявленная цена для отложенного ордера или цена открытия рыночного ордера.

stoploss - новое значение StopLoss.

takeprofit - новое значение TakeProfit.

expiration - время истечения отложенного ордера.

arrow_color - цвет стрелок модификации StopLoss и/или TakeProfit на графике. Если параметр отсутствует или его значение равно CLR_NONE, то стрелки на графике не отображаются.

Замечания: цену открытия и время истечения можно изменять только для отложенных ордеров.

Если в качестве параметров функции передать неизмененные значения, то в этом случае будет сгенерирована ошибка 1 (ERR_NO_RESULT). На некоторых торговых серверах может быть установлен запрет на применение срока истечения отложенных ордеров. В этом случае при попытке задать ненулевое значение в параметре expiration будет сгенерирована ошибка 147 (ERR_TRADE_EXPIRATION_DENIED).


Модификация рыночных ордеров


Обычный рыночный ордер содержит два стоп-приказа - StopLoss и TakeProfit, - предписывающих закрытие ордера по заявленным ценам с целью ограничения убытков и фиксации прибыли. Модификация рыночных ордеров бывает полезна для изменения заявленных цен стоп-приказов либо в результате получения в программе новых расчётных значений, либо по инициативе трейдера. Клиентский терминал имеет собственное средство для модификации ордера StopLoss - Трейлинг-стоп. Этот инструмент позволяет модифицировать StopLoss, продвигая его вслед за курсом на определённом фиксированном расстоянии (см. Руководство клиентского терминала).

Функция модификации ордеров OrderModify() значительно расширяет возможности модификации: допускается изменение заявленных цен обоих стоп-приказов как в сторону приближения их к рыночной цене, так и удаление. Ограничением при модификации рыночного ордера является минимально допустимая дистанция между стоп-приказом и рыночной ценой, устанавливаемая дилинговым центром (см. Характеристики ордеров и Требования и ограничения торговых операций). В случае, если программа пытается изменить положение стоп-приказа так, что он оказывается ближе к рынку, чем на минимально допустимую дистанцию, то такой торговый приказ будет отклонён клиентским терминалом, и исполнение функции OrderModify() закончится неудачей (ошибка 130). Поэтому в программе обязательно должен быть предусмотрен блок вычислений, учитывающий это ограничение.


Пример простого эксперта, модифицирующего StopLoss всех рыночных ордеров, для которых дистанция между заявленной ценой StopLoss и рыночной котировкой оказывается больше заданной (modifystoploss.mq4)

//--------------------------------------------------------------------
// modifystoploss.mq4
// Предназначен для использования в качестве примера в учебнике MQL4.
//--------------------------------------------------------------------
extern int Tral_Stop=10; // Дист. преследования
//--------------------------------------------------------------- 1 --
int start() // Спец. функция start
{
string Symb=Symbol(); // Финанс. инструмент
//--------------------------------------------------------------- 2 --
for(int i=1; i<=OrdersTotal(); i++) // Цикл перебора ордер
{
if (OrderSelect(i-1,SELECT_BY_POS)==true) // Если есть следующий
{ // Анализ ордеров:
int Tip=OrderType(); // Тип ордера
if(OrderSymbol()!=Symb||Tip>1)continue;// Не наш ордер
double SL=OrderStopLoss(); // SL выбранного орд.
//------------------------------------------------------ 3 --
while(true) // Цикл модификации
{
double TS=Tral_Stop; // Исходное значение
int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL);//Миним. дист
if (TS < Min_Dist) // Если меньше допуст.
TS=Min_Dist; // Новое значение TS
//--------------------------------------------------- 4 --
bool Modify=false; // Не назначен к модифи
switch(Tip) // По типу ордера
{
case 0 : // Ордер Buy
if (NormalizeDouble(SL,Digits)< // Если ниже желаем.
NormalizeDouble(Bid-TS*Point,Digits))
{
SL=Bid-TS*Point; // то модифицируем его
string Text="Buy "; // Текст для Buy
Modify=true; // Назначен к модифи.
}
break; // Выход из switch
case 1 : // Ордер Sell
if (NormalizeDouble(SL,Digits)> // Если выше желаем.
NormalizeDouble(Ask+TS*Point,Digits)
||
NormalizeDouble(SL,Digits)==0)//или равно нулю
{
SL=Ask+TS*Point; // то модифицируем его
Text="Sell "; // Текст для Sell
Modify=true; // Назначен к модифи.
}
} // Конец switch
if (Modify==false) // Если его не модифи
break; // Выход из while
//--------------------------------------------------- 5 --
double TP =OrderTakeProfit(); // TP выбранного орд.
double Price =OrderOpenPrice(); // Цена выбранн. орд.
int Ticket=OrderTicket(); // Номер выбранн. орд.

Alert ("Модификация ",Text,Ticket,". Ждём ответ..");
bool Ans=OrderModify(Ticket,Price,SL,TP,0);//Модифи его!
//--------------------------------------------------- 6 --
if (Ans==true) // Получилось :)
{
Alert ("Ордер ",Text,Ticket," модифицирован:)");
break; // Из цикла модифи.
}
//--------------------------------------------------- 7 --
int Error=GetLastError(); // Не получилось :(
switch(Error) // Преодолимые ошибки
{
case 130:Alert("Неправильные стопы. Пробуем ещё раз.");
RefreshRates(); // Обновим данные
continue; // На след. итерацию
case 136:Alert("Нет цен. Ждём новый тик..");
while(RefreshRates()==false) // До нового тика
Sleep(1); // Задержка в цикле
continue; // На след. итерацию
case 146:Alert("Подсистема торгов занята.Пробуем ещё");
Sleep(500); // Простое решение
RefreshRates(); // Обновим данные
continue; // На след. итерацию
// Критические ошибки
case 2 : Alert("Общая ошибка.");
break; // Выход из switch
case 5 : Alert("Старая версия клиентского терминала.");
break; // Выход из switch
case 64: Alert("Счет заблокирован.");
break; // Выход из switch
case 133:Alert("Торговля запрещена");
break; // Выход из switch
default: Alert("Возникла ошибка ",Error);//Др. ошибки
}
break; // Из цикла модифи.
} // Конец цикла модифи.
//------------------------------------------------------ 8 --
} // Конец анализа орд.
} // Конец перебора орд.
//--------------------------------------------------------------- 9 --
return; // Выход из start()
}
//-------------------------------------------------------------- 10 --

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

Алгоритм представленного эксперта modifystoploss.mq4 очень прост. Основные вычисления выполняются в цикле перебора ордеров (блок 2-9). Выбор ордера ведётся среди рыночных и отложенных ордеров (параметр pool в вызове функции OrderSelect() не указан явно). В блоке 2-3 отсортировываются отложенные ордера, а также ордера, открытые по другому финансовому инструменту; для ордеров, прошедших отбор, определяется значение StopLoss.

Блок 3-9 представляет цикл модификации выбранного ордера. В блоке 3-4 определяется новое текущее значение ограничивающей дистанции (брокер может изменить это значение в любой момент). В блоке 4-5 вычисляется необходимость модификации отобранного ордера (текущего обрабатываемого в цикле for), а также новое значение StopLoss. Если текущий ордер не требует модификации, то в конце блока 4-5 осуществляется выход из цикла while, и модификация этого ордера (в блоке 5-6) не осуществляется. Если же ордер требует модификации, то управление передаётся в блок 5-6, в котором вычисляются необходимые параметры и выполняется обращение к функции OrderModify(), которая и формирует торговый приказ.

Если торговая операция завершилась успешно, то оператор break в блоке 6-7 заканчивает исполнение цикла while, что приводит к окончанию текущей итерации цикла перебора ордеров for (и на следующей итерации начинается обработка очередного ордера). Если же торговая операция закончилась неудачей, то производится обработка ошибок. В случае, когда ошибка оказывается не критической, попытка повторить торговую операцию повторяется. Если же ошибка оценивается как критическая, то управление передаётся за пределы цикла модификации для обработки следующего ордера (в цикле for).

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

Например, имеется рыночный ордер Buy, открытый по цене 1.295467, со стоп-приказами StopLoss = 1.2958 и TakeProfit = 1.2960. Минимальная дистанция, установленная брокером, равна 5 пунктов. При рыночной цене Bid = 1.2959 возникают условия для модификации ордера, а именно для установки StopLoss = 1.2949 (Bid - 10 пунктов). Для исполнения функции OrderModify() требуется указать и новое значение TakeProfit. Наш советник не изменяет положение TakeProfit, поэтому в функции задаётся текущее значение TakeProfit = 1.2960.

Несмотря на то, что новое заявленное значение TakeProfit = 1.2960 находится близко к рыночной цене Bid (всего 1 пункт, т.е меньше допустимой дистанции 5 пунктов), это значение не отличается от текущего значения TakeProfit = 1.2960, поэтому торговый приказ будет признан корректным и исполнен на сервере (в общем случае торговый приказ может быть отклонён, но по другим причинам). На рис. 102 и 103 представлены результаты успешной модификации ордера в этой ситуации.


Рис. 102. Вид окна сообщений и окна финансового инструмента при модификации ордера экспертом modifystoploss.mq4 в момент, когда рыночный курс находится вблизи от заявленного значения TakeProfit.



Рис. 103. Модифицированный ордер в окне Терминала.

На Рис. 103 видно, что в результате модификации новое значение StopLoss = 1.2949, а текущая цена Bid = 1.2959 находится на расстоянии 1 пункта от значения TakeProfit.

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

Модификация отложенных ордеров


Модификация отложенных ордеров несколько отличается от модификации рыночных. Существенное отличие состоит в том, что возможно изменить заявленную цену самого ордера. Обязательно соблюдение правил, ограничивающих положение отложенного ордера относительно рыночной цены и стоп-приказов ордера относительно заявленной цены ордера (см. Характеристики ордеров и Требования и ограничения торговых операций). При этом все характеристики отложенного ордера оцениваются как вновь заявленные, независимо от предыдущей истории событий, связанных с этим ордером.

Например, имеется отложенный ордер BuyStop = 1.2030, имеющий StopLoss = 1.2025 и TakeProfit = 1.2035. Минимально допустимая дистанция установлена брокером в размере 5 пунктов. Нетрудно заметить, что стоп-приказы ордера стоят на допустимой границе, поэтому любая модификация заявленной цены открытия ордера повлечёт обязательную модификацию по крайней мере одного из стоп-приказов. Если же будет сформирован торговый приказ, в соответствии с которым предполагается изменить заявленную цену ордера, а значения стоп-приказов оставить без изменения, то такой торговый приказ будет признан клиентским терминалом как ошибочный и не будет отправлен на сервер для исполнения. Например, если в торговом приказе указаны значения BuyStop = 1. 2028, StopLoss = 1.2025 и TakeProfit = 1.2035, то этот торговый приказ является ошибочным, несмотря на то, что значения стоп-приказов не изменились - в данном случае нарушено правило соблюдения минимальной дистанции между заявленной ценой открытия ордера и ценой одного из стоп-приказов (см. Требования и ограничения торговых операций).

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

Пример простого скрипта, модифицирующего отложенный ордер, заявленная цена которого находится ближе к месту прикрепления скрипта, чем цены других отложенных ордеров (modifyorderprice.mq4).

//--------------------------------------------------------------------
// modifyorderprice.mq4
// Предназначен для использования в качестве примера в учебнике MQL4.
//--------------------------------------------------------------- 1 --
int start() // Спец.функция start
{
int Tral=10; // Дист. приближения
string Symb=Symbol(); // Финанс. инструмент
double Dist=1000000.0; // Предустановка
double Win_Price=WindowPriceOnDropped(); // Здесь брошен скрипт
//--------------------------------------------------------------- 2 --
for(int i=1; i<=OrdersTotal(); i++) // Цикл перебора ордер
{
if (OrderSelect(i-1,SELECT_BY_POS)==true) // Если есть следующий
{ // Анализ ордеров:
//------------------------------------------------------ 3 --
if (OrderSymbol()!= Symb) continue; // Не наш фин.инструм.
if (OrderType()<2) continue; // Рыночный ордер
//------------------------------------------------------ 4 --
if(NormalizeDouble(MathAbs(OrderOpenPrice()-Win_Price),Digits)
<
NormalizeDouble(Dist,Digits)) // Выбираем ближайший
{
Dist=MathAbs(OrderOpenPrice()-Win_Price);// Новое значение
int Tip =OrderType(); // Тип выбранного орд.
int Ticket=OrderTicket(); // Номер выбранн. орд.
double Price =OrderOpenPrice(); // Цена выбранн. орд.
double SL =OrderStopLoss(); // SL выбранного орд.
double TP =OrderTakeProfit(); // TP выбранного орд.
} // Конец if
} // Конец анализа ордера
} // Конец перебора орд.
//--------------------------------------------------------------- 5 --
if (Tip==0) // Если отложенных нет
{
Alert("По ",Symb," отложенных ордеров нет");
return; // Выход из программы
}
//--------------------------------------------------------------- 6 --
while(true) // Цикл закрытия орд.
{
RefreshRates(); // Обновим данные
//--------------------------------------------------------- 7 --
double TS=Tral; // Исходное значение
int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL);//Миним. дистанция
if (TS < Min_Dist) // Если меньше допуст.
TS=Min_Dist; // Новое значение TS
//--------------------------------------------------------- 8 --
string Text=""; // Не назначен к модифи
double New_SL=0;
double New_TP=0;
switch(Tip) // По типу ордера
{
case 2: // BuyLimit
if (NormalizeDouble(Price,Digits) < // Если дальше, чем на
NormalizeDouble(Ask-TS*Point,Digits))//..заданное знач
{
double New_Price=Ask-TS*Point; // Новая его цена
if (NormalizeDouble(SL,Digits)>0)
New_SL=New_Price-(Price-SL); // Новый StopLoss
if (NormalizeDouble(TP,Digits)>0)
New_TP=New_Price+(TP-Price); // Новый TakeProfit
Text= "BuyLimit "; // Будем его модифи.
}
break; // Выход из switch
case 3: // SellLimit
if (NormalizeDouble(Price,Digits) > // Если дальше, чем на
NormalizeDouble(Bid+TS*Point,Digits))//..заданное знач
{
New_Price=Bid+TS*Point; // Новая его цена
if (NormalizeDouble(SL,Digits)>0)
New_SL=New_Price+(SL-Price); // Новый StopLoss
if (NormalizeDouble(TP,Digits)>0)
New_TP=New_Price-(Price-TP); // Новый TakeProfit
Text= "SellLimit "; // Будем его модифи.
}
break; // Выход из switch
case 4: // BuyStopt
if (NormalizeDouble(Price,Digits) > // Если дальше, чем на
NormalizeDouble(Ask+TS*Point,Digits))//..заданное знач
{
New_Price=Ask+TS*Point; // Новая его цена
if (NormalizeDouble(SL,Digits)>0)
New_SL=New_Price-(Price-SL); // Новый StopLoss
if (NormalizeDouble(TP,Digits)>0)
New_TP=New_Price+(TP-Price); // Новый TakeProfit
Text= "BuyStopt "; // Будем его модифи.
}
break; // Выход из switch
case 5: // SellStop
if (NormalizeDouble(Price,Digits) < // Если дальше, чем на
NormalizeDouble(Bid-TS*Point,Digits))//..заданное знач
{
New_Price=Bid-TS*Point; // Новая его цена
if (NormalizeDouble(SL,Digits)>0)
New_SL=New_Price+(SL-Price); // Новый StopLoss
if (NormalizeDouble(TP,Digits)>0)
New_TP=New_Price-(Price-TP); // Новый TakeProfit
Text= "SellStop "; // Будем его модифи.
}
}
if (NormalizeDouble(New_SL,Digits)<0) // Проверка SL
New_SL=0;
if (NormalizeDouble(New_TP,Digits)<0) // Проверка TP
New_TP=0;
//--------------------------------------------------------- 9 --
if (Text=="") // Если его не модифи
{
Alert("Нет условий для модификации.");
break; // Выход из while
}
//-------------------------------------------------------- 10 --
Alert ("Модификация ",Text,Ticket,". Ждём ответ..");
bool Ans=OrderModify(Ticket,New_Price,New_SL,New_TP,0);//Модифи!
//-------------------------------------------------------- 11 --
if (Ans==true) // Получилось :)
{
Alert ("Модифицирован ордер ",Text," ",Ticket," :)");
break; // Выход из цикла закр
}
//-------------------------------------------------------- 12 --
int Error=GetLastError(); // Не получилось :(
switch(Error) // Преодолимые ошибки
{
case 4: Alert("Торговый сервер занят. Пробуем ещё раз..");
Sleep(3000); // Простое решение
continue; // На след. итерацию
case 137:Alert("Брокер занят. Пробуем ещё раз..");
Sleep(3000); // Простое решение
continue; // На след. итерацию
case 146:Alert("Подсистема торговли занята. Пробуем ещё..");
Sleep(500); // Простое решение
continue; // На след. итерацию
}
switch(Error) // Критические ошибки
{
case 2 : Alert("Общая ошибка.");
break; // Выход из switch
case 64: Alert("Счет заблокирован.");
break; // Выход из switch
case 133:Alert("Торговля запрещена");
break; // Выход из switch
case 139:Alert("Ордер заблокирован и уже обрабатывается");
break; // Выход из switch
case 145:Alert("Модификация запрещена. ",
"Ордер слишком близок к рынку");
break; // Выход из switch
default: Alert("Возникла ошибка ",Error);//Другие варианты
}
break; // Выход из цикла закр
} // Конец цикла закр.
//-------------------------------------------------------------- 13 --
Alert ("Скрипт закончил работу -----------------------");
return; // Выход из start()
}
//-------------------------------------------------------------- 14 --

Расстояние между рыночной ценой и заявленной ценой отложенного ордера задано в переменной Tral. Переменная Win_Price содержит значение цены, на которой в окно финансового инструмента прикреплён скрипт. В цикле перебора ордеров (блок 2-5) вычисляются характеристики отложенного ордера, находящегося ближе других к месту прикрепления скрипта. Блок 6-13 представляет цикл закрытия ордеров. В блоке 8-9 определяется, нужно ли модифицировать выбранный ордер, и, если необходимо, вычисляются новые значения заявленной цены и стоп-приказов. Приказ на модификацию отдаётся с помощью функции OrderModify() в блоке 10-11. В блоке 11-13 реализована обработка ошибок.

Блок 8-9 состоит из четырёх однотипных блоков, в которых вычисляются новые значения отложенного ордера, используемые в торговом приказе. Рассмотрим один из них, для ордера SellLimit:

         case 3:                                // SellLimit 
if (NormalizeDouble(Price,Digits) > // Если дальше, чем на
NormalizeDouble(Bid+TS*Point,Digits))//..заданное знач
{
New_Price=Bid+TS*Point; // Новая его цена
if (NormalizeDouble(SL,Digits)>0)
New_SL = New_Price+(SL-Price);// Новый StopLoss
if (NormalizeDouble(TP,Digits)>0)
New_TP = New_Price-(Price-TP);// Новый TakeProfit
Text= "SellLimit "; // Будем его модифи.
}
break; // Выход из switch

Новые параметры ордера вычисляются только в том случае, если текущая цена Price находится дальше от текущей рыночной цены Bid, чем на желаемую дистанцию TS. Если это так, то управление передаётся в тело оператора if, где вычисляется новая цена открытия ордера New_Price. Новые значения StopLoss и TakeProfit вычисляются только для ненулевых значений. При этом дистанции между заявленной ценой ордера и каждой ценой стоп-приказа сохраняется неизменной.

Например, ордер SellLimit установлен по цене 1.2050, его StopLoss = 1.2073, а TakeProfit = 1. 2030. Допустим, в результате вычислений новая цена открытия ордера равна 1.2040. В этом случае новые значения стоп-приказов будут такими: StopLoss = 1.2063, а TakeProfit = 1. 2020. Таким образом, в результате работы программы ордер модифицируется "целиком" - все три основных параметра (цена открытия, StopLoss и TakeProfit) одновременно смещаются вниз, сохраняя при этом дистанцию между собой.

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

Здесь необходимо отметить недостаток подобных небольших программ - как скриптов, так и экспертов. Представленная программа modifyorderprice.mq4 очень ограничена в выборе действий. Модифицируемый ордер может быть смещён только в одном направлении - в сторону рыночного курса, при этом его стоп-приказы жёстко "привязаны" к ордеру. Эта программа не приспособлена для модификации заявленной цены ордера в противоположную сторону от рыночной цены, в ней также не реализована возможность изменения положения отдельно взятого какого-либо из стоп-приказов.

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

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