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.
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 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, save it in the MQL5/Experts folder, and compile with F7.
Generate a custom Multi-pair martingale 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.