Python API Reference
Complete reference for the Python V2 manifest, hooks, context, history, and outputs.
This page is the compact contract for a Python V2 script. The other Scripting V2 pages explain how and when to use each part.
Complete example
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
CANDLES = "btc@candles@hyperliquidf@mmt:timeframe=60"
SOURCE = "btc@candles@hyperliquidf@mmt"
script = {
"name": "python-sma-crossover",
"version": "2",
"description": "Open and close a long position on SMA crossovers",
"lookback": 100,
"params": {
"fast_period": {"type": "number", "default": 5},
"slow_period": {"type": "number", "default": 20},
"margin": {"type": "number", "default": 100},
},
}
def on_data(ctx, history):
if ctx.source != SOURCE:
return
candles = history.source(CANDLES)
slow_period = int(ctx.params["slow_period"])
if len(candles) < slow_period + 1:
return
closes = pd.Series([candle["c"] for candle in candles], dtype=float)
fast = closes.rolling(int(ctx.params["fast_period"])).mean()
slow = closes.rolling(slow_period).mean()
previous = np.sign(fast.iloc[-2] - slow.iloc[-2])
current = np.sign(fast.iloc[-1] - slow.iloc[-1])
position = next(
(item for item in ctx.positions().open if item["symbol"] == "BTC"),
None,
)
if previous <= 0 and current > 0 and position is None:
ctx.trade(
{
"exchange": "hyperliquidf",
"symbol": "BTC",
"position": "open-long",
"margin": ctx.params["margin"],
"order": {"type": "market"},
}
)
elif previous >= 0 and current < 0 and position is not None:
ctx.trade(
{
"exchange": "hyperliquidf",
"symbol": "BTC",
"position": "close-long",
"order": {"type": "market"},
}
)
return {
"metrics": {
"fast_sma": fast.iloc[-1],
"slow_sma": slow.iloc[-1],
}
}
def on_execution(ctx):
print(ctx.execution)
def on_finish(ctx, history):
frame = pd.DataFrame(history.source(CANDLES))
if frame.empty:
return
frame["fast_sma"] = frame["c"].rolling(
int(ctx.params["fast_period"])
).mean()
frame["slow_sma"] = frame["c"].rolling(
int(ctx.params["slow_period"])
).mean()
axis = frame.plot(x="t", y=["c", "fast_sma", "slow_sma"])
chart = ctx.artifact_path("sma-crossover.png")
axis.figure.savefig(chart, dpi=160, bbox_inches="tight")
plt.close(axis.figure)
return {"meta": {"chart": chart}}The history.source(CANDLES) call declares the source. Run it without repeating that selector:
mlab script backtest sma-crossover.py \
--from 2026-07-15 \
--to 2026-07-16Manifest
script: dict| Field | Type | Required | Rules |
|---|---|---|---|
name | str | Yes | Non-empty script name. |
version | str | Yes | Must be "2". |
description | str | No | Metadata only. |
lookback | int | No | Per-selector retention; effective range 2–5,000, default 5,000. |
params | dict | No | Flat parameter definitions. |
Parameter definitions:
"name": {
"type": "number" | "boolean" | "string",
"required": True | False,
"default": value,
}required defaults to False. A required parameter cannot also define a default.
Required hook
def on_data(ctx, history):
...Called once for every accepted source event. The triggering record is added to history before invocation.
Optional hooks
def on_execution(ctx):
...
def on_finish(ctx, history):
...on_execution reads the current live or simulated execution lifecycle update through ctx.execution. on_finish performs final calculations and artifacts but cannot submit execution commands.
Context
Source and runtime values
During on_data, the context exposes:
| Value | Meaning |
|---|---|
ctx.source | Exact normalized selector that triggered the hook. |
ctx.source_type | Base kind such as candles, orderbook, or trades. |
ctx.provider | Standalone provider or mmt. |
ctx.exchange | Normalized source exchange. |
ctx.symbol | Normalized source symbol. |
ctx.source_configs | Parsed settings for configured selectors. |
ctx.positions(account="main").open | Open positions for the main or a named execution account. |
ctx.pnl(...) | Current run's PnL history. Also available during on_finish. |
ctx.study | Native study helpers shared with Scripting V1. |
During on_execution, the current lifecycle payload is available as ctx.execution.
ctx.params
ctx.params: dictContains validated manifest defaults and runtime --param overrides.
ctx.pnl(index=None)
Read the current run's PnL history:
points = ctx.pnl()
latest = ctx.pnl(0)
previous = ctx.pnl(1)
missing = ctx.pnl(99)Without an index, ctx.pnl() returns points in chronological order:
[
{"t": 1786992000000, "pnl": 0.0},
{"t": 1786992060000, "pnl": -2.14},
{"t": 1786992120000, "pnl": 3.56},
]With an index, 0 is the latest point, 1 is the previous point, and an unavailable index returns None. Each point contains a millisecond timestamp in t and the PnL value in pnl.
ctx.pnl(...) is available during on_data and on_finish. For example:
def on_data(ctx, history):
latest = ctx.pnl(0)
if latest is not None and latest["pnl"] <= -100:
returnctx.study
Python V2 exposes the same native study helpers as Scripting V1:
spread = ctx.study.spread(orderbook)
sma = ctx.study.sma(candles, {"field": "c", "window": 20})Available helpers are sma, ema, cvd, spread, depth, imbalance, slippage, and vamp. They accept normal Python dictionaries and lists and return dictionaries or lists.
See Built-In Functions for every input and return shape.
ctx.trade(request)
reference = ctx.trade(request: dict)Submits an intentional perpetual position transition. The request must include an exchange; account defaults to main. Returns:
{"id": "ord_..."}See Execution: ctx.trade for the request fields.
ctx.order(request)
reference = ctx.order(request: dict)Submits a raw buy or sell. The request must include an exchange; account defaults to main. Returns the same local order reference. See Execution: ctx.order.
ctx.positions(account="main")
Read open positions for one execution account:
main = ctx.positions().open
trading_2 = ctx.positions("trading-2").openctx.positions("main") is equivalent to ctx.positions(). Named accounts are isolated from one another in both live execution and backtests.
ctx.cancel(request)
result = ctx.cancel(
{
"order": "ord_...",
}
)Queues cancellation of a managed order and returns its local cancellation status. Do not pass exchange; cancellation uses the exchange stored on the referenced managed order.
ctx.artifact_path(name)
path = ctx.artifact_path("charts/performance.png")Returns an absolute string path inside the current run's owner-only artifact directory. The name must be relative and cannot escape that directory.
History
Retained list
records = history.source(selector: str)Returns copies ordered oldest to newest. Returns [] when the selector has no data.
Indexed record
record = history.source(selector: str, index: int)Index 0 is newest, 1 is previous, and so on. Returns None when unavailable.
The selector must be a non-empty exact source identity. An index must be a non-negative integer and cannot be a boolean.
See History, Sources, and Data Types.
Data hook context
Use context values to identify the source that triggered on_data:
| Field | Meaning |
|---|---|
ctx.source | Exact normalized selector that triggered the hook. |
ctx.source_type | Base kind such as candles, orderbook, or trades. |
ctx.provider | Standalone provider or mmt. |
ctx.exchange | Normalized exchange name. |
ctx.symbol | Normalized symbol. |
ctx.source_configs | Parsed selector settings such as timeframe or depth. |
ctx.positions(account="main").open | Open positions for the selected execution account. |
The market record itself is read through history.source(ctx.source, 0).
Execution hook context
ctx.execution contains the current execution lifecycle payload. Typical fields include:
| Field | Meaning |
|---|---|
seq | Monotonic event sequence within the job. |
jobId | Script job identifier. |
tsMs | Local daemon receipt and journal time. |
type | Lifecycle type such as order.fill. |
orderId | Stable local order ID, when applicable. |
symbol | Order or position symbol. |
venue | Execution venue. |
venueOrderId | Venue-assigned order ID, when available. |
status | Current managed-order status. |
terminal | Whether this event ends the managed order lifecycle. |
data | Venue or event-specific payload. |
Hook output
All hooks accept these return forms:
None
{}
{"metrics": {...}}
{"meta": {...}}
{"metrics": {...}, "meta": {...}}metrics and meta must be dictionaries. Trading signals in returned output are not interpreted as orders.
The runner can serialize normal JSON values plus pathlib.Path, NumPy scalar values, NumPy arrays, and other values implementing a compatible tolist() method.
Interpreter resolution
For a .py script, Market Lab resolves:
--python <path-or-command>;- an adjacent
.venv/bin/python; - an adjacent
.venv/Scripts/python.exe; python3, thenpython, fromPATH.
Python 3.9 or newer is required. --python is rejected for JavaScript files.
No Python-only mode object
Python V2 deliberately has no ctx.mode. script backtest and script run feed the same sequential hook contract. The command owns historical or live data loading, while each ctx.trade(...) or ctx.order(...) request selects its own execution exchange.
Python V2 does not accept --venue. BULK and Hyperliquid use mainnet unless the job is started with --testnet; that single flag applies to supported sources and execution requests in the job. HyperLink execution is mainnet-only.