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
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
| Aspect | Pine Script (TradingView) | MQL5 (MetaTrader 5) |
|---|---|---|
| Execution | Series evaluated on chart updates | Event handlers with explicit buffers |
| Deployment | Runs in TradingView with alerts | Runs in the MT5 terminal or on a VPS |
| Broker access | Via TradingView broker integration | Direct broker connectivity |
| Backtesting | Strategy Tester for strategy scripts | Strategy Tester for Expert Advisors |
| Code complexity | Simpler, functional syntax | C++-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.