Horizontal lines are fundamental tools in technical analysis, frequently used to mark support, resistance, entry, exit, or stop loss levels. While a line itself indicates a price level, a label adds crucial context, making charts more informative and strategy execution clearer.
This article delves into the creation and customization of labels attached to horizontal lines within the MetaTrader 5 platform using MQL5. We’ll explore the core object management functions and properties required to go beyond simple lines and build interactive, informative chart elements.
Introduction to Horizontal Line Labels in MQL5
Effective chart annotation is vital for both manual analysis and the visualization outputs of algorithmic systems. Horizontal lines serve as key visual markers, and their associated labels provide immediate context regarding the price level they represent or the strategic significance of that level.
Understanding how to programmatically manage these visual elements is a key skill for MQL5 developers creating indicators, scripts, or Expert Advisors.
Understanding Horizontal Lines and Their Properties
A horizontal line in MQL5 is a graphical object of type OBJ_HLINE. Like all graphical objects, it exists within a chart and has a unique name, a time anchor (though horizontal lines are typically anchored only by price), and various properties controlling its appearance and behavior.
Key properties include:
- Price: The specific price level the line is drawn at.
- Color: The color of the line.
- Style: The dash/dot style of the line (e.g., solid, dashed, dotted).
- Width: The thickness of the line.
- Visibility: On which timeframes the object is visible.
- Selectable: Whether the object can be selected by mouse.
- Snap to Price: Whether the line snaps to specific price values.
Crucially, horizontal lines, like many other objects, can have associated text labels.
The Significance of Labels for Horizontal Lines
While the horizontal line itself shows where a level is, the label tells you what that level means. A label could indicate:
- “Daily Resistance”
- “Entry Price”
- “Stop Loss Level”
- “Target Profit 1”
- The actual price value of the line (useful when zooming).
Without labels, charts populated with multiple lines can become confusing. Labels transform raw lines into understandable strategic markers, improving usability for human traders or providing clear feedback from an EA’s operation.
Basic Syntax for Creating Horizontal Lines in MQL5
The fundamental function for creating graphical objects, including horizontal lines, is ObjectCreate(). This function requires specifying the chart ID, a unique object name, the object type (OBJ_HLINE), the subwindow (0 for the main chart window), and the anchor point(s). For a horizontal line, only one anchor point is relevant: the price.
The basic syntax looks like this:
bool ObjectCreate(
long chart_id, // chart ID
string name, // object name
ENUM_OBJECT type, // object type
int window, // window index
datetime time1, // first point time
double price1, // first point price
datetime time2=0, // second point time
double price2=0, // second point price
datetime time3=0, // third point time
double price3=0 // third point price
);
For OBJ_HLINE, we only need chart_id, name, OBJ_HLINE, window (usually 0), and price1. time1, time2, time3, price2, price3 are not used for simple horizontal lines.
Following ObjectCreate(), various ObjectSet... functions are used to configure the object’s properties.
Creating Basic Horizontal Line Labels
Adding a label to a newly created horizontal line is straightforward using ObjectSetString() with the OBJPROP_TEXT property.
Using ObjectCreate() to Draw Horizontal Lines
First, create the horizontal line object. It’s good practice to check if the object already exists and delete it if necessary, or generate a unique name, especially in dynamic environments like EAs.
string line_name = "MyHorizontalLine_" + DoubleToString(price_level, Digits());
long chart_id = ChartID();
// Check if object exists and delete if necessary
if(ObjectFind(chart_id, line_name) >= 0)
{
ObjectDelete(chart_id, line_name);
}
// Create the horizontal line
if(!ObjectCreate(chart_id, line_name, OBJ_HLINE, 0, 0, price_level))
{
Print("Error creating HLINE ", line_name, ": ", GetLastError());
return;
}
ChartRedraw(chart_id);
This code snippet demonstrates the basic creation of a horizontal line at a specified price_level.
Adding Initial Labels Using OBJPROP_TEXT
Once the line object is created, you can add text using ObjectSetString():
// Set the text label
if(!ObjectSetString(chart_id, line_name, OBJPROP_TEXT, "My Label Text"))
{
Print("Error setting HLINE label ", line_name, ": ", GetLastError());
}
ChartRedraw(chart_id);
This simple step adds the specified string “My Label Text” as the label for the horizontal line. By default, the label will appear near the line, typically towards the left or right edge of the chart, depending on MetaTrader’s internal logic or anchor settings.
Setting Label Properties: Font, Size, and Color
You can customize the appearance of the label text using various object properties via ObjectSetString() and ObjectSetInteger().
- Font: Set the font face using
ObjectSetString()withOBJPROP_FONT. - Size: Set the font size using
ObjectSetInteger()withOBJPROP_FONTSIZE. - Color: Set the text color using
ObjectSetInteger()withOBJPROP_COLOR.
Example:
// Set label properties
ObjectSetString(chart_id, line_name, OBJPROP_FONT, "Arial");
ObjectSetInteger(chart_id, line_name, OBJPROP_FONTSIZE, 10);
ObjectSetInteger(chart_id, line_name, OBJPROP_COLOR, clrBlue);
ObjectSetInteger(chart_id, line_name, OBJPROP_SELECTABLE, false); // Make line non-selectable for cleaner charts
ObjectSetInteger(chart_id, line_name, OBJPROP_HIDDEN, false); // Ensure visibility
ObjectSetInteger(chart_id, line_name, OBJPROP_RAY, false); // Ensure it's a segment, not a ray
ChartRedraw(chart_id);
These properties allow you to match the label’s appearance to your chart’s theme or highlight important levels.
Customizing Horizontal Line Labels
Going beyond basic text, you can dynamically change the label content and precisely control its position and style.
Dynamic Label Text: Incorporating Real-Time Data
A powerful feature is the ability to update the label text dynamically. This is essential for displaying real-time data like current profit/loss for a position associated with an entry line, distance to stop loss, or any other metric that changes.
This involves calling ObjectSetString(chart_id, line_name, OBJPROP_TEXT, new_text) whenever the relevant data changes, typically within OnTick() or OnTimer() functions.
Example (updating P/L):
// Assuming 'entry_line_name' is the name of the HLINE at entry price
// and 'position_ticket' is the ticket of the relevant position
long ticket = position_ticket;
if(PositionSelectByTicket(ticket))
{
double current_profit = PositionGetDouble(POSITION_PROFIT);
string profit_text = StringFormat("P/L: %.2f", current_profit);
// Update the label text
ObjectSetString(0, entry_line_name, OBJPROP_TEXT, profit_text);
ChartRedraw(0);
}
This allows labels to become dashboards for individual trading levels.
Adjusting Label Position Relative to the Line
By default, MQL places the label based on internal logic. You can override this using OBJPROP_XOFFSET and OBJPROP_YOFFSET. These properties control the offset of the label’s top-left corner (or anchor point, see below) from the object’s anchoring price/time point.
OBJPROP_XOFFSET: Horizontal offset in pixels.
OBJPROP_YOFFSET: Vertical offset in pixels.
You can also influence placement using OBJPROP_ANCHOR, which determines which point of the object (or its label) is considered the anchor relative to the chart coordinates. For horizontal lines with labels, OBJPROP_ANCHOR combined with offsets allows fine-tuning where the text block sits relative to the line.
Common anchor points for text include ANCHOR_LEFT_UPPER, ANCHOR_RIGHT_LOWER, ANCHOR_CENTER, etc.
Example (positioning label):
// Set label position relative to the line
ObjectSetInteger(chart_id, line_name, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER); // Anchor text block at top-left
ObjectSetInteger(chart_id, line_name, OBJPROP_XOFFSET, 10); // 10 pixels right of the line's start (left edge)
ObjectSetInteger(chart_id, line_name, OBJPROP_YOFFSET, -15); // 15 pixels above the line
ChartRedraw(chart_id);
Experimentation is often needed to get the exact desired placement due to varying font sizes and chart scaling.
Changing Label Background and Border Styles
Labels can be given background and border properties for better visibility or emphasis.
- Background: Set
OBJPROP_BACKtotrueandOBJPROP_BGCOLORto the desired color. - Border: Set
OBJPROP_BORDER_TYPEtoBORDER_FLAT(or others) andOBJPROP_BORDER_COLOR.
Example:
// Add background and border
ObjectSetInteger(chart_id, line_name, OBJPROP_BACK, true); // Enable background
ObjectSetInteger(chart_id, line_name, OBJPROP_BGCOLOR, clrYellow); // Set background color
ObjectSetInteger(chart_id, line_name, OBJPROP_BORDER_TYPE, BORDER_FLAT); // Set border type
ObjectSetInteger(chart_id, line_name, OBJPROP_BORDER_COLOR, clrRed); // Set border color
ChartRedraw(chart_id);
These styles can significantly improve the readability of labels, especially on complex charts.
Implementing Interactive Labels: Mouse Events
While the horizontal line itself can be made selectable, you can also make the label interactive by handling chart events. By setting OBJPROP_SELECTABLE to true for the horizontal line object, you enable the possibility of detecting clicks on the line or its label area (depending on internal object hit-testing).
In your OnChartEvent() function, you can check for CHARTEVENT_CLICK or CHARTEVENT_OBJECT_CLICK and identify the object name (lparam) that was clicked.
void OnChartEvent(
const int id, // Event ID
const long lparam, // Event parameter 1
const double dparam, // Event parameter 2
const string sparam // Event parameter 3
)
{
if (id == CHARTEVENT_OBJECT_CLICK)
{
string clicked_object_name = sparam;
// Check if clicked_object_name corresponds to one of your HLINEs
if(StringFind(clicked_object_name, "MyHorizontalLine_") == 0)
{
Print("Clicked on HLINE: ", clicked_object_name);
// Implement custom logic, e.g., show properties, delete line, place order
}
}
}
This advanced technique allows labels to act as rudimentary controls or information triggers.
Advanced Label Techniques
Pushing the boundaries further, you can create more complex and informative labels.
Creating Multi-Line Labels
Using the newline character \n within the string assigned to OBJPROP_TEXT allows you to create labels spanning multiple lines.
string multi_line_text = "Line 1: Entry\nLine 2: TP Level\nLine 3: Profit: " + DoubleToString(current_profit, 2);
ObjectSetString(chart_id, line_name, OBJPROP_TEXT, multi_line_text);
ChartRedraw(chart_id);
This is particularly useful for condensing several pieces of information into a single label.
Using Special Characters and Symbols in Labels
MQL5 supports Unicode strings, allowing you to include a wide range of special characters and symbols in your labels. You can use Wingdings, Webdings, or other font symbols if the font is available and specified using OBJPROP_FONT.
For example, using code points directly (be mindful of font support):
string symbol_text = "\u2714 Success Level!"; // Unicode checkmark
ObjectSetString(chart_id, line_name, OBJPROP_TEXT, symbol_text);
ChartRedraw(chart_id);
Or using character codes from symbol fonts:
ObjectSetString(chart_id, line_name, OBJPROP_FONT, "Wingdings");
ObjectSetString(chart_id, line_name, OBJPROP_TEXT, "J"); // 'J' in Wingdings is a checkmark
ChartRedraw(chart_id);
This adds visual flair and can convey information concisely.
Implementing Data Binding to Update Labels Automatically
While OnTick() or OnTimer() work, for complex systems with many dynamic labels, consider a dedicated class or structure to manage label objects and their associated data. This allows for cleaner code and more efficient updates.
A simple approach is to store label information (object name, data source, update frequency) in an array or list and iterate through it in a timer function (OnTimer) to update only what’s necessary.
For instance, creating a struct:
struct DynamicLabelInfo
{
string object_name;
long associated_ticket; // e.g., position ticket
// Add other relevant data fields
};
DynamicLabelInfo label_list[];
// ... add entries to label_list when creating objects ...
void OnTimer()
{
for(int i = 0; i < ArraySize(label_list); i++)
{
string name = label_list[i].object_name;
long ticket = label_list[i].associated_ticket;
if(ObjectFind(0, name) >= 0 && PositionSelectByTicket(ticket))
{
double profit = PositionGetDouble(POSITION_PROFIT);
string new_text = StringFormat("Pos #%d P/L: %.2f", ticket, profit);
if(ObjectGetString(0, name, OBJPROP_TEXT) != new_text)
{
ObjectSetString(0, name, OBJPROP_TEXT, new_text);
ChartRedraw(0);
}
}
}
}
This pattern scales better than scattered update calls.
Practical Examples and Use Cases
Let’s look at common scenarios where labeled horizontal lines are invaluable.
Example 1: Displaying Profit/Loss Levels with Labels
When an EA opens a position, it can draw horizontal lines at the entry price, stop loss, and take profit levels. Labels can display:
- Entry Line: The entry price and maybe position size.
- Stop Loss Line: The potential loss amount if hit (
PositionGetDouble(POSITION_STOPLOSS) - PositionGetDouble(POSITION_PRICEOPEN)* lotsize * point value). - Take Profit Line: The potential profit amount if hit (
PositionGetDouble(POSITION_TAKeprofit) - PositionGetDouble(POSITION_PRICEOPEN)* lotsize * point value).
These labels can be updated dynamically to show the current unrealized P/L relative to these levels as the market moves.
Example 2: Highlighting Key Price Levels with Custom Labels
An indicator analyzing historical data could identify significant support/resistance zones. It could draw horizontal lines at these levels and label them appropriately, e.g., “Major Support Zone”, “Weekly High”, “Fibonacci Retracement 61.8%”. The labels provide instant interpretation of the lines drawn by the indicator.
Troubleshooting Common Label Issues
- Label Not Appearing:
- Ensure
ObjectCreate()returnedtrueand checkGetLastError(). The name might be invalid or already exist (if not checked/deleted). - Ensure
ObjectSetString(..., OBJPROP_TEXT, ...)returnedtrue. - Call
ChartRedraw()after creating/modifying the object. - Check object visibility properties (
OBJPROP_HIDDEN,OBJPROP_TIMEFRAMES).
- Ensure
- Label Not Updating:
- Verify the update logic is placed in a function that runs frequently (like
OnTickorOnTimer). - Ensure
ObjectFind()successfully finds the object by name before attempting to update its properties. - Call
ChartRedraw()after updating the text.
- Verify the update logic is placed in a function that runs frequently (like
- Label Position is Off:
- Adjust
OBJPROP_XOFFSETandOBJPROP_YOFFSET. - Experiment with different
OBJPROP_ANCHORvalues.ANCHOR_LEFTorANCHOR_RIGHTwith offsets are common for labels along horizontal lines.
- Adjust
- Text Cut Off: The label area might be too small for the text. This is often managed by MetaTrader automatically, but excessive length or large fonts can sometimes cause issues. Consider using multi-line text or reducing font size.
- Performance Issues with Many Dynamic Labels: Frequent
ChartRedraw()calls with a large number of objects can impact performance. Only callChartRedraw()after all necessary object modifications in a single event handler run. Consider the data binding approach described earlier to manage updates efficiently.
Mastering the creation and customization of horizontal line labels significantly enhances the usability and informational value of your MQL5 programs, providing clear, dynamic insights directly on the price chart.