This educational example confirms new gaps and fill events at bar close. It processes existing zones before adding new ones, so a new gap cannot fill itself. Filled zones remain muted until the retention cap removes them. The cap includes both active and filled zones, and the drawing starts on the confirmation candle. ATR warmup bars produce no detections. Syntax validation does not establish chart behavior or trading performance.
Complete example: three fill rules, bounded drawings and one event summary per barpinescript
//@version=6
indicator("Pineify FVG rules example", overlay = true, max_boxes_count = 100, max_lines_count = 100)
fillRule = input.string("Midpoint", "Fill rule", options = ["Touch", "Midpoint", "Full fill"])
atrLength = input.int(14, "ATR length", minval = 1)
minAtr = input.float(0.25, "Minimum gap / ATR", minval = 0, step = 0.05)
keepZones = input.int(25, "Retained zones (active and filled)", minval = 1, maxval = 100)
type Gap
box area
line midpointLine
float top
float bottom
bool bullish
bool active
var array<Gap> zones = array.new<Gap>()
float minimumSize = ta.atr(atrLength) * minAtr
bool bullishGap = low > high[2] and low - high[2] >= minimumSize
bool bearishGap = high < low[2] and low[2] - high >= minimumSize
if barstate.isconfirmed
string events = ""
// Only zones from earlier bars are eligible for a fill.
for zone in zones
if zone.active
box.set_right(zone.area, time)
line.set_x2(zone.midpointLine, time)
float midpoint = (zone.top + zone.bottom) * 0.5
float threshold = fillRule == "Midpoint" ? midpoint : fillRule == "Touch" ? (zone.bullish ? zone.top : zone.bottom) : (zone.bullish ? zone.bottom : zone.top)
bool filled = zone.bullish ? low <= threshold : high >= threshold
if filled
zone.active := false
box.set_bgcolor(zone.area, color.new(color.gray, 92))
box.set_border_color(zone.area, color.new(color.gray, 65))
line.set_color(zone.midpointLine, color.new(color.gray, 75))
events += (zone.bullish ? "Bullish" : "Bearish") + " FVG reached " + fillRule + "; "
if bullishGap or bearishGap
// Delete before creating to stay within both drawing budgets.
if array.size(zones) >= keepZones
Gap oldest = array.shift(zones)
box.delete(oldest.area)
line.delete(oldest.midpointLine)
float top = bullishGap ? low : low[2]
float bottom = bullishGap ? high[2] : high
float midpoint = (top + bottom) * 0.5
color zoneColor = bullishGap ? color.teal : color.red
box area = box.new(left = time, top = top, right = time, bottom = bottom, xloc = xloc.bar_time, border_color = zoneColor, bgcolor = color.new(zoneColor, 85))
line midline = line.new(x1 = time, y1 = midpoint, x2 = time, y2 = midpoint, xloc = xloc.bar_time, color = zoneColor, style = line.style_dashed)
array.push(zones, Gap.new(area, midline, top, bottom, bullishGap, true))
events += (bullishGap ? "Bullish" : "Bearish") + " FVG created; "
if str.length(events) > 0
alert(events, alert.freq_once_per_bar_close)