Showing posts with label Trading strategy. Show all posts
Showing posts with label Trading strategy. Show all posts

Sunday, February 1, 2026

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

1. Introduction

In the fast-paced world of Forex trading, the ability to see the Big Picture while maintaining focus on execution is what separates a professional from an amateur. Today, I am proud to introduce ZigZag Multi-timeframe (ZigZag_MTF), a high-performance multi-timeframe dashboard for MetaTrader 5 that redefines how we monitor market structure.

In high-speed forex trading, switching between timeframes to find a trend is a chore that often leads to missed opportunities. A true professional needs a Bird’s Eye View of the market. This is why I developed ZigZag_MTF for MetaTrader 5—not just an indicator, but a comprehensive multi-timeframe command center.

The ZigZag Multi-timeframe Indicator for MT5 (ZigZag_MTF) utilizes a template and displays a panel on the chart. Its function, operation, and usage are identical to the William's Percent Range Multi-Timeframe Indicator for MT5 (WPR_MTF) as detailed in the previous article. For a comprehensive guide on creating a multi-timeframe indicator, you can refer to the following resources: William's Percent Range Multi-Timeframe Indicator for MT5

USDCADH4_ZigZag_MTF - The ZiGZag_MTF indicator is displayed on the upper of the chartFigure 1: The ZiGZag_MTF indicator is displayed on the upper of the chart.
USDCADH4_ZigZag_MTF_bottom - The ZiGZag_MTF indicator is displayed on the bottom of the chartFigure 2: The ZiGZag_MTF indicator is displayed on the bottom of the chart.

2. The Interactive Dashboard Feature

The standout feature of my ZigZag_MTF is the Interactive UI Controller. Unlike standard MTF indicators that clutter your screen with messy lines, this tool provides a clean, clickable dashboard:

  • Instant Sync: Click any timeframe button (from M1 to MN) on the screen to instantly switch the chart.
  • Symbol Switcher: Navigate between different currency pairs directly from the dashboard.
  • Real-time Scanning: The indicator works in the background, calculating ZigZag points for every timeframe to ensure your data is always current.

3. The Fractal Nature of the Market

The market moves in fractals. A single trend on a Daily chart is composed of hundreds of smaller waves on the lower timeframes. To trade effectively, you need to see both. ZigZag_MTF provides a centralized command center, scanning ZigZag patterns across multiple timeframes simultaneously, ensuring you never miss a structural shift.

By tracking ZigZag patterns across all timeframes simultaneously, you can:

  • Identify the Major Trend from Daily/Monthly charts.
  • Pinpoint Optimal Entry on lower timeframes.
  • Avoid Noise by filtering out minor price fluctuations that don't align with the higher-order structure.

4. The "Engine" Logic: Stability vs. Agility

A unique feature of this indicator lies in its core calculation function: ZigZagMovementCalculation(). Unlike standard MTF indicators that treat all timeframes equally, ZigZag_MTF is engineered with a selective logic:

  • 1. Strategic Stability (H2 to MN1): On higher timeframes (H2, H4, up to Monthly), the ZigZag points represent the Grand Structure of the market. These levels are stable and serve as massive support and resistance zones. In my code, these timeframes provide the visual context (color-coding) but are not used for volatile signal generation, as their movement is naturally static.
  • 2. Tactical Agility (M1 to H1): The real energy of a trend reversal occurs within the lower timeframes. By using the logic `if(x < 12)`, the indicator focuses its analytical power on the 11 timeframes from M1 to H1. This is where the ZigZag Rise or Down signals are calculated. This ensures that your entry signals are fresh, agile, and reactive to current price action, while still being protected by the Big Picture trend from above.

5. Key Features of the Dashboard

  • Interactive UI Controller: Switch between timeframes (M1 to MN) or change currency symbols with a single click directly on your chart. No more messy terminal tabs.
  • Optimized Performance: Built with efficient handle management, this indicator is light on your CPU, making it ideal for traders who run multiple assets simultaneously.
  • Responsive Scaling: Whether you use a 4K monitor or a laptop, the dashboard scales perfectly thanks to its internal window-scaling logic.

6. How to Trade with ZigZag_MTF

  • Identify the Anchor: Use the dashboard to see if higher timeframes (like D1) are showing a stable ZigZag Low.
  • Wait for the Pulse: Look for the active signal (Rise) on the lower timeframes (M15-H1) as calculated by the ZigZagMovementCalculation() function logic and look at the arrows and alerts provided by the indicator
  • Confirm the Break: As discussed in our previous article on Newtonian mechanics, use the 30% / 70% threshold for a high-probability entry.

7. Conclusion

ZigZag_MTF is not just an indicator; it is a tactical map. By separating structural stability from execution agility, it allows you to trade with the calmness of a long-term investor and the precision of a scalper.

⚠️ 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 ZigZag Multi-timeframe (ZigZag_MTF) logic and indicators provided in this article are for educational purposes and do not guarantee profits. Past performance is not indicative of future results.

Vital Records

If you think the ZigZag Multi-timeframe (ZigZag_MTF) 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.

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_MTF.mq5 |
//|        Copyright 2026, Roberto Jacobs (3rjfx) ~ Date: 2026-26-01 |
//|                              https://www.mql5.com/en/users/3rjfx |
//+------------------------------------------------------------------+
#property copyright   "Copyright 2026, Roberto Jacobs (3rjfx) ~ Date: 2026-01-26"
#property link        "https://www.mql5.com/en/users/3rjfx"
#property version     "1.00"
#property description "ZigZag_MTF is the ZigZag Indicator in Multi Timeframe"
#property description "for MT5 which is calculated and scan ZigZag on each timeframe."
//---
#property indicator_chart_window
#property indicator_plots   1
#property indicator_buffers 1
//---
//--
enum YN
  {
   No,
   Yes
  };
//--
enum fonts
  {
   Verdana,
   Bodoni_MT_Black
  };
//--
//---
//--- ZigZag Indicator Input Properties
input group              "=== ZigZag Indicator Input Properties ===";  // ZigZag Indicator Input Properties
input int                zzDepth = 12;               // Input ZigZag Depth, default 12
input int                zzDevia = 5;                // Input ZigZag Deviation, default 5
input int                zzBackS = 3;                // Input ZigZag Back Step, default 3
input group   "====  Indicator Color and Font   ===="
input color              ArrowUp = clrMediumSeaGreen; // Arrow Up Color
input color              ArrowDn = clrDeepPink;       // Arrow Down Color
input color              NTArrow = clrGold;           // Arrow No Signal
input fonts              f_model = Bodoni_MT_Black;   // Select Font Model
input group   "====  Input parameters for alerts  ===="
input YN                  alerts = Yes;               // Display Alerts Pop-up on Chart (Yes) or (No)
input YN           UseEmailAlert = No;                // Email Alert (Yes) or (No)
input YN           UseSendnotify = No;                // Send Notification (Yes) or (No)
//---
//---------//
//+------------------------------------------------------------------+
//| class for MTF indicator                                          |
//+------------------------------------------------------------------+
class MTF_Indi
  {
   //---
   public:
   //--
   int               fbar;
   int               star,
                     tstar,
                     bstar;
   int               hZigZag[];
   int               TNamex,
                     TNamexn,
                     TNamey1,
                     TNamey2,
                     TNamey3,
                     TNamey1n,
                     TNamey2n,
                     TNamey3n;
   int               tfxar;
   int               up,dw;
   int               tfhalf;
   int               maxbar;
   int               ttlbars;
   int               scaleX,
                     scaleA,
                     scaleY,
                     horizL1,
                     horizL2,
                     horizL3,
                     horizL4,
                     horizL5,
                     horizL6,
                     vertiL1,
                     vertiL2,
                     vertiL3,
                     vertiL4,
                     vertiL5,
                     vertiL6,
                     offsetX,
                     offsetY,
                     fontSize,
                     windchar,
                     windsize;
   int               corx,
                     cory,
                     txttf;
   int               curmin,
                     prvmin;
   int               corpos;
   int               pospos,
                     postop,
                     posbot;
   int               curAlert;
   int               prvAlert;
   int               ZZ_Period;
   //--
   long              CI;
   string            posisi,
                     sigpos,
                     indname,
                     msgText,
                     InHname,
                     ObjName;
   string            cstar,
                     artop,
                     arbot;
   string            hName1,
                     hName2,
                     hName3;
   string            font_mode;
   bool              display;
   double            OPEN[],
                     HIGH[],
                     LOW[],
                     CLOSE[];
   long              VOLUME[];
   datetime          TIME[];
   datetime          cbartime;
   //--
   int               year,  // Year
                     mon,   // Month
                     day,   // Day
                     hour,  // Hour
                     min,   // Minutes
                     sec,   // Seconds
                     dow,   // Day of week (0-Sunday, 1-Monday, ... ,6-Saturday)
                     doy;   // Day number of the year (January 1st is assigned the number value of zero)
   //---- buffers
   double            PowerMove[];
   string            TFSc[];
   color             Arwcolor[];
   color             TColor;
   ENUM_TIMEFRAMES   TFId[];
   ENUM_BASE_CORNER  bcor;
   ENUM_ANCHOR_POINT ancp;
   //---
   //---
   //- Constructor
                     MTF_Indi(void):
                     year(0),
                     mon(1),
                     day(2),
                     hour(3),
                     min(4),
                     sec(5),
                     dow(6),
                     doy(7),
                     fbar(125),
                     star(181),
                     maxbar(3),
                     pospos(0),
                     postop(0),
                     posbot(1),
                     tstar(217),
                     bstar(218),
                     tfhalf(11),
                     scaleX(35),
                     scaleA(36),
                     scaleY(50),
                     offsetY(18),
                     offsetX(120),
                     fontSize(7),
                     cbartime(0),
                     posisi(""),
                     sigpos(""),
                     msgText(""),
                     curAlert(0),
                     prvAlert(0),
                     windsize(12),
                     windchar(108),
                     CI(ChartID()),
                     display(false),
                     ObjName("ZigZag_"),
                     font_mode(FontsModel(f_model)),
                     cstar(CharToString((uchar)star)),
                     artop(CharToString((uchar)tstar)),
                     arbot(CharToString((uchar)bstar)),
                     InHname("Examples\\ZigZag.ex5"),
                     indname(MQLInfoString(MQL_PROGRAM_NAME))
     {
     }
   //---
   //- Destructor
                    ~MTF_Indi(void)
     {
     }
   //---
   //---
   virtual void      VZZ_MTF_Config(void)
     {
      //--
      ENUM_TIMEFRAMES TFIx[]= {PERIOD_M1,PERIOD_M2,PERIOD_M3,PERIOD_M4,PERIOD_M5,PERIOD_M6,PERIOD_M10,PERIOD_M12,PERIOD_M15,
                               PERIOD_M20,PERIOD_M30,PERIOD_H1,PERIOD_H2,PERIOD_H3,PERIOD_H4,PERIOD_H6,PERIOD_H8,PERIOD_H12,
                               PERIOD_D1,PERIOD_W1,PERIOD_MN1};
      tfxar=ArraySize(TFIx);
      ArrayResize(TFId,tfxar,tfxar);
      ArrayResize(Arwcolor,tfxar,tfxar);
      ArrayCopy(TFId,TFIx,0,0,WHOLE_ARRAY);
      //--
      string TFxc[]= {"M1","M2","M3","M4","M5","M6","M10","M12","M15","M20","M30","H1",
                      "H2","H3","H4","H6","H8","H12","D1","W1","MN1"}; // 21 Timeframes
      //--
      ArrayResize(hZigZag,tfxar,tfxar);
      ArrayResize(TFSc,tfxar,tfxar);
      ArrayCopy(TFSc,TFxc,0,0,WHOLE_ARRAY);
      //--
      if(zzDepth<5 || zzDepth>fbar)
        {
         ZZ_Period=12;
         PrintFormat("Incorrect value for input variable zzDepth = %d. Indicator will use value %d for calculations.",
                  zzDepth,ZZ_Period);
        }
      else
         ZZ_Period=zzDepth;
      //--
      ttlbars=fbar;
      //--
      for(int x=0; x<tfxar; x++)
        hZigZag[x]=iCustom(Symbol(),TFId[x],InHname,ZZ_Period,zzDevia,zzBackS); // Handle of ZigZag Indicator on each timeframe
      //--
      DeletedZigZagObject();
      ZigZagMovementCalculation(25);
      PositionCore();
      //--
      if(display)
         DrawZigZagObject();
      //---
     }
   //---
   //---
   void              PositionCore(void)
     {
      corpos=pospos;
      if(corpos>-1)
        {
         if(corpos==postop)
           {
            bcor=CORNER_LEFT_UPPER;
            ancp=ANCHOR_CENTER;
            corx=155;
            cory=13;
            txttf=45;
            horizL1=8;
            horizL2=-5;
            horizL3=8;
            horizL4=-5;
            horizL5=11;
            horizL6=10;
            vertiL1=39;
            vertiL2=18;
            vertiL3=69;
            vertiL4=48;
            vertiL5=52;
            vertiL6=67;
            TNamex=554;
            TNamexn=562;
            TNamey1=30;
            TNamey2=46;
            TNamey3=62;
            TNamey1n=38;
            TNamey2n=54;
            TNamey3n=70;
            hName1="Z";
            hName2="Z";
            hName3="I";
            //--
            DisplayButtonClick("cstar");
            DisplayButtonClick("arbot");
            //--
           }
         if(corpos==posbot)
           {
            bcor=CORNER_LEFT_LOWER;
            ancp=ANCHOR_CENTER;
            corx=155;
            cory=74;
            txttf=45;
            horizL1=8;
            horizL2=-5;
            horizL3=8;
            horizL4=-5;
            horizL5=11;
            horizL6=10;
            vertiL1=34;
            vertiL2=29;
            vertiL3=65;
            vertiL4=59;
            vertiL5=49;
            vertiL6=61;
            TNamex=554;
            TNamexn=562;
            TNamey1=13;
            TNamey2=46;
            TNamey3=62;
            TNamey1n=18;
            TNamey2n=33;
            TNamey3n=49;
            hName1="I";
            hName2="Z";
            hName3="Z";
            //--
            DisplayButtonClick("cstar");
            DisplayButtonClick("artop");
            //--
           }
         display=true;
        }
     }
   //---
   //---
   void              DrawZigZagObject(void)
     {
      //--
      CreateButtonTemplate(CI,ObjName+"Template",397,66,STYLE_SOLID,9,BORDER_RAISED,clrMistyRose,clrLavenderBlush,clrWhite,bcor,corx,cory,true);
      for(int x=0; x<tfhalf; x++)
        {
         CreateArrowLabel(CI,ObjName+"_win_arrow_"+string(x),CharToString((uchar)windchar),"Wingdings",windsize,Arwcolor[x],bcor,
                             txttf+horizL1+(x*scaleX)+offsetX+x,vertiL1,true,"Arrow_"+TFSc[x]);
         CreateButtonClick(CI,TFSc[x],27,15,font_mode,fontSize,BORDER_FLAT,TFSc[x],clrBurlyWood,clrSilver,clrBlue,
                              bcor,txttf+horizL2+(x*scaleA)+offsetX,vertiL2,true,
                              "Change Timeframe to : "+TFSc[x]);
        }
      for(int x=tfhalf, x2=0; x<tfxar; x++, x2++)
        {
         CreateArrowLabel(CI,ObjName+"_win_arrow_"+string(x),CharToString((uchar)windchar),"Wingdings",windsize,Arwcolor[x],bcor,
                             txttf+horizL3+(x2*scaleX)+offsetX+x2,vertiL3,true,"Arrow_"+TFSc[x]);
         CreateButtonClick(CI,TFSc[x],27,15,font_mode,fontSize,BORDER_FLAT,TFSc[x],clrBurlyWood,clrSilver,clrBlue,
                              bcor,txttf+horizL4+(x2*scaleA)+offsetX,vertiL4,true,
                              "Change Timeframe to : "+TFSc[x]);
         //--
         if(x==20)
           {
            int arrowChar=TColor==ArrowUp ? 200 : TColor==ArrowDn ? 202 : windchar;
            CreateArrowLabel(CI,ObjName+"_tfx_arrow_"+string(x+1),"Move",font_mode,fontSize,clrBlue,bcor,
                                519+horizL5,vertiL5,true,"Move");
            CreateArrowLabel(CI,ObjName+"_win_arrow_"+string(x+1),CharToString((uchar)arrowChar),"Wingdings",15,TColor,bcor,
                                522+horizL6,vertiL6,true,"Arrow Indicator Movement");
           }
        }
      DisplayButtonClick("X");
      CreateButtonTemplate(CI,ObjName+"TemplateName1",17,15,STYLE_SOLID,1,BORDER_FLAT,clrMistyRose,clrLavenderBlush,clrWhite,bcor,TNamex,TNamey1,true);
      CreateButtonTemplate(CI,ObjName+"TemplateName2",17,15,STYLE_SOLID,1,BORDER_FLAT,clrMistyRose,clrLavenderBlush,clrWhite,bcor,TNamex,TNamey2,true);
      CreateButtonTemplate(CI,ObjName+"TemplateName3",17,15,STYLE_SOLID,1,BORDER_FLAT,clrMistyRose,clrLavenderBlush,clrWhite,bcor,TNamex,TNamey3,true);
      CreateArrowLabel(CI,ObjName+"_name1",hName1,font_mode,fontSize+1,clrBlue,bcor,TNamexn,TNamey1n,true,hName1);
      CreateArrowLabel(CI,ObjName+"_name2",hName2,font_mode,fontSize+1,clrBlue,bcor,TNamexn,TNamey2n,true,hName2);
      CreateArrowLabel(CI,ObjName+"_name3",hName3,font_mode,fontSize+1,clrBlue,bcor,TNamexn,TNamey3n,true,hName3);
      //--
      if(corpos==postop)
        {
          DisplayButtonClick("cstar");
          DisplayButtonClick("arbot");
        }
      if(corpos==posbot)
        {
          DisplayButtonClick("cstar");
          DisplayButtonClick("artop");
        }
      //--
      return;
      //---
     } //-end DrawZigZagObject()
   //---
   //---
   void              PanelPosChange(int inpos)
     {
      corpos=inpos;
      //--
      if(inpos>=0)
        {
         if(inpos==postop)
           {
            bcor=CORNER_LEFT_UPPER;
            ancp=ANCHOR_CENTER;
            corx=155;
            cory=13;
            txttf=45;
            horizL1=8;
            horizL2=-5;
            horizL3=8;
            horizL4=-5;
            horizL5=11;
            horizL6=10;
            vertiL1=39;
            vertiL2=18;
            vertiL3=69;
            vertiL4=48;
            vertiL5=52;
            vertiL6=67;
            TNamex=554;
            TNamexn=562;
            TNamey1=30;
            TNamey2=46;
            TNamey3=62;
            TNamey1n=38;
            TNamey2n=54;
            TNamey3n=70;
            hName1="Z";
            hName2="Z";
            hName3="I";
            //--
            DisplayButtonClick("cstar");
            DisplayButtonClick("arbot");
            //--
           }
         if(inpos==posbot)
           {
            bcor=CORNER_LEFT_LOWER;
            ancp=ANCHOR_CENTER;
            corx=155;
            cory=74;
            txttf=45;
            horizL1=8;
            horizL2=-5;
            horizL3=8;
            horizL4=-5;
            horizL5=11;
            horizL6=10;
            vertiL1=34;
            vertiL2=29;
            vertiL3=65;
            vertiL4=59;
            vertiL5=49;
            vertiL6=61;
            TNamex=554;
            TNamexn=562;
            TNamey1=25;
            TNamey2=41;
            TNamey3=57;
            TNamey1n=18;
            TNamey2n=33;
            TNamey3n=49;
            hName1="I";
            hName2="Z";
            hName3="Z";
            //--
            DisplayButtonClick("cstar");
            DisplayButtonClick("artop");
            //--
           }
         display=true;
        }
      //---
     }
   //---
   //---
   void              UpdatePrice(ENUM_TIMEFRAMES xtf)
     {
      maxbar=fbar;
      //--
      ArrayFree(OPEN);
      ArrayFree(HIGH);
      ArrayFree(LOW);
      ArrayFree(CLOSE);
      ArrayFree(TIME);
      ArrayFree(VOLUME);
      //--
      ArrayResize(OPEN,maxbar,maxbar);
      ArrayResize(HIGH,maxbar,maxbar);
      ArrayResize(LOW,maxbar,maxbar);
      ArrayResize(CLOSE,maxbar,maxbar);
      ArrayResize(TIME,maxbar,maxbar);
      ArrayResize(VOLUME,maxbar,maxbar);
      //--
      ArraySetAsSeries(OPEN,true);
      ArraySetAsSeries(HIGH,true);
      ArraySetAsSeries(LOW,true);
      ArraySetAsSeries(CLOSE,true);
      ArraySetAsSeries(TIME,true);
      ArraySetAsSeries(VOLUME,true);
      //--
      ArrayInitialize(OPEN,0.0);
      ArrayInitialize(HIGH,0.0);
      ArrayInitialize(LOW,0.0);
      ArrayInitialize(CLOSE,0.0);
      ArrayInitialize(TIME,0);
      ArrayInitialize(VOLUME,0);
      //--
      int barx=PeriodSeconds(xtf)/60*maxbar;
      RefreshPrice(PERIOD_M1,maxbar);
      RefreshPrice(xtf,barx);
      //--
      int co=CopyOpen(Symbol(),xtf,0,maxbar,OPEN);
      int ch=CopyHigh(Symbol(),xtf,0,maxbar,HIGH);
      int cl=CopyLow(Symbol(),xtf,0,maxbar,LOW);
      int cc=CopyClose(Symbol(),xtf,0,maxbar,CLOSE);
      int ct=CopyTime(Symbol(),xtf,0,maxbar,TIME);
      int cv=CopyTickVolume(Symbol(),xtf,0,maxbar,VOLUME);
      //--
      return;
      //---
     } //-end UpdatePrice()
   //---
   //---
   void              RefreshPrice(ENUM_TIMEFRAMES xtf,int bars)
     {
      //--
      MqlRates parray[];
      ArraySetAsSeries(parray,true);
      int copied=CopyRates(Symbol(),xtf,0,bars,parray);
      //--
      return;
      //---
     } //-end RefreshPrice()
   //---
   //---
   int               ZigZagDirectionScan(const ENUM_TIMEFRAMES stf) // Scan ZigZag Direction
     {
      //--
      int ret=0;
      int rise=1,
          down=-1;
      int zzH=-1;
      int zzL=-1;
      //--
      bool ZZUps=false;
      bool ZZDwn=false;
      //--
      double res=0.0;
      UpdatePrice(stf);
      //--
      double VZZ[];
      ArrayResize(VZZ,fbar,fbar);
      ArraySetAsSeries(VZZ,true);
      //--
      int xx=TFIndexArray(stf);
      CopyBuffer(hZigZag[xx],0,0,fbar,VZZ);
      //--
      for(int i=fbar-1; i>=0; i--)
        {
          if(VZZ[i]==HIGH[i]) zzH=i;
          if(VZZ[i]==LOW[i])  zzL=i; 
        }
      //--
      ZZUps=(zzL<zzH && zzL>0)||(zzL>zzH && zzH==0);
      ZZDwn=(zzH<zzL && zzH>0)||(zzH>zzL && zzL==0);
      //--
      if(ZZUps) ret=rise; 
      if(ZZDwn) ret=down;
      //--
      return(ret);
      //---
     } //-end ZigZagDirectionScan()
   //---
   //---
   void              ZigZagMovementCalculation(int barCnt) // Scan the direction of ZigZag on each timeframe
     {
      //--
      ArrayResize(PowerMove,barCnt,barCnt);
      ArraySetAsSeries(PowerMove,true);
      //--
      for(int i=barCnt-1; i>=0; i--)
        {
         up=0;
         dw=0;
         //--
         for(int x=0; x<tfxar; x++)
           {
            Arwcolor[x]=NTArrow;
            PowerMove[i]=0.0;
            int PPM=ZigZagDirectionScan(TFId[x]);
            //--
            if(PPM>0)
              {
               up++;
               Arwcolor[x]=ArrowUp;
              }
            if(PPM<0)
              {
               dw++;
               Arwcolor[x]=ArrowDn;
              }
            //--
            if(x<12)
              {
                //--
                if(up>dw+1)
                  {
                   PowerMove[i]=1.0;
                   TColor=ArrowUp;
                   curAlert=1;
                  }
                if(dw>up+1)
                  {
                   PowerMove[i]=-1.0;
                   TColor=ArrowDn;
                   curAlert=-1;
                  }
              }
           }
        }
      //--
      return;
      //---
     } //-end ZigZagMovementCalculation()
   //---
   //---
   double NonZeroDiv(double val1,double val2)
     {
      //--
      double resval=0;
      if(val1==0.0 || val2==0.0) resval=0.00;
      else
      resval=val1/val2;
      //--
      return(resval);
      //---
     } //-end NonZeroDiv()
   //---
   //---
   int TFIndexArray(ENUM_TIMEFRAMES TF)
     {
      //--
      int res=-1;
      //--
      for(int x=0; x<tfxar; x++)
        {
          if(TF==TFId[x])
            {
              res=x;
              break;
            }
        }
      //--
      return(res);
      //---
     } //-end TFIndexArray()
   //---
   //---
   int               ThisTime(const int reqmode)
     {
      //--
      MqlDateTime tm;
      TimeCurrent(tm);
      int valtm=0;
      //--
      switch(reqmode)
        {
         case 0:
            valtm=tm.year;
            break;        // Return Year
         case 1:
            valtm=tm.mon;
            break;        // Return Month
         case 2:
            valtm=tm.day;
            break;        // Return Day
         case 3:
            valtm=tm.hour;
            break;        // Return Hour
         case 4:
            valtm=tm.min;
            break;        // Return Minutes
         case 5:
            valtm=tm.sec;
            break;        // Return Seconds
         case 6:
            valtm=tm.day_of_week;
            break;        // Return Day of week (0-Sunday, 1-Monday, ... ,6-Saturday)
         case 7:
            valtm=tm.day_of_year;
            break;        // Return Day number of the year (January 1st is assigned the number value of zero)
        }
      //--
      return(valtm);
      //---
     } //-end ThisTime()
   //---
   //---
   void              Do_Alerts(string msg)
     {
      //--
      Print(indname," --- "+Symbol()+","+strTF(Period())+": "+msg+
            "\n--- at: ",TimeToString(TimeCurrent(),TIME_DATE|TIME_MINUTES));
      //--
      if(alerts==Yes)
        {
         Alert(indname," --- "+Symbol()+","+strTF(Period())+": "+msg+
               "--- at: ",TimeToString(TimeCurrent(),TIME_DATE|TIME_MINUTES));
        }
      //--
      if(UseEmailAlert==Yes)
         SendMail(indname," --- "+Symbol()+" "+strTF(Period())+": "+msg+
                  "\n--- at: "+TimeToString(TimeCurrent(),TIME_DATE|TIME_MINUTES));
      //--
      if(UseSendnotify==Yes)
         SendNotification(indname+" --- "+Symbol()+" "+strTF(Period())+": "+msg+
                          "\n--- at: "+TimeToString(iTime(Symbol(),0,0),TIME_DATE|TIME_MINUTES));
      //--
      return;
      //---
     } //-end Do_Alerts()
   //---
   //---
   string            FontsModel(int mode)
     {
      string str_font;
      switch(mode)
        {
         case 0:
            str_font="Verdana";
            break;
         case 1:
            str_font="Bodoni MT Black";
            break;
        }
      //--
      return(str_font);
      //---
     } //-end FontsModel()
   //---
   //---
   void              ChangeChartSymbol(string tf_name,ENUM_TIMEFRAMES stf)
     {
      //---
      //--- unpress the button
      ObjectSetInteger(CI,tf_name,OBJPROP_STATE,false);
      ObjectSetInteger(CI,tf_name,OBJPROP_ZORDER,0);
      //--
      DeletedZigZagObject();
      PanelPosChange(corpos);
      ChartSetSymbolPeriod(CI,Symbol(),stf);
      if(display)
         DrawZigZagObject();
      //--
      ChartRedraw(CI);
      //--
      return;
      //---
     } //-end ChangeChartSymbol()
   //---
   //---
   void              DisplayPanelButton(void)
     {
      //--
      ObjectDelete(CI,cstar);
      ObjectDelete(CI,artop);
      ObjectDelete(CI,arbot);
      //--
      CreateButtonClick(CI,cstar,20,20,"Wingdings",13,BORDER_FLAT,cstar,clrWhite,clrWhite,TColor,CORNER_RIGHT_UPPER,25,40,true,"Open Panel Indicator");
      CreateButtonClick(CI,artop,18,18,"Wingdings",11,BORDER_FLAT,artop,clrWhite,clrWhite,clrGreen,CORNER_RIGHT_UPPER,24,20,true,"Change Panel to Top");
      CreateButtonClick(CI,arbot,18,18,"Wingdings",11,BORDER_FLAT,arbot,clrWhite,clrWhite,clrGreen,CORNER_RIGHT_UPPER,24,62,true,"Change Panel to Bottom");
      //--
      ChartRedraw(CI);
      //--
      return;
      //---
     } //-end DisplayPanelButton()
   //---
   //---
   void              DisplayButtonClick(string actreq)
     {
      //--
      if(actreq=="cstar")
         CreateButtonClick(CI,cstar,20,20,"Wingdings",13,BORDER_FLAT,cstar,clrWhite,clrWhite,TColor,CORNER_RIGHT_UPPER,25,40,true,"Open Panel Indicator");
      if(actreq=="artop")
         CreateButtonClick(CI,artop,18,18,"Wingdings",11,BORDER_FLAT,artop,clrWhite,clrWhite,clrGreen,CORNER_RIGHT_UPPER,24,20,true,"Change Panel to Top");
      if(actreq=="arbot")
         CreateButtonClick(CI,arbot,18,18,"Wingdings",11,BORDER_FLAT,arbot,clrWhite,clrWhite,clrGreen,CORNER_RIGHT_UPPER,24,62,true,"Change Panel to Bottom");
      if(actreq=="X")
         CreateButtonClick(CI,"X",17,15,"Arial Black",fontSize,BORDER_FLAT,"X",clrWhite,clrWhite,clrRed,bcor,txttf-7+(11*scaleA)+offsetX,cory,true,"Close Panel");
      //--
      ChartRedraw(CI);
      //--
      return;
      //---
     } //-end DisplayButtonClick()
   //---
   //---
   void              DeletedZigZagObject(void)
     {
      //--
      string name;
      for(int i=ObjectsTotal(CI,-1,-1)-1; i>=0; i--)
        {
         name=ObjectName(CI,i,-1,-1);
         if(StringFind(name,ObjName,0)>-1)
            ObjectDelete(CI,name);
         for(int x=0; x<tfxar; x++)
           {
            if(StringFind(name,TFSc[x],0)>-1)
               ObjectDelete(CI,name);
           }
         //--
         ObjectDelete(CI,"X");
         ObjectDelete(CI,cstar);
         ObjectDelete(CI,artop);
         ObjectDelete(CI,arbot);
        }
      //--
      return;
      //---
     } //-end DeletedZigZagObject()
   //---
   //---
   string            strTF(ENUM_TIMEFRAMES period)
     {
      string intf="";
      //--
      switch(period)
        {
         //--
         case PERIOD_M1:
           {intf="M1";  break;}
         case PERIOD_M2:
           {intf="M2";  break;}
         case PERIOD_M3:
           {intf="M3";  break;}
         case PERIOD_M4:
           {intf="M4";  break;}
         case PERIOD_M5:
           {intf="M5";  break;}
         case PERIOD_M6:
           {intf="M6";  break;}
         case PERIOD_M10:
           {intf="M10"; break;}
         case PERIOD_M12:
           {intf="M12"; break;}
         case PERIOD_M15:
           {intf="M15"; break;}
         case PERIOD_M20:
           {intf="M20"; break;}
         case PERIOD_M30:
           {intf="M30"; break;}
         case PERIOD_H1:
           {intf="H1";  break;}
         case PERIOD_H2:
           {intf="H2";  break;}
         case PERIOD_H3:
           {intf="H3";  break;}
         case PERIOD_H4:
           {intf="H4";  break;}
         case PERIOD_H6:
           {intf="H6";  break;}
         case PERIOD_H8:
           {intf="H8";  break;}
         case PERIOD_H12:
           {intf="H12"; break;}
         case PERIOD_D1:
           {intf="D1";  break;}
         case PERIOD_W1:
           {intf="W1";  break;}
         case PERIOD_MN1:
           {intf="MN1"; break;}
            //--
        }
      return(intf);
      //---
     } //-end strTF()
   //---
   //---
   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()
   //---
   //---
   int               WS(int width) // Width Scaling factor wide button
     {
      //--
      int res=0;
      int reswidth=0;
      //-- Calculating the scaling factor wide button on a screen
      int scale_factor=(TerminalInfoInteger(TERMINAL_SCREEN_DPI));
      // The basic width in the screen points for standard monitors with DPI=96
      //-- Use of the scaling factor
      reswidth=(width * scale_factor) / 96;
      res=(int)NormalizeDouble(reswidth*1.25,0);
      //--
      return(res);
      //---
     } //-end WS()
   //---
   //---
   void              CreateButtonClick(long   chartid,
                                       string button_name,
                                       int    button_x_size,
                                       int    button_y_size,
                                       string button_font_model,
                                       int    button_font_size,
                                       int    button_border,
                                       string button_name_text,
                                       color  button_bord_color,
                                       color  button_bg_color,
                                       color  button_color,
                                       int    button_corner,
                                       int    button_xdist,
                                       int    button_ydist,
                                       bool   button_hidden,
                                       string tooltip)
     {
      //--
      ObjectCreate(chartid,button_name,OBJ_BUTTON,0,0,0); // create button
      ObjectSetInteger(chartid,button_name,OBJPROP_XSIZE,WS(button_x_size));
      ObjectSetInteger(chartid,button_name,OBJPROP_YSIZE,WS(button_y_size));
      ObjectSetString(chartid,button_name,OBJPROP_TEXT,button_name_text);
      ObjectSetString(chartid,button_name,OBJPROP_FONT,button_font_model);
      ObjectSetInteger(chartid,button_name,OBJPROP_FONTSIZE,WS(button_font_size));
      ObjectSetInteger(chartid,button_name,OBJPROP_BORDER_TYPE,WS(button_border));
      ObjectSetInteger(chartid,button_name,OBJPROP_BORDER_COLOR,button_bord_color);
      ObjectSetInteger(chartid,button_name,OBJPROP_BGCOLOR,button_bg_color);
      ObjectSetInteger(chartid,button_name,OBJPROP_COLOR,button_color);
      ObjectSetInteger(chartid,button_name,OBJPROP_ANCHOR,ancp);
      ObjectSetInteger(chartid,button_name,OBJPROP_CORNER,button_corner);
      ObjectSetInteger(chartid,button_name,OBJPROP_XDISTANCE,WS(button_xdist));
      ObjectSetInteger(chartid,button_name,OBJPROP_YDISTANCE,WS(button_ydist));
      ObjectSetInteger(chartid,button_name,OBJPROP_HIDDEN,button_hidden);
      ObjectSetString(chartid,button_name,OBJPROP_TOOLTIP,tooltip);
      ChartRedraw(chartid);
      //--
      return;
      //---
     } //-end CreateButtonClick()
   //---
   //---
   bool              CreateArrowLabel(long   chart_id,
                                      string lable_name,
                                      string label_text,
                                      string font_model,
                                      int    font_size,
                                      color  label_color,
                                      int    chart_corner,
                                      int    x_cor,
                                      int    y_cor,
                                      bool   price_hidden,
                                      string tooltip)
     {
      //--
      ObjectDelete(chart_id,lable_name);
      //--
      if(!ObjectCreate(chart_id,lable_name,OBJ_LABEL,0,0,0,0,0)) // create Label
        {
         Print(__FUNCTION__, ": failed to create \"Arrow Label\" sign! Error code = ",GetLastError());
         return(false);
        }
      //--
      ObjectSetString(chart_id,lable_name,OBJPROP_TEXT,label_text);
      ObjectSetString(chart_id,lable_name,OBJPROP_FONT,font_model);
      ObjectSetInteger(chart_id,lable_name,OBJPROP_FONTSIZE,WS(font_size));
      ObjectSetInteger(chart_id,lable_name,OBJPROP_COLOR,label_color);
      ObjectSetInteger(chart_id,lable_name,OBJPROP_CORNER,chart_corner);
      ObjectSetInteger(chart_id,lable_name,OBJPROP_ANCHOR,ancp);
      ObjectSetInteger(chart_id,lable_name,OBJPROP_XDISTANCE,WS(x_cor));
      ObjectSetInteger(chart_id,lable_name,OBJPROP_YDISTANCE,WS(y_cor));
      ObjectSetInteger(chart_id,lable_name,OBJPROP_HIDDEN,price_hidden);
      ObjectSetString(chart_id,lable_name,OBJPROP_TOOLTIP,tooltip);
      //-- successful execution
      return(true);
      //---
     } //-end CreateArrowLabel()
   //---
   //---
   void              CreateButtonTemplate(long chartid,
                                          string obj_name,
                                          int    x_size,
                                          int    y_size,
                                          int    style,
                                          int    width,
                                          int    border,
                                          color  bordcolor,
                                          color  bgcolor,
                                          color  objcolor,
                                          int    corner,
                                          int    x_dist,
                                          int    y_dist,
                                          bool   hidden)
     {
      //--
      ObjectCreate(chartid,obj_name,OBJ_RECTANGLE_LABEL,0,0,0); // create Rectangle Label
      ObjectSetInteger(chartid,obj_name,OBJPROP_XSIZE,WS(x_size));
      ObjectSetInteger(chartid,obj_name,OBJPROP_YSIZE,WS(y_size));
      ObjectSetInteger(chartid,obj_name,OBJPROP_STYLE,style);
      ObjectSetInteger(chartid,obj_name,OBJPROP_WIDTH,WS(width));
      ObjectSetInteger(chartid,obj_name,OBJPROP_BORDER_TYPE,WS(border));
      ObjectSetInteger(chartid,obj_name,OBJPROP_BORDER_COLOR,bordcolor);
      ObjectSetInteger(chartid,obj_name,OBJPROP_BGCOLOR,bgcolor);
      ObjectSetInteger(chartid,obj_name,OBJPROP_COLOR,objcolor);
      ObjectSetInteger(chartid,obj_name,OBJPROP_CORNER,corner);
      ObjectSetInteger(chartid,obj_name,OBJPROP_XDISTANCE,WS(x_dist));
      ObjectSetInteger(chartid,obj_name,OBJPROP_YDISTANCE,WS(y_dist));
      ObjectSetInteger(chartid,obj_name,OBJPROP_HIDDEN,hidden);
      ChartRedraw(chartid);
      //--
      return;
      //---
     } //-end CreateButtonTemplate()
   //---
   //---
  }; //-end class MTF_Indi()
//---------//

MTF_Indi mi;

//---------//
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   mi.VZZ_MTF_Config();
//--
   SetIndexBuffer(0,mi.PowerMove,INDICATOR_DATA);
   PlotIndexSetString(0,PLOT_LABEL,"ZZMTF_Move");
//--
   IndicatorSetString(INDICATOR_SHORTNAME,mi.indname);
   IndicatorSetInteger(INDICATOR_DIGITS,2);
//---
   return(INIT_SUCCEEDED);
  }
//---------//
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   Comment("");
   Print(mi.getUninitReasonText(reason));
   //-- Release all handle indicators for all symbols
   for(int x=0; x<mi.tfxar; x++) 
     IndicatorRelease(mi.hZigZag[x]);
//--
   mi.DeletedZigZagObject();
//--
   ChartRedraw(mi.CI);
//---
   return;
//---
  } //-end OnDeinit()
//-------//
//+------------------------------------------------------------------+
//| 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[])
  {
//---
   ResetLastError();
   //--
   int limit;
   limit=rates_total-prev_calculated;
   if(prev_calculated>0)
      limit++;
   if(limit>mi.ttlbars)
      limit=mi.ttlbars;
   if(limit-3<=1)
      limit=3;
   //--
   mi.ZigZagMovementCalculation(limit);
   //--
   if(alerts==Yes||UseEmailAlert==Yes||UseSendnotify==Yes)
     {
       mi.curmin=mi.ThisTime(mi.min);
       if(mi.curmin!=mi.prvmin && mi.curAlert==1 && mi.curAlert!=mi.prvAlert)
         {
           string AlertTxt="The strength of the ZigZag movement appears to be Rise.";
           mi.Do_Alerts(AlertTxt);
           mi.prvAlert=mi.curAlert;
           mi.prvmin=mi.curmin;
         }
       if(mi.curmin!=mi.prvmin && mi.curAlert==-1 && mi.curAlert!=mi.prvAlert)
         {
           string AlertTxt="The strength of the ZigZag movement appears to be Down.";
           mi.Do_Alerts(AlertTxt);
           mi.prvAlert=mi.curAlert;
           mi.prvmin=mi.curmin;
         }
     }
   //--
   if(mi.display)
     mi.DrawZigZagObject();
   //---
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//---------//
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
//--- handling CHARTEVENT_CLICK event ("Clicking the chart")
   ResetLastError();
//--
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- if "X" button is click
      if(sparam=="X")
        {
         mi.DeletedZigZagObject();
         //--- unpress the button
         ObjectSetInteger(mi.CI,"X",OBJPROP_STATE,false);
         ObjectSetInteger(mi.CI,"X",OBJPROP_ZORDER,0);
         //--
         mi.display=false;
         ObjectDelete(mi.CI,"X");
         mi.DisplayPanelButton();
        }
      //--- if "cstar" button is click
      if(sparam==mi.cstar)
        {
         mi.DeletedZigZagObject();
         mi.DisplayPanelButton();
         //--- unpress the button
         ObjectSetInteger(mi.CI,mi.cstar,OBJPROP_STATE,false);
         ObjectSetInteger(mi.CI,mi.cstar,OBJPROP_ZORDER,0);
         if(!mi.display)
            mi.display=true;
         if(mi.corpos==mi.posbot) ObjectDelete(mi.CI,mi.arbot);
         if(mi.corpos==mi.postop) ObjectDelete(mi.CI,mi.artop);
         mi.DrawZigZagObject();
         //--
         ChartRedraw(mi.CI);
        }
      //--- if "artop" button is click
      if(sparam==mi.artop)
        {
         mi.DeletedZigZagObject();
         mi.DisplayPanelButton();
         //--- unpress the button
         ObjectSetInteger(mi.CI,mi.artop,OBJPROP_STATE,false);
         ObjectSetInteger(mi.CI,mi.artop,OBJPROP_ZORDER,0);
         if(!mi.display)
            mi.display=true;
         ObjectDelete(mi.CI,mi.artop);
         mi.PanelPosChange(mi.postop);
         //--
         ObjectDelete(mi.CI,"X");
         mi.DisplayButtonClick("arbot");
         mi.DrawZigZagObject();
         //--
         ChartRedraw(mi.CI);
        }
      //--- if "arbot" button is click
      if(sparam==mi.arbot)
        {
         mi.DeletedZigZagObject();
         mi.DisplayPanelButton();
         //--- unpress the button
         ObjectSetInteger(mi.CI,mi.arbot,OBJPROP_STATE,false);
         ObjectSetInteger(mi.CI,mi.arbot,OBJPROP_ZORDER,0);
         if(!mi.display)
            mi.display=true;
         ObjectDelete(mi.CI,mi.arbot);
         mi.PanelPosChange(mi.posbot);
         //--
         ObjectDelete(mi.CI,"X");
         mi.DisplayButtonClick("artop");
         mi.DrawZigZagObject();
         //--
         ChartRedraw(mi.CI);
        }
      //--- if TF button is click
      //--
      if(sparam==mi.TFSc[0])
        {
         mi.ChangeChartSymbol(mi.TFSc[0],mi.TFId[0]);
        }
      //--
      if(sparam==mi.TFSc[1])
        {
         mi.ChangeChartSymbol(mi.TFSc[1],mi.TFId[1]);
        }
      //--
      if(sparam==mi.TFSc[2])
        {
         mi.ChangeChartSymbol(mi.TFSc[2],mi.TFId[2]);
        }
      //--
      if(sparam==mi.TFSc[3])
        {
         mi.ChangeChartSymbol(mi.TFSc[3],mi.TFId[3]);
        }
      //--
      if(sparam==mi.TFSc[4])
        {
         mi.ChangeChartSymbol(mi.TFSc[4],mi.TFId[4]);
        }
      //--
      if(sparam==mi.TFSc[5])
        {
         mi.ChangeChartSymbol(mi.TFSc[5],mi.TFId[5]);
        }
      //--
      if(sparam==mi.TFSc[6])
        {
         mi.ChangeChartSymbol(mi.TFSc[6],mi.TFId[6]);
        }
      //--
      if(sparam==mi.TFSc[7])
        {
         mi.ChangeChartSymbol(mi.TFSc[7],mi.TFId[7]);
        }
      //--
      if(sparam==mi.TFSc[8])
        {
         mi.ChangeChartSymbol(mi.TFSc[8],mi.TFId[8]);
        }
      //--
      if(sparam==mi.TFSc[9])
        {
         mi.ChangeChartSymbol(mi.TFSc[9],mi.TFId[9]);
        }
      //--
      if(sparam==mi.TFSc[10])
        {
         mi.ChangeChartSymbol(mi.TFSc[10],mi.TFId[10]);
        }
      //--
      if(sparam==mi.TFSc[11])
        {
         mi.ChangeChartSymbol(mi.TFSc[11],mi.TFId[11]);
        }
      //--
      if(sparam==mi.TFSc[12])
        {
         mi.ChangeChartSymbol(mi.TFSc[12],mi.TFId[12]);
        }
      //--
      if(sparam==mi.TFSc[13])
        {
         mi.ChangeChartSymbol(mi.TFSc[13],mi.TFId[13]);
        }
      //--
      if(sparam==mi.TFSc[14])
        {
         mi.ChangeChartSymbol(mi.TFSc[14],mi.TFId[14]);
        }
      //--
      if(sparam==mi.TFSc[15])
        {
         mi.ChangeChartSymbol(mi.TFSc[15],mi.TFId[15]);
        }
      //--
      if(sparam==mi.TFSc[16])
        {
         mi.ChangeChartSymbol(mi.TFSc[16],mi.TFId[16]);
        }
      //--
      if(sparam==mi.TFSc[17])
        {
         mi.ChangeChartSymbol(mi.TFSc[17],mi.TFId[17]);
        }
      //--
      if(sparam==mi.TFSc[18])
        {
         mi.ChangeChartSymbol(mi.TFSc[18],mi.TFId[18]);
        }
      //--
      if(sparam==mi.TFSc[19])
        {
         mi.ChangeChartSymbol(mi.TFSc[19],mi.TFId[19]);
        }
      //--
      if(sparam==mi.TFSc[20])
        {
         mi.ChangeChartSymbol(mi.TFSc[20],mi.TFId[20]);
        }
      //--
     }
//---
  } //-end OnChartEvent()
//---------//
***Copyright © 2026 3rjfx ~ For educational purposes only.***

Please download the ZigZag_MTF indicator: ZigZag_MTF


© 2026 ZigZag_MTF - Developed by Roberto Jacobs (3rjfx)

Wednesday, November 27, 2024

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

MT5 Indicator Program Explanation and Function

Overview:

The TimeBarCloseCheck MT5 Indicator is designed to detect the bar close time and the close price in the MetaTrader 5 trading platform. This program assists traders by providing accurate timing and pricing information, which is essential for making informed trading decisions.

Functions:

1. Bar Close Time Detection:

The indicator precisely identifies when a trading bar (or candlestick) closes. This is crucial for traders who rely on closing prices to determine market trends and make trading decisions.

2. Price Close Detection:

It captures the exact price at which the bar closes. This data is vital for analyzing market conditions and planning future trades.

3. Alerts and Notifications:

The program can be configured to send alerts or notifications when a bar closes, allowing traders to stay updated without constantly monitoring the charts.

4. Customizable Settings:

Users can customize the indicator settings according to their trading strategy and preferences, making it a versatile tool for different trading styles.

Benefits:

1. Accuracy: Provides precise timing and pricing information to enhance trading strategies.

2. Convenience: Automated alerts save time and help traders stay focused on their trading goals.

3. Customization: Adaptable to various trading strategies, ensuring it meets individual needs.

How to detect Bar Close Time in MT5 and MT4

Let's go through the code section provided and explain each part:


//+------------------------------------------------------------------+
//|                                            TimeBarCloseCheck.mq5 |
//|        Copyright 2024, Roberto Jacobs (3rjfx) ~ Date: 2024-11-26 |
//|                              https://www.mql5.com/en/users/3rjfx |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Roberto Jacobs (3rjfx) ~ Date: 2024-11-26"
#property link      "https://www.mql5.com/en/users/3rjfx"
#property version   "1.00"
#property indicator_chart_window

Code Explanation:

Metadata Section:

This section contains metadata about the indicator, including the name, author, version, and copyright information.

#property copyright specifies the copyright details.

#property link provides a link to the author's profile or website.

#property version indicates the version of the indicator.

#property indicator_chart_window tells MT5 that this indicator will be applied to the chart window.

#property indicator_plots 0
#property indicator_plots = 0, indicates that this indicator does not have any graphical plots.


//-- Enumeration
enum YN
 {
   No,  
   Yes
 };

Enumeration YN:

An enumeration defining Yes and No options. Useful for input parameters that need a binary choice.


enum corner
 {  
   LeftHand=0,
   RightHand=1
 };

Enumeration corner:

This enumeration is used to define possible positions (left or right) for text display on the chart.


enum fonts
  {
    Verdana,
    Bodoni_MT_Black
  }; 

Enumeration fonts:

An enumeration listing the font options available for text display on the chart.


input ENUM_TIMEFRAMES  Timeframe = PERIOD_H1;     // Select Expert TimeFrame, default PERIOD_H1
input corner                 cor = LeftHand;     // Corner Position Text
//--- Input parameters for alerts
input YN                  alerts = Yes;            // Display Alerts Pop-up on Chart (Yes) or (No)
input YN           UseEmailAlert = No;             // Email Alert (Yes) or (No)
input YN           UseSendnotify = No;             // Send Notification (Yes) or (No)
//--- Input text parameters
input fonts              f_model = Verdana;        // Select Font Model
input color              t_color = clrGold;        // Select Text Color

Input Parameters:

input ENUM_TIMEFRAMES Timeframe = PERIOD_H1; - Allows the user to select the timeframe for the indicator, default is 1 hour (H1).

Explanation:

The input ENUM_TIMEFRAMES Timeframe, Allowing users to select the timeframe (with the default being H1) for the TimeBarCloseCheck indicator provides significant flexibility. Even if a user is analyzing a Symbol chart on a different timeframe, such as D1, setting the indicator to H1 ensures it continues to provide information and alerts based on the H1 timeframe.

This functionality is highly advantageous for traders who prefer to monitor multiple timeframes simultaneously. For instance, a trader could keep an eye on long-term trends on a D1 chart while still receiving timely alerts and information pertinent to the H1 timeframe without switching their primary chart’s timeframe. This approach provides a comprehensive view of the market, facilitating more informed trading decisions.

input corner cor = LeftHand; - Allows the user to select the position of the text on the chart.

Alert input parameters to enable or disable different types of alerts:

  • input YN alerts = Yes; - Shows a pop-up alert on the chart.
  • input YN UseEmailAlert = No; - Enables or disables email alerts.
  • input YN UseSendnotify = No; - Enables or disables notifications.

Text input parameters to customize the appearance:

  • input fonts f_model = Verdana; - Selects the font model for the text.
  • input color t_color = clrGold; - Selects the color of the text.

Variables:

  • double CLOSE[];
  • datetime TIME[];
  • datetime TFOpenTime=0;
  • datetime TFClosTime=0;
  • datetime TFNextTime=0;
  • datetime TFprevTime=0;
  • datetime TFTimeRem=0;
  • datetime remd=0;
  • datetime tmrem=0;
  • datetime tmrem=0;
  • double tmdrem=0;
  • string txtday;
  • string font_mode;

Various global variables to store close prices (CLOSE[]), time values (TIME[], TFOpenTime, etc.), and text settings (txtday, font_mode).

Time Variables:

Variables to store different components of the time (year, month, day, hour, minute, second).

  • int x_year; // Year
  • int x_mon; // Month
  • int x_day; // Day of the month
  • int x_hour; // Hour in a day
  • int x_min; // Minutes
  • int x_sec; // Seconds

Time Constants:

Constants used for indexing time components and day information.

  • int year=0; // Year
  • int mon=1; // Month
  • int day=2; // Day
  • int hour=3; // Hour
  • int min=4; // Minutes
  • int sec=5; // Seconds
  • int dow=6; // Day of week (0-Sunday, 1-Monday, ... ,6-Saturday)
  • int doy=7; // Day number of the year (January 1st is assigned the number value of zero)

Miscellaneous Variables:

Various other global variables for specific functionalities like extime, exday, f_size, cbar, scale, garis, and alert-related variables (doalert, AlertTxt).

  • int extime=0;
  • int exday=0;
  • int f_size=9;
  • int cbar=9;
  • int scale=15;
  • int garis=12;
  • int xdis=0,ydis=90;
  • ENUM_BASE_CORNER PilCor;
  • bool doalert;
  • string AlertTxt;
  • string _pname;
  • string indiname;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
     _pname=MQLInfoString(MQL_PROGRAM_NAME);
     indiname=_pname+" : "+TF2Str(Timeframe);
     //--
     cor==RightHand ? PilCor=ENUM_BASE_CORNER(CORNER_RIGHT_UPPER) : PilCor=ENUM_BASE_CORNER(CORNER_LEFT_UPPER);
     xdis=PilCor==(ENUM_BASE_CORNER)CORNER_RIGHT_UPPER ? 288 : 30;
     //--
     font_mode=FontsModel(f_model);
//---
   return(INIT_SUCCEEDED);
  }
//---------//

This function primarily sets up the necessary parameters for the indicator to function properly, including determining the position of text elements on the chart and setting the font style.

Explanation:

1. Function Purpose:

The OnInit function is the initialization function for the custom indicator. It sets up various parameters and prepares the indicator to run.

  • _pname=MQLInfoString(MQL_PROGRAM_NAME);
  • indiname=_pname+" : "+TF2Str(Timeframe);

_pname stores the name of the MQL program.

indiname combines the program name and the selected timeframe into a single string for display or logging purpose

2. Setting the Text Position:

cor==RightHand ? PilCor=ENUM_BASE_CORNER(CORNER_RIGHT_UPPER) : PilCor=ENUM_BASE_CORNER(CORNER_LEFT_UPPER); xdis=PilCor==(ENUM_BASE_CORNER)CORNER_RIGHT_UPPER ? 288 : 30;

This conditional (ternary) operator checks if the cor variable is set to RightHand.

If cor is RightHand, PilCor is set to CORNER_RIGHT_UPPER; otherwise, it is set to CORNER_LEFT_UPPER.

xdis is set based on the value of PilCor: 288 if CORNER_RIGHT_UPPER, otherwise 30.

Font Mode: font_mode=FontsModel(f_model);

Return Value: return(INIT_SUCCEEDED);

The function returns INIT_SUCCEEDED, indicating that the initialization was successful.


string FontsModel(int mode)
  { 
   string str_font;
   switch(mode) 
     { 
      case 0: str_font="Verdana"; break;
      case 1: str_font="Bodoni MT Black"; break; 
     }
   //--
   return(str_font);
//----
  } //-end FontsModel()
//---------//

This function helps convert the integer mode to a string representing the font name, which can then be used elsewhere in the indicator for setting text properties.

Let's break down the FontsModel function:

Explanation:

Function Name and Parameters: string FontsModel(int mode)

The function FontsModel takes an integer parameter mode.

String Variable Declaration: string str_font;

A string variable str_font is declared to hold the name of the font.

Switch Statement: The switch statement evaluates the value of mode to determine which font to select.

Case Statements:

  • case 0: str_font="Verdana"; break;
  • case 1: str_font="Bodoni MT Black"; break;

If mode is 0, str_font is set to "Verdana".

If mode is 1, str_font is set to "Bodoni MT Black".

Default Case: The default case is not explicitly provided here, meaning if mode is neither 0 nor 1, str_font will remain uninitialized.

Return Statement: return(str_font);

The function returns the value of str_font.


//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//----
    Comment("");
    PrintFormat("%s: Deinitialization reason code=%d",__FUNCTION__,reason);
    Print(getUninitReasonText(reason));
    //--
    ObjectsDeleteAll(ChartID(),0,-1);
//----
   return;
  }
//-----//

Let's dive into the OnDeinit function and break down each part.

Explanation:

1. Function Purpose: The OnDeinit function is called when the custom indicator is being removed or unloaded from the chart. It handles cleanup tasks and ensures that resources are properly released.

Function Definition: void OnDeinit(const int reason)

The function takes an integer parameter reason which indicates the reason for deinitialization. The void keyword means this function does not return any value.

Clearing Comments: Comment("");

This line clears any comments displayed on the chart.

Printing Deinitialization Reason:

PrintFormat("%s: Deinitialization reason code=%d", __FUNCTION__, reason);

Print(getUninitReasonText(reason));

PrintFormat outputs a formatted string to the log, showing the function name (__FUNCTION__) and the deinitialization reason code.

Print outputs the text returned by getUninitReasonText(reason), which provides a more descriptive reason for deinitialization.

Deleting Objects: ObjectsDeleteAll(ChartID(), 0, -1);

This line deletes all objects from the chart. ChartID() gets the current chart's ID, and the 0 and -1 parameters indicate that all objects on all subwindows of the chart should be deleted.

Returning: return;

The function ends with a return statement, which is redundant for a void function but included for clarity.

This function ensures that when the indicator is removed, all related objects and comments are cleared from the chart, and relevant information about the deinitialization reason is logged for debugging or informational purposes.


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 © 2024 3rjfx ~ For educational purposes only.***

Let's explain the getUninitReasonText function and how it works:

Explanation:

1. Function Purpose: The getUninitReasonText function converts a deinitialization reason code into a human-readable string explanation. It helps in understanding why the indicator was deinitialized.

2. Function Definition: string getUninitReasonText(int reasonCode)

String Initialization: string text="";

Initializes an empty string text to store the explanation.

Switch Statement: switch(reasonCode)

The switch statement evaluates the reasonCode and sets the text variable accordingly.

Case Statements:

Each case in the switch statement corresponds to a specific reason code:

  • 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;

For each possible reasonCode, the corresponding text explanation is assigned to text. The __FILE__ macro is used to include the filename in the explanation for the REASON_REMOVE case.

Default Case: default: text="Another reason"; break;

If reasonCode does not match any of the specified cases, the default text "Another reason" is assigned.

Return Statement: return text;

The function returns the text variable containing the explanation.

This function is useful for logging and debugging, providing clear reasons why the indicator was deinitialized, which can help in diagnosing issues or understanding the behavior of the indicator.


//+------------------------------------------------------------------+
//| 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[])
  {
//---
     ArrayResize(TIME,cbar);
     ArrayResize(CLOSE,cbar);
     ArraySetAsSeries(TIME,true);
     ArraySetAsSeries(CLOSE,true);
     ArraySetAsSeries(time,true);
     ArraySetAsSeries(close,true);
     //--
     RefreshPrice(Timeframe);
     //--
     TFOpenTime=TIME[0];
     CheckSeconds();
     //--
     TFNextTime=TFOpenTime+extime;
     TFClosTime=TFNextTime-1;
     TFTimeRem=fabs(TimeCurrent()-TFClosTime);
     tmrem=TFClosTime-TimeCurrent();
     datetime dtB=0; 
     if(TFClosTime!=TFprevTime) doalert=false;
     string SDT=ReqDate(TimeCurrent(),ReqTime(TimeCurrent(),day),ReqTime(TimeCurrent(),hour),ReqTime(TimeCurrent(),min),ReqTime(TimeCurrent(),sec));
     //--
     if(TimeCurrent()<TFClosTime)
       {
         remd=ReqTime(TFClosTime,day)-ReqTime(TimeCurrent(),day);
         exday=int(remd);
         if(exday<=0) exday=0;
         txtday=exday==0 ? (string)ReqTime(TFTimeRem,hour)+":"+(string)ReqTime(TFTimeRem,min)+":"+(string)ReqTime(TFTimeRem,sec) :
         (string)exday+" Day ~ "+(string)ReqTime(TFTimeRem,hour)+":"+(string)ReqTime(TFTimeRem,min)+":"+(string)ReqTime(TFTimeRem,sec);
       }
     //--
     CreateChartText(0,indiname+"SDT0","Server Date Time : "+SDT,font_mode,f_size,t_color,PilCor,xdis,ydis);
     CreateChartText(0,indiname+"SDT11","------------------------------------------------------",font_mode,f_size,t_color,PilCor,xdis,ydis+garis);
     CreateChartText(0,indiname+"SDT1",_pname+" : Period "+TF2Str(Timeframe),font_mode,f_size,t_color,PilCor,xdis,ydis+garis+(1*scale));
     CreateChartText(0,indiname+"SDT2","Bar Open Time : "+
                       TimeToString(TFOpenTime,TIME_DATE|TIME_MINUTES|TIME_SECONDS),font_mode,f_size,t_color,PilCor,xdis,ydis+garis+(2*scale));
     CreateChartText(0,indiname+"SDT3","Bar Close Time : "+
                       TimeToString(TFClosTime,TIME_DATE|TIME_MINUTES|TIME_SECONDS),font_mode,f_size,t_color,PilCor,xdis,ydis+garis+(3*scale));
     CreateChartText(0,indiname+"SDT4","Next Bar Open Time : "+
                       TimeToString(TFNextTime,TIME_DATE|TIME_MINUTES|TIME_SECONDS),font_mode,f_size,t_color,PilCor,xdis,ydis+garis+(4*scale));
     CreateChartText(0,indiname+"SDT5","Time Remaining : "+txtday,font_mode,f_size,t_color,PilCor,xdis,ydis+garis+(5*scale));                         
     //--
     if(alerts==Yes||UseEmailAlert==Yes||UseSendnotify==Yes)
       {
         if(TimeCurrent()>=TFClosTime && TimeCurrent()<=TFNextTime && !doalert)
           {
             dtB=TimeCurrent();
             AlertTxt="Bar Close time triggered in Timeframe : "+TF2Str(Timeframe)+" @Price: "+DoubleToString(CLOSE[0],Digits());
             Do_Alerts(AlertTxt,dtB);
             TFprevTime=TFClosTime;
             doalert=true;
           }
       }
    //--
//--- return value of prev_calculated for next call
   return(rates_total);
  }
***Copyright © 2024 3rjfx ~ For educational purposes only.***

Let's break down the OnCalculate function of MT5 indicator. This function is called on every tick to update the indicator's values and perform calculations. Here's a detailed explanation:

Function Definition and Parameters:


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[])

1. Function Purpose: This function is responsible for calculating and updating the indicator's values on each new tick.

2. Parameters:

  • rates_total: - The total number of bars.
  • prev_calculated: - The number of bars calculated on the previous call.
  • time, open, high, low, close, tick_volume, volume, spread - Arrays containing the bar data for the symbol.

3. Code Breakdown:

  • ArrayResize(TIME, cbar);
  • ArrayResize(CLOSE, cbar);
  • ArraySetAsSeries(TIME, true);
  • ArraySetAsSeries(CLOSE, true);
  • ArraySetAsSeries(time, true);
  • ArraySetAsSeries(close, true);

Array Setup: Resizes and sets arrays to series to access data from the current bar to the oldest.

Fuction Call:

RefreshPrice(Timeframe);

Refresh Price Data: Calls a custom function to refresh price data for the specified timeframe.

  • TFOpenTime = TIME[0];
  • CheckSeconds();

Initialize Variables: Sets the opening time of the current bar and calls a custom function to check seconds.

  • TFNextTime = TFOpenTime + extime;
  • TFClosTime = TFNextTime - 1;
  • TFTimeRem = fabs(TimeCurrent() - TFClosTime);
  • tmrem = TFClosTime - TimeCurrent();
  • datetime dtB = 0;
  • if (TFClosTime != TFprevTime) doalert = false;
  • string SDT = ReqDate(TimeCurrent(), ReqTime(TimeCurrent(), day), ReqTime(TimeCurrent(), hour), ReqTime(TimeCurrent(), min), ReqTime(TimeCurrent(), sec));

Calculate Times: Calculates the next bar's opening and closing times, the remaining time until the bar closes, and other time-related variables.


//--
if(TimeCurrent()<TFClosTime)
  {
    remd=ReqTime(TFClosTime,day)-ReqTime(TimeCurrent(),day);
    exday=int(remd);
    if(exday<=0) exday=0;
    txtday=exday==0 ? (string)ReqTime(TFTimeRem,hour)+":"+(string)ReqTime(TFTimeRem,min)+":"+(string)ReqTime(TFTimeRem,sec) :
                    (string)exday+" Day ~ "+(string)ReqTime(TFTimeRem,hour)+":"+(string)ReqTime(TFTimeRem,min)+":"+   (string)ReqTime(TFTimeRem,sec);
  }
//--

Calculate Remaining Time: Determines the remaining time until the current bar closes and formats it for display.

CreateChartText Function Calls:

  • CreateChartText(0, indiname + "SDT0", "Server Date Time : " + SDT, font_mode, f_size, t_color, PilCor, xdis, ydis);
  • CreateChartText(0, indiname + "SDT11", "------------------------------------------------------", font_mode, f_size, t_color, PilCor, xdis, ydis + garis);
  • CreateChartText(0, indiname + "SDT1", _pname + " : Period " + TF2Str(Timeframe), font_mode, f_size, t_color, PilCor, xdis, ydis + garis + (1 * scale));
  • CreateChartText(0, indiname + "SDT2", "Bar Open Time : " + TimeToString(TFOpenTime, TIME_DATE|TIME_MINUTES|TIME_SECONDS), font_mode, f_size, t_color, PilCor, xdis, ydis + garis + (2 * scale));
  • CreateChartText(0, indiname + "SDT3", "Bar Close Time : " + TimeToString(TFClosTime, TIME_DATE|TIME_MINUTES|TIME_SECONDS), font_mode, f_size, t_color, PilCor, xdis, ydis + garis + (3 * scale));
  • CreateChartText(0, indiname + "SDT4", "Next Bar Open Time : " + TimeToString(TFNextTime, TIME_DATE|TIME_MINUTES|TIME_SECONDS), font_mode, f_size, t_color, PilCor, xdis, ydis + garis + (4 * scale));
  • CreateChartText(0, indiname + "SDT5", "Time Remaining : " + txtday, font_mode, f_size, t_color, PilCor, xdis, ydis + garis + (5 * scale));

Display Information:

Uses custom CreateChartText function to display various pieces of information on the chart, such as server date/time, bar open/close times, and time remaining.

Alerts:


if (alerts == Yes || UseEmailAlert == Yes || UseSendnotify == Yes)
{
  if (TimeCurrent() >= TFClosTime && TimeCurrent() <= TFNextTime && !doalert)
  {
    dtB = TimeCurrent();
    AlertTxt = "Bar Close time triggered in Timeframe : " + TF2Str(Timeframe) + " @Price: " + DoubleToString(CLOSE[0], Digits());
    Do_Alerts(AlertTxt, dtB);
    TFprevTime = TFClosTime;
    doalert = true;
  }
}

Trigger Alerts: Checks if current time is within the bar's closing period and triggers alerts if necessary.

Return Value: return(rates_total);

Return Statement: Returns the total number of rates (bars) calculated, which is used for the next call to OnCalculate.

This function ensures that the indicator updates accurately with each new tick, displaying relevant information on the chart and triggering alerts as needed.

CheckSeconds Function:

Let's explain the CheckSeconds function:

1. Purpose: The CheckSeconds function determines the number of seconds in a specific timeframe and assigns it to the extime variable. This helps to calculate the duration of each bar in the selected timeframe.

2. Code Breakdown:


int CheckSeconds(void)
  {
   //---
   switch(Timeframe)
     {
       //--
       case PERIOD_M1:   extime=PeriodSeconds(PERIOD_M1);     break;
       case PERIOD_M2:   extime=PeriodSeconds(PERIOD_M2);     break;
       case PERIOD_M3:   extime=PeriodSeconds(PERIOD_M3);     break;
       case PERIOD_M4:   extime=PeriodSeconds(PERIOD_M4);     break;
       case PERIOD_M5:   extime=PeriodSeconds(PERIOD_M5);     break;
       case PERIOD_M6:   extime=PeriodSeconds(PERIOD_M6);     break;
       case PERIOD_M10:  extime=PeriodSeconds(PERIOD_M10);    break;
       case PERIOD_M12:  extime=PeriodSeconds(PERIOD_M12);    break;
       case PERIOD_M15:  extime=PeriodSeconds(PERIOD_M15);    break;
       case PERIOD_M20:  extime=PeriodSeconds(PERIOD_M20);    break;
       case PERIOD_M30:  extime=PeriodSeconds(PERIOD_M30);    break;
       case PERIOD_H1:   extime=PeriodSeconds(PERIOD_H1);     break;
       case PERIOD_H2:   extime=PeriodSeconds(PERIOD_H2);     break;
       case PERIOD_H3:   extime=PeriodSeconds(PERIOD_H3);     break;
       case PERIOD_H4:   extime=PeriodSeconds(PERIOD_H4);     break;
       case PERIOD_H6:   extime=PeriodSeconds(PERIOD_H6);     break;
       case PERIOD_H8:   extime=PeriodSeconds(PERIOD_H8);     break;
       case PERIOD_H12:  extime=PeriodSeconds(PERIOD_H12);    break;
       case PERIOD_D1:   extime=PeriodSeconds(PERIOD_D1);     break;
       case PERIOD_W1:   extime=PeriodSeconds(PERIOD_W1);     break;
       case PERIOD_MN1:  extime=PeriodSeconds(PERIOD_MN1);    break;
       //--
     }
    //--
    return(extime);
  }
***Copyright © 2024 3rjfx ~ For educational purposes only.*** 

3. Explanation:

1. Function Signature: int CheckSeconds(void)

This function returns an integer value representing the number of seconds in the current timeframe.

2. Switch Statement: switch(Timeframe)

The switch statement evaluates the Timeframe variable to determine which timeframe is being used.

Case Statements:

  • case PERIOD_M1: extime=PeriodSeconds(PERIOD_M1); break;
  • case PERIOD_M2: extime=PeriodSeconds(PERIOD_M2); break;
  • case PERIOD_M3: extime=PeriodSeconds(PERIOD_M3); break;
  • case PERIOD_M4: extime=PeriodSeconds(PERIOD_M4); break;
  • case PERIOD_M5: extime=PeriodSeconds(PERIOD_M5); break;
  • case PERIOD_M6: extime=PeriodSeconds(PERIOD_M6); break;
  • case PERIOD_M10: extime=PeriodSeconds(PERIOD_M10); break;
  • case PERIOD_M12: extime=PeriodSeconds(PERIOD_M12); break;
  • case PERIOD_M15: extime=PeriodSeconds(PERIOD_M15); break;
  • case PERIOD_M20: extime=PeriodSeconds(PERIOD_M20); break;
  • case PERIOD_M30: extime=PeriodSeconds(PERIOD_M30); break;
  • case PERIOD_H1: extime=PeriodSeconds(PERIOD_H1); break;
  • case PERIOD_H2: extime=PeriodSeconds(PERIOD_H2); break;
  • case PERIOD_H3: extime=PeriodSeconds(PERIOD_H3); break;
  • case PERIOD_H4: extime=PeriodSeconds(PERIOD_H4); break;
  • case PERIOD_H6: extime=PeriodSeconds(PERIOD_H6); break;
  • case PERIOD_H8: extime=PeriodSeconds(PERIOD_H8); break;
  • case PERIOD_H12: extime=PeriodSeconds(PERIOD_H12); break;
  • case PERIOD_D1: extime=PeriodSeconds(PERIOD_D1); break;
  • case PERIOD_W1: extime=PeriodSeconds(PERIOD_W1); break;
  • case PERIOD_MN1: extime=PeriodSeconds(PERIOD_MN1); break;

For each timeframe (e.g., PERIOD_M1, PERIOD_H1, etc.), the corresponding number of seconds is calculated using the PeriodSeconds function and assigned to the extime variable.

Return Statement: return(extime);

The function returns the value of extime, which represents the duration of each bar in seconds for the selected timeframe.

This function is crucial for ensuring that the indicator can accurately calculate the time remaining until the current bar closes, regardless of the timeframe selected.

RefreshPrice Function.

1. Purpose:

The RefreshPrice function updates the price data arrays for a specified timeframe. This helps ensure that the indicator has the latest data for its calculations.

2. Code Breakdown:


void RefreshPrice(ENUM_TIMEFRAMES xtf)
  {
    //---
    MqlRates parray[]; 
    ArraySetAsSeries(parray, true); 
    int copied = CopyRates(Symbol(), xtf, 0, cbar, parray);
    //--
    int cc = CopyClose(Symbol(), xtf, 0, cbar, CLOSE);
    int ct = CopyTime(Symbol(), xtf, 0, cbar, TIME);
    //--
    return;
    //---
  } //-end RefreshPrice()
//---------//

3. Explanation:

1. Function Signature: void RefreshPrice(ENUM_TIMEFRAMES xtf)

The function takes a single parameter xtf of type ENUM_TIMEFRAMES, which specifies the timeframe to update.

2. Declare and Initialize MqlRates Array:

  • MqlRates parray[];
  • ArraySetAsSeries(parray, true);

Declares an array parray of type MqlRates to store the price data.

ArraySetAsSeries(parray, true) sets the array to be accessed as a series (with the latest data at index 0).

3. Copy Rates Data: int copied = CopyRates(Symbol(), xtf, 0, cbar, parray);

Uses CopyRates to copy the rates (OHLC and others data) for the specified symbol and timeframe into the parray array.

  • Symbol() returns the current symbol.
  • xtf is the timeframe.
  • 0 is the starting position.
  • cbar is the number of bars to copy.
  • parray is the destination array.

The function returns the number of copied rates and stores it in copied.

Copy Close Prices: int cc = CopyClose(Symbol(), xtf, 0, cbar, CLOSE);

Uses CopyClose to copy the close prices for the specified symbol and timeframe into the CLOSE array.

The function returns the number of copied close prices and stores it in cc.

Copy Time Data: int ct = CopyTime(Symbol(), xtf, 0, cbar, TIME);

Uses CopyTime to copy the time data for the specified symbol and timeframe into the TIME array.

The function returns the number of copied time values and stores it in ct.

Return Statement: return;

The function returns void, indicating it does not return any value. The return statement is used to exit the function.

This function is crucial for ensuring that the indicator always has the most up-to-date price data, which is essential for accurate calculations and display.

timehr Function

1. Purpose: The timehr function formats the given hour and minute values into a string representation of time in the "HH:MM" format.

2. Code Breakdown:


string timehr(int hr,int mn)
  {
//---
    string scon="";
    string men=mn==0 ? "00" : string(mn);
    int shr=hr==24 ? 0 : hr;
    if(shr<10) scon="0"+string(shr)+":"+men;
    else scon=string(shr)+":"+men;
    //--
    return(scon);
//---
  } //-end timehr()
//---------//

3. Explanation:

1. Function Signature: The function takes two integer parameters: hr for hours and mn for minutes. It returns a string representing the formatted time.

2. Variable Initialization:

  • string scon = "";
  • string men = mn == 0 ? "00" : string(mn);
  • int shr = hr == 24 ? 0 : hr;

scon is initialized as an empty string.

men is set to "00" if mn is 0; otherwise, it is set to the string representation of mn.

shr is set to 0 if hr is 24 (to represent midnight as "00:00"); otherwise, it is set to hr.

3. Conditional Formatting:


if (shr < 10) scon = "0" + string(shr) + ":" + men;
else scon = string(shr) + ":" + men;

If shr is less than 10, scon is formatted with a leading zero (e.g., "09:00").

If shr is 10 or greater, scon is formatted without a leading zero (e.g., "10:00").

4. Return Statement: The function returns the formatted string scon, which represents the time in "HH:MM" format.

This function is useful for ensuring that time values are consistently formatted, which is particularly important for displaying times in a readable and standardized manner on charts or logs.

ReqDate Function.

1. Purpose: The ReqDate function formats a given datetime value into a custom string representation that includes the date and time.

2. Code Breakdown:


string ReqDate(datetime reqtime,int d,int h,int m,int s) 
  { 
//---
   MqlDateTime mdt; 
   datetime t=TimeToStruct(reqtime,mdt);
   x_year=mdt.year; 
   x_mon=mdt.mon; 
   x_day=d; 
   x_hour=h; 
   x_min=m;
   x_sec=s;
   //--
   string mdr=string(x_year)+"."+string(x_mon)+"."+string(x_day)+" ~ "+timehr(x_hour,x_min)+":"+string(x_sec);
   return(mdr);
//---
  } //-end ReqDate()
//---------//

3. Explanation:

1. Function Signature: string ReqDate(datetime reqtime, int d, int h, int m, int s)

The function takes five parameters:

  • reqtime: The datetime value to be converted.
  • d: Day component to be used.
  • h: Hour component to be used.
  • m: Minute component to be used.
  • s: Second component to be used.

It returns a string representing the formatted date and time.

2. Variable Initialization:

  • MqlDateTime mdt;
  • datetime t = TimeToStruct(reqtime, mdt);

Declares an MqlDateTime structure mdt to hold the components of the datetime.

Converts reqtime into a structured datetime format using TimeToStruct and stores it in mdt.

3. Assign Date and Time Components:

  • x_year = mdt.year;
  • x_mon = mdt.mon;
  • x_day = d;
  • x_hour = h;
  • x_min = m;
  • x_sec = s;

Assigns the year and month from mdt to global variables x_year and x_mon.

Assigns the day, hour, minute, and second from the parameters d, h, m, and s to global variables x_day, x_hour, x_min, and x_sec.

4. Format String:

string mdr = string(x_year) + "." + string(x_mon) + "." + string(x_day) + " ~ " + timehr(x_hour, x_min) + ":" + string(x_sec);

Formats the date and time into a string mdr in the format "YYYY.MM.DD ~ HH:MM:SS".

Uses the timehr function to format the hour and minute components.

5. Return Statement: return(mdr);

The function returns the formatted string mdr.

This function is useful for generating a custom string representation of a datetime value, which can be displayed on the chart or used in logs.

ReqTime Function.

1. Purpose: The ReqTime function extracts a specific component of a given datetime value and returns it based on the requested mode.

2. Code Breakdown:


int ReqTime(datetime reqtime,
            const int reqmode) 
  {
    MqlDateTime tm;
    TimeToStruct(reqtime,tm);
    int valtm=0;
    //--
    switch(reqmode)
      {
        case 0: valtm=tm.year; break;        // Return Year 
        case 1: valtm=tm.mon;  break;        // Return Month 
        case 2: valtm=tm.day;  break;        // Return Day 
        case 3: valtm=tm.hour; break;        // Return Hour 
        case 4: valtm=tm.min;  break;        // Return Minutes 
        case 5: valtm=tm.sec;  break;        // Return Seconds 
        case 6: valtm=tm.day_of_week; break; // Return Day of week (0-Sunday, 1-Monday, ... ,6-Saturday) 
        case 7: valtm=tm.day_of_year; break; // Return Day number of the year (January 1st is assigned the number value of zero) 
      }
    //--
    return(valtm);
//---
  } //-end ReqTime()
//---------//
***Copyright © 2024 3rjfx ~ For educational purposes only.***

3. Explanation:

1. Function Signature: int ReqTime(datetime reqtime, const int reqmode)

The function takes two parameters:

  • reqtime: The datetime value from which to extract the component.
  • reqmode: An integer indicating which component of the datetime to return.

The function returns an integer value representing the requested component of the datetime.

2. Convert datetime to MqlDateTime:

  • MqlDateTime tm;
  • TimeToStruct(reqtime, tm);

Declares a MqlDateTime structure tm.

Converts the reqtime datetime value into the structured tm format using TimeToStruct.

3. Initialize Return Variable: int valtm = 0;

Initializes an integer variable valtm to store the value of the requested datetime component.

4. Switch Statement:


switch(reqmode)
{
  case 0: valtm = tm.year; break;        // Return Year 
  case 1: valtm = tm.mon;  break;        // Return Month 
  case 2: valtm = tm.day;  break;        // Return Day 
  case 3: valtm = tm.hour; break;        // Return Hour 
  case 4: valtm = tm.min;  break;        // Return Minutes 
  case 5: valtm = tm.sec;  break;        // Return Seconds 
  case 6: valtm = tm.day_of_week; break; // Return Day of week (0-Sunday, 1-Monday, ... ,6-Saturday) 
  case 7: valtm = tm.day_of_year; break; // Return Day number of the year (January 1st is assigned the number value of zero) 
}
***Copyright © 2024 3rjfx ~ For educational purposes only.***

The switch statement evaluates reqmode to determine which component of the datetime to return.

Each case in the switch statement corresponds to a specific component of the datetime structure:

  • 0 returns the year.
  • 1 returns the month.
  • 2 returns the day.
  • 3 returns the hour.
  • 4 returns the minutes.
  • 5 returns the seconds.
  • 6 returns the day of the week.
  • 7 returns the day number of the year.

5. Return Statement: return(valtm);

The function returns the value of valtm, which contains the requested component of the datetime.

This ReqTime function is useful for extracting specific components of a datetime value, allowing for flexible handling of date and time data.

TF2Str Function.

1.Purpose: The TF2Str function converts a given period code (representing a timeframe) into a string representation.

2. Code Breakdown:


string TF2Str(int period)
  {
   switch(period)
     {
       //--
       case PERIOD_M1:   return("M1");
       case PERIOD_M2:   return("M2");
       case PERIOD_M3:   return("M3");
       case PERIOD_M4:   return("M4");
       case PERIOD_M5:   return("M5");
       case PERIOD_M6:   return("M6");
       case PERIOD_M10:  return("M10");
       case PERIOD_M12:  return("M12");
       case PERIOD_M15:  return("M15");
       case PERIOD_M20:  return("M20");
       case PERIOD_M30:  return("M30");
       case PERIOD_H1:   return("H1");
       case PERIOD_H2:   return("H2");
       case PERIOD_H3:   return("H3");
       case PERIOD_H4:   return("H4");
       case PERIOD_H6:   return("H6");
       case PERIOD_H8:   return("H8");
       case PERIOD_H12:  return("H12");
       case PERIOD_D1:   return("D1");
       case PERIOD_W1:   return("W1");
       case PERIOD_MN1:  return("MN1");
       //--
     }
   return(string(period));
  }
***Copyright © 2024 3rjfx ~ For educational purposes only.***

3. Explanation:

1. Function Signature: string TF2Str(int period)

The function takes a single integer parameter period and returns a string representation of the timeframe.

2. Switch Statement: switch(period)

The switch statement evaluates the period variable to determine which timeframe it represents.

Case Statements:

  • case PERIOD_M1: return("M1");
  • case PERIOD_M2: return("M2");
  • case PERIOD_M3: return("M3");
  • case PERIOD_M4: return("M4");
  • case PERIOD_M5: return("M5");
  • case PERIOD_M6: return("M6");
  • case PERIOD_M10: return("M10");
  • case PERIOD_M12: return("M12");
  • case PERIOD_M15: return("M15");
  • case PERIOD_M20: return("M20");
  • case PERIOD_M30: return("M30");
  • case PERIOD_H1: return("H1");
  • case PERIOD_H2: return("H2");
  • case PERIOD_H3: return("H3");
  • case PERIOD_H4: return("H4");
  • case PERIOD_H6: return("H6");
  • case PERIOD_H8: return("H8");
  • case PERIOD_H12: return("H12");
  • case PERIOD_D1: return("D1");
  • case PERIOD_W1: return("W1");
  • case PERIOD_MN1: return("MN1");

Each case corresponds to a specific period constant (e.g., PERIOD_M1, PERIOD_H1, etc.) and returns the corresponding string representation (e.g., "M1", "H1").

Default Case: return(string(period));

If the period does not match any of the specified cases, the function converts the period integer to a string and returns it.

This TF2Str function is useful for converting timeframe constants into readable string representations, which can be displayed on the chart or used in logs.

Do_Alerts Function.

1. Purpose: The Do_Alerts function generates and sends alerts in various formats (print message, alert box, email, and notification) based on the user settings.

2. Code Breakdown:


void Do_Alerts(string msgText, datetime Altime)
  {
    //---
    // Print message in the terminal
    Print(_pname + " --- " + Symbol() + ": " + msgText +
          "\n --- at: ", TimeToString(Altime, TIME_DATE | TIME_MINUTES | TIME_SECONDS));
    //--
    if(alerts == Yes)
      {
        Alert(_pname, " --- " + Symbol() + ": " + msgText +
              " --- at: ", TimeToString(Altime, TIME_DATE | TIME_MINUTES | TIME_SECONDS));
      }
    //--
    if(UseEmailAlert == Yes) 
      SendMail(_pname, " --- " + Symbol() + " " + TF2Str(Period()) + ": " + msgText +
                       "\n--- at: " + TimeToString(Altime, TIME_DATE | TIME_MINUTES | TIME_SECONDS));
    //--
    if(UseSendnotify == Yes) 
      SendNotification(_pname + "--- " + Symbol() + " " + TF2Str(Period()) + ": " + msgText +
                      "\n --- at: " + TimeToString(Altime, TIME_DATE | TIME_MINUTES | TIME_SECONDS));
    //--
    return;
    //---
  } //-end Do_Alerts()
//---------//
***Copyright © 2024 3rjfx ~ For educational purposes only.***

3. Explanation:

1. Function Signature: void Do_Alerts(string msgText, datetime Altime)

The function takes two parameters:

  • msgText: A string containing the alert message text.
  • Altime: A datetime value indicating the alert time.

2. Print Alert in Terminal:

  • Print(_pname + " --- " + Symbol() + ": " + msgText + "\n --- at: ", TimeToString(Altime, TIME_DATE | TIME_MINUTES | TIME_SECONDS));

Prints the alert message to the terminal, including the program name (_pname), symbol, message text, and formatted alert time.

Alert Box:


if(alerts == Yes)
  {
    Alert(_pname, " --- " + Symbol() + ": " + msgText +
          " --- at: ", TimeToString(Altime, TIME_DATE | TIME_MINUTES | TIME_SECONDS));
  }

If the alerts setting is enabled (Yes), displays an alert box with the program name, symbol, message text, and formatted alert time.

Email Alert:


if(UseEmailAlert == Yes) 
  SendMail(_pname, " --- " + Symbol() + " " + TF2Str(Period()) + ": " + msgText +
                   "\n--- at: " + TimeToString(Altime, TIME_DATE | TIME_MINUTES | TIME_SECONDS));

If the UseEmailAlert setting is enabled (Yes), sends an email with the program name, symbol, timeframe, message text, and formatted alert time.

Notification Alert:


if(UseSendnotify == Yes) 
  SendNotification(_pname + "--- " + Symbol() + " " + TF2Str(Period()) + ": " + msgText +
                  "\n --- at: " + TimeToString(Altime, TIME_DATE | TIME_MINUTES | TIME_SECONDS));

If the UseSendnotify setting is enabled (Yes), sends a notification with the program name, symbol, timeframe, message text, and formatted alert time.

3. Return Statement: return;

The function returns void, indicating it does not return any value. The return statement is used to exit the function.

This Do_Alerts function provides a comprehensive alerting mechanism, ensuring that users are notified in multiple ways based on their preferences.

CreateChartText Function.

Let's break down the CreateChartText function and explain its components in detail:

1. Purpose: The CreateChartText function creates and manages a text label on the chart with specific properties, including text content, font style, size, color, and position.

2. Code Breakdown:


void CreateChartText(long   chart_id, 
                     string lable_name, 
                     string label_text,
                     string font_model,
                     int    font_size,
                     color  label_color,
                     int    chart_corner,
                     int    x_cor, 
                     int    y_cor) 
  { 
//--- 
   if(ObjectFind(chart_id,lable_name)<0)
     {
       if(ObjectCreate(chart_id,lable_name,OBJ_LABEL,0,0,0,0,0)) 
         { 
           ObjectSetString(chart_id,lable_name,OBJPROP_TEXT,label_text);
           ObjectSetString(chart_id,lable_name,OBJPROP_FONT,font_model); 
           ObjectSetInteger(chart_id,lable_name,OBJPROP_FONTSIZE,font_size);
           ObjectSetInteger(chart_id,lable_name,OBJPROP_COLOR,label_color);
           ObjectSetInteger(chart_id,lable_name,OBJPROP_CORNER,chart_corner);
           ObjectSetInteger(chart_id,lable_name,OBJPROP_XDISTANCE,x_cor);
           ObjectSetInteger(chart_id,lable_name,OBJPROP_YDISTANCE,y_cor);
         } 
       else 
          {Print("Failed to create the object OBJ_LABEL ",lable_name,", Error code = ", GetLastError());}
     }
   else
     {
       ObjectSetString(chart_id,lable_name,OBJPROP_TEXT,label_text);
       ObjectSetString(chart_id,lable_name,OBJPROP_FONT,font_model); 
       ObjectSetInteger(chart_id,lable_name,OBJPROP_FONTSIZE,font_size);
       ObjectSetInteger(chart_id,lable_name,OBJPROP_COLOR,label_color);
       ObjectSetInteger(chart_id,lable_name,OBJPROP_CORNER,chart_corner);
       ObjectSetInteger(chart_id,lable_name,OBJPROP_XDISTANCE,x_cor);
       ObjectSetInteger(chart_id,lable_name,OBJPROP_YDISTANCE,y_cor);
     }
//---
  }
//---------//
***Copyright © 2024 3rjfx ~ For educational purposes only.***

3. Explanation:

1. Function Signature:


void CreateChartText(long chart_id, 
                     string lable_name, 
                     string label_text,
                     string font_model,
                     int font_size,
                     color label_color,
                     int chart_corner,
                     int x_cor, 
                     int y_cor)

The function takes several parameters:

  • chart_id: The ID of the chart where the label will be created.
  • lable_name: The name of the label object.
  • label_text: The text content of the label.
  • font_model: The font style for the label text.
  • font_size: The font size for the label text.
  • label_color: The color of the label text.
  • chart_corner: The corner of the chart where the label will be positioned.
  • x_cor: The horizontal distance from the specified corner.
  • y_cor: The vertical distance from the specified corner.

2. Check if Label Exists:


if(ObjectFind(chart_id, lable_name) < 0)

Checks if the label object already exists on the chart.

If it does not exist (< 0), it proceeds to create a new label.

3. Create New Label:


if(ObjectCreate(chart_id, lable_name, OBJ_LABEL, 0, 0, 0, 0, 0))

Attempts to create a new label object with the specified parameters.

If the creation is successful, it sets various properties for the label:

  • ObjectSetString(chart_id, lable_name, OBJPROP_TEXT, label_text);
  • ObjectSetString(chart_id, lable_name, OBJPROP_FONT, font_model);
  • ObjectSetInteger(chart_id, lable_name, OBJPROP_FONTSIZE, font_size);
  • ObjectSetInteger(chart_id, lable_name, OBJPROP_COLOR, label_color);
  • ObjectSetInteger(chart_id, lable_name, OBJPROP_CORNER, chart_corner);
  • ObjectSetInteger(chart_id, lable_name, OBJPROP_XDISTANCE, x_cor);
  • ObjectSetInteger(chart_id, lable_name, OBJPROP_YDISTANCE, y_cor);

4. Error Handling:


else 
{
  Print("Failed to create the object OBJ_LABEL ", lable_name, ", Error code = ", GetLastError());
}

If the label creation fails, it prints an error message with the error code.

5. Update Existing Label:


else
{
  ObjectSetString(chart_id, lable_name, OBJPROP_TEXT, label_text);
  ObjectSetString(chart_id, lable_name, OBJPROP_FONT, font_model); 
  ObjectSetInteger(chart_id, lable_name, OBJPROP_FONTSIZE, font_size);
  ObjectSetInteger(chart_id, lable_name, OBJPROP_COLOR, label_color);
  ObjectSetInteger(chart_id, lable_name, OBJPROP_CORNER, chart_corner);
  ObjectSetInteger(chart_id, lable_name, OBJPROP_XDISTANCE, x_cor);
  ObjectSetInteger(chart_id, lable_name, OBJPROP_YDISTANCE, y_cor);
}

If the label already exists, it updates the properties of the existing label with the new values.

This CreateChartText function allows you to dynamically create and manage text labels on the chart, ensuring that the labels are created if they do not exist or updated if they already exist.

TimeBarCloseCheck Indicator Alerts.

Overview: The TimeBarCloseCheck indicator is designed to assist traders by providing real-time alerts when a bar (or candlestick) closes on the MetaTrader 5 platform. This feature is crucial for traders who rely on precise timing to make informed trading decisions.

Key Features:

Immediate Notifications: The indicator generates instant alerts as soon as a bar closes. This ensures that traders are always aware of the exact moment a bar closes, allowing them to respond swiftly to market changes.

Multiple Alert Options: Traders can choose from various alert types, including pop-up alerts, email notifications, and push notifications to their mobile devices. This flexibility ensures that traders never miss an important alert, regardless of where they are.

Customizable Settings: The alert settings are highly customizable, enabling traders to set up alerts according to their specific trading strategies and preferences. Whether it's adjusting the timeframe or selecting the type of alert, traders have full control.

Enhanced Decision Making: By providing timely and accurate alerts, the TimeBarCloseCheck indicator helps traders make better decisions. Knowing the precise time and price at which a bar closes can significantly improve the timing of trade entries and exits.

User-Friendly Interface: The alerts are displayed in an easy-to-read format, complete with the symbol, timeframe, and closing price. This ensures that traders can quickly understand the alert without any confusion.

Considerations:

Despite the robust features, traders should be aware of the potential delays caused by varying internet speeds and broker server delays. These factors can impact the timing accuracy of alert.

1. Internet Speed:Different traders have varying internet speeds, which can affect how quickly they receive alerts. Slower internet speeds may result in a slight delay in receiving the alert.

2. Broker Server Delay: The delay from the broker’s server when sending data to MT5 can also impact alert timing. If an alert is supposed to trigger one second before the new bar opens, and there is a server delay, the alert might not trigger at the exact time. Instead, it might trigger slightly late, or in some cases, it may miss the timing entirely because the new bar has already opened by the time the delay is resolved.

Example Alert:

Alert: TimeBarCloseCheck --- GBPJPY: Bar Close time triggered in Timeframe: M1 @Price: 192.311 --- at: 2024.11.26 20:09:59

In this example, the alert informs the trader that the GBPJPY bar on the M1 timeframe has closed at a price of 192.311. The exact time of the bar close is also provided, allowing the trader to take immediate action if necessary.

Conclusion:

The TimeBarCloseCheck indicator is an essential tool for traders who value precision and timely information. By leveraging its powerful alert features and being mindful of potential delays due to internet speed and broker server latency, traders can stay ahead of the market and make more informed trading decisions.

That's all for the article How to detect Bar Close Time in MT5 and MT4 programs, hopefully it's useful.

Vital Records

We hope that this article and the TimeBarCloseCheck for MT5 program will be useful for traders in learning and generating new ideas for forex trading.

If you think the TimeBarCloseCheck Indicator for MT5 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.

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.


//+------------------------------------------------------------------+
//|                                            TimeBarCloseCheck.mq5 |
//|        Copyright 2024, Roberto Jacobs (3rjfx) ~ Date: 2024-11-26 |
//|                              https://www.mql5.com/en/users/3rjfx |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Roberto Jacobs (3rjfx) ~ Date: 2024-11-26"
#property link      "https://www.mql5.com/en/users/3rjfx"
#property version   "1.00"
#property indicator_chart_window
//--
#property indicator_plots   0
//-- Enumeration
enum YN
 {
   No,  
   Yes
 };
//--
enum corner
 {  
   LeftHand=0,
   RightHand=1
 };
 
enum fonts
  {
    Verdana,
    Bodoni_MT_Black
  }; 
//--
//---
input ENUM_TIMEFRAMES  Timeframe = PERIOD_H1;     // Select Expert TimeFrame, default PERIOD_H1
input corner                 cor = LeftHand;     // Corner Position Text
//--- Input parameters for alerts
input YN                  alerts = Yes;            // Display Alerts Pop-up on Chart (Yes) or (No)
input YN           UseEmailAlert = No;             // Email Alert (Yes) or (No)
input YN           UseSendnotify = No;             // Send Notification (Yes) or (No)
//--- Input text parameters
input fonts              f_model = Verdana;        // Select Font Model
input color              t_color = clrGold;        // Select Text Color
//---
//--
double   CLOSE[];
datetime TIME[];
datetime TFOpenTime=0;
datetime TFClosTime=0;
datetime TFNextTime=0;
datetime TFprevTime=0;
datetime TFTimeRem=0;
datetime remd=0;
datetime tmrem=0;
int extime=0;
int exday=0;
//--
string txtday;
string font_mode;
//--
int  x_year;  // Year 
int  x_mon;   // Month 
int  x_day;   // Day of the month 
int  x_hour;  // Hour in a day 
int  x_min;   // Minutes 
int  x_sec;   // Seconds
//--
int  year=0;  // Year 
int  mon=1;   // Month 
int  day=2;   // Day 
int  hour=3;  // Hour 
int  min=4;   // Minutes 
int  sec=5;   // Seconds 
int  dow=6;   // Day of week (0-Sunday, 1-Monday, ... ,6-Saturday) 
int  doy=7;   // Day number of the year (January 1st is assigned the number value of zero)
//--
int f_size=9;
int cbar=9;
//--
int scale=15;
int garis=12;
int xdis=0,
    ydis=90;
ENUM_BASE_CORNER 
    PilCor;
//--
bool doalert;
string AlertTxt;
string _pname;
string indiname;
//---------//
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
     _pname=MQLInfoString(MQL_PROGRAM_NAME);
     indiname=_pname+" : "+TF2Str(Timeframe);
     //--
     cor==RightHand ? PilCor=ENUM_BASE_CORNER(CORNER_RIGHT_UPPER) : PilCor=ENUM_BASE_CORNER(CORNER_LEFT_UPPER);
     xdis=PilCor==(ENUM_BASE_CORNER)CORNER_RIGHT_UPPER ? 288 : 30;
     //--
     font_mode=FontsModel(f_model);
//---
   return(INIT_SUCCEEDED);
  }
//---------//
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//----
    Comment("");
    PrintFormat("%s: Deinitialization reason code=%d",__FUNCTION__,reason);
    Print(getUninitReasonText(reason));
    //--
    ObjectsDeleteAll(ChartID(),0,-1);
//----
   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[])
  {
//---
     ArrayResize(TIME,cbar);
     ArrayResize(CLOSE,cbar);
     ArraySetAsSeries(TIME,true);
     ArraySetAsSeries(CLOSE,true);
     ArraySetAsSeries(time,true);
     ArraySetAsSeries(close,true);
     //--
     RefreshPrice(Timeframe);
     //--
     TFOpenTime=TIME[0];
     CheckSeconds();
     //--
     TFNextTime=TFOpenTime+extime;
     TFClosTime=TFNextTime-1;
     TFTimeRem=fabs(TimeCurrent()-TFClosTime);
     tmrem=TFClosTime-TimeCurrent();
     datetime dtB=0; 
     if(TFClosTime!=TFprevTime) doalert=false;
     string SDT=ReqDate(TimeCurrent(),ReqTime(TimeCurrent(),day),ReqTime(TimeCurrent(),hour),ReqTime(TimeCurrent(),min),ReqTime(TimeCurrent(),sec));
     //--
     if(TimeCurrent()<TFClosTime)
       {
         remd=ReqTime(TFClosTime,day)-ReqTime(TimeCurrent(),day);
         exday=int(remd);
         if(exday<=0) exday=0;
         txtday=exday==0 ? (string)ReqTime(TFTimeRem,hour)+":"+(string)ReqTime(TFTimeRem,min)+":"+(string)ReqTime(TFTimeRem,sec) :
         (string)exday+" Day ~ "+(string)ReqTime(TFTimeRem,hour)+":"+(string)ReqTime(TFTimeRem,min)+":"+(string)ReqTime(TFTimeRem,sec);
       }
     //--
     CreateChartText(0,indiname+"SDT0","Server Date Time : "+SDT,font_mode,f_size,t_color,PilCor,xdis,ydis);
     CreateChartText(0,indiname+"SDT11","------------------------------------------------------",font_mode,f_size,t_color,PilCor,xdis,ydis+garis);
     CreateChartText(0,indiname+"SDT1",_pname+" : Period "+TF2Str(Timeframe),font_mode,f_size,t_color,PilCor,xdis,ydis+garis+(1*scale));
     CreateChartText(0,indiname+"SDT2","Bar Open Time : "+
                       TimeToString(TFOpenTime,TIME_DATE|TIME_MINUTES|TIME_SECONDS),font_mode,f_size,t_color,PilCor,xdis,ydis+garis+(2*scale));
     CreateChartText(0,indiname+"SDT3","Bar Close Time : "+
                       TimeToString(TFClosTime,TIME_DATE|TIME_MINUTES|TIME_SECONDS),font_mode,f_size,t_color,PilCor,xdis,ydis+garis+(3*scale));
     CreateChartText(0,indiname+"SDT4","Next Bar Open Time : "+
                       TimeToString(TFNextTime,TIME_DATE|TIME_MINUTES|TIME_SECONDS),font_mode,f_size,t_color,PilCor,xdis,ydis+garis+(4*scale));
     CreateChartText(0,indiname+"SDT5","Time Remaining : "+txtday,font_mode,f_size,t_color,PilCor,xdis,ydis+garis+(5*scale));                         
     //--
     if(alerts==Yes||UseEmailAlert==Yes||UseSendnotify==Yes)
       {
         if(TimeCurrent()>=TFClosTime && TimeCurrent()<=TFNextTime && !doalert)
           {
             dtB=TimeCurrent();
             AlertTxt="Bar Close time triggered in Timeframe : "+TF2Str(Timeframe)+" @Price: "+DoubleToString(CLOSE[0],Digits());
             Do_Alerts(AlertTxt,dtB);
             TFprevTime=TFClosTime;
             doalert=true;
           }
       }
    //--
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

int CheckSeconds(void)
  {
//---
   switch(Timeframe)
     {
       //--
       case PERIOD_M1:   extime=PeriodSeconds(PERIOD_M1);     break;
       case PERIOD_M2:   extime=PeriodSeconds(PERIOD_M2);     break;
       case PERIOD_M3:   extime=PeriodSeconds(PERIOD_M3);     break;
       case PERIOD_M4:   extime=PeriodSeconds(PERIOD_M4);     break;
       case PERIOD_M5:   extime=PeriodSeconds(PERIOD_M5);     break;
       case PERIOD_M6:   extime=PeriodSeconds(PERIOD_M6);     break;
       case PERIOD_M10:  extime=PeriodSeconds(PERIOD_M10);    break;
       case PERIOD_M12:  extime=PeriodSeconds(PERIOD_M12);    break;
       case PERIOD_M15:  extime=PeriodSeconds(PERIOD_M15);    break;
       case PERIOD_M20:  extime=PeriodSeconds(PERIOD_M20);    break;
       case PERIOD_M30:  extime=PeriodSeconds(PERIOD_M30);    break;
       case PERIOD_H1:   extime=PeriodSeconds(PERIOD_H1);     break;
       case PERIOD_H2:   extime=PeriodSeconds(PERIOD_H2);     break;
       case PERIOD_H3:   extime=PeriodSeconds(PERIOD_H3);     break;
       case PERIOD_H4:   extime=PeriodSeconds(PERIOD_H4);     break;
       case PERIOD_H6:   extime=PeriodSeconds(PERIOD_H6);     break;
       case PERIOD_H8:   extime=PeriodSeconds(PERIOD_H8);     break;
       case PERIOD_H12:  extime=PeriodSeconds(PERIOD_H12);    break;
       case PERIOD_D1:   extime=PeriodSeconds(PERIOD_D1);     break;
       case PERIOD_W1:   extime=PeriodSeconds(PERIOD_W1);     break;
       case PERIOD_MN1:  extime=PeriodSeconds(PERIOD_MN1);    break;
       //--
     }
    //--
    return(extime);
//---
  } //-end CheckSeconds()
//---------// 

void RefreshPrice(ENUM_TIMEFRAMES xtf)
  {
//---
    MqlRates parray[]; 
    ArraySetAsSeries(parray,true); 
    int copied=CopyRates(Symbol(),xtf,0,cbar,parray);
    //--
    int cc=CopyClose(Symbol(),xtf,0,cbar,CLOSE);
    int ct=CopyTime(Symbol(),xtf,0,cbar,TIME);
    //--
    return;
//---
  } //-end RefreshPrice()
//---------//

string timehr(int hr,int mn)
  {
//---
    string scon="";
    string men=mn==0 ? "00" : string(mn);
    int shr=hr==24 ? 0 : hr;
    if(shr<10) scon="0"+string(shr)+":"+men;
    else scon=string(shr)+":"+men;
    //--
    return(scon);
//---
  } //-end timehr()
//---------//

string ReqDate(datetime reqtime,int d,int h,int m,int s) 
  { 
//---
   MqlDateTime mdt; 
   datetime t=TimeToStruct(reqtime,mdt);
   x_year=mdt.year; 
   x_mon=mdt.mon; 
   x_day=d; 
   x_hour=h; 
   x_min=m;
   x_sec=s;
   //--
   string mdr=string(x_year)+"."+string(x_mon)+"."+string(x_day)+" ~ "+timehr(x_hour,x_min)+":"+string(x_sec);
   return(mdr);
//---
  } //-end ReqDate()
//---------//

int ReqTime(datetime reqtime,
            const int reqmode) 
  {
    MqlDateTime tm;
    TimeToStruct(reqtime,tm);
    int valtm=0;
    //--
    switch(reqmode)
      {
        case 0: valtm=tm.year; break;        // Return Year 
        case 1: valtm=tm.mon;  break;        // Return Month 
        case 2: valtm=tm.day;  break;        // Return Day 
        case 3: valtm=tm.hour; break;        // Return Hour 
        case 4: valtm=tm.min;  break;        // Return Minutes 
        case 5: valtm=tm.sec;  break;        // Return Seconds 
        case 6: valtm=tm.day_of_week; break; // Return Day of week (0-Sunday, 1-Monday, ... ,6-Saturday) 
        case 7: valtm=tm.day_of_year; break; // Return Day number of the year (January 1st is assigned the number value of zero) 
      }
    //--
    return(valtm);
//---
  } //-end ReqTime()
//---------//

string TF2Str(int period)
  {
   switch(period)
     {
       //--
       case PERIOD_M1:   return("M1");
       case PERIOD_M2:   return("M2");
       case PERIOD_M3:   return("M3");
       case PERIOD_M4:   return("M4");
       case PERIOD_M5:   return("M5");
       case PERIOD_M6:   return("M6");
       case PERIOD_M10:  return("M10");
       case PERIOD_M12:  return("M12");
       case PERIOD_M15:  return("M15");
       case PERIOD_M20:  return("M20");
       case PERIOD_M30:  return("M30");
       case PERIOD_H1:   return("H1");
       case PERIOD_H2:   return("H2");
       case PERIOD_H3:   return("H3");
       case PERIOD_H4:   return("H4");
       case PERIOD_H6:   return("H6");
       case PERIOD_H8:   return("H8");
       case PERIOD_H12:  return("H12");
       case PERIOD_D1:   return("D1");
       case PERIOD_W1:   return("W1");
       case PERIOD_MN1:  return("MN1");
       //--
     }
   return(string(period));
  }  
//---------//

void Do_Alerts(string msgText,datetime Altime)
  {
//---
    //--
    Print(_pname+" --- "+Symbol()+": "+msgText+
          "\n --- at: ",TimeToString(Altime,TIME_DATE|TIME_MINUTES|TIME_SECONDS));
    //--
    if(alerts==Yes)
      {
        Alert(_pname," --- "+Symbol()+": "+msgText+
              " --- at: ",TimeToString(Altime,TIME_DATE|TIME_MINUTES|TIME_SECONDS));
      }
    //--
    if(UseEmailAlert==Yes) 
      SendMail(_pname," --- "+Symbol()+" "+TF2Str(Period())+": "+msgText+
                       "\n--- at: "+TimeToString(Altime,TIME_DATE|TIME_MINUTES|TIME_SECONDS));
    //--
    if(UseSendnotify==Yes) 
      SendNotification(_pname+"--- "+Symbol()+" "+TF2Str(Period())+": "+msgText+
                      "\n --- at: "+TimeToString(Altime,TIME_DATE|TIME_MINUTES|TIME_SECONDS));
    //--
    return;
    //--
//---
  } //-end Do_Alerts()
//---------//

string FontsModel(int mode)
  { 
   string str_font;
   switch(mode) 
     { 
      case 0: str_font="Verdana"; break;
      case 1: str_font="Bodoni MT Black"; break; 
     }
   //--
   return(str_font);
//----
  } //-end FontsModel()
//---------//

void CreateChartText(long   chart_id, 
                     string lable_name, 
                     string label_text,
                     string font_model,
                     int    font_size,
                     color  label_color,
                     int    chart_corner,
                     int    x_cor, 
                     int    y_cor) 
  { 
//--- 
   if(ObjectFind(chart_id,lable_name)<0)
     {
       if(ObjectCreate(chart_id,lable_name,OBJ_LABEL,0,0,0,0,0)) 
         { 
           ObjectSetString(chart_id,lable_name,OBJPROP_TEXT,label_text);
           ObjectSetString(chart_id,lable_name,OBJPROP_FONT,font_model); 
           ObjectSetInteger(chart_id,lable_name,OBJPROP_FONTSIZE,font_size);
           ObjectSetInteger(chart_id,lable_name,OBJPROP_COLOR,label_color);
           ObjectSetInteger(chart_id,lable_name,OBJPROP_CORNER,chart_corner);
           ObjectSetInteger(chart_id,lable_name,OBJPROP_XDISTANCE,x_cor);
           ObjectSetInteger(chart_id,lable_name,OBJPROP_YDISTANCE,y_cor);
         } 
       else 
          {Print("Failed to create the object OBJ_LABEL ",lable_name,", Error code = ", GetLastError());}
     }
   else
     {
       ObjectSetString(chart_id,lable_name,OBJPROP_TEXT,label_text);
       ObjectSetString(chart_id,lable_name,OBJPROP_FONT,font_model); 
       ObjectSetInteger(chart_id,lable_name,OBJPROP_FONTSIZE,font_size);
       ObjectSetInteger(chart_id,lable_name,OBJPROP_COLOR,label_color);
       ObjectSetInteger(chart_id,lable_name,OBJPROP_CORNER,chart_corner);
       ObjectSetInteger(chart_id,lable_name,OBJPROP_XDISTANCE,x_cor);
       ObjectSetInteger(chart_id,lable_name,OBJPROP_YDISTANCE,y_cor);
     }
//---
  }
//---------//

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 © 2024 3rjfx ~ For educational purposes only.***

Please download the indicator: TimeBarCloseCheck for MT5


© 2024 TimeBarCloseCheck for MT5 - Developed by Roberto Jacobs (3rjfx)

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...