Range Breakout EA MT5 issues

 

I wrote that code by some youtube programming channel.
No errors, compiles fine but in tester DrawingObjects doesn't look right(specially range) and finally it stops taking trades(probably because of that buggy range)

//+------------------------------------------------------------------+
//|                                               Range Breakout.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include  <Trade\Trade.mqh>

input long InpMagicNumber=12345;
input double InpLots=0.01;
input int InpStopLoss=150;
input int InpTakeProfit=200;

input int InpRangeStart=600;
input int InpRangeDuration=1200;
input int InpRangeClose=600;      //range close time in minutes (-1=off)

enum BREAKOUT_MODE_ENUM
  {
   ONE_SIGNAL,                //one breakout per range
   TWO_SIGNALS               //high and low breakout
  };
input BREAKOUT_MODE_ENUM InpBreakoutMode=ONE_SIGNAL;

input bool InpMonday=true;
input bool InpTuesday=true;
input bool InpWednesday=true;
input bool InpThursday=true;
input bool InpFriday=true;

struct RANGE_STRUCT
  {
   datetime          start_time;
   datetime          end_time;
   datetime          close_time;
   double            high;
   double            low;
   bool              f_entry;
   bool              f_high_breakout;
   bool              f_low_breakout;
                     RANGE_STRUCT() : start_time(0),end_time(0),close_time(0),high(0),low(DBL_MAX),f_high_breakout(false),f_low_breakout(false) {};
  };

RANGE_STRUCT range;
MqlTick prevTick, lastTick;
CTrade trade;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {

   if(!CheckInputs())
     {
      return INIT_PARAMETERS_INCORRECT;
     }

   trade.SetExpertMagicNumber(InpMagicNumber);

   if(_UninitReason==REASON_PARAMETERS && CountOpenPositions()==0)
     {
      CalculateRange();
     }

   DrawObjects();

   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ObjectsDeleteAll(NULL,"range");
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {

   prevTick=lastTick;
   SymbolInfoTick(_Symbol,lastTick);

   if(lastTick.time >= range.start_time && lastTick.time < range.end_time){
      
      range.f_entry=true;

      if(lastTick.ask > range.high){
         range.high = lastTick.ask;
         DrawObjects();
        }
        
      if(lastTick.bid < range.low){
         range.low = lastTick.bid;
         DrawObjects();
        }
   }

   if(InpRangeClose >= 0 && lastTick.time >= range.close_time){
      if(!ClosePositions()) {return;}
   }

   if(((InpRangeClose >= 0 && lastTick.time >= range.close_time)
       || (range.f_high_breakout && range.f_low_breakout)
       || (range.end_time == 0)
       || (range.end_time!=0 && lastTick.time > range.end_time && !range.f_entry))
      && CountOpenPositions() == 0){

      CalculateRange();
   }
   CheckBreakouts();
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CheckInputs()
  {

   if(InpMagicNumber <= 0){
      Alert("MagicNumber<=0");
      return false;
     }
   if(InpLots <= 0 || InpLots > 1){
      Alert("Lots=0 or >1");
      return false;
     }
   if(InpStopLoss < 0 || InpStopLoss>1000){
      Alert("Lots=0 or >1");
      return false;
     }
   if(InpTakeProfit < 0 || InpTakeProfit>1000){
      Alert("Lots=0 or >1");
      return false;
     }
   if(InpRangeClose < 0 && InpStopLoss==0){
      Alert("Lots=0 or >1");
      return false;
     }
   if(InpRangeStart < 0 || InpRangeStart >= 1440){
      Alert("Lots=0 or >1");
      return false;
     }
   if(InpRangeDuration <= 0 || InpRangeDuration >= 1440){
      Alert("Lots=0 or >1");
      return false;
     }

   if(InpRangeClose >= 1440 || (InpRangeStart+InpRangeDuration)%1440 == InpRangeClose){
      Alert("Lots=0 or >1");
      return false;
     }

   if(InpMonday+InpTuesday+InpWednesday+InpThursday+InpFriday == 0){
      Alert("Lots=0 or >1");
      return false;
   }

   return true;
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CalculateRange()
  {
   range.start_time=0;
   range.end_time=0;
   range.close_time=0;
   range.high=0.0;
   range.low=DBL_MAX;
   range.f_entry=false;
   range.f_high_breakout=false;
   range.f_low_breakout=false;

   int time_cycle=86400;

   range.start_time = (lastTick.time-(lastTick.time % time_cycle)) + InpRangeStart*60;
   for(int i=0; i<0; i++){
      MqlDateTime tmp;
      TimeToStruct(range.start_time,tmp);
      int dow = tmp.day_of_week;
      if(lastTick.time >= range.start_time || dow==6 || dow==0 || (dow==1 && !InpMonday) || (dow==2 && !InpTuesday) || (dow==3 && !InpWednesday)
         || (dow==4 && !InpThursday) || (dow==5 && !InpFriday)){
         range.start_time += time_cycle;
        }
     }

   range.end_time = range.start_time + InpRangeDuration*60;
   for(int i=0; i<2; i++){
      MqlDateTime tmp;
      TimeToStruct(range.end_time,tmp);
      int dow = tmp.day_of_week;
      if(dow==6 || dow==0){
         range.end_time += time_cycle;
        }
    }

   if(InpRangeClose>=0){
      range.close_time = (range.end_time - (range.end_time % time_cycle)) + InpRangeClose*60;
      for(int i=0; i<3; i++){
         MqlDateTime tmp;
         TimeToStruct(range.close_time,tmp);
         int dow = tmp.day_of_week;
         if(range.close_time <= range.end_time || dow==6 || dow==0){
            range.close_time += time_cycle;
           }
        }
     }

   DrawObjects();
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CountOpenPositions(){

   int counter = 0;
   int total = PositionsTotal();
   for(int i=total; i>0; i--){
      ulong ticket = PositionGetTicket(i);
      if(ticket<=0){Print("Failed to get positions ticket"); return -1;}
      if(!PositionSelectByTicket(ticket)){Print("Failed to select position by ticket"); return-1;}
      ulong magicnumber;
      if(!PositionGetInteger(POSITION_MAGIC,magicnumber)){Print("Failed to get position magicnumber"); return -1;}
      if(InpMagicNumber==magicnumber){counter++;}
     }
   return counter;
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CheckBreakouts()
  {

   if(lastTick.time >= range.end_time && range.end_time > 0 && range.f_entry){

      if(!range.f_high_breakout && lastTick.ask >= range.high){
         range.f_high_breakout = true;
         if(InpBreakoutMode==ONE_SIGNAL){range.f_low_breakout = true;}

         double sl=InpStopLoss==0 ? 0: NormalizeDouble(lastTick.bid-((range.high-range.low)*InpStopLoss*0.01),_Digits);
         double tp=InpTakeProfit==0 ? 0: NormalizeDouble(lastTick.bid+((range.high-range.low)*InpTakeProfit*0.01),_Digits);

         trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,InpLots,lastTick.ask,sl,tp,"Time range EA");
        }

      if(!range.f_low_breakout && lastTick.bid <= range.low){
         range.f_low_breakout = true;
         if(!InpBreakoutMode==ONE_SIGNAL){range.f_high_breakout = true;}
         
         double sl=InpStopLoss==0 ? 0: NormalizeDouble(lastTick.ask + ((range.high-range.low) * InpStopLoss*0.01),_Digits);
         double tp=InpTakeProfit==0 ? 0: NormalizeDouble(lastTick.ask - ((range.high-range.low) * InpTakeProfit*0.01),_Digits);

         trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,InpLots,lastTick.bid,sl,tp,"Time range EA");
        }
     }
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool ClosePositions(){

   int total = PositionsTotal();
   for(int i=total; i>=0; i--){
      if(total!=PositionsTotal()){total=PositionsTotal(); i=total; continue;}
      ulong ticket = PositionGetTicket(i);
      if(ticket<=0){Print("Failed to get position ticket"); return false;}
      if(!PositionSelectByTicket(ticket)){Print("Failed to select position by ticket"); return false;}
      long magicnumber;
      if(!PositionGetInteger(POSITION_MAGIC,magicnumber)){Print("Failed to get position magicnumber"); return false;}
      if(!magicnumber == InpMagicNumber){
         trade.PositionClose(ticket);
         if(trade.ResultRetcode()!=TRADE_RETCODE_DONE){
            Print("Failed to close position: " +(string)trade.ResultRetcode()+":"+trade.ResultRetcodeDescription());
            return false;
           }
        }
     }
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void DrawObjects()
  {

   ObjectDelete(NULL,"range start");
   if(range.start_time>0){
      ObjectCreate(NULL,"range start",OBJ_VLINE,0,range.start_time,0);
      ObjectSetString(NULL,"range start",OBJPROP_TOOLTIP,"start of the range \n"+TimeToString(range.start_time,TIME_DATE|TIME_MINUTES));
      ObjectSetInteger(NULL,"range start",OBJPROP_COLOR,clrBlue);
      ObjectSetInteger(NULL,"range start",OBJPROP_WIDTH,2);
      ObjectSetInteger(NULL,"range start",OBJPROP_BACK,true);
   }
   
   ObjectDelete(NULL,"range end");
   if(range.end_time>0){
      ObjectCreate(NULL,"range end",OBJ_VLINE,0,range.end_time,0);
      ObjectSetString(NULL,"range end",OBJPROP_TOOLTIP,"end of the range \n"+TimeToString(range.end_time,TIME_DATE|TIME_MINUTES));
      ObjectSetInteger(NULL,"range end",OBJPROP_COLOR,clrBlue);
      ObjectSetInteger(NULL,"range end",OBJPROP_WIDTH,2);
      ObjectSetInteger(NULL,"range end",OBJPROP_BACK,true);
   }
   
   ObjectDelete(NULL,"range close");
   if(range.close_time>0){
      ObjectCreate(NULL,"range close",OBJ_VLINE,0,range.close_time,0);
      ObjectSetString(NULL,"range close",OBJPROP_TOOLTIP,"close of the range \n"+TimeToString(range.close_time,TIME_DATE|TIME_MINUTES));
      ObjectSetInteger(NULL,"range close",OBJPROP_COLOR,clrRed);
      ObjectSetInteger(NULL,"range close",OBJPROP_WIDTH,2);
      ObjectSetInteger(NULL,"range close",OBJPROP_BACK,true);
   }

   ObjectsDeleteAll(NULL,"range high");
   if(range.high>0){
      ObjectCreate(NULL,"range high",OBJ_TREND,0,range.start_time,range.high,range.end_time,range.high);
      ObjectSetString(NULL,"range high",OBJPROP_TOOLTIP,"high of the range \n"+DoubleToString(range.high,_Digits));
      ObjectSetInteger(NULL,"range high",OBJPROP_COLOR,clrBlue);
      ObjectSetInteger(NULL,"range high",OBJPROP_WIDTH,2);
      ObjectSetInteger(NULL,"range high",OBJPROP_BACK,true);

      ObjectCreate(NULL,"range high",OBJ_TREND,0,range.end_time,range.high,InpRangeClose>=0 ? range.close_time : INT_MAX,range.high);
      ObjectSetString(NULL,"range high",OBJPROP_TOOLTIP,"high of the range \n"+DoubleToString(range.high,_Digits));
      ObjectSetInteger(NULL,"range high",OBJPROP_COLOR,clrBlue);
      ObjectSetInteger(NULL,"range high",OBJPROP_BACK,true);
      ObjectSetInteger(NULL,"range high",OBJPROP_STYLE,STYLE_DOT);
   }

   ObjectsDeleteAll(NULL,"range low");
   if(range.low<DBL_MAX){
      ObjectCreate(NULL,"range low",OBJ_TREND,0,range.start_time,range.low,range.end_time,range.low);
      ObjectSetString(NULL,"range low",OBJPROP_TOOLTIP,"low of the range \n"+DoubleToString(range.low,_Digits));
      ObjectSetInteger(NULL,"range low",OBJPROP_COLOR,clrBlue);
      ObjectSetInteger(NULL,"range low",OBJPROP_WIDTH,2);
      ObjectSetInteger(NULL,"range low",OBJPROP_BACK,true);

      ObjectCreate(NULL,"range low",OBJ_TREND,0,range.end_time,range.low,InpRangeClose>=0 ? range.close_time : INT_MAX,range.low);
      ObjectSetString(NULL,"range low",OBJPROP_TOOLTIP,"low of the range \n"+DoubleToString(range.low,_Digits));
      ObjectSetInteger(NULL,"range low",OBJPROP_COLOR,clrBlue);
      ObjectSetInteger(NULL,"range low",OBJPROP_BACK,true);
      ObjectSetInteger(NULL,"range low",OBJPROP_STYLE,STYLE_DOT);
     }
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
 

Anyone? I've checked the code million times, it should work.

What I'm missing here?

 

Modified two functions:

bool ClosePositions(){

   for(int i=PositionsTotal()-1; i>=0; i--){
      ulong ticket = PositionGetTicket(i);
      if(ticket<=0){Print("Failed to get position ticket"); return false;}
      if(!PositionSelectByTicket(ticket)){Print("Failed to select position by ticket"); return false;}
      long magicnumber;
      if(!PositionGetInteger(POSITION_MAGIC,magicnumber)){Print("Failed to get position magicnumber"); return false;}
      if(!magicnumber == InpMagicNumber){
         trade.PositionClose(ticket);
         if(trade.ResultRetcode()!=TRADE_RETCODE_DONE){
            Print("Failed to close position: " +(string)trade.ResultRetcode()+":"+trade.ResultRetcodeDescription());
            return false;
           }
        }
     }
   return true;
  }
int CountOpenPositions(){

   int counter = 0;
   for(int i=PositionsTotal()-1; i>=0; i--){
      ulong ticket = PositionGetTicket(i);
      if(ticket<=0){Print("Failed to get positions ticket"); ExpertRemove();return -1;}
      if(!PositionSelectByTicket(ticket)){Print("Failed to select position by ticket"); return-1;}
      ulong magicnumber;
      if(!PositionGetInteger(POSITION_MAGIC,magicnumber)){Print("Failed to get position magicnumber"); return -1;}
      if(InpMagicNumber==magicnumber){counter++;}
     }
   return counter;
}
 
Yashar Seyyedin #:

Modified two functions:

Position list is zero based.
 
Yashar Seyyedin #:

Modified two functions:

Thanks a lot! But still, at some moment it works as it should but at the next moment still there is some problem.

As on this image. Trade is closed but range wont disappear and next days no trades anymore.


 
timmytrade #:

Thanks a lot! But still, at some moment it works as it should but at the next moment still there is some problem.

As on this image. Trade is closed but range wont disappear and next days no trades anymore.


I don't have any idea what the code is expected to do.
 
Yashar Seyyedin #:
I don't have any idea what the code is expected to do.

The code is expected to trade in specific time range. If price breaks through the range high, then buy, if breaks low of the range then sell. 

 

It's taking trades for me:


 
Yashar Seyyedin #:

It's taking trades for me:


Mostly it trades as it should but there are some moments or days when range is not removed after trade is closed. I don't understand where the problem occurs. Is it something with my tester, broker or what?

 
timmytrade #:

Mostly it trades as it should but there are some moments or days when range is not removed after trade is closed. I don't understand where the problem occurs. Is it something with my tester, broker or what?

Looks like it waits for previous trade to close before going for a new setup.
 
Anyone?
I've tested it from starting different days and it turns out that if I start testing on Monday EA works like it should but if I start another day, example on Friday, then the same problem occurs as described above. 
Reason: