Модификация ордеров
Язык 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) |
extern int Tral_Stop=10;
int start()
{
string Symb=Symbol();
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();
while(true)
{
double TS=Tral_Stop;
int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL);
if (TS < Min_Dist)
TS=Min_Dist;
bool Modify=false;
switch(Tip)
{
case 0 :
if (NormalizeDouble(SL,Digits)<
NormalizeDouble(Bid-TS*Point,Digits))
{
SL=Bid-TS*Point;
string Text="Buy ";
Modify=true;
}
break;
case 1 :
if (NormalizeDouble(SL,Digits)>
NormalizeDouble(Ask+TS*Point,Digits)
|| NormalizeDouble(SL,Digits)==0)
{
SL=Ask+TS*Point;
Text="Sell ";
Modify=true;
}
}
if (Modify==false)
break;
double TP =OrderTakeProfit();
double Price =OrderOpenPrice();
int Ticket=OrderTicket();
Alert ("Модификация ",Text,Ticket,". Ждём ответ..");
bool Ans=OrderModify(Ticket,Price,SL,TP,0);
if (Ans==true)
{
Alert ("Ордер ",Text,Ticket," модифицирован:)");
break;
}
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;
case 5 : Alert("Старая версия клиентского терминала.");
break;
case 64: Alert("Счет заблокирован.");
break;
case 133:Alert("Торговля запрещена");
break;
default: Alert("Возникла ошибка ",Error);
}
break;
}
}
}
return;
}
Представленная для примера программа - эксперт. В случае необходимости ничего не
мешает реализовать функцию модификации ордеров и в скрипте. Однако использование
обычного скрипта в данном случае не удобно - после выполнения торговых операций
скрипт закончит работу. Использование скрипта может быть оправданно в том случае,
если в программе реализуется выполнение разовой операции - например, открытие или
закрытие ордеров. В данном случае решается задача, требующая постоянного контроля
ситуации - изменить положение стоп-приказа, если выполняется некоторое условие, а именно, дистанция между рыночным курсом и заявленным
значением стоп-приказа превышает некоторую заданную величину (в нашем случае 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).
|
int start()
{
int Tral=10;
string Symb=Symbol();
double Dist=1000000.0;
double Win_Price=WindowPriceOnDropped();
for(int i=1; i<=OrdersTotal(); i++)
{
if (OrderSelect(i-1,SELECT_BY_POS)==true)
{
if (OrderSymbol()!= Symb) continue;
if (OrderType()<2) continue;
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();
double TP =OrderTakeProfit();
}
}
}
if (Tip==0)
{
Alert("По ",Symb," отложенных ордеров нет");
return;
}
while(true)
{
RefreshRates();
double TS=Tral;
int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL);
if (TS < Min_Dist)
TS=Min_Dist;
string Text="";
double New_SL=0;
double New_TP=0;
switch(Tip)
{
case 2:
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);
if (NormalizeDouble(TP,Digits)>0)
New_TP=New_Price+(TP-Price);
Text= "BuyLimit ";
}
break;
case 3:
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);
if (NormalizeDouble(TP,Digits)>0)
New_TP=New_Price-(Price-TP);
Text= "SellLimit ";
}
break;
case 4:
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);
if (NormalizeDouble(TP,Digits)>0)
New_TP=New_Price+(TP-Price);
Text= "BuyStopt ";
}
break;
case 5:
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);
if (NormalizeDouble(TP,Digits)>0)
New_TP=New_Price-(Price-TP);
Text= "SellStop ";
}
}
if (NormalizeDouble(New_SL,Digits)<0)
New_SL=0;
if (NormalizeDouble(New_TP,Digits)<0)
New_TP=0;
if (Text=="")
{
Alert("Нет условий для модификации.");
break;
}
Alert ("Модификация ",Text,Ticket,". Ждём ответ..");
bool Ans=OrderModify(Ticket,New_Price,New_SL,New_TP,0);
if (Ans==true)
{
Alert ("Модифицирован ордер ",Text," ",Ticket," :)");
break;
}
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;
case 64: Alert("Счет заблокирован.");
break;
case 133:Alert("Торговля запрещена");
break;
case 139:Alert("Ордер заблокирован и уже обрабатывается");
break;
case 145:Alert("Модификация запрещена. ",
"Ордер слишком близок к рынку");
break;
default: Alert("Возникла ошибка ",Error);
}
break;
}
Alert ("Скрипт закончил работу -----------------------");
return;
}
Расстояние между рыночной ценой и заявленной ценой отложенного ордера задано в переменной
Tral. Переменная Win_Price содержит значение цены, на которой в окно финансового
инструмента прикреплён скрипт. В цикле перебора ордеров (блок 2-5) вычисляются
характеристики отложенного ордера, находящегося ближе других к месту прикрепления
скрипта. Блок 6-13 представляет цикл закрытия ордеров. В блоке 8-9 определяется,
нужно ли модифицировать выбранный ордер, и, если необходимо, вычисляются новые значения
заявленной цены и стоп-приказов. Приказ на модификацию отдаётся с помощью функции
OrderModify() в блоке 10-11. В блоке 11-13 реализована обработка ошибок.
Блок 8-9 состоит из четырёх однотипных блоков, в которых вычисляются новые значения
отложенного ордера, используемые в торговом приказе. Рассмотрим один из них, для
ордера SellLimit:
case 3:
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);
if (NormalizeDouble(TP,Digits)>0)
New_TP = New_Price-(Price-TP);
Text= "SellLimit ";
}
break;
Новые параметры ордера вычисляются только в том случае, если текущая цена 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, однако для этой цели
необходимо использовать другой - более "интеллектуальный" - алгоритм.
Возможно создать программу механизации торговли, которая позволила бы модифицировать
ордера в соответствии с пожеланиями пользователя. В качестве дополнительных средств
управления для ручной торговли в такой программе можно применить, например,
графические объекты.