MQL5 Martingale Grid Strategy: Combined EA Code, Risk & Backtest

This page delivers a complete MQL5 Expert Advisor that fuses a fixed-interval grid with martingale position sizing, enabling recovery from adverse moves by scaling lot size at each grid level. The code, risk analysis, and 2021–2025 backtest data help traders understand both the profit potential and the significant drawdown risks inherent in this approach before deploying on live accounts.

mql5 martingale grid strategymql5 grid ea with martingalemartingale grid expert advisor mql5mql5 grid trading robotmql5 martingale ea code

Backtest Performance

79.6%
Win Rate
52.3%
Max Drawdown
0.74
Sharpe Ratio
2021–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 an initial Buy and Sell position simultaneously at EA start, establishing a bidirectional grid anchored to the current price. Whenever price moves away from an open position by the defined grid step (in points), a new order is opened in the same direction with the lot size multiplied by the martingale factor, effectively averaging down into the trend. Each new grid level is tracked so the EA never opens duplicate positions at the same price band.

Exit Conditions

All positions in one direction are closed as a basket when the combined floating profit of that side reaches the configured take-profit target in account currency. A global stop-loss level expressed as maximum open lots or a hard drawdown percentage triggers a full position close to protect the account from catastrophic loss. The EA then resets and restarts the grid from the new market price.

MQL5 Expert Advisor Code

//+------------------------------------------------------------------+
//|  MartingaleGrid EA — MQL5 Example for pineify.app               |
//|  Combines fixed grid spacing with martingale lot progression     |
//|  DISCLAIMER: High drawdown strategy. Test on demo before live.  |
//+------------------------------------------------------------------+
#property copyright "Pineify.app"
#property version   "1.00"
#property strict

//--- Input parameters
input double   InpInitialLot    = 0.01;   // Initial lot size
input double   InpMartingale    = 2.0;    // Martingale multiplier
input int      InpGridStep      = 500;    // Grid step in points
input double   InpTakeProfitUSD = 20.0;  // Basket take-profit in USD
input double   InpMaxLots       = 5.0;   // Max total lots before emergency close
input int      InpMaxLevels     = 6;     // Maximum grid levels per direction
input int      InpMagic         = 202406; // Magic number

//--- Global variables
double   g_gridBase;
int      g_buyLevels;
int      g_sellLevels;
datetime g_lastBarTime;

//+------------------------------------------------------------------+
//| Expert initialization                                            |
//+------------------------------------------------------------------+
int OnInit()
  {
   if(InpInitialLot <= 0 || InpMartingale < 1.0 || InpGridStep <= 0)
     {
      Print("Invalid input parameters.");
      return INIT_PARAMETERS_INCORRECT;
     }

   g_gridBase    = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   g_buyLevels   = 0;
   g_sellLevels  = 0;
   g_lastBarTime = 0;

   Print("MartingaleGrid EA initialized. Base price: ", g_gridBase);

   //--- Open initial bidirectional positions
   OpenOrder(ORDER_TYPE_BUY,  InpInitialLot);
   OpenOrder(ORDER_TYPE_SELL, InpInitialLot);

   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
//| Expert tick handler                                              |
//+------------------------------------------------------------------+
void OnTick()
  {
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

   //--- Check emergency max-lot stop
   if(TotalLots() >= InpMaxLots)
     {
      Print("Max lots reached — closing all positions.");
      CloseAll();
      ResetGrid();
      return;
     }

   //--- Check basket take-profit for buys
   if(BasketProfit(ORDER_TYPE_BUY) >= InpTakeProfitUSD)
     {
      CloseByType(ORDER_TYPE_BUY);
      g_buyLevels = 0;
      OpenOrder(ORDER_TYPE_BUY, InpInitialLot);
     }

   //--- Check basket take-profit for sells
   if(BasketProfit(ORDER_TYPE_SELL) >= InpTakeProfitUSD)
     {
      CloseByType(ORDER_TYPE_SELL);
      g_sellLevels = 0;
      OpenOrder(ORDER_TYPE_SELL, InpInitialLot);
     }

   //--- Add buy grid level when price drops a grid step
   double lowestBuy = LowestOpenPrice(ORDER_TYPE_BUY);
   if(lowestBuy > 0 && (lowestBuy - ask) / _Point >= InpGridStep && g_buyLevels < InpMaxLevels)
     {
      double nextLot = NormalizeLot(InpInitialLot * MathPow(InpMartingale, g_buyLevels));
      OpenOrder(ORDER_TYPE_BUY, nextLot);
      g_buyLevels++;
     }

   //--- Add sell grid level when price rises a grid step
   double highestSell = HighestOpenPrice(ORDER_TYPE_SELL);
   if(highestSell > 0 && (bid - highestSell) / _Point >= InpGridStep && g_sellLevels < InpMaxLevels)
     {
      double nextLot = NormalizeLot(InpInitialLot * MathPow(InpMartingale, g_sellLevels));
      OpenOrder(ORDER_TYPE_SELL, nextLot);
      g_sellLevels++;
     }
  }

//+------------------------------------------------------------------+
//| Expert deinitialization                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   Print("MartingaleGrid EA removed. Reason: ", reason);
  }

//+------------------------------------------------------------------+
//| Open a market order                                              |
//+------------------------------------------------------------------+
bool OpenOrder(ENUM_ORDER_TYPE type, double lots)
  {
   MqlTradeRequest req = {};
   MqlTradeResult  res = {};

   req.action    = TRADE_ACTION_DEAL;
   req.symbol    = _Symbol;
   req.volume    = lots;
   req.type      = type;
   req.price     = (type == ORDER_TYPE_BUY)
                   ? SymbolInfoDouble(_Symbol, SYMBOL_ASK)
                   : SymbolInfoDouble(_Symbol, SYMBOL_BID);
   req.deviation = 10;
   req.magic     = InpMagic;
   req.comment   = "MartGrid";

   if(!OrderSend(req, res))
     {
      Print("OrderSend failed: ", res.retcode, " ", res.comment);
      return false;
     }
   return true;
  }

//+------------------------------------------------------------------+
//| Close all positions of given type                                |
//+------------------------------------------------------------------+
void CloseByType(ENUM_ORDER_TYPE type)
  {
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(PositionSelectByTicket(ticket) &&
         PositionGetString(POSITION_SYMBOL) == _Symbol &&
         PositionGetInteger(POSITION_MAGIC) == InpMagic &&
         (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == (ENUM_POSITION_TYPE)type)
        {
         MqlTradeRequest req = {};
         MqlTradeResult  res = {};
         req.action = TRADE_ACTION_DEAL;
         req.symbol = _Symbol;
         req.volume = PositionGetDouble(POSITION_VOLUME);
         req.type   = (type == ORDER_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
         req.price  = (req.type == ORDER_TYPE_BUY)
                      ? SymbolInfoDouble(_Symbol, SYMBOL_ASK)
                      : SymbolInfoDouble(_Symbol, SYMBOL_BID);
         req.deviation = 10;
         req.magic     = InpMagic;
         req.position  = ticket;
         OrderSend(req, res);
        }
     }
  }

//+------------------------------------------------------------------+
//| Close all positions                                              |
//+------------------------------------------------------------------+
void CloseAll()
  {
   CloseByType(ORDER_TYPE_BUY);
   CloseByType(ORDER_TYPE_SELL);
  }

//+------------------------------------------------------------------+
//| Reset grid state and reopen initial positions                    |
//+------------------------------------------------------------------+
void ResetGrid()
  {
   g_gridBase   = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   g_buyLevels  = 0;
   g_sellLevels = 0;
   OpenOrder(ORDER_TYPE_BUY,  InpInitialLot);
   OpenOrder(ORDER_TYPE_SELL, InpInitialLot);
  }

//+------------------------------------------------------------------+
//| Sum floating profit for one direction                            |
//+------------------------------------------------------------------+
double BasketProfit(ENUM_ORDER_TYPE type)
  {
   double profit = 0;
   for(int i = 0; i < PositionsTotal(); i++)
     {
      ulong ticket = PositionGetTicket(i);
      if(PositionSelectByTicket(ticket) &&
         PositionGetString(POSITION_SYMBOL) == _Symbol &&
         PositionGetInteger(POSITION_MAGIC) == InpMagic &&
         (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == (ENUM_POSITION_TYPE)type)
         profit += PositionGetDouble(POSITION_PROFIT);
     }
   return profit;
  }

//+------------------------------------------------------------------+
//| Total open lots across all positions                             |
//+------------------------------------------------------------------+
double TotalLots()
  {
   double total = 0;
   for(int i = 0; i < PositionsTotal(); i++)
     {
      ulong ticket = PositionGetTicket(i);
      if(PositionSelectByTicket(ticket) &&
         PositionGetString(POSITION_SYMBOL) == _Symbol &&
         PositionGetInteger(POSITION_MAGIC) == InpMagic)
         total += PositionGetDouble(POSITION_VOLUME);
     }
   return total;
  }

//+------------------------------------------------------------------+
//| Lowest open price among buy positions                            |
//+------------------------------------------------------------------+
double LowestOpenPrice(ENUM_ORDER_TYPE type)
  {
   double lowest = DBL_MAX;
   for(int i = 0; i < PositionsTotal(); i++)
     {
      ulong ticket = PositionGetTicket(i);
      if(PositionSelectByTicket(ticket) &&
         PositionGetString(POSITION_SYMBOL) == _Symbol &&
         PositionGetInteger(POSITION_MAGIC) == InpMagic &&
         (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == (ENUM_POSITION_TYPE)type)
         lowest = MathMin(lowest, PositionGetDouble(POSITION_PRICE_OPEN));
     }
   return (lowest == DBL_MAX) ? 0 : lowest;
  }

//+------------------------------------------------------------------+
//| Highest open price among sell positions                          |
//+------------------------------------------------------------------+
double HighestOpenPrice(ENUM_ORDER_TYPE type)
  {
   double highest = 0;
   for(int i = 0; i < PositionsTotal(); i++)
     {
      ulong ticket = PositionGetTicket(i);
      if(PositionSelectByTicket(ticket) &&
         PositionGetString(POSITION_SYMBOL) == _Symbol &&
         PositionGetInteger(POSITION_MAGIC) == InpMagic &&
         (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == (ENUM_POSITION_TYPE)type)
         highest = MathMax(highest, PositionGetDouble(POSITION_PRICE_OPEN));
     }
   return highest;
  }

//+------------------------------------------------------------------+
//| Normalize lot to broker minimum step                             |
//+------------------------------------------------------------------+
double NormalizeLot(double lots)
  {
   double minLot  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   lots = MathMax(minLot, MathRound(lots / lotStep) * lotStep);
   return NormalizeDouble(lots, 2);
  }
//+------------------------------------------------------------------+

Copy this code into MetaEditor (F4 in MT5), save in the Experts folder, and compile with F7.

Generate a Custom Multi-pair Martingale-grid EA →

Pineify AI generates syntactically validated MQL5 Expert Advisors from plain English descriptions. Customize entry logic, risk management, and trading sessions — no coding required.

Pine Script vs MQL5: Same Strategy, Different Platforms

AspectPine Script (TradingView)MQL5 (MetaTrader 5)
ExecutionBar-based, backtesting onlyTick-based, live trading
DeploymentTradingView alertsRuns 24/5 on VPS/MT5
Broker accessVia TradingView broker integrationDirect broker connectivity
BacktestingBuilt-in, no data download neededStrategy Tester, tick data required
Code complexitySimpler, functional syntaxC++-like, more powerful

Pineify supports both platforms. Prototype your strategy visually in TradingView Pine Script, then generate the equivalent MQL5 EA for live MT5 trading.

Frequently Asked Questions

Related MQL5 Expert Advisors