Download MetaTrader 5

Range not behaving as expected

To add comments, please log in or register
The_Rezzer
44
The_Rezzer  

Goodmorning all. 

I'm trying to create a "dynamic" range using ArrayMax/Min - as each new bar comes in (and assuming some other criteria are met) the range extends in length - to achieve this I have elected to create a variable, the value of which changes upon each new candle (and some other criteria), and placed this into the count parameter in place of a fixed value. My ArrayMin range appears to be working OK but not the ArrayMax, even though both functions are pulling their data from the same source.

Just so we're all on the same page:
1. My array index positions and "ï" match - 0 is the oldest bar on the chart.
2. My terminal options are set to have a Max of 5000 candles in the history and 5000 on the chart; as i'm printing my own candles i turn off the built in bars/candles.
3. I've been testing this on a bar by bar basis - by manually incrementing expression2 (starting with candle 4 - so you'll need to scroll all the way to the left) of the for loop on a weekly EUR/USD chart and have left the attached code in this state so you can see for yourselves - should you need to look. I've highlighted the for loop in question.

Assuming your set-up matches that which i have stated above you should see the following:
1. [0] is the "last" (in this case also the first) swing point.
2. [1] is the highest candle on the chart.
3. [3] is the lowest candle on the chart.
4. i=[4].
5. The number of bars in either range is 4 (derived by the calculation: int Bars_In_Range=i-Last_Swing).
6. The range Low is [3].
7. The range High is [5] where i'm expecting it to be [1] - This is particularly confusing to me as [5] hasn't even been printed on the chart yet.
8. If you manually enter 4 into the ArrayMax "count" parameter then the range does what i'm expecting it to and reports [1] as the range high.

Apologies if that's a little long winded i just wanted to make sure i gave you the correct info.  

Here's the code:

//+------------------------------------------------------------------+
//|                                   Heiken Ashi Trend Detector.mq4 |
//|       Copyright © 2016 Carl Carver Software. All Rights Reserved |
//|                                          carl_carver@hotmail.com |
//+------------------------------------------------------------------+

//Indicator Basic Properties
#property copyright "Copyright © 2016 Carl Carver Software. All Rights Reserved."
#property link      "carl_carver@hotmail.com"
#property version   "1.00"
#property description "Heiken Ashi Trend Detector"
#property strict
#property indicator_chart_window
#property indicator_buffers 11

//Indicator Format Properties
//HA Properties
#property indicator_color1 clrBlack
#property indicator_color2 clrRed
#property indicator_color3 clrLimeGreen
#property indicator_width1 1
#property indicator_width2 1
#property indicator_width3 3
#property indicator_width4 3

//Swing Properties
#property indicator_color5 clrBlack
#property indicator_color6 clrBlack
#property indicator_color7 clrBlack
#property indicator_color8 clrBlack
#property indicator_color9 clrBlack
#property indicator_color10 clrBlack
#property indicator_width5 2
#property indicator_width6 2
#property indicator_width7 2
#property indicator_width8 2
#property indicator_width9 2
#property indicator_width10 2 

//User Inputs
//HA Candle Properties                                                      
input color Shadow=clrBlack;
input color Bear_Body = clrRed;
input color Bull_Body = clrLimeGreen;

//Buffer Arrays
//HA Buffers                                               
double HA_High_Buffer[];
double HA_Low_Buffer[];
double HA_Open_Buffer[];
double HA_Close_Buffer[];

//Swing Buffers   
double Swing_1_Buffer[];    //Wingding symbol code: 140 - On-screen symbol: 1
double Swing_2_Buffer[];    //Wingding symbol code: 141 - On-screen symbol: 2
double Swing_3_Buffer[];   //Wingding symbol code: 142 - On-screen symbol: 3
double Swing_4_Buffer[];   //Wingding symbol code: 143 - On-screen symbol: 4
double Swing_HL_Buffer[];   //Wingding symbol code: 144 - On-screen symbol: 5
double Swing_HH_Buffer[];   //Wingding symbol code: 145 - On-screen symbol: 6

//Counter Buffer
double Counter_Buffer[];

//Pre-program buffer implementation and mapping
void OnInit(void)
  {
   IndicatorDigits(Digits());

//HA Buffers
   SetIndexBuffer(0,HA_High_Buffer);
   SetIndexStyle(0,DRAW_HISTOGRAM,0,1,Shadow);
   SetIndexDrawBegin(0,0);
   SetIndexLabel(0,"HA High");

   SetIndexBuffer(1,HA_Low_Buffer);
   SetIndexStyle(1,DRAW_HISTOGRAM,0,1,Shadow);
   SetIndexDrawBegin(1,0);
   SetIndexLabel(1,"HA Low");

   SetIndexBuffer(2,HA_Open_Buffer);
   SetIndexStyle(2,DRAW_HISTOGRAM,0,3,Bear_Body);
   SetIndexDrawBegin(2,0);
   SetIndexLabel(2,"HA Open");

   SetIndexBuffer(3,HA_Close_Buffer);
   SetIndexStyle(3,DRAW_HISTOGRAM,0,3,Bull_Body);
   SetIndexDrawBegin(3,0);
   SetIndexLabel(3,"HA Close");

//Swing Buffers   
   SetIndexBuffer(4,Swing_1_Buffer);
   SetIndexStyle(4,DRAW_ARROW);
   SetIndexArrow(4,140);
   SetIndexLabel(4,"Rule 1");

   SetIndexBuffer(5,Swing_2_Buffer);
   SetIndexStyle(5,DRAW_ARROW);
   SetIndexArrow(5,141);
   SetIndexLabel(5,"Rule 2");

   SetIndexBuffer(6,Swing_3_Buffer);
   SetIndexStyle(6,DRAW_ARROW);
   SetIndexArrow(6,142);
   SetIndexLabel(6,"Rule 3");

   SetIndexBuffer(7,Swing_4_Buffer);
   SetIndexStyle(7,DRAW_ARROW);
   SetIndexArrow(7,143);
   SetIndexLabel(7,"Rule 4");

   SetIndexBuffer(8,Swing_HL_Buffer);
   SetIndexStyle(8,DRAW_ARROW);
   SetIndexArrow(8,144);
   SetIndexLabel(8,"Sw. Higher Low");

   SetIndexBuffer(9,Swing_HH_Buffer);
   SetIndexStyle(9,DRAW_ARROW);
   SetIndexArrow(9,145);
   SetIndexLabel(9,"Sw. Higher High");

//Counter Buffer
   SetIndexBuffer(10,Counter_Buffer,INDICATOR_DATA);
   SetIndexStyle(10,DRAW_LINE,STYLE_SOLID,1,CLR_NONE);
   SetIndexLabel(10,"Bar #");
  }

//+------------------------------------------------------------------+
//| Globalscope Custom Function and Variable Declarations            |
//+------------------------------------------------------------------+  
//Some code here

//+------------------------------------------------------------------+
//| Main program                                                     |
//+------------------------------------------------------------------+
int OnCalculate
(
 //Standard bar price and data arrays
 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[]
 )
  {

//Price Arrays
   ArraySetAsSeries(open,false);
   ArraySetAsSeries(high,false);
   ArraySetAsSeries(low,false);
   ArraySetAsSeries(close,false);
   ArraySetAsSeries(time,false);

//Heiken Ashi Arrays
   ArraySetAsSeries(HA_High_Buffer,false);
   ArraySetAsSeries(HA_Low_Buffer,false);
   ArraySetAsSeries(HA_Open_Buffer,false);
   ArraySetAsSeries(HA_Close_Buffer,false);

//Swing Arrays
   ArraySetAsSeries(Swing_1_Buffer,false);
   ArraySetAsSeries(Swing_2_Buffer,false);
   ArraySetAsSeries(Swing_3_Buffer,false);
   ArraySetAsSeries(Swing_4_Buffer,false);
   ArraySetAsSeries(Swing_HL_Buffer,false);
   ArraySetAsSeries(Swing_HH_Buffer,false);

//Counter Array
   ArraySetAsSeries(Counter_Buffer,false);

//Heikin Ashi Candle Variables           
int
i=0,
Lookback=IndicatorCounted();

double
HA_Open,
HA_High,
HA_Low,
HA_Close;

//Swing variables
int
Last_Swing=0,
Bars_In_Range=0,
Swing_Rule=0,
Range_Low=ArrayMinimum(HA_Low_Buffer,Bars_In_Range,i),//Outputs shift of range low
Range_High=ArrayMaximum(HA_High_Buffer,Bars_In_Range,i); //Outputs shift of range high

//Error Check - Sufficient candles on chart to complete calculations? 
   if(Lookback<1) return(-1);

//Setting The First Candle and Swing Point
   for(i=0; i<1; i++)
     {
      if(close[i]>open[i] || close[i]<open[i])
        {
         //Update the counter on each iteration of the loop - in this case once
         Counter_Buffer[i]=i;
         //Candles print as follows
         HA_Low_Buffer[i]=low[i];
         HA_High_Buffer[i]=high[i];
         HA_Open_Buffer[i]=open[i];
         HA_Close_Buffer[i]=close[i];
         //Set the first swing point on the first candle on the chart
         Swing_1_Buffer[i]=HA_Low_Buffer[i];
        }
     }

//Setting the first 3 HA candles - necessary so to avoid an "array out of range" error during the swing calc
   for(i=1; i<4; i++)
     {
      //Update the counter on each iteration of the loop - in this case thrice
      Counter_Buffer[i]=i;
      //Establish current HA OHLC on each iteration of the loop
      HA_Open=(HA_Open_Buffer[i-1]+HA_Close_Buffer[i-1])/2;
      HA_Close=(open[i]+high[i]+low[i]+close[i])/4;
      HA_High=MathMax(high[i],MathMax(HA_Open,HA_Close));
      HA_Low=MathMin(low[i],MathMin(HA_Open,HA_Close));
      //Candles print as follows:
      HA_Low_Buffer[i]=HA_Low;
      HA_High_Buffer[i]=HA_High;
      HA_Open_Buffer[i]=HA_Open;
      HA_Close_Buffer[i]=HA_Close;
     }

//Main HA candle and swing detection calculations and loop
   for(i=4; i<=4; i++)
     {
      //Update the counter on each iteration of the loop - in this case on every new candle
      Counter_Buffer[i]=i;
      Bars_In_Range=i-Last_Swing;
      //Establish current HA OHLC on each iteration of the loop
      HA_Open=(HA_Open_Buffer[i-1]+HA_Close_Buffer[i-1])/2;
      HA_Close=(open[i]+high[i]+low[i]+close[i])/4;
      HA_High=MathMax(high[i],MathMax(HA_Open,HA_Close));
      HA_Low=MathMin(low[i],MathMin(HA_Open,HA_Close));
      //Candles print as follows:
      HA_Low_Buffer[i]=HA_Low;
      HA_High_Buffer[i]=HA_High;
      HA_Open_Buffer[i]=HA_Open;
      HA_Close_Buffer[i]=HA_Close;

Comment
      ("i: ",i,
       "\nBars: ",Bars,
       "\nLast Swing: ",Last_Swing,
       "\n",
       "\nBars in Range: ",Bars_In_Range,
       "\ni-Last Swing: ",i-Last_Swing,
       "\n",
       "\nRange_Low: ",Range_Low,
       "\nRange_High: ",Range_High);
     }

//End of program  
   return(rates_total);
  }

As always, thanks to anyone that can advise what it is i'm doing wrong. 

Thanks, 

Carl. 

Fernando Carreiro
2340
Fernando Carreiro  

Something seems definitely wrong in your Indicator:

  • Your loops don't go over the available data and are instead "stuck" in time! Since, you set all arrays to NOT as "Series", you are constantly referring to the "oldest" bars available, never current ones.
  • Also, yet again you are mixing Old Style way with New Style. Since you are using the "OnCalculate()" event, you must use the parameters "rates_total" and "prev_calculated". Do not use "IndicatorCounted()".
  • Another, problem is that you start using the array contents with "ArrayMaximum()" and "ArrayMinimum()" but you never initialise the contents of those arrays on the first call to the event.

As a reference to help you out, take a look at the "Heiken Ashi" indicator provided by MetaTrader. I've attached here just for easier access but it should be in your Indicator's folder.

Files:
whroeder1
15735
whroeder1  
FMIC: Also, yet again you are mixing Old Style way with New Style. Since you are using the "OnCalculate()" event, you must use the parameters "rates_total" and "prev_calculated". Do not use "IndicatorCounted()".
False. You can use rates_total and prev_calculated, you don't have to.

You can use IndicatorCounted in OnCalculate just fine. Ignore the arguments, use the Predefined Variables, all the Timeseries and Indicators Access functions (e.g. iBarShift, iCustom,) and leave the buffers defaulted to series and count down to zero (current bar.)

You can use rates_total and prev_calculated, set everything to non-series and count up.


.
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[]){

   int    counted_bars=IndicatorCounted();
   int shift=Bars-1-MathMax(Length, counted_bars);
   if(counted_bars == 0){ ... }
   for(; shift>=0; --shift){
      int      iHL   = iHighest(NULL,0, MODE_LOW, Length, shift);
Fernando Carreiro
2340
Fernando Carreiro  
WHRoeder:
False. You can use rates_total and prev_calculated, you don't have to.

You can use IndicatorCounted in OnCalculate just fine. Ignore the arguments, use the Predefined Variables, all the Timeseries and Indicators Access functions (e.g. iBarShift, iCustom,) and leave the buffers defaulted to series and count down to zero (current bar.)

You can use rates_total and prev_calculated, set everything to non-series and count up.

Given that MQL5 does not use "IndicatorCounted()" and that it follows the "OnCalculate()" style, what you saying may be true but is VERY BAD ADVICE especially taking into account the near future merging of Metatrader 4 and 5.

The_Rezzer
44
The_Rezzer  

Hi guys, thanks for both of your imput. 

Fernando, the indicator you have attached is actually the "template" for the code I've posted. I've made a few adjustments to the code in order to make it easier to read but you'll notice that the arrays are set as NOT as series in there, too (i've just continued using that format as i assumed as it's a stock indicator it would be ok to carry on in doing so).

Are there any articles relating to the merger of MQL4 and 5? I was of the understanding that they were going to remain separate due to the different functionality of MT4 and 5 - although i can't find the article where i think i read that, so could quite easily be mistaken and have taken it out of context!

As an experiment (and it'll be good practice, too) i'll re-code my indicator counting down and see if that makes any kind of difference.  

Bwalya, I was originally using iLowest/Highest but as I'm counting in the direction i am it was getting a bit confusing - iLowest/Highest count and start parameters got a bit awkward to use; ArrayMax/Min use the index positions which of course match the way in which i count (due to the arrays being NOT as series).

***Edit: I believe the arrays need to be set as series as the first candle on a Heiken Ashi chart is always a standard (OHLC) candle and any subsequent candle is then a HA candle. 

Fernando Carreiro
2340
Fernando Carreiro  
The_Rezzer:

Hi guys, thanks for both of your imput. 

Fernando, the indicator you have attached is actually the "template" for the code I've posted. I've made a few adjustments to the code in order to make it easier to read but you'll notice that the arrays are set as NOT as series in there, too (i've just continued using that format as i assumed as it's a stock indicator it would be ok to carry on in doing so).

Are there any articles relating to the merger of MQL4 and 5? I was of the understanding that they were going to remain separate due to the different functionality of MT4 and 5 - although i can't find the article where i think i read that, so could quite easily be mistaken and have taken it out of context!

As an experiment (and it'll be good practice, too) i'll re-code my indicator counting down and see if that makes any kind of difference.  

Bwalya, I was originally using iLowest/Highest but as I'm counting in the direction i am it was getting a bit confusing - iLowest/Highest count and start parameters got a bit awkward to use; ArrayMax/Min use the index positions which of course match the way in which i count (due to the arrays being NOT as series).

***Edit: I believe the arrays need to be set as series as the first candle on a Heiken Ashi chart is always a standard (OHLC) candle and any subsequent candle is then a HA candle. 

The problem is not the "setting as NOT as series". The problem is that your loops are "static"! They must "roll" (for lack of a better term), just like in the original "Heiken Ashi" indicator!

PS! Yes, MT4 and MT5 may remain separate, but surely you can see that they are more and more similar by the day. Sooner or later, MT4 will die off (or be killed), so the sooner you adapt to the new style MQL5 way, the better your code will adapt for the future.

The_Rezzer
44
The_Rezzer  

Are you referring to my loop stating:    for(i=4; i<=4; i++) ? When i'm not intentionally going through the chart 1 bar at a time it states:    for(i=4; i<=Lookback; i++) - Lookback=IndicatorCounted. When the latter is stated then my chart is fully populated with HA candles. Perhaps i misunderstand what it is you mean. Sorry, this is my first attempt at coding. 

Fernando Carreiro
2340
Fernando Carreiro  
The_Rezzer:

Are you referring to my loop stating:    for(i=4; i<=4; i++) ? When i'm not intentionally going through the chart 1 bar at a time it states:    for(i=4; i<=Lookback; i++) - Lookback=IndicatorCounted. Perhaps i misunderstand?

But why repeat the process on every call to "OnCalculate()". It should just pick-up from the previous calculations. That is what the parameter "prev_calculated" is for. Contrary to what WHRoeder has stated, I don't agree with him, because "rates_total" and "prev_calculated" works differently to "IndicatorCounted", and for someone like yourself that has still not understood the mechanism, it just confuses and messes everything up.

Also why use a loop for "for(i=0; i<1; i++)" for just one index and then execute that on every single call to "OnCalculate()".

Besides the Heiken Ash Indicator, take a look at some of the others that use the OnCalculate() and compare them to find the similarities and differences, so that you can understand the concept.

PS! If you want, send me a PM with your "real" code, and I will look it over and make changes to it for you based on what you require. Just send me the details of what you want coded.

The_Rezzer
44
The_Rezzer  

Fernando, that's an extremely kind offer, however, i really need to get to grips with this if i stand any chance of being able to properly understand it/fix any issue in the future. Also, a whole heap of things have just clicked for me off the back of your last 2 comments; I didn't realise i was going through every bar on the chart on each call to OnCalculate and now see the relevance prev_calculated. I've just had a look at the CCI indicator code and i see exactly what it is your refering to. I'm going to modify my code and retest. If i'm still having the same problems after implementing your recommendations i'll update this thread/PM you.

Thanks again. :) 

whroeder1
15735
whroeder1  
First define your maximum lookback.
int lookback = ... // iMA(period) has look back of period.
                        // buffer[i+2] has look back of 2 (as TimeSeries)
                        // buffer[i-2] has look back of 2 (not TimeSeries)
                       // use maximum of all.
int lookback = ... // iMA(period) has look back of period.
                   // buffer[i+2] has look back of 2 (as TimeSeries)
                   // buffer[i-2] has look back of 2 (not TimeSeries)
                   // use maximum of all.
Old way, counting down as a TimeSeries
   int counted = IndicatorCounted();
   for(int iBar = Bars - 1 - MathMax(lookback, counted); iBar >= 0; --iBar){
      // Timeseries and Indicators Access uses index=iBar
   }
New way, counting down as a TimeSeries (Bars always equals rates_total so can be substituted.)
   for(int iBar = Bars-1-MathMax(lookback, prev_calculated); iBar >= 0; --iBar){
      // Timeseries and Indicators Access uses index=iBar
      https://docs.mql4.com/series
   }
   return rates_total-1; // Recalculate current bar next tick.

Returning rates_total-1 makes prev_calculated the same as IndicatorCounted().

Alternatively if you return rates_total then must decrement prev_calculated if non-zero at the beginning of On_Calculate to recalculate the current bar.

Always count down so you're not using future values.

New way, counting up, not a TimeSeries.
   set all accessed arrays and buffers to ArraySetAsSeries(false) Not the Predefined Variables

   for(int iBar = MathMax(lookback, prev_calculated; iBar < Bars; ++iBar){
      // To access a Timeseries use index=Bars -1- iBar
      // and convert any results back index=Bars -1- result
   }
   return rates_total-1; // Recalculate current bar next tick.
Always count up so you're not using future values.
whroeder1
15735
whroeder1  
New way, counting down as a TimeSeries, but not through bar zero.
   #define LAST 1
   for(int iBar = Bars-1-MathMax(lookback, prev_calculated); iBar >= LAST; --iBar){
      :
   }
   return rates_total-MathMax(1,LAST); // Recalculate Bar LAST once on new bar.
This can be used to not cover newest bars, or can be used to update the indicator in steps, e.g. 1000-900, 899-799, ... 99-0, 0...
12
To add comments, please log in or register