MQL4 iCustom Indicators Not Working in Backtesting

 

All,

 

I'm having a problem with iCustom indicators.  They work when I debug, and they work in live trading with a demo account, but when backtesting they return the EMPTY_VALUE or they return "0" when I SetIndexEmptyValue(0,0).

 

Here is how I'm calling them:

 

priceSignalCurrent=iCustom(NULL,0,"PriceAction",0,0);

volumeSignalCurrent=iCustom(NULL,0,"WeightAction",0,0);

rsiSignalCurrent=iCustom(NULL,0, "StochasticAction",0,0); 

 

From PriceAction:

SetIndexBuffer(0,pSignal);

SetIndexStyle(0,DRAW_LINE,STYLE_SOLID,2);                       //SetIndexStyle(1,DRAW_LINE);

SetIndexEmptyValue(0, 0); 

 

From WeightAction:

SetIndexBuffer(0,tpeak);

SetIndexStyle(0,DRAW_LINE,STYLE_SOLID,2);                       //SetIndexStyle(1,DRAW_LINE);

SetIndexEmptyValue(0, 0); 

 

From StochasticAction:

SetIndexBuffer(0,StochRSISignal);

SetIndexStyle(0,DRAW_LINE,STYLE_SOLID,2);                       //SetIndexStyle(1,DRAW_LINE);

SetIndexEmptyValue(0, 0); 

 

Confusingly, StochasticAction returns the correct values, but WeightAction and PriceAction do not return the correct values in backtesting (no value, actually), but they work fine in live mode.  I really need some assistance.

 

Thank you. 

 

Anyone?  I've spent hours working on this and I can't find a solution. Here is the code for the "PriceAction" function that I call.

 

//+------------------------------------------------------------------+
//|                                               stochrsi.mq4       |
//|                                            By BPLTURNER          |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "BPLTURNER"
#property link      "no."
#property version   "1.00"
#property strict

#include <MovingAverages.mqh>

#property indicator_buffers 100
#property indicator_chart_window
#property indicator_color1 White
#property indicator_color2 HotPink
#property indicator_color3 LightBlue
#property indicator_color4 LimeGreen 
#property indicator_color5 Red  
#property indicator_color6 Red  
#property indicator_color7 Aqua 
#property indicator_color8 Yellow 
#property indicator_color9 Red 

//--- input parameters
//Price calculation parameters
extern int VWAP_Period = 2; //VWAP Period
extern int EMA_Period = 15; //EMA Period

//--- buffers
double p_VWAP[];
double p_EMA[];
double p_EMAVWAP[];
double p_MA_Diff[];
double p_Signal[];

int init()
{
IndicatorShortName("Custom Price Action");
IndicatorBuffers(20);

//---- Drawing settings
SetIndexBuffer(0,p_Signal);
SetIndexStyle(0,DRAW_LINE,STYLE_SOLID,2);                       //SetIndexStyle(1,DRAW_LINE);

SetIndexBuffer(1,p_VWAP);
SetIndexStyle(1,DRAW_LINE,STYLE_SOLID,2);                       //SetIndexStyle(1,DRAW_LINE);

SetIndexBuffer(2,p_EMA);
SetIndexStyle(2,DRAW_LINE,STYLE_SOLID,2);                       //SetIndexStyle(1,DRAW_LINE);

SetIndexBuffer(3,p_EMAVWAP);
SetIndexStyle(3,DRAW_LINE,STYLE_SOLID,2);                       //SetIndexStyle(1,DRAW_LINE);

SetIndexBuffer(4,p_MA_Diff);
SetIndexStyle(4,DRAW_LINE,STYLE_SOLID,2);                       //SetIndexStyle(1,DRAW_LINE);

return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int start()
{

int counted_bars=IndicatorCounted();
int limit = Bars-counted_bars-1;

//+------------------------------------------------------------------+
//| Price calculations below
//+------------------------------------------------------------------+
for(int i=limit; i>=0; i--) p_EMA[i] = iMA(NULL,0,EMA_Period,0,MODE_EMA,PRICE_TYPICAL,i); //EMA of price

for (int i=limit-VWAP_Period;i>=0;i--) //Volume weighted average price
{ 
         double s1 = 0;
         double s2 = 0;
         double r = 0;
         for (int k=0;k<=VWAP_Period;k++){
            //s1 = s1 + ((Close[i+k] + Low[i+k] + High[i+k])/3)*Volume[i+k];
            s1 = s1 + ((Low[i+k] + High[i+k])/2)*Volume[i+k];
            s2=s2+Volume[i+k];
         }
         r = s1/s2;
         p_VWAP[i]=r;
}
for(int i=limit; i>=0; i--) p_EMAVWAP[i] = iMAOnArray(p_VWAP,0,EMA_Period,0,MODE_EMA,i); //EMA of VWAP

for(int i=limit-VWAP_Period;i>=0;i--) //Difference between EMA of VWAP and VWAP
{
      
   p_MA_Diff[i] = p_EMAVWAP[i] - p_VWAP[i];
}

for(int i=limit-VWAP_Period;i>=0;i--) //If VWAP < EMA of VWAP, buy=-1, if VWAP>EMA of VWAP, sell=1
{
   if(p_MA_Diff[i]>0) 
   {
      p_Signal[i] = -1*p_VWAP[ArrayMinimum(High, 100, 0)];
   }
   else if(p_MA_Diff[i]<0)
   {
      p_Signal[i] = 1*p_VWAP[ArrayMaximum(Low, 100, 0)];
   }
   else p_Signal[i] = 0;
   
}

//----
return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
int deinit()
{
//----

//----
return(0);
}
//+------------------------------------------------------------------+

 

I think the error comes from the calculation of p_VWAP, but I can't find an obvious error.  Again, this works just fine in live mode or indicator mode, but does NOT work in backtesting.  Someone please help! 

 

Here are a few points that can help clear up the problem:

  1. You are not using 100 buffers! So the following is just wasteful of RAM and incorrect.
    #property indicator_buffers 100 // You are only using 5 buffers
  2. You are assuming that there will always be 100 bars of data available to you, which is not always true. You should check the number of available bars. This is especially important, when you are generating your FXT files for back-tests with 3rd party generators, as they don't provide the extra 1000 bar at the beginning of the test range.
    ... ArrayMinimum(High, 100, 0) ...
    ... ArrayMaximum(Low,  100, 0) ...
  3. It is best to use the updated modern style of MQL4+ code which is more streamlined and uses the OnInit() and OnCalculate() event handlers. This is especially valid, since you are using the updated method of Moving Average calculations via the <MovingAverages.mqh> include header file and also because you are using the strict compilation.
    #property strict
    
    #include <MovingAverages.mqh>
 

Hi FMIC,

 

Thank you for your comments.  I have corrected the problems you described.

 

Below is the new code:

 

//+------------------------------------------------------------------+
//|                                               stochrsi.mq4       |
//|                                            By BPLTURNER          |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "BPLTURNER"
#property link      "no."
#property version   "1.00"
#property strict

#include <MovingAverages.mqh>

#property indicator_buffers 5
#property indicator_chart_window
#property indicator_color1 White
#property indicator_color2 HotPink
#property indicator_color3 LightBlue
#property indicator_color4 LimeGreen 
#property indicator_color5 Red  


//--- input parameters
//Price calculation parameters
extern int VWAP_Period = 2; //VWAP Period
extern int EMA_Period = 15; //EMA Period

//--- buffers
double p_VWAP[];
double p_EMA[];
double p_EMAVWAP[];
double p_MA_Diff[];
double p_Signal[];

int OnInit()
{
IndicatorShortName("Custom Price Action");
IndicatorBuffers(20);

//---- Drawing settings
SetIndexBuffer(0,p_Signal);
SetIndexStyle(0,DRAW_LINE,STYLE_SOLID,2);                       //SetIndexStyle(1,DRAW_LINE);

SetIndexBuffer(1,p_VWAP);
SetIndexStyle(1,DRAW_LINE,STYLE_SOLID,2);                       //SetIndexStyle(1,DRAW_LINE);

SetIndexBuffer(2,p_EMA);
SetIndexStyle(2,DRAW_LINE,STYLE_SOLID,2);                       //SetIndexStyle(1,DRAW_LINE);

SetIndexBuffer(3,p_EMAVWAP);
SetIndexStyle(3,DRAW_LINE,STYLE_SOLID,2);                       //SetIndexStyle(1,DRAW_LINE);

SetIndexBuffer(4,p_MA_Diff);
SetIndexStyle(4,DRAW_LINE,STYLE_SOLID,2);                       //SetIndexStyle(1,DRAW_LINE);

return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator initialization 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();
int limit = Bars(Symbol(),0)-counted_bars-1;

//+------------------------------------------------------------------+
//| Price calculations below
//+------------------------------------------------------------------+
for(int i=limit; i>=0; i--) p_EMA[i] = iMA(NULL,0,EMA_Period,0,MODE_EMA,PRICE_TYPICAL,i); //EMA of price

for (int i=limit-VWAP_Period;i>=0;i--) //Volume weighted average price
{ 
         double s1 = 0;
         double s2 = 0;
         double r = 0;
         for (int k=0;k<=VWAP_Period;k++){
            //s1 = s1 + ((Close[i+k] + Low[i+k] + High[i+k])/3)*Volume[i+k];
            s1 = s1 + ((Low[i+k] + High[i+k])/2)*Volume[i+k];
            s2=s2+Volume[i+k];
         }
         r = s1/s2;
         p_VWAP[i]=r;
}
for(int i=limit; i>=0; i--) p_EMAVWAP[i] = iMAOnArray(p_VWAP,0,EMA_Period,0,MODE_EMA,i); //EMA of VWAP

for(int i=limit-VWAP_Period;i>=0;i--) //Difference between EMA of VWAP and VWAP
{
      
   p_MA_Diff[i] = p_EMAVWAP[i] - p_VWAP[i];
}

for(int i=limit-VWAP_Period;i>=0;i--) //If VWAP < EMA of VWAP, buy=-1, if VWAP>EMA of VWAP, sell=1
{
   if(p_MA_Diff[i]>0) 
   {
      p_Signal[i] = -1;
   }
   else if(p_MA_Diff[i]<0)
   {
      p_Signal[i] = 1;
   }
   else p_Signal[i] = 0;
   
}

//----
return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//----
//----
}
//+------------------------------------------------------------------+

 

I still have the same problem.  I am calling this from a custom EA that references it with the following code tidbit:

 

   priceSignalCurrent=iCustom(NULL, 0, "PriceAction", 0,1);

 

Do you have any other suggestions?  I am still returning EMPTY_VALUE every time I query the indicator.

 

Thank you. 

 
bplturner: Thank you for your comments.  I have corrected the problems you described.

I still have the same problem.  I am calling this from a custom EA that references it with the following code tidbit:

Do you have any other suggestions?  I am still returning EMPTY_VALUE every time I query the indicator.

You cannot mix the Old style and the New style!

So, you have to use the parameters passed to the OnCalculate() function instead of the older variables. So you will have to use "rates_total", "prev_calculated", "time[]", "open[]", "high[]", "low[]", "close[]" and "tick_volume[]" instead of the older "Bars", "IndicatorCounted()", "Time[]", "Open[]", "High[]", "Low[]", "Close[]" or "Volume[]".

Also, you will have to make sure of the Series Flag for all buffers and arrays parameters are in the same direction because for OnCalculate() the default for the event handler is now the non-series ordering.

Also, you cannot just "return(0)" for the OnCalculate() function. You have to return the actual number of elements that have been calculated by your code.

Please read the linked documentation on OnCalculate() and also look at the example code provided.

EDIT: Also, if you are using the <MovingAverages.mqh>, you should use it appropriately and not mix it with the use of iMAOnArray(), especially with the New style of MQL4 where the array parameters are not in Series Array ordering by default.

EDIT2: Before moving on to the new style, first test your original old style code, with regards to points 1 & 2, to make sure that is not the root cause of the problem. Once that is fixed, you can then change your code to the new style because it involves many changes. So first, fix point 1 and 2 on the old style. Leave point 3 for later.

 

Hi FMIC,

 

I fixed points 1 and 2 and it didn't fix my error.  I will now try to convert it over to the new style, but it seems like an entirely different philosophy. 

 
bplturner: I fixed points 1 and 2 and it didn't fix my error.  I will now try to convert it over to the new style, but it seems like an entirely different philosophy. 
Don't try the new style yet, until you fix the old one. Post your code (old style) with the fixes.
 
bplturner: I fixed points 1 and 2 and it didn't fix my error.  I will now try to convert it over to the new style, but it seems like an entirely different philosophy. 

Here is another point related to point 2. You are assuming that there are enough Bars to cover the iMA for the Moving average, which might be the case.

for(int i=limit; i>=0; i--) p_EMA[i] = iMA(NULL,0,EMA_Period,0,MODE_EMA,PRICE_TYPICAL,i); //EMA of price (this could return the wrong values, if there is not enough bars)
 
  1.          for (int k=0;k<=VWAP_Period;k++){
                //s1 = s1 + ((Close[i+k] + Low[i+k] + High[i+k])/3)*Volume[i+k];
                s1 = s1 + ((Low[i+k] + High[i+k])/2)*Volume[i+k];
                s2=s2+Volume[i+k];
             }
             r = s1/s2;
             p_VWAP[i]=r;
    You are summing VWAP_Period + 1 values. Drop the equals.
  2. bplturner they return the EMPTY_VALUE or they return "0" when I SetIndexEmptyValue(0,0).
    for (int i=limit-VWAP_Period;i>=0;i--) //Volume weighted average price
    After the first run, limit will always be zero and your loop never runs. Do your lookbacks correctly.
    Lookback for p_EMA is the EMA length.
    for(int i=limit; i>=0; i--) p_EMA[i] = iMA(NULL,0,EMA_Period,0,MODE_EMA,PRICE_TYPICAL,i); //EMA of price
    Lookback for p_VWAP is VWAP_Period  - 1
    for (int k=0;k < VWAP_Period;k++){
    Lookback for p_EMAVARP is  ema length + p_VWAP lookback.
    for(int i=limit; i>=0; i--) p_EMAVWAP[i] = iMAOnArray(p_VWAP,0,EMA_Period,0,MODE_EMA,i); //EMA of VWAP
    
    Lookback for p_MA_Diff and p_Signal is the Maximum of P_EMA lookback or p_EMAVWAP lookback.
     
 

Thank you very much for your suggestions.  I highly appreciate it. 

 

This is the newest code, and I still have problems with iCustom.  I have followed the guidelines of FMIC and WHRoeder above.  Any additional guidance would be helpful.

 

//+------------------------------------------------------------------+
//|                                            Price Action          |
//|                                            By BPLTURNER          |
//+------------------------------------------------------------------+
#property copyright "BPLTURNER"
#property link      "no."
#property version   "1.00"
#property strict

#include <MovingAverages.mqh>

#property indicator_buffers 5
#property indicator_chart_window
#property indicator_color1 White
#property indicator_color2 HotPink
#property indicator_color3 LightBlue
#property indicator_color4 LimeGreen 
#property indicator_color5 Red  

//--- input parameters
//Price calculation parameters
extern int VWAP_Period = 2; //VWAP Period
extern int EMA_Period = 15; //EMA Period

//Lookback definitions
int EMA_l = EMA_Period;
int VWAP_l= VWAP_Period-1;
int EMAVWAP_l = VWAP_Period-1+EMA_Period;
int MA_Diff_l = MathMax(EMA_Period, VWAP_Period-1+EMA_Period);

//--- buffers
double p_VWAP[];
double p_EMA[];
double p_EMAVWAP[];
double p_MA_Diff[];
double pSignal[];

int init()
{
IndicatorBuffers(5);
//---- Drawing settings
SetIndexBuffer(0,pSignal);
SetIndexStyle(0,DRAW_LINE,STYLE_SOLID,2);                       //SetIndexStyle(1,DRAW_LINE);
SetIndexEmptyValue(0, 0);

SetIndexBuffer(1,p_VWAP);
SetIndexStyle(1,DRAW_LINE,STYLE_SOLID,2);                       //SetIndexStyle(1,DRAW_LINE);

SetIndexBuffer(2,p_EMA);
SetIndexStyle(2,DRAW_LINE,STYLE_SOLID,2);                       //SetIndexStyle(1,DRAW_LINE);

SetIndexBuffer(3,p_EMAVWAP);
SetIndexStyle(3,DRAW_LINE,STYLE_SOLID,2);                       //SetIndexStyle(1,DRAW_LINE);

SetIndexBuffer(4,p_MA_Diff);
SetIndexStyle(4,DRAW_LINE,STYLE_SOLID,2);                       //SetIndexStyle(1,DRAW_LINE);

return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int start()
{

//Lookback calculations
int lookback = EMA_l;
if(VWAP_l > lookback) lookback=VWAP_l;
if(EMAVWAP_l > lookback) lookback = EMAVWAP_l;
if(MA_Diff_l > lookback) lookback = MA_Diff_l;
int counted=IndicatorCounted();
int limit = Bars-1-MathMax(lookback, counted);

//+------------------------------------------------------------------+
//| Price calculations below
//+------------------------------------------------------------------+
for(int i=limit; i>=0; i--) p_EMA[i] = iMA(NULL,0,EMA_Period,0,MODE_EMA,PRICE_TYPICAL,i); //EMA of price

for (int i=0;i<=limit-VWAP_Period;i++) //Volume weighted average price
{ 
      double s1 = 0;
      double s2 = 0;
      double r = 0;
      for (int k=0;k<VWAP_Period;k++){
         s1 = s1 + ((Close[i+k] + Low[i+k] + High[i+k])/3)*Volume[i+k];
         s2=s2+Volume[i+k];
      }
      r = s1/s2;
      p_VWAP[i]=r;
}
for(int i=limit; i>=0; i--) p_EMAVWAP[i] = iMAOnArray(p_VWAP,0,EMA_Period,0,MODE_EMA,i); //EMA of VWAP

for(int i=0;i<=limit-VWAP_Period;i++) //Difference between EMA of VWAP and VWAP
{
p_MA_Diff[i] = p_EMAVWAP[i] - p_VWAP[i];
}

for(int i=0;i<=limit-VWAP_Period;i++) //If VWAP < EMA of VWAP, buy=-1, if VWAP>EMA of VWAP, sell=1
{
   if(p_MA_Diff[i]>0) 
   {
      pSignal[i] = -1;
   }
   else if(p_MA_Diff[i]<0)
   {
      pSignal[i] = 1;
   }
   else pSignal[i] = 0;
   
}

//----
return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
int deinit()
{
//----

//----
return(0);
}
//+------------------------------------------------------------------+

 
bplturner: I still have problems with iCustom. 
Because you didn't follow the link provided and change your lookbacks.
Your code
for(int i = Bars - 1 - MathMax(EMA_l, counted); i >= 0; --i){
for(int i = Bars - 1 - MathMax(VWAP_Period-1, counted); i >= 0; --i){
for(int i = Bars - 1 - MathMax(EMAVWAP_l, counted); i >= 0; --i){
for(int i = Bars - 1 - MathMax(MA_Diff_l, counted); i >= 0; --i){
Each loop is dependent on the previous. You can't use one limit.
for(int i=limit; i>=0; i--) 
for (int i=0;i<=limit-VWAP_Period;i++) 
for(int i=limit; i>=0; i--) 
for(int i=0;i<=limit-VWAP_Period;i++) 
for(int i=0;i<=limit-VWAP_Period;i++) 
Reason: