Issue with MACD Indicator When Modularized in MT5 EA

 

Hey everyone,

I'm facing an issue that’s been driving me a bit mad, and I’m hoping someone here can shed some light. I have a simple Expert Advisor (EA) where I'm using the MACD indicator. When I run the MACD inside my core module, everything works as expected. The MACD values are retrieved successfully, and the logic performs as intended.

However, when I attempt to modularize the MACD logic into a separate file (to keep things cleaner and more organized), the MACD no longer works. Specifically, the handle initializes successfully, but it fails to retrieve MACD values. It seems as though the MACD isn’t being properly called or calculated when moved outside the core module.

I know it’s likely something small that I’m overlooking or a minor misunderstanding in how the code is structured or how MT5 handles the indicator across modules. But I can’t even figure out what to search for because I don’t fully understand why this isn’t working when it should.

Here’s the self-contained version of the code where everything is in the same module and works fine:


// Declare the handle for the MACD indicator
int macdHandle;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   // Create MACD indicator handle
   macdHandle = iMACD(_Symbol, _Period, 12, 26, 9, PRICE_CLOSE);

   // Check if the handle is valid
   if(macdHandle == INVALID_HANDLE)
     {
      Print("Failed to create MACD handle. Error: ", GetLastError());
      return(INIT_FAILED);
     }
   
   Print("Expert started. MACD initialized.");

   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   static datetime lastBarTime = 0;

   // Get the time of the current bar
   datetime currentBarTime = iTime(_Symbol, _Period, 0);

   // Check if a new bar is formed
   if(currentBarTime != lastBarTime)
     {
      lastBarTime = currentBarTime;

      // Create arrays to store MACD values
      double macdMain[], macdSignal[];

      // Set the arrays as timeseries (newest value at index 0)
      ArraySetAsSeries(macdMain, true);
      ArraySetAsSeries(macdSignal, true);

      // Copy the MACD main line and signal line values for the current bar
      if(CopyBuffer(macdHandle, 0, 0, 2, macdMain) <= 0 || 
         CopyBuffer(macdHandle, 1, 0, 2, macdSignal) <= 0)
        {
         Print("Failed to copy MACD values. Error: ", GetLastError());
         return;
        }

      // Calculate the MACD Histogram (difference between Main and Signal)
      double macdHistogram = macdMain[0] - macdSignal[0];

      // Calculate MACD Slope (difference between current and previous MACD main)
      double macdSlope = macdMain[0] - macdMain[1];

      // Get price data (Open, High, Low, Close)
      double open  = iOpen(_Symbol, _Period, 0);
      double high  = iHigh(_Symbol, _Period, 0);
      double low   = iLow(_Symbol, _Period, 0);
      double close = iClose(_Symbol, _Period, 0);

      // Get the current bar's volume
      double volume = iVolume(_Symbol, _Period, 0);

      // Print all the gathered data
      Print("New Bar: Time = ", TimeToString(currentBarTime, TIME_DATE | TIME_MINUTES));
      Print("Price: Open = ", open, " | High = ", high, " | Low = ", low, " | Close = ", close);
      Print("Volume = ", volume);
      Print("MACD Main = ", macdMain[0], " | MACD Signal = ", macdSignal[0], " | MACD Histogram = ", macdHistogram);
      Print("MACD Slope = ", macdSlope);
     }
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   // Release the MACD handle
   if(macdHandle != INVALID_HANDLE)
      IndicatorRelease(macdHandle);
   
   Print("Expert removed.");
  }
//+------------------------------------------------------------------+


And then here is my attempt to modularize it:


Core Module:

// Include the MACD module
#include <MACDTest.mqh>

input string InpSymbol = "EURUSD";                 // Input for symbol
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_M15;    // Input for timeframe (M15)

// Global variables to hold market prices
double openPrice, closePrice, highPrice, lowPrice;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   Print("Core Module: Initializing EA...");

   // Initialize MACD module (with the symbol, timeframe, and MACD parameters)
   Print("Core Module: Calling InitializeMACD() with parameters: ");
   Print("Core Module: Symbol = ", InpSymbol, ", Timeframe = ", EnumToString(InpTimeframe), ", FastEMA = 12, SlowEMA = 26, SignalSMA = 9");

   if (!InitializeMACD(InpSymbol, InpTimeframe, 12, 26, 9))  // Ensure we pass InpTimeframe correctly
   {
      Print("Core Module: Failed to initialize MACD module.");
      return INIT_FAILED;
   }

   Print("Core Module: EA Initialized.");
   return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   static datetime lastBarTime = 0;
   datetime currentBarTime = iTime(InpSymbol, InpTimeframe, 0);  // Using correct InpTimeframe

   // Check if a new bar is formed
   if (currentBarTime != lastBarTime)
   {
      lastBarTime = currentBarTime;

      // Retrieve bar data (open, close, high, low)
      openPrice = iOpen(InpSymbol, InpTimeframe, 0);   // Correct access of InpTimeframe
      closePrice = iClose(InpSymbol, InpTimeframe, 0); // Correct access of InpTimeframe
      highPrice = iHigh(InpSymbol, InpTimeframe, 0);   // Correct access of InpTimeframe
      lowPrice = iLow(InpSymbol, InpTimeframe, 0);     // Correct access of InpTimeframe

      // Log the bar data retrieved from the market
      Print("Core Module: Bar data retrieved:");
      Print("Core Module: Open = " + DoubleToString(openPrice, 6) +
            " | Close = " + DoubleToString(closePrice, 6) +
            " | High = " + DoubleToString(highPrice, 6) +
            " | Low = " + DoubleToString(lowPrice, 6));

      // Log the actual values being sent to the MACD module
      Print("Core Module: Sending the following bar data to MACD module:");
      Print("Open = " + DoubleToString(openPrice, 6) +
            " | Close = " + DoubleToString(closePrice, 6) +
            " | High = " + DoubleToString(highPrice, 6) +
            " | Low = " + DoubleToString(lowPrice, 6));

      // Retrieve MACD values from the MACD module
      double macdMain, macdSignal, macdHistogram;
      Print("Core Module: Calling GetMACDValues()...");
      if (GetMACDValues(macdMain, macdSignal, macdHistogram))
      {
         // MACD values received, print them
         Print("Core Module: MACD values received:");
         Print("Core Module: MACD Main = " + DoubleToString(macdMain, 6));
         Print("Core Module: MACD Signal = " + DoubleToString(macdSignal, 6));
         Print("Core Module: MACD Histogram = " + DoubleToString(macdHistogram, 6));
      }
      else
      {
         Print("Core Module: Failed to retrieve MACD values.");
      }
   }
}


MACDTest.mqh


#ifndef __MACDTEST_MQH__
#define __MACDTEST_MQH__

// Include necessary headers
#include <Trade\SymbolInfo.mqh>    // For symbol information
#include <TheGreyInclude\Logger.mqh> // For logging messages

// Declare MACD functions
bool InitializeMACD(string symbol, ENUM_TIMEFRAMES timeframe, int fastEMA, int slowEMA, int signalSMA);
bool GetMACDValues(double &macdMain, double &macdSignal, double &macdHistogram);
void DeinitializeMACD();

// Internal variables
int macdHandle = INVALID_HANDLE;
string macdSymbol = "";
ENUM_TIMEFRAMES macdTimeframe;

// Initialize the MACD indicator handle
bool InitializeMACD(string symbol, ENUM_TIMEFRAMES timeframe, int fastEMA, int slowEMA, int signalSMA)
{
   macdSymbol = symbol;
   macdTimeframe = PERIOD_M15;  // Explicitly set timeframe for debugging

   // Add additional log to make sure the function is reached
   Print("MACD Module: Entering InitializeMACD function.");
   Print("MACD Module: Symbol = ", macdSymbol, ", Timeframe = ", EnumToString(macdTimeframe));
   Print("MACD Module: FastEMA = ", fastEMA, ", SlowEMA = ", slowEMA, ", SignalSMA = ", signalSMA);

   // Initialize the MACD handle
   macdHandle = iMACD(macdSymbol, macdTimeframe, fastEMA, slowEMA, signalSMA, PRICE_CLOSE);

   if (macdHandle == INVALID_HANDLE)
   {
      int errorCode = GetLastError();
      Print("MACD Module: Failed to initialize MACD handle. Error code: ", errorCode);
      return false;
   }

   Print("MACD Module: MACD handle initialized successfully.");
   return true;
}

// Deinitialize the MACD handle
void DeinitializeMACD()
{
   if (macdHandle != INVALID_HANDLE)
   {
      Print("MACD Module: Releasing MACD handle...");
      IndicatorRelease(macdHandle);  // Release the MACD handle when done
      macdHandle = INVALID_HANDLE;
      Print("MACD Module: MACD handle released.");
   }
}

// Retrieve MACD values for the most recent bar
bool GetMACDValues(double &macdMain, double &macdSignal, double &macdHistogram)
{
   if (macdHandle == INVALID_HANDLE)
   {
      Print("MACD Module: MACD handle is invalid.");
      return false;
   }

   // Check how many bars have been calculated by the indicator
   int calculatedBars = BarsCalculated(macdHandle);
   if (calculatedBars < 0)  // Handle error case for BarsCalculated
   {
      int errorCode = GetLastError();
      Print("MACD Module: Error in BarsCalculated. Error code: ", errorCode);
      return false;
   }

   Print("MACD Module: ", calculatedBars, " bars have been calculated by the MACD indicator.");
   
   if (calculatedBars <= 10)  // Adjust this threshold if needed
   {
      Print("MACD Module: Not enough bars calculated by MACD indicator yet. Only ", calculatedBars, " bars available.");
      return false;
   }

   // Print that we're about to calculate the MACD values
   Print("MACD Module: Retrieving MACD values for symbol: ", macdSymbol, " timeframe: ", EnumToString(macdTimeframe));

   // Create arrays to store MACD data
   double macdMainArray[], macdSignalArray[], macdHistogramArray[];

   // Set arrays to time series (newest value at index 0)
   ArraySetAsSeries(macdMainArray, true);
   ArraySetAsSeries(macdSignalArray, true);
   ArraySetAsSeries(macdHistogramArray, true);

   // Log that we're about to call CopyBuffer for MACD values
   Print("MACD Module: Copying MACD buffers...");

   // Copy MACD values from the indicator's buffer for the last 5 bars to ensure historical data is available
   int copiedMain = CopyBuffer(macdHandle, 0, 0, 5, macdMainArray);
   int copiedSignal = CopyBuffer(macdHandle, 1, 0, 5, macdSignalArray);
   int copiedHistogram = CopyBuffer(macdHandle, 2, 0, 5, macdHistogramArray);
   
   // Log copied values
   Print("MACD Module: MACD Main copied: ", copiedMain, " | MACD Signal copied: ", copiedSignal, " | MACD Histogram copied: ", copiedHistogram);

   if (copiedMain <= 0)
   {
      int errorCode = GetLastError();
      Print("MACD Module: Failed to retrieve MACD Main values. Error code: ", errorCode);
      return false;
   }
   if (copiedSignal <= 0)
   {
      int errorCode = GetLastError();
      Print("MACD Module: Failed to retrieve MACD Signal values. Error code: ", errorCode);
      return false;
   }
   if (copiedHistogram <= 0)
   {
      int errorCode = GetLastError();
      Print("MACD Module: Failed to retrieve MACD Histogram values. Error code: ", errorCode);
      return false;
   }

   // Pass the values back to the core module (use the most recent bar, index 0)
   macdMain = macdMainArray[0];
   macdSignal = macdSignalArray[0];
   macdHistogram = macdMain - macdSignal;

   // Print MACD values being sent to core module
   Print("MACD Module: MACD values calculated:");
   Print("MACD Module: MACD Main = " + DoubleToString(macdMain, 6));
   Print("MACD Module: MACD Signal = " + DoubleToString(macdSignal, 6));
   Print("MACD Module: MACD Histogram = " + DoubleToString(macdHistogram, 6));

   return true;
}

#endif // __MACDTEST_MQH__



As you can see, I've butchered the code by adding extreme logging features, but I've still not been able to figure out where the flow of data is breaking. It seems as if the indicator is not really initializing properly. 

I would really like to learn how to modularize my code more which is why I am trying with something so simple as the above. But apparrently it's not simple enough for my brain. Any help would be greatly appreciated.

 
MrGrayline:

I would really like to learn how to modularize my code more which is why I am trying with something so simple as the above. But apparrently it's not simple enough for my brain. Any help would be greatly appreciated.

I have not the time to look in detail today but you are not really creating a separate module because you are declaring variables for the data in the EA but trying to populate them in the include
Instead of doing that and returning a Boolean why not do the processing you want in the include and return a signal instead of values 

if you leave variable dependencies then it is not really separate to cleanly separate create a class
 
Paul Anscombe #:
I have not the time to look in detail today but you are not really creating a separate module because you are declaring variables for the data in the EA but trying to populate them in the include
Instead of doing that and returning a Boolean why not do the processing you want in the include and return a signal instead of values 

if you leave variable dependencies then it is not really separate to cleanly separate create a class


Hi Paul,

Thanks so much for the feedback! It's great to have access to a community that is willing to assist. To give a bit more context, what I'm ultimately trying to achieve is to get the MACD values and pass them to an external Python script for further analysis. The goal isn't just to generate signals within the EA, but to capture and analyze the data externally.

Given that, I was thinking of keeping the values in a modular format so that they can be sent outside of MetaTrader to Python using http. Do you think creating a class for this would still work, or would you recommend another approach for easy transfer of those values?

Looking forward to your thoughts!

EDIT*

Let me clarify my broader goal. The reason I'm separating the MACD module (and eventually other indicators) is because I want to collect their data for post-processing in Python. Instead of using the indicator signals directly within MetaTrader, I’m aiming to send the raw values (like the MACD line, signal line, and histogram) to an external Python script for further analysis.

In this context, I’m trying to modularize the MACD logic so that it can easily feed data to Python, but it seems like the current approach isn’t fully achieving that separation. Would you recommend creating a class for this purpose, even if my end goal is to funnel the indicator data out for Python processing? If so, how would you structure the process for sending this data outside of MetaTrader efficiently if I wanted to keep each indicator as a separate module?

Thank you for your time in reading and replying, I really appreciate it.

 
MrGrayline #:


Hi Paul,

Thanks so much for the feedback! It's great to have access to a community that is willing to assist. To give a bit more context, what I'm ultimately trying to achieve is to get the MACD values and pass them to an external Python script for further analysis. The goal isn't just to generate signals within the EA, but to capture and analyze the data externally.

Given that, I was thinking of keeping the values in a modular format so that they can be sent outside of MetaTrader to Python using http. Do you think creating a class for this would still work, or would you recommend another approach for easy transfer of those values?

Looking forward to your thoughts!

EDIT*

Let me clarify my broader goal. The reason I'm separating the MACD module (and eventually other indicators) is because I want to collect their data for post-processing in Python. Instead of using the indicator signals directly within MetaTrader, I’m aiming to send the raw values (like the MACD line, signal line, and histogram) to an external Python script for further analysis.

In this context, I’m trying to modularize the MACD logic so that it can easily feed data to Python, but it seems like the current approach isn’t fully achieving that separation. Would you recommend creating a class for this purpose, even if my end goal is to funnel the indicator data out for Python processing? If so, how would you structure the process for sending this data outside of MetaTrader efficiently if I wanted to keep each indicator as a separate module?

Thank you for your time in reading and replying, I really appreciate it.

if all you are doing is gathering the MACD data to send to python what is the purpose of the include file?  MACD calcs are already separated in an indicator, just get the values from the indicator in your EA and send them to your python, seems you are adding an extra step for no gain

 
Paul Anscombe #:
if all you are doing is gathering the MACD data to send to python what is the purpose of the include file?  MACD calcs are already separated in an indicator, just get the values from the indicator in your EA and send them to your python, seems you are adding an extra step for no gain

Hi Paul,

Thanks again for your input, I really appreciate the time you're taking to help me out!

Based on what you’ve said, it makes sense to call the MACD data directly within the EA and send it to Python without adding any extra steps. I’ve streamlined my approach as you suggested, and it’s working well for now.

However, I wanted to get your thoughts on something else — would this approach still be efficient and sustainable if I were working with 20 or 30 different indicators? If I were to call all the values directly from within the EA and send them to Python, do you think that could potentially overload the system, or would it be fine?

In my experience with Python, I typically modularize the code by breaking files apart to make it more scalable and reusable across different contexts. I’m still new to MQL5, though, so I’m wondering if it would be better to keep things as they are in a single EA or if it might make sense to separate the indicators into various include files for the sake of clarity and scalability, even if the only goal is to gather data for external analysis in Python.

Looking forward to hearing your thoughts!

Best regards,
Jacques

 
MrGrayline #:

Hi Paul,

Thanks again for your input, I really appreciate the time you're taking to help me out!

Based on what you’ve said, it makes sense to call the MACD data directly within the EA and send it to Python without adding any extra steps. I’ve streamlined my approach as you suggested, and it’s working well for now.

However, I wanted to get your thoughts on something else — would this approach still be efficient and sustainable if I were working with 20 or 30 different indicators? If I were to call all the values directly from within the EA and send them to Python, do you think that could potentially overload the system, or would it be fine?

In my experience with Python, I typically modularize the code by breaking files apart to make it more scalable and reusable across different contexts. I’m still new to MQL5, though, so I’m wondering if it would be better to keep things as they are in a single EA or if it might make sense to separate the indicators into various include files for the sake of clarity and scalability, even if the only goal is to gather data for external analysis in Python.

Looking forward to hearing your thoughts!

Best regards,
Jacques

splitting your code into mulptile files via includes will make no performance difference, the includes are compiled into the same ex5. but if you wan to spearate and tidy then use classes as mentioned before, but also as I said before I see little point when you are simply after indicator data.

indicators will run in their own thread so calling them from the EA is fine, the only other choice you have is to mod the indicator to send the data directly to your python.

what you really need to think about is when you are doing this, is it a one-off run to populate python or are you planning sending data realtime every tick or new bar and design accordingly