Summary: Professional MT4 EA combining EMA trend detection with ADX strength confirmation. Features multi-timeframe validation, dynamic position sizing, trailing stop, and complete trade management. Optimized for trend-following strategies.




# EMA Trend Strength EA with ADX Confirmation - Complete MQL4 Source Code (2025)

This article provides a fully functional Expert Advisor that combines exponential moving average trend detection with ADX (Average Directional Index) strength filtering. The strategy logic is inspired by multi-indicator filtering approaches that have shown stable performance across various market conditions . Unlike single-indicator systems, this EA requires both trend direction and trend strength confirmation before entering trades.

Strategy Logic



The EA uses two EMA lines (fast and slow) for trend direction identification, and ADX for trend strength validation. A trade is only executed when:

1. The fast EMA crosses above the slow EMA (bullish) OR below (bearish)
2. The ADX value exceeds the threshold (default 25), indicating a strong trend
3. Optional multi-timeframe confirmation checks the higher timeframe trend alignment

This filtering mechanism significantly reduces false signals during ranging markets and has demonstrated approximately 80% win rates in backtests according to community reports .

Complete MQL4 Code



```mql4
//+------------------------------------------------------------------+
//| EMA_Strength_EA.mq4 |
//| Independent Compilation|
//| Based on Multi-Indicator Logic|
//+------------------------------------------------------------------+
#property copyright "AI Assistant"
#property link ""
#property version "1.00"
#property strict

//--- Input Parameters - Trend Settings
input int FastEMAPeriod = 9; // Fast EMA period
input int SlowEMAPeriod = 21; // Slow EMA period
input int ADXPeriod = 14; // ADX period
input double ADXThreshold = 25.0; // ADX trend strength threshold

//--- Input Parameters - Multi-Timeframe
input bool UseMTFConfirmation = true; // Enable multi-timeframe filter
input int HigherTFFactor = 4; // Higher TF multiplier (4 = H1 if M15)
input int HigherTFEMAPeriod = 50; // Higher TF EMA period

//--- Input Parameters - Money Management
input double LotSize = 0.1; // Fixed lot size
input double RiskPercent = 1.0; // Risk % per trade (0=use fixed lot)
input int StopLoss = 60; // Stop loss in pips
input double RiskRewardRatio = 2.0; // Risk:Reward ratio for TP

//--- Input Parameters - Risk Management
input int TrailingStop = 35; // Trailing stop in pips (0=off)
input int MaxSpread = 35; // Maximum spread in pips
input int Slippage = 10; // Maximum slippage
input int MagicNumber = 202511; // EA magic number
input bool CloseOnOppositeSignal = true; // Close on opposite signal

//--- Input Parameters - Trade Limits
input int MaxDailyTrades = 3; // Maximum trades per day
input double MaxDailyLoss = 5.0; // Maximum daily loss % (0=disabled)

//--- Global variables
int pointMultiplier = 10;
datetime lastTradeDate = 0;
int dailyTradeCount = 0;
double dailyStartingBalance = 0;

//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Detect broker digit format
if(Digits == 3 || Digits == 5)
pointMultiplier = 10;
else if(Digits == 2 || Digits == 4)
pointMultiplier = 1;

// Parameter validation
if(FastEMAPeriod >= SlowEMAPeriod)
{
Print("Error: Fast EMA period must be less than Slow EMA period");
return(INIT_PARAMETERS_INCORRECT);
}

if(ADXPeriod < 2)
{
Print("Error: ADX period must be at least 2");
return(INIT_PARAMETERS_INCORRECT);
}

dailyStartingBalance = AccountBalance();
Print("EMA Strength EA initialized successfully");
Print("Fast EMA: ", FastEMAPeriod, " | Slow EMA: ", SlowEMAPeriod);
Print("ADX Threshold: ", ADXThreshold);

return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print("EMA Strength EA removed. Reason: ", reason);
}

//+------------------------------------------------------------------+
//| Check if new day for daily limit reset |
//+------------------------------------------------------------------+
bool IsNewDay()
{
datetime currentTime = TimeCurrent();
MqlDateTime dt;
TimeToStruct(currentTime, dt);
int currentDay = dt.day;

static int lastDay = 0;

if(lastDay != currentDay)
{
lastDay = currentDay;
dailyTradeCount = 0;
dailyStartingBalance = AccountBalance();
return true;
}
return false;
}

//+------------------------------------------------------------------+
//| Check daily loss limit |
//+------------------------------------------------------------------+
bool IsDailyLossExceeded()
{
if(MaxDailyLoss <= 0) return false;

double currentBalance = AccountBalance();
double lossPercent = (dailyStartingBalance - currentBalance) / dailyStartingBalance * 100;

if(lossPercent >= MaxDailyLoss)
{
Print("Daily loss limit reached: ", lossPercent, "%");
return true;
}
return false;
}

//+------------------------------------------------------------------+
//| Check daily trade limit |
//+------------------------------------------------------------------+
bool IsDailyTradeLimitReached()
{
if(MaxDailyTrades <= 0) return false;
return (dailyTradeCount >= MaxDailyTrades);
}

//+------------------------------------------------------------------+
//| Get higher timeframe value (multi-timeframe confirmation) |
//+------------------------------------------------------------------+
double GetHigherTFClose(int shift)
{
ENUM_TIMEFRAMES currentTF = Period();
ENUM_TIMEFRAMES higherTF = (ENUM_TIMEFRAMES)(currentTF * HigherTFFactor);

// Ensure valid timeframe
if(higherTF > PERIOD_MN1)
higherTF = PERIOD_H4;
if(higherTF < PERIOD_M1)
higherTF = PERIOD_H1;

return iClose(Symbol(), higherTF, shift);
}

//+------------------------------------------------------------------+
//| Get higher timeframe EMA value |
//+------------------------------------------------------------------+
double GetHigherTFEMA(int shift)
{
ENUM_TIMEFRAMES currentTF = Period();
ENUM_TIMEFRAMES higherTF = (ENUM_TIMEFRAMES)(currentTF * HigherTFFactor);

if(higherTF > PERIOD_MN1)
higherTF = PERIOD_H4;
if(higherTF < PERIOD_M1)
higherTF = PERIOD_H1;

return iMA(Symbol(), higherTF, HigherTFEMAPeriod, 0, MODE_EMA, PRICE_CLOSE, shift);
}

//+------------------------------------------------------------------+
//| Check multi-timeframe trend alignment |
//+------------------------------------------------------------------+
bool IsMTFAligned(int signalDirection)
{
if(!UseMTFConfirmation) return true;

double higherClose = GetHigherTFClose(1);
double higherEMA = GetHigherTFEMA(1);

bool higherBullish = (higherClose > higherEMA);
bool higherBearish = (higherClose < higherEMA);

if(signalDirection == 1 && higherBullish) return true;
if(signalDirection == -1 && higherBearish) return true;

return false;
}

//+------------------------------------------------------------------+
//| Calculate EMA values and trend direction |
//+------------------------------------------------------------------+
int GetTrendDirection()
{
double fastEMA0 = iMA(Symbol(), 0, FastEMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 0);
double fastEMA1 = iMA(Symbol(), 0, FastEMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 1);
double slowEMA0 = iMA(Symbol(), 0, SlowEMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 0);
double slowEMA1 = iMA(Symbol(), 0, SlowEMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 1);

// Bullish crossover
if(fastEMA1 <= slowEMA1 && fastEMA0 > slowEMA0)
return 1;

// Bearish crossover
if(fastEMA1 >= slowEMA1 && fastEMA0 < slowEMA0)
return -1;

return 0;
}

//+------------------------------------------------------------------+
//| Check ADX trend strength |
//+------------------------------------------------------------------+
bool IsTrendStrong()
{
double adxMain = iADX(Symbol(), 0, ADXPeriod, PRICE_CLOSE, MODE_MAIN, 0);

if(adxMain == EMPTY_VALUE) return false;

return (adxMain >= ADXThreshold);
}

//+------------------------------------------------------------------+
//| Get ADX value for display |
//+------------------------------------------------------------------+
double GetADXValue()
{
return iADX(Symbol(), 0, ADXPeriod, PRICE_CLOSE, MODE_MAIN, 0);
}

//+------------------------------------------------------------------+
//| Calculate dynamic lot size based on risk percentage |
//+------------------------------------------------------------------+
double CalculateLotSize()
{
if(RiskPercent <= 0)
return LotSize;

double accountBalance = AccountBalance();
double riskAmount = accountBalance * RiskPercent / 100.0;

double stopLossPoints = StopLoss * pointMultiplier;
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);

if(tickValue <= 0 || lotStep <= 0)
return LotSize;

double calculatedLot = riskAmount / (stopLossPoints * tickValue);
calculatedLot = MathFloor(calculatedLot / lotStep) * lotStep;

double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);

if(calculatedLot < minLot) calculatedLot = minLot;
if(calculatedLot > maxLot) calculatedLot = maxLot;

return NormalizeDouble(calculatedLot, 2);
}

//+------------------------------------------------------------------+
//| Open market order with calculated SL and TP |
//+------------------------------------------------------------------+
void OpenOrder(int cmd)
{
double price = (cmd == OP_BUY) ? Ask : Bid;
double lot = CalculateLotSize();
double sl = 0, tp = 0;

// Calculate stop loss
if(StopLoss > 0)
{
if(cmd == OP_BUY)
sl = price - StopLoss * Point * pointMultiplier;
else
sl = price + StopLoss * Point * pointMultiplier;
}

// Calculate take profit based on risk-reward ratio
if(RiskRewardRatio > 0 && StopLoss > 0)
{
double tpDistance = StopLoss * RiskRewardRatio;
if(cmd == OP_BUY)
tp = price + tpDistance * Point * pointMultiplier;
else
tp = price - tpDistance * Point * pointMultiplier;
}

int ticket = OrderSend(Symbol(), cmd, lot, price, Slippage, sl, tp, "EMA Strength EA", MagicNumber, 0, clrNONE);

if(ticket < 0)
{
Print("OrderSend failed. Error: ", GetLastError());
}
else
{
Print("Order opened. Ticket: ", ticket, " | Lot: ", lot, " | Direction: ", cmd == OP_BUY ? "BUY" : "SELL");
dailyTradeCount++;
}
}

//+------------------------------------------------------------------+
//| Manage trailing stop for existing positions |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
if(TrailingStop <= 0) return;

double trailPoints = TrailingStop * Point * pointMultiplier;

for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderType() == OP_BUY)
{
double newSL = Bid - trailPoints;
if(newSL > OrderStopLoss() && OrderStopLoss() != 0)
{
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
Print("Trailing stop updated for BUY #", OrderTicket());
}
}
else if(OrderType() == OP_SELL)
{
double newSL = Ask + trailPoints;
if((newSL < OrderStopLoss() || OrderStopLoss() == 0) && newSL > 0)
{
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
Print("Trailing stop updated for SELL #", OrderTicket());
}
}
}
}
}
}

//+------------------------------------------------------------------+
//| Close all positions by type |
//+------------------------------------------------------------------+
void ClosePositionsByType(int type)
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == type)
{
double closePrice = (type == OP_BUY) ? Bid : Ask;
bool closed = OrderClose(OrderTicket(), OrderLots(), closePrice, Slippage, clrNONE);

if(!closed)
Print("Failed to close order ", OrderTicket(), ". Error: ", GetLastError());
}
}
}
}

//+------------------------------------------------------------------+
//| Count open positions by type |
//+------------------------------------------------------------------+
int CountPositions(int type = -1)
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(type == -1 || OrderType() == type)
count++;
}
}
}
return count;
}

//+------------------------------------------------------------------+
//| Check if spread is within limit |
//+------------------------------------------------------------------+
bool IsSpreadOK()
{
if(MaxSpread <= 0) return true;

int currentSpread = (int)((Ask - Bid) / Point / pointMultiplier);
return (currentSpread <= MaxSpread);
}

//+------------------------------------------------------------------+
//| Get signal description for logging |
//+------------------------------------------------------------------+
string GetSignalDescription(int direction)
{
if(direction == 1) return "BUY (EMA Crossover + ADX Strong)";
if(direction == -1) return "SELL (EMA Crossover + ADX Strong)";
return "NO SIGNAL";
}

//+------------------------------------------------------------------+
//| Main tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Reset daily counters if needed
IsNewDay();

// Check daily limits
if(IsDailyLossExceeded())
return;
if(IsDailyTradeLimitReached())
return;

// Check spread
if(!IsSpreadOK())
return;

// Manage trailing stop for existing positions
ManageTrailingStop();

// Only check for new signals on new bar
static datetime lastBarTime = 0;
datetime currentBarTime = iTime(Symbol(), 0, 0);

if(currentBarTime == lastBarTime)
return;
lastBarTime = currentBarTime;

// Get trend direction from EMA crossover
int trendDirection = GetTrendDirection();

// Check ADX strength confirmation
bool strongTrend = IsTrendStrong();

// No signal if no crossover or weak trend
if(trendDirection == 0 || !strongTrend)
return;

// Multi-timeframe confirmation
if(!IsMTFAligned(trendDirection))
{
Print("MTF rejection: Higher timeframe not aligned");
return;
}

// Log signal details
Print("Signal detected: ", GetSignalDescription(trendDirection));
Print("ADX Value: ", DoubleToStr(GetADXValue(), 2));

// Close opposite positions if configured
if(CloseOnOppositeSignal)
{
if(trendDirection == 1)
ClosePositionsByType(OP_SELL);
else if(trendDirection == -1)
ClosePositionsByType(OP_BUY);
}

// Open position if no existing position in same direction
if(trendDirection == 1 && CountPositions(OP_BUY) == 0)
{
OpenOrder(OP_BUY);
}
else if(trendDirection == -1 && CountPositions(OP_SELL) == 0)
{
OpenOrder(OP_SELL);
}
}
//+------------------------------------------------------------------+
```

Parameter Explanation



| Parameter | Description | Recommended |
|-----------|-------------|-------------|
| FastEMAPeriod | Fast exponential moving average period | 9-13 |
| SlowEMAPeriod | Slow exponential moving average period | 21-34 |
| ADXPeriod | ADX calculation period | 14 |
| ADXThreshold | Minimum ADX value for trend strength (20-25 weak, 25-40 strong) | 25 |
| UseMTFConfirmation | Enable higher timeframe trend check | true |
| HigherTFFactor | Multiplier for higher timeframe (4 = H1 if M15) | 4 |
| HigherTFEMAPeriod | EMA period on higher timeframe | 50 |
| LotSize | Fixed lot size (if RiskPercent=0) | 0.01-0.1 |
| RiskPercent | Risk per trade as % of balance | 1.0-2.0 |
| StopLoss | Stop loss distance in pips | 50-80 |
| RiskRewardRatio | Ratio of take profit to stop loss | 1.5-2.5 |
| TrailingStop | Trailing stop distance (0=off) | 30-50 |
| MaxSpread | Maximum allowed spread | 30-40 |
| MaxDailyTrades | Maximum trades per day | 3-5 |
| MaxDailyLoss | Maximum daily loss percentage | 5.0 |

Installation Instructions



1. Copy the code into MetaEditor (press F4 in MT4)
2. Click Compile (F7) - verify zero errors
3. Attach EA to a chart (EURUSD, GBPUSD recommended on M15 or H1)
4. Adjust parameters in Inputs tab
5. Enable AutoTrading (Alt+T)

Strategy Logic Explanation



The core of this EA combines three critical filters :

1. EMA Crossover: The primary entry trigger occurs when the fast EMA (9) crosses above or below the slow EMA (21). This identifies potential trend reversal or continuation points.

2. ADX Confirmation: The Average Directional Index measures trend strength. An ADX reading above 25 indicates a strong trending market . This filter prevents entries during ranging/choppy conditions where EMA crossovers often produce false signals.

3. Multi-Timeframe Alignment: When enabled, the EA checks that the higher timeframe (e.g., H1 if trading on M15) also shows trend alignment. This follows the principle that "trend is your friend" across multiple time horizons .

Compilation & Modification Tips



Key modifications to consider:

  • Adjust FastEMAPeriod and SlowEMAPeriod for different trading styles (shorter = more signals, longer = fewer but higher quality)

  • Lower ADXThreshold to 20 for more frequent entries in moderate trends, or raise to 30 for strong trends only

  • Disable UseMTFConfirmation for pure single-timeframe trading

  • Increase RiskRewardRatio to 3.0 for higher profit targets with lower win rates


  • Best market conditions:
    This trend-following strategy performs best during sustained directional movements. The ADX filter automatically reduces trading during range-bound markets, protecting capital during unfavorable conditions .

    Reference



    This EA source code is independently compiled based on multi-indicator filtering principles documented in community EA collections. The combination of EMA crossover with ADX strength confirmation is a validated approach for trend-following systems with reported win rates of approximately 80% in optimal conditions .

    *For professionally optimized EA strategies with advanced AI-powered market regime detection, complete backtest portfolios, and dedicated support, check our premium EA collection. Subscribe for weekly updates and exclusive trading tools.*