MQL5 EA for Funded Traders: Rule Review and Risk-Guard Example
This educational H1 EMA/RSI EA illustrates a configurable daily equity guard and approximate position sizing. It is not certified or verified for a funding provider and cannot ensure a challenge pass or protect an account from a breach. Match the exact provider product, account phase, rule version, timezone, and loss formula before adapting any automation. No reproducible 2023–2025 backtest supports the former performance figures on this page.
Strategy logic
Entry conditions
On a new H1 bar with complete data, the EA compares the two completed EMA samples for a crossover and requires completed-bar RSI between the configured bounds. The ATR filter compares the most recent completed ATR with the mean of the last three completed ATR values, including that value. This is not an economic-calendar filter and can allow entries during news. The code avoids new entries while a matching symbol/magic position exists; it does not coordinate risk across separate instances or isolate systems sharing one netting position.
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 |
//| Generic example, not certified for any funding provider. |
//| 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 configured daily threshold
input double DailyLimitFraction = 0.05; // Example daily loss fraction; not a provider rule
input int MagicNumber = 202600; // EA magic number
//--- Indicator handles
int handleFastEMA = INVALID_HANDLE;
int handleSlowEMA = INVALID_HANDLE;
int handleRSI = INVALID_HANDLE;
int handleATR = INVALID_HANDLE;
//--- Daily tracking
double dayStartBalance;
bool tradingHaltedToday;
//+------------------------------------------------------------------+
//| Expert initialization |
//+------------------------------------------------------------------+
int OnInit()
{
if(RiskPercent <= 0 || RiskPercent > 100 || RRRatio <= 0 ||
FastEMA <= 0 || SlowEMA <= FastEMA || RSIPeriod <= 0 || ATRPeriod <= 0 ||
RSILow < 0 || RSIHigh > 100 || RSILow >= RSIHigh ||
ATRVolatilityMult <= 0 || TrailingATRMult <= 0 || DailyLossCutoff <= 0 ||
DailyLossCutoff > 1 || DailyLimitFraction <= 0 || DailyLimitFraction >= 1 || MagicNumber < 0)
return INIT_PARAMETERS_INCORRECT;
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)
{
if(handleFastEMA != INVALID_HANDLE) IndicatorRelease(handleFastEMA);
if(handleSlowEMA != INVALID_HANDLE) IndicatorRelease(handleSlowEMA);
if(handleRSI != INVALID_HANDLE) IndicatorRelease(handleRSI);
if(handleATR != INVALID_HANDLE) 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()
{
if(dayStartBalance <= 0) return true;
double dailyLoss = (dayStartBalance - AccountInfoDouble(ACCOUNT_EQUITY)) / dayStartBalance;
if(dailyLoss >= DailyLimitFraction * DailyLossCutoff) tradingHaltedToday = true;
if(!tradingHaltedToday) return false;
// Keep retrying a failed or partial close while the daily latch is active.
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0 || PositionGetString(POSITION_SYMBOL) != _Symbol ||
PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
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;
req.type_filling = ORDER_FILLING_FOK;
if(!OrderSend(req, res) || res.retcode != TRADE_RETCODE_DONE)
Print("Daily guard close not completed: ", res.retcode, " ", res.comment);
}
return true;
}
//+------------------------------------------------------------------+
//| 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 || tickValue <= 0 || balance <= 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);
if(step <= 0) return 0;
lots = MathFloor(MathMin(maxLot, lots) / step) * step;
if(lots < minLot) return 0;
return NormalizeDouble(lots, 8);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
CheckDailyReset();
if(IsDailyDrawdownBreached()) return;
if(CountOpenPositions() > 0)
{
ManageTrailingStop();
return;
}
static datetime lastBar = 0;
datetime bar = iTime(_Symbol, PERIOD_H1, 0);
if(bar <= 0 || bar == lastBar) return;
double fastEMABuf[2], slowEMABuf[2], rsiBuf[1], atrBuf[3];
if(CopyBuffer(handleFastEMA, 0, 1, 2, fastEMABuf) != 2) return;
if(CopyBuffer(handleSlowEMA, 0, 1, 2, slowEMABuf) != 2) return;
if(CopyBuffer(handleRSI, 0, 1, 1, rsiBuf) != 1) return;
if(CopyBuffer(handleATR, 0, 1, 3, atrBuf) != 3) return;
double fastNow = fastEMABuf[1], fastPrev = fastEMABuf[0];
double slowNow = slowEMABuf[1], slowPrev = slowEMABuf[0];
double rsiNow = rsiBuf[0];
double atrNow = atrBuf[2];
double atrAvg = (atrBuf[0] + atrBuf[1] + atrBuf[2]) / 3.0;
if(atrNow <= 0 || atrAvg <= 0 || atrNow == EMPTY_VALUE) return;
lastBar = bar;
// 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 = ask - 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";
req.type_filling = ORDER_FILLING_FOK;
if(!OrderSend(req, res) ||
(res.retcode != TRADE_RETCODE_DONE && res.retcode != TRADE_RETCODE_DONE_PARTIAL))
Print("BUY OrderSend failed: ", res.retcode);
}
// SELL signal
if(bearCross && rsiNow >= RSILow && rsiNow <= RSIHigh)
{
double sl = bid + 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";
req.type_filling = ORDER_FILLING_FOK;
if(!OrderSend(req, res) ||
(res.retcode != TRADE_RETCODE_DONE && res.retcode != TRADE_RETCODE_DONE_PARTIAL))
Print("SELL OrderSend failed: ", res.retcode);
}
}
//+------------------------------------------------------------------+
//| Trailing stop management |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
double atrBuf[1];
if(CopyBuffer(handleATR, 0, 1, 1, atrBuf) < 1) return;
double atrNow = atrBuf[0];
if(atrNow <= 0 || atrNow == EMPTY_VALUE) return;
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);
if(!OrderSend(req, res) || res.retcode != TRADE_RETCODE_DONE)
Print("Stop update not completed: ", res.retcode, " ", res.comment);
}
}
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);
if(!OrderSend(req, res) || res.retcode != TRADE_RETCODE_DONE)
Print("Stop update not completed: ", res.retcode, " ", res.comment);
}
}
}
}
//+------------------------------------------------------------------+Copy this code into MetaEditor, save it in the MQL5/Experts folder, and compile with F7.
Generate a custom Multi-pair prop-firm EA
Describe the entry, exit, position, order, and risk rules 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 | Strategy orders are simulated; live automation needs a separate 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.
Frequently Asked Questions
Related MQL5 pages
Technical references
Risk and testing note
These educational examples require independent compilation and testing. A compile pass or an illustrative setting does not establish execution correctness or profitability. Any reported performance must be supported by reproducible data and test settings; past results do not predict future returns. Trading involves significant risk of loss. This content is not financial advice.