# HftBacktest

Tick-by-tick backtesting that models order queue position and feed and order latency.

*https://cryptomarkets.tools/tools/hftbacktest · Backtesting & Research Libraries*

## Facts

### At a glance

| Field | Value |
| --- | --- |
| Vendor | nkaz001 |
| Category | Backtesting & Research Libraries |
| Job | backtesting |
| Website | https://github.com/nkaz001/hftbacktest |
| Pricing model | open-source |
| Free tier | true |
| Open source | true |
| Licence | MIT |
| Self-hosted | true |
| KYC required | false |
| Tested hands-on | false |
| Last updated | 2026-09-19 |

### Coverage

| Field | Value |
| --- | --- |
| Asset classes | spot, perpetuals, futures |
| Chains | none |
| Venues | cex, derivatives |
| Data latency | none |
| Platforms | library |
| AI features | none |

### Interfaces

| Field | Value |
| --- | --- |
| API | false |
| Webhooks | false |
| Scripting | Python, Rust |
| MCP server | false |
| Export | none |

### Capabilities

Yes: backtesting, automation, live_trading

No: charting, screening, paper_trading, portfolio_tracking, exchange_import, tax_reporting, alerts, news, onchain_data, wallet_tracking, derivatives_analytics

*Verified: pricing 2026-09-19; capabilities 2026-09-19; coverage 2026-09-19.*

## What it is

A backtester for strategies whose profit and loss is decided by where an order sat in the queue
and by how long it took the exchange to hear about it. It replays a full tick feed — every
Market-By-Price depth update and every trade — reconstructs the book from it, and simulates your
order against that book with two things most engines do not have: a model of your position in the
queue at your price level, and separate latency for the feed arriving, the order leaving and the
response coming back.

That is the whole reason to pick it over anything else in this category. The six libraries already
here decide a limit fill by comparing your price to a bar's range, which means a passive order at
the touch fills every time the market trades through it. At a horizon of minutes that
approximation is survivable. At the horizon a market maker works at, it is the entire result: a
strategy that quotes at the best bid and collects the spread is profitable in a candle backtest by
construction, because the candle backtest has no concept of the forty other orders that were at
that price first.

The trade for that accuracy is paid in three places — the data, the run time, and the fact that
the interesting parts of the engine are Rust. All three are below.

## Data & coverage

**It ships no data, and this is the largest cost of using it.** The feed format is an
eight-field NumPy structured array — event flags, exchange timestamp, local timestamp, price,
quantity, order id, and two reserved fields — loaded from `npz` or `npy`. Two timestamps rather
than one is the point: the gap between them *is* the feed latency the simulation uses, so data
recorded without a local receive timestamp cannot drive the latency model at all.

**Converters shipped in the Python package** (read from the 2.4.4 wheel, not the README):
`binancefutures`, `binancehistmktdata`, `bybit`, `bybithistmktdata`, `hyperliquid`, `mexc`,
`tardis` and `databento`, plus snapshot and validation helpers. So the practical venue list for a
crypto reader is Binance Futures, Binance historical market data, Bybit, Hyperliquid and MEXC
direct, and anything Tardis or DataBento carries by way of those two. There is no CCXT anywhere in
the project, in either half.

**Level-3 is supported and is mostly not a crypto feature.** The engine reconstructs the book from
Market-By-Order feeds as well as Market-By-Price, and with MBO your queue position is known rather
than modelled. But the docs' own Level-3 tutorial builds its feed from DataBento's **CME**
Market-By-Order data for the `BTCM4` contract, because crypto venues publish aggregated depth and
not per-order messages. On a crypto exchange you are on the Market-By-Price path, and the docs say
so directly: the queue models exist because "if an exchange doesn't provide Market-By-Order, you
have to guess it by modeling".

**Queue models, in order of pessimism.** `RiskAverseQueueModel` advances your order only when a
trade happens at your price and assumes every cancellation happens behind you — the most
conservative assumption available. `ProbQueueModel` and its power and log variants assume
cancellations occur on both sides of your position with a probability that depends on where you
are, which is closer to how a book actually empties and is the one you have to justify.

**Latency models.** `ConstantLatency` takes an entry and a response figure in the same time unit
as the data. `IntpOrderLatency` interpolates from real recorded latencies, and the docs tell you
how to collect them: submit unexecutable orders on a schedule and record request, exchange and
receipt timestamps. A third option generates artificial order latency from the feed latency in
your data when you have no recordings. The first is a placeholder, the second is the reason the
project exists, and the difference between them on a quoting strategy is not small.

**Fees are chosen, not defaulted into.** The asset builder takes one of three fee models —
`trading_value_fee_model`, `trading_qty_fee_model`, `flat_per_trade_fee_model` — and the
documented example passes `0.0002` maker and `0.0007` taker with the note that a negative maker
figure is a rebate. Rebates matter here more than in any other card in this category, because a
maker rebate is often the whole edge being tested.

## Integrations

Two implementations of one design. The Python package runs your strategy inside a Numba `njit`
function against a compiled Rust core; the Rust crate runs the same simulation natively and is
where the live bot lives. Both are versioned together and released together — `rust-v0.9.4` and
`py-v2.4.4` on the same day.

Live trading is Rust only, and the wheel proves it rather than the README claiming it: the
compiled extension in Python 2.4.4 does not contain `build_hashmap_livebot` or
`build_roivec_livebot`, so the package's own `LIVE_FEATURE` flag resolves to false on install.
The Rust connectors are three — Binance Futures, Binance spot and Bybit — each speaking the
exchange's own WebSocket and REST directly.

Results come out through a `Recorder` that writes timestamped mid price, position, balance, fee,
trade count, volume and notional, and a `stats` module that turns those into a summary table and
equity plots through Polars and matplotlib or HoloViews. `Recorder.to_npz()` is the only writer in
the package — there is no CSV or Parquet export, which is why this card claims none.

The documentation carries a "Market Maker Program" page covering Binance Futures, Bybit Futures,
OKX Futures and Hyperliquid, which tells you plainly who this is aimed at: people whose fee tier
depends on quoting obligations they have to meet. That page opens by disclaiming its own accuracy
and telling you to check with the exchange, which is the correct way to publish a list like that
and also a reason not to plan against it.

## Limitations

**Replay cannot model your own impact.** The docs state the assumption in the first paragraph of
the order-fill page: your order changes nothing in the replayed market. A liquidity-taking order
is filled at the best price for its whole size under the default exchange model, and under the
partial-fill model it walks the recorded book while that book stays exactly as recorded. Size your
simulated orders as though they are invisible, because in this engine they are.

**Nine months without a commit.** Master last moved on 2025-12-23 and the last release is dated
2025-12-10, against 4,728 stars, 6 open issues and 12 open pull requests as of 2026-09-19. The
repository is not archived and nothing about a file-format-plus-converter design rots quickly —
but if Bybit changes a WebSocket payload, the fix is yours.

**The extension points are Rust, and the tutorial says so three times.** Custom latency model,
custom queue model, custom exchange model: "To implement your own … please use Rust." The Python
surface is configuration over a fixed menu. If your reason for choosing this engine is that the
provided queue models do not match your venue, you are writing Rust, and that should be a decision
made before the data is downloaded rather than after.

**No portfolio layer and no risk layer.** It simulates orders against books. There is no
cross-asset margin engine, no funding-payment model for perpetuals, no liquidation model and no
position-sizing framework — multi-asset means several books replayed on one clock, not a portfolio.
Balance, position and fees per asset are what the recorder gives you, and everything above that
you write.

**No data, and the one pointer in the README is somebody else's server.** The README links a
third-party host described as "hosted by the supporter" for USD-M futures data; on 2026-09-19 that
host did not present a certificate matching its own name, so an ordinary HTTPS fetch of it fails.
Either record your own feed with the shipped collector, or buy the ticks.

**Documentation disagrees with the package about Python.** The docs index still says 3.10+; the
published 2.4.4 metadata requires 3.11 or newer, as does the README. Minor, and a useful reminder
that the readthedocs build is not regenerated from the same source of truth as the wheel.

## Alternatives

If your strategy holds for hours and turns over a few times a week, this is the wrong instrument
and an expensive one: you will spend the first week on data before producing a number that a
candle backtester would have produced in an afternoon and been approximately right about. The rest
of [Backtesting & Research Libraries](https://cryptomarkets.tools/categories/backtesting-libraries) is where that reader
should start.

Choose this one when the question is specifically whether a passive quote gets filled — market
making, rebate capture, queue-position strategies, anything where the answer changes if the order
ahead of yours cancels. It is the only engine in this category that can answer that question at
all, and the only one whose backtest is allowed to tell you that your edge was always latency.

## FAQ

### How is HftBacktest different from a candle backtester?

A candle backtester decides whether your limit order filled by comparing its price to the bar's low or high. This one replays every book update and every trade, tracks where your order sits in the queue at its price level, and fills it only when the queue in front of it is gone. On a market-making strategy those two methods do not disagree by a few percent — they disagree about whether there is a strategy.

### Can I run a live bot from Python?

No. The live connectors are Rust, and the published Python wheel does not even contain the live-bot constructors — the symbols build_hashmap_livebot and build_roivec_livebot are absent from the compiled extension in hftbacktest 2.4.4, so the package's LIVE_FEATURE flag is false. Python is the research half; going live means the Rust crate.

### Does it come with tick data?

No, and tick data is the expensive part. It ships converters for Binance Futures, Bybit, Hyperliquid, MEXC, Tardis and DataBento feeds into its own NumPy format, plus a collector you run yourself. Budget for storage — full book updates for one perpetual run to gigabytes a day.

### Is it still maintained?

It is not archived, but the last commit to master is dated 2025-12-23 and the last release, rust-v0.9.4 and py-v2.4.4, is dated 2025-12-10 — nine months of silence as of 2026-09-19. Nothing is broken by that on its own, since the file format and the exchange converters are stable, but a new venue's feed format is your problem.

## Also worth comparing

- [Backtesting.py](https://cryptomarkets.tools/tools/backtesting-py.md) — Two thousand lines, one OHLC series, and no idea what a funding payment is.
- [Barter](https://cryptomarkets.tools/tools/barter.md) — Rust trading engine and market-data streams — live execution is a trait you implement.
- [LEAN](https://cryptomarkets.tools/tools/lean.md) — The engine behind QuantConnect, Apache-2.0 and runnable on your own machine.
- [Lumibot](https://cryptomarkets.tools/tools/lumibot.md) — One Python strategy that backtests and then trades live, across eight CCXT exchanges.
- [NautilusTrader](https://cryptomarkets.tools/tools/nautilus-trader.md) — Event-driven Rust engine that settles perpetual funding at the venue boundary.
- [Jesse](https://cryptomarkets.tools/tools/jesse.md) — Python backtesting and research framework whose live-trading half is a paid plugin.
