Institutional 5-Min Opening Range Breakout (ORB)

đŸĻŠ Premium Script

Strategy Overview: The Institutional 5-Min Opening Range Breakout (ORB) is a premier volatility expansion strategy designed to capture explosive morning momentums. Operating on the premise that institutional order flow dictates the daily trend within the first 15 minutes, this algorithm meticulously maps the highest high and lowest low from 9:15 to 9:30 AMs. Once the initial balance is established, the system remains coiled and waits for a decisive breakouts. A price surge above the opening range high triggers an aggressive long position, while a collapse below the range low initiates a short bias. By eliminating lagging indicators, the strategy focuses entirely on pure price action and immediate market supply/demand imbalances. To protect against sudden mean reversion, the framework integrates an asymmetric 1:2 risk-reward model, strictly enforcing a 50-point stop loss while targeting a 100-point profit zone. Furthermore, it embeds an agile reversal mechanism to instantly flip directions if the initial breakout proves to be a false traps. This highly disciplined approach culminates with a mandatory 15:15 square-off protocol, ensuring zero overnight exposure to unpredictable market gaps and theta decay, making it an indispensable tool for intraday quantitative traders.

📈 EXPECTED WIN RATE
38.59%
📉 MAX DRAWDOWN
678 Pts
💰 TOTAL PNL
+15,010 Pts
🔄 TOTAL TRADES
4,289
🏆 TOTAL WINS
1,655
!
Important This strategy specifically isolates the 09:15 to 09:30 AM window using Pandas time-filtering to establish the Opening Range High and Low, executing trades dynamically upon breakout.
â„šī¸
Disclaimer These results are generated based on automated backtesting performed using Python code and algorithms. Actual results may vary, and manual backtesting outcomes could differ due to varying assumptions, data interpretation, and market conditions. This information is provided for educational and informational purposes only and should not be considered as financial advice. Before making any trading or investment decisions, please consult with a qualified financial advisor or professional.

1. Imports and Configuration

Summary: This section imports required libraries and defines fundamental backtesting parameters. It establishes the 5-minute timeframe, designates the critical 9:15 to 9:30 AM opening range window, enforces a 15:15 intraday exit time, and sets a rigid 50-point stop loss alongside a 100-point profit target.

python script Copy Code
import pandas as pd
import numpy as np
import datetime as dt

# ===================== CONFIG =====================
FILE_PATH = "Index_1_minute.csv"  
DAYFIRST = True                    
EXIT_TIME = dt.time(15, 15)        
SLIPPAGE = 0.5                     

# Strategy params
TIMEFRAME = "5min"   
ORB_START = dt.time(9, 15)         # Start of the opening range
ORB_END = dt.time(9, 30)           # End of the opening range (15-min ORB)

# Risk Management
STOP_LOSS_POINTS = 50.0            
TARGET_POINTS = 100.0              
# ==================================================

2. Data Loading and Preparation

Summary: This module efficiently processes historical CSV data by parsing date strings into standardized datetime formats, converting price fields to numeric values, removing missing data, and resampling the dataset into precise 5-minute candles.

python script Copy Code
# ----------------- LOAD & PREP --------------------
print("Loading and preparing data...")
df = pd.read_csv(FILE_PATH)
df.columns = [c.strip().lower() for c in df.columns]
required = {"date","open","high","low","close"}
missing = required - set(df.columns)
if missing:
    raise ValueError(f"CSV missing columns: {missing}. Need exactly {sorted(required)}")

df["dt"] = pd.to_datetime(df["date"], dayfirst=DAYFIRST)
df = df.sort_values("dt").reset_index(drop=True)

for c in ["open","high","low","close"]:
    df[c] = pd.to_numeric(df[c], errors="coerce")
df = df.dropna(subset=["open","high","low","close","dt"]).copy()

# Resampling
df = df.set_index("dt").resample(TIMEFRAME).agg({
    "open": "first",
    "high": "max",
    "low": "min",
    "close": "last"
}).dropna().reset_index()

df["d"] = df["dt"].dt.date
df["t"] = df["dt"].dt.time

3. Opening Range Calculation

Summary: This code zeroes in on the first 15 minutes of the trading session. It groups the data by day to find the absolute highest and lowest price points during this critical window, establishing the orb_high and orb_low boundaries, and then maps these levels onto the rest of the intraday data.

python script Copy Code
# ----------------- INDICATORS --------------------
print("Calculating Opening Range Breakout (ORB) levels...")

# Filter the data to only include the opening range window
orb_df = df[(df["t"] >= ORB_START) & (df["t"] < ORB_END)]

# Group by date to find the highest high and lowest low of that specific morning window
orb_levels = orb_df.groupby("d").agg(
    orb_high=("high", "max"),
    orb_low=("low", "min")
).reset_index()

# Merge the ORB levels back into our main dataframe
df = df.merge(orb_levels, on="d", how="left")

4. Utility Functions

Summary: Provides clean helper logic for backtesting. The stats function aggregates all executed trades into high-level metrics like Win Rate and Average PnL, whilst close_out universally structures point calculation for both long and short positions.

python script Copy Code
# -------------- UTILITIES ----------------
def stats(trades):
    if not trades:
        return {"trades":0,"wins":0,"win_rate":0.0,"avg_pnl":0.0,"total_pnl":0.0}
    pnl = np.array([x["pnl"] for x in trades])
    wins = (pnl > 0).sum()
    return {
        "trades": len(trades),
        "wins": int(wins),
        "win_rate": round(100*wins/len(trades), 2),
        "avg_pnl": round(pnl.mean(), 2),
        "total_pnl": round(pnl.sum(), 2),
    }

def close_out(direction, entry, row_close):
    return (row_close - entry) if direction=="long" else (entry - row_close)

5. The Core Backtest Engine

Summary: The central logic loop. It skips the initial 15-minute range formation window, waiting to execute trades sequentially based on breakouts. In addition to monitoring hard Stop-Loss/Take-Profit triggers, it facilitates an automatic 15:15 market-close liquidation. Dynamic positional flips occur instantly if price breaks down in the opposite direction.

python script Copy Code
# -------------- STRATEGY: OPENING RANGE BREAKOUT --------------
def backtest_orb(df):
    print("Running backtest loop...")
    trades = []
    in_trade = False
    direction = None
    entry = None
    entry_time = None

    for _, r in df.iterrows():
        # 1. INTRADAY TIME EXIT
        if r["t"] >= EXIT_TIME:
            if in_trade:
                pnl = close_out(direction, entry, r["close"]) - SLIPPAGE
                trades.append({
                    "date": r["d"], "entry_time": entry_time, "exit_time": r["dt"],
                    "strategy":"ORB", "dir":direction, "entry":entry,
                    "exit":r["close"], "pnl":pnl, "outcome":"TIME_EXIT"
                })
                in_trade = False
            continue

        # Skip trading if we are still inside the opening range formation time
        if r["t"] < ORB_END:
            continue

        if pd.isna(r["orb_high"]) or pd.isna(r["orb_low"]):
            continue

        # 2. SL / TP LOGIC
        if in_trade:
            sl_hit = False
            tp_hit = False
            exit_price = None
            outcome = None

            # Check conservative High/Low hits within the current candle
            if direction == "long":
                if r["low"] <= entry - STOP_LOSS_POINTS:
                    sl_hit = True
                    exit_price = entry - STOP_LOSS_POINTS
                    outcome = "STOP_LOSS"
                elif r["high"] >= entry + TARGET_POINTS:
                    tp_hit = True
                    exit_price = entry + TARGET_POINTS
                    outcome = "TARGET"
            elif direction == "short":
                if r["high"] >= entry + STOP_LOSS_POINTS:
                    sl_hit = True
                    exit_price = entry + STOP_LOSS_POINTS
                    outcome = "STOP_LOSS"
                elif r["low"] <= entry - TARGET_POINTS:
                    tp_hit = True
                    exit_price = entry - TARGET_POINTS
                    outcome = "TARGET"

            # If stopped out or target reached, close the trade
            if sl_hit or tp_hit:
                pnl = close_out(direction, entry, exit_price) - SLIPPAGE
                trades.append({
                    "date": r["d"], "entry_time": entry_time, "exit_time": r["dt"],
                    "strategy":"ORB", "dir":direction, "entry":entry,
                    "exit":exit_price, "pnl":pnl, "outcome":outcome
                })
                in_trade = False
                direction = None
                entry = None
                entry_time = None
                # Skip entry logic on the same candle we hit SL/TP
                continue

        # Signal Logic - Breakout beyond the opening range levels
        breakout_up = r["close"] > r["orb_high"]
        breakout_down = r["close"] < r["orb_low"]

        # 3. ENTRY LOGIC
        if not in_trade:
            if breakout_up:
                direction = "long"
                entry = r["close"]
                entry_time = r["dt"]
                in_trade = True
            elif breakout_down:
                direction = "short"
                entry = r["close"]
                entry_time = r["dt"]
                in_trade = True

        # 4. REVERSAL LOGIC
        else:
            if direction=="long" and breakout_down:
                exit_price = r["close"]
                pnl = (exit_price - entry) - SLIPPAGE
                trades.append({
                    "date": r["d"], "entry_time": entry_time, "exit_time": r["dt"],
                    "strategy":"ORB", "dir":"long", "entry":entry,
                    "exit":exit_price, "pnl":pnl, "outcome":"REVERSE"
                })
                # Reverse to short
                direction = "short"
                entry = r["close"]
                entry_time = r["dt"]

            elif direction=="short" and breakout_up:
                exit_price = r["close"]
                pnl = (entry - exit_price) - SLIPPAGE
                trades.append({
                    "date": r["d"], "entry_time": entry_time, "exit_time": r["dt"],
                    "strategy":"ORB", "dir":"short", "entry":entry,
                    "exit":exit_price, "pnl":pnl, "outcome":"REVERSE"
                })
                # Reverse to long
                direction = "long"
                entry = r["close"]
                entry_time = r["dt"]

    return trades

6. Execution and Reporting

Summary: Acts as the system’s runtime . It triggers the backtest engine, displays terminal summaries, then structures the detailed timeline of trades into an Excel workbook complete with dedicated Weekly, Monthly, and Drawdown tracking tabs.

python script Copy Code
# -------------- EXECUTION & REPORTING --------------
orb_trades = backtest_orb(df)
summary = {"ORB": stats(orb_trades)}

print(f"✅ Backtest complete ({TIMEFRAME} candles)")
for name, s in summary.items():
    print(f"\n{name} -> Trades: {s['trades']}, Wins: {s['wins']}, WinRate: {s['win_rate']}%, "
          f"AvgPnL: {s['avg_pnl']}, TotalPnL: {s['total_pnl']}")

# Write results to Excel
output_file = "orb_results.xlsx"
print(f"\nSaving results to {output_file}...")

with pd.ExcelWriter(output_file, engine="xlsxwriter") as writer:
    if orb_trades:
        trades_df = pd.DataFrame(orb_trades)
        trades_df.to_excel(writer, sheet_name="ORB", index=False)

        trades_df["date_dt"] = pd.to_datetime(trades_df["date"])
        trades_df["week"] = trades_df["date_dt"].dt.to_period("W-FRI")
        weekly = trades_df.groupby("week", as_index=False)["pnl"].sum()
        weekly.rename(columns={"pnl": "total_points"}, inplace=True)
        weekly["week"] = weekly["week"].astype(str)
        weekly.to_excel(writer, sheet_name="Weekly_Report", index=False)

        trades_df["month"] = trades_df["date_dt"].dt.to_period("M")
        monthly = trades_df.groupby("month", as_index=False)["pnl"].sum()
        monthly.rename(columns={"pnl": "total_points"}, inplace=True)
        monthly["month"] = monthly["month"].astype(str)
        monthly.to_excel(writer, sheet_name="Monthly_Report", index=False)

        dd_rows = []
        for m, mdf in trades_df.groupby("month"):
            mdf = mdf.sort_values("exit_time")
            cum = mdf["pnl"].cumsum()
            max_dd = cum.min()
            dd_rows.append({"month": str(m), "max_drawdown": max_dd})
        dd_df = pd.DataFrame(dd_rows)
        dd_df.to_excel(writer, sheet_name="Drawdown_Report", index=False)

    pd.DataFrame([{"strategy": k, **v} for k, v in summary.items()]).to_excel(
        writer, sheet_name="Summary", index=False
    )

print("Process finished successfully.")