Problem Description
Solution
To place a text label inside a specific indicator window (subwindow) in MetaTrader 4, you need to determine the Window Index of that indicator.
There are two primary ways to achieve this:
- Inside a Custom Indicator: The indicator identifies its own window index automatically.
- From an EA or Script: The program searches for an existing indicator (like RSI) by its "Short Name" to find the window index.
Below are the implementations for both scenarios.
Method 1: Creating a Custom Indicator with a Label
This is the most reliable method. We create a custom indicator that calculates RSI and places a label directly into its own subwindow.
//+------------------------------------------------------------------+
//| RSI_With_Label.mq4 |
//| Copyright 2023, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Quantitative AI"
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
//--- Indicator Settings
#property indicator_separate_window // Essential: Draws in a subwindow
#property indicator_buffers 1
#property indicator_color1 clrDodgerBlue
#property indicator_width1 2
//--- Inputs
input int InpRsiPeriod = 14; // RSI Period
input color InpLabelColor = clrWhite; // Label Color
input int InpFontSize = 10; // Font Size
input string InpFont = "Arial"; // Font Name
input int InpCorner = CORNER_LEFT_UPPER; // Corner to anchor
//--- Buffers
double RsiBuffer[];
//--- Global Variables for Object Management
string labelName = "RSI_Info_Label";
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Indicator Buffer Mapping
SetIndexBuffer(0, RsiBuffer);
SetIndexStyle(0, DRAW_LINE);
//--- Set Short Name (Important for WindowFind in other scripts)
string shortName = "RSI_Label(" + IntegerToString(InpRsiPeriod) + ")";
IndicatorShortName(shortName);
//--- Append unique ID to label name to avoid conflicts
labelName += "_" + shortName;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- Clean up: Remove the object when indicator is removed
if(ObjectFind(0, labelName) >= 0)
{
ObjectDelete(0, labelName);
}
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
//--- 1. Calculate RSI
int limit = rates_total - prev_calculated;
if(prev_calculated > 0) limit++;
for(int i = 0; i < limit; i++)
{
RsiBuffer[i] = iRSI(NULL, 0, InpRsiPeriod, PRICE_CLOSE, i);
}
//--- 2. Manage the Text Label
// We do this in OnCalculate to ensure the window exists
UpdateLabel(RsiBuffer[0]);
return(rates_total);
}
//+------------------------------------------------------------------+
//| Helper function to create/update the label |
//+------------------------------------------------------------------+
void UpdateLabel(double currentRsiValue)
{
//--- Find the window index where this indicator is running
// ChartWindowFind() with no arguments returns the window of the calling indicator
int windowIndex = ChartWindowFind();
//--- Check if object exists, if not, create it
if(ObjectFind(0, labelName) < 0)
{
// ObjectCreate arguments: ChartID, Name, Type, SubWindow, Time1, Price1
// Note: OBJ_LABEL ignores Time/Price, but we must pass the windowIndex
if(!ObjectCreate(0, labelName, OBJ_LABEL, windowIndex, 0, 0))
{
Print("Failed to create label. Error: ", GetLastError());
return;
}
// Initial properties
ObjectSetInteger(0, labelName, OBJPROP_CORNER, InpCorner);
ObjectSetInteger(0, labelName, OBJPROP_XDISTANCE, 10); // 10 pixels from edge
ObjectSetInteger(0, labelName, OBJPROP_YDISTANCE, 10); // 10 pixels from edge
ObjectSetString(0, labelName, OBJPROP_FONT, InpFont);
ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, InpFontSize);
ObjectSetInteger(0, labelName, OBJPROP_COLOR, InpLabelColor);
ObjectSetInteger(0, labelName, OBJPROP_BACK, false);
ObjectSetInteger(0, labelName, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, labelName, OBJPROP_HIDDEN, true); // Hide from object list
}
//--- Update the text
string text = "RSI(" + IntegerToString(InpRsiPeriod) + "): " + DoubleToString(currentRsiValue, 2);
ObjectSetString(0, labelName, OBJPROP_TEXT, text);
//--- Optional: Change color based on levels
if(currentRsiValue > 70) ObjectSetInteger(0, labelName, OBJPROP_COLOR, clrRed);
else if(currentRsiValue < 30) ObjectSetInteger(0, labelName, OBJPROP_COLOR, clrLime);
else ObjectSetInteger(0, labelName, OBJPROP_COLOR, InpLabelColor);
//--- Force redraw to show changes immediately
ChartRedraw(0);
}
Method 2: Using a Script to Label an Existing Indicator
Use this script if you have a standard RSI already on the chart and want to inject a label into its window without replacing the indicator.
Note: This requires the RSI "Short Name" to match exactly. Standard MT4 RSI is usually named RSI(14) (if period is 14).
//+------------------------------------------------------------------+
//| Label_Existing_Window.mq4 |
//| Copyright 2023, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Quantitative AI"
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
#property show_inputs // Show inputs window on run
//--- Inputs
input string InpTargetIndicator = "RSI(14)"; // Exact short name of the indicator
input string InpLabelText = "RSI Window Label";
input color InpColor = clrYellow;
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
//--- 1. Find the window index of the target indicator
// ChartWindowFind(0, Name) searches the main chart (0) for the indicator short name
int windowIndex = ChartWindowFind(0, InpTargetIndicator);
//--- 2. Validate the window
if(windowIndex == -1)
{
Alert("Indicator '", InpTargetIndicator, "' not found on chart!");
Print("Available windows: ", (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL));
return;
}
if(windowIndex == 0)
{
Alert("Found on Main Chart. This script is intended for subwindows.");
// We continue anyway, but warn the user
}
//--- 3. Create the Label Object
string objName = "External_Label_" + InpTargetIndicator;
// Delete if exists to reset
if(ObjectFind(0, objName) >= 0) ObjectDelete(0, objName);
// Create OBJ_LABEL in the specific windowIndex
if(ObjectCreate(0, objName, OBJ_LABEL, windowIndex, 0, 0))
{
//--- Set Properties
ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, 20);
ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, 20);
ObjectSetString(0, objName, OBJPROP_TEXT, InpLabelText);
ObjectSetString(0, objName, OBJPROP_FONT, "Arial Bold");
ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, 12);
ObjectSetInteger(0, objName, OBJPROP_COLOR, InpColor);
ChartRedraw(0);
Print("Label placed successfully in Window #", windowIndex);
}
else
{
Print("Failed to create object. Error: ", GetLastError());
}
}
Key Technical Details
#property indicator_separate_window: This directive in Method 1 tells MT4 to create a new subwindow rather than overlaying on the price chart.ChartWindowFind():- When called without parameters inside an indicator, it returns the index of the window that indicator occupies.
- When called with parameters (
ChartWindowFind(0, "Name")), it searches for a specific indicator name.
ObjectCreateParameter: The 4th parameter inObjectCreateis thesub_windowindex.0= Main Chart.1and higher = Indicator subwindows.
OBJ_LABELvsOBJ_TEXT:OBJ_LABEL: Uses X/Y pixel coordinates relative to a corner. It stays in place when you scroll the chart. This is usually preferred for status labels.OBJ_TEXT: Uses Time/Price coordinates. It moves when you scroll the chart.
Q&A
Q: How do I find the exact "Short Name" for Method 2?
A: Open the "Data Window" (Ctrl+D) in MT4. Hover your mouse over the indicator line. The name displayed at the top of the value list (e.g., RSI(14)) is the short name. Alternatively, right-click the chart, select "Indicators List", select the indicator, and click "Edit". The name is usually derived from the inputs.
Q: Can I place a label in the RSI window from an Expert Advisor (EA)?
A: Yes. Use the logic from Method 2 inside the EA's OnInit or OnTick. However, the RSI indicator must already be attached to the chart manually or via a template for the EA to find it. EAs run in Window 0, so they must explicitly look up the subwindow index.
Q: Why does my label disappear when I change timeframes?
A: MQL4 objects persist on the chart ID. However, if your code logic deletes objects in OnDeinit (which runs on timeframe change) and fails to recreate them properly in OnInit or OnCalculate, they will vanish. Ensure your creation logic checks if(ObjectFind(...) < 0) inside OnCalculate or OnTick to ensure persistence.