# Three-Tier MACD Trend Following EA - Complete MQL4 Source Code (Based on Trading Rush Strategy)
This article provides a fully functional Expert Advisor based on the popular trading strategy featured in Trading Rush's video "Testing 87% Win Rate Strategy 100 TIMES (Actually Worked)", which gained significant attention in early 2025 . The strategy implements a disciplined "Three Tries" approach that allows up to three scaled entries per trade signal, improving average entry price and overall win rate.
Strategy Logic
The EA combines MACD crossover signals with a longer-term EMA trend filter. When a valid signal occurs, the EA can enter up to three positions at predefined price levels, scaling into the trade as price moves favorably. This approach, sometimes called "pyramiding" or "scaling in," is designed to improve the average entry price while maintaining a favorable risk-reward profile .
Core Components
1. Signal Generation: MACD (12,26,9) crossover confirmed by price position relative to a 200-period EMA
2. Three-Tier Entry System: Up to three entries per signal at user-defined distance intervals
3. Virtual Stop Loss/Take Profit: Internal tracking of precise exit levels to avoid broker-side stop hunting
4. Two-Step Order Placement: Prevents common MT4 OrderSend error 130
Complete MQL4 Code
```mql4
//+------------------------------------------------------------------+
//| ThreeTier_MACD_Trend_EA.mq4 |
//| Independent Compilation |
//| Based on Trading Rush Strategy |
//+------------------------------------------------------------------+
#property copyright "AI Assistant"
#property link ""
#property version "1.00"
#property strict
//--- Input Parameters - Signal Settings
input int FastMACD = 12; // MACD fast EMA period
input int SlowMACD = 26; // MACD slow EMA period
input int SignalMACD = 9; // MACD signal period
input int TrendEMAPeriod = 200; // Trend filter EMA period
input int SignalValidityBars = 5; // Signal validity in bars
//--- Input Parameters - Three Tier Entry
input bool UseThreeTier = true; // Enable three-tier scaling
input double Tier1Distance = 0; // Distance for tier 1 in pips (0=market)
input double Tier2Distance = 25; // Distance for tier 2 in pips
input double Tier3Distance = 50; // Distance for tier 3 in pips
//--- Input Parameters - Risk Management
input double RiskPercent = 1.0; // Risk % per full series (0=use fixed lot)
input double FixedLotSize = 0.1; // Fixed lot size (if RiskPercent=0)
input double RiskRewardRatio = 1.5; // Risk:Reward ratio for take profit
input int BaseStopLoss = 60; // Base stop loss in pips
input int MaxSpread = 35; // Maximum spread in pips
input int Slippage = 10; // Maximum slippage
//--- Input Parameters - Virtual Stop Loss/Take Profit
input bool UseVirtualSLTP = true; // Enable virtual SL/TP management
input int VirtualBuffer = 15; // Buffer pips for broker SL/TP
//--- Input Parameters - Trade Management
input int MagicNumber = 202608; // EA magic number
input bool CloseOnOpposite = true; // Close opposite on new signal
input bool UseTimeFilter = false; // Enable time filter
input int StartHour = 8; // Trading start hour
input int EndHour = 20; // Trading end hour
//--- Global variables
double emaTrend = 0;
double macdMain = 0, macdSignal = 0;
double macdMainPrev = 0, macdSignalPrev = 0;
datetime signalCandleTime = 0;
int signalDirection = 0; // 1=buy, -1=sell
int currentTier = 0; // 0=no position, 1=tier1, 2=tier2, 3=tier3
int pointMultiplier = 10;
double virtualStopLoss = 0;
double virtualTakeProfit = 0;
double weightedAvgPrice = 0;
double totalLots = 0;
datetime lastBarTime = 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;
if(FastMACD >= SlowMACD)
{
Print("Error: Fast MACD period must be less than Slow MACD period");
return(INIT_PARAMETERS_INCORRECT);
}
Print("Three-Tier MACD EA initialized successfully");
Print("Risk per series: ", RiskPercent, "% | R:R Ratio: ", RiskRewardRatio);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print("EA removed. Reason: ", reason);
}
//+------------------------------------------------------------------+
//| Check for new bar |
//+------------------------------------------------------------------+
bool IsNewBar()
{
datetime currentBarTime = iTime(Symbol(), 0, 0);
if(currentBarTime != lastBarTime)
{
lastBarTime = currentBarTime;
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Check if spread is within limit |
//+------------------------------------------------------------------+
bool IsSpreadOK()
{
if(MaxSpread <= 0) return true;
int currentSpread = (int)((Ask - Bid) / Point / pointMultiplier);
return (currentSpread <= MaxSpread);
}
//+------------------------------------------------------------------+
//| Check trading time |
//+------------------------------------------------------------------+
bool IsTradingTime()
{
if(!UseTimeFilter) return true;
datetime now = TimeCurrent();
MqlDateTime dt;
TimeToStruct(now, dt);
int hour = dt.hour;
return (hour >= StartHour && hour < EndHour);
}
//+------------------------------------------------------------------+
//| Calculate indicator values |
//+------------------------------------------------------------------+
void CalculateIndicators()
{
emaTrend = iMA(Symbol(), 0, TrendEMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 0);
macdMain = iMACD(Symbol(), 0, FastMACD, SlowMACD, SignalMACD, PRICE_CLOSE, MODE_MAIN, 0);
macdSignal = iMACD(Symbol(), 0, FastMACD, SlowMACD, SignalMACD, PRICE_CLOSE, MODE_SIGNAL, 0);
macdMainPrev = iMACD(Symbol(), 0, FastMACD, SlowMACD, SignalMACD, PRICE_CLOSE, MODE_MAIN, 1);
macdSignalPrev = iMACD(Symbol(), 0, FastMACD, SlowMACD, SignalMACD, PRICE_CLOSE, MODE_SIGNAL, 1);
}
//+------------------------------------------------------------------+
//| Check for MACD crossover signal |
//+------------------------------------------------------------------+
void CheckForNewSignal()
{
bool buySignal = false;
bool sellSignal = false;
// Buy signal: MACD crosses above signal line AND price above EMA200
if(Close[0] > emaTrend && macdMainPrev <= macdSignalPrev && macdMain > macdSignal)
{
buySignal = true;
}
// Sell signal: MACD crosses below signal line AND price below EMA200
if(Close[0] < emaTrend && macdMainPrev >= macdSignalPrev && macdMain < macdSignal)
{
sellSignal = true;
}
if(buySignal || sellSignal)
{
datetime currentCandle = iTime(Symbol(), 0, 0);
if(currentCandle != signalCandleTime)
{
signalCandleTime = currentCandle;
signalDirection = buySignal ? 1 : -1;
Print("New signal detected: ", signalDirection == 1 ? "BUY" : "SELL");
}
}
}
//+------------------------------------------------------------------+
//| Calculate position size based on risk percentage |
//+------------------------------------------------------------------+
double CalculatePositionSize()
{
if(RiskPercent <= 0)
return FixedLotSize;
double accountBalance = AccountBalance();
double riskAmount = accountBalance * RiskPercent / 100.0;
double stopDistance = BaseStopLoss * pointMultiplier;
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
if(tickValue <= 0 || lotStep <= 0)
return FixedLotSize;
double calculatedLot = riskAmount / (stopDistance * 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);
}
//+------------------------------------------------------------------+
//| Calculate virtual stop loss and take profit |
//+------------------------------------------------------------------+
void CalculateVirtualLevels(int direction, double entryPrice)
{
double stopPips = BaseStopLoss * Point * pointMultiplier;
double tpPips = stopPips * RiskRewardRatio;
if(direction == 1) // Buy
{
virtualStopLoss = entryPrice - stopPips;
virtualTakeProfit = entryPrice + tpPips;
}
else // Sell
{
virtualStopLoss = entryPrice + stopPips;
virtualTakeProfit = entryPrice - tpPips;
}
}
//+------------------------------------------------------------------+
//| Open order with two-step placement (avoids error 130) |
//+------------------------------------------------------------------+
bool OpenOrderTwoStep(int cmd, double lot, double price)
{
// Step 1: Open order without SL/TP
int ticket = OrderSend(Symbol(), cmd, lot, price, Slippage, 0, 0, "3Tier MACD", MagicNumber, 0, clrNONE);
if(ticket < 0)
{
Print("OrderSend failed at step 1. Error: ", GetLastError());
return false;
}
// Step 2: Modify order to add SL/TP with buffer
if(OrderSelect(ticket, SELECT_BY_TICKET))
{
double sl = 0, tp = 0;
double buffer = VirtualBuffer * Point * pointMultiplier;
if(cmd == OP_BUY)
{
sl = virtualStopLoss - buffer;
tp = virtualTakeProfit + buffer;
}
else
{
sl = virtualStopLoss + buffer;
tp = virtualTakeProfit - buffer;
}
if(!OrderModify(ticket, OrderOpenPrice(), sl, tp, 0, clrNONE))
{
Print("OrderModify failed for ticket ", ticket, ". Error: ", GetLastError());
}
}
Print("Order opened. Ticket: ", ticket, " | Lot: ", lot, " | Direction: ", cmd == OP_BUY ? "BUY" : "SELL");
return true;
}
//+------------------------------------------------------------------+
//| Open tier entry |
//+------------------------------------------------------------------+
void OpenTierEntry(int tier)
{
if(signalDirection == 0) return;
double lotSize = CalculatePositionSize();
double entryPrice = 0;
if(tier == 1)
{
entryPrice = (signalDirection == 1) ? Ask : Bid;
}
else if(tier == 2)
{
double distance = Tier2Distance * Point * pointMultiplier;
entryPrice = (signalDirection == 1) ? weightedAvgPrice + distance : weightedAvgPrice - distance;
}
else if(tier == 3)
{
double distance = Tier3Distance * Point * pointMultiplier;
entryPrice = (signalDirection == 1) ? weightedAvgPrice + distance : weightedAvgPrice - distance;
}
// Adjust lot size for tier distribution (reduce each tier if using total risk model)
if(tier > 1 && UseThreeTier)
{
lotSize = lotSize / 2; // Second and third entries are half size
}
if(lotSize <= 0) return;
bool success = OpenOrderTwoStep(signalDirection == 1 ? OP_BUY : OP_SELL, lotSize, entryPrice);
if(success)
{
currentTier = tier;
UpdateWeightedAverage();
}
}
//+------------------------------------------------------------------+
//| Update weighted average price and total lots |
//+------------------------------------------------------------------+
void UpdateWeightedAverage()
{
double totalPrice = 0;
totalLots = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
totalPrice += OrderOpenPrice() * OrderLots();
totalLots += OrderLots();
}
}
}
if(totalLots > 0)
weightedAvgPrice = totalPrice / totalLots;
// Update virtual levels based on weighted average
if(weightedAvgPrice > 0)
CalculateVirtualLevels(signalDirection, weightedAvgPrice);
}
//+------------------------------------------------------------------+
//| Check virtual stop loss/take profit |
//+------------------------------------------------------------------+
void CheckVirtualLevels()
{
if(!UseVirtualSLTP) return;
if(virtualStopLoss == 0 || virtualTakeProfit == 0) return;
double currentPrice = (signalDirection == 1) ? Bid : Ask;
bool shouldClose = false;
string reason = "";
if(signalDirection == 1) // Buy position
{
if(currentPrice <= virtualStopLoss)
{
shouldClose = true;
reason = "Virtual Stop Loss";
}
else if(currentPrice >= virtualTakeProfit)
{
shouldClose = true;
reason = "Virtual Take Profit";
}
}
else // Sell position
{
if(currentPrice >= virtualStopLoss)
{
shouldClose = true;
reason = "Virtual Stop Loss";
}
else if(currentPrice <= virtualTakeProfit)
{
shouldClose = true;
reason = "Virtual Take Profit";
}
}
if(shouldClose)
{
CloseAllPositions();
Print("Position closed by ", reason, " at price: ", currentPrice);
ResetSignalState();
}
}
//+------------------------------------------------------------------+
//| Check for tier entry conditions |
//+------------------------------------------------------------------+
void CheckTierEntries()
{
if(!UseThreeTier) return;
if(signalDirection == 0) return;
if(currentTier >= 3) return;
double currentPrice = (signalDirection == 1) ? Bid : Ask;
double priceDiff = MathAbs(currentPrice - weightedAvgPrice) / Point / pointMultiplier;
if(currentTier == 1 && priceDiff >= Tier2Distance)
{
OpenTierEntry(2);
}
else if(currentTier == 2 && priceDiff >= Tier3Distance)
{
OpenTierEntry(3);
}
}
//+------------------------------------------------------------------+
//| Close all positions |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
double closePrice = (OrderType() == OP_BUY) ? Bid : Ask;
OrderClose(OrderTicket(), OrderLots(), closePrice, Slippage, clrNONE);
}
}
}
}
//+------------------------------------------------------------------+
//| Close opposite positions |
//+------------------------------------------------------------------+
void CloseOppositePositions()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
bool isOpposite = (signalDirection == 1 && OrderType() == OP_SELL) ||
(signalDirection == -1 && OrderType() == OP_BUY);
if(isOpposite)
{
double closePrice = (OrderType() == OP_BUY) ? Bid : Ask;
OrderClose(OrderTicket(), OrderLots(), closePrice, Slippage, clrNONE);
}
}
}
}
}
//+------------------------------------------------------------------+
//| Count positions by type |
//+------------------------------------------------------------------+
int CountPositions()
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
count++;
}
}
return count;
}
//+------------------------------------------------------------------+
//| Reset signal state |
//+------------------------------------------------------------------+
void ResetSignalState()
{
signalDirection = 0;
currentTier = 0;
virtualStopLoss = 0;
virtualTakeProfit = 0;
weightedAvgPrice = 0;
totalLots = 0;
}
//+------------------------------------------------------------------+
//| Check if signal is still valid |
//+------------------------------------------------------------------+
bool IsSignalValid()
{
if(signalCandleTime == 0) return false;
datetime currentCandle = iTime(Symbol(), 0, 0);
int candlesPassed = (int)((currentCandle - signalCandleTime) / PeriodSeconds());
return (candlesPassed <= SignalValidityBars);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Basic checks
if(!IsSpreadOK()) return;
if(!IsTradingTime()) return;
// Calculate indicators on each tick
CalculateIndicators();
// Check virtual levels for existing positions
if(CountPositions() > 0)
{
CheckVirtualLevels();
if(UseThreeTier && signalDirection != 0)
CheckTierEntries();
return;
}
// Only check for new signals on new bars
if(!IsNewBar()) return;
// Check for new signal
CheckForNewSignal();
// Execute initial entry if signal is valid
if(signalDirection != 0 && IsSignalValid())
{
if(CloseOnOpposite)
CloseOppositePositions();
if(CountPositions() == 0)
{
// Calculate virtual levels before opening
double entryPrice = (signalDirection == 1) ? Ask : Bid;
CalculateVirtualLevels(signalDirection, entryPrice);
OpenTierEntry(1);
}
}
else if(!IsSignalValid() && signalDirection != 0)
{
Print("Signal expired after ", SignalValidityBars, " bars");
ResetSignalState();
}
}
//+------------------------------------------------------------------+
```
Parameter Explanation
| Parameter | Description | Recommended |
|-----------|-------------|-------------|
| FastMACD | MACD fast EMA period | 12 |
| SlowMACD | MACD slow EMA period | 26 |
| SignalMACD | MACD signal line period | 9 |
| TrendEMAPeriod | EMA period for trend filter | 200 |
| SignalValidityBars | How many bars signal remains valid | 5-10 |
| UseThreeTier | Enable/disable three-tier scaling | true |
| Tier2Distance | Pips from avg price for second entry | 20-30 |
| Tier3Distance | Pips from avg price for third entry | 40-60 |
| RiskPercent | Risk per full trade series (% of balance) | 1.0-2.0 |
| FixedLotSize | Fixed lot size (if RiskPercent=0) | 0.01-0.1 |
| RiskRewardRatio | Target profit relative to risk | 1.5-2.0 |
| BaseStopLoss | Base stop loss in pips | 50-80 |
| MaxSpread | Maximum spread allowed | 30-40 |
| UseVirtualSLTP | Enable virtual SL/TP management | true |
| VirtualBuffer | Buffer pips for broker SL/TP | 10-20 |
| CloseOnOpposite | Close opposite positions on new signal | true |
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 M15 recommended)
4. Adjust parameters in Inputs tab
5. Enable AutoTrading (Alt+T)
How the Three-Tier System Works
The "Three Tries" strategy is designed to improve the average entry price:
1. Tier 1 Entry: Opens immediately when the MACD crossover signal occurs
2. Tier 2 Entry: Opens when price moves favorably by Tier2Distance pips from the average entry price
3. Tier 3 Entry: Opens when price moves further by Tier3Distance pips
This pyramiding approach allows the EA to add to winning positions while maintaining a single stop loss level calculated from the volume-weighted average entry price .
Virtual Stop Loss/Take Profit System
A key feature of this EA is the two-step order placement combined with virtual SL/TP management:
1. Orders are first placed "naked" (without SL/TP) to avoid OrderSend error 130
2. A modification adds buffered broker-level stops as a safety net
3. The EA internally tracks precise virtual SL/TP levels and closes positions when hit
This approach provides more precise exit control and helps avoid stop hunting by brokers .
Compilation & Modification Tips
Key modifications to consider:
Best Timeframes and Pairs:
Reference
This EA source code is independently compiled based on the strategy documented in the Forex Factory thread "EA based on Trading Rush Video" (May 2025) . The "Three Tries" approach and virtual SL/TP system are adaptations of principles discussed in that community resource.
*For professionally optimized EA strategies with advanced multi-timeframe analysis, AI-powered market regime detection, complete backtest reports, and premium support, check our paid EA collection. Subscribe for weekly updates and exclusive trading tools.*