A vectorbt alternative built on Rust
vectorbt is an excellent, mature library for vectorized backtesting in Python. If you are weighing it against Manifold-BT, the real question is how your strategy is shaped: vectorized and signal-heavy, or path-dependent with stops and fills. Here is the honest comparison.
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 realistically by default: market-impact slippage, funding, and next-bar fills.
Side by side
| Manifold-BT | vectorbt | |
|---|---|---|
| Core engine | Rust: vectorized signals + a sequential fills pass (hybrid) | NumPy / Numba, vectorized |
| Speed | 353x faster than vectorbt under the same conditions (13ms vs 4.66s); several times faster still on GPU | 4.66s on the same benchmark |
| Path-dependent logic | First-class (stops, partial fills, inventory) | Awkward, often needs manual workarounds |
| Execution realism | Market-impact slippage, funding, partial fills, signal delay | Simplified; costs as flat parameters |
| Parameter search | Parallel sweeps, 2-D heatmaps, walk-forward | Vectorized parameter grids |
| Live trading | No, research only | No, research only |
| API style | Expression DSL, no pandas required | Pandas / NumPy-centric |
| Best for | Realistic, path-dependent research at scale and speed | Quick vectorized signals in a pandas-native API |
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:
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.
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)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: realistic execution on by default, 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.
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.