Scripts
Write your own chart indicators, marks and alerts in a short language of their own.
Your first script
A script is written in the editor on the chart, saved on your device and added to the chart like any other indicator.
Open the Indicators menu on the chart, find Scripts at the foot and open the Script editor.
Choose Start from an example, or change the starter script. The checker reads what you type and marks any line it does not accept, with the reason.
When it says no problems were found, add the script to the chart. Its inputs become its settings there, and the script is saved on this device.
Signed in with your vault unlocked, your scripts also follow you to your other devices.
How a script runs
A script is a few lines of text that EdgeVault runs once for every candle on the chart, from the oldest to the newest. What it works out on a candle is what it draws there. It starts with a line naming it and saying whether it draws over the price (overlay) or in a pane of its own (pane).
script "Counting" pane
let average = sma(close, 10) // worked out again on every bar
keep upBars = 0 // set once, then carried from bar to bar
if close > open {
upBars = upBars + 1
}
plot(average, "Average")
plot(upBars, "Up bars so far", style: step)
plot(close[1], "Previous close") // [1] is the value one bar agolet and keep
let is worked out again on every bar, so let average = sma(close, 10) is the ten-bar average as it stands on each candle. A keep is set once, on the first bar, and holds its value from bar to bar until the script changes it, which is how a script counts or remembers something.History
[1] after a name for its value one bar ago, [2] for two bars ago, and so on up to the lookback limit below. close[1] is the previous close, and f[1] is what f was on the bar before.Missing values
none is a value that does not exist yet, such as an average before it has enough bars. Anything worked out from none is none, and a none draws nothing. isnone(x) tests for it and nz(x) swaps it for zero.Decisions and loops
if, else if and else choose between blocks in braces. for i in 0..9 counts from one number to another and includes both ends, while repeats while a condition holds, and break and continue work as you would expect. A name made inside a block or a function exists only there.Your own functions
fn name(a, b) with a block in braces makes a function that gives back what return names, and return [a, b] gives back two values to take apart with let [u, l] = name(x, y).script "Range" pane
fn spread(a, b) {
return a - b
}
plot(spread(high, low), "Bar range", style: histogram)Where it runs
Inputs
An input line makes a setting. It appears in the script's settings on the chart with a control of its own, and the first value is where it starts. A number can carry min, max and step. Choosing source lets the setting pick which price the script reads, from open, high, low, close, hl2, hlc3, ohlc4, volume.
input length = int(20, "Length", min: 1, max: 200)
input mult = float(2, "Width", min: 0.1, step: 0.1)
input show = bool(true, "Show the line")
input src = source(close, "Source")
input tone = colour(cyan, "Line colour")
input kind = options("EMA", ["SMA", "EMA"], "Average type")Outputs
plot
plot(series, "Title", colour:, width:, style:, offset:) draws a value for each bar. The style is line, histogram, step, area, circles or cross, and offset moves the plot that many bars either way. Write let p = plot(...) to give a plot a name that a fill can use.hline
hline(price, "Title", colour:, style:) draws a horizontal line at one value, solid, dashed or dotted.mark
mark(condition, "Title", shape:, at:, colour:, text:, size:) puts a small shape on every bar where the condition is true. The shape is triangle_up, triangle_down, circle, square or cross, placed above or below the bar, or at: value with a value: of your own. text: "Buy" writes up to 40 characters beside it in the same colour, shape: text draws only the text, and size: is small, normal or large.barcolour
barcolour(colour) colours the chart's own candles bar by bar, and none leaves a candle as it is. A script has one, and where several scripts set it, the last one on the chart wins. It works on the Candles chart type, and switching the script off puts the candles back as they were.band
band(condition, colour) shades the background on every bar where the condition is true.fill
fill(p, q, colour:, title:) fills the space between two plots, or a plot and a line, that you have named with let.alert
alert(condition, "Message") raises an alert when a closed bar meets the condition. Script alerts fire while the terminal is open.Colours
#rrggbb, or #rrggbbaa when the last two digits set how see-through it is, or it is one of the names cyan, orange, red, green, lime, white, grey. A colour can follow a value, as in colour: hist >= 0 ? green : red.Functions
Every function a script can call, read from the checker itself, so the list is always what the editor accepts. In these, src is a number series, length a whole number, and a ? marks an argument you can leave out. A call that gives several values is taken apart with let [a, b, c] = call(...).
abs(x)max(a, b)min(a, b)round(x, digits?)floor(x)ceil(x)sqrt(x)pow(base, exponent)log(x)exp(x)sign(x)sin(x)cos(x)tan(x)asin(x)acos(x)atan(x)nz(x, default?)gives x, or default (0 if left out) where x is noneisnone(x) -> true/falsetint(colour, transparency)sets how see-through a colour is, from 0 (solid) to 100 (invisible)gradient(value, low, high, low_colour, high_colour)picks a colour from a number: low_colour at or below low, high_colour at or above high, and a straight blend between; none gives nonesma(src, length)ema(src, length)rma(src, length)wma(src, length)vwma(src, length)stdev(src, length)highest(src, length)lowest(src, length)dev(src, length)is the average distance of the last length values from their own averagechange(src, length?)is the difference from length bars ago (1 if left out); on a true-or-false value it is true where the value changedmom(src, length)roc(src, length)crossover(a, b) -> true/falseis true on the bar where a goes from at or below b to above itcrossunder(a, b) -> true/falseis true on the bar where a goes from at or above b to below itcross(a, b) -> true/falsersi(src, length)atr(length)reads each bar’s own high, low and close, so it takes only a lengthmacd(src, fast, slow, signal) -> 3 valuesgives the MACD line, the signal line and the histogram, in that orderbb(src, length, mult) -> 3 valuesgives the middle line, the upper band and the lower band, in that orderstoch(src, high, low, length)gives the raw %K; smooth it with sma if you want the usual slow linecci(src, length)mfi(src, length)pivothigh(src, left, right)gives the pivot’s value on the bar that confirms it, right bars after the pivot itselfpivotlow(src, left, right)gives the pivot’s value on the bar that confirms it, right bars after the pivot itselfvaluewhen(cond, src, occurrence?)gives the value src had on a bar where cond was true, counting back occurrence times (0 is the latest)barssince(cond)counts the bars since cond was last true, and is none before it first isdmi(di_length, adx_smoothing) -> 3 valuesgives plus DI, minus DI and ADX, in that orderadx(di_length, adx_smoothing)supertrend(factor, atr_length) -> 2 valuesgives the line and the direction, which is -1 while the line sits under price and 1 while it sits over itsar(start, increment, maximum)kc(src, length, mult, use_true_range?) -> 3 valuesgives the middle line, the upper band and the lower band, in that orderbbw(src, length, mult)linreg(src, length, offset?)cum(src)rising(src, length) -> true/falseis true when this bar is above each of the previous length values, and false until there are that many earlier valuesfalling(src, length) -> true/falseis true when this bar is below each of the previous length values, and false until there are that many earlier valuesvwapfrom(src, restart)is the volume-weighted average of src since the last bar where restart was true, that bar includedvwapbands(src, restart, mult) -> 3 valuesgives the volume-weighted average, the upper band and the lower band, in that order, mult volume-weighted standard deviations either side; restart works as in vwapfromhighestbars(src, length)lowestbars(src, length)median(src, length)percentrank(src, length)swma(src)alma(src, length, offset?, sigma?, floor?)cmo(src, length)tsi(src, short_length, long_length)hour(t)reads a time, in UTC; minute, second, dayofweek (Sunday is 1), dayofmonth, month and year do the sameminute(t)second(t)dayofweek(t)dayofmonth(t)month(t)year(t)
Series a script reads by name, with nothing to call: open, high, low, close, volume, hl2, hlc3, ohlc4, time, bar_index, tr, obv, accdist, vwap, is_last_bar, last_bar_index, is_intraday, is_daily, is_weekly, is_monthly, is_dwm, timeframe_multiplier. Of these, time is in seconds, bar_index counts bars from zero and is_last_bar is true on the newest bar, while is_intraday, is_daily, is_weekly, is_monthly, is_dwm and timeframe_multiplier read the chart's bar length.
Examples
Each one is a complete script. Copy it into the editor, or open the editor and choose Start from an example. They show how the language works and say nothing about what to trade.
EMA cross with marks and an alert
Two moving averages, a mark on the bar where the fast one crosses the slow one, and an alert for the same moment. It shows inputs, let, plot, mark and alert.
script "EMA cross" overlay
input fast = int(9, "Fast length", min: 1, max: 200)
input slow = int(21, "Slow length", min: 1, max: 200)
let f = ema(close, fast)
let s = ema(close, slow)
plot(f, "Fast", colour: cyan)
plot(s, "Slow", colour: orange, width: 2)
mark(crossover(f, s), "Fast crossed above", shape: triangle_up, at: below, colour: green)
mark(crossunder(f, s), "Fast crossed below", shape: triangle_down, at: above, colour: red)
alert(crossover(f, s), "Fast average crossed above the slow one")RSI with levels and zones
RSI in its own pane with two horizontal levels, and the background shaded on bars where it is beyond either level. It shows pane, hline and band.
script "RSI with zones" pane
input length = int(14, "Length", min: 2)
input upper = float(68.5, "Upper level", min: 1, max: 99)
input lower = float(31.5, "Lower level", min: 1, max: 99)
let r = rsi(close, length)
plot(r, "RSI", colour: cyan)
hline(upper, "Upper level", colour: grey, style: dashed)
hline(lower, "Lower level", colour: grey, style: dashed)
band(r > upper, tint(red, 87))
band(r < lower, tint(green, 87))Bollinger bands with a fill
The middle line and the two outer lines of Bollinger bands, with the space between the outer lines filled. It shows a function that gives three values, named plots and fill.
script "Bollinger bands" overlay
input length = int(20, "Length", min: 2)
input mult = float(2.1, "Width", min: 0.1, step: 0.1)
input src = source(close, "Source")
let [mid, top, bottom] = bb(src, length, mult)
let upperLine = plot(top, "Upper", colour: cyan)
let lowerLine = plot(bottom, "Lower", colour: cyan)
plot(mid, "Middle", colour: grey, style: step)
fill(upperLine, lowerLine, colour: tint(cyan, 87), title: "Band")MACD histogram
The MACD line and its signal line, with the difference drawn as a histogram that changes colour either side of zero. It shows a colour chosen by a condition and the histogram style.
script "MACD" pane
input fast = int(12, "Fast", min: 1)
input slow = int(26, "Slow", min: 1)
input signal = int(9, "Signal", min: 1)
let [macdLine, signalLine, hist] = macd(close, fast, slow, signal)
plot(hist, "Histogram", colour: hist >= 0 ? green : red, style: histogram)
plot(macdLine, "MACD", colour: cyan)
plot(signalLine, "Signal", colour: orange)
hline(0, "Zero", colour: grey)Volume spike marker
A mark under any bar whose volume is a chosen multiple of its recent average, and a running count of those bars kept across the whole run. It shows keep and the history of a value with [1].
script "Volume spikes" overlay
input length = int(30, "Average over", min: 2)
input multiple = float(1.7, "Spike at", min: 1, step: 0.1)
let average = sma(volume, length)
let spike = volume > average * multiple
mark(spike, "Volume spike", shape: circle, at: below, colour: orange)
keep spikes = 0
if spike and not spike[1] {
spikes = spikes + 1
}
plot(spikes, "Spikes so far", colour: grey, style: step)Trailing level with keep
A line that stays a set distance from price and only moves one way until price closes through it, then turns to the other side. It shows keep, if and else, and a colour that follows a value.
script "Trailing level" overlay
input length = int(10, "Range length", min: 1)
input mult = float(3.2, "Distance", min: 0.1, step: 0.1)
let range = atr(length)
keep trail = close
keep above = true
if not isnone(range) {
if above {
trail = max(trail, close - range * mult)
if close < trail {
above = false
trail = close + range * mult
}
} else {
trail = min(trail, close + range * mult)
if close > trail {
above = true
trail = close - range * mult
}
}
}
plot(trail, "Trail", colour: above ? green : red, style: step)Limits
- A script can be up to 50,000 characters long and run over up to 100,000 candles.
- It can hold up to 32 plots, 16 horizontal lines, 16 fills, 16 bands, 32 mark statements and 16 alerts, and it can take up to 32 inputs and define up to 32 functions.
- Only the newest 1,000 marks and 1,000 alert events are kept.
- x[n] can look back up to 5,000 bars, and a for or while loop can run up to 10,000 passes each time it is reached.
- Titles can be up to 80 characters, alert messages up to 200, and a piece of text used as a value up to 40.
- Every script has a fixed allowance of work for each bar and for the whole run. A script that goes over it, or that runs for more than a few seconds, is stopped with a plain message that says so.
What is not supported
These are not in the language yet. The checker and the TradingView converter both name them plainly when they meet one.
- Other symbols and other timeframes, such as request.security. A script sees the candles of the chart it is on.
- Strategies and back-tests, such as strategy.entry. A script draws and alerts; it never places or simulates an order.
- Arrays, maps and matrices.
- Drawing objects: labels, lines, boxes and tables. Use mark, hline, band and fill.
- Drawing your own candles, such as plotcandle. Colouring the chart’s candles is barcolour.
- A few of Pine’s functions: correlation, kcw and mode.
- Text as a value to work with. Text can be compared with == and !=, passed along and printed in a title or message, but not joined, plotted or used as a number.
Coming from TradingView
Choose Paste from TradingView in the editor, paste a Pine script and convert it. You read the result, with a note on every line that did not come across, before any of it is used, and nothing is added to the chart until you add it.
What converts
var becomes keep, := becomes =, na becomes none, ta.ema becomes ema and color becomes colour.What to do with lines that do not
mark where Pine placed a label, or leave it out. If nothing in the converted script would draw, the editor says so and does not offer it for use; a script that is mostly drawing objects or strategy code is quicker to write from an example.Check the numbers
Using your own AI
Give an AI assistant the plain-text reference at /scripts.txt, or paste its text into the chat, and ask for a script. It holds the syntax, every function with its arguments, the limits, what is not supported and two examples, and it tells the assistant not to write Pine.
Paste what it writes into the editor. The checker marks any line it does not accept, and the message names what to change, so it can be sent straight back to the assistant.
If your Claude is connected through the EdgeVault MCP server, it can do that round trip itself. It can fetch the reference, check a script with the same checker and fix the errors it is given before it hands the script over. The checker only reads the text, and nothing is run or saved. You make the token in your profile settings, under Integrations, in Connect your AI.
Updated 6 November 20267 min read