Beyond OnTick(): Mastering the Full MQL5 Event Model – Trading Systems – 18 September 2025

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.




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


datetime lastBarTime = 0;




int OnInit()
{
   
   EventSetTimer(60);
   Print("Timer EA Initialized. Checking for new H1 bar every minute.");
   
   return(INIT_SUCCEEDED);
}



void OnDeinit(const int reason)
{
   
   EventKillTimer();
   Print("Timer EA Removed. Timer stopped.");
}



void OnTimer()
{
   
   datetime newBarTime = (datetime)SeriesInfoInteger(_Symbol, PERIOD_H1, SERIES_LASTBAR_DATE);
   
   
   if(newBarTime > lastBarTime)
   {
      
      lastBarTime = newBarTime;
      
      Print("New H1 Bar Detected at: ", TimeToString(newBarTime));
      
      
      
   }
}



void OnTick()
{
  
}

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.




#property copyright "Copyright 2025, EAHQ"
#property indicator_chart_window




void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{
   
   if(id == CHARTEVENT_OBJECT_CREATE)
   {
      
      Print("Object Created: ", sparam);
      
      
      if(ObjectType(sparam) == OBJ_HLINE)
      {
         
         double priceLevel = ObjectGetDouble(0, sparam, OBJPROP_PRICE, 0);
         Print("Take-Profit level set by HLine '", sparam, "' at price: ", DoubleToString(priceLevel, _Digits));
         
         
      }
   }
   
   
   if(id == CHARTEVENT_OBJECT_DRAG)
   {
       
       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.




#property copyright "Copyright 2025, EAHQ"
#property version   "1.00"




void OnTradeTransaction(const MqlTradeTransaction &trans,
                        const MqlTradeRequest &request,
                        const MqlTradeResult &result)
{
   
   if(trans.type == TRADE_TRANSACTION_DEAL_ADD)
   {
      
      ulong deal_ticket = trans.deal;
      
      
      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);
         
         
         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);
                     
         
         
         
      }
   }
}


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.

Leave a Comment