Pine Script Tutorial for Beginners: Hello World, Pine Editor Setup & First Chart Script

Pine Script is TradingView's built-in scripting language, purpose-built for creating custom technical indicators, strategies, and libraries directly on live charts. This step-by-step tutorial walks through opening the Pine Editor, understanding the minimal required structure of a Pine Script, writing a verified Hello World script, and applying it to a chart — all grounded in Pine Script v6 syntax and TradingView's documented behavior.


1. What Is Pine Script? A Technical Overview

Pine Script is a domain-specific language (DSL) compiled and executed on TradingView's servers. It operates on a bar-by-bar execution model: the script is called once per historical bar and once per real-time tick, processing data as a time series. Key engine characteristics include:

  • Strongly typed with implicit typing: Pine is strongly typed, but the compiler infers types in most cases. Variables carry series qualifiers such as series float or series int, reflecting that each variable holds a sequence of values across bars rather than a single scalar.
  • Execution model: Scripts execute sequentially from the oldest bar to the most recent, then re-execute on each new tick.
  • Drawing object limits: TradingView enforces limits on drawing objects (lines, labels, boxes, tables). In practice, creating objects unconditionally on every bar will quickly exhaust these limits; consult TradingView's official documentation for current per-type limits.
graph TD A["TradingView Chart Engine"] --> B["Pine Script Compiler"] B --> C["Bar-by-Bar Executor"] C --> D["Bar 0: Oldest Historical Bar"] D --> E["Bar 1"] E --> F["..."] F --> G["Bar N-1: Most Recent Bar"] G --> H{"barstate.islast = true"} H -->|"Yes"| I["Execute last-bar-only logic (e.g., label.new)"] H -->|"No"| J["Skip last-bar-only logic"] I --> K["Render Visual Output on Chart"] J --> K

2. Opening the Pine Editor

The Pine Editor is embedded directly in the TradingView platform. Follow these steps to access it:

  1. Log in to TradingView and open any chart.
  2. At the bottom of the screen, click the "Pine Editor" tab 
  3. The editor panel opens with a default template. Click "New" to start a blank script.
  4. After writing code, click "Add to chart" (or "Save & Add to chart") to compile and apply the script.

Insert a screenshot here showing the TradingView interface with the Pine Editor tab highlighted at the bottom of the screen, and the "Add to chart" button visible in the editor toolbar.


3. Anatomy of a Pine Script: Required Structure

A valid Pine Script requires at minimum two declarations: the version directive and the script type declaration. Everything else — plots, calculations, inputs — is optional depending on the script's purpose. Note that library() scripts and some strategy() configurations may produce no visual output on the chart by design.

Component Required? Example Purpose
Version Directive Yes //@version=6 Declares the Pine Script version for the compiler
Script Type Declaration Yes indicator("My Script") Defines the script as an indicator, strategy, or library
Calculations & Logic No sma20 = ta.sma(close, 20) User-defined computations on price series
Visual Output No plot(close) Renders data visually on the chart pane

The mathematical relationship between bars and series values can be expressed as:

$$\text{series}[i] = f(\text{bar}_i), \quad i \in \{0, 1, 2, \ldots, N-1\}$$

where $N$ is the total number of bars loaded, and $f$ is the user-defined computation applied at each bar index $i$.


4. Your First Script: Hello World in Pine Script v6

The canonical "Hello World" in Pine Script renders a label on the most recent bar displaying the text "Hello, World!". Because labels are drawing objects subject to TradingView's per-script limits, the script must guard against creating a new label on every bar — the correct pattern is to create the label only on the last bar using barstate.islast.

🔽 [Click to expand] View Full Pine Script Code — Hello World v6
//@version=6
// ─────────────────────────────────────────────────────────────
// Script Type: indicator
// Title      : Hello World — Pine Script v6 Tutorial
// Description: Renders a 'Hello, World!' label on the last bar
//              and plots the closing price as a baseline.
// ─────────────────────────────────────────────────────────────
indicator(
     title          = "Hello World — Pine Script v6",  // Name shown on chart
     shorttitle     = "HW",                             // Abbreviated name in legend
     overlay        = true                              // Draw on the main price chart
     )

// ── Section 1: Plot the closing price ────────────────────────
// plot() renders a continuous line on the chart.
// 'close' is a built-in series float representing the bar's closing price.
plot(
     series = close,           // The data series to plot
     title  = "Close Price",   // Label shown in the Data Window
     color  = color.blue,      // Line color
     linewidth = 1             // Line thickness in pixels
     )

// ── Section 2: Hello World label on the last bar ─────────────
// barstate.islast is true only on the most recently confirmed bar.
// Guarding label creation with this condition ensures we create
// at most one label, staying well within TradingView's drawing
// object limits for labels.
if barstate.islast
    label.new(
         x      = bar_index,          // x-position: current bar index (series int — valid for x)
         y      = high,               // y-position: current bar's high price
         text   = "Hello, World! 👋", // Display text
         style  = label.style_label_down, // Label shape pointing downward
         color  = color.new(color.green, 20), // Background color with 20% transparency
         textcolor = color.white,     // Text color
         size   = size.normal         // Text size
         )

4.1 Code Walkthrough

  • //@version=6 — Instructs the compiler to use Pine Script version 6 semantics. This must be the first non-comment line.
  • indicator(..., overlay = true) — Declares this as an indicator script overlaid on the main price pane. Without overlay = true, the script renders in a separate sub-pane.
  • plot(close, ...) — Plots the built-in close series (type: series float) as a continuous line.
  • if barstate.islast — Conditional guard ensuring label.new() is called only once, on the final bar. Calling label.new() unconditionally on every bar creates a new label object each bar and in practice quickly exhausts TradingView's drawing object limits for labels.
  • bar_index — A built-in series int representing the zero-based index of the current bar. It is a valid argument for the x parameter of label.new().



5. Common Beginner Errors and Fixes

The following table documents the most frequently encountered errors when writing a first Pine Script, along with their root causes and verified fixes.

Error / Symptom Root Cause Fix
Could not determine the referencing length Using a dynamic (series) value as a history reference offset, e.g., close[n] where n is a series int Use a simple int (literal or input.int()) as the offset, or restructure with array
Script compiles but no label appears label.new() called on every bar without a guard; drawing object limit reached and older labels are recycled or dropped Wrap label.new() in if barstate.islast or manage labels with an array and explicit deletion
Indentation error Mixing tabs and spaces within the same block; Pine Script requires consistent indentation using spaces only Use spaces only (do not mix tabs and spaces); configure your editor to insert spaces on Tab key press
table.new() called on every bar causes drawing limit issues table.new() creates a new table object on each bar execution, quickly exceeding TradingView's drawing object limits for tables Declare the table with var so it is created only once: var myTable = table.new(...)
Version directive missing The //@version=6 line is absent or placed after code Place //@version=6 as the very first line of the script

6. Execution Flow: Bar-by-Bar Processing

Understanding Pine Script's execution model is essential for writing correct scripts. The diagram below illustrates how the engine processes bars sequentially and how barstate flags change across the timeline.

sequenceDiagram participant Engine as TradingView Engine participant Script as Pine Script participant Chart as Chart Renderer Engine->>Script: Execute on Bar 0 (oldest) Script->>Chart: plot(close) → renders point Engine->>Script: Execute on Bar 1 Script->>Chart: plot(close) → renders point Engine->>Script: Execute on Bar N-1 (last) Script->>Script: barstate.islast = true Script->>Chart: label.new() → renders label Script->>Chart: plot(close) → renders point Engine->>Script: New real-time tick arrives Script->>Chart: Update last bar values

7. Extending the Hello World: Adding a Simple Moving Average

Once the basic structure is confirmed, a natural extension is to overlay a Simple Moving Average (SMA). The SMA over $n$ periods is defined as:

$$\text{SMA}_n(i) = \frac{1}{n} \sum_{k=0}^{n-1} \text{close}[i-k]$$

In Pine Script v6, this is computed with the built-in ta.sma() function:

🔽 [Click to expand] View Full Pine Script Code — Hello World + SMA Extension
//@version=6
// ─────────────────────────────────────────────────────────────
// Script Type: indicator
// Title      : Hello World + SMA — Pine Script v6 Tutorial
// Description: Extends the Hello World script with a user-
//              configurable Simple Moving Average overlay.
// ─────────────────────────────────────────────────────────────
indicator(
     title     = "Hello World + SMA — Pine Script v6",
     shorttitle = "HW+SMA",
     overlay   = true   // Overlay on the main price pane
     )

// ── Section 1: User Input ─────────────────────────────────────
// input.int() creates an interactive integer input in the
// script's Settings panel. 'minval' enforces a minimum of 1.
int smaPeriod = input.int(
     defval  = 20,          // Default period
     title   = "SMA Period", // Label in Settings panel
     minval  = 1            // Minimum allowed value
     )

// ── Section 2: SMA Calculation ───────────────────────────────
// ta.sma() computes the Simple Moving Average of a series.
// Result type: series float
float smaValue = ta.sma(close, smaPeriod)

// ── Section 3: Plot Close and SMA ────────────────────────────
plot(
     series    = close,
     title     = "Close",
     color     = color.new(color.blue, 60),  // Semi-transparent blue
     linewidth = 1
     )

plot(
     series    = smaValue,
     title     = "SMA",
     color     = color.orange,
     linewidth = 2
     )

// ── Section 4: Hello World label on last bar ─────────────────
// var keyword: the label variable is declared once and persists.
// We delete and recreate it on the last bar to keep exactly one
// label object alive at all times.
var label helloLabel = na

if barstate.islast
    // Delete the previous label instance if it exists
    label.delete(helloLabel)
    // Create a fresh label at the current last bar
    helloLabel := label.new(
         x         = bar_index,
         y         = high,
         text      = "Hello, World! 👋\nSMA(" + str.tostring(smaPeriod) + ") = " + str.tostring(math.round(smaValue, 2)),
         style     = label.style_label_down,
         color     = color.new(color.green, 20),
         textcolor = color.white,
         size      = size.normal
         )

7.1 Key Additions Explained

  • input.int() — Exposes a configurable integer parameter in the script's Settings panel, allowing users to change the SMA period without editing code.
  • ta.sma(close, smaPeriod) — Computes the rolling SMA. Returns na for the first smaPeriod - 1 bars where insufficient history exists.
  • var label helloLabel = na — The var keyword initializes the variable only on bar 0 and retains its value across bars, enabling the delete-and-recreate pattern that keeps exactly one label object alive.
  • str.tostring(math.round(smaValue, 2)) — Converts the rounded float SMA value to a string for display in the label text.

8. Pine Script Type System: A Practical Reference

Pine Script is strongly typed with implicit typing. The compiler infers types automatically in most assignments, but understanding the type qualifiers helps diagnose errors. The most important qualifier is series, which indicates the variable holds a distinct value for each bar.

Type Qualifier Description Example
const Value known at compile time; never changes const float PI = 3.14159
simple Single value determined on bar 0; constant across all bars simple int len = input.int(20)
series A distinct value per bar; the default qualifier for most built-ins series float c = close
series int Integer series; e.g., bar_index is a built-in series int bar_index (valid as x in label.new())

9. Conclusion

This tutorial established the foundational workflow for Pine Script development on TradingView. The core takeaways are:

  • Minimum required structure: A valid Pine Script needs only the version directive (//@version=6) and a script type declaration (indicator(), strategy(), or library()). Visual output is optional and context-dependent.
  • Drawing object discipline: Functions like label.new() and table.new() create persistent drawing objects. Calling them unconditionally on every bar in practice quickly exhausts TradingView's per-type drawing limits. Use barstate.islast guards or the var + delete pattern.
  • Series execution model: Pine executes bar-by-bar from oldest to newest. Every built-in price variable (close, high, etc.) is a series float, and understanding this qualifier is key to avoiding type-related compiler errors.

Ideas for Script Advancement

  1. Multi-timeframe (MTF) data: Extend the SMA script to fetch the same SMA from a higher timeframe using request.security(), and compare it to the current timeframe's SMA to build a basic trend-alignment filter.
  2. Alert integration: Add alertcondition() to trigger TradingView alerts when the close price crosses the SMA, transforming the visual indicator into an actionable notification system.

Related Posts

Comments

Popular posts from this blog

Pine Script v6: indicator() vs strategy() — Core Functional Differences Explained

Pine Script Variable Declaration: Mastering var, varip, and Proper Initialization Across Historical Bars

Pine Script v6 plot() Function: Complete Guide to Lines, Histograms, and Circles