VP Script
Reference
Everything a script can call. Names are as you type them; a leading namespace means it lives on that object.
Writing this with an AI assistant?
Copy the language, its full reference and its rules as one Markdown document, then paste it into a new chat before you ask for an indicator. Without it you will get Pine, which does not run here.
It is also a plain URL, if your tool can fetch one: /docs/script/llms
Declaring the script
indicator(title, { shortTitle?, pane?, precision?, scale? })- Names the script and says where it draws.
pane: 'separate'puts it below the price instead of on it.
The bars
open- Open of every bar, as a series.
high- High of every bar.
low- Low of every bar.
close- Close of every bar. The default source for most studies.
volume- Volume of every bar. Zero or absent on instruments that do not report it.
hl2- (high + low) / 2.
hlc3- (high + low + close) / 3, the typical price.
ohlc4- (open + high + low + close) / 4.
bars- The raw bar objects, when a series is not enough.
barCount- How many bars there are —
bars.length, named for readability. barAt(timeMs)- Where a timestamp falls on the time axis, as a FRACTIONAL bar index — the same resolution a
{ time }anchor uses. Past the last bar it extrapolates with the feed's own bar span, which is how a session box reaches its close before that bar exists.nullwhen there is no bar duration to project against.
Inputs
input.int(title, default, { min?, max?, step?, group?, tooltip? })- A whole number — a lookback length.
input.float(title, default, { min?, max?, step?, group?, tooltip? })- A decimal — a multiplier, a threshold. Give a
step: the spinner defaults to free-form, and arrow keys on a 0-to-1 ratio want 0.05, not 1. input.color(title, default, { group?, tooltip? })- A colour, with an opacity slider attached.
input.string(title, default, { options?, group?, tooltip? })- Free text, or a dropdown when
optionsis given. input.timeframe(title, default, { options?, group?, tooltip? })- A resolution the reader can change —
'5','60','1D'. Refuses a value the datafeed cannot read, naming the input, rather than letting it become arequestnothing answers. input.source(title, default?, { group?, tooltip? })- Lets the reader choose which price series feeds the study.
Drawing
plot(series, { title?, color?, width?, style?, lineStyle?, offset?, ignoreScale? })- Draw a series as a line, histogram, area, stepline or circles.
colortakes one colour or one per bar;offsetdisplaces the DRAWING only, leaving the values where they were computed. Returns an idfillcan use. hline(price, { color?, lineStyle?, title? })- A horizontal line at a fixed price — a zero line, an overbought level.
fill(fromPlot, toPlot, { color?, offset? })- Shade the band between two plots, or between two fixed prices.
colortakes one colour or one per bar, so a cloud can change colour where its edges cross; the displacement follows the plots unless you say otherwise. marker(condition, { shape?, color?, location?, text? })- A shape on every bar where the condition is truthy. The usual way to show a signal.
label(at, price, text, { color?, background?, size?, align?, offset? })- Text anchored at one price.
atis a bar index,{ index }or{ time }. Newlines break as lines.offsetdisplaces the drawing only, so a label can sit in the empty space right of the last bar. line(pointA, pointB, { color?, width?, lineStyle?, extend?, offset? })- A segment between two points. Each point is
{ index, price }or{ time, price }. Returns an idlinefillcan use.offsetdisplaces the drawing only. polyline(points, { color?, width?, lineStyle?, closed?, fillColor?, offset? })- A curve through many points, each
{ index, price }or{ time, price }. Use it where aplotcannot: a plot is one value per BAR, and a curve sampled from intrabar data has several points inside one bar and none in the next. AfillColorcloses the path. linefill(lineA, lineB, { color? })- Shade the band between two lines, by the ids
line()returned. The band is the quadrilateral between their endpoints, so two segments that do not span the same range give a wedge rather than a guess about where either continues. box(cornerA, cornerB, { color?, fillColor?, text?, offset? })- A rectangle between two corners, each
{ index, price }or{ time, price }— a session range, a supply zone.offsetdisplaces the drawing only. table(rows, { position?, background?, border?, color?, size? })- A dashboard pinned to a corner of the pane, below whatever chrome already covers it (the legend) — rows of cells, each a string or
{ text, color, background, align, bold, size }. Columns are measured and lined up for you. Use this rather than a label full of newlines: a label is anchored to a bar, so it slides off screen when you pan, and its columns cannot line up in a proportional font. bgcolor(condition, { color? })- Tint the background of every bar where the condition is truthy.
barcolor(colors)- Recolour the price bars themselves. One entry per bar;
nullkeeps a bar's normal colour. plotcandle(open, high, low, close, { upColor?, downColor?, wickColor?, title? })- Draw your own OHLC series as candles — Heikin-Ashi, a smoothed series, another timeframe.
Indicators and statistics
ta.sma(source, length)- Simple moving average.
ta.ema(source, length)- Exponential moving average.
ta.rma(source, length)- Wilder's smoothing — what RSI and ATR are built on.
ta.wma(source, length)- Linearly weighted moving average.
ta.stdev(source, length)- Rolling standard deviation.
ta.rsi(source, length)- Relative Strength Index, 0 to 100.
ta.change(source, length?)- Difference from
lengthbars ago (default 1). ta.crossover(a, b)- True on the bar where
acrosses aboveb. ta.crossunder(a, b)- True on the bar where
acrosses belowb. ta.cross(a, b)- True on a cross in either direction.
ta.highest(source, length)- Highest value over the last
lengthbars. ta.lowest(source, length)- Lowest value over the last
lengthbars. ta.midline(length)- Midpoint of the rolling high/low channel — the Donchian centre.
ta.atr(length)- Average true range.
ta.tr()- True range, unsmoothed.
ta.sum(source, length)- Rolling sum.
ta.cum(source)- Running total from the first bar.
ta.dev(source, length)- Mean absolute deviation.
ta.median(source, length)- Rolling median.
ta.rising(source, length)- True when the series rose on each of the last
lengthbars. ta.falling(source, length)- True when it fell on each of the last
lengthbars. ta.barssince(condition)- Bars since the condition was last true. 0 on the bar it is true.
ta.valuewhen(condition, source, occurrence?)- The value of
sourcewhen the condition was last true. ta.pivothigh(source, left, right)- Pivot high, reported
rightbars later — when it first becomes knowable, not when it happened. ta.pivotlow(source, left, right)- Pivot low, on the same delayed basis.
ta.macd(source, fast?, slow?, signal?)- Returns the MACD line, its signal and the histogram.
ta.bb(source, length?, mult?)- Bollinger Bands: basis, upper and lower.
ta.kc(length?, mult?)- Keltner Channels, from ATR rather than deviation.
ta.cci(length?)- Commodity Channel Index.
ta.obv()- On-balance volume.
ta.mfi(length?)- Money Flow Index — RSI weighted by volume.
ta.roc(source, length)- Rate of change, as a percentage.
ta.mom(source, length)- Momentum — the raw difference.
ta.linreg(source, length, offset?)- Linear regression value at the end of the window.
ta.percentrank(source, length)- Where the current value sits in its own recent range, 0 to 100.
ta.correlation(a, b, length)- Rolling correlation between two series.
ta.stoch(k?, d?, smooth?)- Stochastic oscillator: %K and %D.
ta.supertrend(factor?, length?)- SuperTrend line and its direction.
ta.hma(source, length)- Hull moving average: weighted averages combined to reduce lag.
ta.swma(source)- Symmetrically weighted four-bar moving average using the fixed 1, 2, 2, 1 kernel.
ta.vwma(source, length)- Volume-weighted moving average. A bar without volume is a gap rather than a zero-weight observation.
ta.alma(source, length, offset?, sigma?)- Arnaud Legoux moving average.
offsetdefaults to 0.85 andsigmato 6. ta.vwap(source?)- Session-anchored volume-weighted average price. The source defaults to
hlc3. ta.dmi(diLength?, adxSmoothing?)- Wilder directional movement: returns
{ plus, minus, adx }. Both lengths default to 14. ta.adx(diLength?, adxSmoothing?)- Average Directional Index without the two directional-indicator series.
ta.sar(start?, increment?, maximum?)- Parabolic SAR. Defaults are 0.02, 0.02 and 0.2.
ta.tsi(source, shortLength?, longLength?)- True Strength Index. The short and long smoothing lengths default to 13 and 25.
ta.cmo(source, length)- Chande Momentum Oscillator, from -100 to 100.
ta.wpr(length?)- Williams %R, from -100 to 0. The length defaults to 14.
ta.bbw(source, length?, mult?)- Bollinger bandwidth: the band's span as a fraction of its basis.
ta.variance(source, length)- Rolling population variance.
ta.cog(source, length)- Ehlers Centre of Gravity oscillator.
ta.highestbars(source, length)- Bars back to the window's highest value. Returns 0 or a negative offset.
ta.lowestbars(source, length)- Bars back to the window's lowest value. Returns 0 or a negative offset.
ta.percentile_linear_interpolation(source, length, percentage)- Rolling percentile with linear interpolation between neighbouring values.
ta.percentile_nearest_rank(source, length, percentage)- Rolling nearest-rank percentile; the answer is always a value that occurred in the window.
ta.mode(source, length)- Most frequent value in the window. Ties resolve to the smallest value.
Arithmetic that respects gaps
A series has holes, and a + b on a hole gives NaN — which draws as a break nobody asked for. These propagate the hole instead.
math.add(a, b)- Add two series, or a series and a number.
math.sub(a, b)- Subtract.
math.mul(a, b)- Multiply.
math.div(a, b)- Divide. Division by zero gives
NaN, never Infinity. math.abs(a)- Absolute value.
math.min(a, b)- Smaller of the two, bar by bar.
math.max(a, b)- Larger of the two, bar by bar.
math.shift(a, n)- Move a series forward (+) or back (−) by
nbars. math.pow(a, b)- Raise each value in
ato the corresponding power inb. Either input may be a number or series. math.sqrt(a)- Square root, bar by bar.
math.log(a)- Natural logarithm, bar by bar.
math.log10(a)- Base-10 logarithm, bar by bar.
math.exp(a)- Exponential function, bar by bar.
math.floor(a)- Round each value down to an integer.
math.ceil(a)- Round each value up to an integer.
math.sign(a)- Return -1, 0 or 1 for each value's sign.
math.round(a, decimals?)- Round each value to
decimalsplaces. Negativedecimalsvalues round to tens, hundreds and beyond. math.avg(a, b)- Element-wise mean of two numbers or series.
math.clamp(a, low, high)- Clamp each value into the inclusive range from
lowtohigh.
The chart itself
Mostly for multi-timeframe studies: a layer finer than the chart is not a view of anything, and this is how a script declines to draw one.
timeframe.period- The chart's own resolution, as the datafeed spells it —
'60','1D','1W'. timeframe.seconds- How long one chart bar is, in seconds.
0when the resolution cannot be parsed. timeframe.secondsOf(resolution)- Any resolution's length in seconds —
timeframe.secondsOf('1W')— or0if it cannot be read. Saves carrying your own table of resolution strings. timeframe.compare(a, b)-1ifais shorter thanb,1if longer,0if equal — or if either cannot be read, so an unknown resolution never hides a layer.
Time zones and sessions
Every one takes an epoch-millisecond time — a bar.time, or one from request.security_lower_tf — and an IANA zone name such as America/New_York. An unknown zone throws rather than answering in UTC: a session drawn five hours from where you put it looks entirely normal, and a refusal names the zone.
time.zoned(timeMs, zone)- Every wall-clock piece at once —
{ year, month, day, hour, minute, second, weekday, minuteOfDay, offsetMinutes }. time.hour(timeMs, zone)- The hour in that zone, 0–23.
time.minute(timeMs, zone)- The minute past the hour.
time.minuteOfDay(timeMs, zone)hour * 60 + minute— the form a session window is written in. 09:30 New York is 570.time.dayOfWeek(timeMs, zone)- 0 = Sunday, as
Datehas it. time.offsetMinutes(timeMs, zone)- Minutes east of UTC at that instant, DST included. Subtract two to show one session's clock beside another's without a table that goes stale twice a year.
time.formatMinuteOfDay(minuteOfDay)570→'09:30'. Wraps past midnight in both directions.
Trading sessions
session.isIn(timeMs, spec, zone?)- Whether an instant is inside a trading session. The zone defaults to
syminfo.timezone. session.minutesInto(timeMs, spec, zone?)- Minutes since the session opened, or
nulloutside it. The zone defaults tosyminfo.timezone.
The instrument
syminfo- The frozen instrument descriptor:
ticker,description,exchange,type,currency,mintick,pricescale,timezoneandsession.
Calculated colours
color.rgb(r, g, b, transp?)- Build a colour from 0-to-255 channels. Pine transparency is used: 0 is opaque and 100 is invisible.
color.new(color, transp)- Restate a colour at a new Pine-style transparency, replacing its existing alpha.
color.r(color)- Red channel, from 0 to 255.
color.g(color)- Green channel, from 0 to 255.
color.b(color)- Blue channel, from 0 to 255.
color.t(color)- Pine-style transparency, from 0 for opaque to 100 for invisible.
color.mix(from, to, weight)- Blend colours and alpha. A weight of 0 returns
from; 1 returnsto. color.fromGradient(value, low, high, lowColor, highColor)- Map a clamped numeric range to a two-colour gradient.
Number formatting
str.number(value, decimals?)- A number with grouped thousands and a dot decimal separator. Decimals default to 2.
str.price(value, decimals?)- A price using the instrument's quoted tick precision unless
decimalsis provided. str.percent(value, decimals?, fraction?)- A percentage string. Set
fractionwhen 0.523 means 52.3 percent. str.compact(value, decimals?)- Compact large numbers with K, M, B or T suffixes.
Other symbols and timeframes
request.security(symbol, resolution, compute?, { warmupBars? })- A value from another symbol or timeframe, projected causally: a foreign bar appears only from the chart bar that opened after it closed.
warmupBarsasks for extra history BEFORE the visible range, which an indicator with a long lookback needs. request.security_lower_tf(symbol, resolution)- The finer bars INSIDE each chart bar, one array per bar.
nullwhere the finer feed does not fully cover the bar.
Alerts
alertcondition(condition, { title, message? })- Ask the host to watch a condition. Fires on closed bars only, at most once per bar, never for history.
While you are writing
Development only. Capture is refused for any script the reader did not write, so an installed indicator's log calls do nothing on their chart and you never have to strip them before publishing.
log(...values)- Print while you are writing. Shown in the editor's console for a script you wrote yourself, and captured for nobody else — a published indicator's
logcalls do nothing on a reader's chart, so you never have to strip them before publishing.
Found what you need? Open a chart and run it from the Scripts panel, or see how others use these functions in community indicators.