Rust-based tick-level backtester (WIP)
Project description
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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
00310463db89d33895cb3d4269a0b23f92088aedb1e3b12d89a33d437ab56eda
|
|
| MD5 |
154383b0e633761fab2d5aacbfbc1158
|
|
| BLAKE2b-256 |
373426f51befd2ddd5107b05166b2480339f38aa9880990bc1a7c6b0d84f2240
|
Provenance
The following attestation bundles were made for crypto_rs_backtester-0.1.2.tar.gz:
Publisher:
release.yml on takurot/crypto-rs-backtester
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
crypto_rs_backtester-0.1.2.tar.gz -
Subject digest:
00310463db89d33895cb3d4269a0b23f92088aedb1e3b12d89a33d437ab56eda - Sigstore transparency entry: 1706507663
- Sigstore integration time:
-
Permalink:
takurot/crypto-rs-backtester@ceab23de926c4a31e70d0270ae23046e4d0fe909 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/takurot
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ceab23de926c4a31e70d0270ae23046e4d0fe909 -
Trigger Event:
release
-
Statement type:
File details
Details for the file crypto_rs_backtester-0.1.2-cp39-abi3-win_amd64.whl.
File metadata
- Download URL: crypto_rs_backtester-0.1.2-cp39-abi3-win_amd64.whl
- Upload date:
- Size: 2.1 MB
- Tags: CPython 3.9+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
44f7862ef9040c15490b2b551c91d855fe503fbd9c0b86e5d9be77755a6476a9
|
|
| MD5 |
f493af99d24b70b0f993aca06f567b5c
|
|
| BLAKE2b-256 |
bb7ac11f71c5af6ed66346faf7efecb5c949a3a2d20aa04fb800f6907a2dce11
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
crypto_rs_backtester-0.1.2-cp39-abi3-win_amd64.whl -
Subject digest:
44f7862ef9040c15490b2b551c91d855fe503fbd9c0b86e5d9be77755a6476a9 - Sigstore transparency entry: 1706507724
- Sigstore integration time:
-
Permalink:
takurot/crypto-rs-backtester@ceab23de926c4a31e70d0270ae23046e4d0fe909 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/takurot
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ceab23de926c4a31e70d0270ae23046e4d0fe909 -
Trigger Event:
release
-
Statement type:
File details
Details for the file crypto_rs_backtester-0.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: crypto_rs_backtester-0.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 1.8 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
98d75da7bd1f5a7dbe0364f707e22ed135bbf5ac4cf70f4445e10243bb4077be
|
|
| MD5 |
8c439c7b7fc90701750e44980d30ebf2
|
|
| BLAKE2b-256 |
42dda812c08c07ebcb4623804321ed15f259d0d990617b5717451d1ebf80310c
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
crypto_rs_backtester-0.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
98d75da7bd1f5a7dbe0364f707e22ed135bbf5ac4cf70f4445e10243bb4077be - Sigstore transparency entry: 1706507773
- Sigstore integration time:
-
Permalink:
takurot/crypto-rs-backtester@ceab23de926c4a31e70d0270ae23046e4d0fe909 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/takurot
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ceab23de926c4a31e70d0270ae23046e4d0fe909 -
Trigger Event:
release
-
Statement type:
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
- Download URL: crypto_rs_backtester-0.1.2-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
- Upload date:
- Size: 3.6 MB
- Tags: CPython 3.9+, macOS 10.12+ universal2 (ARM64, x86-64), macOS 10.12+ x86-64, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4e23fd9db51d3733929463051c5369aaf9bf2803de3fc9d3caa53c3bc01bc766
|
|
| MD5 |
f056e50bb434a583cba962f8dca3d0ea
|
|
| BLAKE2b-256 |
930e8974891ad6d38f8dd9e08726b5e78bf3c74afe39f45a9574a6c92fdae9f5
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
crypto_rs_backtester-0.1.2-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl -
Subject digest:
4e23fd9db51d3733929463051c5369aaf9bf2803de3fc9d3caa53c3bc01bc766 - Sigstore transparency entry: 1706507838
- Sigstore integration time:
-
Permalink:
takurot/crypto-rs-backtester@ceab23de926c4a31e70d0270ae23046e4d0fe909 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/takurot
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ceab23de926c4a31e70d0270ae23046e4d0fe909 -
Trigger Event:
release
-
Statement type: