Build Your First Expert Advisor

Create a simple moving average crossover EA from scratch with entry, exit, and position sizing logic.

What We Will Build

We will create a complete Expert Advisor that trades a simple moving average crossover strategy: buy when the fast MA crosses above the slow MA, sell when it crosses below. The EA includes stop-loss, take-profit, and only one position at a time.

Complete EA Source Code

//+------------------------------------------------------------------+
//| MACrossoverEA.mq5                                                 |
//| Simple Moving Average Crossover Expert Advisor                     |
//+------------------------------------------------------------------+
#include <Trade/Trade.mqh>  // Include the CTrade class

// Input parameters
input int    FastMAPeriod = 10;     // Fast MA Period
input int    SlowMAPeriod = 50;     // Slow MA Period
input double LotSize      = 0.1;   // Lot Size
input int    StopLoss     = 100;   // Stop Loss (points)
input int    TakeProfit   = 200;   // Take Profit (points)
input int    MagicNumber  = 12345; // Magic Number (unique EA ID)

// Global variables
int fastMAHandle, slowMAHandle;
datetime lastBarTime;
CTrade trade;  // Trade execution object

//+------------------------------------------------------------------+
int OnInit()
{
    // Validate inputs
    if(FastMAPeriod >= SlowMAPeriod)
    {
        Print("Error: Fast MA must be smaller than Slow MA");
        return INIT_PARAMETERS_INCORRECT;
    }

    // Create indicator handles
    fastMAHandle = iMA(_Symbol, PERIOD_CURRENT, FastMAPeriod,
                       0, MODE_EMA, PRICE_CLOSE);
    slowMAHandle = iMA(_Symbol, PERIOD_CURRENT, SlowMAPeriod,
                       0, MODE_EMA, PRICE_CLOSE);

    if(fastMAHandle == INVALID_HANDLE || slowMAHandle == INVALID_HANDLE)
    {
        Print("Error creating MA handles");
        return INIT_FAILED;
    }

    // Set magic number for order identification
    trade.SetExpertMagicNumber(MagicNumber);

    lastBarTime = 0;
    Print("MA Crossover EA initialized. Fast: ", FastMAPeriod,
          " Slow: ", SlowMAPeriod);

    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
void OnTick()
{
    // Only trade on new bars (not every tick)
    datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
    if(currentBarTime == lastBarTime) return;
    lastBarTime = currentBarTime;

    // Get MA values for the last 3 completed bars
    double fastMA[], slowMA[];
    ArraySetAsSeries(fastMA, true);
    ArraySetAsSeries(slowMA, true);
    CopyBuffer(fastMAHandle, 0, 1, 3, fastMA);
    CopyBuffer(slowMAHandle, 0, 1, 3, slowMA);

    // Detect crossover on the LAST COMPLETED bar
    bool bullishCross = (fastMA[0] > slowMA[0]) &&
                        (fastMA[1] <= slowMA[1]);
    bool bearishCross = (fastMA[0] < slowMA[0]) &&
                        (fastMA[1] >= slowMA[1]);

    // Check if we have an open position
    bool hasPosition = PositionSelect(_Symbol);

    // BULLISH CROSSOVER — close sell, open buy
    if(bullishCross)
    {
        if(hasPosition && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
            trade.PositionClose(_Symbol);

        if(!PositionSelect(_Symbol))  // no position after close
        {
            double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
            double sl = ask - StopLoss * _Point;
            double tp = ask + TakeProfit * _Point;
            trade.Buy(LotSize, _Symbol, ask, sl, tp, "MA Cross Buy");
        }
    }

    // BEARISH CROSSOVER — close buy, open sell
    if(bearishCross)
    {
        if(hasPosition && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
            trade.PositionClose(_Symbol);

        if(!PositionSelect(_Symbol))
        {
            double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
            double sl = bid + StopLoss * _Point;
            double tp = bid - TakeProfit * _Point;
            trade.Sell(LotSize, _Symbol, bid, sl, tp, "MA Cross Sell");
        }
    }
}

//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    IndicatorRelease(fastMAHandle);
    IndicatorRelease(slowMAHandle);
    Print("MA Crossover EA removed. Reason: ", reason);
}
//+------------------------------------------------------------------+

Key Concepts Explained

The CTrade Class

The #include <Trade/Trade.mqh> line imports the standard trade library. The CTrade class provides clean methods for order operations:

trade.Buy(volume, symbol, price, sl, tp, comment);
trade.Sell(volume, symbol, price, sl, tp, comment);
trade.PositionClose(symbol);
trade.PositionModify(ticket, sl, tp);

This is much easier than the raw OrderSend() function.

Magic Number

The Magic Number is a unique identifier for your EA's trades. If you run multiple EAs on the same account, each should have a different Magic Number so they do not interfere with each other's positions.

New Bar Detection

We only check for signals when a new bar opens (not on every tick). This prevents the EA from repeatedly acting on the same signal and ensures we use completed bar data for analysis.

Crossover Detection

A crossover is detected by comparing two consecutive bars: if the fast MA was below the slow MA on bar[1] and is now above on bar[0], that is a bullish crossover. We use bars starting from index 1 (last completed bar) to avoid using the still-forming current bar.

Testing Your EA

1
Open Strategy Tester

In MT5, press Ctrl + R or go to View > Strategy Tester.

2
Configure the test

Select your EA, choose a symbol (e.g., EURUSD), set the timeframe (e.g., H1), and choose a date range with at least 1 year of data.

3
Run and analyze

Click Start. After the backtest, examine the Results tab (trade list), Graph tab (equity curve), and Report tab (statistics).

⚠️
Backtest results are not guarantees

Past performance does not predict future results. Backtests use perfect historical data without real-world issues like slippage, requotes, and variable spreads. Always forward-test on a demo account before going live.

Improvements for Production

This EA is educational. A production EA would also need:

  • Dynamic lot sizing based on account balance and risk percentage
  • Trailing stop-loss to lock in profits
  • Trading session filters (avoid low-liquidity hours)
  • News event filters
  • Error handling and retry logic for failed orders
  • Magic number filtering to only manage its own positions
  • Logging for diagnostics and auditing