Dealing with new old time series functions from MQL4, added into MQL5 since MetaTrader 1860

29 June 2018, 16:41
Stanislav Korotky
0
138

MetaQuotes have at last decided to add MQL4-ish time series functions into MQL5 after years of users groaning. Indeed, MetaTrader 5 would normally have much better compatibility with MetaTrader 4 and provide these functions from very beginning. Yet it took almost 10 years for MQ to include these functions into MetaTrader 5 build 1860. Now we have native support for - iTime, iOpen, iHigh, iLow, iClose, iVolume, iBars, iBarShift, iLowest, iHighest in MQL5. There are 3 new functions as well - iRealVolume, iTickVolume, iSpread.

The news could be positive, if not a small nuance. Since its inception, MQL5 has a constantly growing code base and Market, where the similar time series functions were already implemented by many members of community, and these codes are now included in dependencies of millions of other MQL products. What does this mean?

In short, this new native feature breaks all these codes and requires their revision to make "compilable" again. Until you did this, you'll get the compiler error "override system function".

Here I'm providing a short example of how the revision can be done.

First we need to detect current MT5 build and define a special mark telling us if the native time series functions are available or not. In C++ we could do it automatically very simple:

#if __MQLBUILD__ > 1835

Unfortunately, MQL preprocessor does not support conditional if-statement. So we need to define the mark explicitly according to known build number:

#define MT4_ON_MT5_NATIVE true // use this line only if __MQL5BUILD__ > 1835, comment otherwise

Then if you have a code with, for example, replacement functions such as:

int iBarShift(string symbol, ENUM_TIMEFRAMES tf, datetime time)
{
  return(Bars(symbol, tf, time, D'2121.01.01') - 1);
}

long iVolume(string symbol, ENUM_TIMEFRAMES tf, int b)
{
  long result[1];
  return CopyTickVolume(symbol, tf, b, 1, result) > 0 ? result[0] : 0;
}

...

you can wrap them with the conditional preprocessing directive:

#ifndef MT4_ON_MT5_NATIVE

int iBarShift(string symbol, ENUM_TIMEFRAMES tf, datetime time)
{
  return(Bars(symbol, tf, time, D'2121.01.01') - 1);
}

long iVolume(string symbol, ENUM_TIMEFRAMES tf, int b)
{
  long result[1];
  return CopyTickVolume(symbol, tf, b, 1, result) > 0 ? result[0] : 0;
}

...

#endif

This will eliminate your custom function from code.

If you have all such function in a separate include file (*.mqh), you can simply eliminate the include directive using the same approach.

Or you can prefer to rename all your custom time series functions implicitly and continue using them, something like this:

// in front of the rest of code
#ifdef MT4_ON_MT5_NATIVE
#define iBarShift _iBarShift
#define iTime _iTime
#define iHigh _iHigh
#define iLow _iLow
#define iOpen _iOpen
#define iClose _iClose
#define iVolume _iVolume
...
#endif

This will actually define and use everywhere in your code the functions with underscore, not the native ones. This is especially important, if you think the native functions may produce inconsistent results or work slowly under some circumstances (at least, in comparison with your own implementation, which is already proved to work as you expect).

This looks simple, but affects enormous pile of codes and should be fixed manually.

If, in addition to the time series functions, you'd like to support MQL4-styled access to global arrays Time[], Open[], Close[], High[], Low[], Volume[], then I remind you one possible solutions, presented earlier in my blog in the context of porting MT4 indicators to MT5.

Basically, you can do:

#define DefineBroker(NAME,TYPE) \
class NAME##Broker \
{ \
  public: \
    TYPE operator[](int b) \
    { \
      return i##NAME(_Symbol, _Period, b); \
    } \
}; \
NAME##Broker NAME;

and then define all related arrays:

DefineBroker(Time, datetime);
DefineBroker(Open, double);
DefineBroker(High, double);
DefineBroker(Low, double);
DefineBroker(Close, double);
DefineBroker(Volume, long);

Every one is equivalent to the following code:

class OpenBroker
{
  public:
    double operator[](int b)
    {
      return iOpen(_Symbol, _Period, b);
    }
};
OpenBroker Open;

Now you can use the legacy notation Time[], Open[], Close[], High[], Low[], Volume[] in your codes as usual. If the approach is not clear to you, please find explanations in my MQL OOP blog mentioned above.



Share it with friends: