How to Program a Moving Average Crossover Expert Advisor in MQL5?

Introduction to Moving Average Crossover Expert Advisors in MQL5

This article guides you through developing a Moving Average (MA) Crossover Expert Advisor (EA) in MQL5. We’ll cover the core concepts, MQL5 syntax, implementation details, and essential steps for testing and deployment.

What is a Moving Average Crossover Strategy?

A Moving Average Crossover strategy is a technical analysis technique where buy or sell signals are generated based on the intersection of two moving averages with different periods. Typically, a faster (shorter period) moving average crossing above a slower (longer period) moving average indicates a buy signal (potential uptrend), while the opposite indicates a sell signal (potential downtrend). This strategy aims to capitalize on trend changes.

Why Use MQL5 for Expert Advisors?

MQL5 offers several advantages for developing EAs:

  • Object-Oriented Programming (OOP): MQL5 supports OOP principles, enabling modular and reusable code.
  • Event Handling: MQL5 provides event handling capabilities to respond to market events (e.g., new ticks, order execution).
  • Backtesting and Optimization: MetaTrader 5’s Strategy Tester allows robust backtesting and parameter optimization.
  • Performance: MQL5 generally offers better performance compared to MQL4, crucial for complex trading strategies.
  • 64-bit compatibility: MQL5 is 64-bit compatible which improves processing speed.

Setting Up MetaEditor and MQL5 Environment

  1. Open MetaTrader 5.
  2. Press F4 to open MetaEditor.
  3. Navigate to the MQL5/Experts folder. This is where you’ll save your EA.

Defining the Expert Advisor Structure and Input Parameters

Creating the MQL5 EA File

Create a new file named MACrossoverEA.mq5 in the MQL5/Experts folder.

Declaring Input Parameters (Moving Average Periods, Trade Size)

Input parameters allow users to customize the EA’s behavior without modifying the code.

input int FastMAPeriod = 12;  // Period for the faster moving average
input int SlowMAPeriod = 26;  // Period for the slower moving average
input double Lots = 0.01;      // Trade size in lots
input double StopLossPips = 50;  // Stop Loss in pips
input double TakeProfitPips = 100; // Take Profit in pips
input ENUM_MA_METHOD MAMethod = MODE_SMA; // Moving average method

Defining Global Variables

Declare global variables to store moving average handles, current and previous moving average values and other relevant data.

int fastMAHandle;             // Handle for the faster moving average
int slowMAHandle;             // Handle for the slower moving average
double fastMAValue[];          // Array to store faster MA values
double slowMAValue[];          // Array to store slower MA values
double Ask, Bid;               // Current Ask and Bid prices

Implementing the Moving Average Calculation

Using iMA() Function to Calculate Moving Averages

The iMA() function calculates the moving average for a specified symbol, timeframe, and period. Create OnInit() function. This function executes once when the EA is initialized. Here, you initialize the moving average indicators.

int OnInit()
{
    fastMAHandle = iMA(Symbol(), Period(), FastMAPeriod, 0, MAMethod, PRICE_CLOSE);
    slowMAHandle = iMA(Symbol(), Period(), SlowMAPeriod, 0, MAMethod, PRICE_CLOSE);

    if (fastMAHandle == INVALID_HANDLE || slowMAHandle == INVALID_HANDLE)
    {
        Print("Failed to create MA indicators!");
        return INIT_FAILED;
    }

    ArraySetAsSeries(fastMAValue, true); // Store values from current to past
    ArraySetAsSeries(slowMAValue, true);

    return INIT_SUCCEEDED;
}

Handling Different Moving Average Types (SMA, EMA)

The ENUM_MA_METHOD enumeration allows you to select different moving average types (Simple, Exponential, Smoothed, Linear Weighted). The MAMethod input parameter enables users to choose the desired type.

Ensuring Data Accuracy and Refreshing Moving Average Values

The OnTick() function is called every time a new tick arrives. Update the moving average values using CopyBuffer().

void OnTick()
{
    if (CopyBuffer(fastMAHandle, 0, 0, 2, fastMAValue) != 2 || CopyBuffer(slowMAHandle, 0, 0, 2, slowMAValue) != 2)
    {
        Print("Failed to copy MA values!");
        return;
    }

    Ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
    Bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);

    CheckForCrossOver();
}

Implementing the Crossover Logic and Trade Execution

Detecting Moving Average Crossovers (Buy/Sell Signals)

Create a function CheckForCrossOver() to determine if crossover occurred and to generate the trade signal.

void CheckForCrossOver()
{
    bool buyCondition = (fastMAValue[1] <= slowMAValue[1]) && (fastMAValue[0] > slowMAValue[0]);
    bool sellCondition = (fastMAValue[1] >= slowMAValue[1]) && (fastMAValue[0] < slowMAValue[0]);

    if (buyCondition)
        ExecuteTrade(ORDER_TYPE_BUY);
    else if (sellCondition)
        ExecuteTrade(ORDER_TYPE_SELL);
}

Implementing Trade Execution Logic (OrderSend Function)

Implement ExecuteTrade() function to send trading orders to the broker.

void ExecuteTrade(ENUM_ORDER_TYPE orderType)
{
    double stopLoss, takeProfit;
    double point = SymbolInfoDouble(Symbol(), SYMBOL_POINT);
    int digits = (int)SymbolInfoInteger(Symbol(), SYMBOL_DIGITS);

    if (orderType == ORDER_TYPE_BUY)
    {
        stopLoss = Bid - StopLossPips * point;
        takeProfit = Bid + TakeProfitPips * point;
    }
    else
    {
        stopLoss = Ask + StopLossPips * point;
        takeProfit = Ask - TakeProfitPips * point;
    }

    MqlTradeRequest request = {0};
    MqlTradeResult result = {0};

    request.action = TRADE_ACTION_DEAL;
    request.symbol = Symbol();
    request.volume = Lots;
    request.type = orderType;
    request.price = (orderType == ORDER_TYPE_BUY) ? Ask : Bid;
    request.sl = NormalizeDouble(stopLoss, digits);
    request.tp = NormalizeDouble(takeProfit, digits);
    request.magic = 12345; // Magic number for the EA
    request.type_filling = ORDER_FILLING_FOK;
    request.type_time = ORDER_TIME_GTC;

    OrderSend(request, result);

    if (result.retcode != TRADE_RETCODE_DONE)
    {
        PrintFormat("OrderSend failed: %d", result.retcode);
    }
}

Setting Stop Loss, Take Profit, and Trailing Stop Levels

The ExecuteTrade function includes setting Stop Loss and Take Profit levels based on the input parameters StopLossPips and TakeProfitPips. Trailing stops can be implemented within the OnTick() function.

Implementing Risk Management Rules (Position Sizing, Maximum Risk per Trade)

Position sizing can be adjusted dynamically based on account balance and risk tolerance. The Lots input parameter can be calculated based on a percentage of the account balance.

Testing, Optimization, and Deployment

Backtesting the Expert Advisor in Strategy Tester

  1. Open MetaTrader 5 Strategy Tester (View -> Strategy Tester).
  2. Select your EA (MACrossoverEA.mq5).
  3. Choose the symbol, timeframe, and backtesting period.
  4. Click “Start” to begin the backtest.

Optimizing Input Parameters for Maximum Profitability

Use the Strategy Tester’s optimization feature to find the optimal values for the input parameters (e.g., FastMAPeriod, SlowMAPeriod).

Implementing Error Handling and Logging

Thorough error handling and logging are crucial for debugging and monitoring the EA’s performance. Check the return values of functions like OrderSend() and log any errors to the Experts tab in MetaTrader 5.

Deploying the Expert Advisor on a Live Trading Account

  1. Ensure your EA is thoroughly tested and optimized.
  2. Attach the EA to a chart in MetaTrader 5.
  3. Enable Autotrading (Tools -> Options -> Expert Advisors -> Allow Automated Trading).
  4. Monitor the EA’s performance closely.

This detailed guide provides a solid foundation for developing a Moving Average Crossover EA in MQL5. Remember to adapt and expand upon these concepts to create a trading strategy that aligns with your specific risk tolerance and trading goals.


Leave a Reply