Pine Script v6 plot() Function: Complete Guide to Lines, Histograms, and Circles

The plot() function is the most fundamental visualization tool in Pine Script v6, enabling developers to render numeric series data directly onto a TradingView chart as lines, histograms, circles, and more. Understanding its styling parameters — including color, linewidth, and style — is essential for building readable, professional-grade indicators. This article provides a rigorous, fact-checked walkthrough of the plot() function based strictly on the official Pine Script v6 Reference Manual.


1. What Does plot() Do?

The plot() function renders a series of numeric values as a visual element on the chart pane. It returns a plot object, which can later be used with the fill() function to shade areas between two plots. Every call to plot() creates one persistent visual layer on the chart.

Reference: https://www.tradingview.com/pine-script-reference/v6/#fun_plot

2. Core Parameters (Individually Verified)

Rather than reproducing the full function signature (which risks introducing errors), the following table documents each parameter used in this article, verified individually against the v6 Reference Manual.

Parameter Accepted Type Description
series series int / series float The numeric data series to plot. This is the only required argument.
title const string Label shown in the chart legend and Data Window.
color series color Color of the plotted element. Accepts built-in color constants, color.new(), or conditional color expressions.
linewidth input int Thickness of the line in pixels. Applies to line-based styles.
style input plot_style Visual style of the plot. Accepts plot.style_* constants (see Section 3).
offset series int Shifts the plot left (negative) or right (positive) by the specified number of bars.
display input display Controls where the plot value is shown (e.g., display.all, display.none). Multiple locations combined with |.
The same SMA series rendered with three different plot.style_* constants side by side (e.g., style_line, style_histogram, style_circles)

3. Verified plot.style_* Constants

The style parameter accepts one of the following verified constants. Each constant changes the geometric representation of the series on the chart.

Constant Visual Output Notes
plot.style_line Continuous line Default style. Bridges over na gaps.
plot.style_linebr Line with breaks Breaks the line at na values instead of bridging.
plot.style_stepline Step-function line Horizontal then vertical segments — useful for regime indicators.
plot.style_stepline_diamond Step line with diamond markers Adds diamond shapes at each data point on a step line.
plot.style_histogram Vertical bars from zero Each bar extends from the zero baseline to the series value.
plot.style_area Filled area under line Bridges over na gaps.
plot.style_areabr Filled area with breaks Breaks the filled area at na values.
plot.style_circles Circle markers Renders a circle at each bar's value. No connecting line.
plot.style_cross Cross markers Renders a cross/plus symbol at each bar's value.
plot.style_columns Column bars Similar to histogram but rendered as full-width columns.

4. Logic Flow: How plot() Processes Each Bar

Because Pine Script executes bar-by-bar, the plot() function is called once per bar. On each execution, it reads the current value of the series argument and renders the corresponding visual element for that bar. The diagram below illustrates this execution model.

graph TD A[Bar N begins execution] --> B[Evaluate series argument] B --> C{Is value na?} C -- Yes, style_line --> D[Bridge gap: connect previous to next non-na] C -- Yes, style_linebr --> E[Break line: render nothing for this bar] C -- No --> F[Evaluate color argument per bar] F --> G[Evaluate style constant] G --> H1[style_line: draw segment to previous point] G --> H2[style_histogram: draw vertical bar from 0 to value] G --> H3[style_circles: draw circle at current value] G --> H4[style_columns: draw full-width column bar] H1 --> I[Render to chart pane] H2 --> I H3 --> I H4 --> I I --> J[Return plot object for use with fill] J --> K[Advance to Bar N plus 1]

5. Practical Code Examples

Example 1: Basic Line Plot with Dynamic Color

This example plots a 20-period Simple Moving Average as a line, coloring it green when price is above the SMA and red when below. The color parameter accepts a series color, so conditional expressions are fully supported.

🔽 [Click to expand] View Full Pine Script Code — Example 1
//@version=6
indicator(
    title    = "SMA Line with Dynamic Color",
    overlay  = true  // Draw on the price chart, not a separate pane
)

// --- Inputs ---
int   smaLength = input.int(20, title = "SMA Length", minval = 1)

// --- Calculation ---
float smaValue  = ta.sma(close, smaLength)  // 20-period SMA of closing price

// --- Dynamic Color Logic ---
// color is series color, so a conditional expression is valid here
color lineColor = close >= smaValue ? color.green : color.red

// --- Plot ---
// style_line is the default; shown explicitly for clarity
// linewidth = 2 makes the line 2 pixels thick
plot(
    series    = smaValue,
    title     = "SMA(20)",
    color     = lineColor,
    linewidth = 2,
    style     = plot.style_line
)

Example 2: Histogram Plot (MACD-style)

Histograms are ideal for oscillators. This example computes the difference between a fast and slow EMA and renders it as a histogram, with bars colored based on whether the value is positive or negative.

🔽 [Click to expand] View Full Pine Script Code — Example 2
//@version=6
indicator(
    title   = "EMA Difference Histogram",
    overlay = false  // Draw in a separate pane below the chart
)

// --- Inputs ---
int fastLen = input.int(12, title = "Fast EMA Length", minval = 1)
int slowLen = input.int(26, title = "Slow EMA Length", minval = 1)

// --- Calculation ---
float fastEMA = ta.ema(close, fastLen)  // Fast Exponential Moving Average
float slowEMA = ta.ema(close, slowLen)  // Slow Exponential Moving Average
float diff    = fastEMA - slowEMA       // Difference series (oscillator)

// --- Dynamic Color for Histogram Bars ---
// Positive diff → teal, Negative diff → red
color barColor = diff >= 0.0 ? color.teal : color.red

// --- Plot as Histogram ---
// Each bar extends vertically from 0 to the diff value
plot(
    series = diff,
    title  = "EMA Diff",
    color  = barColor,
    style  = plot.style_histogram
)

// --- Zero baseline reference ---
hline(0, "Zero", color = color.gray, linestyle = hline.style_dashed)

Example 3: Circles and Transparency with color.new()

The plot.style_circles style renders a discrete marker at each bar. Combining it with color.new() allows per-bar transparency control, which is useful for highlighting specific conditions without cluttering the chart.

🔽 [Click to expand] View Full Pine Script Code — Example 3
//@version=6
indicator(
    title   = "RSI Circle Markers",
    overlay = false
)

// --- Inputs ---
int rsiLen = input.int(14, title = "RSI Length", minval = 1)

// --- Calculation ---
float rsiValue = ta.rsi(close, rsiLen)  // RSI series (0–100)

// --- Conditional Transparency ---
bool isExtreme = rsiValue > 70.0 or rsiValue < 30.0
int  transp     = isExtreme ? 0 : 80  // 0 = fully opaque, 100 = invisible

// color.new() accepts a base color and a transparency (0–100)
color circleColor = color.new(color.purple, transp)

// --- Plot as Circles ---
// linewidth controls the size of the circle markers
plot(
    series    = rsiValue,
    title     = "RSI",
    color     = circleColor,
    linewidth = 3,
    style     = plot.style_circles
)

// --- Reference lines ---
hline(70, "Overbought", color = color.red,   linestyle = hline.style_dashed)
hline(30, "Oversold",   color = color.green, linestyle = hline.style_dashed)
hline(50, "Midline",    color = color.gray,  linestyle = hline.style_dotted)

6. Using fill() with Two plot() Objects

The plot() function returns a plot object. Two such objects can be passed to fill() to shade the area between them. This is a common pattern for Bollinger Bands or channel indicators.

🔽 [Click to expand] View Full Pine Script Code — fill() Example
//@version=6
indicator(
    title  = "SMA Channel with Fill",
    overlay = true
)

// --- Inputs ---
int   len    = input.int(20, title = "Length",     minval = 1)
float mult   = input.float(2.0, title = "Multiplier", minval = 0.1)

// --- Calculation ---
float basis  = ta.sma(close, len)          // Middle band: SMA
float dev    = mult * ta.stdev(close, len) // Standard deviation scaled by multiplier
float upper  = basis + dev                 // Upper band
float lower  = basis - dev                 // Lower band

// --- Plot bands and capture return values ---
// plot() returns a plot object used by fill()
upperPlot = plot(upper, title = "Upper", color = color.blue,  linewidth = 1)
lowerPlot = plot(lower, title = "Lower", color = color.blue,  linewidth = 1)
plot(basis,                  title = "Basis", color = color.orange, linewidth = 1)

// --- Fill the area between upper and lower bands ---
// color.new() adds transparency so the fill doesn't obscure candles
fill(upperPlot, lowerPlot, color = color.new(color.blue, 90))

7. The display Parameter

The display parameter controls where the plot's value appears in the TradingView UI. Multiple display locations are combined using the + operator (add a location) or removed using the `-` operator.

//@version=6
indicator("Display Demo", overlay = true)

// Show value in the price scale AND the status line, but NOT the Data Window
plot(
    series  = ta.sma(close, 20),
    title   = "SMA",
    color   = color.blue,
    display = display.all - display.data_window
)

Using display.all shows the value everywhere. Using display.none hides it from all locations (useful for intermediate series passed to fill()).

Note: The `|` (bitwise OR) operator does NOT work with `display.*` constants — use `+` and `-` instead.


8. Key Mathematical Relationships

When using plot() with oscillators, it is important to understand the mathematical domain of the series being plotted. For example:

  • RSI is bounded: $RSI \in [0, 100]$
  • MACD histogram is unbounded: $\text{MACD}_{hist} = EMA_{fast} - EMA_{slow} \in (-\infty, +\infty)$
  • Bollinger Band width: $\text{Width} = \text{Upper} - \text{Lower} = 2 \times k \times \sigma$, where $k$ is the multiplier and $\sigma$ is the rolling standard deviation.

Choosing the correct plot.style_* constant should reflect the mathematical nature of the series: bounded oscillators suit style_line or style_circles, while zero-centered oscillators suit style_histogram or style_columns.


9. Conclusion

  • The plot() function renders a numeric series bar-by-bar using a verified set of plot.style_* constants; the color parameter accepts series color, enabling fully dynamic, conditional coloring per bar.
  • The display parameter uses `+` to add locations and `-` to remove them. Never use `|` with `display.*` constants.
  • plot() returns a plot object that can be passed directly to fill() to shade areas between two series, enabling channel and band visualizations.

Ideas for Further Development

  1. Multi-timeframe plot overlay: Use request.security() to fetch a higher-timeframe SMA and plot it alongside the current-timeframe SMA using plot.style_stepline to visually distinguish the two resolutions.
  2. Adaptive transparency engine: Build a system where the color.new() transparency argument is computed from a normalized volatility measure (e.g., ATR percentile), making the plot visually fade during low-volatility regimes and become opaque during high-volatility periods.

Related Posts

Comments

Popular posts from this blog

Pine Script v6: indicator() vs strategy() — Core Functional Differences Explained

Pine Script Variable Declaration: Mastering var, varip, and Proper Initialization Across Historical Bars