Advertisement IC Markets

The Opening Range Strategy, First 30 Minutes of New York

Discuss 1-minute to 15-minute price action setups, fading intraday momentum, key support/resistance zones, and proven short-term trading methodologies.
PTScalper
Site Admin
Posts: 4805
Joined: Mon Jul 20, 2026 1:28 pm

Re: The Opening Range Strategy, First 30 Minutes of New York

Post by PTScalper »

CRUCIAL STEP: Broker Server Time

MetaTrader does not run on your computer's local time or New York time; it runs on your broker's Server Time.
If your broker operates on GMT+3 (which most Forex brokers do), 09:30 AM in New York is actually 16:30 on your MetaTrader chart. You MUST adjust the OR_Start_Hour and OR_End_Hour in the indicator inputs to match your broker's server time.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Recommended broker for automated trading & scalping IC Markets
PTScalper
Site Admin
Posts: 4805
Joined: Mon Jul 20, 2026 1:28 pm

Re: The Opening Range Strategy, First 30 Minutes of New York

Post by PTScalper »

1. MetaTrader 4 (MQL4) Pro Version

Open MT4 -> Open MetaEditor (F4) -> New -> Custom Indicator -> Name it ProNYOpeningRange. Paste this code and click Compile.

Code: Select all

//+------------------------------------------------------------------+
//|                                            ProNYOpeningRange.mq4 |
//+------------------------------------------------------------------+
#property copyright "Pro Opening Range"
#property indicator_chart_window
#property indicator_buffers 8
#property indicator_color1 clrSeaGreen
#property indicator_color2 clrCrimson
#property indicator_color3 clrSeaGreen
#property indicator_color4 clrCrimson
#property indicator_color5 clrSeaGreen
#property indicator_color6 clrCrimson
#property indicator_color7 clrDodgerBlue
#property indicator_color8 clrDarkOrange

#property indicator_width1 2
#property indicator_width2 2
#property indicator_style3 STYLE_DASH
#property indicator_style4 STYLE_DASH

//--- Inputs
input string   _time_settings = "--- Time Settings (Use Broker Time) ---";
input int      OR_Start_Hour = 9;
input int      OR_Start_Minute = 30;
input int      OR_End_Hour = 10;
input int      OR_End_Minute = 0;
input int      Session_End_Hour = 16;
input int      Session_End_Minute = 0;

input string   _filter_settings = "--- Filters & UI ---";
input bool     UseVolumeFilter = true;
input bool     ShowTargets = true;
input bool     ShowDashboard = true;
input int      ArrowOffsetPips = 5;

//--- Buffers
double ExtHigh[], ExtLow[], ExtT1Up[], ExtT1Dn[];
double ExtBOUp[], ExtBODn[], ExtFOUp[], ExtFODn[];

int OnInit()
{
    SetIndexBuffer(0, ExtHigh); SetIndexStyle(0, DRAW_LINE); SetIndexLabel(0, "OR High");
    SetIndexBuffer(1, ExtLow);  SetIndexStyle(1, DRAW_LINE); SetIndexLabel(1, "OR Low");
    
    SetIndexBuffer(2, ExtT1Up); SetIndexStyle(2, DRAW_LINE); SetIndexLabel(2, "1R Target Up");
    SetIndexBuffer(3, ExtT1Dn); SetIndexStyle(3, DRAW_LINE); SetIndexLabel(3, "1R Target Down");
    
    SetIndexBuffer(4, ExtBOUp); SetIndexStyle(4, DRAW_ARROW); SetIndexArrow(4, 233); SetIndexLabel(4, "BO Up");
    SetIndexBuffer(5, ExtBODn); SetIndexStyle(5, DRAW_ARROW); SetIndexArrow(5, 234); SetIndexLabel(5, "BO Down");
    
    SetIndexBuffer(6, ExtFOUp); SetIndexStyle(6, DRAW_ARROW); SetIndexArrow(6, 225); SetIndexLabel(6, "FO Up");
    SetIndexBuffer(7, ExtFODn); SetIndexStyle(7, DRAW_ARROW); SetIndexArrow(7, 226); SetIndexLabel(7, "FO Down");

    return(INIT_SUCCEEDED);
}

void DrawText(string name, string text, int x, int y, color clr, int fontSize, bool bold=false)
{
    if(ObjectFind(0, name) < 0) ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
    ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
    ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
    ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
    ObjectSetString(0, name, OBJPROP_TEXT, text);
    ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
    ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontSize);
    ObjectSetString(0, name, OBJPROP_FONT, bold ? "Arial Bold" : "Arial");
}

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[])
{
    int limit = rates_total - prev_calculated;
    if (prev_calculated == 0) limit = rates_total - 22; // Leave room for 20-MA volume calculation
    
    int start_mins = OR_Start_Hour * 60 + OR_Start_Minute;
    int end_mins = OR_End_Hour * 60 + OR_End_Minute;
    int session_end_mins = Session_End_Hour * 60 + Session_End_Minute;
    
    double pips = Point;
    if (Digits == 3 || Digits == 5) pips *= 10;
    
    static int last_day = -1;
    static double or_high = 0, or_low = 999999;
    static datetime start_time = 0;

    for(int i = limit; i >= 0; i--) 
    {
        int bar_day = TimeDay(time[i]);
        int time_mins = TimeHour(time[i]) * 60 + TimeMinute(time[i]);
        
        if (bar_day != last_day) {
            last_day = bar_day;
            or_high = 0;
            or_low = 999999;
            start_time = 0;
        }
        
        ExtHigh[i]=EMPTY_VALUE; ExtLow[i]=EMPTY_VALUE; ExtT1Up[i]=EMPTY_VALUE; ExtT1Dn[i]=EMPTY_VALUE;
        ExtBOUp[i]=EMPTY_VALUE; ExtBODn[i]=EMPTY_VALUE; ExtFOUp[i]=EMPTY_VALUE; ExtFODn[i]=EMPTY_VALUE;

        // 1. Establish the Range & Draw the Shaded Box
        if (time_mins >= start_mins && time_mins < end_mins) {
            if(start_time == 0) start_time = time[i];
            if (high[i] > or_high) or_high = high[i];
            if (low[i] < or_low) or_low = low[i];
            
            // Draw visual rectangle (Only for last 30 days to save memory)
            if(time[i] >= TimeCurrent() - (30 * 86400)) {
                string rectName = "OR_Box_" + IntegerToString(bar_day);
                if(ObjectFind(0, rectName) < 0) {
                    ObjectCreate(0, rectName, OBJ_RECTANGLE, 0, start_time, or_high, time[i], or_low);
                    ObjectSetInteger(0, rectName, OBJPROP_COLOR, clrDarkGray);
                    ObjectSetInteger(0, rectName, OBJPROP_BACK, true);
                } else {
                    ObjectSetDouble(0, rectName, OBJPROP_PRICE1, or_high);
                    ObjectSetDouble(0, rectName, OBJPROP_PRICE2, or_low);
                    ObjectSetInteger(0, rectName, OBJPROP_TIME2, time[i]);
                }
            }
        }
        
        // 2. Project Lines and Detect Signals
        if (time_mins >= end_mins && time_mins <= session_end_mins) {
            if (or_high > 0 && or_low < 999999) {
                double range_size = or_high - or_low;
                ExtHigh[i] = or_high;
                ExtLow[i] = or_low;
                
                if(ShowTargets) {
                    ExtT1Up[i] = or_high + range_size;
                    ExtT1Dn[i] = or_low - range_size;
                }
                
                double prev_close = close[i+1];
                
                // Volume Filter
                double vol_sum = 0;
                for(int v=1; v<=20; v++) vol_sum += (double)tick_volume[i+v];
                double vol_avg = vol_sum / 20.0;
                bool vol_spike = (!UseVolumeFilter) || ((double)tick_volume[i] > vol_avg);
                
                // Breakouts
                if (close[i] > or_high && prev_close <= or_high && vol_spike) ExtBOUp[i] = low[i] - (ArrowOffsetPips * pips);
                if (close[i] < or_low && prev_close >= or_low && vol_spike) ExtBODn[i] = high[i] + (ArrowOffsetPips * pips);
                
                // Fakeouts (Sweeps)
                if (low[i] < or_low && close[i] > or_low && prev_close >= or_low) ExtFOUp[i] = low[i] - ((ArrowOffsetPips * 2) * pips);
                if (high[i] > or_high && close[i] < or_high && prev_close <= or_high) ExtFODn[i] = high[i] + ((ArrowOffsetPips * 2) * pips);
            }
        }
        
        // 3. Update Dashboard on Current Tick
        if (i == 0 && ShowDashboard) {
            double atr = iATR(Symbol(), PERIOD_D1, 14, 0);
            double range_pips = (or_high - or_low) / pips;
            double atr_pips = atr / pips;
            double exh_pct = (atr_pips > 0) ? (range_pips / atr_pips) * 100.0 : 0;
            
            string status = "Closed"; color statusClr = clrGray;
            if (time_mins >= start_mins && time_mins < end_mins) { status = "Forming..."; statusClr = clrGold; }
            else if (time_mins >= end_mins && time_mins <= session_end_mins) { status = "Active"; statusClr = clrLimeGreen; }

            DrawText("dashLbl1", "NY OR Status:", 160, 20, clrDarkGray, 10, true);
            DrawText("dashVal1", status, 20, 20, statusClr, 10, true);
            
            DrawText("dashLbl2", "Range Size:", 160, 40, clrDarkGray, 10, true);
            DrawText("dashVal2", DoubleToString(range_pips, 1) + " pips", 20, 40, clrWhite, 10, false);
            
            DrawText("dashLbl3", "Daily ATR:", 160, 60, clrDarkGray, 10, true);
            DrawText("dashVal3", DoubleToString(atr_pips, 1) + " pips", 20, 60, clrWhite, 10, false);
            
            DrawText("dashLbl4", "Exhaustion:", 160, 80, clrDarkGray, 10, true);
            DrawText("dashVal4", DoubleToString(exh_pct, 1) + "%", 20, 80, exh_pct > 50 ? clrRed : clrLimeGreen, 10, true);
        }
    }
    return(rates_total);
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 4805
Joined: Mon Jul 20, 2026 1:28 pm

Re: The Opening Range Strategy, First 30 Minutes of New York

Post by PTScalper »

2. MetaTrader 5 (MQL5) Pro Version

Open MT5 -> Open MetaEditor (F4) -> New -> Custom Indicator -> Name it ProNYOpeningRange. Paste this code and click Compile.

Code: Select all

//+------------------------------------------------------------------+
//|                                            ProNYOpeningRange.mq5 |
//+------------------------------------------------------------------+
#property copyright "Pro Opening Range"
#property indicator_chart_window
#property indicator_buffers 8
#property indicator_plots   8

#property indicator_label1  "OR High"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrSeaGreen
#property indicator_width1  2

#property indicator_label2  "OR Low"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrCrimson
#property indicator_width2  2

#property indicator_label3  "1R Target Up"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrSeaGreen
#property indicator_style3  STYLE_DASH

#property indicator_label4  "1R Target Down"
#property indicator_type4   DRAW_LINE
#property indicator_color4  clrCrimson
#property indicator_style4  STYLE_DASH

#property indicator_label5  "BO Up"
#property indicator_type5   DRAW_ARROW
#property indicator_color5  clrSeaGreen

#property indicator_label6  "BO Down"
#property indicator_type6   DRAW_ARROW
#property indicator_color6  clrCrimson

#property indicator_label7  "FO Up"
#property indicator_type7   DRAW_ARROW
#property indicator_color7  clrDodgerBlue

#property indicator_label8  "FO Down"
#property indicator_type8   DRAW_ARROW
#property indicator_color8  clrDarkOrange

//--- Inputs
input string   _time_settings = "--- Time Settings (Use Broker Time) ---";
input int      OR_Start_Hour = 9;
input int      OR_Start_Minute = 30;
input int      OR_End_Hour = 10;
input int      OR_End_Minute = 0;
input int      Session_End_Hour = 16;
input int      Session_End_Minute = 0;

input string   _filter_settings = "--- Filters & UI ---";
input bool     UseVolumeFilter = true;
input bool     ShowTargets = true;
input bool     ShowDashboard = true;
input int      ArrowOffsetPips = 5;

//--- Buffers
double ExtHigh[], ExtLow[], ExtT1Up[], ExtT1Dn[];
double ExtBOUp[], ExtBODn[], ExtFOUp[], ExtFODn[];

int atr_handle;
double atr_buffer[];

int OnInit()
{
    SetIndexBuffer(0, ExtHigh, INDICATOR_DATA);
    SetIndexBuffer(1, ExtLow, INDICATOR_DATA);
    SetIndexBuffer(2, ExtT1Up, INDICATOR_DATA);
    SetIndexBuffer(3, ExtT1Dn, INDICATOR_DATA);
    
    SetIndexBuffer(4, ExtBOUp, INDICATOR_DATA); PlotIndexSetInteger(4, PLOT_ARROW, 233);
    SetIndexBuffer(5, ExtBODn, INDICATOR_DATA); PlotIndexSetInteger(5, PLOT_ARROW, 234);
    SetIndexBuffer(6, ExtFOUp, INDICATOR_DATA); PlotIndexSetInteger(6, PLOT_ARROW, 225);
    SetIndexBuffer(7, ExtFODn, INDICATOR_DATA); PlotIndexSetInteger(7, PLOT_ARROW, 226);

    for(int i=0; i<8; i++) PlotIndexSetDouble(i, PLOT_EMPTY_VALUE, 0.0);
    
    atr_handle = iATR(_Symbol, PERIOD_D1, 14);
    ArraySetAsSeries(atr_buffer, true);
    
    return(INIT_SUCCEEDED);
}

void DrawText(string name, string text, int x, int y, color clr, int fontSize, bool bold=false)
{
    if(ObjectFind(0, name) < 0) ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
    ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
    ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
    ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
    ObjectSetString(0, name, OBJPROP_TEXT, text);
    ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
    ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontSize);
    ObjectSetString(0, name, OBJPROP_FONT, bold ? "Arial Bold" : "Arial");
}

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 < 22) return 0;
    
    // Reverse arrays to match MT4 logic (i=0 is newest bar)
    ArraySetAsSeries(time, true);
    ArraySetAsSeries(high, true);
    ArraySetAsSeries(low, true);
    ArraySetAsSeries(close, true);
    ArraySetAsSeries(tick_volume, true);
    
    int limit = rates_total - prev_calculated;
    if (prev_calculated == 0) limit = rates_total - 22;
    if (limit < 0) limit = 0;
    
    int start_mins = OR_Start_Hour * 60 + OR_Start_Minute;
    int end_mins = OR_End_Hour * 60 + OR_End_Minute;
    int session_end_mins = Session_End_Hour * 60 + Session_End_Minute;
    
    double pips = _Point;
    if (_Digits == 3 || _Digits == 5) pips *= 10;
    
    static int last_day = -1;
    static double or_high = 0, or_low = 999999;
    static datetime start_time = 0;

    for(int i = limit; i >= 0; i--) 
    {
        MqlDateTime dt;
        TimeToStruct(time[i], dt);
        
        int bar_day = dt.day;
        int time_mins = dt.hour * 60 + dt.min;
        
        if (bar_day != last_day) {
            last_day = bar_day;
            or_high = 0;
            or_low = 999999;
            start_time = 0;
        }
        
        ExtHigh[i]=0.0; ExtLow[i]=0.0; ExtT1Up[i]=0.0; ExtT1Dn[i]=0.0;
        ExtBOUp[i]=0.0; ExtBODn[i]=0.0; ExtFOUp[i]=0.0; ExtFODn[i]=0.0;

        // 1. Establish the Range & Draw the Shaded Box
        if (time_mins >= start_mins && time_mins < end_mins) {
            if(start_time == 0) start_time = time[i];
            if (high[i] > or_high) or_high = high[i];
            if (low[i] < or_low) or_low = low[i];
            
            if(time[i] >= TimeCurrent() - (30 * 86400)) {
                string rectName = "OR_Box_" + IntegerToString(bar_day);
                if(ObjectFind(0, rectName) < 0) {
                    ObjectCreate(0, rectName, OBJ_RECTANGLE, 0, start_time, or_high, time[i], or_low);
                    ObjectSetInteger(0, rectName, OBJPROP_COLOR, clrDarkGray);
                    ObjectSetInteger(0, rectName, OBJPROP_BACK, true);
                } else {
                    ObjectSetDouble(0, rectName, OBJPROP_PRICE1, or_high);
                    ObjectSetDouble(0, rectName, OBJPROP_PRICE2, or_low);
                    ObjectSetInteger(0, rectName, OBJPROP_TIME2, time[i]);
                }
            }
        }
        
        // 2. Project Lines and Detect Signals
        if (time_mins >= end_mins && time_mins <= session_end_mins) {
            if (or_high > 0 && or_low < 999999) {
                double range_size = or_high - or_low;
                ExtHigh[i] = or_high;
                ExtLow[i] = or_low;
                
                if(ShowTargets) {
                    ExtT1Up[i] = or_high + range_size;
                    ExtT1Dn[i] = or_low - range_size;
                }
                
                double prev_close = close[i+1];
                
                // Volume Filter
                double vol_sum = 0;
                for(int v=1; v<=20; v++) vol_sum += (double)tick_volume[i+v];
                double vol_avg = vol_sum / 20.0;
                bool vol_spike = (!UseVolumeFilter) || ((double)tick_volume[i] > vol_avg);
                
                // Signals
                if (close[i] > or_high && prev_close <= or_high && vol_spike) ExtBOUp[i] = low[i] - (ArrowOffsetPips * pips);
                if (close[i] < or_low && prev_close >= or_low && vol_spike) ExtBODn[i] = high[i] + (ArrowOffsetPips * pips);
                if (low[i] < or_low && close[i] > or_low && prev_close >= or_low) ExtFOUp[i] = low[i] - ((ArrowOffsetPips * 2) * pips);
                if (high[i] > or_high && close[i] < or_high && prev_close <= or_high) ExtFODn[i] = high[i] + ((ArrowOffsetPips * 2) * pips);
            }
        }
        
        // 3. Update Dashboard on Current Tick
        if (i == 0 && ShowDashboard) {
            CopyBuffer(atr_handle, 0, 0, 1, atr_buffer);
            double atr = atr_buffer[0];
            
            double range_pips = (or_high - or_low) / pips;
            double atr_pips = atr / pips;
            double exh_pct = (atr_pips > 0) ? (range_pips / atr_pips) * 100.0 : 0;
            
            string status = "Closed"; color statusClr = clrGray;
            if (time_mins >= start_mins && time_mins < end_mins) { status = "Forming..."; statusClr = clrGold; }
            else if (time_mins >= end_mins && time_mins <= session_end_mins) { status = "Active"; statusClr = clrLimeGreen; }

            DrawText("dashLbl1", "NY OR Status:", 160, 20, clrDarkGray, 10, true);
            DrawText("dashVal1", status, 20, 20, statusClr, 10, true);
            
            DrawText("dashLbl2", "Range Size:", 160, 40, clrDarkGray, 10, true);
            DrawText("dashVal2", DoubleToString(range_pips, 1) + " pips", 20, 40, clrWhite, 10, false);
            
            DrawText("dashLbl3", "Daily ATR:", 160, 60, clrDarkGray, 10, true);
            DrawText("dashVal3", DoubleToString(atr_pips, 1) + " pips", 20, 60, clrWhite, 10, false);
            
            DrawText("dashLbl4", "Exhaustion:", 160, 80, clrDarkGray, 10, true);
            DrawText("dashVal4", DoubleToString(exh_pct, 1) + "%", 20, 80, exh_pct > 50 ? clrRed : clrLimeGreen, 10, true);
        }
    }
    return(rates_total);
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Fairman
Posts: 3303
Joined: Tue Jul 21, 2026 7:11 am
Location: Abuja

Re: The Opening Range Strategy, First 30 Minutes of New York

Post by Fairman »

PTScalper wrote: If the OR is extremely wide and extends right into a major higher-timeframe supply/demand zone, the breakout is likely a trap.
This is the filter I'd add to the post honestly. A wide opening range also wrecks the measured move target and makes the stop huge for your size, so even when the breakout is real the R is ugly. When the range is already a big chunk of the day's ATR I mostly skip the breakout and only look at the sweep side.
It’s Fairman :geek:
LondonNewsTrader
Posts: 421
Joined: Mon Sep 21, 2026 9:30 am

Re: The Opening Range Strategy, First 30 Minutes of New York

Post by LondonNewsTrader »

Fairman wrote: News at the open can break everything
Your caution about news at the open understates it for FX, in my view. The first 30 minutes of the New York session, if you count from 8:00 New York time, contain the 8:30 release slot, which is where payrolls, CPI, retail sales, PPI and weekly jobless claims all land. So on a large share of days the "opening range" is really the range of the data spike, and its high and low are set by a few seconds of thin liquidity.

If you anchor to the 9:30 equity open instead, the range is usually more orderly, but on data days it still sits inside the aftermath of the 8:30 move.

For the 50-session backtest I'd split days into "8:30 release" and "no release". My guess is that the fakeout version performs better on data days and the breakout version on quiet ones, but the numbers would settle it.
Post Reply