MACD custom indicator

 

Hi all,

I attempted to create a custom indicator for MACD in MT5 that allows the user to selected MA for the oscillator and the signal line. Currently, in MT5 you get an EMA for the oscillator and a SMA for the signal line. The below code attempted to do this but its not working as expected. 

//+------------------------------------------------------------------+
//|                                                      MACD_TV.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_plots   3

//--- plot MACD
#property indicator_label1  "MACD"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- plot Signal
#property indicator_label2  "Signal"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrOrange
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

//--- plot Hist
#property indicator_label3  "Hist"
#property indicator_type3   DRAW_HISTOGRAM
#property indicator_color3  clrLavender
#property indicator_style3  STYLE_SOLID
#property indicator_width3  1

// --- Libraries
#include <MovingAverages.mqh> // Include the library containing the MA functions

//--- MACD inputs
input int FastEMAPeriod = 12; // Fast MA Period
input int SlowEMAPeriod = 26; // Slow MA Period
input ENUM_MA_METHOD OscillatorMAMethod = MODE_EMA; // MA Method for Oscillator Calculation (Fast-Slow)
input int SignalMAPeriod = 9; // Signal MA Period
input ENUM_MA_METHOD SignalMAMethod = MODE_EMA; // MA Method for Signal Line Calculation
input ENUM_APPLIED_PRICE AppliedPrice = PRICE_CLOSE; // Applied price for Oscillator Calculation

//--- indicator buffers
double MACDBuffer[];
double SignalBuffer[];
double HistBuffer[];

//--- handles for the individual moving averages
int fast_ma_handle;
int slow_ma_handle;
int signal_ma_handle;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   Print("OnInit: Custom MACD trading view loaded.");
//--- indicator buffers mapping
   SetIndexBuffer(0,MACDBuffer,INDICATOR_DATA);  // MACD Line Buffer
   SetIndexBuffer(1,SignalBuffer,INDICATOR_DATA); // Signal Line Buffer
   SetIndexBuffer(2,HistBuffer,INDICATOR_DATA);   // Histogram Buffer

//--- Set index settings for drawing
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN, SlowEMAPeriod - 1); // MACD Line can be calculated after Slow MA period bars
   PlotIndexSetInteger(1,PLOT_DRAW_BEGIN, SlowEMAPeriod + SignalMAPeriod - 2); // Signal Line needs enough bars for both MAs
   PlotIndexSetInteger(2,PLOT_DRAW_BEGIN, SlowEMAPeriod + SignalMAPeriod - 2); // Histogram needs enough bars for both MAs


//--- create handles for the individual moving averages
   fast_ma_handle = iMA(_Symbol, _Period, FastEMAPeriod, 0, OscillatorMAMethod, AppliedPrice);
   slow_ma_handle = iMA(_Symbol, _Period, SlowEMAPeriod, 0, OscillatorMAMethod, AppliedPrice);
   signal_ma_handle = iMA(_Symbol, _Period, SignalMAPeriod, 0, SignalMAMethod, PRICE_CLOSE); // PRICE_CLOSE here is just a placeholder

//--- check for errors in handle creation
   if (fast_ma_handle == INVALID_HANDLE || slow_ma_handle == INVALID_HANDLE || signal_ma_handle == INVALID_HANDLE) {
      //--- print error message and fail initialization
      Print("Failed to create one or more MA indicator handles. Last error code: ", GetLastError());
      return(INIT_FAILED);
   }

//--- Initialization succeeded
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[]) {

   //--- Check if enough bars exist and handle initial buffer initialization, get calculation limit
   int limit = HandleInitialBars(rates_total, prev_calculated, SlowEMAPeriod, SignalMAPeriod, MACDBuffer, SignalBuffer, HistBuffer);

   // If HandleInitialBars returned -1, it means there weren't enough bars, and it handled initialization.
   if (limit == -1) {
       return rates_total; // Return rates_total as required when processing stops early
   }

//--- Calculate the MACD Line for the determined range
   CalculateMACDLineForRange(rates_total, limit, prev_calculated, fast_ma_handle, slow_ma_handle, MACDBuffer, SignalBuffer, HistBuffer);

//--- Calculate the Signal Line (MA of the MACD Line) using library functions
   int signal_calculated_count = CalculateSignalLineForRange(rates_total, prev_calculated, begin, SignalMAPeriod, SignalMAMethod, MACDBuffer, SignalBuffer);

//--- Calculate the Histogram for the relevant range
   CalculateHistogramForRange(rates_total, limit, MACDBuffer, SignalBuffer, HistBuffer);
   
//--- return value of prev_calculated for next call
   return(rates_total);
  } 
  
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   Print("OnDeinit: Custom MACD (original version) deinitialized. Reason: ", reason);

   if (fast_ma_handle != INVALID_HANDLE) {
      IndicatorRelease(fast_ma_handle);
      Print("OnDeinit: Released fast MA handle.");
      fast_ma_handle = INVALID_HANDLE; // Set the handle to invalid after releasing
   }

   if (slow_ma_handle != INVALID_HANDLE) {
      IndicatorRelease(slow_ma_handle);
      Print("OnDeinit: Released slow MA handle.");
      slow_ma_handle = INVALID_HANDLE;
   }

   if (signal_ma_handle != INVALID_HANDLE) {
      IndicatorRelease(signal_ma_handle);
      Print("OnDeinit: Released signal MA handle.");
      signal_ma_handle = INVALID_HANDLE;
   }

   Print("OnDeinit: Indicator deinitialization complete.");
}

//+------------------------------------------------------------------+
//| Checks if enough bars exist and initializes buffers              |
//+------------------------------------------------------------------+
// Returns the starting index (limit) for the main calculation loop.
// Returns -1 if not enough bars to proceed with calculation (OnCalculate should return rates_total).
int HandleInitialBars(const int rates_total,
                      const int prev_calculated,
                      const int slow_ma_period,
                      const int signal_ma_period,
                      double& macd_buffer[],
                      double& signal_buffer[],
                      double& hist_buffer[]) {

    // We need enough bars for the slowest MA plus the Signal MA period for the full indicator to be valid
    int required_bars = slow_ma_period + signal_ma_period - 1;

    if (rates_total < required_bars) {
        // Not enough bars for full indicator calculation, initialize uncalculated bars to EMPTY_VALUE
        int initial_limit = (prev_calculated == 0) ? 0 : prev_calculated - 1;
        for (int i = initial_limit; i < rates_total; i++) {
            macd_buffer[i] = EMPTY_VALUE;
            signal_buffer[i] = EMPTY_VALUE;
            hist_buffer[i] = EMPTY_VALUE;
        }
        // Indicate that OnCalculate should stop processing this time by returning an invalid limit
        return -1;
    }

    // Determine the starting index for the main calculation loop (limit)
    int limit;
    if (prev_calculated == 0) {
        // Start MACD Line calculation after the slowest MA period needed for the difference
        limit = slow_ma_period - 1; // MACD Line is valid after the slowest MA period (index 0 is first bar)

        // Initialize bars before the MACD calculation limit to EMPTY_VALUE
        for (int i = 0; i < limit; i++) {
            macd_buffer[i] = EMPTY_VALUE;
            signal_buffer[i] = EMPTY_VALUE;
            hist_buffer[i] = EMPTY_VALUE;
        }
    } else {
        // Calculate only new bars
        limit = prev_calculated - 1; // Start from the last calculated bar to potentially recalculate it
        // Ensure we don't start before the slowest MA period needed for the MACD line
        if (limit < slow_ma_period - 1) limit = slow_ma_period - 1;
    }

    // Return the calculated starting index for the main loop
    return limit;
}

//+------------------------------------------------------------------+
//| Calculates the MACD Line for a specified range of bars           |
//+------------------------------------------------------------------+
void CalculateMACDLineForRange(const int rates_total,
                               const int limit,
                               const int prev_calculated,
                               const int p_fast_ma_handle,
                               const int p_slow_ma_handle,
                               double& macd_buffer[],
                               double& signal_buffer[],
                               double& hist_buffer[]) {

   // Temporary buffers for single MA values
   double temp_fast_ma_buffer[1];
   double temp_slow_ma_buffer[1];

   // Loop through the bars that need calculation for the MACD Line
   for (int i = limit; i < rates_total; i++) {
       // Get Fast, Slow MA value for the current bar 'i' using its handle
       if ((CopyBuffer(p_fast_ma_handle, 0, i, 1, temp_fast_ma_buffer) <= 0) || (CopyBuffer(p_slow_ma_handle, 0, i, 1, temp_slow_ma_buffer) <= 0) ) {
           // Handle error or insufficient data for this specific bar from source MAs
           macd_buffer[i] = EMPTY_VALUE;
           // We cannot calculate Signal or Hist if Fast or Slow MA is not available for this bar
           signal_buffer[i] = EMPTY_VALUE;
           hist_buffer[i] = EMPTY_VALUE;
           continue; // Move to the next bar
       }

       //--- Calculate the MACD Line (Fast MA - Slow MA) and store in our MACD buffer
       macd_buffer[i] = temp_fast_ma_buffer[0] - temp_slow_ma_buffer[0];

       // SignalBuffer and HistBuffer will be filled in subsequent steps (identifies bars that are being calculated for the first time in the current OnCalculate call)
        if (prev_calculated <= i) {
            signal_buffer[i] = EMPTY_VALUE;
            hist_buffer[i] = EMPTY_VALUE;
        }
   }
}

//+------------------------------------------------------------------+
//| Calculates the Signal Line for a range using library functions |
//+------------------------------------------------------------------+
// Returns the number of bars calculated by the library function.
int CalculateSignalLineForRange(const int rates_total,
                                const int prev_calculated,
                                const int begin,
                                const int signal_ma_period,
                                const ENUM_MA_METHOD signal_ma_method,
                                const double& macd_buffer[], // Source array (MACD Line values)
                                double& signal_buffer[]) {   // Target array (Signal Line values)

   int calculated_count = 0; // Variable to store the number of bars calculated by the library function

   if (signal_ma_method == MODE_SMA) {
       // Call the SMA function from the library
       // The parameters are: rates_total, prev_calculated, begin, period, source_array, target_array
       calculated_count = SimpleMAOnBuffer(rates_total, prev_calculated, begin, signal_ma_period, macd_buffer, signal_buffer);

   } else if (signal_ma_method == MODE_EMA) {
       // Call the EMA function from the library (ExponentialMAOnBuffer)
       calculated_count = ExponentialMAOnBuffer(rates_total, prev_calculated, begin, signal_ma_period, macd_buffer, signal_buffer);

   } else {
       // Handle unsupported methods by defaulting to EMA
       Print("Unsupported Signal MA Method selected (", signal_ma_method, "). Defaulting to EMA for Signal Line calculation.");
       // Call the EMA function from the library (ExponentialMAOnBuffer)
       calculated_count = ExponentialMAOnBuffer(rates_total, prev_calculated, begin, signal_ma_period, macd_buffer, signal_buffer);
   }

    return calculated_count; // Return the number of bars calculated by the library function
}

//+------------------------------------------------------------------+
//| Calculates the Histogram for a specified range of bars         |
//+------------------------------------------------------------------+
void CalculateHistogramForRange(const int rates_total,
                                 const int macd_calculation_limit, // The 'limit' where MACD calculation started
                                 const double& macd_buffer[], // Source MACD buffer
                                 const double& signal_buffer[], // Source Signal buffer
                                 double& hist_buffer[]) {   // Target Histogram buffer

   // Calculate the starting index for the histogram loop
   // Start from the same limit as the MACD calculation.
   // The EMPTY_VALUE checks inside the loop will handle bars where the Signal Line is not yet valid.
   int hist_limit = macd_calculation_limit;

   // Loop through the bars that need calculation for the histogram
   for (int i = hist_limit; i < rates_total; i++) {
       // Check if both MACD and Signal have valid values for this bar
       if (macd_buffer[i] != EMPTY_VALUE && signal_buffer[i] != EMPTY_VALUE) {
           HistBuffer[i] = macd_buffer[i] - signal_buffer[i];
       } else {
           // If either is empty, set Hist to empty for this bar
           hist_buffer[i] = EMPTY_VALUE;
       }
   }
}
 
Martingale Duncan:

Hi all,

I attempted to create a custom indicator for MACD in MT5 that allows the user to selected MA for the oscillator and the signal line. Currently, in MT5 you get an EMA for the oscillator and a SMA for the signal line. The below code attempted to do this but its not working as expected. 

It looks like you're trying to recreate the MACD indicator in your Indicators ==> Oscillators folder.

Try starting with the source code of the MACD indicator in your Indicators ==> Examples folder (attached).

Files:
MACD.mq5  5 kb