Docs·Delta DSL Script·Library

Live market data (orderflow)

Read-only orderbook, trade-tape and ticker primitives streamed DIRECT from the exchange in your browser — best bid/ask, depth, imbalance, walls, live CVD, buy/sell pressure. No server, no socket in script-space, follows the chart symbol by default.

The live market-data primitives let a script READ the current orderbook, the trade tape, and the 24h ticker for the charted instrument — the same raw feeds the native Orderbook / DOM / Walls widgets use. They power orderflow indicators: depth ladders, imbalance gauges, liquidity walls, live CVD, absorption / aggression read-outs.

These are the only DSL functions that touch the network — and they do it WITHOUT breaking the sandbox (see How it stays safe). Your script never opens a socket; it just reads scalars the host already streamed.

At a glance

GroupFunctions
Top of bookbestBid bestAsk midPrice spread
Depth by levelbidPrice askPrice bidSize askSize
Aggregated depthsumBidQty sumAskQty bookImbalance wallBid wallAsk
Trade tapebuyVolume sellVolume cvdLive tradeCount lastTradePrice lastTradeQty lastTradeSide
TickertickerLast tickerBid tickerAsk tickerVol

Calling convention

Every function takes all-optional arguments and returns a single scalar number (or NaN):

Delta DSL
fn([level_or_n], [exchange], [symbol])
  • Numeric arg — a book level (0 = best, deeper = further from mid) or a depth n, depending on the function.
  • String arg(s) — the exchange, and optionally a symbol. A trailing string that isn't a known exchange is treated as the symbol.
  • Omit the symbol → follows the CHART symbol. Pass a symbol to read a different instrument; pass an exchange to read a different venue (e.g. compare two exchanges in one script).

Supported exchanges:

StringVenue
"binancef"Binance USD-M futures (default in the samples)
"binance"Binance spot
"bybit" / "bybitf"Bybit spot / linear futures
"coinbase"Coinbase Advanced Trade
"hyperliquid"Hyperliquid perps
Delta DSL
bb  = bestBid()                 // chart symbol, default exchange
bb2 = bestBid("bybit")          // chart symbol on Bybit
bb3 = bestBid("binancef", "ETHUSDT")   // a different symbol
sz  = bidSize(3, "coinbase")    // size at the 4th bid level on Coinbase

Live-only & cold reads

These read a live stream, not historical bars. Two rules:

  1. NaN until warm. A feed takes a moment to connect and fill. Every function returns NaN while cold, off a live chart, or on an unknown exchange. Always guard with isna(...) and draw a "waiting…" hint on the cold path.
  2. Trade & ticker totals are cumulative SINCE subscribe — i.e. since the indicator was added or the socket last reconnected, NOT since session open. buyVolume / sellVolume / cvdLive / tradeCount reset to 0 on reconnect. They are a running tally, not a per-bar value.
Delta DSL
bb = bestBid()
if isna(bb)
  plotLabel(last_bar_time, at(close, last_bar_index), "Waiting for orderbook…",
            color="#FFFFFF", bg="rgba(246,70,93,0.72)", anchor="left", size=12)
else
  // … safe to read the book here …
end

Top of book

FunctionReturns
bestBid(exchange?, symbol?)highest bid price
bestAsk(exchange?, symbol?)lowest ask price
midPrice(exchange?, symbol?)(bestBid + bestAsk) / 2
spread(exchange?, symbol?)bestAsk − bestBid (absolute price)

Depth by level

FunctionReturns
bidPrice(level=0, exchange?, symbol?)price at bid level
askPrice(level=0, exchange?, symbol?)price at ask level
bidSize(level=0, exchange?, symbol?)resting size at bid level
askSize(level=0, exchange?, symbol?)resting size at ask level

level is clamped to the available depth (up to 50 levels per side where the venue provides them). Level 0 is the inside market; higher levels step away from mid.

Delta DSL
// Walk the top 10 bid levels and sum the size below mid.
total = arrFrom(0)            // box: a plain acc inside a for is a LOCAL
for i = 0 to 9
  s = bidSize(i)
  if not isna(s)
    arrSet(total, 0, arrGet(total, 0) + s)
  end
end

Reassigning a plain scalar inside a for declares a LOCAL binding that never escapes the loop. Use the arrFrom/arrSet box pattern (or the aggregated-depth helpers below) instead. See Pitfalls.

Aggregated depth

FunctionReturns
sumBidQty(n=10, exchange?, symbol?)total bid size over the top n levels
sumAskQty(n=10, exchange?, symbol?)total ask size over the top n levels
bookImbalance(n=10, exchange?, symbol?)(sumBid − sumAsk) / (sumBid + sumAsk) over top n — ranges −1 (all ask) to +1 (all bid)
wallBid(n=20, exchange?, symbol?)largest single resting bid SIZE within the top n
wallAsk(n=20, exchange?, symbol?)largest single resting ask SIZE within the top n

wallBid / wallAsk return the wall size, not its price. To get the price of the biggest wall, scan bidSize / bidPrice yourself (see the Liquidity Walls recipe).

Trade tape

FunctionReturns
buyVolume(exchange?, symbol?)cumulative aggressive BUY volume since subscribe
sellVolume(exchange?, symbol?)cumulative aggressive SELL volume since subscribe
cvdLive(exchange?, symbol?)live CVD = buyVolume − sellVolume
tradeCount(exchange?, symbol?)number of prints since subscribe
lastTradePrice(exchange?, symbol?)price of the most recent print
lastTradeQty(exchange?, symbol?)size of the most recent print
lastTradeSide(exchange?, symbol?)aggressor side: +1 buy, −1 sell

Ticker

FunctionReturns
tickerLast(exchange?, symbol?)last price (24h ticker stream)
tickerBid(exchange?, symbol?)best bid (ticker)
tickerAsk(exchange?, symbol?)best ask (ticker)
tickerVol(exchange?, symbol?)24h rolling volume

Examples

These three ship as built-in samples (Script editor → Samples). They all park a big panel to the RIGHT of the last candle so they never crowd live price.

Example — Book Pressure (imbalance gauge)

Delta DSL
@version 1
@name "Book Pressure"

@input depth     = input.int(20, "Depth (levels)", minval=1, maxval=50)
@input rightOff  = input.int(36, "Right offset (bars)", minval=0, maxval=200)
@input widthBars = input.int(44, "Bar width (bars)", minval=4, maxval=400)
@input heightPct = input.float(3.5, "Bar height (% price)", minval=0.1, maxval=50, step=0.1)

tNow = last_bar_time
sb   = sumBidQty(depth)
sa   = sumAskQty(depth)

if isna(sb) or isna(sa) or (sb + sa) <= 0
  plotLabel(tNow, at(close, last_bar_index), "Waiting for orderbook…",
            color="#FFFFFF", bg="rgba(246,70,93,0.72)", anchor="left", size=12)
else
  barMs = tNow - at(time, last_bar_index - 1)
  if barMs <= 0
    barMs = 60000
  end
  tA = tNow + barMs * rightOff
  tB = tA + barMs * widthBars

  c    = midPrice()
  half = c * heightPct * 0.005
  tot  = sb + sa
  tSplit = tA + (tB - tA) * (sb / tot)

  plotBox(tA, c - half, tSplit, c + half, bg=withAlpha("#0ECB81", 0.80), border=withAlpha("#0ECB81", 0.95))
  plotBox(tSplit, c - half, tB, c + half, bg=withAlpha("#F6465D", 0.80), border=withAlpha("#F6465D", 0.95))

  imb = (sb - sa) / tot
  col = iff(imb >= 0, "#0ECB81", "#F6465D")
  plotLabel((tA + tB) / 2, c + half, "IMBALANCE " + tostring(imb * 100, "0.0") + "%",
            color="#FFFFFF", bg=withAlpha(col, 0.90), anchor="above", size=13)
end

Example — Live CVD & Tape

Delta DSL
@version 1
@name "Live CVD"

tNow = last_bar_time
bv   = buyVolume()
sv   = sellVolume()

if isna(bv) and isna(sv)
  plotLabel(tNow, at(close, last_bar_index), "Waiting for tape…",
            color="#FFFFFF", bg="rgba(246,70,93,0.72)", anchor="left", size=12)
else
  cvd = cvdLive()
  yp  = at(close, last_bar_index)
  col = iff(cvd >= 0, "#0ECB81", "#F6465D")
  plotLabel(tNow, yp,
            "CVD " + tostring(cvd, "#.##") + "    B " + tostring(bv, "#.##") + "    S " + tostring(sv, "#.##"),
            color="#FFFFFF", bg=withAlpha(col, 0.85), anchor="left", size=12)
end

Example — Liquidity Walls

Delta DSL
@version 1
@name "Liquidity Walls"

@input depth   = input.int(20, "Scan depth", minval=2, maxval=50)
@input minMult = input.float(2.5, "Wall vs avg (x)", minval=1, maxval=20, step=0.5)

tNow = last_bar_time
bb   = bestBid()

if not isna(bb)
  barMs = tNow - at(time, last_bar_index - 1)
  if barMs <= 0
    barMs = 60000
  end
  bAvg = sumBidQty(depth) / depth

  // Biggest bid wall + its PRICE (box writeback — no scalar-in-loop).
  bBox = arrFrom(0, 0)        // [size, price]
  for i = 0 to depth - 1
    s = bidSize(i)
    p = bidPrice(i)
    if not isna(s) and not isna(p) and s > arrGet(bBox, 0)
      arrSet(bBox, 0, s)
      arrSet(bBox, 1, p)
    end
  end

  bMax = arrGet(bBox, 0)
  if bMax > 0 and bMax >= bAvg * minMult
    bpx = arrGet(bBox, 1)
    plotHline(bpx, color="#0ECB81", width=2)
    plotLabel(tNow, bpx, tostring(bMax, "#.##"), color="#0ECB81", anchor="left", size=12)
  end
end

How it stays safe

The DSL sandbox forbids fetch / WebSocket / any I/O from script-space — that invariant is unchanged. These primitives don't break it:

  • The host (not your script) owns the WebSocket. Your script calls bidPrice() etc.; the host lazily opens the public exchange socket on first request and feeds the read-only snapshot back. The script only ever READS a scalar.
  • No credentials. The exchanges' public market streams need no auth. Nothing about your account is exposed.
  • No alerts / no BE. Live market reads run in the browser only. A script that uses them can't drive server-side alerts off the live feed (there's nothing for the BE to replay) — it's a live, on-chart overlay. Save still works; the live reads just return NaN in the BE preview.

Performance & limits

  • Main-thread only. A script that mentions any market function is pinned to the main thread (the worker realm has no feed). This is automatic — no directive needed.
  • Connection budget. The host caps the number of live streams and de-duplicates them: two indicators reading the same symbol/venue share one socket. Idle streams are dropped; hidden tabs park the feed.
  • Keep compute light. These run every tick. Reading dozens of levels + drawing per-level labels can blow the 16 ms per-eval budget (the engine then auto-throttles to bar-close). Prefer the aggregated helpers (sumBidQty, bookImbalance) over hand loops, and turn value labels off by default.
  • Lifecycle. A feed lives only while its indicator is on the chart — alive on add, gone on remove. Cumulative tape totals reset on reconnect.

Next

  • Drawing — the world-coord primitives (plotBox, plotLabel, plotHline) these overlays draw with.
  • Pitfalls — why a scalar accumulator inside a for needs the arrFrom/arrSet box.
  • Recipes — full scripts combining the stdlib.