Inputs & directives
@version, @name, @pane, and @input — the four header directives that wire your script to the chart engine. Plus every input.* builder, its parameters, and the settings widget it produces.
The first few lines of every Delta DSL script are directives — declarative metadata the engine reads at script-load time to decide which language version the script targets, where the indicator lives, what it's called, and which knobs the user can tweak. Directives start with @, MUST sit on their own line, and conventionally cluster at the top of the file.
Four directives exist:
@version— pin the script to a specific Delta DSL language version (optional but recommended for new scripts).@name— display name in the indicator catalog and legend.@pane— where the indicator renders (overlay on candles, sub-pane below, or a custom aux pane).@input— declare a user-tunable parameter and pick its widget.
@version
@version 1 // pin to Delta DSL v1 — current language version
- Optional. Defaults to v1 when omitted, so legacy scripts keep working unchanged.
- Must be the first non-blank line if present (apart from comments and other directives — order among directives doesn't matter, but
@versionmust precede every executable statement). - Accepts a positive integer. Bare integer form (
@version 1) is canonical and matches Pine-style//@version=5. The quoted form (@version "1") is also accepted as an alias. - One per script. Declaring it twice is rejected.
- Cannot exceed the platform's currently shipped version. Writing
@version 9against a v1 runtime surfaces an editor error.
The directive's job is to freeze your script's language semantics at the version you wrote it for. When the platform ships a future Delta DSL version with breaking changes, your @version 1 script keeps running under v1 rules — bullet-proof against semantic drift. New scripts adopting newer language versions opt in by bumping the value.
For the full migration story, see Versioning & Migration.
@name
@name "RSI Divergence"
- One per script (additional
@namedeclarations override earlier ones). - The string becomes the legend label, the catalog title, and the alert builder section header.
- Plain text only — emoji and HTML render literally and look wrong on the dark trading surface. Stick to ASCII / Unicode glyphs (
·,→,▲,▼). - If omitted, the script falls back to
"Untitled script"in the catalog.
@pane
@pane "overlay" // default — draw on the candle pane in price space
@pane "below" // legacy single shared sub-pane at the bottom
@pane "rsi" // independent aux pane keyed by id
@pane "macd" // a second independent pane stacked below "rsi"
The pane id determines:
- Which drawing primitives are available (overlay →
plot*, sub-pane →pane*+panePlot*). - Whether the indicator's Y axis is price or its own value space.
- Whether multiple scripts share a band (same id) or each get their own.
Pane id rules:
- Lowercase letters, digits,
_, and-only. Max 32 characters. "overlay"and"below"are reserved (see below).- Any other id allocates an independent aux pane. Multiple scripts with the same id share the band; different ids stack vertically in registration order.
"overlay" (default)
Every plotLine / plotHline / plotBand / plotShape call renders on the candle pane via the engine's world_to_screen_y(price). Right mode for moving averages, Bollinger Bands, support/resistance, anchored VWAPs — anything in price space.
For oscillators (0-100 RSI, MACD histogram) drawn on the candle pane, you have to map the series into the visible price range or the line draws at the bottom of the chart and is invisible when price is in the thousands:
@pane "overlay"
r = rsi(close, 14)
priceLow = lowest(low, 200)
priceHigh = highest(high, 200)
rsiLine = normalize(r, 0, 100, priceLow, priceHigh)
plotLine(rsiLine, color="#F0B90B", width=1.5)
normalize(src, fromMin, fromMax, toMin, toMax) accepts series for toMin / toMax, so the overlay tracks price as you pan / zoom.
"below"
@pane "below" reserves a band at the bottom of the candle area, masks the candles behind it opaque so they don't bleed through, and remaps every pane* call into the band. Use it for momentum indicators with their own Y axis.
@pane "below"
@name "RSI"
@input period = input.int(14, "Period")
paneHeight(0.25) // 25 % of the candle area (0.10..0.50)
paneRange(0, 100) // lock the Y axis (omit to auto-fit)
r = rsi(close, period)
paneBand(70, 30, color="rgba(240,185,11,0.06)")
paneLine(r, color="#F0B90B", width=1.5)
paneHline(70, color="#F23645", width=1)
paneHline(30, color="#0ECB81", width=1)
paneLabel(70, "70", align="left")
paneLabel(30, "30", align="left")
The pane Y axis prints three labels automatically (min / mid / max) on the right edge, plus the script's @name in the top-left of the band so users can tell which script owns the band.
See Panes for the full pane reference.
Custom aux pane (any other id)
Any id other than "overlay" / "below" allocates a dedicated aux pane that stacks below the "below" pane in registration order. Two scripts with the same id share one pane — useful for compound indicators (e.g. @pane "macd" for histogram + signal line + MACD line in the same band).
@pane "rsi"
@name "RSI"
@input period = input.int(14, "Period")
paneHeight(0.18)
paneRange(0, 100)
r = rsi(close, period)
paneBand(70, 30, color="rgba(240,185,11,0.06)")
paneLine(r, color="#F0B90B", width=1.5)
A second script with @pane "macd" opens its own pane below the RSI pane, with its own Y range and crosshair tag.
Limitations (not blockers — things to know):
- Aux pane order is registration order. To reorder, disable + re-enable scripts.
- The candle pane's last price-axis tick may peek behind the topmost aux pane border by 1 px on Retina displays.
@input
@input <name> = <default-or-builder> [, "Label"]
Two accepted shapes:
- Literal default — shorthand. The settings widget is inferred from the literal type.
@input length = 20 // → number stepper, no clamp @input use_ema = false // → toggle @input mode = "standard" // → text field @input lineColor = "#F0B90B" // → color picker (recognises hex / rgba) @input title = 50, "Period" // → number stepper labelled "Period" - Typed builder — full control over the widget. Use this when you need clamps, dropdown options, or a label that doesn't fit on the same line.
@input length = input.int(20, "Length", minval=2, maxval=500, step=1) @input mode = input.string("standard", "Mode", options="standard,smooth,exp") @input src = input.source("close", "Source")
Rules:
- The identifier name (
length,mode,srcabove) becomes a variable in the script's scope, bound to the current user value of that input. - Each
@inputdeclaration must sit on its own line. - Convention: cluster all
@inputdeclarations at the top, after@name/@pane. The order in the file is the order in the settings panel. - The optional
"Label"after a comma overrides the builder's label argument when both are present (rare — usually you pick one).
The settings panel auto-builds itself from these declarations. No UI code anywhere.
Input builders
Seven typed builders, each rendering as a specific widget:
| Builder | Widget | Used for |
|---|---|---|
input.int | Integer stepper | Lookback periods, lengths |
input.float | Decimal stepper | Multipliers, thresholds |
input.bool | Toggle | Feature flags |
input.string | Text field or dropdown | Mode names, free text |
input.source | Source dropdown | open / high / low / close / hl2 / hlc3 / ohlc4 |
input.color | Color picker | Hex / rgba colors |
input.price | Number stepper (price-formatted) | Absolute price thresholds |
input.int
input.int(default, label, minval=…, maxval=…, step=1)
default— initial integer.label(positional orlabel="…") — displayed in the settings panel.minval,maxval— clamp the UI; the user cannot set values outside this range.step— increment when clicking up/down on the stepper. Default1.
Returns: the user's current int value (a scalar number).
@input length = input.int(50, "Length", minval=2, maxval=500, step=1)
input.float
input.float(default, label, minval=…, maxval=…, step=…)
Same as input.int but accepts decimals. Default step is 0.1.
@input mult = input.float(2.0, "Std Dev", minval=0.1, maxval=10.0, step=0.1)
Returns: the user's current float value (a scalar number).
input.bool
input.bool(default, label)
Renders a toggle. No clamp options.
@input showSignals = input.bool(true, "Show signal markers")
Returns: true / false.
input.string
input.string(default, label, options="a,b,c")
- Without
options=— renders as a free-text field. The user can type anything. - With
options=— renders as a dropdown of comma-separated values. The default MUST be one of the options.
@input mode = input.string("standard", "Mode",
options="standard,smoothed,exponential")
@input note = input.string("", "Note") // free-text
Returns: the current string.
input.source
input.source(default, label)
Dropdown of the eight pre-bound series: open / high / low / close / volume / hl2 / hlc3 / ohlc4. The runtime resolves the user's pick and binds the actual series to the variable — your script can call sma(src, 14) directly without dereferencing.
@input src = input.source("close", "Source")
ma = sma(src, 20)
plotLine(ma, color="#F0B90B")
Returns: a series.
input.color
input.color(default, label)
Renders a color picker. Default must be a #RRGGBB, #RRGGBBAA, or rgba(...) string.
@input upColor = input.color("#0ECB81", "Bull color")
@input dnColor = input.color("#F6465D", "Bear color")
Returns: a color string suitable for any color= / bg= / border= argument.
input.price
input.price(default, label, step=0.01)
Same shape as input.float but with finer-grained step defaults appropriate for price levels. Use for absolute price thresholds (a hard support level the user wants to monitor, a take-profit target, …).
@input target = input.price(50000, "Target price", step=0.01)
isAboveTarget = close > target
plotBgColor(isAboveTarget, color="rgba(14, 203, 129, 0.04)")
Returns: a scalar number.
Common patterns
Group related inputs visually
The settings panel renders inputs in declaration order. Group them by topic by ordering them deliberately:
@input length = input.int(50, "Length", minval=2, maxval=500)
@input src = input.source("close", "Source")
@input upColor = input.color("#0ECB81", "Bull color")
@input dnColor = input.color("#F6465D", "Bear color")
@input showAlert = input.bool(true, "Enable alert")
The visual gap is implicit — there is no group= argument.
Validate a typed input
Builder clamps catch most bad inputs. For non-trivial validation (cross-input invariants — e.g. fast < slow), do a script-eval-time if and pick a safe fallback:
@input fast = input.int(12, "Fast EMA", minval=2, maxval=200)
@input slow = input.int(26, "Slow EMA", minval=2, maxval=400)
if fast >= slow
fast := 12
slow := 26
end
The user sees the clamp take effect on the next redraw — the indicator just keeps working with sane defaults instead of flatlining.
Branch on a string mode
Combine input.string(options=...) with if blocks at the top of the script:
@input mode = input.string("rsi", "Mode", options="rsi,stoch,cci")
if mode == "rsi"
v := rsi(close, 14)
elseif mode == "stoch"
v := stoch_k(close, high, low, 14)
else
v := cci(high, low, close, 14)
end
paneLine(v, color="#F0B90B", width=1.5)
Source picker with custom default
input.source accepts the eight pre-bound names as defaults; pass any of them:
@input src = input.source("hlc3", "Source")
Default "hlc3" is (high + low + close) / 3 — a smoother input than close for moving averages.
Order of operations at script-load time
When the engine loads your script:
- The analyzer scans all
@name/@pane/@inputdeclarations. - The settings dialog is built from the ordered list of
@inputentries. - The user's saved values for each
@inputare read; defaults fill in for inputs the user hasn't touched. - The script body runs with each
@inputname bound to its current value.
This means an @input declaration's default is only used the first time the user enables your script. Subsequent loads use whatever the user set in the settings panel.
Next
- Built-in variables — what
close,time,bar_indexactually contain. - Panes — the pane drawing primitives.
- Recipes — full scripts with realistic input shapes.