Skip to main content
On this page

Global Variables in Pine Script

· 10 min read
Pineify Team
Pine Script and AI trading workflow research team
Pineify | Best Pine Script Editor

Global variables in Pine Script are declared in the script's top-level scope. Scope determines where a name can be used; declaration mode determines when its value is initialized. A global variable does not automatically preserve its value across bars. Use var when a counter, running total, or state needs that persistence.

If you're new to Pine Script, check out our complete beginner's guide to writing Pine Script first.

What are global variables anyway?​

A global declaration sits outside local blocks such as functions, loops, and conditionals. Code later in the script can use that name, including function bodies. Local variables belong to their containing scope.

Persistence is a separate choice: both global and local declarations can use var. A local var initializes the first time its block executes. See TradingView's variable declaration rules.

Creating global variables the right way​

The basic syntax is straightforward — just declare your variable outside any functions:

//@version=6
indicator(title="Global Variable Demo", overlay=true)

// This is a global variable
myCounter = 0

plot(myCounter, title="Counter reset each execution")

The example is already global, but initializes myCounter to zero on every execution. To retain updates across bars, use var:

//@version=6
indicator(title="Persistent Global Variable", overlay=true)

// This remembers its value across bars
var myCounter = 0
var totalVolume = 0.0
var highestPrice = 0.0

// Demonstrate a persistent counter
myCounter += 1
plot(myCounter, title="Bars processed")

On an open realtime bar, ordinary var values roll back to their last committed state before each execution. varip escapes that intrabar rollback; it is a separate choice, not a replacement for global scope.

The function limitation (and how to work around it)​

Functions can read accessible global variables but cannot reassign them. For scalar state, return the updated value and assign it in the caller. Mutating an object referenced by a global array is a separate case, discussed below.

Here's what works:

//@version=6
indicator(title="Global Variable with Functions", overlay=false)

// Global variable that persists
var float runningCount = 0.0

// Function that reads the global and returns a new value
calculateNewCount() =>
runningCount + nz(math.abs(ta.change(close)), 0.0)

// Update the global variable in the main scope
runningCount := calculateNewCount()

// Plot the result
plot(runningCount, title="Running Count", color=color.blue)

The function returns a value; the main scope reassigns the global. The accumulator is a float because price changes are floats. nz() treats the unavailable first-bar change as zero so it does not turn the entire accumulation into na.

The Best Pine Script Generator

Real-world examples that actually make sense​

Some practical uses for global variables:

Tracking consecutive conditions​

//@version=6
indicator("Consecutive Bulls", overlay=false)

var consecutiveBulls = 0
var consecutiveBears = 0

if close > open
consecutiveBulls := consecutiveBulls + 1
consecutiveBears := 0
else if close < open
consecutiveBears := consecutiveBears + 1
consecutiveBulls := 0

plot(consecutiveBulls, title="Bull Count", color=color.green)
plot(consecutiveBears, title="Bear Count", color=color.red)

The counters reset on an opposite-direction bar; a doji (close == open) leaves both unchanged. Thus they count directional runs while ignoring dojis, rather than requiring every intervening bar to move in the same direction.

Building cumulative indicators​

//@version=6
indicator("Volume Accumulation", overlay=false)

var totalBuyVolume = 0.0
var totalSellVolume = 0.0

if close > open
totalBuyVolume := totalBuyVolume + volume
else if close < open
totalSellVolume := totalSellVolume + volume

plot(totalBuyVolume - totalSellVolume, title="Net Volume", color=color.purple)

This measures volume on bars closing above their own open minus volume on bars closing below their own open. It does not compare each close with the previous close. It does not identify actual buyer-initiated and seller-initiated trades.

If you're looking for more advanced examples, our Pine Script v6 strategy examples article explains one code listing and additional rule specifications.

Choose Scope and Initialization Deliberately​

Keep declarations readable and choose their lifetime deliberately:

Use descriptive names: bullishBarsCount tells you way more than bbc or x. Your future self will thank you when you're debugging at 2 AM.

Always initialize your variables: Don't leave them hanging. Give every global variable a starting value that makes sense.

Group related globals together: Keep your variable declarations organized at the top of your script. It makes everything easier to find and maintain. Group them by category — price trackers, volume trackers, state flags — with a blank line between groups.

Document your globals: Add comments explaining what each variable tracks.

//@version=6
indicator("Well-Organized Globals", overlay=false)

// Price tracking variables
var float highestHigh = high // Initialized from the first bar
var float lowestLow = low // Initialized from the first bar
var int barsAboveMA = 0 // Counts bars above moving average

// Volume tracking variables
var float avgVolume = 0.0 // Running average of volume
var int volumeSpikes = 0 // Counts volume spikes above average

highestHigh := math.max(highestHigh, high)
lowestLow := math.min(lowestLow, low)
plot(highestHigh, title="Highest price seen")
plot(lowestLow, title="Lowest price seen")

Only the price extrema are updated in the organization example. The MA and volume variables are declaration placeholders, not implemented calculations.

Common mistakes that'll trip you up​

Check these common scope and initialization mistakes:

Trying to modify globals inside functions: This throws a compilation error. Use the return-and-assign pattern for scalar updates.

Confusing scope with persistence: Use var for state retained across bars. A global calculated series such as an SMA normally needs to recalculate on every bar.

Not initializing properly: Leaving variables undefined leads to weird behavior that's hard to debug.

Overusing globals: Not everything needs to be global. Only use them when you actually need to share data across functions or maintain state between bars.

How Pine Script functions really work with globals​

Pine functions cannot reassign global variables. This does not make every function free of side effects: functions can, for example, modify objects referenced by global arrays. For scalar counters, returning a value and assigning it in the caller keeps the update explicit.

This design choice makes your code more predictable and easier to debug. When you need to update a global variable based on some calculation, the pattern is always:

  1. Read the global variable in your function
  2. Calculate the new value
  3. Return the new value
  4. Assign it to the global variable in the main scope

Advanced global variable techniques​

Once you're comfortable with the basics, you can use globals for more sophisticated patterns:

State machines​

//@version=6
indicator("Trend State Machine", overlay=false)

var int trendState = 0 // 0=neutral, 1=uptrend, 2=downtrend
var int stateCounter = 0

// Calculate history-dependent series on every bar
fastAverage = ta.sma(close, 20)
slowAverage = ta.sma(close, 50)
crossUp = ta.crossover(fastAverage, slowAverage)
crossDown = ta.crossunder(fastAverage, slowAverage)

// Update state from those precomputed conditions
if crossUp
trendState := 1
stateCounter := 0
else if crossDown
trendState := 2
stateCounter := 0
else
stateCounter := stateCounter + 1

plot(trendState, title="Trend State")

This example calculates both moving averages and crossing conditions on every bar before updating state. Keeping history-dependent calculations outside conditional branches avoids skipped evaluations. It is a state demonstration, not a tested trading strategy.

Rolling calculations​

//@version=6
indicator("Custom Rolling Average", overlay=false)

var array<float> priceArray = array.new<float>(20)
var float customAverage = 0.0

// Add new price and remove old ones
array.push(priceArray, close)
if array.size(priceArray) > 20
array.shift(priceArray)

// Calculate our custom average
if array.size(priceArray) == 20
customAverage := array.avg(priceArray)

plot(customAverage, title="Custom 20-bar Average")

The array is created with 20 na elements. Each bar pushes a close and removes the oldest element. array.avg() ignores those remaining na elements, so the first 19 bars use fewer than 20 observed prices. Only after 20 observations does it average a full 20-price window. See the official array calculation rules.

For more advanced Pine Script techniques like this, check out our Pine Script v6 cheat sheet.

Working with Pineify's visual editor​

When reviewing generated Pine Script, inspect where variables are declared and whether they should persist. Check the Pineify feature overview for supported workflows.

Pineify | Best Pine Script Editor

Website: Pineify

Click here to view all the features of Pineify.

Frequently Asked Questions​

What is a global variable in Pine Script?

In Pine Script, a global variable lives at the top level of your script, outside any functions. It is accessible to subsequent code, including functions. Whether it persists depends on declaration mode: a plain declaration reinitializes on execution; var preserves committed values across bars.

What does the var keyword do in Pine Script?

var initializes a global variable on the first bar, or a local variable the first time its block runs, then preserves committed values across bars. Without var, the variable recalculates from scratch on every bar. It's the go-to for cumulative counters, running totals, and state tracking.

Can you modify a global variable inside a Pine Script function?

No — Pine Script won't let you assign directly to a global variable from inside a function. The workaround: have the function read the global, compute the new value, and return it. Then assign that returned value to your global in the main script scope using :=.

What is the difference between var and varip in Pine Script?

var sets the variable once and keeps it across bars on both historical and real-time data. varip (var intrabar persist) goes further — it holds the value across intrabar ticks during real-time bars, which matters for tick-level tracking. For most indicators and strategies, var is what you want.

How do you track cumulative volume across bars in Pine Script?

Declare a var float global and add to it conditionally on each bar. For example: put var totalBuyVolume = 0.0 at the top, then inside if close > open, write totalBuyVolume := totalBuyVolume + volume. Since var keeps the value between bars, your total accumulates correctly across historical and real-time bars.

When should you NOT use global variables in Pine Script?

Use local scope when only that block needs the value. Use global scope for values shared by later code, even when they recalculate every bar. Choose var separately when persistence is required.

How do global variables work in Pine Script state machines?

State machines use var int globals to track the current state — say 0 for neutral, 1 for uptrend, 2 for downtrend. On each bar, conditional logic checks the state and updates it with :=. A separate counter tracks how many bars you've been in the current state, letting you build time-based transitions between states.