# Sources (/scripting-v2/sources)



In Python V2, `history.source(...)` both declares a source and reads its history. There is no separate `--source`, TOML `[sources]`, or `script.sources` declaration.

## Selectors [#selectors]

```text
<symbol>@<source>@<exchange>
<symbol>@<source>@<exchange>@<provider>
```

Add source options after the final segment:

```python
candles = history.source("btc@candles@binancef:timeframe=60")
book = history.source("btc@orderbook@bulkf:depth=20", 0)
oi = history.source("btc@oi@hyperliquidf@mmt:timeframe=30")
```

Core futures use the base asset, such as `btc`. Hyperliquid HIP-3 uses `{dex}:{coin}`, such as `xyz:tsla`. Spot uses the exact pair, such as `hype/usdc`. Outcome symbols keep their side, such as `1009:0`.

```python
xyz_direct = history.source("xyz:tsla@candles@hyperliquidf:timeframe=60")
xyz_mmt = history.source("xyz:tsla@candles@hyperliquidf@mmt:timeframe=60")
```

Selector lookup is ASCII case-insensitive, so `XYZ:TSLA` and `xyz:tsla` resolve to the same declared feed. Market Lab keeps `hyperliquidf` as the canonical exchange and handles the provider-specific DEX route internally.

MMT selectors include both the indexed exchange and `mmt`:

```python
candles = history.source("btc@candles@binancef@mmt:timeframe=60")
trades = history.source("btc@trades@binancef@mmt")
```

Declare selectors with literal strings or module-level string constants. Market Lab inspects them before the job starts so every subscription is deterministic. F-strings and other dynamic lookups may read only feeds declared elsewhere in the script.

## Runtime access [#runtime-access]

With no index, `history.source` returns the retained list. Index `0` returns the newest record:

```python
CANDLES = "btc@candles@binancef:timeframe=60"


def on_data(ctx, history):
    records = history.source(CANDLES)
    latest = history.source(CANDLES, 0)
```

Source options are part of the declaration, but Market Lab exposes a normalized identity on `ctx`:

```python
ctx.source          # btc@candles@binancef
ctx.source_type     # candles
ctx.provider
ctx.exchange
ctx.symbol
ctx.source_configs
```

The market record remains in `history`; it is not copied onto `ctx`.

## Availability [#availability]

| Source      | Standalone Binance | MMT               | BULK              | Hyperliquid       |
| ----------- | ------------------ | ----------------- | ----------------- | ----------------- |
| `candles`   | Backtest           | Live and backtest | Live and backtest | Live and backtest |
| `orderbook` | No                 | Live and backtest | Live              | Live              |
| `trades`    | No                 | Live              | Live              | Live              |
| `vd`        | No                 | Live and backtest | Live              | Live              |
| `oi`        | No                 | Live and backtest | Live              | Live              |
| `volumes`   | Backtest           | Live and backtest | Live and backtest | Live and backtest |

Provider and market type determine whether a source is valid. Spot and outcome markets do not provide perpetual-only OI.

## Multiple sources [#multiple-sources]

Declare each feed where it is read:

```python
def on_data(ctx, history):
    btc = history.source("btc@candles@binancef:timeframe=900", 0)
    zec = history.source("zec@candles@binancef:timeframe=900", 0)
    if btc is None or zec is None:
        return
```

Then backtest without repeating the selectors:

```bash
mlab script backtest pairs.py \
  --from 2026-07-15 \
  --to 2026-07-16
```

Each selector gets an independent history buffer. See [History](/scripting-v2/history).

Source references:

* [Candles](/scripting-v2/sources/candles)
* [Orderbook](/scripting-v2/sources/orderbook)
* [Trades](/scripting-v2/sources/trades)
* [Volume Delta](/scripting-v2/sources/vd)
* [Open Interest](/scripting-v2/sources/oi)
* [Volumes](/scripting-v2/sources/volumes)
