Plotting
Plot series, arrows, labels, circles, candles, backgrounds, and fills.
Plot APIs are called inside onBar() and write draw commands for the current bar.
Limits and Guardrails
| Limit | Value |
|---|---|
| Max plot series labels | 64 |
| Max plot commands per bar | 64 |
| Max fill pairs per bar | 16 |
Additional behavior:
- Reusing the same label in a bar updates that slot (last call wins).
fill()requires series indices returned by plot functions.fill()is limited to16calls per bar.forceOverlayis available on all plot functions andbg(). It is sticky per series: once any call for a series usesforceOverlay: true, that series stays overlay-targeted for the rest of the runtime.fill()requires both source series to use the same stickyforceOverlaystate.- For value-based plot series (
plot,plotHistogram,plotMarker,plotLabel,plotCircle,plotArrow,plotCandle), if you do not call the plot function on a bar, that bar is treated as missing (na).
Value Conventions
- Numeric plot values accept
numberorna. narender as missing (NaN) for that value.- Colors accept hex strings (
#RRGGBB/#RRGGBBAA) or ABGR numbers. - Plot offsets: negative values shift left to earlier bars, positive values shift right.
This is especially useful with delayed-confirmation functions such as pivots:
//@version=2
indicator("Pivot Plot Example", true)
const candles = subscribe(data.OHLCV)
function onBar(index) {
const ph = ta.pivothigh(candles.high(), 2, 2)
plot("Pivot High", na(ph) ? NaN : ph, { color: color.red, offset: -2 })
}forceOverlay
indicator(name, overlay) still sets the default chart target for the whole script. forceOverlay is an opt-in override on individual plot series and bg():
- In
indicator(..., false)scripts,forceOverlay: truepromotes that series to the main overlay chart. - In
indicator(..., true)scripts,forceOverlayis redundant because everything already targets the overlay chart. - Promotion is sticky per series label. A later call with
forceOverlay: falsedoes not move that series back into the pane. fill()does not take its ownforceOverlayoption. Both source series must already have the same stickyforceOverlaystate.
Example: promote one series from a non-overlay script:
//@version=2
indicator("Mixed Overlay Example", false)
const candles = subscribe(data.OHLCV)
function onBar(index) {
plot("Pane EMA", ta.ema(candles.close(), 21), { color: color.blue })
plot("Overlay Close", candles.close(), { color: color.yellow, forceOverlay: true })
}Example: in an overlay script, forceOverlay changes nothing:
//@version=2
indicator("Overlay Script", true)
const candles = subscribe(data.OHLCV)
function onBar(index) {
plot("Close", candles.close(), { color: color.green, forceOverlay: true })
}Invalid fill() mismatch:
//@version=2
indicator("Invalid Fill Overlay Mix", false)
const candles = subscribe(data.OHLCV)
function onBar(index) {
const pane = plot("Pane", ta.ema(candles.close(), 9))
const overlay = plot("Overlay", candles.close(), { forceOverlay: true })
fill(pane, overlay, { color: color.transp(color.blue, 85) }) // throws
}plot
plot(label, value, options?) -> seriesIndexplotLine(...) is an alias for plot(...) and accepts the same options.
//@version=2
indicator("plot Example", true)
const candles = subscribe(data.OHLCV)
function onBar(index) {
plot("Close", candles.close(), {
color: color.blue,
width: 2,
style: linestyle.solid,
shaded: false,
showLabel: true,
showValue: true
})
}| Option | Type | Default | Description |
|---|---|---|---|
color | color | color.white | Line color |
width | number | 1 | Line width in pixels |
style | linestyle.solid | linestyle.dotted | linestyle.dashed | linestyle.solid | Line style (Solid, Dotted, Dashed) |
shaded | bool | false | Fill area below line |
showLabel | bool | false | Show label on chart |
showValue | bool | true | Show current value on price axis |
offset | number | 0 | Bar offset |
forceOverlay | bool | false | Promote this series to the overlay chart. Sticky per label. |
plotHistogram
plotHistogram(label, value, options?) -> seriesIndex//@version=2
indicator("plotHistogram Example", false)
const candles = subscribe(data.OHLCV)
function onBar(index) {
plotHistogram("Volume", candles.volume(), { color: color.teal })
}| Option | Type | Default | Description |
|---|---|---|---|
color | color | color.white | Bar color |
showLabel | bool | true | Show label on chart |
showValue | bool | true | Show current value on price axis |
offset | number | 0 | Bar offset |
forceOverlay | bool | false | Promote this series to the overlay chart. Sticky per label. |
plotMarker
plotMarker(label, value, options?) -> seriesIndex//@version=2
indicator("plotMarker Example", true)
const candles = subscribe(data.OHLCV)
function onBar(index) {
plotMarker("Signal", candles.high(), {
color: color.red,
marker: shape.diamond,
size: 8,
border: 1,
borderColor: color.white
})
}| Option | Type | Default | Description |
|---|---|---|---|
color | color | color.white | Marker fill color |
marker | shape.* | shape.circle | Marker shape |
size | number | 5 | Marker size in pixels |
border | number | 0 | Border width |
borderColor | color | — | Border color |
offset | number | 0 | Bar offset |
forceOverlay | bool | false | Promote this series to the overlay chart. Sticky per label. |
plotCandle
plotCandle(label, open, high, low, close, options?) -> seriesIndex//@version=2
indicator("plotCandle Example", false)
const cvd = subscribe(data.CVD)
function onBar(index) {
const o = cvd.open()
const h = cvd.high()
const l = cvd.low()
const c = cvd.close()
plotCandle("CVD", o, h, l, c, {
bodyColor: c >= o ? color.green : color.red,
wickColor: color.gray,
borderColor: color.white
})
}| Option | Type | Default | Description |
|---|---|---|---|
bodyColor | color | — | Candle body color |
wickColor | color | — | Wick color |
borderColor | color | — | Body border color |
showLabel | bool | true | Show label on chart |
showValue | bool | true | Show current value on price axis |
offset | number | 0 | Bar offset |
forceOverlay | bool | false | Promote this series to the overlay chart. Sticky per label. |
plotLabel
plotLabel(label, price, text, options?) -> seriesIndex//@version=2
indicator("plotLabel Example", true)
const candles = subscribe(data.OHLCV)
function onBar(index) {
plotLabel("tag", candles.close(), "Breakout", {
color: color.white,
size: 12,
offset: 0
})
}| Option | Type | Default | Description |
|---|---|---|---|
color | color | color.white | Text color |
size | number | 12 | Font size |
offset | number | 0 | Bar offset |
forceOverlay | bool | false | Promote this series to the overlay chart. Sticky per label. |
plotCircle
plotCircle(label, price, options?) -> seriesIndex//@version=2
indicator("plotCircle Example", true)
const candles = subscribe(data.OHLCV)
function onBar(index) {
plotCircle("dot", candles.close(), { color: color.orange, size: 7, offset: 0 })
}| Option | Type | Default | Description |
|---|---|---|---|
color | color | color.white | Circle color |
size | number | 8 | Circle size in pixels |
offset | number | 0 | Bar offset |
forceOverlay | bool | false | Promote this series to the overlay chart. Sticky per label. |
plotArrow
plotArrow(label, value, options?) -> seriesIndex//@version=2
indicator("plotArrow Example", true)
const candles = subscribe(data.OHLCV)
function onBar(index) {
plotArrow("Momentum", ta.change(candles.close()), {
colorUp: color.green,
colorDown: color.red,
minHeight: 6,
maxHeight: 24,
paddingPx: 2,
shaftWidth: 1.5,
headLengthPx: 8,
headWidthRatio: 0.6,
showLabel: true,
showValue: true
})
}Requires //@version=2.
Positive values draw upward arrows, negative values draw downward arrows, and 0 or na draw nothing. Arrow height scales between minHeight and maxHeight based on the largest absolute value in the series. In overlay scripts, arrow tips anchor to the candle low for positive values and the candle high for negative values.
| Option | Type | Default | Description |
|---|---|---|---|
colorUp | color | color.green | Color for positive arrows |
colorDown | color | color.red | Color for negative arrows |
offset | number | 0 | Bar offset |
minHeight | number | 6 | Minimum arrow height in pixels |
maxHeight | number | 24 | Maximum arrow height in pixels |
paddingPx | number | 2 | Gap in pixels between the anchor point and the shaft |
shaftWidth | number | 1.5 | Arrow shaft width in pixels |
headLengthPx | number | 8 | Arrow head length in pixels |
headWidthRatio | number | 0.6 | Arrow head width relative to head length |
showLabel | bool | false | Show label on chart |
showValue | bool | false | Show current value on price axis |
forceOverlay | bool | false | Promote this series to the overlay chart. Sticky per label. |
bg
bg(label, options)Sets a background color for the current bar.
//@version=2
indicator("bg Example", true)
const candles = subscribe(data.OHLCV)
function onBar(index) {
if (candles.close() > candles.open()) {
bg("session", { color: color.transp(color.blue, 85), offset: 0 })
}
}| Option | Type | Default | Description |
|---|---|---|---|
color | color | — | Background color (use color.transp() for transparency) |
offset | number | 0 | Bar offset |
forceOverlay | bool | false | Promote this background series to the overlay chart. Sticky per label. |
fill
fill(series1, series2, options?)Fills the area between two plot series.
For more complete gradient and overlay fill examples, see Colors & Gradients.
//@version=2
indicator("fill Example", true)
const candles = subscribe(data.OHLCV)
function onBar(index) {
const fast = plot("Fast", ta.ema(candles.close(), 9), { color: color.blue })
const slow = plot("Slow", ta.ema(candles.close(), 21), { color: color.red })
fill(fast, slow, { color: color.transp(color.blue, 90) })
}| Option | Type | Default | Description |
|---|---|---|---|
color | color | — | Solid fill color. Takes precedence over gradient options when present. |
topValue | number | — | Gradient upper bound. Requires bottomValue, topColor, and bottomColor. |
bottomValue | number | — | Gradient lower bound. Requires topValue, topColor, and bottomColor. |
topColor | color | — | Gradient color at or above topValue. Requires the full gradient option set. |
bottomColor | color | — | Gradient color at or below bottomValue. Requires the full gradient option set. |
Gradient fills use the same fill(series1, series2, options) shape:
//@version=2
indicator("fill Gradient Example", true)
const candles = subscribe(data.OHLCV)
function onBar(index) {
const upper = plot("Upper", candles.high())
const lower = plot("Lower", candles.low())
fill(upper, lower, {
topValue: candles.high(),
bottomValue: candles.low(),
topColor: color.transp(color.green, 80),
bottomColor: color.transp(color.red, 80),
})
}If any gradient option is provided without the full set of topValue, bottomValue, topColor, and bottomColor, fill() throws an error.
fill() also throws if series1 and series2 do not use the same sticky forceOverlay value.