The function shape
A Pine function takes typed inputs and returns a value. The simplest:
percentChange(src, lookback) =>
(src - src[lookback]) / src[lookback] * 100
This is a function literal — a one-liner with =>. Use it: plot(percentChange(close, 20)).
Multi-line functions
Use the block form for anything non-trivial:
atrPercent(length) =>
atr = ta.atr(length)
atr / close * 100
plot(atrPercent(14))
Returning multiple values with tuples
Pine v5 functions can return tuples:
macdSignal(src, fast, slow, signalLen) =>
fast_ma = ta.ema(src, fast)
slow_ma = ta.ema(src, slow)
macd = fast_ma - slow_ma
sig = ta.ema(macd, signalLen)
[macd, sig]
[m, s] = macdSignal(close, 12, 26, 9)
plot(m)
plot(s)
Doc strings
Pine supports doc comments above functions:
//@function Returns the percent change of a series over a lookback window.
//@param src The series to measure
//@param lookback Number of bars back
//@returns The percent change as a float series
percentChange(src, lookback) =>
(src - src[lookback]) / src[lookback] * 100
Hovering the function call in the editor shows the docs.
Where to put functions
Top of the script, before any code that calls them. Functions can call other functions. There is no "header file" — Pine is a single-file language.
What this unlocks
Once you write functions for your common building blocks (custom MAs, signal generators, sizing logic), every new strategy becomes "compose the functions, plot the signals" instead of writing the same math from scratch.