Data and update events
Subscribe to market data, react to open, update, close and trade events, and read earlier values.
A subscription names a market-data series the script reads. For a chart indicator, start with the chart’s candles:
script "Chart data"
data chart = subscribe(data.ohlcv)chart is a name you choose, not a built-in global. It exposes candle fields such
as open, high, low, close and volume. These can be missing, so their types
are nullable (float?).
Sources
| Source | Record | What it holds |
|---|---|---|
data.ohlcv | OhlcvSub | Candles: open high low close volume buyVolume sellVolume buyCount sellCount trades. |
data.vd | VdSub | Volume delta per bar as open high low close. |
data.cvd | CvdSub | Cumulative volume delta as open high low close. |
data.oi | OiSub | Open interest as open high low close. |
data.stat | StatSub | Market statistics: sellLiq buyLiq markPrice fundingRate. |
data.trades | TradesSub | Individual trades, delivered to an on t.trade(tr) handler. |
data.book | BookSub | Order-book snapshots, read from another subscription’s handler. |
data.volume | VolumeSub | Volume-profile snapshots, read from another subscription’s handler. |
What a chart provides
On a chart, subscriptions serve the chart’s own market:
- Candles, delta, CVD and open interest at the chart’s interval or a whole
multiple of it (a 15m chart serves
timeframe: 1h, not5m). - Statistics from the market’s liquidations and funding:
sellLiqandbuyLiqare the value of liquidation orders in the bar (a sell liquidates a long),fundingRatethe last settled rate (the current rate on the newest bar).markPriceis not available. Spot markets have no statistics. - Trades from the trades the chart holds in memory.
- Order books from the book the chart has recorded (what its liquidity heatmap
shows): a snapshot is the book resting at the end of a period. Before the
recording starts, reads return
null. - Volume profiles from the footprints of the chart’s bars: a snapshot sums the
volume traded in a period by footprint row. While a script reads a profile, the
chart loads footprints for the bars on screen; bars without one read
null.
Volume delta needs a volume split: the data hub’s candles carry one, being built from recorded trades, and so does the live tape.
Other exchanges and symbols
Candles, delta and CVD can come from other markets than the chart’s. exchange:
takes one exchange id or several joined with markets.agg(...); their bought and
sold volume are added up, and the prices are those of the first exchange that
traded in the bar. Without symbol: each exchange’s market of the chart’s coin is
read (BTCUSDT on Binance, BTC-USDT-SWAP on OKX, BTC on Hyperliquid), so one
script works on any chart:
script "Spot CVD"
setup spot = markets.agg("binance", "bybit", "okx", "coinbase", "kraken")
data (
chart = subscribe(data.ohlcv)
spotCvd = subscribe(data.cvd, exchange: spot)
)
pane cvdPane = pane(title: "Spot CVD", height: 0.25)
plot line = plot.line(title: "Spot CVD", on: cvdPane)
on chart.update {
line.plot(spotCvd.close)
}The bars are the chart’s bars: a bar no exchange of the subscription traded in
reads null. The other markets load after the script starts, so the first values
appear a moment later. Open interest, statistics, trades, order books and volume
profiles of other markets are not available: they read null.
markets.exchangeNames("spot") and markets.exchangeNames("futures") list the
exchange ids.
Respond to updates
Put calculations in a handler of the subscription that drives them:
script "Candle range"
data chart = subscribe(data.ohlcv)
plot range = plot.histogram(title: "Candle range")
on chart.update {
let candleRange = (chart.high ?? 0.0) - (chart.low ?? 0.0)
range.plot(candleRange)
}Several handlers
Each subscription reacts to its own events, so different rates stay in separate, focused blocks. This script saves levels when an hourly candle closes and plots them as the chart advances:
script "Previous hour high and low"
data (
chart = subscribe(data.ohlcv)
h1 = subscribe(data.ohlcv, timeframe: 1h)
)
state (
prevHigh: float? = null
prevLow: float? = null
)
plot (
highPlot = plot.line(title: "Previous hour high", color: color.red, style: linestyle.step)
lowPlot = plot.line(title: "Previous hour low", color: color.green, style: linestyle.step)
)
on h1.close {
prevHigh = h1.high
prevLow = h1.low
}
on chart.update {
highPlot.plot(prevHigh)
lowPlot.plot(prevLow)
}on h1.closeruns when an hourly candle is confirmed and saves values that do not change afterwards.on chart.updateruns as the chart advances and draws the saved levels.
state connects the handlers: what one writes, later calls of the other read.
Period subscriptions support open, update and close handlers. A trades
subscription uses a trade handler for each print. There is at most one handler per
subscription and event.
Statistics: liquidations and funding
script "Liquidations"
data (
chart = subscribe(data.ohlcv)
stats = subscribe(data.stat)
)
pane liqPane = pane(title: "Liquidations (USD)", height: 0.25)
plot (
longs = plot.histogram(title: "Longs liquidated", color: color.red, on: liqPane)
shorts = plot.histogram(title: "Shorts liquidated", color: color.green, on: liqPane)
)
on stats.close {
longs.plot(stats.sellLiq)
shorts.plot(stats.buyLiq == null ? null : 0.0 - (stats.buyLiq ?? 0.0))
}Funding as a line in basis points:
script "Funding (bps)"
data stats = subscribe(data.stat)
pane fundingPane = pane(title: "Funding", height: 0.2)
plot funding = plot.line(title: "Funding (bps)", color: color.amber, on: fundingPane)
on stats.close {
let rate = stats.fundingRate
funding.plot(rate == null ? null : (rate ?? 0.0) * 10000.0)
}Trades
A trades subscription runs its trade handler once for each print, oldest first.
minSize: keeps only prints of at least that size. Each tr is a Trade with
time, price, size and isBuy.
script "Large buys"
input minSize = input.float(5.0, title: "Smallest trade (base units)", min: 0.0)
data (
chart = subscribe(data.ohlcv)
tape = subscribe(data.trades, minSize: minSize)
)
state bought = 0.0
pane buysPane = pane(title: "Large buy volume", height: 0.2)
plot buys = plot.histogram(title: "Large buys", color: color.green, on: buysPane)
on chart.open {
bought = 0.0
}
on tape.trade(tr) {
if tr.isBuy {
bought += tr.size
}
}
on chart.update {
buys.plot(bought)
}On a chart, trades reach back as far as the chart keeps them in memory, so older bars have no trades.
Order books and volume profiles
Book and profile subscriptions have no handlers of their own: read them from another
subscription’s handler. Each read gets the snapshot of the last period completed at
that moment, so in on chart.close it describes the bar that just closed; barsAgo:
reaches further back.
script "Book imbalance and bar POC"
data (
chart = subscribe(data.ohlcv)
book = subscribe(data.book)
profile = subscribe(data.volume)
)
pane imbalancePane = pane(title: "Book imbalance within 1%", height: 0.2)
plot (
imbalance = plot.histogram(title: "Imbalance", on: imbalancePane)
poc = plot.marker(title: "Bar POC", shape: shape.diamond, color: color.amber, size: 4.0)
)
on chart.close {
// (bids - asks) / (bids + asks) by value, from -1 to 1.
let ratio = book.imbalance(1.0)
imbalance.plot(ratio, color: (ratio ?? 0.0) >= 0.0 ? color.green : color.red)
let level = profile.poc()
if level != null {
poc.plot(level.price)
}
}profile.profile(n) merges the last n periods into one profile with the same
queries (poc, valueArea, buckets, summary).
Read earlier values
Brackets read history on period fields:
script "Previous close"
data chart = subscribe(data.ohlcv)
plot previous = plot.line(title: "Previous close")
on chart.update {
previous.plot(chart.close[1])
}Earlier values can be null, especially near the start of the chart. Keep that
absence where it matters instead of inventing a value.
For live candles and close-confirmed signals, continue with repainting and confirmed values.