ScriptingFAQFunctions

Functions

Can I use a variable length in functions?

Many built-in technical analysis (TA) functions have a length parameter, such as talib.sma(source, length). A majority of these functions can process “series” lengths, which means lengths that can change from bar to bar. However, some functions only accept “simple” integer lengths, which must be known on bar zero and remain constant throughout the execution of the script.

Check the Reference Manual entry for a function to see what types of values a function can process.

User-defined functions

For built-in functions that do not accept “series” lengths, consider creating a user-defined function.

How can I calculate values depending on variable lengths that reset on a condition?

To calculate certain values that depend on varying lengths, which also reset under specific conditions, the talib.barssince() function can be useful. This function counts the number of bars since the last occurrence of a specified condition, automatically resetting the count each time this condition is met. However, there are some considerations to keep in mind when using this function for this purpose.

Firstly, before the condition is met for the first time in a chart’s history, talib.barssince() returns na. This value is not usable as a length for functions and can cause errors, especially during execution on a chart’s early bars. For a more robust version, use nz() to replace the na return of talib.barssince() with zero for early bars.

Secondly, when the condition is met, talib.barssince() returns zero for that bar, as zero bars have elapsed since the condition was last true.

Since lengths cannot be zero, it is necessary to add one to a returned value of zero, ensuring that the length is always at least one.

How can I round a number to x increments?

Rounding numbers to specific increments is useful for tasks such as calculating levels for grid trading, dealing with fractional shares, or aligning trading parameters to specific pip values.

In this example, the roundToIncrement() function accepts a value and an increment as parameters. It divides the value by the increment, rounds the result, and then multiplies it by the increment to produce the rounded value. To demonstrate the function, the closing price is rounded to the nearest increment defined in the user menu:

indicator("Round to x increment demo", overlay = true)
 
float incrementInput = input.float(0.75, "Increment", step = 0.25)
 
// @function                Rounds a value to the nearest multiple of a specified increment.
// @param value             The value to round.
// @param increment         The increment to round the value to.
// @returns                 The rounded value.
roundToIncrement(float value, float increment) {
return math.round(value / increment) * increment
}
plot(series = roundToIncrement(close, incrementInput))

How can I control the precision of values my script displays?

The precision and format arguments in the indicator() declaration statement control the number of decimals in the values that a script displays.

By default, scripts use the precision of the price scale. To display more decimal places, specify a precision argument that exceeds the value of the current price scale.

How can I control the precision of values used in my calculations?

The math.round(number, precision) variation of the math.round() function rounds values according to a specified precision. Alternatively, the math.round_to_mintick() function rounds values to the nearest tick precision of the chart’s symbol.

How can I round to ticks?

To round values to the tick precision of a chart’s symbol, use the function math.round_to_mintick(). To convert the resulting number to a string, use str.tostring(myValue, format.mintick) to first round the number to tick precision and then return its string representation, where myValue is the number to convert into a rounded string.

How can I abbreviate large values?

There are different ways to abbreviate large numerical values, such as volume. For instance, the number 1,222,333.0 can be simplified to 1.222M. Here are some methods to accomplish this:

  1. Apply a global setting

    Use the argument format = format.volume within either the indicator() ` statements. Using this setting displays all values in the script in their abbreviated forms.

  2. Abbreviate specific values

    To abbreviate only certain values for string display, use the str.tostring(value, format.volume) function.

How do I calculate averages?

The method of calculating averages depends on the type of values to average.

  1. Distinct variables

    To find the average of a small number of discrete variables, use the function math.avg(number0, number1, …). Simply pass each of the variables as an argument to this function.

  2. Bar prices

    To find the average price of a single bar, use the built-in variables hl2, hlc3, and ohlc4.

  3. Series values

    To compute the average of the last n values in a series, use the function talib.sma(series,n).

How can I generate a random number?

Use the math.random() function to generate random numbers. This example script creates a circle plot with random RGB color values and a random y value between 0 and 1:

indicator("Random demo", overlay = false)
// Generate a random price value (the default range is 0 to 1).
float y = math.random()
r = math.floor(math.random(0, 255))
g = math.floor(math.random(0, 255))
b = math.floor(math.random(0, 255))
// Generate a color with red, green, and blue values as separate random values between 0 and 255.
color plotColor = color.rgb(r, g, b)
plot(series = y, title = "Random number", color = plotColor, linewidth = 2, style = plotStyle.scatter)
 

What does nz() do?

The nz() function replaces any na values with zero, or with a user-defined value if the replacement argument is specified. This function helps to prevent na values from interfering with calculations.

The following example script shows an exaggerated failure as a result of a single na value. The barRangeRaw variable is na only once, on the first bar, because it references a bar that does not exist, using the history-referencing operator. The alternative variable barRangeWithNz uses nz() to prevent an na value from ever occurring.

The dependentCalculation variable takes one of these values and uses it to calculate a crude average of the bar range. If the input to this calculation is ever na, the series will be na forever after that.

Choose between the two values for bar range using the input setting, and the range either displays or does not. In the latter case, the Data Window shows that the value of dependentCalculation is ϴ, meaning na.

indicator("`na` values on first bar demo")
 
bool useNzInput = input.bool(true, "Use `nz` to ensure value is never na")
 
// This variable is na on the first bar.
float barRangeRaw = close - close[1]
// This variable is never na.
float barRangeWithNz = close - nz(close[1], open)
// Choose the value to use based on the input
float barRange = useNzInput ? barRangeWithNz : barRangeRaw
 
// Perform a calculation that depends on the barRange
var float dependentCalculation = 0
dependentCalculation := ((dependentCalculation + barRange)/2)
// Plot the results
plot(dependentCalculation, title="Average Bar Range")

The nz() function is also useful to protect against any potential divide-by-zero errors. It guarantees a return value even when an equation unintentionally features a zero in the denominator.

Consider the following code snippet that intentionally creates a divide-by-zero scenario by setting the denominator to zero. Without the nz() function, this expression would return na, instead of zero:

float dbzTest = nz(close / (close - close))