Edgoro
Indicator · Markers, drawings and colors

Pine Script label.new and line.new: drawing examples

Draw labels and lines in Pine Script v6: swing labels with their price, a zigzag, a level that moves instead of being redrawn, and the max_labels_count limit.

Short answer

label.new(x, y, text) and line.new(x1, y1, x2, y2) each create one drawing and return its ID: keep the ID in a var variable or an array to move, edit or delete the drawing later. A script shows about 50 of each by default; raise it to 500 with max_labels_count and max_lines_count in indicator().

The script

Labels with the price of each swing high and low, a zigzag joining them, and one dashed line that moves to the latest swing high. Paste it into a new indicator in the Pine Editor, then save and add it to the chart.

Pine Script v6
//@version=6
indicator("Swing labels and lines", overlay = true, max_labels_count = 100, max_lines_count = 100)

len     = input.int(5, "Pivot strength (bars each side)", minval = 1)
keepMax = input.int(30, "Labels to keep", minval = 2, maxval = 100)

ph = ta.pivothigh(high, len, len)
pl = ta.pivotlow(low, len, len)

// IDs of the labels drawn so far, oldest first
var array<label> marks = array.new<label>()
// The previous swing, to join it to the next one
var int   lastX = na
var float lastY = na
// One dashed line, moved to each new swing high instead of drawing another
var line lastHighLine = na

if not na(ph)
    // The pivot happened len bars before the bar that confirms it
    int x = bar_index - len
    marks.push(label.new(x, ph, str.tostring(ph, format.mintick), style = label.style_label_down, color = color.red, textcolor = color.white, size = size.small))
    if not na(lastX)
        line.new(lastX, lastY, x, ph, color = color.gray)
    lastX := x
    lastY := ph
    if na(lastHighLine)
        lastHighLine := line.new(x, ph, bar_index, ph, extend = extend.right, color = color.red, style = line.style_dashed)
    else
        lastHighLine.set_xy1(x, ph)
        lastHighLine.set_xy2(bar_index, ph)

if not na(pl)
    int x = bar_index - len
    marks.push(label.new(x, pl, str.tostring(pl, format.mintick), style = label.style_label_up, color = color.teal, textcolor = color.white, size = size.small))
    if not na(lastX)
        line.new(lastX, lastY, x, pl, color = color.gray)
    lastX := x
    lastY := pl

// Delete the oldest labels beyond the number to keep
while marks.size() > keepMax
    marks.shift().delete()

How it works

  • max_labels_count = 100, max_lines_count = 100. Without them TradingView keeps about the 50 most recent labels and lines and deletes older ones automatically. Both go up to 500.
  • label.new(x, ph, str.tostring(ph, format.mintick), ...). By default x is a bar index (xloc.bar_index). bar_index - len puts the label on the pivot bar, not on the bar that confirmed it. Unlike plotshape(), a label's text can come from data: str.tostring(value, format.mintick) prints the price with the symbol's tick precision.
  • marks.push(label.new(...)). label.new() returns the label's ID. Storing the IDs in a var array lets the script delete them by rule; here marks.shift().delete() removes the oldest once there are more than keepMax. TradingView would delete old labels on its own past the limit, the array is there to choose how many stay.
  • The zigzag. line.new(lastX, lastY, x, ph) joins the previous swing to the new one. The previous point lives in two var variables, so it survives from one pivot to the next.
  • lastHighLine. The dashed level is created once, then moved with set_xy1() and set_xy2(). extend = extend.right draws it to the right edge of the chart whatever the second point.

Pitfalls

  • Only the last 50 appear. Without max_labels_count, older labels disappear as new ones are drawn; the same goes for lines (max_lines_count) and boxes (max_boxes_count). The maximum is 500 of each kind per script.
  • A new label on every bar. label.new() on every bar for a "current value" tag stacks thousands of labels. Create the tag once with var and move it with set_xy() and set_text(), as in the variations.
  • Pivots drawn on the wrong bar. ta.pivothigh() returns the price len bars after the pivot, so label.new(bar_index, ph, ...) sits len bars too far right. Use bar_index - len.
  • Text joined with +. "High: " + ph stops the script with operator + with a string and a number. Convert the number first: "High: " + str.tostring(ph, format.mintick).
  • Bars or time. With xloc = xloc.bar_time, x is a timestamp in milliseconds (such as time), not a bar index. Passing bar_index there, or time without it, puts the drawing far outside the visible chart.

Variations

A tag that follows the last bar. Created once, then moved and rewritten on every bar: one label on the chart, whatever the history length.

//@version=6
indicator("Last price tag", overlay = true)
atr = ta.atr(14)
var label tag = label.new(bar_index, close, "", style = label.style_label_left, color = color.new(color.blue, 20), textcolor = color.white)
tag.set_xy(bar_index + 2, close)
tag.set_text(str.tostring(close, format.mintick) + "\nATR " + str.tostring(atr, format.mintick))

A line into the future. With xloc.bar_time, x is a time, so the line can end at a time that has no bar yet: here ten bars after the last one.

//@version=6
indicator("Projected level", overlay = true)
var line proj = line.new(time, close, time, close, xloc = xloc.bar_time, color = color.orange, width = 2)
int barMs = timeframe.in_seconds() * 1000
proj.set_xy1(time, close)
proj.set_xy2(time + 10 * barMs, close)

Questions

What is the maximum number of labels in Pine Script?

500 per script, set with max_labels_count = 500 in indicator() or strategy(). Without it the default is about 50. Past the limit, TradingView deletes the oldest labels first. Lines, boxes and polylines have their own limits.

How do I delete old labels or lines in Pine Script?

Call label.delete(id) or line.delete(id), or the method form id.delete(). Keep the IDs in an array declared with var so you can find the old ones, for example the oldest with array.shift().

How do I update a label instead of drawing a new one every bar?

Declare it once with var label tag = label.new(...), then call tag.set_xy(x, y) and tag.set_text(text) on each bar or only if barstate.islast. The same label moves, so the chart never has more than one.

Every script on this page was checked with Edgoro Pine Check (Pine Script v6 rules). Pine Check is a static checker, not TradingView's compiler: TradingView compiles the script when you save it in the Pine Editor. The scripts are examples to learn from, not trading systems. Trading involves substantial risk of loss and is not suitable for every investor. Edgoro is a software tool, not a broker or financial adviser: nothing it produces is investment advice, and past or backtested performance does not guarantee future results.

Need a different version of this script?

Describe the change in plain English and Edgoro writes the Pine Script v6 for you, checked with Pine Check. For example:

“Label every swing high and low with its price, join them with a zigzag, and keep only the last 20 labels on the chart.”