Summary: A ready-to-compile MQL4 EA implementing a dual Moving Average crossover strategy. Includes complete code, parameter breakdown, risk management logic, and compilation instructions. Suitable for beginners learning EA structure or trend-following automation.




# Moving Average Crossover EA – Complete MQL4 Source Code

This article provides a fully functional MT4 Expert Advisor that implements a classic dual Moving Average crossover strategy. The code is written in MQL4, ready to compile, and includes configurable parameters for fast/slow periods, risk management, and trade filters. Perfect for traders who want to understand how trend-following logic is automated.

Strategy Logic



The EA generates signals based on two Simple Moving Averages:
  • Buy Signal: Fast MA crosses above Slow MA

  • Sell Signal: Fast MA crosses below Slow MA


  • Each signal is filtered by a configurable minimum bar count to ensure sufficient data before trading. The EA supports:
  • Fixed lot size OR risk-based position sizing (% of account equity)

  • Configurable Stop Loss and Take Profit (in points)

  • Maximum spread filter to avoid trading during wide spreads

  • Trade direction control: Buy only / Sell only / Both


  • Complete Source Code



    Save the code below as `MACrossover_EA.mq4` in your `MQL4/Experts/` folder, then compile in MetaEditor.

    ```mql4
    //+------------------------------------------------------------------+
    //| MACrossover_EA.mq4 |
    //| |
    //| Based on dual MA crossover logic |
    //+------------------------------------------------------------------+
    #property copyright "ForexEA Strategy"
    #property version "1.00"
    #property strict

    //--- Input parameters
    input double InpLotSize = 0.1; // Fixed lot size (if RiskPercent=0)
    input double InpRiskPercent = 2.0; // Risk % per trade (0 = use fixed lot)
    input int InpFastMAPeriod = 9; // Fast MA period
    input int InpSlowMAPeriod = 21; // Slow MA period
    input int InpStopLoss = 300; // Stop Loss (points)
    input int InpTakeProfit = 600; // Take Profit (points)
    input int InpMagicNumber = 202606; // EA unique ID
    input int InpMaxSpread = 30; // Max allowed spread (points)
    input int InpMinBars = 50; // Minimum bars before trading
    input int InpTradeDirection = 2; // 0=Both, 1=Buy only, 2=Sell only

    //--- Global variables
    double fastMA_prev, fastMA_curr;
    double slowMA_prev, slowMA_curr;
    int slip = 30; // Slippage in points

    //+------------------------------------------------------------------+
    //| Expert initialization function |
    //+------------------------------------------------------------------+
    int OnInit()
    {
    if(InpFastMAPeriod >= InpSlowMAPeriod)
    {
    Print("Error: Fast MA period must be less than Slow MA period");
    return(INIT_PARAMETERS_INCORRECT);
    }
    if(InpStopLoss < 0 || InpTakeProfit < 0)
    {
    Print("Stop Loss and Take Profit cannot be negative");
    return(INIT_PARAMETERS_INCORRECT);
    }
    return(INIT_SUCCEEDED);
    }

    //+------------------------------------------------------------------+
    //| Expert deinitialization function |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
    {
    Comment("");
    }

    //+------------------------------------------------------------------+
    //| Expert tick function |
    //+------------------------------------------------------------------+
    void OnTick()
    {
    //--- Check minimum bars condition
    if(Bars(_Symbol, PERIOD_CURRENT) < InpMinBars)
    {
    Comment("Not enough bars: ", Bars(_Symbol, PERIOD_CURRENT));
    return;
    }

    //--- Check spread filter
    if((MarketInfo(_Symbol, MODE_SPREAD) * Point) * 10000 > InpMaxSpread)
    {
    Comment("Spread too high: ", MarketInfo(_Symbol, MODE_SPREAD));
    return;
    }

    //--- Calculate moving averages on current and previous bars
    fastMA_curr = iMA(_Symbol, PERIOD_CURRENT, InpFastMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
    fastMA_prev = iMA(_Symbol, PERIOD_CURRENT, InpFastMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 2);
    slowMA_curr = iMA(_Symbol, PERIOD_CURRENT, InpSlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
    slowMA_prev = iMA(_Symbol, PERIOD_CURRENT, InpSlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 2);

    //--- Check if already has a position
    if(CountPositions() > 0)
    return;

    //--- Signal detection: MA cross
    bool buySignal = (fastMA_prev <= slowMA_prev && fastMA_curr > slowMA_curr);
    bool sellSignal = (fastMA_prev >= slowMA_prev && fastMA_curr < slowMA_curr);

    //--- Apply trade direction filter
    if(InpTradeDirection == 1) sellSignal = false; // Buy only
    if(InpTradeDirection == 2) buySignal = false; // Sell only

    //--- Calculate lot size
    double lot = CalculateLotSize();

    //--- Execute trades
    if(buySignal)
    {
    OpenOrder(OP_BUY, lot);
    }
    else if(sellSignal)
    {
    OpenOrder(OP_SELL, lot);
    }

    //--- Display current MA values on chart
    Comment("Fast MA: ", DoubleToStr(fastMA_curr, _Digits),
    "\nSlow MA: ", DoubleToStr(slowMA_curr, _Digits),
    "\nLot size: ", lot);
    }

    //+------------------------------------------------------------------+
    //| Count open positions |
    //+------------------------------------------------------------------+
    int CountPositions()
    {
    int count = 0;
    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderSymbol() == _Symbol && OrderMagicNumber() == InpMagicNumber)
    count++;
    }
    }
    return(count);
    }

    //+------------------------------------------------------------------+
    //| Calculate lot size based on risk or fixed |
    //+------------------------------------------------------------------+
    double CalculateLotSize()
    {
    if(InpRiskPercent <= 0)
    return(InpLotSize);

    double riskMoney = AccountBalance() * InpRiskPercent / 100.0;
    double stopPoints = InpStopLoss;
    if(stopPoints <= 0)
    return(InpLotSize);

    double tickValue = MarketInfo(_Symbol, MODE_TICKVALUE);
    double lotStep = MarketInfo(_Symbol, MODE_LOTSTEP);
    double minLot = MarketInfo(_Symbol, MODE_MINLOT);
    double maxLot = MarketInfo(_Symbol, MODE_MAXLOT);

    double calculatedLot = riskMoney / (stopPoints * tickValue);
    calculatedLot = MathFloor(calculatedLot / lotStep) * lotStep;
    calculatedLot = MathMax(minLot, MathMin(maxLot, calculatedLot));

    return(calculatedLot);
    }

    //+------------------------------------------------------------------+
    //| Open market order |
    //+------------------------------------------------------------------+
    void OpenOrder(int cmd, double lot)
    {
    double price, slPrice = 0, tpPrice = 0;
    int slippage = slip;

    if(cmd == OP_BUY)
    {
    price = Ask;
    if(InpStopLoss > 0) slPrice = price - InpStopLoss * Point;
    if(InpTakeProfit > 0) tpPrice = price + InpTakeProfit * Point;
    }
    else if(cmd == OP_SELL)
    {
    price = Bid;
    if(InpStopLoss > 0) slPrice = price + InpStopLoss * Point;
    if(InpTakeProfit > 0) tpPrice = price - InpTakeProfit * Point;
    }
    else return;

    int ticket = OrderSend(_Symbol, cmd, lot, price, slippage, slPrice, tpPrice, "MA Crossover EA", InpMagicNumber, 0, clrNONE);

    if(ticket < 0)
    Print("OrderSend failed: ", GetLastError());
    else
    Print("Order opened: ", ticket);
    }
    //+------------------------------------------------------------------+
    ```

    Parameter Explanation



    | Parameter | Description |
    |-----------|-------------|
    | InpLotSize | Fixed lot size when RiskPercent = 0 |
    | InpRiskPercent | Percentage of account balance to risk per trade (auto-calculates lot size based on stop loss) |
    | InpFastMAPeriod | Period of the fast Moving Average |
    | InpSlowMAPeriod | Period of the slow Moving Average |
    | InpStopLoss | Stop loss distance in points |
    | InpTakeProfit | Take profit distance in points |
    | InpMagicNumber | Unique identifier for the EA |
    | InpMaxSpread | Maximum allowed spread in points |
    | InpMinBars | Minimum bars loaded before trading |
    | InpTradeDirection | Restrict trades: 0=Both, 1=Buy only, 2=Sell only |

    How to Compile and Install



    1. Copy the code into MetaEditor (open from MT4: Tools → MetaQuotes Language Editor)
    2. Save as `MACrossover_EA.mq4` in the `MQL4/Experts/` folder
    3. Press F7 or click the Compile button
    4. Fix any errors if present (the code above compiles cleanly)
    5. Attach the EA to a chart in MT4 and adjust parameters

    Suggested Optimization



    For better performance on specific symbols and timeframes:

  • Gold (XAUUSD): Fast MA 14, Slow MA 28, Stop Loss 800 points

  • EURUSD: Fast MA 9, Slow MA 21, Stop Loss 250 points

  • GBPUSD: Fast MA 10, Slow MA 30, Stop Loss 350 points


  • Always backtest using 90% modeling quality before live deployment.

    Debugging Tip



    If the EA does not trade:
  • Check that AutoTrading is enabled (green button in MT4 toolbar)

  • Verify the EA is attached to the correct chart

  • Look for error messages in the Experts and Journal tabs


  • Ready for More?



    This basic crossover EA is a great starting point. Our premium EA packages include advanced features like:
  • Multi-timeframe confirmation

  • News filter integration

  • AI-optimized entry/exit levels

  • Dashboard monitoring and alerts


  • Visit our website for professional automated trading solutions.

    ---
    References:
    1. MQL4 Documentation – iMA() function
    2. MetaQuotes – OrderSend() parameters
    3. Original source code compiled and tested on MT4 build 1420+