MMTMMT Docs

Data and update events

Subscribe to chart data and run calculations when that data changes.

A subscription names a market-data series that the script can read. For a typical chart indicator, start with the chart's candles:

script "Chart data"

data chart = subscribe(data.ohlcv)

chart is the name chosen by this script; it is not a built-in global. The subscription exposes candle fields such as open, high, low, close, and volume. These values can be missing, so their type includes null.

Respond to updates

Put calculations in the subscription's update handler:

script "Candle range"

data chart = subscribe(data.ohlcv)

plot range = plot.histogram(title: "Candle Range")

on chart.update {
  let candleRange = (chart.high ?? 0.0) - (chart.low ?? 0.0)
  range.plot(candleRange)
}

The handler runs as the series advances. Keeping the calculation beside the subscription event makes it clear what causes the output to change.

Use multiple handlers

Scripting v3 is not limited to one main update handler. Each subscription can react to its own events, so fast chart rendering, higher-timeframe confirmation, and individual trades can stay in separate, focused blocks.

This complete script captures levels only when an hourly candle closes, then plots those saved levels whenever the chart advances:

script "Previous H1 High/Low"

data chart = subscribe(data.ohlcv)
data h1 = subscribe(data.ohlcv, timeframe: tf("1h"))

state (
  prevHigh: float? = null
  prevLow: float? = null
)

plot (
  highPlot = plot.line(title: "Prev H1 High", color: color.red, style: linestyle.step)
  lowPlot = plot.line(title: "Prev H1 Low", color: color.green, style: linestyle.step)
)

on h1.close {
  prevHigh = h1.high
  prevLow = h1.low
}

on chart.update {
  highPlot.plot(prevHigh)
  lowPlot.plot(prevLow)
}

The two handlers have different jobs:

  • on h1.close runs when an hourly candle is confirmed. It saves values that should not change afterward.
  • on chart.update runs as the chart subscription advances. It draws the latest saved hourly levels across the chart.

state connects the handlers. Values written by one handler remain available to later handler calls.

This separation is useful whenever data arrives at different rates. Instead of forcing everything through the chart handler, attach work to the event that should actually trigger it.

Period-based subscriptions support open, update, and close handlers. A trades subscription uses a trade handler for each print. A script can contain handlers for several subscriptions, and it can use different event handlers on the same subscription. There can be one handler for each subscription-and-event combination.

Order-book and volume-profile subscriptions do not have their own event handlers. Read their values inside a handler on another data source. Their timeframe: can differ from that handler's timeframe. Reads select completed source buckets as of the handler time; barsAgo: counts periods on the source's schedule. See volume profiles and order books on multiple timeframes.

If you are coming from Scripting v2, treat handlers as small entry points rather than rebuilding one large global handler. Keep shared results in state, and let each handler update only the part of the script it owns.

Trade history

A trades subscription runs its trade handler once for each print. While the script loads history, the handler runs for earlier trades in time order; after that it runs for every live trade. history: on a trades subscription takes a duration and decides how far back the earlier trades reach:

history:Trades the handler receives
omitted or nullTrades across the chart's loaded range, up to your plan's trade-history limit.
a positive duration, such as 2h or 1sTrades from that long before the current chart bar opened, then every live trade.
0s or time.seconds(0)Live trades only. Nothing earlier is loaded, and the handler runs only for trades that arrive after the script goes live, not for trades of the current bar from before that.
a negative constantCompile error S026. A negative value from an input means live trades only.
script "Live buy volume"

data chart = subscribe(data.ohlcv)
data tape = subscribe(data.trades, minSize: 10.0, history: 0s)

state liveBuyVolume = 0.0

plot buys = plot.line(title: "Live Buy Volume")

on tape.trade(tr) {
  if tr.isBuy {
    liveBuyVolume += tr.size
  }
}

on chart.update {
  buys.plot(liveBuyVolume)
}

What a positive history: guarantees:

  • The window is measured from the opening time of the chart bar that was forming when the script started, not from the current moment. history: 1s on a 15-minute chart started ten minutes into a bar therefore delivers the trades from one second before that bar opened, which is all ten minutes of the forming bar plus the last second of the bar before it, and then every live trade. When a subscription finer than the chart sets the replay timeframe (see multiple timeframes), the window is measured from the opening time of that finer bar instead.
  • There is no minimum. Any positive duration is accepted, down to 1s.
  • The maximum is your plan's trade-history limit: 7 days on Ultra. Free and Pro scripts receive trades from the live edge only. A longer request is shortened to the limit. The limit is the most a script can receive, not the amount it loads.
  • When every trades subscription on the same market in a script sets a positive history:, only the longest of those spans is loaded, rounded out to whole minutes, however long the chart's range is. If any trades subscription on that market omits history:, trades across the chart's range are loaded, and each subscription still receives only its own window.

history: on a trades subscription must be a duration; a bar count such as history: 0 or history: 500 is rejected. 0s is valid wherever a duration is expected, with any unit (0s, 0m, 0h, 0D), but a zero timeframe such as tf("0m") or timeframe: 0s is rejected with T112.

Trades are stored once per market and shared by every script that reads them, so the stored trades for a market can reach further back than your script asked for. In the script's metrics window, the Series table's delivered column counts the trades your script received for a subscription, which is what its trade handler ran on. The store rows column counts the shared trades for that market, which other scripts may have loaded further back; it is not what your script received and is not charged to its memory limit. The history column shows the history: each subscription asked for.

Live-only subscriptions

Any subscription that accepts history: can ask for live data only with a zero: history: 0s, history: 0 for sources that count bars, or time.seconds(0). A live-only subscription is useful as a realtime clock or a live filter that has no meaning over history.

script "Live one-minute clock"

data chart = subscribe(data.ohlcv)
data minute = subscribe(data.ohlcv, timeframe: tf("1m"), history: 0s)

state liveMinutes = 0

plot count = plot.line(title: "Live Minutes")

on minute.close {
  liveMinutes += 1
}

on chart.update {
  count.plot(liveMinutes)
}

For candles, open interest, volume delta, CVD, and stats:

  • Nothing is loaded before the script goes live, and the subscription does not change how history is replayed. A 15-minute chart with a live-only one-second subscription replays its history on the 15-minute bars exactly as it does without it.
  • Its handlers do not run during history, and reading it during history returns null.
  • Once the script is live, it behaves like any other subscription on its own timeframe. Its first bar is its newest bar when the script went live, and its handlers run on its updates from then on.
  • A live-only subscription finer than the chart adds its updates to the chart's live history, so after running for a while the chart keeps fewer of its own live bars.
  • The chart's own subscription, subscribe(data.ohlcv) with no exchange, symbol, or timeframe, cannot be live only (S027).
  • A live-only subscription with the same market and timeframe as one the script needs for a .forming read of a longer timeframe loads history like a normal subscription.

For order books and volume profiles, a live-only subscription loads no earlier snapshots; reads return snapshots from the period the script started in onward.

Read earlier values

Use brackets to read history:

script "Previous close"

data chart = subscribe(data.ohlcv)

plot previous = plot.line(title: "Previous Close")

on chart.update {
  let previousClose = chart.close[1]
  previous.plot(previousClose)
}

Earlier values can be null, especially near the beginning of the chart. Preserve that absence when it matters rather than silently inventing a value.

For live candles and close-confirmed signals, continue with repainting and confirmed values.

On this page