# Candles (/scripting/sources/candles)



The `candles` source provides OHLCVT records. The `timeframe` value is an integer number of seconds.

```bash
--source btc@candles@binance:timeframe=60
--source btc@candles@binancef:timeframe=60
--source btc@candles@binancef@mmt:timeframe=60
--source btc@candles@bulkf:timeframe=5
--source btc@candles@hyperliquidf:timeframe=5
```

## Live Candles [#live-candles]

During live execution, Market Lab subscribes to raw trades for MMT, BULK, and Hyperliquid and derives candles locally. It does not depend on a provider's prebuilt candle stream.

This makes any positive whole-second interval valid during live execution:

```bash
--source btc@candles@binancef@mmt:timeframe=1
--source btc@candles@hyperliquidf@mmt:timeframe=5
--source btc@candles@bulkf:timeframe=30
--source btc@candles@hyperliquidf:timeframe=2
```

Trade timestamps are placed into Unix/epoch-aligned buckets. For a 60-second timeframe, boundaries are `12:07:00`, `12:08:00`, `12:09:00`, and so on. For 30 seconds, boundaries include `12:07:00`, `12:07:30`, and `12:08:00`.

Only completed candles are sent to the script. Market Lab does not expose an in-progress candle that changes on every trade.

## Startup Alignment [#startup-alignment]

The interval containing the script's startup time is discarded unless the script starts exactly on a boundary. This prevents a partial first bar from being treated like a full bar.

For example:

* Start at `12:07:39` with `timeframe=60`: trades before `12:08:00` are ignored. The first clean bucket is `12:08:00`–`12:09:00`, and its candle becomes available after that bucket closes.
* Start at `12:07:14` with `timeframe=30`: trades before `12:07:30` are ignored. The first clean bucket is `12:07:30`–`12:08:00`.
* Start exactly at `12:08:00` with `timeframe=60`: that boundary begins the first accepted bucket immediately.

A completed candle is emitted when a trade from a later bucket arrives. If a period has no trades, Market Lab does not invent an empty candle or carry a previous close forward. A quiet market can therefore delay emission until the next trade, and gaps can exist between candle timestamps.

## Live OHLCVT Construction [#live-ohlcvt-construction]

For each accepted trade:

* `o` is the first trade price in the bucket.
* `h` and `l` are the highest and lowest trade prices.
* `c` is the final trade price.
* `vb` and `vs` sum buy and sell base quantities.
* `tb` and `ts` count buy and sell trades.
* `volume` is `vb + vs`.
* `trades` is `tb + ts`.
* `t` is the bucket start in Unix milliseconds.
* `close_time` is the exclusive bucket end in Unix milliseconds.

The raw trade side supplied by the provider determines buy versus sell. Late trades older than the active bucket are ignored; batched trades are sorted by timestamp before aggregation.

## Backtest Candles [#backtest-candles]

Backtests load stored provider candles rather than reconstructing them from live trades. They cannot request an arbitrary second interval.

A stored candle enters the replay only at its closing boundary, after its final OHLC values would have been known.

Standalone Binance Spot and USD-M perpetual candles use provider-supported historical intervals. They are available in backtests only; standalone Binance does not currently provide live script streams.

MMT historical candle intervals:

```txt
60, 300, 900, 1800, 3600, 14400, 86400 seconds
```

The minimum MMT backtest interval is 60 seconds.

BULK historical candle intervals:

```txt
10, 60, 180, 300, 900, 1800, 3600, 7200, 14400,
21600, 28800, 43200, 86400, 259200, 604800, 2592000 seconds
```

The minimum BULK backtest interval is 10 seconds.

Hyperliquid historical candle intervals:

```txt
60, 180, 300, 900, 1800, 3600, 7200, 14400,
28800, 43200, 86400, 259200, 604800, 2592000 seconds
```

The minimum Hyperliquid backtest interval is 60 seconds.

Historical volume fields retain the semantics available from the provider. In particular, older BULK candles do not invent directional `vb`, `vs`, `tb`, or `ts` values when the stored candle does not contain them.

## Commands [#commands]

Live MMT candles:

```bash
mlab script run ./scripts/candle-script.js \
  --source btc@candles@binancef@mmt:timeframe=5
```

Live BULK candles:

```bash
mlab script run ./scripts/candle-script.js \
  --source btc@candles@bulkf:timeframe=5
```

Live Hyperliquid candles:

```bash
mlab script run ./scripts/candle-script.js \
  --source btc@candles@hyperliquidf:timeframe=5
```

MMT backtest:

```bash
mlab script backtest ./scripts/candle-script.js \
  --from "2026-06-01 09:52:39" \
  --to "2026-06-03 21:54:05" \
  --source btc@candles@binancef@mmt:timeframe=60
```

Standalone Binance USD-M backtest:

```bash
mlab script backtest ./scripts/candle-script.js \
  --from "2026-06-01 09:52:39" \
  --to "2026-06-03 21:54:05" \
  --source btc@candles@binancef:timeframe=60
```

Use `btc@candles@binance` for Binance Spot. No Binance API key is required.

Read the data in live execution and backtests with exact selectors:

```js
const binanceDirect = history.source('btc@candles@binancef')
const candles = history.source('btc@candles@binancef@mmt')
const current = history.source('btc@candles@binancef@mmt', 0)
const previous = history.source('btc@candles@binancef@mmt', 1)
```

See [Candles Data Type](/scripting/data-types/candles) for the normalized shape.
