What Is MQL5?
MQL5 (MetaQuotes Language 5) is the programming language used to create trading robots (Expert Advisors), custom indicators, scripts, and libraries for MetaTrader 5. It is a high-level, C++-like language that provides direct access to market data, trading functions, and the MetaTrader platform API.
With MQL5, you can:
- Automate trading strategies that execute 24/5 without manual intervention.
- Create custom indicators with unique calculations and visual representations.
- Build scripts for one-time tasks like closing all positions or adjusting stop losses.
- Develop services that run in the background (e.g., data collection, custom alerts).
MQL5 is an object-oriented language with support for classes, interfaces, inheritance, and polymorphism. If you have experience with C++, Java, or C#, the transition will be smooth.
The MetaEditor IDE
MetaEditor is the built-in IDE (Integrated Development Environment) for writing MQL5 code. Access it from MetaTrader by pressing F4 or clicking the MetaEditor icon in the toolbar.
Key features:
- Syntax highlighting: Color-coded keywords, functions, and comments.
- IntelliSense: Auto-complete for functions, properties, and methods.
- Debugger: Step-through debugging with breakpoints, watch variables, and call stack.
- Profiler: Identify performance bottlenecks in your code.
- MQL5 Wizard: Generate EA/indicator templates with a wizard interface.
// Your first MQL5 program — a script that prints to the Experts log
//+------------------------------------------------------------------+
//| HelloWorld.mq5 |
//+------------------------------------------------------------------+
void OnStart()
{
Print("Hello, MetaTrader 5!");
Print("Account Balance: ", AccountInfoDouble(ACCOUNT_BALANCE));
Print("Server: ", AccountInfoString(ACCOUNT_SERVER));
}
Program Types: EA, Indicator, Script
MQL5 supports four types of programs, each serving a different purpose:
| Type | File Extension | Purpose | Runs on Chart |
|---|---|---|---|
| Expert Advisor | .mq5 / .ex5 | Automated trading strategy. Runs continuously. | Yes (one per chart) |
| Custom Indicator | .mq5 / .ex5 | Custom calculations and chart overlays. | Yes (multiple allowed) |
| Script | .mq5 / .ex5 | One-time execution tasks. | Yes (runs once, then stops) |
| Service | .mq5 / .ex5 | Background processes (no chart needed). | No |
Syntax and Data Types
MQL5 syntax is very similar to C++. Here are the essential building blocks:
// --- Variables and Data Types ---
int orderCount = 0; // Integer
double lotSize = 0.01; // Double (floating point)
string symbol = "EURUSD"; // String
bool isTrading = true; // Boolean
datetime tradeTime = TimeCurrent(); // Date/Time
// --- Arrays ---
double prices[]; // Dynamic array
ArrayResize(prices, 100); // Resize to 100 elements
// --- Conditional Statements ---
if (lotSize > 0.1) {
Print("Large position");
} else if (lotSize > 0.01) {
Print("Standard position");
} else {
Print("Micro position");
}
// --- Loops ---
for (int i = 0; i < 10; i++) {
Print("Iteration: ", i);
}
// --- Functions ---
double CalculateRisk(double balance, double riskPercent)
{
return balance * riskPercent / 100.0;
}
Event Handlers
MQL5 uses an event-driven architecture. Your code responds to events triggered by the platform:
// --- Key Event Handlers for Expert Advisors ---
// Called once when EA is attached to chart
int OnInit()
{
Print("EA initialized on ", _Symbol);
return INIT_SUCCEEDED;
}
// Called on every new tick (price update)
void OnTick()
{
// This is where your main trading logic goes
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
Print("Bid: ", bid, " | Ask: ", ask);
}
// Called when EA is removed from chart
void OnDeinit(const int reason)
{
Print("EA removed. Reason: ", reason);
}
// Called on timer events (if set with EventSetTimer)
void OnTimer()
{
// Periodic tasks (e.g., check news, update dashboard)
}
// Called on chart events (clicks, key presses)
void OnChartEvent(const int id, const long &lparam,
const double &dparam, const string &sparam)
{
// Handle user interaction
}
Code and Test in Real-Time
Open a free MT5 demo account to use MetaEditor, compile your EAs, and backtest against real market data.
Open Free MT5 Demo →Accessing Indicator Values in MQL5
To use indicator values in your EA, you create an indicator handle and then copy its buffer data into an array:
// Step 1: Create indicator handle (do this in OnInit)
int maHandle;
int OnInit()
{
// Create a 50-period SMA handle
maHandle = iMA(_Symbol, PERIOD_H1, 50, 0, MODE_SMA, PRICE_CLOSE);
if (maHandle == INVALID_HANDLE) {
Print("Failed to create MA indicator");
return INIT_FAILED;
}
return INIT_SUCCEEDED;
}
// Step 2: Read indicator values (do this in OnTick)
void OnTick()
{
double maValues[];
ArraySetAsSeries(maValues, true); // Most recent value first
// Copy last 3 values from the indicator buffer
if (CopyBuffer(maHandle, 0, 0, 3, maValues) < 3) {
Print("Not enough data");
return;
}
double currentMA = maValues[0]; // Current bar
double previousMA = maValues[1]; // Previous bar
double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if (currentBid > currentMA && previousMA > maValues[2]) {
Print("Price above rising MA - bullish signal");
}
}
// Step 3: Release handle (do this in OnDeinit)
void OnDeinit(const int reason)
{
IndicatorRelease(maHandle);
}
Order Management
MQL5 uses a structured approach to order management through the MqlTradeRequest and MqlTradeResult structures:
// --- Opening a Buy Position ---
void OpenBuy(double lots, double slPoints, double tpPoints)
{
MqlTradeRequest request = {};
MqlTradeResult result = {};
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = lots;
request.type = ORDER_TYPE_BUY;
request.price = ask;
request.sl = ask - slPoints * point; // Stop Loss
request.tp = ask + tpPoints * point; // Take Profit
request.deviation = 10; // Max slippage in points
request.magic = 123456; // Magic number to identify EA orders
request.comment = "My First EA";
if (!OrderSend(request, result)) {
Print("OrderSend failed: ", GetLastError());
} else {
Print("Order placed. Ticket: ", result.order);
}
}
// --- Closing a Position ---
void ClosePosition(ulong ticket)
{
MqlTradeRequest request = {};
MqlTradeResult result = {};
if (!PositionSelectByTicket(ticket)) return;
request.action = TRADE_ACTION_DEAL;
request.position = ticket;
request.symbol = PositionGetString(POSITION_SYMBOL);
request.volume = PositionGetDouble(POSITION_VOLUME);
// Reverse the direction to close
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
request.type = ORDER_TYPE_SELL;
request.price = SymbolInfoDouble(request.symbol, SYMBOL_BID);
} else {
request.type = ORDER_TYPE_BUY;
request.price = SymbolInfoDouble(request.symbol, SYMBOL_ASK);
}
request.deviation = 10;
if (!OrderSend(request, result))
Print("Close failed: ", GetLastError());
}
Building Your First Expert Advisor: SMA Crossover
Let's combine everything into a complete, working Expert Advisor. This EA trades the classic Moving Average crossover strategy:
//+------------------------------------------------------------------+
//| SMA_Crossover_EA.mq5 |
//| MetaTraderGuide.com |
//+------------------------------------------------------------------+
#property copyright "MetaTraderGuide.com"
#property version "1.00"
// --- Input Parameters (adjustable in EA settings) ---
input int FastMA_Period = 10; // Fast MA Period
input int SlowMA_Period = 50; // Slow MA Period
input double LotSize = 0.01; // Position Size
input int StopLoss = 500; // Stop Loss (points)
input int TakeProfit = 1000; // Take Profit (points)
input int MagicNumber = 100001; // EA Magic Number
// --- Global Variables ---
int fastHandle, slowHandle;
//+------------------------------------------------------------------+
int OnInit()
{
fastHandle = iMA(_Symbol, PERIOD_CURRENT, FastMA_Period,
0, MODE_SMA, PRICE_CLOSE);
slowHandle = iMA(_Symbol, PERIOD_CURRENT, SlowMA_Period,
0, MODE_SMA, PRICE_CLOSE);
if (fastHandle == INVALID_HANDLE ||
slowHandle == INVALID_HANDLE) {
Print("Failed to create indicators");
return INIT_FAILED;
}
Print("SMA Crossover EA initialized");
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnTick()
{
// Only trade on new bar open (avoid multiple signals per bar)
static datetime lastBar = 0;
datetime currentBar = iTime(_Symbol, PERIOD_CURRENT, 0);
if (currentBar == lastBar) return;
lastBar = currentBar;
// Get indicator values
double fast[], slow[];
ArraySetAsSeries(fast, true);
ArraySetAsSeries(slow, true);
if (CopyBuffer(fastHandle, 0, 0, 3, fast) < 3) return;
if (CopyBuffer(slowHandle, 0, 0, 3, slow) < 3) return;
// Check for crossover (using previous completed bars)
bool bullishCross = fast[1] > slow[1] && fast[2] <= slow[2];
bool bearishCross = fast[1] < slow[1] && fast[2] >= slow[2];
// Check if we already have a position
bool hasPosition = false;
for (int i = 0; i < PositionsTotal(); i++) {
if (PositionGetSymbol(i) == _Symbol &&
PositionGetInteger(POSITION_MAGIC) == MagicNumber) {
hasPosition = true;
break;
}
}
// Trading logic
if (bullishCross && !hasPosition) {
OpenBuy();
} else if (bearishCross && !hasPosition) {
OpenSell();
}
}
//+------------------------------------------------------------------+
void OpenBuy()
{
MqlTradeRequest req = {};
MqlTradeResult res = {};
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
req.action = TRADE_ACTION_DEAL;
req.symbol = _Symbol;
req.volume = LotSize;
req.type = ORDER_TYPE_BUY;
req.price = ask;
req.sl = ask - StopLoss * point;
req.tp = ask + TakeProfit * point;
req.deviation = 10;
req.magic = MagicNumber;
req.comment = "SMA Cross BUY";
if (!OrderSend(req, res))
Print("Buy failed: ", GetLastError());
else
Print("Buy opened at ", ask);
}
//+------------------------------------------------------------------+
void OpenSell()
{
MqlTradeRequest req = {};
MqlTradeResult res = {};
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
req.action = TRADE_ACTION_DEAL;
req.symbol = _Symbol;
req.volume = LotSize;
req.type = ORDER_TYPE_SELL;
req.price = bid;
req.sl = bid + StopLoss * point;
req.tp = bid - TakeProfit * point;
req.deviation = 10;
req.magic = MagicNumber;
req.comment = "SMA Cross SELL";
if (!OrderSend(req, res))
Print("Sell failed: ", GetLastError());
else
Print("Sell opened at ", bid);
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
IndicatorRelease(fastHandle);
IndicatorRelease(slowHandle);
Print("SMA Crossover EA removed");
}
Backtesting Your EA
After compiling your EA in MetaEditor (press F7), test it using the Strategy Tester:
- Open Strategy Tester: In MetaTrader, press Ctrl+R or go to View > Strategy Tester.
- Select your EA: Choose "SMA_Crossover_EA" from the dropdown.
- Configure settings: Choose the symbol (e.g., EURUSD), timeframe (e.g., H1), date range, and modeling type.
- Modeling type: Use "Every tick based on real ticks" for the most accurate results. "Open prices only" is fastest but less precise.
- Run the test: Click Start and wait for results.
- Analyze results: Check the Results, Graph, and Statistics tabs. Look at net profit, drawdown, profit factor, and number of trades.
Key metrics to evaluate:
- Profit Factor: Total profit / Total loss. Above 1.5 is considered good.
- Maximum Drawdown: Largest peak-to-trough decline. Keep below 20% for sustainable strategies.
- Recovery Factor: Net profit / Max drawdown. Higher is better.
- Total Trades: Enough trades for statistical significance (at least 100-200).
A profitable backtest does not guarantee future results. Always forward-test on a demo account for at least 3 months before deploying any EA with real money. Market conditions change, and past performance is not predictive.
Next Steps
Once you are comfortable with the basics covered here, explore these advanced topics:
- CTrade class: The MQL5 standard library includes the
CTradeclass that simplifies order management with cleaner method calls. - Error handling: Implement robust error handling with retry logic for order failures.
- Multi-timeframe analysis: Access indicator data from different timeframes within a single EA.
- Optimization: Use the Strategy Tester's optimization mode to find optimal parameters for your EA.
- Custom indicators: Create your own indicators with unique calculations and overlay them on charts.
- MQL5 Market: Sell your EAs and indicators to other traders worldwide.
Start Coding on MetaTrader 5
Open a free demo account to access MetaEditor, compile your EAs, and backtest with real historical data.
Open Free MT5 Account →