File shape
//@version=1
// <one-line description>
// <author / licence>
indicator("Title", overlay=true)
// ─── Constants ─────────────────────────────────────────────────────────
color BULL = color.lime
color BEAR = color.red
// ─── Inputs ────────────────────────────────────────────────────────────
length = input.int(14, "Length", minval=1)
source = input.source(close, "Source")
// ─── Helpers ───────────────────────────────────────────────────────────
smooth(src, len) =>
ta.ema(src, len)
// ─── Calculations ──────────────────────────────────────────────────────
sm = smooth(source, length)
// ─── Visuals ───────────────────────────────────────────────────────────
plot(sm, "Smoothed", color=color.aqua, linewidth=2)
// ─── Alerts ────────────────────────────────────────────────────────────
alertcondition(ta.crossover(close, sm), "Cross up", "{{ticker}} crossed above its smoothed series")
The six-section layout (Constants / Inputs / Helpers / Calculations / Visuals / Alerts) scales — add Risk / Strategy sections after Visuals for strategies, Imports before Constants for library-consuming scripts.
Naming
lowerCamelCasefor variables and parameters:kernelLength,isTrendingUp,smoothedRsi.UPPER_SNAKE_CASEfor script-level constants:MAX_ZONES,BULL_COLOR.PascalCasefor UDT and enum type names:type Pivot,enum Trend.- Prefix variables with their unit when they could be confused:
lenvslenBarsvslenPctvslenPrice. - Suffix input ids with their intent:
showMa(bool),maLen(int),maColor(color).
Formatting
- 4-space indentation.
- One blank line between logical sections; two blank lines are unnecessary.
- Line length under ~110 characters is a target, not a rule.
- Align
=within a block of consecutive declarations if it helps readability — but don't fight the editor to maintain alignment.
length = input.int (14, "Length", minval=1)
source = input.source(close, "Source")
mult = input.float (2.0, "Multiplier", step=0.1)
Comments
- Describe why, not what.
- A comment that restates the code (
ma = ta.sma(close, 20) // compute the SMA) is noise. - A comment that explains the choice (
// We use SMA 20 — not EMA — because the user sees it as "flat".) is signal.
Function design
- A pure function (depends only on parameters + current-bar built-ins) is almost always a win over a function that reads top-level state.
- Return a tuple instead of mutating globals:
[mid, up, lo] = bollinger(src, len, mult). - Keep bodies to one screen's worth; split a long calculation into named helpers.
Inputs
- Every input has a
title=. - Related inputs share a
group=; booleans + their colour swatches share aninline=. - Use
tooltip=for anything whose unit or effect isn't obvious. - Use
active=to grey out dependent controls instead of silently ignoring them. - Pin numeric input ranges with
minval=/maxval=.
Plots
- Every plot has a
title=. - Pick a palette and stick to it — a script that uses 7 different random greens is harder to read than one that uses
color.lime+color.greenconsistently. - Use
display.data_windowwhen the value is useful but noisy on the pane itself. trackprice=trueis for last-value trackers;hlineis for constant levels. Don't mix.
Strategies
- Decide once whether orders fill at bar close (
process_orders_on_close=true) or next bar's open (default). State the choice in a header comment. initial_capital,default_qty_type,default_qty_value,commission_type,commission_value,slippagealways set explicitly — defaults are easy to miss.- Guard entries with
barstate.isconfirmedunless you deliberately want tick-level signalling.
Libraries
- One concept per library (
Zones,Pivots,Signal, …) rather than a kitchen sink. - Export every type a function signature references — the compiler enforces it for UDTs, but it reads cleaner too.
- Keep bodies pure and typed.
Example of applying the style
//@version=1
// Supertrend with crossover alerts.
// Signals confirm on bar close; stops are placed `atrMult` × ATR from the mid price.
indicator("Supertrend", overlay=true, max_lines_count=100)
// ─── Inputs ────────────────────────────────────────────────────────────
grpCore = "Core"
grpStyle = "Style"
atrLen = input.int (10, "ATR length", minval=1, group=grpCore)
atrMult = input.float (3.0, "ATR multiplier", minval=0.1, step=0.1, group=grpCore)
colUp = input.color (color.lime, "Bullish", group=grpStyle, inline="col")
colDn = input.color (color.red, "Bearish", group=grpStyle, inline="col")
// ─── Calculations ──────────────────────────────────────────────────────
[trend, dir] = ta.supertrend(atrMult, atrLen)
// ─── Visuals ───────────────────────────────────────────────────────────
plot(trend, "Supertrend", color = dir > 0 ? colUp : colDn, linewidth = 2)
// ─── Alerts ────────────────────────────────────────────────────────────
alertcondition(ta.change(dir) == 2, "Flip up", "{{ticker}} Supertrend flipped bullish")
alertcondition(ta.change(dir) == -2, "Flip down", "{{ticker}} Supertrend flipped bearish")