TradingCharts

User-Defined Functions

Functions let you factor a repeated calculation behind a name.

Single-expression form

smooth(source, length) =>
    ta.ema(source, length)
  • The right side of => is a single expression; its value is what the function returns.
  • Parameters have no type annotations here — the compiler infers them from each call site.

Block form

A multi-line body is indented; the last statement is the return value:

rangeStretch(src, lookback) =>
    high  = ta.highest(src, lookback)
    low   = ta.lowest(src,  lookback)
    span  = high - low
    span == 0 ? 0.0 : (src - low) / span

Typed parameters (required for exported library functions)

Prefix a parameter with its type to pin it explicitly. This is mandatory for a library() script's exported functions:

export smoothTyped(float source, int length) =>
    ta.ema(source, length)

Accepted type keywords: int, float, bool, string, color, any UDT, any enum, array<T>, map<K, V>, matrix<T>.

Tuple return

Return multiple values with a bracketed list:

bollinger(src, len, mult) =>
    basis = ta.sma(src, len)
    dev   = mult * ta.stdev(src, len)
    [basis, basis + dev, basis - dev]

[mid, up, lo] = bollinger(close, 20, 2)

The caller must destructure or discard the result — a tuple return cannot be assigned to a single variable.

Default arguments

Specify defaults with = after the parameter name:

smooth(source, length = 20) =>
    ta.ema(source, length)

m1 = smooth(close)        // length defaults to 20
m2 = smooth(close, 50)
m3 = smooth(close, length = 50)   // named argument

Named arguments can be used at any call site, in any order.

Nested functions

A function body may declare helper functions, visible only inside the enclosing function:

weightedMean(src, len) =>
    weight(i) => math.exp(-i / 10.0)
    total  = 0.0
    norm   = 0.0
    for i = 0 to len - 1
        total := total + src[i] * weight(i)
        norm  := norm  + weight(i)
    total / norm

Overloading

Multiple functions can share a name as long as the arity or the first parameter's type differ:

roll(float x) => x * 1.0
roll(int x)   => x * 1
roll(x, len)  => ta.sma(x, len)

The compiler picks the best match at each call site; ambiguous calls raise an error.

Recursion

Recursive calls are not supported — a user-defined function cannot call itself. Rewrite with a loop or an explicit stack.

Series semantics

A function that transitively calls a series-mode built-in (like ta.ema) is itself a series-mode function. It must be called on every bar from the top-level body for its internal state to stay in sync. The compiler warns when a series-mode call sits inside a conditional block.

See also