Pine Script is TradingView's language for indicators and strategies. It's small, purpose-built and forgiving in many ways — but it runs differently from most programming languages, and that difference trips up almost every beginner. Once you understand how a script executes, the rest is mostly vocabulary.
This guide covers the concepts you need to read, tweak and debug Pine Script v6 code, whether you write it yourself or generate it.
Three kinds of scripts
Every script starts with a version annotation and a declaration:
indicator()— calculates and draws things: lines, shapes, backgrounds, tables, alerts.strategy()— everything an indicator can do, plus simulated orders that appear in the Strategy Tester.library()— reusable functions for other scripts to import.
The first line, //@version=6, tells the compiler which version of the language to use. Without it, or with an older number, the same code can behave differently or fail to compile.
The execution model: one bar at a time
Your script runs once on every bar of the chart, from the oldest to the newest, and then again on every price update of the live bar. Each run sees only the data up to that bar.
So when you write close, you don't get "the closing price" as a single number — you get the close of whichever bar the script is processing. Values that change from bar to bar are called series.
To look back in time, use the history-referencing operator []:
close[1]— the previous bar's closehigh[5]— the high from five bars ago
On the live bar, close is the latest price and keeps changing until the bar closes, so logic that depends on it can flicker on and off in real time. If you only want to act on completed bars, check barstate.isconfirmed.
Variables and var
A normal declaration runs again on every bar:
count = 0
count := count + 1 // always 1: reset to 0 on each bar
The var keyword initialises a variable once and keeps its value across bars:
var int count = 0
count += 1 // counts bars
Note the two operators: = declares a variable, while := (or += and friends) reassigns one.
Your first indicator
//@version=6
indicator("RSI with zones")
len = input.int(14, "RSI length", minval = 2)
src = input.source(close, "Source")
upper = input.int(70, "Overbought")
lower = input.int(30, "Oversold")
r = ta.rsi(src, len)
plot(r, "RSI", color = color.purple, linewidth = 2)
hline(upper, "Overbought", color = color.gray)
hline(lower, "Oversold", color = color.gray)
bgcolor(r > upper ? color.new(color.red, 85) : r < lower ? color.new(color.green, 85) : na)
What's going on:
input.*()functions create settings the user can change in the indicator's settings dialog.ta.rsi()comes from thetanamespace of built-in technical analysis functions:ta.ema,ta.sma,ta.atr,ta.crossover,ta.highestand many more.plot()draws a series,hline()draws a fixed horizontal level, andbgcolor()tints the background.color.new(color.red, 85)is red at 85% transparency.- The
? :ternary operator picks a value based on a condition;nameans "no value", so no tint.
Because the declaration doesn't set overlay = true, the indicator appears in its own pane below the price chart.
Your first strategy
//@version=6
strategy("Trend pullback", overlay = true, initial_capital = 10000,
default_qty_type = strategy.percent_of_equity, default_qty_value = 10,
commission_type = strategy.commission.percent, commission_value = 0.05)
trend = ta.ema(close, 200)
r = ta.rsi(close, 14)
atr = ta.atr(14)
longSignal = close > trend and ta.crossover(r, 40)
var float stopPrice = na
var float targetPrice = na
if longSignal and strategy.position_size == 0
stopPrice := close - 2 * atr
targetPrice := close + 3 * atr
strategy.entry("Long", strategy.long)
strategy.exit("Exit", "Long", stop = stopPrice, limit = targetPrice)
plot(trend, "EMA 200", color = color.gray)
Key points:
strategy.entry()opens a position;strategy.exit()attaches a stop and a target to it.- Orders placed on a bar normally fill at the next bar's open in the backtest.
- The stop and target are stored with
varat entry time, so they don't drift as ATR changes. - Commission is set in the declaration — leaving costs out is one of the backtesting mistakes that inflate results.
ta.crossover()is called on every bar, outside theifblock. Functions that depend on history should run on every bar so their internal values stay consistent.- The long
strategy()call wraps across lines. Continuation lines are indented by a number of spaces that isn't a multiple of four, so the compiler doesn't mistake them for a new block.
What changed in v6
If you're reading older v5 code, these v6 changes matter most:
| Change | What it means |
|---|---|
| No implicit numeric-to-bool casting | if volume no longer compiles; write if volume > 0 or use bool() |
Booleans can't be na | A bool is always true or false, and na() no longer accepts bools |
Lazy and / or | The right-hand side is only evaluated when needed — don't hide required calculations there |
| Dynamic requests by default | request.security() can be called inside loops and conditional blocks |
when parameter removed | Wrap strategy.entry() and similar calls in an if block instead |
The Pine Editor can convert a v5 script to v6 from its menu. Review the result, particularly around boolean logic.
Reading compiler errors
Errors appear in the console under the editor with a line number. The common ones are typos (Undeclared identifier), indentation inside if blocks (local blocks use four spaces or a tab), and calling a function with the wrong argument type. Fix the first error first — later ones are often caused by it.
Next steps
Once your script compiles, add it to a TradingView chart, then change one input at a time and watch the effect. When you're ready to turn an idea into code, read how to describe a trading strategy to AI.
Try it with Edgoro
Describe an indicator or strategy in plain English and Edgoro writes it in Pine Script v6, ready to paste. Ask it to explain any line you don't understand or to change the logic, and every version is saved in your library. Start free.
Trading involves substantial risk of loss. Learning to code a strategy doesn't make it profitable; test before you trade.