How can I fix rates_total/Bars()/iBars() being out of sync with charts?

 

The iTime() function (and no doubt other i____() functions) return 0 if they get an index that's greater than the TERMINAL_MAXBARS value returned by TerminalInfoInteger(). Rates_total, Bars(), and iBars() have no trouble returning bar counts that exceed  TERMINAL_MAXBARS. The standard arrays passed to OnCalculate (time(), open(), high(), low(), and close()) contain valid data for those same beyond-the- chart-max bars.

So, how do I get the date of one of those beyond-the-chart-max bars (or anything else) using an i____() function)?

This is important because the most current bar on a chart belongs to the (rates_total - 1) index of any of the indicator arrays passed to OnCalculate. If those indexes exceed TERMINAL_MAXBARS, the most recent data on the chart cannot be processed by an i____() function.

I'm sure someone will say, "Well, just access the OnCalculate arrays 'as series'!"

Well, then they should present to OnCalculate that way. And the example indicators that come with Metatrader 5, and the code available the the MQL5 website for common indicators such as Moving Average and Stochastic would need to be changed, since they're almost always coded to process the arrays as-is, with ArrayIsSeries() = false, starting for loops at 0 and running as long as the index < rates_total.


void OnStart()
{
        int ibars = Bars(_Symbol,_Period);
        
        Print("\nChecking dates for bars 119990 thru 120010 on a chart where Bars(",_Symbol,",",_Period,") = <",ibars,
                        "> and TerminalInfoInteger(TERMINAL_MAXBARS) = <",TerminalInfoInteger(TERMINAL_MAXBARS),">");
                for(int ix = 119990; ix < 120010 && !IsStopped(); ix++)
        {
           datetime dtBarDT_SRS                = iTime(_Symbol,_Period,ix);
                Print("iTime(",_Symbol,",",_Period,",",ix,") = <",dtBarDT_SRS,">");
        }
}


Here is the printout for the code above.

2024.08.05 15:33:19.567 Test 17 (USDJPY,M5)     Checking dates for bars 119990 thru 120010 on a chart where Bars(USDJPY,5) = <121249> and TerminalInfoInteger(TERMINAL_MAXBARS) = <120000>
2024.08.05 15:33:19.567 Test 17 (USDJPY,M5)     iTime(USDJPY,5,119990) = <2022.12.28 05:20:00>
2024.08.05 15:33:19.567 Test 17 (USDJPY,M5)     iTime(USDJPY,5,119991) = <2022.12.28 05:15:00>
2024.08.05 15:33:19.567 Test 17 (USDJPY,M5)     iTime(USDJPY,5,119992) = <2022.12.28 05:10:00>
2024.08.05 15:33:19.567 Test 17 (USDJPY,M5)     iTime(USDJPY,5,119993) = <2022.12.28 05:05:00>
2024.08.05 15:33:19.567 Test 17 (USDJPY,M5)     iTime(USDJPY,5,119994) = <2022.12.28 05:00:00>
2024.08.05 15:33:19.567 Test 17 (USDJPY,M5)     iTime(USDJPY,5,119995) = <2022.12.28 04:55:00>
2024.08.05 15:33:19.567 Test 17 (USDJPY,M5)     iTime(USDJPY,5,119996) = <2022.12.28 04:50:00>
2024.08.05 15:33:19.567 Test 17 (USDJPY,M5)     iTime(USDJPY,5,119997) = <2022.12.28 04:45:00>
2024.08.05 15:33:19.567 Test 17 (USDJPY,M5)     iTime(USDJPY,5,119998) = <2022.12.28 04:40:00>
2024.08.05 15:33:19.567 Test 17 (USDJPY,M5)     iTime(USDJPY,5,119999) = <2022.12.28 04:35:00>
2024.08.05 15:33:19.567 Test 17 (USDJPY,M5)     iTime(USDJPY,5,120000) = <1970.01.01 00:00:00>
2024.08.05 15:33:19.567 Test 17 (USDJPY,M5)     iTime(USDJPY,5,120001) = <1970.01.01 00:00:00>
2024.08.05 15:33:19.567 Test 17 (USDJPY,M5)     iTime(USDJPY,5,120002) = <1970.01.01 00:00:00>
2024.08.05 15:33:19.567 Test 17 (USDJPY,M5)     iTime(USDJPY,5,120003) = <1970.01.01 00:00:00>
2024.08.05 15:33:19.567 Test 17 (USDJPY,M5)     iTime(USDJPY,5,120004) = <1970.01.01 00:00:00>
2024.08.05 15:33:19.567 Test 17 (USDJPY,M5)     iTime(USDJPY,5,120005) = <1970.01.01 00:00:00>
2024.08.05 15:33:19.567 Test 17 (USDJPY,M5)     iTime(USDJPY,5,120006) = <1970.01.01 00:00:00>
2024.08.05 15:33:19.567 Test 17 (USDJPY,M5)     iTime(USDJPY,5,120007) = <1970.01.01 00:00:00>
2024.08.05 15:33:19.567 Test 17 (USDJPY,M5)     iTime(USDJPY,5,120008) = <1970.01.01 00:00:00>
2024.08.05 15:33:19.567 Test 17 (USDJPY,M5)     iTime(USDJPY,5,120009) = <1970.01.01 00:00:00>
 
Millard Melnyk:
So, how do I get the date of one of those beyond-the-chart-max bars (or anything else) using an i____() function)?

ask your broker to give more bars than 121249 for the period you used or accept SERIES_FIRSTDATE

So, how do I get the date of one of those beyond-the-chart-max bars (or anything else) using an i____() function)? create your own i__() that calculates time before the opening bar time of index 121249 

 
Sardion Maranatha #:

ask your broker to give more bars than 121249 for the period you used or accept SERIES_FIRSTDATE

So, how do I get the date of one of those beyond-the-chart-max bars (or anything else) using an i____() function)? create your own i__() that calculates time before the opening bar time of index 121249 

That's beside the point. This is a design inconsistency in the MQL5 platform itself. The i___() functions should work with all available data regardless of TERMINAL_MAXBARS, which is a limitation on charts. GUI variables should not limit data-level functions. I'll look at SERIES_FIRSTDATE, thanks. Maybe you could explain how that will help the i___() functions gain access to the affected data in the time() and OHLC price arrays in OnCalculate?

As far as "create your own" -- that is precisely my point and my aggravation. Why should coders be forced to Band-Aid the system they're trying to use in order to code? I've done plenty of that kind of coding to make up for MQL5 deficiencies already, you can be sure.

 

OOPS!!!! I spoke too soon.

And DRAT!

The reason iBars was returning the TERMINAL_MAXBARS value was because I'd just opened the platform and ran the script before a new bar was added on a 5-minute chart.

Nothing has changed, unfortunately. 

But (hint, hint) using TERMINAL_MAXBARS consistently throughout all the functions and built-in arrays would, indeed, dissolve the problem (in contrast to solving it -- there's a difference.) 


Original comment:

OK guys, I'm LMFAO! This one is fixed.

If you tried running that script the other day, try running it again now. I ran it again this morning trying to figure out a way around this little snafu, and what do ya know?!! MAGICALLY, without notice or explanation, the <<int ibars = Bars(_Symbol,_Period);>> statement now returns the same value as TERMINAL_MAXBARS. In indicators, too. All the arrays passed to OnCalculate are now sized to the TERMINAL_MAXBARS value.

PROBLEM SOLVED! 😁

I wonder whatever in the world could have happened? 😜 All hail the MQL5 gods!

Four Approaches to Problem Solving:
Four Approaches to Problem Solving:
  • 2016.09.26
  • Harish
  • harishsnotebook.wordpress.com
As a Quality professional, I am always interested in learning about problem solving. In today’s post I will be looking at the four approaches to Problem Solving as taught by the late great Systems Thinker, Russell Ackoff. He called these “Problem Treatments” – the ways one deals with problems. They are; Absolution – This is a common reaction to...
 
Millard Melnyk #:

OK guys, I'm LMFAO! This one is fixed.

If you tried running that script the other day, try running it again now. I ran it again this morning trying to figure out a way around this little snafu, and what do ya know?!! MAGICALLY, without notice or explanation, the <<int ibars = Bars(_Symbol,_Period);>> statement now returns the same value as TERMINAL_MAXBARS. In indicators, too. All the arrays passed to OnCalculate are now sized to the TERMINAL_MAXBARS value.

PROBLEM SOLVED! 😁

I wonder whatever in the world could have happened? 😜 All hail the MQL5 gods!

You can find more details in the book, at https://www.mql5.com/en/book/applications/timeseries/timeseries_bars, for example:

int iBars(const string symbol, ENUM_TIMEFRAMES timeframe)        

The functions return the number of bars available for the MQL program for the given symbol and period. This value is influenced by the parameter Max. bars in chart in the terminal Options (see the note in the section Technical features of organization and storage of timeseries). For example, if a history is downloaded to the terminal, which for a specific timeframe is 20,000 bars, but the limit is set to 10,000 bars in the settings, then the second value will be decisive. Immediately after the launch of the terminal, the functions will return the number of 10,000 bars, but as new bars are formed, it will increase (if free memory allows). In MQL5, this limit can be found by calling TerminalInfoInteger(TERMINAL_MAXBARS).

So, this is how MQ has designed the storage of quotes and access to them via API.

The bars outside TERMINAL_MAXBARS are "shadow" (if you wish) and their number is normaly increases during a session (as new bars are added from online), but they may disappear at any moment, when/if the terminal will need more memory to free.

If you want a reliable access to complete history, you can set the setting to a very long number, but this will also affect the charts.

MQL5 Book: Creating application programs / Timeseries / Number of available bars (Bars/iBars)
MQL5 Book: Creating application programs / Timeseries / Number of available bars (Bars/iBars)
  • www.mql5.com
A shorter way to find out the total number of bars in a timeseries by symbol/period is provided by the functions Bars and iBars (there is no...
 
Stanislav Korotky #:

You can find more details in the book, at https://www.mql5.com/en/book/applications/timeseries/timeseries_bars, for example:

So, this is how MQ has designed the storage of quotes and access to them via API.

The bars outside TERMINAL_MAXBARS are "shadow" (if you wish) and their number is normaly increases during a session (as new bars are added from online), but they may disappear at any moment, when/if the terminal will need more memory to free.

If you want a reliable access to complete history, you can set the setting to a very long number, but this will also affect the charts.

So, the DEFICIENCY is documented. Who cares? Bad design. And nothing posted here, so far, addresses the actual problem: HOW DO YOU GET ACCESS TO THAT "SHADOW" DATA, ESPECIALLY SEEING THAT IT'S THE MOST CURRENT OF ALL THE DATA?

I did what @Sardion Maranatha suggested and fixed it myself, see the code below.

There is no valid reason from a coder's perspective why the i___() functions should work only with series-style index values, nor why they should be limited to a GUI limitation like TERMINAL_MAXBARS when the underlying data is not thus limited.

 

OK guys, I've fixed the problem as far as needed for my purposes. Here is the code. Enjoy!

void OnStart()
{
   datetime dtBarDT_REG                 = 0;
   datetime dtBarDT_SRS                 = 0;
   int iSRS_IX                          = -1;

   int iBars_CNT                        = Bars(_Symbol,_Period);
   int iBars_MAX                        = TerminalInfoInteger(TERMINAL_MAXBARS);

   iSRS_IX                              = iBarsFLIP_IX(iBars_CNT-1);

   Print("\nChecking dates for bars 119990 thru 120010 on a chart where Bars(",_Symbol,",",_Period,") = <",iBars_CNT,
         "> and TerminalInfoInteger(TERMINAL_MAXBARS) = <",iBars_MAX,">");

   for(int REG_IX = iBars_MAX - 10; REG_IX < iBars_CNT && !IsStopped(); REG_IX++)
   {
      iSRS_IX                           = iBarsFLIP_IX(REG_IX);
      dtBarDT_REG                       = iTimeREG(REG_IX);
      dtBarDT_SRS                       = iTime(_Symbol,_Period,iSRS_IX);

      Print("Flipped REG_IX<",REG_IX,"> to series value iSRS_IX<",iSRS_IX,">, iTime(",_Symbol,",",_Period,",",iSRS_IX,") = <",dtBarDT_SRS,">");
   }
        
}

int  iBarsFLIP_IX(int piIX,int piBars_CNT=0,string psSymb=NULL,ENUM_TIMEFRAMES peTF=PERIOD_CURRENT) 
{

//--- Flips a given index value between series-style and regular
//

   int iBars_CNT                                                   = 0;
        
   if(piBars_CNT == 0)
   {
      string           sSymbol         = (psSymb == NULL) ? _Symbol : psSymb;
      ENUM_TIMEFRAMES  eChrtTF         = (peTF == PERIOD_CURRENT) ? _Period : peTF;
      iBars_CNT                        = (piBars_CNT == 0)? Bars(sSymbol,eChrtTF): piBars_CNT;
   }

   int iFlippedIX                      = iBars_CNT - piIX - 1;

   return(iFlippedIX); 
}

datetime iTimeREG(int piREG_IX,int piBarsCNT=0,string psSymb=NULL,ENUM_TIMEFRAMES peTF=PERIOD_CURRENT)
{
//--- All the i-functions like iTime() expect a reversed-index "series" bar index value.
//    This function returns the datetime for the same bar when given a regular index value.

   int iBarzCNT                        = (piBarsCNT == 0) ? Bars(psSymb,peTF) : piBarsCNT;
   int iSRS_IX                         = iBarsFLIP_IX(piREG_IX,iBarzCNT);
     
   datetime dtBarDT_SRS                = iTime(psSymb,peTF,iSRS_IX);
   datetime dtBarDT_REG                = dtBarDT_SRS;
 
   return(dtBarDT_REG);

}
 
Stanislav Korotky #:

You can find more details in the book, at https://www.mql5.com/en/book/applications/timeseries/timeseries_bars, for example:

So, this is how MQ has designed the storage of quotes and access to them via API.

The bars outside TERMINAL_MAXBARS are "shadow" (if you wish) and their number is normaly increases during a session (as new bars are added from online), but they may disappear at any moment, when/if the terminal will need more memory to free.

If you want a reliable access to complete history, you can set the setting to a very long number, but this will also affect the charts.

Regarding your explanation, I read the material at  Timeseries and Indicators Access.

It states the following there:

Parameter "Max bars in chart"

The "Max bars in charts" parameter restricts number of bars in HC format available to charts, indicators and mql5 programs. 
This is valid for all available timeframes and serves (sic), first of all, to save computer resources.


That statement is false. The arrays provided stock to the OnCalculate() function (OHLCT) -- along with the counts provided by 'rates_total', Bars(), and iBars() -- are not restricted by the "Max bars in charts" parameter. 

If they were, they would never exceed TERMINAL_MAXBARS and there would be no problem in the first place.

Documentation on MQL5: Timeseries and Indicators Access / Organizing Data Access
Documentation on MQL5: Timeseries and Indicators Access / Organizing Data Access
  • www.mql5.com
In this section questions connected with obtaining, storing and requesting price data ( timeseries ) are considered. Before price data become...
 
Millard Melnyk #:

Regarding your explanation, I read the material at  Timeseries and Indicators Access.

It states the following there:


That statement is false. The arrays provided stock to the OnCalculate() function (OHLCT) -- along with the counts provided by 'rates_total', Bars(), and iBars() -- are not restricted by the "Max bars in charts" parameter. 

If they were, they would never exceed TERMINAL_MAXBARS and there would be no problem in the first place.

Here you provided a link to the documentation, which I did not write. I admit that the wording is not clear enough (at least it requires a bit of clarification). And this is why I use another wording in the book, which you could find by following link provided above - https://www.mql5.com/en/book/applications/timeseries/timeseries_storage_tech:

Maximum number of bars

It should be noted that the maximum number of bars that will be calculated for each requested symbol/timeframe pair does not exceed the value of the parameter Max. bars in chart in the Options dialog of the terminal. Thus, this parameter imposes restrictions not only on charts of any timeframes but also on all MQL programs.

This limitation is primarily intended to save resources. When setting large values of this parameter, it should be remembered that if there is a sufficiently deep history of price data for lower timeframes, the memory consumption for storing timeseries and indicator buffers can amount to hundreds of megabytes and take up all the RAM.

Changing the bar limit takes effect only after restarting the client terminal. It affects the amount of data requested from the server to build the required number of bars of working timeframes.

The limit set by the parameter is not hard and can be exceeded in certain cases. For example, if at the beginning of the session, the history of quotes for a specific timeframe is sufficient to select the entire limit, then as new bars form, their number may become greater than the current value of the parameter. The actual number of available bars is returned by the Bars/iBars functions.

MQL5 Book: Creating application programs / Timeseries / Technical aspects of timeseries organization and storage
MQL5 Book: Creating application programs / Timeseries / Technical aspects of timeseries organization and storage
  • www.mql5.com
Before proceeding to the practical issues of using the MQL5 API functions designed to work with time series, we should consider the technical...
 
Millard Melnyk #:

So, the DEFICIENCY is documented. Who cares? Bad design. And nothing posted here, so far, addresses the actual problem: HOW DO YOU GET ACCESS TO THAT "SHADOW" DATA, ESPECIALLY SEEING THAT IT'S THE MOST CURRENT OF ALL THE DATA?


MQ designed the API with efficiency in mind. Your use case is not clear so far. Especially taking into account that you're wrong: most current data goes at lower indices - 0-th bar is the current bar, so the most outdated bars are in the "shadow", and they are highly likely irrelevant for current analysis.

 
Stanislav Korotky #:

MQ designed the API with efficiency in mind. Your use case is not clear so far. Especially taking into account that you're wrong: most current data goes at lower indices - 0-th bar is the current bar, so the most outdated bars are in the "shadow", and they are highly likely irrelevant for current analysis.

No, I'm not wrong. IF and ONLY IF you access a time or price array AS SERIES, I would be wrong. But I've quite clearly in both words and code explained a problem that occurs when arrays are accessed via regular (not reversed) indexing. I demonstrated the problem in the OP.

So, I'm not wrong that in an array or index buffer where ArrayIsSeries() = false, the newest data is found at the highest indices.