#property copyright "master_255"
#define   VER       "1.1"
#property version   VER
#property strict

#include <stderror.mqh>
#include <stdlib.mqh>

extern int    MagicNumber = 9999;
extern int    slip = 3;
extern double Lots = 0.1;
extern int    DepoPer001Lot = 0;
extern string _2 = "Настройки";
input ENUM_TIMEFRAMES CandlePeriod = PERIOD_H4;
extern int    FiboLevelTP = 400;
extern int    DistanceOpenPoint = 150;
extern int    DistanceStopLossPoint = 100;
input  bool   CloseAllFridayEnd = False;
input  bool   CloseAllDayEnd = False;
extern string _3 = "Фильтры";
input  int    BodyShadowPercent = 110;
extern int    MaxCandlePips = 600;
extern int    HourStart = 0;
extern int    HourStop  = 0;
extern string _4 = "Трейлинг стоп и бу";
extern int    BreakEvenAfterTPPercent = 30;
input  bool   BreakEvenOnlyNewBar = False;
input  int    CloseAfterBEPercent = 70;
extern int    TrailStart = 200;
extern int    TrailStop = 0;
extern int    TrailStep = 100;
//---------------------------
double minLot, maxLot;
string EAName="Test-GRC-any-timeframe";
datetime timeprev = 0, TimeLastTrail=0, LastOpen=0;
int LastHistoryTotal=0,ht=0, Min_Dist;
bool RealTrade=false; 
//+------------------------------------------------------------------+
int OnInit() {
   minLot  = MarketInfo(Symbol(), MODE_MINLOT);
   maxLot  = MarketInfo(Symbol(), MODE_MAXLOT);
   Min_Dist= (int)MarketInfo(Symbol(), MODE_STOPLEVEL);

   if (_Digits == 2 || _Digits == 4){
        TrailStart /= 10;
        TrailStop /= 10;
        TrailStep /= 10;
        MaxCandlePips /= 10;
        DistanceOpenPoint /= 10;
        DistanceStopLossPoint /= 10;
        slip /= 10; if(slip<1) slip=1;
        Print("Correction 4 digits: DistanceOpenPoint:",DistanceOpenPoint," DistanceStopLossPoint:",DistanceStopLossPoint," MaxCandlePips:",MaxCandlePips," TrailStart:",TrailStart," TrailStop:",TrailStop," slp:",slip);
   }

   if(!IsTesting() && !IsOptimization()) RealTrade=true;
   
   return (INIT_SUCCEEDED);
}
//нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн
void OnTick(){
   //Трейлинг раз в минуту
   if(TrailStop>0){
      if(TimeLastTrail+60 < TimeCurrent()){
         TrailingStandart(MagicNumber, TrailStop);
         TimeLastTrail=TimeCurrent();
      }
   }

   //Закрываем сделки в пятницу, или начало понедельника если в пятницу небыло тиков
   if(CloseAllFridayEnd){
      if((DayOfWeek()==5 && Hour()>=23 && Minute()>=50) || DayOfWeek()==1){
         CloseAll();
      }
   }
   
   //Закрываем сделки в конце дня
   if(CloseAllDayEnd){
      if(Hour()>=23 && Minute()>=50){
         CloseAll();
      }
   }
   
   //бу
   if(!BreakEvenOnlyNewBar) checkBE();
   
   //новый бар
   if (timeprev == iTime(_Symbol, CandlePeriod,0)) return;
   timeprev = iTime(_Symbol, CandlePeriod,0);
   
   //бу
   if(BreakEvenOnlyNewBar) checkBE();
   
   //удаление несработавших отложек
   DeleteAllPending();

   //динамический лот
   if(DepoPer001Lot>0){
      Lots = NormalizeDouble((AccountFreeMargin()/DepoPer001Lot)/100,2);
      if(Lots<minLot) Lots = minLot; 
   }
   
   //параметры свечей
   MqlRates Rates[];
   ArraySetAsSeries(Rates,true);
   CopyRates(_Symbol, CandlePeriod, 0, 3, Rates);

   //фильтны сигнала
   double body,upShadow,downShadow;
   if(Rates[1].open > Rates[1].close){ 
      body = Rates[1].open-Rates[1].close;
      upShadow = Rates[1].high-Rates[1].open;
      downShadow = Rates[1].close-Rates[1].low;
   }else{
      body = Rates[1].close-Rates[1].open;
      upShadow = Rates[1].high-Rates[1].close;
      downShadow = Rates[1].open-Rates[1].low;
   }
   
   if(MaxCandlePips>0){
      if((Rates[1].high-Rates[1].low)/_Point > MaxCandlePips){
         DrawWarn("Слишком большая свеча");
         return;
      }
   }
   
   if(BodyShadowPercent>0){
      if(upShadow>=(body*BodyShadowPercent/100) || downShadow>=(body*BodyShadowPercent/100)){
         DrawWarn("Тень больше "+IntegerToString(BodyShadowPercent)+"% тела");
         return;
      }
   }
   
   if(TimeFilter()) return;
   
   //сигнал
   bool c1=Rates[1].open>Rates[1].close;
   bool c2=Rates[2].open>Rates[2].close;
   
   if(c1!=c2){
      if(c1){
         double soprice = Rates[1].low-DistanceOpenPoint*_Point;
         double slprice = Rates[1].high+DistanceStopLossPoint*_Point;
         double tpprice =  getFiboLevel(Rates[1].high, Rates[1].low, FiboLevelTP, -1);
         
         if(OrderSend(_Symbol, OP_SELLSTOP, Lots, soprice, slip, slprice, tpprice, EAName, MagicNumber, TimeCurrent()+CandlePeriod*60, HotPink)<0)
            Print("Ошибка открытия ордера: ", ErrorDescription(GetLastError()));
      }else{
         double boprice = Rates[1].high+DistanceOpenPoint*_Point;
         double slprice = Rates[1].low-DistanceStopLossPoint*_Point;
         double tpprice =  getFiboLevel(Rates[1].high, Rates[1].low, FiboLevelTP, 1);
         if(OrderSend(_Symbol, OP_BUYSTOP, Lots, boprice, slip, slprice, tpprice, EAName, MagicNumber, TimeCurrent()+CandlePeriod*60, Blue)<0)
            Print("Ошибка открытия ордера: ", ErrorDescription(GetLastError()));
      }
      LastOpen=Time[0];
   }
}

//нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн
void CloseAll(){
   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()>1) if(!OrderDelete(OrderTicket())) Print("Ошибка удаления ордера: ", GetLastError());
      if(OrderType()==OP_BUY) OrderCloseX(OrderTicket(), OrderLots(), OP_BUY, slip);
      if(OrderType()==OP_SELL) OrderCloseX(OrderTicket(), OrderLots(), OP_SELL, slip);
   }
}

void checkBE(){
   if(BreakEvenAfterTPPercent>0){
      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()==OP_BUY && Bid>=OrderOpenPrice()+((OrderTakeProfit()-OrderOpenPrice())*BreakEvenAfterTPPercent/100) && OrderStopLoss()<OrderOpenPrice()){
            OrderModifyX(OrderTicket(),OrderOpenPrice(),OrderOpenPrice(),OrderTakeProfit(),0,clrDarkOrange);
            if(CloseAfterBEPercent>0){
               OrderCloseX(OrderTicket(), OrderLots()*CloseAfterBEPercent/100, OP_BUY, slip);
            }
         }
         if(OrderType()==OP_SELL && Ask<=OrderOpenPrice()-((OrderOpenPrice()-OrderTakeProfit())*BreakEvenAfterTPPercent/100) && OrderStopLoss()>OrderOpenPrice()){
            OrderModifyX(OrderTicket(),OrderOpenPrice(),OrderOpenPrice(),OrderTakeProfit(),0,clrDarkOrange);
            if(CloseAfterBEPercent>0){
               OrderCloseX(OrderTicket(), OrderLots()*CloseAfterBEPercent/100, OP_SELL, slip);
            }
         }
      }
   }
}

bool TimeFilter(){
   if(HourStart<HourStop){
      if(Hour()<HourStart || Hour()>HourStop) return false;
   }
   if(HourStart>HourStop){
      if(Hour()<HourStop || Hour()>HourStart) return false;
   }
   if(HourStart==HourStop) return false;
   return true;
}

double getFiboLevel( double up, double dw, double lev, int direction ){
   double ling = up - dw;
   double pLev = ( ling / 100 ) * lev;
   
   return ( direction < 0 ) ? NormalizeDouble( up - pLev, Digits ) : NormalizeDouble( dw + pLev, Digits ) ;
}

void DeleteAllPending(){
   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()>1) if(!OrderDelete(OrderTicket())) Print("Ошибка удаления ордера: ", GetLastError());
   }
}

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 ("Ошибка закрытия ордера #" + IntegerToString(ticket1) + " Код ошибки: " + ErrorDescription(err));
}
//+------------------------------------------------------------------+
double ND(double ad_0) {
   return (NormalizeDouble(ad_0, _Digits));
}


void OrderModifyX(int ticket1, double price, double stoploss, double takeprofit, datetime expiration, color arrow_color=CLR_NONE){
    int err = 0;
    bool exit_loop = false;

    int retry = 10;
    int cnt1   = 0;
    
    while(!exit_loop && cnt1<retry){
        if(!OrderModify(ticket1, price, stoploss, takeprofit, expiration, arrow_color)){
           err = GetLastError();
           switch(err){
             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 ("Ошибка модификации ордера #" + IntegerToString(ticket1) + " Код ошибки: " + ErrorDescription(err));
}


void TrailingStandart(int CurrentMagic, int trldistance){ 
   double nextsl,sl,tp;
   int trlstart=TrailStart;
  
   Min_Dist=(int)MarketInfo(Symbol(),MODE_STOPLEVEL);
   if(trldistance<Min_Dist){
      //Print("StopLevel: "+Min_Dist+" < stop: "+stop);
      trlstart+=Min_Dist-trldistance;
      trldistance=Min_Dist;
   }
   
   int total=OrdersTotal();
   for(int pos=0;pos<total;pos++) {
      if (OrderSelect(pos, SELECT_BY_POS, MODE_TRADES)==false) continue;
      if (OrderSymbol() != Symbol() || OrderMagicNumber() != CurrentMagic) continue;

      sl=ND(OrderStopLoss());
      if(sl>0) trldistance+=TrailStep;
      if (OrderType()==OP_BUY){
         if(Bid>OrderOpenPrice()+ND(trlstart*_Point)){
            nextsl = ND(Bid - trldistance*_Point);
            tp = ND(OrderTakeProfit());
            if(nextsl>=tp) tp=0;
            if(nextsl>sl || sl==0){
               OrderModifyX(OrderTicket(),OrderOpenPrice(),nextsl,tp,0,clrDarkOrange);
            }
         }
      }
      
      if (OrderType()==OP_SELL){ 
         if(Ask<OrderOpenPrice()-ND(trlstart*_Point)){
            nextsl = ND(Ask + trldistance*_Point);
            tp = ND(OrderTakeProfit());
            if(nextsl<=tp) tp=0;
            if((nextsl<sl || sl==0)){
               OrderModifyX(OrderTicket(),OrderOpenPrice(),nextsl,tp,0,clrDarkOrange);
            }
         }
      }   
   }    
}

//--------
void DrawWarn(string text){
   if(RealTrade || IsVisualMode()){
      string obid=_Symbol+TimeToStr(Time[1]);
      if(ObjectFind(obid)!=-1) ObjectDelete(obid);
      
      ENUM_ANCHOR_POINT anc;
      double price;
   
      if((WindowPriceMax()+WindowPriceMin())/2 < Bid){
         price=Low[1];
         anc=ANCHOR_RIGHT;
      }else{
         price=High[1];
         anc=ANCHOR_LEFT;
      }
      
      ObjectCreate(obid, OBJ_TEXT, 0, Time[1], price);
      ObjectSet(obid, OBJPROP_ANGLE, 90);
      ObjectSet(obid,OBJPROP_ANCHOR,anc);
      ObjectSet(obid,OBJPROP_BACK,false);
      ObjectSetText(obid,text,10,"Arial",clrPlum);
   }
}

//--------

double OnTester(){
  double Res = 0;
  double MaxDD = TesterStatistics(STAT_EQUITY_DD);
  if (MaxDD != 0)
      Res = TesterStatistics(STAT_PROFIT) / MaxDD;
  return Res;
}