Summary: This guide explains the complete execution mechanism of Forex EA on charts, covering initialization phase, tick-by-tick loop processing, timer events, and proper deinitialization cleanup.




Understanding EA Execution Mechanism
When you attach an Expert Advisor to a chart in MetaTrader, a specific sequence of events occurs. Understanding this execution mechanism is essential for writing reliable EA code.

Three Core Phases of EA Lifecycle
| Phase | Function | Trigger | Runs |
|-------|----------|---------|------|
| Initialization | OnInit() | EA attached to chart | Once |
| Execution | OnTick() / OnTimer() | Each price tick / timer interval | Repeatedly |
| Deinitialization | OnDeinit() | EA removed or chart closed | Once |

Phase 1: Initialization - What Happens When EA Loads
When you drag an EA onto a chart or restart MT4/MT5, the following sequence occurs:
  • Platform loads the EX4/EX5 compiled file

  • Memory is allocated for global variables

  • Input parameters are read from chart settings

  • OnInit() function is called

  • If OnInit() returns INIT_SUCCEEDED, EA starts normally

  • If OnInit() returns INIT_FAILED, EA stops automatically


  • Complete Initialization Code Example
    ``mql4
    // Input parameters with validation ranges
    input double InpLotSize = 0.1; // Lot size (0.01 to 10)
    input int InpStopLoss = 50; // Stop loss in points
    input int InpTakeProfit = 100; // Take profit in points
    input int InpMagicNumber = 12345; // EA magic number

    // Global variables
    double g_spread;
    int g_totalOrders;
    bool g_initSuccess = false;

    //+------------------------------------------------------------------+
    //| Expert initialization function |
    //+------------------------------------------------------------------+
    int OnInit() {
    Print("========== EA INITIALIZATION START ==========");

    // Step 1: Validate input parameters
    if(InpLotSize < 0.01 || InpLotSize > 10) {
    Print("ERROR: Lot size must be between 0.01 and 10");
    return(INIT_FAILED);
    }

    if(InpStopLoss < 10) {
    Print("ERROR: Stop loss must be at least 10 points");
    return(INIT_FAILED);
    }

    // Step 2: Check trading conditions
    if(IsTradeAllowed() == false) {
    Print("ERROR: Auto-trading is disabled");
    return(INIT_FAILED);
    }

    // Step 3: Get market information
    g_spread = MarketInfo(Symbol(), MODE_SPREAD);
    Print("Current spread: ", g_spread);

    // Step 4: Create chart objects (optional)
    CreateInfoPanel();

    // Step 5: Set timer if needed (1 second interval)
    EventSetTimer(1);

    g_initSuccess = true;
    Print("========== EA INITIALIZATION SUCCESS ==========");
    Print("Symbol: ", Symbol(), " | Period: ", Period());
    Print("Lot size: ", InpLotSize, " | Magic: ", InpMagicNumber);

    return(INIT_SUCCEEDED);
    }

    //+------------------------------------------------------------------+
    //| Create information panel on chart |
    //+------------------------------------------------------------------+
    void CreateInfoPanel() {
    string objName = "InfoPanel";
    if(ObjectFind(0, objName) < 0) {
    ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0);
    ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_TOP_LEFT);
    ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, 10);
    ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, 10);
    ObjectSetString(0, objName, OBJPROP_TEXT, "EA Ready");
    ObjectSetInteger(0, objName, OBJPROP_COLOR, clrWhite);
    }
    }
    `

    Phase 2: Tick Execution - How OnTick Processes Each Price Change
    Every time the market price changes (bid or ask updates), MT4/MT5 triggers the OnTick() function. Understanding this execution frequency is critical:

    | Symbol | Average Ticks Per Minute | EA Execution Per Minute |
    |--------|-------------------------|------------------------|
    | EURUSD (Normal) | 30-60 | 30-60 OnTick calls |
    | EURUSD (News) | 200-500 | 200-500 OnTick calls |
    | XAUUSD (Gold) | 20-40 | 20-40 OnTick calls |
    | BTCUSD (Crypto) | 10-20 | 10-20 OnTick calls |

    Optimized OnTick Structure with Bar Control
    `mql4
    //+------------------------------------------------------------------+
    //| Expert tick function - Optimized version |
    //+------------------------------------------------------------------+
    void OnTick() {
    // CRITICAL: Control execution frequency to avoid over-processing
    static datetime lastBarTime = 0;
    static datetime lastTradeTime = 0;

    // Option 1: Execute only on new bar (recommended for most strategies)
    if(Time[0] == lastBarTime) return;
    lastBarTime = Time[0];

    // Option 2: Execute every N seconds (for timer-based strategies)
    if(TimeCurrent() - lastTradeTime < 5) return;
    lastTradeTime = TimeCurrent();

    // Check if EA is allowed to trade
    if(!IsTradeAllowed()) {
    Comment("Trading disabled. Check Auto-Trading button.");
    return;
    }

    // Main trading logic goes here
    double maFast = iMA(NULL, 0, 10, 0, MODE_SMA, PRICE_CLOSE, 1);
    double maSlow = iMA(NULL, 0, 30, 0, MODE_SMA, PRICE_CLOSE, 1);

    if(maFast > maSlow && CountOrders(InpMagicNumber) == 0) {
    OpenBuyOrder();
    }
    else if(maFast < maSlow && CountOrders(InpMagicNumber) == 0) {
    OpenSellOrder();
    }

    // Update chart display
    UpdateInfoPanel(maFast, maSlow);
    }

    //+------------------------------------------------------------------+
    //| Count orders by magic number |
    //+------------------------------------------------------------------+
    int CountOrders(int magic) {
    int count = 0;
    for(int i = 0; i < OrdersTotal(); i++) {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == magic) {
    count++;
    }
    }
    }
    return count;
    }
    `

    Timer Events - Alternative to OnTick
    Timer events are ideal for strategies that don't need every tick:
    `mql4
    // Set timer in OnInit()
    EventSetTimer(5); // Trigger OnTimer() every 5 seconds

    // Timer event handler
    void OnTimer() {
    static datetime lastCheck = 0;
    if(TimeCurrent() - lastCheck < 60) return; // Check once per minute
    lastCheck = TimeCurrent();

    // Execute strategy at controlled intervals
    CheckMarketConditions();
    }

    // Kill timer in OnDeinit()
    EventKillTimer();
    `

    Phase 3: Deinitialization - Proper Cleanup Process
    When EA is removed, OnDeinit() executes. Proper cleanup prevents memory leaks and chart clutter:
    `mql4
    //+------------------------------------------------------------------+
    //| Expert deinitialization function |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason) {
    Print("========== EA DEINITIALIZATION ==========");
    Print("Reason code: ", reason);
    Print("Reason: ", GetDeinitReason(reason));

    // Step 1: Kill timer if used
    EventKillTimer();

    // Step 2: Delete all objects created by EA
    DeleteAllEAObjects();

    // Step 3: Close any open file handles
    if(FileHandle != INVALID_HANDLE) {
    FileClose(FileHandle);
    }

    // Step 4: Clear chart comment
    Comment("");

    // Step 5: Print final statistics
    Print("Final balance: ", AccountBalance());
    Print("Total ticks processed: ", g_tickCounter);
    Print("=========================================");
    }

    //+------------------------------------------------------------------+
    //| Delete all objects with EA prefix |
    //+------------------------------------------------------------------+
    void DeleteAllEAObjects() {
    int total = ObjectsTotal(0);
    for(int i = total - 1; i >= 0; i--) {
    string objName = ObjectName(0, i);
    if(StringFind(objName, "EA_") == 0) {
    ObjectDelete(0, objName);
    }
    }
    }

    //+------------------------------------------------------------------+
    //| Get readable deinitialization reason |
    //+------------------------------------------------------------------+
    string GetDeinitReason(int reason) {
    switch(reason) {
    case REASON_REMOVE: return "Removed by user";
    case REASON_RECOMPILE: return "Recompiled";
    case REASON_CHARTCLOSE: return "Chart closed";
    case REASON_PARAMETERS: return "Parameters changed";
    case REASON_ACCOUNT: return "Account changed";
    default: return "Unknown reason";
    }
    }
    `

    EA State Diagram
    `
    [EA Dragged to Chart]

    [OnInit()]

    [INIT_SUCCEEDED?]
    / \
    YES NO
    ↓ ↓
    [OnTick Loop] [EA Stops]

    [Price Change?] → YES → [Execute Trading Logic]
    ↓ NO
    [Timer Event?] → YES → [Execute Timer Logic]

    [EA Removed?] → YES → [OnDeinit()] → [Cleanup]
    ``

    Common Execution Issues and Solutions
    | Issue | Cause | Solution |
    |-------|-------|----------|
    | EA executes too many trades | No bar control in OnTick() | Add new bar detection before trading logic |
    | Chart freezes during news | OnTick() too slow | Move heavy calculations to OnTimer() |
    | EA doesn't start | OnInit() returns INIT_FAILED | Check validation conditions and error logs |
    | Object persists after removal | Missing cleanup in OnDeinit() | Add ObjectDelete() for all EA-created objects |
    | Timer not triggering | EventSetTimer() missing or killed | Check EventSetTimer() call in OnInit() |

    Best Practices Summary
  • [ ] Always validate input parameters in OnInit()

  • [ ] Use new bar detection to control OnTick() frequency

  • [ ] Clean up all objects in OnDeinit()

  • [ ] Kill timers before EA removal

  • [ ] Check IsTradeAllowed() before placing orders

  • [ ] Add debug prints for initialization and deinitialization

  • [ ] Use static variables to track state between ticks


  • Reference:
  • MetaQuotes Ltd. "MQL4 Documentation - Execution Model" (2024)

  • Pardo, Robert. "The Evaluation and Optimization of Trading Strategies" (2018)


  • 9. Next Step
    Part 6 will explain MQL4 Programming Quick Start - Variables and Data Types – Understanding all MQL4 data types, variable declaration, scope rules, and type conversion.