Beyond OnTick(): Mastering the Full MQL5 Event Model

Beyond OnTick(): Mastering the Full MQL5 Event Model

18 September 2025, 03:38
Michael Prescott Burney
0
448

Most MQL5 coders build their trading robots around a core trio of functions: OnInit() , OnDeinit() , and OnTick() . While this foundation is essential, it's like trying to cook a gourmet meal with only a pot, a pan, and a single burner. The MQL5 environment offers a rich event model with specialized handlers that can make your Expert Advisors (EAs) and indicators more efficient, interactive, and powerful.

By venturing beyond the basics, you can build EAs that don't rely on every single tick, create dynamic user interfaces directly on the chart, and even monitor and react to trading activity from other sources. Let's explore three of the most powerful—and underutilized—event handlers: OnTimer() , OnChartEvent() , and OnTradeTransaction() .

Ditch the Tick: Building EAs with OnTimer()

The OnTick() event handler is the default workhorse for most EAs, executing its logic every time a new price quote arrives. This is great for high-frequency strategies but is incredibly inefficient and unnecessary for strategies designed for higher timeframes like H1, H4, or D1. Why check your logic multiple times a second when you only care about the state of a new bar once an hour?

The OnTimer() event handler solves this problem. It allows you to create a custom, periodic trigger for your code, completely independent of incoming ticks. 🕒

How It Works

  1. Set the Timer: In your OnInit() function, you call EventSetTimer(seconds) . This tells the terminal to start generating a timer event every specified number of seconds.

  2. Execute the Logic: You place your trading logic inside the OnTimer() function. This function will now be called at the interval you defined.

  3. Kill the Timer: In your OnDeinit() function, you must call EventKillTimer() to stop the timer event when the EA is removed from the chart. This is crucial for preventing resource leaks.

Example: A Non-Ticking EA for Higher Timeframes

Let's build a simple EA that checks for a new bar on the H1 timeframe every minute, rather than on every tick.

//+------------------------------------------------------------------+
//|                                                      OnTimerEA.mq5 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, EAHQ"
#property link      "https://www.mql5.com/en/users/michael4308"
#property version   "1.00"

// Global variable to store the time of the last checked bar
datetime lastBarTime = 0;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   // Set a timer to trigger every 60 seconds
   EventSetTimer(60);
   Print("Timer EA Initialized. Checking for new H1 bar every minute.");
   
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   // It's crucial to kill the timer on deinitialization
   EventKillTimer();
   Print("Timer EA Removed. Timer stopped.");
}
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
{
   // Get the time of the most recent H1 bar
   datetime newBarTime = (datetime)SeriesInfoInteger(_Symbol, PERIOD_H1, SERIES_LASTBAR_DATE);
   
   // Check if a new bar has formed since our last check
   if(newBarTime > lastBarTime)
   {
      // A new H1 bar has appeared! Update our last bar time
      lastBarTime = newBarTime;
      
      Print("New H1 Bar Detected at: ", TimeToString(newBarTime));
      
      // --- INSERT YOUR TRADING LOGIC HERE ---
      // For example, check moving average crossover, RSI levels, etc.
   }
}
//+------------------------------------------------------------------+
//| OnTick function (not used in this EA)                            |
//+------------------------------------------------------------------+
void OnTick()
{
  //--- This function is intentionally left empty ---
}

By using OnTimer() , this EA is far more efficient. It only consumes CPU resources once a minute, leaving your terminal more responsive and reducing the processing load, which is especially important when running multiple EAs.


Making Charts Interactive with OnChartEvent()

Have you ever wanted to let a user draw a line on a chart to set a take-profit level or drag a rectangle to define a trading zone? The OnChartEvent() handler is your gateway to creating rich, interactive chart tools. 🎨

This function is a master listener that captures a wide range of user interactions with the chart, such as mouse clicks, key presses, and—most powerfully—interactions with graphical objects.

How It Works

The OnChartEvent() function receives several parameters, but the most important are:

  • id : The type of event that occurred (e.g., a key was pressed, an object was created).

  • lparam , dparam , sparam : Parameters containing detailed information about the event. Their meaning depends on the id .

For object interactions, you'll often check for these event IDs:

  • CHARTEVENT_OBJECT_CREATE : Fired when a user finishes drawing a new object.

  • CHARTEVENT_OBJECT_DRAG : Fired when a user drags an object across the chart.

  • CHARTEVENT_OBJECT_CLICK : Fired when a user clicks on an object.

Example: Setting a Take-Profit Level with a Line

Let's create an indicator that allows the user to draw a horizontal line. The indicator will then read the price level of that line and print it to the Experts log, simulating how an EA could use it to set a Take-Profit.

//+------------------------------------------------------------------+
//|                                             InteractiveChart.mq5 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, EAHQ"
#property indicator_chart_window

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{
   // Check if the event is the creation of a new object
   if(id == CHARTEVENT_OBJECT_CREATE)
   {
      // sparam contains the name of the newly created object
      Print("Object Created: ", sparam);
      
      // Check if the object is a Horizontal Line
      if(ObjectType(sparam) == OBJ_HLINE)
      {
         // Get the price level of the horizontal line
         double priceLevel = ObjectGetDouble(0, sparam, OBJPROP_PRICE, 0);
         Print("Take-Profit level set by HLine '", sparam, "' at price: ", DoubleToString(priceLevel, _Digits));
         
         // An EA could now use this 'priceLevel' variable for trading
      }
   }
   
   // Check if an existing object is being dragged
   if(id == CHARTEVENT_OBJECT_DRAG)
   {
       // sparam contains the name of the object being dragged
       if(ObjectType(sparam) == OBJ_HLINE)
       {
         double priceLevel = ObjectGetDouble(0, sparam, OBJPROP_PRICE, 0);
         Comment("Current TP Level: ", DoubleToString(priceLevel, _Digits));
       }
   }
}

This simple example opens up a world of possibilities for creating intuitive, user-friendly trading tools that bridge the gap between manual analysis and automated execution.


The Ultimate Watchdog: OnTradeTransaction()

What if your EA needs to know about everything happening in your trading account? This includes manual trades you place, trades executed by other EAs, or even actions taken by your broker. The OnTradeTransaction() event handler is the ultimate watchdog, giving you real-time insight into all trading activity. 🕵️

This handler is triggered whenever a trade transaction occurs on the account, such as placing an order, modifying a stop-loss, closing a position, or a deal being executed.

How It Works

The OnTradeTransaction() function receives three arguments:

  • trans : An MqlTradeTransaction structure containing detailed information about the transaction (type, order ticket, price, volume, etc.).

  • request : The original MqlTradeRequest that initiated this transaction.

  • result : The MqlTradeResult of executing the request.

You can inspect the trans.type field to understand what kind of transaction just happened. A common and very useful type is TRADE_TRANSACTION_DEAL_ADD , which signals that a new deal has been added to the account history (i.e., a trade was executed).

Example: Monitoring and Logging All New Trades

Here's an EA that does nothing but monitor the account. When any new trade (deal) is executed—whether by this EA, another EA, or manually—it logs the details.

//+------------------------------------------------------------------+
//|                                               TradeMonitorEA.mq5 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, EAHQ"
#property version   "1.00"

//+------------------------------------------------------------------+
//| TradeTransaction function                                        |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction &trans,
                        const MqlTradeRequest &request,
                        const MqlTradeResult &result)
{
   // Check if the transaction is for a newly executed deal
   if(trans.type == TRADE_TRANSACTION_DEAL_ADD)
   {
      // A deal was just executed on the account. Let's get the details.
      ulong deal_ticket = trans.deal;
      
      // Select the deal from history to read its properties
      if(HistoryDealSelect(deal_ticket))
      {
         long deal_type = HistoryDealGetInteger(deal_ticket, DEAL_TYPE);
         long deal_magic = HistoryDealGetInteger(deal_ticket, DEAL_MAGIC);
         double deal_volume = HistoryDealGetDouble(deal_ticket, DEAL_VOLUME);
         string deal_symbol = HistoryDealGetString(deal_ticket, DEAL_SYMBOL);
         
         // Log the information
         PrintFormat("New Deal Executed: Ticket #%d, Symbol: %s, Type: %s, Volume: %.2f, Magic: %d",
                     deal_ticket,
                     deal_symbol,
                     (deal_type == DEAL_TYPE_BUY ? "Buy" : "Sell"),
                     deal_volume,
                     deal_magic);
                     
         // --- REACTIVE LOGIC GOES HERE ---
         // For example, if deal_magic is 0 (manual trade),
         // you could attach your EA's trailing stop logic to it.
      }
   }
}

// These are not used but must exist for the EA to compile
void OnInit() {}
void OnDeinit(const int reason) {}
void OnTick() {}

This handler is incredibly powerful. You can build:

  • A Trade Manager: An EA that automatically applies stop-loss and take-profit levels to any trade opened on the account, regardless of its source.

  • An Equity Protector: An EA that monitors for new deals and closes all open positions if the account drawdown exceeds a certain threshold.

  • A Synchronization Tool: An EA that copies trades from one account to another in real-time.

By mastering these advanced event handlers, you elevate your MQL5 coding from simple automation to creating truly intelligent, efficient, and interactive trading systems. Go ahead and experiment—your trading tools will never be the same again.