Panes
How @pane controls where your indicator lives — overlay on candles, sub-pane below, or a custom aux pane shared across multiple scripts. Plus paneRange / paneHeight tuning.
The @pane directive at the top of a script picks where the indicator renders. Three shapes are accepted:
@pane value | Where it renders | Y axis |
|---|---|---|
"overlay" (default, or omit) | On the candle pane | Price |
"below" | A reserved band at the bottom of the candle area | Pane value space (auto or paneRange) |
Any other id (e.g. "rsi", "macd") | A dedicated aux pane keyed by id | Pane value space |
Every drawing primitive belongs to one of three categories — overlay-only, pane-only, either-or. Calling a pane primitive from an @pane "overlay" script renders the value at its raw price-space coordinate (which usually looks wrong — RSI 0-100 collapses into a 50-tick band on a chart trading at $50k). The mismatch is intentional: it's a visible cue that the @pane directive is missing.
Overlay (@pane "overlay", default)
The script paints on the candle pane. Use this for moving averages, support / resistance, anchored VWAPs, range boxes — anything in price space.
Available primitives: every plot* function plus the world-coord primitives (plotLineXY, plotBox, plotLabel, …) and persistent slots (labelNew / boxNew / lineNew / polylineNew) with pane="overlay".
@name "Anchored VWAP"
@pane "overlay"
@input length = input.int(50, "Length", minval=10, maxval=500)
vw = sum(close * volume, length) / sum(volume, length)
plotLine(vw, color="#F0B90B", width=2)
bb_u = bb_upper(close, length, 2)
bb_l = bb_lower(close, length, 2)
plotLine(bb_u, color="rgba(240,185,11,0.5)")
plotLine(bb_l, color="rgba(240,185,11,0.5)")
plotBand(bb_u, bb_l, color="rgba(240,185,11,0.04)")
Drawing oscillators on the overlay
Native overlay mode draws values in their own numeric range. An RSI line plotted on a Bitcoin chart appears at y = 70 (not 70 in the price scale, but 70 dollars), invisible against price candles in the thousands. To display an oscillator on the candle pane, use normalize to map it into the visible price range:
@pane "overlay"
r = rsi(close, 14)
priceLow = lowest(low, 200)
priceHigh = highest(high, 200)
rsiOverlay = normalize(r, 0, 100, priceLow, priceHigh)
plotLine(rsiOverlay, color="#F0B90B", width=1.5)
normalize accepts series for both toMin and toMax, so the overlay tracks the visible price window as the user pans / zooms.
For most oscillators this trick is unnecessary — just use @pane "below" instead.
Sub-pane (@pane "below")
@pane "below" reserves a band at the bottom of the candle area, masks the candles behind it opaque, and remaps every pane* call into the band. The pane has its own Y axis with three labels (min / mid / max) and the script's @name displayed in the top-left.
@pane "below"
@name "RSI"
@input period = input.int(14, "Period", minval=2, maxval=200)
paneHeight(0.25) // 25 % of the candle area (0.10..0.50)
paneRange(0, 100) // lock the Y axis (omit to auto-fit)
r = rsi(close, period)
paneBand(70, 30, color="rgba(240,185,11,0.06)")
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)")
paneLabel(70, "70", align="left")
paneLabel(30, "30", align="left")
Pane primitives
| Function | Purpose |
|---|---|
paneLine(series, color, width=1.5) | Polyline inside the band |
paneDashedLine(series, color, width=1, dash, gap) | Dashed polyline |
paneHline(value, color, width=1) | Horizontal at a fixed pane value |
paneBand(upper, lower, color) | Filled band between two pane series |
paneFill(series, base=0, color) | Histogram fill between base and series[i] |
paneCandle(open, high, low, close, …) | Per-bar OHLC inside the pane |
paneLabel(value, text, color, size=10, align) | Annotate a level |
paneRange(min, max) | Lock Y axis (else auto-fit each frame) |
paneHeight(ratio) | Pane height as fraction of candle area, 0.10..0.50 |
panePlotLine, panePlotVLine, panePlotBox, panePlotLabel | World-coord pane primitives |
paneRange and paneHeight
Both are one-shot configuration calls. Call each once near the top of the script. Multiple calls override (the last wins).
paneRange(min, max)locks the Y axis. Without it, the engine auto-scans every pane draw call for the visible window's[min, max]each frame, which is right for MACD-style indicators but produces a "wobbling" axis on RSI (range expands the moment the line touches a new extreme).paneHeight(ratio)sets the pane's vertical real estate as a fraction of the candle area. The default is0.25(a quarter of the chart). Range:0.10(a thin strip) to0.50(half the chart).
When to use paneFill vs paneBand
| You want | Reach for |
|---|---|
| Solid colour between TWO series | paneBand(upper, lower, color) |
| Histogram bars from 0 (or a fixed value) | paneFill(series, base=0, color) |
paneFill is the canonical primitive for MACD histograms, momentum bars, anything zero-anchored.
@pane "below"
line = macd_line(close, 12, 26)
sig = macd_signal(close, 12, 26, 9)
hist = macd_hist(close, 12, 26, 9)
// Two-tone histogram (positive / negative bars):
posHist = mask(hist, hist >= 0)
negHist = mask(hist, hist < 0)
paneFill(posHist, base=0, color="rgba(14,203,129,0.5)")
paneFill(negHist, base=0, color="rgba(246,70,93,0.5)")
paneLine(line, color="#0ECB81", width=1.5)
paneLine(sig, color="#F6465D", width=1.5)
Pane size on mobile
The paneHeight(0.25) ratio applies on every device. On phones (where the chart area is short), this means the pane consumes roughly the same fraction of available space — usually the right call. If the user has the chart maximised on desktop and the pane looks too tall, drop to 0.18 or 0.20.
Custom aux panes (any other id)
Any string that is not "overlay" or "below" allocates a dedicated aux pane. Two scripts using the same id share one band — useful for compound indicators (MACD line + signal + histogram all in @pane "macd"). Different ids stack vertically in registration order.
@pane "rsi"
@name "RSI"
@input period = input.int(14, "Period")
paneHeight(0.18)
paneRange(0, 100)
r = rsi(close, period)
paneBand(70, 30, color="rgba(240,185,11,0.06)")
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)")
Pane id rules
- Lowercase letters, digits,
_, and-only. - Maximum 32 characters.
"overlay"and"below"are reserved.
Sharing a pane across scripts
Two scripts with the same aux pane id collapse onto one band. They keep separate slot tables (a lineNew("trail", …) in each script doesn't conflict) but share the pane's Y axis.
// Script A
@pane "macd"
@name "MACD line"
hist = macd_hist(close, 12, 26, 9)
paneFill(hist, base=0, color="rgba(240,185,11,0.4)")
// Script B (separate file, same chart)
@pane "macd"
@name "MACD signals"
line = macd_line(close, 12, 26)
sig = macd_signal(close, 12, 26, 9)
paneLine(line, color="#0ECB81")
paneLine(sig, color="#F6465D")
Both scripts paint into the same aux band. Disable / re-enable to reorder.
Stacking multiple aux panes
Different ids → different bands, registered in script-load order:
// Script A → @pane "rsi"
// Script B → @pane "macd"
// Script C → @pane "vol"
The user sees three stacked panes below the candles. The order is fixed at registration time; to reorder, disable + re-enable in the desired sequence.
Persistent slots in a pane
Every persistent slot primitive (labelNew, boxNew, lineNew, polylineNew) accepts a pane= argument so a single script can scatter HUDs across multiple panes:
@pane "rsi"
@name "RSI HUD"
r = rsi(close, 14)
labelNew("rsi_value", time[0], r[0], "RSI " + tostring(r[0], "0.0"),
pane="rsi", anchor="left", color="#F0B90B")
The label lives in the "rsi" pane's slot table, distinct from any "overlay" or "below" slots the script might also be using.
Limitations
- Aux pane order is registration order. To reorder, disable the scripts in the order you want and re-enable.
- The legacy
"below"pane is one shared band — any script with@pane "below"paints into it. Use a custom id for true separation. - Crosshair price reads from the candle pane on the candle area; over an aux pane it reads that pane's value space. The legacy
"below"pane still reports candle price for backwards compatibility.
Next
- Drawing reference — every primitive, scoped per pane.
- Alerts — alerts work the same in every pane.
- Recipes — multi-pane scripts.