Plotting
Plots, fills, levels, shapes and labels, plus panes, scales and colour rules.
plot is the primary output. It takes a series, a title, a colour, a width and a style, and registers a line the chart can draw and the settings panel can restyle. The title is the second argument and may be passed positionally, plot(basis, "SMA", width = 2), or by name as title = "SMA"; either way it has to be distinct within the script, because that title is what alerts, exports and the data window use to refer to the line. Styles are plot.line, plot.step, plot.dashed, plot.dotted, plot.dots, plot.histogram, plot.area and plot.cross.
offset displaces a plot along the time axis and changes no value whatsoever. plot(spanA, "Leading span A", offset = disp) draws each value disp bars to the right of the bar that produced it, which is how the Ichimoku cloud is projected ahead of price, and a negative offset draws it back — offset = -disp is the lagging span. Envelope and moving-average scripts usually expose it as an input so a reference line can be nudged clear of the bar currently forming. Two things follow and both matter: a forward-displaced line sits over bars that contributed nothing to it, so it must never be read as a signal for those bars, and the arithmetic is untouched, so a script that wants the projected value compares against spanA[disp] in code rather than reading it off the shifted line.
Colour can be a constant or an expression evaluated per bar. The semantic constants follow the active theme, so a script written in dark mode still reads correctly in light mode: color.bull and color.bear for direction, color.accent for the line the script is actually about, color.accent_soft for a fill behind it, color.fg for a neutral foreground line and color.fg_subtle for anything meant to sit quietly in the background. color.fade takes a colour and a transparency from 0 to 100, and color.new does the same for a named colour with an explicit alpha. Avoid hard-coded literals if you want a script to look right for everyone.
fill shades the region between two plotted series and accepts a when argument so the shading can be conditional. plot takes when as well, which is how an optional smoothing line is hidden behind a bool input instead of being drawn as a flat zero. hline draws a static horizontal level with its own style, usually style = line.dashed, and can be given a pane. plotshape and plotchar mark individual bars with a glyph above or below the candle, which is the right tool for discrete signals, while a plotted series is the right tool for continuous values.
Labels and tables handle text. label.new attaches text to a specific bar and price, label.value renders a live readout in a chart corner, and table.cell builds a small grid for a dashboard of statistics. All three are capped per script so a runaway loop cannot fill the chart with thousands of objects.
Panes and scales are set on the plot, not globally. A plot with pane = lower goes below price with its own scale; several plots can share a pane by naming the same pane string. If a script draws values in wildly different magnitudes, split them across panes rather than fighting the auto-scale — a single stray series two orders of magnitude larger will flatten everything else.
Example
AlgoBeamScript — Momentum Ribbon indicator
//@version=2
indicator("Momentum Ribbon", overlay = true, precision = 2)
// ---- inputs --------------------------------------------------------
len = input.int(14, "RSI length", minval = 2, maxval = 200)
fast = input.int(21, "Fast EMA", minval = 2)
slow = input.int(55, "Slow EMA", minval = 5)
span = input.int(8, "Weighted window", minval = 2, maxval = 100)
level = input.float(55.0, "Bull threshold", step = 0.5)
src = input.source(close, "Source")
// ---- series --------------------------------------------------------
series r = ta.rsi(src, len)
series emaF = ta.ema(src, fast)
series emaS = ta.ema(src, slow)
series slope = (emaF - emaF[3]) / 3
// A linearly weighted average written out longhand. The accumulator is
// declared with = before the loop and updated with := inside it; it is a
// plain local rather than a var, so it starts again from zero every bar.
weighted = 0.0
for i = 0 to span - 1
weighted := weighted + (span - i) * src[i]
series pull = weighted / (span * (span + 1) / 2)
bull = r > level and emaF > emaS and slope > 0
bear = r < 100 - level and emaF < emaS and slope < 0
// ---- output --------------------------------------------------------
plot(emaF, title = "Fast", width = 2,
color = bull ? color.bull : bear ? color.bear : color.fg_subtle)
plot(emaS, title = "Slow", width = 1, color = color.fg_subtle)
plot(pull, title = "Weighted", width = 1, color = color.accent)
fill(emaF, emaS, color = color.fade(bull ? color.bull : color.bear, 88))
plot(r, title = "RSI", pane = "lower", color = color.accent, width = 2)
hline(level, "Bull", pane = "lower", style = line.dashed)
hline(100 - level, "Bear", pane = "lower", style = line.dashed)
alert.when(ta.crossover(r, level), "Ribbon flipped bullish on {symbol} at {close}")Copy it, change one input, and run it again — the numbers are deterministic, so a difference in the output is always a difference you made.