Built-in indicators
Moving averages (SMA/EMA/WMA/RMA/HMA/DEMA/TEMA/VWMA), oscillators (RSI/Stoch/CCI/MFI/MACD/Williams%R/Mom/ROC), volatility (ATR/BB/KC/DC), volume (OBV/AD/CMF/PVT), structure (pivots/Supertrend/DMI/ADX). Every canonical indicator in one page.
The indicators below are the same primitives our built-in catalog (RSI, MACD, Bollinger, etc.) uses internally. Every function is vectorised — input one or more series, get a series back. Combine them however you like.
This is a reference page; for usage patterns see Recipes.
Moving averages
sma(src, period)
Simple moving average. Sum of the last period bars divided by period. NaN during warmup.
ma50 = sma(close, 50)
ma200 = sma(close, 200)
ema(src, period)
Exponential moving average with smoothing factor k = 2 / (period + 1). Reacts faster than sma to recent moves; never fully forgets old data.
fast = ema(close, 12)
slow = ema(close, 26)
plotLine(fast - slow, color="#F0B90B") // MACD-style spread
wma(src, period)
Linearly-weighted MA — older bars contribute progressively less. Smoother than sma, less laggy than ema.
rma(src, period)
Wilder's smoothed MA (k = 1 / period). Used by RSI, ADX, and ATR. Smoother than ema for the same period; sometimes called "running moving average".
hma(src, period)
Hull MA — designed for low lag with high smoothness. Approximately:
hma = wma(2 * wma(src, period/2) - wma(src, period), sqrt(period))
dema(src, period), tema(src, period)
Double / Triple EMA — dema = 2*ema − ema(ema), tema = 3*ema − 3*ema(ema) + ema(ema(ema)). Less lag than EMA at the cost of more volatility on noisy inputs.
vwma(src, volume, period)
Volume-weighted moving average — Σ(src · vol) / Σ(vol) over period bars. Tracks where most of the volume actually traded, useful for institutional support/resistance.
vwma20 = vwma(close, volume, 20)
plotLine(vwma20, color="#F0B90B", width=2)
Oscillators
rsi(src, period)
Wilder's Relative Strength Index. Returns values in [0, 100].
@pane "below"
r = rsi(close, 14)
paneRange(0, 100)
paneLine(r, color="#F0B90B", width=1.5)
paneHline(70, color="rgba(246,70,93,0.6)")
paneHline(30, color="rgba(14,203,129,0.6)")
stoch_k(close, high, low, period)
Stochastic %K — 100 * (close − lowest(low, p)) / (highest(high, p) − lowest(low, p)).
stoch_d(close, high, low, kPeriod, dPeriod=3)
Stochastic %D — SMA of %K over dPeriod bars. The classic 14-3 stoch is stoch_d(close, high, low, 14, 3).
cci(high, low, close, period)
Commodity Channel Index — (typical − sma(typical)) / (0.015 · mean_abs_dev). Typical price is hlc3. CCI is unbounded; > +100 and < −100 are the canonical OB / OS levels.
mfi(high, low, close, volume, period)
Money Flow Index — volume-weighted RSI on typical price. Range [0, 100]. Better than RSI for confirming moves with volume.
willr(high, low, close, period)
Williams %R, range [−100, 0]. > −20 is overbought, < −80 is oversold. Inverted version of stoch %K.
mom(src, period)
Momentum — src[i] − src[i − period]. Raw difference; not normalized.
roc(src, period)
Rate of Change — 100 · (src − src[period]) / src[period]. Percentage version of mom.
macd_line(src, fast, slow), macd_signal(src, fast, slow, signal), macd_hist(src, fast, slow, signal)
MACD components. line = ema(src, fast) − ema(src, slow). signal = ema(line, signal). hist = line − signal. Default config is (close, 12, 26, 9).
@pane "below"
line = macd_line(close, 12, 26)
sig = macd_signal(close, 12, 26, 9)
hist = macd_hist(close, 12, 26, 9)
paneFill(hist, base=0, color="rgba(240,185,11,0.4)")
paneLine(line, color="#0ECB81", width=1.5)
paneLine(sig, color="#F6465D", width=1.5)
Volatility / channels
tr(high, low, close)
True Range — max(high − low, |high − close[1]|, |low − close[1]|). Captures the full bar including overnight gaps.
atr(high, low, close, period)
Average True Range — Wilder smoothing of tr. Standard volatility metric used by Supertrend, Keltner channels, and most stop-loss formulas.
@input atrMult = input.float(1.5, "ATR multiplier", minval=0.5, maxval=5.0, step=0.1)
stop = close - atr(high, low, close, 14) * atrMult
plotLine(stop, color="rgba(246,70,93,0.7)", width=1)
bb_basis(src, period), bb_upper(src, period, mult), bb_lower(src, period, mult)
Bollinger Bands. basis = sma(src, p), upper = basis + mult · stdev(src, p), lower = basis − mult · stdev(src, p). Default (close, 20, 2).
mid = bb_basis(close, 20)
upper = bb_upper(close, 20, 2)
lower = bb_lower(close, 20, 2)
plotLine(mid, color="#F0B90B")
plotLine(upper, color="rgba(240,185,11,0.5)")
plotLine(lower, color="rgba(240,185,11,0.5)")
plotBand(upper, lower, color="rgba(240,185,11,0.04)")
kc_basis(src, period), kc_upper(src, high, low, close, period, mult), kc_lower(src, high, low, close, period, mult)
Keltner Channels — EMA basis with ATR-scaled bands.
basis = kc_basis(close, 20)
upper = kc_upper(close, high, low, close, 20, 1.5)
lower = kc_lower(close, high, low, close, 20, 1.5)
dc_upper(high, period), dc_lower(low, period), dc_basis(high, low, period)
Donchian Channels — straight rolling-extreme bands. upper = highest(high, p), lower = lowest(low, p), basis = (upper + lower) / 2. Used by trend-following systems (Turtle Trading uses 20-day Donchian breakouts).
Volume / accumulation
obv(close, volume)
On-Balance Volume — cumulative volume signed by close direction. Adds today's volume on up bars, subtracts on down bars, ignores flat bars.
@pane "below"
o = obv(close, volume)
paneLine(o, color="#F0B90B")
ad(high, low, close, volume)
Accumulation/Distribution Line — cumulative MFM · volume where MFM = ((close − low) − (high − close)) / (high − low). Reads buying / selling pressure within each bar.
cmf(high, low, close, volume, period)
Chaikin Money Flow — Σ(MFM · volume) / Σ(volume) over period bars. Range [−1, +1]. > 0.1 and < −0.1 are the canonical thresholds.
pvt(close, volume)
Price-Volume Trend — cumulative (change(close) / close[1]) · volume. Similar to OBV but weighted by the percentage move, so big moves contribute more than small ones.
Market structure
pivothigh(src, left, right)
Returns src[i − right] if it is the strict maximum of the left + right + 1 window centred right bars in the past. NaN on every non-pivot bar.
ph = pivothigh(high, 5, 5) // 5-bar swing pivot
// Continuous step line that updates on each new pivot:
phLine = fixnan(ph)
plotLine(phLine, color="rgba(14,203,129,0.6)", width=1)
The output is lagging by right bars — that's structural, not a bug. A pivot is only confirmable once right bars after it have closed.
pivotlow(src, left, right)
Symmetric to pivothigh.
pivotAvg(pivotSeries, count)
Stair-step rolling average of the last count non-NaN pivots in pivotSeries. Updates on every new pivot, carries forward between events.
ph = pivothigh(high, 5, 5)
pl = pivotlow(low, 5, 5)
phAvg = pivotAvg(ph, 3) // average of the last 3 swing highs
plAvg = pivotAvg(pl, 3)
plotLine(phAvg, color="rgba(14,203,129,0.7)", width=1.5)
plotLine(plAvg, color="rgba(246,70,93,0.7)", width=1.5)
supertrend(high, low, close, atrPeriod, mult)
Supertrend BAND value — the visible trail line. Below price during uptrends, above price during downtrends.
supertrend_dir(high, low, close, atrPeriod, mult)
Supertrend direction series — +1 during bull trends, −1 during bear trends. Pair with supertrend for two-tone plotting:
band = supertrend(high, low, close, 10, 3.0)
dir = supertrend_dir(high, low, close, 10, 3.0)
bull = mask(band, dir == 1)
bear = mask(band, dir == -1)
plotLine(bull, color="#0ECB81", width=2)
plotLine(bear, color="#F6465D", width=2)
trail(close, upBand, dnBand, initDir=1)
Generic ratcheting trail with caller-supplied bands. Supertrend is one fixed parameterisation of "follow the trend at a distance" — HL2 ± mult × ATR with a ratchet so the band only tightens. Lots of Pine indicators (Chandelier Exit, JMA Trail, Volatility Stop, Range Anchor, every "Supertrend ON INDICATOR" reskin) want the same recurrence but with a DIFFERENT basis (EMA × volatility, Donchian mid ± stdev, pivot ± fixed offset, …). trail() lets the script compute the two bands itself and outsources the ratcheting state machine to the stdlib — without it, the Pine var float trail := … recurrence has no bulk-vector closed form.
Recurrence (see lib/structure.js for the full implementation):
- Bull regime (
dir > 0):trail := max(upBand[i], trail[i-1])— ratchet UP only. Whenclose < trail, flip to bear and jump todnBand[i]. - Bear regime (
dir < 0):trail := min(dnBand[i], trail[i-1])— ratchet DOWN only. Whenclose > trail, flip to bull and jump toupBand[i].
Drop-in for any EMA-based Supertrend variant:
@input basisLen = input.int(28, "Basis length", minval=1, maxval=200)
@input volLen = input.int(15, "Volatility length", minval=1, maxval=100)
@input volMult = input.float(1.25, "Trail × volatility", minval=0.5, maxval=8.0, step=0.25)
ma = ema(close, basisLen)
volr = atr(high, low, close, volLen)
upBand = ma - volr * volMult // bull-side target (tighter when price > ma)
dnBand = ma + volr * volMult // bear-side target (wider when price < ma)
drift = trail(close, upBand, dnBand, 1)
dir = trail_dir(close, upBand, dnBand, 1)
plotLine(mask(drift, dir == 1), color="#0ECB81", width=2)
plotLine(mask(drift, dir == -1), color="#F6465D", width=2)
Other parametrisations the same primitive covers:
- Chandelier Exit:
upBand = highest(high, n) - mult * atr(...),dnBand = lowest(low, n) + mult * atr(...). - Pivot Trail:
upBand = lastSwingLow + cushion,dnBand = lastSwingHigh - cushion(usingvaluewhen+pivot*). - Donchian Trail:
upBand = dc_basis(...) - margin,dnBand = dc_basis(...) + margin. - JMA Trail: replace
emawith the user's preferred smoother.
initDir seeds the regime on the first valid bar (+1 starts bull, -1 starts bear). The first close-vs-trail check immediately on bar 1 corrects the seed if the market already disagrees.
trail_dir(close, upBand, dnBand, initDir=1)
Companion +1 / -1 direction series for the same trail. Cheap to call alongside trail() (each is an independent O(N) pass — no shared cache to avoid stale state when inputs change between renders). Use the direction series to drive mask(...) for two-tone plotting and crossover(dir, 0) / crossunder(dir, 0) for flip events.
plus_di(high, low, close, period), minus_di(high, low, close, period)
Wilder's Directional Movement Indicator — +DI and −DI lines. Compare the two to gauge trend direction; combine with ADX for trend strength.
adx(high, low, close, period)
Average Directional Index — 100 · ema(|+DI − -DI| / (+DI + -DI), period) (Wilder smoothing). Range [0, 100]. > 25 is the canonical "trending" threshold; < 20 indicates ranging.
@pane "below"
di_plus = plus_di(high, low, close, 14)
di_minus = minus_di(high, low, close, 14)
ax = adx(high, low, close, 14)
paneRange(0, 100)
paneLine(di_plus, color="#0ECB81", width=1.2)
paneLine(di_minus, color="#F6465D", width=1.2)
paneLine(ax, color="#F0B90B", width=2)
paneHline(25, color="rgba(255,255,255,0.25)")
Composite recipes
A few common multi-indicator setups:
Squeeze breakout
@input length = input.int(20, "Length", minval=5, maxval=200)
@input bbMult = input.float(2.0, "BB mult")
@input kcMult = input.float(1.5, "KC mult")
bbU = bb_upper(close, length, bbMult)
bbL = bb_lower(close, length, bbMult)
kcU = kc_upper(close, high, low, close, length, kcMult)
kcL = kc_lower(close, high, low, close, length, kcMult)
inSqueeze = (bbU < kcU) and (bbL > kcL)
plotBgColor(inSqueeze, color="rgba(240,185,11,0.06)")
breakUp = crossunder(bbU, kcU) == false and not inSqueeze and shift(inSqueeze, 1)
breakDown = breakUp // simplified; real implementation tracks direction
Momentum + trend gate
@input rsiThresh = input.int(55, "RSI bull threshold", minval=50, maxval=70)
@input adxThresh = input.int(25, "ADX trend threshold", minval=15, maxval=40)
trendOk = adx(high, low, close, 14) > adxThresh
momoOk = rsi(close, 14) > rsiThresh
bullishGate = trendOk and momoOk
plotShape(crossover(close, sma(close, 50)) and bullishGate,
low, shape="arrowUp", color="#0ECB81", size=8)
Choosing the right indicator
| Goal | Reach for |
|---|---|
| Smooth average of price | sma, ema, wma, rma |
| Volume-aware average | vwma |
| Low-lag MA | hma, dema, tema |
| Overbought / oversold | rsi, stoch_k + stoch_d, cci, willr, mfi |
| Trend strength | adx, plus_di vs minus_di |
| Volatility-scaled stop / band | atr, bb_*, kc_* |
| Breakout channel | dc_*, bb_* |
| Volume pressure | obv, ad, cmf, pvt, mfi |
| Swing structure | pivothigh, pivotlow, pivotAvg |
| Continuous trend signal (HL2 ± ATR) | supertrend + supertrend_dir |
| Continuous trend signal (custom basis) | trail + trail_dir |
| Anchored cumulative (session VWAP, etc.) | cumReset + maxSinceReset + minSinceReset (see Series) |
Next
- Statistics —
stdev,correlation,linreg,polyreg2,zscore. - Drawing — turning these series into chart objects.
- Recipes — full scripts that combine multiple indicators.