The ta.* and math.* namespaces
Lesson 7 · about 11 min
Pine ships with most of the indicators you would otherwise write yourself, grouped in namespaces. ta.* holds technical analysis functions, math.* holds arithmetic, and knowing what is already there saves you from reinventing an RSI with a subtle bug in it. This lesson is a guided tour rather than a reference; the reference manual has the full list.
Averages and oscillators
| Function | Returns |
|---|---|
ta.sma(src, len) |
simple moving average |
ta.ema(src, len) |
exponential moving average (simple int length) |
ta.wma(src, len) |
weighted moving average |
ta.rma(src, len) |
Wilder's smoothing, used inside RSI and ATR |
ta.vwma(src, len) |
volume-weighted moving average |
ta.rsi(src, len) |
RSI 0 to 100 |
ta.atr(len) |
average true range |
ta.tr(true) |
true range for the current bar |
ta.stoch(src, high, low, len) |
stochastic %K |
ta.cci(src, len) |
commodity channel index |
ta.mfi(src, len) |
money flow index |
Several functions return more than one value as a tuple:
//@version=6
indicator("Tuple built-ins", overlay=false)
[macdLine, signalLine, hist] = ta.macd(close, 12, 26, 9)
[middle, upper, lower] = ta.bb(close, 20, 2.0)
[superTrend, direction] = ta.supertrend(3.0, 10)
plot(hist, "MACD histogram", style=plot.style_histogram, color=hist >= 0 ? color.green : color.red)
plot(macdLine, "MACD", color=color.blue)
plot(signalLine, "Signal", color=color.orange)
You must unpack every element of a tuple, but you can name the ones you do not need _, as in [_, _, hist] = ta.macd(close, 12, 26, 9).
Highs, lows and events
ta.highest(src, len)/ta.lowest(src, len): extreme over the lastlenbars, including the current one.ta.highest(high, 20)[1]is the prior 20-bar high excluding this bar, which is what a breakout test needs.ta.highestbars(src, len): how many bars ago the extreme happened, as a negative offset.ta.crossover(a, b)/ta.crossunder(a, b): true on the single bar whereamoves above or belowb.ta.cross(a, b)is either.ta.change(src):src - src[1];ta.change(src, n)for n bars.ta.rising(src, len)/ta.falling(src, len): true ifsrcrose or fell on each of the lastlenbars.ta.barssince(cond): bars since the condition was last true.ta.valuewhen(cond, src, n): the value ofsrcthe n-th most recent timecondwas true (0 is the most recent).ta.pivothigh(src, left, right)/ta.pivotlow: the pivot price when a bar'ssrcis the highest ofleftbars before andrightbars after; returnsnaon other bars. Because it needsrightbars after the pivot, the value appearsrightbars late. That is lag, not repainting, but plotted labels look like they "knew" in advance.ta.cum(src): running total from the first bar.ta.vwap(src): volume-weighted average price, anchored to the session by default.
//@version=6
indicator("Events demo", overlay=true)
basis = ta.sma(close, 20)
barsSinceCross = ta.barssince(ta.crossover(close, basis))
ph = ta.pivothigh(high, 5, 5)
lastPivotHigh = ta.valuewhen(not na(ph), ph, 0)
plot(basis, "SMA 20", color=color.blue)
plot(lastPivotHigh, "Last pivot high", color=color.red, style=plot.style_stepline)
plotchar(barsSinceCross == 0, "Cross", char="x", location=location.belowbar, color=color.green)
math.*
math.abs, math.max, math.min, math.avg, math.round(x, precision), math.floor, math.ceil, math.sqrt, math.pow, math.log, math.sign, math.sum(src, len). Two that matter for trading specifically:
math.round_to_mintick(price)rounds a computed price to the symbol's tick size, which you need before using it as a stop or limit price; the emulator rejects prices between ticks.math.maxandmath.minaccept several arguments, somath.max(a, b, c)works.
Percent changes and normalisation are plain arithmetic:
atrPct = ta.atr(14) / close * 100
distFromHigh = (ta.highest(high, 52) - close) / ta.highest(high, 52) * 100
Volume and other built-in series
volume, open, high, low, close, hl2, hlc3, ohlc4, bar_index, time, time_close. Symbol details live in syminfo.*: syminfo.tickerid, syminfo.ticker, syminfo.mintick, syminfo.pointvalue, syminfo.currency, syminfo.type (stock, futures, forex, crypto and so on). Chart details are in timeframe.* and chart.*.
Key idea: Before writing a calculation, check
ta.*. The built-in is faster, tested, and returnsnacorrectly on the warm-up bars, which hand-rolled versions often do not.
Two habits
First, call ta.* functions unconditionally at the global scope and use the results conditionally, as module 2 explained. Second, when a built-in takes a length, make it an input so it is simple and adjustable; hard-coded literals in the middle of a script are the first thing you will regret.
Try it: Build a small dashboard in its own pane: RSI 14, ATR as a percent of close, and bars since the last 20-bar high (
ta.barssince(high == ta.highest(high, 20))). Then add aplotcharon bars where RSI crosses above 30 usingta.crossover.
Recap
ta.*holds averages, oscillators, extremes, crosses, pivots and VWAP; several return tuples you unpack with[a, b, c] =.ta.highest(high, n)[1]is the prior n-bar high excluding the current bar, the usual breakout reference.- Pivots confirm
rightbars late: lag, not prediction. math.round_to_mintickprepares computed prices for orders;math.max/math.mintake multiple arguments.- Prefer built-ins, call them every bar, and make lengths inputs.
See it drawn
Original diagrams for the ideas on this page. Illustrative, not real market data.