Manifold-BT vs RaptorBT

RaptorBT is a genuinely fast Rust engine, and a good one. But “fast backtester” means nothing without the workload attached. On a single backtest, every serious engine answers in milliseconds. On the parameter sweep that actually validates a strategy, the two separate by more than an order of magnitude, and that is the number that decides your research loop. Here is the head-to-head, measured.

Two engines, two different jobs

RaptorBT is built as a drop-in replacement for vectorbt: the same vectorized model and metric set, re-implemented in Rust so a single backtest runs faster than pure NumPy plus Numba. It is small, focused, and quick on one call.

Manifold-BT is a research engine rather than a faster vectorbt. It ingests real market data, evaluates signals vectorized over the full series in Rust, then runs a sequential pass for fills and PnL on an Apache Arrow columnar core, and it exposes the parameter sweep as a single native call that fans out across every core. That last point is where the benchmark below turns decisive.

About that “5,800x faster than vectorbt” number

It is the headline that makes RaptorBT sound in a different league, so it is worth understanding what it measures. Much of that ratio comes from comparing a warm Rust run against vectorbt’s first call, which pays Numba’s JIT compilation cost up front. It is a statement about vectorbt’s cold start, not a moat over other Rust engines, and you cannot line it up against Manifold-BT’s own “faster than vectorbt” figure because the two are measured against different baselines. The only comparison that means anything is the two engines, same machine, same strategy. That is what follows.

One backtest: everyone answers in milliseconds

On a single call there is barely a story. RaptorBT is the fastest small-series engine: about 0.02ms on 1,000 bars, where Manifold-BT pays a small fixed floor and vectorbt pays roughly 3.3ms of JIT overhead. From around 100,000 bars the lead flips, and it holds. At one million bars, Manifold-BT answers in 20ms with the full metric suite, RaptorBT in 35ms, vectorbt in 62ms.

Put that crossover in context. At one-minute resolution, 100,000 bars is under ten weeks of data, and one million is under two years. So RaptorBT’s single-call edge only really covers tiny series, a week or two of minute bars; on any realistic minute-resolution backtest, months or years long, Manifold-BT is already ahead on a single call, before you even reach the sweeps below.

Per-call latency of Manifold-BT, RaptorBT and vectorbt against backtest size, single core, log-log. RaptorBT is fastest below ~10k bars; Manifold-BT leads from 100k bars up, 20ms vs 35ms at 1M bars.
One backtest, one core, log-log. RaptorBT wins the smallest series; Manifold-BT takes the lead from ~100k bars and holds it to 1M.

At a human scale, all three are instant. Nobody’s research is gated by a call that costs tens of milliseconds. If engine speed only mattered here, the choice would be indifferent. It is the next workload that decides it.

The map is where they separate

Validating a strategy is not one backtest, it is thousands: sweep the parameter grid, bound the stable plateau, then repeat it for every walk-forward fold and robustness check. That is tens of thousands to millions of calls per idea, and sweep throughput is the metric that actually governs a research loop.

Here the two are built very differently. Manifold-BT runs the sweep as one native call: the grid goes to the Rust core, data is loaded once, and combinations fan out across all cores with no Python in the loop. RaptorBT has no sweep API, so the only option is a Python for-loop over single backtests, one core, the GIL in the middle. Same grid, same data, full-sweep wall time:

Full sweep wall time by engine across small, medium and large grids, log scale. Manifold-BT on 20 cores is 12ms, 306ms for RaptorBT and 2.4s for vectorbt on the medium grid.
Full sweep wall time, log scale. On the medium grid Manifold-BT runs in 12ms against RaptorBT's 306ms (25x); on the large grid, 135ms against 2.9s (22x).

Parallelism is not the whole story. Pinned to a single core, Manifold-BT still runs the large sweep about 4x faster than RaptorBT’s loop, because the batch shares data loading and stays in native code between combinations; the rest is the twenty cores a GIL-serialised loop cannot use. And this is the axis where GPU offload, which RaptorBT does not have, keeps scaling Manifold-BT further.

What that looks like in a working session

Put a real research session behind the ratio. A parameter map of 250,000 backtests, the kind of grid that actually bounds a strategy’s stable region, takes about 7.7 seconds on Manifold-BT’s twenty cores, and 2 seconds on the GPU. The same map in a RaptorBT loop is 3.2 minutes.

Time to run a 250,000-backtest parameter map by engine, log scale. Manifold-BT GPU 2.0s, 20 cores 7.7s, RaptorBT Python loop 3.2 min, vectorbt chunked 24.7 min.
Time to run a 250,000-backtest parameter map. At 7.7s the map is interactive; at 3.2 minutes it becomes a batch job.

At a few seconds the map is interactive: you read it, change a threshold, run it again. At several minutes it becomes a batch job, and in practice you stop drawing maps and fall back to point estimates, which is exactly the habit that overfits a strategy.

The sweep RaptorBT has no API for

Both tabs run the identical sweep, the same SMA crossover over the same grid, with the same costs. The only difference is the API: in Manifold-BT the whole grid is one native call that fans out across all cores, while RaptorBT has no sweep API, so it becomes a hand-written Python loop over single backtests, one core at a time.

import manifoldbt as mbt
from manifoldbt.indicators import close, sma

# SMA crossover, the two periods declared as swept parameters
fast, slow = sma(close, mbt.param("fast")), sma(close, mbt.param("slow"))
strategy = (
    mbt.Strategy.create("sma_cross")
    .signal("fast", fast)
    .signal("slow", slow)
    .size(mbt.when(fast > slow, 1.0, 0.0))
)

# One native call: the whole grid fans out across every core.
# (config + store are set up once, elsewhere.)
sweep = mbt.run_sweep(
    strategy,
    param_grid={"fast": range(5, 100), "slow": range(10, 300)},
    config=config, store=store,
)
best = sweep.best("sharpe")

When to choose Manifold-BT

Choose Manifold-BT when high performance means the workload that actually validates a strategy, not just the latency of one call: real market data in, realistic execution and funding modeled by default, a deep indicator library, walk-forward and Monte Carlo, and parameter sweeps that run more than 20x faster and scale from all your cores onto the GPU.

Benchmark notes: one machine (Windows 11, 20 logical cores), a synthetic random walk, an SMA(10/50) crossover translated exactly into each engine, zero transaction cost, timed fastest-of-N with warmup discarded. raptorbt 0.4.0, vectorbt 0.28.4. This is a vendor benchmark, Manifold-BT is our engine, so the harness, the exact strategy translations, and the raw timings are published for you to rerun. RaptorBT is genuinely the fastest engine on small single calls; the ranking that survives the details is the sweep, where the gap is structural.

Keep reading

Run your first backtest

Install Manifold-BT and reproduce the backtest above in seconds. The Rust core runs years of bars sub-second so you can sweep parameters instead of waiting.

$pip install manifoldbt