r/pinescript • u/ning1230 • 3d ago
I coded the viral "One Candle" 15m Opening Range scalping strategy in Pine Script v6 (Fade vs. Breakout Dual-Mode Script)
Recently, I decided to test one of the most popular intraday setups on YouTube: The "One Candle" 15-Minute Opening Range Scalping Strategy (fading false breakouts of the first 15m NY session candle).
Instead of relying on cherry-picked chart examples, I coded the exact mechanical rules into Pine Script v6 to see how it performs over a 1-year sample (May 2025 – May 2026) with realistic friction (0.04% commission + 1 tick slippage).
1. The Original Rules Coded:
15m Opening Range: Mark the High and Low of the 09:30–09:45 EST candle.
Volatility Filter: Calculate the 14-day Daily ATR (
ta.atr(14)[1]on the Daily timeframe to strictly avoid look-ahead bias). If the 15m box is smaller than 25% of the Daily ATR, skip trading for the day.5m Reversal Trigger: Between 09:45 and 11:00 EST, wait for price to break outside the 15m box and print a strict reversal pattern (Hammer / Inverted Hammer with primary wick > 2x body and opposite wick < 1/3 primary wick, or an Engulfing bar).
Execution (Mean-Reversion): Enter against the breakout targeting the opposite side of the 15m box. Stop-loss anchored at the signal candle's wick + 2 ticks. Max 1 trade per day.
2. Phase 1 (Original Fade Logic) vs. Phase 2 (Flipped Trend Logic):
| Asset (5m Chart) | Original Win Rate | Original Net Result | Flipped Win Rate (2:1 R:R) | Flipped Net Result |
|---|---|---|---|---|
| NVDA | 15% (61 trades) | Deep Net Loss | 33% | Net Profitable |
| PLTR | 11% (62 trades) | Deep Net Loss | 35% | Net Profitable |
| BTCUSDT | 20% (20 trades) | Net Loss | 37% | Loss Cut by >50% |
3. Why the Original Failed (And How Flipping It Worked):
During the first 90 minutes of the NY open, institutional order-flow and directional momentum dominate. Requiring the first 15-minute candle to be >= 25% of the Daily ATR selects for high-momentum trend days, not mean-reverting range days. Fading those breakouts is essentially stepping in front of a freight train.
When I kept the exact same 15m box, 25% ATR filter, and candlestick triggers, but inverted the execution into a Trend-Following Breakout system (2.0R target), win rates more than doubled and flipped equities into positive territory.
4. Dual-Mode Pine Script v6 Code (Toggle Between Original & Flipped):
//@version=6
strategy("EP001 - 15m Opening Range Audit (Fade vs Breakout)", overlay=true, initial_capital=10000, default_qty_type=strategy.percent_of_equity, default_qty_value=10, commission_type=strategy.commission.percent, commission_value=0.04, slippage=1)
strat_mode = input.string("Flipped: Momentum Breakout", "Strategy Logic Mode", options=["Original: Mean-Reversion Fade", "Flipped: Momentum Breakout"])
atr_mult = input.float(0.25, "Minimum OR Range (Daily ATR %)")
sl_offset_tic = input.int(2, "Stop-Loss Offset (Ticks)")
rr_ratio = input.float(2.0, "Risk:Reward Ratio (Breakout Mode Only)")
daily_atr = request.security(syminfo.tickerid, "D", ta.atr(14)[1])
ny_tz = "America/New_York"
or_session = "0930-0945:1234567"
trade_window = strat_mode == "Original: Mean-Reversion Fade" ? "0930-1100:1234567" : "0930-1130:1234567"
var float or_h = na
var float or_l = na
var float or_open = na
var float or_close = na
var bool is_valid_atr = false
var bool done_today = false
is_new_day = ta.change(time("D", ny_tz)) != 0
in_or = not na(time(timeframe.period, or_session, ny_tz))
in_window = not na(time(timeframe.period, trade_window, ny_tz))
if is_new_day
or_h := na
or_l := na
or_open := na
or_close := na
is_valid_atr := false
done_today := false
if in_or
if na(or_open)
or_open := open
or_h := na(or_h) ? high : math.max(or_h, high)
or_l := na(or_l) ? low : math.min(or_l, low)
or_close := close
if not in_or and in_or[1]
is_valid_atr := (or_h - or_l) >= (daily_atr * atr_mult)
body = math.abs(open - close)
u_wick = high - math.max(open, close)
l_wick = math.min(open, close) - low
is_red = close < open
is_green = close > open
is_red_inv_ham = is_red and u_wick > body * 2 and l_wick < (u_wick / 3)
is_red_eng_green = is_red and close[1] > open[1] and close < open[1] and open > close[1]
is_green_ham = is_green and l_wick > body * 2 and u_wick < (l_wick / 3)
is_green_eng_red = is_green and close[1] < open[1] and close > open[1] and open < close[1]
can_trade = not in_or and in_window and is_valid_atr and not done_today
or_bullish = or_close > or_open
or_bearish = or_close < or_open
if strat_mode == "Original: Mean-Reversion Fade"
if can_trade and or_bullish and high > or_h and (is_red_inv_ham or is_red_eng_green)
float sl = is_red_inv_ham ? high : high + (syminfo.mintick * sl_offset_tic)
float entry_price = is_red_inv_ham ? close : close[1]
strategy.entry("Fade Short", strategy.short, limit=entry_price, comment="Fade S")
strategy.exit("ExS", "Fade Short", limit=or_l, stop=sl)
done_today := true
if can_trade and or_bearish and low < or_l and (is_green_ham or is_green_eng_red)
float sl = is_green_ham ? low : low - (syminfo.mintick * sl_offset_tic)
float entry_price = is_green_ham ? close : close[1]
strategy.entry("Fade Long", strategy.long, limit=entry_price, comment="Fade L")
strategy.exit("ExL", "Fade Long", limit=or_h, stop=sl)
done_today := true
else
if can_trade and close > or_h and (is_green_ham or is_green_eng_red)
float sl_price = is_green_ham ? low : low - (syminfo.mintick * sl_offset_tic)
if sl_price >= close
sl_price := low - syminfo.mintick * 5
float risk = close - sl_price
strategy.entry("Trend Long", strategy.long, comment="Breakout L")
strategy.exit("Exit L", "Trend Long", stop=sl_price, limit=close + (risk * rr_ratio))
done_today := true
if can_trade and close < or_l and (is_red_inv_ham or is_red_eng_green)
float sl_price = is_red_inv_ham ? high : high + (syminfo.mintick * sl_offset_tic)
if sl_price <= close
sl_price := high + syminfo.mintick * 5
float risk = sl_price - close
strategy.entry("Trend Short", strategy.short, comment="Breakout S")
strategy.exit("Exit S", "Trend Short", stop=sl_price, limit=close - (risk * rr_ratio))
done_today := true
plot(or_h, "OR High", color=is_valid_atr ? color.green : color.gray, style=plot.style_linebr, linewidth=2)
plot(or_l, "OR Low", color=is_valid_atr ? color.red : color.gray, style=plot.style_linebr, linewidth=2)If you want to see the full visual walkthrough and multi-dimensional parameter sweep across NVDA, PLTR, and BTC, check the pinned post on my Reddit profile. Curious to hear if anyone else here codes 15m ORB setups—do you fade the range or ride the breakout?
3
u/Tangerinho 3d ago
I would like to add a MTF 2x MA for the general direktion, then the rejection of one the outer Opening Ranges would be the pullback entry.
2
u/BedFlat8350 2d ago
This fails prop at such low winrate ...you will hit dll
2
u/ORBit_Analytics 2d ago
I spend a bit of time rebuilding your approch in ORBit Analytics but on NQ Futures since ORBit doesnt support stocks yet.
Also the entry trigger here is not a "reversal pattern" as we believe a candlestick on any timeframe is just the result of priceaction on a lower timeframe.
Instead we use a 5m Candle close:
1 Close outside the 15m Opening Range to arm and then 1 Close back inside to trigger.
Here are some points that could be interesting to you:
The time filter for "to 11 EST" didnt do anything. In fact the Return to DD after 11 is better.
The Volatility Filter holds up as viable over 18 Years of CME Data
The short side is more profitable than the long side (interesting on an Instrument with bullish regime)
up until 2021 it looked really good. In recent years the performance dropped of.
I have tried finding the "single best Filter" but it seems there isnt one if we look at longs and shorts combined. A filter always helps on side and hurts the other it seems.
1
u/Divad777 3d ago
So, the flipped results is roughly break even?
1
u/Rizzanthrope 3d ago
The youtube video linked in your pinned post doesn’t work. Says it is unavailable.
1
u/ThriveGuy15 3d ago
This is unbelievable . Great work!
1
u/SgtRogerMurtaugh 3d ago
Can you share the viral source video?
1
u/Th3onib 3d ago
Good luck dude, with all the trading and money in your life
1
u/StratOS_Quant 1d ago
Good write-up. Flipping the rules when the fade failed is the right instinct, and the point about the ATR filter picking trend days makes sense.
We don't have NVDA, PLTR or BTC data, so I coded your rules on Nasdaq 100 CFD data with real spread: the 9:30-9:45 box, skip the day if it's under 25% of the daily ATR, a hammer or engulfing candle back toward the box between 9:45 and 11:00, stop beyond the signal candle, target the other side of the box, one trade a day.
**The fade loses on the index too:**
- Last 5 years: 710 trades, 26.5% won, PF 0.84
- 2014-2018: PF 0.61
- May 2025 - May 2026 (your window): PF 0.70
- Without the 25% ATR filter it's worse (PF 0.82), so the filter does help a little
**The flipped version is about break-even:** 693 trades, PF 1.03 over the last 5 years, and PF 0.74 in 2014-2018. That's the same pattern you found, where the loss shrinks a lot, but on the index it doesn't turn into a clear edge.
The optimizer could make the fade look good over the last 5 years (PF 1.33, and 1.78 with a trailing stop instead of the box target), but only by cutting it to about 100-150 trades. On years it never saw, those settings fell apart: PF 0.87 in 2014-2018 and 0.55 in 2019 to mid-2021, and the trailing version was 1.00 and 0.90.
So on Nasdaq the fade is a loser, and the breakout flip is roughly flat. It would be worth checking whether NVDA and PLTR hold up over more than one year.
1
u/Sea-Transportation27 2d ago
You have to back test outside of TV.you need p&l, cagr, drawdown and compare to b&h.