A vectorbt alternative built on Rust

Updated

Choose Manifold-BT when the result depends on execution (stops, partial fills, funding, size-aware slippage) or on large parameter sweeps; choose vectorbt when you think in pandas and screen vectorized signals. On the same backtest with the same metric suite, Manifold-BT ran 375x faster than vectorbt, 217.2 ms against 81.49 s over 10,000,000 bars, in the public CI run of 2026-11-12, where both engines were installed from PyPI and their results compared before either was timed; the full table is on the benchmarks page. That run measured vectorbt 0.28.4 on its numba path, before the optional Rust backend vectorbt added in 1.0.

Where the two are built differently

vectorbt expresses the whole backtest as operations over NumPy arrays, signals and execution alike. That is extremely fast for signal research and sweeping huge grids of simple rules, and it fits naturally if you already think in pandas.

Manifold-BT evaluates signals the same vectorized way, over the full series in Rust, then runs a sequential pass for fills and PnL. That keeps the speed, years of bars in sub-second, while handling path-dependent logic, stop-losses, take-profits, partial fills, inventory, that is cumbersome to vectorize. It also models execution explicitly: market-impact slippage, funding, and a configurable signal delay.

Side by side

 Manifold-BTvectorbt
Core engineRust: vectorized signals + a sequential fills pass (hybrid)NumPy / Numba, vectorized
Speed375x faster than vectorbt on the same backtest, metrics included (217.2 ms against 81.49 s over 10,000,000 bars, measured in public CI on 2026-11-12); faster again on GPU for large parameter sweeps81.49 s on the same benchmark
Path-dependent logicFirst-class (stops, partial fills, inventory)Awkward, often needs manual workarounds
Execution realismMarket-impact slippage, funding, partial fills, signal delaySimplified; costs as flat parameters
Parameter searchParallel sweeps, 2-D heatmaps, walk-forwardVectorized parameter grids
Live tradingNo, research onlyNo, research only
API styleExpression DSL, no pandas requiredPandas / NumPy-centric
Best forRealistic, path-dependent research at scale and speedQuick vectorized signals in a pandas-native API

What the 375x figure measures

One moving-average crossover, run by both engines over the same 10,000,000 bars with the same metric suite computed at the end, since nobody runs a backtest and then declines to know its Sharpe. Both engines come from PyPI, the same wheels a user installs, on a GitHub-hosted runner. Before anything is timed, the run checks that the two produced the same final equity, the same round trips and the same fees; a workload where they disagree is never quoted as a speedup.

Two limits. The figure was measured against vectorbt 0.28.4 on its numba path, and vectorbt 1.0 has since added an optional Rust backend that this run did not cover. And it is one workload of many: every figure on this page is read from the same run as the benchmarks page and moves with it when a new run is published. That page lists every workload, series length, sweep and memory reading, including the points where the engines diverge and the ones where the gap is much smaller than this one.

The same sweep, with realism intact

Here is a parallel parameter sweep in Manifold-BT. Unlike a vectorized sweep, every combination is evaluated through the full execution engine, so stops and realistic fills apply to each one, with no loss of speed:

sweep.py
import manifoldbt as mbt
from manifoldbt.indicators import close, ema
from manifoldbt.helpers import time_range, Slippage, Interval

fast_p = mbt.param("fast", default=20)
slow_p = mbt.param("slow", default=50)
fast, slow = ema(close, fast_p), ema(close, slow_p)
strategy = (
    mbt.Strategy.create("ema")
    .signal("fast", fast)
    .signal("slow", slow)
    .size(mbt.when(fast > slow, 1.0, 0.0))
)

start, end = time_range("2021-01-01", "2026-01-01")
config = mbt.BacktestConfig(
    universe={"binance": ["BTCUSDT"]},
    time_range_start=start,
    time_range_end=end,
    bar_interval=Interval.hours(1),
    initial_capital=10_000,
    fees=mbt.FeeConfig.binance_perps(),
    slippage=Slippage.fixed_bps(2),
    warmup_bars=50,
)
store = mbt.ingest(provider="binance", symbol="BTCUSDT", symbol_id=1,
                   interval="1h", start="2021-01-01T00:00:00Z",
                   end="2026-01-01T00:00:00Z")

# Sweep the whole grid in parallel on Rust, with stops and realistic fills intact
sweep = mbt.run_sweep(
    strategy,
    param_grid={"fast": [10, 20, 30], "slow": [50, 100, 150]},
    config=config,
    store=store,
)
print(sweep.best("sharpe"))

Is the DSL heavier than vectorbt?

A fair worry: the pandas-native API of vectorbt is famously terse, so a dedicated DSL can sound like more to learn. In practice it is not heavier, just explicit. The same long-only crossover is a handful of lines in both, and the Manifold-BT version reads top to bottom, signal then sizing, with no pandas accessors or array plumbing to wire up.

vectorbt.py
import vectorbt as vbt

price = vbt.YFData.download("BTC-USD").get("Close")
fast = vbt.MA.run(price, 20)
slow = vbt.MA.run(price, 50)
entries = fast.ma_crossed_above(slow)
exits = fast.ma_crossed_below(slow)
pf = vbt.Portfolio.from_signals(price, entries, exits, fees=0.001)
manifold.py
import manifoldbt as mbt
from manifoldbt.indicators import close, ema

fast, slow = ema(close, 20), ema(close, 50)
strategy = (
    mbt.Strategy.create("ema_cross")
    .signal("fast", fast)
    .signal("slow", slow)
    .size(mbt.when(fast > slow, 1.0, 0.0))
)
result = mbt.run(strategy, config, store)

The gap opens the moment you add path-dependent logic. A stop-loss or a take-profit is one chained call in Manifold-BT, for example .stop_loss(pct=3.0), whereas a vectorized engine usually means leaving the fast path and hand-rolling the exit. You keep the concise style and still get realistic, path-dependent execution.

Which should you pick?

Choose Manifold-BT if you want the backtest to match the fill: an explicit execution model, stops, partial fills, funding on crypto perpetuals, and size-aware slippage, path-dependent logic that never leaves the fast path, and large parameter sweeps and walk-forward that finish in seconds, including on GPU. For systematic research where execution and costs decide the edge, that is the tool. vectorbt stays a fair pick only if you already live in its pandas ecosystem and your work is purely vectorized, cross-sectional signal research.

Frequently asked questions

Is Manifold-BT a drop-in replacement for vectorbt?

No. The API is different: a Manifold-BT strategy is declared as expressions in a Python DSL (signals, then sizing), not as pandas arrays passed to Portfolio.from_signals, so moving a strategy over means rewriting its definition. For a crossover that is a handful of lines, as the side-by-side code on this page shows. If you want to keep vectorbt's API and swap the engine underneath, RaptorBT is the project built as a drop-in vectorbt replacement; Manifold-BT vs RaptorBT is compared on its own page.

Which is faster, Manifold-BT or vectorbt?

On the public CI run of 2026-11-12, Manifold-BT: 217.2 ms against 81.49 s for the same backtest with the same metric suite over 10,000,000 bars, 375x faster. Both engines were installed from PyPI on a GitHub-hosted runner and their final equity, round trips and fees were compared before anything was timed. The run measured vectorbt 0.28.4 on its numba path; vectorbt 1.0 added an optional Rust backend, which has not been measured here yet. Parameter sweeps, cold start and memory are on the benchmarks page, which shows the same run.

Does vectorbt model slippage and funding?

Slippage, yes; funding, not as a built-in. In the open-source version's order model, fees are a percentage of order value plus an optional fixed fee per order, slippage is a penalty applied to the price, and an order larger than max_size is partly filled. There is no funding-rate or borrow-cost field, so carry on perpetuals or shorts has to be added by hand. Manifold-BT applies perpetual funding per bar from a funding-rate column in your data, charges borrow on shorts in annualised basis points, offers fixed, spread-based and volume-impact slippage, and caps fills at a share of each bar's volume.

Can vectorbt handle stops and path-dependent logic?

Yes. from_signals supports stop-loss and take-profit exits, and order functions with numba callbacks can express most path-dependent rules. The fair criticism is that the second route is awkward and thinly documented. In Manifold-BT a stop or a target is one chained call on the strategy, applied by the sequential fills pass.

Is Manifold-BT free? Is vectorbt?

Both have a free version. Manifold-BT's Community tier runs the full-speed engine, including for commercial use, with sweeps capped at 256 backtests, daily output resolution and 1,000 Monte Carlo simulations; the Python package is Apache 2.0 and the engine ships as a compiled wheel under the Manifold-BT Engine License. Pro, at $16/mo billed annually, or $19 month to month, lifts those caps and adds walk-forward. vectorbt is source-available under Apache 2.0 with a Commons Clause, which forbids selling a product built on it; vectorbt PRO is a separate membership at $25 a month, $240 a year or $500 for life, for personal use.

Does either one trade live?

No. Both are research tools and neither places orders. The usual workflow is to find and validate the strategy in one of them, then deploy it on a platform built for execution, such as NautilusTrader or a broker API.

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