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.
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.
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 := y23. 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.
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.
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.
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.
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])