Lines and boxes
Introduction
Pine Script™ facilitates drawing lines, boxes, and other geometric formations from code using the line, box, and polyline types. These types provide utility for programmatically drawing support and resistance levels, trend lines, price ranges, and other custom formations on a chart.
Unlike plots, the flexibility of these types makes them particularly well-suited for visualizing current calculated data at virtually any available point on the chart, irrespective of the chart bar the script executes on.
Lines, boxes, and polylines are objects, like labels, tables, and other special types. Scripts reference objects of these types using IDs, which act like pointers. As with other objects, line, box, and polyline IDs are qualified as “series” values, and all functions that manage these objects accept “series” arguments.
Lines drawn by a script may be vertical, horizontal, or angled. Boxes are always rectangular. Polylines sequentially connect multiple vertical, horizontal, angled, or curved line segments. Although all of these drawing types have different characteristics, they do have some things in common:
- Lines, boxes, and polylines can have coordinates at any available location on the chart, including ones at future times beyond the last chart bar.
- Objects of these types can use chart.point instances to set their coordinates.
- The x-coordinates of each object can be bar index or time values,
depending on their specified
xloc
property. - Each object can have one of multiple predefined line styles.
- Scripts can call the functions that manage these objects from within the scopes of loops and conditional structures, allowing iterative and conditional control of their drawings.
- There are limits on the number of these objects that a script can
reference and display on the chart. A single script instance can
display up to 500 lines, 500 boxes, and 100 polylines. Users can
specify the maximum number allowed for each type via the
max_lines_count
,max_boxes_count
, andmax_polylines_count
parameters of the script’s indicator() or strategy() declaration statement. If unspecified, the default is ~50. As with label and table types, lines, boxes, and polylines utilize a garbage collection mechanism that deletes the oldest objects on the chart when the total number of drawings exceeds the script’s limit.
Lines
The built-ins in the line.*
namespace control the creation and
management of
line
objects:
- The line.new() function creates a new line.
- The
line.set_*()
functions modify line properties. - The
line.get_*()
functions retrieve values from a line instance. - The line.copy() function clones a line instance.
- The line.delete() function deletes an existing line instance.
- The
line.all
variable references a read-only
array
containing the IDs of all lines displayed by the script. The
array’s
size
depends on the
max_lines_count
of the indicator() or strategy() declaration statement and the number of lines the script has drawn.
Scripts can call line.set_*()
, line.get_*()
,
line.copy(),
and
line.delete()
built-ins as functions or methods.
Creating lines
The line.new() function creates a new line instance to display on the chart. It has the following signatures:
The first overload of this function contains the first_point
and
second_point
parameters. The first_point
is a
chart.point
representing the start of the line, and the second_point
is a
chart.point
representing the line’s end. The function copies the information from
these
chart points to determine the line’s coordinates. Whether it uses the
index
or time
fields from the first_point
and second_point
as
x-coordinates depends on the function’s xloc
value.
The second overload specifies x1
, y1
, x2
, and y2
values
independently, where x1
and x2
are
int
values representing the starting and ending x-coordinates of the line,
and y1
and y2
are
float
values representing the y-coordinates. Whether the line considers the
x
values as bar indices or timestamps depends on the xloc
value in
the function call.
Both overloads share the same additional parameters:
xloc
xloc
value of
xloc.bar_index
tells the function to use the index
fields of the first_point
and second_point
, and a value of
xloc.bar_time
tells the function to use the time
fields of the points.
xloc
value of
xloc.bar_index
prompts the function to treat the x1
and x2
arguments as bar
index values. When using
xloc.bar_time,
the function will treat x1
and x2
as time values.
bar_index - 9999
. For larger offsets, one can use
xloc.bar_time.
extend
color
style
width
force_overlay
true
, the drawing will display on the main chart pane, even when the script occupies a separate pane. Optional. The default is false
.
The example below demonstrates how one can draw lines in their simplest form. This script draws a new vertical line connecting the open and close prices at the horizontal center of each chart bar:
Note that:
- If the
firstPoint
andsecondPoint
reference identical coordinates, the script will not display a line since there is no distance between them to draw. However, the line ID will still exist. - The script will only display approximately the last 50 lines on
the chart, as it does not have a specified
max_lines_count
in the indicator() function call. Line drawings persist on the chart until deleted using line.delete() or removed by the garbage collector. - The script redraws the line on the open chart bar (i.e., the bar with an orange background highlight) until it closes. After the bar closes, it will no longer update the drawing.
Let’s look at a more involved example. This script uses the previous
bar’s
hl2
price and the current bar’s
high
and low
prices to draw a fan with a user-specified number of lines projecting a
range of hypothetical price values for the following chart bar. It calls
line.new()
within a for loop to
create linesPerBar
lines on each bar:
Note that:
- We’ve included
max_lines_count = 500
in the indicator() function call, meaning the script preserves up to 500 lines on the chart. - Each
line.new()
call copies the information from the
chart.point
referenced by the
firstPoint
andsecondPoint
variables. As such, the script can change theprice
field of thesecondPoint
on each loop iteration without affecting the y-coordinates in other lines.
Modifying lines
The line.*
namespace contains multiple setter functions that modify
the properties of
line
instances:
- line.set_first_point()
and
line.set_second_point()
respectively update the start and end points of the
id
line using information from the specifiedpoint
. - line.set_x1()
and
line.set_x2()
set one of the x-coordinates of the
id
line to a newx
value, which can represent a bar index or time value depending on the line’sxloc
property. - line.set_y1()
and
line.set_y2()
set one of the y-coordinates of the
id
line to a newy
value. - line.set_xy1()
and
line.set_xy2()
update one of the
id
line’s points with newx
andy
values. - line.set_xloc()
sets the
xloc
of theid
line and updates both of its x-coordinates with newx1
andx2
values. - line.set_extend()
sets the
extend
property of theid
line. - line.set_color()
updates the
id
line’scolor
value. - line.set_style()
changes the
style
of theid
line. - line.set_width()
sets the
width
of theid
line.
All setter functions directly modify the id
line passed into the call
and do not return any value. Each setter function accepts “series”
arguments, as a script can change a line’s properties throughout its
execution.
The following example draws lines connecting the opening price of a
timeframe
to its closing price. The script uses the
var
keyword to declare the periodLine
and the variables that reference
chart.point
values (openPoint
and closePoint
) only on the first chart bar, and
it assigns new values to these variables over its execution. After
detecting a
change
on the timeframe
, it sets the color
of the existing periodLine
using
line.set_color(),
creates new values for the openPoint
and closePoint
using
chart.point.now(),
then assigns a new
line
using those points to the periodLine
.
On other bars where the periodLine
value is not
na, the
script assigns a new
chart.point
to the closePoint
, then uses
line.set_second_point()
and
line.set_color()
as methods to update the
line’s properties:
Note that:
- Each line drawing in this example uses the line.style_arrow_right style. See the Line styles section below for an overview of all available style settings.
Line styles
Users can control the style of their scripts’ line drawings by passing
one of the following variables as the style
argument in their
line.new()
or
line.set_style()
function calls:
Argument | Line |
---|---|
line.style_solid | |
line.style_dotted | |
line.style_dashed | |
line.style_arrow_left | |
line.style_arrow_right | |
line.style_arrow_both |
Note that:
- Polylines can also use any of these variables as their
line_style
value. See the Creating polylines section of this page.
Reading line values
The line.*
namespace includes getter functions, which allow a script
to retrieve values from a
line
object for further use:
- line.get_x1()
and
line.get_x2()
respectively get the first and second x-coordinate from the
id
line. Whether the value returned represents a bar index or time value depends on the line’sxloc
property. - line.get_y1()
and
line.get_y2()
respectively get the
id
line’s first and second y-coordinate. - line.get_price()
retrieves the price (y-coordinate) from a line
id
at a specifiedx
value, including at bar indices outside the line’s start and end points. This function is only compatible with lines that use xloc.bar_index as thexloc
value.
The script below draws a new line upon the onset of a
rising
or
falling
price pattern forming over length
bars. It uses the
var
keyword to declare the directionLine
variable on the first chart bar.
The ID assigned to the directionLine
persists over subsequent bars
until the newDirection
condition occurs, in which case the script
assigns a new line to the variable.
On every bar, the script calls the
line.get_y2(),
line.get_y1(),
line.get_x2(),
and
line.get_x1()
getters as methods to
retrieve values from the current directionLine
and calculate its
slope
, which it uses to determine the color of each drawing and plot.
It retrieves extended values of the directionLine
from beyond its
second point using
line.get_price()
and plots them on the chart:
Note that:
- This example calls the second overload of the
line.new()
function, which uses
x1
,y1
,x2
, andy2
parameters to define the start and end points of the line. Thex1
value islength
bars behind the current bar_index, and they1
value is the hlc3 value at that index. Thex2
andy2
in the function call use the current bar’s bar_index and hlc3 values. - The
line.get_price()
function call treats the
directionLine
as though it extends infinitely, regardless of itsextend
property. - The script only displays approximately the last 50 lines on the chart, but the plot of extrapolated values spans throughout the chart’s history.
Cloning lines
Scripts can clone a line id
and all its properties with the
line.copy()
function. Any changes to the copied line instance do not affect the
original.
For example, this script creates a horizontal line at the the bar’s
open
price once every length
bars, which it assigns to a mainLine
variable. On all other bars, it creates a copiedLine
using
line.copy()
and calls line.set_*()
functions to
modify its properties. As we see below, altering the copiedLine
does not affect the mainLine
in any way:
Note that:
- The
index
field of thesecondPoint
islength
bars beyond the current bar_index. Since the maximum x-coordinate allowed with xloc.bar_index isbar_index + 500
, we’ve set themaxval
of thelength
input to 500.
Deleting lines
To delete a line id
drawn by a script, use the
line.delete()
function. This function removes the line instance from the script and
its drawing on the chart.
Deleting line instances is often handy when one wants to only keep a specific number of lines on the chart at any given time or conditionally remove drawings as a chart progresses.
For example, this script draws a horizontal line with the extend.right property whenever an RSI crosses its EMA.
The script stores all line IDs in a lines
array that it
uses as a queue to only display the last numberOfLines
on the chart. When
the
size
of the
array
exceeds the specified numberOfLines
, the script removes the array’s
oldest line ID using
array.shift()
and deletes it with
line.delete():
Note that:
- We declared a
MAX_LINES_COUNT
variable with the “const int” qualified type, which the script uses as themax_lines_count
in the indicator() function and themaxval
of the input.int() assigned to thenumberOfLines
variable. - This example uses the second overload of the
line.new()
function, which specifies
x1
,y1
,x2
, andy2
coordinates independently.
Filling the space between lines
Scripts can fill the space between two
line
drawings by creating a
linefill
object that references them with the
linefill.new()
function. Linefills automatically determine their fill boundaries using
the properties from the line1
and line2
IDs that they reference.
For example, this script calculates a simple linear regression channel.
On the first chart bar, the script declares the basisLine
,
upperLine
, and lowerLine
variables to reference the channel’s
line
IDs, then it makes two
linefill.new()
calls to create
linefill
objects that fill the upper and lower portions of the channel. The first
linefill
fills the space between the basisLine
and the upperLine
, and the
second fills the space between the basisLine
and lowerLine
.
The script updates the coordinates of the lines across subsequent bars. However, notice that the script never needs to update the linefills declared on the first bar. They automatically update their fill regions based on the coordinates of their assigned lines:
To learn more about the linefill type, see this section of the Fills page.
Boxes
The built-ins in the box.*
namespace create and manage
box
objects:
- The box.new() function creates a new box.
- The
box.set_*()
functions modify box properties. - The
box.get_*()
functions retrieve values from a box instance. - The box.copy() function clones a box instance.
- The box.delete() function deletes a box instance.
- The
box.all
variable references a read-only
array
containing the IDs of all boxes displayed by the script. The
array’s
size
depends on the
max_boxes_count
of the indicator() or strategy() declaration statement and the number of boxes the script has drawn.
As with lines,
users can call box.set_*()
, box.get_*()
,
box.copy(),
and
box.delete()
built-ins as functions or methods.
Creating boxes
The box.new() function creates a new box object to display on the chart. It has the following signatures:
This function’s first overload includes the top_left
and
bottom_right
parameters, which accept
chart.point
objects representing the top-left and bottom-right corners of the box,
respectively. The function copies the information from these
chart points to set the coordinates of the box’s corners. Whether it
uses the index
or time
fields of the top_left
and bottom_right
points as x-coordinates depends on the function’s xloc
value.
The second overload specifies left
, top
, right
, and bottom
edges
of the box. The left
and right
parameters accept
int
values specifying the box’s left and right x-coordinates, which can be
bar index or time values depending on the xloc
value in the function
call. The top
and bottom
parameters accept
float
values representing the box’s top and bottom y-coordinates.
The function’s additional parameters are identical in both overloads:
border_color
border_width
border_style
extend
xloc
xloc
value of
xloc.bar_index
means that the function will use the index
fields of the
top_left
and bottom_right
chart points, and an xloc
value of
xloc.bar_time
means that it will use their time
fields.
xloc
value of
xloc.bar_index
means the function treats the left
and right
values as bar
indices, and
xloc.bar_time
means it will treat them as timestamps.
bar_index - 9999
. For larger offsets, one can use
xloc.bar_time.
bgcolor
text
text_size
size.*
constants. The “int” size can be any positive integer. The size.*
constants and their equivalent “int” sizes are:
size.auto (0),
size.tiny (8),
size.small (10),
size.normal (14),
size.large (20),
and size.huge (36).
The default value is size.auto.
text_color
text_halign
text_valign
text_wrap
text_font_family
force_overlay
true
, the drawing will display on the main chart pane, even when the script occupies a separate pane. Optional. The default is false
.
text_formatting
text.format_bold + text.format_italic
applies both formats together to display text that is both bold and italicized. The default value is text.format_none.
Let’s write a simple script to display boxes on a chart. The example below draws a box projecting each bar’s high and low values from the horizontal center of the current bar to the center of the next available bar.
On each bar, the script creates topLeft
and bottomRight
points via
chart.point.now()
and
chart.point_from_index(),
then calls
box.new()
to construct a new box and display it on the chart. It also highlights
the background on the unconfirmed chart bar using
bgcolor()
to indicate that it redraws that box until the bar’s last update:
Note that:
- The
bottomRight
point’sindex
field is one bar greater than theindex
in thetopLeft
. If the x-coordinates of the corners were equal, the script would draw a vertical line at the horizontal center of each bar, resembling the example in this page’s Creating lines section. - Similar to lines, if the
topLeft
andbottomRight
contained identical coordinates, the box wouldn’t display on the chart since there would be no space between them to draw. However, its ID would still exist. - This script only displays approximately the last 50 boxes on the
chart, as we have not specified a
max_boxes_count
in the indicator() function call.
Modifying boxes
Multiple setter functions exist in the box.*
namespace, allowing
scripts to modify the properties of
box
objects:
- box.set_top_left_point()
and
box.set_bottom_right_point()
respectively update the top-left and bottom-right coordinates of the
id
box using information from the specifiedpoint
. - box.set_left()
and
box.set_right()
set the left or right x-coordinate of the
id
box to a newleft/right
value, which can be a bar index or time value depending on the box’sxloc
property. - box.set_top()
and
box.set_bottom()
set the top or bottom y-coordinate of the
id
box to a newtop/bottom
value. - box.set_lefttop()
sets the
left
andtop
coordinates of theid
box, and box.set_rightbottom() sets itsright
andbottom
coordinates. - box.set_border_color(),
box.set_border_width()
and
box.set_border_style()
respectively update the
color
,width
, andstyle
of theid
box’s border. - box.set_extend()
sets the horizontal
extend
property of theid
box. - box.set_bgcolor()
sets the color of the space inside the
id
box to a newcolor
. - box.set_text(),
box.set_text_size(),
box.set_text_color(),
box.set_text_halign(),
box.set_text_valign(),
box.set_text_wrap(),
box.set_text_font_family(),
and box.set_text_formatting()
update the
id
box’s text-related properties.
As with setter functions in the line.*
namespace, all box setters
modify the id
box directly without returning a value, and each setter
function accepts “series” arguments.
Note that, unlike lines, the box.*
namespace does not contain a setter function to
modify a box’s xloc
. Users must
create a new box with the desired xloc
setting for such cases.
This example uses boxes to visualize the ranges of upward and downward
bars with the highest
volume
over a user-defined timeframe
. When the script detects a
change
in the timeframe
, it assigns new
boxes
to its upBox
and downBox
variables, resets its upVolume
and
downVolume
values, and highlights the chart background.
When an upward or downward bar’s
volume
exceeds the upVolume
or downVolume
, the script updates the
volume-tracking variables and calls
box.set_top_left_point()
and
box.set_bottom_right_point()
to update the upBox
or downBox
coordinates. The setters use the
information from the
chart points created with
chart.point.now()
and
chart.point.from_time()
to project that bar’s
high
and low
values from the current time to the closing
time
of the timeframe
:
Note that:
- The
indicator()
function call contains
max_boxes_count = 100
, meaning the script will preserve the last 100 boxes on the chart. - We utilized both overloads of
box.new()
in this example. On the first bar of the
timeframe
, the script calls the first overload for theupBox
when the bar is rising, and it calls that overload for thedownBox
when the bar is falling. It uses the second overload to assign a new box with na values to the other box variable on that bar.
Box styles
Users can include one of the following line.style_*
variables in their
box.new()
or
box.set_border_style()
function calls to set the border styles of boxes drawn by their scripts:
Argument | Box |
---|---|
line.style_solid | |
line.style_dotted | |
line.style_dashed |
Reading box values
The box.*
namespace features getter functions that allow scripts to
retrieve coordinate values from a box instance:
- box.get_left()
and
box.get_right()
respectively get the x-coordinates of the left and right edges of
the
id
box. Whether the value returned represents a bar index or time value depends on the box’sxloc
property. - box.get_top()
and
box.get_bottom()
respectively get the top and bottom y-coordinates of the
id
box.
The example below draws boxes to visualize hypothetical price ranges
over a period of length
bars. At the start of each new period, it uses
the average candle range multiplied by the scaleFactor
input to
calculate the corner points of a box centered at the
hl2
price with an initialRange
height. After drawing the first box, it
creates numberOfBoxes - 1
new boxes inside a
for
loop.
Within each loop iteration, the script gets the lastBoxDrawn
by
retrieving the
last
element from the read-only
box.all
array, then calls
box.get_top()
and
box.get_bottom()
to get its y-coordinates. It uses these values to calculate the
coordinates for a new box that’s scaleFactor
times taller than the
previous:
Note that:
- The
indicator()
function call uses
max_boxes_count = 500
, meaning the script can display up to 500 boxes on the chart. - Each drawing has a
right
indexlength
bars beyond theleft
index. Since the x-coordinates of these drawings can be up to 500 bars into the future, we’ve set themaxval
of thelength
input to 500. - On each new period, the script uses randomized
color.rgb()
values for the
border_color
andbgcolor
of the boxes. - Each
box.new()
call copies the coordinates from the
chart.point
objects assigned to the
topLeft
andbottomRight
variables, which is why the script can modify theirprice
fields on each loop iteration without affecting the other boxes.
Cloning boxes
To clone a specific box id
, use
box.copy().
This function copies the box and its properties. Any changes to the
copied box do not affect the original.
For example, this script declares an originalBox
variable on the first
bar and assigns a new
box
to it once every length
bars. On other bars, it uses
box.copy()
to create a copiedBox
and calls box.set_*()
functions to
modify its properties. As shown on the chart below, these changes
do not modify the originalBox
:
Deleting boxes
To delete boxes drawn by a script, use
box.delete().
As with *.delete()
functions in other drawing namespaces, this
function is handy for conditionally removing boxes or maintaining a
specific number of boxes on the chart.
This example displays boxes representing periodic cumulative volume
values. The script
creates a new box ID and stores it in a boxes
array once every
length
bars. If the array’s
size
exceeds the specified numberOfBoxes
, the script removes the oldest box
from the array using
array.shift()
and deletes it using
box.delete().
On other bars, it accumulates
volume
over each period by
modifying the top
of the
last
box in the boxes
array. The script then uses
for loops to find the
highestTop
of all the array’s boxes and set the bgcolor
of each box
with a gradient
color
based on its
box.get_top()
value relative to the highestTop
:
Note that:
- At the top of the code, we’ve declared a
MAX_BOXES_COUNT
variable with the “const int” qualified type. We use this value as themax_boxes_count
in the indicator() function and the maximum possible value of thenumberOfBoxes
input. - This script uses the second overload of the
box.new()
function, which specifies the box’s
left
,top
,right
, andbottom
coordinates separately. - We’ve included
format.volume
as the
format
argument in the indicator() call, which tells the script that the y-axis of the chart pane represents volume values. Each box also displays its top value as volume-formatted text.
Polylines
Pine Script™ polylines are advanced drawings that sequentially connect the coordinates from an array of chart.point instances using straight or curved line segments.
These powerful drawings can connect up to 10,000 points at any available location on the chart, allowing scripts to draw custom series, polygons, and other complex geometric formations that are otherwise difficult or impossible to draw using line or box objects.
The polyline.*
namespace features the following built-ins for creating
and managing
polyline
objects:
- The polyline.new() function creates a new polyline instance.
- The polyline.delete() function deletes an existing polyline instance.
- The
polyline.all
variable references a read-only
array
containing the IDs of all polylines displayed by the script. The
array’s
size
depends on the
max_polylines_count
of the indicator() or strategy() declaration statement and the number of polylines drawn by the script.
Unlike lines or boxes, polylines do not have functions for modification or reading their properties. To redraw a polyline on the chart, one can delete the existing instance and create a new polyline with the desired changes.
Creating polylines
The polyline.new() function creates a new polyline instance to display on the chart. It has the following signature:
The following eight parameters affect the behavior of a polyline drawing:
points
index
or time
field from each
chart point for its x-coordinates depends on the xloc
value in the
function call.
curved
points
array. The default value is false
, meaning it uses
straight line segments.
closed
points
array to the first, forming a closed polyline. The
default value is false
.
xloc
points
array the polyline uses for its x-coordinates. When
its value is
xloc.bar_index,
the function uses the index
fields to create the polyline. When
its value is
xloc.bar_time,
the function uses the time
fields. The default value is
xloc.bar_index.
line_color
color.blue
.
fill_color
line_style
line_width
force_overlay
true
, the drawing will display on the main chart pane, even when the script occupies a separate pane. Optional. The default is false
.
This script demonstrates a simple example of drawing a polyline on the
chart. It
pushes
a new
chart.point
with an alternating price
value into a points
array and colors the
background with
bgcolor()
once every length
bars.
On the last confirmed historical bar, the script draws a new polyline on the chart, connecting the coordinates from each chart point in the array, starting from the first:
Note that:
- This script uses only one polyline to connect each chart point from the array with straight line segments, and this drawing spans throughout the available chart data, starting from the first bar.
- While one can achieve a similar effect using
lines,
doing so would require a new
line
instance on each occurrence of the
newPoint
condition, and such a drawing would be limited to a maximum of 500 line segments. This single unclosed polyline drawing, on the other hand, can contain up to 9,999 line segments.
Curved drawings
Polylines can draw curves that are otherwise impossible to produce
with lines or
boxes. When
enabling the curved
parameter of the
polyline.new()
function, the resulting polyline interpolates nonlinear values between
the coordinates from each
chart.point
in its
array
of points
to generate a curvy effect.
For instance, the “Oscillating polyline” script in our previous
example uses straight line segments to produce a drawing resembling a
triangle wave, meaning a waveform that zig-zags between its peaks and
valleys. If we set the curved
parameter in the
polyline.new()
call from that example to true
, the resulting drawing would connect
the points using curved segments, producing a smooth, nonlinear shape
similar to a sine wave:
Notice that in this example, the smooth curves have relatively consistent behavior, and no portion of the drawing extends past its defined coordinates, which is not always the case when drawing curved polylines. The data used to construct a polyline heavily impacts the smooth, piecewise function it interpolates between its points. In some cases, the interpolated curve can reach beyond its actual coordinates.
Let’s add some variation to the
chart points in our example’s points
array to demonstrate this
behavior. In the version below, the script multiplies the yValue
by a
random
value in the
chart.point.now()
calls.
To visualize the behavior, this script also creates a horizontal
line
at the price
value from each
chart.point
in the points
array, and it displays another polyline connecting the
same points with straight line segments. As we see on the chart, both
polylines pass through all coordinates from the points
array. However,
the curvy polyline occasionally reaches beyond the vertical boundaries
indicated by the horizontal
lines, whereas
the polyline drawn using straight segments does not:
Closed shapes
Since a single polyline can contain numerous straight or curved line
segments, and the closed
parameter allows the drawing to connect the
coordinates from the first and last
chart.point
in its
array
of points
, we can use polylines to draw many different types of closed
polygonal shapes.
Let’s draw some polygons in Pine. The following script periodically draws randomized polygons centered at hl2 price values.
On each occurrence of the newPolygon
condition, it
clears
the points
array, calculates the numberOfSides
and rotationOffset
of the new polygon drawing based on
math.random()
values, then uses a for loop to push numberOfSides
new
chart points into the
array
that contain stepped coordinates from an elliptical path with xScale
and yScale
semi-axes. The script draws the polygon by connecting each
chart.point
from the points
array using a closed polyline with straight line
segments:
Note that:
- This example shows the last ~50 polylines on the chart, as we
have not specified a
max_polylines_count
value in the indicator() function call. - The
yScale
calculation multiplies an input.float() by ta.atr(2) to adapt the vertical scale of the drawings to recent price ranges. - The resulting polygons have a maximum width of twice the
horizontal semi-axis (
2 * xScale
), rounded to the nearest integer. ThenewPolygon
condition uses this value to prevent the polygon drawings from overlapping. - The script rounds the
xValue
calculation to the nearest integer because theindex
field of a chart.point only accepts an int value, as the x-axis of the chart does not include fractional bar indices.
Deleting polylines
To delete a specific polyline id
, use
polyline.delete().
This function removes the
polyline
object from the script and its drawing on the chart.
As with other drawing objects, we can use polyline.delete() to maintain a specific number of polyline drawings or conditionally remove drawings from a chart.
For example, the script below periodically draws approximate arithmetic spirals and stores their polyline IDs in an array, which it uses as a queue to manage the number of drawings it displays.
When the newSpiral
condition occurs, the script creates a points
array and adds
chart points within a for loop. On each loop iteration, it calls the spiralPoint()
user-defined function to create a new
chart.point
containing stepped values from an elliptical path that grows with
respect to the angle
. The script then creates a randomly colored
curved polyline connecting the coordinates from the points
and
pushes
its ID into the polylines
array.
When the array’s
size
exceeds the specified numberOfSpirals
, the script removes the oldest
polyline using
array.shift()
and deletes the object using
polyline.delete():
Note that:
- We declared a
MAX_POLYLINES_COUNT
global variable with a constant value of 100. The script uses this constant as themax_polylines_count
value in the indicator() function and themaxval
of thenumberOfSpirals
input. - As with our “N-sided polygons” example in the
previous section, we round the calculation of x-coordinates to the
nearest integer since the
index
field of a chart.point can only accept an int value. - Despite the smooth appearance of the drawings, each polyline’s
points
array only contains four chart.point objects per spiral rotation. Since the polyline.new() call includescurved = true
, each polyline uses smooth curves to connect theirpoints
, producing a visual approximation of the spiral’s actual curvature. - The width of each spiral is approximately
4 * math.pi * rotations * xScale
, rounded to the nearest integer. We use this value in thenewSpiral
condition to space each drawing and prevent overlaps.
Redrawing polylines
It may be desirable in some cases to change a polyline drawing
throughout a script’s execution. While the polyline.*
namespace does
not contain built-in setter functions, we can redraw polylines
referenced by variables or
collections by deleting the existing polylines and assigning new
instances with the desired changes.
The following example uses polyline.delete() and polyline.new() calls to update the value of a polyline variable.
This script draws closed polylines that connect the open, high, low, and
close points of periods containing length
bars. It creates a
currentDrawing
variable on the first bar and assigns a polyline ID to
it on every chart bar. It uses the openPoint
, highPoint
, lowPoint
,
and closePoint
variables to reference
chart points that track the period’s developing OHLC values. As new
values emerge, the script assigns new
chart.point
objects to the variables, collects them in an
array
using
array.from,
then creates a new
polyline
connecting the coordinates from the array’s points and assigns it to
the currentDrawing
.
When the newPeriod
condition is false
(i.e., the current period is
not complete), the script
deletes the polyline referenced by the currentDrawing
before
creating a new one, resulting in a dynamic drawing that changes over the
developing period:
Realtime behavior
Lines, boxes, and polylines are subject to both commit and rollback actions, which affect the behavior of a script when it executes on a realtime bar. See the page on Pine Script™‘s Execution model.
This script demonstrates the effect of rollback when it executes on the realtime, unconfirmed chart bar:
The line.new() call in this example creates a new line ID on each iteration when values change on the unconfirmed bar. The script automatically deletes the objects created on each change in that bar because of the rollback before each iteration. It only commits the last line created before the bar closes, and that line instance is the one that persists on the confirmed bar.
Limitations
Total number of objects
Lines, boxes, and polylines consume server resources, which is why there are limits on the total number of drawings per script. When a script creates more drawing objects than the allowed limit, the Pine Script™ runtime automatically deletes the oldest ones in a process referred to as garbage collection.
A single script can contain up to 500 lines, 500 boxes, and 100
polylines. Users can control the garbage collection limits by specifying
the max_lines_count
, max_boxes_count
, and max_polylines_count
values in their script’s
indicator()
or
strategy()
declaration statement.
This script demonstrates how garbage collection works in Pine. It
creates a new line, box, and polyline on each chart bar. We haven’t
specified values for the max_lines_count
, max_boxes_count
, or
max_polylines_count
parameters in the
indicator()
function call, so the script will maintain the most recent ~50 lines,
boxes, and polylines on the chart, as this is the default setting for
each parameter:
Note that:
- We’ve used TradingView’s “Measure” drawing tool to measure the number of bars covered by the script’s drawing objects.
Future references with `xloc.bar_index`
Objects positioned using xloc.bar_index can contain x-coordinates no further than 500 bars into the future.
Other contexts
Scripts cannot use lines, boxes, or
polylines
in request.*()
functions. Instances of these types can use the values
from request.*()
calls, but scripts can only create and draw them in
the chart’s context.
This limitation is also why drawing objects will not work when using the
timeframe
parameter in the
indicator()
declaration statement.
Historical buffer and `max_bars_back`
Using barstate.isrealtime in combination with drawings may sometimes produce unexpected results. For example, the intention of this script is to ignore all historical bars and draw horizontal lines spanning 300 bars back on realtime bars:
However, it will fail at runtime and raise an error. The script fails because it cannot determine the buffer size for historical values of the underlying time series. Although the code doesn’t contain the built-in time variable, the built-in bar_index uses the time series in its inner workings. Therefore, accessing the value of the bar_index from 300 bars back requires the history buffer of the time series to be at least 300 bars.
Pine Script™ includes a mechanism that detects the required historical buffer size automatically in most cases. It works by letting the script access historical values any number of bars back for a limited duration. In this script’s case, using barstate.isrealtime to control the drawing of lines prevents it from accessing the historical series, so it cannot infer the required historical buffer size, and the script fails.
The simple solution to this issue is to use the max_bars_back() function to explicitly define the historical buffer of the time series before evaluating the conditional structure:
Such issues can be confusing, but they’re quite rare. The Pine Script™ team hopes to eliminate them over time.