MQL5 ObjectCreate & OnChartEvent Guide: UI Buttons & Event Handling
Learn how to create chart objects in MQL5 using ObjectCreate and ObjectFind, handle click events with OnChartEvent, and understand why scripts cannot process chart events.
What this MQL5 guide covers
ObjectCreate creates visual and interactive elements including OBJ_BUTTON, OBJ_LABEL, and OBJ_RECTANGLE_LABEL on MetaTrader 5 charts.
Always verify existence with ObjectFind(chart_id, name) before calling ObjectCreate to prevent duplicate creation errors and property resets.
The OnChartEvent handler captures user interactions such as CHARTEVENT_OBJECT_CLICK and CHARTEVENT_KEYDOWN in Expert Advisors and indicators.
OnChartEvent does not function in MQL5 scripts because scripts run synchronously inside OnStart and exit without an event message pump.
ObjectCreate Syntax, Object Types and ObjectFind
The ObjectCreate function instantiates graphical objects on a specified chart window. Parameters define the chart identifier, unique object name, object type enum, target subwindow, and anchor time/price coordinates.
Calling ObjectFind(chart_id, name) checks whether an object already exists. If the object exists, ObjectFind returns the subwindow number (0 for main chart); if not found, it returns a negative value. Always test ObjectFind < 0 before creating objects to avoid overwriting user states.
| Object Type | Coordinate Mode | Common Trading Use Case |
|---|---|---|
| OBJ_BUTTON | Pixels (X, Y from corner) | One-click trading panels, buy/sell buttons |
| OBJ_LABEL | Pixels (X, Y from corner) | Static text overlays, account info readouts |
| OBJ_RECTANGLE_LABEL | Pixels (X, Y, width, height) | Background container panels for custom GUIs |
| OBJ_HLINE | Price (single price level) | Support, resistance, breakeven thresholds |
| OBJ_TREND | Time & Price (two points) | Trend lines, channel boundaries, breakout alerts |
Handling User Interaction with OnChartEvent
Expert Advisors and custom indicators process chart events through the OnChartEvent callback. When a trader clicks a chart button, MetaTrader 5 triggers this function with event ID CHARTEVENT_OBJECT_CLICK.
The sparam argument contains the string name of the clicked object. Match sparam against your known button names to execute corresponding trading actions, then reset button click states with ObjectSetInteger.
| Event Constant | Trigger Description | Key Parameters |
|---|---|---|
| CHARTEVENT_OBJECT_CLICK | Mouse click on a chart graphical object | sparam = object name |
| CHARTEVENT_CLICK | Mouse click on an empty chart area | dparam = price, lparam = time |
| CHARTEVENT_KEYDOWN | Keyboard key pressed while chart has focus | lparam = key code |
| CHARTEVENT_CHART_CHANGE | Chart scale, size, or timeframe altered | lparam, dparam, sparam |
Why OnChartEvent Does Not Work for Scripts
A common source of confusion in MQL5 is placing an OnChartEvent handler inside a script program and finding that clicks are completely ignored.
Scripts operate on a synchronous execution model. When attached, a script runs its OnStart() function from start to finish on its own thread, then terminates and unloads. Scripts do not possess an active event loop and cannot receive asynchronous chart events.
- Scripts execute sequentially and terminate; they cannot listen for clicks.
- If your trading tool requires interactive buttons, write it as an Expert Advisor.
- If visual indicators must process clicks, place OnChartEvent inside an indicator.
Redraws and Deinitialization Cleanup
After creating, moving, or changing properties on chart objects, call ChartRedraw(chart_id) to force MetaTrader 5 to update the visual canvas immediately rather than waiting for the next incoming price tick.
In OnDeinit(), iterate through your program objects using ObjectDelete() or call ObjectsDeleteAll(chart_id, prefix) to leave the user chart clean when removing the EA.
Implementation details & mechanics
Core implementation pattern
In OnInit(), check ObjectFind(0, "btn_Buy") < 0. If not found, call ObjectCreate(0, "btn_Buy", OBJ_BUTTON, 0, 0, 0) and set anchor corner, pixel offsets, width, height, background color, and caption text. Call ChartRedraw(). In OnChartEvent(), inspect id == CHARTEVENT_OBJECT_CLICK and match sparam == "btn_Buy". Reset button state via ObjectSetInteger(0, "btn_Buy", OBJPROP_STATE, false) and execute a buy order.
Error handling & safety rules
In OnChartEvent(), handle clicks on "btn_CloseAll" by iterating open positions and closing matching tickets via CTrade. In OnDeinit(const int reason), invoke ObjectsDeleteAll(0, "btn_") to remove all custom panel buttons and labels from the chart.
MQL5 Code Implementation Code
//+------------------------------------------------------------------+
//| ChartObjectsEventsGuideEA.mq5|
//| Copyright 2026, Pineify Guides Series |
//| https://pineify.app |
//+------------------------------------------------------------------+
#property copyright "Pineify Guides Series"
#property link "https://pineify.app/mql5/chart-objects-events-guide"
#property version "1.00"
#property strict
#include <Trade\Trade.mqh>
#define PREFIX "PineifyUI_"
CTrade g_trade;
//+------------------------------------------------------------------+
//| Helper: Create a chart button with pixel positioning |
//+------------------------------------------------------------------+
bool CreateButton(const string name, const string text,
const int x, const int y,
const int width, const int height,
const color bgCol, const color textCol)
{
// Check if object already exists to avoid redundant creation
if(ObjectFind(0, name) >= 0)
return true;
if(!ObjectCreate(0, name, OBJ_BUTTON, 0, 0, 0))
{
PrintFormat("Failed to create button %s! Error: %d", name, GetLastError());
return false;
}
// Anchor to top-left corner using pixel coordinates
ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
ObjectSetInteger(0, name, OBJPROP_XSIZE, width);
ObjectSetInteger(0, name, OBJPROP_YSIZE, height);
// Styling
ObjectSetString(0, name, OBJPROP_TEXT, text);
ObjectSetString(0, name, OBJPROP_FONT, "Segoe UI");
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 10);
ObjectSetInteger(0, name, OBJPROP_COLOR, textCol);
ObjectSetInteger(0, name, OBJPROP_BGCOLOR, bgCol);
ObjectSetInteger(0, name, OBJPROP_BORDER_COLOR, clrDarkGray);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, name, OBJPROP_STATE, false);
return true;
}
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
g_trade.SetExpertMagicNumber(998877);
// Create UI buttons
CreateButton(PREFIX + "BuyBtn", "BUY 0.10", 20, 40, 110, 36, clrDodgerBlue, clrWhite);
CreateButton(PREFIX + "CloseBtn", "CLOSE ALL", 140, 40, 110, 36, clrCrimson, clrWhite);
// Refresh chart to display objects immediately
ChartRedraw(0);
Print("Chart objects UI created successfully.");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Delete all graphical objects matching program prefix
int deleted = ObjectsDeleteAll(0, PREFIX);
ChartRedraw(0);
PrintFormat("Removed %d UI chart objects. Reason: %d", deleted, reason);
}
//+------------------------------------------------------------------+
//| Chart event handler |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
// Filter for object click events
if(id == CHARTEVENT_OBJECT_CLICK)
{
// Handle BUY button click
if(sparam == PREFIX + "BuyBtn")
{
Print("User clicked BUY button.");
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
g_trade.Buy(0.10, _Symbol, ask, 0, 0, "Pineify Button Click");
// Reset button pressed state
ObjectSetInteger(0, sparam, OBJPROP_STATE, false);
ChartRedraw(0);
}
// Handle CLOSE ALL button click
else if(sparam == PREFIX + "CloseBtn")
{
Print("User clicked CLOSE ALL button.");
// Close all positions matching this symbol
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionGetString(POSITION_SYMBOL) == _Symbol)
g_trade.PositionClose(ticket);
}
// Reset button pressed state
ObjectSetInteger(0, sparam, OBJPROP_STATE, false);
ChartRedraw(0);
}
}
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Standard EA calculations can proceed concurrently with UI events
}
Copy this code into MetaEditor, save it in your MQL5 folder (Experts, Scripts, or Include), and compile with F7.
Generate custom MQL5 code with Pineify AI
Describe the trade execution, indicator calculations, risk management, or data processing logic you need. Pineify produces clean, typed MQL5 source code ready to compile in MetaEditor.
Frequently Asked Questions
Related MQL5 pages
Technical references
Risk and testing note
Past performance is not indicative of future results. Backtest statistics are based on historical data and do not guarantee future profits. Trading involves significant risk of loss. This content is for educational purposes only and does not constitute financial advice.