CILChartIndicatorLab

Pine Script v6 guide

How to program TradingView drawings in Pine Script v6

Drawings are how a script speaks visually: support and resistance zones, trendlines between pivots, session boxes and floating labels. This guide walks through each drawing object in Pine Script v6 with copy-paste snippets you can drop straight into the TradingView editor.

1. Declaration & setup

Every drawing script starts with a single indicator() declaration with overlay = true so objects render on the price chart. Raise the object caps up front — TradingView defaults to 50 lines, boxes and labels.

pine v6
1//@version=62indicator("Drawings Lab", overlay = true, max_lines_count = 500, max_boxes_count = 500, max_labels_count = 500)34// Inputs keep the script reusable without editing code5lookback   = input.int(20, "Pivot lookback", minval = 2)6zoneColor  = input.color(color.new(color.teal, 80), "Zone fill")7lineColor  = input.color(color.rgb(0, 255, 136), "Line color")

2. Trendlines between pivots

line.new() takes two coordinates. Store the previous pivot in var variables, then delete and redraw the line whenever a fresh confirmed pivot appears — that keeps exactly one trendline on the chart.

pine v6
1// A trendline between the last two swing lows, redrawn as new pivots print2pl = ta.pivotlow(low, lookback, lookback)34var float prevX = na5var float prevY = na6var line trend = na78if not na(pl) and barstate.isconfirmed9    x2 = bar_index - lookback10    y2 = pl11    if not na(prevX)12        line.delete(trend)13        trend := line.new(int(prevX), prevY, x2, y2, xloc = xloc.bar_index, color = lineColor, width = 2, extend = extend.right)14    prevX := x215    prevY := y2

3. Support & resistance zones

A zone is just a box with a transparent background. Deriving thickness from ATR makes the same script look correct on FX, crypto and indices without retuning inputs.

pine v6
1// Support / resistance zones built from confirmed pivots2ph = ta.pivothigh(high, lookback, lookback)3pl2 = ta.pivotlow(low, lookback, lookback)45// Zone thickness derived from volatility so it adapts per symbol6pad = ta.atr(14) * 0.3578if not na(ph) and barstate.isconfirmed9    box.new(bar_index - lookback, ph + pad, bar_index + 20, ph - pad, border_color = color.new(color.red, 40), bgcolor = color.new(color.red, 88))1011if not na(pl2) and barstate.isconfirmed12    box.new(bar_index - lookback, pl2 + pad, bar_index + 20, pl2 - pad, border_color = color.new(color.teal, 40), bgcolor = zoneColor)

4. Boxes that update live

Instead of creating a new box per bar, create one and mutate it with box.set_top(), box.set_bottom() and box.set_right(). This session-range box is the standard pattern.

pine v6
1// One rolling box that tracks the current session range2var box rangeBox = na3var float hi = na4var float lo = na56newDay = timeframe.change("1D")78if newDay9    hi := high10    lo := low11    rangeBox := box.new(bar_index, hi, bar_index, lo, border_color = lineColor, bgcolor = color.new(lineColor, 90))12else if not na(rangeBox)13    hi := math.max(hi, high)14    lo := math.min(lo, low)15    box.set_top(rangeBox, hi)16    box.set_bottom(rangeBox, lo)17    box.set_right(rangeBox, bar_index)

5. Labels & signal tags

Use one persistent label for live readouts and separate event labels for signals. Gate event labels on barstate.isconfirmed so they never repaint mid-bar.

pine v6
1// A single label that follows price instead of spawning one per bar2var label priceTag = na34if na(priceTag)5    priceTag := label.new(bar_index, close, "", style = label.style_label_left, color = color.new(color.black, 20), textcolor = lineColor)67label.set_xy(priceTag, bar_index, close)8label.set_text(priceTag, str.tostring(close, format.mintick) + "  |  ATR " + str.tostring(ta.atr(14), "#.##"))910// Event labels on confirmed crossovers only11fast = ta.ema(close, 21)12slow = ta.ema(close, 55)1314if ta.crossover(fast, slow) and barstate.isconfirmed15    label.new(bar_index, low, "BUY", style = label.style_label_up, color = color.new(color.teal, 20), textcolor = color.white, size = size.small)

6. Managing drawing limits

Pine Script keeps every object you create until you delete it. Push drawings into an array and shift the oldest out to hold a fixed window — the safest way to avoid hitting the object cap on long histories.

pine v6
1// Keep only the newest N drawings so the chart never hits the object cap2var line[] lines = array.new<line>()34drawLevel(float price) =>5    ln = line.new(bar_index - 1, price, bar_index, price, extend = extend.right, color = lineColor)6    array.push(lines, ln)7    if array.size(lines) > 108        line.delete(array.shift(lines))910if bool(ta.pivothigh(high, lookback, lookback)) and barstate.isconfirmed11    drawLevel(high[lookback])