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.
The Opening Range Strategy, First 30 Minutes of New York
Re: The Opening Range Strategy, First 30 Minutes of New York
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: The Opening Range Strategy, First 30 Minutes of New York
1. MetaTrader 4 (MQL4) Pro Version
Open MT4 -> Open MetaEditor (F4) -> New -> Custom Indicator -> Name it ProNYOpeningRange. Paste this code and click Compile.
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.
Re: The Opening Range Strategy, First 30 Minutes of New York
2. MetaTrader 5 (MQL5) Pro Version
Open MT5 -> Open MetaEditor (F4) -> New -> Custom Indicator -> Name it ProNYOpeningRange. Paste this code and click Compile.
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.
Re: The Opening Range Strategy, First 30 Minutes of New York
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 
-
LondonNewsTrader
- Posts: 421
- Joined: Mon Sep 21, 2026 9:30 am
Re: The Opening Range Strategy, First 30 Minutes of New York
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.