//@version=6
// ============================================================================
// CoilForge Zones v1.2.2 [WavesUnchained]
// Version: 1.3.1
// Author: WavesUnchained
// Build: 2026-10-12
// ============================================================================
// Detects price compression zones using ATR, ADX, volume, impulse, and
// historical S/R context, then tracks confirmed breakouts for a configurable
// window after the zone ends.
//
// Features:
// ✓ Multi-module compression scoring (ATR, ADX, volume, impulse, historical S/R)
// ✓ Sensitivity presets (Strict / Balanced / Aggressive / Custom)
// ✓ Dynamic bias detection via touch pressure and EMA structure (ADX = strength only)
// ✓ Zone boxes with quality-based color and directional border
// ✓ Post-zone breakout detection with configurable watch window
// ✓ Historical pivot level reuse detection
// ✓ Alert conditions for breakout up / down
//
// Data Contract:
// Price: REQUIRED chart symbol
// Volume: OPTIONAL real trade volume only — Volume Dry-Up module degrades to neutral
// OI: NO
// Reference: NO
// Verdict: CFD-degraded
//
// ============================================================================
indicator("CoilForge Zones v1.2.2 [WavesUnchained]",
shorttitle = "CFZ v1",
overlay = true,
max_boxes_count = 100,
max_labels_count = 100)
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Inputs
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
groupCore = "Core"
sensitivity = input.string("Balanced", "Sensitivity", options = ["Strict", "Balanced", "Aggressive", "Custom"], group = groupCore,
tooltip = "Preset bundle of range/ATR limit, ADX limit, minimum score and minimum boundary retests. The minimum-retest values of the presets (Strict 3, Balanced 2, Aggressive 2) are a provisional default — not yet validated on data.")
lookback = input.int(16, "Zone Lookback", minval = 8, maxval = 100, group = groupCore)
minZoneBars = input.int(5, "Minimum Zone Bars", minval = 3, maxval = 50, group = groupCore)
customRangeAtrMult = input.float(4.2, "Custom: Max Range / ATR", step = 0.1, group = groupCore)
customAdxMax = input.float(27.0, "Custom: Max ADX", step = 0.5, group = groupCore)
customMinScore = input.int(35, "Custom: Minimum Score", minval = 0, maxval = 100, group = groupCore)
customMinTouches = input.int(2, "Custom: Minimum Boundary Touches", minval = 2, maxval = 20, group = groupCore,
tooltip = "Minimum retests of the range edges (upper + lower). The bar that sets each range extreme is not counted, and each side needs at least one retest. Provisional default — not yet validated on data.")
minStructEvidence = input.int(2, "Minimum Structure Evidence", minval = 1, maxval = 2, group = groupCore,
tooltip = "How many structural conditions (range compression, touch structure) must agree. Lower = more permissive; neither acts as a lone hard veto.")
groupModules = "Modules"
useAtrCompression = input.bool(true, "Use ATR Compression", group = groupModules)
useAdxFilter = input.bool(true, "Use ADX Chop Filter", group = groupModules)
useTouches = input.bool(true, "Use Touch Structure", group = groupModules)
useVolume = input.bool(true, "Use Volume Dry-Up", group = groupModules)
useImpulse = input.bool(true, "Use Impulse-In Context", group = groupModules)
useHistorical = input.bool(true, "Use Historical S/R Reuse", group = groupModules)
groupCalc = "Calculation"
atrLen = input.int(14, "ATR Length", minval = 2, group = groupCalc)
adxLen = input.int(14, "DI Length", minval = 2, group = groupCalc)
adxSmooth = input.int(14, "ADX Smoothing", minval = 2, group = groupCalc)
touchTolAtr = input.float(0.25, "Touch Tolerance / ATR", step = 0.05, group = groupCalc)
volLen = input.int(20, "Volume MA Length", minval = 2, group = groupCalc)
impulseLookback = input.int(12, "Impulse Lookback", minval = 3, maxval = 100, group = groupCalc)
impulseAtrMult = input.float(1.8, "Impulse Strength / ATR", step = 0.1, group = groupCalc)
histLookback = input.int(250, "Historical Pivot Lookback", minval = 50, maxval = 2000, group = groupCalc)
histTolAtr = input.float(0.5, "Historical Level Tolerance / ATR", step = 0.1, group = groupCalc)
pivotLen = input.int(5, "Pivot Length", minval = 2, maxval = 20, group = groupCalc)
groupVisual = "Visuals"
showOldZones = input.bool(true, "Show Old Zones", group = groupVisual)
oldZoneLife = input.int(180, "Old Zone Lifetime Bars", minval = 10, maxval = 2000, group = groupVisual)
maxOldZones = input.int(8, "Max Old Zones", minval = 0, maxval = 50, group = groupVisual)
showLabels = input.bool(true, "Show Zone Label", group = groupVisual)
showTable = input.bool(true, "Show Info Table", group = groupVisual)
colorByRead = input.bool(true, "Color by Interpretation", group = groupVisual)
tablePos = input.string("Top Right", "Table Position", options = ["Top Right", "Top Left", "Bottom Right", "Bottom Left"], group = groupVisual,
tooltip = "Move the table when another indicator's dashboard already occupies the corner.")
groupBreakout = "Breakout"
breakoutWatchBars = input.int(20, "Breakout Watch Bars After Zone", minval = 1, maxval = 200, group = groupBreakout)
breakoutQuality = input.string("Confirmed", "Breakout Quality", options = ["Early", "Confirmed", "Strict"], group = groupBreakout)
avoidContraryRead = input.bool(true, "Avoid Breakouts Against Box Read", group = groupBreakout)
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Sensitivity mapping
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
rangeAtrMult = sensitivity == "Strict" ? 2.4 : sensitivity == "Balanced" ? 4.2 : sensitivity == "Aggressive" ? 5.0 : customRangeAtrMult
adxMax = sensitivity == "Strict" ? 20.0 : sensitivity == "Balanced" ? 27.0 : sensitivity == "Aggressive" ? 32.0 : customAdxMax
minScore = sensitivity == "Strict" ? 50 : sensitivity == "Balanced" ? 35 : sensitivity == "Aggressive" ? 25 : customMinScore
minTouches = sensitivity == "Strict" ? 3 : sensitivity == "Balanced" ? 2 : sensitivity == "Aggressive" ? 2 : customMinTouches
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Base calculations
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
atr = ta.atr(atrLen)
[plusDI, minusDI, adx] = ta.dmi(adxLen, adxSmooth)
rangeHigh = ta.highest(high, lookback)
rangeLow = ta.lowest(low, lookback)
rangeSize = rangeHigh - rangeLow
rangeAtrRatio = atr > 0 ? rangeSize / atr : na
volMa = ta.sma(volume, volLen)
volumeAvailable = (ta.cum(nz(volume)) > 0 and syminfo.volumetype != "tick") and
not na(volume) and volume > 0 and not na(volMa)
volDrying = volumeAvailable and volume < volMa and ta.sma(volume, math.max(3, math.floor(volLen / 2))) < volMa
priorMove = close - close[impulseLookback]
impulseBull = priorMove > atr * impulseAtrMult
impulseBear = priorMove < -atr * impulseAtrMult
impulseDir = impulseBull ? 1 : impulseBear ? -1 : 0
atrMa = ta.sma(atr, lookback)
rangeOk = rangeAtrRatio <= rangeAtrMult
adxOk = adx <= adxMax
touchTol = atr * touchTolAtr
// Price-structure trend basis (direction source). EMA stack on close: fast vs slow
// gives a structural up/down read independent of DI. ADX/DI is reserved as a
// strength/quality weight only, never the direction decider.
structEmaFast = ta.ema(close, int(math.max(2, math.round(lookback / 2))))
structEmaSlow = ta.ema(close, lookback)
structTrendUp = structEmaFast > structEmaSlow
structTrendDown = structEmaFast < structEmaSlow
adxStrong = adx >= 20.0 // ADX as strength confirmation, not direction
// The bar that sets each range extreme is not a retest of it — skip it, so only
// genuine returns to the edge count as touches.
rangeHighOffset = -ta.highestbars(high, lookback)
rangeLowOffset = -ta.lowestbars(low, lookback)
rangeUpperTouches = 0
rangeLowerTouches = 0
for i = 0 to lookback - 1
if i != rangeHighOffset and high[i] >= rangeHigh - touchTol
rangeUpperTouches += 1
if i != rangeLowOffset and low[i] <= rangeLow + touchTol
rangeLowerTouches += 1
touchStructureOk = rangeUpperTouches > 0 and rangeLowerTouches > 0 and rangeUpperTouches + rangeLowerTouches >= minTouches
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Historical levels
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
var float[] histLevels = array.new_float()
var int[] histBars = array.new_int()
ph = ta.pivothigh(high, pivotLen, pivotLen)
pl = ta.pivotlow(low, pivotLen, pivotLen)
if useHistorical and not na(ph)
array.push(histLevels, ph)
array.push(histBars, bar_index - pivotLen)
if useHistorical and not na(pl)
array.push(histLevels, pl)
array.push(histBars, bar_index - pivotLen)
while array.size(histLevels) > 0 and bar_index - array.get(histBars, 0) > histLookback
array.shift(histLevels)
array.shift(histBars)
histNearTop = false
histNearBottom = false
histTol = atr * histTolAtr
// Only levels from before the current range window count as historical reuse —
// a pivot inside the window is the range's own extreme and would match itself.
histCutoffBar = bar_index - lookback + 1
if useHistorical and array.size(histLevels) > 0
for i = 0 to array.size(histLevels) - 1
if array.get(histBars, i) >= histCutoffBar
continue
lvl = array.get(histLevels, i)
if math.abs(lvl - rangeHigh) <= histTol
histNearTop := true
if math.abs(lvl - rangeLow) <= histTol
histNearBottom := true
histReuse = histNearTop or histNearBottom
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Score
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
score = 0.0
if useAtrCompression
atrScore = rangeAtrRatio <= rangeAtrMult ? 25.0 * (1.0 - math.min(rangeAtrRatio / rangeAtrMult, 1.0)) : 0.0
score += atrScore
else
score += 25.0
if useAdxFilter
adxScore = adx <= adxMax ? 15.0 * (1.0 - math.min(adx / adxMax, 1.0)) : 0.0
score += adxScore
else
score += 15.0
if useVolume
score += not volumeAvailable ? 15.0 : volDrying ? 15.0 : 0.0
else
score += 15.0
if useImpulse
score += impulseDir != 0 ? 15.0 : 0.0
else
score += 15.0
if useHistorical
score += histReuse ? 10.0 : 0.0
else
score += 10.0
// Structural evidence: each enabled module contributes a vote rather than acting
// as a lone hard veto. The zone needs enough agreeing structure plus a passing score.
structEvidence = 0
if useAtrCompression and rangeOk
structEvidence += 1
if useTouches and touchStructureOk
structEvidence += 1
activeStructModules = (useAtrCompression ? 1 : 0) + (useTouches ? 1 : 0)
structRequired = math.min(minStructEvidence, math.max(activeStructModules, 1))
structureOk = activeStructModules == 0 or structEvidence >= structRequired
candidate = structureOk and score >= minScore
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Active zone state
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
var bool inZone = false
var int zoneStart = na
var float zoneHigh = na
var float zoneLow = na
var int upperTouches = 0
var int lowerTouches = 0
var string zoneRead = "No active zone"
var box activeBox = na
var label activeLabel = na
var box[] oldBoxes = array.new_box()
var int[] oldBoxEndBars = array.new_int()
var bool armedZone = false
var int armedUntil = na
var float armedHigh = na
var float armedLow = na
var string armedRead = "No active zone"
zoneAge = inZone ? bar_index - zoneStart + 1 : 0
startZone = candidate and not inZone
continueZone = candidate and inZone
endZone = not candidate and inZone
if startZone
inZone := true
zoneStart := bar_index - lookback + 1
zoneHigh := rangeHigh
zoneLow := rangeLow
upperTouches := 0
lowerTouches := 0
activeBox := box.new(
left = zoneStart,
top = zoneHigh,
right = bar_index,
bottom = zoneLow,
bgcolor = color.new(color.gray, 88),
border_color = color.new(color.gray, 35),
border_width = 1)
if continueZone
zoneHigh := math.max(zoneHigh, rangeHigh)
zoneLow := math.min(zoneLow, rangeLow)
if inZone
if high >= zoneHigh - touchTol
upperTouches += 1
if low <= zoneLow + touchTol
lowerTouches += 1
pressureRaw = upperTouches - lowerTouches
// Direction derives from price structure (touch pressure + EMA stack). ADX only
// tightens/relaxes the pressure needed: a strong trend lets weaker structural
// pressure confirm a read, but ADX never sets the direction itself.
pressureNeeded = adxStrong ? 1 : 2
string bias = "Neutral"
zoneRead := "Balance / compression"
if pressureRaw >= 3 or (pressureRaw >= pressureNeeded and structTrendUp)
bias := "Up"
zoneRead := "Bullish pressure"
else if pressureRaw <= -3 or (pressureRaw <= -pressureNeeded and structTrendDown)
bias := "Down"
zoneRead := "Bearish pressure"
else if impulseDir == 1
bias := "Up / Continuation"
zoneRead := "Bullish continuation"
else if impulseDir == -1
bias := "Down / Continuation"
zoneRead := "Bearish continuation"
color readColor = color.gray
if zoneRead == "Bullish pressure"
readColor := #00c853
else if zoneRead == "Bearish pressure"
readColor := #d50000
else if zoneRead == "Bullish continuation"
readColor := #66bb6a
else if zoneRead == "Bearish continuation"
readColor := #ef5350
baseFillTransp = score >= 70 ? 82 : score >= 45 ? 88 : 92
continuationRead = zoneRead == "Bullish continuation" or zoneRead == "Bearish continuation"
fillTransp = continuationRead ? math.min(baseFillTransp + 6, 96) : baseFillTransp
color boxColor = colorByRead ? color.new(readColor, fillTransp) : color.new(color.gray, 94)
if not colorByRead
if score >= 70
boxColor := color.new(color.blue, 82)
else if score >= 45
boxColor := color.new(color.gray, 88)
color borderColor = color.new(color.gray, 30)
if colorByRead
borderColor := color.new(readColor, 15)
else
if bias == "Up" or bias == "Up / Continuation"
borderColor := color.new(#00c853, 20)
else if bias == "Down" or bias == "Down / Continuation"
borderColor := color.new(#d50000, 20)
if not na(activeBox)
box.set_right(activeBox, bar_index)
box.set_top(activeBox, zoneHigh)
box.set_bottom(activeBox, zoneLow)
box.set_bgcolor(activeBox, boxColor)
box.set_border_color(activeBox, borderColor)
box.set_border_width(activeBox, score >= 70 ? 2 : 1)
if showLabels
labelText =
"CoilForge Zone\n" +
"Score: " + str.tostring(math.round(score)) + "\n" +
"Read: " + zoneRead + "\n" +
"Bias: " + bias + "\n" +
"Upper Tests: " + str.tostring(upperTouches) + "\n" +
"Lower Tests: " + str.tostring(lowerTouches) + "\n" +
"Hist: " + (histNearTop ? "R " : "") + (histNearBottom ? "S" : "")
if na(activeLabel)
activeLabel := label.new(bar_index, zoneHigh, labelText,
style = label.style_label_down,
textcolor = color.white,
color = color.new(readColor, 15))
else
label.set_x(activeLabel, bar_index)
label.set_y(activeLabel, zoneHigh)
label.set_text(activeLabel, labelText)
label.set_color(activeLabel, color.new(readColor, 15))
if endZone
if zoneAge >= minZoneBars
armedZone := true
armedUntil := bar_index + breakoutWatchBars
armedHigh := zoneHigh
armedLow := zoneLow
armedRead := zoneRead
// Zone edges feed the historical S/R memory regardless of visuals
if useHistorical
array.push(histLevels, zoneHigh)
array.push(histBars, bar_index)
array.push(histLevels, zoneLow)
array.push(histBars, bar_index)
if showOldZones and not na(activeBox)
array.push(oldBoxes, activeBox)
array.push(oldBoxEndBars, bar_index)
else
if not na(activeBox)
box.delete(activeBox)
else
if not na(activeBox)
box.delete(activeBox)
if not na(activeLabel)
label.delete(activeLabel)
activeBox := na
activeLabel := na
inZone := false
zoneStart := na
zoneHigh := na
zoneLow := na
upperTouches := 0
lowerTouches := 0
zoneRead := "No active zone"
while array.size(oldBoxes) > maxOldZones
old = array.shift(oldBoxes)
array.shift(oldBoxEndBars)
box.delete(old)
if showOldZones and array.size(oldBoxes) > 0
for i = array.size(oldBoxes) - 1 to 0
oldEnd = array.get(oldBoxEndBars, i)
if bar_index - oldEnd > oldZoneLife
oldBox = array.get(oldBoxes, i)
box.delete(oldBox)
array.remove(oldBoxes, i)
array.remove(oldBoxEndBars, i)
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Breakout detection (armed zone)
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
if armedZone and bar_index > armedUntil
armedZone := false
armedUntil := na
armedHigh := na
armedLow := na
armedRead := "No active zone"
breakoutVolumeOk = not useVolume or not volumeAvailable or volume > volMa
breakoutBuffer = atr * (breakoutQuality == "Early" ? 0.0 : breakoutQuality == "Confirmed" ? 0.05 : 0.12)
minBodyAtr = breakoutQuality == "Early" ? 0.0 : breakoutQuality == "Confirmed" ? 0.12 : 0.25
minClosePosition = breakoutQuality == "Early" ? 0.0 : breakoutQuality == "Confirmed" ? 0.60 : 0.70
barRange = high - low
bodySize = math.abs(close - open)
closePosition = barRange > 0 ? (close - low) / barRange : 0.5
bodyOk = atr > 0 ? bodySize >= atr * minBodyAtr : true
closeUpOk = closePosition >= minClosePosition
closeDownOk = closePosition <= 1.0 - minClosePosition
readUpOk = not avoidContraryRead or (armedRead != "Bearish pressure" and armedRead != "Bearish continuation")
readDownOk = not avoidContraryRead or (armedRead != "Bullish pressure" and armedRead != "Bullish continuation")
// Breakout direction is set by price structure (close beyond the zone edge). ADX/DI
// is no longer a direction veto here — it only flavors the read elsewhere.
breakoutUp = armedZone and close > armedHigh + breakoutBuffer and breakoutVolumeOk and bodyOk and closeUpOk and readUpOk
breakoutDown = armedZone and close < armedLow - breakoutBuffer and breakoutVolumeOk and bodyOk and closeDownOk and readDownOk
if breakoutUp or breakoutDown
armedZone := false
armedUntil := na
armedHigh := na
armedLow := na
armedRead := "No active zone"
plotshape(breakoutUp, title = "Breakout Up", style = shape.triangleup, location = location.belowbar, size = size.small, color = #00c853, text = "BO↑")
plotshape(breakoutDown, title = "Breakout Down", style = shape.triangledown, location = location.abovebar, size = size.small, color = #d50000, text = "BO↓")
bool alertsOnBarClose = input.bool(true, "Alerts only on bar close (confirmed)", group="Alerts")
bool alertOk = not alertsOnBarClose or barstate.isconfirmed
alertcondition(alertOk and (breakoutUp), "CoilForge Breakout Up", "CFZ · BREAKOUT UP LONG · {{ticker}} {{interval}}")
alertcondition(alertOk and (breakoutDown), "CoilForge Breakout Down", "CFZ · BREAKOUT DOWN SHORT · {{ticker}} {{interval}}")
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Info table
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
color tc = color.new(color.gray, 20)
color hd = color.new(color.gray, 90)
var table info = table.new(
tablePos == "Top Right" ? position.top_right : tablePos == "Top Left" ? position.top_left : tablePos == "Bottom Right" ? position.bottom_right : position.bottom_left,
2, 9,
bgcolor = color.new(color.white, 5),
border_color = color.new(color.gray, 60),
border_width = 1,
frame_color = color.new(color.gray, 40),
frame_width = 1)
if showTable and barstate.islast
string currentRead = zoneRead
if not inZone
currentRead := "No active zone"
armedStr = not armedZone ? "–" : "Armed (" + str.tostring(armedUntil - bar_index) + ") " + armedRead
table.cell(info, 0, 0, "CoilForge Zones", text_color = tc, bgcolor = hd, text_size = size.small)
table.cell(info, 1, 0, "v1.2.2", text_color = tc, bgcolor = hd, text_size = size.small)
table.cell(info, 0, 1, "Zone", text_color = tc, text_size = size.small)
table.cell(info, 1, 1, inZone ? "Active" : "None", text_color = tc, text_size = size.small)
table.cell(info, 0, 2, "Score", text_color = tc, text_size = size.small)
table.cell(info, 1, 2, str.tostring(math.round(score)), text_color = tc, text_size = size.small)
table.cell(info, 0, 3, "Read", text_color = tc, text_size = size.small)
table.cell(info, 1, 3, currentRead, text_color = tc, text_size = size.small)
table.cell(info, 0, 4, "Range/ATR", text_color = tc, text_size = size.small)
table.cell(info, 1, 4, str.tostring(rangeAtrRatio, "#.##"), text_color = tc, text_size = size.small)
table.cell(info, 0, 5, "ADX", text_color = tc, text_size = size.small)
table.cell(info, 1, 5, str.tostring(adx, "#.##"), text_color = tc, text_size = size.small)
table.cell(info, 0, 6, "Volume", text_color = tc, text_size = size.small)
table.cell(info, 1, 6, not volumeAvailable ? "Unavailable" : volDrying ? "Drying" : "Normal/High", text_color = tc, text_size = size.small)
table.cell(info, 0, 7, "Historical", text_color = tc, text_size = size.small)
table.cell(info, 1, 7, histReuse ? "Reused Level" : "No", text_color = tc, text_size = size.small)
table.cell(info, 0, 8, "BO Watch", text_color = tc, text_size = size.small)
table.cell(info, 1, 8, armedStr, text_color = tc, text_size = size.small)
Overview
CoilForge Zones [WavesUnchained] measures ATR compression, ADX chop levels, boundary touches, volume dry-up, and historical pivot levels to identify consolidation areas and signal potential breakouts. Key settings control sensitivity presets, lookback length, minimum touch thresholds, and display elements including boxes and info tables. The volume module degrades to neutral on instruments lacking real trade volume.
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Sensitivity
string
Balanced
sensitivity
Zone Lookback
int
16
lookback
Minimum Zone Bars
int
5
minZoneBars
Custom: Max Range / ATR
float
4.2
customRangeAtrMult
Custom: Max ADX
float
27.0
customAdxMax
Custom: Minimum Score
int
35
customMinScore
Custom: Minimum Boundary Touches
int
2
customMinTouches
Minimum Structure Evidence
int
2
minStructEvidence
Use ATR Compression
bool
true
useAtrCompression
Use ADX Chop Filter
bool
true
useAdxFilter
Use Touch Structure
bool
true
useTouches
Use Volume Dry-Up
bool
true
useVolume
Show all 35 settings
Setting
Type
Default
Variable
Use Impulse-In Context
bool
true
useImpulse
Use Historical S/R Reuse
bool
true
useHistorical
ATR Length
int
14
atrLen
DI Length
int
14
adxLen
ADX Smoothing
int
14
adxSmooth
Touch Tolerance / ATR
float
0.25
touchTolAtr
Volume MA Length
int
20
volLen
Impulse Lookback
int
12
impulseLookback
Impulse Strength / ATR
float
1.8
impulseAtrMult
Historical Pivot Lookback
int
250
histLookback
Historical Level Tolerance / ATR
float
0.5
histTolAtr
Pivot Length
int
5
pivotLen
Show Old Zones
bool
true
showOldZones
Old Zone Lifetime Bars
int
180
oldZoneLife
Max Old Zones
int
8
maxOldZones
Show Zone Label
bool
true
showLabels
Show Info Table
bool
true
showTable
Color by Interpretation
bool
true
colorByRead
Table Position
string
Top Right
tablePos
Breakout Watch Bars After Zone
int
20
breakoutWatchBars
Breakout Quality
string
Confirmed
breakoutQuality
Avoid Breakouts Against Box Read
bool
true
avoidContraryRead
Alerts only on bar close (confirmed)
bool
true
alertsOnBarClose
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.
This indicator draws a daily-anchored volume-weighted average price on the price chart and marks the first long or short reclaim of that level. A long needs a bar below the average whose high reaches it, a later close above it, and confirmation after at least
This indicator plots a custom Anchored Volume-Weighted Average Price (VWAP) directly on the price chart along with optional standard deviation bands. Users can anchor the VWAP to standard time periods like sessions, weeks, or months, or to corporate events suc
VWAP - Basic plots an anchored Volume Weighted Average Price directly on the main price chart alongside two standard deviation bands. It calculates typical price weighted by volume, resetting the cumulative calculations at user-selected session, weekly, or mon
This indicator calculates and plots Donchian Channels directly on the price chart. It determines the highest high and lowest low over a user-selected lookback period using an optimized monotonic deque algorithm, as well as a middle basis line computed as their
This indicator plots daily, weekly, and monthly volume-weighted average prices on the price chart, using a selectable source that defaults to hlc3. For each enabled period it also draws two standard-deviation bands around the VWAP, with multipliers defaulting
The Donchian Channels indicator plots the middle basis line as the average of the high and low over the user-defined period. It also draws the upper band as the highest high and the lower band as the lowest low across the same period on the price chart. The ma