Exocet92andGitHub 6cae686283 bench: add a cost workload and a multi-asset one, and go to three repetitions (#10)
Two gaps a reader could name without running anything: costs appeared on one
workload out of four, and nothing in the suite was a portfolio.

Costs could not simply be switched on across the board, and the reason is
measured. On FractionOfEquity sizing a 5 bps fee puts the engines 1.3e-4 of
capital apart and 2 bps of slippage 2.1e-5, against a 1e-9 tolerance, while the
round-trip counts stay identical: the trading agrees, the cost arithmetic does
not, because one charges the fee on top of the notional and the other reserves
it out of cash first. In fixed units both land exactly, to 1e-15. So
`sma_cross_costs` carries a fee and slippage on the headline signal, sized in
units, and the price of that is visible rather than hidden: x48.0 against x50.6
at 100k bars.

`multi_asset` runs five independent series in one shared book. It is the
workload manifoldbt does worst on, and it is here for that reason: going from
one asset to five costs it 6.1x and vectorbt 1.4x, so the ratio falls from x36.7
to x8.8 at a million bars. Broadcasting a column per asset is close to free;
walking five books is not. A portfolio is also what people actually run, and a
suite that only measures where it wins is not evidence.

Both are capped where a materialised five-column simulation would stop measuring
the engine and start measuring the swap file, and `ema_rsi_fees` keeps the
ceiling it got for going bankrupt.

Repetitions go from two to three: the floor at which a median is a median rather
than the mean of two.
2026-08-20 18:45:05 +02:00
2026-08-18 00:19:32 +00:00
2026-08-17 00:02:26 +00:00
2026-08-17 00:02:26 +00:00
2026-08-18 12:29:01 +00:00

ManifoldBT logo

ManifoldBT
Rust-powered backtesting engine for quantitative research

Discord

Website · Documentation · Examples


ManifoldBT is a Python backtesting library with a Rust core. Strategies are written in a fluent Python DSL, compiled to a vectorized Rust expression graph, then run through a sequential fill simulation with realistic fees, slippage, funding and look-ahead protection. Vectorized speed with event-driven execution realism.

Why ManifoldBT

  • Fast — 500K bars in ~13 ms. 353x faster than vectorbt, ~3,500x faster than backtrader.
  • Expressive — fluent DSL with 30+ indicators, conditional logic, cross-asset references
  • Rigorous — Monte Carlo, walk-forward, parameter sweeps, lookahead detection, exposure diagnostics
  • Portablepip install, no Rust toolchain needed. Works on Python 3.9+.

Installation

pip install manifoldbt              # engine only: backtests, sweeps, metrics
pip install manifoldbt[plot]        # + interactive charts and native windows (show=True)
pip install manifoldbt[all]         # everything: plots, windows, PNG export, pandas/polars

The base install stays light (no browser, no GUI) for scripts, servers and CI. [plot] adds plotly and a native window backend; [all] also pulls kaleido for static PNG/SVG export (which bundles a headless Chromium).

Quick Start

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

fast = ema(close, 12)
slow = ema(close, 26)

strategy = (
    mbt.Strategy.create("ema_crossover")
    .signal("fast", fast)
    .signal("slow", slow)
    .signal("signal", mbt.when(fast > slow, mbt.lit(1.0), mbt.lit(-1.0)))
    .size(mbt.col("signal") * mbt.lit(0.25))
)

start, end = time_range("2022-01-01", "2025-01-01")

config = mbt.BacktestConfig(
    universe=[1],
    time_range_start=start,
    time_range_end=end,
    bar_interval=Interval.hours(12),
    initial_capital=10_000,
    execution=mbt.ExecutionConfig(allow_short=True, max_position_pct=0.5),
    fees=mbt.FeeConfig.binance_perps(),
    slippage=Slippage.fixed_bps(2),
    warmup_bars=30,
)

store = mbt.ingest(provider="binance", symbol="BTCUSDT", symbol_id=1,
                   start="2022-01-01T00:00:00Z", end="2025-01-01T00:00:00Z", interval="1h")
result = mbt.run(strategy, config, store)
print(result.summary())

Loading data

Bring your own data, or pull it from a built-in connector — both return a DataStore ready for mbt.run(...).

CSV — free on all tiers, auto-detects standard / MetaTrader 4 / MetaTrader 5:

store = mbt.import_csv("EURUSD_1m.csv", symbol="EURUSD", symbol_id=1,
                       interval="1m", asset_class="forex")

Exchange connectors — Binance, Bybit, Hyperliquid, dYdX, Bitstamp (free); Databento, Massive (Pro):

store = mbt.ingest(provider="binance", symbol="BTCUSDT", symbol_id=1,
                   start="2024-01-01T00:00:00Z", end="2025-01-01T00:00:00Z")

Or from the CLI:

manifoldbt import-csv data.csv --symbol EURUSD --symbol-id 1 --interval 1m
manifoldbt ingest --provider binance --symbol BTCUSDT --symbol-id 1 --start ... --end ...

Examples

# Example What it shows
00 Template Minimal starting point
01 Trend Following EMA crossover, volume filter, stop-loss
02 Mean Reversion EMA crossover with parameter sweep
03 Multi-Asset Momentum Cross-asset signals
04 Linear Regression Regression-based signal
05 Statistical Arbitrage Pairs trading, spread z-score
06 Full Visualization Tearsheet and charts
07 Walk-Forward Out-of-sample validation
08 2D Sweep Parameter grid heatmap
09 3D Surface Parameter surface plot
10 Monte Carlo Permutation-based robustness
11 Portfolio Multi-strategy portfolio
12 Diagnostics Lookahead & exposure safety checks
13 Stochastic Simulation SDE path simulation (GBM, Heston, …)
14 Multi-Timeframe Combining signals across timeframes
15 Cross-Exchange Signal on one venue, execute on another
16 Exogenous Data External series (e.g. hashrate) as a signal
17 Per-Venue Fees Per-venue funding & borrow costs
18 CSV Import Load OHLCV from CSV (standard / MT4 / MT5)

Performance

EMA(12/26) + RSI(14) on 500K synthetic 1-min bars (manifoldbt/vectorbt: median of 5 runs; backtrader: median of 3):

Engine Time vs ManifoldBT
ManifoldBT (Rust) 13 ms 1x
vectorbt (NumPy) 4,662 ms 353x slower
backtrader (Python) 46,944 ms ~3,556x slower

ManifoldBT and vectorbt produce identical results (30.23% vs 30.24% return, same trade count); backtrader's event-driven fills give a different PnL.

Reproduce: python benchmarks/bench_vs_competitors.py --rows 500000 --runs 5

How it compares

ManifoldBT vectorbt backtrader Nautilus
Engine Rust (vectorized + sequential fills) Numba/NumPy (vectorized) Python (event-driven) Rust/Python (event-driven)
Execution realism¹ High Basic High High
Focus Backtesting + research Backtesting at scale Backtest + live Backtest + live (production)

¹ fees, slippage, funding, partial fills, look-ahead detection.

On GPU (Pro), the Monte Carlo engine runs ~36x faster than the all-core CPU path (SDE path simulation, RTX 3090, f32).

Documentation

Full API reference, indicator list, configuration guide, and best practices:

www.manifoldbt.com/docs/documentation.html

Community vs Pro

Community Pro
Output resolution Daily 1m, 5m, 15m, 1h
Monte Carlo 1K sims Unlimited
Walk-Forward - Anchored + Rolling
Parameter Stability - Yes
Crypto connectors (Binance, Bybit, Hyperliquid) Yes Yes
Databento & Massive connectors - Yes
Safety checks (lookahead, exposure) - Yes
Tearsheets & export - Yes

License

Apache 2.0 with Commons Clause. The source is available, free to use, modify and self-host. Reselling the software or offering it as a paid hosted service is not permitted. See LICENSE for the full text.

S
Description
No description provided
Readme Apache-2.0
1.2 MiB
Languages
Python 100%