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

Engine API

Three ways to run a script: the quas command line, the quas serve worker protocol, and the terminal's HTTP endpoints (which use quas serve behind the scenes). All three return the same result format, described at the end of this chapter.

Command line

CommandDoes
quas run <script.qs> (<data.csv> | --synth N)runs the script, JSON result to stdout (or --out FILE)
quas check <script.qs>compiles; prints the outputs, --json prints diagnostics + schema, --dis the bytecode
quas bench <script.qs> [--synth N] [--runs R]best-of-R timing in ns/bar; --keep-series also times the JSON export; --json for scripts
quas sweep <script.qs> (<data.csv> | --synth N) --param id=from:to:step ...parameter grid search over threads
quas doc [--json]lists every builtin; --json prints the full catalog (arguments, defaults, docs)
quas servethe long-lived worker described below
OptionMeaning
--set id=valueparameter override (repeatable)
--asset key=valueinstrument: ticker exchange kind currency base_currency timezone tick_size point_value lot_size min_qty price_precision interval
--interval 15shorthand for --asset interval=15
--live-from Nbars with index >= N are live (bot signals are emitted from there)
--forming-lastthe last bar is a forming bar
--bot-id user:strategythe identity used in bot signal ids
--lib-dir DIRextra library folder (repeatable)
--no-barsleave the input bars out of the JSON
--seed Sseed of --synth data
--threads N --top K --sort net|pf|win|dd|sharpesweep options

Exit status: 0 success, 2 compile or run error (the message is on stderr; compile errors list line:col code: message and the fix when there is one).

The `quas serve` worker

quas serve reads one JSON request per line on stdin and writes one JSON response per line on stdout, in order. Compiled programs are cached by content (the script and its libraries), and sessions keep the engine state of one script on one chart alive, so new bars and price updates execute only the bars that changed.

Every response has id (echoed from the request), ok, op, ms (time in the worker) and timing: {parseMs, execMs, encodeMs}. A failure is {"ok": false, "error": {"code": "COMPILE" | "LIMIT_EXCEEDED" | "REQUEST", "message": ..., "diagnostics": [...]}}.

open

data.json
{"op": "open", "id": 1, "session": "chart-1:ema",
 "script": "analysis \"EMA\" { overlay = true }\nrender.line(ta.ema(close, 20), \"EMA\")\n",
 "libraries": [{"name": "viz/palette", "source": "library \"viz/palette\" ..."}],
 "bars": {"time": [1700000000, 1700000060], "open": [100, 101], "high": [102, 103],
          "low": [99, 100], "close": [101, 102], "volume": [500, 450]},
 "params": {"length": 20}, "asset": {"ticker": "BTCUSDT", "timezone": "UTC", "tick_size": 0.1},
 "formingLast": true, "encoding": "json", "view": {"from": 0, "to": 1}}
FieldMeaning
sessionthe caller's id for this script on this chart; opening an existing id replaces it
barscolumns of equal length; numbers or null; host holds extra columns ({"bar.delta": [...]}); book holds the order book (below)
params, assetas --set and --asset
formingLastthe last bar is still forming (it can then be updated with update_last)
liveFromfirst live bar (default: no history bar is live)
botIduser:strategy for bot signal ids
encodingjson (default) or base64: per-bar columns travel as base64 little-endian float64 (bars in, plots out), about 2x faster for large histories
viewthe visible bar range, for period = "visible" profiles
includeBarsalso return the bars (barData)

bars.book carries the real order book of each bar, one snapshot (or null) per bar:

data.json
"book": [
  {"bids": [[100.9, 12, 3], [100.8, 30]], "asks": [[101.1, 8, 1], [101.2, 25]]},
  {"bids": [[101.0, 5]], "asks": [[101.2, 9]]},
  {"orders": [["bid", 101.0, 2, 9001], ["ask", 101.3, 4, 9002]]},
  null
]

A level is [price, size, orders?] (or {"price", "size", "orders"}), best price first: one level a side is L1, more are L2. orders (L3) lists individual orders as [side, price, size, id?] (side -1 / 1 or "bid" / "ask" / "buy" / "sell"); when a snapshot has orders and no levels, the levels are aggregated from the orders. append takes the same field; update_last takes one snapshot as "book" on its bar and replaces the forming bar's book. schema.hostData lists the host data the script reads (order_book, order_flow, derivatives, fundamentals) and schema.bookDepth the depth it needs (1 L1, 2 L2, 3 L3), so a host sends the book only to scripts that use it.

The response has session, key (program hash), schema (the outputs, see below), bars, from (0), forming and the outputs of every bar.

append, update_last, view, close

data.json
{"op": "append", "session": "chart-1:ema", "formingLast": true,
 "bars": {"time": [1700000120], "open": [102], "high": [102], "low": [102], "close": [102], "volume": [0]}}
{"op": "update_last", "session": "chart-1:ema",
 "bar": {"time": 1700000120, "open": 102, "high": 103.5, "low": 101.8, "close": 103.1, "volume": 220}}
{"op": "view", "session": "chart-1:ema", "from": 400, "to": 900}
{"op": "close", "session": "chart-1:ema"}
  • append adds newer bars. A first bar whose time equals the last bar's time replaces it (that is how a forming bar is confirmed with its final prices).
  • update_last replaces the prices of the forming bar and re-executes only that bar.
  • The response of append / update_last contains the outputs of bars from .. bars - 1 only: replace your copy from index from on. Strategy trades come from tradesFrom on, the equity curve from equityFrom on, bot signals only for the new bars.
  • view recomputes visible-range profiles only.
  • Other ops: run (open + close in one, no session), check (compile only: valid, schema, symbols, diagnostics), stats (sessions, cached programs, cache hits), ping.

HTTP endpoints (terminal)

All endpoints take and return JSON. session and check require a signed-in user (bearer token or the session cookie) and answer 401 otherwise; session ids are private to the user.

POST /api/quascript/session

The worker protocol above, one operation per request. The body is the request object (op, session, ...); libraries named by use are added from the bundled library folder when the client does not send them. The response is the worker's response, unchanged.

StatusWhen
200ok: true
400compile error, invalid request, unknown op
401not signed in
409SESSION_LOST: the session is unknown (expired or its worker restarted); send open again
413more than 2,000,000 bars
422LIMIT_EXCEEDED
503 / 504worker exited / timed out

POST /api/quascript/check

{ "script": "...", "libraries": [...] } → { ok, valid, key, schema, symbols, diagnostics }. Each diagnostic has code, severity, message, note, line, col, endLine, endCol and fixes: [{line, col, endLine, endCol, text}]. Used by the editor for underlines and quick fixes.

POST /api/quascript/run

One complete run in the chart's format: { script, bars: [{time, open, high, low, close, volume}], params, libraries, asset } → { success, data } where data has plots (with data: [{time, value, color?}]), markers + markSites, hlines, fills, candleColors, backgrounds, drawings, alerts, strategy and meta.timing. Settings are matched to the script's parameters by id or title. Use session for live charts; run recomputes everything.

Configuration

VariableDefaultMeaning
QUAS_BINpackages/quascript/build/quasengine binary
QUAS_WORKERShalf the CPU cores (1 .. 8)worker processes
QUAS_REQUEST_TIMEOUT_MS60000per request; a stuck worker is restarted
QUAS_LIBRARY_DIRpackages/quascript/presets/librarybundled libraries

Result format (schema version 2)

A full result (quas run) is one JSON object:

KeyContent
schemaversion, kind, title, overlay, render, maxObjects, params, plots, marks, levels, bands, backgrounds, profiles, alertRules, feeds, grid, warnings
metabars, seconds, instructions, hasStrategy, runtimeWarnings
barsthe input columns (unless --no-bars)
plotsone array per plot, one value per bar (null = na)
plotColors{plotIndex: [color or null per bar]} for plots with per-bar colors
marksper mark site: {bars: [...], prices: [...]}
backgrounds, candleColorssparse [[bar, color], ...]
pricesper render.price.*: {o, h, l, c} columns
profilesper profile: poc vah val rowStep totalVolume fromBar toBar and levels: [[price, volume, buy, sell, isPoc, inValueArea]]
drawingslive objects: {id, kind, num, text, xs, ys, rows, cols, cells, merges}
alerts{bar, rule, message}
strategycapital, equity, drawdown, trades, stats (or null)
botposition, pending orders, signals

Plot metadata: index title style color width stroke pane offset base upColor downColor shape size dynamicColor backend priceIndex profileIndex box. Styles: 0 line, 1 histogram, 2 area, 4 dots, 5 spline, 6 step, 7 baseline, 8 .. 17 price styles, 13 volume profile, 18 stepped area, 20 session profile, 21 TPO, 26 level. Strokes: 0 solid, 1 dashed, 2 dotted.

Colors are numbers 0xAARRGGBB: the low 24 bits are RGB and the high byte is the opacity, with 0 meaning fully opaque (so plain palette colors are just 0xRRGGBB). In per-bar color arrays null means "the plot's color" and 0 is black.


© 2026 Quatick Technologies. All rights reserved.