How to Code a Moving Average Crossover Strategy in MQL5?

The moving average crossover strategy is a fundamental technique in algorithmic trading. It identifies potential buy and sell signals based on the interaction of two moving averages with different periods. MQL5, the programming language of the MetaTrader 5 platform, provides robust tools for automating this strategy.

Understanding Moving Averages: Simple Moving Average (SMA) and Exponential Moving Average (EMA)

Moving averages smooth price data to identify trends. The two primary types are:

  • SMA (Simple Moving Average): Calculates the average price over a specified period. It gives equal weight to all prices within the period.
  • EMA (Exponential Moving Average): Gives more weight to recent prices, making it more responsive to current price changes.

Choosing between SMA and EMA depends on the trader’s preference for lag versus responsiveness. EMA reacts faster but can generate more false signals.

Crossover Strategy Fundamentals: Bullish and Bearish Signals

The core of the crossover strategy lies in identifying when a shorter-period moving average crosses a longer-period moving average.

  • Bullish Signal: When the faster moving average crosses above the slower moving average, it suggests an upward trend and a potential buy signal.
  • Bearish Signal: When the faster moving average crosses below the slower moving average, it indicates a downward trend and a potential sell signal.

MQL5 as a Tool for Algorithmic Trading: Advantages and Capabilities

MQL5 offers several advantages for developing and automating trading strategies:

  • Speed and Efficiency: MQL5 is a compiled language, resulting in fast execution speeds, crucial for time-sensitive trading decisions.
  • Object-Oriented Programming: MQL5 supports OOP, enabling modular and reusable code.
  • Backtesting Capabilities: MetaTrader 5 provides a robust strategy tester for evaluating strategy performance on historical data.
  • Event Handling: MQL5 allows for handling events like new ticks, order changes, and timer events.

Setting Up the MQL5 Development Environment

Installing MetaTrader 5 and the MetaEditor

  1. Download and install MetaTrader 5 from the MetaQuotes website.
  2. The MetaEditor is integrated into MetaTrader 5. Open it by pressing F4 or selecting ‘MetaQuotes Language Editor’ from the ‘Tools’ menu.

Creating a New Expert Advisor Project

  1. In the MetaEditor, click ‘New’ or press Ctrl+N.
  2. Select ‘Expert Advisor’ and click ‘Next’.
  3. Enter a name for your EA (e.g., ‘MACrossover’).
  4. Define any necessary input parameters (we’ll add these later). Click ‘Finish’.

Understanding the Basic MQL5 Program Structure

An MQL5 Expert Advisor has several key functions:

  • OnInit(): Called once when the EA is initialized (e.g., when attached to a chart).
  • OnDeinit(): Called when the EA is removed from the chart or MetaTrader 5 is closed.
  • OnTick(): Called on every new tick of the symbol the EA is attached to. This is where the core logic of the trading strategy resides.
  • OnTrade(): Called when a trade event occurs.
  • OnTimer(): Called when timer event occurs (if timer is set).

Coding the Moving Average Crossover Strategy in MQL5

Defining Input Parameters: Fast and Slow Moving Average Periods

First, define the input parameters for the fast and slow moving average periods. These can be adjusted in the MetaTrader 5 interface.

input int FastMAPeriod = 12;   // Period for the fast moving average
input int SlowMAPeriod = 26;   // Period for the slow moving average
input ENUM_APPLIED_PRICE AppliedPrice = PRICE_CLOSE; // Price used for calculation

Calculating Moving Average Values using iMA() Function

Use the iMA() function to calculate the moving average values. iMA() is deprecated, and the IndicatorCreate() function with IND_MA property should be used for new developments.

double FastMA, SlowMA;

int fastMAHandle = IndicatorCreate(_Symbol, _Period, IND_MA, FastMAPeriod, 0, AppliedPrice);
int slowMAHandle = IndicatorCreate(_Symbol, _Period, IND_MA, SlowMAPeriod, 0, AppliedPrice);

MqlRates rates[];
ArraySetAsSeries(rates, true);
CopyRates(_Symbol, _Period, 0, 2, rates);

double fastMABuffer[], slowMABuffer[];
ArraySetAsSeries(fastMABuffer, true);
ArraySetAsSeries(slowMABuffer, true);

CopyBuffer(fastMAHandle, 0, 0, 1, fastMABuffer);
CopyBuffer(slowMAHandle, 0, 0, 1, slowMABuffer);

FastMA = fastMABuffer[0];
SlowMA = slowMABuffer[0];

Generating Trading Signals Based on Crossover Conditions

Implement the logic to detect crossovers. Store the previous moving average values to detect crossovers.

static double PreviousFastMA = 0;
static double PreviousSlowMA = 0;

bool BuySignal = false;
bool SellSignal = false;

if (FastMA > SlowMA && PreviousFastMA <= PreviousSlowMA) {
    BuySignal = true;
}

if (FastMA < SlowMA && PreviousFastMA >= PreviousSlowMA) {
    SellSignal = true;
}

PreviousFastMA = FastMA;
PreviousSlowMA = SlowMA;

Implementing Order Execution: Buy and Sell Orders using OrderSend()

Use the OrderSend() function to place buy and sell orders. First, populate the MqlTradeRequest and MqlTradeResult structures.

#include <Trade

MqlTradeRequest request;
MqlTradeResult  result;

double lotSize = 0.01; // Define the lot size

if (BuySignal) {
    ZeroMemory(request);
    request.action   = TRADE_ACTION_DEAL;
    request.symbol   = _Symbol;
    request.volume   = lotSize;
    request.type     = ORDER_TYPE_BUY;
    request.price    = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
    request.sl       = 0; // Stop Loss (optional)
    request.tp       = 0; // Take Profit (optional)
    request.magic    = 12345; // Magic number to identify orders placed by this EA
    request.type_filling = ORDER_FILLING_FOK; // Filling type
    request.type_time = ORDER_TIME_GTC; // Order lifetime

    Trade.Send(request, result);
    if(result.retcode != 10009)
        Print("Buy Order Failed: " + IntegerToString(result.retcode));

}

if (SellSignal) {
    ZeroMemory(request);
    request.action   = TRADE_ACTION_DEAL;
    request.symbol   = _Symbol;
    request.volume   = lotSize;
    request.type     = ORDER_TYPE_SELL;
    request.price    = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    request.sl       = 0; // Stop Loss (optional)
    request.tp       = 0; // Take Profit (optional)
    request.magic    = 12345; // Magic number
    request.type_filling = ORDER_FILLING_FOK;
    request.type_time = ORDER_TIME_GTC;

    Trade.Send(request, result);
    if(result.retcode != 10009)
        Print("Sell Order Failed: " + IntegerToString(result.retcode));
}

Backtesting and Optimization

Using the MetaTrader 5 Strategy Tester

  1. Open the Strategy Tester (View -> Strategy Tester).
  2. Select your EA from the ‘Expert Advisor’ dropdown.
  3. Choose the symbol and timeframe.
  4. Select the ‘Every tick based on real ticks’ mode for the most accurate testing.

Setting Backtesting Parameters: Date Range, Symbol, and Timeframe

Configure the date range to test your strategy on relevant historical data. Ensure the symbol and timeframe match your intended trading environment.

Analyzing Backtesting Results: Profit Factor, Drawdown, and Win Rate

Pay close attention to the following metrics:

  • Profit Factor: Gross profit divided by gross loss. A value greater than 1 indicates a profitable strategy.
  • Drawdown: The maximum loss from a peak to a trough during the testing period. Lower drawdown is desirable.
  • Win Rate: The percentage of winning trades. A higher win rate doesn’t always guarantee profitability; consider the average win/loss ratio.

Optimizing Moving Average Periods using Genetic Algorithm

The Strategy Tester can optimize input parameters using a genetic algorithm. This method searches for the best combination of FastMAPeriod and SlowMAPeriod that maximizes your chosen performance metric (e.g., profit factor, Sharpe ratio).

  1. In the Strategy Tester, select ‘Optimization’.
  2. Define the start, step, and stop values for the FastMAPeriod and SlowMAPeriod input parameters.
  3. Select the optimization criterion (e.g., ‘Maximum profit factor’).
  4. Start the optimization process.

Enhancements and Advanced Features

Adding Stop Loss and Take Profit Levels

Include sl (stop loss) and tp (take profit) values in the MqlTradeRequest structure to manage risk and lock in profits.

request.sl = SymbolInfoDouble(_Symbol, SYMBOL_BID) - StopLossPips * _Point; //stop loss in pips
request.tp = SymbolInfoDouble(_Symbol, SYMBOL_BID) + TakeProfitPips * _Point; //take profit in pips

Implementing Trailing Stop Functionality

Implement OnTrade() event to handle trailing stop. If order is profitable – move stop loss after price to secure profits.

Incorporating Additional Indicators for Confirmation (e.g., RSI, MACD)

Combine the moving average crossover with other indicators like RSI (Relative Strength Index) or MACD (Moving Average Convergence Divergence) to filter out false signals and improve accuracy. Calculate the values of these indicators within the OnTick() function and add conditions to your trading logic.

Error Handling and Robustness: Ensuring Reliable Execution

Implement error handling to catch and log any errors during order execution. Check the return code of OrderSend() and log any failures. This helps in debugging and ensures the EA’s reliability.


Leave a Reply