How to Code your first Expert Advisor (MQL5 Beginner's Tutorial)
Learn to code automated trading strategies in MT5. A step-by-step MQL5 tutorial covering ticks, orders, historical backtesting, and risk management.
How to Code your first Expert Advisor (MQL5 Beginner's Tutorial)
In the contemporary financial landscape of 2026, algorithmic trading has evolved from an exclusive institutional luxury into the standard operating model for successful retail day traders. Over 65% of all active trading volume on MetaTrader 5 (MT5) is executed by automated trading bots, commonly referred to as Expert Advisors (EAs).
While manual trading is highly vulnerable to emotional fatigue, hesitation, execution latency, and physical fatigue, algorithmic execution operates with absolute mathematical precision. A custom-coded Expert Advisor scans market tick feeds, computes mathematical indicators, evaluates strict risk parameters, and executes buy or sell orders within milliseconds—24 hours a day, 5 days a week.
This comprehensive, institutional-grade masterclass guide serves as your absolute MQL5 Beginner's Tutorial. We will break down the structural architecture of MetaTrader 5, introduce the lifecycle of an algorithm, compare MQL5 against other major trading languages, provide a complete, compilable Moving Average Crossover EA script, and outline a step-by-step roadmap to backtest and deploy your first trading bot.
[!IMPORTANT] Helpful Content Regulatory Compliance & Risk Notice Disclaimer: Coding and deploying automated Expert Advisors on leveraged financial markets, derivatives, and CFDs carries an extreme risk of capital loss. Algorithms can execute trades rapidly, which can multiply losses during unexpected market volatility. Always backtest EAs rigorously on high-fidelity tick data, test setups on demo accounts first, and configure hard equity-based circuit breakers before allocating live funded capital.
1. MetaTrader 5 (MT5) Architecture & The MQL5 Language
Before writing your first line of code, you must understand the environment in which your Expert Advisor will run. MetaTrader 5 is a multi-threaded, highly optimized trading platform designed to interface directly with institutional brokers and liquidity clearinghouses.
1.1 The MQL5 Programming Environment
MQL5 (MetaQuotes Language 5) is a high-level, object-oriented programming language based on C++. If you have any experience with C++, C#, Java, or JavaScript, you will find MQL5's syntax remarkably familiar.
Unlike general-purpose languages, MQL5 is specifically built for financial engineering. It features native classes, functions, and data structures for:
- Fetching historical price candles (Open, High, Low, Close, Volume).
- Reading live broker market feeds (Ask, Bid, Spread, Tick volume).
- Interfacing with technical indicators (Moving Averages, RSI, MACD).
- Constructing and executing trade orders via ECN execution bridges.
1.2 MQL5 vs. Other Trading Languages
To help you understand MQL5's role, we have compiled a structural comparison against the other primary trading development environments in 2026.
| Programming Language | Speed / Performance | Learning Curve | Backtesting Engine | Primary Platforms | Best Used For |
|---|---|---|---|---|---|
| MQL5 (C++ derivative) | Extremely Fast (Compiled) | Moderate to Hard | Exceptional (Multi-threaded ticks) | MetaTrader 5 | High-performance automated EAs |
| MQL4 (Legacy MT4) | Moderate (Legacy) | Moderate | Basic (Single-threaded) | MetaTrader 4 | Legacy retail trading bots |
| Pine Script v6 | Slow (Server-executed) | Very Easy | Basic (Bar-based) | TradingView | Chart indicators & basic web alerts |
| C# (.NET Framework) | Fast (Compiled) | Moderate | Excellent | cTrader | Institutional-grade API systems |
| Python | Slow (Interpreted) | Very Easy | Custom (Variable) | Independent / API | Quantitative research & machine learning |
2. The Lifecycle of an Expert Advisor
An Expert Advisor is not a standard program that runs from start to finish and then closes. It is an event-driven program. The platform is constantly listening for events—such as a new price quote (tick), a user changing chart parameters, or a trade order filling—and triggers specific blocks of code in response.
2.1 The Core MQL5 Event Handlers
A standard MQL5 Expert Advisor is structured around three primary entry points:
OnInit(): Triggered immediately when you drag and drop the EA onto a chart. This function is used to initialize indicators, configure global variables, and set up the trading dashboard.OnTick(): Triggered every single time a new price quote (tick) is received for the chart's symbol. This is the heart of your algorithm, where entry and exit logic, position monitoring, and risk sizing calculations are executed.OnDeinit(const int reason): Triggered when the EA is removed from the chart, the terminal is closed, or chart settings are modified. This function cleans up memory, deletes custom chart objects, and resets indicators.
2.2 Algorithm Execution Flowchart
The following flowchart demonstrates how an order request flows through the event-driven MQL5 structure:
graph TD
Start[User Drags EA to MT5 Chart] --> OnInit[OnInit: Initialize indicators & load parameters]
OnInit --> Loop{Is terminal active?}
Loop -- Yes --> OnTick[OnTick: Awaits New Price Tick]
OnTick --> Indicator[Read Indicator Buffer Data]
Indicator --> Logic{Do entry rules align?}
Logic -- Buy/Sell --> Order[Submit trade order via CTrade]
Logic -- No Action --> Loop
Order --> Execution[Execution logged on ECN Bridge]
Execution --> Loop
Loop -- No / EA Removed --> OnDeinit[OnDeinit: Clear memory & delete UI labels]
3. The 9/21 EMA Crossover Strategy Specification
Before we write the code, we must specify the exact mathematical and technical rules governing our strategy. We will build a highly popular Exponential Moving Average (EMA) Crossover Bot.
3.1 Strategy Parameters:
- Fast EMA: 9 Periods (Reacts quickly to short-term price momentum).
- Slow EMA: 21 Periods (Identifies the broader intraday trend).
- Execution Asset: EUR/USD (Highly liquid, ultra-tight spreads).
- Timeframe: 15-Minute (M15 - Excellent balance between noise reduction and trade frequency).
3.2 Strategy Entry Logic:
- Buy Trigger (Long Entry): The 9 EMA crosses above the 21 EMA. This indicates bullish momentum is accelerating.
- Sell Trigger (Short Entry): The 9 EMA crosses below the 21 EMA. This indicates bearish momentum is accelerating.
3.3 Stop-Loss and Take-Profit Mathematics:
We must configure structural protection on every single trade to comply with professional risk guidelines.
- Stop-Loss (SL): Placed at a fixed distance of 20 Pips from the entry price.
- Take-Profit (TP): Placed at a fixed distance of 40 Pips from the entry price, establishing a clean 1:2 Risk-to-Reward Ratio.
- Lot Sizing Sizer: Programmatically calculated to risk exactly 1.0% of total account equity per trade.
Lot Size Calculation Formula:
Lots = (Account Balance * Risk %) / (Stop Loss in Pips * Pip Value)
4. Complete, Compilable MQL5 Moving Average Crossover EA
Let us write the complete, compilable MQL5 code. To create this file, open your MetaEditor (F4 in MetaTrader 5), create a new Expert Advisor project, and paste the following code. Every line has been heavily commented to serve as an exhaustive educational resource.
//+------------------------------------------------------------------+
//| EMACrossoverExpert.mq5 |
//| Copyright 2026, AlphaTradeCircle|
//| https://alphatradecircle.com|
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, AlphaTradeCircle"
#property link "https://alphatradecircle.com"
#property version "1.00"
#property description "Automated 9/21 EMA Crossover EA with dynamic lot sizing and UI panel"
//--- Include the standard trade library class
#include <Trade\Trade.mqh>
CTrade trade; // Declare the global trading execution object
//--- Input parameters (Editable in MT5 properties window)
input group "---- TIMEFRAME CONFIG ----"
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_M15; // Trading Timeframe
input group "---- STRATEGY CONFIG ----"
input int InpFastEMAPeriod = 9; // Fast EMA Period
input int InpSlowEMAPeriod = 21; // Slow EMA Period
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; // Indicator Price Source
input group "---- RISK MANAGEMENT ----"
input double InpRiskPercent = 1.00; // Risk Percent per Trade (%)
input double InpStopLossPips = 20.0; // Fixed Stop Loss in Pips
input double InpTakeProfitPips = 40.0; // Fixed Take Profit in Pips
input ulong InpMagicNumber = 999123; // Unique EA Magic Number
//--- Global Variables
int g_fast_ema_handle = INVALID_HANDLE;
int g_slow_ema_handle = INVALID_HANDLE;
double g_fast_ema_values[];
double g_slow_ema_values[];
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Set the EA's trade identifier Magic Number
trade.SetExpertMagicNumber(InpMagicNumber);
// Allocate indicators handles
g_fast_ema_handle = iMA(_Symbol, InpTimeframe, InpFastEMAPeriod, 0, MODE_EMA, InpAppliedPrice);
g_slow_ema_handle = iMA(_Symbol, InpTimeframe, InpSlowEMAPeriod, 0, MODE_EMA, InpAppliedPrice);
// Safety check indicator creation
if(g_fast_ema_handle == INVALID_HANDLE || g_slow_ema_handle == INVALID_HANDLE)
{
Print("CRITICAL: Failed to initialize indicator handles! Terminating EA.");
return(INIT_FAILED);
}
// Set array indexing style to match chronological flow (0 = current bar, 1 = previous bar, etc.)
ArraySetAsSeries(g_fast_ema_values, true);
ArraySetAsSeries(g_slow_ema_values, true);
// Create Chart HUD Dashboard
CreateDashboardUI();
Print("Initialization successful! EMACrossoverExpert loaded on ", _Symbol);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Release indicator resources
IndicatorRelease(g_fast_ema_handle);
IndicatorRelease(g_slow_ema_handle);
// Clean up charts visual objects
ObjectsDeleteAll(0, "UI_");
Print("Deinitialization complete. Resources released.");
}
//+------------------------------------------------------------------+
//| Expert tick processing function |
//+------------------------------------------------------------------+
void OnTick()
{
// Check if a new candle bar has formed to prevent executing multiple trades on a single candle tick
static datetime last_bar_time = 0;
datetime current_bar_time = iTime(_Symbol, InpTimeframe, 0);
if(current_bar_time == last_bar_time) return; // Wait for the close of the current bar
// Copy latest indicator values into buffers
if(CopyBuffer(g_fast_ema_handle, 0, 0, 3, g_fast_ema_values) < 3 ||
CopyBuffer(g_slow_ema_handle, 0, 0, 3, g_slow_ema_values) < 3)
{
Print("WARNING: Failed to copy indicator buffer values. Retrying next tick.");
return;
}
// Define Crossover logic states:
// Bar 0 is the current active forming candle. To prevent false signals, we evaluate closed candles:
// Bar 1 = Most recently closed complete candle.
// Bar 2 = The candle preceding Bar 1.
double fastEMA_bar1 = g_fast_ema_values[1];
double fastEMA_bar2 = g_fast_ema_values[2];
double slowEMA_bar1 = g_slow_ema_values[1];
double slowEMA_bar2 = g_slow_ema_values[2];
// Check for active open positions for this specific Magic Number
bool has_buy_position = false;
bool has_sell_position = false;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_MAGIC) == InpMagicNumber)
{
long type = PositionGetInteger(POSITION_TYPE);
if(type == POSITION_TYPE_BUY) has_buy_position = true;
if(type == POSITION_TYPE_SELL) has_sell_position = true;
}
}
// Update live dashboard HUD values
UpdateDashboardUI(fastEMA_bar1, slowEMA_bar1);
// BUY Trigger Check: Fast EMA crosses ABOVE Slow EMA
// Condition: Fast EMA (bar2) <= Slow EMA (bar2) AND Fast EMA (bar1) > Slow EMA (bar1)
if(fastEMA_bar2 <= slowEMA_bar2 && fastEMA_bar1 > slowEMA_bar1)
{
if(!has_buy_position)
{
// If a sell position exists, close it first
if(has_sell_position) CloseAllPositions(POSITION_TYPE_SELL);
ExecuteMarketOrder(ORDER_TYPE_BUY);
last_bar_time = current_bar_time; // Lock execution for this bar
}
}
// SELL Trigger Check: Fast EMA crosses BELOW Slow EMA
// Condition: Fast EMA (bar2) >= Slow EMA (bar2) AND Fast EMA (bar1) < Slow EMA (bar1)
if(fastEMA_bar2 >= slowEMA_bar2 && fastEMA_bar1 < slowEMA_bar1)
{
if(!has_sell_position)
{
// If a buy position exists, close it first
if(has_buy_position) CloseAllPositions(POSITION_TYPE_BUY);
ExecuteMarketOrder(ORDER_TYPE_SELL);
last_bar_time = current_bar_time; // Lock execution for this bar
}
}
}
//+------------------------------------------------------------------+
//| Execute Market Order with Risk Sizing |
//+------------------------------------------------------------------+
void ExecuteMarketOrder(ENUM_ORDER_TYPE order_type)
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double price = (order_type == ORDER_TYPE_BUY) ? ask : bid;
// Calculate Stop Loss and Take Profit levels
double sl_distance = InpStopLossPips * _Point * 10; // Convert pips to points (for 5-digit brokers)
double tp_distance = InpTakeProfitPips * _Point * 10;
double sl = (order_type == ORDER_TYPE_BUY) ? (price - sl_distance) : (price + sl_distance);
double tp = (order_type == ORDER_TYPE_BUY) ? (price + tp_distance) : (price - tp_distance);
// Programmatic Lot Sizing Calculation
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double risk_amount = balance * (InpRiskPercent / 100.0);
double tick_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double lot_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
double min_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double max_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
if(tick_value <= 0 || tick_size <= 0) return;
// Calculate risk per point
double points_at_risk = sl_distance;
double value_per_lot_point = (tick_value / tick_size) * _Point;
double calculated_lots = risk_amount / (points_at_risk * value_per_lot_point);
// Round lot size to broker specifications
calculated_lots = MathFloor(calculated_lots / lot_step) * lot_step;
// Clip lot sizes to broker limits
if(calculated_lots < min_lot) calculated_lots = min_lot;
if(calculated_lots > max_lot) calculated_lots = max_lot;
// Execute Order
if(order_type == ORDER_TYPE_BUY)
{
trade.Buy(calculated_lots, _Symbol, price, sl, tp, "EMA Bullish Cross");
}
else
{
trade.Sell(calculated_lots, _Symbol, price, sl, tp, "EMA Bearish Cross");
}
}
//+------------------------------------------------------------------+
//| Close all positions by type |
//+------------------------------------------------------------------+
void CloseAllPositions(ENUM_POSITION_TYPE filter_type)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0)
{
if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == InpMagicNumber)
{
long type = PositionGetInteger(POSITION_TYPE);
if(type == filter_type)
{
trade.PositionClose(ticket);
}
}
}
}
}
//+------------------------------------------------------------------+
//| Chart Visual UI routines |
//+------------------------------------------------------------------+
void CreateDashboardUI()
{
// Main dashboard panel panel
ObjectCreate(0, "UI_Panel", OBJ_RECTANGLE_LABEL, 0, 0, 0);
ObjectSetInteger(0, "UI_Panel", OBJPROP_XDISTANCE, 20);
ObjectSetInteger(0, "UI_Panel", OBJPROP_YDISTANCE, 50);
ObjectSetInteger(0, "UI_Panel", OBJPROP_XSIZE, 240);
ObjectSetInteger(0, "UI_Panel", OBJPROP_YSIZE, 110);
ObjectSetInteger(0, "UI_Panel", OBJPROP_BGCOLOR, clrBlack);
ObjectSetInteger(0, "UI_Panel", OBJPROP_COLOR, clrGold);
ObjectSetInteger(0, "UI_Panel", OBJPROP_BORDER_TYPE, BORDER_FLAT);
// Dashboard Titles
CreateLabelUI("UI_Title", "ALPHA CODER MQL5 EA", 30, 60, 10, "Outfit", clrGold);
CreateLabelUI("UI_FastEMA", "Fast EMA (9): 0.00000", 30, 85, 9, "Inter", clrWhite);
CreateLabelUI("UI_SlowEMA", "Slow EMA (21): 0.00000", 30, 105, 9, "Inter", clrWhite);
CreateLabelUI("UI_Status", "Status: Scanning Markets", 30, 125, 9, "Inter", clrLightGray);
}
void UpdateDashboardUI(double fast_ema, double slow_ema)
{
ObjectSetString(0, "UI_FastEMA", OBJPROP_TEXT, "Fast EMA (9): " + DoubleToString(fast_ema, 5));
ObjectSetString(0, "UI_SlowEMA", OBJPROP_TEXT, "Slow EMA (21): " + DoubleToString(slow_ema, 5));
string status = "Status: Scanning Markets";
color col = clrLightGray;
if(fast_ema > slow_ema)
{
status = "Status: Bullish Momentum";
col = clrGold;
}
else if(fast_ema < slow_ema)
{
status = "Status: Bearish Momentum";
col = clrRed;
}
ObjectSetString(0, "UI_Status", OBJPROP_TEXT, status);
ObjectSetInteger(0, "UI_Status", OBJPROP_COLOR, col);
ChartRedraw(0);
}
void CreateLabelUI(string name, string text, int x, int y, int size, string font, color col)
{
ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
ObjectSetString(0, name, OBJPROP_TEXT, text);
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, size);
ObjectSetString(0, name, OBJPROP_FONT, font);
ObjectSetInteger(0, name, OBJPROP_COLOR, col);
}
//--- Theme Colors
color clrGold = D'212,175,55';
color clrLightGray = D'200,200,200';
color clrRed = D'220,50,50';
color clrWhite = D'255,255,255';
5. Deconstructing the MQL5 Source Code
To build a solid programming foundation, you must understand exactly what the compiler is doing behind the scenes of our script. Let us deconstruct the key functions and architectures of our Moving Average Crossover EA.
5.1 The CTrade Class and Order Execution
In legacy MQL4 (MetaTrader 4), opening a trade required using the complex OrderSend() function, which demanded 11 separate arguments, including manual slip calculations, ask/bid tick adjustments, and ticket tracking.
MQL5 simplifies this dramatically by introducing the Standard Library. By importing <Trade\Trade.mqh> and declaring the CTrade trade; object, we gain access to highly optimized, institutional-grade order wrappers:
trade.Buy()andtrade.Sell(): Execute market orders with absolute slip protection.trade.SetExpertMagicNumber(): Automatically tags every transaction ticket with your EA's unique Magic Number. This prevents the bot from modifying or closing trades executed manually by the user or by other algorithms running on the same account.
5.2 Dynamic Lot Sizing Mathematics
Professional prop firm risk models mandate that traders never risk a fixed lot size. If your account size grows or shrinks, your risk exposure per trade must adapt proportionally.
Our Expert Advisor features a fully automated Dynamic Sizing Sizer. It retrieves:
ACCOUNT_BALANCE: The current baseline capital.SYMBOL_TRADE_TICK_SIZEandSYMBOL_TRADE_TICK_VALUE: The broker's wholesale pricing properties.SYMBOL_VOLUME_STEP,SYMBOL_VOLUME_MIN, andSYMBOL_VOLUME_MAX: The broker's strict volume limits.
Using these parameters, the algorithm calculates the exact value of a single point, multiplies it by your stop-loss distance (20 pips = 200 points), and computes the maximum lot size that will risk exactly 1.0% of your capital. It then rounds the resulting volume to match the broker's step size, preventing common "Invalid Volume" order rejection errors.
6. How to Compile, Backtest, and Deploy Your EA
Once your code is written in MetaEditor, you must follow the professional deployment lifecycle.
6.1 Compiling the Code
To convert your human-readable MQL5 source code (.mq5) into an executable file (.ex5) that MetaTrader 5 can read:
- Press F7 inside the MetaEditor, or click the Compile button in the top menu bar.
- The compiler will scan your script for syntax errors, missing semicolons, or invalid type declarations.
- If successful, the "Errors" tab at the bottom will display
0 error(s), 0 warning(s). If errors occur, double-click the error message to jump to the exact line requiring correction.
6.2 Backtesting in the MT5 Strategy Tester
Before deploying any algorithm on live or funded capital, you must backtest it over historical market data to verify its statistical edge. MT5 features the industry's most advanced backtesting engine.
[MT5 Strategy Tester Parameters Setup]
1. Press Ctrl+R to open the Strategy Tester.
2. Select your compiled "EMACrossoverExpert.ex5".
3. Set Symbol to "EURUSD" and Timeframe to "M15".
4. Set Date to "Last 1 Year" or "Custom Period".
5. Set Execution Modeling to "Every tick based on real ticks" (Crucial for high-fidelity spreads).
6. Enable Optimization to find the best EMA settings.
7. Click "Start".
Once complete, review the Backtest Report. Key metrics to evaluate include:
- Profit Factor: Gross profits divided by gross losses (Aim for > 1.4).
- Max Equity Drawdown: The maximum peak-to-trough decline (Must stay strictly below your prop limit, ideally under 4%).
- Win Rate: The percentage of profitable trades (Typically 40-55% for trend-following crossover strategies).
7. The 30-Day Algorithmic Developer Roadmap
Mastering algorithmic trading is a progressive journey. We have designed a step-by-step 30-Day Developer Roadmap to guide your progress from an absolute beginner to an advanced MQL5 architect.
Phase 1: Foundations (Days 1–7)
- Goal: Master basic C++ syntax, structural types, and operators.
- Tasks:
- Complete our MQL5 Beginner's Tutorial.
- Practice declaring variables (
double,int,string,bool). - Experiment with basic loops (
for,while) and conditional blocks (if,else).
Phase 2: Indicator Integration (Days 8–14)
- Goal: Integrate indicators and pull buffer data programmatically.
- Tasks:
- Study how handles are initialized inside
OnInit(). - Write a custom script that reads RSI indicator buffers and prints values to the terminal log.
- Understand the difference between
Seriesarray sorting and standard sorting.
- Study how handles are initialized inside
Phase 3: Trade Execution & Risk Control (Days 15–21)
- Goal: Manage active orders, stop-losses, and lot-sizing models.
- Tasks:
- Master the
CTradelibrary functions. - Implement trailing stop-losses that automatically secure profits as price moves in your favor.
- Integrate our compilable MQL5 Daily Drawdown Guardian logic to lock in accounts before a breach.
- Master the
Phase 4: Strategy Testing & Optimization (Days 22–30)
- Goal: Run high-fidelity backtests and deploy on live demo servers.
- Tasks:
- Obtain historical tick data from premium ECN brokers.
- Run multi-variable optimization sweeps in the Strategy Tester.
- Deploy your compiled
.ex5files on a collocated London (LD4) Virtual Private Server (VPS).
8. Deep-Dive Frequently Asked Questions (FAQ)
Q1: What is the difference between CopyBuffer() and reading indicator values?
In MQL5, you do not read values directly from indicator functions. Instead, the platform creates an indicator "handle" inside OnInit(). To read the values, you must copy a specified range of data from that handle's memory buffer into a local array using CopyBuffer().
Q2: Why does my EA open multiple trades on the exact same candle?
By default, the OnTick() function executes on every single price tick (which can happen multiple times per second). If your crossover condition remains true throughout the entire candle, the EA will continuously trigger buy or sell orders. To prevent this, our script integrates a static datetime last_bar_time check to guarantee only one transaction executes per closed candle.
Q3: How do I resolve the "Invalid volume/lot step" trade rejection error?
Brokers enforce minimum lot steps (usually 0.01 lots). If your dynamic lot sizing calculation outputs an unrounded number (e.g. 0.1235 lots), the broker's server will reject the order. You must round your lot sizing calculation to match the broker's step size: MathFloor(calculated_lots / lot_step) * lot_step.
Q4: Does MQL5 work on Mac or Linux?
The MetaTrader 5 terminal and MetaEditor are native Windows programs. However, they run flawlessly on Mac and Linux systems using compatibility layers like Wine or through specialized Mac packages provided directly by premier brokers.
Q5: How do I backtest using real spreads and commissions?
In the Strategy Tester, select "Every tick based on real ticks" under execution modeling. This fetches high-fidelity historical market logs, including the broker's actual spread expansions and commissions, ensuring your backtest results accurately reflect live trading conditions.
Q6: What is a Magic Number, and why is it mandatory?
A Magic Number is a unique ulong identifier assigned to every trade executed by an EA. It acts as an electronic signature, allowing your bot to isolate and manage its own positions without interfering with trades opened manually by you or by other automated EAs running on the same account.
Q7: Why did my EA compile successfully but fails to execute on the chart?
Verify that you have enabled Algo Trading in your MT5 terminal (the gold button in the top menu bar) and checked the Allow Algo Trading box in your Expert Advisor's properties window. Check the "Experts" tab at the bottom of MT5 to view execution logs and identify error codes.
Q8: How can I send trade alerts directly to my mobile phone?
MT5 features a built-in push notification system. Fetch your unique MetaQuotes ID from your mobile app, paste it under Tools -> Options -> Notifications in your desktop terminal, and call SendNotification() inside your MQL5 script.
Q9: Can I run multiple EAs on the same trading account?
Yes, you can run multiple EAs on a single account, provided they are applied to separate charts and utilize unique Magic Numbers. This is a powerful diversification strategy to balance multiple indicators and timeframes.
Q10: How do I deploy my compiled EA on a VPS server?
Simply right-click your broker server's active name in the MT5 Navigator panel, select "Register a Virtual Server", choose your collocated cloud center, and click "Migrate EAs and Indicators". This pushes your compiled .ex5 files directly to the cloud, ensuring 99.99% runtime.
9. Conclusion & Professional Development Guidelines
Coding your first Expert Advisor represents a major milestone in your algorithmic trading journey. By transitioning from manual execution to automated, rules-based systems, you eliminate the emotional biases that lead to retail account failures.
As you continue developing your skills, remember to maintain strict risk parameters, respect your account's drawdown floors, perform rigorous backtests on real ticks, and treat your trading portfolios with the institutional care they demand.
Treat every coding project as an opportunity to refine your quantitative systems, respect the mathematical limits of the markets, and trade with discipline. Good luck compiling your first Expert Advisor!
Ready to choose a broker?
Use our tools to find the perfect match for your trading style.
Get Weekly Forex Insights
Join traders who receive our weekly broker reviews, market analysis, and trading tool updates. Free, no spam.
No spam. Unsubscribe anytime. We respect your privacy.
Related Articles
How to Pass the FTMO Challenge: A Math-Backed Trader Blueprint
Passing the FTMO challenge is not about luck; it is about risk management and math. We detail the exact capital sizing, drawdown buffers, and daily reset rules.
Cheapest Prop Firm Challenges compared: Fee vs Account Size Matrix
Looking for the best value prop firm? We compare challenge fees, refund policies, and account sizes across 20+ prop trading firms in 2026.
Instant Funding Prop Firms 2026: Skip Evaluations, Earn Splits from Day 1
Skip the multi-phase evaluation stress. We compare the best direct instant funding prop firms on profit splits, drawdowns, and scaling plans.
Drawdown Calculations Decoded: Equity-based vs Balance-based Drawdowns
Understanding how your daily and total drawdown limits are calculated is the difference between keeping your account and getting breached.