Variables, var, if and ternaries
Lesson 5 · about 11 min
Because the script re-runs on every bar, a plain variable is created fresh each time. That is fine for "what is the SMA on this bar" and useless for "how many bars in a row has price been above the SMA", which needs to remember something from the previous run. Pine gives you var for that. Combined with if and the ternary operator, it covers nearly all the control flow you will ever write.
Declaration versus reassignment
= declares a new variable. := changes the value of one that already exists. Using = twice on the same name is an error ("already defined"), and using := on a name that has not been declared is also an error. This split is deliberate: the compiler wants to know which lines create state and which lines change it.
//@version=6
indicator("Assignment demo", overlay=true)
level = ta.sma(close, 20) // declared, recomputed every bar
if close > level
level := level * 1.001 // reassigned on this bar only
plot(level)
var: initialise once, then persist
A variable declared with var is created on the first bar and keeps its value from bar to bar until you reassign it.
//@version=6
indicator("var demo", overlay=false)
sma = ta.sma(close, 20)
var int barsAbove = 0
var float highestClose = na
if close > sma
barsAbove += 1
else
barsAbove := 0
if na(highestClose) or close > highestClose
highestClose := close
plot(barsAbove, "Bars above SMA", style=plot.style_columns, color=color.teal)
Without var, barsAbove = 0 would run on every bar and the count would never exceed one. With it, the count accumulates. highestClose is declared as float because na alone gives the compiler no type to infer.
var is also the tool for objects you create once and update, such as a table or a label that follows the last bar, which module 4 uses heavily.
A relative, varip, persists across live ticks inside a bar rather than resetting when a bar recalculates. It is a specialised tool for counting ticks or volume deltas in real time, and it makes a script behave differently on historical bars than live ones. Avoid it until you have read the repainting lesson in module 5.
if, else if, else
Blocks are indented by four spaces. else if chains are allowed.
//@version=6
indicator("if demo", overlay=true)
rsi = ta.rsi(close, 14)
var string state = "neutral"
if rsi > 70
state := "overbought"
else if rsi < 30
state := "oversold"
else
state := "neutral"
barcolor(state == "overbought" ? color.red : state == "oversold" ? color.green : na)
An if can also be used as an expression that returns the last value of the chosen branch:
mult = if timeframe.isintraday
1.5
else
2.0
The ternary
condition ? valueIfTrue : valueIfFalse is the one-line version and the most common way to pick a colour or plot something conditionally. Ternaries chain, as in the barcolor line above; read chained ternaries left to right and keep them to two or three levels before switching to if or switch.
Both branches must have the same type, with na allowed on either side. close > open ? close : na is a float series that is na on down bars, and that is exactly what plot() needs for a conditional plot.
switch
For several discrete cases, switch reads better than a ternary chain:
maType = "EMA"
ma = switch maType
"SMA" => ta.sma(close, 20)
"EMA" => ta.ema(close, 20)
=> ta.wma(close, 20)
The final case with no value is the default. A switch with no expression after the keyword works like an if/else if chain on booleans.
Do not hide ta.* calls inside conditions
Functions such as ta.sma, ta.rsi and ta.highest keep internal history and must be evaluated on every bar to stay correct. Calling them only inside an if that is sometimes false gives you a compiler warning and, worse, a value that skips bars. The fix is to compute on every bar and use the result conditionally:
rsi = ta.rsi(close, 14) // every bar
signal = close > open and rsi > 50
The switch example above technically breaks this rule, because only one branch is evaluated per bar. It compiles and works when maType is fixed for the whole run, but computing all three averages and then selecting is the pattern that never surprises you.
Key idea:
=creates,:=changes,varremembers across bars. Compute series on every bar; decide what to do with them conditionally.
Try it: Write an indicator that counts consecutive up closes with
var int streak = 0, resets it on a down close, and colours the background when the streak reaches three. Then removevarand observe what the count does.
Recap
=declares and runs every bar;:=reassigns an existing variable.varinitialises once and persists across bars; declare its type when starting fromna.if/else if/elseblocks are indented;ifcan also be used as an expression.- The ternary
c ? a : bpicks a value or colour per bar; branches must share a type,naallowed. - Evaluate
ta.*functions on every bar, not inside conditions, so their history stays consistent.
See it drawn
Original diagrams for the ideas on this page. Illustrative, not real market data.