Закрываем открытые ордера с помощью функции OrderClose

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

При программировании советника мы всегда должны защищать свой торговый счет от больших потерь. Управление рисками является основой работы любого советника. Необходимо установить правила, чтобы ваш советник мог закрыть все ордера, если выполняется какое-либо условие. К примеру:

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

Это только некоторые ситуации, которые могут подтолкнуть вас к немедленному закрытию всех открытых ордеров.

Давайте рассмотрим, как можно закрывать ордера с помощью функции OrderClose(). OrderClose() — это функция MQL4, которая позволяет полностью или частично закрывать открытые рыночные ордера. Эта функция требует следующие параметры:

bool  OrderClose(
   int        ticket,      // номер ордера
   double     lots,        // количество лотов
   double     price,       // цена закрытия
   int        slippage,    // максимальное проскальзывание
   color      arrow_color  // цвет
   );
  • ticket — номера тикета, чтобы определить, какой ордер нужно закрыть.
  • lots — это размер лота, который нужно закрыть. Например, если ордер является ордером BUY с 1 лотом, вы можете закрыть всю позицию, указав 1 лот, или закрыть ее частично, например, 0,6 лота, остальные 0,4 останутся открытыми.
  • price — цена закрытия ордера.
  • slippage — проскальзывание или допустимая разница между запрашиваемой ценой закрытия и ценой, которую предоставляет брокер в данный момент времени.
  • arrow_color — цвет стрелки на графике (необязательный параметр).

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

Закроем рыночный ордер на покупку:

OrderSelect(CloseTicket,SELECT_BY_TICKET);
 
if(OrderCloseTime() == 0 && OrderType() == OP_BUY)
      {
        double CloseLots = OrderLots();
        double ClosePrice = Bid;
        bool Closed = OrderClose(CloseTicket,CloseLots,ClosePrice,UseSlippage,Red);
      }

Переменная CloseTicket — это номер тикета ордера, который мы хотим закрыть. Функция OrderSelect() выбирает ордер и позволяет нам получить информацию о нем. Мы используем OrderCloseTime(), чтобы проверить время закрытия ордера, чтобы увидеть, был ли ордер уже закрыт. Если OrderCloseTime() возвращает 0, тогда мы знаем, что ордер еще не закрыт.

Нам также необходимо проверить тип ордера, так как тип ордера определяет цену его закрытия. Функция OrderType() возвращает целое число, указывающее на тип ордера. Если это рыночный ордер на покупку, обозначенный OP_BUY, мы продолжим закрывать ордер.

Затем мы получаем размер лота ордера с помощью OrderLots() и сохраняем это значение в переменную CloseLots. Мы назначаем текущую цену Bid для ClosePrice. Затем мы вызываем функцию OrderClose(), чтобы закрыть наш ордер.

Мы задаем наш параметр Slippage с помощью UseSlippage и указываем красную стрелку, которая будет напечатана на графике. Возвращаемое значение сохраняется в переменной Closed. Если ордер был успешно закрыт, значение Closed будет true, в противном случае — false.

Чтобы закрыть ордер на продажу, все, что вам нужно сделать, это изменить тип ордера на OP_SELL и назначить текущую цену Ask для ClosePrice:

if (OrderCloseTime () == 0 && OrderType () == OP_SELL) {
        double CloseLots = OrderLots (); 
        double ClosePrice = Ask;
        bool Closed = OrderClose (CloseTicket, CloseLots, ClosePrice, UseSlippage, Red);
      }

Как закрыть все открытые ордера?

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

void CloseOrders(){
 
   //Обновляем значения цен перед закрытием ордеров
   RefreshRates();
      //Выводим в терминал общее количество открытых и отложенных ордеров.
      Print(OrdersTotal());
 
   //Создаем цикл, который перебирает все ордера. Цикл начинает работать с последнего ордера, чтобы ни один ордер не был пропущен.
   for(int i=(OrdersTotal()-1);i>=0;i--){
 
      //Если ордер не может быть выбран, выводим ошибку.
     if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false){
         Print("Ошибка. Нельзя выбрать ордер - ",GetLastError());
         break;
      } 
 
      //Создаем переменную результата, чтобы проверить, успешна ли прошла операция.
      bool res=false;
 
      //Проскальзывание, которое представляет собой разницу между текущей ценой и ценой закрытия.
      int Slippage=0;
 
      //Цены Bid и Ask для ордера.
      double BidPrice=MarketInfo(OrderSymbol(),MODE_BID);
      double AskPrice=MarketInfo(OrderSymbol(),MODE_ASK);
 
      //Закрытие ордера по его текущей цене в зависимости от типа ордера.
      if(OrderType()==OP_BUY){
         res=OrderClose(OrderTicket(),OrderLots(),BidPrice,Slippage);
      }
      if(OrderType()==OP_SELL){
         res=OrderClose(OrderTicket(),OrderLots(),AskPrice,Slippage);
      }
 
      //Если была ошибка, выводим ее в журнал ошибок.
      if(res==false) Print("Ошибка. Нельзя закрыть ордер - ",OrderTicket()," - ",GetLastError());
   }
}

В данной функции мы:

  1. Обновляем текущие цены.
  2. C помощью цикла перебираем все открытые ордера.
  3. Если ордер открыт, получаем его детали.
  4. Закрываем ордер по его текущей цене.
  5. Проверяем, была ли операция успешной для каждого ордера. Если нет, выводим ошибку.

Функция не очень сложна и может быть улучшена с помощью дополнительных фильтров. Например, вы можете создать некоторые из следующих фильтров:

  • Закрыть только прибыльные ордера.
  • Закрыть только убыточные ордера.
  • Закрыть только ордера для конкретной валютной пары.
  • Закрыть только ордера с определенным магическим номером.

Рассмотрим еще один пример скрипта, который позволяет закрыть все указанные ордера. Иногда могут возникнуть обстоятельства, когда вам нужно быстро закрыть все ваши открытые ордера, в том числе:

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

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

 
#property copyright     "Александр Паркер"
#property link          "https://traderblog.net/"
#property version       "1.00"
#property strict
 
#property show_inputs
 
//Внешние переменные.
extern bool OnlyCurrentSymbol=false;   //Закрывать только ордера на текущем графике.
extern bool OnlyInProfit=false;        //Закрывать только прибыльные ордера.
extern bool OnlyInLoss=false;          //Закрывать только убыточные ордера.
extern bool OnlyMagicNumber=false;     //Закрывать только ордера с определенным magic number.
extern int MagicNumber=0;              //Magic number.
extern bool OnlyWithComment=false;     //Закрывать только ордера с определенным комментарием.
extern string MatchingComment="";      //Комментарий.
extern double Slippage=2;              //Проскальзывание.
extern int Delay=0;                    //Задержка между закрытием ордеров.
 
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   //Счетчик для закрытых ордеров.
   int TotalClosed=0;
 
   //Нормализируем проскальзывание.
   if(Digits==3 || Digits==5){
      Slippage=Slippage*10;
   }
 
   //Перебираем ордера.
   for(int i=OrdersTotal()-1; i>=0; i--){
 
      //Выбераем ордер. Если ордер не выбран, выводим ошибку и переходите к следующему индексу.
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) == false) {
         Print("Ошибка - ",GetLastError());
         continue;
      } 
 
      //Проверяем, можно ли закрыть ордер в соответствии с заданными критериями, если критерии не совпадают, перейти к следующему
      if(OnlyCurrentSymbol && OrderSymbol()!=Symbol()) continue;
      if(OnlyInProfit && OrderProfit()<=0) continue;
      if(OnlyInLoss && OrderProfit()>=0) continue;
      if(OnlyMagicNumber && OrderMagicNumber()!=MagicNumber) continue;
      if(OnlyWithComment && StringCompare(OrderComment(),MatchingComment)!=0) continue;
 
      //Подготавливаем цены закрытия.
      double ClosePrice=0;
      RefreshRates();
      if(OrderType()==OP_BUY) ClosePrice=NormalizeDouble(MarketInfo(OrderSymbol(),MODE_BID),Digits);
      if(OrderType()==OP_SELL) ClosePrice=NormalizeDouble(MarketInfo(OrderSymbol(),MODE_ASK),Digits);
 
      //Закрываем ордер.
      if(OrderClose(OrderTicket(),OrderLots(),ClosePrice,Slippage,CLR_NONE)){
         TotalClosed++;
      }
      else{
         Print("Ошибка - ",GetLastError());
      }      
 
      //Задержка.
      Sleep(Delay);
 
   }
 
   //Выводим общее количество закрытых ордеров
   Print("Всего ордеров закрыто = ",TotalClosed);
 
  }