Back to all posts

MQL5 Expert Advisor: Complete Guide to Building Trading Bots

Learn how to build automated trading bots for MetaTrader 5 using MQL5. Complete guide with code examples and best practices.

SpendDock Team··6 min read
MQL5MetaTraderExpert AdvisorAlgo TradingTrading Bot

MetaTrader 5 remains one of the most popular trading platforms worldwide, and MQL5 Expert Advisors (EAs) allow you to automate your strategies. This guide covers everything from basics to deployment.

What is an Expert Advisor?

An Expert Advisor (EA) is an automated trading program written in MQL5 that runs on MetaTrader 5. EAs can:

  • Monitor markets 24/7
  • Execute trades automatically
  • Manage positions and risk
  • Backtest strategies on historical data

MQL5 vs MQL4

FeatureMQL4MQL5
PlatformMT4MT5
SpeedSlower4-10x faster
OOP SupportLimitedFull
TestingSingle-threadedMulti-threaded
MarketsForex onlyStocks, futures, forex

Verdict: MQL5 is the future. New traders should start with MQL5.

Basic EA Structure

Every Expert Advisor follows this basic template:

mql5
//+------------------------------------------------------------------+
//| Expert Advisor Template                                           |
//+------------------------------------------------------------------+
#property copyright "SpendDock"
#property version   "1.00"

#include <Trade\Trade.mqh>

// Input parameters (user can modify)
input double LotSize = 0.1;
input int StopLoss = 50;
input int TakeProfit = 100;

// Global variables
CTrade trade;

//+------------------------------------------------------------------+
//| Initialization - called once when EA starts                       |
//+------------------------------------------------------------------+
int OnInit()
{
    Print("EA initialized successfully");
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Deinitialization - called when EA stops                           |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    Print("EA stopped. Reason: ", reason);
}

//+------------------------------------------------------------------+
//| Main tick function - called on every price tick                   |
//+------------------------------------------------------------------+
void OnTick()
{
    // Your trading logic goes here
}

Complete EMA Crossover EA

Here's a production-ready EMA crossover Expert Advisor:

mql5
//+------------------------------------------------------------------+
//| EMA Crossover Expert Advisor                                      |
//+------------------------------------------------------------------+
#property copyright "SpendDock"
#property version   "1.00"
#property strict

#include <Trade\Trade.mqh>

// Input parameters
input int FastEMA = 20;              // Fast EMA Period
input int SlowEMA = 50;              // Slow EMA Period
input double LotSize = 0.1;          // Position Size
input int StopLossPips = 50;         // Stop Loss in Pips
input int TakeProfitPips = 100;      // Take Profit in Pips
input int MagicNumber = 123456;      // Magic Number

// Global variables
CTrade trade;
int fastHandle, slowHandle;

//+------------------------------------------------------------------+
//| Initialization                                                    |
//+------------------------------------------------------------------+
int OnInit()
{
    // Create indicator handles
    fastHandle = iMA(_Symbol, PERIOD_CURRENT, FastEMA, 0, MODE_EMA, PRICE_CLOSE);
    slowHandle = iMA(_Symbol, PERIOD_CURRENT, SlowEMA, 0, MODE_EMA, PRICE_CLOSE);

    if(fastHandle == INVALID_HANDLE || slowHandle == INVALID_HANDLE)
    {
        Print("Failed to create indicator handles");
        return(INIT_FAILED);
    }

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

    Print("EMA Crossover EA initialized");
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Main trading logic                                                |
//+------------------------------------------------------------------+
void OnTick()
{
    // Only check on new bar
    if(!IsNewBar())
        return;

    // Get EMA values
    double fastEMA[], slowEMA[];
    ArraySetAsSeries(fastEMA, true);
    ArraySetAsSeries(slowEMA, true);

    if(CopyBuffer(fastHandle, 0, 0, 3, fastEMA) < 3) return;
    if(CopyBuffer(slowHandle, 0, 0, 3, slowEMA) < 3) return;

    // Check for crossover (using previous bars to avoid repainting)
    bool bullishCross = fastEMA[2] <= slowEMA[2] && fastEMA[1] > slowEMA[1];
    bool bearishCross = fastEMA[2] >= slowEMA[2] && fastEMA[1] < slowEMA[1];

    // Calculate SL/TP
    double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
    double slDistance = StopLossPips * point * 10;
    double tpDistance = TakeProfitPips * point * 10;

    // Check existing positions
    bool hasPosition = PositionSelect(_Symbol);

    // Execute trades
    if(bullishCross && !hasPosition)
    {
        double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        double sl = ask - slDistance;
        double tp = ask + tpDistance;

        if(trade.Buy(LotSize, _Symbol, ask, sl, tp, "EMA Cross Buy"))
            Print("Buy order placed at ", ask);
    }

    if(bearishCross && hasPosition)
    {
        if(trade.PositionClose(_Symbol))
            Print("Position closed on bearish cross");
    }
}

//+------------------------------------------------------------------+
//| Check for new bar                                                 |
//+------------------------------------------------------------------+
bool IsNewBar()
{
    static datetime lastBar = 0;
    datetime currentBar = iTime(_Symbol, PERIOD_CURRENT, 0);

    if(currentBar != lastBar)
    {
        lastBar = currentBar;
        return true;
    }
    return false;
}

//+------------------------------------------------------------------+
//| Deinitialization                                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    IndicatorRelease(fastHandle);
    IndicatorRelease(slowHandle);
    Print("EA stopped");
}

RSI Strategy EA

mql5
//+------------------------------------------------------------------+
//| RSI Expert Advisor                                                |
//+------------------------------------------------------------------+
#property copyright "SpendDock"
#property version   "1.00"

#include <Trade\Trade.mqh>

input int RSI_Period = 14;
input int Oversold = 30;
input int Overbought = 70;
input double LotSize = 0.1;
input int StopLossPips = 30;
input int TakeProfitPips = 60;

CTrade trade;
int rsiHandle;

int OnInit()
{
    rsiHandle = iRSI(_Symbol, PERIOD_CURRENT, RSI_Period, PRICE_CLOSE);
    if(rsiHandle == INVALID_HANDLE)
        return(INIT_FAILED);
    return(INIT_SUCCEEDED);
}

void OnTick()
{
    double rsi[];
    ArraySetAsSeries(rsi, true);

    if(CopyBuffer(rsiHandle, 0, 0, 3, rsi) < 3)
        return;

    double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
    bool hasPosition = PositionSelect(_Symbol);

    // Buy signal: RSI crosses above oversold
    if(rsi[2] < Oversold && rsi[1] >= Oversold && !hasPosition)
    {
        double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        double sl = ask - StopLossPips * point * 10;
        double tp = ask + TakeProfitPips * point * 10;
        trade.Buy(LotSize, _Symbol, ask, sl, tp, "RSI Buy");
    }

    // Close signal: RSI reaches overbought
    if(rsi[1] >= Overbought && hasPosition)
    {
        trade.PositionClose(_Symbol);
    }
}

void OnDeinit(const int reason)
{
    IndicatorRelease(rsiHandle);
}

Why MQL5 is Hard to Learn

  1. C++ syntax — Steep learning curve for non-programmers
  2. Platform-specific — Concepts don't transfer to other platforms
  3. Debugging — Limited error messages and logging
  4. Documentation — Technical and assumes programming knowledge
  5. Testing — Requires understanding of backtesting limitations

Common MQL5 Mistakes

1. Not checking for new bars

mql5
// BAD: Runs on every tick
void OnTick()
{
    // This code runs thousands of times per minute
    ExecuteTrade();
}

// GOOD: Only run on new bars
void OnTick()
{
    if(!IsNewBar()) return;
    ExecuteTrade();
}

2. Hardcoded position sizing

mql5
// BAD: Fixed lot size
double lots = 0.1;

// GOOD: Risk-based position sizing
double lots = CalculateLotSize(AccountBalance(), RiskPercent, StopLossPips);

3. Not handling errors

mql5
// BAD: No error checking
trade.Buy(LotSize, _Symbol, ask, sl, tp);

// GOOD: Check result
if(!trade.Buy(LotSize, _Symbol, ask, sl, tp))
{
    Print("Order failed: ", trade.ResultRetcode());
}

Deploying Your EA

  1. Save the .mq5 file to MQL5/Experts/ folder
  2. Compile in MetaEditor (press F7)
  3. Drag EA onto chart in MT5
  4. Enable "Allow Algo Trading" in settings
  5. Start with demo account first!

Best Practices

  1. Always backtest before live trading
  2. Use demo accounts for at least 1 month
  3. Start small with position sizes
  4. Monitor regularly even though it's automated
  5. Keep logs of all trades for analysis
  6. Use magic numbers to identify your EA's trades

Skip the Learning Curve with SpendDock

Instead of spending months learning MQL5, describe your strategy in plain English:

"Create an EMA crossover strategy for MetaTrader 5. Buy when 20 EMA crosses above 50 EMA, sell when it crosses below. Use 50 pip stop loss and 100 pip take profit."

SpendDock generates production-ready MQL5 code in seconds — plus exports to PineScript, NinjaScript, and Python.

SpendDock advantages:

  • No coding required
  • Generates clean, optimized MQL5 code
  • Multi-platform export (TradingView, MT5, NinjaTrader, Python)
  • Built-in risk management
  • Instant strategy validation

Skip the Coding — Generate Your Strategy

Describe your trading strategy in plain English and get production-ready code for TradingView, MetaTrader, NinjaTrader, or Python.

Try SpendDock Free