Canvas & Drawing

Placement & Coordinates

A draw call maps a field's columns to bars or timestamps and its rows to a vertical range. Each call returns the Canvas and replaces its previous drawings.

Updated Publisher Alien_AlgorithmsPine Script v6

Five ways to place a field

CallHorizontal positionUse it when
drawLookback()Ends at the current barThe picture follows the latest bar. This is the usual choice.
draw()Between two bar indicesThe picture belongs to a fixed stretch of bars.
drawTime()Between two timestampsThe stretch is far in the past, or defined by time.
drawGrid()Explicit bar-index edgesColumns or rows have uneven widths.
drawTimeGrid()Explicit timestamp edgesUneven columns placed by time.

All five accept a ValueField or a PaletteField. Rolling sources are drawn with their own form of drawLookback().

drawLookback

Ending at the current barpine
// field, lookback, bottom, top, rightOffset = 0
canvas.drawLookback(field, 0, bottomPrice, topPrice)       // one bar per column
canvas.drawLookback(field, 600, bottomPrice, topPrice)     // stretched over 600 bars
canvas.drawLookback(field, 0, bottomPrice, topPrice, 20)   // right edge 20 bars ahead
  • lookback is the width in bars. 0 means one bar per column. Any other value must be at least the number of columns.
  • rightOffset moves the right edge away from the current bar, forward when positive and back when negative.
  • The left edge sits at bar_index + rightOffset minus the width. It cannot be before the first bar, so guard the call until enough bars exist.
Wait for enough historypine
if barstate.islast and bar_index >= field.columns
    canvas.drawLookback(field, 0, bottomPrice, topPrice)

draw

Between two bar indicespine
// field, leftIndex, rightIndex, bottom, top
canvas.draw(field, bar_index - 300, bar_index - 100, bottomPrice, topPrice)

The span between the two indices must be at least the number of columns, so that every column is at least one bar wide.

drawTime

Between two timestampspine
// field, leftTime, rightTime, bottom, top (UNIX time in milliseconds)
canvas.drawTime(field, sessionOpenTime, time, bottomPrice, topPrice)

Time placement is not limited to 10,000 bars of history. Use it for distant pictures, and whenever the edges come from session or event times.

Explicit grid edges

drawGrid() and drawTimeGrid() take the position of every cell edge, which allows columns and rows of different sizes. An edge array has one more entry than the field has cells in that direction, and its values must strictly increase.

A field of 3 rows and 4 columns on uneven edgespine
array<int> columnEdges = array.from(bar_index - 40, bar_index - 30, bar_index - 15, bar_index - 5, bar_index)
array<float> rowEdges = array.from(100.0, 101.0, 103.0, 106.0)

canvas.drawGrid(field, columnEdges, rowEdges)

drawTimeGrid(field, timeEdges, rowEdges) is the same with timestamps in milliseconds as the horizontal edges.

Vertical bounds

On the price chart

bottom and top are prices. Row 0 starts at bottom and the last row ends at top, with rows of equal height between them. A value at a given price belongs to the row at int(math.floor((price - bottom) / rowHeight)), where rowHeight is (top - bottom) / rows.

In a separate pane

The bounds are any numbers. Using the row count as top and 0 as bottom makes each row one unit tall, which keeps the pane's scale readable.

Logarithmic rows

With logarithmicY() set on the Canvas, the rows between bottom and top are spaced logarithmically. Both bounds must be positive. This matches a chart on a log price scale.

top must exceed bottom
A draw call with top at or below bottom, or with either bound na, stops the script. Check the range before drawing when it is computed from data.

Bounds and history limits

  • 10,000 bars back: a bar-index placement cannot start further back than that. Use drawTime() or drawTimeGrid() instead.
  • 500 bars forward: a bar-index placement cannot end further ahead than that.
  • Not before the first bar: the left edge cannot be a negative bar index.
  • 2 by 2 at least: a field needs at least 2 rows and 2 columns to be drawn.

Framing a separate pane

A pane scales itself to its plots, not to its drawings. Two invisible plots at the picture's vertical bounds keep the whole picture in view.

Invisible anchorspine
plot(0.0, "Frame low", color = color.new(color.white, 100), editable = false)
plot(float(field.rows), "Frame high", color = color.new(color.white, 100), editable = false)
PineCanvas is a graphics library for Pine Script. TradingView drawing, memory, and execution limits still apply. © Alien_Algorithms.