MQL5: How to Use Volume Moving Average?

Trading volume provides crucial insights into market activity and conviction behind price movements. While raw volume data can be erratic, applying a moving average to it – creating a Volume Moving Average (VMA) – helps smooth out this data, revealing underlying trends and significant shifts in market participation. This article delves into how to implement and utilize VMA within the MQL5 environment for MetaTrader 5.

What is Volume Moving Average (VMA)?

A Volume Moving Average (VMA) is a technical indicator that calculates the average trading volume over a specified number of periods. Unlike price-based moving averages (e.g., SMA, EMA applied to close prices), VMA focuses exclusively on volume figures. Its primary purpose is to provide a clearer view of volume trends by mitigating the noise of daily fluctuations.

Why Use Volume Moving Average in Trading?

Integrating VMA into trading strategies offers several analytical advantages:

  • Trend Strength Confirmation: A rising VMA alongside a price uptrend suggests strong buying interest, supporting the trend’s validity. Conversely, declining volume during an uptrend might indicate weakening momentum. The same logic applies to downtrends.
  • Identifying Potential Reversals: Divergence between price and VMA can signal impending reversals. For instance, if price makes a new high but VMA fails to confirm with a corresponding peak, it could suggest that conviction behind the new high is lacking.
  • Spotting Anomalous Volume: VMA helps identify spikes or troughs in trading activity that deviate significantly from the norm, potentially indicating institutional activity or the start of a significant market move.
  • Filtering Trade Signals: VMA can serve as a filter, for example, by only considering trade signals that occur when volume is above its moving average, suggesting higher market participation and conviction.

Prerequisites: Setting up MQL5 Development Environment

To follow the practical examples in this article, you will need a working MetaTrader 5 terminal and access to MetaEditor, its integrated development environment (IDE). Familiarity with MQL5 syntax, custom indicator structure, and basic algorithmic trading concepts is assumed.

Implementing Volume Moving Average in MQL5

Standard MQL5 functions like iMA() are designed for price data. To calculate a moving average on volume, we must first obtain an array of volume data and then apply a moving average function designed for arrays, such as iMAOnArray().

Understanding iMAOnArray Function for Volume Data

The iMAOnArray() function calculates a moving average on any given array of double values. Its syntax is:

int iMAOnArray(
   const double& array[],      // Source array
   int           total,        // Elements to calculate (from the end of array)
   int           period,       // Averaging period
   int           ma_shift,     // MA shift
   int           ma_method,    // MA method (ENUM_MA_METHOD)
   double&       result[]      // Target array to store MA values
);

To use this for VMA, the array[] parameter will be populated with historical volume data. This data can be tick volume or real volume, typically obtained using CopyRates() or directly from the OnCalculate event’s parameters in a custom indicator.

MQL5’s MqlRates structure, retrieved by CopyRates(), contains tick_volume and real_volume (if provided by the broker). You’ll need to extract one of these into a double array to serve as input for iMAOnArray().

Writing a Custom Indicator for VMA Calculation

Creating a custom VMA indicator involves these steps within the MQL5 framework:

  1. Indicator Properties: Define properties like indicator_chart_window, indicator_buffers, indicator_plots, and input parameters.
  2. Input Parameters: Allow users to specify the VMA period, MA method (ENUM_MA_METHOD), and the type of volume to use (tick or real volume).
  3. Indicator Buffers: Declare a double array for the VMA output that will be plotted on the chart.
  4. OnInit() Function: Initialize indicator buffers, set plot properties (labels, colors), and configure array properties (e.g., ArraySetAsSeries).
  5. OnCalculate() Function: This is where the core logic resides.
    • Determine the range of bars to calculate.
    • Prepare the source volume array. The tick_volume[] and volume[] (real volume) arrays are directly available as parameters in OnCalculate. Select the desired volume type based on user input.
    • Ensure the source volume array is in chronological order (oldest data at index 0) as expected by iMAOnArray().
    • Call iMAOnArray() with the prepared volume data, period, method, and the target buffer.
    • Handle the indexing correctly when copying results from iMAOnArray()‘s output to the indicator buffer, especially if the indicator buffer is set as a series.

Below is an MQL5 custom indicator demonstrating VMA calculation:

#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1

//--- Plot VMA
#property indicator_label1  "VMA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- Input parameters
input int InpVPeriod = 20;                         // VMA Period
input ENUM_MA_METHOD InpMaMethod = MODE_SMA;       // MA Method
input ENUM_APPLIED_VOLUME InpAppliedVolume = VOLUME_TICK; // Applied Volume (Tick or Real)

//--- Indicator buffers
double VmaBuffer[];
double TempVolumeArray[]; // Temporary array for source volume data

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   SetIndexBuffer(0, VmaBuffer, INDICATOR_DATA);
   PlotIndexSetString(0, PLOT_LABEL, "VMA(" + (string)InpVPeriod + ", " + EnumToString(InpAppliedVolume) + ")");
   IndicatorSetInteger(INDICATOR_DIGITS, 0); // Volume typically has 0 decimal places

   ArraySetAsSeries(VmaBuffer, true);
   // TempVolumeArray does not need to be series, as iMAOnArray expects normal array indexing

   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[],      // Real volume
                const int &spread[])
  {
   // Check for sufficient data
   if(rates_total < InpVPeriod)
      return(0);

   // Ensure TempVolumeArray is correctly sized
   ArrayResize(TempVolumeArray, rates_total);

   // Populate TempVolumeArray with chosen volume type in chronological order (oldest at index 0)
   for(int i = 0; i < rates_total; i++)
     {
      // The input arrays (tick_volume, volume) are indexed with 0 as the current bar.
      // We need to reverse this for TempVolumeArray to have oldest at index 0.
      if(InpAppliedVolume == VOLUME_TICK)
         TempVolumeArray[i] = (double)tick_volume[rates_total - 1 - i];
      else
         TempVolumeArray[i] = (double)volume[rates_total - 1 - i];
     }

   // Temporary buffer for iMAOnArray output (chronological)
   double tempVmaOutput[];
   ArrayResize(tempVmaOutput, rates_total);
   ArrayInitialize(tempVmaOutput, EMPTY_VALUE);

   // Calculate VMA using iMAOnArray
   int calculated_count = iMAOnArray(TempVolumeArray, rates_total, InpVPeriod, 0, InpMaMethod, tempVmaOutput);

   if(calculated_count < InpVPeriod)
     {
      // Handle cases where MA couldn't be calculated for enough bars
      // This check might be redundant if iMAOnArray returns 0 on critical failure
     }

   // Copy calculated VMA values to the indicator buffer (VmaBuffer, which is as_series)
   // tempVmaOutput[i] corresponds to bar (rates_total - 1 - i) on the chart if VmaBuffer is as_series
   // iMAOnArray fills starting from index (InpVPeriod - 1)
   for(int i = 0; i < rates_total; i++)
     {
      if (i < InpVPeriod -1 ) // Bars before MA calculation starts
         VmaBuffer[rates_total - 1 - i] = EMPTY_VALUE;
      else
         VmaBuffer[rates_total - 1 - i] = tempVmaOutput[i];
     }

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

MQL4 Note: In MQL4, you would typically use iVolume() in a loop to populate the volume array. The OnCalculate signature differs, and ENUM_APPLIED_VOLUME and direct real volume array are not standard. The iMAOnArray function itself is available.

Code Snippet: Calculating VMA using iMAOnArray (Conceptual Example)

Here’s a conceptual snippet illustrating the core logic of fetching volume data (e.g., tick volume) and applying iMAOnArray, outside the context of a full indicator’s OnCalculate function, for clarity on iMAOnArray usage:

void CalculateVMA_Example(string symbol, ENUM_TIMEFRAMES timeframe, int period, ENUM_MA_METHOD ma_method)
  {
   MqlRates rates_array[];
   // Get the last 200 bars (period + some history for MA calculation)
   int count_to_copy = period + 100; 
   if(CopyRates(symbol, timeframe, 0, count_to_copy, rates_array) < count_to_copy)
     {
      Print("Error copying rates data.");
      return;
     }

   int rates_copied = ArraySize(rates_array);
   if(rates_copied < period)
     {
      Print("Not enough data for VMA calculation: ", rates_copied, " bars available, ", period, " required.");
      return;
     }

   double volume_data[];
   ArrayResize(volume_data, rates_copied);

   // Extract tick volume into volume_data array (oldest at index 0)
   for(int i = 0; i < rates_copied; i++)
     {
      volume_data[i] = (double)rates_array[i].tick_volume;
     }

   double vma_output[];
   ArrayResize(vma_output, rates_copied);
   ArrayInitialize(vma_output, EMPTY_VALUE);

   // Calculate VMA
   int calculated_count = iMAOnArray(volume_data, rates_copied, period, 0, ma_method, vma_output);

   if(calculated_count > 0)
     {
      Print("VMA Calculation Results (last 5 values, if available):");
      // Output is chronological: vma_output[0] is for rates_array[0] (oldest)
      // First 'period-1' values of vma_output will be empty or 0 as MA isn't calculated.
      for(int i = MathMax(0, rates_copied - 5); i < rates_copied; i++)
        {
         if(vma_output[i] != EMPTY_VALUE && vma_output[i] != 0)
            PrintFormat("Bar %d (Time: %s): Volume = %.0f, VMA(%d) = %.2f", 
                        i, TimeToString(rates_array[i].time), 
                        volume_data[i], period, vma_output[i]);
         else
            PrintFormat("Bar %d (Time: %s): Volume = %.0f, VMA(%d) = N/A", 
                        i, TimeToString(rates_array[i].time), 
                        volume_data[i], period);
        }
     }
   else
     {
      Print("Failed to calculate VMA using iMAOnArray.");
     }
  }

This example uses CopyRates to fetch historical bar data, extracts tick volumes into a double array, and then applies iMAOnArray. The rates_array from CopyRates is ordered from past to present (oldest bar at index 0), which is the order iMAOnArray expects for its input array.

Applying Volume Moving Average in Trading Strategies

Once you have a VMA indicator, it can be incorporated into various trading strategies within Expert Advisors (EAs) or used for manual trading confirmation.

Identifying Trend Direction with VMA

While VMA primarily reflects volume trends, it indirectly supports price trend analysis:

  • Rising VMA: Suggests increasing market interest. If prices are also rising, it confirms bullish conviction. If prices are falling on rising VMA, it confirms bearish conviction.
  • Falling VMA: Indicates waning interest. If prices are rising on falling VMA, the rally might be losing steam. If prices are falling on falling VMA, the sell-off might be losing momentum.
  • VMA Crossovers: Some traders use a shorter-term VMA crossing a longer-term VMA as a signal, similar to price MA crossovers, but applied to volume activity.

Using VMA for Confirmation Signals

VMA excels as a confirmation tool for signals generated by other indicators or price action:

  • Breakout Confirmation: A price breakout (e.g., above resistance) accompanied by volume significantly above its VMA is generally considered more reliable than a breakout on low volume.
  • Reversal Patterns: When a price chart forms a reversal pattern (e.g., head and shoulders, double top/bottom), an increase in volume (reflected by VMA rising or volume crossing above VMA) during the confirming leg of the pattern adds weight to the signal.
  • Filtering Low-Participation Periods: An EA could be programmed to only take trades if the current volume (or a very short-term VMA) is above the longer-term VMA, avoiding periods of low liquidity or indecision.

Combining VMA with Price Moving Averages

A common approach is to use VMA in conjunction with price-based Moving Averages (PMAs):

  • Dual Confirmation: A buy signal might be triggered when price crosses above its PMA, and VMA is above its own threshold or also trending upwards. This ensures both price and volume support the move.
  • Volume-Weighted Strategies: For instance, only consider PMA crossover signals if the VMA is above a certain percentile or a fixed level, indicating sufficient market activity to validate the price signal.

Advanced Techniques and Considerations

Smoothing Techniques for VMA

The choice of ENUM_MA_METHOD (Simple, Exponential, Smoothed, Linear Weighted) in iMAOnArray() inherently defines the smoothing technique for VMA. Each method reacts differently to volume changes:

  • SMA (Simple Moving Average): Gives equal weight to all volume data points in the period.
  • EMA (Exponential Moving Average): Gives more weight to recent volume data, making it more responsive.
  • SMMA (Smoothed Moving Average): Similar to EMA but smoother due to a longer effective period.
  • LWMA (Linear Weighted Moving Average): Gives more weight to recent data in a linear fashion.

Experimentation is key to finding the MA method that best suits your VMA analysis for a particular instrument and timeframe.

Optimizing VMA Parameters (Period, MA Method)

The effectiveness of VMA heavily depends on its parameters: the averaging period and the ma_method. There’s no one-size-fits-all setting.

  • Period: A shorter period makes VMA more sensitive to recent volume changes, potentially leading to more signals (and noise). A longer period provides smoother VMA but may lag significantly.
  • MA Method: As discussed, different methods offer varying degrees of responsiveness and smoothing.

These parameters should be optimized using the Strategy Tester in MetaTrader 5, based on historical data for the specific financial instrument and timeframe you are trading. The goal is to find settings that historically provided meaningful insights or improved strategy performance.

Backtesting VMA Strategies in MQL5

When incorporating VMA into an Expert Advisor, thorough backtesting is crucial.

  1. Accessing VMA Data: Your EA will need to access VMA values. This can be done by calling the custom VMA indicator created earlier using iCustom(), or by re-implementing the VMA calculation logic directly within the EA.
  2. Defining Trading Logic: Specify how VMA signals (e.g., VMA level, slope, crossover with a threshold) will be used to generate, confirm, or filter trading entry and exit signals.
  3. Using Real Volume: Whenever possible, especially with MQL5, prefer real_volume over tick_volume for VMA calculation if your broker provides it. Real volume is a more accurate measure of market activity.
  4. Performance Metrics: Evaluate strategy performance not just on profitability, but also on metrics like drawdown, profit factor, and trade frequency, considering how VMA influences these.

Backtesting helps validate whether the chosen VMA parameters and strategy rules are robust or if they are a result of overfitting to historical data.

Conclusion

Summary of Volume Moving Average in MQL5

The Volume Moving Average is a valuable tool for MQL5 programmers and traders, offering a smoothed perspective on market participation. By using iMAOnArray() on volume data (tick or real), developers can create custom VMA indicators and integrate volume-based analysis into their Expert Advisors. VMA aids in confirming trend strength, identifying breakouts, spotting divergences, and filtering trades, ultimately contributing to more informed trading decisions.

Further Resources and Learning

To deepen your understanding and explore more advanced MQL5 capabilities:

  • Refer to the official MQL5 Reference documentation for detailed information on functions, constants, and language structures.
  • Engage with the MQL5 community forums for discussions, examples, and solutions to common programming challenges.
  • Experiment with different MA methods and periods for VMA, and combine it with other technical analysis tools to discover synergistic effects in your trading strategies.

By continuously learning and experimenting, you can leverage the full power of MQL5 to develop sophisticated and effective automated trading systems incorporating nuanced indicators like the Volume Moving Average.


Leave a Reply