(Update) High Open Low Indicator (Automatic Fibonacci) MT5
Wednesday, August 12, 2026In this update I want to explain the newest version of my MQL5 indicator called HOL Indicator v3. This version is not only a visual Fibonacci tool, but also a complete intraday trading assistant for MetaTrader 5. The main purpose of this indicator is to display the most important price levels of the current trading day and the previous trading day, while also providing additional information such as average daily range, spread, time remaining in the current candle, floating profit or loss, and optional alert signals. The indicator works directly on the chart window and uses chart objects instead of traditional indicator buffers, which makes the display clean and lightweight. The indicator is designed especially for traders who focus on price action, market structure, and intraday reversals.
The core concept of the HOL indicator is based on three important levels: today’s open price, today’s highest price, and today’s lowest price. In this version I create a Fibonacci object from the lowest price to the highest price of the current H1 trading session. The open price is then calculated as a Fibonacci level between those two extremes. This gives a very clear visual representation of where the market opened relative to the current daily range. I believe this information is very useful because many institutional and intraday traders pay close attention to the opening price of the day. The same logic is also applied to yesterday’s data, so traders can compare today’s movement with the previous day’s structure.
One important improvement in version 3 is the synchronization and safety check before any calculation is executed. The indicator first waits until H1 data is fully synchronized and ensures that enough bars are available. This prevents many common errors that can happen when MetaTrader is just opened or when historical data is not fully loaded. I also add protection against zero range calculations. If the range between the highest and lowest price is too small, the indicator will skip the Fibonacci calculation instead of producing invalid values or division-by-zero errors.
The object management is also improved. All objects created by the indicator contain a unique name with the prefix “holo”. During deinitialization the indicator automatically scans all chart objects and deletes only the objects that belong to this indicator. This is very useful when changing timeframe, removing the indicator, or updating the code, because it keeps the chart clean and avoids leaving old Fibonacci objects behind.
Another feature that I add in this update is the secondary open level analysis. The indicator can scan a lower timeframe, for example M5, and find the highest open price and the lowest open price of the current day. These levels are drawn as dashed horizontal trend lines. In my trading approach these secondary open levels can act as intraday reaction zones. When price moves above the highest M5 open but still remains below the daily high, the indicator can generate a potential sell alert. When price moves below the lowest M5 open but remains above the daily low, the indicator can generate a potential buy alert. The alert system can be enabled or disabled through the input settings.
I also add a detailed information panel using the Comment function. The panel displays GMT time, server time, and local time. This helps traders who work with multiple sessions or compare broker time with real market sessions. The panel also shows the Average Daily Range based on the last nine completed trading days, today’s current range in both pips and points, and the percentage of today’s range relative to the ADR. This gives a quick measurement of whether the market is still quiet or has already expanded significantly.
In addition, the panel shows the current Ask price, the current spread, and the remaining time before the current candle closes. For active traders this information is very practical because it is available directly on the chart without opening other windows. I also include a floating PnL calculation for all open positions of the current symbol. The indicator sums the profit or loss in points, making it easy to monitor exposure while analyzing the chart.
The CreateTodayH1Line function is one of the most important parts of the code. It identifies the first H1 candle of the current broker day, gets its open price, and then scans all H1 candles from that point until the current time to find the highest and lowest prices. After that it creates a Fibonacci object and assigns custom colors for the highest, lowest, and open levels. The highest level is shown in maroon, the lowest level in dark green, and the open level in purple. I intentionally use different colors so the structure can be recognized instantly even when many objects are present on the chart.
The CreateYesterdayH1Line function is simpler because it uses completed D1 data. Since yesterday is already finished, the indicator can directly obtain the high, low, open, and close values from the daily timeframe. The Fibonacci object is then projected from the beginning of yesterday to the beginning of today. This creates a stable reference that does not repaint during the current session.
I also include utility functions for calculating Average Daily Range and converting it into points. These functions ignore the current unfinished day and use only completed daily candles, which produces more reliable statistics. Another helper function calculates the total floating PnL of all open positions on the current symbol. Although this is not required for drawing Fibonacci levels, I add it because it makes the indicator more informative during live trading.
Overall, HOL Indicator v3 is a significant upgrade compared with my previous versions. The indicator combines daily structure, intraday structure, volatility measurement, session information, object management, and optional signal alerts in a single lightweight tool. My goal is not to create a fully automated trading system, but to build a visual decision-support indicator that helps traders read the market more clearly. I use this indicator mainly for intraday trading on pairs such as XAUUSD and major forex symbols, especially when I want to compare the current price with today’s open, today’s range, and yesterday’s structure.
Source Code
//+------------------------------------------------------------------+
//| HOL v3.mq5 |
//| Fibo from Highest Close (0%) to Lowest Close (100%) |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_plots 0 // No plots, only objects
//+------------------------------------------------------------------+
//| Custom Settings |
//+------------------------------------------------------------------+
input bool ShowAlert = true; // Turn on Alert
input bool ShowToday = true; // Today Line
input bool ShowYesterday = true; // Yesterday Line
input color ColorOpen = clrPurple;
input color ColorHighest = clrMaroon;
input color ColorLowest = clrDarkGreen;
input bool SecondaryLine = true;
input ENUM_TIMEFRAMES SecondLineTF = PERIOD_M5;
string fiboName = "holo";
int OnInit() {
return (INIT_SUCCEEDED);
}
void OnDeinit(const int reason) {
int total = ObjectsTotal(0, 0, -1);
for (int i = total - 1; i >= 0; i--) {
string name = ObjectName(0, i, 0, -1);
// delete only Fibonacci objects with "holo" in the name
if (StringFind(name, fiboName) >= 0) {
ObjectDelete(0, name);
}
}
Comment("");
}
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[]) {
//--- Wait until synchronize
if (!SeriesInfoInteger(_Symbol, PERIOD_H1, SERIES_SYNCHRONIZED))
return rates_total;
//--- Wait until data enough
if (Bars(_Symbol, PERIOD_H1) < 10)
return rates_total;
if (ShowToday) {
CreateTodayH1Line(fiboName);
}
if (ShowYesterday) {
CreateYesterdayH1Line(fiboName);
}
double todayRange = iHigh(_Symbol, PERIOD_D1, 0) - iLow(_Symbol, PERIOD_D1, 0);
if (MathAbs(todayRange) <= _Point) {
Print("Range not ready yet, skip fibo calculation");
return rates_total;
}
double averageRange = GetAverageDailyRange(9);
double averageRangePoint = GetAverageDailyRangePoints(9);
double todayRangePercent = (todayRange / averageRange) * 100.00;
string theComment = "--- HOL indicator v3 ---\n";
theComment = theComment + "\nGMT Date Time = " + TimeToString(TimeGMT(), TIME_DATE | TIME_SECONDS);
theComment = theComment + "\nServer Date Time = " + TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS);
theComment = theComment + "\nLocal Date Time = " + TimeToString(TimeLocal(), TIME_DATE | TIME_SECONDS);
theComment = theComment + "\n";
theComment = theComment + "\nADR (9 days) = " + DoubleToString(averageRange, 0) + " pips / " + DoubleToString(averageRangePoint, 0) + " point";
theComment = theComment + "\nToday Range = " + DoubleToString(todayRange, 0) + " pips / " + DoubleToString(todayRange / _Point, 0) + " point / " + DoubleToString(todayRangePercent, 2) + "%";
theComment = theComment + "\n";
theComment = theComment + "\nPrice = " + DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_ASK), Digits());
theComment = theComment + "\nSpread = " + IntegerToString(SymbolInfoInteger(_Symbol, SYMBOL_SPREAD));
theComment = theComment + "\nBar End = " + TimeToString((iTime(_Symbol, _Period, 0) + PeriodSeconds(_Period)) - TimeCurrent(), TIME_SECONDS);
theComment = theComment + "\nPnL = " + DoubleToString(GetTotalPnLPips(), 0) + " point";
theComment = theComment + "\n";
Comment(theComment);
return (rates_total);
}
//+------------------------------------------------------------------+
//| Create Fibonacci H1 Today |
//+------------------------------------------------------------------+
void CreateTodayH1Line(string name = "HOLO") {
//--- fibo name
int dayShift = 0;
name = name + "H1" + IntegerToString(dayShift);
//--- start of today
datetime today = StringToTime(TimeToString(TimeCurrent(), TIME_DATE));
//--- shift of first H1 candle today (00:00)
int firstShift = iBarShift(_Symbol, PERIOD_H1, today, false);
if (iTime(_Symbol, PERIOD_H1, firstShift) < today) {
firstShift--;
}
if (firstShift < 0) {
Print("Cannot find today's H1 candle");
return;
}
//--- open of first H1 candle today
double open = iOpen(_Symbol, PERIOD_H1, firstShift);
//--- find highest and lowest H1 of today
double highest = -DBL_MAX;
double lowest = DBL_MAX;
// scan from first H1 candle today down to current H1 candle
for (int i = firstShift; i >= 0; i--) {
datetime t = iTime(_Symbol, PERIOD_H1, i);
if (t < today)
break;
double h = iHigh(_Symbol, PERIOD_H1, i);
double l = iLow(_Symbol, PERIOD_H1, i);
if (h > highest) highest = h;
if (l < lowest) lowest = l;
}
//--- remove old fibo if exists
ObjectDelete(0, name);
//--- create Fibonacci object
datetime time1 = iTime(_Symbol, PERIOD_H1, firstShift); // 00:00 H1
datetime time2 = TimeCurrent();
if (!ObjectCreate(0, name, OBJ_FIBO, 0, time1, lowest, time2, highest)) {
Print("Failed to create Fibonacci");
return;
}
//--- style
ObjectSetInteger(0, name, OBJPROP_COLOR, clrNONE);
ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, true);
ObjectSetInteger(0, name, OBJPROP_LEVELCOLOR, 0, ColorHighest);
ObjectSetInteger(0, name, OBJPROP_LEVELCOLOR, 1, ColorLowest);
ObjectSetInteger(0, name, OBJPROP_LEVELCOLOR, 2, ColorOpen);
ObjectSetInteger(0, name, OBJPROP_BACK, true);
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
//--- levels: Open, High, Low
ObjectSetInteger(0, name, OBJPROP_LEVELS, 3);
// Level 0 = Highest
ObjectSetDouble(0, name, OBJPROP_LEVELVALUE, 0, 0.0);
ObjectSetString(0, name, OBJPROP_LEVELTEXT, 0, IntegerToString(dayShift) + " H1 highest" + " = %$ ");
// Level 1 = Lowest
ObjectSetDouble(0, name, OBJPROP_LEVELVALUE, 1, 1.0);
ObjectSetString(0, name, OBJPROP_LEVELTEXT, 1, IntegerToString(dayShift) + " H1 lowest" + " = %$ ");
// Level 2 = Open
double rangeHighestLowest = highest - lowest;
if (MathAbs(rangeHighestLowest) <= _Point) {
Print("Range not ready yet, skip fibo calculation");
return;
}
double openLevel = ((highest - open) / rangeHighestLowest) * 1.0;
ObjectSetDouble(0, name, OBJPROP_LEVELVALUE, 2, openLevel);
ObjectSetString(0, name, OBJPROP_LEVELTEXT, 2, IntegerToString(dayShift) + " H1 open" + " = %$ ");
//Print(_Symbol, " Today Open=", open, " Highest=", highest, " Lowest=", lowest);
if (SecondaryLine) {
string SecondaryHighName = name + "HighSecondary";
string SecondaryLowName = name + "LowSecondary";
// Delete old objects
ObjectDelete(0, SecondaryHighName);
ObjectDelete(0, SecondaryLowName);
// Start of today (broker time)
datetime today = StringToTime(TimeToString(TimeCurrent(), TIME_DATE));
// Find first M5 bar of today
int firstShift = iBarShift(_Symbol, SecondLineTF, today, false);
if (firstShift < 0) {
Print("Cannot find first M5 bar of today");
return;
}
double SecondaryHighestOpen = -DBL_MAX;
double SecondaryLowestOpen = DBL_MAX;
int SecondaryHighestShift = -1;
int SecondaryLowestShift = -1;
// Scan all M5 bars of today
for (int i = firstShift; i >= 0; i--) {
double op = iOpen(_Symbol, SecondLineTF, i);
if (op > SecondaryHighestOpen) {
SecondaryHighestOpen = op;
SecondaryHighestShift = i;
}
if (op < SecondaryLowestOpen) {
SecondaryLowestOpen = op;
SecondaryLowestShift = i;
}
}
if (SecondaryHighestShift == -1 || SecondaryLowestShift == -1)
return;
datetime time1 = iTime(_Symbol, SecondLineTF, firstShift);
datetime time2 = TimeCurrent();
//--- Create highest open trend line
ObjectCreate(0, SecondaryHighName, OBJ_TREND, 0, time1, SecondaryHighestOpen, time2, SecondaryHighestOpen);
ObjectSetInteger(0, SecondaryHighName, OBJPROP_COLOR, ColorHighest);
ObjectSetInteger(0, SecondaryHighName, OBJPROP_WIDTH, 1);
ObjectSetInteger(0, SecondaryHighName, OBJPROP_RAY_RIGHT, true);
ObjectSetInteger(0, SecondaryHighName, OBJPROP_STYLE, STYLE_DASHDOT);
//--- Create lowest open trend line
ObjectCreate(0, SecondaryLowName, OBJ_TREND, 0, time1, SecondaryLowestOpen, time2, SecondaryLowestOpen);
ObjectSetInteger(0, SecondaryLowName, OBJPROP_COLOR, ColorLowest);
ObjectSetInteger(0, SecondaryLowName, OBJPROP_WIDTH, 1);
ObjectSetInteger(0, SecondaryLowName, OBJPROP_RAY_RIGHT, true);
ObjectSetInteger(0, SecondaryLowName, OBJPROP_STYLE, STYLE_DASHDOT);
if (ShowAlert) {
MqlTick last_tick;
SymbolInfoTick(_Symbol, last_tick);
double currentBid = last_tick.bid;
if (currentBid > SecondaryHighestOpen && currentBid < highest) {
Alert("Alhamdulillah, " + _Symbol + " SELL signal");
}
double currentAsk = last_tick.bid;
if (currentAsk < SecondaryLowestOpen && currentAsk > lowest) {
Alert("Alhamdulillah, " + _Symbol + " BUY signal, Bismillahirrohmanirrohim!!");
}
}
}
}
//+------------------------------------------------------------------+
//| Create Fibonacci H1 Yesterday |
//+------------------------------------------------------------------+
void CreateYesterdayH1Line(string name = "HOLO") {
//--- fibo name
int dayShift = 1;
name = name + "H1" + IntegerToString(dayShift);
//--- Get OHLC
double highest = iHigh(_Symbol, PERIOD_D1, dayShift);
double lowest = iLow(_Symbol, PERIOD_D1, dayShift);
double open = iOpen(_Symbol, PERIOD_D1, dayShift);
double close = iClose(_Symbol, PERIOD_D1, dayShift);
//--- remove old fibo if exists
ObjectDelete(0, name);
//--- create Fibonacci object
datetime timeStart = iTime(_Symbol, PERIOD_D1, dayShift);
datetime timeEnd = iTime(_Symbol, PERIOD_D1, dayShift - 1);
if (!ObjectCreate(0, name, OBJ_FIBO, 0, timeStart, lowest, timeEnd, highest)) {
Print("Failed to create Fibonacci");
return;
}
//--- style
ObjectSetInteger(0, name, OBJPROP_COLOR, clrNONE);
ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, true);
ObjectSetInteger(0, name, OBJPROP_RAY_LEFT, false);
ObjectSetInteger(0, name, OBJPROP_LEVELCOLOR, 0, ColorHighest);
ObjectSetInteger(0, name, OBJPROP_LEVELCOLOR, 1, ColorLowest);
ObjectSetInteger(0, name, OBJPROP_LEVELCOLOR, 2, ColorOpen);
ObjectSetInteger(0, name, OBJPROP_BACK, true);
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
//--- levels: Open, High, Low
ObjectSetInteger(0, name, OBJPROP_LEVELS, 3);
// Level 0 = Highest
ObjectSetDouble(0, name, OBJPROP_LEVELVALUE, 0, 0.0);
ObjectSetString(0, name, OBJPROP_LEVELTEXT, 0, IntegerToString(dayShift) + " H1 highest" + " = %$ ");
// Level 1 = Lowest
ObjectSetDouble(0, name, OBJPROP_LEVELVALUE, 1, 1.0);
ObjectSetString(0, name, OBJPROP_LEVELTEXT, 1, IntegerToString(dayShift) + " H1 lowest" + " = %$ ");
// Level 2 = Open
double rangeHighestLowest = highest - lowest;
if (MathAbs(rangeHighestLowest) <= _Point) {
Print("Range not ready yet, skip fibo calculation");
return;
}
double openLevel = ((highest - open) / rangeHighestLowest) * 1.0;
ObjectSetDouble(0, name, OBJPROP_LEVELVALUE, 2, openLevel);
ObjectSetString(0, name, OBJPROP_LEVELTEXT, 2, IntegerToString(dayShift) + " H1 open" + " = %$ ");
}
//+------------------------------------------------------------------+
//| Calculate total Profit/Loss of all open positions in pips |
//+------------------------------------------------------------------+
double GetTotalPnLPips() {
double total_pips = 0;
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (PositionGetString(POSITION_SYMBOL) == _Symbol) {
double open_price = PositionGetDouble(POSITION_PRICE_OPEN);
double current_price = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ?
SymbolInfoDouble(_Symbol, SYMBOL_BID) :
SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
double tick_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
// Calculate PnL in points
double pnl_points = (current_price - open_price) / tick_size;
// Convert points to pips
double pnl_pips = pnl_points * (tick_size / point);
// Add to total (for Buy, profit is positive; for Sell, profit is reversed)
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
total_pips -= pnl_pips;
else
total_pips += pnl_pips;
}
}
return total_pips;
}
//+------------------------------------------------------------------+
//| Average Daily Range (ADR) for last N completed days |
//+------------------------------------------------------------------+
double GetAverageDailyRange(int days = 10) {
double total = 0.0;
int count = 0;
// Start from yesterday (shift 1), skip current unfinished day
for (int i = 1; i <= days; i++) {
double high = iHigh(_Symbol, PERIOD_D1, i);
double low = iLow(_Symbol, PERIOD_D1, i);
if (high > 0 && low > 0) {
total += (high - low);
count++;
}
}
if (count == 0)
return 0.0;
return total / count;
}
//+------------------------------------------------------------------+
//| Average Daily Range in points |
//+------------------------------------------------------------------+
double GetAverageDailyRangePoints(int days = 10) {
return GetAverageDailyRange(days) / _Point;
}
//+------------------------------------------------------------------+
//| Create horizontal trend line at H4 |
//+------------------------------------------------------------------+
void CreateH4OpenHorizontal(string name = "H4Open", int barShift = 1) {
name = name + "H4Open";
datetime time1 = iTime(_Symbol, PERIOD_H4, barShift);
datetime time2 = TimeCurrent();
double open1 = iOpen(_Symbol, PERIOD_H4, barShift);
if (ObjectFind(0, name) >= 0)
ObjectDelete(0, name);
ObjectCreate(0, name, OBJ_TREND, 0, time1, open1, time2, open1);
ObjectSetInteger(0, name, OBJPROP_COLOR, clrOrange);
ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DASHDOT);
ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, true);
}
//+------------------------------------------------------------------+
//| Create trend lines on highest and lowest M5 OPEN of today |
//+------------------------------------------------------------------+
void CreateOpenTrendLinesToday(string name = "HighestLowestOpen") {
string highName = name + "HighSecondary";
string lowName = name + "LowSecondary";
// Delete old objects
ObjectDelete(0, highName);
ObjectDelete(0, lowName);
// Start of today (broker time)
datetime today = StringToTime(TimeToString(TimeCurrent(), TIME_DATE));
// Find first M5 bar of today
int firstShift = iBarShift(_Symbol, SecondLineTF, today, false);
if (firstShift < 0) {
Print("Cannot find first M5 bar of today");
return;
}
double highestOpen = -DBL_MAX;
double lowestOpen = DBL_MAX;
int highestShift = -1;
int lowestShift = -1;
// Scan all M5 bars of today
for (int i = firstShift; i >= 0; i--) {
double op = iOpen(_Symbol, SecondLineTF, i);
if (op > highestOpen) {
highestOpen = op;
highestShift = i;
}
if (op < lowestOpen) {
lowestOpen = op;
lowestShift = i;
}
}
if (highestShift == -1 || lowestShift == -1)
return;
datetime time1 = iTime(_Symbol, SecondLineTF, firstShift);
datetime time2 = TimeCurrent();
//--- Create highest open trend line
ObjectCreate(0, highName, OBJ_TREND, 0, time1, highestOpen, time2, highestOpen);
ObjectSetInteger(0, highName, OBJPROP_COLOR, ColorHighest);
ObjectSetInteger(0, highName, OBJPROP_WIDTH, 1);
ObjectSetInteger(0, highName, OBJPROP_RAY_RIGHT, true);
ObjectSetInteger(0, highName, OBJPROP_STYLE, STYLE_DASHDOT);
//--- Create lowest open trend line
ObjectCreate(0, lowName, OBJ_TREND, 0, time1, lowestOpen, time2, lowestOpen);
ObjectSetInteger(0, lowName, OBJPROP_COLOR, ColorLowest);
ObjectSetInteger(0, lowName, OBJPROP_WIDTH, 1);
ObjectSetInteger(0, lowName, OBJPROP_RAY_RIGHT, true);
ObjectSetInteger(0, lowName, OBJPROP_STYLE, STYLE_DASHDOT);
}
For traders who enjoy studying MQL5, this update also demonstrates several useful programming techniques such as object-oriented chart drawing, safe range calculation, multi-timeframe scanning, automatic object cleanup, and real-time information display. I hope this explanation helps readers understand both the trading logic and the programming logic behind the indicator. Thank you for following my development journey, and I will continue improving the HOL indicator in future updates as I discover new ideas and receive feedback from other traders.
