Skip to content

Plots and colours ​

How do you show a value your script works out? Pass it to plot(), which draws one point on every bar and puts the value in the legend, on the price scale and in the data window. The rest of the family covers the other forms a value can take: hline() for a fixed level, fill() for the space between two lines, plotshape() and plotchar() for a mark on a bar, and bgcolor() and barcolor() for colour.

pine
//@version=6
indicator("Trend band", overlay = true)
int len = input.int(20, "Length")
float mid = ta.sma(close, len)
float dev = ta.stdev(close, len)
// The colour argument can change from bar to bar
plot(mid, "Middle", color = mid > mid[1] ? color.teal : color.red,
     linewidth = 2)
pUp = plot(mid + 2 * dev, "Upper", color = color.gray)
pDn = plot(mid - 2 * dev, "Lower", color = color.gray)
fill(pUp, pDn, color = color.new(color.gray, 90), title = "Band")
bool breakout = ta.crossover(close, mid + 2 * dev)
plotshape(breakout, "Breakout", shape.triangleup, location.belowbar,
     color.teal, size = size.small)
bgcolor(breakout ? color.new(color.teal, 85) : na, title = "Breakout bar")

Every call on this page sits at the top level of the script, outside any if, loop or function, and its title is a constant string. A plot's title is also its name elsewhere: in the settings, the data window and the Screener's columns.

Styles and gaps ​

plot() draws a line unless style says otherwise. A bar whose value is na has no point, and the style decides what happens around it.

styleDrawsAt an na
plot.style_line (default)A lineJoins the points on either side
plot.style_linebrA lineLeaves a gap
plot.style_steplineStepsJoins across
plot.style_steplinebrStepsLeaves a gap
plot.style_stepline_diamondSteps with a diamond on each valueJoins across
plot.style_areaA line filled down to histbaseJoins across
plot.style_areabrThe sameLeaves a gap
plot.style_histogramA bar from histbase to the valueLeaves the bar out
plot.style_columnsA wide bar from histbaseLeaves the bar out
plot.style_circlesA dot on each valueLeaves it out
plot.style_crossA cross on each valueLeaves it out

Use a br style when an na means "no reading here", such as a level that exists only inside a trade. With plot.style_line, the line would run straight from the last trade to the next one.

color can be a different colour on every bar, and an na colour hides that bar's point. linewidth sets the thickness, and linestyle (plot.linestyle_dashed or plot.linestyle_dotted) breaks a line into dashes or dots.

Where a plot shows ​

A plot shows in four places: the pane, the legend at the pane's top left, a label on the price scale, and the data window. display picks which. Add the constants together to pick more than one.

displayShows the value
display.all (default)Everywhere
display.paneAs a line or shape on the chart
display.status_lineIn the legend
display.price_scaleAs a label on the price scale
display.data_windowIn the data window
display.noneNowhere, though the script still works it out
pine
//@version=6
indicator("Plot that shows only in the data window")
float range20 = ta.highest(high, 20) - ta.lowest(low, 20)
plot(ta.rsi(close, 14), "RSI")
// Drawn nowhere, but you can read it bar by bar in the data window
plot(range20, "20-bar range", display = display.data_window)
// Drawn and in the legend, with no price-scale label
plot(50, "Midline", color = color.gray,
     display = display.pane + display.status_line)

editable = false takes a plot off the settings' Style tab, so nobody can switch it off or recolour it there. The chart, the legend and the data window still show it as its display says. display.pine_screener is accepted and does nothing here: the Screener's columns come from every plot's title.

Decimals and units ​

A plot's value shows with the chart symbol's decimals unless the script names a format. Name one for the whole study with indicator(format = ..., precision = ...), or for one plot with the same arguments on plot(). The plot's own wins.

What the script saysThe value reads
Nothing, or format.mintickWith the symbol's decimals, in a pane too
precision = 4Four decimals
format.price with no precisionTwo decimals, whatever the symbol
format.percentTwo decimals, or precision, then %
format.volumeK, M, B or T past a thousand, with up to two decimals; a whole number under a thousand unless precision says otherwise

This is how the chart, the legend and the data window show a plot's value, as TradingView shows it. str.tostring writes the same constants into text a little differently, also as TradingView does: see Strings.

format.percent writes the value as it is. A ratio of 0.05 reads 0.05%, so multiply by 100 first if you mean five percent.

Levels and bands ​

hline(price) draws a horizontal line across the whole pane, at a price that doesn't change bar to bar: a literal or an input. It's grey, one pixel wide and dashed unless you say otherwise.

fill(a, b) colours the space between two plots, or between two hline()s, and takes the ids those calls return. Give it top_value, bottom_value, top_color and bottom_color instead of color to fade from one colour to the other between two values.

pine
//@version=6
indicator("RSI zones")
float r = ta.rsi(close, 14)
pR = plot(r, "RSI", color = color.purple)
h70 = hline(70, "Overbought")
h30 = hline(30, "Oversold")
fill(h70, h30, color = color.new(color.purple, 92), title = "Middle zone")
pMid = plot(50, "Mid", display = display.none)
// A gradient: stronger green the higher the RSI goes above 50
fill(pR, pMid, 100, 50, color.new(color.green, 0),
     color.new(color.green, 100), "Above 50")

Marks on a bar ​

plotshape() puts a shape on each bar where its condition is true, and nothing where it's false. plotchar() does the same with a character, and plotarrow() draws an up arrow for a positive value and a down arrow for a negative one, longer the larger the value.

locationThe mark sits
location.abovebar (default)Above the bar's high
location.belowbarBelow the bar's low
location.topAt the pane's top
location.bottomAt the pane's bottom
location.absoluteAt the series' own value, which must then be a price

plotcandle(open, high, low, close) and plotbar(open, high, low, close) draw candles or bars of your own, such as Heikin-Ashi on a pane. A bar with any of the four na draws nothing.

Bar and background colour ​

bgcolor(color) colours the whole width of each bar's slot, from the top of the pane to the bottom, behind every series. An na colour leaves that bar as it is. On an overlay study it colours the chart's pane, and on a pane study its own pane.

barcolor(color) recolours the chart's candles themselves, the forming bar included, so its colour can change while the bar is still open.

Shifting a plot, and keeping its last bars ​

offset = 2 draws each bar's value two bars to the right, which is how a displaced moving average is drawn. The value still belongs to the bar that worked it out.

show_last = 50 draws only the last 50 bars of a plot, a fill or a shape, while the script still works out every bar.

A negative offset draws the future

offset = -2 draws each value two bars before the bar that worked it out, so a chart's history shows a value that nobody had at that time. A Pine script runs with a leak warning, as TradingView runs it. A leak-safe AmenoScript run refuses it and stops.

Colours and transparency ​

A colour is red, green and blue from 0 to 255 plus a transparency from 0, solid, to 100, invisible. You can write one four ways:

FormExampleTransparency
A constantcolor.red0
A hex literal#F23645, or #F2364580 with alpha80 hex is about 50
color.rgb(r, g, b, t)color.rgb(242, 54, 69, 40)40
color.new(c, t)color.new(color.red, 70)70

A transparency past 100 reads as 100. color.r(), color.g(), color.b() and color.t() read the four parts back, so color.t(color.new(color.red, 30)) is 30.

An na colour means no colour at all: a plot's point, a bar's background or a fill with na colour isn't drawn. It's the usual way to colour only some bars, as bgcolor(cond ? color.blue : na) does.

color.from_gradient(value, bottom, top, bottomColor, topColor) mixes two colours by where value sits between bottom and top, and holds either end past it.

A gradient with no position is transparent black, not na

When value is na, or bottom and top are equal, color.from_gradient has no position to read. It gives transparent black (0, 0, 0 at transparency 100), which is a colour, so na(c) is false. It draws nothing, but color.t(c) reads 100 and color.r(c) reads 0. Test the value with na() before you mix if you need to know.

Runs that draw nothing ​

Some runs read only a strategy's results, such as each try of an Optimizer sweep. They skip every plot, drawing, alert and log call, arguments and all, which saves much of a run's time. The results are the same as a chart's, because the engine checks first that nothing in your script could see the difference.

It keeps the calls when an argument could change something, such as plot(array.pop(values)), or when the script reads a drawing back. Such a script still gets the same results, only slower. To keep a sweep fast, work values out on their own line and pass plain values to plots and drawings.

Traps ​

  • A plot in an if or a function. TradingView refuses both. To plot on some bars only, plot cond ? value : na at the top level. Strict Pine refuses a plot in an if. For one in a function, see scope.
  • A level that moves. hline() takes a fixed price. This engine draws hline(close) without a word, but TradingView refuses to compile it. Use plot(value, style = plot.style_linebr) for a level that changes.
  • More than 64 plots. TradingView's manual caps a script's plot count at 64, where some calls count more than once. This engine has no cap, so a script with many plots can run here and fail there.
  • A negative offset draws history with values from later bars. See the warning above.

See also ​

AmenoScript is compatible with Pine Script v6. Pine Script is a trademark of TradingView, Inc., which has no part in AmenoScript.