Indicator Examples
SMA Overlay
A simple moving average plotted on the price chart.
indicator('My SMA', { overlay: true })
const len = input.int('length', 20, 'Length', { min: 2, max: 500 })
const src = input.source('source', 'close', 'Source')
const sma = ta.sma(src, len)
plot(sma, 'SMA', '#2962ff')How it works: input.int creates a configurable length parameter. input.source lets the user choose which price to average. ta.sma computes the SMA and returns a Series that plot draws as a blue line.
Bollinger Bands
Upper, middle, and lower bands with a shaded fill.
indicator('My Bollinger', { overlay: true })
const len = input.int('length', 20, 'Length', { min: 2, max: 200 })
const mult = input.float('mult', 2.0, 'StdDev', { min: 0.1, max: 5 })
const bb = ta.bollinger(close, len, mult)
plot(bb.middle, 'Middle', '#2962ff')
plot(bb.upper, 'Upper', '#f44336')
plot(bb.lower, 'Lower', '#4caf50')
fill(bb.upper, bb.lower, 'rgba(33,150,243,0.1)')How it works: ta.bollinger returns three Series — middle (SMA), upper (SMA + mult × stddev), and lower (SMA - mult × stddev). fill shades the area between the upper and lower bands.
RSI Sub-pane
RSI displayed in its own pane below the price chart, with horizontal reference lines.
indicator('My RSI', { overlay: false })
const len = input.int('length', 14, 'Length', { min: 2, max: 100 })
const src = input.source('source', 'close', 'Source')
const rsi = ta.rsi(src, len)
plot(rsi, 'RSI', '#7E57C2')
hline(70, 'Overbought', '#EF5350')
hline(30, 'Oversold', '#26A69A')How it works: overlay: false puts this indicator in a sub-pane. hline draws fixed horizontal lines at the overbought/oversold levels. RSI values range from 0 to 100.
MACD Histogram
MACD with signal line, shown in a sub-pane.
indicator('My MACD', { overlay: false })
const fast = input.int('fast', 12, 'Fast Period')
const slow = input.int('slow', 26, 'Slow Period')
const sig = input.int('signal', 9, 'Signal Period')
const m = ta.macd(close, fast, slow, sig)
plot(m.macd, 'MACD', '#2962ff')
plot(m.signal, 'Signal', '#ff6d00')
plot(m.histogram, 'Histogram', '#26A69A')How it works: ta.macd returns three Series. The histogram is the difference between the MACD line and the signal line. All three are plotted in a sub-pane.
Multi-Timeframe Daily SMA
Plot the daily 50-period SMA on a lower-timeframe chart using request.timeframe.
indicator('Daily SMA Overlay', { overlay: true })
const daily = request.timeframe('D')
const dailySMA = ta.sma(daily.close, input.int('length', 50, 'Daily SMA Length', { min: 5, max: 200 }))
plot(dailySMA, 'Daily SMA', '#FF9800', { lineWidth: 2 })How it works: request.timeframe('D') fetches daily OHLCV data, forward-filled to match the chart's bar count. ta.sma is then applied to the daily close Series. The result is a smooth line showing the daily moving average even on a 5-minute or 1-hour chart.
AMD — Accumulation / Manipulation / Distribution
A stateful pattern detector that marks the ICT "Power of 3" cycle with labeled boxes. It tracks price structure across bars using state and draws zones with box. Works on any symbol and any timeframe — no sessions or fixed times.
indicator('AMD (Accum / Manip / Distrib)', { overlay: true })
// A tight consolidation (Accumulation) is followed by a liquidity sweep that
// closes back inside the range (Manipulation), then an expansion that closes
// beyond the OPPOSITE side (Distribution).
const accumBars = input.int('accumBars', 10, 'Accumulation Lookback (bars)', { min: 3, max: 200 })
const rangeFactor = input.float('rangeFactor', 1.5, 'Max Range (x ATR)', { min: 0.2, max: 10, step: 0.1 })
const atrLen = input.int('atrLen', 14, 'ATR Length', { min: 1, max: 200 })
const waitSweep = input.int('waitSweep', 10, 'Max Bars to Sweep', { min: 1, max: 200 })
const waitDist = input.int('waitDist', 15, 'Max Bars to Distribute', { min: 1, max: 200 })
const showLabels = input.bool('showLabels', true, 'Show Labels')
const accColor = input.color('accColor', '#2962FF', 'Accumulation Color')
const manipColor = input.color('manipColor', '#FF9800', 'Manipulation Color')
const bullColor = input.color('bullColor', '#26A69A', 'Bullish Distribution')
const bearColor = input.color('bearColor', '#EF5350', 'Bearish Distribution')
const atr = ta.atr(atrLen)
const rngHigh = ta.highest(high, accumBars)
const rngLow = ta.lowest(low, accumBars)
// phase: 0 = seek range, 1 = await sweep, 2 = await expansion
state.phase = state.phase ?? 0
state.accHigh = state.accHigh ?? 0
state.accLow = state.accLow ?? 0
state.accT0 = state.accT0 ?? 0
state.accT1 = state.accT1 ?? 0
state.dir = state.dir ?? 0
state.swExt = state.swExt ?? 0
state.swT = state.swT ?? 0
state.bars = state.bars ?? 0
const i = bar_index
const a = atr[0]
const rH = rngHigh[0]
const rL = rngLow[0]
if (state.phase === 0) {
const ready = i >= accumBars && isFinite(a) && a > 0 && isFinite(rH) && isFinite(rL)
if (ready && (rH - rL) <= rangeFactor * a) {
state.accHigh = rH; state.accLow = rL
state.accT0 = time[accumBars - 1]; state.accT1 = time[0]
state.phase = 1; state.bars = 0
}
} else if (state.phase === 1) {
state.bars = state.bars + 1
const sweptUp = high[0] > state.accHigh && close[0] < state.accHigh
const sweptDn = low[0] < state.accLow && close[0] > state.accLow
if (sweptUp || sweptDn) {
state.dir = sweptUp ? 1 : -1
state.swExt = sweptUp ? high[0] : low[0]
state.swT = time[0]; state.phase = 2; state.bars = 0
} else if (close[0] > state.accHigh || close[0] < state.accLow) {
state.phase = 0 // clean breakout, no liquidity grab
} else if (state.bars > waitSweep) {
state.phase = 0
}
} else if (state.phase === 2) {
state.bars = state.bars + 1
const distBear = state.dir === 1 && close[0] < state.accLow
const distBull = state.dir === -1 && close[0] > state.accHigh
if (distBear || distBull) {
const tNow = time[0]
const distColor = distBull ? bullColor : bearColor
box(state.accT0, state.accT1, state.accHigh, state.accLow, {
color: accColor + '22', strokeColor: accColor,
label: showLabels ? 'Accumulation' : undefined, labelColor: accColor,
})
if (state.dir === 1) {
box(state.accT1, state.swT, state.swExt, state.accHigh, {
color: manipColor + '2E', strokeColor: manipColor, dash: [4, 3],
label: showLabels ? 'Manipulation' : undefined, labelColor: manipColor,
})
} else {
box(state.accT1, state.swT, state.accLow, state.swExt, {
color: manipColor + '2E', strokeColor: manipColor, dash: [4, 3],
label: showLabels ? 'Manipulation' : undefined, labelColor: manipColor,
})
}
if (distBull) {
box(state.swT, tNow, close[0], state.accHigh, {
color: distColor + '24', strokeColor: distColor,
label: showLabels ? 'Distribution' : undefined, labelColor: distColor,
})
} else {
box(state.swT, tNow, state.accLow, close[0], {
color: distColor + '24', strokeColor: distColor,
label: showLabels ? 'Distribution' : undefined, labelColor: distColor,
})
}
state.phase = 0; state.bars = 0
} else if (state.bars > waitDist) {
state.phase = 0
}
}How it works: A three-state machine persists across bars via state. Accumulation locks a range once the last accumBars bars are tight relative to ATR (range ≤ rangeFactor × ATR). Manipulation is a wick beyond one side that closes back inside the range — a liquidity sweep; a clean close outside instead cancels the setup. Distribution confirms when price closes beyond the opposite boundary. On completion it draws three labeled boxes — blue accumulation, dashed-orange manipulation at the swept extreme, and a green/red expansion box — using the box options object (see Plotting).