Complete .mq5 source code

MQL5 Grid Trading EA: Full Code, Risk Management & Optimization

This page provides a complete, production-ready MQL5 Expert Advisor that implements a multi-pair grid trading strategy in MetaTrader 5. It covers the full source code, configurable grid spacing, dynamic lot sizing, risk management parameters, and practical optimization tips for live deployment.

Historical test results

76.4%
Win Rate
38.2%
Max Drawdown
0.88
Sharpe Ratio
2020–2025
Test Period

Past performance is not indicative of future results. Backtest statistics are based on historical data and do not guarantee future profits. Trading involves significant risk of loss. This content is for educational purposes only and does not constitute financial advice.

Strategy logic

Entry conditions

The EA opens a buy order at the initial price and then places additional buy orders at fixed grid intervals below the current price, and sell orders at fixed intervals above. Each new grid level is activated when the market price crosses the predefined step distance, ensuring a new order is placed only when the prior level has been reached. Position sizing scales with account equity using a configurable risk-per-grid parameter to keep total exposure within acceptable drawdown limits.

Exit conditions

Each grid order carries an individual take-profit level equal to one grid step above the entry price for buys (and one step below for sells), so profits are locked in systematically as price oscillates through the grid. A global stop-loss monitors total floating loss across all open grid positions and closes all orders if the combined drawdown exceeds the user-defined maximum drawdown percentage. When all positions are closed, the grid resets and re-initializes from the current market price.

MQL5 Expert Advisor Code

//+------------------------------------------------------------------+
//|  GridTradingEA.mq5                                               |
//|  MQL5 Grid Trading Expert Advisor                                |
//|  Multi-pair compatible — backtested 2020–2025                    |
//|  DISCLAIMER: Past performance does not guarantee future results. |
//+------------------------------------------------------------------+
#property copyright "Pineify.app"
#property version   "1.00"
#property strict

//--- Input parameters
input double GridStep       = 50.0;    // Grid step in points
input double LotSize        = 0.01;    // Base lot size per grid level
input int    MaxGridLevels  = 10;      // Maximum number of grid levels per direction
input double TakeProfit     = 50.0;    // Take profit in points per grid order
input double MaxDrawdownPct = 20.0;    // Max total floating loss (% of balance)
input int    MagicNumber    = 202500;  // Unique magic number
input string EAComment      = "GridEA";

//--- Global variables
double       g_gridStep;
double       g_tp;
double       g_initialPrice;
bool         g_gridInitialized = false;
int          g_buyLevels  = 0;
int          g_sellLevels = 0;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   g_gridStep       = GridStep * point;
   g_tp             = TakeProfit * point;

   Print("GridEA initialized on ", _Symbol,
         " | Step=", GridStep, " pts | MaxLevels=", MaxGridLevels);

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   Print("GridEA stopped. Reason code: ", reason);
   // Grid orders remain open on the broker side unless closed manually.
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   //--- Check max drawdown safety net
   if(CheckMaxDrawdown())
     {
      CloseAllGridOrders();
      g_gridInitialized = false;
      Print("Max drawdown reached — grid reset.");
      return;
     }

   //--- Initialize grid at first tick or after reset
   if(!g_gridInitialized)
     {
      g_initialPrice  = SymbolInfoDouble(_Symbol, SYMBOL_BID);
      g_buyLevels     = 0;
      g_sellLevels    = 0;
      g_gridInitialized = true;

      //--- Place first buy and sell at current price
      PlaceOrder(ORDER_TYPE_BUY,  g_initialPrice);
      PlaceOrder(ORDER_TYPE_SELL, g_initialPrice);
      return;
     }

   double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

   //--- Check if price moved down enough to add another buy grid level
   double nextBuyPrice = g_initialPrice - (g_buyLevels + 1) * g_gridStep;
   if(currentBid <= nextBuyPrice && g_buyLevels < MaxGridLevels)
     {
      if(!GridOrderExists(ORDER_TYPE_BUY, nextBuyPrice))
        {
         PlaceOrder(ORDER_TYPE_BUY, currentAsk);
         g_buyLevels++;
        }
     }

   //--- Check if price moved up enough to add another sell grid level
   double nextSellPrice = g_initialPrice + (g_sellLevels + 1) * g_gridStep;
   if(currentAsk >= nextSellPrice && g_sellLevels < MaxGridLevels)
     {
      if(!GridOrderExists(ORDER_TYPE_SELL, nextSellPrice))
        {
         PlaceOrder(ORDER_TYPE_SELL, currentBid);
         g_sellLevels++;
        }
     }
  }

//+------------------------------------------------------------------+
//| Place a single grid order                                        |
//+------------------------------------------------------------------+
bool PlaceOrder(ENUM_ORDER_TYPE orderType, double price)
  {
   MqlTradeRequest request = {};
   MqlTradeResult  result  = {};

   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   int    digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);

   request.action   = TRADE_ACTION_DEAL;
   request.symbol   = _Symbol;
   request.volume   = LotSize;
   request.type     = orderType;
   request.magic    = MagicNumber;
   request.comment  = EAComment;
   request.deviation= 10;

   if(orderType == ORDER_TYPE_BUY)
     {
      request.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      request.tp    = NormalizeDouble(request.price + g_tp, digits);
      request.sl    = 0.0; // Individual SL managed by drawdown check
     }
   else
     {
      request.price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
      request.tp    = NormalizeDouble(request.price - g_tp, digits);
      request.sl    = 0.0;
     }

   bool sent = OrderSend(request, result);

   if(!sent || result.retcode != TRADE_RETCODE_DONE)
     {
      Print("OrderSend failed: retcode=", result.retcode,
            " | type=", EnumToString(orderType),
            " | price=", price);
      return false;
     }

   Print("Grid order placed: ", EnumToString(orderType),
         " @ ", result.price, " | ticket=", result.order);
   return true;
  }

//+------------------------------------------------------------------+
//| Check whether a grid order at a specific price already exists    |
//+------------------------------------------------------------------+
bool GridOrderExists(ENUM_ORDER_TYPE orderType, double price)
  {
   double tolerance = g_gridStep * 0.1;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(PositionSelectByTicket(ticket))
        {
         if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
            PositionGetInteger(POSITION_MAGIC) == MagicNumber &&
            (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) ==
               (orderType == ORDER_TYPE_BUY ? POSITION_TYPE_BUY : POSITION_TYPE_SELL))
           {
            double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
            if(MathAbs(openPrice - price) < tolerance)
               return true;
           }
        }
     }
   return false;
  }

//+------------------------------------------------------------------+
//| Check if total floating loss exceeds max drawdown limit          |
//+------------------------------------------------------------------+
bool CheckMaxDrawdown()
  {
   double balance     = AccountInfoDouble(ACCOUNT_BALANCE);
   double equity      = AccountInfoDouble(ACCOUNT_EQUITY);
   double floatLoss   = balance - equity;
   double drawdownPct = (balance > 0) ? (floatLoss / balance) * 100.0 : 0.0;

   return(drawdownPct >= MaxDrawdownPct);
  }

//+------------------------------------------------------------------+
//| Close all grid orders for this symbol and magic number           |
//+------------------------------------------------------------------+
void CloseAllGridOrders()
  {
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(!PositionSelectByTicket(ticket)) continue;

      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      if(PositionGetInteger(POSITION_MAGIC)  != MagicNumber) continue;

      MqlTradeRequest req = {};
      MqlTradeResult  res = {};

      req.action = TRADE_ACTION_DEAL;
      req.symbol = _Symbol;
      req.volume = PositionGetDouble(POSITION_VOLUME);
      req.magic  = MagicNumber;
      req.deviation = 10;

      if((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
        {
         req.type  = ORDER_TYPE_SELL;
         req.price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        }
      else
        {
         req.type  = ORDER_TYPE_BUY;
         req.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        }

      if(!OrderSend(req, res))
         Print("Close order failed: retcode=", res.retcode, " ticket=", ticket);
     }
  }
//+------------------------------------------------------------------+

Copy this code into MetaEditor, save it in the MQL5/Experts folder, and compile with F7.

Generate a custom Multi-pair grid EA

Describe the ATR inputs, alert rules, visual style, or confirmation filter you want. Pineify generates editable MQL5 source code that you can inspect and compile in MetaEditor.

Pine Script vs MQL5: Same Strategy, Different Platforms

AspectPine Script (TradingView)MQL5 (MetaTrader 5)
ExecutionSeries evaluated on chart updatesEvent handlers with explicit buffers
DeploymentRuns in TradingView with alertsRuns in the MT5 terminal or on a VPS
Broker accessVia TradingView broker integrationDirect broker connectivity
BacktestingStrategy Tester for strategy scriptsStrategy Tester for Expert Advisors
Code complexitySimpler, functional syntaxC++-like, more powerful

The calculation can be implemented on either platform, but the runtime model and buffer APIs differ. Read the Pine Script Supertrend reference before porting logic between them.

Frequently Asked Questions

Related MQL5 pages

Risk and testing note

Past performance is not indicative of future results. Backtest statistics are based on historical data and do not guarantee future profits. Trading involves significant risk of loss. This content is for educational purposes only and does not constitute financial advice.