Howwise RSS
Trading Software7 min read

Pine Script Separate Pane: Fix Labels in the Wrong Pane

Short answer

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.

Key takeaways
  • 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.

SymptomCauseFix
Labels or SMA appear in the RSI paneScript is overlay=false, so everything draws in the pane's scaleSplit into two scripts, or add force_overlay=true to those calls
Labels are missing or squashed at the top of the panePrice-level y-values (for example 4500) sit far outside the 0 to 100 RSI axisDraw on the chart pane, not the oscillator pane
Pane remains after deleting plot(rsi)Pane belongs to the script declaration, not to the RSI plotSet overlay=true, then remove and re-add the script
Changed overlay but nothing movedOld instance on the chart keeps its saved settingsRemove the indicator with the X, then add it again
force_overlay gives an error or is ignoredScript is on an older version without the parameterChange to //@version=6
Only the newest 50 labels showDefault label limitSet 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.

Warning: Removing an indicator from the chart discards its per-chart input and style changes. Save the script and note any custom inputs before you remove it.

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.

  1. Open the Pine Editor, create a new indicator and paste the price script.
  2. Click Add to chart. Check that the SMA and labels sit on the candles.
  3. Create a second new indicator, paste the RSI script, and Add to chart. It gets its own pane.
  4. 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)
Tip: Paste the two scripts into two separate editor tabs. They are shown together here only for copying; one file can contain a single indicator() declaration.

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.

OptionWhere things drawBest forLimits
overlay=trueEverything on the price chartSMA, labels, signals, strategiesOscillators distort the price scale
overlay=falseEverything in a separate paneRSI, MACD, volume-style valuesLabels use the pane's scale
force_overlay=trueOnly that call, on the price chartMixed scripts with one oscillatorNeeds v6 or a later v5; set per call
Two scriptsEach script in its own paneClear separation, easy settingsTwo 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.

Related topics:pine scripttradingviewoverlayrsiforceoverlay