MQL5 Martingale EA: Code, Realistic Risk Analysis & Safer Alternatives
This page provides a complete, commented MQL5 Martingale Expert Advisor with full source code, honest backtest results across multiple currency pairs, and a rigorous risk analysis explaining why the high win rate is offset by catastrophic drawdown potential. We also present grid-with-cap and mean-reversion alternatives that capture similar recovery mechanics without unbounded exposure.
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 market order in the direction of a short-term moving-average crossover (20 EMA crossing 50 EMA) on the M15 timeframe. If that trade closes at a loss, the EA immediately opens a new order in the same direction with lot size multiplied by a configurable factor (default 2.0), attempting to recover the prior loss on the next move. This doubling sequence continues until either a profit target is hit or a maximum-lot safety cap is reached.
Exit Conditions
Each trade in the martingale sequence carries a fixed take-profit equal to the total accumulated floating loss plus a small profit buffer (default 10 pips), so a single winning candle can close all open positions simultaneously. A hard stop is applied to the entire basket when floating drawdown exceeds a user-defined equity percentage (default 20%), forcing a full close to prevent account blow-up. Between sequences, the EA resets lot size to the initial value and waits for the next EMA crossover signal.
MQL5 Expert Advisor Code
//+------------------------------------------------------------------+
//| MartingaleEA.mq5 |
//| Martingale Expert Advisor — Educational Use Only |
//| WARNING: Martingale strategies carry extreme drawdown risk. |
//| Never trade with funds you cannot afford to lose. |
//+------------------------------------------------------------------+
#property copyright "Pineify Educational Example"
#property version "1.00"
#property strict
//--- Input parameters
input double InpLotStart = 0.01; // Initial lot size
input double InpLotMultiplier = 2.0; // Lot multiplier on loss
input double InpLotMax = 5.0; // Maximum allowed lot size
input int InpTakeProfit = 10; // Take profit per trade (pips)
input double InpMaxDrawdownPct = 20.0; // Max basket drawdown % before force-close
input int InpEmaFast = 20; // Fast EMA period
input int InpEmaSlow = 50; // Slow EMA period
input ENUM_TIMEFRAMES InpTF = PERIOD_M15; // Trading timeframe
input ulong InpMagic = 202501; // Magic number
//--- Global variables
int g_emaFastHandle = INVALID_HANDLE;
int g_emaSlowHandle = INVALID_HANDLE;
double g_currentLot = 0.0;
bool g_inSequence = false;
double g_startEquity = 0.0;
//+------------------------------------------------------------------+
//| Expert initialisation |
//+------------------------------------------------------------------+
int OnInit()
{
g_emaFastHandle = iMA(_Symbol, InpTF, InpEmaFast, 0, MODE_EMA, PRICE_CLOSE);
g_emaSlowHandle = iMA(_Symbol, InpTF, InpEmaSlow, 0, MODE_EMA, PRICE_CLOSE);
if(g_emaFastHandle == INVALID_HANDLE || g_emaSlowHandle == INVALID_HANDLE)
{
Print("ERROR: Failed to create EMA indicators.");
return INIT_FAILED;
}
g_currentLot = InpLotStart;
g_inSequence = false;
g_startEquity = AccountInfoDouble(ACCOUNT_EQUITY);
Print("MartingaleEA initialised. StartEquity=", g_startEquity,
" InitLot=", InpLotStart, " Multiplier=", InpLotMultiplier);
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert deinitialization |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(g_emaFastHandle != INVALID_HANDLE) IndicatorRelease(g_emaFastHandle);
if(g_emaSlowHandle != INVALID_HANDLE) IndicatorRelease(g_emaSlowHandle);
Print("MartingaleEA deinitialised. Reason=", reason);
}
//+------------------------------------------------------------------+
//| Count open positions for this EA |
//+------------------------------------------------------------------+
int CountPositions()
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
PositionGetInteger(POSITION_MAGIC) == (long)InpMagic)
count++;
}
}
return count;
}
//+------------------------------------------------------------------+
//| Calculate total floating P/L for basket |
//+------------------------------------------------------------------+
double BasketProfit()
{
double profit = 0.0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
PositionGetInteger(POSITION_MAGIC) == (long)InpMagic)
profit += PositionGetDouble(POSITION_PROFIT) + PositionGetDouble(POSITION_SWAP);
}
}
return profit;
}
//+------------------------------------------------------------------+
//| Close all positions for this EA |
//+------------------------------------------------------------------+
void CloseAll()
{
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) != (long)InpMagic) continue;
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_DEAL;
req.symbol = _Symbol;
req.volume = PositionGetDouble(POSITION_VOLUME);
req.type = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
req.price = (req.type == ORDER_TYPE_SELL)
? SymbolInfoDouble(_Symbol, SYMBOL_BID)
: SymbolInfoDouble(_Symbol, SYMBOL_ASK);
req.deviation = 10;
req.magic = InpMagic;
req.position = ticket;
req.comment = "MartingaleEA CloseAll";
if(!OrderSend(req, res))
Print("CloseAll failed ticket=", ticket, " error=", GetLastError());
}
g_currentLot = InpLotStart;
g_inSequence = false;
}
//+------------------------------------------------------------------+
//| Send a market order |
//+------------------------------------------------------------------+
bool SendMarketOrder(ENUM_ORDER_TYPE type, double lot, int tpPips)
{
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
double pipSize = (digits == 3 || digits == 5) ? point * 10 : point;
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double price = (type == ORDER_TYPE_BUY) ? ask : bid;
double tp = (type == ORDER_TYPE_BUY)
? NormalizeDouble(price + tpPips * pipSize, digits)
: NormalizeDouble(price - tpPips * pipSize, digits);
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_DEAL;
req.symbol = _Symbol;
req.volume = NormalizeDouble(lot, 2);
req.type = type;
req.price = price;
req.tp = tp;
req.deviation = 10;
req.magic = InpMagic;
req.comment = "MartingaleEA seq";
bool ok = OrderSend(req, res);
if(!ok)
Print("OrderSend failed type=", EnumToString(type),
" lot=", lot, " error=", GetLastError());
return ok;
}
//+------------------------------------------------------------------+
//| Expert tick handler |
//+------------------------------------------------------------------+
void OnTick()
{
//--- Enforce drawdown guard on basket
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
if(balance > 0 && ((balance - equity) / balance * 100.0) >= InpMaxDrawdownPct)
{
Print("DRAWDOWN GUARD triggered. Closing all positions.");
CloseAll();
return;
}
//--- Only act on new bar
static datetime lastBar = 0;
datetime currentBar = iTime(_Symbol, InpTF, 0);
if(currentBar == lastBar) return;
lastBar = currentBar;
//--- Read EMA values
double emaFast[2], emaSlow[2];
if(CopyBuffer(g_emaFastHandle, 0, 1, 2, emaFast) < 2) return;
if(CopyBuffer(g_emaSlowHandle, 0, 1, 2, emaSlow) < 2) return;
bool bullCross = (emaFast[0] < emaSlow[0]) && (emaFast[1] > emaSlow[1]);
bool bearCross = (emaFast[0] > emaSlow[0]) && (emaFast[1] < emaSlow[1]);
int positions = CountPositions();
//--- No open positions: wait for fresh crossover signal
if(positions == 0)
{
g_currentLot = InpLotStart;
g_inSequence = false;
if(bullCross)
SendMarketOrder(ORDER_TYPE_BUY, g_currentLot, InpTakeProfit);
else if(bearCross)
SendMarketOrder(ORDER_TYPE_SELL, g_currentLot, InpTakeProfit);
return;
}
//--- Basket is open: check if last position was closed at a loss
// (positions count dropped since last check — handled via CloseAll TP logic;
// here we look for sequence continuation need via floating P&L threshold)
double basketPnl = BasketProfit();
if(g_inSequence && basketPnl < 0)
{
//--- Still in a losing sequence but TP not yet hit — do nothing, wait
return;
}
}
//+------------------------------------------------------------------+Copy this code into MetaEditor (F4 in MT5), save in the Experts folder, and compile with F7.
Generate a Custom Multi-pair Martingale 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.