Quick Reference
Condensed API reference for MMT Scripting v2.
Every v2 script starts with //@version=2. Top-level code runs once; onBar(index) runs per bar. index is distance from newest bar (0 = newest).
//@version=2
indicator("My Script", overlay?)
const len = input.int("Length", 20)
const candles = subscribe(data.OHLCV)
function onBar(index) { plot("SMA", ta.sma(candles.close(), len)) }Top-level only: indicator(), subscribe(), subscription.calc(), input.*(), alert.define()
Limits
| Resource | Max |
|---|---|
| Inputs | 256 |
| Subscriptions | 30 |
| Plot labels | 64 |
| Plot commands/bar | 64 |
| Fill pairs/bar | 16 |
| Alert definitions | 32 |
| Alert fields/definition | 32 |
| Entities per type | 2000 |
Retained history, Free non-BOOK | 10,000 bars |
Retained history, Free BOOK | 5,000 bars |
Retained history, Pro non-BOOK | 20,000 bars |
Retained history, Pro BOOK | 10,000 bars |
data.VOLUME follows the non-BOOK plan limit.
Context
| Field | Type | Description |
|---|---|---|
context.timeframe | number | Chart timeframe (seconds) |
context.exchange | string | Active exchange |
context.symbol | string | Active symbol |
context.tickSize | number | Min price increment |
context.stepSize | number | Min quantity increment |
context.isFirst | bool | Bar index is 0 |
context.isHistory | bool | History execution |
context.isLast | bool | Last available bar |
context.isNew | bool | Unix changed vs previous |
context.isRealtime | bool | Not history |
Data Sources
subscribe(source)
subscribe(source, { exchange?: string, symbol?: string, timeframe?: number | string })| Constant | Shape | Key Reads / Helpers |
|---|---|---|
data.OHLCV | Candle data | open high low close volume buyVolume sellVolume buyCount sellCount unix |
data.OI | Open interest candles | open high low close unix |
data.VD | Volume delta candles | open high low close unix; bucket support: bucket() bucketInfo() |
data.CVD | Cumulative volume delta candles | open high low close unix; bucket support: bucket() bucketInfo() |
data.STAT | Market stats | sellLiq buyLiq markPrice fundingRate unix |
data.BOOK | Order book snapshot | top of book: bestBid bestAsk mid; levels: askPrice bidPrice askSize bidSize; helpers: depth imbalance depthMany imbalanceMany; metadata: binSize numLevels numBids numAsks unix |
data.VOLUME | Volume profile snapshot | levels: price buyVol sellVol; structured snapshot: dataView data; helpers: summary range rangeMany poc valueArea rebucket; metadata: binSize numLevels unix |
All accessors accept an optional offset, where 0 is current and 1 is previous.
BOOK at a Glance
- Snapshot metadata:
book.unix(),book.binSize(),book.numLevels(),book.numBids(),book.numAsks() - Top of book:
book.bestBid(),book.bestAsk(),book.mid() - Per-level reads:
book.askPrice(level),book.bidPrice(level),book.askSize(level),book.bidSize(level) - Native helpers:
book.depth(...),book.imbalance(...),book.depthMany(...),book.imbalanceMany(...) book.data()andbook.dataView()are removed for BOOK and now throw a migration error
book.depth(refPrice, outerPct, opts?, offset?) -> BookDepth | null
book.imbalance(refPrice, outerPct, opts?, offset?) -> BookImbalance | null
book.depthMany(refPrice, requests, offset?) -> Array<BookDepth | null>
book.imbalanceMany(refPrice, requests, offset?) -> Array<BookImbalance | null>refPrice: finite number greater than0outerPct: finite percent distance fromrefPrice,>= 0opts:{ innerPct?: number, mode?: "cumulative" | "differential", unit?: "quote" | "base", distanceDecay?: number }requests[i]:{ outerPct: number, opts?: { innerPct?: number, mode?: "cumulative" | "differential", unit?: "quote" | "base", distanceDecay?: number } }
BookDepth = {
unix, binSize, bidValue, askValue, bidQty, askQty, bidLevels, askLevels
}
BookImbalance = {
unix, bid, ask, ratio
}VOLUME at a Glance
- Snapshot metadata:
volume.unix(),volume.binSize(),volume.numLevels() - Per-level reads:
volume.price(level),volume.buyVol(level),volume.sellVol(level) - Structured snapshot access:
volume.dataView()andvolume.data() - Native helpers:
volume.summary(...),volume.range(...),volume.rangeMany(...),volume.poc(...),volume.valueArea(...),volume.rebucket(...) - Native helpers can return
null; guard before reading fields such as.price,.lowPrice, or.totals
volume.dataView(offset?) -> VolumeDataView
volume.data(offset?) -> VolumeDataView
volume.summary(offset?) -> VolumeSummary | null
volume.range(minPrice, maxPrice, offset?) -> VolumeRange | null
volume.rangeMany(requests, offset?) -> Array<VolumeRange | null>
volume.poc(offset?) -> VolumePoc | null
volume.poc(opts, offset?) -> VolumePoc | null
volume.valueArea() -> VolumeValueArea | null
volume.valueArea(percent, offset?) -> VolumeValueArea | null
volume.valueArea(opts, offset?) -> VolumeValueArea | null
volume.valueArea(percent, opts, offset?) -> VolumeValueArea | null
volume.rebucket(opts, offset?) -> VolumeBuckets | nulldataView()/data()result:{ unix, binSize, prices: Float64Array, buys: Float64Array, sells: Float64Array }rangeMany requests[i]:{ minPrice: number, maxPrice: number }poc/valueArea opts:{ minPrice?: number, maxPrice?: number, basis?: "total" | "buy" | "sell" | "deltaAbs" }valueArea percent: defaults to70rebucket opts: exactly one of{ bucketCount: number }or{ levelsPerBucket: number }, with optionalminPrice/maxPrice- out-of-range
dataView(offset?)returnsNaNscalar fields with empty typed arrays
VolumeSummary = {
unix, binSize, levels, minPrice, maxPrice, buy, sell, total, delta, ratio
}
VolumeRange = {
unix, binSize, levels, fromPrice, toPrice, buy, sell, total, delta, ratio
}
VolumePoc = {
unix, price, index, buy, sell, total, delta, ratio
}
VolumeValueArea = {
unix, pocPrice, lowPrice, highPrice, levels, buy, sell, total, delta, ratio
}
VolumeBuckets = {
unix, sourceBinSize, prices, buys, sells, totals, deltas
}VD / CVD Buckets
data.VD and data.CVD support trade-size buckets via subscribe(source, { bucket: number }).
const vd = subscribe(data.VD, { bucket: 10 })
const cvd = subscribe(data.CVD, { bucket: 3 })Extra accessors on bucketed subscriptions:
vd.bucket() -> number
vd.bucketInfo() -> { id: number, label: string, minValue: number, maxValue: number }| Bucket | Label | Range |
|---|---|---|
1 | all | all trades |
2 | 1-1K | $1–$1K |
3 | 1K-10K | $1K–$10K |
4 | 10K-25K | $10K–$25K |
5 | 25K-50K | $25K–$50K |
6 | 50K-100K | $50K–$100K |
7 | 100K-250K | $100K–$250K |
8 | 250K-500K | $250K–$500K |
9 | 500K-1M | $500K–$1M |
10 | 1M-5M | $1M–$5M |
11 | 5M+ | $5M+ |
Both support aggregated exchanges: subscribe(data.VD, { exchange: agg("bybitf", "binancef"), bucket: 3 }).
See Data Sources for full signatures, argument types, result shapes, and working examples.
HTF offsets use that subscription's clock, not the chart clock.
Higher Timeframes & calc
const htf = subscribe(data.OHLCV, { timeframe: "1h" })
const htfEma = htf.calc(src => ta.ema(src.close(), 20)) // top-level only
function onBar(index) { plot("EMA", htfEma()) }calc runs on the subscription's clock. Callbacks may return a number or a flat object snapshot. No plotting, entities, logging, inputs, or subscribe allowed inside calc callbacks.
Inputs
All input functions accept shared options: key?: string, description?: string, sameLine?: bool, required?: bool, onlyIf?: condition.
| Function | Signature | Extra Options |
|---|---|---|
input.int | (name, value, opts?) -> number | min?: number, max?: number |
input.float | (name, value, opts?) -> number | min?: number, max?: number |
input.bool | (name, value, opts?) -> boolean | — |
input.color | (name, value, opts?) -> string | value: number (ABGR) or "#RRGGBB" / "#RRGGBBAA". Returns "#RRGGBBAA" |
input.timeframe | (name, value, opts?) -> number | value: seconds or string ("5m", "1h", "1D", "1W", "3M", "1Y"). Returns seconds |
input.select | (name, defaultIndex, opts) -> string | selectables: string[] (required) |
input.exchange | (name, default, opts?) -> string | filter?: string[] ("futures", "inverse", "spot") |
input.exchanges | (name, defaults, opts?) -> string[] | filter?: string[] ("futures", "inverse", "spot"), minSelected?: number, maxSelected?: number |
input.section | (name, opts?) | — |
input.tab | (name, opts?) | key?: string only |
input.group | (name, opts?) | key?: string, collapsible?: bool, collapsed?: bool |
Conditional visibility — use in onlyIf option:
| Helper | Description |
|---|---|
input.when(key, value) | Equality (or membership if array) |
input.whenEq(key, value) | Strict equality |
input.whenNeq(key, value) | Not equal |
input.whenIn(key, values) | Value in array |
input.whenTrue(key) | Boolean true |
input.whenFalse(key) | Boolean false |
Plotting
All plot functions accept forceOverlay?: bool to render on the price chart regardless of indicator overlay setting.
| Function | Signature | Options |
|---|---|---|
plot | (label, value, opts?) -> seriesIndex | color, width: number, style: linestyle.*, shaded: bool, showLabel: bool, showValue: bool, offset: number, forceOverlay: bool |
plotHistogram | (label, value, opts?) -> seriesIndex | color, showLabel: bool, showValue: bool, offset: number, forceOverlay: bool |
plotMarker | (label, value, opts?) -> seriesIndex | color, marker: shape.*, size: number, border: number, borderColor, offset: number, forceOverlay: bool |
plotCandle | (label, o, h, l, c, opts?) -> seriesIndex | bodyColor, wickColor, borderColor, showLabel: bool, showValue: bool, offset: number, forceOverlay: bool |
plotLabel | (label, price, text, opts?) -> seriesIndex | color, size: number, offset: number, forceOverlay: bool |
plotCircle | (label, price, opts?) -> seriesIndex | color, size: number, offset: number, forceOverlay: bool |
plotArrow | (label, value, opts?) -> seriesIndex | colorUp, colorDown, minHeight: number, maxHeight: number, paddingPx: number, shaftWidth: number, headLengthPx: number, headWidthRatio: number, showLabel: bool, showValue: bool, offset: number, forceOverlay: bool |
bg | (label, opts) | color, offset: number, forceOverlay: bool |
fill | (s1, s2, opts?) | Solid: color. Gradient: topValue, bottomValue, topColor, bottomColor (all four required) |
Skipping a plot call on a bar = na. Declare all labels during history warmup. bg/fill are effect-style — no na gap when skipped.
Entities
All require //@version=2 and a unique key string per type per bar. All return a handle with .set(opts?), .clone(newKey), .delete(). All accept forceOverlay?: bool.
| Entity | Signature | Options |
|---|---|---|
Line | (key, opts?) | x1, y1, x2, y2: number, width: number, color, style: linestyle.*, label: string, zIndex: number |
Box | (key, opts?) | x1, y1, x2, y2: number, color, rounding: number, borderColor, borderWidth: number, borderStyle: linestyle.*, extend: extend.* | extend.*[], text: string, textSize: size.*, textColor, textHAlign: text.align.*, textVAlign: text.align.*, textWrap: text.wrap.*, zIndex: number |
Label | (key, opts?) | x, y: number, text: string, color, size: number, textAlign: text.align.*, zIndex: number |
Marker | (key, opts?) | x, y: number, shape: shape.*, size: number, color, zIndex: number. Use marker key (not shape) in .set() |
Arrow | (key, opts?) | x1, y1, x2, y2: number, width: number, color, style: linestyle.*, headLengthPx: number, headWidthRatio: number, zIndex: number |
LineFill | (key, lineA, lineB, opts?) | color |
Key identity is (type, key, bar unix). Recreate or upsert keyed entities inside onBar on every bar where they should exist, and use the handle returned for that bar. If you keep a handle variable, overwrite it with the latest constructor result before calling .set().
Stale-handle behavior:
- After
.delete(), the handle becomes invalid and later.set()/.clone()/.delete()calls throwinvalid entity handle. - If the underlying entity was removed later,
.set()/.clone()can throwentity does not exist.
entity.delete(handle) -> bool — global delete helper.
Technical Analysis — ta.*
Moving Averages
| Function | Signature | Returns |
|---|---|---|
ta.sma | (value: number, period: number) | number |
ta.ema | (value: number, period: number) | number |
ta.rma | (value: number, period: number) | number |
ta.wma | (value: number, period: number) | number |
ta.vwma | (price: number, volume: number, period: number) | number |
ta.hma | (value: number, length: number) | number |
ta.alma | (value: number, length: number, offset: number, sigma: number, floor?: bool) | number |
ta.swma | (value: number) | number |
ta.linreg | (value: number, period: number, offset?: number) | number |
Oscillators
| Function | Signature | Returns |
|---|---|---|
ta.rsi | (value: number, period: number) | number |
ta.stoch | (source: number, high: number, low: number, length: number) | number |
ta.macd | (value: number, fast?: number, slow?: number, signal?: number) | { macd, signal, hist } |
ta.dmi | (ohlcvAccessor, diLength: number, adxSmoothing: number) | { plusDI, minusDI, adx } with NaN fields when unavailable |
Volume-Based
| Function | Signature | Returns |
|---|---|---|
ta.accdist | (ohlcvAccessor) | number |
ta.mfi | (source: number, volume: number, length: number) | number |
ta.vwap | (source: number, volume: number, anchor?: bool, stdevMult?: number) | number or { vwap, upper, lower } with NaN fields when unavailable |
Volatility
| Function | Signature | Returns |
|---|---|---|
ta.atr | (high: number, low: number, close: number, period: number) | number |
ta.stdev | (value: number, period: number) | number |
ta.bb | (value: number, length: number, mult: number) | { basis, upper, lower } with NaN fields when unavailable |
Aggregation
| Function | Signature | Returns |
|---|---|---|
ta.highest | (value: number, period: number) | number |
ta.lowest | (value: number, period: number) | number |
ta.max | (value: number) | number |
ta.median | (value: number, length: number) | number |
ta.mode | (value: number, length: number) | number |
ta.dev | (value: number, length: number) | number |
ta.pivothigh | (source: number, leftbars: number, rightbars: number) | number |
ta.pivotlow | (source: number, leftbars: number, rightbars: number) | number |
Change Detection
| Function | Signature | Returns |
|---|---|---|
ta.change | (value: number, period?: number) | number |
ta.barssince | (condition: boolean) | number |
ta.cum | (value: number) | number |
ta.roc | (value: number, period: number) | number |
ta.rising | (value: number, length: number) | boolean |
ta.falling | (value: number, length: number) | boolean |
Crossovers
| Function | Signature | Returns |
|---|---|---|
ta.cross | (a: number, b: number) | boolean |
ta.crossover | (a: number, b: number) | boolean |
ta.crossunder | (a: number, b: number) | boolean |
Math — math.*
Rounding
| Function | Signature |
|---|---|
math.round | (value: number, precision?: number) -> number |
math.roundToTick | (value: number, tickSize?: number) -> number — defaults to context.tickSize |
math.roundToTickUp | (value: number, tickSize?: number) -> number |
math.roundToTickDown | (value: number, tickSize?: number) -> number |
math.roundToStep | (value: number, stepSize?: number) -> number — defaults to context.stepSize |
math.roundToStepUp | (value: number, stepSize?: number) -> number |
math.roundToStepDown | (value: number, stepSize?: number) -> number |
Aggregation & Clamping
| Function | Signature |
|---|---|
math.sum | (source: number, length: number) -> number |
math.avg | (...values) -> number — variadic or single array |
math.min | (...values) -> number — variadic or single array |
math.max | (...values) -> number — variadic or single array |
math.clamp | (value: number, min: number, max: number) -> number |
Basic & Logarithms
math.abs(v) math.floor(v) math.ceil(v) math.trunc(v) math.pow(base,exp) math.sqrt(v) math.exp(v) math.sign(v) math.log10(v) math.log2(v) math.logN(v,base)
Utilities
| Function | Signature |
|---|---|
math.isFinite | (value) -> boolean |
math.safeDiv | (numerator: number, denominator: number, fallback?: number) -> number — returns fallback on div by zero |
math.random | () -> [0,1) / (max) -> [0,max) / (min, max) -> [min,max) |
Strings — str.*
| Function | Signature |
|---|---|
str.toString | (value, format?: string) -> string — formats: "mintick", "percent", "volume", or pattern like "#.00" |
str.format | (template: string, ...args) -> string — placeholders: {0}, {1,number,#.##} |
str.length | (source: string) -> number |
str.includes | (source: string, sub: string) -> boolean |
str.startsWith | (source: string, sub: string) -> boolean |
str.endsWith | (source: string, sub: string) -> boolean |
str.indexOf | (source: string, sub: string, occurrence?: number) -> number — returns -1 if not found |
str.split | (source: string, separator: string) -> string[] |
str.replace | (source: string, target: string, replacement: string, occurrence?: number) -> string |
str.replaceAll | (source: string, target: string, replacement: string) -> string |
str.substring | (source: string, begin: number, end?: number) -> string |
str.toUpperCase | (source: string) -> string |
str.toLowerCase | (source: string) -> string |
str.trim | (source: string) -> string |
str.repeat | (source: string, count: number, separator?: string) -> string |
Colors — color.*
Constants: up down red green blue yellow teal magenta white orange black pink purple brown gray transparent cyan indigo silver emerald amber lightskyblue maroon navy olive
Gradient aliases: color.mode.clamp color.mode.wrap color.mode.mirror color.space.rgb color.space.hsl color.easing.linear color.easing.smoothstep color.easing.smootherstep
Examples: Colors & Gradients
| Function | Signature |
|---|---|
color.rgb | (r: number, g: number, b: number, a?: number) -> string |
color.r | (color) -> number |
color.g | (color) -> number |
color.b | (color) -> number |
color.t | (color) -> number |
color.transp | (color, percentage: number) -> string — 0 = opaque, 100 = invisible |
color.fromGradient | (value: number, bottomValue: number, topValue: number, bottomColor, topColor) -> string |
color.grade | (value: number, stops: array, options?: object) -> string — multi-stop grading with mode, space, and easing options; prefer color.mode.*, color.space.*, and color.easing.* |
color.scale | (stops: array, options?: object) -> { sample(value: number): string } — reusable multi-stop scale with the same options as color.grade; prefer the same aliases |
color.lighten | (color, percent: number) -> string |
color.darken | (color, percent: number) -> string |
Colors accept hex strings (#RRGGBB / #RRGGBBAA) or ABGR numbers. Functions return hex strings.
color.up / color.down are based on your terminals theme up and down colors.
Timeframes — timeframe.*
| Field | Type | Description |
|---|---|---|
timeframe.period | string | Normalized string ("1m", "4h", "1D") |
timeframe.multiplier | number | Numeric component |
timeframe.isSeconds | bool | Second charts |
timeframe.isMinutes | bool | Minute charts |
timeframe.isIntraday | bool | Second, minute, or hour charts |
timeframe.isDaily | bool | Daily charts |
timeframe.isWeekly | bool | Weekly charts |
timeframe.isMonthly | bool | Monthly charts |
timeframe.isDwm | bool | Day/week/month charts |
| Function | Signature |
|---|---|
timeframe.change | (tf: number | string) -> boolean — true on first bar of new period |
timeframe.fromSeconds | (tf?: number | string) -> string — normalized canonical timeframe string |
timeframe.inSeconds | (tf?: number | string) -> number — defaults to chart timeframe |
Timeframe strings: 10s, 30m, 4h, 1D, 2W, 3M, 1Y.
Globals
| Function | Signature | Description |
|---|---|---|
na | -> NaN | Missing value sentinel (bare or called) |
na(v) | -> boolean | Check if value is missing (undefined, null, NaN) |
nz | (value, replacement?: number) -> number | Replace na with fallback (default 0) |
Series | (name: string) -> number[] | Mutable per-bar storage |
log | (...args) | Write to script log panel |
unix | (offset?: number) -> number | Bar unix timestamp (0=current, 1=previous) |
barIndex | () -> number | Absolute index (0 = oldest loaded) |
barCount | () -> number | Bars in first subscription |
loadedBarCount | () -> number | Total loaded bars |
lastBarIndex | () -> number | Last bar index |
isNewBar | () -> boolean | Unix changed vs previous evaluation |
isRealtimeBar | () -> boolean | !isNewBar() |
setDisplayName | (name: string) | Override indicator title in UI |
getExchangeList | () -> string[] | Available exchange identifiers |
Alerts
| Function | Signature | Description |
|---|---|---|
alert.define | (key: string, opts?) -> AlertHandle | Define a top-level alert |
AlertHandle.trigger | (payload?) -> void | Trigger the selected realtime alert |
const crossUp = alert.define("ema.cross.up", {
title: "EMA Cross Up",
fields: [
{ key: "price", type: "number" },
{ key: "fast", type: "number" },
{ key: "slow", type: "number" }
]
})
crossUp.trigger({ price: close, fast, slow })See Alerts for trigger mode behavior and payload validation.
Drawing Constants
| Namespace | Values |
|---|---|
shape.* | circle square diamond up down left right cross plus asterisk |
linestyle.* | solid dotted dashed arrow.right arrow.left arrow.both |
size.* | auto=0 xxs=8 xs=10 sm=12 md=14 lg=16 xl=20 xxl=24 xxxl=32 xxxxl=40 |
extend.* | none left right up down x y |
text.align.* | left center right top bottom |
text.wrap.* | none auto |