Manifold-BT vs hftbacktest

hftbacktest is the open-source reference for backtesting a market maker on tick data: a Rust core, strategies written in Python and compiled with Numba, level-2 and level-3 books, queue position and latency models, and live bots for Binance Futures and Bybit. Manifold-BT added the same kind of simulation to its research library in 0.27.0, on the Researcher plan. Both say they model the queue, so instead of arguing about it we gave them the same quotes and counted how often they agree. If you are weighing an hftbacktest alternative in Python, those counts are the place to start.

The same quotes, two engines

Tick backtests disagree most about the queue, since a level-2 book shows a level shrinking but never whose order left. Manifold-BT's three level-2 queues are hftbacktest's own formulas, reproduced rather than approximated so the two can be compared fill by fill: risk_adverse is its RiskAdverseQueueModel, power its PowerProbQueueModel, log its LogProbQueueModel. On three BTCUSDT perpetual days, the same quotes went to both engines, each living one bar. hftbacktest read Bybit's archives through its own converter and Manifold-BT through its own reader; neither touched the other's files. Agreement is per quote, at zero latency:

DayQueue modelAgreementQuotesFilled, Manifold-BTFilled, hftbacktest
2025-08-13risk_adverse99.994%15,4151,6531,654
2025-08-13power, n=399.974%15,4151,6541,656
2025-08-13log99.955%15,4151,6571,658
2025-08-23risk_adverse99.994%16,745733732
2025-08-23power, n=399.970%16,745736735
2025-08-23log99.964%16,745738736
2025-08-30risk_adverse100%17,232740740
2025-08-30power, n=399.983%17,232740741
2025-08-30log99.994%17,232740741

Latency does not pull them apart either. A maker quoting both sides every second, written for Manifold-BT's simulator (bt.sim) and for hftbacktest 2.4.4 with partial fills and the risk-averse queue, agrees on 99.990% to 99.998% of 518,382 quotes, at every latency tested from 0 to 500 ms.

On order-by-order data

Level 3 is the harder test. A day of Nasdaq TotalView-ITCH for AAPL and QQQ (2025-12-09) went to hftbacktest as its own order-by-order events, run with l3_fifo_queue_model and no_partial_fill_exchange. Every 100 ms, both engines cancel their quotes and post two new post-only ones at the best bid and ask, so the quotes depend only on the book and each one gets a verdict in each engine. At zero latency:

SymbolQuotesOnly in hftbacktestFilled, same instantFilled earlier in Manifold-BTOnly in Manifold-BT
AAPL467,99807,6591,330464
QQQ467,998069,99579955

hftbacktest never fills a quote that Manifold-BT leaves unfilled. Every difference runs the other way and comes from one rule, the third one below. With 50 µs of feed latency and 100 µs on orders, the counts barely move: 7,662 and 70,017 quotes filled at the same instant. The full tables and the traced disagreements are in the public strategy authoring guide.

Where they disagree, and why

The risk-averse disagreements and those of the two makers were traced quote by quote on the raw event stream. Apart from one print stamped at the very instant a quote left the book, which the harness driving hftbacktest counted, each comes from a rule the two engines state differently. The three to six a day left on the probabilistic queues have not been traced yet.

  1. A print on your own side. When a buyer trades below your bid, the level you rest on has been emptied, so Manifold-BT fills your bid. hftbacktest reads only the prints of the other side against a bid, and does not.
  2. A book update filed before its print. When the book state that already shows a trade comes before the trade itself, hftbacktest counts that trade twice in its queue. Manifold-BT puts the prints of an instant before the state that shows them.
  3. An order alone at a better price (L3). Once the orders ahead of yours have executed, yours can be the only one left at its price, better than the rest of the market. hftbacktest fills an order through its price only if that price lies between the market's best price, your own orders left out, and the traded price. A lone order at a better price lies outside that range, so an execution one tick further does not reach it. The quote fills later, when an order entering the other side crosses it (a median of 58 µs later on AAPL, 47 µs on QQQ), or not at all if none does before the quote is cancelled. Manifold-BT fills it at that execution, because the aggressor who paid a worse price met your order first.
  4. Partial fills (L3). hftbacktest's level-3 exchange fills the whole order or nothing. Manifold-BT fills as much as an execution takes: at 100 shares a quote, 1,704 quotes on AAPL and 5,425 on QQQ fill for a different size.

How far does a whole strategy drift because of them? Take a maker whose quotes depend on its inventory: at the best bid and ask every 100 ms, a side withdrawn at five lots of inventory or when the best level's imbalance runs against it by more than 0.6. Written with Strategy.quote in Manifold-BT and in Numba for hftbacktest, one share a quote, zero latency and no fee, the two runs part at their first different fill and end here:

SymbolP&L, Manifold-BTP&L, hftbacktestFills, Manifold-BTFills, hftbacktest
AAPL-47.32-46.557,1466,952
QQQ-77.74-78.0843,07743,041

Both lose money, which a maker this simple does on these days. They lose it to within a dollar of each other, and both finish the day with the same position (-4 shares on AAPL, -5 on QQQ).

Side by side

 Manifold-BThftbacktest
LicenceCommunity is free on PyPI (Apache 2.0 with Commons Clause); the tick layer is on the Researcher planMIT, free and open source
Strategy codeA few DSL expressions per quote, compiled once by the Rust engine, or a Python function per wake-up with bt.simYour own event loop, in Python compiled with Numba, or in Rust
Market dataReads the Binance and Bybit archives and Nasdaq ITCH 5.0 days straight into its storeConverters for Binance, Bybit, Hyperliquid, MEXC, Tardis and Databento into its own event arrays
L2 queue modelsrisk_adverse, power and log: hftbacktest's formulas, reproducedRiskAdverse, PowerProb and LogProb, with a few variants
L3 (market by order)Exact FIFO queue, with partial fillsFIFO queue, whole fills only
LatencyFour delays: order, cancel, response and feedConstant, or interpolated from a recorded order-latency log; feed latency from the data's timestamps
Beyond the tick runBar backtests, parameter sweeps across every core and GPU, walk-forward and Monte Carlo in the same libraryTick simulation; sweeps and validation are your own code
Live tradingNo, research onlyYes, Binance Futures and Bybit, from Rust
Latest release0.27.0, October 20262.4.4 on PyPI, December 2025

A queue-aware maker in a few expressions

In hftbacktest you write the event loop: wait for the next event, read the depth, cancel and post, all inside a Numba function. In Manifold-BT a quote is three expressions, where to stand, how much, and whether to stand at all. The engine compiles them once and runs the day without calling back into Python, and a strategy written this way sweeps like any other. The queue below is hftbacktest's PowerProbQueueModel with n = 3, under the name Manifold-BT gives it:

maker.py
import manifoldbt as bt
from manifoldbt import book
from manifoldbt.helpers import Interval, time_range

# One day of Bybit's free BTCUSDT perpetual archives: the tape and the book
store = bt.ingest_trades("bybit", "BTCUSDT", symbol_id=1,
                         start="2025-08-13", end="2025-08-13", category="linear")
bt.ingest_book("bybit", "BTCUSDT", symbol_id=1,
               start="2025-08-13", end="2025-08-13", category="linear")

CLIP, MAX_INVENTORY = 0.01, 0.05
inventory = bt.position()
book_ok = (book.bid_levels() > 0) & (book.ask_levels() > 0)

# Join the best bid and ask, post-only, within an inventory limit
strategy = (
    bt.Strategy.create("maker")
    .quote("buy", bt.when(book_ok, book.bid_price_at(1)), CLIP, tif="GTX",
           enabled=inventory < MAX_INVENTORY - 1e-12)
    .quote("sell", bt.when(book_ok, book.ask_price_at(1)), CLIP, tif="GTX",
           enabled=inventory > -MAX_INVENTORY + 1e-12)
)

start, end = time_range("2025-08-13", "2025-08-14")
config = bt.BacktestConfig(
    universe=[1], time_range_start=start, time_range_end=end,
    bar_interval=Interval.millis(100),          # the wake-up clock
    initial_capital=100_000.0,
    fees=bt.FeeConfig(maker_fee_bps=2.0, taker_fee_bps=5.5),
    execution=bt.ExecutionConfig(
        latency={"order": Interval.millis(5), "cancel": Interval.millis(5),
                 "response": Interval.millis(5), "feed": Interval.millis(2)},
        # hftbacktest's PowerProbQueueModel with n = 3, under its name here
        fill_model={"queue": {"model": "power", "n": 3.0, "depth_source": "book"}},
    ),
)
result = bt.run(strategy, config, store)
result.fills_df()

When the decision does not fit in expressions, bt.sim hands the venue to a Python function instead, closer to the hftbacktest way of working, and on the same days the two give the same order journal, fill for fill. Where the data comes from, and what each archive holds, is in free crypto tick data and MBO, MBP and BBO order book data.

When hftbacktest is the right call

When the backtest is a step toward a live bot on Binance Futures or Bybit, and you want the simulator and the bot to share their code, hftbacktest does that and Manifold-BT does not, on purpose. It is also the answer if you need an MIT licence, want to change the simulator itself in Rust, or already have strategies written in Numba. And it is free, while the tick layer of Manifold-BT is a paid plan.

Which should you pick?

Pick Manifold-BT if the market maker is one strategy among the ones you research. The tick run sits in the same library as your bar backtests, sweeps and walk-forward; it reads the free crypto archives and the ITCH days itself; and on level 3 it fills orders partially, at the execution that reached them. You give up nothing on the queue, since the models are hftbacktest's and the verdicts agree on more than 99.95% of quotes. Pick hftbacktest if the goal is a live bot on the venues it connects to, or if the budget is zero. The tick layer is described on the Researcher plan page.

Frequently asked questions

Does Manifold-BT give the same results as hftbacktest?

Close to it, and measured. With the same queue model on the same BTCUSDT quotes, the two engines agree on 99.955% to 100% of quotes, and a two-sided maker agrees on 99.990% to 99.998% of 518,382 quotes at latencies from 0 to 500 ms. On a Nasdaq level-3 day, hftbacktest never fills a quote that Manifold-BT leaves unfilled. What is left comes from rules each engine states, such as how a print on your own side or a partial fill is handled.

Is hftbacktest free? Is Manifold-BT?

hftbacktest is MIT-licensed and free. Manifold-BT's Community edition is free on PyPI, but the tick layer it is compared on here (trades, order book, queue models, level-3 data) is part of the paid Researcher plan.

Can Manifold-BT trade live like hftbacktest?

No. Manifold-BT is a research library and places no orders. hftbacktest has live connectors for Binance Futures and Bybit, written in Rust. If the goal is a live bot on one of those venues that shares its code with the backtest, that is a good reason to choose hftbacktest.

Does Manifold-BT support level-3 (MBO) data like hftbacktest?

Yes. It reads Nasdaq TotalView-ITCH 5.0 days order by order with ingest_mbo, and the place of an order in its queue is then read from the data instead of estimated. Unlike hftbacktest's level-3 exchange, it fills an order partially when an execution takes only part of it.

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