Smoothed ADX on Heiken Ashi Bars (with thanks to TradingView)

Andrew Thompson
353
Andrew Thompson  

TradingView calculates their DMI (equivalent to MT4 ADX) on the
bars used, i.e. when you select Heiken Ashi bars for the price display, the Open and Close values for the ADX calculation are the modified HA values. In addition, their DMI takes an extra input, a smoothing period, by which the ADX line is smoothed.

Their code for the DMI is freely available. I have tried to replicate the use of their DMI on Heken Ashi Bars in the quoted script below, but have become stuck with an array out of range error in line 78. 

Relevant line numbers are in comments which also contain the original PINE code.

Update:

The obvious errors have been corrected (see my comment below), but, even when the scale is fixed at 1:100 I get 3 horizontal lines. Clearly there is an error in the iterations after line 94 as the buffers up to that point change:

Output of debug print statement at end

Does anyone see where I have yet to find more errors?



//+------------------------------------------------------------------+ //| SmoothedHAADX.mq4 | //| Copyright 2021, andydoc1@gmail.com | //| mailto://andydoc1@gmail.com | //+------------------------------------------------------------------+

#property copyright "Copyright 2020, andydoc1" #property link "mailto://andydoc1@gmail.com" #property version "1.00" #property strict #property indicator_separate_window #property indicator_buffers 9 #property indicator_color1 White #property indicator_width1 2 #property indicator_color2 Green #property indicator_width2 1 #property indicator_style2 1 #property indicator_color3 Red #property indicator_width3 1 #property indicator_style3 2 //---- buffers double ExtMapBufferADX[]; //ADX double ExtMapBufferPDI[]; //+DI double ExtMapBufferNDI[]; //-DI double ExtMapBufferHAO[]; //O double ExtMapBufferHAC[]; //C double ExtMapBufferTR[]; //tr double ExtMapBufferPDM[]; //plusDM double ExtMapBufferMDM[]; //minusDM double ExtMapBufferADXlead[]; //adxlead double up, down, trur, sum, diff; extern int ADXperiod=14; extern int ADXsmperiod =14; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //---- indicators SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,ExtMapBufferADX); //ADX SetIndexLabel(0,"ADX"); SetIndexStyle(1,DRAW_LINE); SetIndexBuffer(1,ExtMapBufferPDI); SetIndexLabel(1,"Plus DI"); SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,ExtMapBufferNDI); SetIndexLabel(2,"Minus DI"); SetIndexBuffer(3,ExtMapBufferHAO); //ADX SetIndexBuffer(4,ExtMapBufferHAC); //ADX SetIndexBuffer(5,ExtMapBufferTR); //ADX SetIndexBuffer(6,ExtMapBufferPDM); //ADX SetIndexBuffer(7,ExtMapBufferMDM); //ADX SetIndexBuffer(8,ExtMapBufferADXlead); //ADX IndicatorShortName("ADX("+IntegerToString(ADXperiod)+"), based on Heiken Ashi Candles, smoothed over "+IntegerToString(14)+" periods."); //---- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ 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(); if(counted_bars < 0) return(-1); if(counted_bars>0) counted_bars--; int limit=Bars-counted_bars; if(counted_bars==0) limit-=1+ADXperiod+ADXsmperiod; //---- main loop for(int i=0; i<limit-1; i++) { ExtMapBufferHAO[i] = (Open[i+1]+Close[i+1])/2;// HA modified open ExtMapBufferHAC[i] = (Open[i]+High[i]+Low[i]+Close[i])/4;// HA modified close } for(int i=0; i<limit-2; i++) { up = High[i]-High[i+1]; //up = change(high) down = -(Low[i]-Low[i+1]); //down = -change(low) ExtMapBufferPDM[i] = !up ? NULL : (up > down && up > 0 ? up : 0); //plusDM = na(up) ? na : (up > down and up > 0 ? up : 0) ExtMapBufferMDM[i] = !down ? NULL : (down > up && down > 0 ? down : 0);//minusDM = na(down) ? na : (down > up and down > 0 ? down : 0) ExtMapBufferTR[i] = MathMax(High[i] - Low[i+1], MathMax(MathAbs(High[i] - ExtMapBufferHAC[i+1]), MathAbs(Low[i+1] - ExtMapBufferHAC[i+1])));// --tr definition for line 91 }// -- line94 for(int i=limit-2; i>=0; i--) { trur = iMAOnArray(ExtMapBufferTR,0,ADXperiod,0,1,0);//trur = rma(tr, len) ExtMapBufferPDI[i] = !(100*iMAOnArray(ExtMapBufferPDM,0,ADXperiod,0,1,0)/trur) ? ExtMapBufferPDI[i+1] : 100*iMAOnArray(ExtMapBufferPDM,0,ADXperiod,0,1,0)/trur;//plus = fixnan(100 * rma(plusDM, ADXperiod) / trur) ExtMapBufferNDI[i] = !(100*iMAOnArray(ExtMapBufferMDM,0,ADXperiod,0,1,0)/trur) ? ExtMapBufferNDI[i+1] : 100*iMAOnArray(ExtMapBufferMDM,0,ADXperiod,0,1,0)/trur;//minus = fixnan(100 * rma(minusDM, len) / trur) sum = ExtMapBufferPDI[i] + ExtMapBufferNDI[i] == 0.0 ? 1.0 : ExtMapBufferPDI[i] + ExtMapBufferNDI[i];//sum == 0 ? 1 : sum --from line line 96 diff = MathAbs(ExtMapBufferPDI[i] - ExtMapBufferNDI[i]);//abs(plus - minus) --from line line 96 ExtMapBufferADXlead[i] = diff / sum;//abs(plus - minus) / (sum == 0 ? 1 : sum) --from line line 100 } for(int i=limit-2; i>=0; i--) { ExtMapBufferADX[i] = 100*iMAOnArray(ExtMapBufferADXlead,0,ADXsmperiod,0,1,0);//adx = 100 * rma(abs(plus - minus) / (sum == 0 ? 1 : sum), adxlen) } for(int i = 0;i<100;i++) { //Print(ExtMapBufferHAO[i],",",ExtMapBufferHAC[i],",",ExtMapBufferPDM[i],",",ExtMapBufferMDM[i],",",ExtMapBufferTR[i],",",ExtMapBufferPDI[i],",",ExtMapBufferNDI[i],",",ExtMapBufferADXlead[i],",",ExtMapBufferADX[i]); } //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+

Any ideas on how to get this working? The indicator is the basis for a very profitable backtest in Tradingview and I will be happy to share the strategy with anyone who helps get this working...

The checks a trading robot must pass before publication in the Market
The checks a trading robot must pass before publication in the Market
  • www.mql5.com
Before any product is published in the Market, it must undergo compulsory preliminary checks in order to ensure a uniform quality standard. This article considers the most frequent errors made by developers in their technical indicators and trading robots. An also shows how to self-test a product before sending it to the Market.
Andrew Thompson
353
Andrew Thompson  
Before anyone jumps on me, I found the noob mistake courtesy of this that I had overlooked: 

"in the end I just forgot to call SetIndexBuffer(...) for this buffer array in onInit() {...} of my indicator."

Feel free to modify, correct the mistake and use.


Ernst Van Der Merwe
7778
Ernst Van Der Merwe  
#property strict
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_plots   3
//--- plot ADX
#property indicator_label1  "ADX"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- plot PDI
#property indicator_label2  "+DI"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrMediumSeaGreen
#property indicator_style2  STYLE_DOT
#property indicator_width2  1
//--- plot NDI
#property indicator_label3  "-DI"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrTomato
#property indicator_style3  STYLE_DOT
#property indicator_width3  1
//---
input int      ADXPeriod=14;
input int      ADXSMPeriod=14;   
//--- indicator buffers
double         ADXBuffer[];
double         PDIBuffer[];
double         NDIBuffer[];
double         HAOBuffer[];
double         HACBuffer[];
double         TRGBuffer[];
double         PDMBuffer[];
double         NDMBuffer[];
double         ADLBuffer[];
//---
double up, down, trur, pdi, ndi, sum, diff;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,ADXBuffer);
   SetIndexBuffer(1,PDIBuffer);
   SetIndexBuffer(2,NDIBuffer);
   IndicatorBuffers(8);
   SetIndexBuffer(3,HACBuffer);
   SetIndexBuffer(4,TRGBuffer);
   SetIndexBuffer(5,PDMBuffer);
   SetIndexBuffer(6,NDMBuffer);
   SetIndexBuffer(7,ADLBuffer);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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 begin=(rates_total!=prev_calculated)?rates_total-1-prev_calculated:0;
   if(prev_calculated<1)
     {
      HACBuffer[begin]=(open[begin]+high[begin]+low[begin]+close[begin])/4;
      PDMBuffer[begin]=NDMBuffer[begin]=TRGBuffer[begin]=PDIBuffer[begin]=NDIBuffer[begin]=ADLBuffer[begin]=ADXBuffer[begin]=0; 
      begin--;
     }
//---- main loop
   for(int i=begin; i>=0; i--)
      HACBuffer[i] = (open[i]+high[i]+low[i]+close[i])/4;// HA modified close
//---
   for(int i=begin; i>=0; i--)
     {
      up = high[i]-high[i+1]; //up = change(high) 
      down = -(low[i]-low[i+1]); //down = -change(low)
      PDMBuffer[i] = !up ? NULL : (up > down && up > 0 ? up : 0); //plusDM = na(up) ? na : (up > down and up > 0 ? up : 0)
      NDMBuffer[i] = !down ? NULL : (down > up && down > 0 ? down : 0);//minusDM = na(down) ? na : (down > up and down > 0 ? down : 0)
      TRGBuffer[i] = MathMax(high[i],HACBuffer[i+1])-MathMin(low[i],HACBuffer[i+1]);// --tr definition for line 91
     }
//---
   for(int i=begin; i>=0; i--)
     {
      trur = iMAOnArray(TRGBuffer,rates_total,ADXPeriod,0,MODE_EMA,i);//trur = rma(tr, len) -- line 91
      PDIBuffer[i] = (!(pdi=100*iMAOnArray(PDMBuffer,rates_total,ADXPeriod,0,MODE_EMA,i)) || !trur) ? PDIBuffer[i+1] : pdi/trur;//plus = fixnan(100 * rma(plusDM, ADXperiod) / trur)
      NDIBuffer[i] = (!(ndi=100*iMAOnArray(NDMBuffer,rates_total,ADXPeriod,0,MODE_EMA,i)) || !trur) ? NDIBuffer[i+1] : ndi/trur;//minus = fixnan(100 * rma(minusDM, len) / trur)
      sum = PDIBuffer[i] + NDIBuffer[i];//sum == 0 ? 1 : sum --from line 96
      diff = MathAbs(PDIBuffer[i] - NDIBuffer[i]);//abs(plus - minus) --from line 96
      ADLBuffer[i] = sum ? diff / sum : diff / 1;//abs(plus - minus) / (sum == 0 ? 1 : sum) -- line 96 from line line 100
     }
//---
   for(int i=begin; i>=0; i--)
      ADXBuffer[i] = 100*iMAOnArray(ADLBuffer,rates_total,ADXSMPeriod,0,MODE_EMA,i);//adx = 100 * rma(abs(plus - minus) / (sum == 0 ? 1 : sum), adxlen) // line 100
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
Andrew Thompson
353
Andrew Thompson  

So here is the amended code, still not producing equivalent plots - see pics. Any ideas?

Metatrader

Tradingview

//+------------------------------------------------------------------+

//|                                                SmoothedHAADX.mq4 |

//|                               Copyright 2021, andydoc1@gmail.com |

//|                                      mailto://andydoc1@gmail.com |

//+------------------------------------------------------------------+

//---- port of DMI from Tradingview

//---- (https://www.tradingview.com/pine-script-reference/v4/#fun_dmi)

//---- applied to Heiken-Ashi Candles.

//---- Still not correct as plots differently.

//---- 

//---- PINE source code for DMI:

//---- up = change(security(heikinashi(syminfo.tickerid), timeframe.period, high))

//---- down = -change(security(heikinashi(syminfo.tickerid), timeframe.period, low))

//---- plusDM = na(up) ? na : (up > down and up > 0 ? up : 0)

//---- minusDM = na(down) ? na : (down > up and down > 0 ? down : 0)

//---- trur = rma(tr, di_len)

//---- plus = fixnan(100 * rma(plusDM, di_len) / trur)

//---- minus = fixnan(100 * rma(minusDM, di_len) / trur)

//---- sum = plus + minus

//---- adx = 100 * rma(abs(plus - minus) / (sum == 0 ? 1 : sum), adx_len)

//---- 

//---- na fills an empty value with NULL

//---- tr is true range

//---- rma is EMA

//---- fixnan fills empty value with previous value

//---- 

//---- original inputs:

//---- di_len = ADX period

//---- adx_len = ADX Smoothing period



#property copyright "Copyright 2020, andydoc1"

#property link      "mailto://andydoc1@gmail.com"

#property version   "1.00"

#property strict



#property indicator_separate_window

#property indicator_buffers 9

#property indicator_color1 White

#property indicator_width1 2

#property indicator_color2 Green

#property indicator_width2 1

#property indicator_style2 1

#property indicator_color3 Red

#property indicator_width3 1

#property indicator_style3 2



//---- buffers

double ExtMapBufferADX[]; //ADX

double ExtMapBufferPDI[]; //+DI

double ExtMapBufferNDI[]; //-DI

double ExtMapBufferHAO[]; //O

double ExtMapBufferHAC[]; //C

double ExtMapBufferTR[]; //tr

double ExtMapBufferPDM[]; //plusDM

double ExtMapBufferMDM[]; //minusDM

double ExtMapBufferDIratio[]; //DI ratio

double up, down, trur, sum, diff;



extern int ADXperiod=14;

extern int ADXsmperiod =14;

//+------------------------------------------------------------------+

//| Custom indicator initialization function                         |

//+------------------------------------------------------------------+

int OnInit()

  {

//---- indicators

   SetIndexStyle(0,DRAW_LINE);

   SetIndexBuffer(0,ExtMapBufferADX); //ADX

   SetIndexLabel(0,"ADX"); 

   SetIndexStyle(1,DRAW_LINE);

   SetIndexBuffer(1,ExtMapBufferPDI); //+DI

   SetIndexLabel(1,"Plus DI");

   SetIndexStyle(2,DRAW_LINE);

   SetIndexBuffer(2,ExtMapBufferNDI); //-DI

   SetIndexLabel(2,"Minus DI");

   SetIndexBuffer(3,ExtMapBufferHAO); //Heiken-Ashi Open

   SetIndexBuffer(4,ExtMapBufferHAC); //Heiken-Ashi Close

   SetIndexBuffer(5,ExtMapBufferTR); //True Range

   SetIndexBuffer(6,ExtMapBufferPDM); //+DM

   SetIndexBuffer(7,ExtMapBufferMDM); //-DM

   SetIndexBuffer(8,ExtMapBufferDIratio); //Ratio of Diff(+DI, -DI):Sum(+DI, -DI)

   IndicatorShortName("ADX("+IntegerToString(ADXperiod)+"), based on Heiken Ashi Candles, smoothed over "+IntegerToString(14)+" periods.");

//----

   return(INIT_SUCCEEDED);

  }

//+------------------------------------------------------------------+

//| Custom indicator iteration function                              |

//+------------------------------------------------------------------+

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();

   if(counted_bars < 0)  return(-1);

   if(counted_bars>0) counted_bars--;

   int limit=Bars-counted_bars;

   if(counted_bars==0) limit-=1+ADXperiod+ADXsmperiod;

//---- main loop

   for(int i=limit-1; i>=0; i--)

     {

      ExtMapBufferHAO[i] = (Open[i+1]+Close[i+1])/2;

      ExtMapBufferHAC[i] = (Open[i]+High[i]+Low[i]+Close[i])/4;

     }

   for(int i=limit-1; i>=0; i--)

     {

      up = High[i]-High[i+1]; //up = change(high) 

      down = -(Low[i]-Low[i+1]); //down = -change(low)

      ExtMapBufferPDM[i] = !up ? NULL : (up > down && up > 0 ? up : 0); //+DM = na(up) ? na : (up > down and up > 0 ? up : 0)

      ExtMapBufferMDM[i] = !down ? NULL : (down > up && down > 0 ? down : 0);//-DM = na(down) ? na : (down > up and down > 0 ? down : 0)

      ExtMapBufferTR[i] = MathMax(High[i] - Low[i+1], MathMax(MathAbs(High[i] - ExtMapBufferHAC[i+1]), MathAbs(Low[i+1] - ExtMapBufferHAC[i+1])));// --tr definition for line 122

     } // -- line 94

   for(int i=limit-(2+ADXperiod); i>=0; i--)

     {

      trur = iMAOnArray(ExtMapBufferTR,0,ADXperiod,0,1,i);//trur = rma(tr, len) -- line 122

      ExtMapBufferPDI[i] = !(100*iMAOnArray(ExtMapBufferPDM,0,ADXperiod,0,1,i)/trur) ? ExtMapBufferPDI[i+1] : 100*iMAOnArray(ExtMapBufferPDM,0,ADXperiod,0,1,i)/trur;//plus = fixnan(100 * rma(plusDM, ADXperiod) / trur)

      ExtMapBufferNDI[i] = !(100*iMAOnArray(ExtMapBufferMDM,0,ADXperiod,0,1,i)/trur) ? ExtMapBufferNDI[i+1] : 100*iMAOnArray(ExtMapBufferMDM,0,ADXperiod,0,1,i)/trur;//minus = fixnan(100 * rma(minusDM, len) / trur)

      sum = ExtMapBufferPDI[i] + ExtMapBufferNDI[i] == 0.0 ? 1.0 : ExtMapBufferPDI[i] + ExtMapBufferNDI[i];//sum == 0 ? 1 : sum -- for line 127

      diff = MathAbs(ExtMapBufferPDI[i] - ExtMapBufferNDI[i]);//abs(plus - minus) -- for line 127

      ExtMapBufferDIratio[i] = diff / sum;//abs(plus - minus) / (sum == 0 ? 1 : sum) -- line 127 for line 131

     }

   for(int i=limit-(3+ADXperiod+ADXsmperiod); i>=0; i--)

     {

      ExtMapBufferADX[i] = 100*iMAOnArray(ExtMapBufferDIratio,0,ADXsmperiod,0,1,i);//adx = 100 * rma(abs(plus - minus) / (sum == 0 ? 1 : sum), adxlen) -- line 131

     }

   //--- Debugging

   for(int i = 0;i<100;i++)

      {

      //Print(ExtMapBufferHAO[i],",",ExtMapBufferHAC[i],",",ExtMapBufferPDM[i],",",ExtMapBufferMDM[i],",",ExtMapBufferTR[i],",",ExtMapBufferPDI[i],",",ExtMapBufferNDI[i],",",ExtMapBufferDIratio[i],",",ExtMapBufferADX[i]);

      }

//--- return value of prev_calculated for next call

   return(rates_total);

  }

//+------------------------------------------------------------------+


Andrew Thompson
353
Andrew Thompson  
Ernst Van Der Merwe:

Thank you, Ernst, for tidying that up - and well done for spotting that the HA open is not actually used :) 

However, the ADX line is still not as smooth as that of Trading View...

Ernst Van Der Merwe
7778
Ernst Van Der Merwe  
Andrew Thompson:

Thank you, Ernst, for tidying that up - and well done for spotting that the HA open is not actually used :) 

However, the ADX line is still not as smooth as that of Trading View...

You're welcome. Should probably have to bring the open buffer back then:)

//---- main loop
   for(int i=begin; i>=0; i--)
     {
      HAOBuffer[i]=haop=(HAOBuffer[i+1]+HACBuffer[i+1])/2;
      HACBuffer[i]=hacl=(open[i]+high[i]+low[i]+close[i])/4;// HA modified close
      HAHBuffer[i]=hahi=MathMax(high[i],haop,hacl);
      HALBuffer[i]=halo=MathMin(low[i],haop,hacl);
      up = hahi-HAHBuffer[i+1]; //up = change(high) 
      down = -(halo-HALBuffer[i+1]); //down = -change(low)
      up = !up ? NULL : (up > down && up > 0 ? up : 0); //plusDM = na(up) ? na : (up > down and up > 0 ? up : 0)
      down = !down ? NULL : (down > up && down > 0 ? down : 0);//minusDM = na(down) ? na : (down > up and down > 0 ? down : 0)
      PDMBuffer[i] = iEMA(up,PDMBuffer[i+1],ADXPeriod);
      NDMBuffer[i] = iEMA(down,NDMBuffer[i+1],ADXPeriod); 
      trur = MathMax(hahi,HACBuffer[i+1])-MathMin(halo,HACBuffer[i+1]);//or MathMax(MathMax(MathAbs(high[i]-low[i]),MathAbs(high[i]-HACBuffer[i+1])),MathAbs(low[i]-HACBuffer[i+1]));
      ATRBuffer[i] = iEMA(trur,ATRBuffer[i+1],ADXPeriod);
      PDIBuffer[i] = pdi = (!(pd=100*PDMBuffer[i]) || !(trur=ATRBuffer[i])) ? PDIBuffer[i+1] : pd/trur;//plus = fixnan(100 * rma(plusDM, ADXperiod) / trur)
      NDIBuffer[i] = ndi = (!(nd=100*NDMBuffer[i]) || !(trur=ATRBuffer[i])) ? NDIBuffer[i+1] : nd/trur;//minus = fixnan(100 * rma(minusDM, len) / trur)
      sum = pdi + ndi;//sum == 0 ? 1 : sum --from line 96
      diff = MathAbs(pdi - ndi);//abs(plus - minus) --from line 96
      sum = sum ? diff / sum : diff / 1;//abs(plus - minus) / (sum == 0 ? 1 : sum) -- line 96 from line line 100
      ADLBuffer[i] = adl = iEMA(sum,ADLBuffer[i+1],ADXPeriod);
      ADXBuffer[i] = 100*adl;//adx = 100 * rma(abs(plus - minus) / (sum == 0 ? 1 : sum), adxlen) // line 100
     }
Files:
Andrew Thompson
353
Andrew Thompson  
Ernst Van Der Merwe:

You're welcome. Should probably have to bring the open buffer back then:)

Not really changing the plot shape

See the peak in the screenshot - matches nicely with the peak on Tradingview, but the nadir circled in red does not reflect the nice smooth descent on TV

MT4

TV