//+------------------------------------------------------------------+
//|                                                    Страховка.mq4 |
//+------------------------------------------------------------------+
/*
 Открывает страховочные позиции абсолютно ко всем рыночным ордерам по счету 
(т.е. прикрепляем к любой паре, а робот работает со всеми сразу). 
Если рыночный ордер будет закрыт (вручную, или по тейку), а отложенный ордер останется, 
то робот это заметит и удалит отложенный ордер.
=====================================================
  Есть возможность выбора цены открытия: либо по цене стопа рыночного ордера 
(тогда если стоп равен 0, то робот не станет открывать отложку), либо на чётко заданной дистанции.
=====================================================
  Для пятизнаков  добавьте 0 
*/

extern double K_Lots         = 1.0;  //коэффициент для объёма новых сделок (во сколько раз увеличивать открытие лоты)
extern int    SL             = 0, //величина Stop Loss (в пунктах) новых сделок
              TP             = 0; //величина Take Profit (в пунктах) новых сделок
extern int    Slipage        = 3; //уровень максимально допустимого проскальзывания (в пунктах)
extern int    Magic_Number   = 0; //магическое число - метка ордеров для робота (чтобы отличал "свои" сделки)

extern bool   Op_By_SL       = false; //ставить ордера строго на уровни SL: true - включено, false - выключено
extern int    Points_To_Open = 50; //дистанция (в пунктах) для открытия ордеров относительно цен рыночных ордеров (учитывается, если Op_By_SL=false)

extern bool   Second_Ord     = true; // Выставлять второй отложенник
extern int    Point_Sec_Ord  = 100;  // Дистанция для второго отложенника

//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
//----
   
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
  {
//----
   
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+

//+-------------------------------------------------------------------------------------------------------------------+
//|                                        функция поиска ордеров данного типа                                        |
bool find_orders(int magic=-1, int type=-1, string symb="NONE", string comm="NONE") {
  /* возвращает истину, если найден хотя бы один ордер данного типа с данным магическим номером по данному символу */
  for (int i=OrdersTotal()-1; i>=0; i--) {
    if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) break;
    if(((OrderType()==type) || (type==-1))
       && ((OrderMagicNumber()==magic) || (magic==-1))
       && (OrderSymbol()==Symbol())
       && ((OrderComment()==comm) || (comm=="NONE"))
       ) {
      //если ордер найден, то возвращаем true и выходим из цикла
      return(true);
      break;
    } //end if((OrderType()==type) && (OrderMagicNumber()==magic) && (OrderSymbol()==Symbol()))
  } //end for (int i2=OrdersTotal()-1; i2>=0; i2--)

  return(false); //возвращаем false
} //end bool find_orders(int magic, int type)
//|                                        функция поиска ордеров данного типа                                        |
//+-------------------------------------------------------------------------------------------------------------------+

//+-------------------------------------------------------------------------------------------------------------------+
//|                                           функция определения Stop Loss                                           |
double sl(int sl_value, int mode, string symb="NONE", double price=0.0) {
  //mode=0 -> рыночные покупки
  //mode=1 -> рыночные продажи
  symb=Symbol();
  if(price==0.0) {
    if(mode==0) price=MarketInfo(symb,MODE_ASK);
    if(mode==1) price=MarketInfo(symb,MODE_BID);
  } //end if(price==0.0)
  if(sl_value<=0) return(0);
  if(mode==0 || mode==2 || mode==4)     return(price-sl_value*MarketInfo(symb,MODE_POINT)); //для покупок
  if(mode==1 || mode==3 || mode==5)     return(price+sl_value*MarketInfo(symb,MODE_POINT)); //для продаж
return(0);} //end double sl(int sl_value, int mode, string symb="NONE", double price=0.0)
//|                                           функция определения Stop Loss                                           |
//+-------------------------------------------------------------------------------------------------------------------+

//+-------------------------------------------------------------------------------------------------------------------+
//|                                          функция определения Take Profit                                          |
double tp(int tp_value, int mode, string symb="NONE", double price=0.0) {
  //mode=0 -> рыночные покупки
  //mode=1 -> рыночные продажи
  symb=Symbol();
  if(price==0.0) {
    if(mode==0) price=MarketInfo(symb,MODE_ASK);
    if(mode==1) price=MarketInfo(symb,MODE_BID);
  } //end if(price==0.0)
  if(tp_value<=0) return(0);
  if(mode==0 || mode==2 || mode==4)     return(price+tp_value*MarketInfo(symb,MODE_POINT)); //для покупок
  if(mode==1 || mode==3 || mode==5)     return(price-tp_value*MarketInfo(symb,MODE_POINT)); //для продаж
return(0);} //end double tp(int tp_value, int mode, string symb="NONE", double price=0.0)
//|                                          функция определения Take Profit                                          |
//+-------------------------------------------------------------------------------------------------------------------+


//+-------------------------------------------------------------------------------------------------------------------+
//|    функция открытия ордеров   

void open_positions(int signal, double lot, double price=0.0, string symb="NONE", string comm="NONE") {
  //signal=0 -> сигнал на открытие покупки
  //signal=1 -> сигнал на открытие продажи
  /* extern */ int Count_Of_Trade_Try=5, Pause_Of_Trade_Try=5;

  int    i = 0; //переменная для счётчика цикла

  symb=Symbol();
  if(signal==0)
    price=MarketInfo(symb,MODE_ASK); //цена открытия для покупок
  if(signal==1)
    price=MarketInfo(symb,MODE_BID); //цена открытия для продаж

  while(i<=Count_Of_Trade_Try) {
    //сама функия открытия ордера (встроенная). Для удобства восприятия параметры разнесены на разные строки:
    int ticket = OrderSend(Symbol(),      //символ
                           signal,        //тип ордера
                           lot,           //объем
                           price,         //цена открытия
                           Slipage,       //уровень допустимого реквота
                           sl(SL,signal,symb,price), //величина Stop Loss
                           tp(TP,signal,symb,price), //величина Take Profit
                           comm, //комментарий ордера
                           Magic_Number,  //магическое число
                           0,             //срок истечения (используется при отложенных ордерах)
                           CLR_NONE);     //цвет отображаемой стрелки на графике (CLR_NONE - стрелка не рисуется)
    if(ticket!=-1) //если открытие произошло успешно, наносим графический объект и выходим из цикла
      break;
    int err=GetLastError(); 
    if(err!=0) Print("Ошибка: "+Market_Err_To_Str(err));
    i++;
    Sleep(Pause_Of_Trade_Try*100); //в случае ошибки делаем паузу перед новой попыткой
  } //end while(i<=count)
} //end void open_positions(int signal, double lot, double price=0.0, string symb="NONE")
//|                                              функция открытия ордеров                                             |
//+-------------------------------------------------------------------------------------------------------------------+

//+-------------------------------------------------------------------------------------------------------+
//|                                      функция расшифровки кодов ошибок                                 |
string Market_Err_To_Str(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(145): return("Модификация запрещена, т.к. ордер слишком близок к рынку");
    case(146): return("Подсистема торговли занята");
    case(147): return("Использование даты истечения запрещено брокером");
    case(148): return("Количество открытых и отложенных ордеров достигло предела, установленного брокером");
    case(149): return("Попытка открыть противоположную позицию к уже существующей в случае, если хеджирование запрещено");
    case(150): return("Попытка закрыть позицию по инструменту в противоречии с правилом FIFO");
   
    default:   return("");
  } //end switch(err)
} //end string Err_To_Str(int err)
//|                                      функция расшифровки кодов ошибок                                 |
//+-------------------------------------------------------------------------------------------------------+


void open_by_order(int magic=-1) {
    double op_price=0.0,op_priceSec=0.0, lots=0.0, l_sl=0.0, l_op=0.0;
    int op_type=-1,op_typeSec=-1, l_type;
    string symb="", comm="";
    for (int i=OrdersTotal()-1; i>=0; i--) {
    if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) break;
    if(OrderMagicNumber()==Magic_Number&&OrderSymbol()==Symbol()){
    l_type=OrderType();
    symb=OrderSymbol();
    comm=DoubleToStr(OrderTicket(),0);
    lots=NormalizeDouble(OrderLots()*K_Lots,dig_by_lots(MarketInfo(Symbol(),MODE_LOTSTEP)));
    l_sl=OrderStopLoss();
    l_op=OrderOpenPrice();
    op_price=0.0;
    if((l_type<=1)

       && ((l_sl!=0) || (!Op_By_SL))
       ) {

     
      if(!find_orders(magic, -1, symb, comm)) {
        if(l_type==OP_BUY) {
          op_type=OP_SELLSTOP;
          op_typeSec=OP_BUYLIMIT;
          if(Op_By_SL){
            op_price=NormalizeDouble(l_sl,MarketInfo(symb, MODE_DIGITS));
            op_priceSec=NormalizeDouble(l_sl,MarketInfo(symb, MODE_DIGITS));    }
          else{
            op_price=NormalizeDouble(l_op-Points_To_Open*MarketInfo(symb, MODE_POINT),MarketInfo(symb, MODE_DIGITS));
           op_priceSec=NormalizeDouble(l_op-Point_Sec_Ord*MarketInfo(symb, MODE_POINT),MarketInfo(symb, MODE_DIGITS));
        }}
        if(l_type==OP_SELL) {
          op_type=OP_BUYSTOP;
          op_typeSec=OP_SELLLIMIT;
          if(Op_By_SL){
            op_price=NormalizeDouble(l_sl,MarketInfo(symb, MODE_DIGITS));
          op_priceSec=NormalizeDouble(l_sl,MarketInfo(symb, MODE_DIGITS));}
          else {
            op_price=NormalizeDouble(l_op+Points_To_Open*MarketInfo(symb, MODE_POINT),MarketInfo(symb, MODE_DIGITS));
           op_priceSec=NormalizeDouble(l_op+Point_Sec_Ord*MarketInfo(symb, MODE_POINT),MarketInfo(symb, MODE_DIGITS));
        }}
        //Comment(symb," ",comm," ",lots," ",op_type, " ",op_price);
        open_positions(op_type, lots, op_price, symb, comm);
        if(Second_Ord)open_positions(op_typeSec, lots, op_priceSec, symb, comm);
        
        continue; 
      }
    } //end if((OrderType()==type) && (OrderMagicNumber()==magic) && (OrderSymbol()==Symbol()))
  }} //end for (int i2=OrdersTotal()-1; i2>=0; i2--)
}

int dig_by_lots(double lot_step) {
  string lot_s=DoubleToStr(lot_step,8);
  int i=0, n=0, c=0;
  n=StringFind(lot_s,".",0);
  for(i=0; i<=100; i++) {
    if(StrToInteger(StringSubstr(lot_s, n+i, 1))>0)
      c=i;
  }
  return(c);
}


void del_orders() {
  int f_ticket=0, m_ticket=0;
  for (int i=OrdersTotal()-1; i>=0; i--) {
    if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) continue;
    f_ticket=StrToInteger(OrderComment());
    m_ticket=OrderTicket();
    if(OrderMagicNumber()==Magic_Number
       && OrderType()>1&&OrderSymbol()==Symbol()) {
      if((!OrderSelect(f_ticket,SELECT_BY_TICKET)) || ((OrderSelect(f_ticket,SELECT_BY_TICKET))&&(OrderCloseTime()!=0)))
        close_by_ticket(m_ticket, Slipage);
    } //end if((OrderType()==type) && (OrderMagicNumber()==magic) && (OrderSymbol()==Symbol()))
  } //end for (int i2=OrdersTotal()-1; i2>=0; i2--)
}

//+-------------------------------------------------------------------------------------------------------------------+
//|                                  функция закрытия ордера по её номеру (тикету)                                    |


bool close_by_ticket(int c_ticket, int slipage) {
  /*
     функция закрытия сделки по её номеру (тикету).
     При закрытии рыночного ордера учитывается уровень максимального допустимого проскальзывания (slipage)
  */
/* extern */ int Count_Of_Trade_Try=5, Pause_Of_Trade_Try=5;

int   i        = 0;     //переменная для счетчика цикла
bool  ticket   = false; //перменная для обозначения (не)успешности факта закрытия сделки
double price   = 0.0; //цена для закрываемой сделки (для рыночных ордеров)
  if(OrderSelect(c_ticket,SELECT_BY_TICKET,MODE_TRADES)&&OrderMagicNumber()==Magic_Number&&OrderSymbol()==Symbol()) { //выбираем ордер по тикету
    if(OrderType()==OP_BUY)  price = Bid; //цена для покупок
    if(OrderType()==OP_SELL) price = Ask; //цена для продаж
    for(i=0;i<=Count_Of_Trade_Try;i++) {
      if(OrderType()<=1) //если рыночный ордер - закрываем его, если отложенный - удаляем
        ticket=OrderClose(OrderTicket(),OrderLots(),price,slipage,CLR_NONE);
      else
        ticket=OrderDelete(OrderTicket());

      if(ticket) { //если закрытие или удаление прошло успешно - возвращаем true и выходим из цикла
        return(true);
        break;
      } //end if(ticket)
      if(GetLastError()!=0) Print("Ошибка: "+Market_Err_To_Str(GetLastError()));
      Sleep(Pause_Of_Trade_Try*100); //в случае ошибки делаем паузу перед новой попыткой
    } //end for(i=0;i<=Count_Of_Trade_Try;i++)
  } //end if(OrderSelect(c_ticket,SELECT_BY_TICKET,MODE_TRADES))

  return(false); //возвращаем false
} //end bool close_by_ticket(int c_ticket)
//|                                  функция закрытия ордера по её номеру (тикету)                                    |
//+-------------------------------------------------------------------------------------------------------------------+

int start()
  {
//----
   open_by_order(Magic_Number);
   del_orders();
//----
   return(0);
  }
//+------------------------------------------------------------------+


