Functions
Name a calculation, give it defaults, return several values, and reuse indicators inside helpers.
Functions turn a calculation into a named operation: they remove repetition, keep handlers readable and give important rules one home.
Declare and call a function
Parameters and return values have explicit types:
script "Candle strength"
data chart = subscribe(data.ohlcv)
fn candleStrength(open: float, high: float, low: float, close: float) -> float {
let range = high - low
return range > 0.0 ? math.abs(close - open) / range : 0.0
}
pane strengthPane = pane(title: "Candle strength", height: 0.25)
plot strength = plot.line(title: "Strength", on: strengthPane)
on chart.update {
strength.plot(candleStrength(chart.open ?? 0.0, chart.high ?? 0.0, chart.low ?? 0.0, chart.close ?? 0.0))
}Functions are declared at the top level and may be called before they appear in the
file. Candle strength is a 0–1 ratio, so it gets its own pane with on:.
Defaults and named arguments
Optional parameters follow required ones; defaults are literals. Named arguments make calls easy to read:
script "Normalised move"
data chart = subscribe(data.ohlcv)
fn normalizedMove(value: float, scale: float = 1.0) -> float {
return scale > 0.0 ? value / scale : 0.0
}
pane movePane = pane(title: "Move in ATRs", height: 0.25)
plot move = plot.line(title: "Move", on: movePane)
on chart.update {
let raw = (chart.close ?? 0.0) - (chart.open ?? 0.0)
move.plot(normalizedMove(raw, scale: chart.atr(14) ?? 1.0))
}Return several values
A function can return a few related values without a type, and the caller takes
them apart at once. _ ignores a value:
script "Candle parts"
data chart = subscribe(data.ohlcv)
fn candleParts(open: float, high: float, low: float, close: float) -> (float, float, float) {
let body = math.abs(close - open)
let upper = high - math.max(open, close)
let lower = math.min(open, close) - low
return body, upper, lower
}
pane partsPane = pane(title: "Wicks", height: 0.25)
plot (
upperWick = plot.histogram(title: "Upper wick", color: color.red, on: partsPane)
lowerWick = plot.histogram(title: "Lower wick", color: color.green, on: partsPane)
)
on chart.close {
let _, upper, lower = candleParts(chart.open ?? 0.0, chart.high ?? 0.0, chart.low ?? 0.0, chart.close ?? 0.0)
upperWick.plot(upper)
lowerWick.plot(0.0 - lower)
}Use a user-defined type when the result has an identity or methods of its own.
Indicators inside helpers
ta.* calls can live in functions. Each place the function is called keeps its own
history, so the two calls below stay separate:
script "Rate of change"
data chart = subscribe(data.ohlcv)
fn rateOfChange(value: float?, length: int) -> float? {
return ta.roc(value, length)
}
pane rocPane = pane(title: "Rate of change (%)", height: 0.25)
plot (
priceRoc = plot.line(title: "Price", color: color.blue, on: rocPane)
volumeRoc = plot.line(title: "Volume", color: color.orange, on: rocPane)
)
on chart.close {
priceRoc.plot(rateOfChange(chart.close, 12))
volumeRoc.plot(rateOfChange(chart.volume, 12))
}Calls that keep history cannot run inside loops or lambdas; call them from a handler or a helper.
Early returns
A bare return ends a function without a value early. A function with a return type
must return a value on every path.
script "Breakout marker"
data chart = subscribe(data.ohlcv)
plot breakout = plot.marker(title: "Breakout", shape: shape.up, color: color.green)
fn markBreakout(price: float, level: float?) {
if level == null {
return
}
if price > (level ?? price) {
breakout.plot(price)
}
}
on chart.close {
// The highest high of the 20 bars before this one.
markBreakout(chart.close ?? 0.0, ta.highest(chart.high[1], 20))
}Keep functions focused
A good function does one of three things: computes and returns a value, updates a clearly named piece of state, or draws one coherent result. When it needs many related parameters, they probably belong in a user-defined type.