BTC·
ETH·
SOL·
BNB·
XRP·
ADA·
DOGE·
AVAX·
LINK·
MATIC·
DOT·
ATOM·
BTC·
ETH·
SOL·
BNB·
XRP·
ADA·
DOGE·
AVAX·
LINK·
MATIC·
DOT·
ATOM·
Back to blog
Pine ScriptTradingViewIndicatorTutorial

Pine Script v6 Complete Guide: Build Professional TradingView Indicators

iL
Ismaël Ladjohounlou
November 10, 202522 min read

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:

  1. Create Alert → select this indicator
  2. Webhook URL → your execution server (n8n, custom Python, etc.)
  3. The JSON message gets sent to your server
  4. 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:

  1. Pine Editor → Save As → choose "Publish"
  2. Set to "Public" or "Invite Only" for paid access
  3. Write a compelling description with screenshots
  4. 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

See my Pine Script products → or contact me →