🚀 AI One-Click Generation of mql4 Strategy Code

How to Build a Master/Slave EA Architecture in Mql4 with Custom Events

mql4 | Published on: 2025/12/11
The following content is generated by EasyQuant .

Problem Description

I want to build a dashboard that can send buy or sell commands to other trading robots running on different currency pairs. How can I do that?

Solution

To achieve this in MetaTrader 4, the most robust and native method is using Global Variables of the Client Terminal.

Here is the architecture of the solution:

  1. The Dashboard (Master EA): Creates buttons on the chart. When you click a button, it creates a Global Variable (e.g., CMD_EURUSD with a value of 1 for Buy or 2 for Sell).
  2. The Receiver (Slave EA): Runs on the specific currency pair. It constantly checks for the existence of that Global Variable. If found, it executes the trade and resets the variable to 0 to prevent duplicate orders.

Below are the two separate code files required.

1. The Dashboard (Master EA)

Compile this code and attach it to one chart (e.g., XAUUSD or an empty chart). It will control the others.

//+------------------------------------------------------------------+
//|                                            SignalDashboard.mq4   |
//|                                     Copyright 2023, MetaQuotes   |
//|                                             https://mql4.com     |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Quantitative Assistant"
#property link      "https://mql4.com"
#property version   "1.00"
#property strict

//--- Input Parameters
input string   InpSymbols  = "EURUSD,GBPUSD,USDJPY,XAUUSD"; // Comma-separated symbols
input color    ColorBuy    = clrForestGreen;
input color    ColorSell   = clrFireBrick;
input int      BtnWidth    = 80;
input int      BtnHeight   = 30;
input int      X_Start     = 20;
input int      Y_Start     = 50;

//--- Global arrays to store parsed symbols
string symbolArray[];

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   // Parse the comma-separated string into an array
   StringSplit(InpSymbols, ',', symbolArray);
   
   // Create the UI
   CreateDashboard();
   
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   // Clean up all objects created by this EA
   ObjectsDeleteAll(0, "Dash_");
  }

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   // Handle Button Clicks
   if(id == CHARTEVENT_OBJECT_CLICK)
     {
      // Check if the clicked object is one of ours
      if(StringFind(sparam, "Dash_") >= 0)
        {
         // Reset button state immediately (visual effect)
         ObjectSetInteger(0, sparam, OBJPROP_STATE, false);
         
         // Extract command and symbol from name (Format: Dash_CMD_SYMBOL)
         string parts[];
         StringSplit(sparam, '_', parts);
         
         if(ArraySize(parts) == 3)
           {
            string cmdType = parts[1]; // "Buy" or "Sell"
            string sym     = parts[2]; // "EURUSD", etc.
            
            // Define the Global Variable Name
            string gvName = "CMD_" + sym;
            
            // Set Global Variable: 1 for Buy, 2 for Sell
            double signal = (cmdType == "Buy") ? 1.0 : 2.0;
            
            // We use GlobalVariableSet. 
            // The Slave EA will pick this up, execute, and delete/reset it.
            GlobalVariableSet(gvName, signal);
            
            Print("Signal Sent: " + cmdType + " for " + sym);
            PlaySound("tick.wav");
           }
        }
     }
  }

//+------------------------------------------------------------------+
//| Helper: Create the Dashboard UI                                  |
//+------------------------------------------------------------------+
void CreateDashboard()
  {
   int total = ArraySize(symbolArray);
   
   for(int i = 0; i < total; i++)
     {
      string sym = symbolArray[i];
      // Strip whitespace
      StringTrimLeft(sym);
      StringTrimRight(sym);
      
      int y = Y_Start + (i * (BtnHeight + 10));
      
      // Create Label
      CreateLabel("Dash_Lbl_" + sym, sym, X_Start, y + 5);
      
      // Create Buy Button
      CreateButton("Dash_Buy_" + sym, "BUY", X_Start + 80, y, ColorBuy);
      
      // Create Sell Button
      CreateButton("Dash_Sell_" + sym, "SELL", X_Start + 80 + BtnWidth + 10, y, ColorSell);
     }
   
   ChartRedraw();
  }

//+------------------------------------------------------------------+
//| Helper: Create a Button Object                                   |
//+------------------------------------------------------------------+
void CreateButton(string name, string text, int x, int y, color bg)
  {
   if(ObjectFind(0, name) < 0)
      ObjectCreate(0, name, OBJ_BUTTON, 0, 0, 0);
      
   ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
   ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
   ObjectSetInteger(0, name, OBJPROP_XSIZE, BtnWidth);
   ObjectSetInteger(0, name, OBJPROP_YSIZE, BtnHeight);
   ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetString(0, name, OBJPROP_TEXT, text);
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 10);
   ObjectSetInteger(0, name, OBJPROP_COLOR, clrWhite);
   ObjectSetInteger(0, name, OBJPROP_BGCOLOR, bg);
   ObjectSetInteger(0, name, OBJPROP_BORDER_COLOR, clrNONE);
   ObjectSetInteger(0, name, OBJPROP_STATE, false);
  }

//+------------------------------------------------------------------+
//| Helper: Create a Label Object                                    |
//+------------------------------------------------------------------+
void CreateLabel(string name, string text, int x, int y)
  {
   if(ObjectFind(0, name) < 0)
      ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
      
   ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
   ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
   ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetString(0, name, OBJPROP_TEXT, text);
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 10);
   ObjectSetInteger(0, name, OBJPROP_COLOR, clrBlack);
  }
//+------------------------------------------------------------------+

2. The Receiver (Slave EA)

Compile this code and attach it to every chart you want to trade (e.g., open a EURUSD chart and attach it, open a GBPUSD chart and attach it).

//+------------------------------------------------------------------+
//|                                             SignalReceiver.mq4   |
//|                                     Copyright 2023, MetaQuotes   |
//|                                             https://mql4.com     |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Quantitative Assistant"
#property link      "https://mql4.com"
#property version   "1.00"
#property strict

//--- Input Parameters
input double   InpLotSize     = 0.1;
input int      InpStopLoss    = 200; // Points (0 to disable)
input int      InpTakeProfit  = 400; // Points (0 to disable)
input int      InpMagicNum    = 123456;
input int      InpSlippage    = 3;

//--- Global variable name to listen to
string gvName;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   // Define the specific variable name for THIS chart's symbol
   // Example: If attached to EURUSD, it listens for "CMD_EURUSD"
   gvName = "CMD_" + Symbol();
   
   Print("Receiver started. Listening for Global Variable: ", gvName);
   
   // Ensure the variable exists and is 0 to start
   if(!GlobalVariableCheck(gvName))
      GlobalVariableSet(gvName, 0);
      
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   // 1. Check if the Global Variable exists
   if(GlobalVariableCheck(gvName))
     {
      // 2. Get the value
      double signal = GlobalVariableGet(gvName);
      
      // 3. If signal is 0, do nothing (waiting state)
      if(signal == 0) return;
      
      // 4. Process Signal
      if(signal == 1.0) // BUY
        {
         ExecuteTrade(OP_BUY);
        }
      else if(signal == 2.0) // SELL
        {
         ExecuteTrade(OP_SELL);
        }
        
      // 5. Reset the Global Variable to 0 immediately to prevent double entry
      GlobalVariableSet(gvName, 0);
     }
  }

//+------------------------------------------------------------------+
//| Helper: Execute Trade                                            |
//+------------------------------------------------------------------+
void ExecuteTrade(int type)
  {
   double price, sl, tp;
   color clr;
   
   if(type == OP_BUY)
     {
      price = Ask;
      sl = (InpStopLoss > 0) ? Ask - InpStopLoss * Point : 0;
      tp = (InpTakeProfit > 0) ? Ask + InpTakeProfit * Point : 0;
      clr = clrBlue;
     }
   else
     {
      price = Bid;
      sl = (InpStopLoss > 0) ? Bid + InpStopLoss * Point : 0;
      tp = (InpTakeProfit > 0) ? Bid - InpTakeProfit * Point : 0;
      clr = clrRed;
     }

   // Normalize prices
   price = NormalizeDouble(price, Digits);
   sl    = NormalizeDouble(sl, Digits);
   tp    = NormalizeDouble(tp, Digits);

   // Send Order
   int ticket = OrderSend(Symbol(), type, InpLotSize, price, InpSlippage, sl, tp, "Dashboard Trade", InpMagicNum, 0, clr);
   
   if(ticket > 0)
      Print("Order Opened Successfully. Ticket: ", ticket);
   else
      Print("Error Opening Order: ", GetLastError());
  }
//+------------------------------------------------------------------+

How to Setup and Use

  1. Compile: Open MetaEditor, create the two files above, paste the code, and compile both.
  2. Setup Slave EAs:
    • Open a chart for EURUSD. Attach SignalReceiver.mq4.
    • Open a chart for GBPUSD. Attach SignalReceiver.mq4.
    • (Repeat for any pairs you want to trade).
  3. Setup Master EA:
    • Open a separate chart (e.g., XAUUSD or just a blank chart).
    • Attach SignalDashboard.mq4.
    • In the inputs, ensure InpSymbols matches the charts you opened in Step 2 (e.g., "EURUSD,GBPUSD").
  4. Trade:
    • Click "BUY" next to EURUSD on the dashboard.
    • The Dashboard sets the Global Variable CMD_EURUSD to 1.
    • The Receiver on the EURUSD chart detects the 1, executes a Buy order, and resets the variable to 0.

Key Technical Details

  • Global Variables: These are stored in the terminal's memory (and saved to disk). They are accessible by all EAs running in that specific terminal instance.
  • StringSplit: This function is used in the Dashboard to dynamically generate buttons based on the list of symbols you provide in the settings.
  • Handshake: The most critical part is GlobalVariableSet(gvName, 0) in the Receiver. Without this, the Receiver would execute a trade on every single tick as long as the variable remained 1.

Q&A

Q: Can I run the Dashboard on a Demo account and the Receiver on a Real account?
A: No, not with this code. Global Variables only work within the same terminal instance. To trade across terminals (or computers), you would need to use File operations (writing/reading CSVs in a shared folder) or Windows Sockets, which is significantly more complex.

Q: What happens if I click the button twice quickly?
A: If the Receiver hasn't processed the first click yet (reset the variable to 0), the second click just overwrites the variable with the same value, resulting in only one trade. If the Receiver has processed it, a second trade will open.

Q: How do I verify the communication is working?
A: In MT4, press F3. This opens the "Global Variables" window. When you click a button on the dashboard, you should briefly see a variable like CMD_EURUSD appear with value 1 or 2, and then almost instantly disappear (or turn to 0) as the Receiver processes it.