Functions

Drawing Primitives

Per-bar visual primitives: the plotShape marker set with locations and character markers, barcolor(), fillBetween(), and rich table cells.

Intermediate 8 min read

Introduction

These are the declarative per-bar visuals (the counterpart to the stateful Drawing Objects): markers, candle recoloring, and band fills emitted bar by bar like any plot.

Shape markers

plotShape supports ten shapes:

circle, cross, triangle, diamond, arrowUp, arrowDown, flag, square, label, char

if (crossover(fast, slow)) {
  plotShape(low, shape="arrowUp", location="belowBar", colorIndex="#16a34a")
}

Locations

The location parameter places the marker relative to the bar:

ValuePlacement
"aboveBar"Above the bar's high
"belowBar"Below the bar's low
"top" / "bottom"Pinned to the pane edge
"absolute"At the supplied y value (the default; legacy behavior)

Character markers

plotShape(high, shape="char", char="B", location="aboveBar")


`char` must be exactly one character; anything else is a line/column error. Invalid shapes and locations are caught at compile time when literal, at runtime otherwise, always with position info.

barcolor

Recolor the bar's candle itself:

timeseries delta = fp.cells.map((c) => c[2] - c[3]).reduce((s, x) => s + x, 0)

if (delta > 0) { barcolor("#16a34a") }
if (delta < 0) { barcolor("#dc2626") }

Bars you do not color keep the chart default. Colors go through the standard color validation (typed colors and strings both work). Delta-colored candles, driven by real per-bar buy/sell volume, are a one-screen kScript example with no Pine equivalent.

fillBetween

Fill the band between two plotted series:

timeseries upper = sma(source=trade_data, period=20) + stdev(source=trade_data, period=20) * 2
timeseries lower = sma(source=trade_data, period=20) - stdev(source=trade_data, period=20) * 2

fillBetween(upper, lower, "#0ea5e9", 0.15)


`fillBetween(series1, series2, color, opacity?)` references the two series rather than copying them. Both arguments must be plottable series (line/column error otherwise). Conditional fills are fine: bars where you do not call it simply have no band.

Rich table cells

plotTable (the declarative dashboard plot, distinct from the table.new drawing object) accepts per-cell objects alongside plain scalars:

plotTable([
  ["Metric", "Value"],
  [{ value: "CVD", tooltip: "cumulative volume delta" },
   { value: tostring(cvd, "0.00"), color: cvd > 0 ? "#16a34a" : "#dc2626" }]
], position="top_right")


Cell fields: `value`, `color`, `textColor`, `tooltip`, `align`, `colspan`, `rowspan`. Scalar cells behave exactly as before (full backward compatibility); spans must be positive integers; colors are validated; misuse reports line/column.

Everything rolls back correctly

All of these primitives are per-bar outputs, so they participate in the engine's forming-bar rollback: a conditional barcolor or fillBetween evaluated during a live, still-forming bar is replayed cleanly on every tick. The differential harness pins this byte-for-byte.