#ForexHomeExpert_image_slider1 #ForexHomeExpert_image_slider2 #ForexHomeExpert_image_slider3 #ForexHomeExpert_image_slider4

Tuesday, March 3, 2026

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

Introduction

This GMM_MD_MCEA - Python MT5 integration Multi-Currency Expert Advisor program is a development of the GMM Master Dashboard that I explained in the previous article GMM Master Dashboard: The Story Behind the Innovation.

While the GMM Master Dashboard program is simply a Python program that provides signals for manual trading, the GMM_MD_MCEA program enhances the GMM Master Dashboard's capabilities by not only providing signals on the dashboard but also managing orders as a multi-currency expert advisor.

In the fast-paced world of algorithmic trading, precision is not just an advantage—it is a necessity. While many traders remain tethered to the limitations of traditional MQL5 scripts, the GMM_MD_MCEA marks a significant leap forward into the era of Python-driven high-frequency monitoring. By moving beyond simple loops and embracing Advanced Dictionary-Based Logic, this Expert Advisor introduces a level of surgical precision in multi-currency execution that was previously unattainable. From automated timeframe synchronization to pinpoint lot normalization across diverse symbols like XAGUSD and Crypto, GMM_MD_MCEA is engineered for the modern trader who demands absolute control, rock-solid discipline, and a visual monitoring experience that sets a new standard for the Forex Home Expert community.

GMM_monitoring_terminal_01Figure 1: GMM_MD_MCEA - Monitoring Terminal

GMM_MD_MCEA: Technical Excellence & Key Features

  • Advanced Multi-Pair Dictionary Logic

    Unlike standard Python scripts that use a single global variable, this engine utilizes a Dictionary-based tracking system (`GMM_PREV_TIME_BUY/SELL`). This allows the EA to maintain an independent memory for each currency pair, ensuring that a trade execution on one symbol never blocks a valid signal on another.
  • Surgical Precision via GMM_Normalize_Double

    Engineered to handle the complexities of different pairs and broker digit systems. Whether you are trading high-precision Forex pairs (5 digits) or specialized commodities like Silver (XAGUSD) and Gold (XAUUSD), our normalization engine ensures every SL, TP, and Price request is perfectly formatted to prevent the dreaded 10015 (Invalid Price) error.
  • Intelligent Lot & Volume Digit Calibration

    Equipped with GMM_LotDigit, the EA automatically detects the specific lot step and decimal requirements for every symbol. This guarantees seamless execution even on instruments with non-standard volume requirements, ensuring your risk management is always executed exactly as planned.
  • Adaptive Timeframe Synchronization (`GMM_Check_TF_Change`)

    A sophisticated sensor that monitors manual user intervention. If you switch timeframes on the fly (e.g., from H1 to M15), the EA intelligently detects the change and resets its internal timers, allowing it to instantly adapt to the new bar structure without needing a manual restart.
  • Dynamic Trade Evolution (`GMM_Order_Close_Open`)

    Going beyond simple order closing, this function manages complex trade transitions. It allows the EA to swiftly exit a position and re-enter a new trade based on fresh signal alignment, ensuring the bot always stays on the right side of the market momentum.
  • Pro-Level Monitoring Interface

    The GMM MONITORING TERMINAL provides a real-time, color-coded dashboard. It calculates total running profit, normalizes current market prices, and displays active positions with crystal-clear visibility, giving you a professional institutional-grade trading console right in your Python terminal.

Additional Elite Features: The "Broker-Proof" Engine

  • Smart Order Filling Logic (`GMM_Filling_Mode`)

    One of the most common pitfalls in MT5 Python development is the 10030 TRADE_RETCODE_INVALID_FILL error. Our engine includes a dedicated Dynamic Filling Detector. It automatically identifies whether your broker requires FOK (Fill or Kill), IOC (Immediate or Cancel), or Return modes. This ensures your orders are executed instantly without rejection, regardless of the broker’s execution policy.
  • Advanced Trade Evolution (`GMM_Order_Close_Open`)

    While most Python bots only know how to open and wait, GMM_MD_MCEA can evolve. This function replaces the rigid OrderCloseBy limitations. It enables a seamless Reverse-and-Reopen strategy, allowing the bot to instantly close a losing or stagnant position and open a fresh one based on real-time signal reversals—all in one smooth programmatic flow.
  • Surgical Risk Management (`GMM_Partial_Close_Modify`)

    The hallmark of a professional trader is securing profits while letting winners run. This function performs Real-Time Volume Slicing. Once a specific profit target is hit, the EA automatically executes a partial close (e.g., cutting 50% of the lot) and modifies the remaining position to Break-Even (BEP). This Set and Forget security layer protects your capital from sudden market reversals.

Comparison: Standard Python Bot vs. GMM_MD_MCEA (The Great White Shark)

Comparison Standard Python Bot vs GMM_MD_MCEAFigure 2: Comparison Standard Python Bot vs GMM_MD_MCEA

How to Install and Activate the GMM_MD_MCEA

To experience the power of the GMM_MD_MCEA 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_MD_MCEA.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_MD_MCEA 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 GMM_MD_MCEA Dashboard - Symbol List and GMM MONITORING TERMINAL.
    • Keep this window open so you can monitor GMM_MD_MCEA trading and manage orders for 30 currency 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 EA on an MT5 Demo Account first to witness how it manages 30 pairs simultaneously.
  • Real Account Trading: If you proceed to use this EA 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_MD_MCEA logic provided in this article are for educational purposes and do not guarantee profits. Past performance is not indicative of future results.

The Philosophical Quote

*In the ocean of data, the Great White Shark doesn't strike at every shadow; it strikes with precision.
Success in trading is not about how many orders you open, but how perfectly you manage the ones that matter.*

Vital Records

We hope that this article and the GMM_MD_MCEA - Python MT5 integration Multi-Currency Expert Advisor program will be useful for traders in learning and generating new ideas, which is ultimately expected to be successful in forex trading.

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 EA 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/24 ---
# --- GMM : GEMINI MANTAP MENTONG!
import MetaTrader5 as mt5
import numpy as np
import time
import os
from decimal import Decimal, ROUND_HALF_UP
from colorama import init, Fore, Style

init()

# ====================================================
# --- GMM_ GLOBAL SETTINGS & RISK MANAGEMENT ---
# ====================================================
GMM_MAGIC           = 223344      # ID - Magic Number
GMM_LOT_SIZE        = 0.10        # Initial lot of each position
GMM_DEFAULT_SL      = 0           # Stop Loss (in points)
GMM_DEFAULT_TP      = 450         # Take Profit (in points)
GMM_CLS_TRIGGER     = 150         # Price ran 15 pips, do partial close
GMM_BEP_LOCK        = 50          # BEP 5 pips profit key, move SL
GMM_PARTIAL_PERCENT = 0.5         # Cut 50% Volume when BEP is active
GMM_DEVIATION       = 15          # Sets Deviation property value
GMM_SLEEP_INTERVAL  = 12          # 12 second break (per round all pairs)

# --- TIME CONTROL VARIABLE (ABOVE SCRIPT) ---
GMM_CURR_TIME_BUY  = None
GMM_CURR_TIME_SELL = None
# Using Dictionary so that each pair has its own time record
GMM_PREV_TIME_BUY  = {} 
GMM_PREV_TIME_SELL = {}
GMM_LAST_TF        = None # To save the previous timeframe track

# --- 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"))


def GMM_Check_TF_Change(current_tf):
    global GMM_LAST_TF, GMM_PREV_TIME_BUY, GMM_PREV_TIME_SELL
    if GMM_LAST_TF is not None and current_tf != GMM_LAST_TF:
        GMM_PREV_TIME_BUY.clear()  # Clear all Buy records
        GMM_PREV_TIME_SELL.clear() # Clear all Sell records
        print(f"{Fore.MAGENTA}GMM_ SYSTEM: Timeframe Changed! Resetting All Pairs...{Style.RESET_ALL}")
    GMM_LAST_TF = current_tf    


# --- 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 = GMM_Normalize_Double((270 + ((lr_buffers[-1] - lmin) / (hmax - lmin)) * 180), 2)
    prev1_degrees = GMM_Normalize_Double((270 + ((lr_buffers[-2] - lmin) / (hmax - lmin)) * 180), 2)
    prev2_degrees = GMM_Normalize_Double((270 + ((lr_buffers[-3] - lmin) / (hmax - lmin)) * 180), 2)
    div1_degrees, div0_degrees = GMM_Normalize_Double(prev1_degrees - prev2_degrees, 2), GMM_Normalize_Double(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"}
]

def GMM_Manage_Order(pair, req_order_type):
    pos_exist = GMM_Get_Position(pair)
    if pos_exist is None:
        GMM_Open_Order(pair, req_order_type)
    else:
        pos_type    = pos_exist.type
        pos_open    = pos_exist.price_open
        pos_current = pos_exist.price_current
        pos_profit  = pos_exist.profit
        pair_info   = GMM_Get_Symbol_Info(pair)
        ppoint      = pair_info.point
        if ((req_order_type == mt5.ORDER_TYPE_BUY and req_order_type == pos_type and pos_current > pos_open + (GMM_CLS_TRIGGER * ppoint)) |
        (req_order_type == mt5.ORDER_TYPE_SELL and req_order_type == pos_type and pos_current < pos_open - (GMM_CLS_TRIGGER * ppoint))):
            GMM_Partial_Close_Modify(pair)
        elif (req_order_type != pos_type and pos_profit < 0):
            GMM_Order_Close_Open(pair, req_order_type)

def GMM_Open_Order(pair, req_order_type):
    global GMM_PREV_TIME_BUY, GMM_PREV_TIME_SELL
    
    # 1. CHECK NEW BAR (Directly call GMM_Check_NewBar)
    if not GMM_Check_NewBar(pair, GMM_LAST_TF, req_order_type):
        return # STOP if still on the same bar
        

    # 2. Get the latest symbol data using the GMM_Get_Symbol_Info() function.
    pair_info = GMM_Get_Symbol_Info(pair)
    if pair_info is None: return
    else: # 3. Basic Parameter Preparation 
        p_volume   = GMM_LOT_SIZE
        pdigits    = pair_info.digits
        ppoint     = pair_info.point
        p_vol_step = pair_info.volume_step
        vol_digit  = GMM_LotDigit(pair)
        p_ask      = mt5.symbol_info_tick(pair).ask
        p_bid      = mt5.symbol_info_tick(pair).bid
        mode_fill  = pair_info.filling_mode
        fill_type  = GMM_Filling_Mode(mode_fill)
        p_vol_min  = pair_info.volume_min
        p_vol_max  = pair_info.volume_max
        if (p_volume <= p_vol_min):
            p_volume = p_vol_min * 2.0
        elif (p_volume > p_vol_max):
            p_volume = p_vol_max

    #4. Determining Order Type, Price, SL, and TP (GMM_ Logic)
        if req_order_type == mt5.ORDER_TYPE_BUY:
            order_type = mt5.ORDER_TYPE_BUY
            useprice   = GMM_Normalize_Double(p_ask, pdigits)
            pos_sl     = GMM_Normalize_Double(p_ask - (GMM_DEFAULT_SL * ppoint), pdigits) if (GMM_DEFAULT_SL > 0) else 0.0
            pos_tp     = GMM_Normalize_Double(p_ask + (GMM_DEFAULT_TP * ppoint), pdigits) if (GMM_DEFAULT_TP > 0) else 0.0
            col_msg    = Fore.GREEN
        else:
            order_type = mt5.ORDER_TYPE_SELL
            useprice   = GMM_Normalize_Double(p_bid, pdigits)
            pos_sl     = GMM_Normalize_Double(p_bid + (GMM_DEFAULT_SL * ppoint), pdigits) if (GMM_DEFAULT_SL > 0) else 0.0
            pos_tp     = GMM_Normalize_Double(p_bid - (GMM_DEFAULT_TP * ppoint), pdigits) if (GMM_DEFAULT_TP > 0) else 0.0
            col_msg    = Fore.RED

    #5. Clean Request Structure
        request = {
                    "action"      : mt5.TRADE_ACTION_DEAL, # Direct call trade action deal
                    "symbol"      : pair,
                    "volume"      : GMM_Normalize_Double(p_volume, vol_digit),
                    "type"        : order_type,
                    "price"       : useprice,
                    "sl"          : pos_sl,
                    "tp"          : pos_tp,
                    "deviation"   : GMM_DEVIATION,
                    "magic"       : GMM_MAGIC,
                    "comment"     : "GMM_ Open Order",
                    "type_time"   : mt5.ORDER_TIME_GTC,
                    "type_filling": fill_type,
                  }

        #6. Execute Send Order
        result = mt5.order_send(request)
        
        #7. Report to Dashboard (Monitor)
        if result.retcode == mt5.TRADE_RETCODE_DONE:
            print(f"{col_msg}GMM_ EXECUTE: Success Open {pair} | Ticket: {result.order}{Style.RESET_ALL}")
            # TIME LOCK: Update PREV_TIME to prevent opening another order on the same bar
            # UPDATE PREV_TIME ONLY IF SUCCESSFUL
            # Take the current bar time to lock this pair specifically
            rates = mt5.copy_rates_from_pos(pair, GMM_LAST_TF, 0, 1)
            current_bar_time = rates[-1]['time']
        
            if req_order_type == mt5.ORDER_TYPE_BUY:
                GMM_PREV_TIME_BUY[pair] = current_bar_time
            else:
                GMM_PREV_TIME_SELL[pair] = current_bar_time
            
            print(f"{Fore.CYAN}GMM_ INFO: {pair} Locked for this Bar.{Style.RESET_ALL}")
            
            time.sleep(0.5)
            
        else:
            print(f"{Fore.YELLOW}GMM_ ERROR: Failed to open {pair}. Code: {result.retcode}{Style.RESET_ALL}")
            

def GMM_Partial_Close_Modify(pair):
    pos_exist = GMM_Get_Position(pair)
    if pos_exist is None: return
    else:
        pos_type    = pos_exist.type
        pos_ticket  = pos_exist.ticket
        pos_open    = pos_exist.price_open
        pos_current = pos_exist.price_current
        pos_volume  = pos_exist.volume
        pos_profit  = pos_exist.profit
        pos_magic   = pos_exist.magic
        
        pair_info   = GMM_Get_Symbol_Info(pair)
        pdigits     = pair_info.digits
        p_vol_step  = pair_info.volume_step
        vol_digit   = GMM_LotDigit(pair)
        p_ask       = pair_info.ask
        p_bid       = pair_info.bid
        ppoint      = pair_info.point
        pos_close_b = GMM_Normalize_Double( pos_open + (GMM_CLS_TRIGGER * ppoint), pdigits)
        pos_close_s = GMM_Normalize_Double(pos_open - (GMM_CLS_TRIGGER * ppoint), pdigits)
        mode_fill   = pair_info.filling_mode
        fill_type   = GMM_Filling_Mode(mode_fill)
        
        p_vol_close = pos_volume * GMM_PARTIAL_PERCENT
        p_vol_min   = pair_info.volume_min
        p_vol_max   = pair_info.volume_max
        if (p_vol_close < p_vol_min):
            p_vol_close = p_vol_min

        if ((pos_type == mt5.ORDER_TYPE_BUY and pos_current > pos_close_b) | (pos_type == mt5.ORDER_TYPE_SELL and pos_current < pos_close_s)):
            if (pos_type == mt5.ORDER_TYPE_BUY):
                req_type, at_price  = mt5.ORDER_TYPE_SELL, p_bid
            elif (pos_type == mt5.ORDER_TYPE_SELL):
                req_type, at_price = mt5.ORDER_TYPE_BUY, p_ask

        if (req_type is not None and at_price is not None):
            request = { 
                        "action"   : mt5.TRADE_ACTION_DEAL, 
                        "symbol"   : pair, 
                        "volume"   : GMM_Normalize_Double(p_vol_close, vol_digit), 
                        "type"     : req_type,
                        "position" : pos_ticket,
                        "price"    : GMM_Normalize_Double(at_price, pdigits),
                        "deviation": GMM_DEVIATION, 
                        "magic"    : pos_magic,
                        "comment"  : "GMM_ Partial Close order", 
                        "type_time": mt5.ORDER_TIME_GTC, 
                        "type_filling": fill_type, 
                      }
            
            # send a close request 
            result=mt5.order_send(request)
            
            # check the execution result 
            if result.retcode == mt5.TRADE_RETCODE_DONE:
            # MENTONG'S GREAT NOTIFICATION!
                print(f"{Fore.CYAN}GMM_ INFO: 50% Volume Cut for {pair} Success! {Style.RESET_ALL}")
                print(f"{Fore.CYAN}>> Remaining Vol: {GMM_Normalize_Double(pos_volume - p_vol_close, 2)} | Ticket: {pos_ticket}{Style.RESET_ALL}")
                time.sleep(1)
            else:
                print(f"{Fore.RED}GMM_ ERROR: Partial Close {pair} Failed. Code: {result.retcode}{Style.RESET_ALL}")

        pos_exist = GMM_Get_Position(pair)
        if pos_exist is None: return
        else:
            pos_type    = pos_exist.type
            pos_ticket  = pos_exist.ticket
            pos_open    = pos_exist.price_open
            pos_current = pos_exist.price_current
            pos_sl      = pos_exist.sl
            pos_tp      = pos_exist.tp
            pos_volume  = pos_exist.volume
            pos_profit  = pos_exist.profit
            pos_magic   = pos_exist.magic

            pair_info   = GMM_Get_Symbol_Info(pair)
            pdigits     = pair_info.digits
            p_vol_step  = pair_info.volume_step
            vol_digit   = GMM_LotDigit(pair)
            p_ask       = pair_info.ask
            p_bid       = pair_info.bid
            ppoint      = pair_info.point
            price_level = p_ask - p_bid

            pos_modi_sl = None
            pos_modi_tp = None
            col_msg = Fore.GREEN if pos_type == mt5.ORDER_TYPE_BUY else Fore.RED

        if (pos_type == mt5.ORDER_TYPE_BUY):
            if ((pos_volume < GMM_LOT_SIZE) and ((pos_sl < pos_open) | (pos_sl == 0)) and (pos_current > pos_open + (GMM_CLS_TRIGGER * ppoint))):
                pos_modi_sl, pos_modi_tp = pos_open + (GMM_BEP_LOCK * ppoint) + price_level, pos_tp + (GMM_CLS_TRIGGER * ppoint) + price_level
            elif ((pos_volume < GMM_LOT_SIZE) and (pos_sl > pos_open) and (pos_current > pos_tp - (GMM_CLS_TRIGGER * ppoint))):
                pos_modi_sl, pos_modi_tp = pos_sl + (GMM_BEP_LOCK * ppoint) + price_level, pos_tp + (GMM_BEP_LOCK * ppoint) + price_level
        elif (pos_type == mt5.ORDER_TYPE_SELL):
            if ((pos_volume < GMM_LOT_SIZE) and ((pos_sl > pos_open) | (pos_sl == 0)) and (pos_current < pos_open - (GMM_CLS_TRIGGER * ppoint))):
                pos_modi_sl, pos_modi_tp = pos_open - (GMM_BEP_LOCK * ppoint) - price_level , pos_tp - (GMM_CLS_TRIGGER * ppoint) - price_level
            elif ((pos_volume < GMM_LOT_SIZE) and (pos_sl > pos_open) and (pos_current > pos_tp - (GMM_CLS_TRIGGER * ppoint))):
                pos_modi_sl, pos_modi_tp = pos_sl - (GMM_BEP_LOCK * ppoint) - price_level, pos_tp - (GMM_BEP_LOCK * ppoint) - price_level

        if (pos_modi_sl is not None and pos_modi_tp is not None):
            request = { 
                        "action"   : mt5.TRADE_ACTION_SLTP,
                        "symbol"   : pair, 
                        "volume"   : GMM_Normalize_Double(pos_volume, vol_digit), 
                        "type"     : pos_type,
                        "position" : pos_ticket,
                        "sl"       : GMM_Normalize_Double(pos_modi_sl, pdigits), 
                        "tp"       : GMM_Normalize_Double(pos_modi_tp, pdigits), 
                        "magic"    : pos_magic,
                        "comment"  : "GMM_ Modify SL/TP order", 
                      }
            
            # send a close request 
            result=mt5.order_send(request)
            
            # check the execution result 
            if result.retcode == mt5.TRADE_RETCODE_DONE:
                print(f"{col_msg}GMM_ EXECUTE: Success Modify Position {pair} | Ticket: {pos_ticket}{Style.RESET_ALL}")
                # Berikan jeda agar server MT5 tidak overload
                time.sleep(0.5) 
            else:
                print(f"{Fore.YELLOW}GMM_ ERROR: Failed to Modify {pair}. Code: {result.retcode}{Style.RESET_ALL}")


def GMM_Order_Close_Open(pair, req_order_type):
    pos_exist = GMM_Get_Position(pair)
    if pos_exist is None: return
    else:
        pos_symbol   = pos_exist.symbol
        pos_type     = pos_exist.type
        pos_ticket   = pos_exist.ticket
        pos_open     = pos_exist.price_open
        pos_current  = pos_exist.price_current
        pos_sl       = pos_exist.sl
        pos_tp       = pos_exist.tp
        pos_volume   = pos_exist.volume
        pos_profit   = pos_exist.profit
        pos_magic    = pos_exist.magic
        
        pair_info   = GMM_Get_Symbol_Info(pair)
        pdigits     = pair_info.digits
        p_vol_step  = pair_info.volume_step
        vol_digit   = GMM_LotDigit(pair)
        p_ask       = pair_info.ask
        p_bid       = pair_info.bid
        ppoint      = pair_info.point
        mode_fill   = pair_info.filling_mode
        fill_type   = GMM_Filling_Mode(mode_fill)

        if (pos_symbol == pair and pos_magic == GMM_MAGIC and pos_profit < 0 and req_order_type != pos_type ):
            if (pos_type == mt5.ORDER_TYPE_SELL): req_type = mt5.ORDER_TYPE_BUY 
            elif (pos_type == mt5.ORDER_TYPE_BUY): req_type = mt5.ORDER_TYPE_SELL 

            if req_type == mt5.ORDER_TYPE_BUY: at_price = p_ask
            else: at_price = p_bid
            col_msg = Fore.GREEN if pos_type == mt5.ORDER_TYPE_BUY else Fore.RED
            
            request = { 
                        "action"   : mt5.TRADE_ACTION_DEAL, 
                        "symbol"   : pair, 
                        "volume"   : GMM_Normalize_Double(pos_volume, vol_digit), 
                        "type"     : req_type,
                        "position" : pos_ticket,
                        "price"    : GMM_Normalize_Double(at_price, pdigits),
                        "deviation": GMM_DEVIATION, 
                        "magic"    : pos_magic,
                        "comment"  : "GMM_ Close order loss", 
                        "type_time": mt5.ORDER_TIME_GTC, 
                        "type_filling": fill_type, 
                      }
            
        # send a close request 
        result=mt5.order_send(request) 
        # check the execution result 
        if result.retcode == mt5.TRADE_RETCODE_DONE:
            print(f"{col_msg}GMM_ EXECUTE: Success Close Position {pair} | Ticket: {result.order}{Style.RESET_ALL}")
            time.sleep(1)
        else:
            print(f"{Fore.YELLOW}GMM_ ERROR: Failed to Close Position {pair}. Code: {result.retcode}{Style.RESET_ALL}")

        # Get the latest symbol data using the GMM_Get_Symbol_Info() function.
        pair_info = GMM_Get_Symbol_Info(pair)
        if pair_info is None: return
        else: # Basic Parameter Preparation 
            p_volume   = GMM_LOT_SIZE
            pdigits    = pair_info.digits
            ppoint     = pair_info.point
            p_vol_step = pair_info.volume_step
            vol_digit  = GMM_LotDigit(pair)
            p_ask      = mt5.symbol_info_tick(pair).ask
            p_bid      = mt5.symbol_info_tick(pair).bid
            mode_fill  = pair_info.filling_mode
            fill_type  = GMM_Filling_Mode(mode_fill)
            p_vol_min  = pair_info.volume_min
            p_vol_max  = pair_info.volume_max
            if (p_volume <= p_vol_min):
                p_volume = p_vol_min * 2.0
            elif (p_volume > p_vol_max):
                p_volume = p_vol_max

        # Determining Order Type, Price, SL, and TP (GMM_ Logic)
        if req_order_type == mt5.ORDER_TYPE_BUY:
            order_type = mt5.ORDER_TYPE_BUY
            useprice   = GMM_Normalize_Double(p_ask, pdigits)
            pos_sl     = GMM_Normalize_Double(p_ask - (GMM_DEFAULT_SL * ppoint), pdigits) if (GMM_DEFAULT_SL > 0) else 0.0
            pos_tp     = GMM_Normalize_Double(p_ask + (GMM_DEFAULT_TP * ppoint), pdigits) if (GMM_DEFAULT_TP > 0) else 0.0
            col_msg    = Fore.GREEN
        else:
            order_type = mt5.ORDER_TYPE_SELL
            useprice   = GMM_Normalize_Double(p_bid, pdigits)
            pos_sl     = GMM_Normalize_Double(p_bid + (GMM_DEFAULT_SL * ppoint), pdigits) if (GMM_DEFAULT_SL > 0) else 0.0
            pos_tp     = GMM_Normalize_Double(p_bid - (GMM_DEFAULT_TP * ppoint), pdigits) if (GMM_DEFAULT_TP > 0) else 0.0
            col_msg    = Fore.RED

        # Clean Request Structure
            request = {
                        "action"      : mt5.TRADE_ACTION_DEAL, # Direct call trade action deal
                        "symbol"      : pair,
                        "volume"      : GMM_Normalize_Double(p_volume, vol_digit),
                        "type"        : order_type,
                        "price"       : useprice,
                        "sl"          : pos_sl,
                        "tp"          : pos_tp,
                        "deviation"   : GMM_DEVIATION,
                        "magic"       : GMM_MAGIC,
                        "comment"     : "GMM_ Open Order",
                        "type_time"   : mt5.ORDER_TIME_GTC,
                        "type_filling": fill_type,
                      }

        #5. Execute Send Order
        result = mt5.order_send(request)
        #6. Report to Dashboard (Monitor)
        if result.retcode == mt5.TRADE_RETCODE_DONE:
            print(f"{col_msg}GMM_ EXECUTE: Success to Open Order Opposite {pair} | Ticket: {result.order}{Style.RESET_ALL}")
            # Take the current bar time to lock this pair specifically
            rates = mt5.copy_rates_from_pos(pair, GMM_LAST_TF, 0, 1)
            current_bar_time = rates[-1]['time']
        
            if req_order_type == mt5.ORDER_TYPE_BUY:
                GMM_PREV_TIME_BUY[pair] = current_bar_time
            else:
                GMM_PREV_TIME_SELL[pair] = current_bar_time
                
            time.sleep(0.5)    
        else:
            print(f"{Fore.YELLOW}GMM_ ERROR: Failed to Order Open Opposite {pair}. Code: {result.retcode}{Style.RESET_ALL}")                
                

def GMM_Get_Symbol_Info(pair):
    # Retrieving complete symbol data from MT5 server
    pair_info = mt5.symbol_info(pair)
    
    # Validation: If the symbol is not found (typo or not in the market watch)
    if pair_info is None:
        print(f"{Fore.RED}GMM_ ERROR: {pair} not found! Check Market Watch.{Style.RESET_ALL}")
        return None
        
    # If the symbol is inactive (disabled by the broker)
    if not pair_info.visible:
        if not mt5.symbol_select(pair, True):
            print(f"{Fore.RED}GMM_ ERROR: Failed to select {pair}{Style.RESET_ALL}")
            return None
            
    # GREAT! Send back the pair_info object data so it can be used in other functions.
    return pair_info


def GMM_Get_Position(pair):
    # Take positions that ONLY match the symbol and GMM_MAGIC
    all_pos = mt5.positions_get(symbol=pair)
    
    if all_pos is None or len(all_pos) == 0:
        return None
        
    # Filter only those belonging to GMM_MAGIC
    gmm_positions = [p for p in all_pos if p.magic == GMM_MAGIC]
    
    if len(gmm_positions) > 0:
        return gmm_positions[-1] # Take the latest
        
    return None

def GMM_Normalize_Double(value, digits):
    # Securing precision so that there are no "garbage" numbers after the decimal point
    # Very useful for Modify SL/TP and Partial Close Volume
    if value is None: return 0.0
    
    context = Decimal(f"1.{'0' * digits}")
    return float(Decimal(str(value)).quantize(context, rounding=ROUND_HALF_UP))

def GMM_LotDigit(pair):
    info = mt5.symbol_info(pair)
    if info is None: return 2
    
    # Rounded to 2 digits so that 0.0099999 becomes 0.01 for sure!
    lots_step = round(info.volume_step, 2)
    ldig = 2 
    
    if lots_step == 0.01: ldig = 2
    elif lots_step == 0.1: ldig = 1
    elif lots_step == 1.0: ldig = 0
        
    return ldig

def GMM_Filling_Mode(order_fill):
    order_fill = mt5.ORDER_FILLING_FOK
    
    if (order_fill == 1):   order_fill = mt5.ORDER_FILLING_FOK
    elif (order_fill == 2): order_fill = mt5.ORDER_FILLING_IOC
    elif (order_fill == 4): order_fill = mt5.ORDER_FILLING_BOC
    
    return order_fill

def GMM_Check_NewBar(pair, tf, order_type):
    global GMM_PREV_TIME_BUY, GMM_PREV_TIME_SELL
    
    # We take just 1 bar (start from 0, total 1)
    rates = mt5.copy_rates_from_pos(pair, tf, 0, 1)
    if rates is None or len(rates) == 0: return False
    
    # Use [-1] to always refer to the latest data in the array
    curr_time = rates[-1]['time'] 
    
    if order_type == mt5.ORDER_TYPE_BUY:
        if pair not in GMM_PREV_TIME_BUY or curr_time != GMM_PREV_TIME_BUY[pair]:
            return True
    else:
        if pair not in GMM_PREV_TIME_SELL or curr_time != GMM_PREV_TIME_SELL[pair]:
            return True
            
    return False


def GMM_Monitoring_Dashboard():
    #1. Take all active positions with our MAGIC
    all_pos = mt5.positions_get()
    if all_pos is None: return

    gmm_active = [p for p in all_pos if p.magic == GMM_MAGIC]
    
    # Header Monitoring Style
    print(f"{Fore.YELLOW}{'='*68}")
    print(f"{Fore.WHITE} GMM MONITORING TERMINAL | Active Positions: {len(gmm_active)} | Time: {time.strftime('%H:%M:%S')}")
    print(f"{Fore.YELLOW}{'='*68}{Style.RESET_ALL}")
    
    header = f"{' '}{'SYMBOL':<10} | {'TYPE':<5} | {' LOT':<6} | {'  OPEN':<10}| {' CURRENT':<9} | {' PROFIT ($)':<12}"
    print(header)
    print("-" * 68)

    total_profit = 0.0
    
    for pos in gmm_active:
        p_type = "BUY" if pos.type == mt5.ORDER_TYPE_BUY else "SELL"
        p_color = Fore.GREEN if pos.profit >= 0.0 else Fore.RED
        s_color = Fore.RED
        if p_type == "BUY": s_color = Fore.GREEN
        
        pair_info   = GMM_Get_Symbol_Info(pos.symbol)
        pdigits     = pair_info.digits
        price_open  = GMM_Normalize_Double(pos.price_open, pdigits)
        price_curr  = GMM_Normalize_Double(pos.price_current, pdigits)
        
        # Calculate Total Profit
        total_profit += pos.profit
        
        # Rows per Pair
        row = f"{' '}{s_color}{pos.symbol:<10} {Fore.WHITE}{'|'} {Fore.YELLOW}{p_type:<5} {Fore.WHITE}{'|'} {' '}{Fore.YELLOW}{pos.volume:<5} {Fore.WHITE}{'|'} {' '}{Fore.YELLOW}{price_open:<8} {Fore.WHITE}{'|'} {' '}{Fore.YELLOW}{price_curr:<9}{Fore.WHITE}{'|'} {p_color}{pos.profit:>12.2f}{Style.RESET_ALL}"
        print(row)

    # 2. Footer Summary
    print("-" * 68)
    total_color = Fore.GREEN if total_profit >= 0 else Fore.RED
    print(f"{' '}{'TOTAL RUNNING PROFIT':<47} : {total_color}$ {total_profit:,.2f}{Style.RESET_ALL}")
    print(f"{Fore.YELLOW}{'='*68}{Style.RESET_ALL}\n")


# --- 5. MAIN LOOP ---
if __name__ == "__main__":
    if not mt5.initialize(): quit()
    selected_tf_id, tf_name = GMM_Select_TF()
    
    try:
        while True:
            GMM_Check_TF_Change(selected_tf_id)
            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
                            # 1. Get the latest data rates to get iTime(0)
                            rates = mt5.copy_rates_from_pos(l["Cross"], selected_tf_id, 0, 1)
                            if rates is None: continue
    
                            # 2. Update CURR_TIME from rates[last index] -> iTime(0)
                            # This should be updated EVERY second so the robot knows whether the bar has changed or not.
                            GMM_CURR_TIME_BUY = rates[-1]['time'] 

                            # 3. Then call the execute function
                            GMM_Manage_Order(l["Cross"], mt5.ORDER_TYPE_BUY)
                            
                        elif "⚠️ UP-REJ-DN" in rA["magnet"]:
                            signal, col = "🔥 TRY TO BUY!", 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
                            # 1. Get the latest data rates to get iTime(0)
                            rates = mt5.copy_rates_from_pos(l["Cross"], selected_tf_id, 0, 1)
                            if rates is None: continue
    
                            # 2. Update CURR_TIME from rates[last index] -> iTime(0)
                            # This should be updated EVERY second so the robot knows whether the bar has changed or not.
                            GMM_CURR_TIME_SELL = rates[-1]['time']
                            
                            GMM_Manage_Order(l["Cross"], mt5.ORDER_TYPE_SELL)
                            
                        elif "⚠️ DN-REJ-UP" in rA["magnet"]:
                            signal, col = "🔥 TRY TO SELL!", 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}")
                    
            GMM_Monitoring_Dashboard() # Appears at the bottom!
            #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(GMM_SLEEP_INTERVAL)
            
    except KeyboardInterrupt:
        print(Fore.RED + "\nDashboard Stopped." + Style.RESET_ALL)
    finally:
        mt5.shutdown()
***Copyright © 2026 3rjfx ~ For educational purposes only.***        

Please download the GMM_MD_MCEA: GMM_MD_MCEA


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

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.

Footnote:
Regarding other ways of applying the Moving Average indicator to expert advisors, you can see the following 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)

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