Pine Script Plot Styles: Chart Visualization on TradingView
Pine Script plot styles are the visual modes the plot() function uses to draw data on a chart — lines, histograms, areas, columns, circles, and crosses. Choose a style that makes the existing data easier to read; changing its appearance does not create an earlier signal.
This guide covers common plot() styles and when each is useful. The official plots documentation lists the available styles and their rendering rules.
What Are Plot Styles in Pine Script?
Plot styles in Pine Script determine how a data series is drawn on the chart. The plot() function accepts a style parameter that tells TradingView whether to connect data points with a line, fill the space beneath them with color, draw bars, or mark individual dots. Same data, different story.
Common style constants include: plot.style_line, plot.style_stepline, plot.style_area, plot.style_histogram, plot.style_columns, plot.style_circles, and plot.style_cross. Each one has specific use cases, and some work better for certain data types than others.
Common Pine Script Plot Styles
These examples show connected lines, filled regions, bars, and markers. For series containing na, also consider plot.style_linebr and plot.style_areabr, which preserve breaks instead of bridging them.
Line Style (plot.style_line)
This is the default: a line connecting data points across bars. Common uses include:
- Moving averages (like in any SMA Pine Script strategy)
- Price data
- Oscillators like RSI or MACD
- Any smoothly flowing series
//@version=5
indicator("Basic Line Plot", overlay=true)
close_sma = ta.sma(close, 20)
plot(close_sma, title="20 SMA", color=color.blue, linewidth=2)
Step Line Style (plot.style_stepline)
Step lines connect discrete values with horizontal and vertical segments. They can represent states or reference levels without suggesting a diagonal transition between observations.
//@version=5
indicator("Step Line Example", overlay=false)
rsi_value = ta.rsi(close, 14)
rsi_zones = rsi_value > 70 ? 70 : rsi_value < 30 ? 30 : 50
plot(rsi_zones, title="RSI Zones", color=color.orange, style=plot.style_stepline, linewidth=2)
Area Style (plot.style_area)
An area plot fills between the series and its baseline. It can emphasize magnitude, but choose the baseline and scale carefully: a filled region is a display choice, not additional information.
//@version=5
indicator("Area Plot Example", overlay=false)
volume_sma = ta.sma(volume, 10)
plot(volume_sma, title="Volume SMA", color=color.new(color.green, 70), style=plot.style_area)
Histogram Style (plot.style_histogram)
Classic bars extending from zero. The MACD guide on Pineify covers this in detail. Examples include:
- MACD histogram
- Volume comparison
- Any oscillator where the distance from a baseline matters
//@version=5
indicator("Histogram Example", overlay=false)
[macd_line, signal_line, histogram] = ta.macd(close, 12, 26, 9)
plot(histogram, title="MACD Histogram", color=color.red, style=plot.style_histogram)
Columns Style (plot.style_columns)
Columns fill the available bar width and ignore linewidth. Histogram width is controlled by linewidth in pixels. Use columns for a bar-width display, or histograms when an explicit pixel width matters.
//@version=5
indicator("Columns Example", overlay=false)
rsi_value = ta.rsi(close, 14)
rsi_centered = rsi_value - 50 // Center around zero
plot(rsi_centered, title="Centered RSI", color=rsi_centered > 0 ? color.green : color.red, style=plot.style_columns)
Discrete Styles: Circles and Crosses
Circles and crosses mark series values without connecting lines by default. Use na on bars that should have no marker. A marker can display a condition; it does not create an alert or order.
//@version=5
indicator("Signal Markers", overlay=true)
rsi_value = ta.rsi(close, 14)
buy_signal = ta.crossover(rsi_value, 30)
sell_signal = ta.crossunder(rsi_value, 70)
plotshape(buy_signal, title="Buy Signal", location=location.belowbar, color=color.green, style=shape.triangleup, size=size.small)
plot(sell_signal ? close : na, title="Sell Marker", color=color.red, style=plot.style_circles, linewidth=4)
Advanced Plot Styling
The following examples demonstrate display choices without changing the underlying signal rules.
Dynamic Color Changes
This color reflects whether the fast EMA is currently above the slow EMA. It does not predict a crossover or guarantee an earlier signal; the color can change during an open bar.
//@version=5
indicator("Dynamic Color Plot", overlay=true)
ema_fast = ta.ema(close, 12)
ema_slow = ta.ema(close, 26)
// Change color based on trend
trend_color = ema_fast > ema_slow ? color.green : color.red
plot(ema_fast, title="Fast EMA", color=trend_color, linewidth=2)
Transparency and Layering
Overlapping opaque plots can obscure other chart elements. color.new() sets transparency from 0 (opaque) to 100 (invisible). The example uses 70 as an illustrative display setting; check legibility on the chart background.
//@version=5
indicator("Layered Plots", overlay=true)
[bb_basis, bb_upper, bb_lower] = ta.bb(close, 20, 2)
plot(bb_upper, title="BB Upper", color=color.new(color.blue, 70))
plot(bb_lower, title="BB Lower", color=color.new(color.blue, 70))
Input Options for User Customization
A style selector lets the reader change the display without editing the calculation.
//@version=5
indicator("Customizable Plot Style", overlay=false)
// User inputs
plot_style_input = input.string("Line", "Plot Style", options=["Line", "Histogram", "Area", "Columns"])
color_input = input.color(color.blue, "Plot Color")
rsi_value = ta.rsi(close, 14)
// Convert string input to plot style
selected_style = plot_style_input == "Histogram" ? plot.style_histogram :
plot_style_input == "Area" ? plot.style_area :
plot_style_input == "Columns" ? plot.style_columns : plot.style_line
plot(rsi_value, title="RSI", color=color_input, style=selected_style)
Choosing the Right Style
There is no universal best plot style. Match the display to the question:
Lines for continuous data where the trend path matters: moving averages, smoothed oscillators, price itself.
Histograms when baseline distance matters: MACD, momentum, volume delta. The bars make zero-crossing changes visible at a glance.
Areas when highlighting distance from a defined baseline helps explain the series.
Columns when you want bars that use the available chart-bar width; choose histograms when you need linewidth to control pixel width.
Circles and crosses when individual observations or events matter. Decide how many markers remain readable at the intended zoom.
Common Mistakes
Check these display choices:
-
Fancy styling on simple data. A plain line beats a rainbow histogram for most use cases. Don't add styling because you can — add it because the data needs it.
-
Style-data mismatch. Area plots for price data hide the actual price movement behind color fills. Line plots for volume underrepresent the magnitude. Match the style to what the data measures.
-
Too many colors. Two or three colors per indicator max. Use colors consistently so their meaning stays clear.
-
Ignoring transparency. Opacity is not a virtue. A 70% transparent area fill overlaid on price tells you more than a solid block that blocks everything behind it.
Practical Example
Here is a multi-style RSI indicator. It combines raw and smoothed RSI lines, horizontal overbought and oversold levels, and triangular crossing markers.
//@version=5
indicator("Multi-Style Indicator", overlay=false)
// RSI with different visualizations
rsi_value = ta.rsi(close, 14)
rsi_sma = ta.sma(rsi_value, 5)
// Main RSI as line
plot(rsi_value, title="RSI", color=color.blue, linewidth=1)
// Smoothed RSI as thicker line
plot(rsi_sma, title="RSI SMA", color=color.orange, linewidth=2)
// Overbought/oversold areas
hline(70, "Overbought", color=color.red, linestyle=hline.style_dashed)
hline(30, "Oversold", color=color.green, linestyle=hline.style_dashed)
// Signal markers
buy_condition = ta.crossover(rsi_value, 30)
sell_condition = ta.crossunder(rsi_value, 70)
plotshape(buy_condition, title="Buy", location=location.bottom, color=color.green, style=shape.triangleup)
plotshape(sell_condition, title="Sell", location=location.top, color=color.red, style=shape.triangledown)
The markers highlight the same RSI crossings already present in the data. They are display aids, not evidence of a profitable entry rule. ta.bb() in the layering example returns a tuple, so its values are obtained with a tuple declaration; [] after a series refers to history, not tuple-member indexing.
What is plot.style_line in Pine Script?
It is the default plot style and connects series values across bars. It can display moving averages or oscillators. Use linebr when gaps should remain visible.
What is the difference between plot.style_histogram and plot.style_columns?
Histogram width uses linewidth in pixels. Columns use the available bar width and ignore linewidth. Both extend from the histbase value, which defaults to zero.
How do I change plot color dynamically based on a condition?
Pass a ternary expression to the color parameter. Something like plot(series, color=series > 0 ? color.green : color.red). The expression evaluates when the script executes. It can change during an open bar in an indicator.
When should I use plot.style_area instead of plot.style_line?
Use an area when shading the distance from a defined baseline helps the reader. Check the scale, baseline, and transparency; a filled region does not establish trading significance.
Can I let users choose the plot style via an input?
Yes. Use input.string() with an options list, map the string to a plot style constant with ternaries, and pass it to the style parameter. This makes the same indicator work for people who prefer lines versus histograms without touching code.
What are plot.style_circles and plot.style_cross used for?
They mark series values at individual bars without connecting lines by default. A conditional series can limit them to selected events. Plotting such an event does not create an active alert.
How does transparency work in Pine Script plots?
Use color.new(color.blue, 70) where 70 is the transparency percentage. Zero is fully opaque and 100 is invisible. Choose a value that keeps overlapping data readable on the intended background.

