Pine Script Separate Pane: Fix Labels in the Wrong Pane
A Pine Script separate pane is controlled by one setting: overlay in indicator() or strategy(). overlay=false puts the whole script in its own pane, overlay=true uses the price chart, and in v6 force_overlay=true sends single drawings to the chart.
- A script owns exactly one pane, chosen by overlay in indicator() or strategy(), and every plot and label follows it unless force_overlay is set.
- Labels appear in the RSI pane because label.new() uses the pane's own scale, so price-level y-values are drawn against a 0 to 100 axis.
- Deleting the RSI code does not move the labels, because the script is still declared overlay=false.
- The standard fix is two scripts: an overlay=true price script and an overlay=false RSI script, both added to the chart.
- To keep one script, use force_overlay=true on each plot(), plotshape(), label.new() and line.new() call, and check that //@version is 6 (or a later v5 release).
How does Pine Script decide which pane an output goes to?
Every script has one pane, and that pane is set in the declaration: indicator(..., overlay=true) draws on the main price chart, while overlay=false (the default) creates a separate pane below it. Plots, labels, lines, boxes, fills and shapes all follow that choice.
A single script cannot create two separate panes. If you want price signals on the chart and RSI underneath, you either need two scripts or a single script that uses force_overlay on the chart-bound drawings.
Use this table to match what you see to the cause.
| Symptom | Cause | Fix |
|---|---|---|
| Labels or SMA appear in the RSI pane | Script is overlay=false, so everything draws in the pane's scale | Split into two scripts, or add force_overlay=true to those calls |
| Labels are missing or squashed at the top of the pane | Price-level y-values (for example 4500) sit far outside the 0 to 100 RSI axis | Draw on the chart pane, not the oscillator pane |
| Pane remains after deleting plot(rsi) | Pane belongs to the script declaration, not to the RSI plot | Set overlay=true, then remove and re-add the script |
| Changed overlay but nothing moved | Old instance on the chart keeps its saved settings | Remove the indicator with the X, then add it again |
| force_overlay gives an error or is ignored | Script is on an older version without the parameter | Change to //@version=6 |
| Only the newest 50 labels show | Default label limit | Set max_labels_count=500 in indicator() |
Why do labels land in the RSI pane, and why do they stay after I delete the RSI code?
When the script is overlay=false, the pane has its own vertical scale. An RSI plot is auto-scaled to roughly 0 to 100, and label.new(bar_index, high, ...) puts the label at the y-value of the bar's high, for example 4500. The label is placed in the pane at that y-value, so it is either off-scale, or it stretches the axis and flattens the RSI line.
The pane is created by the declaration, not by the plot. Removing plot(rsi) leaves indicator(..., overlay=false) in place, so every remaining drawing still belongs to that pane. Even a script with no plots keeps its own pane for any labels it draws.
A second cause of confusion is a stale instance. The chart stores each added script's settings, and an instance added earlier does not always pick up an edited declaration. After changing overlay, use the X or Remove on the old instance and add the script again from the editor with Add to chart.
How do I fix it with two scripts (the standard way)?
Put everything that belongs on price into an overlay=true script and the oscillator into its own overlay=false script. This is the most robust layout, because each script has an obvious pane and no version dependencies.
The first script below also holds the worked example: label the second consecutive green bar that opens above the 9 SMA. The condition is open > sma and close > open. A counter that resets whenever the condition fails is simpler and safer than ta.barssince, because it gives you the exact run length.
- Open the Pine Editor, create a new indicator and paste the price script.
- Click Add to chart. Check that the SMA and labels sit on the candles.
- Create a second new indicator, paste the RSI script, and Add to chart. It gets its own pane.
- If anything sits in the wrong pane, remove that instance with the X and add it again.
//@version=6
// Script 1: price overlay
indicator("Green bars above SMA", overlay=true, max_labels_count=500)
sma = ta.sma(close, 9)
cond = open > sma and close > open
var int run = 0
run := cond ? run + 1 : 0
secondBar = run == 2
plot(sma, "SMA 9", color=color.orange)
if secondBar
label.new(bar_index, high, "2nd", style=label.style_label_down,
color=color.green, textcolor=color.white)
// Lighter alternative to labels (no 500 object limit pressure):
// plotshape(secondBar, "2nd", shape.triangledown, location.abovebar, color.green, text="2nd")
//@version=6
// Script 2: separate RSI pane (a different script)
indicator("RSI 14", overlay=false)
r = ta.rsi(close, 14)
plot(r, "RSI", color=color.purple)
hline(70)
hline(30)How do I keep one script and use force_overlay?
In Pine v6, plot(), plotshape(), plotchar(), label.new(), line.new(), box.new() and similar calls accept force_overlay=true. The script stays overlay=false, so RSI gets its own pane, but each call marked with force_overlay=true draws on the main chart instead. Later v5 releases also added the parameter, so check the //@version line first. If your editor rejects it, convert to v6.
Note that hline() and fill() have no force_overlay of their own for pane placement, so keep them tied to the RSI plots they belong to.
//@version=6
indicator("RSI pane + price signals", overlay=false, max_labels_count=500)
sma = ta.sma(close, 9)
cond = open > sma and close > open
var int run = 0
run := cond ? run + 1 : 0
secondBar = run == 2
// Goes to the RSI pane (script default)
r = ta.rsi(close, 14)
plot(r, "RSI", color=color.purple)
hline(70)
hline(30)
// Goes to the main chart
plot(sma, "SMA 9", color=color.orange, force_overlay=true)
plotshape(secondBar, "2nd", shape.triangledown, location.abovebar,
color.green, text="2nd", force_overlay=true)
if secondBar
label.new(bar_index, high, "2nd", style=label.style_label_down,
color=color.green, textcolor=color.white,
force_overlay=true) overlay=true vs overlay=false vs force_overlay: which should I use?
The declaration sets the default for the whole script, while force_overlay overrides it for one call. Pick based on how much of the script belongs on price.
With strategies, strategy(overlay=true) puts plots on the chart, and the order markers and trade list always relate to the main chart. An RSI inside an overlay=true strategy cannot get its own pane. To show RSI, declare strategy(overlay=false) and put force_overlay=true on every plot or label that should sit on price. Otherwise, run the RSI as a separate indicator.
| Option | Where things draw | Best for | Limits |
|---|---|---|---|
| overlay=true | Everything on the price chart | SMA, labels, signals, strategies | Oscillators distort the price scale |
| overlay=false | Everything in a separate pane | RSI, MACD, volume-style values | Labels use the pane's scale |
| force_overlay=true | Only that call, on the price chart | Mixed scripts with one oscillator | Needs v6 or a later v5; set per call |
| Two scripts | Each script in its own pane | Clear separation, easy settings | Two items to manage |
What are the common mistakes with pane placement?
Expecting a second pane from one script is the most common. Only one pane per script exists, so a script with RSI and MACD in separate panes must be split into two indicators.
Mixing scales is next. If you plot price-level values in an overlay=false pane without force_overlay, the autoscale ruins the oscillator. Normalize values or move them to the chart. The same applies to values returned from request.security(): they carry the scale of the requested symbol, so a plot of another symbol's close in an RSI pane is meaningless against 0 to 100.
Version mismatch causes quiet failures. Code copied from a v6 answer into a //@version=5 script may fail on syntax, such as how booleans and na are handled. Fix the version line, then use the editor's Convert code option if offered.
Finally, object limits. Labels, lines and boxes are capped (labels default to 50, max 500 via max_labels_count). If older labels vanish, raise the limit, or use plotshape(), which is not an object and has no such cap, though it cannot hold dynamic text.
Common questions
Can one Pine Script create multiple panes?
No. A script has exactly one pane, either the main chart (overlay=true) or one separate pane (overlay=false). To get RSI and MACD in different panes, write two indicators and add both.
Should I use plotshape or label.new for signals?
Use plotshape() or plotchar() for simple fixed markers, because they are cheap, not subject to the label cap, and easy to alert on. Use label.new() when you need dynamic text, custom positions or per-bar content, and set max_labels_count if you need more than 50.
Does force_overlay work in strategies?
Yes, in v6 it works in strategy() scripts as well. Declare strategy(overlay=false) so the RSI has a pane, then add force_overlay=true to the SMA, labels and shapes you want on the chart.
Why is my overlay change not showing on the chart?
The instance already on the chart keeps its saved placement and style. Remove it with the X on the indicator legend, then add the saved script again from the Pine Editor.
How do I check whether my script is v5 or v6?
Look at the first line, //@version=5 or //@version=6. If force_overlay is flagged as an unknown argument, change it to 6 and fix any compile errors, such as using na in conditions.