---
title: "What's New in v3"
description: "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."
---

# What's New in v3

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](#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"`).

```javascript title="MA cross strategy" lines wrap
//@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](../strategies/first-strategy.md), then
the [Strategies section](../strategies/overview.md) 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.

```javascript title="Struct with a method" lines wrap
//@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](/kscript/core-concepts/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](/kscript/functions/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](/kscript/core-concepts/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](/kscript/core-concepts/multi-source) and
[Data Sources](/kscript/core-concepts/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](/kscript/functions/ta-library) and
[Named Streams](/kscript/core-concepts/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](/kscript/functions/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](/kscript/functions/foreign-panes) for the full
reference and the [Historical Returns Matrix](/kscript/cookbook/historical-returns)
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](/kscript/core-concepts/data-sources) and the
[Whale vs Retail CVD](/kscript/cookbook/tape-reader) 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](/kscript/strategies/perps).

### 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](/kscript/functions/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](/kscript/releases/changelog) 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](/kscript/migrations/v2-vs-v3) 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.

```javascript title="v3 marker" lines wrap
//@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](/kscript/faq/common-errors) page maps every real engine error
message to its cause and fix.
