High-performance iTimeSeries for time-sensitive applications – library MetaTrader 5

One of the major issues with MQL5 was the removal of the built in time-series functions. While doing so gave programmers a more finite level of development latitude, it also slowed things down due to the necessary step of creating and destroying new memory locations every-time one needs to access some time-series data.

...
   if(CopyTime(symbol,timeframe,time,1,checkcandle)==1)
     {
...

Let’s look at a popular iBarShift algorithm. In order to return the index of a bar by its datetime – we first have to call ::CopyTime(…) which creates a dynamic array, resizes it, copies data, and then destroy it in memory. This is not an issue for a few casual calls, but since time series functions are typically called more than just a few times on many different time-frames, this memory overhead can add up so a significant slow-down. Ponder for a moment all of the potential bloated-overhead and wasted resources required to allocate new memory each and every-time a program calls for any time-series data using these type of methods.

In order to speed things up this library implements the standard-library’s CObject and CArrayObj classes to copy the rates-array once and then re-access it from all time-series calls for that specific symbol and period. This can be a double-edged sword because the initialization phase takes longer than your typical implementation but all subsequent calls can access data in about 1/100th the time. In the example of iBarShift(), this new algorithm works faster by creating an int[] array which stores the index-integer of the bar in the array and then accesses it by using the time (casted to type int) as the array address. So in other words, you pass the time in as a direct address to access the data.

This brings us to a couple of caveats:

A bulk of the time is consumed during the initialization phase. If you don’t plan on accessing time-series data more than a few thousand (aggregate) times per period bar you may want to consider an alternative method.

Objects of the CiTimeSeries class are set to automatically refresh the stored data when a new bar is formed. Setting this to “false” will put the object into high-performance mode allowing lightning fast calls from the mission-critical “hot-path”, but a manual-refresh is required during a subsequent maintenance cycle.

Alternative:  ColorBarOpen_HTF - indicator MetaTrader 5

Example:

#include <itimeseries_nicholishen.mqh>
//--- global declaration of iTimeSeries object
CiTimeSeries iBar;
int OnInit()
{
//--- initialization phase
   iBar.Init(  NULL,
               PERIOD_CURRENT,
               false  // bool auto-refresh              
               );
//---
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
//---
   if(hot_path_operations)
   {  //example
      index = iBar.Shift(time);
   }
   else if(maintenance_path_operations)
   {
      iBar.Refresh();   
   }
}

Additionally, you can call the global functions directly (just as you would in MQL4) without instantiating an object of CiTimeSeries, but the first access time will be slow because it will have to first initialize a global object behind the scenes. Using the library this way can be slower if you only call any of the time-series functions a few times, however, there is a distinct performance advantage when your algorithm needs to call (>) several thousand iterations of time-series data from the same symbol+period set.

The developer of the (currently) most popular iBarShift algorithm did a competitive benchmark test for “iBarShift” which you may find here

AV Benchmark

Since this was the most accurate and fastest method (at the time of this post) I decided to use it as the benchmark for testing.

New Benchmark

The resulting computation time of 100,000 direct (global) function calls is 50x faster than that of the fastest method currently available, while calling the public method after initialization in “performance mode” is over 100x faster.

Available Public methods and global functions:

Note: Global functions are the same as MetaTrader 4 e.g. iBarShift, iTime, etc.

//--- initializes one symbol and period; set Auto-refresh to false for performance mode
   bool              Init(string            symbol=NULL,
                          ENUM_TIMEFRAMES   period      = PERIOD_CURRENT,
                          const bool        autoRefresh = true   ); 
//--- initializes one symbol and all periods within an ENUM_TIMEFRAMES array; set Auto-refresh to false for performance mode
   bool              Init(string            symbol,
                          ENUM_TIMEFRAMES   &period[],
                          const bool        autoRefresh = true   );
//--- initializes all periods
   bool InitAllPeriods (  string            symbol      = NULL,
                        const bool        autoRefresh=true);
//--- set auto-refresh to false to manage the refreshing of data manually
   void              AutoRefresh(const bool ref) { m_autoRefresh=ref;     }
   bool              AutoRefresh() const { return m_autoRefresh;          }
//--- manual refresh call; refreshes the data of ALL initialized periods
   bool              Refresh();
   CRatesArray      *GetArrayObjPointer(string            symbol,
                                        ENUM_TIMEFRAMES   period);
  • .Open = iOpen
  • .High = iHigh
  • .Low = iLow
  • .Close = iClose
  • .Highest = iHighest est
  • .Lowest = iLowest est
  • .Volume = iVolume
  • .BarsTotal = iBars
  • .Time = iTime
  • .Shift = iBarShift hift
Alternative:  Spread_Oscillator - indicator MetaTrader 5
📈 ROBOTFX MetaTrader Expert Advisors and Indicators to maximize profits and minimize the risks