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.
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
| Feature | MQL4 | MQL5 |
|---|---|---|
| Platform | MT4 | MT5 |
| Speed | Slower | 4-10x faster |
| OOP Support | Limited | Full |
| Testing | Single-threaded | Multi-threaded |
| Markets | Forex only | Stocks, futures, forex |
Verdict: MQL5 is the future. New traders should start with MQL5.
Basic EA Structure
Every Expert Advisor follows this basic template:
//+------------------------------------------------------------------+
//| 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:
//+------------------------------------------------------------------+
//| 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
//+------------------------------------------------------------------+
//| 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
- C++ syntax — Steep learning curve for non-programmers
- Platform-specific — Concepts don't transfer to other platforms
- Debugging — Limited error messages and logging
- Documentation — Technical and assumes programming knowledge
- Testing — Requires understanding of backtesting limitations
Common MQL5 Mistakes
1. Not checking for new bars
// 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
// BAD: Fixed lot size
double lots = 0.1;
// GOOD: Risk-based position sizing
double lots = CalculateLotSize(AccountBalance(), RiskPercent, StopLossPips);3. Not handling errors
// 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
- Save the .mq5 file to
MQL5/Experts/folder - Compile in MetaEditor (press F7)
- Drag EA onto chart in MT5
- Enable "Allow Algo Trading" in settings
- Start with demo account first!
Best Practices
- Always backtest before live trading
- Use demo accounts for at least 1 month
- Start small with position sizes
- Monitor regularly even though it's automated
- Keep logs of all trades for analysis
- 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