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
varkeyword) 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 thevarkeyword, 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:
- 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. - 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.
- 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.
- State Persistence (with
var): When used with thevarkeyword, 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 (trueorfalse) (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 (likecloseorta.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-
varglobals are recalculated each bar.varglobals persist. This distinction is critical for logic that depends on past values. - Best Practice: If a function modifies a global variable (especially a
varone), 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: Useplot()to display the value of a global series variable on the chart. This helps visualize its behavior over time.label.new()for Specific Values: Forvarvariables 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-
varglobal 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 usingvarto compute it once or less frequently, or optimize the calculation. - Large Arrays/Matrices: Global
vararrays 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, usevaripinstead ofvar. This is crucial for preventing re-initialization on each tick whencalc_on_every_tick=trueis 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.