Skip to main content

backtest360

PyPI version Python versions License: MIT tests

Official Python client for the Backtest360 API.

Run backtests, automate, integrate with your research or trading workflows. The client handles authentication, serialization, and error mapping so you can call a single method instead of building and parsing the raw HTTP requests yourself.

import matplotlib.pyplot as plt
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)
result.summary()
result.strategy_equity.plot(title="Equity curve")
plt.show()

Save this snippet as quickstart.py, drop in your API key, and run it with python quickstart.py (install the extras if needed: pip install matplotlib yfinance).


Install

pip install backtest360

Requires Python 3.9+. The only runtime dependencies are httpx and pandas.

Need to install Python first? (Windows / macOS / Linux / WSL)

Windows (native)

  1. Install Python from python.org/downloads (check "Add to PATH"), or via winget:
    winget install Python.Python.3.12
    
  2. Open PowerShell and verify:
    python --version
    
  3. Install the client and yfinance for the quickstart:
    pip install backtest360 yfinance
    
  4. Run a script:
    python quickstart.py
    
  1. Install WSL:
    wsl --install
    
    Restart, then open the Ubuntu terminal.
  2. Install Python:
    sudo apt update && sudo apt install python3 python3-pip -y
    
  3. Install the client:
    pip3 install backtest360 yfinance
    
  4. Run a script:
    python3 quickstart.py
    

macOS

# via Homebrew (recommended)
brew install python

# or download from python.org/downloads

Then:

pip3 install backtest360 yfinance
python3 quickstart.py

Linux

sudo apt install python3 python3-pip -y   # Debian/Ubuntu
pip3 install backtest360 yfinance
python3 quickstart.py

Get an API key

Sign up at backtest360.com and copy your key. Store it in the BACKTEST360_API_KEY environment variable or pass it directly:

client = Client(api_key="b360_...")
# or: export BACKTEST360_API_KEY=b360_...
client = Client()

The engine URL can be overridden with the optional BACKTEST360_ENGINE_URL environment variable, read when the Client is constructed. An explicit base_url= argument takes precedence over the environment variable, which in turn takes precedence over the default:

export BACKTEST360_ENGINE_URL=https://api.backtest360.com

Features

  • Synchronous client — straightforward to use in scripts, notebooks, and pipelines
  • Hand-written wrapper over the public REST API — no generated code, no schema sync
  • Built-in strategy templates (Strategy.rsi_threshold_long(), Strategy.ma_crossover(), …)
  • Grouped-knob classes: Execution, Costs, Risk, Sizing, MarketHours, Settings — set only what you need
  • Pre-computed signals path: client.backtest_signals(series, df) for model-generated signals
  • Pandas-native — pass a DataFrame, get pandas Series back (result.strategy_equity, result.returns)
  • Built-in sample data served by the engine — examples run without external data feeds (client.sample_data("BTC"))
  • Chart-annotation markers and data-quality report on every result (result.markers, result.data_quality)
  • Engine introspection: client.version() for version compatibility checks, client.me() for your key's scopes/limits/usage
  • Strategy templates: client.list_templates() to discover ready-to-run predesigned strategies
  • Raw-API escape hatch for full control (client.backtest_raw({...}))
  • Strict type hints + py.typed — first-class IDE and mypy support
  • MIT licensed

Common patterns

Custom strategy

from backtest360 import Client, Strategy, Execution, Costs, Risk, Sizing, Settings
import yfinance as yf

df = yf.download("SPY", start="2020-01-01", end="2024-01-01", interval="1d",
                 auto_adjust=False, multi_level_index=False, progress=False)
df.columns = df.columns.str.lower()

strat = Strategy(
    name="rsi_mean_reversion",
    long_entry="(rsi_14 > 30) & (rsi_14 < 50)",
    long_exit="rsi_14 >= 50",
    indicators=[Strategy.indicator("rsi", ref="rsi_14", period=14)],
)

result = Client(api_key="b360_...").backtest(
    strat, df,
    execution=Execution(entry="open", exit="close", signal_frequency="daily"),
    costs=Costs(slippage_bps=2.5, fee_pct=0.001),
    risk=Risk(stop="atr", value=2.5, atr_period=14, max_drawdown=0.25),
    sizing=Sizing(weight=1.0, vol_target=0.15, leverage_limit=2.0),
    settings=Settings(risk_free_rate=0.04),
)

result.summary()
print("Max Drawdown:", result.stats.get("max_drawdown", "n/a"))
for t in result.trades[:5]:
    print(t["entry_date"], t["direction"], t["return_net"])

Indicator library (names, params, output columns): https://api.backtest360.com/docs

Strategy templates: see the Strategy.<name>() classmethods (e.g. Strategy.rsi_threshold_long()).

Built-in sample data

No data feed handy? The engine serves a small set of ready-made daily OHLCV datasets, so you can run a backtest with no external dependency:

from backtest360 import Client, Strategy

client = Client(api_key="b360_...")
print(client.sample_symbols())     # ['SPY', 'QQQ', 'BTC']
df = client.sample_data("BTC")     # DatetimeIndex + lowercase OHLCV — drop-in for backtest()
result = client.backtest(Strategy.rsi_threshold_long(), df)

Pre-computed signals

import pandas as pd
from backtest360 import Client

# Any signal series of {-1, 0, 1} — your ML model, custom indicator, etc.
signals = pd.Series(..., index=df.index)

result = Client(api_key="b360_...").backtest_signals(signals, df)
print(result.stats["sharpe"])

Raw API escape hatch

For users who want exact control with the API docs open:

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

Latest signal

Ask the engine for the target position on the most-recent bar — the live-trading hook:

from backtest360 import Client, Strategy

signal = Client(api_key="b360_...").latest_signal(Strategy.rsi_threshold_long(), df)
position = {1: "LONG", 0: "FLAT", -1: "SHORT"}[signal["signal"]]
print(position, signal["long_entry_fired"])

Validate a strategy

Check that a strategy is valid without running a backtest:

from backtest360 import Client, Strategy

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

Discover indicators

from backtest360 import Client

client = Client(api_key="b360_...")
print([i["name"] for i in client.list_indicators()])

Discover strategy templates

Browse the predesigned templates available to your key, then fetch one in full and run it:

from backtest360 import Client

client = Client(api_key="b360_...")
for t in client.list_templates():                     # compact catalog
    print(t["id"], "-", t["description"])

strat = client.list_templates(name="rsi_mean_reversion")   # one full definition
df = client.sample_data("SPY")
resp = client.backtest_raw({
    "run": {"stats_keys": "ids"},
    "legs": [{"id": "main", "strategy": strat, "data_source": {"ohlcv": ...}}],
})
result = resp["legs"][0]["result"]

Filter and page through a larger catalog with collection, q, tags, limit, and offset; pass raw=True to get back the response envelope (count, total, next_offset) needed to fetch the next page:

page = client.list_templates(
    collection="all", tags=["momentum"], q="breakout", limit=20, offset=0, raw=True
)
for t in page["strategies"]:
    print(t["id"], t.get("collection"), t.get("tags"))

if page["next_offset"] is not None:
    next_page = client.list_templates(collection="all", limit=20, offset=page["next_offset"])

Inspect your API key

See your key's scopes, rate limits, and current usage up front — so you can size requests before hitting a limit:

from backtest360 import Client

info = Client(api_key="b360_...").me()
print(info["scopes"])                       # ['backtest.run', 'meta.read', ...]
print(info["limits"]["rpm"], "req/min")
print(info["usage"]["day"]["remaining"], "requests left today")

Error handling

from backtest360 import Backtest360Error

try:
    result = client.backtest(strategy, df)
except Backtest360Error as e:
    if e.status == 401:
        print("Invalid or expired API key — renew at backtest360.com")
    elif e.status in (429, 503):
        # Rate limited or the engine is at capacity — retry with backoff.
        # e.retry_after carries the Retry-After header (seconds) when set.
        print(f"Busy — retry in {e.retry_after or 5:.0f}s")
    elif e.status == 504:
        print("Run exceeded the compute time limit — reduce the date range "
              "or strategy complexity")
    elif e.status == 422:
        detail = e.body.get("detail") if isinstance(e.body, dict) else e.body
        print("Strategy validation failed:", detail)
    else:
        raise   # unexpected — let it propagate

On error responses, e.request_id carries the response's X-Request-ID — quote it when reporting an issue. You can also pass your own correlation id to any method: client.backtest(strategy, df, request_id="my-run-42").


Versioning

MAJOR.MINOR.PATCH. Pre-1.0 (0.x.y): the API may move between minor versions. Pre-release suffixes: aN (alpha), bN (beta), rcN (release candidate). See CHANGELOG.md for the release history.


Full documentation

Questions / feedback

Questions or feedback? hello@backtest360.com — we read everything. The client is in active development, so help shape it.

Bug reports and feature requests: open an issue on GitHub.

License

MIT — see LICENSE.

Metadata

Release files for backtest360 0.8.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for backtest360 0.8.1
File Size Uploaded Provenance Actions
backtest360-0.8.1.tar.gz 35.3 kB Without provenance Details for backtest360-0.8.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for backtest360 0.8.1
File Interpreter ABI Platform Actions
backtest360-0.8.1-py3-none-any.whl Python 3 none any Details for backtest360-0.8.1-py3-none-any.whl

Total release size: 63.6 kB

Release files / backtest360-0.8.1.tar.gz

Download URL backtest360-0.8.1.tar.gz
Size 35.3 kB
Tags Source
SHA-256 checksum
How to use checksums
a1d547fb5db801d7c0bbdbd7d8fda47f48f3a73f49e7215bbe97932e98723ea3
BLAKE2b-256 checksum
How to use checksums
73c6753377aa95e97e776bc8e69b0699bbfba56fef6589a1735f012c96887117
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.15

Release files / backtest360-0.8.1-py3-none-any.whl

Download URL backtest360-0.8.1-py3-none-any.whl
Size 28.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d654cdc6ca5b41649682ce6576df77c637741a8b0d8630c3ef9286d9b7a3897d
BLAKE2b-256 checksum
How to use checksums
173d58eecc27849c8123c1104af5887357d34575f4caa0a5ba7c6d4fedd79141
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.15

Security for backtest360 0.8.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page