Summary: A complete Dual Moving Average Crossover EA for MT4 with ATR filter to avoid sideways markets. Includes Fast MA (5-20), Slow MA (20-100), ATR filter threshold, and full source code with explanations.




# 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:

  • Fast MA (5-20 period) – captures short-term momentum

  • Slow MA (50-200 period) – defines the primary trend

  • ATR Filter – only trades when volatility exceeds threshold, avoiding sideways chop


  • 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