Types & Variables
Variable Declaration
Variables can be declared with explicit or inferred types:
// Type inferred
a = 10 // inferred as int
name = "AAPL" // inferred as string
// Explicit type
float b = 3.14
string label = "Buy"
bool flag = trueIf the type cannot be inferred, an explicit annotation is required:
a = na // ERROR: cannot infer variable type
float a = na // OKReassignment
Use := to reassign a variable. Compound assignment operators are also supported:
a = 10
a := 20 // Reassignment
a += 5 // a := a + 5
a -= 1 // a := a - 1
a *= 2 // a := a * 2
a /= 3 // a := a / 3
a %= 4 // a := a % 4Type Qualifiers
Pine Script has four type qualifiers that describe when a value is known, from most restrictive to least:
| Qualifier | Meaning |
|---|---|
const | Known at compile time, never changes |
input | Known at script startup (e.g., user inputs) |
simple | Known on bar 0, same value on every bar |
series | Can change on every bar |
const int a = 2
input int b = 10
simple int c = a + b
series float d = closeThe qualifier hierarchy flows: const -> input -> simple -> series. A value can be auto-promoted up this hierarchy but not demoted.
var — Initialize Once, Persist Across Bars
Without a declaration mode, variables are re-initialized on every script execution (every bar, every tick). The var keyword changes this — the variable is initialized only once on the first bar, and retains its last assigned value on all subsequent bars:
//@version=6
indicator("Green Bars Count")
var count = 0
isGreen = close >= open
if isGreen
count := count + 1
plot(count)Without var, count would reset to 0 on every bar, so the plot would only show 0 or 1. With var, the value accumulates across bars.
var inside if blocks
var can be used inside if blocks. The variable is initialized on the first execution that enters the block:
var a = close
var b = 0.0
if close > open
var x = close // initialized once when this branch first executes
b := xvar with collections
var works with all types including arrays and objects. A var array persists and can grow across bars:
var a = array.new<float>(0)
array.push(a, close) // array grows by 1 element on each barReal-time behavior of var
On historical bars, var and non-var behave identically because the script runs exactly once per bar. The difference is visible on real-time bars: without var, the variable resets on each tick; with var, it retains its value across ticks but is subject to rollback when the bar closes (the value reverts to what it was at the last confirmed bar, then re-executes once for the new confirmed bar).
varip — Persist Without Rollback
varip (var intrabar persist) is similar to var — initialized only once. The key difference is that varip variables are not subject to rollback on real-time bars. They retain their values across all executions, including multiple ticks within the same unconfirmed bar:
//@version=6
indicator("varip demo")
varip int updateCount = na
if barstate.isnew
updateCount := 1
else
updateCount := updateCount + 1
plot(updateCount, style = plot.style_circles)This is useful for tracking tick-level data. On historical bars, varip behaves identically to var.
varip on type fields
varip can be used on individual type fields to make them persist across intrabar updates while other fields still roll back:
type Counter
int bars = 0
varip int ticks = 0
var Counter counter = Counter.new()
counter.bars += 1 // subject to rollback on unconfirmed bars
counter.ticks += 1 // NOT subject to rollbackComparison Table
| Behavior | No keyword | var | varip |
|---|---|---|---|
| Initialization | Every execution | Once (first bar) | Once (first bar) |
| Historical bars | Re-init each bar | Persists | Persists |
| Real-time ticks | Re-init each tick | Persists (with rollback) | Persists (no rollback) |
Tuple Destructuring
Functions that return tuples can be destructured with [...] syntax:
[median, upperBand, lowerBand] = ta.bb(close, 20, 2.0)
plot(median)
plot(upperBand)
plot(lowerBand)Discard (_)
Use _ to discard unwanted values. It can be used multiple times:
[_, upper, _] = ta.bb(close, 20, 2.0)
_ = someFunction() // Discard return value
_ = anotherFunction()na — Missing Values
na represents a missing or undefined value. Many functions return na when there isn't enough data:
sma5 = ta.sma(close, 5) // na for the first 4 bars
// Check for na with na()
if not na(sma5)
label.new(bar_index, sma5, str.tostring(sma5))Use nz() to replace na with a default:
value = nz(ta.sma(close, 5), 0.0) // Replace na with 0Use fixnan() to replace na with the last non-na value:
value = fixnan(ta.sma(close, 5))Next Steps
- Control Structures — if, for, while, switch
- Functions & Methods — defining and calling functions