MACD MQL5 Indicator: Difference Histogram and Closed-Bar Alerts
Build a custom MT5 indicator with MACD main and signal lines, a main-minus-signal histogram, and completed-bar crossover alerts. The source uses the chart symbol and timeframe. It is an indicator, not an Expert Advisor: it cannot place orders, size positions, or manage stops. No strategy performance is established by these indicator values.
How the indicator signal works
Calculation and signal conditions
iMACD supplies main line buffer 0 and signal line buffer 1. This indicator plots their difference as a separate histogram. An upward signal compares completed bars 2 and 1: main was at or below signal and is now above it. A downward signal uses the reverse comparison. The histogram sign change expresses the same crossover, not independent confirmation. There is no below-zero, swing-low, volume, or ATR filter.
Interpretation and limitations
Alerts report a completed-bar crossover and do not create or close a trade. Current-bar lines can change as prices arrive. Historical values can also change when source history changes; closed-bar signaling does not guarantee immutable broker data. If an EA consumes this indicator with iCustom, its custom buffers are 0 difference, 1 main, and 2 signal, which differs from the built-in iMACD mapping. Trade rules and costs require a separate tested EA.
MQL5 Custom Indicator Code
// MACD_Custom.mq5: indicator only, no trading operations.
#property copyright "Pineify.app"
#property version "1.10"
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_plots 3
#property indicator_label1 "Main minus signal"
#property indicator_type1 DRAW_HISTOGRAM
#property indicator_color1 clrDodgerBlue
#property indicator_width1 2
#property indicator_label2 "MACD main"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrRed
#property indicator_label3 "Signal"
#property indicator_type3 DRAW_LINE
#property indicator_color3 clrOrange
input int InpFastEMA = 12;
input int InpSlowEMA = 26;
input int InpSignalSMA = 9;
input bool InpAlerts = true;
input bool InpPush = false;
double Histogram[], MainLine[], SignalLine[];
int handle = INVALID_HANDLE;
datetime lastChecked = 0;
int OnInit()
{
if(InpFastEMA <= 0 || InpSlowEMA <= InpFastEMA || InpSignalSMA <= 0)
return INIT_PARAMETERS_INCORRECT;
SetIndexBuffer(0, Histogram, INDICATOR_DATA);
SetIndexBuffer(1, MainLine, INDICATOR_DATA);
SetIndexBuffer(2, SignalLine, INDICATOR_DATA);
ArraySetAsSeries(Histogram, false);
ArraySetAsSeries(MainLine, false);
ArraySetAsSeries(SignalLine, false);
for(int p = 0; p < 3; p++)
{
PlotIndexSetInteger(p, PLOT_DRAW_BEGIN, InpSlowEMA + InpSignalSMA - 2);
PlotIndexSetDouble(p, PLOT_EMPTY_VALUE, EMPTY_VALUE);
}
IndicatorSetString(INDICATOR_SHORTNAME, "MACD difference and lines");
handle = iMACD(_Symbol, _Period, InpFastEMA, InpSlowEMA, InpSignalSMA, PRICE_CLOSE);
return handle == INVALID_HANDLE ? INIT_FAILED : INIT_SUCCEEDED;
}
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 warmup = InpSlowEMA + InpSignalSMA - 2;
if(rates_total < warmup + 3) return 0;
ArraySetAsSeries(time, false);
double main[], signal[];
if(CopyBuffer(handle, 0, 0, rates_total, main) != rates_total ||
CopyBuffer(handle, 1, 0, rates_total, signal) != rates_total) return prev_calculated;
// Full chronological copy for clarity; optimize only after profiling.
for(int i = 0; i < rates_total; i++)
{
MainLine[i] = i < warmup ? EMPTY_VALUE : main[i];
SignalLine[i] = i < warmup ? EMPTY_VALUE : signal[i];
Histogram[i] = i < warmup ? EMPTY_VALUE : main[i] - signal[i];
}
int bar = rates_total - 2;
if(prev_calculated == 0) { lastChecked = time[bar]; return rates_total; }
if(time[bar] == lastChecked) return rates_total;
lastChecked = time[bar];
bool up = main[bar-1] <= signal[bar-1] && main[bar] > signal[bar];
bool down = main[bar-1] >= signal[bar-1] && main[bar] < signal[bar];
if(up || down)
{
string msg = _Symbol + " " + EnumToString(_Period) + (up ? " MACD cross up" : " MACD cross down");
if(InpAlerts) Alert(msg);
if(InpPush && !SendNotification(msg)) Print("Push failed: ", GetLastError());
}
return rates_total;
}
void OnDeinit(const int reason)
{
if(handle != INVALID_HANDLE) IndicatorRelease(handle);
}Copy this code into MetaEditor, save it in the MQL5/Indicators folder, and compile with F7.
Change the settings or add your own signal filter
Describe the inputs, buffers, alerts, visual style, or confirmation filter you want. Pineify generates editable MQL5 source code that you can inspect and compile in MetaEditor.
Pine Script vs MQL5: Same Strategy, Different Platforms
| Aspect | Pine Script (TradingView) | MQL5 (MetaTrader 5) |
|---|---|---|
| Execution | Series evaluated on chart updates | Event handlers with explicit buffers |
| Deployment | Runs in TradingView with alerts | Runs in the MT5 terminal or on a VPS |
| Broker access | Strategy orders are simulated; live automation needs a separate integration | Direct broker connectivity |
| Backtesting | Strategy Tester for strategy scripts | Strategy Tester for Expert Advisors |
| Code complexity | Simpler, functional syntax | C++-like, more powerful |
The calculation can be implemented on either platform, but the runtime model and buffer APIs differ. Read the Pine Script reference guide before porting logic between them.
Frequently Asked Questions
Related MQL5 pages
Risk and testing note
These educational examples require independent compilation and testing. A compile pass or an illustrative setting does not establish execution correctness or profitability. Any reported performance must be supported by reproducible data and test settings; past results do not predict future returns. Trading involves significant risk of loss. This content is not financial advice.