Monday, January 20, 2025

In this article I will discuss about making the Strength of Price Movement Multi Timeframe indicator for MT5, where the indicator will calculate price movement comparison in percent on each timeframe, in this case 21 timeframes from M1 to MN1.

Introduction to the Multi-Timeframe Indicator for MT5.

The multi-timeframe (MTF) indicator is an incredibly useful tool for forex traders. It allows traders to view market conditions from various timeframes on a single chart, enabling them to make more informed decisions based on a broader perspective of market trends.

Key Functions of the MTF Indicator.

  • 1. Displays Multiple Timeframes: This indicator displays various timeframes such as W1 (weekly),D1 (daily), H4 (4-hour), H1 (1-hour), and others on a single chart.
  • 2. Helps Identify Market Trends: By viewing the indicator from multiple timeframes, traders can more easily identify both larger and smaller market trends.
  • 3. Supports Trading Decisions: The indicator assists traders in making decisions to enter or exit trades based on market conditions observed across different timeframes.
USDCADH1_SPM_MTF_no_display

How the MTF Indicator Works.

The MTF indicator has a unique ability to display the condition, position, and movement of indicators and/or prices across 21 timeframes within a single user-selected timeframe. In other words, users can choose one timeframe to display information from all 21 timeframes available in MT5.

For instance, on an H1 chart, this indicator can display data from the W1, D1, H4, and 18 other timeframes. This allows traders to gain a comprehensive view of market conditions.

Additionally, the indicator uses the OnChartEvent function from MT5. This feature enables users to select the display panel with a single click to place it above the chart, below the chart, or hide it completely. The indicator will only show an arrow button for navigating the panel display, making it easier and more flexible to use.

Benefits of Using the MTF Indicator.

  • 1. Early Trend Recognition:
    • Traders can detect trend changes earlier by viewing indicators from smallest to larger timeframes.
  • 2. Better Decision-Making:
    • By viewing market conditions from multiple timeframes, traders can make more accurate and data-driven decisions.

Strategies Using Multi-Timeframe Indicator.

Utilizing a multi-timeframe (MTF) indicator in technical analysis allows traders to gain a broader and deeper perspective on market movements across different timeframes. Here are some strategies based on the key functions of the MTF indicator:

  • 1. Displays Multiple Timeframes:
    • Strategy: Leverage the indicator's ability to display multiple timeframes on a single chart to identify trends and price movements more clearly.
    • Implementation: For instance, if the weekly timeframe shows an uptrend, traders can look for buy signals on the daily or hourly timeframe when the price pulls back to major support levels.
  • 2. Helps Identify Market Trends:
    • Strategy: Use the indicator to view both larger and smaller market trends simultaneously to get a more comprehensive picture.
    • Implementation: If the daily timeframe indicates a strong uptrend, traders can use the 4-hour or 1-hour timeframe to look for buy confirmations when the price approaches support levels or when momentum indicators signal a price reversal.
  • 3. Supports Trading Decisions:
    • Strategy: Utilize information from various timeframes to make more informed trading decisions, whether it’s entering or exiting positions.
    • Implementation: If the 4-hour timeframe shows starting to see a decline in price movements, traders can verify this signal by checking the 1-hourtimeframe. If the 1-hour timeframe also shows starting to see a decline in price movements, this could be a strong signal to sell.
  • 4. Indicator Alert:
    • In addition, this indicator will also provide alerts when there is a change in position, condition and strength of price movement on all timeframes.
    • In this case, the calculation of the strength of price movement is calculated based on how many timeframes show an increase and how many timeframes show a decrease.
      • If the timeframes that show an increase are more than the timeframes that show a decrease in price, then the indicator will give a buy signal, and vice versa.

By applying these strategies, traders can leverage the advantages of the multi-timeframe indicator to optimize trading decisions and manage risk more effectively. Always remember to backtest and evaluate your strategies periodically to ensure their effectiveness under various market conditions.

  • 1. Introduction:
    • The SPM_MTF indicator, or Strength of Price Movement Multi Timeframe indicator for MT5 which is calculated price movement comparison in percent on each timeframe.
  • 2. Key Functions of the SPM_MTF Indicator:
    • Displays Multiple Timeframes:
      • The indicator displays various timeframes on a single chart, helping traders to analyze market trends and movements effectively.
    • Helps Identify Market Trends:
      • By viewing the indicator across multiple timeframes, traders can identify both larger and smaller market trends more easily.
    • Supports Trading Decisions:
      • The indicator assists traders in making informed decisions to enter or exit trades based on market conditions observed across different timeframes.
  • 3. The Multi-Timeframe Indicator Program:
  • 
    //+------------------------------------------------------------------+
    //|                                                      SPM_MTF.mq5 |
    //|        Copyright 2025, Roberto Jacobs (3rjfx) ~ Date: 2025-01-08 |
    //|                              https://www.mql5.com/en/users/3rjfx |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2025, Roberto Jacobs (3rjfx) ~ Date: 2025-01-08"
    #property link      "https://www.mql5.com/en/users/3rjfx"
    #property version   "1.00"
    #property description "SPM_MTF is the Strength of Price Movement Multi Timeframe indicator for MT5"
    #property description "which is calculated price movement comparison in percent 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
      };
    //--
    //---
    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)
    //---
    //---------//
    
    • Code Overview:
      • The code is written in MQL5, a language used for creating custom indicators, scripts, and automated trading strategies on the MetaTrader 5 platform.
  • 4. Main Functions in the Program:
    • Function Display Timeframes:
      • This function is responsible for displaying multiple timeframes on a single chart, allowing traders to see various periods simultaneously.
    • Function Identify Trends:
      • This function helps to identify market trends by analyzing price movements across different timeframes.
    • Function Support Trading Decisions:
      • This function supports trading decisions by providing signals based on multi-timeframe analysis.
  • 5. Code Explanation:
    • Input Parameters:
      • The code defines several input parameters for customizing the indicator's appearance and alert settings, including arrow colors, font selection, and alert options.
    • Enums:
      • Two enumerations are defined: YN (Yes/No) for boolean-like input parameters and fonts for font selection.
    • Input Groups:
      • Input parameters are organized into groups for better clarity. The first group focuses on the indicator's color and font settings, while the second group handles alert settings.
    • Alerts:
      • Options for displaying alerts on the chart, sending email alerts, and sending notifications are included, providing flexibility in how traders receive alerts.
  • Example Implementation:
    • Indicator Properties:
      • The indicator's properties are defined at the beginning of the code, including copyright information, version, description, and the number of plots and buffers.
    • Indicator Buffers:
      • One buffer is declared for storing indicator values.

Class for MTF indicator:


//+------------------------------------------------------------------+
//| class for MTF indicator                                          |
//+------------------------------------------------------------------+
class MTF_Indi
  {
   //---
   public:
   //--
   int               fbar;
   int               star,
                     tstar,
                     bstar;
   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;
   //--
   long              CI;
   string            posisi,
                     sigpos,
                     indname,
                     msgText,
                     ObjName;
   string            cstar,
                     artop,
                     arbot;
   string            hName1,
                     hName2,
                     hName3;
   string            font_mode;
   bool              display;
   double            OPEN[],
                     HIGH[],
                     LOW[],
                     CLOSE[];
   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(15),
                     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),
                     ttlbars(125),
                     windsize(12),
                     windchar(108),
                     CI(ChartID()),
                     display(false),
                     ObjName("SPM_"),
                     font_mode(FontsModel(f_model)),
                     cstar(CharToString((uchar)star)),
                     artop(CharToString((uchar)tstar)),
                     arbot(CharToString((uchar)bstar)),
                     indname(MQLInfoString(MQL_PROGRAM_NAME))
     {
     }
   //---
   //- Destructor
                    ~MTF_Indi(void)
     {
     }
   //---

Code Explanation of the MTF_Indi Class:

  • Class Definition:
    • The MTF_Indi class encapsulates the functionality of the multi-timeframe indicator.
  • Public Members:
    • Various public member variables are declared to store different attributes and settings related to the indicator, such as timeframe coordinates, alert settings, font size, and display parameters.
  • Constructor:
    • The constructor initializes the member variables with default values, ensuring that the indicator is set up correctly when instantiated.
  • Destructor:
    • The destructor is defined but currently does not perform any specific actions.
  • Member Variables:
    • The class defines numerous member variables for storing values such as chart coordinates, alert settings, font details, and indicator data buffers.
    • These variables are used throughout the class methods to perform various indicator functions.

Main Functions in the Program:

  • Function SPM_MTF_Config():
    • This function is responsible for configuring the multi-timeframe settings and preparing the indicator for use.
  • Code Explanation of the SPM_MTF_Config Function:
    • Timeframe Enumeration:
      • The function defines an array TFIx of different timeframes that the indicator will use.
      • 
        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};
        
      • Array Resizing:
        • The arrays TFId and Arwcolor are resized to match the number of timeframes defined.
        • 
                tfxar=ArraySize(TFIx);
                ArrayResize(TFId,tfxar,tfxar);
                ArrayResize(Arwcolor,tfxar,tfxar);
                ArrayCopy(TFId,TFIx,0,0,WHOLE_ARRAY);
          
    • Timeframe Labels:
      • An array TFxc of timeframe labels (e.g., "M1", "M2", "M3") is defined, and TFSc is resized and populated accordingly.
      • 
        //--
        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(TFSc,tfxar,tfxar);
        ArrayCopy(TFSc,TFxc,0,0,WHOLE_ARRAY);
        //--
        
    • Supporting Functions:
      • The function calls several other functions to complete the configuration:
        • DeletedSPMObject(): Deletes any previous objects related to the indicator.
        • CalculationStrength(25): Initializes or recalculates the strength of price movement with a given parameter.
        • PositionCore(): Sets up the core position settings for the indicator.
    • Display Conditional:
      • If the display flag is true, the DrawSPMObject() function is called to draw the indicator objects on the chart.
      • 
        if(display)
          DrawSPMObject();
        

Function PositionCore():

This function is responsible for setting up the core position settings for the indicator, including the display and layout of various elements on the chart.


   //---
   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="S";
            hName2="P";
            hName3="M";
            //--
            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="M";
            hName2="P";
            hName3="S";
            //--
            DisplayButtonClick("cstar");
            DisplayButtonClick("artop");
            //--
           }
         display=true;
        }
     }
   //---
  • Code Explanation of the PositionCore Function:
    • Position Setup:
      • The function begins by assigning the corpos variable the value of pospos.
    • Upper Position Settings:
      • If corpos equals postop, several parameters are set to position elements in the upper left corner of the chart.
    • Lower Position Settings:
      • If corpos equals posbot, several parameters are set to position elements in the lower left corner of the chart.
    • Display Activation:
      • The display flag is set to true, indicating that the setup for displaying elements on the chart is complete.

Function DrawSPMObject(void):

This function is responsible for drawing the various elements of the indicator on the chart.


//---
void              DrawSPMObject(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 Price 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 DrawSPMObject()
//---
  • Code Explanation of the DrawSPMObject Function:
    • Template Creation:
      • A button template is created as a base for other objects.
      • 
        CreateButtonTemplate(CI,ObjName+"Template",397,66,STYLE_SOLID,9,BORDER_RAISED,clrMistyRose,clrLavenderBlush,clrWhite,bcor,corx,cory,true);
        
    • Upper Timeframe Labels and Buttons:
      • The function iterates over the first half of the timeframes to create arrow labels and buttons.
      • 
        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]);
                }
        
    • Lower Timeframe Labels and Buttons:
      • The function iterates over the remaining timeframes to create arrow labels and buttons.
      •           
        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]);
                 // Special case for the last timeframe
                 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 Price Movement");
                   }
        
    • Button Templates and Labels:
      • Additional button templates and labels are created for the indicator's display.
      • 
              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);
              //--
        
    • Display Conditional:
      • Depending on the corpos value, specific buttons are displayed.
      • 
              if(corpos==postop)
                {
                  DisplayButtonClick("cstar");
                  DisplayButtonClick("arbot");
                }
              if(corpos==posbot)
                {
                  DisplayButtonClick("cstar");
                  DisplayButtonClick("artop");
                }
        

Function PanelPosChange(int inpos)::

This function is responsible for changing the position of the panel based on the input position value.


   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="S";
            hName2="P";
            hName3="M";
            //--
            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="M";
            hName2="P";
            hName3="S";
            //--
            DisplayButtonClick("cstar");
            DisplayButtonClick("artop");
            //--
           }
         display=true;
        }
      //---
     }
   //---
  • Code Explanation of the PanelPosChange Function:
    • Position Setup:
      • The function begins by assigning the corpos variable the value of inpos.
      • 
        corpos=inpos;
        
    • Upper Position Settings:
      • If inpos equals postop, several parameters are set to position elements in the upper left corner of the chart.
      • 
                 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="S";
                    hName2="P";
                    hName3="M";
                    //--
                    DisplayButtonClick("cstar");
                    DisplayButtonClick("arbot");
                    //--
                   }
        
    • Lower Position Settings:
      • If inpos equals posbot, several parameters are set to position elements in the lower left corner of the chart.
      • 
                 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="M";
                    hName2="P";
                    hName3="S";
                    //--
                    DisplayButtonClick("cstar");
                    DisplayButtonClick("artop");
                    //--
                   }
        
    • Display Activation:
      • The display flag is set to true, indicating that the setup for displaying elements on the chart is complete.
      • 
                 display=true;
                }
        

Function UpdatePrice(ENUM_TIMEFRAMES xtf):

This function is responsible for updating the price data arrays for a given timeframe.


   //---
   void              UpdatePrice(ENUM_TIMEFRAMES xtf)
     {
      //--
      ArrayFree(OPEN);
      ArrayFree(HIGH);
      ArrayFree(LOW);
      ArrayFree(CLOSE);
      ArrayFree(TIME);
      //--
      ArrayResize(OPEN,maxbar,maxbar);
      ArrayResize(HIGH,maxbar,maxbar);
      ArrayResize(LOW,maxbar,maxbar);
      ArrayResize(CLOSE,maxbar,maxbar);
      ArrayResize(TIME,maxbar,maxbar);
      //--
      ArraySetAsSeries(OPEN,true);
      ArraySetAsSeries(HIGH,true);
      ArraySetAsSeries(LOW,true);
      ArraySetAsSeries(CLOSE,true);
      ArraySetAsSeries(TIME,true);
      //--
      ArrayInitialize(OPEN,0.0);
      ArrayInitialize(HIGH,0.0);
      ArrayInitialize(LOW,0.0);
      ArrayInitialize(CLOSE,0.0);
      ArrayInitialize(TIME,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);
      //--
      return;
      //---
     } //-end UpdatePrice()
   //---
  • Code Explanation of the UpdatePrice Function:
    • Array Initialization:
      • The function begins by freeing any existing arrays for open, high, low, close prices, and time.
      • 
              //--
              ArrayFree(OPEN);
              ArrayFree(HIGH);
              ArrayFree(LOW);
              ArrayFree(CLOSE);
              ArrayFree(TIME);
              //--
        
    • Array Resizing:
      • The arrays are then resized to hold the maximum number of bars (maxbar).
      • 
              //--
              ArrayResize(OPEN,maxbar,maxbar);
              ArrayResize(HIGH,maxbar,maxbar);
              ArrayResize(LOW,maxbar,maxbar);
              ArrayResize(CLOSE,maxbar,maxbar);
              ArrayResize(TIME,maxbar,maxbar);
              //--
        
    • Array Series Setting:
      • The arrays are set to behave as series, where index 0 is the most recent data.
      • 
              //--
              ArraySetAsSeries(OPEN,true);
              ArraySetAsSeries(HIGH,true);
              ArraySetAsSeries(LOW,true);
              ArraySetAsSeries(CLOSE,true);
              ArraySetAsSeries(TIME,true);
              //--
        
    • Array Initialization:
      • The arrays are initialized to default values to clear any old data.
      • 
              //--
              ArrayInitialize(OPEN,0.0);
              ArrayInitialize(HIGH,0.0);
              ArrayInitialize(LOW,0.0);
              ArrayInitialize(CLOSE,0.0);
              ArrayInitialize(TIME,0);
              //--
        
    • Price Data Refresh:
      • The function calculates the number of bars based on the specified timeframe (xtf) and calls the RefreshPrice function for both the M1 timeframe and the specified timeframe.
      • 
              //--
              int barx=PeriodSeconds(xtf)/60*maxbar;
              RefreshPrice(PERIOD_M1,maxbar);
              RefreshPrice(xtf,barx);
              //--
        
    • Copying Price Data:
      • The function copies the open, high, low, close prices, and time data into the respective arrays for the specified timeframe.
      • 
              //--
              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);
              //--
        

Function RefreshPrice(ENUM_TIMEFRAMES xtf, int bars):

This function is responsible for refreshing the price data for a given timeframe and a specified number of bars.

  • Code Explanation of the RefreshPrice Function:
    • Array Declaration:
      • The function begins by declaring an array parray of type MqlRates to store the price data.
      • 
        MqlRates parray[];
        
    • Array Series Setting:
      • The array is set to behave as a series, where index 0 is the most recent data.
      • 
        ArraySetAsSeries(parray,true);
        
    • Copying Price Data:
      • The CopyRates function is called to copy the price data for the specified symbol and timeframe into the parray array. The number of bars tocopy is specified by the bars parameter.
      • 
        int copied=CopyRates(Symbol(),xtf,0,bars,parray);
        

Function PriceStrengthScan(const ENUM_TIMEFRAMES stf, int shift):

This function calculates the direction of the price bar in percentage terms.

  • Code Explanation of the PriceStrengthScan Function:
    • Variable Initialization:
      • The function starts by initializing several variables to store return values, rise/fall indicators, and intermediate results.
      • 
              //--
              int ret=0;
              int rise=1,
                  down=-1;
              //--
              int br=shift+2;
              double res=0.0;
              double move=0.0;
        
    • Update Price Data:
      • The UpdatePrice function is called to refresh the price data for the specified timeframe.
      • 
        UpdatePrice(stf);
        
    • Array Initialization:
      • An array CL is resized and set as a series to store close prices.
      • 
              //--
              double CL[];
              ArrayResize(CL,br,br);
              ArraySetAsSeries(CL,true);
              //--
        
    • Populating Close Prices:
      • The close prices are copied into the CL array.
      • 
              //--
              for(int x=br-1; x>=shift; x--)
                 CL[x]=CLOSE[x];
              //--
        
    • Calculating Price Strength:
      • The function calculates the percentage change between the current and previous close prices.
      • 
              //--
              double close_now  = CL[shift];
              double close_prev = CL[shift+1];
              //--
              if((close_now==0 || close_now==EMPTY_VALUE) || (close_prev==0 || close_prev==EMPTY_VALUE))
                 res=0.0;
              else
                 res=NormalizeDouble((close_now / close_prev * 100) - 100,3);
              res=NormalizeDouble(res*100,3); // because its value less than 1 then multiplied with 100.
              //--
        
    • Determining Price Direction:
      • The function sets the return value based on whether the price has risen or fallen.
      • 
              //--
              if(res>move)
                 ret=rise;
              if(res<move)
                 ret=down;
              //--
        
    • Returning the Result:
      • The function returns the calculated direction of the price bar.
      • 
              //--
              return(ret);
              //---
        

Function CalculationStrength(int barCnt):

This function scans the direction of movement of each timeframe and calculates the strength of price movement in percentage terms.

  • Code Explanation of the CalculationStrength Function:
    • Array Initialization:
      • The function begins by resizing and setting the PowerMove array as a series to store the calculated strength values.
      • 
              //--
              ArrayResize(PowerMove,barCnt,barCnt);
              ArraySetAsSeries(PowerMove,true);
              //--
        
    • Main Calculation Loop:
      • The function loops through each bar in the specified count (barCnt) and initializes counters for up and down movements.
      • 
        for(int i=barCnt-1; i>=0; i--)
          {
           up=0;
           dw=0;
           //--
        
    • Inner Loop for Timeframes:
      • Within the main loop, the function iterates over each timeframe defined in tfxar and initializes arrow colors and power move values.
      • 
        for(int x=0; x<tfxar; x++)
          {
           Arwcolor[x]=NTArrow;
           PowerMove[i]=0.0;
           int PPM=PriceStrengthScan(TFId[x],0);
        
    • Price Strength Scan:
      • The PriceStrengthScan function is called to determine the price movement direction for each timeframe.
      • 
        int PPM=PriceStrengthScan(TFId[x],0);
        
    • Updating Counters and Arrow Colors:
      • The function updates the up and down counters and sets the appropriate arrow colors based on the result of PriceStrengthScan.
      •           
        if(PPM>0)
          {
            up++;
            Arwcolor[x]=ArrowUp;
          }
        if(PPM<0)
          {
            dw++;
            Arwcolor[x]=ArrowDn;
          }         
        
    • Final Calculation and Alert Setting:
      • After iterating through all timeframes, the function sets the PowerMove value and alert color based on the counters' comparison.
      • 
        if(x==tfxar-1)
          {
           if(up>dw+1)
             {
              PowerMove[i]=1.0;
              TColor=ArrowUp;
              curAlert=1;
             }
        if(dw>up+1)
             {
              PowerMove[i]=-1.0;
              TColor=ArrowDn;
              curAlert=-1;
             }
        

Function ThisTime(const int reqmode):

This function retrieves the current time information based on the requested mode.

  • Code Explanation of the ThisTime Function:
    • Variable Initialization:
      • The function starts by declaring a MqlDateTime structure and an integer to store the time value.
      • 
        //--
        MqlDateTime tm;
        TimeCurrent(tm);
        int valtm=0;
        //--
        
    • Switch Statement for Time Retrieval:
    • The function uses a switch statement to assign the appropriate time component based on the reqmode parameter.
    • 
            //--
            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 the Time Value:
      • The function returns the time value based on the selected mode.
      • 
        return(valtm);
        

Function Do_Alerts(string msg):

This function handles the generation of various types of alerts (print, alert, email, and notification) based on the provided message.

  • Code Explanation of the Do_Alerts Function:
    • Print Alert:
      • The function begins by printing the message along with the current symbol, timeframe, and time.
      • 
        //--
        Print("--- "+Symbol()+","+strTF(Period())+": "+msg+
              "\n--- at: ",TimeToString(TimeCurrent(),TIME_DATE|TIME_MINUTES));
        //--
        
    • Chart Alert:
      • If alerts are enabled, it triggers an alert with the same message details.
      • 
        //--
        if(alerts==Yes)
          {
            Alert("--- "+Symbol()+","+strTF(Period())+": "+msg+
                  "--- at: ",TimeToString(TimeCurrent(),TIME_DATE|TIME_MINUTES));
          }
        //--
        
    • Email Alert:
      • If email alerts are enabled, it sends an email with the message details.
      • 
        //--
        if(UseEmailAlert==Yes)
           SendMail(indname,"--- "+Symbol()+" "+strTF(Period())+": "+msg+
                    "\n--- at: "+TimeToString(TimeCurrent(),TIME_DATE|TIME_MINUTES));
        //--
        
    • Notification Alert:
      • If notifications are enabled, it sends a notification with the message details.
      • 
        //--
        if(UseSendnotify==Yes)
           SendNotification(Symbol()+"--- "+Symbol()+" "+strTF(Period())+": "+msg+
                            "\n--- at: "+TimeToString(iTime(Symbol(),0,0),TIME_DATE|TIME_MINUTES));
        //--
        

Function FontsModel(int mode):

This function returns the font name based on the input mode.

  • Code Explanation of the FontsModel Function:
    • Variable Initialization:
      • The function starts by declaring a string variable str_font to store the font name.
      • 
        string str_font;
        
    • Switch Statement for Font Selection:
      • The function uses a switch statement to assign the appropriate font name based on the mode parameter.
      • 
        switch(mode)
          {
            case 0:
               str_font="Verdana";
               break;
            case 1:
               str_font="Bodoni MT Black";
               break;
          }
        //--
        
    • Return the Font Name:
      • The function returns the font name based on the selected mode.
      • 
        return(str_font);
        

Function ChangeChartSymbol(string tf_name, ENUM_TIMEFRAMES stf):

This function changes the chart's symbol and timeframe based on the input parameters.

  • Code Explanation of the ChangeChartSymbol Function:
    • Unpress Button:
      • The function starts by unpressing the button associated with the given timeframe name (tf_name).
      • 
        //--- unpress the button
        ObjectSetInteger(CI,tf_name,OBJPROP_STATE,false);
        ObjectSetInteger(CI,tf_name,OBJPROP_ZORDER,0);
        
    • Delete Existing Objects:
      • The function calls DeletedSPMObject() to delete any existing objects related to the indicator.
      • 
        DeletedSPMObject();
        
    • Change Panel Position:
      • The function updates the panel position by calling PanelPosChange(corpos).
      • 
        PanelPosChange(corpos);
        
    • Set Chart Symbol and Timeframe:
      • The chart's symbol and timeframe are updated using ChartSetSymbolPeriod.
      • 
        ChartSetSymbolPeriod(CI,Symbol(),stf);
        
    • Redraw Objects:
      • If the display flag is true, the function calls DrawSPMObject() to redraw the indicator objects on the chart.
      • 
        if(display)
          DrawOBVObject();
        
    • Redraw Chart:
      • Finally, the function calls ChartRedraw(CI) to refresh the chart display.
      • 
        ChartRedraw(CI);
        

Function DisplayPanelButton():

This function is responsible for displaying the panel buttons on the chart, allowing users to interact with the indicator.

  • Code Explanation of the DisplayPanelButton Function:
    • Deleting Existing Objects:
      • The function starts by deleting any existing panel buttons (cstar,artop, and arbot) to ensure a clean slate.
      • 
        //--
        ObjectDelete(CI,cstar);
        ObjectDelete(CI,artop);
        ObjectDelete(CI,arbot);
        //--
        
    • Creating Panel Buttons:
      • The function then creates three buttons:
        • cstar Button:
          • This button is created with the label "Open Panel Indicator" and is positioned at the top-right corner of the chart.
          • 
            CreateButtonClick(CI,cstar,20,20,"Wingdings",13,BORDER_FLAT,cstar,clrWhite,clrWhite,TColor,CORNER_RIGHT_UPPER,25,40,true,"Open Panel Indicator");
            
        • artop Button:
          • This button is created with the label "Change Panel to Top" and is positioned at the top-right corner of the chart.
          • 
            CreateButtonClick(CI,artop,18,18,"Wingdings",11,BORDER_FLAT,artop,clrWhite,clrWhite,clrGreen,CORNER_RIGHT_UPPER,24,20,true,"Change Panel to Top");
            
        • arbot Button:
          • This button is created with the label "Change Panel to Bottom" and is positioned at the top-right corner of the chart.
          • 
            CreateButtonClick(CI,arbot,18,18,"Wingdings",11,BORDER_FLAT,arbot,clrWhite,clrWhite,clrGreen,CORNER_RIGHT_UPPER,24,62,true,"Change Panel to Bottom");
            
    • Redrawing the Chart:
      • The function calls ChartRedraw(CI) to refresh the chart and display the newly created buttons.
      • 
        ChartRedraw(CI);
        

Function DisplayButtonClick(string actreq):

This function handles the creation and display of various buttons on the chart based on the action requested.

  • Code Explanation of the DisplayButtonClick Function:
    • Conditional Button Creation:
      • The function uses a series of if statements to determine which button to create based on the actreq parameter:
        • cstar Button:
          • This button is created with the label "Open Panel Indicator" and is positioned at the top-right corner of the chart.
          • 
            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");
            
        • artop Button:
          • This button is created with the label "Change Panel to Top" and is positioned at the top-right corner of the chart.
          • 
            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");
            
        • arbot Button:
          • This button is created with the label "Change Panel to Bottom" and is positioned at the top-right corner of the chart.
          • 
            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");
            
        • x Button:
      • arbot Button:
        • This button is created with the label "Close Panel" and is positioned based on the indicator's settings.
        • 
          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");
          
    • Redrawing the Chart:
      • The function calls ChartRedraw(CI) to refresh the chart and display the newly created buttons.
      • 
        ChartRedraw(CI);
        

Function DeletedSPMObject():

This function is responsible for deleting all objects related to the SPM_MTF indicator from the chart.

  • Code Explanation of the DeletedSPMObject Function:
    • Loop Through Objects:
      • The function starts by looping through all objects on the chart in reverse order.
      • 
        string name;
        for(int i=ObjectsTotal(CI,-1,-1)-1; i>=0; i--)
          {
            name=ObjectName(CI,i,-1,-1);
        
    • Delete Matching Objects:
      • If the object name contains the ObjName prefix, it is deleted.
      • 
        if(StringFind(name,ObjName,0)>-1)
           ObjectDelete(CI,name);
        
    • Delete Timeframe Specific Objects:
      • Within the outer loop, the function also checks for objects related to specific timeframes and deletes them if found.
      • 
        for(int x=0; x<tfxar; x++)
          {
           if(StringFind(name,TFSc[x],0)>-1)
              ObjectDelete(CI,name);
          }
        
    • Delete Common Objects:
      • Finally, the function deletes common panel buttons ("X",cstar,artop,arbot).
      • 
        //--
        ObjectDelete(CI,"X");
        ObjectDelete(CI,cstar);
        ObjectDelete(CI,artop);
        ObjectDelete(CI,arbot);
        

Function strTF(ENUM_TIMEFRAMES period):

This function returns the string representation of the given timeframe period.

  • Code Explanation of the strTF Function:
    • Variable Initialization:
      • The function starts by declaring a string variable intf to store the string representation of the timeframe.
      • 
        string intf="";
        
    • Switch Statement for Timeframe Conversion:
      • The function uses a switch statement to assign the appropriate string value to intf based on the period parameter.
      • 
              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 the Timeframe String:
      • The function returns the string representation of the given timeframe.
      • 
        return(intf);
        

Function getUninitReasonText(int reasonCode):

This function returns a text description of the reason why the expert advisor (EA) was uninitialized based on the provided reason code.

  • Code Explanation of the getUninitReasonText Function:
    • Variable Initialization:
      • The function starts by declaring a string variable text to store the reason description.
      • 
        string text="";
        
    • Switch Statement for Reason Conversion:
      • The function uses a switch statement to assign the appropriate description to text based on the reasonCode parameter.
      • 
              //--
              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 the Reason Text:
      • The function returns the text description of the uninitialization reason.
      • 
        return text;
        

Function WS(int width):

This function calculates the width scaling factor for wide buttons based on the screen's DPI.

  • Code Explanation of the WS Function:
    • Variable Initialization:
      • The function starts by declaring two integer variables res and reswidth to store the result and intermediate width respectively.
      • 
        int res=0;
        int reswidth=0;
        
    • Scaling Factor Calculation:
      • The scaling factor is calculated using the screen's DPI obtained from TerminalInfoInteger.
      • 
        //-- Calculating the scaling factor wide button on a screen
        int scale_factor=(TerminalInfoInteger(TERMINAL_SCREEN_DPI));
        
    • Width Calculation:
      • The function calculates the width in screen points for standard monitors with DPI=96 and scales it accordingly.
      • 
        //-- Use of the scaling factor
        reswidth=(width * scale_factor) / 96;
        res=(int)NormalizeDouble(reswidth*1.25,0);
        
    • Return the Scaled Width:
      • The function returns the scaled width value.
      • 
        return(res);
        

Function CreateButtonClick():

This function creates a clickable button on the chart with the specified properties.


//---
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()
//---
  • Code Explanation of the CreateButtonClick Function:
    • Button Creation:
      • The function starts by creating a button object on the chart using ObjectCreate.
      • 
        ObjectCreate(chartid,button_name,OBJ_BUTTON,0,0,0); // create button
        
    • Setting Button Properties:
      • The function sets various properties for the button, such as size, font, border, colors, position, and tooltip.
      • 
              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);
        
    • Redrawing the Chart:
      • The function calls ChartRedraw(chartid) to refresh the chart and display the newly created button.
      • 
        ChartRedraw(chartid);
        

Function CreateArrowLabel():

This function creates an arrow label on the chart with the specified properties.

  • Code Explanation of the CreateArrowLabel Function:
    • Deleting Existing Object:
      • The function starts by deleting any existing object with the same label name.
      • 
        ObjectDelete(chart_id,lable_name);
        
    • Creating the Label:
      • The function attempts to create a new label object. If the creation fails, it prints an error message and returns false.
      • 
        //--
        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);
          }
        //--
        
    • Setting Label Properties:
      • The function sets various properties for the label, such as text, font, size, color, position, and tooltip.
      • 
        //--
        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);
        //--
        
    • Returning Successful Execution:
      • The function returns true to indicate successful execution.
      • 
        //-- successful execution
        return(true);
        

Function CreateButtonTemplate():

This function creates a rectangular button on the chart with the specified properties.

  • Code Explanation of the CreateButtonTemplate Function:
    • Button Creation:
      • The function starts by creating a rectangle label object on the chart using ObjectCreate.
      • 
        ObjectCreate(chartid,obj_name,OBJ_RECTANGLE_LABEL,0,0,0); // create Rectangle Label
        
    • Setting Button Properties:
      • The function sets various properties for the button, such as size, style, width, border, colors, position, and visibility.
      • 
        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);
        
    • Redrawing the Chart:
      • The function calls ChartRedraw(chartid) to refresh the chart and display the newly created button.
      • 
        ChartRedraw(chartid);
        

Function OnInit():

This is the custom indicator initialization function that is executed when the indicator is first loaded onto the chart.

  • Code Explanation of the OnInit Function:
    • Indicator Buffers Mapping:
      • The function starts by calling the SPM_MTF_Config method of the "mi" object to configure the multi-timeframe settings.
      • 
        mi.SPM_MTF_Config();
        
    • SetIndexBuffer:
      • The buffer for the PowerMove indicator data is set.
      • 
        SetIndexBuffer(0,mi.PowerMove,INDICATOR_DATA);
        
    • PlotIndexSetString:
      • The label for the plot index is set to "Move."
      • 
        PlotIndexSetString(0,PLOT_LABEL,"Move");
        
    • IndicatorSetString:
      • The short name of the indicator is set using the indname property of the "mi" object.
      • 
        IndicatorSetString(INDICATOR_SHORTNAME,mi.indname);
        
    • IndicatorSetInteger:
      • The number of digits displayed for the indicator values is set to 2.
      • 
        IndicatorSetInteger(INDICATOR_DIGITS,2);
        
    • Return Initialization Success:
      • The function returns INIT_SUCCEEDED to indicate that the initialization was successful.
      • 
        return(INIT_SUCCEEDED);
        

Function OnDeinit(const int reason):

This is the custom indicator deinitialization function that is executed when the indicator is removed from the chart or the platform is closed.

  • Code Explanation of the OnDeinit Function:
    • Clearing Comments:
      • The function starts by clearing any comments on the chart.
      • 
        Comment("");
        
    • Printing Deinitialization Reason:
      • The function prints the reason for deinitialization by calling the getUninitReasonText method of the "mi" object.
      • 
        Print(mi.getUninitReasonText(reason));
        
    • Deleting Indicator Objects:
      • The function calls DeletedSPMObject to delete all objects related to the SPM_MTF indicator from the chart.
      • 
        mi.DeletedSPMObject();
        
    • Redrawing the Chart:
      • The function calls ChartRedraw to refresh the chart and reflect the changes.
      • 
        ChartRedraw(mi.CI);
        
    • Returning from the Function:
      • The function ends with a return statement.
      • 
        return;
        

Function OnCalculate():

This function is executed on every new price tick and handles the main calculations for the indicator.

  • Code Explanation of the OnCalculate Function:
    • Resetting Last Error:
      • The function starts by resetting the last error using ResetLastError.
      • 
        ResetLastError();
        
    • Calculating the Limit:
      • The function calculates the limit for the number of bars to process.
      • 
        //--
        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;
        //--
        
    • Calculating Strength:
      • The function calls CalculationStrength to calculate the strength of price movement.
      • 
        mi.CalculationStrength(limit);
        
    • Alert Logic:
      • If alerts are enabled, the function checks for changes in price movement direction and triggers alerts accordingly.
      • 
        //--
        mi.CalculationStrength(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 price 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 price movement appears to be Down.";
                mi.Do_Alerts(AlertTxt);
                mi.prvAlert=mi.curAlert;
                mi.prvmin=mi.curmin;
              }
          }
        //--
        
    • Drawing Objects:
      • If the display flag is true, the function calls DrawSPMObject to redraw the indicator objects on the chart.
      • 
        if(mi.display)
          mi.DrawSPMObject();
        
    • Returning the Total Number of Rates:
      • The function returns the total number of rates for the next call.
      • 
        return(rates_total);
        

Function OnChartEvent():

This function handles chart events, particularly button clicks, to manage the indicator's display and settings.

  • Code Explanation of the OnChartEvent Function:
    • Resetting Last Error:
      • The function starts by resetting the last error using ResetLastError.
      • 
        ResetLastError();
        
    • Handling Object Click Events:
      • The function checks if the event ID is CHARTEVENT_OBJECT_CLICK and proceeds to handle specific button clicks based on sparam.
      • 
        if(id==CHARTEVENT_OBJECT_CLICK) {...}
        
    • Handling "X" Button Click:
      • If the "X" button is clicked, the function deletes the SPM objects, unpresses the button, sets the display flag to false, and displays the panelbutton.
      • 
        //--- if "X" button is click
        if(sparam=="X")
          {
           mi.DeletedSPMObject();
           //--- 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();
          }
        
    • Handling "cstar" Button Click:
      • If the "cstar" button is clicked, the function deletes the SPM objects, displays the panel button, unpresses the button, sets the display flag to true, and draws the SPM object.
      • 
        //--- if "cstar" button is click
        if(sparam==mi.cstar)
          {
           mi.DeletedSPMObject();
           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.DrawSPMObject();
           //--
           ChartRedraw(mi.CI);
          }
        
    • Handling "artop" Button Click:
      • If the "artop" button is clicked, the function deletes the SPM objects, displays the panel button, unpresses the button, sets the display flag to true, changes the panel position to the top, and draws the SPM object.
      • 
        //--- if "artop" button is click
        if(sparam==mi.artop)
          {
           mi.DeletedSPMObject();
           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.DrawSPMObject();
           //--
           ChartRedraw(mi.CI);
          }
        
    • Handling "arbot" Button Click:
      • If the "arbot" button is clicked, the function deletes the SPM objects, displays the panel button, unpresses the button, sets the display flag to true, changes the panel position to the bottom, and draws the SPM object.
      • 
        //--- if "arbot" button is click
        if(sparam==mi.arbot)
          {
           mi.DeletedSPMObject();
           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.DrawSPMObject();
           //--
           ChartRedraw(mi.CI);
          }
        
    • Handling Timeframe Button Clicks:
      • If any of the timeframe buttons are clicked, the function changes the chart symbol and timeframe accordingly.
      • 
              //--- 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]);
                }
              //--
             }
        

The Strength of Price Movement Multi-Timeframe (SPM_MTF) indicator is designed for MetaTrader 5 (MT5). It calculates the strength of price movement as a percentage on each timeframe bar, assisting traders in analyzing market trends and making informed trading decisions.

Key Functions of the SPM_MTF Indicator:

  • 1. Displays Multiple Timeframes: This feature allows traders to view various timeframes on a single chart, enhancing their ability to analyze marketmovements effectively.
  • 2. Helps Identify Market Trends: By viewing the indicator across multiple timeframes, traders can identify both larger and smaller market trends more easily.
  • 3. Supports Trading Decisions: The indicator assists traders in making informed decisions on entering or exiting trades based on observed market conditions across different timeframes.

Detailed Breakdown of Functions:

  • 1. SPM_MTF_Config(): Configures the multi-timeframe settings and prepares the indicator for use.
  • 2. PositionCore(): Sets up core position settings for the indicator elements on the chart.
  • 3. DrawSPMObject(): Draws various elements of the indicator on the chart.
  • 4. PanelPosChange(int inpos): Changes the panel position based on the input position value.
  • 5. UpdatePrice(ENUM_TIMEFRAMES xtf): Updates price data arrays for a given timeframe.
  • 6. RefreshPrice(ENUM_TIMEFRAMES xtf, int bars): Refreshes price data for a given timeframe and a specified number of bars.
  • 7. PriceStrengthScan(const ENUM_TIMEFRAMES stf, int shift): Calculates the direction of the price bar in percentage terms.
  • 8. CalculationStrength(int barCnt): Scans the direction of price movement for each timeframe and calculates the strength of price movement.
  • 9. ThisTime(const int reqmode): Retrieves current time information based on the requested mode
  • 10. Do_Alerts(string msg): Generates various types of alerts (print, alert, email, and notification) based on the provided message.
  • 11. FontsModel(int mode): Returns the font name based on the input mode.
  • 12. ChangeChartSymbol(string tf_name, ENUM_TIMEFRAMES stf): Changes the chart symbol and timeframe based on the input parameters.
  • 13. DisplayPanelButton(): Displays panel buttons on the chart.
  • 14. DisplayButtonClick(string actreq): Handles the creation and display of various buttons on the chart.
  • 15. DeletedSPMObject(): Deletes all objects related to the SPM_MTF indicator from the chart.
  • 16. strTF(ENUM_TIMEFRAMES period): Converts timeframe enumerations to string representations.
  • 17. getUninitReasonText(int reasonCode): Provides descriptive reasons for the uninitialization of the indicator (program) based on the given reason code.
  • 18. WS(int width): Calculates the width scaling factor for buttons based on screen DPI.
  • 19. CreateButtonClick(): Creates a clickable button on the chart with specified properties.
  • 20. CreateArrowLabel(): Creates an arrow label on the chart with specified properties.
  • 21. CreateButtonTemplate(): Creates a rectangular button on the chart with specified properties.

Main Functions of the Program:

  • OnInit(): Initializes the custom indicator, configuring indicator buffers and setting indicator properties.
  • OnDeinit(const int reason): Deinitializes the custom indicator, cleans up indicator objects, and prints the reason for deinitialization.
  • OnCalculate(): Handles the main calculations for the indicator on every price tick, including scanning price strength and generating alerts.
  • OnChartEvent(): Handles chart events, particularly button clicks, to manage the indicator's display and settings.

Summary:

The SPM_MTF indicator provides traders with a powerful tool to analyze price movements across multiple timeframes, helping them to identify market trends and make better trading decisions. The program includes various functions to configure, update, and display the indicator's elements, ensuring an enhanced trading experience.

We hope that this article and the SPM_MTF or Strength of Price Movement Multi Timeframe indicator for MT5 program will be useful for traders in learning and generating new ideas, which can ultimately make money from forex trading.

Thanks for reading this article.

Please download the SPM_MTF Indicator: Strength of Price Movement Indicator

If you are subscribed to my YouTube Channel, and would like to receive the source program of this article, please send a request via the Contact Us form page, and I will send it to your email, source code: Strength of Price Movement Indicator

Don't forget to stop by and subscribe to Forex Home Experts YouTube Channel:

YouTube Channel: @ForexHomeExperts

YouTube Playlist: @ForexHomeExperts YouTube Playlist

Friday, January 3, 2025

Introduction of Multi-Currency Expert Advisor.

While this article focuses on the Three Moving Average Crossover, it is important to note that the implementation of a multi-currency expert advisor follows the same principles and templates as outlined in my previous articles. For a detailed guide on creating a multi-currency expert advisor, you can refer to the following resources:

How to create a simple Multi-Currency Expert Advisor using MQL5 with Zigzag and RSI Indicators Signal: This article provides a comprehensive guide on building a multi-currency expert advisor using MQL5, leveraging Zigzag and RSI indicators for signal generation. The step-by-step approach ensures you can easily adapt these techniques for various trading strategies.

How to create a FiboPivotCandleBar Expert Advisor that trades Multi-Currency for MT5: In this article, I explain how to create a multi-currency expert advisor using the FiboPivotCandleBar indicator. The focus is on integrating the indicator into your trading strategy and automating the process for multiple currency pairs.

A Comprehensive Guide to FiboPivotCandleBar: Functions and Performance of the Expert Advisor for MT5: This comprehensive guide dives into the functions and performance of the FiboPivotCandleBar expert advisor, offering insights into its effectiveness and how to optimize it for multi-currency trading.

By following the principles and templates provided in these articles, we can effectively create and implement a multi-currency expert advisor that incorporates the Three Moving Average Crossover strategy.

GBPUSDH1_strategy_MA_Crossover

Introduction the Three Moving Average Crossover Strategy in Forex Trading

What is a moving average?

  • "A moving average is a simple technical analysis tool that helps smooth out pricedata by creating a constantly updated average price. It is calculated by adding up a certain number of closing prices and then dividing this total by that number."
  • Moving averages are a type of technical analysis tool that smooths out price data to create a trend-following indicator. There are four main types of moving averages:
    • 1. Simple Moving Average (SMA)
      • The Simple Moving Average (SMA) is the most basic type of moving average. It is calculated by adding up the closing prices of an asset over a specific number of periods and then dividing the total by that number of periods. The SMA gives equal weight to each price point within the chosen period.
      • Formula: SMA = (P1 + P2 + ... + Pn) / n. Where P represents the closing prices and n is the number of periods. Where 𝑃 represents the closing prices and 𝑛 is the number of periods.
      • Usage: The SMA is commonly used to identify trend directions and potential support and resistance levels. It is straightforward and easy to understand, making it popular among traders.
    • 2. Exponential Moving Average (EMA)
      • The Exponential Moving Average (EMA) gives more weight to recent prices, making it more responsive to new information. Unlike the SMA, the EMA reacts more quickly to price changes because it applies a smoothing factor to give more importance to recent data points.
      • Formula: EMA = [(Pt - EMAy) / (k + 1)] + EMAy. Where Pt is the current price, EMAy is the previous EMA value, and k is the smoothing factor which depends on the number of periods.
      • Usage: The EMA is preferred by traders who want to capture trends faster and more accurately. It is useful for identifying short-term trading opportunities and trend reversals.
    • 3. Smoothed Moving Average (SMMA)
      • The Smoothed Moving Average (SMMA) is a combination of the SMA and the EMA. It provides a smoother line by reducing the noise from random price fluctuations. The SMMA calculates an average similar to the SMA but applies a smoothing factor, which decreases the influence of past prices gradually.
      • Formula: SMMA = [(SMMAprevious * (n - 1)) + Pt] / n. Where SMMAprevious is the previous smoothed average, Pt is the current price, and n is the number of periods.
      • Usage: The SMMA is ideal for traders looking for a smoother trend line that filters out volatility. It is often used in longer-term trend analysis.
    • 4. Linear Weighted Moving Average (LWMA)
      • The Linear Weighted Moving Average (LWMA) assigns more weight to recent prices while giving progressively less weight to older prices. This is achieved by multiplying each price point by a weight that decreases linearly from the most recent price to the oldest within the selected period.
      • Formula: LWMA = [Σ(Pi * i)] / [Σi]. Where Pi is the price at period i and n is the number of periods.
      • Usage: The LWMA is used by traders who want to place a stronger emphasis on recent price actions. It is useful for detecting short-term price movements and trends.

Why moving average crossovers are popular?

  • Moving average crossovers are a popular trading strategy because they provide clear signals to buy or sell an asset. When a short-term moving average crosses above a long-term moving average, it often signals a bullish trend, and viceversa.
  • In the world of forex trading, the Moving Average Crossover strategy is a popular and effective tool used by both beginners and experienced traders. This strategy involves the use of two or more different moving averages to identify potential buying and selling opportunities in the market. In this article, we will explore the basics of the Moving Average Crossover strategy, how it works, and tips for maximizing its effectiveness.

Introduction:

The Triple Moving Average Crossover strategy is a powerful tool used by forex traders to identify potential trading opportunities. By employing three moving averages with different periods, this strategy aims to increase the accuracy of signals and minimize the chances of false breakouts. In this article, we will delve into the details of this strategy, explaining how it works and how traders can use it to enhance their trading decisions.

Moving Averages Used

In the Triple Moving Average Crossover strategy, three moving averages are utilized:

  • Slow MA: 30-period Smoothed Moving Average (SMMA) using the median price. This moving average helps to identify the overall long-term trend.
  • Middle MA: 20-period Simple Moving Average (SMA) using the median price. This moving average acts as a filter to smooth out price fluctuations.
  • Fast MA: 2-period Simple Moving Average (SMA) using the typical price. This moving average responds quickly to price changes, providing early signals for potential trades.

Buy Signals

The strategy generates buy signals based on the interaction of the three moving averages:

  • 1. Signal Up 1: A buy signal is generated when the Fast MA (2-period) crosses above the Slow MA (30-period), and the Middle MA (20-period) is below the Slow MA. This indicates the potential start of an uptrend.
    Example: If the Fast MA (2) moves from below to above the Slow MA (30), and the Middle MA (20) remains below the Slow MA, it suggests a bullish signal.
  • 2. Signal Up 2: Another buy signal occurs when the Middle MA (20-period) crosses above the Slow MA (30-period), and the Fast MA (2-period) subsequently crosses above the Middle MA. This confirms the continuation of the uptrend.
    Example: If the Middle MA (20) is above the Slow MA (30) and the Fast MA (2) crosses from below to above the Middle MA (20), it indicates a strong bullish signal.

Sell Signals

Similarly, the strategy generates sell signals when the moving averages align in the opposite direction:

  • 1. Signal Down 1: A sell signal is generated when the Fast MA (2-period) crosses below the Slow MA (30-period), and the Middle MA (20-period) is above the Slow MA. This suggests the potential start of a downtrend.
    Example: If the Fast MA (2) moves from above to below the Slow MA (30), and the Middle MA (20) remains above the Slow MA, it signals a bearish trend.
  • 2. Signal Down 2: Another sell signal occurs when the Middle MA (20-period) crosses below the Slow MA (30-period), and the Fast MA (2-period) subsequently crosses below the Middle MA. This confirms the continuation of the downtrend.
    Example: If the Middle MA (20) is below the Slow MA (30) and the Fast MA (2) crosses from above to below the Middle MA (20), it indicates a strong bearish signal.

Let's break down the ExpMACross1_MCEA_Config function of ExpMACross1_MCEA Expert Advisor for MT5.

void MCEA::ExpMACross1_MCEA_Config(void)

This function configures the Moving Average Crossover strategy and configures various elements. It sets up the timeframes, moving averages, and various parameters for the trading strategy, such as handling symbol arrays, setting up the moving averages for different timeframes, and configuring risk management settings like stop-loss and take-profit levels.


//+------------------------------------------------------------------+
//| Expert Configuration                                             |
//+------------------------------------------------------------------+
void MCEA::ExpMACross1_MCEA_Config(void) 
  {
//---
    //--
    HandlingSymbolArrays(); // With this function we will handle all pairs that will be traded
    //--
    TFT05=PERIOD_M5;
    ENUM_TIMEFRAMES TFs[]={PERIOD_M5,PERIOD_M15,PERIOD_M30,PERIOD_H1,PERIOD_H2,PERIOD_H3,PERIOD_H4,PERIOD_H6,PERIOD_H8,PERIOD_H12,PERIOD_D1};
    int arTFs=ArraySize(TFs);
    //--
    for(int x=0; x<arTFs; x++) if(tfinuse==x) TFt=TFs[x]; // TF for calculation signal
    //--
    int SMMAL=30;
    int SMAMi=20;
    int SMAKc=2;
    //-- Indicators handle for all symbol
    //TesterHideIndicators(true);
    //--
    for(int x=0; x&lt;arrsymbx; x++)
      {
        hSMMAL[x]=iMA(DIRI[x],TFt,SMMAL,0,MODE_SMMA,PRICE_MEDIAN);  //-- Handle for the Slow MA indicator
        hSMAMi[x]=iMA(DIRI[x],TFt,SMAMi,0,MODE_SMA,PRICE_MEDIAN);   //-- Handle for the Middle MA indicator
        hSMAKc[x]=iMA(DIRI[x],TFt,SMAKc,0,MODE_SMA,PRICE_TYPICAL);  //-- Handle for the Fast MA indicator
        hPar05[x]=iSAR(DIRI[x],TFT05,SARstep,SARmaxi);              //-- Handle for the iSAR indicator for M5 Timeframe
        //--
      }
    //--
    minprofit=NormalizeDouble(TSmin/100.0,2);
    //--
    ALO=(int)mc_account.LimitOrders()>sall ? sall : (int)mc_account.LimitOrders();
    if(Close_by_Opps==No) 
      {
        if((int)mc_account.LimitOrders()>=(sall*2)) ALO=sall*2;
        else 
        ALO=(int)(mc_account.LimitOrders()/2);
      }
    //--
    LotPS=(double)ALO;
    maxSpread=maxsprd;
    if(MQLInfoInteger(MQL_TESTER))
      maxSpread=(int)SymbolInfoInteger(Symbol(),SYMBOL_SPREAD);
    //--
    valSL=TSval==0.0 ? 38.0 : TSval;
    valTP=TPval==0.0 ? 35.0 : TPval;
    minSL=TSmin==0.0 ? 5.0 :  TSmin;
    minTP=TPmin==0.0 ? 25.0 : TPmin;
    //--
    mc_trade.SetExpertMagicNumber(magicEA);
    mc_trade.SetDeviationInPoints(slip);
    mc_trade.SetMarginMode();
    Set_Time_Zone();
    //--
    return;
//---
  } //-end ExpMACross1_MCEA_Config()
//---------//
  • Indicator Handles for Each Pair
    • In this EA, indicator handles are created for each symbol (pair) that intend to trade. This setup allows the EA to calculate and use indicators specifically for each pair. Let's go through the setup step-by-step:
      • 1. Handling Symbol Arrays: This function initializes and handles all the symbols that will be traded. It ensures the EA has access to the required symbols.
      • 2. Indicator Handles Setup: For each symbol, the EA creates handles for the moving averages (Slow, Middle, Fast) and the SAR indicator. Here's how it's done:
        
        //--
        for(int x=0; x<arrsymbx; x++) 
          {
            //-- Handle for the Slow MA indicator
            hSMMAL[x]=iMA(DIRI[x],TFt,SMMAL,0,MODE_SMMA,PRICE_MEDIAN); 
            //-- Handle for the Middle MA indicator
            hSMAMi[x]=iMA(DIRI[x],TFt,SMAMi,0,MODE_SMA,PRICE_MEDIAN);
            //-- Handle for the Fast MA indicator
            hSMAKc[x]=iMA(DIRI[x],TFt,SMAKc,0,MODE_SMA,PRICE_TYPICAL);  
            //-- Handle for the iSAR indicator for M5 Timeframe
            hPar05[x]=iSAR(DIRI[x],TFT05,SARstep,SARmaxi);              
            //--
          }
        //--
        
      • 3. Explanation of Each Handle:
        • hSMMAL[x]: This handle is used to calculate the Slow Moving Average (SMMA) for each symbol. It uses a 30-period setting with the median price (PRICE_MEDIAN).
        • hSMAMi[x]: This handle calculates the Middle Moving Average (SMA) for each symbol. It uses a 20-period setting with the median price (PRICE_MEDIAN).
        • hSMAKc[x]: This handle calculates the Fast Moving Average (SMA) for each symbol. It uses a 2-period setting with the typical price (PRICE_TYPICAL).
        • hPar05[x]: This handle calculates the Parabolic SAR indicator for each symbol. It uses specific SAR step and maximum values and is set for the M5 timeframe.
      • 4. Using the Handles in the EA: These handles are used within the EA to perform calculations and make trading decisions based on the Moving Average Crossover strategy. The EA monitors the crossovers of these moving averages to generate buy or sell signals for each symbol.
        By setting up these indicator handles for each pair, the EA can accurately calculate the necessary values and generate trading signals for all the symbols in the multi-currency strategy, including XAUUSD and XAGUSD.

Let's break down the ExpertActionTrade function to understand its various components and their purposes.


void MCEA::ExpertActionTrade(void)
  {
//---
    //--Check Trading Terminal
    ResetLastError();
    //--
    if(!DisplayManualButton("M","C","R")) DisplayManualButton(); //-- Show the expert manual button panel
    //--
    if(trade_info_display==Yes) mc.TradeInfo(); //-- Displayed Trading Info on Chart
    //---
    if(!MQLInfoInteger(MQL_TRADE_ALLOWED) && mc.checktml==0) //-- Check whether MT5 Algorithmic trading is Allow or Prohibit
      {
        mc.Do_Alerts(Symbol(),"Trading Expert at "+Symbol()+" are NOT Allowed by Setting.");
        mc.checktml=1;  //-- Variable checktml is given a value of 1, so that the alert is only done once.
        return;
      }
    //--
    //---
    int mcsec=mc.ThisTime(mc.sec); 
    //--
    if(fmod((double)mcsec,5.0)==0) mc.ccur=mcsec;
    //--
    if(mc.ccur!=mc.psec)
      {
        string symbol;
        //-- Here we start with the rotation of the name of all symbol or pairs to be traded
        for(int x=0; x<mc.arrsymbx && !IsStopped(); x++) 
          {
            //--
            switch(trademode)
              {
                case SP:
                  {
                    if(mc.DIRI[x]!=Symbol()) continue;
                    symbol=Symbol();
                    break;
                  }
                case MP:
                  {
                    if(mc.DIRI[x]==Symbol()) symbol=Symbol();
                    else symbol=mc.DIRI[x];
                    break;
                  }
              }
            //--
            mc.CurrentSymbolSet(symbol);
            //--
            if(mc.TradingToday() && mc.Trade_session())
              {
                //--
                mc.OpOr[x]=mc.GetOpenPosition(symbol); //-- Get trading signals to open positions
                //--                                   //-- and store in the variable OpOr[x]
                if(mc.OpOr[x]==mc.Buy) //-- If variable OpOr[x] get result of GetOpenPosition(symbol) as "Buy" (value=1)
                  {
                    //--
                    mc.CheckOpenPMx(symbol);
                    //--
                    if(Close_by_Opps==Yes && mc.xos[x]>0) mc.CloseSellPositions(symbol);
                    //--
                    if(mc.xob[x]==0 && mc.xtto<mc.ALO && mc.IFNewBarsB(symbol)) {mc.OpenBuy(symbol); mc.PbarB[x]=mc.TbarB[x];}
                    else
                    if(mc.xtto>=mc.ALO)
                      {
                        //--
                        mc.Do_Alerts(symbol,"Maximum amount of open positions and active pending orders has reached"+
                                            "n the limit = "+string(mc.ALO)+" Orders ");
                        //--
                        mc.CheckOpenPMx(symbol);
                        //--
                        if(mc.xos[x]>0 && mc.profits[x]<-1.02 && mc.xob[x]==0) {mc.CloseSellPositions(symbol); mc.OpenBuy(symbol);}
                        else
                        mc.CloseAllProfit();
                      }
                  }
                if(mc.OpOr[x]==mc.Sell) //-- If variable OpOr[x] get result of GetOpenPosition(symbol) as "Sell" (value=-1)
                  {
                    //--
                    mc.CheckOpenPMx(symbol);
                    //--
                    if(Close_by_Opps==Yes && mc.xob[x]>0) mc.CloseBuyPositions(symbol);
                    //--
                    if(mc.xos[x]==0 && mc.xtto<mc.ALO && mc.IFNewBarsS(symbol)) {mc.OpenSell(symbol); mc.PbarS[x]=mc.TbarS[x];}
                    else
                    if(mc.xtto>=mc.ALO)
                      {
                        //--
                        mc.Do_Alerts(symbol,"Maximum amount of open positions and active pending orders has reached"+
                                            "n the limit = "+string(mc.ALO)+" Orders ");
                        //--
                        mc.CheckOpenPMx(symbol);
                        //--
                        if(mc.xob[x]>0 && mc.profitb[x]<-1.02 && mc.xos[x]==0) {mc.CloseBuyPositions(symbol); mc.OpenSell(symbol);}
                        else
                        mc.CloseAllProfit();
                      }
                  }
              }
            //--
            mc.CheckOpenPMx(symbol);
            //--
            if(mc.xtto>0)
              {
                //--
                if(SaveOnRev==Yes) //-- Close Trade and Save profit due to weak signal (Yes)
                  {
                    mc.CheckOpenPMx(symbol);
                    if(mc.profitb[x]>mc.minprofit && mc.xob[x]>0 && mc.GetCloseInWeakSignal(symbol,mc.Buy)==mc.Sell) 
                      {
                        mc.CloseBuyPositions(symbol); 
                        mc.Do_Alerts(symbol,"Close BUY order "+symbol+" to save profit due to weak signal.");
                      }
                    if(mc.profits[x]>mc.minprofit && mc.xos[x]>0 && mc.GetCloseInWeakSignal(symbol,mc.Sell)==mc.Buy)
                      {
                        mc.CloseSellPositions(symbol); 
                        mc.Do_Alerts(symbol,"Close SELL order "+symbol+" to save profit due to weak signal.");
                      }
                  }
                //--
                if(UseTCP==Yes) mc.CheckTargetCloseProfit();
                //--
                if(TrailingSL==Yes) mc.ModifyOrdersSL(symbol,trlby); //-- Use Trailing Stop Loss (Yes)
                if(TrailingTP==Yes) mc.ModifyOrdersTP(symbol);       //-- Use Trailing Take Profit (Yes)
                //--
              }
            //--
            mc.CheckOpenPMx(symbol);
            if(Close_by_Opps==No && (mc.xob[x]+mc.xos[x]>1))
              {
                mc.CheckProfitLoss(symbol);
                mc.Do_Alerts(symbol,"Close order due stop in loss.");
              }
            //--
            mc.CheckClose(symbol);
          }
        //--
        mc.psec=mc.ccur;
      }
    //--
    return;
//---
  } //-end ExpertActionTrade()
//---------//

ExpertActionTrade Function Explanation.

1. Check Trading Terminal:

  • Resets any previous errors to ensure fresh error checking.
  • Displays manual button panel for user interaction.
  • Displays trading info on the chart if enabled.

2. Check MT5 Algorithmic Trading Status:

  • Checks if algorithmic trading is allowed. If not, alerts the user and stops further execution.

3. Time Management:

  • Manages the timing of the function to ensure it runs at the correct intervals.

4. Symbol Rotation:

  • Rotates through all symbols (pairs) that will be traded. Handles single pair (SP) and multi-pair (MP) trading modes.

5. Trading Logic:

  • Checks if trading is allowed today and if the trading session is active.
  • Gets trading signals to open positions and executes buy/sell orders based on the signals.
  • Manages open positions, ensures that the maximum allowed positions are not exceeded, and handles closing positions based on profit or weak signals.

6. Risk and Trade Management:

  • Manages trailing stop loss and trailing take profit.
  • Ensures that weak signal positions are closed to save profit.
  • Checks and closes orders if they hit the stop-loss limit.

7. Final Checks and Cleanup:

  • Ensures all checks are made and closes positions if necessary.
  • Returns control back to the main program.

This function encapsulates the entire trading logic, from checking if trading is allowed, processing signals, executing trades, and managing risk. It operates on multiple symbols (pairs) and ensures that trading decisions are made based on the configured strategy and risk management rules.

Let's break down the GetOpenPosition function to understand its components and their purposes:


int MCEA::GetOpenPosition(const string symbol) // Signal Open Position 
  {
//---
    int ret=0;
    int rise=1,
        down=-1;
    //--
    int SMAC=SignalMACross1(symbol);
    int DirM=DirectionMove(symbol,TFt);
    //--
    if(SMAC==rise && DirM==rise) ret=rise;
    if(SMAC==down && DirM==down) ret=down;
    //--
    return(ret);
//---
  } //-end GetOpenPosition()
//---------//

GetOpenPosition Function Explanation

This function determines the signal for opening a position (buy or sell) based on the Moving Average Crossover strategy and the direction of the market.

1. Initialize Variables:

  • ret: This variable will store the result, indicating the type of position to open (buy or sell). It is initialized to 0.
  • rise and down: These constants represent the direction of the trade. rise is set to 1 (indicating a buy signal), and down is set to -1 (indicating a sell signal).

2. Get Signals:

  • SMAC: This variable stores the result of the SignalMACross1 function, which checks for moving average crossovers and returns a signal (rise or down).
  • DirM: This variable stores the result of the DirectionMove function, which determines the overall market direction for the given timeframe (TFt).

3. Determine Open Position Signal:

  • If both the moving average crossover signal (SMAC) and the market direction (DirM) indicate a rise (both equal to rise), the function sets ret to rise, indicating a buy signal.
  • If both SMAC and DirM indicate a downtrend (both equal to down), the function sets ret to down, indicating a sell signal.

4. Return the Result:

  • The function returns the value of ret, which indicates whether to open a buy position (1), a sell position (-1), or no position (0).

Summary

The GetOpenPosition function integrates the results from the moving average crossover signal and the market direction to generate a coherent trading signal. If both indicators agree on the direction (either up or down), the function returns the corresponding signal to open a buy or sell position. Otherwise, it returns 0, indicating no position should be opened.

Let's break down the SignalMACross1 function to understand how it determines the moving average crossover signals:


nt MCEA::SignalMACross1(const string symbol)
  {
//---
    int ret=0;
    int rise=1,
        down=-1;
    //-- 
    double BMAL[],
           BMAM[],
           BMAK[];
    //--
    ArrayResize(BMAL,arper,arper);
    ArrayResize(BMAM,arper,arper);
    ArrayResize(BMAK,arper,arper);
    //--
    int xm=PairsIdxArray(symbol);
    UpdatePrice(symbol,TFt);
    //--
    CopyBuffer(hSMMAL[xm],0,0,arper,BMAL);
    CopyBuffer(hSMAMi[xm],0,0,arper,BMAM);
    CopyBuffer(hSMAKc[xm],0,0,arper,BMAK);
    //--
    ArraySetAsSeries(BMAL,true);
    ArraySetAsSeries(BMAM,true);
    ArraySetAsSeries(BMAK,true);
    //--
    bool SigMAup1=(BMAK[1]<=BMAL[1] && BMAK[0]>BMAL[0] && BMAM[0]<BMAL[0]);
    bool SigMAdw1=(BMAK[1]>=BMAL[1] && BMAK[0]<BMAL[0] && BMAM[0]>BMAL[0]);
    //--
    bool SigMAup2=(BMAM[0]>BMAL[0] && BMAK[1]<=BMAM[1] && BMAK[0]>BMAM[0]);
    bool SigMAdw2=(BMAM[0]<BMAL[0] && BMAK[1]>=BMAM[1] && BMAK[0]<BMAM[0]);
    //--
    int PPMv=PricePercentMove(symbol,TFt,10.0,0);
    //--
    if((SigMAup1||SigMAup2) && PPMv==rise) ret=rise;
    if((SigMAdw1||SigMAdw2) && PPMv==down) ret=down;
    //--
    return(ret);
//---
  } //-end SignalMACross1()
//---------//

SignalMACross1 Function Explanation

This function checks for moving average crossovers and determines the trading signal based on these crossovers and the price movement.

  • 1. Initialize Variables:
    • ret: This variable will store the result, indicating the type of signal (buy or sell). It is initialized to 0.
    • rise and down: These constants represent the direction of the trade. rise is set to 1 (indicating a buy signal), and down is set to -1 (indicating a sell signal).
  • 2. Declare Buffers for Moving Averages:
    • BMAL, BMAM, BMAK: These arrays will store the values of the Slow, Middle, and Fast moving averages, respectively.
  • 3. Resize Arrays:
    • Resizes the arrays to accommodate the required number of periods (arper).
  • 4. Get Index and Update Prices:
    • xm: Gets the index of the symbol in the pairs array.
    • Updates the price data for the specified timeframe (TFt).
  • 5. Copy Indicator Buffers:
    • Copies the buffer values of the Slow, Middle, and Fast moving averages into the respective arrays.
  • 6. Set Arrays as Series:
    • Sets the arrays to work as series, where the most recent data is at index 0.
  • 7. Determine Crossover Signals:
    • Signal Up 1: Checks if the Fast MA crosses above the Slow MA and the Middle MA is below the Slow MA.
    • Signal Down 1: Checks if the Fast MA crosses below the Slow MA and the Middle MA is above the Slow MA.
    • Signal Up 2: Checks if the Middle MA is above the Slow MA and the Fast MA crosses above the Middle MA.
    • Signal Down 2: Checks if the Middle MA is below the Slow MA and the Fast MA crosses below the Middle MA.
  • 8. Check Price Movement:
    • PPMv: Calls the PricePercentMove function to check the price movement direction.
  • 9. Determine Final Signal:
    • If any of the "up" signals are true and the price movement indicates a rise, set ret to rise.
    • If any of the "down" signals are true and the price movement indicates a downtrend, set ret to down.
  • 10. Return the Result:
    • The function returns the value of ret, indicating whether to generate a buy signal (1), a sell signal (-1), or no signal (0).

Summary

The SignalMACross1 function checks for specific moving average crossovers and determines the trading signal based on these crossovers and the overall price movement. It ensures that the signals are consistent with the market direction before returning the appropriate trading signal.

Let's break down the PricePercentMove function to understand its components and their purposes:

PricePercentMove Function Explanation

  • 1. Initialize Variables:
    • ret: This variable will store the result, indicating the direction of the price movement. It is initialized to 0.
    • rise and down: These constants represent the direction of the trade. rise is set to 1 (indicating a price rise), and down is set to -1 (indicating a price fall).
  • 2. Setup for Calculation:
    • br: The number of bars (periods) to be considered, set to shift + 3.
    • hasil: A variable to store the calculated result of the price movement.
    • move: The threshold percentage move to consider for the direction.
  • 3. Update Prices:
    • Updates the price data for the specified symbol and timeframe.
  • 4. Declare and Resize Array for Close Prices:
    • CL: An array to store the close prices.
    • Resizes the array to the number of bars (br) and sets it as a series (most recent data at index 0).
  • 5. Copy Close Prices:
    • Copies the close prices into the array CL for the specified number of bars.
  • 6. Calculate Price Movement:
    • close_now: The most recent close price.
    • close_prev: The previous close price.
    • Calculates the percentage change in price between the two close prices. If either price is 0 or EMPTY_VALUE, sets hasil to 0.0. Otherwise, normalizes the percentage change and adjusts it to be a more readable percentage.
  • 7. Determine Direction:
    • If the percentage move hasil is greater than the threshold move, sets ret to rise.
    • If hasil is less than the negative of move, sets ret to down.
  • 8. Return the Result:
    • The function returns the value of ret, indicating whether the price has risen (1), fallen (-1), or remained within the threshold (0)

Summary

The PricePercentMove function calculates the percentage change in the closing prices of a symbol over a specified period and timeframe. It then determines whether the price has risen, fallen, or remained relatively unchanged based on a specified threshold percentage (pcm). This information is used to identify the direction of the price movement and generate appropriate trading signals.

Let's break down the DirectionMove function to understand how it determines the direction of the bar price movement:

DirectionMove Function Explanation

This function calculates the direction of the price movement of a specific bar in a given timeframe, and returns whether the price is rising or falling.

  • 1. Initialize Variables:
    • ret: This variable will store the result, indicating the direction of the price movement. It is initialized to 0.
    • rise and down: These constants represent the direction of the price movement. rise is set to 1 (indicating a price rise), and down is set to -1 (indicating a price fall).
  • 2. Calculate Pips:
    • Calls the Pips function to set the pip value for the symbol.
  • 3. Normalize Price Difference:
    • difud: Calculates the normalized price difference using mc_symbol.NormalizePrice and 1.5 * pip. This threshold is used to determine significant price movements.
  • 4. Update Prices:
    • Updates the price data for the specified symbol and timeframe.
  • 5. Determine Price Direction:
    • Checks if the closing price of the current bar (CLOSE[0]) is greater than the opening price plus the threshold (OPEN[0] + difud). If true, sets ret to rise.
    • Checks if the closing price of the current bar (CLOSE[0]) is less than the opening price minus the threshold (OPEN[0] - difud). If true, sets ret to down.
  • 6. Return the Result:
    • The function returns the value of ret, indicating whether the price has risen (1), fallen (-1), or remained within the threshold (0).

Summary

The DirectionMove function calculates the direction of the price movement for a specific bar in a given timeframe. It does this by comparing the closing price to the opening price, with a threshold to filter out insignificant movements. The function returns whether the price is rising, falling, or remains within a negligible range.

Let's break down the GetCloseInWeakSignal function to understand its components and how it determines whether to close a position based on weak signals:

GetCloseInWeakSignal Function Explanation

This function determines if a position should be closed based on weak signals from moving averages, direction, and price percent move.

  • 1. Initialize Variables:
    • ret: This variable will store the result, indicating whether to close the position. It is initialized to 0.
    • rise and down: These constants represent the direction. rise is set to 1, and down is set to -1.
    • bar: The number of bars to consider, set to 5.
    • x: The index of the symbol in the pairs array.
  • 2. Declare and Resize Array for Fast MA:
    • BMAK: An array to store the values of the Fast Moving Average.
    • Resizes the array to bar size and copies the buffer values of the Fast MA into the array.
  • 3. Set Array as Series:
    • Sets the array BMAK to work as a series, where the most recent data is at index 0.
  • 4. Determine Fast MA Direction:
    • BMKup: Checks if the Fast MA is moving up (current value is greater than the previous value).
    • BMKdw: Checks if the Fast MA is moving down (current value is less than the previous value).
  • 5. Get Direction and Price Percent Move:
    • DirM: Calls the DirectionMove function to get the direction of the bar price.
    • PPmv: Calls the PricePercentMove function to get the price percent move.
  • 6. Determine Close Signal:
    • If the existing position (exis) is a sell (down), and the direction (DirM), price percent move (PPmv), and Fast MA (BMKup) all indicate a rise, sets ret to rise.
    • If the existing position (exis) is a buy (rise), and the direction (DirM), price percent move (PPmv), and Fast MA (BMKdw) all indicate a fall, sets ret to down.
  • 7. Return the Result:
    • The function returns the value of ret, indicating whether to close the position (rise or fall) based on weak signals.

Summary

The GetCloseInWeakSignal function checks for weak signals based on the Fast Moving Average direction, bar price direction, and price percent move. If the weak signals indicate that the market is moving against the existing position, it returns a signal to close the position.

Backtesting Period: December 2, 2024 - December 20, 2024

I have backtested this strategy from December 1, 2024, to December 20, 2024. I chose this period because, typically, December experiences highly volatile and even sideways market conditions. If a strategy is not robust, it is likely to incur losses during this time. The Expert Advisor (EA) I developed is a multi-currency EA, covering 28 currency pairs and 2 metal pairs (XAUUSD and XAGUSD).

The backtesting of the Triple Moving Average Crossover strategy was conducted from December 2, 2024, to December 20, 2024, across various currency pairs and metals. The following is a summary of the key performance metrics:

Visual Analysis:

ExpMACross1_MCEA_ST01
ExpMACross1_MCEA_ST02
ExpMACross1_MCEA_ST03
ExpMACross1_MCEA_ST04
ReportTester-88118251_ExpMAx1_MCEA_02012025_01
ReportTester-88118251_ExpMAx1_MCEA_02012025_01-holding
ReportTester-88118251_ExpMAx1_MCEA_02012025_01-hst
ReportTester-88118251_ExpMAx1_MCEA_02012025_01-mfemae

The backtesting results provide a comprehensive summary of the performance of the Triple Moving Average Crossover strategy:

  • Balance and Equity Lines: The balance (in blue) and equity (in green) lines show the overall performance, with the balance line generally trending upwards with fluctuations. The equity line exhibits more volatility but also an upward trend.
  • Deposit Load: The bar chart below the main graph indicates varying levels of deposit load throughout the period, reflecting the percentage of the deposit being utilized.
  • Drawdown: The maximum balance drawdown was 2.15%, reflecting a relatively low risk level. The equity drawdown was slightly higher at 3.51%.

These visual elements provide a clear representation of the strategy's performance, illustrating both profit opportunities and risk levels.

Detailed Analysis:

  • Strategy Performance
    • The strategy yielded a total profit of $675.43 with a profit factor of 1.38, indicating that the total profits exceeded the total losses.
  • Key Metrics
    • Expectancy: The expectancy of 2.22 suggests that, on average, each trade generated a profit of 2.22 units.
    • Maximum Drawdown: The maximum drawdown of 3.35% indicates a relatively low risk level.
    • Win Rates: The win rates for both long and short positions were above 50%, suggesting effectiveness in identifying profitable opportunities.
  • Breakdown of Metrics
    • Profit Factor: A value of 1.38 indicates that the total profit is greater than the total loss, which is a positive indicator.
    • Expectancy: A value of 2.22 indicates that on average, each trade generates a profit of 2.22, which is also a good indicator.
    • Win Rate: The combination of win rates for short trades (58.74%) and long trades (67.70%) shows that this strategy is quite effective in identifying profitable opportunities.
    • Largest Profit/Loss: The existence of trades with very large profits and losses indicates that this strategy has a high potential for volatility.
    • Average Consecutive Wins/Losses: The average number of consecutive wins and losses provides an overview of the consistency of the strategy.
  • A Closer Look at the Results
    • Profitability: The strategy has demonstrated a consistent ability to generate profits, with the majority of trades resulting in positive returns.
    • Potential for Large Profits: The presence of trades with exceptionally large profits indicates the strategy's potential for significant gains.
    • Risk Exposure: While most losses were relatively small, the occurrence of a few large losses suggests that the strategy is not entirely risk-free.

Recommendation:

Based on the available data, this strategy has shown fairly good performance during the backtesting period. However, several points need to be considered:

  • Short Backtesting Period: The backtesting period is only one month, which may not be sufficient to provide a comprehensive view of long-term performance.
    • Recommendation: Extend the backtesting period to see if the results remain consistent over a longer duration.
  • Market Volatility: The December 2024 period may have unique market characteristics (e.g., end-of-year, long holidays) that may not reflect general market conditions.
    • Recommendation: Perform backtesting during different periods to account for varying market conditions.
  • Parameter Variation: Try adjusting the indicator parameters to find the optimal combination.
    • Recommendation: Experiment with different parameter settings to identify the most effective configuration.

Conclusion of Backtesting Results: The backtesting results provide a promising initial indication of the strategy'seffectiveness. However, further analysis and testing are necessary to confirm its long-termviability.

Strategy Conclusion:

The Triple Moving Average Crossover strategy is a robust method for forex traders looking to capitalize on market trends. By combining three moving averages with different periods, traders can generate more accurate signals and reduce the likelihood of false breakouts. This strategy not only helps in identifying potential entry and exit points but also provides a clearer picture of the market's overall trend. Remember to combine this strategy with proper risk management techniques to optimize trading performance.

We hope that this article and the MQL5 Multi-Currency Expert Advisor program will be useful for traders in learning and generating new ideas, which can ultimately make money from forex trading.

Thanks for reading this article.

Please download the Expert Advisor ExpMACross1_MCEA

If you are subscribed to my YouTube Channel, and would like to receive the source program of this article, please send a request via the Contact Us form page, and I will send it to your email, source code: ExpMACross1_MCEA

Don't forget to stop by and subscribe to Forex Home Experts YouTube Channel:

YouTube Channel: @ForexHomeExperts

YouTube Playlist: @ForexHomeExperts YouTube Playlist

Featured Post

How to create a simple Multi-Currency Expert Advisor using MQL5 with Zigzag and RSI Indicators Signal

Introduction The Expert Advisor discussed in this article is a multi-currency trading robot that uses the Zigzag and RSI indicators. It fol...