MQL5 EA for Funded Traders: Prop Firm-Compatible Expert Advisors 2026
This page covers MQL5 Expert Advisors specifically engineered to meet the strict rules of prop trading firms such as FTMO, MyForexFunds, and The5ers — including daily loss limits, maximum drawdown caps, and minimum trading day requirements. The EA strategies discussed prioritize capital preservation and consistent profitability over aggressive returns, helping traders pass evaluation challenges and retain funded 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
Entries are triggered when a short-term EMA (e.g., 20-period) crosses above or below a mid-term EMA (e.g., 50-period) on the H1 timeframe, confirmed by RSI staying between 40–60 to avoid entering during overextended momentum. Additional filters include an ATR-based volatility gate that prevents entries when intraday volatility exceeds a configurable multiple of the 14-period ATR average, reducing the likelihood of getting caught in news-driven spikes that could breach daily loss limits.
Exit Conditions
Each trade carries a fixed risk-reward target of at least 1:1.5, with take-profit set dynamically as a multiple of the current ATR to adapt to market conditions. A trailing stop activates once the position is in profit by one ATR unit, locking in gains while allowing the trade to run. The EA also includes a hard daily loss cutoff: if cumulative daily losses reach 80% of the firm's permitted daily drawdown limit, all positions are closed and trading halts for the remainder of the session.
MQL5 Expert Advisor Code
//+------------------------------------------------------------------+
//| PropFirmEA.mq5 |
//| MQL5 Expert Advisor for Funded/Prop Firm Traders |
//| Compatible with FTMO, The5ers, MyForexFunds challenge rules |
//| Disclaimer: Past backtest results do not guarantee future |
//| performance. Use at your own risk. |
//+------------------------------------------------------------------+
#property copyright "Pineify"
#property version "1.00"
#property strict
//--- Input parameters
input double RiskPercent = 0.5; // Risk per trade (% of balance)
input double RRRatio = 1.5; // Reward-to-risk ratio
input int FastEMA = 20; // Fast EMA period
input int SlowEMA = 50; // Slow EMA period
input int RSIPeriod = 14; // RSI period
input double RSILow = 40.0; // RSI lower filter threshold
input double RSIHigh = 60.0; // RSI upper filter threshold
input int ATRPeriod = 14; // ATR period
input double ATRVolatilityMult = 1.8; // Max ATR multiplier for entry filter
input double TrailingATRMult = 1.0; // Trailing stop ATR multiplier
input double DailyLossCutoff = 0.8; // Fraction of firm daily limit to halt trading
input double FirmDailyLimit = 0.05; // Firm's daily drawdown limit (e.g. 5%)
input int MagicNumber = 202600; // EA magic number
//--- Indicator handles
int handleFastEMA;
int handleSlowEMA;
int handleRSI;
int handleATR;
//--- Daily tracking
double dayStartBalance;
bool tradingHaltedToday;
//+------------------------------------------------------------------+
//| Expert initialization |
//+------------------------------------------------------------------+
int OnInit()
{
handleFastEMA = iMA(_Symbol, PERIOD_H1, FastEMA, 0, MODE_EMA, PRICE_CLOSE);
handleSlowEMA = iMA(_Symbol, PERIOD_H1, SlowEMA, 0, MODE_EMA, PRICE_CLOSE);
handleRSI = iRSI(_Symbol, PERIOD_H1, RSIPeriod, PRICE_CLOSE);
handleATR = iATR(_Symbol, PERIOD_H1, ATRPeriod);
if(handleFastEMA == INVALID_HANDLE ||
handleSlowEMA == INVALID_HANDLE ||
handleRSI == INVALID_HANDLE ||
handleATR == INVALID_HANDLE)
{
Print("ERROR: Failed to create indicator handles.");
return INIT_FAILED;
}
dayStartBalance = AccountInfoDouble(ACCOUNT_BALANCE);
tradingHaltedToday = false;
Print("PropFirmEA initialized. Magic=", MagicNumber);
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert deinitialization |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
IndicatorRelease(handleFastEMA);
IndicatorRelease(handleSlowEMA);
IndicatorRelease(handleRSI);
IndicatorRelease(handleATR);
Print("PropFirmEA deinitialized. Reason=", reason);
}
//+------------------------------------------------------------------+
//| Helper: count open positions for this EA |
//+------------------------------------------------------------------+
int CountOpenPositions()
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 &&
PositionGetString(POSITION_SYMBOL) == _Symbol &&
(int)PositionGetInteger(POSITION_MAGIC) == MagicNumber)
count++;
}
return count;
}
//+------------------------------------------------------------------+
//| Helper: check and reset daily balance on new day |
//+------------------------------------------------------------------+
void CheckDailyReset()
{
static datetime lastDay = 0;
datetime today = StringToTime(TimeToString(TimeCurrent(), TIME_DATE));
if(today != lastDay)
{
dayStartBalance = AccountInfoDouble(ACCOUNT_BALANCE);
tradingHaltedToday = false;
lastDay = today;
}
}
//+------------------------------------------------------------------+
//| Helper: enforce daily drawdown rule |
//+------------------------------------------------------------------+
bool IsDailyDrawdownBreached()
{
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
double dailyLoss = (dayStartBalance - equity) / dayStartBalance;
double cutoffLevel = FirmDailyLimit * DailyLossCutoff;
if(dailyLoss >= cutoffLevel)
{
if(!tradingHaltedToday)
{
Print("Daily loss cutoff reached (", DoubleToString(dailyLoss * 100, 2),
"%). Halting trading for today.");
// Close all positions
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 &&
PositionGetString(POSITION_SYMBOL) == _Symbol &&
(int)PositionGetInteger(POSITION_MAGIC) == MagicNumber)
{
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_DEAL;
req.position = ticket;
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 = MagicNumber;
OrderSend(req, res);
}
}
tradingHaltedToday = true;
}
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Helper: calculate lot size from risk |
//+------------------------------------------------------------------+
double CalcLotSize(double slPoints)
{
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskAmount = balance * RiskPercent / 100.0;
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
if(slPoints <= 0 || tickSize <= 0) return 0;
double slMoney = (slPoints / tickSize) * tickValue;
if(slMoney <= 0) return 0;
double lots = riskAmount / slMoney;
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
lots = MathFloor(lots / step) * step;
lots = MathMax(minLot, MathMin(maxLot, lots));
return lots;
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
CheckDailyReset();
if(IsDailyDrawdownBreached()) return;
if(CountOpenPositions() > 0)
{
ManageTrailingStop();
return;
}
// Pull indicator buffers (need at least 2 bars)
double fastEMABuf[3], slowEMABuf[3], rsiBuf[3], atrBuf[3];
if(CopyBuffer(handleFastEMA, 0, 0, 3, fastEMABuf) < 3) return;
if(CopyBuffer(handleSlowEMA, 0, 0, 3, slowEMABuf) < 3) return;
if(CopyBuffer(handleRSI, 0, 0, 3, rsiBuf) < 3) return;
if(CopyBuffer(handleATR, 0, 0, 3, atrBuf) < 3) return;
double fastNow = fastEMABuf[0], fastPrev = fastEMABuf[1];
double slowNow = slowEMABuf[0], slowPrev = slowEMABuf[1];
double rsiNow = rsiBuf[0];
double atrNow = atrBuf[0];
double atrAvg = (atrBuf[0] + atrBuf[1] + atrBuf[2]) / 3.0;
// Volatility filter
if(atrNow > ATRVolatilityMult * atrAvg) return;
bool bullCross = (fastPrev < slowPrev) && (fastNow >= slowNow);
bool bearCross = (fastPrev > slowPrev) && (fastNow <= slowNow);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
// BUY signal
if(bullCross && rsiNow >= RSILow && rsiNow <= RSIHigh)
{
double sl = bid - atrNow;
double tp = ask + atrNow * RRRatio;
double slPts = ask - sl;
double lots = CalcLotSize(slPts);
if(lots <= 0) return;
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_DEAL;
req.symbol = _Symbol;
req.volume = lots;
req.type = ORDER_TYPE_BUY;
req.price = ask;
req.sl = NormalizeDouble(sl, digits);
req.tp = NormalizeDouble(tp, digits);
req.deviation = 10;
req.magic = MagicNumber;
req.comment = "PropFirmEA_BUY";
if(!OrderSend(req, res))
Print("BUY OrderSend failed: ", res.retcode);
}
// SELL signal
if(bearCross && rsiNow >= RSILow && rsiNow <= RSIHigh)
{
double sl = ask + atrNow;
double tp = bid - atrNow * RRRatio;
double slPts = sl - bid;
double lots = CalcLotSize(slPts);
if(lots <= 0) return;
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_DEAL;
req.symbol = _Symbol;
req.volume = lots;
req.type = ORDER_TYPE_SELL;
req.price = bid;
req.sl = NormalizeDouble(sl, digits);
req.tp = NormalizeDouble(tp, digits);
req.deviation = 10;
req.magic = MagicNumber;
req.comment = "PropFirmEA_SELL";
if(!OrderSend(req, res))
Print("SELL OrderSend failed: ", res.retcode);
}
}
//+------------------------------------------------------------------+
//| Trailing stop management |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
double atrBuf[1];
if(CopyBuffer(handleATR, 0, 0, 1, atrBuf) < 1) return;
double atrNow = atrBuf[0];
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
if((int)PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double curSL = PositionGetDouble(POSITION_SL);
ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(ptype == POSITION_TYPE_BUY)
{
double newSL = NormalizeDouble(bid - TrailingATRMult * atrNow, digits);
if(newSL > curSL && bid > openPrice + atrNow)
{
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_SLTP;
req.position = ticket;
req.symbol = _Symbol;
req.sl = newSL;
req.tp = PositionGetDouble(POSITION_TP);
OrderSend(req, res);
}
}
else if(ptype == POSITION_TYPE_SELL)
{
double newSL = NormalizeDouble(ask + TrailingATRMult * atrNow, digits);
if((curSL == 0 || newSL < curSL) && ask < openPrice - atrNow)
{
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_SLTP;
req.position = ticket;
req.symbol = _Symbol;
req.sl = newSL;
req.tp = PositionGetDouble(POSITION_TP);
OrderSend(req, res);
}
}
}
}
//+------------------------------------------------------------------+Copy this code into MetaEditor (F4 in MT5), save in the Experts folder, and compile with F7.
Generate a Custom Multi-pair Prop-firm 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.