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

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)

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