Pine Script v6 Best Practices for Writing Comments: Inline and Block Comment Techniques for Professional Code

Readable, maintainable Pine Script code depends heavily on disciplined commenting habits. Whether you are building a personal indicator or publishing a library for the TradingView community, well-structured comments transform opaque logic into self-documenting systems that any developer can audit, extend, or debug with confidence.

1. The Only Comment Syntax in Pine Script v6

Before diving into best practices, one critical fact must be established: Pine Script v6 supports only single-line comments using the // prefix. The /* */ block comment syntax familiar from C, JavaScript, or Java does not exist in Pine Script and will produce a compile error (CE10156) if used.

Syntax Supported in v6? Notes
// single line ✅ Yes The only valid comment syntax
/* block */ ❌ No — CE10156 Compile error; do not use

To comment out multiple lines simultaneously in the Pine Editor, select the lines and press Ctrl+/ (Windows/Linux) or Cmd+/ (macOS). The editor prepends // to each selected line automatically.

2. The Official Compiler Annotation System

Pine Script v6 ships with a native documentation annotation system that is parsed by the Pine Editor to generate tooltips and library documentation. These annotations use specially prefixed // comments and are the professional standard for any exported library or shared script.

Annotation Purpose Applies To
//@function Describes what a function does User-defined functions
//@param Documents a parameter name and meaning Function parameters
//@returns Describes the return value User-defined functions
//@type Describes a User-Defined Type (UDT) UDT declarations
//@field Documents a field inside a UDT UDT fields
//@variable Documents a script-level variable Script-level variables

These annotations are not merely cosmetic — the Pine Editor parses them to surface inline documentation when users hover over function calls in the editor, making them essential for any published library.

3. Structural Comment Patterns

Because // is the only available syntax, professional Pine Script developers use consistent visual patterns to simulate section headers, dividers, and block-level documentation. The following patterns are widely adopted in production-grade scripts.

3.1 Script Header Block

Every script should begin with a header block that captures authorship, version, and purpose. This block uses repeated // lines to create a visual boundary.

🔽 [Click to expand] View Full Pine Script Code
//@version=6
// ============================================================
// Script  : Adaptive Volatility Band
// Author  : Your Name
// Version : 1.0.0
// Date    : 2025-01-01
// License : Mozilla Public License 2.0
// Desc    : Plots dynamic bands derived from ATR-scaled
//           standard deviation around a chosen moving average.
// ============================================================
indicator("Adaptive Volatility Band", overlay = true)

3.2 Section Dividers

Dividing a script into logical sections — inputs, calculations, plotting — dramatically reduces cognitive load when revisiting code weeks later. Use a consistent divider style throughout the entire file.

🔽 [Click to expand] View Full Pine Script Code
//@version=6
indicator("Section Divider Demo", overlay = true)

// ── INPUTS ────────────────────────────────────────────────
int   lengthInput  = input.int(20,  "MA Length",  minval = 1)
float multInput    = input.float(2.0, "ATR Mult", minval = 0.1)

// ── CALCULATIONS ──────────────────────────────────────────
float basis = ta.sma(close, lengthInput)          // simple moving average
float atr   = ta.atr(lengthInput)                 // average true range
float upper = basis + multInput * atr             // upper band
float lower = basis - multInput * atr             // lower band

// ── PLOTTING ──────────────────────────────────────────────
plot(basis, "Basis", color = color.blue)
plot(upper, "Upper", color = color.green)
plot(lower, "Lower", color = color.red)

4. Inline Comments: Precision Over Verbosity

An inline comment placed at the end of a code line should answer why a value exists or what a non-obvious expression computes — not restate what the code already says. The goal is to reduce the reader's mental parsing effort, not to duplicate information.

Pattern Example Quality
Redundant (avoid) float x = close * 2 // multiply close by 2 ❌ Adds no value
Explanatory (good) float x = close * 2 // double-weighted for momentum bias ✅ Explains intent
Formula reference (good) // z = (x - μ) / σ → z-score normalization ✅ Links code to math
Warning (good) // NOTE: series int — cannot pass to ta.ema() length ✅ Prevents future bugs

5. Documenting Functions with Official Annotations

The following example demonstrates the complete annotation pattern for a user-defined function, including the mathematical formula it implements. The z-score formula is $ z = \frac{x - \mu}{\sigma} $, where $\mu$ is the rolling mean and $\sigma$ is the rolling standard deviation.

🔽 [Click to expand] View Full Pine Script Code
//@version=6
indicator("Z-Score Annotation Demo", overlay = false)

// ── FUNCTION ──────────────────────────────────────────────

//@function  Computes the rolling z-score of a source series.
//           Formula: z = (x - mean(x, len)) / stdev(x, len)
//           Returns na when standard deviation equals zero
//           to avoid division by zero.
//@param src    The input price series to normalize.
//@param len    The lookback window length (simple int required).
//@returns      A series float representing the z-score value.
zscore(float src, simple int len) =>
    float mu    = ta.sma(src, len)          // rolling mean  μ
    float sigma = ta.stdev(src, len)        // rolling stdev σ
    // Guard: return na when σ = 0 to prevent division by zero
    sigma != 0 ? (src - mu) / sigma : na

// ── INPUTS ────────────────────────────────────────────────
//@variable lengthInput  Lookback period chosen by the user.
int lengthInput = input.int(20, "Length", minval = 2)

// ── CALCULATION ───────────────────────────────────────────
float z = zscore(close, lengthInput)        // compute z-score on close

// ── PLOTTING ──────────────────────────────────────────────
plot(z,    "Z-Score",  color = color.blue)
hline(2,   "Upper 2σ", color = color.red,   linestyle = hline.style_dashed)
hline(-2,  "Lower 2σ", color = color.green, linestyle = hline.style_dashed)
hline(0,   "Zero",     color = color.gray,  linestyle = hline.style_solid)

Capture the Pine Editor with the z-score script loaded. Hover over the zscore() function call to show the tooltip generated from the //@function, //@param, and //@returns annotations. This visually confirms that the annotation system is active and functional in the editor.

6. Documenting User-Defined Types (UDTs)

UDTs benefit enormously from //@type and //@field annotations. Without them, a reader must infer the semantic meaning of each field from its name alone — a fragile assumption in complex systems.

🔽 [Click to expand] View Full Pine Script Code
//@version=6
indicator("UDT Annotation Demo", overlay = true)

// ── TYPE DEFINITION ───────────────────────────────────────

//@type  Encapsulates the state of a single swing pivot point.
//       Stores both the price level and the bar index at which
//       the pivot was detected.
type SwingPoint
    //@field price     The high or low price of the pivot bar.
    float price
    //@field barIndex  The absolute bar_index of the pivot bar.
    int   barIndex
    //@field isHigh    True if this pivot is a swing high;
    //                 false if it is a swing low.
    bool  isHigh

// ── USAGE ─────────────────────────────────────────────────
// Initialise a new SwingPoint on the current bar
var SwingPoint lastPivot = SwingPoint.new(
    price    = high,        // capture current high as pivot price
    barIndex = bar_index,   // record current bar index
    isHigh   = true         // mark as swing high
)

// Access field from one bar ago using correct UDT history syntax
// Rule: (obj[n]).field — NOT obj.field[n]
float prevPrice = na(lastPivot[1]) ? na : (lastPivot[1]).price

plot(prevPrice, "Prev Pivot Price", color = color.orange)

7. Simulating Block Comments for Temporarily Disabled Code

Because /* */ does not exist, the standard workflow for temporarily disabling a block of code is to use the Pine Editor's multi-line toggle shortcut. Select the target lines, then press Ctrl+/ to prepend // to every selected line. Press the same shortcut again to uncomment them.

🔽 [Click to expand] View Full Pine Script Code
//@version=6
indicator("Disabled Block Demo", overlay = true)

// The following block is temporarily disabled for debugging.
// To re-enable: select all lines below and press Ctrl+/

// float debugValue = ta.rsi(close, 14)
// float debugSma   = ta.sma(debugValue, 5)
// plot(debugSma, "Debug RSI SMA", color = color.purple)

// Active production code:
plot(close, "Close", color = color.blue)    // always-on baseline plot

Record a short screen capture (GIF or video) demonstrating the Ctrl+/ multi-line comment toggle in the Pine Editor. Select 3–4 lines, press Ctrl+/, show the // prefixes appearing, then press Ctrl+/ again to remove them. This is the definitive visual proof that block-commenting is achievable without /* */.

8. Comment Logic Flow Diagram

The diagram below maps the decision process a developer should follow when deciding which comment type to apply at any given point in a Pine Script file.

flowchart TD A[Start: Writing a line of Pine Script] --> B{Is this the top of the file?} B -- Yes --> C[Write Script Header Block Author, Version, Purpose using // lines] B -- No --> D{Is this a new logical section? Inputs / Calculations / Plotting} D -- Yes --> E[Insert Section Divider // ── SECTION NAME ──────────] D -- No --> F{Is this a user-defined function or UDT?} F -- Yes --> G[Apply Official Annotations //@function //@param //@returns //@type / //@field] F -- No --> H{Is this a script-level variable?} H -- Yes --> I[Apply //@variable annotation] H -- No --> J{Does the line contain non-obvious logic or a formula?} J -- Yes --> K[Add inline // comment explaining WHY or the formula] J -- No --> L{Is code temporarily disabled?} L -- Yes --> M[Select lines in Pine Editor Press Ctrl+/ to prepend //] L -- No --> N[No comment needed Self-explanatory code is clean code] C --> Z[End] E --> Z G --> Z I --> Z K --> Z M --> Z N --> Z

9. Complete Best-Practice Reference Script

The following script consolidates every commenting technique covered in this article into a single, production-ready template that can be used as a starting point for any new Pine Script v6 project.

🔽 [Click to expand] View Full Pine Script Code
//@version=6
// ============================================================
// Script  : Comment Best Practices Template
// Author  : Your Name
// Version : 1.0.0
// Date    : 2025-01-01
// Purpose : Demonstrates all official Pine Script v6 comment
//           patterns: inline, section dividers, annotations,
//           and UDT documentation.
// ============================================================
indicator("Comment Best Practices Template", overlay = false)

// ── TYPE DEFINITIONS ──────────────────────────────────────

//@type  Holds a pair of Bollinger Band boundary values
//       computed at a specific bar.
type BandSnapshot
    //@field upper  Upper band value: mean + k * stdev
    float upper
    //@field lower  Lower band value: mean - k * stdev
    float lower

// ── FUNCTIONS ─────────────────────────────────────────────

//@function  Computes Bollinger Band upper and lower values.
//           Formula:
//             upper = SMA(src, len) + mult * STDEV(src, len)
//             lower = SMA(src, len) - mult * STDEV(src, len)
//@param src   Source series (typically close).
//@param len   Lookback length (simple int).
//@param mult  Standard deviation multiplier (simple float).
//@returns     A BandSnapshot UDT containing upper and lower.
bands(float src, simple int len, simple float mult) =>
    float basis = ta.sma(src, len)          // rolling mean
    float dev   = ta.stdev(src, len)        // rolling standard deviation
    BandSnapshot.new(
        upper = basis + mult * dev,         // upper = μ + k·σ
        lower = basis - mult * dev          // lower = μ - k·σ
    )

// ── INPUTS ────────────────────────────────────────────────

//@variable lengthInput  Lookback window for the Bollinger Band.
int   lengthInput = input.int(20,  "BB Length",  minval = 2)

//@variable multInput    Standard deviation multiplier k.
float multInput   = input.float(2.0, "BB Mult", minval = 0.1, step = 0.1)

// ── CALCULATIONS ──────────────────────────────────────────

// Compute bands for the current bar
BandSnapshot snap = bands(close, lengthInput, multInput)

// Bandwidth: measures band expansion/contraction
// Formula: BW = (upper - lower) / SMA(close, len)
float bw = (snap.upper - snap.lower) / ta.sma(close, lengthInput)

// ── PLOTTING ──────────────────────────────────────────────

plot(snap.upper, "Upper Band", color = color.green)   // upper boundary
plot(snap.lower, "Lower Band", color = color.red)     // lower boundary
plot(bw,         "Bandwidth",  color = color.blue)    // normalised width

// Reference lines
hline(0, "Zero", color = color.gray, linestyle = hline.style_dotted)

Copy the complete best-practice template script above into the TradingView Pine Editor and compile it. Confirm: (1) zero compile errors, (2) the //@function tooltip appears when hovering over the bands() call, (3) the three plots render correctly on the chart. Document any discrepancies and update the code accordingly before publishing.

10. Anti-Patterns to Avoid

Anti-Pattern Problem Correct Approach
Using /* */ CE10156 compile error Use // on each line or Ctrl+/
Redundant inline comments Adds noise, no information gain Comment the why, not the what
No section dividers Long scripts become unnavigable Use consistent // ── SECTION ── headers
Missing //@param on exported functions No tooltip in Pine Editor for library users Annotate every parameter with //@param
Stale comments after refactoring Comments contradict the code — worse than no comment Update comments in the same commit as the code change

Conclusion

  • Pine Script v6 supports only // single-line comments. The /* */ syntax triggers CE10156. Use Ctrl+/ in the Pine Editor to toggle // across multiple selected lines simultaneously.
  • The official annotation system (//@function, //@param, //@returns, //@type, //@field, //@variable) is parsed by the Pine Editor to generate inline tooltips and library documentation — making it the professional standard for any shared or published script.
  • Effective inline comments explain intent and mathematical context, not syntax. Pairing code with its underlying formula (e.g., $ z = \frac{x - \mu}{\sigma} $) and flagging type-qualifier constraints (e.g., simple int required) prevents both reader confusion and future regression bugs.

Ideas for Further Development

  • Automated comment linting: Build a Pine Script library that exports a validation function checking whether key variables carry //@variable annotations, surfacing undocumented identifiers as warning labels on the chart.
  • Comment-driven versioning: Establish a convention where the script header block includes a // CHANGELOG section with dated entries, enabling diff-based auditing of indicator logic changes directly inside the Pine file without relying on external version control.

Related Posts

Comments

Popular posts from this blog

How to Build a Volume Indicator in Pine Script v6: Buying & Selling Pressure Analysis

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

SuperTrend Indicator in Pine Script v6: Canonical Formula, Flip Logic, and Non-Repainting Signals