Skip to main content

crypto-rs-backtester

Open In Colab

Video

Your Crypto Backtest Is Lying to You - Fix It with Rust | crypto-rs-backtester

Your Crypto Backtest Is Lying to You - Fix It with Rust | crypto-rs-backtester

Japanese version: see README.ja.md.

A tick-level, high-precision backtester powered by Rust × Python (Polars), designed for researchers. WIP.

  • Goal: Combine Python's agility with Rust's deterministic, high-performance simulation to eliminate performance bottlenecks, look-ahead bias, and poor reproducibility.
  • Scope: Mainly crypto spot/futures (CEX). Validates microstructure such as multi-exchange, latency, and queue position.

Key Highlights

  • Performant by design: Rust event-driven core with fixed-point arithmetic; Python focuses on research interface.
  • Zero-copy pipeline: Use Polars (Arrow) in Python and hand off to Rust with minimal copying.
  • Microstructure fidelity: Latency modeling, L2/L3 queue logic, and realistic race conditions (e.g., PendingCancel).
  • Determinism first: Seeded RNGs, stable tie-breaking for identical timestamps.

See docs/SPEC.md for details.

Features (current state)

  • Hybrid architecture: Rust core (event-driven, fixed-point i64) + Python strategy interface.
  • Separated timelines: ts_exchange (ground truth) / ts_local (what the strategy observes) / ts_sim (total order of all events).
  • Look-ahead prevention: Strategies only see MarketView after feed latency. Order arrival and ACK are strictly ordered on ts_sim.
  • Execution modes: Tick mode (on_tick) and Batch mode (on_ticks / on_order_updates).
  • Data ingestion: via Polars LazyFrame (minimal copying), or zero-copy via Arrow C Stream (run_arrow).
  • Determinism: Fixed RNG seed, stable tie-breakers for simultaneous events, lexicographic assignment of symbol IDs.

See docs/SPEC.md for details.


Repository Structure

  • backtester-core/: Rust simulation core (src/*.rs, tests/, benches/)
  • backtester-py/: PyO3 wrapper exposing the core to Python
  • python/: Python package rust_backtester/ and tests in python/tests/
  • docs/: Specs and plans (SPEC.md, PLAN.md, etc.)
  • Root Cargo.toml: Rust workspace, pyproject.toml: maturin build

Installation

pip install crypto-rs-backtester

Install & Build (dev)

Prerequisites: Python 3.9+ / Rust toolchain / maturin

# Virtualenv
python -m venv .venv && source .venv/bin/activate

# Dev install (builds Rust extension)
pip install -e .[dev]

# Alternative: direct build
maturin develop

Quickstart (Python)

Required columns: ts_exchange:Int64, price:Int64, qty:Int64, side:Int8 Recommended: seq:Int64 (stable order for same-timestamp), ts_local:Int64 (if missing, applies ts_exchange + feed_latency_ns) Aliases: ts_eventts_exchange, sizeqty

import polars as pl
from rust_backtester import Backtester

# Tiny deterministic dataset (1e-8 fixed point: 100.0 => 100_00000000)
lf = pl.DataFrame({
    "ts_exchange": [1_000, 2_000, 3_000, 4_000],
    "price": [100_00000000, 101_00000000, 99_00000000, 100_00000000],
    "qty":   [  1_00000000,   1_00000000,  1_00000000,   1_00000000],
    "side":  [            1,           -1,           1,           -1],
    "seq": list(range(4)),
}).lazy()

class MyStrategy:
    def on_tick(self, tick: dict, ctx):
        # Example: place a passive order using the received tick
        ctx.submit_order(
            symbol_id=int(tick["symbol_id"]),
            side=1,  # 1=Buy, -1=Sell
            price=int(tick["price"]),
            qty=1_00000000,
        )

bt = Backtester(
    data={"binance:BTC/USDT": lf},
    seed=42,
    python_mode="tick",    # or "batch"
    batch_ms=100,
    feed_latency_ns=1_000,  # applies ts_local = ts_exchange + 1_000 (ns)
)

result = bt.run(MyStrategy())
print(result.stats())
print(result.trades())

Batch mode (higher throughput)

class MyBatch:
    def on_ticks(self, ticks: list[dict], ctx):
        for t in ticks:
            ctx.submit_order(symbol_id=t["symbol_id"], side=1, price=t["price"], qty=1_00000000)

bt = Backtester(data={"binance:BTC/USDT": lf}, seed=42, python_mode="batch", batch_ms=50)
res = bt.run(MyBatch())

Arrow zero-copy path (for large datasets)

# Pass a PyArrow RecordBatchReader implementing __arrow_c_stream__
res = bt.run_arrow(stream=rb_reader, strategy=MyBatch())

Build, Test, Benchmarks

  • Python tests: pytest -q
    • Benchmarks only: pytest -m bench -q
  • Rust build: cargo build -p backtester-core
  • Rust tests: cargo test -p backtester-core
  • Rust benches: cargo bench -p backtester-core

Note: python/tests/conftest.py auto-runs maturin develop if the extension isn't installed.


Benchmark Configuration (Practical Conditions)

Criterion benches in Rust can be tuned via environment variables (defaults: 4 symbols × 250k ticks each):

  • BACKTEST_BENCH_NSYMBOLS (default: 4)
  • BACKTEST_BENCH_TICKS_PER_SYMBOL (default: 250000)
  • BACKTEST_BENCH_DT_NS tick spacing in ns (default: 1000)
  • BACKTEST_BENCH_SYMBOL_STAGGER_NS per-symbol start offset in ns (default: 10000)
  • BACKTEST_BENCH_FEED_LATENCY_NS (default: 2000000)
  • BACKTEST_BENCH_ORDER_UPDATE_LATENCY_NS (default: 1000000)
  • BACKTEST_BENCH_ORDER_LATENCY_NS (default: 500000)
  • BACKTEST_BENCH_SUBMIT_EVERY_N order submission interval in ticks (default: 256)
  • BACKTEST_BENCH_MAX_BATCH_NS batch-mode window in ns (default: 10000000)

Example:

# 8 symbols × 500k ticks per symbol, 5ms batch window
BACKTEST_BENCH_NSYMBOLS=8 \
BACKTEST_BENCH_TICKS_PER_SYMBOL=500000 \
BACKTEST_BENCH_MAX_BATCH_NS=5000000 \
cargo bench -p backtester-core --bench bench_core

The E2E benches (bench_engine_e2e_*) measure both Tick and Batch modes under identical conditions. Synthetic data is deterministic and includes realistic order flow (opposite-side, same-price passive limit orders at a fixed interval).

Profile-Guided Optimization (PGO)

To build with PGO for maximum performance (Linux/macOS):

# Requires llvm-profdata (part of LLVM tools)
make pgo

This runs a 4-step pipeline:

  1. Instrumentation build
  2. Profile generation (runs benchmarks)
  3. Profile merge
  4. Optimized build using profiles

Expected improvement: 5-15% throughput.


Examples (example/)

  • example/colab_backtester_demo.ipynb
    • Minimal E2E demo notebook. Click the badge above to run in Colab.
    • For local use, start Jupyter from the repo root and open files under example/.
  • example/crypto_researcher_adoption_guide.md
    • Practical guide for researchers: onboarding, data schema, strategy modes (tick/batch), performance tuning.

Note: Large datasets are not bundled. Start with the minimal data generated by tests in python/tests/ or the Colab demo.


Coding Style & Core Principles

  • Rust: edition 2024, cargo fmt / cargo clippy. Naming: functions/modules snake_case, types CamelCase.
  • Python: PEP 8, 4-space indent. Type hints required for new/changed code.
  • Determinism first: fixed RNG seeds, stable ordering. Avoid f64 for monetary logic (only at I/O boundaries).

Contribution & Commit Guidelines

  • Start with docs/SPEC.md and docs/PLAN.md.
  • Conventional Commits: e.g., feat(core): add queue model, chore(fmt): rustfmt.
  • Branches: feature/..., fix/..., chore/....
  • PRs should include What/Why, linked issues, test plan (commands + results), and performance notes if core paths changed. Update docs/ when APIs or architecture shift.

Status & Roadmap

This project is under active development (WIP). APIs and internals may change.

  • Technical spec: docs/SPEC.md
  • Plan/tests/benches: docs/PLAN.md
  • Researcher adoption guide: example/crypto_researcher_adoption_guide.md
  • Colab demo: example/colab_backtester_demo.ipynb

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

crypto_rs_backtester-0.1.2.tar.gz (101.4 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

crypto_rs_backtester-0.1.2-cp39-abi3-win_amd64.whl (2.1 MB view details)

Uploaded CPython 3.9+Windows x86-64

crypto_rs_backtester-0.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.8 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ ARM64

crypto_rs_backtester-0.1.2-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl (3.6 MB view details)

Uploaded CPython 3.9+macOS 10.12+ universal2 (ARM64, x86-64)macOS 10.12+ x86-64macOS 11.0+ ARM64

File details

Details for the file crypto_rs_backtester-0.1.2.tar.gz.

File metadata

  • Download URL: crypto_rs_backtester-0.1.2.tar.gz
  • Upload date:
  • Size: 101.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for crypto_rs_backtester-0.1.2.tar.gz
Algorithm Hash digest
SHA256 00310463db89d33895cb3d4269a0b23f92088aedb1e3b12d89a33d437ab56eda
MD5 154383b0e633761fab2d5aacbfbc1158
BLAKE2b-256 373426f51befd2ddd5107b05166b2480339f38aa9880990bc1a7c6b0d84f2240

See more details on using hashes here.

Provenance

The following attestation bundles were made for crypto_rs_backtester-0.1.2.tar.gz:

Publisher: release.yml on takurot/crypto-rs-backtester

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file crypto_rs_backtester-0.1.2-cp39-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for crypto_rs_backtester-0.1.2-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 44f7862ef9040c15490b2b551c91d855fe503fbd9c0b86e5d9be77755a6476a9
MD5 f493af99d24b70b0f993aca06f567b5c
BLAKE2b-256 bb7ac11f71c5af6ed66346faf7efecb5c949a3a2d20aa04fb800f6907a2dce11

See more details on using hashes here.

Provenance

The following attestation bundles were made for crypto_rs_backtester-0.1.2-cp39-abi3-win_amd64.whl:

Publisher: release.yml on takurot/crypto-rs-backtester

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file crypto_rs_backtester-0.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for crypto_rs_backtester-0.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 98d75da7bd1f5a7dbe0364f707e22ed135bbf5ac4cf70f4445e10243bb4077be
MD5 8c439c7b7fc90701750e44980d30ebf2
BLAKE2b-256 42dda812c08c07ebcb4623804321ed15f259d0d990617b5717451d1ebf80310c

See more details on using hashes here.

Provenance

The following attestation bundles were made for crypto_rs_backtester-0.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on takurot/crypto-rs-backtester

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file crypto_rs_backtester-0.1.2-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl.

File metadata

File hashes

Hashes for crypto_rs_backtester-0.1.2-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Algorithm Hash digest
SHA256 4e23fd9db51d3733929463051c5369aaf9bf2803de3fc9d3caa53c3bc01bc766
MD5 f056e50bb434a583cba962f8dca3d0ea
BLAKE2b-256 930e8974891ad6d38f8dd9e08726b5e78bf3c74afe39f45a9574a6c92fdae9f5

See more details on using hashes here.

Provenance

The following attestation bundles were made for crypto_rs_backtester-0.1.2-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl:

Publisher: release.yml on takurot/crypto-rs-backtester

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.2 This release

4 files

0.1.1

5 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page