plotshape, plotchar and bgcolor
Lesson 11 · about 10 min
A line is the right visual for a level. A signal is an event, and events want a marker on the bar where they happened. Pine has three lightweight functions for that: plotshape() draws an icon, plotchar() draws a character, and bgcolor() paints the background. They share the same plot budget as plot() and the same global-scope rule, and they take a boolean series that says on which bars to draw.
plotshape
//@version=6
indicator("Signals on chart", overlay=true)
ema = ta.ema(close, 21)
rsi = ta.rsi(close, 14)
bull = ta.crossover(close, ema) and rsi > 50
bear = ta.crossunder(close, ema) and rsi < 50
plot(ema, "EMA 21", color=color.orange)
plotshape(bull, "Bull", style=shape.labelup, location=location.belowbar,
color=color.green, textcolor=color.white, text="B", size=size.small)
plotshape(bear, "Bear", style=shape.labeldown, location=location.abovebar,
color=color.red, textcolor=color.white, text="S", size=size.small)
The first argument is the condition. When it is true on a bar, the shape is drawn; when false, nothing. Options:
style:shape.triangleup,shape.triangledown,shape.arrowup,shape.arrowdown,shape.circle,shape.square,shape.diamond,shape.cross,shape.xcross,shape.flag,shape.labelup,shape.labeldown.location:location.abovebar,location.belowbar,location.top,location.bottom(edges of the pane),location.absolute(at the value of the series passed as the first argument instead of a boolean).textandtextcolor: a short caption; keep it to one or two characters or it clutters fast.size:size.auto,size.tiny,size.small,size.normal,size.large,size.huge.offset: shift the shape by n bars; negative moves it left, which looks like foresight and should be avoided on signals.
location.absolute is the way to mark a price rather than a bar: plotshape(stopLevel, "Stop", style=shape.cross, location=location.absolute, color=color.red) draws a cross at the stop price on every bar where stopLevel is not na.
plotchar
plotchar() is plotshape() with any single Unicode character instead of a built-in shape:
plotchar(rsi > 70, "Overbought", char="!", location=location.abovebar, color=color.red, size=size.tiny)
plotchar(rsi < 30, "Oversold", char="•", location=location.belowbar, color=color.green, size=size.tiny)
Its second use is debugging. plotchar(someValue, "debug", char="", location=location.top) with an empty character draws nothing on the chart but puts the value in the data window, so you can inspect a series bar by bar without cluttering the pane. That is the Pine equivalent of a print statement.
bgcolor and barcolor
bgcolor(color) paints the background of the bar. Pass na to leave it alone:
bgcolor(rsi > 70 ? color.new(color.red, 90) : rsi < 30 ? color.new(color.green, 90) : na)
High transparency (85 to 95) is essential; anything more opaque hides the candles. bgcolor is the natural way to show regimes: in session, trend up, volatility high.
barcolor(color) recolours the candles themselves. It is effective for a one-condition signal (barcolor(close > ema ? color.teal : color.gray)) and confusing if you stack several, because only one colour can win per bar.
Keeping signals honest
The condition you pass is evaluated on every update of the live bar. A crossover that is true at 10:03 can be false at 10:05 when price dips back, and the shape appears and disappears. Historical bars never show that because they only have their final values. If a marker must mean "this happened on a closed bar", combine the condition with barstate.isconfirmed, at the cost of the marker appearing only when the bar ends. Module 5 covers when this matters and when it does not.
Key idea:
plotshapeandplotcharmark bars where a boolean is true;bgcolorpaints regimes. All three must be in the global scope and count toward the 64-output limit.
A small style guide
- One shape per signal type, consistent colours: green for long, red for short, gray for neutral.
- Put entries below the bar and exits above, or the reverse, and stick to it.
- Use
size.tinyorsize.small; large shapes hide the price action you are trying to read. - Give every call a title. The style dialog lists them, and users can turn individual ones off.
- Prefer
bgcolorfor conditions that persist for many bars and shapes for conditions that are true on single bars.
Try it: Mark bars where the close is the highest of the last 20 with a tiny triangle above and bars where it is the lowest with a triangle below. Paint the background when ATR as a percent of close is above its 50-bar average. Then add a debug
plotcharwith an empty character for the ATR percent and read it in the data window.
Recap
plotshape(cond, title, style, location, color, text, size)draws an icon on bars wherecondis true.plotchardraws any character; withchar=""it exports a value to the data window for debugging.bgcolorpaints regimes with high transparency;barcolorrecolours candles.- Conditions on the live bar can flicker; guard with
barstate.isconfirmedwhen a marker must be final. - Keep shapes small, titled, and consistent in colour and placement.
See it drawn
Original diagrams for the ideas on this page. Illustrative, not real market data.