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.
Backtest Performance
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
| Aspect | Pine Script (TradingView) | MQL5 (MetaTrader 5) |
|---|---|---|
| Execution | Bar-based, backtesting only | Tick-based, live trading |
| Deployment | TradingView alerts | Runs 24/5 on VPS/MT5 |
| Broker access | Via TradingView broker integration | Direct broker connectivity |
| Backtesting | Built-in, no data download needed | Strategy Tester, tick data required |
| Code complexity | Simpler, functional syntax | C++-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.