Variable declarations

Introduction

Variables are identifiers that hold values. They must be declared in your code before you use them. The syntax of variable declarations is:

[<declaration_mode>] [<type>] <identifier> = <expression> | <structure>

or

<tuple_declaration> = <function_call> | <structure>

where:

  • | means “or”, and parts enclosed in square brackets ([]) can appear zero or one time.
  • <declaration_mode> is the variable’s declaration mode. It can be var or varip, or nothing.
  • <type> is optional, as in almost all Pine Script™ variable declarations (see types).
  • <identifier> is the variable’s name.
  • <expression> can be a literal, a variable, an expression or a function call.
  • <structure> can be an if, for, while or switch structure.
  • <tuple_declaration> is a comma-separated list of variable names enclosed in square brackets ([]), e.g., [ma, upperBand, lowerBand].

These are all valid variable declarations. The last one requires four lines:

BULL_COLOR = color.lime i = 1 len = input(20, "Length") float f = 10.5 closeRoundedToTick = math.round_to_mintick(close) st = ta.supertrend(4, 14) var barRange = float(na) var firstBarOpen = open varip float lastClose = na [macdLine, signalLine, histLine] = ta.macd(close, 12, 26, 9) plotColor = if close > open color.green else color.red

The formal syntax of a variable declaration is:

<variable_declaration>
[<declaration_mode>] [<type>] <identifier> = <expression> | <structure>
|
<tuple_declaration> = <function_call> | <structure>
<declaration_mode>
var | varip
<type>
int | float | bool | color | string | line | linefill | label | box | table | array<type> | matrix<type> | UDF

Initialization with `na`

In most cases, an explicit type declaration is redundant because type is automatically inferred from the value on the right of the = at compile time, so the decision to use them is often a matter of preference. For example:

baseLine0 = na // compile time error! float baseLine1 = na // OK baseLine2 = float(na) // OK

In the first line of the example, the compiler cannot determine the type of the baseLine0 variable because na is a generic value of no particular type. The declaration of the baseLine1 variable is correct because its float type is declared explicitly. The declaration of the baseLine2 variable is also correct because its type can be derived from the expression float(na), which is an explicit cast of the na value to the float type. The declarations of baseLine1 and baseLine2 are equivalent.

Tuple declarations

Function calls or structures are allowed to return multiple values. When we call them and want to store the values they return, a tuple declaration must be used, which is a comma-separated set of one or more values enclosed in brackets. This allows us to declare multiple variables simultaneously. As an example, the ta.bb() built-in function for Bollinger bands returns three values:

[bbMiddle, bbUpper, bbLower] = ta.bb(close, 5, 4)

Using an underscore (​_​) as an identifier

When declaring a variable, it is possible to use a single underscore (_) as its identifier. A value assigned to such a variable cannot be accessed. You can assign any number of values to a _ identifier anywhere in the script, even if the current scope already has such an assignment.

This is particularly useful when a tuple returns unneeded values. Let’s write another Bollinger Bands script. Here, we only need the bands themselves, without the center line:

//@version=6 indicator("Underscore demo") // We do not need the middle Bollinger Bands value, and do not use it. // To make this clear, we assign it to the `_` identifier. [_, bbUpper, bbLower] = ta.bb(close, 5, 4) // We can continue to use `_` in the same code without causing compilation errors: [bbMiddleLong, _, _] = ta.bb(close, 20, 2) plot(bbUpper)

Variable reassignment

A variable reassignment is done using the := reassignment operator. It can only be done after a variable has been first declared and given an initial value. Reassigning a new value to a variable is often necessary in calculations, and it is always necessary when a variable from the global scope must be assigned a new value from within a structure’s local block, e.g.:

//@version=6 indicator("", "", true) sensitivityInput = input.int(2, "Sensitivity", minval = 1, tooltip = "Higher values make color changes less sensitive.") ma = ta.sma(close, 20) maUp = ta.rising(ma, sensitivityInput) maDn = ta.falling(ma, sensitivityInput) // On first bar only, initialize color to gray var maColor = color.gray if maUp // MA has risen for two bars in a row; make it lime. maColor := color.lime else if maDn // MA has fallen for two bars in a row; make it fuchsia. maColor := color.fuchsia plot(ma, "MA", maColor, 2)

Note that:

  • We initialize maColor on the first bar only, so it preserves its value across bars.
  • On every bar, the if statement checks if the MA has been rising or falling for the user-specified number of bars (the default is 2). When that happens, the value of maColor must be reassigned a new value from within the if local blocks. To do this, we use the := reassignment operator.
  • If we did not use the := reassignment operator, the effect would be to initialize a new maColor local variable which would have the same name as that of the global scope, but actually be a very confusing independent entity that would persist only for the length of the local block, and then disappear without a trace.

All user-defined variables in Pine Script™ are mutable, which means their value can be changed using the := reassignment operator. Assigning a new value to a variable may change its type qualifier (see the page on Pine Script™‘s type system for more information). A variable can be assigned a new value as many times as needed during the script’s execution on one bar, so a script can contain any number of reassignments of one variable. A variable’s declaration mode determines how new values assigned to a variable will be saved.

Declaration modes

Understanding the impact that declaration modes have on the behavior of variables requires prior knowledge of Pine Script™‘s execution model.

When you declare a variable, if a declaration mode is specified, it must come first. Three modes can be used:

  • “On each bar”, when none is specified
  • var
  • varip

On each bar

When no explicit declaration mode is specified, i.e. no var or varip keyword is used, the variable is declared and initialized on each bar, e.g., the following declarations from our first set of examples in this page’s introduction:

BULL_COLOR = color.lime i = 1 len = input(20, "Length") float f = 10.5 closeRoundedToTick = math.round_to_mintick(close) st = ta.supertrend(4, 14) [macdLine, signalLine, histLine] = ta.macd(close, 12, 26, 9) plotColor = if close > open color.green else color.red

`var`

When the var keyword is used, the variable is only initialized once, on the first bar if the declaration is in the global scope, or the first time the local block is executed if the declaration is inside a local block. After that, it will preserve its last value on successive bars, until we reassign a new value to it. This behavior is very useful in many cases where a variable’s value must persist through the iterations of a script across successive bars. For example, suppose we’d like to count the number of green bars on the chart:

//@version=6 indicator("Green Bars Count") var count = 0 isGreen = close >= open if isGreen count := count + 1 plot(count)

image

Without the var modifier, variable count would be reset to zero (thus losing its value) every time a new bar update triggered a script recalculation.

Declaring variables on the first bar only is often useful to manage drawings more efficiently. Suppose we want to extend the last bar’s close line to the right of the right chart. We could write:

//@version=6 indicator("Inefficient version", "", true) closeLine = line.new(bar_index - 1, close, bar_index, close, extend = extend.right, width = 3) line.delete(closeLine[1])

but this is inefficient because we are creating and deleting the line on each historical bar and on each update in the realtime bar. It is more efficient to use:

//@version=6 indicator("Efficient version", "", true) var closeLine = line.new(bar_index - 1, close, bar_index, close, extend = extend.right, width = 3) if barstate.islast line.set_xy1(closeLine, bar_index - 1, close) line.set_xy2(closeLine, bar_index, close)

Note that:

  • We initialize closeLine on the first bar only, using the var declaration mode
  • We restrict the execution of the rest of our code to the chart’s last bar by enclosing our code that updates the line in an if barstate.islast structure.

There is a very slight penalty performance for using the var declaration mode. For that reason, when declaring constants, it is preferable not to use var if performance is a concern, unless the initialization involves calculations that take longer than the maintenance penalty, e.g., functions with complex code or string manipulations.

`varip`

Understanding the behavior of variables using the varip declaration mode requires prior knowledge of Pine Script™‘s execution model and bar states.

The varip keyword can be used to declare variables that escape the rollback process, which is explained in the page on Pine Script™‘s execution model.

Whereas scripts only execute once at the close of historical bars, when a script is running in realtime, it executes every time the chart’s feed detects a price or volume update. At every realtime update, Pine Script™‘s runtime normally resets the values of a script’s variables to their last committed value, i.e., the value they held when the previous bar closed. This is generally handy, as each realtime script execution starts from a known state, which simplifies script logic.

Sometimes, however, script logic requires code to be able to save variable values between different executions in the realtime bar. Declaring variables with varip makes that possible. The “ip” in varip stands for intrabar persist.

Let’s look at the following code, which does not use varip:

//@version=6 indicator("") int updateNo = na if barstate.isnew updateNo := 1 else updateNo := updateNo + 1 plot(updateNo, style = plot.style_circles)

On historical bars, barstate.isnew is always true, so the plot shows a value of “1” because the else part of the if structure is never executed. On realtime bars, barstate.isnew is only true when the script first executes on the bar’s “open”. The plot will then briefly display “1” until subsequent executions occur. On the next executions during the realtime bar, the second branch of the if statement is executed because barstate.isnew is no longer true. Since updateNo is initialized to na at each execution, the updateNo + 1 expression yields na, so nothing is plotted on further realtime executions of the script.

If we now use varip to declare the updateNo variable, the script behaves very differently:

//@version=6 indicator("") varip int updateNo = na if barstate.isnew updateNo := 1 else updateNo := updateNo + 1 plot(updateNo, style = plot.style_circles)

The difference now is that updateNo tracks the number of realtime updates that occur on each realtime bar. This can happen because the varip declaration allows the value of updateNo to be preserved between realtime updates; it is no longer rolled back at each realtime execution of the script. The test on barstate.isnew allows us to reset the update count when a new realtime bar comes in.

Because varip only affects the behavior of your code in the realtime bar, it follows that backtest results on strategies designed using logic based on varip variables will not be able to reproduce that behavior on historical bars, which will invalidate test results on them. This also entails that plots on historical bars will not be able to reproduce the script’s behavior in realtime.