crypto-rs-backtester
Video
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
MarketViewafter feed latency. Order arrival and ACK are strictly ordered onts_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 Pythonpython/: Python packagerust_backtester/and tests inpython/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_event ≈ ts_exchange, size ≈ qty
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
- Benchmarks only:
- 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_NStick spacing in ns (default:1000)BACKTEST_BENCH_SYMBOL_STAGGER_NSper-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_Norder submission interval in ticks (default:256)BACKTEST_BENCH_MAX_BATCH_NSbatch-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:
- Instrumentation build
- Profile generation (runs benchmarks)
- Profile merge
- 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/modulessnake_case, typesCamelCase. - Python: PEP 8, 4-space indent. Type hints required for new/changed code.
- Determinism first: fixed RNG seeds, stable ordering. Avoid
f64for monetary logic (only at I/O boundaries).
Contribution & Commit Guidelines
- Start with
docs/SPEC.mdanddocs/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
Metadata
Release files for crypto-rs-backtester 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| crypto_rs_backtester-0.1.2.tar.gz | 101.4 kB | Details |
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| crypto_rs_backtester-0.1.2-cp39-abi3-win_amd64.whl | CPython 3.9 | abi3 | Windows x86-64 | Details |
| crypto_rs_backtester-0.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | CPython 3.9 | abi3 | Linux glibc 2.17+ ARM64 | Details |
| crypto_rs_backtester-0.1.2-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl | CPython 3.9 | abi3 | macOS 10.12+ universal2 (ARM64, x86-64), macOS 11.0+ ARM64, macOS 10.12+ x86-64 | Details |
Total release size: 7.6 MB
Release files / crypto_rs_backtester-0.1.2.tar.gz
| Download URL | crypto_rs_backtester-0.1.2.tar.gz |
|---|---|
| Size | 101.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
00310463db89d33895cb3d4269a0b23f92088aedb1e3b12d89a33d437ab56eda
|
|
BLAKE2b-256 checksum How to use checksums |
373426f51befd2ddd5107b05166b2480339f38aa9880990bc1a7c6b0d84f2240
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 3, 2026.
Transparency logRelease files / crypto_rs_backtester-0.1.2-cp39-abi3-win_amd64.whl
| Download URL | crypto_rs_backtester-0.1.2-cp39-abi3-win_amd64.whl |
|---|---|
| Size | 2.1 MB |
| Tags | CPython 3.9 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
44f7862ef9040c15490b2b551c91d855fe503fbd9c0b86e5d9be77755a6476a9
|
|
BLAKE2b-256 checksum How to use checksums |
bb7ac11f71c5af6ed66346faf7efecb5c949a3a2d20aa04fb800f6907a2dce11
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 3, 2026.
Transparency logRelease files / crypto_rs_backtester-0.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | crypto_rs_backtester-0.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 1.8 MB |
| Tags | CPython 3.9 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
98d75da7bd1f5a7dbe0364f707e22ed135bbf5ac4cf70f4445e10243bb4077be
|
|
BLAKE2b-256 checksum How to use checksums |
42dda812c08c07ebcb4623804321ed15f259d0d990617b5717451d1ebf80310c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 3, 2026.
Transparency logRelease files / crypto_rs_backtester-0.1.2-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
| Download URL | crypto_rs_backtester-0.1.2-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl |
|---|---|
| Size | 3.6 MB |
| Tags | CPython 3.9 abi3 macOS 10.12+ universal2 (ARM64, x86-64) macOS 10.12+ x86-64 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
4e23fd9db51d3733929463051c5369aaf9bf2803de3fc9d3caa53c3bc01bc766
|
|
BLAKE2b-256 checksum How to use checksums |
930e8974891ad6d38f8dd9e08726b5e78bf3c74afe39f45a9574a6c92fdae9f5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 3, 2026.
Transparency log