What is backtesting?

Backtesting is the process of testing a trading strategy on historical data to estimate how it would have performed before risking real money. Done well, it is the cheapest way to reject a bad idea. Done carelessly, it manufactures false confidence. This guide covers both sides.

How backtesting works

A backtest steps through historical bars one at a time. At each bar it evaluates your rules using only the information available at that moment, simulates the orders they would have produced, applies trading costs, and tracks the resulting equity curve. The output is a set of performance metrics, total return, Sharpe ratio, maximum drawdown, win rate, that summarise the simulated track record.

The critical phrase is only the information available at that moment. If a backtest peeks at future data, even by one bar, its results are fiction. Reputable engines enforce this ordering for you.

Why a backtest can lie to you

Most failed strategies look great in a naive backtest. The usual culprits are look-ahead bias (using data before it exists), overfitting (tuning parameters until they fit past noise), survivorship bias (testing only the assets that survived), and ignored costs (zero fees, zero slippage, instant fills). The last one is the most common: realistic execution, including slippage, funding, and partial fills, often turns a paper winner into a loser.

Manifold-BT models those costs by default and can detect look-ahead bias automatically, so the number you read is closer to the number you would have lived.

A backtest in ten lines of Python

Here is a complete, realistic backtest of a simple RSI mean-reversion rule on four-hour Ethereum bars. It downloads the data, runs on the Rust core, and prints the headline metrics.

what_is_backtesting.py
import manifoldbt as mbt
from manifoldbt.indicators import close, rsi
from manifoldbt.helpers import time_range, Slippage, Interval

# A minimal backtest: buy when RSI is oversold, flat otherwise
my_rsi = rsi(close, 14)
strategy = (
    mbt.Strategy.create("rsi_reversion")
    .signal("rsi", my_rsi)
    .size(mbt.when(my_rsi < 30, 1.0, 0.0))
)

start, end = time_range("2022-01-01", "2026-01-01")
config = mbt.BacktestConfig(
    universe={"binance": ["ETHUSDT"]},
    time_range_start=start,
    time_range_end=end,
    bar_interval=Interval.hours(4),
    initial_capital=10_000,
    fees=mbt.FeeConfig.binance_perps(),
    slippage=Slippage.fixed_bps(2),
    warmup_bars=14,
)

store = mbt.ingest(provider="binance", symbol="ETHUSDT", symbol_id=1,
                   interval="4h", start="2022-01-01T00:00:00Z",
                   end="2026-01-01T00:00:00Z")
result = mbt.run(strategy, config, store)
print(result.summary())  # total return, Sharpe, max drawdown, win rate

Frequently asked questions

What is backtesting in simple terms?

Backtesting is replaying a trading rule over historical market data to estimate how it would have performed, as if you had traded it in the past with the same logic.

Is backtesting reliable?

A backtest is only as reliable as its assumptions. Realistic fees, slippage, and funding, plus out-of-sample validation, make it informative. Ignoring costs or overfitting to one period makes it misleading.

What is the difference between backtesting and forward testing?

Backtesting uses historical data you already have, so it is instant and repeatable. Forward testing (paper trading) runs the strategy on new, incoming data in real time, which is slower but free of look-ahead bias.

What data do I need to backtest a strategy?

At minimum, OHLCV bars for the assets and timeframe you trade. Manifold-BT ingests bars directly from providers like Binance and Hyperliquid, so you do not have to assemble the dataset yourself.

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