Pine Script Global Variables: How Do They Work?

Global variables are a fundamental concept in Pine Script, forming the backbone for storing and managing data across your indicators and strategies. Understanding their behavior, scope, and appropriate usage is critical for developing robust and efficient trading scripts.

What are Global Variables?

In Pine Script, global variables are those declared outside of any function or local block. Once declared, they reside in the script’s global scope, making them accessible from any point in the script that follows their declaration. This includes user-defined functions, built-in function calls, and plotting logic.

Think of them as script-wide containers for information. Whether it’s a user-defined input, a calculated threshold, or a color setting, global variables hold these values for the entire script’s context on each bar.

Scope and Lifetime of Global Variables

  • Scope: The scope of a global variable extends from its point of declaration to the end of the script. Any code, including functions defined after the variable, can access and potentially modify it.
  • Lifetime: By default, global variables (those not declared with the var keyword) are re-evaluated on every bar. This means their value is recalculated based on their assigned expression as the script executes on each incoming price update. Their lifetime, in this sense, is tied to the execution context of a single bar, though their historical values form a series. Variables declared with the var keyword, however, have a persistent lifetime across bars, being initialized only once.

Why Use Global Variables in Pine Script?

Global variables serve several key purposes in Pine Script development:

  1. User Inputs and Configuration: The input.*() functions (e.g., input.int(), input.source()) inherently create global variables. This is the standard way to allow users to configure script parameters like indicator periods, colors, or calculation sources.
  2. Centralized Constants and Settings: For values that are used in multiple places within a script (e.g., a specific multiplier, a label text, a fixed threshold), defining them as global variables improves readability and maintainability. Changing the value requires only one modification at the declaration point.
  3. Sharing Data Across Script Components: Global variables allow different parts of your script (e.g., calculation logic, plotting logic, alert condition logic) to share common data points seamlessly.
  4. State Persistence (with var): When used with the var keyword, global variables become powerful tools for maintaining state across bars. This is essential for strategies that need to remember entry prices, count occurrences, or track conditions over time.

Declaring and Initializing Global Variables

Proper declaration and initialization are crucial for the correct functioning of global variables.

Basic Syntax for Declaring Global Variables

Global variables are typically declared at the beginning of a script, often after the //@version directive and script declaration (indicator() or strategy()).

The basic syntax is:

variableName = expression

Optionally, you can specify the type, which is good practice for clarity and type safety:

type variableName = expression

For example:

//@version=5
indicator("Global Var Example")

// Global variable for EMA length
emaLength = 20

// Global variable with explicit type for source
series float src = close

// Calculate EMA
emaValue = ta.ema(src, emaLength)

plot(emaValue)

Data Types Supported in Pine Script Global Variables

Pine Script supports several fundamental data types for global variables:

  • int: Integer numbers (e.g., periods = 14).
  • float: Floating-point numbers (e.g., riskFactor = 0.02, price = 105.50).
  • bool: Boolean values (true or false) (e.g., enableTrailingStop = true).
  • string: Text strings (e.g., signalLabel = "Buy Signal").
  • color: Color constants or dynamically calculated colors (e.g., buyColor = color.new(color.green, 0)).
  • Series Types: Any of the above can also be a series type (e.g., series int, series float). When a variable is assigned an expression that can change bar to bar (like close or ta.sma()), it inherently becomes a series.
  • Object IDs: Variables can also store IDs returned by functions like plot(), hline(), label.new(), array.new<type>(), etc. These are effectively pointers to objects managed by the Pine Script runtime.

Initializing Variables with Default Values

It’s essential to initialize global variables at their point of declaration. If a variable is declared without an explicit initial value (e.g., float myVar), it defaults to na (Not a Number).

User input functions provide a natural way to initialize global variables with user-configurable defaults:

//@version=5
indicator("User Inputs as Globals", overlay=true)

// Initialized with user input; defaults to 14 if user doesn't change
g_rsiLength = input.int(14, title="RSI Length", minval=1)

// Initialized with a color; user can change
g_lineColor = input.color(color.purple, title="Line Color")

// Initialized with a boolean for a feature toggle
g_showLabels = input.bool(true, title="Show Price Labels")

rsiValue = ta.rsi(close, g_rsiLength)
plot(rsiValue, color=g_lineColor)

if (g_showLabels and barstate.islast)
    label.new(bar_index, close, str.tostring(close), color=g_lineColor)

Using Global Variables in Pine Script Code

Once declared, global variables can be readily used throughout your script.

Accessing Global Variables within Functions and Studies

Global variables are directly accessible by their name from anywhere in the script following their declaration. This includes inside user-defined functions.

//@version=5
indicator("Global Access Example")

g_threshold = 70.0 // Global variable

// Function accessing the global variable
checkCondition(value) =>
    value > g_threshold // Accessing g_threshold

rsiVal = ta.rsi(close, 14)
isOverbought = checkCondition(rsiVal)

plot(rsiVal, title="RSI")
plotchar(isOverbought, title="Overbought Signal", char='▼', location=location.top, color=color.red)

Modifying Global Variables: Best Practices

Modifying global variables that are not declared with var means you are setting their value for the current bar. Each bar recalculates these variables. If myGlobal = value1 is executed, and later in the script for the same bar, myGlobal = value2 is executed, then myGlobal for that bar will be value2.

For variables declared with var (persistent state variables), modification requires the := operator. This updates the single, persistent value of the variable.

//@version=5
indicator("Modifying Globals")

// Non-persistent global (recalculated each bar)
float dynamicLevel = high
if (close > open)
    dynamicLevel = dynamicLevel + ta.tr // Modifies dynamicLevel for the current bar

// Persistent global (stateful)
var float highestCloseSinceStart = close
if (close > highestCloseSinceStart)
    highestCloseSinceStart := close // Updates the persistent state

plot(dynamicLevel, "Dynamic Level", color.orange)
plot(highestCloseSinceStart, "Highest Close Since Start", color.blue)
  • Caution: Uncontrolled modification of global variables, especially within complex conditional logic or loops, can make code harder to debug and reason about. Encapsulate state changes logically.

Example: Implementing a Simple Moving Average (SMA) with Global Variables

This example showcases how input.*() functions naturally create and initialize global variables used for calculations and plotting.

//@version=5
indicator("SMA with Global Variables", overlay=true)

// Global Variable for SMA Length (User Input)
g_smaLength = input.int(20, title="SMA Length", minval=1)

// Global Variable for Plot Color (User Input)
g_smaColor = input.color(color.new(color.teal, 0), title="SMA Color")

// Global Variable for Data Source (User Input)
g_source = input.source(close, title="Source")

// Calculate SMA using global variables for parameters
smaValue = ta.sma(g_source, g_smaLength)

// Plot SMA using global variables for configuration
plot(smaValue, title="SMA", color=g_smaColor, linewidth=2)

In this script, g_smaLength, g_smaColor, and g_source are global variables. Their values are determined by user inputs, and then used by ta.sma() and plot().

Advanced Techniques with Global Variables

Beyond basic usage, global variables enable more sophisticated script behaviors.

Using var Keyword for Persistent Global Variables

The var keyword is a game-changer for global variables. When a variable is declared with var, it’s initialized only once: on the script’s first bar calculation, or the first time the script block containing the var declaration is executed (if within a conditional structure, though this is less common for globals).

var type variableName = initial_expression

After initialization, its value persists across subsequent bar calculations unless explicitly modified using the := (reassignment) operator.

//@version=5
indicator("Persistent Counter & State", overlay=true)

// Persistent counter: Initialized to 0 on the first bar
var int entryCounter = 0

// Persistent storage for an entry price: Initialized to na
var float lastEntryPrice = na

// Example condition: a simple crossover
longCondition = ta.crossunder(ta.rsi(close, 14), 30)
exitCondition = ta.crossover(ta.rsi(close, 14), 70)

if (longCondition and entryCounter == 0) // Enter only if not already in a trade
    lastEntryPrice := close
    entryCounter := entryCounter + 1
    label.new(bar_index, low, "Entry: " + str.tostring(close) + "\nCounter: " + str.tostring(entryCounter),
              yloc=yloc.belowbar, color=color.green, textcolor=color.white)

if (exitCondition and entryCounter > 0 and close > lastEntryPrice)
    // Reset for next potential entry
    lastEntryPrice := na 
    // entryCounter is not reset here to show total entries over time.
    // To allow re-entry, one might reset entryCounter to 0.
    label.new(bar_index, high, "Exit\nCounter: " + str.tostring(entryCounter),
              yloc=yloc.abovebar, color=color.red, textcolor=color.white)

plot(entryCounter, "Total Entries", color.blue)

var is indispensable for:

  • Counting events (e.g., number of signals, trades).
  • Storing state like entry prices, stop-loss levels, profit targets.
  • Implementing finite state machines for complex trade logic.
  • Ensuring that certain expensive initializations occur only once.

Global Variables for Storing State Information

Building on var, global variables are the primary mechanism for storing inter-bar state in Pine Script. This is critical for strategies that need memory of past events or conditions to make current decisions.

Consider tracking drawdown:

//@version=5
strategy("Stateful Drawdown Tracker")

var float peakEquity = na
var float maxDrawdown = 0.0

// Initialize peakEquity with starting capital or first equity value
if (na(peakEquity))
    peakEquity := strategy.initial_capital

// Update peak equity
if (strategy.equity > peakEquity)
    peakEquity := strategy.equity

// Calculate current drawdown
currentDrawdown = peakEquity - strategy.equity

// Update max drawdown
if (currentDrawdown > maxDrawdown)
    maxDrawdown := currentDrawdown

// Example: Only trade if max drawdown is below a threshold
if (maxDrawdown < 0.10 * strategy.initial_capital) // Max 10% DD
    if (ta.crossover(close, ta.sma(close, 50)))
        strategy.entry("Long", strategy.long)
else
    strategy.close_all()

plot(strategy.equity, "Equity", color.blue)
plotchar(maxDrawdown, "Max Drawdown", "", location.bottom, display=display.data_window) // View in Data Window

Arrays and Matrices as Global Variables

Arrays and matrices can also be declared as global variables, often with var if their state needs to persist and grow or change over time.

//@version=5
indicator("Global Array Example: Recent Highs", overlay=true)

// User input for array size
g_numHighsToTrack = input.int(5, "Number of Highs to Track", minval=1)

// Persistent global array to store recent significant highs
var float[] recentHighs = array.new_float(0)

// Condition for a new significant high (e.g., a pivot high)
isPivotHigh = ta.pivothigh(high, 2, 2)

if (not na(isPivotHigh))
    // Add the new high to the array
    array.unshift(recentHighs, isPivotHigh) // Add to the beginning
    // Keep the array size limited
    if (array.size(recentHighs) > g_numHighsToTrack)
        array.pop(recentHighs) // Remove the oldest element

// Plot the stored highs (example: last one)
if (array.size(recentHighs) > 0 and barstate.islast)
    for i = 0 to array.size(recentHighs) - 1
        label.new(bar_index[i*5], array.get(recentHighs, i), 
                  text="High: " + str.tostring(array.get(recentHighs, i)), 
                  color=color.new(color.orange,50), style=label.style_label_left)

This pattern is useful for tracking dynamic support/resistance levels, storing values from multiple timeframes, or managing collections of trade-related data.

Common Pitfalls and Best Practices

Effective use of global variables requires awareness of potential issues and adherence to best practices.

Avoiding Name Collisions with Built-in Variables

Pine Script has numerous built-in variables (e.g., open, high, low, close, volume, time, bar_index) and functions. Accidentally naming a global variable the same as a built-in one will shadow the built-in, leading to unexpected behavior or errors.

  • Best Practice: Use a clear naming convention for your global variables. Prefixes like g_ (e.g., g_smaLength) or more descriptive names (e.g., longEmaPeriod) can help prevent collisions and improve code clarity.

Managing Scope to Prevent Unexpected Behavior

While global accessibility is convenient, overuse can lead to code that is difficult to trace and maintain (often called “spaghetti code”).

  • Best Practice: Limit the scope of variables where possible. If a variable is only needed within a specific function or block, declare it locally.
  • Best Practice: Understand the re-evaluation cycle. Non-var globals are recalculated each bar. var globals persist. This distinction is critical for logic that depends on past values.
  • Best Practice: If a function modifies a global variable (especially a var one), ensure this side effect is well-documented and intentional. Pure functions (those that don’t modify external state) are generally easier to reason about.

Debugging Tips for Global Variables

Debugging issues related to global variables can sometimes be tricky.

  • plot() for Series: Use plot() to display the value of a global series variable on the chart. This helps visualize its behavior over time.
  • label.new() for Specific Values: For var variables or to inspect a global’s value at specific points (e.g., barstate.islast), label.new() is invaluable. log.info() can also be used in the strategy tester.
  • Data Window: The Data Window in TradingView shows the values of plotted series for the bar under the cursor, useful for verifying calculations involving globals.
  • Isolate: Temporarily comment out sections of code that modify or use the global variable in question to narrow down where an issue might be originating.

Performance Considerations When Using Global Variables

While generally efficient, misuse of global variables can sometimes lead to performance bottlenecks.

  • Complex Calculations: If a non-var global variable is initialized with a computationally expensive expression, this calculation will run on every bar. If the value doesn’t need to change that frequently, consider using var to compute it once or less frequently, or optimize the calculation.
  • Large Arrays/Matrices: Global var arrays that grow indefinitely can consume significant memory and slow down script execution, particularly during historical calculations. Implement logic to manage their size (e.g., keep only the N most recent elements, clear and rebuild periodically if appropriate).
  • varip: For variables that should initialize once per realtime bar and then hold their value throughout intrabar updates, use varip instead of var. This is crucial for preventing re-initialization on each tick when calc_on_every_tick=true is used in strategies.

By mastering global variables, including their nuances with the var keyword and series behavior, you unlock a significant portion of Pine Script’s power, enabling you to build more sophisticated, stateful, and configurable trading tools.


Leave a Reply