🎓 Tutorials

TradingView Pine Script Multi-Chart Layout: Display Multiple Panels from One Indicator

⚠️ Disclosure: Some links on this page are affiliate links. If you sign up through them, I may earn a commission — at no extra cost to you. I only review tools I actually use.
# TradingView Pine Script Multi-Chart Layout: Display Multiple Panels from One Indicator

Every serious trader hits the same wall: you want RSI in one pane, volume in another, and moving averages on the price chart — but TradingView limits how many indicators you can load (3 on the free plan). The solution? Build one Pine Script indicator that outputs to multiple chart panels simultaneously.

This tutorial walks through every technique available in Pine Script v5 and v6 to create multi-panel layouts from a single script. By the end, you'll have a working "Swiss Army Knife" indicator that plots across the main chart and its own separate pane at the same time.

> Need more indicator slots? Upgrade your TradingView plan to unlock up to 25 indicators per chart — essential for complex multi-panel setups.

The Problem: One Indicator, One Pane?

By default, a Pine Script indicator lives in exactly one place:

//@version=6
indicator("My Indicator", overlay = false) // separate pane only
plot(ta.rsi(close, 14), "RSI")

This means if you want RSI below *and* an EMA on the price chart, you traditionally needed two separate indicators — eating into your indicator limit.

That changed with force_overlay.

Step 1: Understanding force_overlay — The Key to Multi-Panel Output

Introduced in mid-2024 and available in both Pine Script v5 and v6, force_overlay lets individual plot elements display on the main chart pane even when the script itself occupies a separate pane.

Here's the core concept:

//@version=6
indicator("Multi-Panel Demo", overlay = false)

// This plots in the indicator's own pane (below the chart)
plot(ta.rsi(close, 14), "RSI", color.purple)

// This plots on the MAIN price chart, despite overlay = false
plot(ta.ema(close, 21), "EMA 21", color.orange, force_overlay = true)

What's happening:

This single parameter is what makes multi-panel layouts possible from one script.

Which Functions Support force_overlay?

Almost every visual function in Pine Script supports it:

Functionforce_overlay Support
plot()
plotshape()
plotchar()
plotarrow()
plotcandle()
plotbar()
bgcolor()
hline()
fill()
label.new()
line.new()
box.new()
table.new()

Step 2: Build a Combined RSI + EMA + Volume Indicator

Let's build something practical — a single indicator that shows:

1. Main chart: EMA crossover lines + buy/sell signals

2. Separate pane: RSI with overbought/oversold zones 3. Separate pane: Volume (normalized) in the same pane as RSI

//@version=6
indicator("Multi-Panel Swiss Knife", overlay = false)

// ─── INPUTS ───
int rsiLen      = input.int(14, "RSI Length")
int emaFast     = input.int(9, "Fast EMA")
int emaSlow     = input.int(21, "Slow EMA")

// ─── CALCULATIONS ───
float rsiVal    = ta.rsi(close, rsiLen)
float emaF      = ta.ema(close, emaFast)
float emaS      = ta.ema(close, emaSlow)
bool  bullCross = ta.crossover(emaF, emaS)
bool  bearCross = ta.crossunder(emaF, emaS)

// ─── PANE: RSI (this indicator's own pane) ───
plot(rsiVal, "RSI", color.new(color.purple, 0), 2)
hline(70, "Overbought", color.red, hline.style_dashed)
hline(30, "Oversold", color.green, hline.style_dashed)
hline(50, "Midline", color.gray, hline.style_dotted)

// Color the RSI pane background on extremes
bgcolor(rsiVal > 70 ? color.new(color.red, 90) : rsiVal < 30 ? color.new(color.green, 90) : na)

// ─── MAIN CHART: EMA lines (force_overlay) ───
plot(emaF, "Fast EMA", color.new(color.blue, 0), 2, force_overlay = true)
plot(emaS, "Slow EMA", color.new(color.orange, 0), 2, force_overlay = true)

// ─── MAIN CHART: Buy/Sell signals (force_overlay) ───
plotshape(bullCross, "Buy Signal", shape.triangleup,
     location.belowbar, color.green, size = size.small,
     force_overlay = true)
plotshape(bearCross, "Sell Signal", shape.triangledown,
     location.abovebar, color.red, size = size.small,
     force_overlay = true)

// ─── MAIN CHART: Highlight background on crossover ───
bgcolor(bullCross ? color.new(color.green, 85) : bearCross ? color.new(color.red, 85) : na,
     force_overlay = true)

💡 TradingView

Like what you're reading? Try it yourself — this link supports ChartedTrader at no cost to you.

Get TradingView Pro — unlock more indicators per chart →

Result: One indicator uses one slot, but you see:

Step 3: Add a Multi-Symbol Comparison Panel

Want to compare multiple assets in the same pane? Use request.security() to pull data from other symbols:

//@version=6
indicator("Multi-Symbol RSI Comparison", overlay = false)

// ─── INPUTS ───
string sym1 = input.symbol("BINANCE:BTCUSDT", "Symbol 1")
string sym2 = input.symbol("BINANCE:ETHUSDT", "Symbol 2")
string sym3 = input.symbol("BINANCE:SOLUSDT", "Symbol 3")
int    len  = input.int(14, "RSI Length")

// ─── FETCH RSI FROM EACH SYMBOL ───
float rsi1 = request.security(sym1, timeframe.period, ta.rsi(close, len))
float rsi2 = request.security(sym2, timeframe.period, ta.rsi(close, len))
float rsi3 = request.security(sym3, timeframe.period, ta.rsi(close, len))

// ─── PLOT ALL IN ONE PANE ───
plot(rsi1, "BTC RSI", color.orange, 2)
plot(rsi2, "ETH RSI", color.blue, 2)
plot(rsi3, "SOL RSI", color.purple, 2)

hline(70, "OB", color.red, hline.style_dashed)
hline(30, "OS", color.green, hline.style_dashed)

// ─── MAIN CHART: Label current values ───
var table infoTable = table.new(position.top_right, 3, 2,
     bgcolor = color.new(color.black, 70),
     force_overlay = true)

if barstate.islast
    table.cell(infoTable, 0, 0, "BTC RSI", text_color = color.orange)
    table.cell(infoTable, 1, 0, "ETH RSI", text_color = color.blue)
    table.cell(infoTable, 2, 0, "SOL RSI", text_color = color.purple)
    table.cell(infoTable, 0, 1, str.tostring(rsi1, "#.0"), text_color = color.white)
    table.cell(infoTable, 1, 1, str.tostring(rsi2, "#.0"), text_color = color.white)
    table.cell(infoTable, 2, 1, str.tostring(rsi3, "#.0"), text_color = color.white)

This gives you a single pane comparing RSI across three symbols, plus a floating info table on the main chart.

Step 4: Simulate Multiple Sub-Panes with Visual Zones

Pine Script doesn't support creating multiple separate panes from one indicator. But you can visually divide a single pane into zones using hline() boundaries and scaled data:

//@version=6
indicator("Dual Zone Panel", overlay = false)

// ─── RSI: Scaled to upper zone (50–100 range) ───
float rsiRaw = ta.rsi(close, 14)
float rsiScaled = 50 + (rsiRaw / 100) * 50  // Maps 0–100 → 50–100

// ─── Stochastic: Scaled to lower zone (0–50 range) ───
float stochRaw = ta.stoch(close, high, low, 14)
float stochScaled = (stochRaw / 100) * 50  // Maps 0–100 → 0–50

// ─── Visual separator ───
hline(50, "─── Separator ───", color.new(color.white, 30), hline.style_solid)

// ─── Upper zone: RSI ───
plot(rsiScaled, "RSI", color.purple, 2)
hline(85, "RSI OB", color.new(color.red, 60), hline.style_dotted)
hline(65, "RSI OS", color.new(color.green, 60), hline.style_dotted)

// ─── Lower zone: Stochastic ───
plot(stochScaled, "Stoch %K", color.teal, 2)
hline(40, "Stoch OB", color.new(color.red, 60), hline.style_dotted)
hline(10, "Stoch OS", color.new(color.green, 60), hline.style_dotted)

// ─── Labels for clarity ───
var label rsiLabel = label.new(na, na, "RSI Zone ↑", style = label.style_none,
     textcolor = color.purple, size = size.small)
var label stochLabel = label.new(na, na, "Stoch Zone ↓", style = label.style_none,
     textcolor = color.teal, size = size.small)

if barstate.islast
    label.set_xy(rsiLabel, bar_index + 3, 90)
    label.set_xy(stochLabel, bar_index + 3, 25)

Result: One pane, two visually separated zones — RSI on top, Stochastic on the bottom, divided by a line at 50.

Step 5: The Full Dashboard — Putting It All Together

Here's a production-ready indicator that combines everything:

//@version=6
indicator("📊 Multi-Chart Dashboard", overlay = false, max_labels_count = 50)

// ─── INPUTS ───
int    rsiLen   = input.int(14, "RSI Length", group = "Oscillators")
int    stochLen = input.int(14, "Stoch Length", group = "Oscillators")
int    emaFast  = input.int(9, "Fast EMA", group = "Trend")
int    emaSlow  = input.int(21, "Slow EMA", group = "Trend")
string sym2     = input.symbol("", "Compare Symbol", group = "Multi-Symbol")

// ─── CALCULATIONS ───
float rsiVal    = ta.rsi(close, rsiLen)
float stochK    = ta.stoch(close, high, low, stochLen)
float stochD    = ta.sma(stochK, 3)
float emaF      = ta.ema(close, emaFast)
float emaS      = ta.ema(close, emaSlow)
bool  bullX     = ta.crossover(emaF, emaS)
bool  bearX     = ta.crossunder(emaF, emaS)

// ─── PANE: RSI (upper zone, 50–100) ───
float rsiDisplay = 50 + (rsiVal / 100) * 50
plot(rsiDisplay, "RSI", color.new(color.purple, 0), 2)

// ─── PANE: Stochastic (lower zone, 0–50) ───
float stochDisplay = (stochK / 100) * 50
float stochDDisplay = (stochD / 100) * 50
plot(stochDisplay, "%K", color.new(color.teal, 0), 2)
plot(stochDDisplay, "%D", color.new(color.orange, 0), 1, plot.style_line)

// ─── PANE: Zone separator and levels ───
hline(50, "───────────", color.new(color.white, 40), hline.style_solid)
hline(85, "RSI OB", color.new(color.red, 70), hline.style_dotted)
hline(65, "RSI OS", color.new(color.green, 70), hline.style_dotted)
hline(40, "Stoch OB", color.new(color.red, 70), hline.style_dotted)
hline(10, "Stoch OS", color.new(color.green, 70), hline.style_dotted)

// ─── MAIN CHART: EMAs ───
plot(emaF, "Fast EMA", color.new(color.blue, 0), 2, force_overlay = true)
plot(emaS, "Slow EMA", color.new(color.orange, 0), 2, force_overlay = true)

// ─── MAIN CHART: Signals ───
plotshape(bullX, "Buy", shape.triangleup, location.belowbar,
     color.green, size = size.small, force_overlay = true)
plotshape(bearX, "Sell", shape.triangledown, location.abovebar,
     color.red, size = size.small, force_overlay = true)

// ─── MAIN CHART: Info table ───
var table dash = table.new(position.top_right, 2, 4,
     bgcolor = color.new(color.black, 75), border_width = 1,
     border_color = color.new(color.gray, 60),
     force_overlay = true)

if barstate.islast
    table.cell(dash, 0, 0, "RSI", text_color = color.purple, text_size = size.small)
    table.cell(dash, 1, 0, str.tostring(rsiVal, "#.0"),
         text_color = rsiVal > 70 ? color.red : rsiVal < 30 ? color.green : color.white,
         text_size = size.small)
    table.cell(dash, 0, 1, "Stoch %K", text_color = color.teal, text_size = size.small)
    table.cell(dash, 1, 1, str.tostring(stochK, "#.0"), text_color = color.white,
         text_size = size.small)
    table.cell(dash, 0, 2, "EMA Trend", text_color = color.blue, text_size = size.small)
    table.cell(dash, 1, 2, emaF > emaS ? "▲ Bullish" : "▼ Bearish",
         text_color = emaF > emaS ? color.green : color.red,
         text_size = size.small)

    // Optional second symbol comparison
    if sym2 != ""
        float sym2Close = request.security(sym2, timeframe.period, close)
        float sym2Rsi   = request.security(sym2, timeframe.period, ta.rsi(close, rsiLen))
        table.cell(dash, 0, 3, sym2, text_color = color.yellow, text_size = size.small)
        table.cell(dash, 1, 3, str.tostring(sym2Rsi, "#.0"),
             text_color = color.white, text_size = size.small)

v5 vs v6: What Changed for Multi-Chart Layouts?

FeaturePine Script v5Pine Script v6
force_overlay✅ Available✅ Available
request.security()✅ (same behavior)
request.footprint()✅ New in Jan 2026
Line wrappingStrict 4-space rules✅ Relaxed (Dec 2025)
AI code assistant✅ Built-in
Migration required?NoRecommended for new features
Bottom line: force_overlay works in both v5 and v6. If you're writing new code, use v6 — future features will only land there. If you have existing v5 scripts, they'll keep working without changes.

Tips and Limitations

What You Can Do

What You Can't Do (Yet)

Performance Tips

Conclusion

The force_overlay parameter is the single most important feature for building multi-panel Pine Script indicators. Combined with data scaling for visual sub-zones and request.security() for multi-symbol data, you can pack a full trading dashboard into one indicator slot.

This matters most on TradingView's free plan (3 indicators) but is useful even on paid plans — fewer indicators means faster chart loading and a cleaner workspace.

Ready to build your own multi-panel indicators? Start your free TradingView trial to get access to the Pine Script editor, more indicator slots, and real-time data across global markets.

---

*This tutorial uses Pine Script v6 syntax. All force_overlay examples are backward-compatible with v5 — just change the version comment to //@version=5.*

TradingView

Ready to get started? Use the link below — it helps support ChartedTrader at no cost to you.

Get TradingView Pro — unlock more indicators per chart →
📈

About the author

I'm a systematic trader running live strategies on IB (USDJPY momentum) and Hyperliquid (crypto perps). Every tool reviewed here is something I've used with real capital. Questions? Reach out.

📚 Related Articles

🎓 Tutorials

TradingView Free Plan Indicator Limit: How to Combine RSI, EMA, and MACD Into One Pine Script (2026)

Hit TradingView's 2-indicator limit on the free plan? Learn how to combine RSI, EMA, and MACD into a single all-in-one Pine Script v6 indicator — with full copy-paste code, visual customization tips, and a clear upgrade path when you outgrow the workaround.

March 23, 2026 ⏱ 10 min read
🎓 Tutorials

TradingView Webhook to Telegram Bot: Get Real-Time Alerts on Your Phone (2026 Setup Guide)

Learn how to send TradingView alerts directly to Telegram using webhooks. Step-by-step guide covering BotFather setup, free relay options, JSON payload formatting, and real trading alert examples — no coding experience required.

March 22, 2026 ⏱ 14 min read
🎓 Tutorials

TradingView Pine Script SuperTrend Strategy: Build a Custom Indicator Step by Step (2026)

Learn how to build a custom SuperTrend strategy indicator in Pine Script v6 with complete code. Includes RSI filter, multi-timeframe confirmation, stop loss/take profit, and backtesting setup for TradingView.

March 21, 2026 ⏱ 17 min read

📬 Get weekly trading insights

Real trades, honest reviews, no fluff. One email per week.