MMTMMT Docs

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

LimitValue
Max plot series labels64
Max plot commands per bar64
Max fill pairs per bar16

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 to 16 calls per bar.
  • forceOverlay is available on all plot functions and bg(). It is sticky per series: once any call for a series uses forceOverlay: true, that series stays overlay-targeted for the rest of the runtime.
  • fill() requires both source series to use the same sticky forceOverlay state.
  • 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 number or na.
  • na render 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: true promotes that series to the main overlay chart.
  • In indicator(..., true) scripts, forceOverlay is redundant because everything already targets the overlay chart.
  • Promotion is sticky per series label. A later call with forceOverlay: false does not move that series back into the pane.
  • fill() does not take its own forceOverlay option. Both source series must already have the same sticky forceOverlay state.

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?) -> seriesIndex

plotLine(...) 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
  })
}
OptionTypeDefaultDescription
colorcolorcolor.whiteLine color
widthnumber1Line width in pixels
stylelinestyle.solid | linestyle.dotted | linestyle.dashedlinestyle.solidLine style (Solid, Dotted, Dashed)
shadedboolfalseFill area below line
showLabelboolfalseShow label on chart
showValuebooltrueShow current value on price axis
offsetnumber0Bar offset
forceOverlayboolfalsePromote 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 })
}
OptionTypeDefaultDescription
colorcolorcolor.whiteBar color
showLabelbooltrueShow label on chart
showValuebooltrueShow current value on price axis
offsetnumber0Bar offset
forceOverlayboolfalsePromote 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
  })
}
OptionTypeDefaultDescription
colorcolorcolor.whiteMarker fill color
markershape.*shape.circleMarker shape
sizenumber5Marker size in pixels
bordernumber0Border width
borderColorcolorBorder color
offsetnumber0Bar offset
forceOverlayboolfalsePromote 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
  })
}
OptionTypeDefaultDescription
bodyColorcolorCandle body color
wickColorcolorWick color
borderColorcolorBody border color
showLabelbooltrueShow label on chart
showValuebooltrueShow current value on price axis
offsetnumber0Bar offset
forceOverlayboolfalsePromote 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
  })
}
OptionTypeDefaultDescription
colorcolorcolor.whiteText color
sizenumber12Font size
offsetnumber0Bar offset
forceOverlayboolfalsePromote 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 })
}
OptionTypeDefaultDescription
colorcolorcolor.whiteCircle color
sizenumber8Circle size in pixels
offsetnumber0Bar offset
forceOverlayboolfalsePromote 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.

OptionTypeDefaultDescription
colorUpcolorcolor.greenColor for positive arrows
colorDowncolorcolor.redColor for negative arrows
offsetnumber0Bar offset
minHeightnumber6Minimum arrow height in pixels
maxHeightnumber24Maximum arrow height in pixels
paddingPxnumber2Gap in pixels between the anchor point and the shaft
shaftWidthnumber1.5Arrow shaft width in pixels
headLengthPxnumber8Arrow head length in pixels
headWidthRationumber0.6Arrow head width relative to head length
showLabelboolfalseShow label on chart
showValueboolfalseShow current value on price axis
forceOverlayboolfalsePromote 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 })
  }
}
OptionTypeDefaultDescription
colorcolorBackground color (use color.transp() for transparency)
offsetnumber0Bar offset
forceOverlayboolfalsePromote 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) })
}
OptionTypeDefaultDescription
colorcolorSolid fill color. Takes precedence over gradient options when present.
topValuenumberGradient upper bound. Requires bottomValue, topColor, and bottomColor.
bottomValuenumberGradient lower bound. Requires topValue, topColor, and bottomColor.
topColorcolorGradient color at or above topValue. Requires the full gradient option set.
bottomColorcolorGradient 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.

On this page