Expert Advisors • Indicators • Scripts • Libraries

MQL.RobotFX.org is the biggest collection of MetaTrader expert advisors (MT5 & MT4), indicators, scripts and libraries that can be used to improve trading results, minimize risks or simply automate trading tasks

Metatrader 5 Indicator | Open Range BreakOut Indicator for MetaTrader 5

Photo for the article Open Range BreakOut Indicator for MetaTrader 5

showMidPoint (default: false)
Plots a line at the exact center of the opening range. The midpoint often acts as a pivot level and can be used for mean reversion strategies.

showShadedBox (default: true)
Draws a filled rectangle over the opening range period, providing clear visual identification of the ORB zone.

Color Customization

shadeColor (default: clrTeal)
Sets the color for the shaded opening range rectangle.

orb50Color (default: clrPurple)
Color for the 50% profit target lines.

orb100Color (default: clrBlue)
Color for the 100% profit target lines.

orbOtherColor (default: clrTeal)
Color for all extended profit targets (150% through 450%).

Time Settings

sORBStartTime (default: "0930-0945")
Custom time override in 24-hour format (HHMM-HHMM). This parameter is only used when sOpeningRangeMinutes is set to 0, allowing complete flexibility in defining the opening range period.

sTimeZone (default: "EST")
Reference timezone for the opening range calculation. While the parameter accepts different timezone abbreviations, the indicator calculates times based on the broker's server time.

Label Formatting

labelOffsetBars (default: 5)
Horizontal spacing in bars between the current price action and the label position. Positive values move labels to the right.

labelOffsetPips (default: 0)
Vertical spacing in pips for label positioning. This helps prevent labels from overlapping with price action or other chart elements.

labelFontSize (default: 8)
Font size for all text labels on the chart. Adjust based on screen resolution and personal preference.

labelAnchor (default: ANCHOR_LEFT)
Determines the anchor point for label positioning. Options include ANCHOR_LEFT, ANCHOR_RIGHT, ANCHOR_CENTER, etc.

maxLineBars (default: 500)
Maximum length in bars for ORB level lines. This prevents the indicator from drawing excessively long lines on historical data, improving performance.

How the Indicator Works

Opening Range Calculation

The indicator identifies the opening range by monitoring the specified time period and recording the highest high and lowest low during that window. For example, with a 15-minute setting, it captures the range between 9:30 and 9:45.

Once the opening range period ends, the high and low levels are locked and extended forward as horizontal lines for the remainder of the trading session.

Price Target Calculation

All profit targets are calculated as multiples of the opening range size. If the opening range is 20 points, the 50% target would be 10 points beyond the ORB high (for long trades) or below the ORB low (for short trades). The 100% target would be 20 points, 150% would be 30 points, and so on.

This dynamic scaling ensures profit targets adjust automatically to market volatility, with larger ranges producing wider targets and smaller ranges producing tighter targets.

Breakout Detection Logic

The indicator employs sophisticated multi-bar confirmation for breakout signals. A valid upside breakout requires price to close above the ORB high on one bar, then continue higher on the next bar with the low remaining above the ORB high level. This filters out false breakouts where price briefly spikes through the level but immediately reverses.

Similarly, downside breakouts require closes below the ORB low followed by continuation with highs remaining below the level.

Retest Identification

After a confirmed breakout, the indicator monitors for pullbacks to the broken level. A valid retest occurs when price returns to touch the ORB level but closes on the correct side, confirming the level has flipped from resistance to support (or vice versa).

Failed retests are flagged when price breaks back through the ORB level in the opposite direction, invalidating the original breakout signal.

Interpretation and Trading Applications

Breakout Trading Strategy

The primary application is identifying high-probability breakout trades. When price breaks out of the opening range with volume and momentum, traders can enter in the direction of the breakout, placing stops below the ORB low (for long trades) or above the ORB high (for short trades).

The first profit target is typically the 50% level, where partial profits can be taken. The remaining position targets the 100% level or beyond in strongly trending conditions.

Retest Entry Method

Conservative traders prefer waiting for a retest after the initial breakout. The "Breakout - Wait for Retest" label signals this opportunity. When price pulls back to test the broken level and finds support (or resistance), it provides a lower-risk entry point with a tighter stop loss.

This approach improves the risk-reward ratio significantly compared to chasing the initial breakout.

Range-Bound Trading

When price remains contained within the opening range, traders can employ mean reversion strategies, buying near the ORB low and selling near the ORB high. The midpoint line serves as a reference for neutral positioning.

Multi-Timeframe Analysis

The indicator can be applied to multiple timeframes simultaneously. Some traders use a 5-minute ORB for scalping, a 15-minute ORB for day trading, and a 30-minute ORB for swing trading, creating a comprehensive view of potential support and resistance zones.

Recommended Symbols and Timeframes

Optimal Timeframes
The indicator works best on 1-minute (M1) through 15-minute (M15) charts, where intraday price action is clearly visible. For detailed analysis of entry timing, the M5 timeframe provides an excellent balance between noise reduction and responsiveness.

Suitable Instruments
The ORB strategy is particularly effective on liquid instruments with clear opening sessions:

  • Forex pairs: EURUSD, GBPUSD, USDJPY during their respective regional opens
  • Gold (XAUUSD): Highly responsive to opening range breakouts due to strong directional moves
  • Stock indices: US30, NAS100, SPX500 during the New York session open
  • Cryptocurrencies: BTCUSD, ETHUSD during periods of high volume

The indicator performs best on instruments with defined trading sessions rather than 24-hour markets without clear opening times.

Performance Optimization

The indicator includes several performance optimizations for smooth chart operation. Buffer initialization to EMPTY_VALUE prevents unnecessary line drawing, while the maxLineBars parameter limits historical line length. The rectangle drawing logic waits until the session concludes before rendering, reducing computational overhead during active trading periods.

Object naming conventions use unique identifiers based on day of year, preventing conflicts when multiple ORB sessions are displayed simultaneously.

Alert System

The notification system provides real-time alerts through MetaTrader 5's built-in alert mechanism. Alerts can be configured for simple level crossings or restricted to confirmed breakouts only. When a breakout occurs, the alert message specifies whether it's above the ORB high or below the ORB low, allowing traders to quickly assess the opportunity without watching the chart continuously.

Visual Presentation

All chart elements follow professional formatting standards with customizable colors. The default color scheme uses lime for ORB high, red for ORB low, purple for 50% targets, blue for 100% targets, and teal for extended targets and the shaded range box. This color-coding system allows for rapid visual identification of key levels even on busy charts.

The shaded rectangle provides an intuitive visual reference for the opening range period, making it immediately obvious when price is trading within or outside the established range.

Conclusion

The Open Range Breakout indicator is a comprehensive trading tool that combines automatic range calculation, multiple profit targets, breakout detection, and retest identification into a single, easy-to-use package. Its flexibility through extensive input parameters allows traders to adapt the indicator to various trading styles, from aggressive breakout trading to conservative retest entries.

The indicator's clean visual presentation and logical alert system make it suitable for both discretionary traders and those developing automated trading systems. By focusing on the critical opening range period, it helps traders identify the most significant support and resistance levels for each trading session.



//+------------------------------------------------------------------+
//|                                    Open Range Breakout-H-Max.mq5 |
//|                By Hieronymos Starch - Quotesy of The Nevek Ratio |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Hieronymos Starch - arete2077@gmail.com  - +263785974079 "
#property link      "https://www.mql5.com"
#property version   "9.00"
#property indicator_chart_window

#property indicator_buffers 22
#property indicator_plots   22

// Plot buffers
#property indicator_label1  "ORB High"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrLime
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

#property indicator_label2  "ORB Low"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2

#property indicator_label3  "ORB Mid Point"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrGray
#property indicator_style3  STYLE_SOLID
#property indicator_width3  1

#property indicator_label4  "ORB High PT 0.5"
#property indicator_type4   DRAW_LINE
#property indicator_color4  clrPurple
#property indicator_style4  STYLE_SOLID
#property indicator_width4  2

#property indicator_label5  "ORB High PT 1.0"
#property indicator_type5   DRAW_LINE
#property indicator_color5  clrBlue
#property indicator_style5  STYLE_SOLID
#property indicator_width5  2

#property indicator_label6  "ORB Low PT 0.5"
#property indicator_type6   DRAW_LINE
#property indicator_color6  clrPurple
#property indicator_style6  STYLE_SOLID
#property indicator_width6  2

#property indicator_label7  "ORB Low PT 1.0"
#property indicator_type7   DRAW_LINE
#property indicator_color7  clrBlue
#property indicator_style7  STYLE_SOLID
#property indicator_width7  2

// Extended targets
#property indicator_label8  "ORB High PT 1.5"
#property indicator_type8   DRAW_LINE
#property indicator_color8  clrTeal

#property indicator_label9  "ORB High PT 2.0"
#property indicator_type9   DRAW_LINE
#property indicator_color9  clrTeal

#property indicator_label10 "ORB High PT 2.5"
#property indicator_type10  DRAW_LINE
#property indicator_color10 clrTeal

#property indicator_label11 "ORB High PT 3.0"
#property indicator_type11  DRAW_LINE
#property indicator_color11 clrTeal

#property indicator_label12 "ORB High PT 3.5"
#property indicator_type12  DRAW_LINE
#property indicator_color12 clrTeal

#property indicator_label13 "ORB High PT 4.0"
#property indicator_type13  DRAW_LINE
#property indicator_color13 clrTeal

#property indicator_label14 "ORB High PT 4.5"
#property indicator_type14  DRAW_LINE
#property indicator_color14 clrTeal

#property indicator_label15 "ORB High PT 5.0"
#property indicator_type15  DRAW_LINE
#property indicator_color15 clrTeal

#property indicator_label16 "ORB Low PT 1.5"
#property indicator_type16  DRAW_LINE
#property indicator_color16 clrTeal

#property indicator_label17 "ORB Low PT 2.0"
#property indicator_type17  DRAW_LINE
#property indicator_color17 clrTeal

#property indicator_label18 "ORB Low PT 2.5"
#property indicator_type18  DRAW_LINE
#property indicator_color18 clrTeal

#property indicator_label19 "ORB Low PT 3.0"
#property indicator_type19  DRAW_LINE
#property indicator_color19 clrTeal

#property indicator_label20 "ORB Low PT 3.5"
#property indicator_type20  DRAW_LINE
#property indicator_color20 clrTeal

#property indicator_label21 "ORB Low PT 4.0"
#property indicator_type21  DRAW_LINE
#property indicator_color21 clrTeal

#property indicator_label22 "ORB Low PT 4.5"
#property indicator_type22  DRAW_LINE
#property indicator_color22 clrTeal

// Input parameters
input string    sOpeningRangeMinutes = "15";     // Period (minutes): 5,15,30,0
input bool      alertBreakoutsOnly = false;      // Alert only on ORB breakouts (not price ticks)
input bool      showLabels = true;               // Show ORB labels
input bool      showPreviousDayORBs = true;      // Show ORB ranges on previous days
input bool      showEntries = true;              // Show potential ORB Breakouts and Retests (BRB)
input bool      showPriceTargets = true;         // Show Default Price Targets (50%, 100%)
input bool      showPriceTargetsExtended = false; // Show Extended Price Targets (150%, 200%)
input bool      showMidPoint = false;            // Show ORB Mid Point
input bool      showShadedBox = true;            // Shade the ORB Range
input color     shadeColor = clrTeal;            // Shaded ORB Range Color
input color     orb50Color = clrPurple;          // ORB 50 Price Target
input color     orb100Color = clrBlue;           // ORB 100 Price Target
input color     orbOtherColor = clrTeal;         // All Other ORB Price Targets
input string    sORBStartTime = "0930-0945";     // Time Override (Format: 0930-0945)
input string    sTimeZone = "EST";               // Timezone
input int       labelOffsetBars = 5;             // Label Horizontal Offset (bars)
input int       labelOffsetPips = 0;             // Label Vertical Offset (pips)
input int       labelFontSize = 8;               // Label Font Size
input ENUM_ANCHOR_POINT labelAnchor = ANCHOR_LEFT; // Label Anchor Point
input int       maxLineBars = 500;               // Maximum Line Length (bars)

// Indicator buffers
double OrbHighBuffer[];
double OrbLowBuffer[];
double OrbMidBuffer[];
double OrbHigh50Buffer[];
double OrbHigh100Buffer[];
double OrbLow50Buffer[];
double OrbLow100Buffer[];
double OrbHigh150Buffer[];
double OrbHigh200Buffer[];
double OrbHigh250Buffer[];
double OrbHigh300Buffer[];
double OrbHigh350Buffer[];
double OrbHigh400Buffer[];
double OrbHigh450Buffer[];
double OrbHigh500Buffer[];
double OrbLow150Buffer[];
double OrbLow200Buffer[];
double OrbLow250Buffer[];
double OrbLow300Buffer[];
double OrbLow350Buffer[];
double OrbLow400Buffer[];
double OrbLow450Buffer[];

// Global variables
struct ORBData {
    double orbHighPrice;
    double orbLowPrice;
    datetime orbStartTime;
    datetime orbEndTime;
    bool isActive;
    string boxName;
    int dayOfYear;
    int sessionStartBar;
    int sessionEndBar;
};

ORBData currentORB;
bool inBreakout = false;
int openingRangeMinutes;
int sessionStartHour, sessionStartMin;
int sessionEndHour, sessionEndMin;
string orbTitle;
double pointMultiplier;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
    openingRangeMinutes = (int)StringToInteger(sOpeningRangeMinutes);
    pointMultiplier = (_Point * 10);
    
    // Determine session times
    if(openingRangeMinutes == 5)
    {
        sessionStartHour = 9; sessionStartMin = 30;
        sessionEndHour = 9; sessionEndMin = 35;
    }
    else if(openingRangeMinutes == 15)
    {
        sessionStartHour = 9; sessionStartMin = 30;
        sessionEndHour = 9; sessionEndMin = 45;
    }
    else if(openingRangeMinutes == 30)
    {
        sessionStartHour = 9; sessionStartMin = 30;
        sessionEndHour = 10; sessionEndMin = 0;
    }
    else if(openingRangeMinutes == 0)
    {
        string parts[];
        if(StringSplit(sORBStartTime, '-', parts) == 2)
        {
            string startTime = parts[0];
            string endTime = parts[1];
            
            sessionStartHour = (int)StringToInteger(StringSubstr(startTime, 0, 2));
            sessionStartMin = (int)StringToInteger(StringSubstr(startTime, 2, 2));
            sessionEndHour = (int)StringToInteger(StringSubstr(endTime, 0, 2));
            sessionEndMin = (int)StringToInteger(StringSubstr(endTime, 2, 2));
        }
    }
    else
    {
        sessionStartHour = 9; sessionStartMin = 30;
        sessionEndHour = 9; sessionEndMin = 45;
    }
    
    orbTitle = "ORB" + IntegerToString(openingRangeMinutes);
    
    // Initialize ORB data
    currentORB.orbHighPrice = 0.0;
    currentORB.orbLowPrice = 0.0;
    currentORB.isActive = false;
    currentORB.boxName = "";
    currentORB.dayOfYear = 0;
    currentORB.sessionStartBar = -1;
    currentORB.sessionEndBar = -1;
    
    // Set indicator buffers
    SetIndexBuffer(0, OrbHighBuffer, INDICATOR_DATA);
    SetIndexBuffer(1, OrbLowBuffer, INDICATOR_DATA);
    SetIndexBuffer(2, OrbMidBuffer, INDICATOR_DATA);
    SetIndexBuffer(3, OrbHigh50Buffer, INDICATOR_DATA);
    SetIndexBuffer(4, OrbHigh100Buffer, INDICATOR_DATA);
    SetIndexBuffer(5, OrbLow50Buffer, INDICATOR_DATA);
    SetIndexBuffer(6, OrbLow100Buffer, INDICATOR_DATA);
    SetIndexBuffer(7, OrbHigh150Buffer, INDICATOR_DATA);
    SetIndexBuffer(8, OrbHigh200Buffer, INDICATOR_DATA);
    SetIndexBuffer(9, OrbHigh250Buffer, INDICATOR_DATA);
    SetIndexBuffer(10, OrbHigh300Buffer, INDICATOR_DATA);
    SetIndexBuffer(11, OrbHigh350Buffer, INDICATOR_DATA);
    SetIndexBuffer(12, OrbHigh400Buffer, INDICATOR_DATA);
    SetIndexBuffer(13, OrbHigh450Buffer, INDICATOR_DATA);
    SetIndexBuffer(14, OrbHigh500Buffer, INDICATOR_DATA);
    SetIndexBuffer(15, OrbLow150Buffer, INDICATOR_DATA);
    SetIndexBuffer(16, OrbLow200Buffer, INDICATOR_DATA);
    SetIndexBuffer(17, OrbLow250Buffer, INDICATOR_DATA);
    SetIndexBuffer(18, OrbLow300Buffer, INDICATOR_DATA);
    SetIndexBuffer(19, OrbLow350Buffer, INDICATOR_DATA);
    SetIndexBuffer(20, OrbLow400Buffer, INDICATOR_DATA);
    SetIndexBuffer(21, OrbLow450Buffer, INDICATOR_DATA);
    
    // Set colors
    PlotIndexSetInteger(3, PLOT_LINE_COLOR, orb50Color);
    PlotIndexSetInteger(4, PLOT_LINE_COLOR, orb100Color);
    PlotIndexSetInteger(5, PLOT_LINE_COLOR, orb50Color);
    PlotIndexSetInteger(6, PLOT_LINE_COLOR, orb100Color);
    
    for(int i = 7; i < 22; i++)
        PlotIndexSetInteger(i, PLOT_LINE_COLOR, orbOtherColor);
    
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                      |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    ObjectsDeleteAll(0, "ORB_");
}

//+------------------------------------------------------------------+
//| 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[])
{
    if(rates_total < 10) return(0);
    
    int start = MathMax(1, prev_calculated);
    if(start == 1) start = 0;
    
    for(int i = start; i < rates_total; i++)
    {
        InitializeBuffers(i);
        
        MqlDateTime dt;
        TimeToStruct(time[i], dt);
        
        bool inSession = IsInSession(dt);
        bool isFirstBar = IsFirstBarOfSession(time, i, dt);
        bool newDayStart = IsNewDay(time, i);
        bool isToday = IsToday(time[i]);
        bool drawOrbs = showPreviousDayORBs || (!showPreviousDayORBs && isToday);
        
        // Start new ORB session
        if(isFirstBar && inSession)
        {
            currentORB.orbHighPrice = high[i];
            currentORB.orbLowPrice = low[i];
            currentORB.orbStartTime = time[i];
            currentORB.sessionStartBar = i;
            currentORB.isActive = true;
            currentORB.dayOfYear = dt.day_of_year;
            inBreakout = false;
            
            // Calculate session end time
            MqlDateTime sessionEnd = dt;
            sessionEnd.hour = sessionEndHour;
            sessionEnd.min = sessionEndMin;
            currentORB.orbEndTime = StructToTime(sessionEnd);
            
            currentORB.boxName = "ORB_Box_" + IntegerToString(dt.day_of_year);
        }
        
        // Update ORB levels during session
        if(inSession && currentORB.isActive)
        {
            if(high[i] > currentORB.orbHighPrice) 
                currentORB.orbHighPrice = high[i];
            if(low[i] < currentORB.orbLowPrice) 
                currentORB.orbLowPrice = low[i];
        }
        
        // Mark end of session
        if(!inSession && currentORB.isActive && i > 0)
        {
            MqlDateTime prevDt;
            TimeToStruct(time[i-1], prevDt);
            if(IsInSession(prevDt))
            {
                currentORB.sessionEndBar = i - 1;
                currentORB.isActive = false;
                
                // Draw the rectangle now that session is complete
                if(showShadedBox)
                    DrawORBRectangle();
            }
        }
        
        // Plot ORB levels and targets
        if(currentORB.orbHighPrice > 0 && currentORB.orbLowPrice > 0)
        {
            bool shouldPlot = false;
            
            if(!inSession && drawOrbs)
            {
                // Limit line length
                int barsFromSessionEnd = (currentORB.sessionEndBar > 0) ? i - currentORB.sessionEndBar : 0;
                if(barsFromSessionEnd <= maxLineBars)
                    shouldPlot = true;
            }
            
            if(shouldPlot)
            {
                double orbRange = currentORB.orbHighPrice - currentORB.orbLowPrice;
                
                // Always show ORB High and Low
                OrbHighBuffer[i] = currentORB.orbHighPrice;
                OrbLowBuffer[i] = currentORB.orbLowPrice;
                
                if(showMidPoint && showPriceTargets)
                    OrbMidBuffer[i] = currentORB.orbLowPrice + (orbRange * 0.5);
                
                if(showPriceTargets)
                    PlotPriceTargets(i, orbRange);
                
                // Draw labels only for today and only once
                if(isToday && showLabels && i == rates_total - 1)
                    DrawPriceTargetLabels(time[i], orbRange);
            }
        }
        
        // Handle breakout detection
        if(!inSession && i > 2 && currentORB.orbHighPrice > 0)
            ProcessBreakouts(time, high, low, close, i, isToday);
    }
    
    return(rates_total);
}

//+------------------------------------------------------------------+
//| Initialize all buffers to EMPTY_VALUE                           |
//+------------------------------------------------------------------+
void InitializeBuffers(int index)
{
    OrbHighBuffer[index] = EMPTY_VALUE;
    OrbLowBuffer[index] = EMPTY_VALUE;
    OrbMidBuffer[index] = EMPTY_VALUE;
    OrbHigh50Buffer[index] = EMPTY_VALUE;
    OrbHigh100Buffer[index] = EMPTY_VALUE;
    OrbLow50Buffer[index] = EMPTY_VALUE;
    OrbLow100Buffer[index] = EMPTY_VALUE;
    OrbHigh150Buffer[index] = EMPTY_VALUE;
    OrbHigh200Buffer[index] = EMPTY_VALUE;
    OrbHigh250Buffer[index] = EMPTY_VALUE;
    OrbHigh300Buffer[index] = EMPTY_VALUE;
    OrbHigh350Buffer[index] = EMPTY_VALUE;
    OrbHigh400Buffer[index] = EMPTY_VALUE;
    OrbHigh450Buffer[index] = EMPTY_VALUE;
    OrbHigh500Buffer[index] = EMPTY_VALUE;
    OrbLow150Buffer[index] = EMPTY_VALUE;
    OrbLow200Buffer[index] = EMPTY_VALUE;
    OrbLow250Buffer[index] = EMPTY_VALUE;
    OrbLow300Buffer[index] = EMPTY_VALUE;
    OrbLow350Buffer[index] = EMPTY_VALUE;
    OrbLow400Buffer[index] = EMPTY_VALUE;
    OrbLow450Buffer[index] = EMPTY_VALUE;
}

//+------------------------------------------------------------------+
//| Plot price targets                                               |
//+------------------------------------------------------------------+
void PlotPriceTargets(int index, double orbRange)
{
    OrbHigh50Buffer[index] = currentORB.orbHighPrice + (orbRange * 0.5);
    OrbHigh100Buffer[index] = currentORB.orbHighPrice + (orbRange * 1.0);
    OrbLow50Buffer[index] = currentORB.orbLowPrice + (orbRange * -0.5);
    OrbLow100Buffer[index] = currentORB.orbLowPrice + (orbRange * -1.0);
    
    if(showPriceTargetsExtended)
    {
        OrbHigh150Buffer[index] = currentORB.orbHighPrice + (orbRange * 1.5);
        OrbHigh200Buffer[index] = currentORB.orbHighPrice + (orbRange * 2.0);
        OrbHigh250Buffer[index] = currentORB.orbHighPrice + (orbRange * 2.5);
        OrbHigh300Buffer[index] = currentORB.orbHighPrice + (orbRange * 3.0);
        OrbHigh350Buffer[index] = currentORB.orbHighPrice + (orbRange * 3.5);
        OrbHigh400Buffer[index] = currentORB.orbHighPrice + (orbRange * 4.0);
        OrbHigh450Buffer[index] = currentORB.orbHighPrice + (orbRange * 4.5);
        OrbHigh500Buffer[index] = currentORB.orbHighPrice + (orbRange * 5.0);
        
        OrbLow150Buffer[index] = currentORB.orbLowPrice + (orbRange * -1.5);
        OrbLow200Buffer[index] = currentORB.orbLowPrice + (orbRange * -2.0);
        OrbLow250Buffer[index] = currentORB.orbLowPrice + (orbRange * -2.5);
        OrbLow300Buffer[index] = currentORB.orbLowPrice + (orbRange * -3.0);
        OrbLow350Buffer[index] = currentORB.orbLowPrice + (orbRange * -3.5);
        OrbLow400Buffer[index] = currentORB.orbLowPrice + (orbRange * -4.0);
        OrbLow450Buffer[index] = currentORB.orbLowPrice + (orbRange * -4.5);
    }
}

//+------------------------------------------------------------------+
//| Draw price target labels                                         |
//+------------------------------------------------------------------+
void DrawPriceTargetLabels(datetime time, double orbRange)
{
    if(!showLabels) return;
    
    string prefix = "ORB_Label_" + IntegerToString(currentORB.dayOfYear);
    ObjectsDeleteAll(0, prefix);
    
    datetime labelTime = time + labelOffsetBars * PeriodSeconds();
    double pipOffset = labelOffsetPips * pointMultiplier;
    
    // Basic ORB levels
    CreatePriceLabel(labelTime, currentORB.orbHighPrice + pipOffset + (20 * _Point), orbTitle + " HIGH", clrLime);
    CreatePriceLabel(labelTime, currentORB.orbLowPrice + pipOffset - (20 * _Point), orbTitle + " LOW", clrRed);
    
    if(showPriceTargets)
    {
        CreatePriceLabel(labelTime, currentORB.orbHighPrice + (orbRange * 0.5) + pipOffset, "PT 50%", orb50Color);
        CreatePriceLabel(labelTime, currentORB.orbHighPrice + (orbRange * 1.0) + pipOffset, "PT 100%", orb100Color);
        CreatePriceLabel(labelTime, currentORB.orbLowPrice + (orbRange * -0.5) + pipOffset, "PT 50%", orb50Color);
        CreatePriceLabel(labelTime, currentORB.orbLowPrice + (orbRange * -1.0) + pipOffset, "PT 100%", orb100Color);
        
        if(showPriceTargetsExtended)
        {
            CreatePriceLabel(labelTime, currentORB.orbHighPrice + (orbRange * 1.5) + pipOffset, "PT 150%", orbOtherColor);
            CreatePriceLabel(labelTime, currentORB.orbHighPrice + (orbRange * 2.0) + pipOffset, "PT 200%", orbOtherColor);
            CreatePriceLabel(labelTime, currentORB.orbHighPrice + (orbRange * 2.5) + pipOffset, "PT 250%", orbOtherColor);
            CreatePriceLabel(labelTime, currentORB.orbHighPrice + (orbRange * 3.0) + pipOffset, "PT 300%", orbOtherColor);
            
            CreatePriceLabel(labelTime, currentORB.orbLowPrice + (orbRange * -1.5) + pipOffset, "PT 150%", orbOtherColor);
            CreatePriceLabel(labelTime, currentORB.orbLowPrice + (orbRange * -2.0) + pipOffset, "PT 200%", orbOtherColor);
            CreatePriceLabel(labelTime, currentORB.orbLowPrice + (orbRange * -2.5) + pipOffset, "PT 250%", orbOtherColor);
            CreatePriceLabel(labelTime, currentORB.orbLowPrice + (orbRange * -3.0) + pipOffset, "PT 300%", orbOtherColor);
        }
        
        if(showMidPoint)
        {
            CreatePriceLabel(labelTime, currentORB.orbLowPrice + (orbRange * 0.5) + pipOffset, "MIDPOINT", clrGray);
        }
    }
}

//+------------------------------------------------------------------+
//| Create price label                                               |
//+------------------------------------------------------------------+
void CreatePriceLabel(datetime time, double price, string text, color clr)
{
    string labelName = "ORB_Label_" + IntegerToString(currentORB.dayOfYear) + "_" + text;
    
    if(ObjectCreate(0, labelName, OBJ_TEXT, 0, time, price))
    {
        ObjectSetString(0, labelName, OBJPROP_TEXT, text);
        ObjectSetInteger(0, labelName, OBJPROP_COLOR, clr);
        ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, labelFontSize);
        ObjectSetInteger(0, labelName, OBJPROP_ANCHOR, labelAnchor);
        ObjectSetString(0, labelName, OBJPROP_FONT, "Arial");
    }
}

//+------------------------------------------------------------------+
//| Process breakout detection                                       |
//+------------------------------------------------------------------+
void ProcessBreakouts(const datetime &time[], const double &high[], const double &low[], const double &close[], int i, bool isToday)
{
    bool highCrossBO = CheckHighBreakout(high, low, close, i);
    bool lowCrossBO = CheckLowBreakout(high, low, close, i);
    
    if(showEntries)
    {
        if(highCrossBO)
        {
            CreateBreakoutLabel(time[i], high[i], "Breakout\nWait for Retest", clrGreen, true);
            inBreakout = true;
        }
        
        if(lowCrossBO)
        {
            CreateBreakoutLabel(time[i], low[i], "Breakout\nWait for Retest", clrGreen, false);
            inBreakout = true;
        }
        
        if(inBreakout)
        {
            bool isRetestHigh = close[i-1] > currentORB.orbHighPrice && low[i] <= currentORB.orbHighPrice && close[i] >= currentORB.orbHighPrice;
            bool isRetestLow = close[i-1] < currentORB.orbLowPrice && high[i] >= currentORB.orbLowPrice && close[i] <= currentORB.orbLowPrice;
            bool failedRetest = (close[i-1] > currentORB.orbHighPrice && close[i] < currentORB.orbHighPrice) || 
                               (close[i-1] < currentORB.orbLowPrice && close[i] > currentORB.orbLowPrice);
            
            if(isRetestHigh || isRetestLow)
            {
                CreateBreakoutLabel(time[i], high[i], "Retest", clrGreen, true);
                inBreakout = false;
            }
            else if(failedRetest)
            {
                CreateBreakoutLabel(time[i], high[i], "Failed Retest", clrRed, true);
                inBreakout = false;
            }
        }
    }
    
    if(isToday)
    {
        if(!alertBreakoutsOnly)
        {
            bool highCross = CheckCross(close, i, currentORB.orbHighPrice);
            bool lowCross = CheckCross(close, i, currentORB.orbLowPrice);
            
            if(highCross)
                SendNotification("Price crossing ORB High Level");
            if(lowCross)
                SendNotification("Price crossing ORB Low Level");
        }
        else
        {
            if(highCrossBO)
                SendNotification("Price breaking out of ORB High Level, Look for Retest");
            if(lowCrossBO)
                SendNotification("Price breaking out of ORB Low Level, Look for Retest");
        }
    }
}

//+------------------------------------------------------------------+
//| Create breakout label                                            |
//+------------------------------------------------------------------+
void CreateBreakoutLabel(datetime time, double price, string text, color clr, bool above)
{
    string labelName = "ORB_Breakout_" + TimeToString(time) + "_" + DoubleToString(price, 5);
    
    double adjustedPrice = above ? price + (50 * _Point) : price - (50 * _Point);
    
    if(ObjectCreate(0, labelName, OBJ_TEXT, 0, time, adjustedPrice))
    {
        ObjectSetString(0, labelName, OBJPROP_TEXT, text);
        ObjectSetInteger(0, labelName, OBJPROP_COLOR, clr);
        ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 8);
        ObjectSetInteger(0, labelName, OBJPROP_ANCHOR, above ? ANCHOR_LOWER : ANCHOR_UPPER);
        ObjectSetString(0, labelName, OBJPROP_FONT, "Arial");
    }
}

//+------------------------------------------------------------------+
//| Draw ORB Rectangle                                               |
//+------------------------------------------------------------------+
void DrawORBRectangle()
{
    if(!showShadedBox || currentORB.boxName == "" || currentORB.sessionStartBar < 0 || currentORB.sessionEndBar < 0) 
        return;
    
    ObjectDelete(0, currentORB.boxName);
    
    if(ObjectCreate(0, currentORB.boxName, OBJ_RECTANGLE, 0, currentORB.orbStartTime, currentORB.orbHighPrice, currentORB.orbEndTime, currentORB.orbLowPrice))
    {
        ObjectSetInteger(0, currentORB.boxName, OBJPROP_COLOR, shadeColor);
        ObjectSetInteger(0, currentORB.boxName, OBJPROP_STYLE, STYLE_SOLID);
        ObjectSetInteger(0, currentORB.boxName, OBJPROP_WIDTH, 2);
        ObjectSetInteger(0, currentORB.boxName, OBJPROP_BACK, true);
        ObjectSetInteger(0, currentORB.boxName, OBJPROP_FILL, true);
        ObjectSetInteger(0, currentORB.boxName, OBJPROP_BGCOLOR, shadeColor);
    }
}

//+------------------------------------------------------------------+
//| Helper functions                                                 |
//+------------------------------------------------------------------+
bool IsInSession(MqlDateTime &dt)
{
    int currentMinutes = dt.hour * 60 + dt.min;
    int sessionStart = sessionStartHour * 60 + sessionStartMin;
    int sessionEnd = sessionEndHour * 60 + sessionEndMin;
    
    return (currentMinutes >= sessionStart && currentMinutes < sessionEnd);
}

bool IsFirstBarOfSession(const datetime &time[], int i, MqlDateTime &dt)
{
    if(i == 0) return false;
    
    MqlDateTime prevDt;
    TimeToStruct(time[i-1], prevDt);
    
    bool inSession = IsInSession(dt);
    bool wasInSession = IsInSession(prevDt);
    
    return (inSession && !wasInSession);
}

bool IsNewDay(const datetime &time[], int i)
{
    if(i == 0) return true;
    
    MqlDateTime dt, prevDt;
    TimeToStruct(time[i], dt);
    TimeToStruct(time[i-1], prevDt);
    
    return (dt.day != prevDt.day);
}

bool IsToday(datetime barTime)
{
    MqlDateTime dt, currentDt;
    TimeToStruct(barTime, dt);
    TimeToStruct(TimeCurrent(), currentDt);
    
    return (dt.year == currentDt.year && dt.day_of_year == currentDt.day_of_year);
}

bool CheckHighBreakout(const double &high[], const double &low[], const double &close[], int i)
{
    return (low[i-2] < currentORB.orbHighPrice && close[i-2] > currentORB.orbHighPrice && 
            low[i-1] > currentORB.orbHighPrice && close[i-1] > currentORB.orbHighPrice && 
            close[i] > low[i-1] && low[i] > currentORB.orbHighPrice);
}

bool CheckLowBreakout(const double &high[], const double &low[], const double &close[], int i)
{
    return (high[i-2] > currentORB.orbLowPrice && close[i-2] < currentORB.orbLowPrice && 
            high[i-1] < currentORB.orbLowPrice && close[i-1] < currentORB.orbLowPrice && 
            close[i] < high[i-1] && high[i] < currentORB.orbLowPrice);
}

bool CheckCross(const double &close[], int i, double level)
{
    if(i == 0) return false;
    return ((close[i-1] <= level && close[i] > level) || 
            (close[i-1] >= level && close[i] < level));
}






65361

Best MetaTrader Indicators + Profitable Expert Advisors