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.
//@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 - lenputs the label on the pivot bar, not on the bar that confirmed it. Unlikeplotshape(), 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 avararray lets the script delete them by rule; heremarks.shift().delete()removes the oldest once there are more thankeepMax. 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 twovarvariables, so it survives from one pivot to the next. lastHighLine. The dashed level is created once, then moved withset_xy1()andset_xy2().extend = extend.rightdraws 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 withvarand move it withset_xy()andset_text(), as in the variations. - Pivots drawn on the wrong bar.
ta.pivothigh()returns the pricelenbars after the pivot, solabel.new(bar_index, ph, ...)sitslenbars too far right. Usebar_index - len. - Text joined with +.
"High: " + phstops 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 astime), not a bar index. Passingbar_indexthere, ortimewithout 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.
Related pages
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.