Как перенести СЛ в безубыток

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

Как перенести СЛ в безубыток

Сообщение Haos » 09 мар 2018, 13:12

Часто при использовании той или иной торговой системы необходимо осуществлять процедуру переноса СЛ в безубыток. Рассмотрим теорию этого вопроса и программную реализацию в MQL4.

Прежде всего определим переменные которые помогут разобрать теорию этого вопроса.
Пусть
Y0 - цена открытия позиции;
Y1 - текущая цена (Bid или Ask в зависимости от типа позиции);
lp - профит в пнт. после которого СЛ переносится в безубыток
lw - уровень безубытка в пунктах

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

1. Рассмотрим позицию на покупку

На рисунке видно (см. рисунок ниже), что как только цена пройдет в прибыльную сторону расстояние не менее lp нужно перенести СЛ (SL0) в положение цена открытие позиции (Y0) плюс заданное расстояние (lw). При этом в коде нужно учесть случай, когда начальный СЛ вообще не задан, т.е. нулевой.

01-Для покупки.png


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

Как перенести СЛ в безубыток

Сообщение Haos » 09 мар 2018, 13:19

Таким образом положение СЛ в безубытке (SL1) определяется:

SL1 = Y0 + lw (для покупки)
SL1 = Y0 - lw (для продажи)

Условие же необходимое для переноса СЛ в безубыток будет выглядеть так:

Y1 > Y0 + lp (для покупки)
Y1 < Y0 - lp (для продажи).
Аватар пользователя
Haos
Специалист MQL
 
Сообщений: 24699
Зарегистрирован: 29 мар 2014, 16:07
Средств на руках: 193.70 Доллар
Группа: Главные модераторы
Благодарил (а): 3379 раз.
Поблагодарили: 8200 раз.

Как перенести СЛ в безубыток

Сообщение Haos » 09 мар 2018, 13:27

Теперь, собственно, сам код, реализованный в функции под названием f_MovingInWL(). Для её работы понадобятся еще ряд функций: f__ModifyOrder(), f__ProcessError(int err), f__PrintError(int err), f__Err2str(int err)

Код: выделить все
void f_MovingInWL(string sy, int op, int lp, int lw, int mn)
{
//  Версия   : 08.03.2018                                                     
//  Описание : Перенос уровня стопа в безубыток     
//  Используется функция f__ModifyOrder()                         
//  Параметры:                                                               
//  sy - наименование инструмента   ( ""  - любой символ,                   
//                                   "0" - текущий символ)                 
//  op - операция                   ( -1  - любая позиция)                 
//  lp - профит в пнт. после которого СЛ переносится в безубыток
//  lw - Уровень безубытка в пунктах
//  mn - MagicNumber                ( -1  - любой магик)

   double   Po,   // величина пункта
            Ysl,  // уровень установки СЛ
            Y0,   // цена установки ордера
            Y1;   // текущая котировка цены
   int      Di;   // количество цифр после точки в котировке
   if(sy == "0") sy = Symbol();

   for(int i = 0; i < OrdersTotal(); i++)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if((OrderSymbol() == sy || sy == "") && (op < 0 || OrderType() == op))
         {
            if(mn < 0 || OrderMagicNumber() == mn)
            {
               Di = (int) MarketInfo(OrderSymbol(), MODE_DIGITS);
               Po = MarketInfo(OrderSymbol(), MODE_POINT);
               if(OrderType() == OP_BUY)
               {
                  Y1 = MarketInfo(OrderSymbol(), MODE_BID);
                  Y0 = OrderOpenPrice();
                  Ysl = NormalizeDouble(Y0 + lw * Po, Di);
                  if(Y1 > Y0 + lp * Po)
                  {
                     if(OrderStopLoss() == 0 || OrderStopLoss() < Ysl)
                     {
                        if(f__ModifyOrder(OrderTicket(), Ysl, -1))
                        {
                           Print(Symbol(), "Modify Trailing ok");         
                        }
                        else Print(Symbol(), " Modify Trailing fail");                     
                     }
                  }
               }
               if(OrderType() == OP_SELL)
               {
                  Y1 = MarketInfo(OrderSymbol(), MODE_ASK);
                  Y0 = OrderOpenPrice();
                  Ysl = NormalizeDouble(Y0 - lw * Po, Di);                 
                  if(Y1 < Y0 - lp * Po)
                  {
                     if(OrderStopLoss() == 0 || OrderStopLoss() > Ysl)
                     {
                        if(f__ModifyOrder(OrderTicket(), Ysl, -1))
                        {
                           Print(Symbol(), "Modify Trailing ok");         
                        }
                        else Print(Symbol(), " Modify Trailing fail");                       
                     }
                  }
               }
            }
         }
      }
   }
}

//*********************************************************************************************

bool f__ModifyOrder(int ti, double sl, double tp)
{
/*
   Описание: модификация предварительно выбранного ордера
   Используется функция: f__ProcessError()
   Параметры:                                                               
   ti - OrderTicket()
   sl - новый СЛ ордера (-1 - не менять)
   tp - новый ТП ордера (-1 - не менять)             
*/
   int int_Try = 5; // rоличество попыток
   if (OrderSelect(ti, SELECT_BY_TICKET) == true)
   {
      int i = 0;
      double dbl_SL = sl; if(sl == -1) dbl_SL = OrderStopLoss();
      double dbl_TP = tp; if(tp == -1) dbl_TP = OrderTakeProfit();
      if((OrderStopLoss()== dbl_SL) && (OrderTakeProfit() == dbl_TP)) return(true);
      GetLastError();
      while(i < int_Try)
      {
         if(OrderModify(OrderTicket(), OrderOpenPrice(), dbl_SL, dbl_TP, 0)) return(true);
         else
         {
            i++;
            if(!f__ProcessError(GetLastError())) return(false);
         }
      }
   }

return(false);
}

//*********************************************************************************************

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);
}

//*********************************************************************************************

int f__PrintError(int err)
{
   Print("ERROR ", err, ":  ", f__Err2str(err));
   return(0);
}

//*********************************************************************************************

//+------------------------------------------------------------------+
//| Получение описания ошибки по ее номеру                           |
//+------------------------------------------------------------------+
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("Описание ошибки не известно");
}

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


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

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

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

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

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