Why doesn't my indicator perform all the way?

 

Hello, MQL community.

I hope you can help me with the following issue:

I am trying to print a zScore of daily ranges (which is just the standardize function in excel) and horizontal lines in the same chart.

When the zScore[i] > 4.36, the code zooms into the 1H time frame of daily bar[i], looks for the 2nd lowest/highest price (depends if daily bar is bull/bear) and prints a H_Line.

 However, the indicator stops in the middle of the way, it doesn't perform the calculations all the way to the last bar.

FICM_DAX 

  

How can I fix this? I can't figure out what's causing this.

The code follows below.

 

#property strict
#property indicator_separate_window
#property indicator_minimum -3
#property indicator_maximum 10
#property indicator_buffers 1

extern int Aver_Bars = 260;
                        
double zScore[];                       // Indicator array of z_scores
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,zScore);           //Assigning an array to a buffer
   SetIndexStyle(0,DRAW_HISTOGRAM,STYLE_SOLID,2,SteelBlue);//Histogram Style
   //---
   return(0);
  }
//+------------------------------------------------------------------+

int start()
  {
   int i,                              //Bar index
       n,                              //Formal parameter
       counted_bars,
       j;
   double Sum_R,                       //Sum of Ranges
          Sum_Dev,                     //Sum of Deviations from mean
          Std_Dev;                     //Std deviation from ranges' mean
          
   bool Bullish;
   int firstExtreme;
   int secondExtreme;
   double oneHourRate[];
   ArraySetAsSeries(oneHourRate,true);
      
   #define LEN 24
   datetime daily_time;
//+------------------------------------------------------------------+
   #define LOOKBACK Aver_Bars - 1
   counted_bars = IndicatorCounted();  //# of calculated bars
   i = Bars - MathMax(LOOKBACK,counted_bars) - 1;        //#index of 1st uncounted
   while(i>=0)                         //Cycle of uncounted bars
      {
        Sum_R=0;
        Sum_Dev=0;
        Std_Dev=0;                     //Nulling at beginning of loop
        
        for(n=i;n<=i+Aver_Bars-1;n++)  //Loop of summing values
         Sum_R=Sum_R + (iHigh(NULL,PERIOD_D1,n)-iLow(NULL,PERIOD_D1,n));//Accumulating range values
        
        for(n=i;n<=i+Aver_Bars-1;n++)  //Loop of summing values
         Sum_Dev=Sum_Dev+((iHigh(NULL,PERIOD_D1,n)-iLow(NULL,PERIOD_D1,n))-Sum_R/Aver_Bars)*
                         ((iHigh(NULL,PERIOD_D1,n)-iLow(NULL,PERIOD_D1,n))-Sum_R/Aver_Bars);
                                       //Sum of sq deviations from mean
         
        Std_Dev = MathSqrt(Sum_Dev/(Aver_Bars-1));//Std Deviation
        
        zScore[i]=((iHigh(NULL,PERIOD_D1,i)-iLow(NULL,PERIOD_D1,i)) - Sum_R/Aver_Bars) / Std_Dev;
                                       //calculation of z_score

                                
//+--------------------------------------------------------------------+       
//If z-score > 3.0, the code will zoom into the one hour bars that     |
//make up the daily bar and find the second extreme. The code will     |
//then print a line from such extreme.                                 |
//+--------------------------------------------------------------------+
         if(zScore[i] > 4.36 && i > 0)                               //if range is large AND not the
         {                                                        //first bar...
            j = i;
            daily_time = iTime(NULL,PERIOD_D1,j - 1);             //select the opening time of 
                                                                  //previous daily candle
            if(iClose(NULL,PERIOD_D1,j) - iOpen(NULL,PERIOD_D1,j) >= 0)
               Bullish = True;                                    //checking feature of candle
            else
               Bullish = False;
   
            if(Bullish == True)
            {
               CopyLow(Symbol(),PERIOD_H1,daily_time,LEN,oneHourRate);  //saved 1H bars into oneHourRate[]
               firstExtreme = ArrayMinimum(oneHourRate,WHOLE_ARRAY,0);  //1st lowest value in 1H TF
               secondExtreme = ArrayMinimum(oneHourRate,LEN-firstExtreme,firstExtreme+1);//2nd lowest to the left
            }
            else
            {
               CopyHigh(Symbol(),PERIOD_H1,daily_time,LEN,oneHourRate);//saved 1H bars into oneHourRate[]
               firstExtreme = ArrayMaximum(oneHourRate,WHOLE_ARRAY,0);  //1st highest value in 1H TF
               secondExtreme = ArrayMaximum(oneHourRate,LEN-firstExtreme,firstExtreme+1);//2nd highest to the left
            }
   
            ObjectCreate("Quasimodo" + j,OBJ_HLINE,0,0,oneHourRate[secondExtreme]);
          }

        i--;                           //index of next bar
      }
     return(0);                           //Exit function start()
  }

 

I know the zScore works fine on its own because I tested it and MQL experts from this community helped me get it right (https://www.mql5.com/en/forum/159623) (Thanks WHRoeder and ffoorr!)

The creation of the H_Lines also work fine because I've tested a script of it here: https://www.mql5.com/en/forum/159693

Thanks in advance

 
  1. if(zScore[i] > 4.36 && i > 0)                               //if range is large AND not the
             {                                                        //first bar...
                j = i;
                daily_time = iTime(NULL,PERIOD_D1,j - 1);             //select the opening time of 
                                                                      //previous daily candle
                if(iClose(NULL,PERIOD_D1,j) - iOpen(NULL,PERIOD_D1,j) >= 0)
    1. You are mixing apples and oranges Use iBarShift.
    2. For time you can just compute it.
         #define HR2400 86400
         SECONDS    time(datetime when=0){
            return SECONDS(when == 0 ? TimeCurrent() : when) % HR2400;               }
         datetime   date(datetime when=0){
            return datetime( (when == 0 ? TimeCurrent() : when) - time(when) );      }
         datetime   tomorrow( datetime when=0){   // Weekends not accounted
            return date(when) + HR2400;                                              }
         /// Returns previous trading day.
         datetime   yesterday(datetime when=0)    /**< Previous relative to
                                                   * when. */{
            if(when==0) when = TimeCurrent();
            // Make sure I have daily history.
            download_history(_Symbol, PERIOD_D1);
            INDEX    iD1   = iBarShift(NULL, PERIOD_D1,  when);   // Find today.
            return iTime(NULL, PERIOD_D1, iD1 + 1);               // Return yesterday.
         }
      ////////////////////////////////////////////////////////////////////////////////
      daily_time = date(Time[i]);

  2. ObjectCreate("Quasimodo" + j,OBJ_HLINE,0,0,oneHourRate[secondExtreme]);
    1. Don't use shift numbers in object names. You created a Quasimodo1 then the next day you'll try to again. Make your names unique "Quasimodo"+Time[j].
    2. You would know this had you Check your return codes What are Function return values ? How do I use them ? - MQL4 forum and Common Errors in MQL4 Programs and How to Avoid Them - MQL4 Articles
 
WHRoeder:
    1. You are mixing apples and oranges Use iBarShift.
    2. For time you can just compute it.

    1. Don't use shift numbers in object names. You created a Quasimodo1 then the next day you'll try to again. Make your names unique "Quasimodo"+Time[j].
    2. You would know this had you Check your return codes What are Function return values ? How do I use them ? - MQL4 forum and Common Errors in MQL4 Programs and How to Avoid Them - MQL4 Articles

Hello, WHRoeder

Thank you very much for your help again.

There are two things I don't understand from your reply:

  1. "You are mixing apples and oranges";
  2. Your computing for time;

      1. Why am I mixing apples with oranges? i, j and shift are all int types and they represent shift in the context of my indicator. I don't understand why this prevents my indicator from performing the calculations all the way to the end.

      2. With regards to your "computing for time" algorithm, the following are the points I don't understand:

    1. I wasn't familiar with the series array Time[]. Why did you choose Time[] over iTime?
    2. What is download_history(_Symbol, PERIOD_D1)?
    3. I noticed you initialise with SECONDS and INDEX (like int, double, etc). Is that MQL5 language?
    4. I suppose when = 0 is just your way of illustrating your point, right? I'm not expected to actually type when = 0, correct? Just    datetime date(datetime when) {... no?
    5. After daily_time = date(Time[i]), yesterday(daily_time) will give me the iTime for the previous daily bar, no?

I would greatly appreciate if you could clarify these questions for me.

Thanks in advance,

 Thad 

 

I have added the following to the top of my code, just below the #property(s):

#define HR2400 86400
datetime time(datetime when=0)
{
   return (when == 0 ? TimeCurrent() : when) % HR2400;
}

datetime date(datetime when=0)
{
   return ((when == 0 ? TimeCurrent() : when) - time(when));
}

datetime tomorrow(datetime when=0)
{
   return date(when) + HR2400;
}

datetime yesterday(datetime when=0)
{
   if(when == 0) when = TimeCurrent();
   int iD1 = barID(when);
   return iTime(NULL, PERIOD_D1, iD1 + 1);
}

int barID (datetime when)
{
   return iBarShift(NULL,PERIOD_D1, when);
}

 

Inside the body (int start()), I have called the above functions, but it is still not working properly:

daily_time = date(Time[i]);                     //select the opening time of daily candle
j = barID(daily_time);

 

 The code is still not performing the calculations to the end.

What am I missing?

Cheers,

 Thad 

 

Thaddeus_39:     1. Why am I mixing apples with oranges? i, j and shift are all int types and they represent shift in the context of my indicator.

  1. I wasn't familiar with the series array Time[]. Why did you choose Time[] over iTime?
  2. What is download_history(_Symbol, PERIOD_D1)?
  3. I noticed you initialise with SECONDS and INDEX (like int, double, etc). Is that MQL5 language?
  4. I suppose when = 0 is just your way of illustrating your point, right? I'm not expected to actually type when = 0, correct? Just    datetime date(datetime when) {... no?
  5. After daily_time = date(Time[i]), yesterday(daily_time) will give me the iTime for the previous daily bar, no?
  1. They do not "represent shift in the context of your indicator." i is the shift for your indicator on the current chart. j is the shift on the D1 chart. Read and understand the links provided. Then look at the yesterday function and compare it to the your code:
    if(zScore[i] > 4.36 && i > 0)                               //if range is large AND not the
             {                                                        //first bar...
                j = i;
                daily_time = iTime(NULL,PERIOD_D1,j - 1);             //select the opening time of 
                                                                      //previous daily candle
                if(iClose(NULL,PERIOD_D1,j) - iOpen(NULL,PERIOD_D1,j) >= 0)
  2. Why use a function call when you can use a Predefined Variables - MQL4 Reference Perhaps you should read the manual.
  3. Check that D1 chart is loaded. For indicators:
    bool download_history(     #define THIS_SYMBOL ""
             SYMBOL            symbol=THIS_SYMBOL,     ///< The symbol required.
             ENUM_TIMEFRAMES   period=PERIOD_CURRENT)  /**< Standard timeframe. */{
       if(symbol == THIS_SYMBOL)     symbol = _Symbol;
       if(period == PERIOD_CURRENT)  period = ENUM_TIMEFRAMES(_Period);
       ResetLastError();
       datetime other = iTime(symbol, period, 0);
       return _LastError == 0;
    }
    int OnCalculate(const int rates_total,
                 const int prev_calculated,
                 const datetime &time[],
                 const double &open[],
                 const double &high[],
                 const double &low[],
                 const double &close[],
                 const long &tick_volume[],
                 const long &volume[],
                 const int &spread[]){
       if(!download_history(THIS_SYMBOL, PERIOD_D1) return prev_calculated; // Keep D1 loaded
  4. No, #defines to document type usage.
  5. No. Formal Parameters - Variables - Language Basics - MQL4 Reference
  6. Obviously. As would yesterday() with no argument, see #5



 

Thanks, WHRoeder

I understood your code (or I think I did). 

I have added your "Time computing" code to the beginning (which I think is super clever, by the way)

(...)
#property indicator_buffers 1

#define HR2400 86400
datetime time(datetime when=0)
{
   return (when == 0 ? TimeCurrent() : when) % HR2400;
}

datetime date(datetime when=0)
{
   return ((when == 0 ? TimeCurrent() : when) - time(when));
}

datetime tomorrow(datetime when=0)
{
   return date(when) + HR2400;
}
(...)

 

and then called the function tomorrow(), thus eliminating the useless variable j:

        (...)
        zScore[i]=((iHigh(NULL,PERIOD_D1,i)-iLow(NULL,PERIOD_D1,i)) - Sum_R/Aver_Bars) / Std_Dev;
                                       //calculation of z_score

                                
//+--------------------------------------------------------------------+       
//If z-score > 3.00, the code will zoom into the one hour bars that     |
//make up the daily bar and find the second extreme. The code will     |
//then print a line from such extreme.                                 |
//+--------------------------------------------------------------------+
         if(zScore[i] > 3.0)                               //if range is large AND not the
         {                                                  //first bar...
            daily_time = tomorrow(Time[i]);                 //select the opening time of daily candle
                                                                              
            if(iClose(NULL,PERIOD_D1,i) - iOpen(NULL,PERIOD_D1,i) >= 0)
               Bullish = True;                                    //checking feature of candle
            else
               Bullish = False;
        (...)

 

(I read your apples and oranges link and the difference between that example and mine is that I only refer to the daily chart: if I couldn't use i in iClose and iOpen, then I could certainly not use it in iHigh and iLow - but the zScore[i] calculations worked perfectly until I started trying to draw lines so using i in iClose and iOpen can't be the issue)

 

This is what is happening.

When I choose if(zScore[i] > 3.0), the calculations only go as far as 23/01/2014, when the zScore is 4.3501.

 3" alt="zScore[i]>3" style="vertical-align: middle;">

 

When I choose if(zScore[i] > 4.36), the final chart is the same as the first chart on this thread (as far as 24/08/2015 when zScore = 4.74).

Finally, when I choose if(zScore[i] > 5.0), then it performs the calculations all the way (and I only get one line).

 

I think the issue could be when executing CopyLow(...) or CopyHigh(...)

What do you think?

Cheers,

 Thad 

 
Thaddeus_39: (I read your apples and oranges link and the difference between that example and mine is that I only refer to the daily chart:
         if(zScore[i] > 3.0)                               //if range is large AND not the
         {                                                  //first bar...
            daily_time = tomorrow(Time[i]);                 //select the opening time of daily candle
                                                                              
            if(iClose(NULL,PERIOD_D1,i) - iOpen(NULL,PERIOD_D1,i) >= 0)

You read but did not understand. The difference is my code used iBarShift.

If you are on the M1 chart and i is 2, your iClose(D1,2) is two days ago. If i is 5, your iClose(D1,5) is 5 days ago. You want the day containing your bar shift i.

 
WHRoeder:

You read but did not understand. The difference is my code used iBarShift.

If you are on the M1 chart and i is 2, your iClose(D1,2) is two days ago. If i is 5, your iClose(D1,5) is 5 days ago. You want the day containing your bar shift i.

1. That is what I understood from the apples and oranges link, which was quite insightful.

So, for consistency, your argument implies that I must change the calculation of zScore[i] as well, to be computed with iBarShift instead of n or i. Do you agree?

(...)
#define LOOKBACK Aver_Bars - 1
   counted_bars = IndicatorCounted();  //# of calculated bars
   i = Bars - MathMax(LOOKBACK,counted_bars) - 1;        //#index of 1st uncounted
   while(i>=0)                         //Cycle of uncounted bars
      {
        Sum_R=0;
        Sum_Dev=0;
        Std_Dev=0;                     //Nulling at beginning of loop
        
        for(n=i;n<=i+Aver_Bars-1;n++)  //Loop of summing values
         Sum_R=Sum_R + (iHigh(NULL,PERIOD_D1,n)-iLow(NULL,PERIOD_D1,n));//Accumulating range values
        
        for(n=i;n<=i+Aver_Bars-1;n++)  //Loop of summing values
         Sum_Dev=Sum_Dev+((iHigh(NULL,PERIOD_D1,n)-iLow(NULL,PERIOD_D1,n))-Sum_R/Aver_Bars)*
                         ((iHigh(NULL,PERIOD_D1,n)-iLow(NULL,PERIOD_D1,n))-Sum_R/Aver_Bars);
                                       //Sum of sq deviations from mean
         
        Std_Dev = MathSqrt(Sum_Dev/(Aver_Bars-1));//Std Deviation
        
        zScore[i]=((iHigh(NULL,PERIOD_D1,i)-iLow(NULL,PERIOD_D1,i)) - Sum_R/Aver_Bars) / Std_Dev;
                                       //calculation of z_score

                                
//+--------------------------------------------------------------------+       
//If z-score > 3.0, the code will zoom into the one hour bars that     |
//make up the daily bar and find the second extreme. The code will     |
//then print a line from such extreme.                                 |
//+--------------------------------------------------------------------+
         if(zScore[i] > 4.36 && i > 0)                               //if range is large AND not the
         {                                                        //first bar...
(...)

 

 

 2. With respect to the outcome that I described in my last post, where the indicator stops short of calculating all the way, I run into such problem even when my main window displays the daily chart.

 So the problem will persist even when I change my code to accommodate iBarShift. Wouldn't you agree?

 

Cheers, 

Thad 

 
  1. Yes. i and PERIOD_D1 can not be used together.
  2. Yes, that has nothing to do with your other problem. Have you looked in the log? Have you added a print statement inside your loop?
 

Thanks, WHRoeder

When I load the indicator, I get the following under the Experts tab:

  1. Custom Indicator loaded successfully;
  2. Initialised;
  3. Incorrect start position 24 for ArrayMaximum function;
  4. Array out of range in "NameOfIndicator.mq4" (116,81)

When I change my if expression from

(...)
         if(zScore[i] > 4.36)                               //if range is large AND not the
         {                                                  //first bar...
            daily_time = tomorrow(Time[i]);                 //select the opening time of daily candle
(...)

 to

(...)
         if(zScore[i] > 3.00)                               //if range is large AND not the
         {                                                  //first bar...
            daily_time = tomorrow(Time[i]);                 //select the opening time of daily candle
(...)

 

 I get:

  1. Incorrect start position 0 for ArrayMaximum function;
  2. Incorrect start position 0 for ArrayMaximum function;
  3. array out of range in "NameOfIndicator.mq4" (116,81);
Finally, when I change my if expression to if(zScore[i] > 5.00)I get no errors or warnings, and the indicator calculates all the way.


Please let me know what you think.

Cheers,

Thad 

 

OK, I understand what's causing the first set of errors, when if(zScore[i] > 4.36).

Sometimes there is an incompatibility between the open time of the daily candle and the opening time of the first hourly candle of that particular day.

The open time of the daily is always 00:00 but sometimes the 1 hour bar for 00:00 doesn't exist. I mention that here: https://www.mql5.com/en/forum/159693

When I add one extra hour to the calculation of daily_time = tomorrow(Time[i]) the indicator works fine, calculates all the way. 

That's good, because I believe the code is easy to fix. Just add a condition such as the following to deal with the mismatch:

(...)
   MqlRates rates[]; 
   ArraySetAsSeries(rates,true); 
   int copied=CopyRates(Symbol(),0,0,100,rates); 
   if(copied>0) 
     { 
      Print("Bars copied: "+copied); 
(...)

 

However, the quick fix didn't work for the errors and warnings under if(zScore[i] > 3.00).

Any ideas?

Cheers,

 

Thad 


Reason: