OPEN-SOURCE SCRIPT
Key Box Zone Finder — ATR + EMA200 Filter

//version=5
indicator("Key Box Zone Finder — ATR + EMA200 Filter", overlay=true, max_lines_count=500, max_boxes_count=100, max_labels_count=500)
// ---------- Inputs ----------
len = input.int(60, "Box Length (bars)", minval=10)
maxWidthPct = input.float(0.6, "Max Box Width %", step=0.05)
edgeTolPct = input.float(0.08, "Edge tolerance %", step=0.01)
bins = input.int(8, "Bins inside box", minval=3, maxval=20)
extendBars = input.int(120, "Extend right (bars)", minval=0)
showTouches = input.bool(true, "Show edge touches")
showSubZone = input.bool(true, "Show densest sub-zone")
boxColor = input.color(color.new(color.teal, 85), "Box fill")
subBoxColor = input.color(color.new(color.orange, 75), "Sub-zone fill")
edgeLineColor = input.color(color.new(color.teal, 0), "Edge line color")
midLineColor = input.color(color.new(color.gray, 0), "Mid line color")
// فیلترها
atrLen = input.int(14, "ATR Length", minval=1)
atrMaxMult = input.float(0.5, "ATR Max % of Close", step=0.01)
emaLen = input.int(200, "EMA Length", minval=1)
emaDistancePct = input.float(0.5, "Max Distance % from EMA", step=0.1)
// ---------- Core calc ----------
hi = ta.highest(high, len)
lo = ta.lowest(low, len)
boxRange = hi - lo
boxRangePct = boxRange / math.max(close, 1e-6) * 100.0
isBoxWidth = boxRangePct <= maxWidthPct
// شمارش برخوردها
edgeTolHi = hi * (1 - edgeTolPct/100.0)
edgeTolLo = lo * (1 + edgeTolPct/100.0)
touchUpper = 0
touchLower = 0
for j = 0 to len - 1
touchUpper += (high[j] >= edgeTolHi) ? 1 : 0
touchLower += (low[j] <= edgeTolLo) ? 1 : 0
// ---------- ATR filter ----------
atrValue = ta.atr(atrLen)
atrCondition = (atrValue / close) * 100 <= atrMaxMult
// ---------- EMA200 filter ----------
emaValue = ta.ema(close, emaLen)
emaCondition = math.abs(close - emaValue) / close * 100 <= emaDistancePct
// ---------- Final filter ----------
isValidBox = isBoxWidth and atrCondition and emaCondition
// ---------- Draw main box ----------
var box bMain = na
var line lTop = na
var line lBot = na
var line lMid = na
var label labU = na
var label labL = na
if barstate.islast and isValidBox
if not na(bMain)
box.delete(bMain)
if not na(lTop)
line.delete(lTop)
if not na(lBot)
line.delete(lBot)
if not na(lMid)
line.delete(lMid)
if not na(labU)
label.delete(labU)
if not na(labL)
label.delete(labL)
left = bar_index - len + 1
right = bar_index + extendBars
bMain := box.new(left, hi, bar_index, lo, bgcolor=boxColor, border_color=edgeLineColor)
box.set_extend(bMain, extend.right)
lTop := line.new(left, hi, right, hi, extend=extend.right, color=edgeLineColor, width=1)
lBot := line.new(left, lo, right, lo, extend=extend.right, color=edgeLineColor, width=1)
mid = (hi + lo) / 2.0
lMid := line.new(left, mid, right, mid, extend=extend.right, color=midLineColor, style=line.style_dotted)
if showTouches
labU := label.new(bar_index, hi, "▲ touches: " + str.tostring(touchUpper), textcolor=color.white, color=color.new(color.teal, 0), style=label.style_label_down, size=size.tiny)
labL := label.new(bar_index, lo, "▼ touches: " + str.tostring(touchLower), textcolor=color.white, color=color.new(color.teal, 0), style=label.style_label_up, size=size.tiny)
// ---------- Densest sub-zone ----------
var box bSub = na
if barstate.islast and isValidBox and showSubZone and boxRange > 0
step = boxRange / bins
maxCount = -1
bestIdx = 0
for i = 0 to bins - 1
binLo = lo + step * i
binHi = binLo + step
cIn = 0
for j = 0 to len - 1
c = close[j]
cIn += (c >= binLo and c <= binHi) ? 1 : 0
if cIn > maxCount
maxCount := cIn
bestIdx := i
subLo = lo + step * bestIdx
subHi = subLo + step
if not na(bSub)
box.delete(bSub)
bSub := box.new(bar_index - len + 1, subHi, bar_index, subLo, bgcolor=subBoxColor, border_color=color.new(color.orange, 0))
box.set_extend(bSub, extend.right)
label.new(bar_index, subHi, "Dense zone", style=label.style_label_down, textcolor=color.white, color=color.new(color.orange, 0), size=size.tiny)
// ---------- Alerts ----------
alertcondition(isValidBox, title="Valid Box Detected", message="Consolidation box detected with ATR & EMA filter")
indicator("Key Box Zone Finder — ATR + EMA200 Filter", overlay=true, max_lines_count=500, max_boxes_count=100, max_labels_count=500)
// ---------- Inputs ----------
len = input.int(60, "Box Length (bars)", minval=10)
maxWidthPct = input.float(0.6, "Max Box Width %", step=0.05)
edgeTolPct = input.float(0.08, "Edge tolerance %", step=0.01)
bins = input.int(8, "Bins inside box", minval=3, maxval=20)
extendBars = input.int(120, "Extend right (bars)", minval=0)
showTouches = input.bool(true, "Show edge touches")
showSubZone = input.bool(true, "Show densest sub-zone")
boxColor = input.color(color.new(color.teal, 85), "Box fill")
subBoxColor = input.color(color.new(color.orange, 75), "Sub-zone fill")
edgeLineColor = input.color(color.new(color.teal, 0), "Edge line color")
midLineColor = input.color(color.new(color.gray, 0), "Mid line color")
// فیلترها
atrLen = input.int(14, "ATR Length", minval=1)
atrMaxMult = input.float(0.5, "ATR Max % of Close", step=0.01)
emaLen = input.int(200, "EMA Length", minval=1)
emaDistancePct = input.float(0.5, "Max Distance % from EMA", step=0.1)
// ---------- Core calc ----------
hi = ta.highest(high, len)
lo = ta.lowest(low, len)
boxRange = hi - lo
boxRangePct = boxRange / math.max(close, 1e-6) * 100.0
isBoxWidth = boxRangePct <= maxWidthPct
// شمارش برخوردها
edgeTolHi = hi * (1 - edgeTolPct/100.0)
edgeTolLo = lo * (1 + edgeTolPct/100.0)
touchUpper = 0
touchLower = 0
for j = 0 to len - 1
touchUpper += (high[j] >= edgeTolHi) ? 1 : 0
touchLower += (low[j] <= edgeTolLo) ? 1 : 0
// ---------- ATR filter ----------
atrValue = ta.atr(atrLen)
atrCondition = (atrValue / close) * 100 <= atrMaxMult
// ---------- EMA200 filter ----------
emaValue = ta.ema(close, emaLen)
emaCondition = math.abs(close - emaValue) / close * 100 <= emaDistancePct
// ---------- Final filter ----------
isValidBox = isBoxWidth and atrCondition and emaCondition
// ---------- Draw main box ----------
var box bMain = na
var line lTop = na
var line lBot = na
var line lMid = na
var label labU = na
var label labL = na
if barstate.islast and isValidBox
if not na(bMain)
box.delete(bMain)
if not na(lTop)
line.delete(lTop)
if not na(lBot)
line.delete(lBot)
if not na(lMid)
line.delete(lMid)
if not na(labU)
label.delete(labU)
if not na(labL)
label.delete(labL)
left = bar_index - len + 1
right = bar_index + extendBars
bMain := box.new(left, hi, bar_index, lo, bgcolor=boxColor, border_color=edgeLineColor)
box.set_extend(bMain, extend.right)
lTop := line.new(left, hi, right, hi, extend=extend.right, color=edgeLineColor, width=1)
lBot := line.new(left, lo, right, lo, extend=extend.right, color=edgeLineColor, width=1)
mid = (hi + lo) / 2.0
lMid := line.new(left, mid, right, mid, extend=extend.right, color=midLineColor, style=line.style_dotted)
if showTouches
labU := label.new(bar_index, hi, "▲ touches: " + str.tostring(touchUpper), textcolor=color.white, color=color.new(color.teal, 0), style=label.style_label_down, size=size.tiny)
labL := label.new(bar_index, lo, "▼ touches: " + str.tostring(touchLower), textcolor=color.white, color=color.new(color.teal, 0), style=label.style_label_up, size=size.tiny)
// ---------- Densest sub-zone ----------
var box bSub = na
if barstate.islast and isValidBox and showSubZone and boxRange > 0
step = boxRange / bins
maxCount = -1
bestIdx = 0
for i = 0 to bins - 1
binLo = lo + step * i
binHi = binLo + step
cIn = 0
for j = 0 to len - 1
c = close[j]
cIn += (c >= binLo and c <= binHi) ? 1 : 0
if cIn > maxCount
maxCount := cIn
bestIdx := i
subLo = lo + step * bestIdx
subHi = subLo + step
if not na(bSub)
box.delete(bSub)
bSub := box.new(bar_index - len + 1, subHi, bar_index, subLo, bgcolor=subBoxColor, border_color=color.new(color.orange, 0))
box.set_extend(bSub, extend.right)
label.new(bar_index, subHi, "Dense zone", style=label.style_label_down, textcolor=color.white, color=color.new(color.orange, 0), size=size.tiny)
// ---------- Alerts ----------
alertcondition(isValidBox, title="Valid Box Detected", message="Consolidation box detected with ATR & EMA filter")
Skrip sumber terbuka
Dalam semangat sebenar TradingView, pencipta skrip ini telah menjadikannya sumber terbuka supaya pedagang dapat menilai dan mengesahkan kefungsiannya. Terima kasih kepada penulis! Walaupun anda boleh menggunakannya secara percuma, ingat bahawa menerbitkan semula kod ini adalah tertakluk kepada Peraturan Dalaman kami.
Penafian
Maklumat dan penerbitan adalah tidak dimaksudkan untuk menjadi, dan tidak membentuk, nasihat untuk kewangan, pelaburan, perdagangan dan jenis-jenis lain atau cadangan yang dibekalkan atau disahkan oleh TradingView. Baca dengan lebih lanjut di Terma Penggunaan.
Skrip sumber terbuka
Dalam semangat sebenar TradingView, pencipta skrip ini telah menjadikannya sumber terbuka supaya pedagang dapat menilai dan mengesahkan kefungsiannya. Terima kasih kepada penulis! Walaupun anda boleh menggunakannya secara percuma, ingat bahawa menerbitkan semula kod ini adalah tertakluk kepada Peraturan Dalaman kami.
Penafian
Maklumat dan penerbitan adalah tidak dimaksudkan untuk menjadi, dan tidak membentuk, nasihat untuk kewangan, pelaburan, perdagangan dan jenis-jenis lain atau cadangan yang dibekalkan atau disahkan oleh TradingView. Baca dengan lebih lanjut di Terma Penggunaan.