Freqtrade alternative for fast, realistic backtesting

Freqtrade is a live crypto trading bot, but the backtesting engine built into it is exactly what most people comparing it against Manifold-BT actually want to know about: it is a real, widely used Python backtester with Optuna-driven hyperopt attached. Here is the honest engine-to-engine comparison, not just “bot vs. no bot”.

Two backtesting engines, built differently

Freqtrade’s strategy interface computes indicators vectorized over the whole dataframe with pandas and TA-Lib, same as most Python backtesters. But the trade simulation itself steps candle by candle in Python, because it has to reproduce exactly what the live bot would do: the same entry/exit signals, stoploss, and ROI logic. That fidelity to the live bot is also why it is the slower half of the loop, especially once you multiply it across hundreds of hyperopt epochs.

Manifold-BT uses the same two-stage shape, vectorized signals, then a sequential pass for fills, but in Rust: evaluate signals over the full series at once, then walk the series once for stops, partial fills, and PnL. Same idea as Freqtrade’s event-driven loop, native speed instead of a Python one.

What Freqtrade’s backtester assumes

Freqtrade’s own documentation is upfront about this: by default, an order fills at the requested price with no slippage and no market impact, as long as that price falls within the candle’s high/low range. OHLC data alone cannot tell you the order in which prices moved within a candle, so the path between the high and the low inside a bar is unknown. Freqtrade’s fix is opt-in: pass --timeframe-detail to re-simulate against a lower timeframe for closer intra-candle accuracy. There is also no built-in walk-forward split; folds are hand-rolled by slicing timeranges yourself.

Manifold-BT models market-impact slippage, funding on perps, partial fills, and signal delay by default, and ships walk-forward folds as a first-class feature, not a manual timerange split.

Side by side

 Manifold-BTFreqtrade
Backtest engineRust: vectorized signals, then a sequential fills passPython: vectorized indicators (pandas/TA-Lib), then a candle-by-candle event loop
Execution realism (backtest)Market-impact slippage, funding on perps, partial fills, signal delay modeled by defaultNo slippage or market impact by default: fills at the requested price if within the candle's high/low
Intra-candle accuracyNext-bar fills with signal delay modeled directlyUnknown from OHLC alone; opt in to a lower `--timeframe-detail` for a closer approximation
Parameter searchOne native call, exhaustive grid, fans out across every core (and GPU), 2-D heatmapsHyperopt: Optuna-driven smart search, each epoch a full Python backtest, hundreds of epochs recommended
Walk-forwardBuilt-in, in-sample / out-of-sample foldsNot built in; timeranges are sliced by hand
Multi-asset / portfolio backtestYesYes, respecting `max_open_trades` across pairs
Live tradingNo, research onlyYes, the main use case: live order routing via CCXT
Best forValidating a strategy's realism and stability before risking capitalOne open-source stack that backtests and then trades the same strategy live

The same sweep, both engines

Same SMA crossover, same grid. In Manifold-BT the whole sweep is one native call that fans out across every core (or the GPU) and evaluates every combination. Freqtrade has no exhaustive-sweep API: the way you search a space is hyperopt, which uses Optuna to start with about 30 random trials, then sample intelligently instead of testing every combination. Each trial still reruns the full Python backtest, and Freqtrade’s own docs are blunt about the cost: it will “burn all your CPU cores” and, with the hundreds of epochs typically recommended, “still take a long time.”

Different trade-off, then: the sweep does more evaluations but gives you the full map, heatmaps included, so you can see the stable plateau around your parameters rather than trusting a single optimum a sampler landed on. Same crossover, same idea, different search:

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")

That difference in shape is a difference in wall time. A single backtest is already milliseconds in Manifold-BT against a Python candle-by-candle pass in Freqtrade. The gap then compounds on the search, where the whole grid stays in native code across every core or the GPU instead of rerunning a Python backtest per trial: a study that lands in seconds on Manifold-BT is the “long time” the Freqtrade docs warn about, minutes to hours. On a full parameter search that is the difference between an interactive map and a batch job, with Manifold-BT coming out hundreds, often thousands, of times faster.

When to pick which

Choose Freqtrade if you want one open-source stack that backtests and then trades live on an exchange, and you are comfortable with candle-level fills (no slippage or market impact unless you opt into --timeframe-detail) and an Optuna-driven search over your parameter space. Choose Manifold-BT when the backtest itself is the priority: realistic execution modeled by default, an exhaustive parameter map instead of a directed search, built-in walk-forward, and Rust/GPU speed, then deploy the strategy you actually trust wherever you trade live.

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