//@version=6
indicator("TZ_Gold_Round_Levels", shorttitle="TZ_Gold_Round_Levels", overlay=true, max_lines_count=100, max_labels_count=100)

// All distances are price units: on XAUUSD, 10 means a $10 price move.
float spacing = input.float(50.0, "Round-number spacing", minval=0.01, group="Levels")
float offset = input.float(10.0, "Green / red offset", minval=0.01, group="Levels")
int eachSide = input.int(2, "Round levels above and below", minval=0, maxval=10, group="Levels")

color baseColor = input.color(color.rgb(41, 98, 255), "Round level", group="Appearance")
color upperColor = input.color(color.rgb(76, 175, 80), "Above (+offset)", group="Appearance")
color lowerColor = input.color(color.rgb(242, 54, 69), "Below (-offset)", group="Appearance")
int lineWidth = input.int(2, "Line width", minval=1, maxval=4, group="Appearance")
bool showPrices = input.bool(true, "Show price labels", group="Appearance")
int labelBars = input.int(5, "Label distance to the right (bars)", minval=1, maxval=100, group="Appearance")

if barstate.isfirst and offset >= spacing / 2.0
    runtime.error("Green / red offset must be less than half the round-number spacing.")

// Create a fixed pool of drawing objects. Move them instead of accumulating
// old grids. Defaults: 5 round levels x 3 lines = 15 horizontal lines.
int lineCount = (eachSide * 2 + 1) * 3
var array<line> gridLines = array.new<line>()
var array<label> priceLabels = array.new<label>()

if barstate.isfirst
    for i = 0 to lineCount - 1
        array.push(gridLines, line.new(bar_index, na, bar_index + 1, na, xloc=xloc.bar_index, extend=extend.both, width=lineWidth))
        if showPrices
            array.push(priceLabels, label.new(na, na, "", xloc=xloc.bar_index, yloc=yloc.price, style=label.style_label_left, textcolor=color.white, size=size.tiny))

// On a live candle, close is the latest available chart price.
// Recenter when the nearest round number changes; midpoint ties round up.
// These are the CURRENT levels, extended across the chart, not historical signals.
if barstate.islast and not na(close)
    float center = math.round(close / spacing) * spacing
    for levelIndex = 0 to eachSide * 2
        float base = center + (levelIndex - eachSide) * spacing
        for band = 0 to 2
            int objectIndex = levelIndex * 3 + band
            float price = math.round_to_mintick(base + (band == 1 ? offset : band == 2 ? -offset : 0.0))
            color levelColor = band == 0 ? baseColor : band == 1 ? upperColor : lowerColor
            line levelLine = array.get(gridLines, objectIndex)
            line.set_xy1(levelLine, bar_index, price)
            line.set_xy2(levelLine, bar_index + 1, price)
            line.set_color(levelLine, levelColor)
            if showPrices
                label priceLabel = array.get(priceLabels, objectIndex)
                label.set_xy(priceLabel, bar_index + labelBars, price)
                label.set_text(priceLabel, str.tostring(price, format.mintick))
                label.set_color(priceLabel, levelColor)
