Quickstart & Lifecycle
Raise the polyline budget, import the library, keep one source and one Canvas, fill the source, and draw once per update.
Setup requirements
//@version=6
indicator("My Canvas", overlay = false, max_polylines_count = 100)
import Alien_Algorithms/PineCanvas/1 as pcPineCanvas draws with polylines, so raise max_polylines_count to 100, which is the platform maximum. Imports are pinned to a version number. Use the latest one shown on the TradingView publication.
Your first picture
//@version=6
indicator("PineCanvas - Candle Colours", overlay = false, max_polylines_count = 100)
import Alien_Algorithms/PineCanvas/1 as pc
// Make sure to update to the latest library version
var pc.PaletteField pixels = pc.paletteField(2, 30)
var pc.Canvas canvas = pc.newCanvas(array.from(color.red, color.lime))
if barstate.islast and bar_index >= 29
for col = 0 to 29
pixels.set(0, col, close[29 - col] >= open[29 - col] ? 1 : 0)
canvas.drawLookback(pixels, 0, 0.0, 2.0)A PaletteField holds colour indices in a grid. The Canvas is given a palette of two colours, each cell selects one of them, and one call draws the strip. Each candle contributes one coloured cell. The second row is never written, so it stays transparent.
The lifecycle
- Create once: keep sources and canvases in
varvariables so they persist from bar to bar. - Fill the source: write values or palette indices into a field, or push one column per bar into a scroll.
- Style the Canvas: choose the palette or gradient, the colour levels, opacity, gamma, and how values are scaled.
- Draw once: call a draw method once per update, normally inside
barstate.islast.
Rows, columns, and transparency
- Row 0 is the bottom row. Call
invertRows()on the Canvas to put row 0 at the top. - Column 0 is the left, oldest column. The last column is the newest.
- Transparent cells cost nothing. In a value field that is
na. In a palette field it is the index-1.
Both field constructors start every cell transparent, so a picture only pays for the cells it sets.
Your first heatmap
//@version=6
indicator("PineCanvas - Rolling RSI Heatmap", overlay = false,
max_polylines_count = 100)
import Alien_Algorithms/PineCanvas/1 as pc
// Make sure to update to the latest library version
// Stateful series calculations must run on every bar.
float rsi5 = ta.rsi(close, 5)
float rsi8 = ta.rsi(close, 8)
float rsi13 = ta.rsi(close, 13)
float rsi21 = ta.rsi(close, 21)
float rsi34 = ta.rsi(close, 34)
var pc.ValueScroll history = pc.valueScroll(5, 150)
var pc.Canvas heat = pc.heatmap(
pc.gradient(#602060, #305bb4, #31b6ad, #f4d24c, #f07735))
.colorLevels(32)
.valueRange(0.0, 100.0)
.opacity(25, 100)
if barstate.isconfirmed
history.push(array.from(rsi5, rsi8, rsi13, rsi21, rsi34))
if barstate.islast
heat.drawLookback(history, 0.0, 5.0)A ValueScroll keeps a moving window when one new column arrives each bar. Five RSI lengths become five rows, and each confirmed bar pushes one column, bottom row first. pc.heatmap() returns a Canvas preset for numerical data, and valueRange(0, 100) pins the colours to the RSI scale so they mean the same thing on every bar.
A separate pane or the price chart
In a separate pane
With overlay = false the vertical bounds are just numbers. The first example draws between 0 and 2, the heatmap between 0 and 5.
On the price chart
With overlay = true, or with forceOverlay() on the Canvas, the bounds are prices. Pass the real lowest and highest price the picture should span, and it stays anchored to the candles. Placement & Coordinates covers every way to position a field.
Global-scope series calculations
Compute history-dependent values such as ta.rsi(), ta.sma(), ta.highest(), and request.security() at global scope, on every bar. Read their results inside the barstate.islast block afterwards. Calling them only on the last bar gives them no history to work from.