Features
Compute backends
Run quantitative workloads with NumPy, Numba, and native Rust backends
✅ Take your strategy directly to Rust. VBT's native simulators let the same strategy process a full price array or step through individual bars as they arrive. Strategy callbacks can inspect cash, positions, and orders during execution, keeping portfolio state within reach of your trading logic.
Note
The examples below share the strategy definition and require vectorbtpro-rust and ndarray = "0.16".
See the Rust setup guide for installation.
use vectorbtpro_rust::error::VbtResult;
use vectorbtpro_rust::portfolio::enums::Order;
use vectorbtpro_rust::portfolio::simulator::{
FnOrderStrategy, OrderContext, OrderStrategy,
};
fn buy_the_dip() -> impl OrderStrategy {
FnOrderStrategy::new(|ctx: &OrderContext<'_, '_>| {
let price = ctx.close(ctx.col());
let position = ctx.position(ctx.col());
let size = if price <= 100.0 && position == 0.0 {
1.0
} else if price >= 110.0 && position > 0.0 {
-position
} else {
return Ok(None);
};
Ok(Some(Order::builder().size(size).build()))
})
}use ndarray::array;
use vectorbtpro_rust::portfolio::simulator::{OrderSimulator, SimulationConfig};
fn main() -> VbtResult<()> {
let close = array![[100.0], [98.0], [105.0], [112.0]];
let groups = array![1];
let config = SimulationConfig::builder()
.target_shape(close.dim())
.group_lens(groups.view())
.close(close.view())
.init_cash(1000.0)
.build()?;
let simulator = OrderSimulator::builder().config(config).build()?;
let output = simulator.run_single(&mut buy_the_dip())?;
for order in &output.order_records {
println!("row {}: {:.0} share at ${:.0}", order.idx, order.size, order.price);
}
Ok(())
}row 0: 1 share at $100
row 3: 1 share at $112Tutorial
Learn more in the From Python to Rust tutorial.
✅ Install the optional vectorbtpro-rust extension and compatible jitted calls can take the Rust
fast lane automatically. VBT exposes the extension as vbt.rs, registers Rust kernels under jitted="rs",
and falls back to the normal implementation, usually Numba, when Rust is unavailable or unsupported.
data = vbt.YFData.pull("BTC-USD", start="2024")
fast_ma = data.close.vbt.rolling_mean(20)
slow_ma = data.close.vbt.rolling_mean(50)
entries = fast_ma.vbt.crossed_above(slow_ma)
exits = fast_ma.vbt.crossed_below(slow_ma)
pf = vbt.Portfolio.from_signals(
data,
entries=entries,
exits=exits,
sl_stop=0.05,
tp_stop=0.15,
fees=0.001,
)✅ VBT now ships with correctness-aware backend benchmarks for almost the entire codebase. The latest benchmark overview covers 748 functions, 768 variants, 1,976 kernels, 15,709 cases, and 59,600 benchmark runs across Numba, Rust, raw backend calls, AutoBench, and AutoBenchMixed. This makes performance inspectable: users can see which backend wins, at which input size, and whether parallel execution is worth it for their workload.
python -m vectorbtpro.benchmarks.bench_engine_cli \
--backend nb \
--backend rs \
--input-model 1d \
--pattern returnsresults = vbt.run_benchmarks(
input_model="1d",
backend_ids=("nb", "rs"),
patterns=["returns"],
)
print(results) function,task_id,ndim,shape,nb_s,rs_s,speedup,auto_bench_backend,nb_elapsed_s,rs_elapsed_s
...✅ Most Numba-compiled functions have been rewritten to process columns in parallel using
automatic parallelization with @jit.
You can enable this with a single command. This approach is best for lightweight functions that are
applied to wide arrays.
np.random.seed(42)
df = pd.DataFrame(np.random.uniform(size=(1000, 1000)))
%timeit df.rolling(10).mean() 45.6 ms ± 138 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)%timeit df.vbt.rolling_mean(10) 5.33 ms ± 302 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)%timeit df.vbt.rolling_mean(10, jitted=dict(parallel=True)) 1.82 ms ± 5.21 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)✅ Jitting stands for just-in-time compiling. In the VBT universe, however, jitting simply means accelerating. While Numba remains the primary jitter, VBT now allows you to implement custom jitter classes, such as those for vectorized NumPy or even JAX with GPU support. Every jitted function is registered globally, so you can switch between different implementations or even disable jitting entirely with a single command.
data = vbt.YFData.pull("BTC-USD", start="7 days ago")
log_returns = np.log1p(data.close.pct_change())
log_returns.vbt.cumsum() Date
2023-01-31 00:00:00+00:00 0.000000
2023-02-01 00:00:00+00:00 0.024946
2023-02-02 00:00:00+00:00 0.014271
2023-02-03 00:00:00+00:00 0.013310
2023-02-04 00:00:00+00:00 0.008288
2023-02-05 00:00:00+00:00 -0.007967
2023-02-06 00:00:00+00:00 -0.010087
Freq: D, Name: Close, dtype: float64log_returns.vbt.cumsum(jitted=False) Date
2023-01-31 00:00:00+00:00 0.000000
2023-02-01 00:00:00+00:00 0.024946
2023-02-02 00:00:00+00:00 0.014271
2023-02-03 00:00:00+00:00 0.013310
2023-02-04 00:00:00+00:00 0.008288
2023-02-05 00:00:00+00:00 -0.007967
2023-02-06 00:00:00+00:00 -0.010087
Freq: D, Name: Close, dtype: float64@vbt.register_jitted(task_id_or_func=vbt.nb.nancumsum_nb)
def nancumsum_np(arr):
return np.nancumsum(arr, axis=0)
log_returns.vbt.cumsum(jitted="np") Date
2023-01-31 00:00:00+00:00 0.000000
2023-02-01 00:00:00+00:00 0.024946
2023-02-02 00:00:00+00:00 0.014271
2023-02-03 00:00:00+00:00 0.013310
2023-02-04 00:00:00+00:00 0.008288
2023-02-05 00:00:00+00:00 -0.007967
2023-02-06 00:00:00+00:00 -0.010087
Freq: D, Name: Close, dtype: float64✅ Rolling metrics based on returns have been optimized for maximum performance—up to 1000x speedup!
import quantstats as qs
index = vbt.date_range("2020", periods=100000, freq="1min")
np.random.seed(42)
returns = pd.Series(np.random.normal(0, 0.001, size=len(index)), index=index)
%timeit qs.stats.rolling_sortino(returns, rolling_period=10) 2.79 s ± 24.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)%timeit returns.vbt.returns.rolling_sortino_ratio(window=10) 8.12 ms ± 199 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)Copyright © 2021–2026 Oleg Polakow. All rights reserved.
Site content and documentation are provided for using and evaluating VectorBT PRO and for educational purposes. Any other use, including building or supporting competing products or services, requires prior written consent.