Docs·Delta DSL Script·Library

Built-in variables

OHLCV series, derived sources, time, bar_index, bars, bar-state accessors, plus the constants pi, e, na. Everything available without an import.

Every Delta DSL script starts with the chart's data already in scope. You don't import OHLCV, you don't subscribe to a feed, you don't poll — the runtime binds these names before the first line of your script runs and keeps them in sync with whatever symbol / timeframe the chart has loaded.

OHLCV series

NameTypeDescription
openseries<number>Open price of each bar
highseries<number>High price of each bar
lowseries<number>Low price of each bar
closeseries<number>Close price of each bar
volumeseries<number>Volume of each bar (base-currency units, exchange-reported)

All five are arrays of length bars. close[0] is the most recent bar's close; close[1] is one bar back; close[bars-1] does not exist as such — use close[bars-1] only via at(close, 0) for the oldest available bar (see at(...) below).

Derived sources

Three convenience derivations the runtime computes for you:

NameFormulaWhen to use
hl2(high + low) / 2Mid-bar reference, quieter than close
hlc3(high + low + close) / 3Typical price — used by CCI, MFI
ohlc4(open + high + low + close) / 4Smoother than close for very noisy series

These are real series, not lazy expressions — using them is exactly as fast as using close.

Derivatives feed

Bar-aligned series for futures-only market microstructure. Each series has length bars and aligns 1-to-1 with open / high / low / close. Use these to write Pine-style "request.security" indicators (CVD divergence, OI build, funding-rate stress, ΔOI vs price) without leaving the sandbox.

NameTypeDescription
taker_buy_volumeseries<number>Per-bar aggressive buy volume (Binance kline k[9]). Source: futures.
taker_sell_volumeseries<number>volume - taker_buy_volume. Per-bar aggressive sell volume.
delta_volumeseries<number>taker_buy_volume - taker_sell_volume. Per-bar net order-flow delta.
cvdseries<number>Cumulative Volume Delta = running sum of delta_volume from chart start. Same series the built-in CVD pane renders when source = futures.
open_interestseries<number>Bar-aligned Open Interest (sumOpenInterest from Binance, forward-filled between OI samples).
oi_deltaseries<number>open_interest - open_interest[1]. Per-bar ΔOI. The first bar is NaN.
funding_rateseries<number>Bar-aligned Binance funding rate in percent (0.01 == 0.01 %). Forward-filled between 8-hour settlement events.

Source-data requirements

These series are populated by the same fetchers that drive the built-in CVD / OI / Funding-Rate indicators. The fetcher only runs when at least one consumer of that data is active:

SeriesRequires (enable any one)
taker_buy_volume, taker_sell_volume, delta_volume, cvdCVD indicator
open_interest, oi_deltaOpen Interest indicator, or OI × CVD Pattern, or Liquidation Heatmap
funding_rateFunding Rate indicator

Without the source indicator enabled, the series is populated with NaN (length bars, every element NaN). Your script keeps running; the math just propagates NaN until the user enables the data source. Use na(...) (or isna(...)) to detect the empty state and either skip your compute or draw a hint label.

Note on na: the bare identifier is polymorphic. In a value position it resolves to the NaN constant (iff(cond, value, na) masks a bar to a gap). In a call position na(x) is the Pine-compat NaN predicate (same semantics as isna(x) — scalar → boolean, series → boolean series). Use whichever spelling matches the script you are porting; the interpreter dispatches based on whether na is followed by (.

Patterns

CVD-vs-price divergence (bullish setup at the live bar):

Delta DSL
@name "CVD Divergence (bull)"
@input lookback = input.int(50, "Lookback", min=10, max=200)

if isna(at(cvd, last_bar_index))
  // CVD indicator not enabled yet — no data to work with.
  plotLabel(last_bar_time, at(close, last_bar_index),
            "Enable CVD indicator to use this script",
            color="#fff", bg="rgba(246,70,93,0.7)", anchor="left")
else
  price_low = lowest(low, lookback)
  cvd_low   = lowest(cvd, lookback)
  bull_div  = low[0] <= price_low[0] and cvd[0] > cvd_low[0]
  plotMarkerUp(bull_div, low, color="#0ECB81", size=10)
end

Open Interest spike + price up = trend build:

Delta DSL
@name "OI Build"
@input oi_z_threshold = input.float(2.0, "OI z-score threshold")

if isna(at(open_interest, last_bar_index))
  plotLabel(last_bar_time, at(close, last_bar_index),
            "Enable Open Interest",
            color="#fff", bg="rgba(246,70,93,0.7)", anchor="left")
else
  oi_z     = zscore(oi_delta, 50)
  price_up = close > shift(close, 1)
  build    = oi_z > oi_z_threshold and price_up
  plotMarkerUp(build, low, color="#F0B90B", size=8)
end

Funding rate stress (long-crowded → short bias):

Delta DSL
@name "Funding Stress"
@input long_threshold = input.float(0.05, "FR threshold (%)", min=0)

stressed = funding_rate > long_threshold
plotBgColor(stressed, color="rgba(246,70,93,0.06)")

Notes

  • These series are computed once per evaluate() pass — using them is exactly as fast as close.
  • Forward-fill is intentional: funding_rate only changes every 8 hours, but every bar carries the latest known value. This matches the built-in pane's behaviour.
  • Spot vs futures: the CVD series here is futures-source. The built-in CVD pane can also show spot-source delta, but that path is not exposed to scripts (would require a per-script source-mode selector — open an issue if you need it).

Time series

NameTypeDescription
timeseries<number>Bar timestamps in epoch seconds (UTC) — same units as the engine's world X axis. Drawing primitives (lineNew, boxNew, labelNew, plotVLine) consume seconds; calendar helpers (year/month/dayofmonth/…) auto-rescale seconds → ms internally, so dayofmonth(time) works without a manual * 1000. See pitfall §12.
bar_indexseries<number>[0, 1, 2, …, bars - 1]

time is what world-coordinate primitives (plotLineXY, plotBox, plotLabel, lineNew, boxNew, …) anchor to. time[0] is the timestamp of the right-most bar; time[5] is five bars to the left.

bar_index is a series — useful for "since-bar" gating:

Delta DSL
plotBgColor(bar_index >= bars - 50, color="rgba(240,185,11,0.04)")

paints the last 50 bars with a dim amber strip.

If the OHLCV doesn't carry timestamps (rare — every live exchange feed includes them), time falls back to bar_index so world-coord primitives still work without crashing.

Bar state

A short list of Pine-flavoured accessors that map cleanly onto Delta DSL's bulk-vector model. Use these to anchor draws at the live bar without writing index boilerplate.

NameTypeValue
last_bar_indexscalarbars - 1 (-1 if no bars are loaded yet)
last_bar_timescalartime[bars - 1] — timestamp (ms) of the rightmost bar
first_bar_timescalartime[0] — timestamp (ms) of the oldest loaded bar
barstate_islastseries<bool>true only at the rightmost bar, false everywhere else

When you'd reach for each

Anchor a one-off label at the live bar:

Delta DSL
@input show_last = true

if show_last
  plotLabel(last_bar_time, at(close, last_bar_index),
            "Last close: " + tostring(at(close, last_bar_index), "0.00"),
            color="#fff", bg="rgba(0,0,0,0.7)", anchor="left")
end

Mark only the live bar with a triangle (the trader-friendly shape for "this is where you act now"):

Delta DSL
buy_setup = crossover(close, sma(close, 20))
plotMarkerUp(buy_setup and barstate_islast, low, color="#0ECB81", size=10)

Draw a vertical line at the first bar of the loaded window (handy when the indicator depends on a clear "since" anchor):

Delta DSL
plotVLine(first_bar_time, color="rgba(255,255,255,0.20)", width=1)

Notes

  • last_bar_index / last_bar_time / first_bar_time are scalars, not series. Use them where the call expects a scalar (plotLabel, lineNew, plotVLine). Inside a for loop with a counter, read the underlying series with at(time, i) instead.
  • barstate_islast is a series — the canonical place to gate per-bar logic with and (e.g. crossover(...) and barstate_islast).

What is NOT exposed (and why)

Pine has a wider barstate.* family — isconfirmed, isrealtime, ishistory, isnew, islastconfirmedhistory, isfirst. Delta DSL deliberately ships a smaller subset because:

  • The isconfirmed / isrealtime / ishistory family requires a "last bar is still forming" signal that Pine's per-bar execution model provides naturally; in our bulk-vector model the script always sees the full closed-history snapshot, so the distinction would need a separate compute-time hint that very few scripts actually use.
  • isnew would be true on every bar in every evaluate() pass (each pass recomputes the whole series) — exposing it would mislead users about what it gates.
  • islastconfirmedhistory is an edge case rarely needed when porting Pine scripts.
  • isfirst is just bar_index == 0 — shorter than the namespaced form, no API surface to maintain.

If you have a concrete use case that one of the omitted identifiers would unlock, open an issue with the script you're trying to write and we'll consider adding it.

Scalar utilities

NameValueType
barsThe number of loaded bars in the visible windowscalar
piMath.PI ≈ 3.141592653589793scalar
eMath.E ≈ 2.718281828459045scalar
naNaNscalar

bars is scalar (not a series) — it's the same number on every "bar". Use it as a guard:

Delta DSL
if bars < 200
  warn := "Need at least 200 bars for this indicator"
end

pi and e are mathematical constants. na is the canonical "no value" placeholder; pass it to iff to mask bars to NaN, which the renderer treats as a gap:

Delta DSL
filtered = iff(close > sma(close, 50), close, na)
plotLine(filtered, color="#F0B90B")  // line breaks where condition is false

Reading the latest bar

The right edge of the visible window is [0]:

Delta DSL
lastClose  = close[0]
lastVolume = volume[0]
prevHigh   = high[1]            // one bar before the right edge
gapPct     = (open[0] - close[1]) / close[1] * 100

series[k] returns a scalar at the right edge for ANY k. Inside a for loop iterating over bar indices, use at(series, i) instead — it returns the scalar at absolute index i, not a relative shift.

Scope and shadowing

Built-in names are not reserved — you CAN reassign them — but doing so usually indicates a bug. The two acceptable cases:

  1. Bind a different source under a generic name:
    Delta DSL
    @input src = input.source("close", "Source")
    
    ma = sma(src, 20)        // src is open/high/low/close/etc., bound by input.source
    
  2. Apply a transform you want named for clarity:
    Delta DSL
    ohlc4_smoothed = ema(ohlc4, 5)
    

If you find yourself writing close = some_other_thing, you almost always want a new variable instead.

Behaviour at the chart edges

  • The right edge is the most recent bar. On a live chart this bar updates as ticks arrive; the script re-runs on every redraw.
  • The left edge is the oldest bar in the loaded window. The user can scroll back further; when they do, the engine fetches more history and re-runs the script with a longer bars.
  • Indicators with a warmup period (e.g. sma(close, 200)) return NaN for the first period - 1 bars. Use nz(value, default) or fixnan(value) to fill those if your downstream math can't tolerate NaN.
  • All series are aligned to the same bars length. You can never mix series from different timeframes — the runtime works with one timeframe at a time.

What's NOT pre-bound

For comparison, things you might expect from other charting DSLs that do not exist in Delta DSL:

  • vwap — not pre-bound. Compute via sum(close * volume, period) / sum(volume, period).
  • syminfo.* (TradingView) — symbol metadata is not exposed to scripts.
  • timeframe.* — the script doesn't know which timeframe it's running on.
  • position.* — broker / position data is not visible to scripts.
  • request.security(...) — multi-symbol / multi-TF fetch is not exposed. The derivatives series above (cvd / open_interest / funding_rate) are the only "non-OHLCV" data piped into the sandbox; they ride the same fetcher the built-in panes use.

These limits are deliberate: the script must run identically against any chart instance, so anything the engine can't guarantee is not exposed.

Next