An indicator on the price chart that detects pivot-based structure breaks (CHoCH) and then looks for a retest of the broken level, using an ATR percentage tolerance.
// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0.
//@version=6
indicator("Structure Retest Engine Delta Hybrid", shorttitle = "SRE-DH", overlay = true, max_labels_count = 500, max_lines_count = 500, max_boxes_count = 500)
// ┌───────────────────────────── Inputs ─────────────────────────────┐
// └──────────────────────────────────────────────────────────────────┘
BULL_COLOR = #089981
BEAR_COLOR = #f23645
GRP_MODEL = "Model Settings"
GRP_DELTA = "Delta Analysis"
GRP_FILTERS = "Retest Filters"
GRP_VISUALS = "Visuals"
GRP_ALERTS = "Alerts"
pivotLen = input.int(5, "Pivot Length", minval = 1, group = GRP_MODEL, tooltip = "Bars required on each side to confirm a swing point.")
sensitivity = input.float(0.5, "Retest Sensitivity (ATR %)", minval = 0.05, step = 0.05, group = GRP_MODEL, tooltip = "Defines how close price must come to the level to trigger a retest. Lower values require a more precise touch.")
latestOnly = input.bool(true, "Show Latest Only", group = GRP_MODEL, tooltip = "Deletes previous structure drawings when a new one forms.")
allowReEntry = input.bool(false, "Allow Re-Entries In Same Zone", group = GRP_MODEL, tooltip = "If disabled, a level is consumed after the first valid entry.")
useDeltaAnalysis = input.bool(true, "Enable Delta Analysis", group = GRP_DELTA, tooltip = "Uses a relative volume delta proxy to judge breakout conviction and retest sentiment.")
deltaMaLen = input.int(20, "Delta MA Period", minval = 1, group = GRP_DELTA)
deltaThresh = input.float(1.5, "Run Away Threshold (xMA)", minval = 0.1, step = 0.1, group = GRP_DELTA, tooltip = "Above this threshold, breakout delta is treated as high conviction.")
showDeltaText = input.bool(true, "Show Delta Insight In Entry Text", group = GRP_DELTA)
showDeltaOnCH = input.bool(true, "Show Strong/Weak On CHoCH Label", group = GRP_DELTA)
confirmMode = input.string("Engulfing", "Confirmation Mode", options = ["Touch", "Close Outside Level", "Engulfing"], group = GRP_FILTERS, tooltip = "Touch = immediate reaction. Close Outside Level = candle returns to level then closes back on the trend side. Engulfing = reversal candle confirmation.")
volMaLen = input.int(20, "Volume MA Length", minval = 1, group = GRP_FILTERS, tooltip = "Period for the Volume Moving Average filter.")
volThresh = input.float(1.30, "Volume Threshold", minval = 0.1, step = 0.1, group = GRP_FILTERS, tooltip = "Required volume relative to its MA to confirm a retest.")
requireVol = input.bool(false, "Require Volume Filter", group = GRP_FILTERS, tooltip = "If enabled, confirmation also requires volume above the selected threshold.")
requireCandle = input.bool(false, "Require Directional Candle", group = GRP_FILTERS, tooltip = "Bullish entries require a green candle; bearish entries require a red candle.")
bullCol = input.color(BULL_COLOR, "Bullish", inline = "col", group = GRP_VISUALS)
bearCol = input.color(BEAR_COLOR, "Bearish", inline = "col", group = GRP_VISUALS)
showStructureLabel = input.bool(true, "Show CHoCH Labels", group = GRP_VISUALS)
compactLabels = input.bool(true, "Compact Labels", group = GRP_VISUALS, tooltip = "Uses CH instead of CHoCH for cleaner charts.")
showEntryLabels = input.bool(true, "Show Entry Text", group = GRP_VISUALS)
colorBarsByTrend = input.bool(true, "Color Bars By Trend", group = GRP_VISUALS)
fadeUsedZone = input.bool(true, "Fade Used Levels", group = GRP_VISUALS, tooltip = "Increases transparency of levels once an entry is triggered.")
zoneTransparency = input.int(85, "Active Transparency", minval = 0, maxval = 100, group = GRP_VISUALS)
usedTransparency = input.int(95, "Used Transparency", minval = 0, maxval = 100, group = GRP_VISUALS)
entrySizeOpt = input.string("Tiny", "Entry Triangle Size", options = ["Tiny", "Small", "Normal"], group = GRP_VISUALS)
showBreakLevelLine = input.bool(true, "Show Break Level Line", group = GRP_VISUALS)
showChochTriangle = input.bool(true, "Show CHoCH Triangle", group = GRP_VISUALS)
chochTriSizeOpt = input.string("Tiny", "CHoCH Triangle Size", options = ["Tiny", "Small", "Normal"], group = GRP_VISUALS)
showEntryTriangle = input.bool(true, "Show Entry Triangle", group = GRP_VISUALS)
entryTextOffsetAtr = input.float(0.60, "Entry Text Offset (ATR)", minval = 0.10, step = 0.05, group = GRP_VISUALS, tooltip = "Moves entry text farther away from the candle so it stays readable.")
entryTextSizeOpt = input.string("Tiny", "Entry Text Size", options = ["Tiny", "Small", "Normal"], group = GRP_VISUALS)
chochTextSizeOpt = input.string("Small", "CHoCH Text Size", options = ["Tiny", "Small", "Normal"], group = GRP_VISUALS)
alertsOnCHoCH = input.bool(true, "Include CHoCH In Alert", group = GRP_ALERTS)
alertsOnBullEntry = input.bool(true, "Include Bull Entry In Alert", group = GRP_ALERTS)
alertsOnBearEntry = input.bool(true, "Include Bear Entry In Alert", group = GRP_ALERTS)
// ┌───────────────────────────── Helpers ────────────────────────────┐
// └──────────────────────────────────────────────────────────────────┘
f_entryText(_mode) =>
_mode == "Touch" ? "TOUCH" : _mode == "Close Outside Level" ? "COL" : "ENG"
// Relative volume delta proxy. This is a candle/volume efficiency estimate, not true bid/ask delta.
f_getDelta() =>
float _range = high - low
_range == 0 ? 0.0 : ((close - open) / _range) * volume
f_deltaTag(_breakStrong, _supportsTrend) =>
_breakStrong and _supportsTrend ? " ⚡" : not _breakStrong and not _supportsTrend ? " ?" : " ~"
f_labelSize(_size) =>
_size == "Tiny" ? size.tiny : _size == "Small" ? size.small : size.normal
// ┌───────────────────────────── Delta ──────────────────────────────┐
// └──────────────────────────────────────────────────────────────────┘
float candleDelta = f_getDelta()
float absDelta = math.abs(candleDelta)
float deltaMa = ta.sma(absDelta, deltaMaLen)
// ┌───────────────────────────── State ──────────────────────────────┐
// └──────────────────────────────────────────────────────────────────┘
var float lastPH = na
var float lastPL = na
var int lastPHIndex = na
var int lastPLIndex = na
var int trendState = 0
var line activeLevelLine = na
var line activeBreakLine = na
var box activeRetestBox = na
var label activeStructLbl = na
var float activeLevel = na
var int activeDir = 0
var int activeStartIdx = na
var bool levelConsumed = false
var bool hasLeftLevel = false
var int lastCHoCHBar = -1
var bool wasInBandPrev = false
var bool entryTriggeredThisTouch = false
var int retestCount = 0
var float breakoutDelta = 0.0
var bool breakoutStrong = false
var float retestCvd = 0.0
// ┌───────────────────────────── Pivot Detection ───────────────────┐
// └─────────────────────────────────────────────────────────────────┘
float ph = ta.pivothigh(high, pivotLen, pivotLen)
float pl = ta.pivotlow(low, pivotLen, pivotLen)
if not na(ph)
lastPH := ph
lastPHIndex := bar_index - pivotLen
if not na(pl)
lastPL := pl
lastPLIndex := bar_index - pivotLen
// ┌───────────────────────────── Structure Logic ───────────────────┐
// └─────────────────────────────────────────────────────────────────┘
bool bullishBreak = not na(lastPH) and ta.crossover(close, lastPH)
bool bearishBreak = not na(lastPL) and ta.crossunder(close, lastPL)
bool isCHoCH = (bullishBreak and trendState == -1) or (bearishBreak and trendState == 1)
if bullishBreak
trendState := 1
else if bearishBreak
trendState := -1
float atr = ta.atr(14)
float buffer = atr * sensitivity
float entryLabelOffset = atr * entryTextOffsetAtr
if isCHoCH
if latestOnly
if not na(activeLevelLine)
line.delete(activeLevelLine)
if not na(activeRetestBox)
box.delete(activeRetestBox)
if not na(activeStructLbl)
label.delete(activeStructLbl)
if not na(activeBreakLine)
line.delete(activeBreakLine)
activeLevel := bullishBreak ? lastPH : lastPL
activeDir := bullishBreak ? 1 : -1
activeStartIdx := activeDir == 1 ? lastPHIndex : lastPLIndex
levelConsumed := false
hasLeftLevel := false
lastCHoCHBar := bar_index
wasInBandPrev := false
entryTriggeredThisTouch := false
retestCount := 0
breakoutDelta := candleDelta
breakoutStrong := useDeltaAnalysis ? math.abs(breakoutDelta) > deltaMa * deltaThresh : false
retestCvd := 0.0
color themeCol = activeDir == 1 ? bullCol : bearCol
string baseChText = compactLabels ? "CH" : "CHoCH"
string strengthTxt = showDeltaOnCH and useDeltaAnalysis ? (breakoutStrong ? "+" : "-") : ""
activeLevelLine := line.new(
bar_index, activeLevel,
bar_index, activeLevel,
color = themeCol,
style = line.style_solid,
width = 2
)
activeRetestBox := box.new(
bar_index, activeLevel + buffer,
bar_index, activeLevel - buffer,
bgcolor = color.new(themeCol, zoneTransparency),
border_color = color.new(themeCol, 100),
border_style = line.style_solid,
border_width = 0
)
if showBreakLevelLine and not na(activeStartIdx)
activeBreakLine := line.new(
activeStartIdx, activeLevel,
bar_index, activeLevel,
color = color.new(themeCol, 50),
style = line.style_dashed,
width = 1
)
if showStructureLabel and not na(activeStartIdx)
int midX = math.round((activeStartIdx + bar_index) / 2)
activeStructLbl := label.new(
midX, activeLevel,
baseChText + strengthTxt,
style = label.style_none,
color = #00000000,
textcolor = themeCol,
size = f_labelSize(chochTextSizeOpt)
)
// ┌───────────────────────────── Retest Logic ──────────────────────┐
// └─────────────────────────────────────────────────────────────────┘
if not na(activeLevelLine)
line.set_x2(activeLevelLine, bar_index)
if not na(activeRetestBox)
box.set_right(activeRetestBox, bar_index)
box.set_top(activeRetestBox, activeLevel + buffer)
box.set_bottom(activeRetestBox, activeLevel - buffer)
float volMa = ta.sma(volume, volMaLen)
bool volumeOK = not requireVol or volume > volMa * volThresh
bool priceAtLevel = false
if not na(activeLevel)
priceAtLevel := low <= activeLevel + buffer and high >= activeLevel - buffer
if useDeltaAnalysis and priceAtLevel
retestCvd += candleDelta
if not hasLeftLevel and bar_index > lastCHoCHBar
if (activeDir == 1 and low > activeLevel + buffer) or (activeDir == -1 and high < activeLevel - buffer)
hasLeftLevel := true
// ┌───────────────────────────── Entry Logic ───────────────────────┐
// └─────────────────────────────────────────────────────────────────┘
bool respectsLevel = activeDir == 1 ? close > activeLevel : close < activeLevel
bool bullCandleOK = not requireCandle or close > open
bool bearCandleOK = not requireCandle or close < open
bool validEntryBar = bar_index > lastCHoCHBar and hasLeftLevel
bool leftBandNow = not priceAtLevel and wasInBandPrev
if leftBandNow
entryTriggeredThisTouch := false
bool touchCycleActive = validEntryBar and priceAtLevel and not entryTriggeredThisTouch
bool reentryAllowed = allowReEntry or retestCount == 0
bool bullTouchConfirm = activeDir == 1 and touchCycleActive and reentryAllowed and respectsLevel and bullCandleOK and volumeOK
bool bearTouchConfirm = activeDir == -1 and touchCycleActive and reentryAllowed and respectsLevel and bearCandleOK and volumeOK
bool bullCOLConfirm = activeDir == 1 and touchCycleActive and reentryAllowed and close > activeLevel + buffer and bullCandleOK and volumeOK
bool bearCOLConfirm = activeDir == -1 and touchCycleActive and reentryAllowed and close < activeLevel - buffer and bearCandleOK and volumeOK
bool bullEngulfConfirm = activeDir == 1 and touchCycleActive and reentryAllowed and close > open and close >= high[1] and open <= close[1] and volumeOK
bool bearEngulfConfirm = activeDir == -1 and touchCycleActive and reentryAllowed and close < open and close <= low[1] and open >= close[1] and volumeOK
bool bullSelectedConfirm = switch confirmMode
"Touch" => bullTouchConfirm
"Close Outside Level" => bullCOLConfirm
=> bullEngulfConfirm
bool bearSelectedConfirm = switch confirmMode
"Touch" => bearTouchConfirm
"Close Outside Level" => bearCOLConfirm
=> bearEngulfConfirm
bool bullEntry = not levelConsumed and bullSelectedConfirm
bool bearEntry = not levelConsumed and bearSelectedConfirm
if bullEntry or bearEntry
entryTriggeredThisTouch := true
retestCount += 1
if not na(activeRetestBox) and fadeUsedZone
box.set_bgcolor(activeRetestBox, color.new(activeDir == 1 ? bullCol : bearCol, usedTransparency))
if not allowReEntry
levelConsumed := true
if showEntryLabels
bool deltaSupportsTrend = activeDir == 1 ? retestCvd > 0 : retestCvd < 0
string deltaTag = useDeltaAnalysis and showDeltaText ? f_deltaTag(breakoutStrong, deltaSupportsTrend) : ""
label.new(
bar_index,
activeDir == 1 ? low - entryLabelOffset : high + entryLabelOffset,
f_entryText(confirmMode) + deltaTag,
style = label.style_none,
color = #00000000,
textcolor = activeDir == 1 ? bullCol : bearCol,
size = f_labelSize(entryTextSizeOpt)
)
wasInBandPrev := priceAtLevel
// ┌───────────────────────────── Alerts ────────────────────────────┐
// └─────────────────────────────────────────────────────────────────┘
bool fireCHoCH = alertsOnCHoCH and isCHoCH
bool fireBullEntry = alertsOnBullEntry and bullEntry
bool fireBearEntry = alertsOnBearEntry and bearEntry
if fireCHoCH or fireBullEntry or fireBearEntry
alert("Structure Retest Signal: " + (fireCHoCH ? "CHoCH" : fireBullEntry ? "Bull Entry" : "Bear Entry"), alert.freq_once_per_bar_close)
// ┌───────────────────────────── Visuals ───────────────────────────┐
// └─────────────────────────────────────────────────────────────────┘
plotshape(showChochTriangle and bullishBreak and trendState == 1 and isCHoCH and chochTriSizeOpt == "Tiny", title = "Bull CHoCH Tiny", style = shape.triangleup, location = location.belowbar, color = bullCol, size = size.tiny)
plotshape(showChochTriangle and bullishBreak and trendState == 1 and isCHoCH and chochTriSizeOpt == "Small", title = "Bull CHoCH Small", style = shape.triangleup, location = location.belowbar, color = bullCol, size = size.small)
plotshape(showChochTriangle and bullishBreak and trendState == 1 and isCHoCH and chochTriSizeOpt == "Normal", title = "Bull CHoCH Normal", style = shape.triangleup, location = location.belowbar, color = bullCol, size = size.normal)
plotshape(showChochTriangle and bearishBreak and trendState == -1 and isCHoCH and chochTriSizeOpt == "Tiny", title = "Bear CHoCH Tiny", style = shape.triangledown, location = location.abovebar, color = bearCol, size = size.tiny)
plotshape(showChochTriangle and bearishBreak and trendState == -1 and isCHoCH and chochTriSizeOpt == "Small", title = "Bear CHoCH Small", style = shape.triangledown, location = location.abovebar, color = bearCol, size = size.small)
plotshape(showChochTriangle and bearishBreak and trendState == -1 and isCHoCH and chochTriSizeOpt == "Normal", title = "Bear CHoCH Normal", style = shape.triangledown, location = location.abovebar, color = bearCol, size = size.normal)
plotshape(showEntryTriangle and bullEntry and entrySizeOpt == "Tiny", title = "Bull Entry Tiny", style = shape.triangleup, location = location.belowbar, color = bullCol, size = size.tiny)
plotshape(showEntryTriangle and bullEntry and entrySizeOpt == "Small", title = "Bull Entry Small", style = shape.triangleup, location = location.belowbar, color = bullCol, size = size.small)
plotshape(showEntryTriangle and bullEntry and entrySizeOpt == "Normal", title = "Bull Entry Normal", style = shape.triangleup, location = location.belowbar, color = bullCol, size = size.normal)
plotshape(showEntryTriangle and bearEntry and entrySizeOpt == "Tiny", title = "Bear Entry Tiny", style = shape.triangledown, location = location.abovebar, color = bearCol, size = size.tiny)
plotshape(showEntryTriangle and bearEntry and entrySizeOpt == "Small", title = "Bear Entry Small", style = shape.triangledown, location = location.abovebar, color = bearCol, size = size.small)
plotshape(showEntryTriangle and bearEntry and entrySizeOpt == "Normal", title = "Bear Entry Normal", style = shape.triangledown, location = location.abovebar, color = bearCol, size = size.normal)
barcolor(colorBarsByTrend ? (trendState == 1 ? bullCol : trendState == -1 ? bearCol : na) : na)
Overview
Confirmation can be a touch, a close outside the level or an engulfing candle, with optional volume and directional candle requirements. A relative volume delta proxy rates breakout conviction and retest sentiment. Settings control showing only the latest structure, re-entries in the same zone, labels, triangles, bar colouring and alerts.
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Pivot Length
int
5
pivotLen
Retest Sensitivity (ATR %)
float
0.5
sensitivity
Show Latest Only
bool
true
latestOnly
Allow Re-Entries In Same Zone
bool
false
allowReEntry
Enable Delta Analysis
bool
true
useDeltaAnalysis
Delta MA Period
int
20
deltaMaLen
Run Away Threshold (xMA)
float
1.5
deltaThresh
Show Delta Insight In Entry Text
bool
true
showDeltaText
Show Strong/Weak On CHoCH Label
bool
true
showDeltaOnCH
Confirmation Mode
string
Engulfing
confirmMode
Volume MA Length
int
20
volMaLen
Volume Threshold
float
1.30
volThresh
Show all 34 settings
Setting
Type
Default
Variable
Require Volume Filter
bool
false
requireVol
Require Directional Candle
bool
false
requireCandle
Bullish
color
BULL_COLOR
bullCol
Bearish
color
BEAR_COLOR
bearCol
Show CHoCH Labels
bool
true
showStructureLabel
Compact Labels
bool
true
compactLabels
Show Entry Text
bool
true
showEntryLabels
Color Bars By Trend
bool
true
colorBarsByTrend
Fade Used Levels
bool
true
fadeUsedZone
Active Transparency
int
85
zoneTransparency
Used Transparency
int
95
usedTransparency
Entry Triangle Size
string
Tiny
entrySizeOpt
Show Break Level Line
bool
true
showBreakLevelLine
Show CHoCH Triangle
bool
true
showChochTriangle
CHoCH Triangle Size
string
Tiny
chochTriSizeOpt
Show Entry Triangle
bool
true
showEntryTriangle
Entry Text Offset (ATR)
float
0.60
entryTextOffsetAtr
Entry Text Size
string
Tiny
entryTextSizeOpt
CHoCH Text Size
string
Small
chochTextSizeOpt
Include CHoCH In Alert
bool
true
alertsOnCHoCH
Include Bull Entry In Alert
bool
true
alertsOnBullEntry
Include Bear Entry In Alert
bool
true
alertsOnBearEntry
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.
The indicator detects liquidity sweeps across key price levels and calculates a cumulative volume delta proxy. It uses pivot high and low lookbacks to identify swing points, marking sweeps when price breaks beyond a level and quickly closes back inside. Featur
Calculates higher-timeframe volume profiles to identify Value Area High (VAH), Value Area Low (VAL), and Point of Control (POC) levels directly on the price chart. It flags potential reversal setups and Change in State of Delivery (CISD) patterns relative to v
Copilot is an order flow range-reversal indicator designed to overlay trading setups directly on the price chart. It evaluates four criteria: market context (sideways vs trending regime based on ATR height), structure (double top or double bottom retests), abs
Supply and Demand Visible Range identifies supply, demand, and equilibrium zones across the visible chart range. It accumulates high, low, and volume data starting from the leftmost visible bar, optionally requesting lower timeframe intrabar data for finer res
This script identifies and visualizes bullish and bearish order blocks on the price chart. It detects low-volatility consolidation ranges followed by breakout moves accompanied by high trading volume. Users can select between Simple, Relative, or Weighted volu
This indicator identifies and visualizes key support and resistance heatmap zones directly on the price chart. Using closed one-hour price data, it evaluates multi-scale pivots, liquidity clusters, relative volume, and round-number resonances to score structur