Skip to content

Client

Client is the entry point to the engine. Construct one with an API key — passed directly or read from the BACKTEST360_API_KEY environment variable — and call its methods to discover indicators, validate strategies, and run backtests.

from backtest360 import Client

client = Client()   # reads BACKTEST360_API_KEY and BACKTEST360_ENGINE_URL

The base URL defaults to https://api.backtest360.com and can be overridden with the base_url argument or the BACKTEST360_ENGINE_URL variable. See Authentication to obtain a key.

Methods at a glance

Method Purpose
version() The engine version and contract.
me() The calling key's identity, plan, and limits.
list_indicators() The indicators the engine offers.
list_templates(name=None, compact=True) The built-in strategy templates.
sample_symbols() A short list of symbols with bundled sample data.
sample_data(symbol="SPY") An OHLCV frame of sample data for quick experiments.
validate_strategy(strategy) Validate a strategy document without running it.
latest_signal(...) The most recent signal a strategy would produce.
backtest(strategy, ohlcv, ...) Run a backtest from a Strategy and an OHLCV frame. Returns a Result.
backtest_signals(signals, ohlcv, ...) Run a backtest from a precomputed signal series. Returns a Result.
backtest_raw(payload) Run a backtest from a raw request envelope, for full control over the wire shape.

The full signatures and argument detail follow.

backtest360.client.Client

Synchronous HTTP client for the Backtest360 API.

Parameters:

Name Type Description Default
api_key str | None

Your Backtest360 API key. Falls back to the BACKTEST360_API_KEY environment variable. Raises Backtest360Error(code="CLIENT_NO_API_KEY") immediately if neither is set.

None
base_url str | None

Engine base URL. Falls back to BACKTEST360_ENGINE_URL env var, then https://api.backtest360.com.

None
timeout float

Request timeout in seconds. Defaults to 300 (backtests can be slow).

_TIMEOUT_SECONDS
Example

import yfinance as yf from backtest360 import Client, Strategy

df = yf.download("BTC-USD", period="1y", interval="1d", ... auto_adjust=False, multi_level_index=False, progress=False) df.columns = df.columns.str.lower()

result = Client(api_key="b360_...").backtest( ... Strategy.rsi_threshold_long(), df ... ) print(result.stats["sharpe"]) result.strategy_equity.plot(title="Equity curve")

version(*, request_id=None)

Return engine version info from GET /api/version.

Parameters:

Name Type Description Default
request_id str | None

Optional correlation id sent as the X-Request-ID header (letters, digits, ., _, -; max 64 chars). Echoed on the response and recorded in the engine's logs; generated server-side when omitted.

None

Returns:

Type Description
dict[str, Any]

Dict with at minimum {"version": "x.y.z", "engine": "...", "api_contract": "..."}.

dict[str, Any]

The response also includes expected_client_contract — the contract

dict[str, Any]

version the engine expects clients to declare. The client sends its own

dict[str, Any]

contract version automatically on every request via the

dict[str, Any]

X-Client-Contract header.

Raises:

Type Description
Backtest360Error

On any non-2xx response.

Example

info = client.version() print(info["version"]) 0.5.3

me(*, request_id=None)

Introspect the calling API key via GET /api/me.

Returns the key's granted scopes, numeric limits, current usage against those limits, and capability flags — so you can discover what the key can do up front instead of learning it from 401/403/429 errors. Requires only a valid key; no particular scope.

Parameters:

Name Type Description Default
request_id str | None

Optional correlation id sent as the X-Request-ID header. See :meth:version.

None

Returns:

Type Description
dict[str, Any]

Dict with:

dict[str, Any]
  • scopes: list of granted scope strings (sorted).
dict[str, Any]
  • limits: per-key numeric limits — rpm, rpd, max_concurrent, max_bars_per_run (the last is None when the key has no bar cap).
dict[str, Any]
  • usage: point-in-time usage including this request — minute / day (each used / remaining / resets_in_seconds) and concurrent (used / remaining).
dict[str, Any]
  • capabilities: boolean flags such as server_side_fetch and full_metrics.

Raises:

Type Description
Backtest360Error

On any non-2xx response.

Example
>>> info = client.me()
>>> print(info["scopes"])
['backtest.run', 'meta.read', 'strategy.validate']
>>> print(info["limits"]["rpm"], info["usage"]["minute"]["remaining"])

list_indicators(*, request_id=None)

Return the engine's indicator library from GET /api/indicators.

Each entry describes an indicator's name, parameters, kind, and output columns. Use this to discover available indicators and their parameter schemas when building custom strategies.

See also: https://api.backtest360.com/docs

Parameters:

Name Type Description Default
request_id str | None

Optional correlation id sent as the X-Request-ID header. See :meth:version.

None

Returns:

Type Description
list[dict[str, Any]]

List of indicator descriptor dicts.

Raises:

Type Description
Backtest360Error

On any non-2xx response.

Example

for ind in client.list_indicators(): ... print(ind["name"], ind.get("params", {}).keys())

list_templates(name=None, compact=True, *, request_id=None)

Return the predesigned strategy templates from GET /api/strategies.

The engine serves code-shipped, versioned strategy definitions. The response is tier-filtered server-side by the calling key's scopes: non-admin keys see only public templates. Each template is a ready-to-run strategy in the canonical shape a backtest accepts (condition_tree + indicators) plus parameter metadata (requires / defaults / locked_params) and an origin tag.

With no arguments, returns a compact catalog — a list of dicts carrying only id, origin, name, and description — enough to browse and choose. Pass compact=False for the full definition of every template. Pass name (an id or name, matched case-insensitively) to return that single template's full entry as a dict; compact is ignored in that case.

Parameters:

Name Type Description Default
name str | None

Optional id or name of one template to fetch in full, matched case-insensitively. When given, the return value is a single template dict rather than a list.

None
compact bool

When True (the default) and name is not given, each returned entry carries only the compact discovery fields. When False, each entry is the template's full definition.

True
request_id str | None

Optional correlation id sent as the X-Request-ID header. See :meth:version.

None

Returns:

Type Description
list[dict[str, Any]] | dict[str, Any]

A list of template dicts, or — when name is given — the single

list[dict[str, Any]] | dict[str, Any]

matching template dict.

Raises:

Type Description
Backtest360Error

On any non-2xx response, or, when name is given and no template matches, with code="CLIENT_TEMPLATE_NOT_FOUND".

Example

for t in client.list_templates(): ... print(t["id"], "-", t["description"]) strat = client.list_templates(name="rsi_mean_reversion") result = client.backtest_raw({"strategy": strat, ...})

sample_symbols(*, request_id=None)

Return the symbols available as bundled sample data.

Uses GET /api/data/samples. The engine serves a small set of ready-made daily OHLCV datasets so examples and quick experiments run without an external data feed. Free to call — no paid data access required.

Parameters:

Name Type Description Default
request_id str | None

Optional correlation id sent as the X-Request-ID header. See :meth:version.

None

Returns:

Type Description
list[str]

List of symbol strings (e.g. ["SPY", "QQQ", "BTC"]).

Raises:

Type Description
Backtest360Error

On any non-2xx response.

Example

client.sample_symbols() ['SPY', 'QQQ', 'BTC']

sample_data(symbol='SPY', *, request_id=None)

Return a bundled sample OHLCV dataset as a ready-to-backtest DataFrame.

Uses GET /api/data/sample. The returned frame has a DatetimeIndex and lowercase open/high/low/close/volume columns — the exact shape :meth:backtest, :meth:backtest_signals, and :meth:latest_signal expect, so it is a drop-in for those methods. Free to call — no paid data access required.

Parameters:

Name Type Description Default
symbol str

One of the symbols returned by :meth:sample_symbols (default "SPY"). Unknown symbols raise Backtest360Error with code="INVALID_SYMBOL".

'SPY'
request_id str | None

Optional correlation id sent as the X-Request-ID header. See :meth:version.

None

Returns:

Type Description
DataFrame

A pd.DataFrame indexed by datetime with lowercase OHLCV columns.

Raises:

Type Description
Backtest360Error

On any non-2xx response (including an unknown symbol).

Example

df = client.sample_data("BTC") result = client.backtest(Strategy.rsi_threshold_long(), df)

validate_strategy(strategy, *, request_id=None)

Validate a strategy without running a backtest.

Parameters:

Name Type Description Default
strategy Strategy

A :class:~backtest360.Strategy instance to validate.

required
request_id str | None

Optional correlation id sent as the X-Request-ID header. See :meth:version.

None

Returns:

Type Description
dict[str, Any]

Validation result dict. For a valid strategy: valid (True),

dict[str, Any]

warmup_bars, referenced_indicators, and related fields.

dict[str, Any]

For an invalid strategy: valid (False) and errors, a list

dict[str, Any]

of dicts with code, location, message, and context.

Raises:

Type Description
Backtest360Error

On HTTP errors other than a validation failure.

Example

v = client.validate_strategy(Strategy.rsi_threshold_long()) print(v["valid"], v.get("errors", []))

latest_signal(strategy, ohlcv, *, execution=None, costs=None, risk=None, sizing=None, market_hours=None, settings=None, request_id=None)

Return the latest signal for a strategy on the given data.

Uses POST /api/latest-signal. Returns only the most-recent bar's signal and per-condition diagnostics — no P&L or statistics.

Parameters:

Name Type Description Default
strategy Strategy

Strategy definition.

required
ohlcv DataFrame

DataFrame indexed by datetime with columns open/high/low/close/volume.

required
execution Execution | None

Execution configuration (optional).

None
costs Costs | None

Cost configuration (optional).

None
risk Risk | None

Risk / stop configuration (optional).

None
sizing Sizing | None

Position sizing configuration (optional).

None
market_hours MarketHours | None

Daily anchor-hour configuration (optional).

None
settings Settings | None

Engine-level run settings (optional).

None
request_id str | None

Optional correlation id sent as the X-Request-ID header. See :meth:version.

None

Returns:

Type Description
dict[str, Any]

Dict with signal (int), long_entry_fired (bool), and

dict[str, Any]

related diagnostic fields.

Raises:

Type Description
Backtest360Error

On HTTP error or invalid strategy.

Example

sig = client.latest_signal(Strategy.rsi_threshold_long(), df) print(sig["signal"]) # -1 / 0 / 1

backtest_raw(payload, *, request_id=None)

Send a raw POST /api/backtest payload, return the raw response dict.

For users who want exact control over the wire format — build the JSON payload yourself with the API docs open. The payload is sent as-is, with no client-side validation and no defaults applied. The engine's backtest request is the leg-based {"run": {...}, "legs": [...]} envelope: each leg needs an id and a data_source, stats_keys lives under run (set it to "ids" for stable metric-id keys), and a benchmark is its own {"benchmark": true} leg. See the metrics catalog on GET /api/sections for the id/label mapping.

Parameters:

Name Type Description Default
payload dict[str, Any]

Dict matching the /api/backtest request body exactly. See https://api.backtest360.com/docs for the full schema.

required
request_id str | None

Optional correlation id sent as the X-Request-ID header. See :meth:version.

None

Returns:

Type Description
dict[str, Any]

The full response dict from the engine (run and legs keys

dict[str, Any]

and all).

Raises:

Type Description
Backtest360Error

On any non-2xx response.

Example

resp = client.backtest_raw({ ... "run": {"stats_keys": "ids"}, ... "legs": [{ ... "id": "strategy", ... "data_source": {"ohlcv": {...}}, ... "strategy": {"condition_tree": {...}, "indicators": [...]}, ... "execution": {"signal_frequency": "daily"}, ... }], ... })

backtest(strategy, ohlcv, *, benchmark=None, execution=None, costs=None, risk=None, sizing=None, market_hours=None, settings=None, stats_keys='ids', request_id=None)

Run a historical backtest and return a :class:Result.

Parameters:

Name Type Description Default
strategy Strategy

Strategy definition. Use a template (e.g. Strategy.rsi_threshold_long()) or build your own.

required
ohlcv DataFrame

DataFrame indexed by datetime with lowercase columns open, high, low, close (and optionally volume).

required
benchmark DataFrame | None

Optional benchmark DataFrame (same shape as ohlcv). When provided, benchmark-relative metrics (Alpha, Beta, Information Ratio, Tracking Error, Up/Down Capture, Capture Ratio) are surfaced on result.relative.

None
execution Execution | None

Execution timing config — entry, exit, signal_frequency, etc.

None
costs Costs | None

Transaction costs — slippage_bps, fee_pct, etc.

None
risk Risk | None

Stop-loss / drawdown protection config.

None
sizing Sizing | None

Position sizing config.

None
market_hours MarketHours | None

Daily anchor-hour config for sub-daily execution.

None
settings Settings | None

Engine-level run settings — RFR, RNG seed, bad-data policy.

None
stats_keys str

How the returned result.stats dict is keyed — "ids" (default) for stable snake_case metric ids (e.g. "sharpe"), or "labels" for legacy display-label keys (e.g. "Sharpe"). See the metrics catalog on GET /api/sections for the full id/label/description mapping.

'ids'
request_id str | None

Optional correlation id sent as the X-Request-ID header. See :meth:version.

None

Returns:

Name Type Description
A Result

class:Result wrapping the engine response.

Raises:

Type Description
Backtest360Error

On any non-2xx response.

Example

from backtest360 import Client, Strategy, Execution, Costs, Settings result = Client(api_key="...").backtest( ... Strategy.rsi_threshold_long(), df, ... execution=Execution(signal_frequency="daily"), ... costs=Costs(slippage_bps=2.5, fee_pct=0.001), ... settings=Settings(risk_free_rate=0.04), ... ) print(result.stats["sharpe"]) result.strategy_equity.plot()

backtest_signals(signals, ohlcv, *, name=None, benchmark=None, execution=None, costs=None, risk=None, sizing=None, market_hours=None, settings=None, stats_keys='ids', request_id=None)

Run a backtest using a pre-computed signal series.

Use this when your signal logic lives outside the engine (e.g. a machine-learning model, a custom indicator). Pass a pd.Series of {-1, 0, 1} indexed by datetime; the engine skips signal generation and runs execution, costing, and statistics directly on your series.

Parameters:

Name Type Description Default
signals Series

Integer series indexed by datetime with values in {-1, 0, 1}. 1 = long, -1 = short, 0 = flat. Boolean series are also accepted (True → 1, False → 0). Must cover the same date range as ohlcv.

required
ohlcv DataFrame

DataFrame indexed by datetime with lowercase columns open, high, low, close (and optionally volume).

required
name str | None

Optional label for the strategy (used in engine output).

None
benchmark DataFrame | None

Optional benchmark DataFrame (same shape as ohlcv).

None
execution Execution | None

Execution timing config.

None
costs Costs | None

Transaction costs.

None
risk Risk | None

Stop-loss / drawdown protection config.

None
sizing Sizing | None

Position sizing config.

None
market_hours MarketHours | None

Daily anchor-hour config for sub-daily execution.

None
settings Settings | None

Engine-level run settings.

None
stats_keys str

How the returned result.stats dict is keyed — "ids" (default) for stable snake_case metric ids (e.g. "sharpe"), or "labels" for legacy display-label keys (e.g. "Sharpe"). See the metrics catalog on GET /api/sections for the full id/label/description mapping.

'ids'
request_id str | None

Optional correlation id sent as the X-Request-ID header. See :meth:version.

None

Returns:

Name Type Description
A Result

class:Result wrapping the engine response.

Raises:

Type Description
Backtest360Error

On any non-2xx response.

Example

import pandas as pd signals = pd.Series([0, 1, 1, 0, -1, 0], index=df.index) result = client.backtest_signals(signals, df) print(result.stats["sharpe"])


Result

Every backtest returns a Result. Its stats dictionary is keyed by stable metric ids by default — result.stats["sharpe"], result.stats["max_drawdown"] — and the rest of its attributes expose the equity curve, trades, and signals as pandas objects.

backtest360.client.Result

Wraps a /api/backtest response.

All properties are derived lazily from the raw response dict.

Attributes:

Name Type Description
stats dict[str, Any]

The full statistic set. Keyed by stable snake_case metric id (e.g. "sharpe") by default, or by legacy display label (e.g. "Sharpe") when the request was made with stats_keys="labels". See the metrics catalog on GET /api/sections for the full id/label/description mapping.

relative dict[str, Any]

Benchmark-relative metrics (Alpha, Beta, Information Ratio, Tracking Error, Up/Down Capture, Capture Ratio), keyed the same way as stats. Empty dict when no benchmark was supplied.

trades list[dict[str, Any]]

List of trade dicts, each with entry_date, exit_date, direction, return_net, etc.

strategy_equity Series

Strategy equity curve as a pd.Series indexed by datetime.

benchmark_equity Series

Benchmark equity curve as a pd.Series indexed by datetime. Empty Series when no benchmark was supplied in the request.

returns Series

Net-of-cost log-return series indexed by datetime.

signals Series

Signal series ({-1, 0, 1}) indexed by datetime.

markers dict[str, Any]

Warmup and trade-boundary markers for chart annotation.

data_quality dict[str, Any]

The engine's assessment of the input data quality.

raw dict[str, Any]

The full result dict — everything the engine returned.

Example

result = client.backtest(strategy, df, benchmark=benchmark_df) result.summary() Performance Summary ───────────────────────────── Total Return 8.3% Annualized Return 12.1% Annualized Std Dev 18.4% Sharpe Ratio 1.42 ax = result.strategy_equity.plot(title="Strategy vs Benchmark", label="Strategy") result.benchmark_equity.plot(ax=ax, label="Benchmark", linestyle="--") for trade in result.trades[:5]: ... print(trade["entry_date"], trade["return_net"])

stats property

Performance statistics dict (120+ metrics).

Keyed by stable snake_case metric id by default (e.g. "sharpe"); see the stats_keys argument on the request method that produced this result and the metrics catalog on GET /api/sections.

Benchmark-relative metrics (Alpha, Beta, Information Ratio, Tracking Error, Up/Down Capture, Capture Ratio) are NOT in this dict — see :attr:relative.

relative property

Benchmark-relative metrics (Alpha, Beta, Information Ratio, Tracking Error, Up/Down Capture, Capture Ratio), keyed the same way as stats (per the request's stats_keys). Empty dict when no benchmark was supplied.

These metrics are NOT in stats — the engine returns them in a separate relative block, and the client surfaces them here.

trades property

Trade log — list of dicts with entry/exit date, direction, return.

strategy_equity property

Strategy equity curve as a pd.Series indexed by datetime.

This series is always present when dates is non-empty. A mismatch in length between dates and strategy_equity is treated as a malformed engine response and raises Backtest360Error.

benchmark_equity property

Benchmark equity curve as a pd.Series indexed by datetime.

Empty Series when no benchmark was supplied in the request. Unlike strategy_equity, returns, and signals, this array is optional — the engine omits it when no benchmark is configured.

returns property

Net-of-cost log-return series indexed by datetime.

Always present when dates is non-empty. A length mismatch raises Backtest360Error (CLIENT_MALFORMED_RESPONSE).

signals property

Signal series ({-1, 0, 1}) indexed by datetime.

Always present when dates is non-empty. A length mismatch raises Backtest360Error (CLIENT_MALFORMED_RESPONSE).

markers property

Warmup and trade-boundary markers for chart annotation.

Dict with the warmup boundary (warmup_bars, warmup_end_index, warmup_end_date) and the first/last trade positions (first_trade_index, first_trade_date, last_trade_exit_index, last_trade_exit_date). Individual fields are None when not applicable (e.g. a run with no trades). Empty dict when the engine response carries no markers.

data_quality property

The engine's assessment of the input data quality.

Reports issues found while preparing the run's data, such as bad prices, missing bars, and quality warnings. Empty dict when the engine response carries no data-quality block.

raw property

Full engine response dict — access any field not exposed as a property.

summary()

Print a high-level performance summary.

Annualized Return, Annualized Std Dev, and Sharpe are read directly from the engine's stats dict (where they are already computed and annualized to the bar frequency used in the run). Each is read id-first with a display-label fallback — the stable metric ids (cagr, vol_ann, sharpe) when present, otherwise the legacy display labels (CAGR, Vol (Ann), Sharpe) — so the summary renders correctly whether the stats are id-keyed or label-keyed. Total Return is derived from the equity curve as last / first - 1.

Missing stats render as n/a — no exception is raised.

Example

result.summary() Performance Summary ───────────────────────────── Total Return 8.3% Annualized Return 12.1% Annualized Std Dev 18.4% Sharpe Ratio 1.42