Showing posts with label Forex Dashboard. Show all posts
Showing posts with label Forex Dashboard. Show all posts

Tuesday, February 17, 2026

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

The Genesis of GMM

The idea to create the GMM Master Dashboard LR Magnet was born from a simple yet ambitious goal: I wanted a standalone price monitoring system that functions independently from the MetaTrader 5 (MT5) terminal interface. While I was aware that MQL5 offers a powerful bridge to integrate Python, I faced a significant hurdle—I wasn't a Python expert.

Driven by the desire to bring this vision to life, I turned to Google AI Gemini for collaboration. I named this project GMM, which stands for "Gemini Mantap Mentong!". This name is a tribute to my roots in Makassar (one of the largest ethnic groups in South Sulawesi, Indonesia). In our local dialect, "Mantap Mentong" translates to "Sungguh-sungguh Mantap" in Indonesian, or in English, it conveys a sense of being "Really Steadfast" or "Truly Excellent."

GMM_MASTER_DASHBOARD_LR_MAGNET_01 Figure 1: Main Dashboard: BASE PAIRS: MAJOR - JPY CROSS AND METALS
GMM_MASTER_DASHBOARD_LR_MAGNET_02 Figure 2: Pair list dashboard: FULL 30 PAIRS LOGIC LIST

Merging Legend with Modern AI

With Gemini's assistance, I successfully translated years of manual observation into a sophisticated automated dashboard. The system integrates two core pillars:

  • 1. Linear Regression Degrees: This is based on the LRDegrees Indicator for MT5, a tool I have shared with the global trading community on the MQL5 CodeBase since 2017.
  • 2. The 2014 Magnet Theory: This logic is derived from my continuous market observations since 2014. I discovered a recurring price behavior: whenever a bar's open price is below the EMA2 (Weighted Close), the price is inevitably "magnetized" back up towards the EMA2. Conversely, if it opens above, it is drawn back down. This phenomenon occurs consistently across all market conditions, whether in a bullish or bearish trend.

Today, the GMM Master Dashboard stands as a "Really Steadfast" decision support system, bridging nearly a decade of trading wisdom with the cutting-edge power of Artificial Intelligence.

Convert MQL5 Code to Python

In the GMM Master Dashboard program, I converted several functions from MQL5 to Python.

ExponentialMA

You can see the ExponentialMA program in MQL5 in the MQL5/Include folder in the MovingAverages.mqh file


//+------------------------------------------------------------------+
//|  Exponential moving average on price array                       |
//+------------------------------------------------------------------+
int ExponentialMAOnBuffer(const int rates_total,const int prev_calculated,const int begin,const int period,const double& price[],double& buffer[])
  {
//--- check period
   if(period<=1 || period>(rates_total-begin))
      return(0);
//--- save and clear 'as_series' flags
   bool as_series_price=ArrayGetAsSeries(price);
   bool as_series_buffer=ArrayGetAsSeries(buffer);

   ArraySetAsSeries(price,false);
   ArraySetAsSeries(buffer,false);
//--- calculate start position
   int    start_position;
   double smooth_factor=2.0/(1.0+period);

   if(prev_calculated==0)  // first calculation or number of bars was changed
     {
      //--- set empty value for first bars
      for(int i=0; i<begin; i++)
         buffer[i]=0.0;
      //--- calculate first visible value
      start_position=period+begin;
      buffer[begin] =price[begin];

      for(int i=begin+1; i<start_position; i++)
         buffer[i]=price[i]*smooth_factor+buffer[i-1]*(1.0-smooth_factor);
     }
   else
      start_position=prev_calculated-1;
//--- main loop
   for(int i=start_position; i<rates_total; i++)
      buffer[i]=price[i]*smooth_factor+buffer[i-1]*(1.0-smooth_factor);
//--- restore as_series flags
   ArraySetAsSeries(price,as_series_price);
   ArraySetAsSeries(buffer,as_series_buffer);
//---
   return(rates_total);
  }

Once converted to Python, ExponentialMA becomes very simple.


def GMM_ExponentialMA(price_array, period):
    if period <= 0 or len(price_array) == 0: return price_array
    pr = 2.0 / (period + 1.0)
    result_ema = np.zeros_like(price_array)
    result_ema[0] = price_array[0] 
    for i in range(1, len(price_array)):
        result_ema[i] = (price_array[i] * pr) + (result_ema[i-1] * (1.0 - pr))
    return result_ema

Key Advantages of NumPy:

  • Vectorization: Operations run in optimized C, making them much faster than MQL5 loops.
  • Conciseness: Complex formulas become single-line expressions.
  • Integration: Easily integrates with pandas for further technical analysis.
  • Moving Average (Rolling Window): In MQL5, you use a for loop with a backward index. In Python, the most efficient way is to use the sliding_window_view function or a companion library like Pandas.

Linear Regression Degree

In the LRDegrees indicator for MT5, I created the following code for calculating linear regression and calculating the degree of linear regression position:


   //--
   for(j=0; j<barsCountLine2; j++)
     {
      suma+=MABuffers[j];
      sumab+=MABuffers[j]*j;
      sumb1+=j;
      sumb2+=j*j;
     }
   //--
   a=sumb2*barsCountLine2-sumb1*sumb1;
   b=(sumab*barsCountLine2-sumb1*suma)/a;
   c=(suma-sumb1*b)/barsCountLine2;
   //--
   //-- Linear regression MA trend 2
   for(l=0; l<barsCountLine2; l++) LRBuffers2[l]=c+b*l;
   //---
   for(int i=bar_count-1; i>=0; i--)
     {
       //--
       if(i==0)
         {
           switch(degress)
             {
               case 0:
                 {
                   cur_degrees=NormalizeDouble(270+(((LRBuffers2[0]-lmin)/(hmax-lmin))*180),2);
                   prev1_degrees=NormalizeDouble(270+(((LRBuffers2[1]-lmin)/(hmax-lmin))*180),2);
                   prev2_degrees=NormalizeDouble(270+(((LRBuffers2[2]-lmin)/(hmax-lmin))*180),2);
                   div1_degrees=prev1_degrees - prev2_degrees;
                   div0_degrees=cur_degrees - prev2_degrees;
                   break;
                 }
               //--
               case 1:
                 {
                   cur_degrees=NormalizeDouble(270+(((LRBuffers1[0]-lmin)/(hmax-lmin))*180),2);
                   prev1_degrees=NormalizeDouble(270+(((LRBuffers1[1]-lmin)/(hmax-lmin))*180),2);
                   prev2_degrees=NormalizeDouble(270+(((LRBuffers1[2]-lmin)/(hmax-lmin))*180),2);
                   div1_degrees=prev1_degrees - prev2_degrees;
                   div0_degrees=cur_degrees - prev2_degrees;
                   break;
                 }
             }
           //--
           if(cur_degrees>360.0) {cur_degrees=NormalizeDouble(cur_degrees-360.0,2);}
           if(cur_degrees==360.0) {cur_degrees=NormalizeDouble(0.0,2);}
           //- To give a value of 90.0 degrees to the indicator, when the price moves up very quickly and make a New Windows Price Max.
           if(cur_degrees==90.0) {cur_degrees=NormalizeDouble(90.0,2);}
           //- To give a value of 270.0 degrees to the indicator, when the price moves down very quickly and make a New Windows Price Min.
           if(cur_degrees==270.0) {cur_degrees=NormalizeDouble(270.0,2);}
           //--
           if(div0_degrees>div1_degrees) {dgrsUp=true; dgrsDn=false; DegreesUp[i]=cur_degrees; DegreesDn[i]=0.0;}
           if(div0_degrees<div1_degrees) {dgrsDn=true; dgrsUp=false; DegreesDn[i]=cur_degrees; DegreesUp[i]=0.0;}
           //---
           if((cur_degrees>=270.0 && cur_degrees<315.0)&&(dgrsDn==true)) {rndclr=stgBear; arrclr=stgBear; txtclr=txtrbl; posalert=11;}
           if((cur_degrees>=270.0 && cur_degrees<315.0)&&(dgrsUp==true)) {rndclr=stgBear; arrclr=stsBear; txtclr=txtrbl; posalert=12;}
           if((cur_degrees>=315.0 && cur_degrees<360.0)&&(dgrsDn==true)) {rndclr=stsBear; arrclr=stgBear; txtclr=txtblk; posalert=21;}
           if((cur_degrees>=315.0 && cur_degrees<360.0)&&(dgrsUp==true)) {rndclr=stsBear; arrclr=stsBull; txtclr=txtblk; posalert=23;}
           if((cur_degrees>=0.0 && cur_degrees<45.0)&&(dgrsUp==true)) {rndclr=stsBull; arrclr=stgBull; txtclr=txtblk; posalert=34;}
           if((cur_degrees>=0.0 && cur_degrees<45.0)&&(dgrsDn==true)) {rndclr=stsBull; arrclr=stsBear; txtclr=txtblk; posalert=32;}
           if((cur_degrees>=45.0 && cur_degrees<=90.0)&&(dgrsUp==true)) {rndclr=stgBull; arrclr=stgBull; txtclr=txtrbl; posalert=44;}
           if((cur_degrees>=45.0 && cur_degrees<=90.0)&&(dgrsDn==true)) {rndclr=stgBull; arrclr=stsBull; txtclr=txtrbl; posalert=43;}
           //---  

After converting into Python, the program looks like this:


def GMM_Get_Data(symbol, timeframe_id):
    rates = mt5.copy_rates_from_pos(symbol, timeframe_id, 0, 100)
    if rates is None or len(rates) < 10: return None
    
    popen, phigh, plow, pclose = np.array([r['open'] for r in rates]), np.array([r['high'] for r in rates]), np.array([r['low'] for r in rates]), np.array([r['close'] for r in rates])
    pweighted = (phigh + plow + pclose + pclose) / 4.0
    ema2_pm = GMM_ExponentialMA(pweighted, 2)
    
    y, j = ema2_pm[-3:], np.arange(3)
    suma, sumab, sumb1, sumb2 = np.sum(y), np.sum(y * j), np.sum(j), np.sum(j**2)
    denom = (sumb2 * 3) - (sumb1**2)
    b = ((sumab * 3) - (sumb1 * suma)) / denom
    c = (suma - (sumb1 * b)) / 3
    lr_buffers = c + (b * j)

    hmax, lmin = np.max(phigh), np.min(plow)
    cur_degrees = round(270 + ((lr_buffers[-1] - lmin) / (hmax - lmin)) * 180, 2)
    prev1_degrees = round(270 + ((lr_buffers[-2] - lmin) / (hmax - lmin)) * 180, 2)
    prev2_degrees = round(270 + ((lr_buffers[-3] - lmin) / (hmax - lmin)) * 180, 2)
    div1_degrees, div0_degrees = round(prev1_degrees - prev2_degrees, 2), round(cur_degrees - prev2_degrees, 2)

    if cur_degrees > 360.0: cur_degrees -= 360.0
    if cur_degrees == 360.0: cur_degrees = 0.0
    dgrs_up, dgrs_dn = div0_degrees > div1_degrees, div0_degrees < div1_degrees

    status_lr = "SIDEWAY"
    if (270.0 <= cur_degrees < 315.0 and dgrs_dn):   status_lr = "DN_Strong▼▼"
    elif (270.0 <= cur_degrees < 315.0 and dgrs_up): status_lr = "DN_Weakened▼"
    elif (315.0 <= cur_degrees < 360.0 and dgrs_dn): status_lr = "DN_Strengthened▼▼"
    elif (315.0 <= cur_degrees < 360.0 and dgrs_up): status_lr = "DN_Reversal▲"
    elif (0.0 <= cur_degrees < 45.0 and dgrs_up):    status_lr = "UP_Strengthened▲▲"
    elif (0.0 <= cur_degrees < 45.0 and dgrs_dn):    status_lr = "UP_Reversal▼"
    elif (45.0 <= cur_degrees <= 90.0 and dgrs_up):  status_lr = "UP_Strong▲▲"
    elif (45.0 <= cur_degrees <= 90.0 and dgrs_dn):  status_lr = "UP_Weakened▲"

Key Changes Explained:

  • np.arange(bars_count): Instantly creates a sequence array of numbers (0, 1, 2, etc.). This replaces the loop variables j and l. Element-wise Operations: In MQL5, you perform MABuffers[j]*j one at a time. In NumPy, y * j multiplies the entire array in a single hardware operation.
  • np.sum(): Replaces manual accumulators like suma += .... NumPy uses a CPU-optimized summation algorithm. No Second Loop: Filling LRBuffers2 is done directly with c + (b * j). If j is the array [0, 1, 2], the result is automatically the array [c + b * 0, c + b * 1, c + b * 2].

Note: Gemini AI says that "If you want more "Pythonic" results for linear regression, you can also use np.polyfit(j, y, 1), but the manual method above is the most accurate translation of your MQL5 logic."

Price Magnetism Theory

As I explained above, this logic is derived from my continuous market observations since 2014. I discovered a recurring price behavior: whenever a bar's open price is below the EMA2 (PRICE WEIGHTED), the price is always "magnetized" back up towards the EMA2. Conversely, if it opens above, it is drawn back down.

The most powerful feature of this system is the detection of Magnet Rejection. This occurs when the price attempts to breach the Magnet but fails, creating a high-conviction reversal signal:

  • ⚠️ DN-REJ-UP (Bullish Rejection): Occurs when the price OPEN below the Magnet, spikes above it (HIGH > EMA2), but closes back below the OPEN price (CLOSE < OPEN).
  • .
  • ⚠️ UP-REJ-DN (Bearish Rejection): Occurs when the price OPEN above the Magnet, dips below it (LOW < EMA2), but reverses to close above the OPEN price (CLOSE > OPEN).

The implementation of this logic, in a Python program would be:

  
    mid = ema2_pm[-1]
    O, H, L, C = popen[-1], phigh[-1], plow[-1], pclose[-1]
    magnet, mag_col = "FLAT", Fore.WHITE
    if O <= mid and C > mid: magnet = "🧲 UP"; mag_col = Fore.CYAN
    elif O >= mid and C < mid: magnet = "🧲 DN"; mag_col = Fore.RED
    elif O <= mid and H > mid and C < O: magnet = "⚠️ DN-REJ-UP"; mag_col = Fore.MAGENTA
    elif O >= mid and L < mid and C > O: magnet = "⚠️ UP-REJ-DN"; mag_col = Fore.GREEN  

The Core Logic: Linear Regression Meets Price Magnetism

In the world of professional trading, standard indicators often lag behind real-time price action. The GMM Master Dashboard was engineered to bridge this gap by combining advanced mathematics with a legendary price action discovery from 2014: The EMA2 Weighted Magnet. This system doesn't just track where the price is; it anticipates where the price is being "drawn" to.


if __name__ == "__main__":
    if not mt5.initialize(): quit()
    selected_tf_id, tf_name = GMM_Select_TF()
    
    try:
        while True:
            data_results = {m: GMM_Get_Data(m, selected_tf_id) for m in majors}
            os.system('cls' if os.name == 'nt' else 'clear')
            
            print(Fore.YELLOW + "="*65 + Style.RESET_ALL)
            print(Fore.YELLOW + f" GMM MASTER DASHBOARD LR MAGNET | TF: {tf_name} | FULL SCAN (30 PAIRS) " + Style.RESET_ALL)
            print(Fore.YELLOW + "="*65 + Style.RESET_ALL)
            print(f"{' SYMBOL':<11} | {' DEGREE':<8} | {'LR STATUS':<18} | {'PRICE MAGNET'}")
            print("-" * 65)
            
            for m in majors:
                res = data_results[m]
                if res:
                    print(f"{' '}{m:<10} | {res['deg']:>7.2f}° | {Fore.WHITE}{res['status']:<18}{Style.RESET_ALL} | {res['mag_col']}{res['magnet']}{Style.RESET_ALL}")
            
            print(Fore.YELLOW + "="*65 + Style.RESET_ALL)
            print(Fore.YELLOW + " GMM CORRELATION & ENTRY STRATEGY :" + Style.RESET_ALL)
            print("-" * 65)
            
            for l in logic_list:
                rA, rB = data_results[l["A"]], data_results[l["B"]]
                if rA and rB:
                    signal, col = "Wait...", Fore.WHITE
                    bull_status = ["UP_Strong▲▲", "UP_Strengthened▲▲", "DN_Reversal▲"]
                    bear_status = ["DN_Strong▼▼", "DN_Strengthened▼▼", "UP_Reversal▼"]
                    bull_magnet = ["🧲 UP", "⚠️ UP-REJ-DN"]
                    bear_magnet = ["🧲 DN", "⚠️ DN-REJ-UP"]

                    a_is_bull, a_is_bear = any(s == rA["status"] for s in bull_status), any(s == rA["status"] for s in bear_status)
                    b_is_bull, b_is_bear = any(s == rB["status"] for s in bull_status), any(s == rB["status"] for s in bear_status)
                    a_in_bull, a_in_bear = any(s == rA["magnet"] for s in bull_magnet), any(s == rA["magnet"] for s in bear_magnet)
                    b_in_bull, b_in_bear = any(s == rB["magnet"] for s in bull_magnet), any(s == rB["magnet"] for s in bear_magnet)

                    if l["Type"] == "self":
                        is_buy, is_sell = (a_is_bull and a_in_bull), (a_is_bear and a_in_bear)
                    elif l["Type"] == "kali":
                        is_buy, is_sell = ((a_is_bull and b_is_bull) and (a_in_bull and b_in_bull)), ((a_is_bear and b_is_bear) and (a_in_bear and b_in_bear))
                    else: # Type bagi
                        is_buy, is_sell = ((a_is_bull and b_is_bear) and (a_in_bull and b_in_bear)), ((a_is_bear and b_is_bull) and (a_in_bear and b_in_bull))

                   # --- GMM_ LOGIC ENTRY STRATEGY WITH MANTAP MENTONG COLORS ---
                    if is_buy:
                        if "🧲 UP" in rA["magnet"]:
                            signal, col = "🚀 STRONG BUY", Fore.GREEN     # Hijau Segar: Tren Sehat
                        elif "⚠️ UP-REJ-DN" in rA["magnet"]:
                            signal, col = "🔥 BUY NOW!", Fore.CYAN        # Biru Elektrik: Reversal Cepat (Favorit!)
                        else:
                            signal, col = "Wait...", Fore.WHITE
                            
                    elif is_sell:
                        if "🧲 DN" in rA["magnet"]:
                            signal, col = "🚨 STRONG SELL", Fore.RED      # Merah Berani: Konfirmasi Bearish
                        elif "⚠️ DN-REJ-UP" in rA["magnet"]:
                            signal, col = "🔥 SELL NOW!", Fore.MAGENTA    # Ungu Magenta: Rejection Tajam
                        else:
                            signal, col = "Wait...", Fore.WHITE
                            
                    else:
                        signal, col = "Wait...", Fore.WHITE
                    
                    print(f"{' '}{l['A']} x {l['B']} => {l['Cross']:<8} | {col}{signal}{Style.RESET_ALL}")

            print("-" * 65)
            print(Fore.WHITE + " GMM_ Info: 🔥 NOW! = Trend + Magnet Alignment (Best Entry)")
            print(" GMM_ Info: ⚠️ REJ = Strong Rejection at Magnet Median")
            print(" GMM_ Info: Press Ctrl+C to stop." + Style.RESET_ALL)
            time.sleep(20)

As you can see, the dashboard operates on two sophisticated analytical layers. First, it calculates the LRDegrees (Linear Regression Degrees) to identify the dominant market trend with mathematical precision. Second, it applies the 2014 Magnet Logic, which utilizes the EMA2 of the WEIGHTED PRICE = ((HIGH + LOW + CLOSE + CLOSE) / 4). This act as a Price Magnet, identifying the equilibrium point that the market constantly seeks to revisit.

The Power of Boolean Aggregation

The decision engine using Python's any() function combined with Dual-Condition Mapping.

  • 1. Status Mapping: Variables like a_is_bull scan for specific trend phases (Strong, Strengthening, or Reversal) on the Linear Regression Degrees indicator (LRDegress)
  • 2. Magnet Mapping: Variables like a_in_bull confirm the price proximity to the EMA2 Weighted Magnet.

By requiring both conditions to be True, the GMM Dashboard effectively filters out high-risk trades, ensuring that every "STRONG BUY" or "NOW!" signal is backed by both mathematical trend strength and physical price alignment. This is the secret behind the dashboard's high-precision entries.

Multi-Pair Correlation: The "Entry Now" Signal

To ensure maximum accuracy, the dashboard monitors 30 currency pairs simultaneously through complex correlation matrices (Multiplication and Division types). An "🔥 ENTRY NOW!" signal is only triggered when the market trend (LRDegrees), the correlation alignment, and the Magnet Rejection occur in perfect synchronization. This multi-layered filter is designed to protect capital while capturing the most explosive market moves.

A Professional Tool for Modern Traders

The GMM_ Master Dashboard is more than just a script; it is a comprehensive trading station. By integrating the 2014 Discovery Logic with modern Python-based automation, we provide traders with a clear, color-coded roadmap to navigate the complexities of the Forex market with confidence.

How to Install and Activate the GMM Master Dashboard

To experience the power of the 2014 Magnet Discovery and Linear Regression Degrees indicator in real-time, you must set up the Python environment on your local machine. Follow these steps to get started:

  • 1. Prerequisites and Installation:
    • Install Python: Ensure you have the latest version of Python installed from python.org.
    • MetaTrader 5 Terminal: You must have the MT5 Desktop terminal installed and logged into your trading account.
    • Required Libraries: Open your terminal or CMD and install the necessary dependencies using the following command: pip install MetaTrader5 numpy colorama
  • 2. Once your environment is set up, launching the dashboard is incredibly simple. You don't need to be a coding expert to keep it running.
  • 3. Direct Launch (Double-Click Method):
    • Locate your GMM_MASTER_DASHBOARD_LR_MAGNET.py file in its folder.
    • Simply double-click the file.
    • The Windows Python Launcher (`py.exe`) will automatically open a Command Prompt window and start the live stream.
  • 4. Upon launch, the GMM Master Dashboard will prompt the user to select the timeframe to use. Options include M1, M5, M15, M30, H1, H4, and D1.
  • 5. Visual Confirmation:
    • You will see a black terminal window displaying real-time Linear Rergession Degrees and the Price Magnet Status.
    • Keep this window open while you trade; it acts as your Radar for 30 currency and metals pairs.
  • 6. To Stop the Program: Simply close the CMD window or press Ctrl + C on your keyboard to safely shut down the MT5 connection.

⚠️ Important: Risk Disclaimer

  • Demo Testing: You are strongly advised to test this GMM Master Dashboard on MT5 Demo Account first to to synchronize your trading style with the GMM logic.
  • Real Account Trading: If you proceed to use this GMM Master Dashboard for automated trading on a Real Account, you do so at your own risk. Algorithmic trading involves substantial risk to your capital.
  • Always remember the rules: Never trade with money you cannot afford to lose.
  • Trading foreign exchange on margin carries a high level of risk and may not be suitable for all investors. The high degree of leverage can work against you as well as for you. Before deciding to invest in foreign exchange, you should carefully consider your investment objectives, level of experience, and risk appetite. The GMM Master Dashboard logic provided in this article are for educational purposes and do not guarantee profits. Past performance is not indicative of future results.

Vital Records

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

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

We hope that this article and the GMM Master Dashboard program will be useful for traders in learning and generating new ideas, which is ultimately expected to be successful in forex trading.

The GMM Master Dashboard is just an idea and tool for trading and still needs further development.

See you in the next article on Expert Advisor programs or indicators for MetaTrader 4 and MetaTrader 5.

If you have any ideas for developing this GMM Master Dashboard program or have a new ideas, please leave your comments below this article.

Thanks for reading this article.

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

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


# --- Copyright: 3rjfx @ https://www.forexhomeexpert.com
# --- Created  : 2026/02/06 ---
# --- GMM : GEMINI MANTAP MENTONG!
import MetaTrader5 as mt5
import numpy as np
import time
import os
from colorama import init, Fore, Style

init()

# --- 1. GMM_ FUNCTIONS ---
def GMM_ExponentialMA(price_array, period):
    if period <= 0 or len(price_array) == 0: return price_array
    pr = 2.0 / (period + 1.0)
    result_ema = np.zeros_like(price_array)
    result_ema[0] = price_array[0] 
    for i in range(1, len(price_array)):
        result_ema[i] = (price_array[i] * pr) + (result_ema[i-1] * (1.0 - pr))
    return result_ema

def GMM_Select_TF():
    os.system('cls' if os.name == 'nt' else 'clear')
    print(Fore.CYAN + "====================================================" + Style.RESET_ALL)
    print(Fore.YELLOW + "   GMM TIMEFRAME SELECTOR | COMMANDER, Select TF:" + Style.RESET_ALL)
    print(Fore.CYAN + "====================================================" + Style.RESET_ALL)
    print(" 1. M1  | 2. M5  | 3. M15 | 4. M30 | 5. H1 | 6. H4 | 7. D1")
    print("-" * 52)
    choice = input(Fore.YELLOW + " Type your preferred number (1-7): " + Style.RESET_ALL)
    tf_map = {
        "1": (mt5.TIMEFRAME_M1, "M1"),   "2": (mt5.TIMEFRAME_M5, "M5"),
        "3": (mt5.TIMEFRAME_M15, "M15"), "4": (mt5.TIMEFRAME_M30, "M30"),
        "5": (mt5.TIMEFRAME_H1, "H1"),   "6": (mt5.TIMEFRAME_H4, "H4"), "7": (mt5.TIMEFRAME_D1, "D1")
    }
    return tf_map.get(choice, (mt5.TIMEFRAME_M30, "M30"))

# --- 2. GMM_ DATA ENGINE (LOGIC AREA 4 + MAGNET PRICE) ---
def GMM_Get_Data(symbol, timeframe_id):
    rates = mt5.copy_rates_from_pos(symbol, timeframe_id, 0, 100)
    if rates is None or len(rates) < 10: return None
    
    popen, phigh, plow, pclose = np.array([r['open'] for r in rates]), np.array([r['high'] for r in rates]), np.array([r['low'] for r in rates]), np.array([r['close'] for r in rates])
    pweighted = (phigh + plow + pclose + pclose) / 4.0
    ema2_pm = GMM_ExponentialMA(pweighted, 2)
    
    y, j = ema2_pm[-3:], np.arange(3)
    suma, sumab, sumb1, sumb2 = np.sum(y), np.sum(y * j), np.sum(j), np.sum(j**2)
    denom = (sumb2 * 3) - (sumb1**2)
    b = ((sumab * 3) - (sumb1 * suma)) / denom
    c = (suma - (sumb1 * b)) / 3
    lr_buffers = c + (b * j)

    hmax, lmin = np.max(phigh), np.min(plow)
    cur_degrees = round(270 + ((lr_buffers[-1] - lmin) / (hmax - lmin)) * 180, 2)
    prev1_degrees = round(270 + ((lr_buffers[-2] - lmin) / (hmax - lmin)) * 180, 2)
    prev2_degrees = round(270 + ((lr_buffers[-3] - lmin) / (hmax - lmin)) * 180, 2)
    div1_degrees, div0_degrees = round(prev1_degrees - prev2_degrees, 2), round(cur_degrees - prev2_degrees, 2)

    if cur_degrees > 360.0: cur_degrees -= 360.0
    if cur_degrees == 360.0: cur_degrees = 0.0
    dgrs_up, dgrs_dn = div0_degrees > div1_degrees, div0_degrees < div1_degrees

    status_lr = "SIDEWAY"
    if (270.0 <= cur_degrees < 315.0 and dgrs_dn):   status_lr = "DN_Strong▼▼"
    elif (270.0 <= cur_degrees < 315.0 and dgrs_up): status_lr = "DN_Weakened▼"
    elif (315.0 <= cur_degrees < 360.0 and dgrs_dn): status_lr = "DN_Strengthened▼▼"
    elif (315.0 <= cur_degrees < 360.0 and dgrs_up): status_lr = "DN_Reversal▲"
    elif (0.0 <= cur_degrees < 45.0 and dgrs_up):    status_lr = "UP_Strengthened▲▲"
    elif (0.0 <= cur_degrees < 45.0 and dgrs_dn):    status_lr = "UP_Reversal▼"
    elif (45.0 <= cur_degrees <= 90.0 and dgrs_up):  status_lr = "UP_Strong▲▲"
    elif (45.0 <= cur_degrees <= 90.0 and dgrs_dn):  status_lr = "UP_Weakened▲"

    mid = ema2_pm[-1]
    O, H, L, C = popen[-1], phigh[-1], plow[-1], pclose[-1]
    magnet, mag_col = "FLAT", Fore.WHITE
    if O <= mid and C > mid: magnet = "🧲 UP"; mag_col = Fore.CYAN
    elif O >= mid and C < mid: magnet = "🧲 DN"; mag_col = Fore.RED
    elif O <= mid and H > mid and C < O: magnet = "⚠️ DN-REJ-UP"; mag_col = Fore.MAGENTA
    elif O >= mid and L < mid and C > O: magnet = "⚠️ UP-REJ-DN"; mag_col = Fore.GREEN

    return {"deg": cur_degrees, "status": status_lr, "magnet": magnet, "mag_col": mag_col}

# --- 3. BASE PAIRS: MAJOR - JPY CROSS AND METALS ---
majors = ["AUDUSD", "EURUSD", "GBPUSD", "NZDUSD", "USDCAD", "USDCHF", "USDJPY", 
          "AUDJPY", "CADJPY", "CHFJPY","EURJPY", "GBPJPY","NZDJPY", "XAGUSD", "XAUUSD"]

# --- 4. FULL 30 PAIRS LOGIC LIST ---
logic_list = [
    {"A": "EURUSD", "B": "AUDUSD", "Cross": "EURAUD", "Type": "bagi"},
    {"A": "EURUSD", "B": "USDCAD", "Cross": "EURCAD", "Type": "kali"},
    {"A": "EURUSD", "B": "USDCHF", "Cross": "EURCHF", "Type": "kali"},    
    {"A": "EURUSD", "B": "GBPUSD", "Cross": "EURGBP", "Type": "bagi"},
    {"A": "EURJPY", "B": "EURJPY", "Cross": "EURJPY", "Type": "self"},
    {"A": "EURUSD", "B": "NZDUSD", "Cross": "EURNZD", "Type": "bagi"},
    {"A": "EURUSD", "B": "EURUSD", "Cross": "EURUSD", "Type": "self"},
    {"A": "GBPUSD", "B": "AUDUSD", "Cross": "GBPAUD", "Type": "bagi"},
    {"A": "GBPUSD", "B": "USDCAD", "Cross": "GBPCAD", "Type": "kali"},
    {"A": "GBPUSD", "B": "USDCHF", "Cross": "GBPCHF", "Type": "kali"},    
    {"A": "GBPJPY", "B": "GBPJPY", "Cross": "GBPJPY", "Type": "self"},
    {"A": "GBPUSD", "B": "NZDUSD", "Cross": "GBPNZD", "Type": "bagi"},
    {"A": "GBPUSD", "B": "GBPUSD", "Cross": "GBPUSD", "Type": "self"},
    {"A": "AUDUSD", "B": "USDCAD", "Cross": "AUDCAD", "Type": "kali"},
    {"A": "AUDUSD", "B": "USDCHF", "Cross": "AUDCHF", "Type": "kali"},    
    {"A": "AUDJPY", "B": "AUDJPY", "Cross": "AUDJPY", "Type": "self"},
    {"A": "AUDUSD", "B": "NZDUSD", "Cross": "AUDNZD", "Type": "bagi"},
    {"A": "AUDUSD", "B": "AUDUSD", "Cross": "AUDUSD", "Type": "self"},
    {"A": "NZDUSD", "B": "USDCAD", "Cross": "NZDCAD", "Type": "kali"},
    {"A": "NZDUSD", "B": "USDCHF", "Cross": "NZDCHF", "Type": "kali"},    
    {"A": "NZDJPY", "B": "NZDJPY", "Cross": "NZDJPY", "Type": "self"},
    {"A": "NZDUSD", "B": "NZDUSD", "Cross": "NZDUSD", "Type": "self"},
    {"A": "USDCAD", "B": "USDCHF", "Cross": "CADCHF", "Type": "bagi"},
    {"A": "CADJPY", "B": "CADJPY", "Cross": "CADJPY", "Type": "self"},
    {"A": "USDCAD", "B": "USDCAD", "Cross": "USDCAD", "Type": "self"},    
    {"A": "CHFJPY", "B": "CHFJPY", "Cross": "CHFJPY", "Type": "self"},
    {"A": "USDCHF", "B": "USDCHF", "Cross": "USDCHF", "Type": "self"},
    {"A": "USDJPY", "B": "USDJPY", "Cross": "USDJPY", "Type": "self"},
    {"A": "XAGUSD", "B": "XAGUSD", "Cross": "XAGUSD", "Type": "self"},
    {"A": "XAUUSD", "B": "XAUUSD", "Cross": "XAUUSD", "Type": "self"}
]

# --- 5. MAIN LOOP ---
if __name__ == "__main__":
    if not mt5.initialize(): quit()
    selected_tf_id, tf_name = GMM_Select_TF()
    
    try:
        while True:
            data_results = {m: GMM_Get_Data(m, selected_tf_id) for m in majors}
            os.system('cls' if os.name == 'nt' else 'clear')
            
            print(Fore.YELLOW + "="*65 + Style.RESET_ALL)
            print(Fore.YELLOW + f" GMM MASTER DASHBOARD LR MAGNET | TF: {tf_name} | FULL SCAN (30 PAIRS) " + Style.RESET_ALL)
            print(Fore.YELLOW + "="*65 + Style.RESET_ALL)
            print(f"{' SYMBOL':<11} | {' DEGREE':<8} | {'LR STATUS':<18} | {'PRICE MAGNET'}")
            print("-" * 65)
            
            for m in majors:
                res = data_results[m]
                if res:
                    print(f"{' '}{m:<10} | {res['deg']:>7.2f}° | {Fore.WHITE}{res['status']:<18}{Style.RESET_ALL} | {res['mag_col']}{res['magnet']}{Style.RESET_ALL}")
            
            print(Fore.YELLOW + "="*65 + Style.RESET_ALL)
            print(Fore.YELLOW + " GMM CORRELATION & ENTRY STRATEGY :" + Style.RESET_ALL)
            print("-" * 65)
            
            for l in logic_list:
                rA, rB = data_results[l["A"]], data_results[l["B"]]
                if rA and rB:
                    signal, col = "Wait...", Fore.WHITE
                    bull_status = ["UP_Strong▲▲", "UP_Strengthened▲▲", "DN_Reversal▲"]
                    bear_status = ["DN_Strong▼▼", "DN_Strengthened▼▼", "UP_Reversal▼"]
                    bull_magnet = ["🧲 UP", "⚠️ UP-REJ-DN"]
                    bear_magnet = ["🧲 DN", "⚠️ DN-REJ-UP"]

                    a_is_bull, a_is_bear = any(s == rA["status"] for s in bull_status), any(s == rA["status"] for s in bear_status)
                    b_is_bull, b_is_bear = any(s == rB["status"] for s in bull_status), any(s == rB["status"] for s in bear_status)
                    a_in_bull, a_in_bear = any(s == rA["magnet"] for s in bull_magnet), any(s == rA["magnet"] for s in bear_magnet)
                    b_in_bull, b_in_bear = any(s == rB["magnet"] for s in bull_magnet), any(s == rB["magnet"] for s in bear_magnet)

                    if l["Type"] == "self":
                        is_buy, is_sell = (a_is_bull and a_in_bull), (a_is_bear and a_in_bear)
                    elif l["Type"] == "kali":
                        is_buy, is_sell = ((a_is_bull and b_is_bull) and (a_in_bull and b_in_bull)), ((a_is_bear and b_is_bear) and (a_in_bear and b_in_bear))
                    else: # Type bagi
                        is_buy, is_sell = ((a_is_bull and b_is_bear) and (a_in_bull and b_in_bear)), ((a_is_bear and b_is_bull) and (a_in_bear and b_in_bull))

                   # --- GMM_ LOGIC ENTRY STRATEGY WITH MANTAP MENTONG COLORS ---
                    if is_buy:
                        if "🧲 UP" in rA["magnet"]:
                            signal, col = "🚀 STRONG BUY", Fore.GREEN     # Fresh Green: A Healthy Trend
                        elif "⚠️ UP-REJ-DN" in rA["magnet"]:
                            signal, col = "🔥 BUY NOW!", Fore.CYAN        # Electric Blue: Quick Reversal (Favorite!)
                        else:
                            signal, col = "Wait...", Fore.WHITE
                            
                    elif is_sell:
                        if "🧲 DN" in rA["magnet"]:
                            signal, col = "🚨 STRONG SELL", Fore.RED      # Bold Red: Bearish Confirmation
                        elif "⚠️ DN-REJ-UP" in rA["magnet"]:
                            signal, col = "🔥 SELL NOW!", Fore.MAGENTA    # Magenta Purple: Sharp Rejection
                        else:
                            signal, col = "Wait...", Fore.WHITE
                            
                    else:
                        signal, col = "Wait...", Fore.WHITE
                    
                    print(f"{' '}{l['A']} x {l['B']} => {l['Cross']:<8} | {col}{signal}{Style.RESET_ALL}")

            print("-" * 65)
            print(Fore.WHITE + " GMM_ Info: 🔥 NOW! = Trend + Magnet Alignment (Best Entry)")
            print(" GMM_ Info: ⚠️ REJ = Strong Rejection at Magnet Median")
            print(" GMM_ Info: Press Ctrl+C to stop." + Style.RESET_ALL)
            time.sleep(20)
            
    except KeyboardInterrupt:
        print(Fore.RED + "\nDashboard Stopped." + Style.RESET_ALL)
    finally:
        mt5.shutdown()
        
//----------//

//+------------------------------------------------------------------+
//|                                                    LRDegrees.mq5 |
//|                           Copyright 2017, Roberto Jacobs (3rjfx) |
//|                              https://www.mql5.com/en/users/3rjfx |
//+------------------------------------------------------------------+
#property copyright "https://www.mql5.com/en/users/3rjfx. ~ By 3rjfx ~ Created: 2017/01/12"
#property link      "http://www.mql5.com"
#property link      "https://www.mql5.com/en/users/3rjfx"
#property version   "1.00"
#property strict
/*
== Last Update: 2026/02/10 ==
*/
//--
#property description "MetaTrader 5 Forex Indicator Double Line of LinearRegression with Degrees and Trend Alerts."
//---
#property indicator_chart_window
#property indicator_buffers 4
#property indicator_plots   4
#property indicator_style1  DRAW_NONE
#property indicator_style2  DRAW_NONE
#property indicator_style3  DRAW_NONE
#property indicator_style4  DRAW_NONE
//--
enum SetDegree
  {
    Line2,
    Line1
  };
//--
//---
input SetDegree           degress = Line2;         // Set Linear Degrees Line
input int          barsCountLine1 = 25;            // Linear Regression 1 Bars to count
input int          barsCountLine2 = 3;             // Linear Regression 2 Bars to count
input color          LRLineColor1 = clrRed;        // Linear Regression Line1 Color
input color          LRLineColor2 = clrBlue;       // Linear Regression Line2 Color
input color          RoundedColor = clrAqua;       // Rounded Color
input color              UpsColor = clrAqua;       // Arrow Up Color
input color              DnsColor = clrOrangeRed;  // Arrow Down Color
input color            PriceColor = clrSnow;       // Arrow Right Price  Color
input ENUM_LINE_STYLE LRLineStyle = STYLE_SOLID;   // Linear Regression Line style
input int             LRLineWidth = 2;             // Linear Regression Line width
//--
input bool              MsgAlerts = true;
input bool            SoundAlerts = true;
input bool            eMailAlerts = false;
input string       SoundAlertFile = "alert.wav";
//--
//--- buffers
double MABuffers[];
double DegreesUp[];
double DegreesDn[];
double LRBuffers1[];
double LRBuffers2[];
//--
ENUM_BASE_CORNER corner=CORNER_RIGHT_UPPER;
int dist_x=144;
int dist_xt=104;
int dist_y=125;
//--
int hMA;
int cmal,xmal;
int posalert;
int prevalert;
int bar_count;
int bars_calculated=0;
//--
color stgBull=clrBlue;
color stsBull=clrAqua;
color stsBear=clrYellow;
color stgBear=clrRed;
color txtrbl=clrWhite;
color txtblk=clrBlack;
color rndclr;
color arrclr;
color txtclr;
//--
long Chart_Id;
//--
string name;
string dtext;
string Albase,AlSubj,AlMsg;
//---------//
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit(void)
  {
//--- indicator buffers mapping
//---
   Chart_Id=ChartID();
   bar_count=120;
   name="LR Degrees";
   IndicatorSetString(INDICATOR_SHORTNAME,name);
   IndicatorSetInteger(INDICATOR_DIGITS,3);
//--- indicator buffers mapping
   //---
   SetIndexBuffer(0,DegreesUp,INDICATOR_DATA);
   SetIndexBuffer(1,DegreesDn,INDICATOR_DATA);
   SetIndexBuffer(2,LRBuffers1,INDICATOR_CALCULATIONS);
   SetIndexBuffer(3,LRBuffers2,INDICATOR_CALCULATIONS);
//--- indicator labels
   PlotIndexSetString(0,PLOT_LABEL,"UpDegrees");
   PlotIndexSetString(1,PLOT_LABEL,"DnDegrees");
   PlotIndexSetString(2,PLOT_LABEL,"LR_Line1");
   PlotIndexSetString(3,PLOT_LABEL,"LR_Line2");
   //--
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0.0);
   //--
   hMA=iMA(Symbol(),Period(),2,0,MODE_EMA,PRICE_WEIGHTED);
   //---
//--- initialization done
   return(INIT_SUCCEEDED);
  }
//---------//
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//----
   //--
   IndicatorRelease(hMA);
   ObjectsDeleteAll(Chart_Id,0,-1);
//----
   return;
  }
//---------//
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//------
   if(rates_total<bar_count) return(0);
   //--
   ArraySetAsSeries(high,true);
   ArraySetAsSeries(low,true);
   ArraySetAsSeries(time,true);
//------
//--- Set Last error value to Zero
   ResetLastError();
   ChartRedraw(0);
   //---
   //--
   int l,r,j,
       dtxt=0;
   //---
   bool dgrsUp=false;
   bool dgrsDn=false;
   //--
   double x,y,z,
          a,b,c;
   double sumy=0.0,
          sumx1=0.0,
          sumxy=0.0,
          sumx2=0.0;
   double suma=0.0,
          sumb1=0.0,
          sumab=0.0,
          sumb2=0.0;
   //--
   double hmax=0.0,
          lmin=0.0;
   //--
   double cur_degrees=0.0,
          prev1_degrees=0.0,
          prev2_degrees=0.0,
          div1_degrees=0.0,
          div0_degrees=0.0;
   //--
   //-- prepare the Highest and Lowest Price
   int HL=100;
   int Hi=iHighest(_Symbol,0,MODE_HIGH,HL,0);
   int Lo=iLowest(_Symbol,0,MODE_LOW,HL,0);
   if(Hi!=-1) hmax=high[Hi];
   if(Lo!=-1) lmin=low[Lo];
   //--
   double rangetb=(hmax-lmin);
   double toplevel1=lmin+(rangetb/1000*838);
   double toplevel2=lmin+(rangetb/1000*618);
   double midlevel=lmin+(rangetb/1000*500);
   double botlevel2=lmin+(rangetb/1000*382);
   double botlevel1=lmin+(rangetb/1000*162);
   //--
//--- number of values copied from the iMA indicator
   int values_to_copy;
//--- determine the number of values calculated in the indicator
   int calculated=BarsCalculated(hMA);
   if(calculated<=0)
     {
      PrintFormat("BarsCalculated() returned %d, error code %d",calculated,GetLastError());
      return(0);
     }
//--- if it is the first start of calculation of the indicator or if the number of values in the iMA indicator changed
//---or if it is necessary to calculated the indicator for two or more bars (it means something has changed in the price history)
   if(prev_calculated==0 || calculated!=bars_calculated || rates_total>prev_calculated+1)
     {
      //--- if the MABuffer array is greater than the number of values in the iMA indicator for symbol/period, then we don't copy everything
      //--- otherwise, we copy less than the size of indicator buffers
      if(calculated>rates_total)
         values_to_copy=rates_total;
      else
         values_to_copy=calculated;
     }
   else
     {
      //--- it means that it's not the first time of the indicator calculation, and since the last call of OnCalculate()
      //--- for calculation not more than one bar is added
      values_to_copy=(rates_total-prev_calculated)+1;
     }
   //--
   ArrayResize(DegreesUp,calculated);
   ArrayResize(DegreesDn,calculated);
   ArrayResize(MABuffers,calculated);
   ArraySetAsSeries(DegreesUp,true);
   ArraySetAsSeries(DegreesDn,true);   
   ArraySetAsSeries(MABuffers,true);
//----
   CopyBuffer(hMA,0,0,values_to_copy,MABuffers);
   //--
   for(r=0; r<barsCountLine1; r++)
     {
      sumy+=MABuffers[r];
      sumxy+=MABuffers[r]*r;
      sumx1+=r;
      sumx2+=r*r;
     }
   //--
   x=sumx2*barsCountLine1-sumx1*sumx1;
   y=(sumxy*barsCountLine1-sumx1*sumy)/x;
   z=(sumy-sumx1*y)/barsCountLine1;
   //--
   //-- Linear regression MA trend 1
   for(l=0; l<barsCountLine1; l++) LRBuffers1[l]=z+y*l; 
   //--
   for(j=0; j<barsCountLine2; j++)
     {
      suma+=MABuffers[j];
      sumab+=MABuffers[j]*j;
      sumb1+=j;
      sumb2+=j*j;
     }
   //--
   a=sumb2*barsCountLine2-sumb1*sumb1;
   b=(sumab*barsCountLine2-sumb1*suma)/a;
   c=(suma-sumb1*b)/barsCountLine2;
   //--
   //-- Linear regression MA trend 2
   for(l=0; l<barsCountLine2; l++) LRBuffers2[l]=c+b*l;
   //---
   for(int i=bar_count-1; i>=0; i--)
     {
       //--
       if(i==0)
         {
           switch(degress)
             {
               case 0:
                 {
                   cur_degrees=NormalizeDouble(270+(((LRBuffers2[0]-lmin)/(hmax-lmin))*180),2);
                   prev1_degrees=NormalizeDouble(270+(((LRBuffers2[1]-lmin)/(hmax-lmin))*180),2);
                   prev2_degrees=NormalizeDouble(270+(((LRBuffers2[2]-lmin)/(hmax-lmin))*180),2);
                   div1_degrees=prev1_degrees - prev2_degrees;
                   div0_degrees=cur_degrees - prev2_degrees;
                   break;
                 }
               //--
               case 1:
                 {
                   cur_degrees=NormalizeDouble(270+(((LRBuffers1[0]-lmin)/(hmax-lmin))*180),2);
                   prev1_degrees=NormalizeDouble(270+(((LRBuffers1[1]-lmin)/(hmax-lmin))*180),2);
                   prev2_degrees=NormalizeDouble(270+(((LRBuffers1[2]-lmin)/(hmax-lmin))*180),2);
                   div1_degrees=prev1_degrees - prev2_degrees;
                   div0_degrees=cur_degrees - prev2_degrees;
                   break;
                 }
             }
           //--
           if(cur_degrees>360.0) {cur_degrees=NormalizeDouble(cur_degrees-360.0,2);}
           if(cur_degrees==360.0) {cur_degrees=NormalizeDouble(0.0,2);}
           //- To give a value of 90.0 degrees to the indicator, when the price moves up very quickly and make a New Windows Price Max.
           if(cur_degrees==90.0) {cur_degrees=NormalizeDouble(90.0,2);}
           //- To give a value of 270.0 degrees to the indicator, when the price moves down very quickly and make a New Windows Price Min.
           if(cur_degrees==270.0) {cur_degrees=NormalizeDouble(270.0,2);}
           //--
           if(div0_degrees>div1_degrees) {dgrsUp=true; dgrsDn=false; DegreesUp[i]=cur_degrees; DegreesDn[i]=0.0;}
           if(div0_degrees<div1_degrees) {dgrsDn=true; dgrsUp=false; DegreesDn[i]=cur_degrees; DegreesUp[i]=0.0;}
           //---
           if((cur_degrees>=270.0 && cur_degrees<315.0)&&(dgrsDn==true)) {rndclr=stgBear; arrclr=stgBear; txtclr=txtrbl; posalert=11;}
           if((cur_degrees>=270.0 && cur_degrees<315.0)&&(dgrsUp==true)) {rndclr=stgBear; arrclr=stsBear; txtclr=txtrbl; posalert=12;}
           if((cur_degrees>=315.0 && cur_degrees<360.0)&&(dgrsDn==true)) {rndclr=stsBear; arrclr=stgBear; txtclr=txtblk; posalert=21;}
           if((cur_degrees>=315.0 && cur_degrees<360.0)&&(dgrsUp==true)) {rndclr=stsBear; arrclr=stsBull; txtclr=txtblk; posalert=23;}
           if((cur_degrees>=0.0 && cur_degrees<45.0)&&(dgrsUp==true)) {rndclr=stsBull; arrclr=stgBull; txtclr=txtblk; posalert=34;}
           if((cur_degrees>=0.0 && cur_degrees<45.0)&&(dgrsDn==true)) {rndclr=stsBull; arrclr=stsBear; txtclr=txtblk; posalert=32;}
           if((cur_degrees>=45.0 && cur_degrees<=90.0)&&(dgrsUp==true)) {rndclr=stgBull; arrclr=stgBull; txtclr=txtrbl; posalert=44;}
           if((cur_degrees>=45.0 && cur_degrees<=90.0)&&(dgrsDn==true)) {rndclr=stgBull; arrclr=stsBull; txtclr=txtrbl; posalert=43;}
           //---
           dtext=DoubleToString(cur_degrees,1)+CharToString(176);
           if(StringLen(dtext)>5) {dtxt=24;}
           else if(StringLen(dtext)==5) {dtxt=20;}
           else {dtxt=17;}
           //--
           //---
           CreateRoundDegrees(Chart_Id,"RoundedDegrees","Wingdings",(string)CharToString(108),67,rndclr,corner,dist_x,dist_y,true);
           //--
           CreateRoundDegrees(Chart_Id,"TextDegrees","Bodoni MT Black",dtext,8,txtclr,corner,dist_xt+dtxt,dist_y+41,true);    
           //--
           if(dgrsUp) 
              CreateArrowDegrees(Chart_Id,"ArrUpDegrees","ArrDnDegrees","Wingdings",(string)CharToString(217),23,arrclr,corner,dist_xt+20,dist_y-2,true);
           //--
           if(dgrsDn) 
              CreateArrowDegrees(Chart_Id,"ArrDnDegrees","ArrUpDegrees","Wingdings",(string)CharToString(218),23,arrclr,corner,dist_xt+20,dist_y+63,true);
           //---
           color clinear1=LRBuffers1[0]>=LRBuffers1[barsCountLine1-1] ? LRLineColor2 : LRLineColor1;
           CreateLRTrendLine(Chart_Id,"LRTrendLine1",time[0],LRBuffers1[0],time[barsCountLine1-1],LRBuffers1[barsCountLine1-1],
                             LRLineWidth,LRLineStyle,clinear1,false,true);
           //--
           color clinear2=LRBuffers2[0]>=LRBuffers2[barsCountLine2-1] ? LRLineColor2 : LRLineColor1;
           CreateLRTrendLine(Chart_Id,"LRTrendLine2",time[0],LRBuffers2[0],time[barsCountLine2-1],LRBuffers2[barsCountLine2-1],
                             LRLineWidth,LRLineStyle,clinear2,false,true);
           //--
           CreateArrowPrice(Chart_Id,"FiboLabelPrice_t1",time[0],toplevel1,PriceColor,STYLE_SOLID,1,ANCHOR_LEFT,true);
           //--
           CreateArrowPrice(Chart_Id,"FiboLabelPrice_t2",time[0],toplevel2,PriceColor,STYLE_SOLID,1,ANCHOR_LEFT,true);
           //--
           CreateArrowPrice(Chart_Id,"FiboLabelPrice_m",time[0],midlevel,PriceColor,STYLE_SOLID,1,ANCHOR_LEFT,true);
           //--
           CreateArrowPrice(Chart_Id,"FiboLabelPrice_b2",time[0],botlevel2,PriceColor,STYLE_SOLID,1,ANCHOR_LEFT,true);
           //--
           CreateArrowPrice(Chart_Id,"FiboLabelPrice_b1",time[0],botlevel1,PriceColor,STYLE_SOLID,1,ANCHOR_LEFT,true);
           //--
         }
     }
   //--
   ChartRedraw(0);
   PosAlerts(posalert);
   //---
//--- done
   return(rates_total);
  }
//--- end OnCalculate()
//---------//

void CreateRoundDegrees(long   chartid, 
                        string lable_name, 
                        string lable_font_model,
                        string lable_obj_text,
                        int    lable_font_size,
                        color  lable_color,
                        int    lable_corner,
                        int    lable_xdist,
                        int    lable_ydist,
                        bool   lable_hidden)
  {  
    //--
    ObjectDelete(chartid,lable_name);
    //--
    ObjectCreate(chartid,lable_name,OBJ_LABEL,0,0,0,0,0); // create rounded degrees
    ObjectSetInteger(chartid,lable_name,OBJPROP_FONTSIZE,lable_font_size); 
    ObjectSetString(chartid,lable_name,OBJPROP_FONT,lable_font_model);
    ObjectSetString(chartid,lable_name,OBJPROP_TEXT,lable_obj_text);
    ObjectSetInteger(chartid,lable_name,OBJPROP_COLOR,lable_color);
    ObjectSetInteger(chartid,lable_name,OBJPROP_CORNER,lable_corner);
    ObjectSetInteger(chartid,lable_name,OBJPROP_XDISTANCE,lable_xdist);
    ObjectSetInteger(chartid,lable_name,OBJPROP_YDISTANCE,lable_ydist);
    ObjectSetInteger(chartid,lable_name,OBJPROP_HIDDEN,lable_hidden);
    //--
  }   
//---------//

void CreateArrowDegrees(long   chartid, 
                        string lable_name1,
                        string lable_name2,
                        string lable_font_model,
                        string lable_obj_text,
                        int    lable_font_size,
                        color  lable_color,
                        int    lable_corner,
                        int    lable_xdist,
                        int    lable_ydist,
                        bool   lable_hidden)
  {  
    //--
    ObjectDelete(chartid,lable_name2);
    ObjectDelete(chartid,lable_name1);
    //--
    ObjectCreate(chartid,lable_name1,OBJ_LABEL,0,0,0,0,0); // create arrow degrees
    ObjectSetInteger(chartid,lable_name1,OBJPROP_FONTSIZE,lable_font_size); 
    ObjectSetString(chartid,lable_name1,OBJPROP_FONT,lable_font_model);
    ObjectSetString(chartid,lable_name1,OBJPROP_TEXT,lable_obj_text);
    ObjectSetInteger(chartid,lable_name1,OBJPROP_COLOR,lable_color);
    ObjectSetInteger(chartid,lable_name1,OBJPROP_CORNER,lable_corner);
    ObjectSetInteger(chartid,lable_name1,OBJPROP_XDISTANCE,lable_xdist);
    ObjectSetInteger(chartid,lable_name1,OBJPROP_YDISTANCE,lable_ydist);
    ObjectSetInteger(chartid,lable_name1,OBJPROP_HIDDEN,lable_hidden);
    //--
  }   
//---------//

void CreateLRTrendLine(long     chartid, 
                       string   line_name,
                       datetime line_time1,
                       double   line_price1,
                       datetime line_time2,
                       double   line_price2,
                       int      line_width,
                       int      line_style,
                       color    line_color,
                       bool     ray_right,
                       bool     line_hidden)
  {  
    //--
    ObjectDelete(chartid,line_name);
    //--
    ObjectCreate(chartid,line_name,OBJ_TREND,0,line_time1,line_price1,line_time2,line_price2); // create trend line
    ObjectSetInteger(chartid,line_name,OBJPROP_WIDTH,line_width);
    ObjectSetInteger(chartid,line_name,OBJPROP_STYLE,line_style);
    ObjectSetInteger(chartid,line_name,OBJPROP_COLOR,line_color);
    ObjectSetInteger(chartid,line_name,OBJPROP_RAY_RIGHT,ray_right);
    ObjectSetInteger(chartid,line_name,OBJPROP_HIDDEN,line_hidden);
    //--
  }   
//---------//

void CreateArrowPrice(long     chart_id,
                      string   arrow_name, 
                      datetime arrow_time,
                      double   arrow_pos,
                      color    arrow_color,
                      int      arrow_style,
                      int      arrow_width,
                      int      anchor,
                      bool     hidden)
  {
    //--
    ObjectDelete(chart_id,arrow_name);
    //--
    ObjectCreate(chart_id,arrow_name,OBJ_ARROW_RIGHT_PRICE,0,arrow_time,arrow_pos); // create arrow right price
    ObjectSetInteger(chart_id,arrow_name,OBJPROP_COLOR,arrow_color);
    ObjectSetInteger(chart_id,arrow_name,OBJPROP_STYLE,arrow_style);
    ObjectSetInteger(chart_id,arrow_name,OBJPROP_WIDTH,arrow_width);
    ObjectSetInteger(chart_id,arrow_name,OBJPROP_ANCHOR,anchor);
    ObjectSetInteger(chart_id,arrow_name,OBJPROP_HIDDEN,hidden);
    //--
  }
//---------//

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

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

void DoAlerts(string msgText,string eMailSub)
  {
     if (MsgAlerts) Alert(msgText);
     if (SoundAlerts) PlaySound(SoundAlertFile);
     if (eMailAlerts) SendMail(eMailSub,msgText);
  }
//---------//

string StrTF(int period)
  {
   switch(period)
     {
       //--
       case PERIOD_M1: return("M1");
       case PERIOD_M5: return("M5");
       case PERIOD_M15: return("M15");
       case PERIOD_M30: return("M30");
       case PERIOD_H1: return("H1");
       case PERIOD_H4: return("H4");
       case PERIOD_D1: return("D1");
       case PERIOD_W1: return("W1");
       case PERIOD_MN1: return("MN");
       //--
     }
   return(string(period));
  }  
//---------//

void PosAlerts(int curalerts)
   {
    //---
    cmal=MqlReturnDateTime(TimeCurrent(),TimeReturn(min));
    if(cmal!=xmal)
      {
        //--
        if((curalerts!=prevalert)&&(curalerts==43))
          {
            Albase=name+" "+_Symbol+", TF: "+StrTF(_Period)+", Position "+dtext;
            AlSubj=Albase+" Trend Began to Fall, Bulish Weakened";
            AlMsg=AlSubj+" @ "+TimeToString(TimeLocal(),TIME_SECONDS);
            DoAlerts(AlMsg,AlSubj);
            prevalert=curalerts;
          }
        //---
        if((curalerts!=prevalert)&&(curalerts==32))
          {     
            Albase=name+" "+_Symbol+", TF: "+StrTF(_Period)+", Position "+dtext;
            AlSubj=Albase+" Trend was Down, Bulish Reversal";
            AlMsg=AlSubj+" @ "+TimeToString(TimeCurrent(),TIME_SECONDS);
            DoAlerts(AlMsg,AlSubj);
            prevalert=curalerts;
          }
        //---
        if((curalerts!=prevalert)&&(curalerts==21))
          {     
            Albase=name+" "+_Symbol+", TF: "+StrTF(_Period)+", Position "+dtext;
            AlSubj=Albase+" Trend was Down, Bearish Strengthened";
            AlMsg=AlSubj+" @ "+TimeToString(TimeCurrent(),TIME_SECONDS);
            DoAlerts(AlMsg,AlSubj);
            prevalert=curalerts;
          }              
        //---
        if((curalerts!=prevalert)&&(curalerts==11))
          {     
            Albase=name+" "+_Symbol+", TF: "+StrTF(_Period)+", Position "+dtext;
            AlSubj=Albase+" Trend was Down, Strong Bearish";
            AlMsg=AlSubj+" @ "+TimeToString(TimeCurrent(),TIME_SECONDS);
            DoAlerts(AlMsg,AlSubj);
            prevalert=curalerts;
          }
        //---
        if((curalerts!=prevalert)&&(curalerts==12))
          {
            Albase=name+" "+_Symbol+", TF: "+StrTF(_Period)+", Position "+dtext;
            AlSubj=Albase+" Trend Began to Rise, Bearish Weakened";
            AlMsg=AlSubj+" @ "+TimeToString(TimeCurrent(),TIME_SECONDS);
            DoAlerts(AlMsg,AlSubj);
            prevalert=curalerts;
          }
        //---
        if((curalerts!=prevalert)&&(curalerts==23))
          {
            Albase=name+" "+_Symbol+", TF: "+StrTF(_Period)+", Position "+dtext;
            AlSubj=Albase+" Trend was Up, Bearish Reversal";
            AlMsg=AlSubj+" @ "+TimeToString(TimeCurrent(),TIME_SECONDS);
            DoAlerts(AlMsg,AlSubj);
            prevalert=curalerts;
          }
        //---
        if((curalerts!=prevalert)&&(curalerts==34))
          {
            Albase=name+" "+_Symbol+", TF: "+StrTF(_Period)+", Position "+dtext;
            AlSubj=Albase+" Trend was Up, Bulish Strengthened";
            AlMsg=AlSubj+" @ "+TimeToString(TimeCurrent(),TIME_SECONDS);
            DoAlerts(AlMsg,AlSubj);
            prevalert=curalerts;
          }
        //---
        if((curalerts!=prevalert)&&(curalerts==44))
          {
            Albase=name+" "+_Symbol+", TF: "+StrTF(_Period)+", Position "+dtext;
            AlSubj=Albase+" Trend was Up, Strong Bulish";
            AlMsg=AlSubj+" @ "+TimeToString(TimeCurrent(),TIME_SECONDS);
            DoAlerts(AlMsg,AlSubj);
            prevalert=curalerts;
          }
        //--
        xmal=cmal;
      }
    //---
    return;
   //----
   } //-end PosAlerts()
//---------//
//+------------------------------------------------------------------+

Please download the GMM Master Dashboard: GMM Master Dashboard and Indicator: LRDegrees


© 2026 GMM Master Dashboard - Developed by Roberto Jacobs (3rjfx)

Sunday, February 1, 2026

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

1. Introduction

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

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

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

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

2. The Interactive Dashboard Feature

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

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

3. The Fractal Nature of the Market

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

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

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

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

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

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

5. Key Features of the Dashboard

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

6. How to Trade with ZigZag_MTF

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

7. Conclusion

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

⚠️ Important: Risk Disclaimer

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

Vital Records

If you think the ZigZag Multi-timeframe (ZigZag_MTF) is worthy of being used for automated trading as an Expert Advisor, please leave a comment below this article.

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

Thanks for reading this article.

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

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


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

MTF_Indi mi;

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

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

Please download the ZigZag_MTF indicator: ZigZag_MTF


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

Featured Post

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

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