VWAP Indicator

 

Hi guys,

I'm trying to produce a VWAP indicator over a daily time period using the 'OnCalculate' event handler as most still use the 'start()' version.

After many hours of struggling with this it works about 90%. The indicator does draw on the chart but I get an 'array out of range' error on this line of code:

while(TimeDay(Time[q]) == TimeDay(Time[p]) && !IsStopped())

Also, the indicator doesn't appear to update on a new candle. 

I'm also trying to make it as fast as possible as down at the smaller timeframes where it is most useful there are a lot of iterations because of huge bar numbers.

i'd appreciate any help in optimising the code.

thank you

p.s I will add code for Weekly and Monthly lines later

#property indicator_chart_window             
#property indicator_buffers 4         
#property indicator_color1  clrGreen 
#property indicator_color2  clrMagenta
#property indicator_color3  clrBrown  
#property indicator_width1  2   
#property indicator_width2  2
#property indicator_width3  2             

input bool day = True;        //Show Daily VWAP 
input bool week = True;       //Show Weekly VWAP 
input bool month = True;      //Show Monthly VWAP 
                   
double   DailyBuffer[]; 
double   WeeklyBuffer[]; 
double   MonthlyBuffer[];   
double   PriceBuffer[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+

int OnInit()
{
 
 SetIndexBuffer(0,DailyBuffer);
 SetIndexStyle(0,DRAW_LINE);
 SetIndexLabel(0,"Daily VWAP");
 
 SetIndexBuffer(1,WeeklyBuffer);
 SetIndexStyle(1,DRAW_LINE);
 SetIndexLabel(1,"Weekly VWAP");
 
 SetIndexBuffer(2,MonthlyBuffer);
 SetIndexStyle(2,DRAW_LINE);
 SetIndexLabel(2,"Monthly VWAP");
 
 SetIndexBuffer(3,PriceBuffer,INDICATOR_CALCULATIONS); 
 SetIndexStyle(3,DRAW_NONE);
 
 IndicatorSetInteger(INDICATOR_DIGITS,4); 

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[])
{
 if(prev_calculated==0)
 {
  ArrayInitialize(DailyBuffer,EMPTY_VALUE); 
  ArrayInitialize(WeeklyBuffer,EMPTY_VALUE);
  ArrayInitialize(MonthlyBuffer,EMPTY_VALUE);
  ArrayInitialize(PriceBuffer,EMPTY_VALUE); 
 }


 PriceBuffer[0]=0.0;
 int nbars = 3000;
 int limit = rates_total - prev_calculated;
 double sumPrice,sumVol;

//calculate price buffer
 for(int i=0;i<limit;i++)PriceBuffer[i]=NormalizeDouble((close[i]+high[i]+low[i])/3,2);
 
// ----  CALCULATE DAILY VWAP ---------------------------------------

 int q=0;
 for(int p=0;p<limit;p++)
 {
   q=p;
   sumPrice=0; sumVol=0;
 
  while(TimeDay(Time[q]) == TimeDay(Time[p]) && !IsStopped())
  {
   sumPrice += PriceBuffer[q]*tick_volume[q];
   sumVol += (double)tick_volume[q];
   q++;
  }
   DailyBuffer[p]= sumPrice/sumVol;
 }

return(rates_total -1);
}
//+------------------------------------------------------------------+
 
  1.   while(TimeDay(Time[q]) == TimeDay(Time[p]) && !IsStopped())

    You are accessing the Predefine array (Time), not the array (time) in OnCalculate.

  2. In MT4, buffers and MT4 predefined arrays are all ordered AsSeries. There is a difference between the arrays passed to OnCalculate (e.g. low[]) and the MT4 predefined variables (e.g. Low[].) The passed arrays have no default direction, just like MT5.

    To determine the indexing direction of time[], open[], high[], low[], close[], tick_volume[], volume[] and spread[], call ArrayGetAsSeries(). In order not to depend on default values, you should unconditionally call the ArraySetAsSeries() function for those arrays, which are expected to work with.
              Event Handling Functions - Functions - Language Basics - MQL4 Reference

    You are accessing your buffers as non-series.

  3.  int limit = rates_total - prev_calculated;
    
     for(int i=0;i<limit;i++)
     for(int p=0;p<limit;p++)

    You are computing all bars to up limit. Instead of limit to rates_total-1.
              How to do your lookbacks correctly #9#14 & #19 (2016)

  4. After the first run you loose the values of SumPrice/SumVol. You must restart to the beginning of the day on the next tick.
 
William Roeder #:
  1. You are accessing the Predefine array (Time), not the array (time) in OnCalculate.

  2. In MT4, buffers and MT4 predefined arrays are all ordered AsSeries. There is a difference between the arrays passed to OnCalculate (e.g. low[]) and the MT4 predefined variables (e.g. Low[].) The passed arrays have no default direction, just like MT5.

  3. You are computing all bars to up limit. Instead of limit to rates_total-1.
              How to do your lookbacks correctly #9#14 & #19 (2016)

  4. After the first run you loose the values of SumPrice/SumVol. You must restart to the beginning of the day on the next tick.

Thanks for your reply. I am still horrendously confused.

The documentation states:


Access to Timeseries and Indicator Data

These are functions for working with timeseries and indicators. A timeseries differs from the usual data array by its reverse ordering - elements of timeseries are indexed from the end of an array to its begin (from the most recent data to the oldest ones). 

In your post   How to do your lookbacks correctly #9 you state:

This counts from low number (bar) to high number (bar) which is a Timeseries (as above).

Am I reading this wrong?

 
sd59 #: Am I reading this wrong?

You understand it, you just are not using it.

You haven't set your OnCalculate arrays, predefined arrays, buffers, and loop to match. Is your loop oldest (0) to newest (rates_total-1) or the other way (as-series)? You are assuming the OnCalculate arrays direction.

 
William Roeder #:

You understand it, you just are not using it.

You haven't set your OnCalculate arrays, predefined arrays, buffers, and loop to match. Is your loop oldest (0) to newest (rates_total-1) or the other way (as-series)? You are assuming the OnCalculate arrays direction.

you didn't really answer my confusion - in your example you state it is not a Timeseries whereas the MQL4 docs say that counting from newest data (low bar number) to old data (highest bar number) IS a Timeseries??

4. After the first run you loose the values of SumPrice/SumVol. You must restart to the beginning of the day on the next tick. - sorry don't understand this.

Anyway, this is latest code with suggested implementations as far as I can understand. There is still the 'out of array range' error if I use all bars but not if I use limited bar number i.e 3000. It draws the indicator but it doesn't look right i.e straight lines here and there.

#property indicator_chart_window             
#property indicator_buffers 4    
#property indicator_label1  "VWAP"           
#property indicator_color1  clrGreen 
#property indicator_color2  clrMagenta
#property indicator_color3  clrBrown  
#property indicator_width1  2   
#property indicator_width2  2
#property indicator_width3  2             

input bool day = True;        //Show Daily VWAP 
input bool week = True;       //Show Weekly VWAP 
input bool month = True;      //Show Monthly VWAP 
                   
double   DailyBuffer[]; 
double   WeeklyBuffer[]; 
double   MonthlyBuffer[];   
double   PriceBuffer[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+

int OnInit()
{
 
 SetIndexBuffer(0,DailyBuffer);
 SetIndexStyle(0,DRAW_LINE);
 SetIndexLabel(0,"Daily VWAP");
 
 SetIndexBuffer(1,WeeklyBuffer);
 SetIndexStyle(1,DRAW_LINE);
 SetIndexLabel(1,"Weekly VWAP");
 
 SetIndexBuffer(2,MonthlyBuffer);
 SetIndexStyle(2,DRAW_LINE);
 SetIndexLabel(2,"Monthly VWAP");
 
 SetIndexBuffer(3,PriceBuffer,INDICATOR_CALCULATIONS); 
 SetIndexStyle(3,DRAW_NONE);
 
 IndicatorSetInteger(INDICATOR_DIGITS,4); 

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[])
{
 if(prev_calculated==0)
 {
  ArrayInitialize(DailyBuffer,EMPTY_VALUE); 
  ArrayInitialize(WeeklyBuffer,EMPTY_VALUE);
  ArrayInitialize(MonthlyBuffer,EMPTY_VALUE);
  ArrayInitialize(PriceBuffer,EMPTY_VALUE); 
 }
 ArraySetAsSeries(DailyBuffer,True);
 ArraySetAsSeries(PriceBuffer,True);
 ArraySetAsSeries(tick_volume,True);
 ArraySetAsSeries(time,True);
 ArraySetAsSeries(close,True);
 ArraySetAsSeries(high,True);
 ArraySetAsSeries(low,True);
 
int limit;
 PriceBuffer[0]=0.0;
 int nbars = 3000;
 limit = rates_total - MathMax(1,prev_calculated) - 1;
 double sumPrice,sumVol;

//calculate price buffer
 for(int i=0;i<limit;i++)PriceBuffer[i]=NormalizeDouble((close[i]+high[i]+low[i])/3,2);
 
// ----  CALCULATE DAILY VWAP ---------------------------------------

 int q=0;
 for(int p=0;p<limit;p++)
 {
   
   q=p;
  sumPrice=0; sumVol=0;
 
  while(TimeDay(time[q]) == TimeDay(time[p]) && !IsStopped())
  {
   sumPrice += PriceBuffer[q]*tick_volume[q];
   sumVol += (double)tick_volume[q];
   q++;
  }
  
   if(sumVol != 0)DailyBuffer[p]= sumPrice/sumVol;
   else DailyBuffer[p]=EMPTY_VALUE;

 }

return(rates_total -1);
}
//+------------------------------------------------------------------+
 
  1. sd59 #: you didn't really answer my confusion - in your example you state it is not a Timeseries whereas the MQL4 docs say that counting from newest data (low bar number) to old data (highest bar number) IS a Timeseries??

    Correct, but are your arrays and loop accessing as timeseries or not?

     ArraySetAsSeries(time,True);
     ArraySetAsSeries(close,True);
     ArraySetAsSeries(high,True);
     ArraySetAsSeries(low,True);

    You have now, set the arrays as-timeseries matching buffers.

  2. sd59 #: 4. After the first run you loose the values of SumPrice/SumVol. You must restart to the beginning of the day on the next tick. - sorry don't understand this.
      sumPrice=0; sumVol=0;
      while(TimeDay(time[q]) == TimeDay(time[p]) && !IsStopped()){
       sumPrice += PriceBuffer[q]*tick_volume[q];
       sumVol += (double)tick_volume[q];
       q++;
      }

    VWAP starts at the beginning of the day and increments has the day progresses. Now I see you are summing all bars of the current day. No problem. But you read backwards up to 24 hours. Therefor you get array exceeded.
              How to do your lookbacks correctly #9#14 & #19 (2016)

 
William Roeder #:
  1. Correct, but are your arrays and loop accessing as timeseries or not?

    You have now, set the arrays as-timeseries matching buffers.

  2. VWAP starts at the beginning of the day and increments has the day progresses. Now I see you are summing all bars of the current day. No problem. But you read backwards up to 24 hours. Therefor you get array exceeded.
              How to do your lookbacks correctly #9#14 & #19 (2016)

1. Yes they appear to be.

2. I still do not understand this - TimeDay() returns the number of day i.e 1st, 2nd , 3rd etc; - whatever timeframe you have displayed simply compares bars on the same day - no hours or minutes included.

I put in a few Print statements (code below) + output

int limit;
 PriceBuffer[0]=0.0;
 int nbars = 3000;
 limit = rates_total - prev_calculated;
 //limit=nbars;
 double sumPrice,sumVol;
 Print("prev_calculated = ",prev_calculated);
 Print("limit = ",limit,"    ","rates_total = ",rates_total);
//calculate price buffer
 for(int i=0;i<limit;i++)PriceBuffer[i]=NormalizeDouble((close[i]+high[i]+low[i])/3,2);
 
// ----  CALCULATE DAILY VWAP ---------------------------------------
 Print("time araysize = ",ArraySize(time));
 int q=0;
 for(int p=0;p<limit;p++)
 {
   
   q=p;
  sumPrice=0; sumVol=0;

  while(TimeDay(time[q]) == TimeDay(time[p]) && !IsStopped())
  {
   sumPrice += PriceBuffer[q]*tick_volume[q];
   sumVol += (double)tick_volume[q];
   q++;
  }
  
   if(sumVol != 0)DailyBuffer[p]= sumPrice/sumVol;
   else DailyBuffer[p]=EMPTY_VALUE;
 }
 Print("prev_calculated = ",prev_calculated);
return(rates_total -1);






The 'array out of range' line is:



while(TimeDay(time[q]) == TimeDay(time[p]) && !IsStopped())