Script Declaration#

mode()

QuantScript
mode(mode_type: "indicator", script_name: "My Script");
mode(mode_type: "live");
mode(mode_type: "backtest", script_name: "My Strategy");

Modes: "indicator", "backtest", "forwardtest", "live". Optional script_name arg.

broker("sim")

QuantScript
broker(adapter: "sim", config: {
  initial_capital: 100000,
  commission: 0.05,
  execution_strategy: "bar-match-hedging"
});

Parameters: initial_capital, commission, commission_type, slippage, leverage, currency, execution_strategy, fill_model, fee_model, margin_model, stop_out_level.

candles()

QuantScript
candles(symbol: "BTCUSD", resolution_type: "1m", bars: 120, adapter: "demotrader.net");
symbol
Data symbol (required, named)
resolution_type
Bar interval (e.g., "1m", "1h", "1d")
bars
Number of bars to fetch
adapter
Data adapter name

ticks()

QuantScript
ticks(symbol: "BTCUSD", adapter: "demotrader.net");
ticks(symbols: ["BTCUSD", "ETHUSD"], adapter: "demotrader.net");

Set the quote stream adapter for real-time tick data. Enables event.onTick() handlers to receive live ticks in forwardtest/live modes.

symbol
One symbol string (required, named)
symbols
Array of symbol strings (required when using multiple symbols, named)
adapter
Quote stream adapter name (required, e.g., "demotrader.net")

Authentication: ticks() requires a broker() declaration — the broker's session token is automatically used for quote stream authentication.

Mode behavior: Active in forwardtest and live modes. Ignored in backtest and indicator modes.

data.loadCandles()#

Load secondary candles from a different symbol/resolution_type. Takes a single object argument. With a callback, evaluates an expression on native bars (like Pine's request.security()). Without a callback, returns a raw FeedAccessor.

Expression evaluation (returns series)

QuantScript
var ethSma = data.loadCandles({ adapter: "demotrader.net", symbol: "ETHUSD", resolution_type: "4h", callback: (c) => ta.sma(c.close, 50) });

// Complex callback with block body
var xauSignal = data.loadCandles({ adapter: "demotrader.net", symbol: "XAUUSD", resolution_type: "1D", callback: (c) => {
  const rsi = ta.rsi(c.close, 14);
  if (rsi === null) return 0;
  return rsi > 70 ? -1 : (rsi < 30 ? 1 : 0);
} });

Raw access (returns FeedAccessor)

QuantScript
var feed = data.loadCandles({ adapter: "demotrader.net", symbol: "ETHUSD", resolution_type: "4h" });
// feed.close, feed.open, feed.high, feed.low, feed.volume

Historical lookback

QuantScript
var prev = ethSma[1];  // previous bar's SMA value
adapter
Data adapter name (required)
symbol
Instrument symbol (required)
resolution_type
Bar interval (required, e.g., "4h", "1D")
callback
Function (c) => expr evaluated on native bars (optional)
gaps
"ffill" | "none" (optional, default "ffill")

All fields go in the single object argument.

Results are forward-filled to the primary resolution_type's timeline. Supports [n] historical lookback on callback results.

broker()

QuantScript
broker(adapter: "demotrader.net", email: "user@example.com", password: "Password@123");

// With allocated_capital for multi-strategy capital isolation
broker(adapter: "demotrader.net", email: "user@example.com", password: "Password@123", config: { allocated_capital: 10000 });

Only required for live trading. Parameters: adapter name, email, password, token, account_id, position_mode.

Virtual Trading Accounts: Set config: { allocated_capital: N } to give this strategy its own isolated balance, equity, and margin tracking when multiple strategies share one broker account. Risk guards (risk()) are enforced against virtual account values. Opt-in — when omitted, broker-wide account values are used. See the guide for details.

risk()

QuantScript
risk(max_position_pct: 20, daily_loss_limit: 3, max_trades_per_day: 10);

Parameters: max_position_pct, daily_loss_limit, daily_profit_target, weekly_loss_limit, max_trades_per_day, max_positions, max_consecutive_losses, max_loss_per_trade, min_balance, min_equity, max_margin_pct, kill_switch.

Execution Model & Events#

event.onCandle(callback)

The primary entry point for bar-by-bar strategy logic.

QuantScript
event.onCandle((candle) => {
    let closePrice = candle.close;
    let highPrice = candle.high;
});

event.onTick(callback)

Fires reactively for each tick/quote received from the quote stream. Requires ticks() declaration and a connected broker() for authentication. The tick object contains symbol, bid, ask, last, and timestamp.

QuantScript
event.onTick((tick) => {
    console.log(`${tick.symbol}: bid=${tick.bid} ask=${tick.ask}`);
});

event.onNewCandle(callback)

Fires when a bar completes.

event.onTimer(callback, intervalMs?)

Fires at a regular time interval. Default: 60000ms.

Broker Event Callbacks

Trading lifecycle events — fired when the broker reports changes to your orders and positions:

  • event.onFill(callback) — an order was executed (full or partial fill)
  • event.onReject(callback) — the broker rejected an order
  • event.onCancel(callback) — a pending order was cancelled or expired
  • event.onPositionOpened(callback) — a new position was opened
  • event.onPositionChanged(callback) — position modified (SL/TP, size, openPnl)
  • event.onPositionClosed(callback) — a position was fully closed (exposes closedPnl)

DataFrame Operations#

Frame Properties

PropertyDescription
candleIndexCurrent candle index (0-based)
lastCandleIndexIndex of the last candle
candle.isFirsttrue if first candle
candle.isLasttrue if last candle in the DataFrame (may be a forming bar)
candle.isFormingtrue if the bar is still forming (incomplete)
candle.isHistorytrue for confirmed candles before the live edge
candle.isNewtrue if new candle
candle.isConfirmedtrue if candle is confirmed (fully closed)
candle.isLivetrue only on the latest confirmed candle (trading edge)
candle.hl2(high + low) / 2
candle.hlc3(high + low + close) / 3
candle.hlcc4(high + low + close + close) / 4
candle.ohlc4(open + high + low + close) / 4

Persistent Variables (var)#

Use var for variables that retain their value across bars.

QuantScript
var highestHigh = 0;
event.onCandle((candle) => {
    if (candle.high > highestHigh) highestHigh = candle.high;
});

Persistent State (state.*)#

Cross-worker persistent storage. Properties survive export/import cycles.

QuantScript
state.entriesToday = 0;
state.lastPrice = 0;

Historical Bar References#

QuantScript
let prevClose = close[1];  // Previous bar's close
let twoAgo = high[2];     // High from 2 bars ago

Technical Analysis (ta.*)#

Null handling: All ta.* functions return null when there is insufficient data. Use ta.nz() to replace nulls.

Moving Averages

ta.sma(source, length)

Simple Moving Average.

ta.ema(source, length)

Exponential Moving Average.

ta.wma(source, length)

Weighted Moving Average.

ta.rma(source, length)

Recursive Moving Average (Wilder's smoothing).

ta.swma(source, length)

Sine-Weighted Moving Average.

ta.hma(source, length)

Hull Moving Average.

ta.alma(source, length, offset?, sigma?)

Arnaud Legoux Moving Average.

ta.linreg(source, length)

Linear Regression.

ta.dema(source, length)

Double Exponential Moving Average.

ta.tema(source, length)

Triple Exponential Moving Average.

ta.vwma(source, length, volume)

Volume-Weighted Moving Average.

Oscillators

ta.rsi(source, length)

Relative Strength Index (0-100).

ta.macd(source, fastLength, slowLength, signalLength)

MACD. Returns [macdLine, signalLine, histogram].

ta.stoch(source, high, low, length)

Stochastic Oscillator (0-100).

ta.roc(source, length)

Rate of Change.

ta.mom(source, length)

Momentum.

ta.willr(source, high, low, length)

Williams %R (-100 to 0).

ta.cmo(source, length)

Chande Momentum Oscillator (-100 to 100).

Volatility

ta.stdev(source, length)

Standard Deviation.

ta.variance(source, length)

Variance.

ta.dev(source, length)

Mean Deviation.

ta.bbands(source, length, mult?)

Bollinger Bands. Returns [lower, middle, upper].

ta.kc(source, length, mult?)

Keltner Channels. Returns [lower, middle, upper].

ta.tr(high, low, close)

True Range.

ta.atr(high, low, close, length)

Average True Range.

ta.vwap(source, volume)

Volume-Weighted Average Price (cumulative).

Trend & Direction

ta.sar(high, low, start?, increment?, max?)

Parabolic SAR.

ta.supertrend(factor?, length?, high, low, close)

Supertrend. Returns [value, direction].

Volume

ta.obv(close, volume)

On Balance Volume.

Utility Functions

ta.crossover(source1, source2)

Check if source1 crosses over source2.

ta.crossunder(source1, source2)

Check if source1 crosses under source2.

ta.highest(source, length?)

Highest value over a period.

ta.lowest(source, length?)

Lowest value over a period.

ta.highestbars(source, length)

Bar index of highest value (negative offset).

ta.lowestbars(source, length)

Bar index of lowest value (negative offset).

ta.change(source, length?)

Change in value over a period.

ta.falling(source, length?)

Check if series is falling.

ta.rising(source, length?)

Check if series is rising.

ta.barssince(condition)

Bars since condition was true.

ta.valuewhen(condition, source, occurrence?)

Value when condition was last true.

ta.cum(source)

Cumulative sum.

ta.median(source, length)

Median over a period.

ta.percentrank(source, length)

Percentile rank (0-100).

ta.pivothigh(source, leftBars, rightBars)

Pivot high point.

ta.pivotlow(source, leftBars, rightBars)

Pivot low point.

ta.correlation(source1, source2, length)

Correlation (-1 to 1).

ta.nz(source, replacement?)

Replace null with default (default: 0).

Trade Execution (trade.*)#

CCXT-inspired unified trading API. All methods return uniform result objects.

Order Placement

trade.createOrder(params)

Universal order. Specify side and type explicitly.

trade.buy(params), trade.sell(params)

Market buy/sell.

trade.buyLimit(params), trade.sellLimit(params)

Limit buy/sell.

trade.buyStop(params), trade.sellStop(params)

Stop buy/sell.

Trade Sizing (size object)

All order methods accept size as an alternative to qty (mutually exclusive). Exactly one field must be set:

FieldExampleBehaviour
unitssize: { units: 0.5 }Direct base-asset quantity (same as qty)
lotssize: { lots: 0.1 }Standardized lot units — resolved to base quantity via the instrument’s lot size
cashsize: { cash: 500 }Spend a fixed quote-currency amount
equity_percentsize: { equity_percent: 2 }Percentage of account equity (0–100)
QuantScript
// FX: 0.5 lots of EURUSD
trade.buy({ symbol: "EURUSD", size: { lots: 0.5 } });

// Crypto: spend $1000 USDT
trade.buy({ symbol: "BTCUSDT", size: { cash: 1000 } });

// Stocks: risk 2% of equity
trade.buy({ symbol: "AAPL", size: { equity_percent: 2 } });

Order Management

trade.modifyOrder(params)

Modify a pending order.

trade.cancelOrder(params)

Cancel a pending order.

trade.closePosition(params)

Close a position (full or partial).

trade.modifyPosition(params)

Modify position stop loss / take profit.

Introspection

trade.positions()

All open positions.

Returned fields: id, symbol, side, qty, avgEntryPrice, markPrice, openPnl, closedPnl, marginUsed, leverage, liquidationPrice, stopLoss, takeProfit, strategyId, openedAt, updatedAt.

openPnl is the floating P&L on the still-open exposure (marked at markPrice); closedPnl is the P&L already booked by partial closes. Neither name uses the ambiguous “realised/realized” spelling.

trade.orders()

All open orders.

trade.fills()

All fills (executed trades).

trade.account()

Account info (equity, cash, balance, etc.).

Returned fields: id, name, currency, balance, equity, cash, buyingPower, marginUsed, marginAvailable, openPnl, closedPnl, leverage, positions, marginLevel.

trade.symbolInfo(symbol)

Instrument info.

trade.capabilities()

Broker's supported features.

trade.instruments()

All available instruments.

Forming Candle & Live Price (trade.*)#

In forwardtest and live modes the runtime maintains an incomplete (forming) candle that updates with every incoming tick. These builtins expose that real-time data so your strategy can make execution decisions (entry checks, SL/TP placement, position sizing) using the freshest available price — while indicator/signal logic continues to use the confirmed candle.close to avoid repainting.

trade.formingCandle(symbol?)

QuantScript
let bar = trade.formingCandle();
if (bar != null) {
  console.log("Forming close: " + bar.close);
}

Returns the current incomplete candle object, or null if no forming bar exists (e.g., in backtest mode).

symbol
Optional. Symbol to query; defaults to the script’s primary symbol.

Returned fields: time, open, high, low, close, volume, incomplete (always true), symbol.

trade.last(symbol?)

QuantScript
let livePrice = trade.last();
if (livePrice == 0) { livePrice = candle.close; }

Returns the freshest last-traded price: the forming bar’s close if available, otherwise the latest quote’s last. Returns 0 when neither exists (e.g., backtest with no quotes).

symbol
Optional. Symbol to query; defaults to the script’s primary symbol.

Tip: Always include the == 0 fallback so the same script works in both backtest and live modes.

trade.bid(symbol?)

QuantScript
let bid = trade.bid();

Returns the current best bid price from the quote stream. Returns 0 if no quote data is available.

symbol
Optional. Symbol to query; defaults to the script’s primary symbol.

trade.ask(symbol?)

QuantScript
let ask = trade.ask();

Returns the current best ask price from the quote stream. Returns 0 if no quote data is available.

symbol
Optional. Symbol to query; defaults to the script’s primary symbol.

When to Use

Use caseData sourceWhy
Indicator / signal logiccandle.close (confirmed bar)Immutable — prevents repainting
Entry condition checkstrade.last()React to the freshest price
SL / TP placementtrade.last()Bracket relative to actual execution price
Position sizing (cash / equity %)trade.last()Accurate unit calculation
Spread-aware limit orderstrade.bid() / trade.ask()Place orders at precise quote levels

Mode behavior: These functions are most useful in forwardtest and live modes. In backtest mode the forming bar is not populated, so trade.last() returns 0 and trade.formingCandle() returns null — hence the fallback pattern.

Strategy Introspection (strategy.*)#

Read-only introspection. Order submission is via trade.*.

PropertyDescription
strategy.position_sizeCurrent position size
strategy.position_avg_priceAverage entry price
strategy.position_side"long", "short", or "flat"
strategy.equityCurrent equity
strategy.cashAvailable cash
strategy.openprofitOpen position profit
strategy.netprofitNet profit
strategy.opentradesNumber of open trades
strategy.closedtradesNumber of closed trades
strategy.wintradesWinning trades count
strategy.losstradesLosing trades count
strategy.eventradesEven trades count
strategy.initial_capitalInitial capital

Chart Functions (chart.*)#

chart.plot(source, title?, color?, options?, pane?)

Plot a data series. Pass { style: "..." } as the 4th argument to change the visual style. Use pane: "oscillator" for a separate lower pane.

Available styles:

"line" (default)Solid connected line
"histogram"Vertical bars from zero-line
"area"Filled area between line and zero
"areabr"Area fill with break — color above zero, contrasting color below
"stepline"Staircase / step line
"stepline_diamond"Step line with diamond markers
"stepline_square"Step line with square markers
"stepline_round"Step line with round markers
"cross"X-shaped markers at each point
"circles"Hollow circle outlines at each point
"line_with_markers"Solid line with filled circle markers
"columns"Full-width column bars from pane bottom
"candle"Mini candlestick markers (body + wicks)
"linebr"Line that breaks at NaN gaps
chart.plotshape(series, title?, style?, color?, size?, text?)

Plot a shape marker.

chart.hline(price, title?, color?, style?, pane?)

Draw a horizontal line.

chart.bgcolor(color, title?, pane?)

Set per-bar background color. Supports conditional coloring — pass different colors based on conditions (e.g., trend direction). Renders as vertical strips spanning the full pane height.

Parameters:

color
CSS color string (hex, rgba, or named). Can be a conditional expression.
title
Optional identifier for the bgcolor series (default: "bg")
pane
Target pane: "main" (default) or "oscillator"
QuantScript
const isUptrend = close > ta.sma(close, 50);
chart.bgcolor(isUptrend ? "rgba(76,175,80,0.1)" : "rgba(244,67,54,0.1)");
chart.fill(plot1, plot2, color?, title?)

Fill area between two plots.

chart.pane(name?)

Switch pane context for subsequent plot calls.

Chart Labels (chart.label.*)

Draw text annotations at specific bar/price locations. Labels can be styled with different visual treatments.

chart.label.new(x, y, text, color?, style?)

Create a text label at bar index x and price y.

Parameters:

x
Bar index (horizontal position)
y
Price value (vertical position)
text
Label text content
color
CSS color string (default: "#c9d1d9")
style
Visual style (see table below)

Available styles:

"label_up" (default)Triangle pointing up, text above
"label_down"Triangle pointing down, text below
"label_left"Text to the left of point
"label_right"Text to the right of point
"label_center"Text centered on point
"label_flag"Small circle with text
"label_arrow_up"Arrow pointing up with text
"label_arrow_down"Arrow pointing down with text
"label_none"Simple box (no triangle/arrow)
QuantScript
const rsi = ta.rsi(close, 14);
if (rsi > 70) {
  chart.label.new(candleIndex, high, "Overbought", "#f44336", "label_down");
}

Input Functions (input.*)#

input.int(defval, title?, minval?, maxval?, step?, tooltip?, inline?, group?)

Integer input. Positional arguments in Pine Script order — pass null to skip a slot.

input.float(defval, title?, minval?, maxval?, step?, tooltip?, inline?, group?)

Float input. Same signature as input.int.

input.bool(defval, title?, tooltip?, inline?, group?)

Boolean input.

input.string(defval, title?, options?, tooltip?, inline?, group?)

String input. Pass an array for options to render a dropdown.

QuantScript
const rsiLen = input.int(14, "RSI Length", 2, 50, null, null, null, "RSI");
const stopLoss = input.float(2.0, "Stop Loss %", 0.5, 10.0, 0.5, null, null, "Risk");
const useStop = input.bool(true, "Use Stop Loss", null, null, "Risk");

Event Functions (event.*)#

See Execution Model & Events for core event callbacks.

Built-in Functions#

console.log(...args)

Print values to console output.

console.error(...args)

Print error to console output.

console.journal(message, params?)

Write a strategy-authored note to the Run Journal. Optional params: { reason, tags }.

exit(reason?)

Terminate script execution immediately.

JavaScript Standard Library#

QuantScript provides full parity with the JavaScript ES6 standard library for the namespaces and types below. They behave identically to their JavaScript counterparts — refer to any JS reference (e.g. MDN) for complete API details.

Namespace / TypeAvailable API
Math.*Math.abs(), Math.max(), Math.min(), Math.sqrt(), Math.pow(), Math.round(), Math.ceil(), Math.floor(), Math.log(), Math.exp(), Math.sin(), Math.cos(), Math.tan(), Math.atan2(), Math.random(), Math.sign(), Math.pi, Math.e, etc.
JSON.*JSON.parse(text) — parse JSON string into an object/array. Returns null on invalid input.
JSON.stringify(value) — serialize any value to a JSON string.
Date.*Date.now() — current timestamp in ms.
Date.parse(dateString) — parse a date string to timestamp.
Date.UTC(y, m, d, h?, min?, s?, ms?) — UTC timestamp from components.
String methodsNative instance methods on any string: .length, .includes(), .startsWith(), .endsWith(), .indexOf(), .charAt(), .match(), .toUpperCase(), .toLowerCase(), .trim(), .replace(), .replaceAll(), .substring(), .slice(), .repeat(), .padStart(), .padEnd(), .split(), .reverse()
Array methodsNative instance methods on any array: .length, .push(), .pop(), .shift(), .unshift(), .splice(), .slice(), .concat(), .includes(), .indexOf(), .find(), .findIndex(), .map(), .filter(), .reduce(), .forEach(), .every(), .some(), .sort(), .reverse(), .join(), .flat(), .flatMap()

Quick Examples

QuantScript
// Math
let x = Math.sqrt(144);  // 12

// JSON
let obj = JSON.parse('{"symbol": "BTCUSD", "qty": 0.5}');
console.log(obj.symbol);  // BTCUSD
let text = JSON.stringify(obj);

// Date
let now = Date.now();  // current timestamp in ms

// String methods
let sym = "btcusd".toUpperCase();  // "BTCUSD"

// Array methods
let prices = [100, 102, 98, 105];
let avg = prices.reduce((a, b) => a + b, 0) / prices.length;

JavaScript Gotchas#

QuantScript looks like JavaScript, but is a deliberately constrained DSL.

No do...while loop

Only while, for, and for...of are supported.

No for...in loop

Use for...of over an array of known keys instead.

No classes

No class, extends, super, new, or constructor.

No try/catch/throw

Errors are fatal and handled by the runtime.

No async/await

Scripts are synchronous. No async, await, Promise.

No spread/rest operator

Use arr.concat() instead.

No Set, Map, or WeakRef

Only plain arrays [] and objects {}.

No object destructuring

Array destructuring is supported, but object destructuring is not.

No bitwise operators

No typeof or instanceof

Use x == null to check for null.

No import/export

Scripts are self-contained. All built-ins are available through global namespaces.

No delete operator

No labeled break

break and continue apply to the innermost loop only.

== is strict equality

Unlike JavaScript, == does no type coercion. 0 == "0" is false.

var means something different

In QuantScript, var declares a persistent variable that retains its value across bars.

Two argument conventions

Built-ins take either positional arguments (ta.*, input.*, risk.* — e.g. input.int(14, "RSI", 2, 50)) or a single object argument (trade.* orders, data.loadCandles — e.g. trade.buy({ symbol: "BTCUSD", qty: 1 })). Named arguments are only used in top-level declarations like mode() and candles().

Semicolons are required

Like JavaScript, every statement must end with a semicolon ;.