#property copyright "forum.tradelikeapro.ru"
#property strict

#include <stderror.mqh>
#include <stdlib.mqh>
#include <WinUser32.mqh>

extern int    MagicNumber = 9999;
extern int    slip = 10;
extern double Lots = 0.01;
extern int    TakeProfit = 0;
extern int    StopLoss = 800;
extern string  _1 = "Настройки индикатора";
extern ENUM_TIMEFRAMES CciTimeframe = PERIOD_M15;
extern int    CciPeriod     = 5;
extern int    CciOpenLevel  = 110; 
extern int    CciCloseLevel = 100;
extern ENUM_TIMEFRAMES MATimeframe = PERIOD_M15;
extern int    MAPeriod      = 5;
extern string  _2 = "Фильтры";
extern int    HourStart = 22;
extern int    HourStop  = 4;
extern double OtstupOtMA  = 60;
extern int    TrallStart  = 50;
extern int    TrallStep  = 10;
//---------------------------


datetime timeprev = 0;
double minLot, maxLot, Min_Dist;
string EAName="Generic A-TLP";
bool RealTrade=false; 
int Trades[2];


//+------------------------------------------------------------------+


int OnInit() {
   minLot  = MarketInfo(Symbol(), MODE_MINLOT);
   maxLot  = MarketInfo(Symbol(), MODE_MAXLOT);
   Min_Dist=MarketInfo(Symbol(),MODE_STOPLEVEL);

   if (_Digits == 2 || _Digits == 4){
        TakeProfit /= 10;
        StopLoss /= 10;
        slip /= 10; if(slip<1) slip=1;
        Print("Correction 4 digits: TakeProfit:",TakeProfit, " StopLoss:",StopLoss," slp:",slip);
   }

   if(!IsTesting() && !IsOptimization()) RealTrade=true;
   
   return (INIT_SUCCEEDED);
}
//нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн

double dist = OtstupOtMA / 100000 ;

void OnTick(){
  
   //новый бар
   if (timeprev == Time[0]) return;
   timeprev = Time[0];
   CountTrades(Trades);
  
   //сигнал
   double cci1 = iCCI(_Symbol,CciTimeframe,CciPeriod,PRICE_WEIGHTED,1);
   double ma1  = iMA(_Symbol,MATimeframe,MAPeriod,0,MODE_EMA,PRICE_WEIGHTED,1);
   
   //закрытие сделок
   if(cci1 < -1*CciCloseLevel && Trades[OP_SELL]>0) CloseAll(OP_SELL);
   if(cci1 > CciCloseLevel    && Trades[OP_BUY]>0)  CloseAll(OP_BUY);
  
   // вызов трала
     for (int i=0;i<OrdersTotal();i++)
         {
         if (!OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) break;
         if (OrderMagicNumber()!=MagicNumber || OrderSymbol()!=Symbol()) continue;
         if ((OrderType()==OP_BUY) || (OrderType()==OP_SELL))
            {
            // !!! ПРИМЕР ВЫЗОВА ФУНКЦИЙ ТРЕЙЛИНГА !!!
            // среди возможных вариантов мы, допустим, выбрали трейлинг по фракталам. Трейлингуем по 
            // 5-барным фракталам на дневках, с отступом от экстремума в 3 п., в зоне лоссов не тралим
            //TrailingByFractals(OrderTicket(),1440,5,3,false);
            // (как видим, достаточно предварительно выбрать ордер OrderSelect() и вызвать функцию, 
            // передав ей тикет позиции и определив необходимые параметры).
            // При желании Вы можете закоментировать данный вид трейлинга и подключить любой другой 
            // или даже "сконструировать" из них более или менее сложную конструкцию.            
           // TrailingByShadows(OrderTicket(),iTmfrm,iBars_n,iIndent,bTrlinloss);          
            //TrailingRatchetB(OrderTicket(),10,25,30,10,25,30,false);
            TrailingStairs(OrderTicket(),TrallStart,TrallStep);  
            //TrailingByATR(OrderTicket(),1440,5,1,20,1,1,false); 
            // и т.д.       
            }
         }

   //открытие сделок
   if(TimeFilter() || Trades[OP_BUY]+Trades[OP_SELL] > 0) return;
   if(cci1 > CciOpenLevel && ma1 + dist < Bid){
      if(!OrderSend(_Symbol, OP_SELL, Lots, Bid, slip, StopShort(Ask, StopLoss), TakeShort(Bid, TakeProfit), EAName, MagicNumber, 0, clrBlue))
         Print("Ошибка открытия ордера: ", ErrorDescription(GetLastError()));
   }   
   if(cci1 < -1*CciOpenLevel && ma1 - dist > Ask){
      if(!OrderSend(_Symbol, OP_BUY, Lots, Ask, slip, StopLong(Bid, StopLoss), TakeLong(Ask, TakeProfit), EAName, MagicNumber, 0, clrBlue))
         Print("Ошибка открытия ордера: ", ErrorDescription(GetLastError()));
   }   
}

bool TimeFilter(){
   if(HourStart<HourStop){
      if(Hour()<HourStart || Hour()>HourStop) return false;
   }else{
      if(Hour()<HourStop || Hour()>HourStart) return false;
   }
   return true;
}



//нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн
double StopLong(double price, int stop) {
   if (stop <= 0) return (0);
   else return ND(price - stop *_Point);
}
double StopShort(double price, int stop) {
   if (stop <= 0) return (0);
   else return ND(price + stop *_Point);
}
double TakeLong(double price, int stop) {
   if (stop <= 0) return (0);
   else return ND(price + stop *_Point);
}
double TakeShort(double price, int stop) {
   if (stop <= 0) return (0);
   else return ND(price - stop *_Point);
}
//+------------------------------------------------------------------+
void OrderCloseX(int ticket1, double lots, int otype, int slp, color clr=CLR_NONE){
    int err = 0; double price = 0;
    bool exit_loop = false;

    int retry = 10;
    int cnt1   = 0;

    lots = NormalizeDouble(lots,2);
    if(lots<minLot) return;
    
    while(!exit_loop && cnt1<retry){
        if(otype == OP_BUY)  price=Bid;
        if(otype == OP_SELL) price=Ask;
        if(!OrderClose(ticket1, lots, price, slp, clr)){
           err = GetLastError();
           switch(err){
             case ERR_INVALID_PRICE:
                  RefreshRates();
                  break;
             case ERR_REQUOTE:
                  RefreshRates();
                  break;
             case ERR_OFF_QUOTES:
                  RefreshRates();
                  break;
             case ERR_BROKER_BUSY:
                  break;
             default:
                  exit_loop = true;
                  break;
           }
           Sleep(1000);
           cnt1++;
        }else
           exit_loop = true;
   }
      
   if(err != ERR_NO_ERROR && err != ERR_NO_RESULT) Print ("Ошибка закрытия ордера #",ticket1," ",ErrorDescription(err));
}
//+------------------------------------------------------------------+
void CountTrades(int& count[]) {
   ArrayFill(count,0,2,0);
   int total=OrdersTotal();
   for(int pos=0;pos<total;pos++){ 
      if(OrderSelect(pos, SELECT_BY_POS, MODE_TRADES)==false) continue;
      if(OrderSymbol() != Symbol()) continue;
      count[OrderType()]++;
   }
}



double ND(double ad_0) {
   return (NormalizeDouble(ad_0, _Digits));
}

void CloseAll(int type) {
   for(int i=OrdersTotal()-1;i>=0;i--){
      if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES)==false) continue;
      if (OrderSymbol() != _Symbol || OrderMagicNumber() != MagicNumber) continue;
      if(OrderType()==type) OrderCloseX(OrderTicket(), OrderLots(), OrderType(), slip);
   }
}



void TrailingStairs(int ticket,int trldistance,int trlstep)
   { 
   
   double nextstair; // ближайшее значение курса, при котором будем менять стоплосс

   // проверяем переданные значения
   if ((trldistance<MarketInfo(Symbol(),MODE_STOPLEVEL)) || (trlstep<1) || (trldistance<trlstep) || (ticket==0) || (!OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES)))
      {
      Print("Трейлинг функцией TrailingStairs() невозможен из-за некорректности значений переданных ей аргументов.");
      return;
      } 
   
   // если длинная позиция (OP_BUY)
   if (OrderType()==OP_BUY)
      {
      // расчитываем, при каком значении курса следует скорректировать стоплосс
      // если стоплосс ниже открытия или равен 0 (не выставлен), то ближайший уровень = курс открытия + trldistance + спрэд
      if ((OrderStopLoss()==0) || (OrderStopLoss()<OrderOpenPrice()))
      nextstair = OrderOpenPrice() + trldistance*Point;
         
      // иначе ближайший уровень = текущий стоплосс + trldistance + trlstep + спрэд
      else
      nextstair = OrderStopLoss() + trldistance*Point;

      // если текущий курс (Bid) >= nextstair и новый стоплосс точно лучше текущего, корректируем последний
      if (Bid>=nextstair)
         {
         if ((OrderStopLoss()==0) || (OrderStopLoss()<OrderOpenPrice()) && (OrderOpenPrice() + trlstep*Point<Bid-MarketInfo(Symbol(),MODE_STOPLEVEL)*Point)) 
            {
            if (!OrderModify(ticket,OrderOpenPrice(),OrderOpenPrice() + trlstep*Point,OrderTakeProfit(),OrderExpiration()))
            Print("Не удалось модифицировать стоплосс ордера №",OrderTicket(),". Ошибка: ",GetLastError());
            }
         }
      else
         {
         if (!OrderModify(ticket,OrderOpenPrice(),OrderStopLoss() + trlstep*Point,OrderTakeProfit(),OrderExpiration()))
         Print("Не удалось модифицировать стоплосс ордера №",OrderTicket(),". Ошибка: ",GetLastError());
         }
      }
      
   // если короткая позиция (OP_SELL)
   if (OrderType()==OP_SELL)
      { 
      // расчитываем, при каком значении курса следует скорректировать стоплосс
      // если стоплосс ниже открытия или равен 0 (не выставлен), то ближайший уровень = курс открытия + trldistance + спрэд
      if ((OrderStopLoss()==0) || (OrderStopLoss()>OrderOpenPrice()))
      nextstair = OrderOpenPrice() - (trldistance + MarketInfo(Symbol(),MODE_SPREAD))*Point;
      
      // иначе ближайший уровень = текущий стоплосс + trldistance + trlstep + спрэд
      else
      nextstair = OrderStopLoss() - (trldistance + MarketInfo(Symbol(),MODE_SPREAD))*Point;
       
      // если текущий курс (Аск) >= nextstair и новый стоплосс точно лучше текущего, корректируем последний
      if (Ask<=nextstair)
         {
         if ((OrderStopLoss()==0) || (OrderStopLoss()>OrderOpenPrice()) && (OrderOpenPrice() - (trlstep + MarketInfo(Symbol(),MODE_SPREAD))*Point>Ask+MarketInfo(Symbol(),MODE_STOPLEVEL)*Point))
            {
            if (!OrderModify(ticket,OrderOpenPrice(),OrderOpenPrice() - (trlstep + MarketInfo(Symbol(),MODE_SPREAD))*Point,OrderTakeProfit(),OrderExpiration()))
            Print("Не удалось модифицировать стоплосс ордера №",OrderTicket(),". Ошибка: ",GetLastError());
            }
         }
      else
         {
         if (!OrderModify(ticket,OrderOpenPrice(),OrderStopLoss()- (trlstep + MarketInfo(Symbol(),MODE_SPREAD))*Point,OrderTakeProfit(),OrderExpiration()))
         Print("Не удалось модифицировать стоплосс ордера №",OrderTicket(),". Ошибка: ",GetLastError());
         }
      }     
   
   }