Как закрыть позицию в МQL4?

Программирование прибыли: от азов к секретам мастерства. Читайте, спрашивайте, делитесь опытом.
Бонус за сообщение 0.5$
Ответственный Модератор - Haos

Как закрыть позицию в МQL4?

Сообщение Haos » 11 июл 2017, 09:45

Для закрытия позиции в MQL имеется специальная встроенная торговая функция OrderClose(). Данная функция имеет следующие параметры:
Код: выделить все
bool  OrderClose(
   int        ticket,      // номер ордера
   double     lots,        // количество лотов
   double     price,       // цена закрытия
   int        slippage,    // максимальное проскальзывание
   color      arrow_color  // цвет
   );

Возвращаемое значение типа bool, т.е. логическое. Функция возвращает истина true при удачном закрытии позиции и false при возникновении какой-то ошибки при работе функции и не-закрытии позиции в итоге.
Как видно из перечня параметров, передающихся в функцию, присутствует количество лотов, т.е. мы должны сразу же отметить, что в данном месте есть возможность частичного закрытия позиции (а такие задачи у разработчика частенько возникают). Также указывается цена закрытия позиции. При этом, если сделка является сделкой на покупку, то необходимо указать цену Bid в качестве значения и Ask - для продажи. Параметр максимальное проскальзывание обычно задается через глобальные переменные кода, т.к. он присутствует во многих функциях как имеющихся в MQL , так и разработанных программистом отдельно.
Номер ордера - представляет собой уникальный порядковый номер для каждого ордера (позиции) и может быть получен через функцию OrderTicket(), когда произошел однозначный выбор нужного ордера. Этот "однозначный выбор" осуществляется через цикл перебора всех имеющихся ордеров при помощи функции OrderSelect(), с заданием требуемых условий для искомого ордера.
Параметр "цвет" определяет цвет стрелки закрытия на графике. Если параметр отсутствует, или его значение равно CLR_NONE, то стрелка на графике не отображается.
Аватар пользователя
Haos
Специалист MQL
 
Сообщений: 24699
Зарегистрирован: 29 мар 2014, 16:07
Средств на руках: 193.70 Доллар
Группа: Главные модераторы
Благодарил (а): 3379 раз.
Поблагодарили: 8200 раз.

Re: Как закрыть позицию в МQL4?

Сообщение Haos » 11 июл 2017, 10:09

Поскольку в практически каждом коде присутствует процедура закрытия позиций, то разработчики давно создали ряд функций, обеспечивающих более корректную работу по выполняемой задаче. Эти функции разработчик может хранить в отдельном файле и использовать каждый раз при выполнении новой задачи. Ниже рассмотрим один из вариантов закрытия позиций при помощи написанных разработчиками функций.
1. f__ClosePositions(string sy, int op, int mn, int si)
Примечание: имя данной функции соответствует шаблону моих разработок. При этом я ставлю букву "f" вначале, что определяет именно то, что это будет название именно функции. Далее я ставлю один или два символа подчеркивания, чтобы отделить "f" от фактического имени функции. Вы, как разработчик, естественно можете выработать свои правила.
Код: выделить все
void f__ClosePositions(string sy, int op, int mn, int si)
{
/*
   Описание : Закрытие позиций по рыночной цене
   Используется функция: f__CloseOrder()   
   Параметры:                                                               
   sy - наименование инструмента   ("0" - текущий символ)                 
   op - операция                   (-1   - любая позиция)                 
   mn - MagicNumber                (-1   - любой магик)
   si - проскальзывание (slippage) (пнт.)               
*/
   if(sy == "0") sy = Symbol(); 
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) == false) break;
      if((OrderSymbol() == sy) && (op < 0 || OrderType() == op))
      {
         if(OrderType() == OP_BUY || OrderType() == OP_SELL)
         {
            if(mn < 0 || OrderMagicNumber() == mn)   
            {
               RefreshRates();
               f__CloseOrder(sy, OrderTicket(), OrderLots(), si);
            }
         }
      }
   }
}

По коду функции (см. код выше) видно, что в ней передается четыре параметра, а именно:
1. Наименование инструмента. Это необходимо для того, чтобы иметь возможность закрыть позиции не только по текущему инструменту на котором запущен советник(скрипт), но и по другим.
2. Операция. Здесь мы можем закрыть только покупки или только продажи, а также и те и другие. Это определяется через указание типа операции: OP_BUY - покупка, OP_SELL - продажа, -1 - и те и другие.
3. MagicNumber. Вот в этом параметре мы выделяем лишь те ордера, которые были открыты только нашим советником (скриптом), а не другие. Это очень важный параметр, т.к. многие не понимают что, задав один раз MagicNumber в советнике, мы можем однозначно выделять только его "родные" ордера и нет необходимости менять мэджик при переходе на другой инструмент или ТФ как думают многие пользователя (с чем я сталкивался неоднократно).
4. Проскальзывание мы уже упоминали в прошлом сообщении и вот здесь как раз тот случай, когда оно передается в качестве параметра.
Аватар пользователя
Haos
Специалист MQL
 
Сообщений: 24699
Зарегистрирован: 29 мар 2014, 16:07
Средств на руках: 193.70 Доллар
Группа: Главные модераторы
Благодарил (а): 3379 раз.
Поблагодарили: 8200 раз.

Re: Как закрыть позицию в МQL4?

Сообщение Haos » 11 июл 2017, 10:27

Итак, функция f__ClosePositions() организует цикл перебора всех ордеров, сужая его в зависимости от заданных нами параметров (см. код выше). Она выбирает те, ордера, которые соответствуют указанному инструменту, потом выбирает из них открытые в данный момент, потом выбирает покупки ли нужны или продажи или и те и другие, потом проверяет чтобы они соответствовали нашему мэджику и только после этого мы можем уверенно заявлять, что произошел выбор именно тех, позиций, которые нам нужны.
Далее, видно, что происходит вызов другой пользовательской функции f__CloseOrder(sy, OrderTicket(), OrderLots(), si), код которой мы и рассмотрим ниже:
Код: выделить все
bool f__CloseOrder(string sy, int ti, double lo, int si)
{
/*
   Описание: 
   Используется функция: f__ProcessError()
   Параметры:                                                               
   sy - наименование инструмента ("0" - текущий символ)                 
   ti - OrderTicket()
   lo - OrderLots()
   si - проскальзывание (slippage) (пнт.)               
*/
   int int_Try = 5; // Количество торговых попыток

   if(lo < MarketInfo(sy, MODE_MINLOT)) return(false);
   if(OrderSelect(ti, SELECT_BY_TICKET) == true)
   {
      int i = 0;
      GetLastError();
      while(i < int_Try)
      {
         RefreshRates();
         double dbl_Pri = MarketInfo(sy, MODE_BID);
         if(OrderType() == OP_SELL) dbl_Pri = MarketInfo(sy, MODE_ASK);
         if(OrderClose(ti, lo, dbl_Pri, si))return(true);
         else
         {
            i++;
            if(!f__ProcessError(GetLastError())) return(false);
         }
      }
   }

return(false);
}

Вот теперь, когда нужный ордер определен первой функцией, данная функция и начинает осуществлять процедуру его закрытия. При этом, как видно из кода, это происходит в течении указанного количества попыток (пять в данном случае), т.к. не всегда возможно закрыть позицию с первой попытке из-за возможных технических сбоев для решения которых именно подойдет алгоритм повторных попыток а не другой, рассмотренный далее.
Данная функция возвращает true если произошло удачное закрытие позиции и false если не удачное и никаким способом перехвата ошибки её не удалось решить.
Вот эти возможные ошибки и пытаются быть решенными при помощи пользовательской функции f__ProcessError(GetLastError()), которая присутствует в теле функции. Её мы рассмотрим далее.
Аватар пользователя
Haos
Специалист MQL
 
Сообщений: 24699
Зарегистрирован: 29 мар 2014, 16:07
Средств на руках: 193.70 Доллар
Группа: Главные модераторы
Благодарил (а): 3379 раз.
Поблагодарили: 8200 раз.

Re: Как закрыть позицию в МQL4?

Сообщение Haos » 11 июл 2017, 10:34

Код функции f__ProcessError():
Код: выделить все
bool f__ProcessError(int err)
{
/*
   Описание : Обработка ошибок
   Используется функция: f__PrintError()
   Параметры:                                                               
   err - номер ошибки           
*/
   int iteration;

   if(err > 1) f__PrintError(err);
   switch(err)
   {
   // Некритические ошибки
   case 0:     // ERR_NO_ERROR
   case 135:   // ERR_PRICE_CHANGED
   case 138:   // ERR_REQUOTE
      return(true);
   // Некритические ошибки, требующие таймаута
   case 4:     // ERR_SERVER_BUSY
   case 128:   // ERR_TRADE_TIMEOUT
      Sleep(1000 * 60);
      return(true);   
   case 129:   // ERR_INVALID_PRICE
   case 130:   // ERR_INVALID_STOPS
      //if (WorkMode==ModeManual && ShowConfirm) MessageBox("Неправильные стопы", VISITCARD, MB_OK);
      return(false);
   case 136:   // ERR_OFF_QUOTES
      Sleep(1000 * 5);
      return(true);   
   case 137:   // ERR_BROKER_BUSY
   case 145:   // ERR_TRADE_MODIFY_DENIED
      Sleep(1000 * 15);
      return(true);   
   // Нет связи с сервером. Пытаемся восстановить связь
   case 6:     // ERR_NO_CONNECTION
   {
      bool connect = false;
      iteration = 0;
      while((!connect) || (iteration < 60)) // в течение 5 минут пытаемся подконнектиться к серверу
      {
         Sleep(1000 * 5);
         connect = IsConnected();
         if(connect)
         {
            Print("Связь восстановлена");
            return(true);
         }
         iteration++;
         Print("Связь не восстановлена, прошло ", iteration * 5, " секунд.");
      }
      Print("Соединение в течение ", iteration * 5, " секунд восстановить не удалось.");
      return(false);
   }
   // Подсистема торговли занята. Ждем, пока освободится
   case 146:   // ERR_TRADE_CONTEXT_BUSY
   {
      bool tradecontextbusy = true;
      iteration = 0;
      while((tradecontextbusy) || (iteration < 60)) // в течение 5 минут ждем освобождения подсистемы торговли
      {
         Sleep(1000 * 5);
         tradecontextbusy = IsTradeContextBusy();
         if(!tradecontextbusy)
         {
            Print("Подсистема торговли освободилась.");
            return(true);
         }
         iteration++;
         Print("Подсистема торговли все еще занята, прошло ", iteration * 5, " секунд.");
      }
      Print("Подсистема торговли в течение ", iteration * 5, " секунд не освободилась.");
      return(false);
   }
   // Критические ошибки. Прекращаем попытки открытия ордера
   case 1:     // ERR_NO_RESULT
   case 2:     // ERR_COMMON_ERROR
   case 3:     // ERR_INVALID_TRADE_PARAMETERS
   case 5:     // ERR_OLD_VERSION
   case 7:     // ERR_NOT_ENOUGH_RIGHTS
   case 8:     // ERR_TOO_FREQUENT_REQUESTS
   case 9:     // ERR_MALFUNCTIONAL_TRADE
   case 64:    // ERR_ACCOUNT_DISABLED
   case 65:    // ERR_INVALID_ACCOUNT
   case 131:   // ERR_INVALID_TRADE_VOLUME
   case 132:   // ERR_MARKET_CLOSED
   case 133:   // ERR_TRADE_DISABLED
   case 134:   // ERR_NOT_ENOUGH_MONEY
   case 139:   // ERR_ORDER_LOCKED
   case 140:   // ERR_LONG_POSITIONS_ONLY_ALLOWED
   case 141:   // ERR_TOO_MANY_REQUESTS
   case 142:
   case 143:
   case 144:
   case 147:   // ERR_TRADE_EXPIRATION_DENIED
   case 148:   // ERR_TRADE_TOO_MANY_ORDERS
   case 4110:  // ERR_LONGS_NOT_ALLOWED
   case 4111:  // ERR_SHORTS_NOT_ALLOWED
      return(false);
   case 4109:  // ERR_TRADE_NOT_ALLOWED
      //MessageBox("Для совершения операций необходимо поставить галочку \"Разрешить советнику торговать\" в настройках терминала", VISITCARD, MB_OK);
      return(false);
   }
   return(false);
}

И функция f__ProcessError() использует пользовательскую функцию f__PrintError(int err).

Код: выделить все
int f__PrintError(int err)
{
   Print("ERROR ", err, ":  ", f__Err2str(err));
   return(0);
}

Функция f__PrintError(int err) использует пользовательскую функцию получения описания ошибки f__Err2str(int err) по ее номеру :
Код: выделить все
string f__Err2str(int err)
{
   switch(err)
   {
   case 0:
      return("Нет ошибки");
   case 1:
      return("Функция модификации ордера пытается изменить уже установленные значения такими же значениями");
   case 2:
      return("Общая ошибка");
   case 3:
      return("В торговую функцию переданы неправильные параметры");
   case 4:
      return("Торговый сервер занят");
   case 5:
      return("Старая версия клиентского терминала");
   case 6:
      return("Нет связи с торговым сервером");
   case 7:
      return("Недостаточно прав");
   case 8:
      return("Слишком частые запросы");
   case 9:
      return("Недопустимая операция нарушающая функционирование сервера");
   case 64:
      return("Счет заблокирован");
   case 65:
      return("Неправильный номер счета");
   case 128:
      return("Истек срок ожидания совершения сделки");
   case 129:
      return("Неправильная цена");
   case 130:
      return("Неправильные стопы");
   case 131:
      return("Неправильный объем сделки");
   case 132:
      return("Рынок закрыт");
   case 133:
      return("Торговля запрещена");
   case 134:
      return("Недостаточно денег для совершения операции");
   case 135:
      return("Цена изменилась");
   case 136:
      return("Нет цен");
   case 137:
      return("Брокер занят");
   case 138:
      return("Реквот! Новые цены");
   case 139:
      return("Ордер заблокирован и уже обрабатывается");
   case 140:
      return("Разрешена только покупка");
   case 141:
      return("Слишком много запросов");
   case 142:
      return("Ордер поставлен в очередь");
   case 143:
      return("Ордер принят дилером к исполнению");
   case 144:
      return("Ордер аннулирован самим клиентом при ручном подтверждении сделки");
   case 145:
      return("Модификация запрещена, так как ордер слишком близок к рынку");
   case 146:
      return("Подсистема торговли занята");
   case 147:
      return("Использование даты истечения ордера запрещено брокером");
   case 148:
      return("Количество открытых и отложенных ордеров достигло предела, установленного брокером");
   case 4109:
      return("Торговля не разрешена. Необходимо включить опцию \"Разрешить советнику торговать\" в свойствах эксперта");
   case 4110:
      return("Длинные позиции не разрешены");
   case 4111:
      return("Короткие позиции не разрешены");
   }
   return("Описание ошибки не известно");
}

Таким образом, возникающие ошибки при закрытии позиции сначала пытаемся устранить, а в случае невозможности этого получаем подробное описание этой ошибки.
Вот такой получился массивный код с применением 5 пользовательских функций, необходимых "всего лишь" для осуществления закрытия позиции. Для применения данных пяти функций разработчик должен все их пять скопировать в код разрабатываемого советника (скрипта), а вызывать только первую рассмотренную функцию f__ClosePositions(), а та в свою очередь сама осуществить отработку остальных четырех функций. Так что сложностей здесь в применении особых нет. В новых статьях мы так или иначе столкнемся с примером использования данных функций.
Аватар пользователя
Haos
Специалист MQL
 
Сообщений: 24699
Зарегистрирован: 29 мар 2014, 16:07
Средств на руках: 193.70 Доллар
Группа: Главные модераторы
Благодарил (а): 3379 раз.
Поблагодарили: 8200 раз.


Вернуться в MQL – теория и практика

Кто сейчас на форуме?

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 56

Права доступа к форуму

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

cron