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:
| Value | Placement |
|---|---|
"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.