Confusing date functions auto updating local variables

To add comments, please log in or register
dweems
43
dweems  

I'm an experienced programmer of over 35 years, so the following is very odd to me. 

I'm trying to develop a function to be used in an expert adviser that would indicate when a new period is encountered.  For example, if the period of my chart is days (PERIOD_D1), I want the first tick when the time moves from one day to the next to be returned as true, otherwise false.  So in this simple case I'm simply saving DayOfYear() as a static int and comparing that value against the current DayOfYear().  But to my surprise, the static int value automatically resets to the current day and my code never sets it.  Here's the function:

bool isNewPeriod()
{
        static int previousDayOfYear = -1;
        
        if (previousDayOfYear == -1)
        {
                // NOTE: this code is never executed, even though it should!!!
                previousDayOfYear = DayOfYear();
                Print("************** First Period ****************");
                Print("New day: ", previousDayOfYear);
                return true;
        }
        
        int dayOfYear = DayOfYear();
        
        switch (Period())
        {
                case PERIOD_D1:
                {
                        Print("Current day: ", dayOfYear, "    Previous day: ", previousDayOfYear);
                        if (previousDayOfYear != dayOfYear)
                        {
                                Print("####### New Period ######  Current day: ", dayOfYear, "    Previous day: ", previousDayOfYear);
                                previousDayOfYear = dayOfYear;
                                return true;
                        }
                        break;
                }
        }
        
        return false;
}


When I execute this function within OnTick(), I never see the print statement within either if-statement.  But the value of previousDayOfYear gets updated.  How's that possible if it never enters either if-statement where the value gets set?


So I decided to try another tactic.  Instead of using a standard function with a static variable holding the previous value, I created a class.  Here's that code:


class Periods
{
public:
        Periods();
        bool isNew();
        
private:
        int m_prevDayOfYear;
};

Periods::Periods()
{
        m_prevDayOfYear = DayOfYear();
        Print("First day of year: ", m_prevDayOfYear);
}

bool Periods::isNew()
{
        int dayOfYear = DayOfYear();
        
        Print("Previous day: ", m_prevDayOfYear, "    Current day: ", dayOfYear);
        
        if (dayOfYear != m_prevDayOfYear)
        {
                Print ("**** New Period ****");
                m_prevDayOfYear = dayOfYear;
                return true;
        }
        
        return false;
}


Same problem!  The constructor is never executed as I never see that Print statement within the Strategy Tester journal.  And I never see "**** New Period ****" printed either.  But the value of m_prevDayOfYear is updated.  That's not logical!


Two things:

  1. What's going here such that an integer value is automatically being updated when set to a datetime function?  It's a simple integer, not a function pointer.  The values of the previous day of years values should not be changing without my explicit execution of DayOfYear().  (PS - I also tried using the MqlDateTime struct with TimeCurrent() in TimeToStruct() and got the same crazy behavior.)
  2. If this cannot be avoided, how can I program this to do what I want?  My full intention is to be able to see a period change of any time (D1, H4, H1, M30, M15, M5 or M1).  This is critical for my expert advisor to know when to perform data analyses.  Is there a function that does the same thing I want I haven't found yet?


Please help!

lippmaje
1039
lippmaje  
The code looks ok, I use the same logic (static int) and it works. Maybe you don't get the print output. In optimization mode the tester won't print logs.
nicholi shen
2392
nicholi shen  

There's an easier way... 

Instead, compare the time of bar zero to a static var. When the values are not equal then you have a new bar. This works on all timeframes. 

bool is_new_bar()
{
   static datetime last_time = WRONG_VALUE;
   datetime curr_time = iTime(_Symbol, _Period, 0);
   if (curr_time != last_time) {
      bool res = (last_time != WRONG_VALUE);
      last_time = curr_time;
      return res;
   }
   return false;
}
dweems
43
dweems  
nicholi shen:

There's an easier way... 

Instead, compare the time of bar zero to a static var. When the values are not equal then you have a new bar. This works on all timeframes. 

Thanks Nicholi,

I still don't know why my code doesn't work but yours does and that at least solves my primary issue.
dweems
43
dweems  
lippmaje:
The code looks ok, I use the same logic (static int) and it works. Maybe you don't get the print output. In optimization mode the tester won't print logs.
Looks like you were on the right track.  I'm not using optimization but I tried commenting out all of the print statements and only wrote a print statement when the function/method returned true.  My code was working all along but for reasons I can't explain the print statements I needed to see never appeared.  But by removing most of them, now the one I need does appear.  Thanks for your review.
Marcin Madrzak
471
Marcin Madrzak  
dweems:
Looks like you were on the right track.  I'm not using optimization but I tried commenting out all of the print statements and only wrote a print statement when the function/method returned true.  My code was working all along but for reasons I can't explain the print statements I needed to see never appeared.  But by removing most of them, now the one I need does appear.  Thanks for your review.

The likely reason is you tried to call your isNewPeriod() function more than once per tick, but it only returns true first time it's called on new bar. If you need the result more than once in OnTick use a variable like bool newBar = isNewPeriod (); throughout OnTick instead of calling the function directly. 

Marcin Madrzak
471
Marcin Madrzak  
nicholi shen:

There's an easier way... 

Instead, compare the time of bar zero to a static var. When the values are not equal then you have a new bar. This works on all timeframes. 

I have run into problem some time ago when I needed similar function to work properly on offline timeframe where some bars can have identical Time value. I made a workaround, imperfect because based on Bars which can change it's value in between. Here is my code. Maybe someone will have a better idea how to solve such problem.

bool IsNewBarCurrentTF()
  {
   static datetime Trend_Candle_prevTime1=-1;
   int barcounter=0;
   static int barcounterprev=0;

   if(Trend_Candle_prevTime1!=Time[0])
     {
      Trend_Candle_prevTime1=Time[0];

      barcounter=0;
      barcounterprev=0;

      return(true);
     }
   else if(Time[0]==Time[1])
     {
      for(int i=0;i<Bars;i++)
        {
         if(Time[i]==Time[i+1]) barcounter++;
         else break;
        }
     }
   if(barcounter!=barcounterprev)
     {
      barcounterprev=barcounter;
      return(true);
     }
   return(false);
  }
Mohamad Zulhairi Baba
16429
Mohamad Zulhairi Baba  
bool NewBar(ENUM_TIMEFRAMES timeframe){
   datetime ArrayTime[];
   static datetime LastTime = 0;
   bool firstRun = false, newBar = false;
   ArraySetAsSeries(ArrayTime,true);
        CopyTime(_Symbol,timeframe,0,2,ArrayTime);
        
        if(LastTime == 0) firstRun = true;
        if(ArrayTime[0] > LastTime){
                if(firstRun == false) newBar = true;
                LastTime = ArrayTime[0];
        }
        return newBar;   
}
void OnTick(){
   if(NewBar(PERIOD_H1)) {
      // your code
   }
}
To add comments, please log in or register