//+------------------------------------------------------------------+ //| BrokerCheck_MACD | //| MACD after Appel: MACD line = EMA(close, fast) - EMA(close, | //| slow), signal line = EMA of the MACD line, histogram = MACD | //| line - signal line. Every EMA is seeded with a simple mean. | //| Signal-line crossings are marked with arrows on closed bars; | //| zero-line crossings are stored in a calculation buffer. | //| Native MQL only: no DLL imports, no WebRequest, no order or | //| account functions, no alerts, no chart objects. | //| Descriptive tool: it marks past crossings and does not forecast | //| prices or trade. It does not detect divergences. | //+------------------------------------------------------------------+ #property copyright "BrokerCheck" #property version "1.00" #property description "MACD (EMA signal line) with histogram and signal-line crossing arrows, confirmed on closed bars." #property strict #property indicator_separate_window #property indicator_level1 0.0 #property indicator_levelcolor clrSilver #property indicator_levelstyle STYLE_DOT #property indicator_buffers 13 #property indicator_plots 6 #property indicator_label1 "Histogram above zero (MACD - signal)" #property indicator_type1 DRAW_HISTOGRAM #property indicator_color1 clrMediumSeaGreen #property indicator_width1 2 #property indicator_label2 "Histogram below zero (MACD - signal)" #property indicator_type2 DRAW_HISTOGRAM #property indicator_color2 clrOrangeRed #property indicator_width2 2 #property indicator_label3 "MACD" #property indicator_type3 DRAW_LINE #property indicator_color3 clrDodgerBlue #property indicator_width3 2 #property indicator_label4 "Signal" #property indicator_type4 DRAW_LINE #property indicator_color4 clrGold #property indicator_width4 1 #property indicator_label5 "Bullish signal-line crossing" #property indicator_type5 DRAW_ARROW #property indicator_color5 clrLime #property indicator_label6 "Bearish signal-line crossing" #property indicator_type6 DRAW_ARROW #property indicator_color6 clrRed input int FastEma = 12; // Fast EMA period (close prices) input int SlowEma = 26; // Slow EMA period (close prices) input int SignalEma = 9; // Signal line: EMA period of the MACD line double HistUpBuffer[]; // 0 histogram where MACD - signal > 0 (drawn) double HistDownBuffer[]; // 1 histogram where MACD - signal < 0 (drawn) double MacdBuffer[]; // 2 MACD line double SignalBuffer[]; // 3 signal line double BullArrowBuffer[]; // 4 up arrow at a bullish signal-line crossing (value = MACD) double BearArrowBuffer[]; // 5 down arrow at a bearish signal-line crossing (value = MACD) double HistogramBuffer[]; // 6 histogram MACD - signal, every bar (calculation) double FastEmaBuffer[]; // 7 fast EMA of close (calculation) double SlowEmaBuffer[]; // 8 slow EMA of close (calculation) double MacdSideBuffer[]; // 9 side of the MACD line: +1 above zero, -1 below, 0 none yet (calculation) double SignalSideBuffer[]; // 10 side of the MACD line versus its signal line (sign of the histogram) (calculation) double ZeroCrossBuffer[]; // 11 zero-line crossing on this closed bar: +1 up, -1 down, 0 none (calculation) double SignalCrossBuffer[]; // 12 signal-line crossing on this closed bar: +1 bullish, -1 bearish, 0 none (calculation) int OnInit() { if(FastEma<1 || SlowEma<=FastEma || SignalEma<1) return(INIT_PARAMETERS_INCORRECT); if(!SetIndexBuffer(0,HistUpBuffer,INDICATOR_DATA)) return(INIT_FAILED); if(!SetIndexBuffer(1,HistDownBuffer,INDICATOR_DATA)) return(INIT_FAILED); if(!SetIndexBuffer(2,MacdBuffer,INDICATOR_DATA)) return(INIT_FAILED); if(!SetIndexBuffer(3,SignalBuffer,INDICATOR_DATA)) return(INIT_FAILED); if(!SetIndexBuffer(4,BullArrowBuffer,INDICATOR_DATA)) return(INIT_FAILED); if(!SetIndexBuffer(5,BearArrowBuffer,INDICATOR_DATA)) return(INIT_FAILED); if(!SetIndexBuffer(6,HistogramBuffer,INDICATOR_CALCULATIONS)) return(INIT_FAILED); if(!SetIndexBuffer(7,FastEmaBuffer,INDICATOR_CALCULATIONS)) return(INIT_FAILED); if(!SetIndexBuffer(8,SlowEmaBuffer,INDICATOR_CALCULATIONS)) return(INIT_FAILED); if(!SetIndexBuffer(9,MacdSideBuffer,INDICATOR_CALCULATIONS)) return(INIT_FAILED); if(!SetIndexBuffer(10,SignalSideBuffer,INDICATOR_CALCULATIONS)) return(INIT_FAILED); if(!SetIndexBuffer(11,ZeroCrossBuffer,INDICATOR_CALCULATIONS)) return(INIT_FAILED); if(!SetIndexBuffer(12,SignalCrossBuffer,INDICATOR_CALCULATIONS)) return(INIT_FAILED); BcSetChronological(); for(int plot=0; plot<6; ++plot) PlotIndexSetDouble(plot,PLOT_EMPTY_VALUE,EMPTY_VALUE); PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,SlowEma+SignalEma-2); PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,SlowEma+SignalEma-2); PlotIndexSetInteger(2,PLOT_DRAW_BEGIN,SlowEma-1); PlotIndexSetInteger(3,PLOT_DRAW_BEGIN,SlowEma+SignalEma-2); PlotIndexSetInteger(4,PLOT_DRAW_BEGIN,SlowEma+SignalEma-1); PlotIndexSetInteger(5,PLOT_DRAW_BEGIN,SlowEma+SignalEma-1); PlotIndexSetInteger(4,PLOT_ARROW,233); PlotIndexSetInteger(4,PLOT_ARROW_SHIFT,12); PlotIndexSetInteger(5,PLOT_ARROW,234); PlotIndexSetInteger(5,PLOT_ARROW_SHIFT,-12); IndicatorSetInteger(INDICATOR_DIGITS,_Digits+1); IndicatorSetString(INDICATOR_SHORTNAME,"BrokerCheck MACD ("+IntegerToString(FastEma)+", "+ IntegerToString(SlowEma)+", "+IntegerToString(SignalEma)+")"); return(INIT_SUCCEEDED); } //=== BROKERCHECK CALCULATION SECTION (identical in the .mq5 and .mq4 files; the native test harness compiles this exact text) === // Indexing is chronological: 0 = oldest bar. The close array and the buffers are switched to that order on every call. // MACD line = EMA(close, FastEma) - EMA(close, SlowEma); signal line = EMA(MACD line, SignalEma); // histogram = MACD line - signal line. Every EMA uses alpha = 2 / (N + 1) and starts from the simple mean // of its first N inputs. First values: MACD at SlowEma - 1, signal and histogram at SlowEma + SignalEma - 2. // Closed-bar rule: the newest bar (rates_total - 1) may still be forming, so it never confirms a crossing. // A crossing is written on the first closed bar that is strictly on the new side and is never back-dated. void BcSetChronological() { ArraySetAsSeries(HistUpBuffer,false); ArraySetAsSeries(HistDownBuffer,false); ArraySetAsSeries(MacdBuffer,false); ArraySetAsSeries(SignalBuffer,false); ArraySetAsSeries(BullArrowBuffer,false); ArraySetAsSeries(BearArrowBuffer,false); ArraySetAsSeries(HistogramBuffer,false); ArraySetAsSeries(FastEmaBuffer,false); ArraySetAsSeries(SlowEmaBuffer,false); ArraySetAsSeries(MacdSideBuffer,false); ArraySetAsSeries(SignalSideBuffer,false); ArraySetAsSeries(ZeroCrossBuffer,false); ArraySetAsSeries(SignalCrossBuffer,false); } void BcClearBars(const int from,const int to) { for(int i=from; i0.0) return(1.0); if(value<0.0) return(-1.0); return(previousSide); } // Sides of bar i: the MACD line against zero, and the MACD line against its signal line (sign of the histogram). // The first bar of each series has no previous side (0). void BcSides(const int i) { const int macdFrom=SlowEma-1; const int signalFrom=SlowEma+SignalEma-2; if(i>=macdFrom) MacdSideBuffer[i]=BcSide(MacdBuffer[i],(i>macdFrom ? MacdSideBuffer[i-1] : 0.0)); if(i>=signalFrom) SignalSideBuffer[i]=BcSide(HistogramBuffer[i],(i>signalFrom ? SignalSideBuffer[i-1] : 0.0)); } // Values of bar i: both EMAs (seeded at their first bar), MACD, signal, histogram and its split, then the sides. void BcCalculateBar(const int i,const double &close[]) { const int macdFrom=SlowEma-1; const int signalFrom=SlowEma+SignalEma-2; if(i=signalFrom) { SignalBuffer[i]=(i==signalFrom ? BcSeedMean(MacdBuffer,macdFrom,SignalEma) : BcEmaStep(SignalBuffer[i-1],MacdBuffer[i],SignalEma)); HistogramBuffer[i]=MacdBuffer[i]-SignalBuffer[i]; HistUpBuffer[i]=(HistogramBuffer[i]>0.0 ? HistogramBuffer[i] : EMPTY_VALUE); HistDownBuffer[i]=(HistogramBuffer[i]<0.0 ? HistogramBuffer[i] : EMPTY_VALUE); } BcSides(i); } // Crossings of closed bar c (the caller guarantees c <= rates_total - 2): a side that changes from +1 to -1 // or from -1 to +1 against the previous bar. The arrow sits at the MACD value of bar c. void BcEvents(const int c) { const int macdFrom=SlowEma-1; const int signalFrom=SlowEma+SignalEma-2; if(c>macdFrom && MacdSideBuffer[c-1]!=0.0 && MacdSideBuffer[c]!=MacdSideBuffer[c-1]) ZeroCrossBuffer[c]=MacdSideBuffer[c]; if(c>signalFrom && SignalSideBuffer[c-1]!=0.0 && SignalSideBuffer[c]!=SignalSideBuffer[c-1]) { SignalCrossBuffer[c]=SignalSideBuffer[c]; if(SignalSideBuffer[c]>0.0) BullArrowBuffer[c]=MacdBuffer[c]; else BearArrowBuffer[c]=MacdBuffer[c]; } } 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[]) { BcSetChronological(); ArraySetAsSeries(close,false); const int macdFrom=SlowEma-1; const int signalFrom=SlowEma+SignalEma-2; if(rates_total<=signalFrom) { // Insufficient history: nothing is drawn; returning 0 forces a full calculation next time. BcClearBars(0,rates_total); return(0); } int from; int eventFrom; if(prev_calculated<=signalFrom+1 || prev_calculated>rates_total) { // Full calculation: first call, reloaded history, or the signal seed bar was still the newest bar. BcClearBars(0,rates_total); from=0; eventFrom=macdFrom+1; } else { // Incremental: the previous newest bar and every new bar are recalculated. BcClearBars(prev_calculated-1,rates_total); from=prev_calculated-1; eventFrom=prev_calculated-1; } for(int i=from; i