Getting Started

What's New in v3

kScript v3 turns the language from plot-an-indicator into model-and-backtest-a-trading-idea: strategy backtesting with honest fills, typed structs, persistent collections, self-managing drawings, no-repaint multi-timeframe, multi-venue aggregation, and a reference-validated TA library. Every capability here is verified against the live engine.

kScript v3 is the largest release since the language launched. v2 let you compute a series and plot it. v3 lets you model a trading idea and then backtest it: declare a strategy(), place orders against a broker emulator with honest, lookahead-free fills, and read the equity curve and trade stats it earns. Under that sits the language upgrade that makes real strategies writable: state in typed structures across bars, drawings you can retire, higher and lower timeframes without repaint risk, symbols and venues combined under one budget, and a technical-analysis library whose every function is validated against an independent reference.

It is additive. Every v2 script keeps running, with its //@version=2 line untouched; new scripts start with //@version=3 (see The version marker below — the engine treats the two markers identically).

            v2                              v3
  ----------------------------    ------------------------------------
  numbers, strings, booleans      + arrays & maps with lambdas/reducers
  fire-and-forget plotting        + type Zone { ... } structs with methods
  one symbol, one timeframe        + na & color as real types
  ~15 ad-hoc TA helpers           + drawings: line/box/label/polyline/
                                    linefill/table handles with .delete()
                                  + htf()/ltf()/request(): no-repaint MTF,
                                    multi-symbol, multi-venue aggregation
                                  + 70+ reference-validated TA functions,
                                    named streams (bb.upper, macd.signal)
                                  + vwap(anchor="day"|"week"|"month")
                                  + strategy(): backtesting with orders,
                                    brackets, equity, and honest fills
                                    (spot and leveraged perps)
                                  + foreign-domain panes: plotMatrix /
                                    plotCurve / plotTiles dashboards
                                  + tape by order size: buy/sell volume
                                    bucketed into real USD size bands
                                  + libraries: import "@owner/name@1.0.0"
                                    shares functions and types
                                  + a named limits manifest; every error
                                    carries a line and column

The headline capabilities

Strategy backtesting

The headline of the tier: a script can now declare strategy(...) instead of define(...), place orders with strategy.* methods, and get a full backtest: a trades list, an equity curve, and performance stats, rendered in the Strategy Tester panel under the chart. Fills are deterministic and lookahead-free, and where a single bar cannot say which level traded first, the engine resolves the question with finer-interval data or discloses the assumption it made. Slippage can even be priced from recorded order-book depth for your order size (slippageModel="bookEstimate").

MA cross strategy
//@version=3
strategy(title="MA Cross Strategy", initialCapital=10000, qtyType="percentOfEquity", qtyValue=50, commissionPercent=0.05, slippageBps=2)

timeseries bars = ohlcv(symbol=currentSymbol, exchange=currentExchange)
timeseries fast = ema(source=bars.close, period=9)
timeseries slow = ema(source=bars.close, period=21)

if (crossover(fast, slow)) {
  strategy.entry("L", "long")
}
if (crossunder(fast, slow)) {
  strategy.closeAll()
}

plotLine(value=fast, width=1, colors=["#4f8cff"], label=["Fast EMA"], desc=["9-period EMA of close"])
plotLine(value=slow, width=1, colors=["#f59e0b"], label=["Slow EMA"], desc=["21-period EMA of close"])

Start with Build Your First Strategy, then the Strategies section for fill semantics, slippage models, and the stats reference.

Typed structs with methods

Declare a type with fields and behavior, construct instances with named fields, and carry them across bars. State now has the shape of the problem instead of a pile of parallel arrays.

Struct with a method
//@version=3
define(title="Struct Methods", position="offchart", axis=true)

type Band {
  top: number,
  bottom: number,

  func width() {
    return this.top - this.bottom
  }
}

timeseries d = ohlcv(symbol=currentSymbol, exchange=currentExchange)
var band = Band.new(top=d.high[0], bottom=d.low[0])
plotLine(value=band.width(), colors=["#2563eb"], width=2, label=["Band width"], desc=["high minus low via a struct method"])

See User-Defined Types.

Self-managing drawings

Lines, boxes, labels, polylines, linefills, and tables are now handles you can create and .delete(). Build supply/demand zones that remove themselves when price mitigates them, or a live dashboard table that updates on the last bar.

See Drawing Objects.

No-repaint multi-timeframe

htf() reads a confirmed higher timeframe with no look-ahead: the 4h value at any bar uses only data that closed before that bar. The repaint trap that security() invites in other languages is impossible by default. ltf() attaches finer bars, and request() is one-line shorthand for loading another symbol.

See Multi-Timeframe.

Multi-venue aggregation

A single script has 10 weighted source slots to load from. Sum real buy/sell flow across four exchanges into one cumulative delta, or aggregate open interest across venues — the kind of cross-market view that took one source per script in v2.

See Multi-Source & Aggregation and Data Sources for all 18 source types.

A reference-validated TA library

70+ canonical indicators, each matching an independent reference implementation. Multi-output indicators expose named streams: bb.upper, macd.signal, stoch.k, supertrend.direction — no more positional guesswork.

See the TA Library and Named Streams.

Session-anchored VWAP

vwap(anchor="day" | "week" | "month") resets on real UTC session boundaries instead of drifting with however much history happened to load. The no-argument form is the familiar cumulative VWAP.

See Special Indicators.

Foreign-domain panes

A script can now emit a whole offchart pane that ignores the time axis: plotMatrix() renders a categorical grid (rows × columns, like a month-by-year returns table), plotCurve() renders series over a numeric X domain (strikes, spot levels, distribution buckets), and plotTiles() renders a KPI card dashboard, including computed text tiles. The pane keeps its own X domain, so time pan and zoom leave it untouched, and every cap is enforced as a named compile error rather than silent truncation.

See Foreign-Domain Panes for the full reference and the Historical Returns Matrix cookbook recipe for a complete worked example.

Tape by order size

The array-celled trade_volume_by_size source delivers buy/sell volume bucketed by real USD order size, so whale flow comes from actual trade sizes instead of candle-geometry proxies, with per-bar trade counts included. buy_sell_volume also gains a currency="USD" option for quote-notional CVD.

See Data Sources and the Whale vs Retail CVD recipe.

Perps backtesting

strategy(...) accepts instrument="perps": leveraged sizing on isolated margin, maker/taker fees, funding from recorded data, a liquidation model with an onLiquidation policy, and a set of perps-specific stats alongside the spot metrics.

See Perps: Margin, Liquidation, and Funding.

Script libraries

Shared code is now a first-class concept: write your normalization math, risk model, or zone structs once as a library("name") of functions and types, and import it from any script with import "@owner/name", an exact version pin (@owner/name@1.0.0), or an alias (as norm). Libraries are authored in the editor's Libraries panel, run under exactly the same sandbox as the importing script, and are frozen into the script at save, so a chart never depends on a registry at runtime and a published library update never silently changes a script that pinned its version.

See Libraries.

Quality-of-life across the line

Landed in the same release train: plotHistogram() for baseline-grown columns (MACD-style deltas), a comparator for array.sort() (.sort((a, b) => b - a)), requestBars(..., anchor: "latest") for datasets pinned to the newest bars regardless of where the chart is panned, and plotStatRow() for the per-bar statistics strip under the price pane. The release notes carry the full list with exact error texts.

Compatibility

  • Every v2 script keeps running. v3 is additive.
  • Eight TA builtins were corrected to their textbook forms (ema seeding, rsi Wilder smoothing, macd, psar, adx, highest/lowest, strict sma warmup). Early-bar values change for those functions; this is a correction, not a regression, since each now matches an independent reference.
  • A few v2 conveniences were tightened or renamed; the v2 vs v3 migration guide lists them.

The version marker

A v3 script starts with //@version=3 on its first line. //@version=2 works exactly the same — the engine routes both markers to the same modern engine, and every v3 capability above compiles and runs under either. //@version=3 is the canonical form (all of the platform's built-in scripts ship with it); existing //@version=2 scripts need no change.

What actually matters is that the marker is present and on the first line: no marker at all, or //@version=1, routes the script to the deprecated legacy v1 engine, where none of the v3 features exist. Unrecognized numbers (//@version=4 and up) are not valid markers; the engine logs a console warning ("Version 4 not supported, using v2") and treats them as modern, but do not rely on that coercion.

v3 marker
//@version=3
define(title="v3 marker", position="offchart", axis=true)

timeseries d = ohlcv(symbol=currentSymbol, exchange=currentExchange)
timeseries sma20 = sma(source=d.close, period=20)
plotLine(value=sma20, colors=["#2563eb"], width=2, label=["SMA 20"], desc=["v3 features under the v3 marker"])

If something does not behave as documented, the Common Errors page maps every real engine error message to its cause and fix.