Skip to content
SmartFinanceData
How It Works Strategies Lessons Market Statistics Tools & Dashboards Contact
View All Pairs
FX Pairs Methodology Testimonials FAQ View All Pairs

Master MT4 EA Programming

Complete guide to creating profitable Expert Advisors from scratch using MQL4

💻 Hands-on Coding
🤖 Build Real EAs
⏱️ 45 Min Read

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

1

MQL4 Syntax Fundamentals

Variables, Functions, and Basic Structure

Essential

Key Concepts

  • ✓ Case-sensitive language
  • ✓ Semicolon-terminated statements
  • ✓ C-style syntax
  • ✓ Strongly typed variables
Basic Variable Declaration
int magicNumber = 12345;
double lotSize = 0.1;
string symbol = "EURUSD";
bool isActive = true;

// Arrays
double prices[100];
string symbols[] = {"EURUSD", "GBPUSD"};
2

Essential Data Types

Understanding MQL4 Data Types

Numeric Types

  • int - Integer values
  • double - Floating point
  • float - Single precision
  • long - Long integer

Other Types

  • string - Text strings
  • bool - Boolean values
  • color - Color values
  • datetime - Date/time

Special Types

  • void - No return value
  • const - Constants
  • static - Static variables
  • extern - External variables
3

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
//+------------------------------------------------------------------+
//| 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
//+------------------------------------------------------------------+
//| 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
//+------------------------------------------------------------------+
//| 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 Docs

MQL4 Community Forums

Engage with other MQL4 developers, ask questions, and share your EAs.

Join the Forum

Ready to Build Your Own EA?

Start automating your trading strategies today and unlock the full potential of algorithmic trading with MT4.

Get Started Now

SmartFinanceData

Providing traders with statistically-backed market probabilities since 2020.

Company

  • About
  • Blog
  • Features
  • Contact Us

Resources

  • Our Process
  • Dashboard (Beta)
  • Calculator Hub
  • Sample Size & Bias
  • How Our Stats Are Calculated
  • Win Rate Limitations
  • Help/FAQ's

Legal

  • Privacy Policy
  • Terms of Service
  • Disclaimer

Subscribe to our newsletter

The latest insights, articles, and resources, sent to your inbox weekly.

© 2026 FX Insights. All rights reserved.