Custom indicator duplicating values only on timeframe change.

 

Hello everyone!

I made an indicator as a request for MT5 that works fine. And now I want to have it also on MT4.

So I did it, and it works well too. When plotted on the chart or when parameters are changed, the indi "refreshes" very well. BUT, sometimes, when the timeframe period is changed (works only on M1, M5 or M15), the lines and the counter value duplicates, as if the bars loop was executed twice after initialization (I really see that happens only when uninit reason is 5). And as it doesn't always happen, I'm having a hard time figuring out what's causing this...

I tried to make the small code as possible where the problem occurs to post here, so here it goes ... I'm using build 1260 by the way.

#property indicator_chart_window
#property indicator_buffers 1
#property indicator_type1 DRAW_ARROW
#property indicator_width1 1
#property indicator_color1 0x0000FF
#property indicator_label1 "Put"
double Buffer1[];

input int VcandlesHist = 80;
double myPoint, Price;
int ObjCount = 0;
int TotalWins = 0;
bool TeveEntrada = false;
bool Vrun = true;
int Minutos;
datetime Tmcount = 0;
//_________________________________________________________________________

int OnInit()
  { CounterCreate();

   IndicatorBuffers(1);
   SetIndexBuffer(0, Buffer1);
   SetIndexArrow(0, 242);
   //ArrayInitialize(Buffer1, EMPTY_VALUE);

   myPoint = Point();
   if(Digits() == 5 || Digits() == 3)
     {
      myPoint *= 10;
     }

   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {  ObjectsDeleteAll(); }
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const datetime& time[],
                const double& open[],  const double& high[],      const double& low[],
                const double& close[], const long& tick_volume[], const long& volume[],
                const int& spread[])
  {
   int limit = rates_total - prev_calculated;
   
   ArraySetAsSeries(Buffer1, true); 
    
   if(prev_calculated <1)
      ArrayInitialize(Buffer1, EMPTY_VALUE);
   else
      limit++;
      
      
   if(limit > VcandlesHist)  limit = VcandlesHist;

   for(int i = limit-1; i >= 0; i--)
     {
      Minutos=TimeMinute(Time[i]);

      if(Tmcount != Time[i])
        {
         Tmcount= Time[i];

         if(TeveEntrada)
           {
            Vrun = true;
            if(Price > Close[1+i])
              {
               GreenLine(1+i);
               TotalWins++;
              }
            ObjectSetString(0,"lblWins",OBJPROP_TEXT,"Wins: " + IntegerToString(TotalWins));
            TeveEntrada = false;
           }

         if(Vrun)
           {
            if(Period() == PERIOD_M1)
              {
               if((MathMod(Minutos,5)==0))//------------M1
                 {
                  VerificaEntrada(i);
                 }
              }
            else if(Period() == PERIOD_M5)
                 {
                  if((Minutos == 0) || (Minutos == 30))//--M5
                    {
                     VerificaEntrada(i);
                    }
                 }
            else if(Period() == PERIOD_M15)
                 {
                  if(Minutos == 0) //---------------------M15
                    {
                     VerificaEntrada(i);
                    }
                 }
           }
        }
     }
   return(rates_total);
  }
//+------------------------------------------------------------------+
void VerificaEntrada(int Vshift)
  {
   Buffer1[Vshift] = High[Vshift] + 2 * myPoint;
   Vrun = false;
   Price = Close[1+Vshift];
   TeveEntrada = true;
  }
//+------------------------------------------------------------------+
void GreenLine(int lineShift)
  {
   ObjCount += 1;
   string objName = "Win_" + IntegerToString(ObjCount);
   if(!ObjectCreate(0,objName, OBJ_VLINE, 0, Time[lineShift], 0))
     {Print(" !! draw error: ", GetLastError());}
   ObjectSetInteger(0, objName, OBJPROP_COLOR, clrGreen);
   ObjectSetInteger(0, objName, OBJPROP_BACK, true);
   ObjectSetInteger(0, objName, OBJPROP_WIDTH, 1);
  }
//+------------------------------------------------------------------+
void CounterCreate()
  {
   ObjectCreate(0,"lblWins",OBJ_LABEL,0,0,0);
   ObjectSetInteger(0,"lblWins",OBJPROP_XDISTANCE,5);
   ObjectSetInteger(0,"lblWins",OBJPROP_YDISTANCE,5);
   ObjectSetInteger(0,"lblWins",OBJPROP_CORNER,CORNER_RIGHT_UPPER);
   ObjectSetString(0,"lblWins",OBJPROP_TEXT,"Wins: --");
   ObjectSetInteger(0,"lblWins",OBJPROP_FONTSIZE,14);
   ObjectSetInteger(0,"lblWins",OBJPROP_ANCHOR,ANCHOR_RIGHT_UPPER);
   ObjectSetInteger(0,"lblWins",OBJPROP_COLOR,clrCyan);
   ObjectSetInteger(0,"lblWins",OBJPROP_BACK,false);
   ObjectSetInteger(0,"lblWins",OBJPROP_SELECTABLE,false);
  }
//+------------------------------------------------------------------+

Also, because it's for a probabilistic strategy, the user requires that he can input how many bars to load on the chart.

Please, if it's wrong and someone can show me the right way to do this, I'll be very grateful, thanks!!

Documentation on MQL5: Constants, Enumerations and Structures / Named Constants / Other Constants
Documentation on MQL5: Constants, Enumerations and Structures / Named Constants / Other Constants
  • www.mql5.com
The CLR_NONE constant is used to outline the absence of color, it means that the graphical object or graphical series of an indicator will not be plotted. This constant was not included into the Web-color constants list, but it can be applied everywhere where the color arguments are required. The EMPTY_VALUE constant usually corresponds...
 
Fábio Albano:

Hello everyone!

I made an indicator as a request for MT5 that works fine. And now I want to have it also on MT4.

So I did it, and it works well too. When plotted on the chart or when parameters are changed, the indi "refreshes" very well. BUT, sometimes, when the timeframe period is changed (works only on M1, M5 or M15), the lines and the counter value duplicates, as if the bars loop was executed twice after initialization (I really see that happens only when uninit reason is 5). And as it doesn't always happen, I'm having a hard time figuring out what's causing this...

I tried to make the small code as possible where the problem occurs to post here, so here it goes ... I'm using build 1260 by the way.

Also, because it's for a probabilistic strategy, the user requires that he can input how many bars to load on the chart.

Please, if it's wrong and someone can show me the right way to do this, I'll be very grateful, thanks!!

You can start by moving the initialization of all your global variables (those that will be changed during execution) into OnInit()... because changing of timeframe does not reset those variables.

 
Seng Joo Thio:

You can start by moving the initialization of all your global variables (those that will be changed during execution) into OnInit()... because changing of timeframe does not reset those variables.

It does with an indicator.

 
Keith Watford:

It does with an indicator.

Ah... if not that, then adding this line in OnInit() will do the trick :

   IndicatorBuffers(1);
   SetIndexBuffer(0, Buffer1);
   SetIndexArrow(0, 242);
   ArrayInitialize(Buffer1, EMPTY_VALUE);
 
Seng Joo Thio:

Ah... if not that, then adding this line in OnInit() will do the trick :

I always initialize my buffers in OnCalculate() so that the buffers are cleared if the chart is updated (eg after a disconnection from the server)

   if(prev_calculated==0)
      ArrayInitialize(Buffer1, EMPTY_VALUE);
 
Keith Watford:

I always initialize my buffers in OnCalculate() so that the buffers are cleared if the chart is updated (eg after a disconnection from the server)

Yes, that'll be better.
Actually, I suspect that this call to initialize buffer (whether in OnInit() or OnCalculate()) is only necessary when:
(1) not all bars are looped at start, or
(2) where no value is necessary, neither a zero nor EMPTY_VALUE is assigned to Buffer[i].
That's why most indicators don't need to call ArrayInitialize().
 

Thanks for the answers guys... I actually had ArrayInitialize on my code,I removed to post here because I think it was not necessary. Anyways, neither on OnInit or OnCalculate works, so the problem stills. I noticed that when happens, it initialize right for a brief of a second, then the values and Vlines duplicates.

I start to think it's a bug. I find some similar issues on old posts, but it seems there was a problem that was fixed in next builds. I updated the code.

 
Fábio Albano:

Thanks for the answers guys... I actually had ArrayInitialize on my code,I removed to post here because I think it was not necessary. Anyways, neither on OnInit or OnCalculate works, so the problem stills. I noticed that when happens, it initialize right for a brief of a second, then the values and Vlines duplicates.

I start to think it's a bug. I find some similar issues on old posts, but it seems there was a problem that was fixed in next builds. I updated the code.

Yes it's a bug but in your code.

So I did it, and it works well too. When plotted on the chart or when parameters are changed, the indi "refreshes" very well. BUT, sometimes, when the timeframe period is changed (works only on M1, M5 or M15), the lines and the counter value duplicates, as if the bars loop was executed twice after initialization (I really see that happens only when uninit reason is 5). And as it doesn't always happen, I'm having a hard time figuring out what's causing this...

This is exactly what is happening, OnCalculate() CAN be called more then once after the initialization, it's normal behaviour and you have to deal with it.

Send and Keith, give you both a part of the solution.

Seng Joo Thio:

You can start by moving the initialization of all your global variables (those that will be changed during execution) into OnInit()... because changing of timeframe does not reset those variables.

Keith Watford:

I always initialize my buffers in OnCalculate() so that the buffers are cleared if the chart is updated (eg after a disconnection from the server)

Seng post was not exact, but he was right saying you need to take care of the initialization. Keith is right to initialize the buffer when prev_calculated is 0.

You need to initialize ALL your global variables (well if they are related to your indicator calculation of course) in OnCalculate(), when prev_calculated is 0.

Like :

  if(prev_calculated==0)
   {
    ArrayInitialize(Buffer1, EMPTY_VALUE);
    TotalWins=0;
    // Up to you to add the other globals needing to be initialized...
   }
 
Alain Verleyen:

Yes it's a bug but in your code.

This is exactly what is happening, OnCalculate() CAN be called more then once after the initialization, it's normal behaviour and you have to deal with it.

Send and Keith, give you both a part of the solution.

Seng post was not exact, but he was right saying you need to take care of the initialization. Keith is right to initialize the buffer when prev_calculated is 0.

You need to initialize ALL your global variables (well if they are related to your indicator calculation of course) in OnCalculate(), when prev_calculated is 0.

Like :

YES! It works!

  if(prev_calculated<1)
   {
    ArrayInitialize(Buffer1, EMPTY_VALUE);
    TotalWins=0;
    Price = 0;
    ObjCount = 0;
    TotalWins = 0;
    TeveEntrada = false;
    Vrun = true;
    Tmcount = 0;
   }    
  else
    limit++;

I tried Seng solution to initialize the variables in OnInit, as it desn't work I didn't think to try on OnCalculate. I thought that when changed timeframes, it would start on OnInit. Well, learned.

Thank you all very much!

 
Alain Verleyen:

Yes it's a bug but in your code.

This is exactly what is happening, OnCalculate() CAN be called more then once after the initialization, it's normal behaviour and you have to deal with it.

Send and Keith, give you both a part of the solution.

Seng post was not exact, but he was right saying you need to take care of the initialization. Keith is right to initialize the buffer when prev_calculated is 0.

You need to initialize ALL your global variables (well if they are related to your indicator calculation of course) in OnCalculate(), when prev_calculated is 0.

Like :

Now I recall... I hit the same issue, and solved it your way before... then I forgot the specifics... 

But I've always thought it wasn't a bug... it was caused by the staggered loading (intentional, perhaps, for start-up speed, or it genuinely had to retrieve more data from server) of chart data - like, while OnCalculate() starts being called, more data gets loaded, hence prev_calculated becomes 0...

 
Seng Joo Thio:

Now I recall... I hit the same issue, and solved it your way before... then I forgot the specifics... 

No worries, it happens to me too to solve the same issue twice ;-)

But I've always thought it wasn't a bug... it was caused by the staggered loading (intentional, perhaps, for start-up speed, or it genuinely had to retrieve more data from server) of chart data - like, while OnCalculate() starts being called, more data gets loaded, hence prev_calculated becomes 0...

Yes there are several triggers for OnCalculate() to be called with prev_calculated=0 ( I know at least 3, but there are maybe more).