docs.rs failed to build wbt-0.1.1
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
wbt
High-performance position-weighted backtesting engine, written in Rust with Python bindings.
Overview
wbt (Weight Back Test) is a standalone library for backtesting trading strategies based on position weights. It computes daily P&L attribution, trade pair matching (FIFO), and comprehensive performance statistics — all powered by a zero-copy, cache-friendly Rust core with rayon-based parallelism.
- Rust crate (
wbt): pure Rust library with core engine - Python package (
wbt): PyO3 bindings + pandas-friendly API via Arrow IPC
Installation
Python
# Requires: Rust toolchain, maturin, Python >= 3.9
&&
&&
Rust
# Cargo.toml
[]
= "0.1"
Quick Start
Python
# Prepare input: DataFrame with columns [dt, symbol, weight, price]
=
=
# dict: Sharpe, Calmar, max drawdown, win rate, etc.
# DataFrame: per-symbol daily returns with 'total' column
# DataFrame: per-symbol daily detail (edge, return, cost, turnover, ...)
# DataFrame: strategy excess return vs. benchmark
# DataFrame: all matched trade pairs (FIFO)
Rust
use ;
use *;
let dfw: DataFrame = /* build your DataFrame with [dt, symbol, weight, price] */;
let mut wb = new?;
wb.backtest?;
let report = wb.report.as_ref.unwrap;
println!;
Input Format
| Column | Type | Description |
|---|---|---|
dt |
datetime | Bar end time; must be a continuous trading time series with no gaps |
symbol |
str | Instrument code |
weight |
float | Position weight at bar end; independent across symbols; positive = long, negative = short, 0 = flat |
price |
float | Trade price (close, next-bar open, TWAP/VWAP, etc.) |
Parameters
| Parameter | Default | Description |
|---|---|---|
digits |
2 |
Decimal places for weight rounding |
fee_rate |
0.0002 |
One-way transaction cost (commission + slippage) |
n_jobs |
1 |
Number of parallel threads (rayon thread pool) |
weight_type |
"ts" |
"ts" (time-series: equal-weight average across symbols) or "cs" (cross-section: sum across symbols) |
yearly_days |
252 |
Trading days per year for annualization |
Output Properties
| Property | Type | Description |
|---|---|---|
stats |
dict | Full performance report (Sharpe, Calmar, drawdown, win rate, trade metrics, ...) |
daily_return |
DataFrame | Pivoted daily returns per symbol + total column |
dailys |
DataFrame | Per-symbol daily detail: n1b, edge, return, cost, turnover, long/short splits |
alpha |
DataFrame | Strategy vs. benchmark excess return |
alpha_stats |
dict | Performance statistics on the excess return series |
pairs |
DataFrame | All FIFO-matched trade pairs with P&L, hold bars, direction |
long_daily_return |
DataFrame | Long-only daily returns |
short_daily_return |
DataFrame | Short-only daily returns |
long_stats |
dict | Long-only performance statistics |
short_stats |
dict | Short-only performance statistics |
bench_stats |
dict | Benchmark (equal-weight n1b) performance statistics |
Architecture
wbt/
├── src/
│ ├── lib.rs # PyO3 bindings (Arrow IPC in/out)
│ └── core/ # Pure Rust engine
│ ├── mod.rs # WeightBacktest struct & public API
│ ├── native_engine.rs # Zero-copy parallel engine (rayon)
│ ├── daily_performance.rs # Sharpe, Calmar, drawdown, etc.
│ ├── evaluate_pairs.rs # Trade pair statistics
│ ├── backtest.rs # Orchestration & alpha computation
│ ├── report.rs # Report structs & JSON serialization
│ ├── trade_dir.rs # Trade direction & action enums
│ ├── errors.rs # Error types
│ └── utils.rs # WeightType, rounding, quantile
└── python/wbt/ # Python API wrapper
├── backtest.py # WeightBacktest class (pandas-friendly)
└── _df_convert.py # Arrow <-> pandas conversion
Benchmark
Tested on China A-share daily data (2017-01 to 2025-04), Apple M-series, 8 threads, release build with LTO:
| Dataset | Rows | Symbols | Time | Throughput |
|---|---|---|---|---|
| Full A-share daily | 8,440,404 | 5,351 | 0.63s | 13.4M rows/s |
Outputs produced in a single pass:
| Output | Size | Description |
|---|---|---|
dailys |
8,435,053 rows × 15 cols | Per-symbol daily attribution |
pairs |
942,679 rows × 11 cols | FIFO-matched trade pairs |
daily_return |
2,023 rows | Equal-weight portfolio daily return |
alpha |
2,023 rows × 4 cols | Strategy vs. benchmark excess return |
stats |
29 metrics | Sharpe, Calmar, drawdown, win rate, etc. |
Performance Design
- O(N) counting sort for symbol grouping (replaces generic sort)
- Struct-of-Arrays (SoA) layout for cache-locality in hot loops
- Lazy DataFrame materialization — internal computation uses raw vectors; DataFrames built only on demand
- FIFO lot matching via stack-based SoA with dynamic resizing
- rayon data-parallelism across symbols with configurable thread pool
- Zero-copy slicing from Polars DataFrames into contiguous memory blocks
- Arrow IPC for zero-serialization-overhead Python <-> Rust data transfer