What this is
A kScript script reads market data through sources. A source is one feed (price candles, funding, order book, options volume, ETF flows) for one instrument on one venue. You subscribe to it with source(...) or, for plain price candles, the ohlcv(...) shorthand. Each source returns a timeseries whose members are the columns of that feed: .close, .buy, .value, and so on.
This page is the reference for every source type the platform serves, the members each one exposes, and the two rules that trip people up: how you pass the symbol and exchange, and how many sources one script may open.
Loading a source
Two calls do the job. Use ohlcv(...) for price candles, and source("<type>", ...) for everything else:
//@version=2
define(title="Close and funding", position="offchart", axis=true)
timeseries trade = ohlcv(symbol=currentSymbol, exchange=currentExchange)
timeseries funding = source("funding_rate", symbol=currentSymbol, exchange=currentExchange)
plotLine(value=trade.close, colors=["#2563eb"], label=["Close"], desc=["chart close price"])
plotLine(value=funding.value, colors=["#7c3aed"], label=["Funding"], desc=["funding rate"])The first positional argument to source() is the source type (a string from the table below). The rest are keyword arguments that identify the instrument. Which keywords a source needs depends on the type: most price-like feeds take symbol and exchange, the Deribit and options feeds take coin, and ETF feeds take symbol only. The table lists the required keys per source.
Symbol and exchange must be env identifiers, not script variables
This is the most common mistake. The arguments to source() are resolved at subscription time, before the bar loop runs, so they cannot reference a variable you computed in the script. Pass one of:
- a literal:
source("ohlcv", symbol="BTCUSDT", exchange="BINANCE") - an env identifier:
currentSymbol,currentCoin,currentExchange(the chart's current context) - an input: a value from
input(...) - an inline env expression built from the above
// Works: env identifiers
timeseries a = source("ohlcv", symbol=currentSymbol, exchange=currentExchange)
// Fails: a script variable is not allowed here
// var wantedSymbol = "ETHUSDT"
// timeseries b = source("ohlcv", symbol=wantedSymbol, exchange=currentExchange)Reaching for a script variable raises a build error that names the offending identifier and tells you to use a literal, an env identifier, an input, or an inline env expression instead. If it slips past the build, the run reports the identifier as undefined.
Source reference
The platform serves 20 source types. Each row lists the keyword arguments source() requires and the members you can read off the returned timeseries.
| Source | Required source() args | Members |
|---|---|---|
ohlcv | symbol, exchange | time, open, high, low, close, volume |
open_interest | symbol, exchange | time, open, high, low, close |
buy_sell_volume | symbol, exchange, currency? | time, buy, sell |
trade_volume_by_size | symbol, exchange | time, cells (array-celled) |
funding_rate | symbol, exchange | time, value |
liquidations | symbol, exchange | time, buy, sell |
orderbook | symbol, exchange | time, bids, asks (array-celled) |
cme_oi | coin | time, open, high, low, close |
deribit_implied_volatility | coin | time, one_week, one_month, three_months |
deribit_volatility_index | coin | time, open, high, low, close |
skew | coin, delta | time, one_week, onemonth, three_months |
etf_premium_rate | symbol | time, value |
etf_holding | symbol | time, value |
options_volume | coin, exchange | time, puts, calls |
options_open_interest | coin, exchange | time, puts, calls |
etf_flow | symbol | time, value |
ethena_positions | coin | time, value |
long_short_ratio | symbol, exchange | total_account, top_trader_account, top_trader_position |
binance_treasury_balance | asset | time, value |
buy_sell_volume in quote notional
buy_sell_volume(symbol, exchange, currency="USD") returns buy/sell volume in USD notional instead of base-asset quantity. Omit currency (or pass anything else) for the base-asset default. USD-normalized series refresh their live edge on the periodic tail poll rather than the per-tick merge, so the newest bar updates on a short delay.
//@version=2
define(title="USD Notional CVD", position="offchart", axis=true);
timeseries ohlcvData = ohlcv(symbol=currentSymbol, exchange=currentExchange);
timeseries bsv = buy_sell_volume(symbol=currentSymbol, exchange=currentExchange, currency="USD");
static cvd = 0
cvd = cvd + bsv.buy[0] - bsv.sell[0]
plotLine(value=cvd, colors=["#42a5f5"], width=2, label=["CVD (USD)"], desc=["Cumulative volume delta in quote notional"]);Tape by order size (array-celled)
trade_volume_by_size exposes the tape below the bar, pre-bucketed by USD order size. It is array-celled: each bar's row carries a list of cells you read with .cells, and it charges weight 2 against the per-script source budget.
trade_volume_by_size(symbol, exchange) delivers one cell per populated bucket:
[bucketId, buyVolUsd, sellVolUsd, buyCount, sellCount]
Bucket ids are fixed USD notional bands: 1 = $0-1K, 2 = $1K-10K, 3 = $10K-100K, 4 = $100K-500K, 5 = $500K-1M, 6 = $1M-10M, 7 = $10M+. This is how you get real whale flow (filter c[0] >= 5) and per-bar trade counts without touching the raw tape.
Working with cells:
//@version=2
define(title="HTF Whale Flow", position="offchart", axis=true);
timeseries ohlcvData = ohlcv(symbol=currentSymbol, exchange=currentExchange);
timeseries tv = trade_volume_by_size(symbol=currentSymbol, exchange=currentExchange);
timeseries h4 = htf(tv, "4h");
var whaleNet = h4.cells.filter((c) => c[0] >= 5).map((c) => c[1] - c[2]).reduce((a, b) => a + b, 0);
plotLine(value=whaleNet, colors=["#8d6e63"], width=1, label=["4h Whale Net"], desc=["Net 500K+ USD flow in the last completed 4h bucket"]);.cells returns the current bar's cell list ([] on a quiet bar, never an error), and the usual .map/.filter/.reduce lambdas do the math. htf() concatenates cells across the higher-timeframe bucket, which makes it the multi-timeframe path for tape data; requestBars() and ltf() reject array-celled sources.
Data depth differs by layer, by design. trade_volume_by_size reaches back about a week at 1m resolution and months at 1h/1d. buy_sell_volume carries years of bar-exact history. So cumulative delta over long ranges belongs to buy_sell_volume, and whale flow over recent months belongs to the bucket source. See the Whale vs Retail CVD cookbook for a full worked example.
Two members are easy to get wrong:
long_short_ratiohas notimemember. It is the one source here that does not carry a timestamp column; read only the three account/position members.skewspells itonemonth, notone_month. Its other two tenors use underscores (one_week,three_months), so the inconsistency catches people. The Deribit implied-volatility source usesone_monthwith the underscore.
Reading a member that doesn't exist on a source won't crash the compiler, but it produces nothing to plot: the series is all na and the line never draws. If a plot is blank, check the member name against this table first.
Direct OHLCV
ohlcv(...) is the dedicated shorthand for the price-candle source. It returns the same shape as source("ohlcv", ...): a timeseries with open, high, low, close, volume. Use it for the chart's own price, or name another symbol to pull a second instrument's candles.
//@version=2
define(title="Direct OHLCV", position="offchart", axis=true)
timeseries trade = ohlcv(symbol=currentSymbol, exchange=currentExchange)
plotLine(value=trade.close, colors=["#2563eb"], label=["Close"], desc=["direct ohlcv close"])Reading members: worked examples
The four examples below open the full source catalog grouped by theme. Each reads real members off the returned timeseries and plots them, so you can see exactly which column lives where.
Market flows
Price, open interest, buy/sell volume, funding, and liquidations. Notice the idiomatic combinations: buy - sell gives net taker flow, and the same shape gives net liquidations.
//@version=2
define(title="Market Flow Sources", position="offchart", axis=true)
timeseries trade = source("ohlcv", symbol=currentSymbol, exchange=currentExchange)
var tradeMembers = trade.open + trade.high + trade.low + trade.close + trade.volume
plotLine(value=tradeMembers, colors=["#2563eb"], label=["ohlcv"], desc=["ohlcv open high low close volume"])
timeseries openInterest = source("open_interest", symbol=currentSymbol, exchange=currentExchange)
var openInterestMembers = openInterest.open + openInterest.high + openInterest.low + openInterest.close
plotLine(value=openInterestMembers, colors=["#059669"], label=["open_interest"], desc=["open_interest open high low close"])
timeseries buySellVolume = source("buy_sell_volume", symbol=currentSymbol, exchange=currentExchange)
var buySellMembers = buySellVolume.buy - buySellVolume.sell
plotLine(value=buySellMembers, colors=["#dc2626"], label=["buy_sell_volume"], desc=["buy_sell_volume buy sell"])
timeseries funding = source("funding_rate", symbol=currentSymbol, exchange=currentExchange)
plotLine(value=funding.value, colors=["#7c3aed"], label=["funding_rate"], desc=["funding_rate value"])
timeseries liq = source("liquidations", symbol=currentSymbol, exchange=currentExchange)
var liquidationMembers = liq.buy - liq.sell
plotLine(value=liquidationMembers, colors=["#ea580c"], label=["liquidations"], desc=["liquidations buy sell"])Derivatives and volatility
The order book, CME open interest, and the Deribit volatility feeds. orderbook is special: its bids and asks members are array-celled, meaning each bar holds a list of price levels rather than a single number, so you render them through plotTable instead of plotLine. The volatility sources are ordinary OHLC- or tenor-shaped series.
//@version=2
define(title="Derivatives Volatility Sources", position="offchart", axis=true)
timeseries book = source("orderbook", symbol=currentSymbol, exchange=currentExchange)
var bidCells = book.bids
var askCells = book.asks
plotTable(data=[["side", "cells"], ["bids", bidCells], ["asks", askCells]], position="top_right")
timeseries cme = source("cme_oi", coin=currentCoin)
var cmeMembers = cme.open + cme.high + cme.low + cme.close
plotLine(value=cmeMembers, colors=["#4f46e5"], label=["cme_oi"], desc=["cme_oi open high low close"])
timeseries impliedVol = source("deribit_implied_volatility", coin=currentCoin)
var impliedVolMembers = impliedVol.one_week + impliedVol.one_month + impliedVol.three_months
plotLine(value=impliedVolMembers, colors=["#0f766e"], label=["deribit_implied_volatility"], desc=["deribit implied volatility tenors"])
timeseries dvol = source("deribit_volatility_index", coin=currentCoin)
var dvolMembers = dvol.open + dvol.high + dvol.low + dvol.close
plotLine(value=dvolMembers, colors=["#9333ea"], label=["deribit_volatility_index"], desc=["deribit volatility index open high low close"])
timeseries skewData = source("skew", coin=currentCoin, delta=25)
var skewMembers = skewData.one_week + skewData.onemonth + skewData.three_months
plotLine(value=skewMembers, colors=["#be123c"], label=["skew"], desc=["skew one_week onemonth three_months"])ETF and options
ETF premium, holdings, and flow are single-value feeds keyed by symbol. The options feeds split into puts and calls, keyed by coin and exchange. Note etf_holding also accepts an optional currency.
//@version=2
define(title="ETF Options Sources", position="offchart", axis=true)
timeseries premium = source("etf_premium_rate", symbol="BTC")
plotLine(value=premium.value, colors=["#65a30d"], label=["etf_premium_rate"], desc=["etf_premium_rate value"])
timeseries holding = source("etf_holding", symbol="BTC", currency="USD")
plotLine(value=holding.value, colors=["#0d9488"], label=["etf_holding"], desc=["etf_holding value"])
timeseries optionVolume = source("options_volume", coin=currentCoin, exchange=currentExchange)
var optionVolumeMembers = optionVolume.puts + optionVolume.calls
plotLine(value=optionVolumeMembers, colors=["#a16207"], label=["options_volume"], desc=["options_volume puts calls"])
timeseries optionInterest = source("options_open_interest", coin=currentCoin, exchange=currentExchange)
var optionInterestMembers = optionInterest.puts + optionInterest.calls
plotLine(value=optionInterestMembers, colors=["#c2410c"], label=["options_open_interest"], desc=["options_open_interest puts calls"])
timeseries etfFlow = source("etf_flow", symbol="BTC")
plotLine(value=etfFlow.value, colors=["#1d4ed8"], label=["etf_flow"], desc=["etf_flow value"])Protocol and positioning
Ethena positions, the long/short ratio, and Binance treasury balance. long_short_ratio is the source with no time member, so read only its three account/position columns.
//@version=2
define(title="Protocol Position Sources", position="offchart", axis=true)
timeseries ethena = source("ethena_positions", coin=currentCoin)
plotLine(value=ethena.value, colors=["#047857"], label=["ethena_positions"], desc=["ethena_positions value"])
timeseries longShort = source("long_short_ratio", symbol=currentSymbol, exchange=currentExchange)
var longShortMembers = longShort.total_account + longShort.top_trader_account + longShort.top_trader_position
plotLine(value=longShortMembers, colors=["#9d174d"], label=["long_short_ratio"], desc=["long_short_ratio account and position members"])
timeseries treasury = source("binance_treasury_balance", asset="BTC")
plotLine(value=treasury.value, colors=["#374151"], label=["binance_treasury_balance"], desc=["binance_treasury_balance value"])Array-celled sources
orderbook above is one example of an array-celled source: each bar carries a list of cells, not a scalar. The other is volume_profile, whose every bar is a row of price-level buckets shaped [priceLow, priceHigh, buyVol, sellVol], with the bucket count changing bar to bar. Because there is no fixed column, volume_profile has no scalar members like .buy. You pull values out of it with the dedicated volume-profile accessors (vpBuy, vpDelta, vpPoc, and friends), and you can walk the raw buckets yourself with array indexing.
How many sources you can open
Each distinct source is a live data subscription, so kScript budgets them. The budget is 10 weighted source slots per script (MAX_SOURCES_PER_SCRIPT): a native stream like ohlcv costs one slot, heavier source types can be configured to cost more, and the chart's own price spine can cost zero. Ten single-slot sources fill the budget exactly.
Two things do not count against the budget:
- Identical calls dedupe. Subscribing to the same
(type, symbol, exchange)twice is one source, so you can reference it freely. - Derived series are free.
htf(...), indicators, and other transforms of a source you already opened fetch nothing new.
Treat the 10 weighted slots as the contract and design multi-venue aggregations within it. For those patterns, see Multi-Source & Aggregation.