OPEN-SOURCE SCRIPT

Defyler 15m ORB

37
// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at mozilla.org/MPL/2.0/
// © jeffparker341
//version=6
indicator("Defyler ORB",
overlay=true, max_labels_count=500, max_boxes_count=500)

// =====================
// Inputs
// =====================
sess = input.session("0930-1600", "RTH Session")
showAfterHours = input.bool(false, "Extend Past RTH Close")

// Range visuals
bandColor = input.color(color.new(color.teal, 85), "Range Fill")
hiLineColor = input.color(color.new(color.white, 0), "High Line")
loLineColor = input.color(color.new(color.white, 0), "Low Line")
lineWidth = input.int(2, "Range Line Width", minval=1, maxval=4)

// Chart-TF VWAP
showHourlyVWAP = input.bool(true, "Show VWAP (Chart TF)")
hVwapColor = input.color(color.new(color.yellow, 0), "VWAP Color")
hVwapWidth = input.int(2, "VWAP Width", minval=1, maxval=4)

// Debug / markers
showBreakMarks = input.bool(true, "Show Break Markers (BRK)")
showReclaimMk = input.bool(true, "Show Reclaim Markers (T/R)")
showEntryLabel = input.bool(true, "Show Entry Labels")
showExitLabel = input.bool(true, "Show Exit Labels")

breakUpColor = input.color(color.new(color.lime, 0), "BRK Up Color")
breakDnColor = input.color(color.new(color.red, 0), "BRK Down Color")
reclUpColor = input.color(color.new(color.aqua, 0), "Reclaim Long Color")
reclDnColor = input.color(color.new(color.fuchsia, 0), "Reclaim Short Color")

// Trade enable
enableLong = input.bool(true, "Enable Long Trades")
enableShort = input.bool(true, "Enable Short Trades")

// Break→Reclaim tuning
reclaimTolPct = input.float(10.0, "Reclaim proximity (% of 30m range)", minval=0.0, step=0.5)

// Filters
useVWAPFilter = input.bool(true, "Require VWAP Alignment (Long>VWAP, Short<VWAP)")
requireCandleDir = input.bool(true, "Require Reclaim Candle Direction (Long green / Short red)")

// =====================
// NEW: Two-Stage Entry (Reclaim = setup, entry on follow-through)
// =====================
useTwoStageEntry = input.bool(true, "Two-Stage Entry (Reclaim -> Confirm)")
confirmBufferTicks = input.int(0, "Confirm Buffer (ticks)", minval=0, maxval=100)

// =====================
// 30m Trend Projection (recommended ON)
// =====================
useTrendProjection30m = input.bool(true, "Use 30m Trend Projection for TP/SL")
projLen30m = input.int(30, "30m Trend Length", minval=5, maxval=200)
projBars30m = input.int(2, "Project N 30m Bars Ahead", minval=1, maxval=12)
minProjATR = input.float(0.75, "Min Projection (x 30m ATR)", minval=0.0, step=0.05)

// Risk model (applies to base move, projected or fallback)
slPct = input.float(50.0, "SL = % of Base", minval=0.1, step=0.1)
tpPct = input.float(100.0,"TP = % of Base", minval=0.1, step=0.1)

// Make boxes larger overall (SL/TP distances)
boxSizeMult = input.float(1.65, "Order Box Size Multiplier", minval=0.5, maxval=3.0, step=0.05)

// Fallback base mode (only used when projection is OFF)
fallbackBase = input.string("VWAPDistance", "Fallback Base (when projection OFF)",
options=["30mRange","BreakerRange","ATR","VWAPDistance"])
atrLen = input.int(14, "ATR Length (fallback)", minval=1)
atrTf = input.string("5", "ATR Timeframe (fallback)", options=["1","3","5","15","30","60"])

minBaseMode = input.string("ATR", "Min Base Floor Type (fallback)", options=["None","ATR","30mRangePct"])
minBasePct30m = input.float(10.0, "Min Base = % of 30m Range", minval=0.0, step=1.0)

// Trade box visuals
riskBoxColor = input.color(color.new(color.red, 85), "Risk Box Fill")
rewardBoxColor = input.color(color.new(color.lime, 88), "Reward Box Fill")
boxBorderColor = input.color(color.new(color.white, 80), "Box Border")

// =====================
// Session flags
// =====================
inSess = not na(time(timeframe.period, sess))
newDay = ta.change(time("D")) != 0

// =====================
// VWAP (Chart timeframe; auto-adapts to selected TF)
// =====================
chartVWAP = ta.vwap(hlc3)
showVW = showHourlyVWAP and (inSess or showAfterHours)
plot(showVW ? chartVWAP : na, "VWAP", color=hVwapColor, linewidth=hVwapWidth, style=plot.style_line)

// =====================
// First 30m RTH candle high/low (computed on 30m series)
// =====================
rth30_firstBar = request.security(
syminfo.tickerid,
"30",
(not na(time("30", sess))) and (na(time("30", sess))[1]),
barmerge.gaps_off,
barmerge.lookahead_off
)

rth30_high = request.security(syminfo.tickerid, "30", high, barmerge.gaps_off, barmerge.lookahead_off)
rth30_low = request.security(syminfo.tickerid, "30", low, barmerge.gaps_off, barmerge.lookahead_off)

// Range state
var float firstHi = na
var float firstLo = na
var bool locked = false

if newDay
firstHi := na
firstLo := na
locked := false

if rth30_firstBar and not locked
firstHi := rth30_high
firstLo := rth30_low
locked := true

showRange = locked and (inSess or showAfterHours)

// Range plots
rangeHi = showRange ? firstHi : na
rangeLo = showRange ? firstLo : na
pHi = plot(rangeHi, "First 30m High", color=hiLineColor, linewidth=lineWidth, style=plot.style_linebr)
pLo = plot(rangeLo, "First 30m Low", color=loLineColor, linewidth=lineWidth, style=plot.style_linebr)
fill(pHi, pLo, color=bandColor, title="First 30m Range Fill")

// =====================
// 30m Trend Projection (points)
// =====================
atr30 = request.security(syminfo.tickerid, "30", ta.atr(14), barmerge.gaps_off, barmerge.lookahead_off)

lr30 = request.security(syminfo.tickerid, "30", ta.linreg(close, projLen30m, 0), barmerge.gaps_off, barmerge.lookahead_off)
lr30_prev = request.security(syminfo.tickerid, "30", ta.linreg(close, projLen30m, 0)[1], barmerge.gaps_off, barmerge.lookahead_off)

slope30 = lr30 - lr30_prev
projMoveRaw = math.abs(slope30) * projBars30m
projFloor = atr30 * minProjATR
projMove = math.max(projMoveRaw, projFloor)

// =====================
// Break → Reclaim state machine
// =====================
var bool armedLong = true
var bool armedShort = true

var bool longBroken = false
var bool longTouched = false
var bool shortBroken = false
var bool shortTouched = false

range30 = locked ? (firstHi - firstLo) : na
tol = (not na(range30) ? range30 * (reclaimTolPct / 100.0) : na)

// Daily reset
if newDay
armedLong := true
armedShort := true
longBroken := false
longTouched := false
shortBroken := false
shortTouched := false

// Re-arm ONLY when price closes back inside the 30m range
insideRange = locked and barstate.isconfirmed and close <= firstHi and close >= firstLo
if insideRange
armedLong := true
armedShort := true
longBroken := false
longTouched := false
shortBroken := false
shortTouched := false

// VWAP alignment (chart TF VWAP)
allowLong = not useVWAPFilter or close > chartVWAP
allowShort = not useVWAPFilter or close < chartVWAP

// Break (confirmed close)
brkUp = showRange and barstate.isconfirmed and armedLong and close > firstHi and allowLong
brkDn = showRange and barstate.isconfirmed and armedShort and close < firstLo and allowShort

if brkUp
longBroken := true
longTouched := false

if brkDn
shortBroken := true
shortTouched := false

// Touch zone after break
longTouch = showRange and longBroken and armedLong and not longTouched and not na(tol) and low <= (firstHi + tol)
if longTouch
longTouched := true

shortTouch = showRange and shortBroken and armedShort and not shortTouched and not na(tol) and high >= (firstLo - tol)
if shortTouch
shortTouched := true

// Reclaim/confirm (confirmed close back through boundary)
longReclaim = showRange and barstate.isconfirmed and armedLong and longBroken and longTouched and close > firstHi and allowLong
shortReclaim = showRange and barstate.isconfirmed and armedShort and shortBroken and shortTouched and close < firstLo and allowShort

if requireCandleDir
longReclaim := longReclaim and close > open
shortReclaim := shortReclaim and close < open

// Markers
plotshape(showBreakMarks and brkUp, title="BRK Up", style=shape.triangleup, location=location.belowbar, size=size.tiny, color=breakUpColor, text="BRK↑")
plotshape(showBreakMarks and brkDn, title="BRK Down", style=shape.triangledown, location=location.abovebar, size=size.tiny, color=breakDnColor, text="BRK↓")
plotshape(showReclaimMk and longTouch, title="Touch Long", style=shape.circle, location=location.belowbar, size=size.tiny, color=reclUpColor, text="T")
plotshape(showReclaimMk and shortTouch, title="Touch Short", style=shape.circle, location=location.abovebar, size=size.tiny, color=reclDnColor, text="T")
plotshape(showReclaimMk and longReclaim, title="Reclaim Long", style=shape.diamond, location=location.belowbar, size=size.tiny, color=reclUpColor, text="R")
plotshape(showReclaimMk and shortReclaim, title="Reclaim Short", style=shape.diamond, location=location.abovebar, size=size.tiny, color=reclDnColor, text="R")

// =====================
// Two-Stage Entry setup state
// =====================
var bool hasSetup = false
var int setupDir = 0
var float setupHi = na
var float setupLo = na

// Reset setup daily
if newDay
hasSetup := false
setupDir := 0
setupHi := na
setupLo := na

// If price goes back inside the ORB range, invalidate any pending setup
if hasSetup and insideRange
hasSetup := false
setupDir := 0
setupHi := na
setupLo := na

// Capture setup on reclaim (instead of entering immediately when Two-Stage is ON)
if useTwoStageEntry and not hasSetup and not na(firstHi) and not na(firstLo)
if enableLong and not hasSetup and longReclaim
hasSetup := true
setupDir := 1
setupHi := high
setupLo := low
if enableShort and not hasSetup and shortReclaim
hasSetup := true
setupDir := -1
setupHi := high
setupLo := low

// Confirm conditions (follow-through beyond reclaim candle extreme + optional buffer)
buf = confirmBufferTicks * syminfo.mintick
confirmLong = useTwoStageEntry and hasSetup and setupDir == 1 and barstate.isconfirmed and close > (setupHi + buf) and allowLong
confirmShort = useTwoStageEntry and hasSetup and setupDir == -1 and barstate.isconfirmed and close < (setupLo - buf) and allowShort

// =====================
// Fallback sizing helpers (only used if projection OFF)
// =====================
breakerRange = high - low
atrBase = request.security(syminfo.tickerid, atrTf, ta.atr(atrLen), barmerge.gaps_off, barmerge.lookahead_off)

f_base_fallback(entry) =>
float b = na
if fallbackBase == "30mRange"
b := range30
else if fallbackBase == "BreakerRange"
b := breakerRange
else if fallbackBase == "ATR"
b := atrBase
else
b := math.abs(entry - chartVWAP)
b

f_floor_fallback() =>
float f = 0.0
if minBaseMode == "None"
f := 0.0
else if minBaseMode == "ATR"
f := atrBase
else
f := (not na(range30) ? range30 * (minBasePct30m / 100.0) : 0.0)
f

f_baseEff(entry) =>
float b = useTrendProjection30m ? projMove : math.max(f_base_fallback(entry), f_floor_fallback())
b

// =====================
// Box helper (bar_index anchored; scales/pans/zooms correctly)
// =====================
f_make_box(_box, _leftBar, _rightBar, _top, _bot, _bg) =>
box _b = _box
if na(_b)
_b := box.new(left=_leftBar, right=_rightBar, top=_top, bottom=_bot, xloc=xloc.bar_index,
bgcolor=_bg, border_color=boxBorderColor)
else
box.set_left(_b, _leftBar)
box.set_right(_b, _rightBar)
box.set_top(_b, _top)
box.set_bottom(_b, _bot)
_b

// =====================
// Trade state + boxes
// =====================
var int dir = 0
var bool active = false
var float entryPx = na
var float slPx = na
var float tpPx = na
var int entryBarIndex = na

var box riskBox = na
var box rewardBox = na

if newDay
dir := 0
active := false
entryPx := na
slPx := na
tpPx := na
entryBarIndex := na
riskBox := na
rewardBox := na

// Signals:
// - If Two-Stage is ON: entry only on confirmLong/confirmShort
// - If Two-Stage is OFF: old behavior (enter on reclaim)
longSignal = enableLong and not active and (useTwoStageEntry ? confirmLong : longReclaim)
shortSignal = enableShort and not active and (useTwoStageEntry ? confirmShort : shortReclaim)

// Once confirmation happens (or if we entered), clear pending setup
if (confirmLong or confirmShort) and useTwoStageEntry
hasSetup := false
setupDir := 0
setupHi := na
setupLo := na

if longSignal
dir := 1
active := true
entryPx := close
entryBarIndex := bar_index

baseEff = f_baseEff(entryPx) * boxSizeMult
slPx := entryPx - baseEff * (slPct / 100.0)
tpPx := entryPx + baseEff * (tpPct / 100.0)

armedLong := false
longBroken := false
longTouched := false

riskBox := na
rewardBox := na
riskBox := f_make_box(riskBox, entryBarIndex, bar_index, entryPx, slPx, riskBoxColor)
rewardBox := f_make_box(rewardBox, entryBarIndex, bar_index, tpPx, entryPx, rewardBoxColor)

if showEntryLabel
label.new(bar_index, low, "LONG\nEntry", style=label.style_label_up, textcolor=color.white)

if shortSignal
dir := -1
active := true
entryPx := close
entryBarIndex := bar_index

baseEff = f_baseEff(entryPx) * boxSizeMult
slPx := entryPx + baseEff * (slPct / 100.0)
tpPx := entryPx - baseEff * (tpPct / 100.0)

armedShort := false
shortBroken := false
shortTouched := false

riskBox := na
rewardBox := na
riskBox := f_make_box(riskBox, entryBarIndex, bar_index, slPx, entryPx, riskBoxColor)
rewardBox := f_make_box(rewardBox, entryBarIndex, bar_index, entryPx, tpPx, rewardBoxColor)

if showEntryLabel
label.new(bar_index, high, "SHORT\nEntry", style=label.style_label_down, textcolor=color.white)

// Extend boxes while active
if active and not na(entryBarIndex)
if dir == 1
riskBox := f_make_box(riskBox, entryBarIndex, bar_index, entryPx, slPx, riskBoxColor)
rewardBox := f_make_box(rewardBox, entryBarIndex, bar_index, tpPx, entryPx, rewardBoxColor)
else if dir == -1
riskBox := f_make_box(riskBox, entryBarIndex, bar_index, slPx, entryPx, riskBoxColor)
rewardBox := f_make_box(rewardBox, entryBarIndex, bar_index, entryPx, tpPx, rewardBoxColor)

// Exit detection only AFTER entry bar
canExit = active and not na(entryBarIndex) and bar_index > entryBarIndex

hitTP = canExit and ((dir == 1 and high >= tpPx) or (dir == -1 and low <= tpPx))
hitSL = canExit and ((dir == 1 and low <= slPx) or (dir == -1 and high >= slPx))

exitNow = hitTP or hitSL
exitIsSL = hitSL

if exitNow
if showExitLabel
label.new(bar_index, exitIsSL ? slPx : tpPx, exitIsSL ? "EXIT\nSL" : "EXIT\nTP",
style=label.style_label_left, textcolor=color.white)

// Freeze boxes at this bar and leave them on chart
if not na(riskBox)
box.set_right(riskBox, bar_index)
if not na(rewardBox)
box.set_right(rewardBox, bar_index)

// Clear trade state (boxes remain)
dir := 0
active := false
entryPx := na
slPx := na
tpPx := na
entryBarIndex := na
riskBox := na
rewardBox := na

Aviso legal

As informações e publicações não se destinam a ser, e não constituem, conselhos ou recomendações financeiras, de investimento, comerciais ou de outro tipo fornecidos ou endossados pela TradingView. Leia mais nos Termos de Uso.