MMTMMT Docs

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

ResourceMax
Inputs256
Subscriptions30
Plot labels64
Plot commands/bar64
Fill pairs/bar16
Alert definitions32
Alert fields/definition32
Entities per type2000
Retained history, Free non-BOOK10,000 bars
Retained history, Free BOOK5,000 bars
Retained history, Pro non-BOOK20,000 bars
Retained history, Pro BOOK10,000 bars

data.VOLUME follows the non-BOOK plan limit.

Context

FieldTypeDescription
context.timeframenumberChart timeframe (seconds)
context.exchangestringActive exchange
context.symbolstringActive symbol
context.tickSizenumberMin price increment
context.stepSizenumberMin quantity increment
context.isFirstboolBar index is 0
context.isHistoryboolHistory execution
context.isLastboolLast available bar
context.isNewboolUnix changed vs previous
context.isRealtimeboolNot history

Data Sources

subscribe(source)
subscribe(source, { exchange?: string, symbol?: string, timeframe?: number | string })
ConstantShapeKey Reads / Helpers
data.OHLCVCandle dataopen high low close volume buyVolume sellVolume buyCount sellCount unix
data.OIOpen interest candlesopen high low close unix
data.VDVolume delta candlesopen high low close unix; bucket support: bucket() bucketInfo()
data.CVDCumulative volume delta candlesopen high low close unix; bucket support: bucket() bucketInfo()
data.STATMarket statssellLiq buyLiq markPrice fundingRate unix
data.BOOKOrder book snapshottop of book: bestBid bestAsk mid; levels: askPrice bidPrice askSize bidSize; helpers: depth imbalance depthMany imbalanceMany; metadata: binSize numLevels numBids numAsks unix
data.VOLUMEVolume profile snapshotlevels: 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() and book.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 than 0
  • outerPct: finite percent distance from refPrice, >= 0
  • opts: { 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() and volume.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 | null
  • dataView() / 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 to 70
  • rebucket opts: exactly one of { bucketCount: number } or { levelsPerBucket: number }, with optional minPrice / maxPrice
  • out-of-range dataView(offset?) returns NaN scalar 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 }
BucketLabelRange
1allall trades
21-1K$1–$1K
31K-10K$1K–$10K
410K-25K$10K–$25K
525K-50K$25K–$50K
650K-100K$50K–$100K
7100K-250K$100K–$250K
8250K-500K$250K–$500K
9500K-1M$500K–$1M
101M-5M$1M–$5M
115M+$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.

FunctionSignatureExtra Options
input.int(name, value, opts?) -> numbermin?: number, max?: number
input.float(name, value, opts?) -> numbermin?: number, max?: number
input.bool(name, value, opts?) -> boolean
input.color(name, value, opts?) -> stringvalue: number (ABGR) or "#RRGGBB" / "#RRGGBBAA". Returns "#RRGGBBAA"
input.timeframe(name, value, opts?) -> numbervalue: seconds or string ("5m", "1h", "1D", "1W", "3M", "1Y"). Returns seconds
input.select(name, defaultIndex, opts) -> stringselectables: string[] (required)
input.exchange(name, default, opts?) -> stringfilter?: 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:

HelperDescription
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.

FunctionSignatureOptions
plot(label, value, opts?) -> seriesIndexcolor, width: number, style: linestyle.*, shaded: bool, showLabel: bool, showValue: bool, offset: number, forceOverlay: bool
plotHistogram(label, value, opts?) -> seriesIndexcolor, showLabel: bool, showValue: bool, offset: number, forceOverlay: bool
plotMarker(label, value, opts?) -> seriesIndexcolor, marker: shape.*, size: number, border: number, borderColor, offset: number, forceOverlay: bool
plotCandle(label, o, h, l, c, opts?) -> seriesIndexbodyColor, wickColor, borderColor, showLabel: bool, showValue: bool, offset: number, forceOverlay: bool
plotLabel(label, price, text, opts?) -> seriesIndexcolor, size: number, offset: number, forceOverlay: bool
plotCircle(label, price, opts?) -> seriesIndexcolor, size: number, offset: number, forceOverlay: bool
plotArrow(label, value, opts?) -> seriesIndexcolorUp, 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.

EntitySignatureOptions
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 throw invalid entity handle.
  • If the underlying entity was removed later, .set() / .clone() can throw entity does not exist.

entity.delete(handle) -> bool — global delete helper.

Technical Analysis — ta.*

Moving Averages

FunctionSignatureReturns
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

FunctionSignatureReturns
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

FunctionSignatureReturns
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

FunctionSignatureReturns
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

FunctionSignatureReturns
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

FunctionSignatureReturns
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

FunctionSignatureReturns
ta.cross(a: number, b: number)boolean
ta.crossover(a: number, b: number)boolean
ta.crossunder(a: number, b: number)boolean

Math — math.*

Rounding

FunctionSignature
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

FunctionSignature
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

FunctionSignature
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.*

FunctionSignature
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

FunctionSignature
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.*

FieldTypeDescription
timeframe.periodstringNormalized string ("1m", "4h", "1D")
timeframe.multipliernumberNumeric component
timeframe.isSecondsboolSecond charts
timeframe.isMinutesboolMinute charts
timeframe.isIntradayboolSecond, minute, or hour charts
timeframe.isDailyboolDaily charts
timeframe.isWeeklyboolWeekly charts
timeframe.isMonthlyboolMonthly charts
timeframe.isDwmboolDay/week/month charts
FunctionSignature
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

FunctionSignatureDescription
na-> NaNMissing value sentinel (bare or called)
na(v)-> booleanCheck if value is missing (undefined, null, NaN)
nz(value, replacement?: number) -> numberReplace na with fallback (default 0)
Series(name: string) -> number[]Mutable per-bar storage
log(...args)Write to script log panel
unix(offset?: number) -> numberBar unix timestamp (0=current, 1=previous)
barIndex() -> numberAbsolute index (0 = oldest loaded)
barCount() -> numberBars in first subscription
loadedBarCount() -> numberTotal loaded bars
lastBarIndex() -> numberLast bar index
isNewBar() -> booleanUnix changed vs previous evaluation
isRealtimeBar() -> boolean!isNewBar()
setDisplayName(name: string)Override indicator title in UI
getExchangeList() -> string[]Available exchange identifiers

Alerts

FunctionSignatureDescription
alert.define(key: string, opts?) -> AlertHandleDefine a top-level alert
AlertHandle.trigger(payload?) -> voidTrigger 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

NamespaceValues
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

On this page