As an experienced Pine Script developer, you quickly encounter situations where a single function needs to compute and return more than one piece of information. Whether you’re calculating multiple related metrics, returning a primary result alongside a status flag, or bundling various components of a complex calculation, the need to return multiple values is fundamental.
Introduction to Returning Multiple Values in Pine Script
Pine Script functions, by default, are designed to return a single value. This is a common pattern in many programming languages. However, real-world trading logic often requires bundling several outputs together.
Why Return Multiple Values? Use Cases in TradingView
In the context of trading scripts, returning multiple values is essential for:
- Bundling related calculations: A function might calculate an EMA and its corresponding deviation from the price.
- Returning main result and status: A function checking for a specific pattern might return
true/falseand the value at which the pattern occurred. - Complex indicator components: Calculating pivot points might require returning the high, low, and close levels simultaneously.
- Strategy entry/exit details: A function determining an entry signal might also need to return the proposed entry price and stop loss level.
Limitations of Single Return Values in Pine Script Functions
The primary limitation is straightforward: a statement like return value1, value2 is not valid syntax in Pine Script. Attempting this will result in a compilation error. Therefore, we need structured approaches to encapsulate multiple values into a single entity that the function can return.
Using Arrays to Return Multiple Values
One of the most common and straightforward methods to return multiple values is by using arrays. Arrays allow you to group a collection of values of the same data type into a single variable.
Creating and Populating Arrays in Pine Script Functions
Inside your function, you can create a new array and populate it with the values you intend to return. Remember that standard Pine Script arrays are homogeneous (hold values of the same type), although version 5 introduced array<tuple> and array<struct>, standard arrays are typically used for returning values of the same base type.
calculateHighLow(source) =>
// Calculate high and low based on source
highVal = ta.highest(source, 10)
lowVal = ta.lowest(source, 10)
// Create an array
// Note: Array must be declared with a specific type in v5+
var float[] resultArray = array.new<float>(0)
// Add values to the array
array.push(resultArray, highVal)
array.push(resultArray, lowVal)
// Return the array
resultArray
Returning Arrays from Functions
As shown above, once the array is created and populated within the function, you simply specify the array variable as the return value. The function’s signature implicitly defines its return type as an array of the type it contains.
Accessing Individual Values from the Returned Array
After calling the function, the returned array can be assigned to a variable. You can then access individual values using the array index operator [] or the array.get() function. Remember that array indices start from 0.
// Call the function
returnedValues = calculateHighLow(close)
// Access individual values
returnedHigh = returnedValues[0] // or array.get(returnedValues, 0)
returnedLow = returnedValues[1] // or array.get(returnedValues, 1)
// Use the values (e.g., plot them)
plot(returnedHigh, color=color.green, title="Calculated High")
plot(returnedLow, color=color.red, title="Calculated Low")
Example: Returning High and Low Prices as an Array
The code snippets above demonstrate this concept directly. calculateHighLow returns a float array containing two elements: the calculated high at index 0 and the calculated low at index 1.
Utilizing Tuples (Arrays) for Named Returns
While Pine Script doesn’t have a dedicated tuple type like Python, we often use the term ‘tuple’ conceptually when using arrays to return a fixed number of heterogeneous or semantically distinct values. The array acts like a tuple, where each index has a predefined meaning.
Defining Tuples for Clarity
Since Pine Script doesn’t enforce tuple structure syntactically, clarity comes from documentation and variable naming. You define what each position in the array represents in your comments or internal documentation.
For instance, if an array is expected to return [averagePrice, volumeMA, signalFlag], you define this structure mentally or in comments:
- Index 0: Average Price (float)
- Index 1: Volume Moving Average (float)
- Index 2: Signal Flag (bool, represented as 0.0 or 1.0 if using a
floatarray)
Returning Multiple Values as a Tuple (Array)
The process is identical to returning a standard array. You create the array, add values in the specific, predefined order, and return the array variable.
calculateMetrics(price, vol, length) =>
avgPrice = ta.sma(price, length)
volMA = ta.sma(vol, length)
// Example signal: price > avgPrice
signalFlag = price > avgPrice ? 1.0 : 0.0 // Represent bool as float
// Create and populate the array (conceptual tuple)
var float[] metricsArray = array.new<float>(0)
array.push(metricsArray, avgPrice)
array.push(metricsArray, volMA)
array.push(metricsArray, signalFlag)
metricsArray
Accessing Named Elements in the Returned Tuple
The key to making this approach readable is immediately assigning the returned array elements to meaningful variables after the function call. This mimics named access.
// Call the function
allMetrics = calculateMetrics(close, volume, 20)
// Assign to named variables based on the defined tuple structure
var float avgPrice = array.get(allMetrics, 0)
var float volMA = array.get(allMetrics, 1)
var float signalFlag = array.get(allMetrics, 2)
// Now use the named variables
plot(avgPrice, title="Avg Price")
plot(volMA, title="Volume MA")
plotchar(signalFlag == 1.0, title="Signal", location=location.top, color=color.blue)
Benefits of Using Tuples (Arrays) for Code Readability
Assigning array elements to named variables immediately after the function call significantly improves code readability compared to repeatedly using returnedArray[0], returnedArray[1], etc., throughout the script. It makes the code’s intent much clearer.
Returning a Structure (User-Defined Type)
Pine Script v5 introduced User-Defined Types (type), which are essentially structures. This provides a much more robust and type-safe way to bundle multiple, potentially heterogeneous values into a single object. Structures offer named fields, eliminating the need to remember array indices.
Defining a User-Defined Type
You define a structure using the type keyword, specifying the name of the structure and the names and types of its fields.
type MyMetrics
float avgPrice
float volMA
bool signalFlag
Creating Function to Return Values of the UDT
Your function can now create an instance of this structure, populate its fields, and return the instance.
calculateComplexMetrics(price, vol, length) =>
// Calculate values
avgPriceVal = ta.sma(price, length)
volMAVal = ta.sma(vol, length)
signalVal = price > avgPriceVal
// Create and populate an instance of the structure
var MyMetrics resultStruct = MyMetrics.new(
avgPrice=avgPriceVal,
volMA=volMAVal,
signalFlag=signalVal
)
// Return the structure instance
resultStruct
Notice the use of MyMetrics.new(...) to create an instance and assign values to fields by name.
Working with Structure Fields
After calling the function, you assign the returned structure instance to a variable. You can then access individual fields using the dot operator (.).
// Call the function
allResults = calculateComplexMetrics(close, volume, 20)
// Access individual fields using the dot operator
currentAvgPrice = allResults.avgPrice
currentVolMA = allResults.volMA
currentSignal = allResults.signalFlag
// Use the values
plot(currentAvgPrice, title="Struct Avg Price")
plot(currentVolMA, title="Struct Volume MA")
plotchar(currentSignal, title="Struct Signal", location=location.top, color=color.blue)
Example: Returning values like Volume, ATR and RSI as a Structure
Let’s define a structure for different indicator values and a function that returns it.
//@version=5
indicator("Multi-Value Return Example (Struct)", shorttitle="MVR Struct")
type IndicatorValues
float currentVolume
float currentATR
float currentRSI
getIndicatorReadings(src, lengthATR, lengthRSI) =>
// Calculate values
vol = volume
atr = ta.atr(lengthATR)
rsi = ta.rsi(src, lengthRSI)
// Create and return structure instance
IndicatorValues.new(
currentVolume = vol,
currentATR = atr,
currentRSI = rsi
)
// --- Usage --- //
lengthATR = input.int(14, "ATR Length")
lengthRSI = input.int(14, "RSI Length")
// Call the function to get the structure
readings = getIndicatorReadings(close, lengthATR, lengthRSI)
// Access and plot values from the structure
plot(readings.currentVolume, title="Volume", color=color.blue)
plot(readings.currentATR, title="ATR", color=color.orange)
plot(readings.currentRSI, title="RSI", color=color.purple)
This example clearly shows how structures encapsulate different data types and provide named access, making the code much cleaner than managing separate variables or relying on array indices.
Best Practices and Considerations
Choosing the right method for returning multiple values depends on the complexity and nature of the data.
Choosing the Right Method: Arrays vs. Tuples vs. Structures
- Arrays: Best for returning a simple, fixed number of values of the same data type, where the order is easily understood (e.g.,
[high, low],[open, close]). Can be used conceptually as tuples for a small, fixed number of values of potentially different types, but requires strict adherence to index meaning. - Structures (UDT): Preferred method in Pine Script v5+ for returning multiple values, especially when they are of different data types or when the number of values is significant. Structures provide named fields, improving readability, maintainability, and type safety significantly.
Error Handling and Validation
When using arrays, consider what happens if the function calculation fails or if input parameters are invalid. The array might be empty or contain na values. Always check array size before accessing elements or handle potential na values appropriately when processing the returned data.
When using structures, ensure that all fields are properly populated before returning. Accessing fields of an na structure instance will result in an error, so consider checking if the returned structure is na if its creation was conditional.
Performance Implications
Creating and returning arrays or structure instances does involve some overhead compared to returning a single primitive value. However, for typical Pine Script indicator or strategy calculations, this overhead is usually negligible and should not be a primary concern unless you are performing these operations millions of times within a single script execution (which is uncommon). Focus on code clarity and correctness first.
Code Maintainability and Readability Tips
- Use Structures (UDT) in v5+: For almost any scenario requiring multiple returns with different data types, structures are superior due to named fields.
- Document Array/Tuple Structure: If using arrays as tuples, clearly comment or document what each index represents.
- Assign Array/Tuple elements to Named Variables: Immediately assign values from a returned array/conceptual tuple to descriptive variables to avoid magic numbers (array indices).
- Keep Functions Focused: Functions should ideally do one thing well. If a function needs to return a large number of disparate values, consider whether it should be broken down into smaller, more focused functions, perhaps returning different structures.
By understanding and applying these methods, you can write more modular, readable, and powerful Pine Script functions that effectively manage and return complex sets of data.