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
|
None
|
base_url
|
str | None
|
Engine base URL. Falls back to |
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 |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Dict with at minimum |
dict[str, Any]
|
The response also includes |
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]
|
|
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 |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Dict with: |
dict[str, Any]
|
|
dict[str, Any]
|
|
dict[str, Any]
|
|
dict[str, Any]
|
|
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 |
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 |
None
|
compact
|
bool
|
When |
True
|
request_id
|
str | None
|
Optional correlation id sent as the |
None
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]] | dict[str, Any]
|
A list of template dicts, or — when |
list[dict[str, Any]] | dict[str, Any]
|
matching template dict. |
Raises:
| Type | Description |
|---|---|
Backtest360Error
|
On any non-2xx response, or, when |
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 |
None
|
Returns:
| Type | Description |
|---|---|
list[str]
|
List of symbol strings (e.g. |
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: |
'SPY'
|
request_id
|
str | None
|
Optional correlation id sent as the |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
A |
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: |
required |
request_id
|
str | None
|
Optional correlation id sent as the |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Validation result dict. For a valid strategy: |
dict[str, Any]
|
|
dict[str, Any]
|
For an invalid strategy: |
dict[str, Any]
|
of dicts with |
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
|
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 |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Dict with |
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 |
required |
request_id
|
str | None
|
Optional correlation id sent as the |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
The full response dict from the engine ( |
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.
|
required |
ohlcv
|
DataFrame
|
DataFrame indexed by datetime with lowercase columns
|
required |
benchmark
|
DataFrame | None
|
Optional benchmark DataFrame (same shape as |
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 — RFR, RNG seed, bad-data policy. |
None
|
stats_keys
|
str
|
How the returned |
'ids'
|
request_id
|
str | None
|
Optional correlation id sent as the |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
Result
|
class: |
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
|
required |
ohlcv
|
DataFrame
|
DataFrame indexed by datetime with lowercase columns
|
required |
name
|
str | None
|
Optional label for the strategy (used in engine output). |
None
|
benchmark
|
DataFrame | None
|
Optional benchmark DataFrame (same shape as |
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 |
'ids'
|
request_id
|
str | None
|
Optional correlation id sent as the |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
Result
|
class: |
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. |
relative |
dict[str, Any]
|
Benchmark-relative metrics (Alpha, Beta, Information Ratio,
Tracking Error, Up/Down Capture, Capture Ratio), keyed the same way
as |
trades |
list[dict[str, Any]]
|
List of trade dicts, each with |
strategy_equity |
Series
|
Strategy equity curve as a |
benchmark_equity |
Series
|
Benchmark equity curve as a |
returns |
Series
|
Net-of-cost log-return series indexed by datetime. |
signals |
Series
|
Signal series ( |
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 |
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