Complete .mq5 source code

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

This educational hedging-account EA opens a fixed-volume buy and sell on one chart symbol, then adds buys after downward moves and sells after upward moves. It demonstrates per-position targets, spacing from actual fills, and a latched account-equity trigger. It is not a tested multi-pair portfolio or a production-ready loss-control system. No reproducible report supports the former 2020–2025 statistics.

Strategy logic

Entry conditions

The first tick seeds one buy and one sell. Subsequent buys require the ask to fall at least GridStep points below the last successful buy fill; sells require the bid to rise that distance above the last sell fill. Orders execute at available market prices rather than pending grid prices. LotSize is fixed, with no equity-based sizing. MaxGridLevels includes the initial order and caps successful entries per direction for the current initialization, not merely concurrently open positions. Closed targets do not replenish that entry budget. Netting accounts are rejected because simultaneous long/short tickets are required.

Exit conditions

Each position requests a target at TakeProfit points, which is a separate input from GridStep. There are no individual broker-side stop-losses. A tick-level guard compares whole-account balance minus equity with current balance. When its threshold is reached, the EA latches off new entries and repeatedly requests closure of matching chart-symbol/magic positions; it does not close other strategies or guarantee a maximum loss. The guard is not peak-equity drawdown or cumulative realized-loss tracking. Restarting clears in-memory state, and initialization is rejected if matching positions already exist. A failed seed order also latches off and closes any successful seed leg. Normal target exits do not automatically restart the grid.

MQL5 Expert Advisor Code

//+------------------------------------------------------------------+
//|  GridTradingEA.mq5                                               |
//|  MQL5 Grid Trading Expert Advisor                                |
//|  Single-symbol hedging example; no documented backtest           |
//|  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 successful entries per direction, including seed
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;
double       g_lastBuyFill = 0;
double       g_lastSellFill = 0;
bool         g_stopped = false;
bool         g_gridInitialized = false;
int          g_buyLevels  = 0;
int          g_sellLevels = 0;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   if(GridStep <= 0 || LotSize <= 0 || MaxGridLevels < 1 || TakeProfit <= 0 ||
      MaxDrawdownPct <= 0 || MaxDrawdownPct >= 100 || MagicNumber < 0)
      return INIT_PARAMETERS_INCORRECT;
   if(AccountInfoInteger(ACCOUNT_MARGIN_MODE) != ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
      return INIT_PARAMETERS_INCORRECT;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      if(PositionGetTicket(i) == 0) continue;
      if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
         PositionGetInteger(POSITION_MAGIC) == MagicNumber)
        {
         Print("Matching positions exist; reconcile them before starting this template.");
         return INIT_FAILED;
        }
     }
   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()) g_stopped = true;
   if(g_stopped)
     {
      CloseAllGridOrders();
      // Latched: do not automatically seed a new basket after realizing a loss.
      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
      bool buyOk = PlaceOrder(ORDER_TYPE_BUY, g_initialPrice);
      if(buyOk) g_buyLevels = 1;
      bool sellOk = buyOk && PlaceOrder(ORDER_TYPE_SELL, g_initialPrice);
      if(sellOk) g_sellLevels = 1;
      if(!buyOk || !sellOk) { g_stopped = true; CloseAllGridOrders(); }
      return;
     }

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

   if(currentAsk <= g_lastBuyFill - g_gridStep && g_buyLevels < MaxGridLevels)
      if(PlaceOrder(ORDER_TYPE_BUY, currentAsk)) g_buyLevels++;
   if(currentBid >= g_lastSellFill + g_gridStep && g_sellLevels < MaxGridLevels)
      if(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;
   request.type_filling = ORDER_FILLING_FOK;

   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;
     }

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

//+------------------------------------------------------------------+
//| 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.position = ticket;
      req.type_filling = ORDER_FILLING_FOK;
      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) || res.retcode != TRADE_RETCODE_DONE)
         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 entry, exit, position, order, and risk rules 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 accessStrategy orders are simulated; live automation needs a separate 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.

Frequently Asked Questions

Related MQL5 pages

Risk and testing note

These educational examples require independent compilation and testing. A compile pass or an illustrative setting does not establish execution correctness or profitability. Any reported performance must be supported by reproducible data and test settings; past results do not predict future returns. Trading involves significant risk of loss. This content is not financial advice.