# Dual Moving Average Crossover EA: Complete MQL4 Source Code
Strategy Overview
This Expert Advisor implements a classic dual moving average crossover strategy with an additional ATR filter to avoid ranging markets. The logic is simple but robust:
When the Fast MA crosses above the Slow MA AND the market shows sufficient volatility (ATR > threshold), the EA opens a BUY position. When the Fast MA crosses below the Slow MA under the same volatility condition, it opens a SELL.
Complete Source Code
```cpp
//+------------------------------------------------------------------+
//| DualMACrossover.mq4 |
//| Generated by EA Wizard |
//| |
//+------------------------------------------------------------------+
#property copyright "EA Code Library"
#property version "1.00"
#property strict
//+------------------------------------------------------------------+
//| Input Parameters |
//+------------------------------------------------------------------+
input double LotSize = 0.01; // Lot size (0.01 to 1.0)
input int FastMAPeriod = 10; // Fast MA period (5-30)
input int SlowMAPeriod = 30; // Slow MA period (20-100)
input int ATRPeriod = 14; // ATR period (10-21)
input double ATRMultiplier = 1.5; // ATR multiplier for filter (1.0-3.0)
input int StopLoss = 300; // Stop loss in points (200-500)
input int TakeProfit = 600; // Take profit in points (400-1000)
input int MagicNumber = 20240601; // EA Magic Number
input bool UseTrailingStop = true; // Enable trailing stop
input int TrailingStart = 200; // Trailing activates at profit (points)
input int TrailingStep = 50; // Trailing step (points)
input int Slippage = 3; // Slippage in points
//+------------------------------------------------------------------+
//| Global Variables |
//+------------------------------------------------------------------+
double lastFastMA = 0, lastSlowMA = 0;
double currentFastMA = 0, currentSlowMA = 0;
datetime lastBarTime = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Validate input parameters
if(FastMAPeriod >= SlowMAPeriod)
{
Print("Error: Fast MA period must be less than Slow MA period");
return INIT_PARAMETERS_INCORRECT;
}
if(ATRMultiplier <= 0)
{
Print("Error: ATR Multiplier must be positive");
return INIT_PARAMETERS_INCORRECT;
}
if(LotSize < MarketInfo(Symbol(), MODE_MINLOT))
{
Print("Warning: Lot size adjusted to minimum: ", MarketInfo(Symbol(), MODE_MINLOT));
LotSize = MarketInfo(Symbol(), MODE_MINLOT);
}
Print("Dual MA Crossover EA initialized. Magic: ", MagicNumber);
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print("Dual MA Crossover EA deinitialized. Reason: ", reason);
}
//+------------------------------------------------------------------+
//| Expert tick function - main logic |
//+------------------------------------------------------------------+
void OnTick()
{
// Check for new bar (only trade on new bar to reduce noise)
if(Time[0] == lastBarTime) return;
lastBarTime = Time[0];
// Calculate moving averages
currentFastMA = iMA(Symbol(), 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
currentSlowMA = iMA(Symbol(), 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
lastFastMA = iMA(Symbol(), 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 2);
lastSlowMA = iMA(Symbol(), 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 2);
// Calculate ATR for volatility filter
double currentATR = iATR(Symbol(), 0, ATRPeriod, 1);
double atrThreshold = ATRMultiplier * currentATR;
// Check if volatility is sufficient (avoid ranging markets)
double currentRange = High[1] - Low[1];
bool sufficientVolatility = (currentRange > atrThreshold);
// Check for crossover signals
bool fastCrossAboveSlow = (lastFastMA <= lastSlowMA && currentFastMA > currentSlowMA);
bool fastCrossBelowSlow = (lastFastMA >= lastSlowMA && currentFastMA < currentSlowMA);
// Count existing positions
int buyCount = 0, sellCount = 0;
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol())
{
if(OrderType() == OP_BUY) buyCount++;
if(OrderType() == OP_SELL) sellCount++;
}
}
}
// Entry logic
if(fastCrossAboveSlow && sufficientVolatility && buyCount == 0)
{
CloseAllPositions(); // Close opposite positions first
OpenBuy();
Print("BUY signal: Fast MA crossed above Slow MA. Volatility: ", currentRange, " > Threshold: ", atrThreshold);
}
else if(fastCrossBelowSlow && sufficientVolatility && sellCount == 0)
{
CloseAllPositions();
OpenSell();
Print("SELL signal: Fast MA crossed below Slow MA. Volatility: ", currentRange, " > Threshold: ", atrThreshold);
}
// Trailing stop management
if(UseTrailingStop)
{
TrailingStop();
}
}
//+------------------------------------------------------------------+
//| Open Buy Order Function |
//+------------------------------------------------------------------+
void OpenBuy()
{
double ask = Ask;
double sl = 0, tp = 0;
if(StopLoss > 0) sl = ask - StopLoss * Point;
if(TakeProfit > 0) tp = ask + TakeProfit * Point;
int ticket = OrderSend(Symbol(), OP_BUY, LotSize, ask, Slippage, sl, tp,
"Dual MA Crossover", MagicNumber, 0, clrGreen);
if(ticket > 0)
{
Print("BUY order opened. Ticket: ", ticket, " Price: ", ask);
}
else
{
Print("BUY order failed. Error: ", GetLastError());
}
}
//+------------------------------------------------------------------+
//| Open Sell Order Function |
//+------------------------------------------------------------------+
void OpenSell()
{
double bid = Bid;
double sl = 0, tp = 0;
if(StopLoss > 0) sl = bid + StopLoss * Point;
if(TakeProfit > 0) tp = bid - TakeProfit * Point;
int ticket = OrderSend(Symbol(), OP_SELL, LotSize, bid, Slippage, sl, tp,
"Dual MA Crossover", MagicNumber, 0, clrRed);
if(ticket > 0)
{
Print("SELL order opened. Ticket: ", ticket, " Price: ", bid);
}
else
{
Print("SELL order failed. Error: ", GetLastError());
}
}
//+------------------------------------------------------------------+
//| Close All Positions Function |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol())
{
bool closeResult = false;
if(OrderType() == OP_BUY)
closeResult = OrderClose(OrderTicket(), OrderLots(), Bid, Slippage, clrWhite);
else if(OrderType() == OP_SELL)
closeResult = OrderClose(OrderTicket(), OrderLots(), Ask, Slippage, clrWhite);
if(closeResult)
Print("Closed position. Ticket: ", OrderTicket());
else
Print("Failed to close position. Ticket: ", OrderTicket(), " Error: ", GetLastError());
}
}
}
}
//+------------------------------------------------------------------+
//| Trailing Stop Function |
//+------------------------------------------------------------------+
void TrailingStop()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol())
{
if(OrderType() == OP_BUY)
{
double currentProfit = Bid - OrderOpenPrice();
if(currentProfit > TrailingStart * Point)
{
double newStop = Bid - TrailingStep * Point;
if(newStop > OrderStopLoss())
{
bool modify = OrderModify(OrderTicket(), OrderOpenPrice(), newStop, OrderTakeProfit(), 0, clrWhite);
if(modify) Print("BUY trailing stop updated: ", newStop);
}
}
}
else if(OrderType() == OP_SELL)
{
double currentProfit = OrderOpenPrice() - Ask;
if(currentProfit > TrailingStart * Point)
{
double newStop = Ask + TrailingStep * Point;
if(newStop < OrderStopLoss() || OrderStopLoss() == 0)
{
bool modify = OrderModify(OrderTicket(), OrderOpenPrice(), newStop, OrderTakeProfit(), 0, clrWhite);
if(modify) Print("SELL trailing stop updated: ", newStop);
}
}
}
}
}
}
}
```
Parameter Explanation
Parameter Range Default Description
LotSize 0.01-1.0 0.01 Fixed lot size per trade. Ensure sufficient margin
FastMAPeriod 5-30 10 Fast moving average period (shorter = more sensitive)
SlowMAPeriod 20-100 30 Slow moving average period (longer = smoother trend)
ATRPeriod 10-21 14 ATR calculation period
ATRMultiplier 1.0-3.0 1.5 Higher = require more volatility to trade
StopLoss 200-500 300 Stop loss in points (300 = 30 pips)
TakeProfit 400-1000 600 Take profit in points (600 = 60 pips)
MagicNumber any 20240601 Unique ID to identify this EA's orders
UseTrailingStop true/false true Enable trailing stop feature
TrailingStart 100-300 200 Points profit before trailing activates
TrailingStep 25-100 50 Distance to trail behind price
Slippage 1-10 3 Maximum allowable slippage
How to Install and Use
1. Save the code as DualMACrossover.mq4 in the MQL4/Experts/ folder
2. Compile in MetaEditor (F7 key)
3. Attach to chart (drag and drop on any currency pair)
4. Set parameters in Inputs tab
5. Enable AutoTrading (green play button)
Recommended testing: Run on EURUSD or GBPUSD, M15 or H1 timeframe, at least 2 years of backtest data.
Optimization Suggestions
For the best performance on specific pairs:
Currency Pair Fast MA Slow MA ATR Multiplier Stop Loss
EURUSD 10 30 1.5 300
GBPUSD 8 25 1.8 400
USDJPY 12 40 1.3 250
XAUUSD 14 50 2.0 800
Risk Warning
This EA is for educational purposes. Always test thoroughly on demo accounts before live trading. Past performance does not guarantee future results.
Code Explanation for Learning
Key concepts demonstrated:
· iMA() – Moving Average indicator call
· iATR() – ATR indicator for volatility filter
· OrdersTotal() loop – Position management
· OrderSend() – Opening trades
· OrderClose() – Closing trades
· OrderModify() – Modifying SL/TP for trailing stop
· OnTick() new bar detection – Prevents multiple signals on same bar
Want More Advanced EAs?
This dual MA crossover is a foundational strategy. For professional-grade EAs with advanced features (multi-timeframe confirmation, news filter, grid recovery, AI optimization), check our premium collection. Subscribe to receive new EA codes and trading strategies weekly.
Reference: Self-compiled, based on classical moving average crossover trading strategy