Appearance

Personalize your experience

Mode
Accent Color
Direction
All Docs/QuaScript//
QuaScript logo

QuaScript Documentation

Language guide, engine API, 601 builtins and the preset library · v3.0.0-phase1

Time, sessions and data

The bar

NameValue
open high low close volume timethe bar's prices, volume and open time (epoch seconds)
bar.index0 for the oldest bar
bar.mid bar.typical bar.average bar.weighted(h+l)/2, (h+l+c)/3, (o+h+l+c)/4, (h+l+2c)/4
bar.range bar.body bar.upper_wick bar.lower_wickcandle geometry
bar.close_timethe time the bar closes
bar.is_first bar.is_lastfirst / newest bar of the data
bar.is_confirmedfalse only for a forming (live, not yet closed) bar
bar.is_live bar.is_newthe bar arrived after the chart opened / its first update

The instrument and interval

asset.ticker exchange kind currency base_currency timezone tick_size point_value lot_size min_qty price_precision describe the instrument. interval.seconds multiplier unit label is_intraday is_daily ... describe the chart interval; interval.to_seconds("4h") converts a spec.

Interval specs are a number with an optional unit: 15 or 15m (minutes), 30s, 4h, 1D, 1W, 1M.

Time and sessions

QuaScriptengine result
Loading the engine result…
sessions.qs
analysis "Opening range" { overlay = true }

in_open = session.in_range("0930-1000", "America/New_York")
persist hi = na
persist lo = na
if session.is_opening_bar("0930-1600", "America/New_York") {
    hi := high
    lo := low
}
if in_open {
    hi := math.max(hi, high)
    lo := math.min(lo, low)
}
render.step(hi, "Opening range high", color = color.emerald.400)
render.step(lo, "Opening range low", color = color.rose.400)
render.background(in_open ? color.translucent(color.amber.500, 92) : na)
quatick-charts · QuaScript engine on synthetic 1-minute bars · drag to pan · scroll to zoom · Run executes the script on the engine (⌘/Ctrl+Enter while editing)
  • time.year month day weekday hour minute second of_day(t, tz) split a time (default: the bar's open time in the instrument's time zone).
  • time.timestamp(year, month, day, hour, minute, second, tz) builds one; time.format(t, "yyyy-MM-dd HH:mm", tz) prints one; time.shift(t, 3, "day") moves one.
  • Sessions are "HHMM-HHMM" with optional weekdays "0930-1600:12345" (1 = Monday). session.is_open in_range is_opening_bar is_closing_bar start_time end_time take a spec and a time zone. session.core is the instrument's regular session.
  • Time zones are IANA names (Europe/London, Asia/Kolkata) and follow daylight saving.

Other intervals and symbols (feeds)

QuaScriptengine result
Loading the engine result…
feeds.qs
analysis "Hourly trend" { overlay = true }

tf = param.interval("60", title = "Interval")
hourly = feed.series("", tf, ta.ema(close, 20))
render.step(hourly, "EMA 20 (higher interval)", color = color.violet.400, width = 2)
render.candle_color(close > hourly ? color.emerald.500 : color.rose.500)
quatick-charts · QuaScript engine on synthetic 1-minute bars · drag to pan · scroll to zoom · Run executes the script on the engine (⌘/Ctrl+Enter while editing)

feed.series(symbol, interval, expression) evaluates expression on the bars of another interval ("" = the chart's symbol) and returns the value of the last closed bar of that interval. It never looks ahead: the value of an hourly bar becomes visible on the chart bar in which that hour closed. forming = true also shows the forming bar's value on live bars.

Higher intervals of the chart symbol are built from the chart's bars. Other symbols are supplied by the host; when they are missing the value is na with one W_FEED_MISSING warning.

Order book

Next to open, high, low, close and volume, every bar carries the real order book the chart recorded for it: book. On history it is the book at the bar's close (from the chart's recorded depth history), on the forming bar the live book, updated with every book tick, exactly like the forming bar's close. The engine never invents a book: a bar the chart has no book for has an empty one.

Feeds differ in depth, and book holds whatever the feed provides:

DepthFeed givesbook.depthRead with
L1best bid / ask and their sizes1book.bid, book.ask, book.bid_size, book.ask_size, book.mid, book.spread, book.microprice
L2price levels, optionally with the order count of each level2book.bids, book.asks and the whole-book builtins below
L3every individual resting order (market by order)3book.orders, book.order_count, book.largest_order; L2 builtins work too (levels are aggregated from the orders)

book.depth is per bar (0 = no book on this bar), so a script can check what it has. The terminal reports in each script's schema the depth it reads (schema.bookDepth).

Price levels: book.bids and book.asks

book.bids and book.asks hold every level of the bar's book, best price first, as a grid with one row per level:

ColumnConstantValue
0book.col_priceprice of the level
1book.col_sizeresting size
2book.col_totalcumulative size from the best price to this level
3book.col_ordersorders at the level (na when the feed does not report it)
QuaScriptengine result
Loading the engine result…
book_walls.qs
analysis "Book walls" {
    overlay = true
    render  = webgpu
}
/// The biggest bid and ask inside 1 % of the mid, and the side the book leans to.
bids = book.bids
asks = book.asks
mid = book.mid
bid_wall = na
ask_wall = na
bid_max = 0.0
ask_max = 0.0
for i in 0..grid.rows(bids) {
    px = grid.get(bids, i, book.col_price)
    sz = grid.get(bids, i, book.col_size)
    if px >= mid * 0.99 and sz > bid_max {
        bid_max := sz
        bid_wall := px
    }
}
for i in 0..grid.rows(asks) {
    px = grid.get(asks, i, book.col_price)
    sz = grid.get(asks, i, book.col_size)
    if px <= mid * 1.01 and sz > ask_max {
        ask_max := sz
        ask_wall := px
    }
}
render.step(bid_wall, "Bid wall", color = color.emerald.400, width = 2)
render.step(ask_wall, "Ask wall", color = color.rose.400, width = 2)
render.candle_color(book.pressure() > 0.2 ? color.emerald.500 : book.pressure() < -0.2 ? color.rose.500 : na)
quatick-charts · QuaScript engine on synthetic 1-minute bars · drag to pan · scroll to zoom · Run executes the script on the engine (⌘/Ctrl+Enter while editing)

Bind book.bids / book.asks to a variable once and loop over the variable: each read of the builtin copies the side into its grid. A grid holds the book of the current bar only; keep what you need from older books in series (imb = book.imbalance(), then imb[1]).

Individual orders: book.orders

With an L3 feed, book.orders lists every resting order: columns book.order_side (-1 bid, 1 ask), book.order_price, book.order_size and book.order_id. Without L3 data it is empty.

script.qs
o = book.orders
big = 0
for i in 0..grid.rows(o) {
    if grid.get(o, i, book.order_size) > 10 * ta.sma(volume, 50) / 100 { big += 1 }
}

Whole-book builtins

BuiltinValue
book.levelsprice levels present on both sides (0 = no book on this bar)
book.bid_volume(levels), book.ask_volume(levels)resting size over the top levels (0 = all)
book.imbalance(levels)(bids - asks) / (bids + asks), -1 .. 1
book.pressure(decay)imbalance with each level weighted by exp(-distance % / decay)
book.liquidity(pct, side)size within pct % of the mid (book.bid_side, book.ask_side or 0)
book.vwap(side, levels)size-weighted price of one side
book.wall(side, levels)(price, size) of the largest level
book.impact(qty, side)percent from the mid of the average fill of a market order of qty
book.order_count(side)resting orders (L3, or the order counts of L2 levels)
book.largest_order(side)(price, size) of the largest single order (L3)
orderbook.ofi(levels)Cont-Kukanov-Stoikov order flow imbalance between consecutive bars' books

Bars without a book give empty grids and na; a chart that supplies no book at all also gives one W_HOST_DATA warning. Combine the book with bar data and fall back where history has no book:

QuaScriptengine result
Loading the engine result…
script.qs
clv = ((close - low) - (high - close)) / math.max(high - low, 1e-12)
lean = math.is_na(book.imbalance()) ? clv : book.imbalance()
quatick-charts · QuaScript engine on synthetic 1-minute bars · drag to pan · scroll to zoom · Run executes the script on the engine (⌘/Ctrl+Enter while editing)

Supplying the book

The terminal does this for every script that reads the book: it normalises its feeds (Delta, Binance, Zerodha, Fyers, ...) into snapshots, attaches the recorded book of each history bar and streams the live book into the forming bar. Other hosts pass it themselves:

  • quas serve: bars.book, one snapshot per bar (see Engine API);
  • CSV files: columns orderbook.bid.N, orderbook.bid_size.N, orderbook.bid_orders.N, orderbook.ask.N, orderbook.ask_size.N, orderbook.ask_orders.N (N = 0 = best).

For engine tests only, quas run --synth N --synth-book adds a generated L2 book to synthetic bars.

Host data

Order flow, order book, derivatives and fundamentals come from the host as extra columns:

DomainValues
bar.buy_volume bar.sell_volume bar.delta bar.tick_countorder flow per bar
book.bid book.bids book.orders book.imbalance(levels) ...the order book (L1 / L2 / L3), see Order book
orderbook.bid(level) ask bid_size ask_size spread spread_ticks mid microprice imbalance depth_ratio slippagesingle levels of the same book
derivative.funding_rate open_interest basis basis_pct put_call_ratio max_pain expiry_days
financial.pe pb eps market_cap revenue net_income roe debt_to_equity debt_ratio fii_net dii_net

A chart without the data gives na and one W_HOST_DATA warning; a script can test math.is_na(bar.delta) and fall back to an estimate.


© 2026 Quatick Technologies. All rights reserved.