Pine Script v6 Complete Guide: Build Professional TradingView Indicators
From syntax basics to advanced tables, multi-timeframe analysis, webhooks and dashboard design — everything you need to write production-grade Pine Script indicators.
Why Pine Script v6?
Pine Script v6 (released 2024) brings major improvements over v5:
- Better type system
- Improved array and matrix operations
- request.security_lower_tf() for intrabar data
- Better error messages
- Native polyline drawings
If you're still on v5, upgrade now. The syntax is mostly compatible but v6 adds powerful new tools.
Your First Indicator: Structure
****`pine
//@version=6
indicator("My First Indicator", overlay=true, max_lines_count=200, max_boxes_count=200)
// ─── Inputs ─────────────────────────────────────────────────────
int ema_fast = input.int(20, "Fast EMA", minval=1, maxval=200)
int ema_slow = input.int(50, "Slow EMA", minval=1, maxval=200)
color bull_col = input.color(color.new(#26a69a, 0), "Bullish color")
color bear_col = input.color(color.new(#ef5350, 0), "Bearish color")
// ─── Calculations ─────────────────────────────────────────────
float fast = ta.ema(close, ema_fast)
float slow = ta.ema(close, ema_slow)
bool bull_cross = ta.crossover(fast, slow)
bool bear_cross = ta.crossunder(fast, slow)
// ─── Plots ────────────────────────────────────────────────────
plot(fast, "Fast EMA", bull_col, 2)
plot(slow, "Slow EMA", bear_col, 2)
plotshape(bull_cross, "Bull Signal", shape.triangleup, location.belowbar, bull_col, size=size.small)
plotshape(bear_cross, "Bear Signal", shape.triangledown, location.abovebar, bear_col, size=size.small)
****`
Multi-Timeframe Analysis (MTF)
The most powerful feature in Pine Script — pulling data from higher timeframes:
****`pine
//@version=6
indicator("MTF Dashboard", overlay=false)
// Get RSI from multiple timeframes
float rsi_5m = request.security(syminfo.tickerid, "5", ta.rsi(close, 14))
float rsi_15m = request.security(syminfo.tickerid, "15", ta.rsi(close, 14))
float rsi_1h = request.security(syminfo.tickerid, "60", ta.rsi(close, 14))
float rsi_4h = request.security(syminfo.tickerid, "240", ta.rsi(close, 14))
float rsi_1d = request.security(syminfo.tickerid, "D", ta.rsi(close, 14))
// Plot all on same chart
plot(rsi_5m, "RSI 5m", color.new(color.white, 40), 1)
plot(rsi_15m, "RSI 15m", color.yellow, 1)
plot(rsi_1h, "RSI 1H", color.orange, 2)
plot(rsi_4h, "RSI 4H", color.red, 2)
plot(rsi_1d, "RSI 1D", color.purple, 3)
hline(70, "OB", color.new(color.red, 50), linestyle=hline.style_dashed)
hline(30, "OS", color.new(color.green, 50), linestyle=hline.style_dashed)
****`
Pro tip: Use lookahead=barmerge.lookahead_off (the default) to avoid repainting. Never use lookahead_on in a live trading indicator.
Building Dashboard Tables
Tables are the most impressive feature for clients — a full dashboard in the corner of the chart:
****`pine
//@version=6
indicator("Market Dashboard", overlay=true)
// ─── Trend Analysis ────────────────────────────────────────────
string getTrend(int tf) =>
float ma = request.security(syminfo.tickerid, str.tostring(tf), ta.ema(close, 200))
float px = request.security(syminfo.tickerid, str.tostring(tf), close)
px > ma ? "BULLISH" : "BEARISH"
// ─── Create Table ──────────────────────────────────────────────
var table t = table.new(position.top_right, 3, 7,
bgcolor=color.new(color.black, 15),
border_width=1,
border_color=color.new(color.white, 70))
if barstate.islast
// Header
table.cell(t, 0, 0, "TF", text_color=color.white, text_size=size.small, bgcolor=color.new(color.gray, 30))
table.cell(t, 1, 0, "TREND", text_color=color.white, text_size=size.small, bgcolor=color.new(color.gray, 30))
table.cell(t, 2, 0, "RSI", text_color=color.white, text_size=size.small, bgcolor=color.new(color.gray, 30))
int[] tfs = array.from(5, 15, 60, 240, 1440)
string[] names = array.from("5m", "15m", "1H", "4H", "1D")
for i = 0 to 4
int tf = array.get(tfs, i)
string name = array.get(names, i)
string trend = getTrend(tf)
float rsi = request.security(syminfo.tickerid, str.tostring(tf), ta.rsi(close, 14))
color tColor = trend == "BULLISH" ? color.new(#26a69a, 0) : color.new(#ef5350, 0)
color rColor = rsi > 70 ? color.red : rsi < 30 ? color.green : color.white
table.cell(t, 0, i+1, name, text_color=color.white, text_size=size.small)
table.cell(t, 1, i+1, trend, text_color=tColor, text_size=size.small)
table.cell(t, 2, i+1, str.tostring(rsi, "#"), text_color=rColor, text_size=size.small)
****`
Order Blocks Detection
****`pine
//@version=6
indicator("Order Blocks", overlay=true, max_boxes_count=50)
int lookback = input.int(5, "Lookback", minval=2, maxval=20)
color bull_ob = input.color(color.new(#26a69a, 80), "Bullish OB")
color bear_ob = input.color(color.new(#ef5350, 80), "Bearish OB")
// Bullish OB: last bearish candle before strong bullish impulse
bool bull_impulse = close > ta.highest(high, lookback)[1]
bool bear_candle = close[1] < open[1]
if bull_impulse and bear_candle
box.new(bar_index[1], high[1], bar_index + 20, low[1],
bgcolor=bull_ob,
border_color=color.new(#26a69a, 40),
border_width=1,
xloc=xloc.bar_index)
// Bearish OB: last bullish candle before strong bearish impulse
bool bear_impulse = close < ta.lowest(low, lookback)[1]
bool bull_candle = close[1] > open[1]
if bear_impulse and bull_candle
box.new(bar_index[1], high[1], bar_index + 20, low[1],
bgcolor=bear_ob,
border_color=color.new(#ef5350, 40),
border_width=1,
xloc=xloc.bar_index)
****`
Connecting to Execution via Webhooks
This is where Pine Script becomes a complete trading system:
****pine // Create an alert condition alertcondition(bull_impulse and bear_candle, title="Bullish OB Formed", message='{"symbol":"{{ticker}}","action":"buy","price":{{close}},"tf":"{{interval}}","time":"{{time}}"}') ****
Then in TradingView:
- Create Alert → select this indicator
- Webhook URL → your execution server (n8n, custom Python, etc.)
- The JSON message gets sent to your server
- Your server places the trade automatically
Performance Optimization
Pine Script runs on every bar — optimization matters:
****`pine
// SLOW: recalculates on every bar
float bad_example = ta.ema(request.security(syminfo.tickerid, "D", close), 200)
// FAST: calculate once, cache result
float htf_close = request.security(syminfo.tickerid, "D", close)
float good_example = ta.ema(htf_close, 200)
// EVEN BETTER: only draw on last bar
if barstate.islast
label.new(bar_index, high, "Today's EMA200: " + str.tostring(good_example, "#.##"))
****`
Publishing Your Indicator on TradingView
Once your indicator is ready:
- Pine Editor → Save As → choose "Publish"
- Set to "Public" or "Invite Only" for paid access
- Write a compelling description with screenshots
- Price it at $15-$79/month for invite-only
I've seen simple but well-documented indicators earn $1,000-$5,000/month on TradingView's marketplace.
Want a Custom Indicator?
I build Pine Script indicators for traders who need:
- Specific entry/exit logic
- Multi-timeframe dashboards
- Proprietary strategies coded to exact specifications
- Webhook integration for automation