Saturday, January 31, 2026

Author: Roberto Jacobs (3rjfx) | Featured on Forex Home Expert

1. Introduction

In the world of technical analysis, the ZigZag indicator is legendary for its ability to filter market noise and highlight the true structure of the market—its peaks and troughs. However, every veteran trader knows its greatest weakness: it repaints. A confirmed low today can disappear tomorrow if the price continues to drop.

To solve this, I developed ZigZag Reverses, an indicator that doesn't just track price; it measures Momentum Energy based on the laws of physics.

NZDUSDH1_ZigZag_Reverses_Black Figure 1: ZigZag_Reverses indicator for MT5 display with 70% threshold line

2. The Newtonian Philosophy: Force and Inertia

My inspiration for this indicator comes from Sir Isaac Newton’s Laws of Motion. Imagine a ball being thrown against the floor. The initial force (the trend) creates a massive bounce. However, as gravity and friction take over, the energy dissipates.

In trading, when a price hits an extremum (High or Low), it possesses 100% of that trend's force. For a reversal to be mathematically and physically probable, that force must decay past a certain threshold. Through my observations, the 30% and 70% levels act as the Threshold of No Return.

NZDUSDM1_ZigZag_ReversesFigure 2: ZigZag Reverses indicator for MT5 display with 70% threshold line - Price Reverses Rice to Down

3. How ZigZag Reverses Works

Unlike standard ZigZag, this indicator calculates two dynamic confirmation lines based on the most recent price swing:

  • The 70% Fall Line (Highest to Fall): After a Peak is formed, we don't assume a reversal immediately. We wait for the price to drop below 70% of the previous swing's range. If the Close Price breaks this level, the bullish momentum is physically exhausted, confirming a Bearish Reversal.
  • NZDUSDD1_ZigZag_Reverses_Rise_to_Down Figure 3: Price breaking the 70% threshold, confirming a bearish momentum shift
  • The 30% Rise Line (Lowest to Rise): Conversely, after a Trough, we wait for the price to climb above the 30% mark. Breaking this level confirms that the selling pressure has faded and a Bullish Reversal is in play.
  • NZDUSDM10_ZigZag_Reverses_Down_to_Rise Figure 4: Price breaking the 30% threshold, confirming a bullish momentum shift

4. Why Close Price Matters

The indicator is programmed to be objective. It creates a TrendLine on the chart to visualize these thresholds. As per my testing, if a bar closes beyond these lines, the probability of the trend continuing in the new direction increases significantly. It is no longer a guess—it is a confirmation of shifting market force.

5. Input Parameters Properties

ZigZag_Reverses_Input_Properties Figure 5: ZigZag Reverses Indicator Input Parameters Properties

6. Technical Specifications:

  • Dynamic Thresholds: Customizable `updown` and `downup` percentages to suit different market volatilities.
  • Visual Confirmation: Automatic horizontal lines marking the breakout zones.
  • Clean Logic: Built with high-performance MQL5 code, ensuring zero lag in calculation.

7. Practical Strategy for Manual Traders

To maximize the effectiveness of the ZigZag Reverses indicator, manual traders can utilize a set-and-forget approach using Pending Orders. Once the ZigZag extremum is stabilized (no longer repainting), follow these steps:

  • Bullish Confirmation: When a stable Extremum Low is identified, traders can place a BUY_STOP order exactly at the 30% Line. This ensures an entry only when the upward momentum is physically confirmed by the price breaking the threshold.
  • Bearish Confirmation: When a stable Extremum High is identified, traders can place a SELL_STOP order exactly at the 70% Line. This ensures you are selling only when the price has officially "exhausted" its bullish force and crossed into the reversal zone.

⚠️ Important: Risk Disclaimer

Trading foreign exchange on margin carries a high level of risk and may not be suitable for all investors. The high degree of leverage can work against you as well as for you. Before deciding to invest in foreign exchange, you should carefully consider your investment objectives, level of experience, and risk appetite. The Newtonian logic and indicators provided in this article are for educational purposes and do not guarantee profits. Past performance is not indicative of future results.

8. Conclusion

Trading shouldn't be about catching falling knives; it should be about measuring the weight of the market. By applying the principles of Newtonian mechanics to the ZigZag structure, we transform a repainting tool into a reliable momentum gauge.

9. Vital Records

If you think the ZigZag Reverses Indicator is worthy of being used for automated trading as an Expert Advisor, please leave a comment below this article.

If at least 25 people agree that this indicator is worthy of being used as an Expert Advisor, I will create an Expert Advisor based on its signals and share it on this blog.

Thanks for reading this article.

See a complete list of other Multi-Timeframe indicators, Multi-Currency Expert Advisors, MQL4/MQL5 tutorials, and algorithmic trading tools at:

Note: Please see the source program and download at the bottom of this article.

Risk Warning: Trading Forex and CFDs involves significant risk and may not be suitable for all investors. All content provided is for educational purposes only.


//+------------------------------------------------------------------+
//|                                              ZigZag Reverses.mq5 |
//|        Copyright 2026, Roberto Jacobs (3rjfx) ~ Date: 2026-01-30 |
//|                              https://www.mql5.com/en/users/3rjfx |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Roberto Jacobs (3rjfx) ~ Date: 2026-01-30"
#property link      "https://www.mql5.com/en/users/3rjfx"
#property version   "1.00"
#property indicator_chart_window
//--
//---
#property indicator_buffers 3
#property indicator_plots   3
//---
#property indicator_type1   DRAW_NONE
#property indicator_type2   DRAW_NONE
#property indicator_type3   DRAW_NONE
//--
//--- input parameters
input int          ZDepth = 25;            // Input Zigzag Depth
input int      ZDeviation = 5;             // Input Zigzag Deviation
input int       ZBackstep = 3;             // Input Zigzag Backstep
input double       updown = 70.0;          // Highest to Fall (in pct)
input double       downup = 30.0;          // Lowest to Rise (in pct)
input color      contRise = clrWhite;      // Color Line Rise Continue
input color      contDown = clrWhite;      // Color Line Down Continue
input color      revsRise = clrRed;        // Color Line Rise Reverses
input color      revsDown = clrRed;        // Color Line Down Reverses
//--
//--- indicator buffers
double         ZZBuffer[];
double         ZRBuffer[];
double         ZDBuffer[];
//--
int hZ;
//--
string obnm="obzz_";
string iname="Examples\\ZigZag.ex5";
string indiname;
//--
#define RDown updown/100.0
#define DRise downup/100.0
#define DATA_LIMIT  369
//---------//
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
    //--
    SetIndexBuffer(0,ZZBuffer,INDICATOR_DATA);
    SetIndexBuffer(1,ZRBuffer,INDICATOR_DATA);
    SetIndexBuffer(2,ZDBuffer,INDICATOR_DATA);
    //--
    hZ=iCustom(Symbol(),0,iname,ZDepth,ZDeviation,ZBackstep);   //-- Handle for the ZigZag indicator
    //--
    if(hZ==INVALID_HANDLE)
      {
        printf("Failed to create handle of the ZigZag indicator for ",Symbol());
        return(INIT_FAILED);
      }
    //--
    PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
    indiname=StringFormat("ZZR(%d,%d,%d)",ZDepth,ZDeviation,ZBackstep);
    IndicatorSetString(INDICATOR_SHORTNAME,indiname);
    PlotIndexSetString(0,PLOT_LABEL,indiname);
    PlotIndexSetString(1,PLOT_LABEL,"Level Rise");
    PlotIndexSetString(2,PLOT_LABEL,"Level Down");
    IndicatorSetInteger(INDICATOR_DIGITS,Digits());
//---
   return(INIT_SUCCEEDED);
  }
//---------//
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//----
    Comment("");
    //--
    IndicatorRelease(hZ);
    ObjectsDeleteAll(0,obnm,-1,-1);
    //--
    Print(getUninitReasonText(reason));
    //--
    if(reason==REASON_REMOVE)
      {
       PrintFormat("An indicator %s deletes from the chart..!!",indiname,__FILE__);
      }
    //--
//----
   return;
  }  
//---------//
//+------------------------------------------------------------------+
//| 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[],
                const int &spread[])
  {
//---
    int i,limit;
    int zh=0,zl=0;
    datetime tmi=0;
//---- insufficient data
    if(rates_total<DATA_LIMIT)
       return(0);
//--- last counted bar will be recounted
    limit=rates_total-prev_calculated;
    if(limit==0) limit=DATA_LIMIT;
    if(prev_calculated>0) limit++;
    //--
    int calculated=BarsCalculated(hZ);
    if(calculated<rates_total)
      {
       Print("Not all data of ZigZag Indicator is calculated (",calculated,"bars ). Error",GetLastError());
       return(0);
      }
    //--
    limit=DATA_LIMIT;
    //--
    ArrayResize(ZZBuffer,limit,limit);
    ArrayResize(ZRBuffer,limit,limit);
    ArrayResize(ZDBuffer,limit,limit);
    //--
    ArraySetAsSeries(ZZBuffer,true);
    ArraySetAsSeries(ZRBuffer,true);
    ArraySetAsSeries(ZDBuffer,true);
    ArraySetAsSeries(open,true);
    ArraySetAsSeries(high,true);
    ArraySetAsSeries(low,true);
    ArraySetAsSeries(close,true);
    ArraySetAsSeries(time,true);
    //--
    //--- we can copy not all data
    int to_copy;
    int values_to_copy; 
    if(prev_calculated>rates_total || prev_calculated<0) 
      {
        to_copy=values_to_copy=rates_total;
      }
    else
      {
       to_copy=values_to_copy=rates_total-prev_calculated;
        if(prev_calculated>0) to_copy++; values_to_copy++;
      }
    //--- get ZigZag buffers
    if(IsStopped()) return(0); //Checking for stop flag
    if(CopyBuffer(hZ,0,0,to_copy,ZZBuffer)<0)
      {
       Print("Getting ZigZag indicator buffers is failed! Error",GetLastError());
       return(0);
      }
    //--
    for(i=limit-1; i>=0; i--)
      {
        if(ZZBuffer[i]==high[i]) { zh=i; tmi=time[i+3]; }
        if(ZZBuffer[i]==low[i])  { zl=i; tmi=time[i+3]; }
        ZDBuffer[i]=0.0;
        ZRBuffer[i]=0.0;
        //--
        if(i==0)
          {
            if((zl<zh && zl>0) || (zl>zh && zh==0)) 
              { 
                ZRBuffer[zl]=NormalizeDouble(low[zl]+((high[zh]-low[zl])*DRise),Digits());
                CreateTrendLine(0,obnm+"Price Reverses from Down To Rise_"+DoubleToString(DRise,2)+"(%)",time[0],ZRBuffer[zl],tmi,ZRBuffer[zl],1,STYLE_SOLID,revsDown,false,true);
                for(int x=0; x<zl; x++)
                  {
                    ZRBuffer[x]=ZRBuffer[zl];
                    ZDBuffer[x]=0.0;
                  }
                CreateTrendLine(0,obnm+"ZigZag Bottom ~ Breakout continue to Down",time[0],ZZBuffer[zl],tmi,ZZBuffer[zl],1,STYLE_SOLID,contDown,false,true);
              }
            if((zh<zl && zh>0) || (zh>zl && zl==0)) 
              { 
                ZDBuffer[zh]=NormalizeDouble(low[zl]+((high[zh]-low[zl])*RDown),Digits());
                CreateTrendLine(0,obnm+"Price Reverses from Rise To Down_"+DoubleToString(RDown,2)+"(%)",time[0],ZDBuffer[zh],tmi,ZDBuffer[zh],1,STYLE_SOLID,revsRise,false,true);
                for(int x=0; x<zh; x++)
                  {
                    ZDBuffer[x]=ZDBuffer[zh];
                    ZRBuffer[x]=0.0;
                  }
                CreateTrendLine(0,obnm+"ZigZag Top ~ Breakout continue to Rise",time[0],ZZBuffer[zh],tmi,ZZBuffer[zh],1,STYLE_SOLID,contRise,false,true);
              }
          }
      }
    //--
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//---------//
//+------------------------------------------------------------------+

bool CreateTrendLine(long     chartid, 
                     string   line_name,
                     datetime line_time1,
                     double   line_price1,
                     datetime line_time2,
                     double   line_price2,
                     int      line_width,
                     int      line_style,
                     color    line_color,
                     bool     ray_right,
                     bool     line_hidden)
  {  
//---
   ObjectDelete(chartid,line_name);
   //--
   if(ObjectCreate(chartid,line_name,OBJ_TREND,0,line_time1,line_price1,line_time2,line_price2)) // create trend line
     {
       ObjectSetInteger(chartid,line_name,OBJPROP_WIDTH,line_width);
       ObjectSetInteger(chartid,line_name,OBJPROP_STYLE,line_style);
       ObjectSetInteger(chartid,line_name,OBJPROP_COLOR,line_color);
       ObjectSetInteger(chartid,line_name,OBJPROP_RAY,ray_right);
       ObjectSetInteger(chartid,line_name,OBJPROP_HIDDEN,line_hidden);
     } 
   else 
      {Print("Failed to create the object OBJ_TREND ",line_name,", Error code = ", GetLastError()); return(false);}
   //--
   return(true);
//---
  } //-end CreateTrendLine()   
//---------//

string getUninitReasonText(int reasonCode) 
  { 
//---
   string text=""; 
   //--- 
   switch(reasonCode) 
     { 
       case REASON_PROGRAM:
            text="The EA has stopped working calling by remove function."; break;
       case REASON_REMOVE: 
            text="Program "+__FILE__+" was removed from chart"; break;
       case REASON_RECOMPILE:
            text="Program recompiled."; break;    
       case REASON_CHARTCHANGE: 
            text="Symbol or timeframe was changed"; break;
       case REASON_CHARTCLOSE: 
            text="Chart was closed"; break; 
       case REASON_PARAMETERS: 
            text="Input-parameter was changed"; break;            
       case REASON_ACCOUNT: 
            text="Account was changed"; break; 
       case REASON_TEMPLATE: 
            text="New template was applied to chart"; break; 
       case REASON_INITFAILED:
            text="The OnInit() handler returned a non-zero value."; break;
       case REASON_CLOSE: 
            text="Terminal closed."; break;
       default: text="Another reason"; break;
     } 
   //--
   return text;
//---
  } //-end getUninitReasonText()
//---------//
***Copyright © 2026 3rjfx ~ For educational purposes only.***

Please download the ZigZag Reverses indicator: ZigZag Reverses

If you want to get the source code of the program, please send your request via the Contact page by mentioning the article and program you want.


© 2026 ZigZag Reverses - Developed by Roberto Jacobs (3rjfx)

Tagged: , , , , , , , , , , ,

0 comments:

Post a Comment

Featured Post

A Comprehensive Guide to FiboPivotCandleBar: Functions and Performance of the Expert Advisor for MT5

Author: Roberto Jacobs (3rjfx) | Featured on Forex Home Expert FiboPivotCandleBar is a sophisticated trading tool designed to prov...