Master MT4 EA Programming
Complete guide to creating profitable Expert Advisors from scratch using MQL4
What is MT4 EA Programming?
Expert Advisors (EAs)
Expert Advisors are automated trading programs that execute trades on your behalf in MetaTrader 4. They're written in MQL4 (MetaQuotes Language 4) and can analyze markets, identify opportunities, and place trades 24/7.
Why Learn MQL4?
MQL4 allows you to automate your trading strategies, remove emotions from trading, backtest strategies on historical data, and create custom indicators. It's the key to systematic trading success.
Prerequisites
Basic understanding of trading concepts, familiarity with MT4 platform, and some programming experience (any language) will help, but we'll start from the fundamentals.
MQL4 Programming Basics
Understanding the fundamental concepts and syntax of MQL4 programming language
MQL4 Syntax Fundamentals
Variables, Functions, and Basic Structure
Key Concepts
- ✓ Case-sensitive language
- ✓ Semicolon-terminated statements
- ✓ C-style syntax
- ✓ Strongly typed variables
int magicNumber = 12345; double lotSize = 0.1; string symbol = "EURUSD"; bool isActive = true; // Arrays double prices[100]; string symbols[] = {"EURUSD", "GBPUSD"};
Essential Data Types
Understanding MQL4 Data Types
Numeric Types
int- Integer valuesdouble- Floating pointfloat- Single precisionlong- Long integer
Other Types
string- Text stringsbool- Boolean valuescolor- Color valuesdatetime- Date/time
Special Types
void- No return valueconst- Constantsstatic- Static variablesextern- External variables
Essential Built-in Functions
Core Functions for Trading Operations
Market Data Functions
// Get current prices double bid = MarketInfo(Symbol(), MODE_BID); double ask = MarketInfo(Symbol(), MODE_ASK); // Get historical data double high = iHigh(Symbol(), PERIOD_H1, 1); double low = iLow(Symbol(), PERIOD_H1, 1);
Order Functions
// Open a trade int ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, 0, 0); // Close a trade bool result = OrderClose(ticket, 0.1, Bid, 3);
EA Structure & Main Functions
Understanding the core structure and lifecycle of an Expert Advisor
Complete EA Template
//+------------------------------------------------------------------+ //| BasicEA.mq4 | //| Copyright 2024, TradeInsightPro | //+------------------------------------------------------------------+ // External variables (user inputs) extern double LotSize = 0.1; extern int MagicNumber = 12345; extern int StopLoss = 50; extern int TakeProfit = 100; // Global variables int ticket = 0; bool isTradeOpen = false; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { Print("EA Initialized successfully"); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { Print("EA Stopped"); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Main trading logic goes here CheckForTrade(); ManageOpenTrades(); } //+------------------------------------------------------------------+ //| Check for trade opportunities | //+------------------------------------------------------------------+ void CheckForTrade() { // Example: Simple moving average crossover strategy double fast_ma = iMA(Symbol(), Period(), 10, 0, MODE_SMA, PRICE_CLOSE, 0); double slow_ma = iMA(Symbol(), Period(), 20, 0, MODE_SMA, PRICE_CLOSE, 0); double prev_fast_ma = iMA(Symbol(), Period(), 10, 0, MODE_SMA, PRICE_CLOSE, 1); double prev_slow_ma = iMA(Symbol(), Period(), 20, 0, MODE_SMA, PRICE_CLOSE, 1); // Check if there are no open trades for this EA if (!isTradeOpen) { // Buy signal: Fast MA crosses above Slow MA if (fast_ma > slow_ma && prev_fast_ma <= prev_slow_ma) { double sl_price = NormalizeDouble(Bid - StopLoss * Point, Digits); double tp_price = NormalizeDouble(Ask + TakeProfit * Point, Digits); ticket = OrderSend(Symbol(), OP_BUY, LotSize, Ask, 3, sl_price, tp_price, "My Buy Order", MagicNumber, 0, Green); if (ticket > 0) { Print("Buy order opened successfully. Ticket: ", ticket); isTradeOpen = true; } else { Print("Error opening buy order: ", GetLastError()); } } // Sell signal: Fast MA crosses below Slow MA else if (fast_ma < slow_ma && prev_fast_ma >= prev_slow_ma) { double sl_price = NormalizeDouble(Ask + StopLoss * Point, Digits); double tp_price = NormalizeDouble(Bid - TakeProfit * Point, Digits); ticket = OrderSend(Symbol(), OP_SELL, LotSize, Bid, 3, sl_price, tp_price, "My Sell Order", MagicNumber, 0, Red); if (ticket > 0) { Print("Sell order opened successfully. Ticket: ", ticket); isTradeOpen = true; } else { Print("Error opening sell order: ", GetLastError()); } } } } //+------------------------------------------------------------------+ //| Manage open trades | //+------------------------------------------------------------------+ void ManageOpenTrades() { // Iterate through all open orders for (int i = OrdersTotal() - 1; i >= 0; i--) { if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { // Check if the order belongs to this EA and current symbol if (OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol()) { // Example: Trailing Stop for Buy orders if (OrderType() == OP_BUY) { if (Bid - OrderOpenPrice() > Point * StopLoss) { if (OrderStopLoss() < NormalizeDouble(Bid - StopLoss * Point, Digits)) { bool modified = OrderModify(OrderTicket(), OrderOpenPrice(), NormalizeDouble(Bid - StopLoss * Point, Digits), OrderTakeProfit(), 0); if (modified) { Print("Buy order ", OrderTicket(), " SL modified to ", OrderStopLoss()); } else { Print("Error modifying Buy order SL: ", GetLastError()); } } } } // Example: Trailing Stop for Sell orders else if (OrderType() == OP_SELL) { if (OrderOpenPrice() - Ask > Point * StopLoss) { if (OrderStopLoss() > NormalizeDouble(Ask + StopLoss * Point, Digits) || OrderStopLoss() == 0.0) { bool modified = OrderModify(OrderTicket(), OrderOpenPrice(), NormalizeDouble(Ask + StopLoss * Point, Digits), OrderTakeProfit(), 0); if (modified) { Print("Sell order ", OrderTicket(), " SL modified to ", OrderStopLoss()); } else { Print("Error modifying Sell order SL: ", GetLastError()); } } } } } } } // Update isTradeOpen status based on current open trades isTradeOpen = OrdersTotal() > 0; }
Main Event Handlers Explained
OnInit()
This function is called once when the Expert Advisor is attached to a chart or when the MT4 terminal starts. It's used for initialization tasks like setting up global variables, checking account details, or loading external data.
int OnInit() { // Initialize variables, check account info, etc. Print("EA Initialized successfully."); return(INIT_SUCCEEDED); }
OnDeinit()
This function is called when the Expert Advisor is removed from a chart, the terminal is closed, or its settings are changed. It's crucial for cleanup operations like closing open files, deleting objects, or saving data.
void OnDeinit(const int reason) { // Clean up resources, save data, etc. Print("EA Deinitialized. Reason: ", reason); }
OnTick()
This is the most frequently called function, executing on every new tick (price change) received by the terminal. It contains the main trading logic, including market analysis, trade entry conditions, and trade management.
void OnTick() { // Check for new bar (optional, but good practice) static datetime last_bar_time = 0; if (last_bar_time != Time[0]) { last_bar_time = Time[0]; // Execute logic once per new bar } // Main trading logic CheckForTrade(); ManageOpenTrades(); }
Practical EA Examples
Hands-on examples to build your first functional Expert Advisors
Example 1: Simple Buy/Sell EA
This example demonstrates how to create a very basic EA that opens a buy trade if the current price is above a certain level and a sell trade if it's below, ensuring only one trade is open at a time.
//+------------------------------------------------------------------+ //| SimpleTradeEA.mq4 | //| Copyright 2024, TradeInsightPro | //+------------------------------------------------------------------+ extern double Lots = 0.01; extern int MyMagic = 7890; extern double BuyLevel = 1.0800; // Example price level extern double SellLevel = 1.0700; // Example price level int OnInit() { return(INIT_SUCCEEDED); } void OnDeinit(const int reason) {} void OnTick() { // Check for existing orders by this EA int open_orders = 0; for (int i = OrdersTotal() - 1; i >= 0; i--) { if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if (OrderMagicNumber() == MyMagic && OrderSymbol() == Symbol()) { open_orders++; } } } // If no orders open, look for new trade if (open_orders == 0) { // Buy condition if (Bid > BuyLevel) { OrderSend(Symbol(), OP_BUY, Lots, Ask, 3, 0, 0, "My Simple Buy", MyMagic, 0, Green); } // Sell condition else if (Ask < SellLevel) { OrderSend(Symbol(), OP_SELL, Lots, Bid, 3, 0, 0, "My Simple Sell", MyMagic, 0, Red); } } }
Example 2: Moving Average Crossover EA
This EA implements a classic moving average crossover strategy. It opens a buy trade when a faster moving average crosses above a slower one, and a sell trade when the faster MA crosses below the slower one. It also includes basic stop loss and take profit.
//+------------------------------------------------------------------+ //| MACrossoverEA.mq4 | //| Copyright 2024, TradeInsightPro | //+------------------------------------------------------------------+ extern double Lots = 0.1; extern int FastMAPeriod = 10; extern int SlowMAPeriod = 20; extern int StopLossPips = 50; extern int TakeProfitPips = 100; extern int MagicNumber = 98765; int OnInit() { return(INIT_SUCCEEDED); } void OnDeinit(const int reason) {} void OnTick() { static datetime last_bar_time = 0; if (last_bar_time == Time[0]) return; last_bar_time = Time[0]; double fast_ma_current = iMA(Symbol(), Period(), FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 0); double slow_ma_current = iMA(Symbol(), Period(), SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 0); double fast_ma_prev = iMA(Symbol(), Period(), FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1); double slow_ma_prev = iMA(Symbol(), Period(), SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1); // Check for open positions by this EA int total_orders = OrdersTotal(); bool trade_open = false; for (int i = 0; i < total_orders; i++) { if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if (OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol()) { trade_open = true; break; } } } // Trading logic if (!trade_open) { // Buy signal: Fast MA crosses above Slow MA if (fast_ma_current > slow_ma_current && fast_ma_prev <= slow_ma_prev) { double sl = NormalizeDouble(Bid - StopLossPips * Point, Digits); double tp = NormalizeDouble(Ask + TakeProfitPips * Point, Digits); OrderSend(Symbol(), OP_BUY, Lots, Ask, 3, sl, tp, "MA Buy", MagicNumber, 0, Green); } // Sell signal: Fast MA crosses below Slow MA else if (fast_ma_current < slow_ma_current && fast_ma_prev >= slow_ma_prev) { double sl = NormalizeDouble(Ask + StopLossPips * Point, Digits); double tp = NormalizeDouble(Bid - TakeProfitPips * Point, Digits); OrderSend(Symbol(), OP_SELL, Lots, Bid, 3, sl, tp, "MA Sell", MagicNumber, 0, Red); } } }
Advanced MQL4 Concepts
Take your EA development to the next level with these advanced topics
Risk Management in EAs
Proper risk management is crucial for long-term profitability. Learn how to incorporate dynamic lot sizing, maximum risk per trade, and daily loss limits into your Expert Advisors.
Dynamic Lot Sizing
Calculate lot size based on a percentage of your account balance and stop loss distance.
extern double RiskPerTrade = 1.0; // 1% risk double CalculateLotSize(double stopLossPips) { double accountBalance = AccountBalance(); double accountCurrencyPerPip = MarketInfo(Symbol(), MODE_TICKVALUE); double riskAmount = accountBalance * (RiskPerTrade / 100.0); double lot = riskAmount / (stopLossPips * accountCurrencyPerPip); // Normalize lot size to valid steps (e.g., 0.01) and min/max double min_lot = MarketInfo(Symbol(), MODE_MINLOT); double max_lot = MarketInfo(Symbol(), MODE_MAXLOT); double step_lot = MarketInfo(Symbol(), MODE_LOTSTEP); lot = NormalizeDouble(lot, 2); // Adjust decimal places as needed if (lot < min_lot) lot = min_lot; if (lot > max_lot) lot = max_lot; return lot; }
Daily Loss Limits
Implement a mechanism to stop trading if a certain daily loss threshold is reached.
extern double MaxDailyLossPercent = 5.0; // 5% daily loss limit static double daily_profit = 0.0; static datetime last_day = 0; void CheckDailyLoss() { if (Day() != DayOfWeek(last_day)) { daily_profit = 0.0; // Reset daily profit at start of new day last_day = TimeCurrent(); } // Calculate current daily profit/loss (simplified) double current_equity = AccountEquity(); double initial_balance = AccountBalance(); // Or balance at start of day daily_profit = current_equity - initial_balance; // Needs more robust tracking for actual daily P/L if (daily_profit < -(initial_balance * (MaxDailyLossPercent / 100.0))) { Print("Daily loss limit reached! Stopping EA."); ExpertRemove(); // Detach EA from chart } }
Error Handling & Logging
Robust EAs include comprehensive error handling and logging to diagnose issues and ensure smooth operation.
Common Errors & Codes
Understand the meaning of common MT4 error codes returned by trading operations.
- •
ERR_NO_ERROR (0): Operation successful - •
ERR_INVALID_TRADE_PARAMETERS (147): Invalid SL/TP, lot size, etc. - •
ERR_NO_MONEY (134): Insufficient funds - •
ERR_TRADE_DISABLED (133): Trading is disabled on account - •
ERR_OFF_QUOTES (138): No quotes for symbol
Logging & Debugging
Use `Print()` and `Comment()` functions, along with the Experts tab, to debug your EA.
// Print message to Experts tab Print("Current Bid: ", Bid); // Display info on chart Comment("Account Balance: ", AccountBalance(), "\nOpen Trades: ", OrdersTotal()); // Check for errors after OrderSend/Modify/Close int error = GetLastError(); if (error != 0) { Print("Trade operation failed. Error: ", error, " - ", ErrorDescription(error)); }
Further Resources
Continue your learning journey with these valuable MQL4 programming resources
Official MQL4 Documentation
The comprehensive official reference for MQL4 functions, variables, and concepts.
Visit MQL4 DocsMQL4 Community Forums
Engage with other MQL4 developers, ask questions, and share your EAs.
Join the ForumReady to Build Your Own EA?
Start automating your trading strategies today and unlock the full potential of algorithmic trading with MT4.
Get Started Now