Complete .mq5 source code

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.

Historical test results

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, save it in the MQL5/Experts folder, and compile with F7.

Generate a custom Multi-pair martingale-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.