Download MetaTrader 5

The "New Bar" Event Handler

11 October 2010, 15:26
Konstantin Gruzdev
4
12 847

Introduction

Authors of indicators and experts have always been interested in writing the compact code in terms of execution time. You can approach to this problem from different angles. From this broad topic in this article we will cover the problem, that is seemingly already have been solved: check for a new bar. This is quite a popular way to limit the calculation loops, since all the calculations and trading operations are carried out once during the generation of a new bar on the chart. So, what will be discussed:  

  • Ways of detecting new bars.
  • Shortcomings of existing algorithms of new bar detection.
  • Creating universal method of new bar detection.
  • Subtleties and ways of applying this method.
  • NewBar event and handler of this event - OnNewBar().

Ways of Detecting New Bars

Now, there are some acceptable solution on how to detect a new bar. For example, they can be found in the Limitations and Verifications in Expert Advisors, The Principles of Economic Calculation of Indicators articles or here. By the way, I recommend to study these materials. It will be easier to understand what I'm talking about.  

These materials use the principle of tracking opening time of currently unfinished bar. This is a very easy and reliable way. There are other methods of detecting a new bar.

For example, in custom indicators for this purpose you can use two input parameters of the OnCalculate() function: rates_total and prev_calculated. Limitation of this method - is basically the fact, that it can be used only to detect a new bar on the current chart and only in indicators. If you want to find a new bar on another period or symbol, it is necessary to use additional techniques.

Or, for example, you can try to catch a new bar on its first tick, when Tick Volume = 1, or when all bar prices are equal: Open = High = Low = Close. These methods may be well used for testing, but in real trading they often glitch. This is due to the moment between the first and second tick sometimes is not enough to catch the generated bar. This is especially noticeable on a strong market movement or when quality of Internet connection is poor.  

There is a way to detect a new bar based on the TimeCurrent() function. By the way, it is a good way, if you need to detect a new bar for the current chart. We will use it in the end of this article.

Well, you can even ask a neighbor: "Hey, is there a new bar?". I wonder what he will answer? Well, okay, let's stop your choice on the principle of tracking time of opening current unfinished bar to detect a new one. Its simplicity and reliability are truly tried-and-true. 

Starting Point

In the above mentioned materials things aren't bad with detection of a new bar. But...  

To understand what this "but" is, as a starting point (or a prototype) we will take simple and good working function to detect a new bar from the Limitations and Verifications in Expert Advisors article. Here it is:

//+------------------------------------------------------------------+
//| Returns true if a new bar has appeared for a symbol/period pair  |
//+------------------------------------------------------------------+
bool isNewBar()
  {
//--- memorize the time of opening of the last bar in the static variable
   static datetime last_time=0;
//--- current time
   datetime lastbar_time=SeriesInfoInteger(Symbol(),Period(),SERIES_LASTBAR_DATE);

//--- if it is the first call of the function
   if(last_time==0)
     {
      //--- set the time and exit
      last_time=lastbar_time;
      return(false);
     }

//--- if the time differs
   if(last_time!=lastbar_time)
     {
      //--- memorize the time and return true
      last_time=lastbar_time;
      return(true);
     }
//--- if we passed to this line, then the bar is not new; return false
   return(false);
  }

This prototype function is actually working and has the full right to life. But... 

Analysis of Prototype Function

I copied this function into source code of my (of course) greatest and best of the best Expert Advisor. Did't work. I began to investigate. Below are my thoughts on this function.

Function header. Let's take look at everything consequently. Let's start with the function header:

bool isNewBar()

I like the function header, it is very simple, intuitive, and no need to deal with passed in parameters. It would be nice to use it in this form in the future.

Number of calls restriction. Following the header is the first statement that initializes the static variable:

//---  memorize the time of opening of the last bar in the static variable
   static datetime last_time=0;

Everything seems pretty well. But...

Problem is, that we use static variable. Help topics tell us: Help topics tells us:

Static variables exist from the moment of program execution and are initialized only once before the specialized OnInit() function is called. If the initial values are not specified, variables of the static storage class are taking zero initial values. 

Local variables, declared with the static keyword retain their values throughout the function lifetime. With each next function call, such local variables contain the values that they had during the previous call.

If you call this prototype function from one place, then we have what we need. But if we want to use this function, for example, again in another place in the same calculation loop, it will always return false, which means that there is no bar. And this will not always be true. Static variable in this case imposes an artificial limit on the number of prototype function calls.

Question of universality. The following statement in the prototype function looks like this:

//--- current time
   datetime lastbar_time=SeriesInfoInteger(Symbol(),Period(),SERIES_LASTBAR_DATE);

It is logical, that to get the opening time of the latest unfinished bar the SeriesInfoInteger() function is used with the SERIES_LASTBAR_DATE modifier.

Our prototype function isNewBar() was originally conceived as a simple and by default it uses the trading instrument and period of current chart. This is acceptable if you want to track new bar only on the current chart. But what to do, if I use period and instrument not only for the current chart? Furthermore, what if I have some intricate chart? For example, what if I decided to plot Renko or Kagi?

Lack of can severely limit us. Later, we will cover how to fix it.  

Error handling. Let's take a look at the SeriesInfoInteger() function. What do you think it would return, if it will run when the chart hasn't been formed yet? Such a situation may arise, for example, if you have attached your Expert Advisor or indicator to a chart and decided to change the period or symbol, or when you restart the terminal. And what will happen during the timeseries update? Incidentally, the is such a warning in Help topics:

Data Availability

Presence of data in HCC format or even in ready-to-use HC format does not always denote the absolute availability of these data to be displayed on a chart or to use in MQL5 programs.

When accessing the price data or indicator values from a MQL5 program, remember that their availability in a certain moment of time or starting from a certain moment of time is not guaranteed. This is due to the fact that in order to save system resources, the full copy of data necessary for a mql5 program isn't stored in MetaTrader 5; only direct access to the terminal data base is given.

The price history for all timeframes is built from common data of HCC format, and any update of data from a server leads to the update of data for all timeframes and to the recalculation of indicators. Due to this, access to data can be denied, even if these data were available a moment ago.

So, what this function will return? To avoid this uncertainty, you need somehow to begin catching query errors of opening time of the last unfinished bar.  

Possibility of initialization. Let's move on. Consider the following statements of our prototype function:

//--- if it is the first call of the function
   if(last_time==0)
     {
      //--- set the time and exit
      last_time=lastbar_time;
      return(false);
     }

Here everything is definitely fine. However, there is a nuance. Did you notice the above sentence from Help: "Static variables exist from the moment of program execution and are initialized only once before the specialized OnInit() function"? And what if we need more time to initialize the last_time variable? More precisely, what to do if you want to artificially create a situation of the first call? Or some other situation? It is easy to ask questions when you know the answers. But more on that later.

Number of bars. Next our prototype function will have the following code:

//--- if the time differs
   if(last_time!=lastbar_time)
     {
      //--- memorize the time and return true
      last_time=lastbar_time;
      return(true);
     }

You see, a programmer like me can do so that the if operator will "surprise" the client terminal and Strategy Tester. The fact is that, logically, the past time is always less than the present. That is last_time < lastbar_time. Due to accidental program error I've got the time machine, or more exactly - the opposite happened: lastbar_time < last_time. What a surprise! In general, such a time paradox is easy to detect and to display an error message.

But every cloud has a silver lining. While watching at my "time machine", I've found that among the isNewBar() calls not only one new bar can appear. The smaller the chart period, the higher is the probability of several bars occurrence between function calls. There can be many reasons of this: beginning from the long computation time and ending with a temporary lack of connection with server. Opportunity to not only receive the signal of a new bar, but also the number of bars, will certainly be useful.

Our prototype function ends like this:

//--- if we passed to this line, then the bar is not new; return false
   return(false);

Yes, if we've passed to this line - the bar is not new.

Creating new isNewBar() function 

Here begins an interesting thing. We will solve the detected weaknesses. You know, I was a bit overmodest, calling the section as "Creating new isNewBar() function". We'll do something more solid.

We will begin with getting rid of restrictions on the number of function calls.

Firth thing, that comes to mind, is that you can use functions with the same name as isNewBar() from The Principles of Economic Calculation of Indicators article or from here isNewBar. That is, to include arrays storing multiple last_time values into function body, put counters of the isNewBar() function calls from different places, and so on. Of course, these are all working versions and they can be implemented. But imagine, if we are writing a multi-currency Expert Advisor to work on 12 currency pairs. There will be so many necessary nuances to consider and not to get confused?

What should we do? The answer is here!

The beauty of Object Oriented Programming is that an object or an instance of some class can "live their own lives" independently of other instances of the same class. So let's start to create a class CisNewBar, so we will be able to produce instances of this class in any place of our Expert Advisor or Indicator any number of times. And let every instance to "live its own life".

That's what we have to start:

class CisNewBar 
  {
   protected:
      datetime          m_lastbar_time;   // Time of opening last bar
      
   public:
      void              CisNewBar();      // CisNewBar constructor
      //--- Methods of detecting new bars:
      bool              isNewBar();       // First type of request for new bar
  };  

bool CisNewBar::isNewBar()
  {
   //--- here is the definition of static variable

   //--- here will be the rest of method's code   
   ...

   //--- if we've reached this line, then the bar is not new; return false
   return(false);
  }

What was the isNewBar() function, is now the method. Note that now there is no static variable last_time - instead we now have protected class variable m_lastbar_time. If we had left static variable in the isNewBar() method, then all our efforts would go awry, as we would face the same problems as before with the isNewBar() function - these are features of static variables.

And now the time of the last bar will be stored in the m_lastbar_time protected variable of class, and in each class instance the memory will be allocated for this variable. Thus we were able to remove the restriction on the number of calls, which was in the prototype function. We can call the isNewBar() method in different places in our MQL-program as many times as we want, creating class instance for each place.

This is something that we've succeeded in. Now let's work on the universality. Before adding something to our new class, I'd like to lead you to one amusing idea.

Let's reason. What do we want? We want to get signal about new bar. How do we want to do this? So, if opening time of current unfinished bar on the last tick (or at the last moment) is more than opening time of current unfinished bar on the previous tick (or at the previous moment), then new bar is formed. Complicated phrase, but its correct. The bottom line is that we need to compare the time. Therefore, I decided that it would be logical to pass the opening time of current unfinished bar newbar_time into the isNewBar() method. Then the method header will be as follows:

bool isNewBar(datetime newbar_time)

Don't ask yet where we'll take the newbar_time - assume that it is already known. We'll look into this later.  

By the way, passing the time into the isNewBar() method, we obtain a very flexible tool for detecting a new bar. We will be able to cover all standard chart periods with all kinds of trading tools. It happened so that now we don't depend on the symbol name and the size of the period.  

We can also use non-standard charts. For example, if you are plotting tick candlesticks, or Renko or Kagi charts, their time of bar opening practically never coincides with time of standard chart periods. In this case, our function will be indispensable.

Well, now it's OK with the versatility. Let us supplement our CisNewBar class in accordance with our idea:

class CisNewBar 
  {
   protected:
      datetime          m_lastbar_time;   // Time of opening last bar
      uint              m_retcode;        // Result code of detecting new bar
      int               m_new_bars;       // Number of new bars
      string            m_comment;        // Comment of execution
      
   public:
      void              CisNewBar();      // CisNewBar constructor
      //--- Methods of detecting new bars:
      bool              isNewBar(datetime new_Time); // First type of request for new bar
  };
   
//+------------------------------------------------------------------+
//| First type of request for new bar                     |
//| INPUT:  newbar_time - time of opening (hypothetically) new bar   |
//| OUTPUT: true   - if new bar(s) has(ve) appeared                  |
//|         false  - if there is no new bar or in case of error      |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
bool CisNewBar::isNewBar(datetime newbar_time)
  {
   //--- Initialization of protected variables
   m_new_bars = 0;      // Number of new bars
   m_retcode  = 0;      // Result code of detecting new bar: 0 - no error
   m_comment  =__FUNCTION__+" Successful check for new bar";
   //---
   
   //--- Just to be sure, check: is the time of (hypothetically) new bar m_newbar_time less than time of last bar m_lastbar_time? 
   if(m_lastbar_time>newbar_time)
     { // If new bar is older than last bar, print error message
      m_comment=__FUNCTION__+" Synchronization error: time of previous bar "+TimeToString(m_lastbar_time)+
                                                  ", time of new bar request "+TimeToString(newbar_time);
      m_retcode=-1;     // Result code of detecting new bar: return -1 - synchronization error
      return(false);
     }
   //---
        
   //--- if it's the first call
   if(m_lastbar_time==0)
     {  
      m_lastbar_time=newbar_time; //--- set time of last bar and exit
      m_comment   =__FUNCTION__+" Initialization of lastbar_time = "+TimeToString(m_lastbar_time);
      return(false);
     }   
   //---

   //--- Check for new bar:
   if(m_lastbar_time<newbar_time)       
     { 
      m_new_bars=1;               // Number of new bars
      m_lastbar_time=newbar_time; // remember time of last bar
      return(true);
     }
   //---
   
   //--- if we've reached this line, then the bar is not new; return false
   return(false);
  }

Looking at the source code for our class, you've probably noticed that we have taken into account tracking of runtime errors, and we've introduced variable storing the number of new bars.

All is good, but our universal method isNewBar(datetime newbar_time) contains one major inconvenience. This inconvenience is that we always have to worry about calculating the time of (hypothetically) newbar_time in the source code of our expert or indicator.  

Fortunately, in some cases, we can simplify your life, entrusting this function to the new additional method of our class. For standard periods and symbols in our prototype function this can be done using the second version of the SeriesInfoInteger() function with the SERIES_LASTBAR_DATE modifier, and in all other cases - using generic method. So, here is what I've got:

//+------------------------------------------------------------------+
//| Second type of request for new bar                     |
//| INPUT:  no.                                                      |
//| OUTPUT: m_new_bars - Number of new bars                          |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
int CisNewBar::isNewBar()
  {
   datetime newbar_time;
   datetime lastbar_time=m_lastbar_time;
      
   //--- Request time of opening last bar:
   ResetLastError(); // Set value of predefined variable _LastError as 0
   if(!SeriesInfoInteger(m_symbol,m_period,SERIES_LASTBAR_DATE,newbar_time))
     { // If request has failed, print error message:
      m_retcode=GetLastError();  // Result code of detecting new bar: write value of variable _LastError
      m_comment=__FUNCTION__+" Error when getting time of last bar opening: "+IntegerToString(m_retcode);
      return(0);
     }
   //---
   
   //---Next use first type of request for new bar, to complete analysis:
   if(!isNewBar(newbar_time)) return(0);
   
   //---Correct number of new bars:
   m_new_bars=Bars(m_symbol,m_period,lastbar_time,newbar_time)-1;
   
   //--- If we've reached this line - then there is(are) new bar(s), return their number:
   return(m_new_bars);
  }

So, what do we have at this moment? Now for standard periods we do not need to care about determining the opening time of last unfinished bar. We have approached to our prototype function with its simple call and without the shortcomings that it had. And even got additional advantages, including error codes, runtime comments and number of new bars.   

Is there something left? Yes. There is the last moment - the initialization. For this we'll use class constructor and several Set-methods. Our class constructor looks like this:  

//+------------------------------------------------------------------+
//| CisNewBar constructor.                                           |
//| INPUT:  no.                                                      |
//| OUTPUT: no.                                                      |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
void CisNewBar::CisNewBar()
  {
   m_retcode=0;         // Result code of detecting new bar
   m_lastbar_time=0;    // Time of opening last bar
   m_new_bars=0;        // Number of new bars
   m_comment="";        // Comment of execution
   m_symbol=Symbol();   // Symbol name, by default - symbol of current chart
   m_period=Period();   // Chart period, by default - period of current chart
  }

And Set-methods like this:

//--- Methods of initializing protected data:
void              SetLastBarTime(datetime lastbar_time){m_lastbar_time=lastbar_time;                            }
void              SetSymbol(string symbol)             {m_symbol=(symbol==NULL || symbol=="")?Symbol():symbol;  }
void              SetPeriod(ENUM_TIMEFRAMES period)    {m_period=(period==PERIOD_CURRENT)?Period():period;      }

Thanks to the class constructor, we don't need to pay attention to the initialization of symbol and period of current chart. As in prototype function they will be used by default. But if we need to use another symbol or chart period, then we can use for this our created Set-methods. In addition, using SetLastBarTime(datetime lastbar_time) you can recreate the situation of the "first call."

In conclusion, lets create several Get-methods to get data from our class in Expert Advisor and Indicators: 

      //--- Methods of access to protected data:
uint              GetRetCode()     const  {return(m_retcode);     }  // Result code of detecting new bar 
datetime          GetLastBarTime() const  {return(m_lastbar_time);}  // Time of opening last bar
int               GetNewBars()     const  {return(m_new_bars);    }  // Number of new bars
string            GetComment()     const  {return(m_comment);     }  // Comment of execution
string            GetSymbol()      const  {return(m_symbol);      }  // Symbol name
ENUM_TIMEFRAMES   GetPeriod()      const  {return(m_period);      }  // Chart period

Now we can get all the necessary information in our mql5-programs. For now we can put a full stop in creating the CisNewBar class.

The full source code for our class is in the Lib CisNewBar.mqh attached file.

Examples of CisNewBar Class Usage

I propose you to consider the examples of our class usage in order to get into all the subtleties of what we've created. Perhaps there can be not only advantages but also disadvantages.

Example 1. To start, let's create an absolutely identical Expert Advisor for the isNewBar() function from the Limitations and Verifications in Expert Advisors article:

//+------------------------------------------------------------------+
//|                                               Example1NewBar.mq5 |
//|                                            Copyright 2010, Lizar |
//|                                               Lizar-2010@mail.ru |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, Lizar"
#property link      "Lizar-2010@mail.ru"
#property version   "1.00"

#include <Lib CisNewBar.mqh>

CisNewBar current_chart; // instance of the CisNewBar class: current chart

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   if(current_chart.isNewBar()>0)
     {     
      PrintFormat("New bar: %s",TimeToString(TimeCurrent(),TIME_SECONDS));
     }
  }

Let's run both Expert Advisors on charts with the same pair and period. Let's see what we've got:


First, both Expert Advisors synchronously report about new bar. Then they fall silent and only four minutes later they inform that there is a new bar (this time is marked as 1). It's OK - I've just disconnected from Internet for a few minutes and decided to see what will happen. Despite the fact that few bars have been formed, we have not received this information. In our new Expert Advisor we can correct this disadvantage, since our isNewBar() method allows to do such a thing.

Next, I've changed chart period to M2. Expert Advisors' reaction was different. CheckLastBar began to report on a new bar every 2 minutes, and Example1NewBar tells about new bars every minute, as if the period hasn't been changed (marked as 2).

The fact that our current_chart instance has been initialized by class constructor when Expert Advisor was attached to chart. When you change the period of Expert Advisor, already attached to the chart, the class constructor does not start, and Expert Advisor continues to work with M1 period. This tells us that our class instance live its own life of and it's not affected by changes in environment. It can be both pro and con - all depends on the tasks.  

In order for our Expert Advisor to act as CheckLastBar, we need to initialize the m_symbol and m_period protected class variables in the OnInit() function. Let's do it.

Example 2. Let's introduce some additions to our Expert Advisor and again compare its performance against CheckLastBar. Expert Advisor's source code is attached as the Example2NewBar.mq5 file. Run Expert Advisors on charts with the same pair and period. Let's create the same obstacles for them as the last time. Let's see what we've got:


Like the last time, Expert Advisors first synchronously report about new bar. Then I disconnect from Internet for a few minutes... Then turn it on. Our new Expert Advisor not only reported on a new bar, but also how many of them appeared (marked as 1). For most indicators and experts, this number will mean the number of uncalculated bars. Thus, we have a good basis for cost-effective recalculation algorithms.  

Next, I've changed charts period to M2. Unlike Example 1, Expert Advisor work synchronously (marked as 2). Initialization of the m_symbol and m_period protected class variables in the OnInit() function has helped! When changing the symbol (marked as 3), Expert Advisors also work the same way. 

Example 3. In our CisNewBar class we have put the possibility to track errors. It may happen that Expert Advisor is designed so that there is no need to track errors. Well, then just don't use this possibility. We will try to artificially create a situation where the error is possible, and try to catch it. For this we will slightly supplement Expert Advisor's source code (the Example3NewBar.mq5 file).

What will I do? As usual I'll run Example3NewBar on minute charts. Then I'll begin to change chart's instruments in the hope that a situation will arise that terminal will not have time to build up timeseries before Expert Advisor's request. In general, I'll torment the client terminal and see what will happen...  

After several attempts, our Expert Advisor caught an error:

 

Now we can say with confidence that we are able to catch runtime errors. How to handle them - is a matter of taste. Note that we've tracked this error four times. When the download is completed and the chart is formed, the Expert Advisor suggested that we've missed only 1 bar.

By the way, those who looked the source code of Expert Advisor may have noticed that it makes sense to check for errors only when the isNewBar() method returns a value less than or equal to zero.

Warning: If during this experiment, you will begin to change the chart period, then when you change chart period from little to bigger, you'll get a synchronization error. This is because the bar opening time (for example) of H1 is earlier than M1 in 59 cases. To avoid this error when switching chart period, you need to properly initialize the m_lastbar_time variable in the OnInit() function with the SetLastBarTime (datetime lastbar_time) method.

Example 4. In this example let's complicate the task of Expert Advisor. Take three currency pairs: EURUSD on M1, GBPUSD on M1 and USDJPY on M2. Chart with the first pair will be current, and on it we'll just watch for a new bar. By the second pair, we will calculate the number of bars formed after the start of Expert Advisor. We will count until the first pair will signal that there is a new bar. And on the third pair we will constantly (when a bar appear on the EURUSD) carry out the initialization of the m_lastbar_time protected class variable. The Expert Advisor's source code is attached as the Example4NewBar.mq5 file.

By creating this example, I want to find out how our class CisNewBar will work in multi-currency mode. Well, I start it... Here's what I've got:


The results raise questions. I'll add fuel to the fire and run this very time interval in Strategy Tester. Strategy Tester results:


Then you can play the game "find ten differences". In addition to oddities of Expert Advisor's work on demo-account, it is obvious that there are differences between demo-account and Strategy Tester - and they clearly visible. A similar comparison with the right approach will not only reveal the disadvantages of Expert Advisor, but also allow to eliminate them. Perhaps, I'm not going to analyze why it happened, how it happened and what needs to be fixed in Expert Advisor.  

Example 5. In examples we have never explicitly used the most universal method of detecting a new bar - isNewBar(datetime newbar_time). To do this, I'll take tick candlestick from the Creating Tick Indicators in MQL5 article and add a buffer to store the bar opening time (file TickColorCandles v2.00.mq5). I will write very short Expert Advisor that will tell about the time of new teak candlestick (file Example5NewBar.mq5):

#property copyright "Copyright 2010, Lizar"
#property link      "Lizar-2010@mail.ru"
#property version   "1.00"

#include <Lib CisNewBar.mqh>

CisNewBar newbar_ind; // instance of the CisNewBar class: detect new tick candlestick
int HandleIndicator;  // indicator handle
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Get indicator handle:
   HandleIndicator=iCustom(_Symbol,_Period,"TickColorCandles v2.00",16,0,""); 
   if(HandleIndicator==INVALID_HANDLE)
     {
      Alert(" Error when creating indicator handle, error code: ",GetLastError());
      Print(" Incorrect initialization of Expert Advisor. Trade is not allowed.");
      return(1);
     }

//--- Attach indicator to chart:  
   if(!ChartIndicatorAdd(ChartID(),1,HandleIndicator))
     {
      Alert(" Error when attaching indicator to chart, error code: ",GetLastError());
      return(1);
     }
//--- If you passed until here, initialization was successful
   Print(" Successful initialization of Expert Advisor. Trade is allowed.");
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   double iTime[1];

//--- Get time of opening last unfinished tick candlestick:
   if(CopyBuffer(HandleIndicator,5,0,1,iTime)<=0)
     {
      Print(" Failed to get time value of indicator. "+
            "\nNext attempt to get indicator values will be made on the next tick.",GetLastError());
      return;
     }
//--- Detect the next tick candlestick:
   if(newbar_ind.isNewBar((datetime)iTime[0]))
     {
      PrintFormat("New bar. Opening time: %s  Time of last tick: %s",
                  TimeToString((datetime)iTime[0],TIME_SECONDS),
                  TimeToString(TimeCurrent(),TIME_SECONDS));
     }
  }

Surely you've noticed how we get the time of tick candlestick opening. Very easy, don't it? I put the indicator and Expert Advisor in their folders, compile and run Expert Advisor. It works, and here are the results:  

 

"New Bar" Event Handler


Approaching the end of this article, I would like to share another idea. On Forum (in Russian) there was an idea that it would be nice to have a standard "new bar" event handler. Perhaps once developers will come to this, but maybe not. But the beauty of MQL5 that it's possible to implement the most stunning ideas elegantly and simply.

If you want to have a "new bar" event handler (or NewBar) - so let's create it! Especially, as we can catch this event now with ease, using our class. That's how our expert (with NewBar event handler OnNewBar()) will look like:

#property copyright "Copyright 2010, Lizar"
#property link      "Lizar-2010@mail.ru"
#property version   "1.00"

#include "OnNewBar.mqh" // Here is the secret of launching the "new bar" event handler

//+------------------------------------------------------------------+
//| New bar event handler function                                   |
//+------------------------------------------------------------------+
void OnNewBar()
  {
   PrintFormat("New bar: %s",TimeToString(TimeCurrent(),TIME_SECONDS));
  }

Looks pretty good. Our Expert Advisor looks very simple. This handler prints string about new bar. That's all he does. To understand how to track the NewBar event and how to run handler, you need to look into the OnNewBar.mqh file:

#property copyright "Copyright 2010, Lizar"
#property link      "Lizar@mail.ru"

#include <Lib CisNewBar.mqh>
CisNewBar current_chart; // instance of the CisNewBar class: current chart

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   int period_seconds=PeriodSeconds(_Period);                     // Number of seconds in current chart period
   datetime new_time=TimeCurrent()/period_seconds*period_seconds; // Time of bar opening on current chart
   if(current_chart.isNewBar(new_time)) OnNewBar();               // When new bar appears - launch the NewBar event handler
  }

As you can see, here there is nothing complicated too. But there is a couple of moments on which I'd like to draw your attention:

First. As you've noticed, I use the TimeCurrent() function to calculate time of bar opening and use the first method of check for the NewBar event from our class. This is a nice plus. It lies in the fact that such method does not require any error processing, as when using the SeriesInfoInteger() with the SERIES_LASTBAR_DATE modifier. For us it is important, as our OnNewBar() handler should reliable as much as possible.

Second. Use of the TimeCurrent() function to calculate the time of bar opening is the fastest way. Use of the SeriesInfoInteger() function, even without error control, for the same purpose is a slower way.

The result of our handler:

   

Conclusion

  In the course of material presentation we've done a good analysis of ways to detect a new bar. We've exposed pros and cons of the existing methods of detecting new bar. Based on what we had, we've created the CisNewBar class, enabling without additional costs on programming to catch the "new bar" event in almost any tasks. At the same time we've got rid of most inconveniences from previous solutions.    

These examples helped us to understand the pros and cons of methods invented by us. Special attention in terms of correct work requires the multi-currency mode. You have to make a thorough analysis of the identified inefficiencies and to develop ways to solve them.

  The created "new bar" event handler is only suitable for single currency Expert Advisors. But we have learned to use the most reliable and fastest way for this purpose. Now you can go ahead and make a multi-currency NewBar event handler. But this is the topic of another article.  

Translated from Russian by MetaQuotes Software Corp.
Original article: https://www.mql5.com/ru/articles/159

Attached files |
example1newbar.mq5 (0.94 KB)
example2newbar.mq5 (2.61 KB)
example3newbar.mq5 (2.98 KB)
example4newbar.mq5 (6.41 KB)
example5newbar.mq5 (2.39 KB)
onnewbar.mq5 (0.8 KB)
onnewbar.mqh (1.12 KB)
Last comments | Go to discussion (4)
behzad jafari
behzad jafari | 12 Jan 2013 at 17:21

thnx
Alain Verleyen
Alain Verleyen | 26 Mar 2013 at 16:56

Very interesting article. Thank you for sharing all of this.

However, some remarks:

Talking about this function, you say :

If you call this prototype function from one place, then we have what we need. But if we want to use this function, for example, again in another place in the same calculation loop, it will always return false, which means that there is no bar. And this will not always be true. Static variable in this case imposes an artificial limit on the number of prototype function calls.

  • You are right. But calling several times a function like isnewbar() during 1 tick is bad practice. Call it only once and keep the result in a variable.
  • A problem is that the solution you provide, using a class without static variable, doesn't resolve what you have stressed. And even situation is now worst. You have to declare the instance of your class as a global variable (object), you have also to initialize it on the OnInit(). One who will use your class must know the operation:
  1. Always use a global variable
  2. Always initialize even if the symbol and period are the default (for time chart).
  • Finally, "if we want to use this function, for example, again in another place in the same calculation loop". Still it ALWAYS RETURN FALSE.

You have replaced a static local variable with a global variable, which is the same and you didn't resolve your problem.


Nevertheless, your article has the merit to push thinking and offer interesting ideas.

Jordi Bassaganas
Jordi Bassaganas | 23 Aug 2013 at 11:36

Good article, thanks for sharing! All that was very useful!

Anyway, I have taken your isNewBar function and it throws the following message when compiling: "possible loss of data due to type conversion".

So I have changed the var types from datetime to long this way:

//+------------------------------------------------------------------+
//| Returns true if a new bar has appeared for a symbol/period pair  |
//+------------------------------------------------------------------+
bool isNewBar()
  {
//--- memorize the time of opening of the last bar in the static variable
   static long last_time=0;
//--- current time
   long lastbar_time=SeriesInfoInteger(CurrencyPair,Period01,SERIES_LASTBAR_DATE);

//--- if it is the first call of the function
   if(last_time==0)
     {
      //--- set the time and exit
      last_time=lastbar_time;
      return(false);
     }

//--- if the time differs
   if(last_time!=lastbar_time)
     {
      //--- memorize the time and return true
      last_time=lastbar_time;
      return(true);
     }
//--- if we passed to this line, then the bar is not new; return false
   return(false);
  }

Now it compiles without any notice and seems to work ok. Thank you!


Alain Verleyen
Alain Verleyen | 23 Aug 2013 at 11:59
laplacianlab:

Good article, thanks for sharing! All that was very useful!

Anyway, I have taken your isNewBar function and it throws the following message when compiling: "possible loss of data due to type conversion".

So I have changed the var types from datetime to long this way:

Now it compiles without any notice and seems to work ok. Thank you!


It's probably clearer to change it this way :

   datetime lastbar_time=(datetime)SeriesInfoInteger(Symbol(),Period(),SERIES_LASTBAR_DATE);
Step on New Rails: Custom Indicators in MQL5 Step on New Rails: Custom Indicators in MQL5

I will not list all of the new possibilities and features of the new terminal and language. They are numerous, and some novelties are worth the discussion in a separate article. Also there is no code here, written with object-oriented programming, it is a too serous topic to be simply mentioned in a context as additional advantages for developers. In this article we will consider the indicators, their structure, drawing, types and their programming details, as compared to MQL4. I hope that this article will be useful both for beginners and experienced developers, maybe some of them will find something new.

Here Comes the New MetaTrader 5 and MQL5 Here Comes the New MetaTrader 5 and MQL5

This is just a brief review of MetaTrader 5. I can't describe all the system's new features for such a short time period - the testing started on 2009.09.09. This is a symbolical date, and I am sure it will be a lucky number. A few days have passed since I got the beta version of the MetaTrader 5 terminal and MQL5. I haven't managed to try all its features, but I am already impressed.

Using text files for storing input parameters of Expert Advisors, indicators and scripts Using text files for storing input parameters of Expert Advisors, indicators and scripts

The article describes the application of text files for storing dynamic objects, arrays and other variables used as properties of Expert Advisors, indicators and scripts. The files serve as a convenient addition to the functionality of standard tools offered by MQL languages.

How to create an indicator of non-standard charts for MetaTrader Market How to create an indicator of non-standard charts for MetaTrader Market

Through offline charts, programming in MQL4, and reasonable willingness, you can get a variety of chart types: "Point & Figure", "Renko", "Kagi", "Range bars", equivolume charts, etc. In this article, we will show how this can be achieved without using DLL, and therefore such "two-for-one" indicators can be published and purchased from the Market.