What a crypto backtest silently assumes

Every engine ships a fill rule and a cost model with defaults. What those defaults assume about fees, funding and liquidation, and which engines model which.

A backtest engine is a fill rule and a cost model, and both ship with defaults. Most crypto engines default to zero fees, zero slippage, a fill at the price you asked for, no funding payment and no liquidation check. On a perpetual that omits the two cash flows which define the instrument, so the curve you are reading is a spot strategy wearing a perpetual's ticker. None of it is hidden; it is documented one level below the README.

You pick a backtesting library from a feature list, point it at two years of candles, and get a curve. The curve is not a lie and the library is not broken. It is the honest output of a set of assumptions you did not choose, most of which are the defaults, and several of which are the difference between a strategy and an artefact.

Every fact below comes from a vendor's own documentation or its source code, and the better-behaved engines in this catalogue say the awkward part out loud — Freqtrade's documentation describes its own fill model as optimistic before you have finished reading the page. The problem is not concealment. It is that the defaults sit in a settings module and a docstring, and the curve renders whether or not you went and read them.

How it works

Strip a backtester of its API and three things are left: a clock, a fill rule and a cost model. Everything a vendor sells you sits on top of those three, and every one of them has a default.

A bar is not a path

Most crypto backtests run on OHLCV candles, and a candle records four prices and no order. It does not say whether the high came before the low. For a strategy with both a stop and a target inside one bar, that missing ordering is the whole result — the same bar is a win or a loss depending on an assumption the data cannot settle.

Engines resolve it differently and mostly silently. NautilusTrader is unusually explicit: on a bar backtest it splits each bar into four synthetic L1 updates in open, high, low, close order, and its documentation calls that a plausible path rather than the real one. Freqtrade fills entries at the candle open and exit signals at the next candle's open, and offers --timeframe-detail to replay a faster timeframe inside each bar — at the cost of the memory, the runtime and the extra download. 3Commas, a hosted bot rather than a library, waits for the one-minute candle to close and then fills at the close of the next one, which is a conservative rule and a coarse one.

Granularity is also a commercial variable once you leave the open-source half of the category. Bitsgap scales backtest depth by plan: 30 days on the entry tier, 180 on the middle one, 365 on the top. Cryptohopper caps a run at about a month where its documentation describes the feature at all, runs one backtest at a time, and asks you to type the commission in by hand.

The fill rule

The default in almost every candle engine is: if price touched your limit, you were filled, in full, at your price. That is a statement about queue position, and a candle contains no information about queue position whatever.

OctoBot fills a limit order completely on a price-hit event against the simulated feed, with no queue and no volume check. NautilusTrader's default fill model treats a touched limit as filled and applies no probabilistic slippage — ten other fill models ship with the engine, from one-tick slippage to tiered synthetic books, and picking one is something you have to do on purpose. Hummingbot executes at the candle close.

hftbacktest is the one engine here built on the opposite assumption. It replays every book update and every trade, tracks where your order sits in the queue at its price level, fills it only when the size in front of it is gone, and models feed and order latency separately. Its queue models are graded by pessimism, with the most conservative advancing your order only when a trade consumes the level. For a market-making strategy the two methods do not disagree by a few percent — they disagree about whether there is a strategy.

Even that engine names the thing it cannot do: a replay cannot simulate your own effect on the market. Your orders were not in the book being replayed, so nobody reacted to them. For a size that would have moved the level, the backtest is optimistic in a way no fill model repairs.

The cost model

Fees and slippage are usually parameters, and parameters have defaults.

  • VectorBT ships fees=0.0, fixed_fees=0.0 and slippage=0.0 in its settings module. Every README example that does not override them is a frictionless run.
  • backtesting.py defaults to spread=0, commission=0 and margin=1.
  • Lumibot's backtesting broker checks for attached fee objects and returns immediately when there are none, with a comment in the code noting that fees are frequently empty in backtests.
  • OctoBot's simulated maker and taker fees come from the exchange profile and default to zero, and the repository contains no slippage model at all.

Two engines do better and are worth naming for it. Jesse pre-fills a real per-exchange taker rate rather than zero — 0.1% for Binance spot, 0.04% for Binance perpetual futures — though it applies one rate to both sides and has no slippage model. Hummingbot charges a flat round-trip cost, 0.0002 by default, identical for maker and taker, which is a real number and the wrong shape for any strategy whose margin is the maker-taker spread. Freqtrade reads the venue's own maker and taker fee from market info instead of assuming zero.

What a perpetual adds

A perpetual swap has two cash flows a spot position does not: a periodic funding payment, and a forced close at a maintenance-margin threshold. An engine that models neither is not backtesting a perpetual. It is backtesting a spot series that happens to carry a perpetual's ticker.

Funding

The mechanics are public and specified per venue, and the specifications do not agree with each other. That disagreement is what most engines paper over.

OKX settles funding every eight hours at 00:00, 08:00 and 16:00 UTC by default, "unless specified otherwise (i.e. every 1, 2 or 4 hours)", charging position value multiplied by the funding rate. You pay or receive only if the position is open at the assessment instant; close before it and you are exempt. The rate itself is a clamped function of an average premium index and a fixed 0.01% interest rate, divided by eight over the interval length.

Binance uses the same eight-hour default and the same shape of formula, with an interest rate of 0.03% daily — 0.01% per interval — and zero on certain pairs. The funding-rate documentation states that Binance charges nothing on funding and that payments are transferred directly between traders holding opposing positions. It also records something an engine with a hard-coded schedule cannot express: since May 2025, settlement switches to hourly automatically when the rate reaches its cap or floor. The cap for major contracts is ±0.75 times the maintenance margin ratio, and ±2% for other USDⓈ-M contracts.

Hyperliquid pays every hour, dividing the eight-hour rate into hourly payments, converts at the spot oracle price rather than the mark price, and caps funding at 4% per hour. Its fixed interest component is 0.01% per eight hours, which its documentation states as 11.6% APR paid to shorts.

dYdX runs a funding tick every hour by default, computing the rate as the premium component divided by eight plus an interest component that is 0% on cross markets and 0.125 basis points per hour on isolated ones.

Four venues, three settlement frequencies, two conversion prices and a rate that can flip sign between any two intervals. Now look at what the engines do with that:

  • NautilusTrader settles funding properly — at each boundary the simulated venue debits longs and credits shorts and moves the account balance — but only from FundingRateUpdate records you supplied. No adapter fetches the history for you, and a rate with no settlement boundary attached is explicitly inert.
  • Freqtrade downloads mark and funding-rate candles alongside OHLCV in futures mode, so funding is part of the result rather than something you add afterwards. The venue-level holes are worth reading first: OKX serves mark candles for roughly the last three months only, so funding before that window cannot be computed correctly, and Hyperliquid returns no historical data at all, so a venue you can trade live cannot be backtested.
  • LEAN computes funding as rate times position notional at 00:00, 08:00 and 16:00 UTC. It ships three crypto-venue classes; two of them are empty subclasses of the Binance one and change nothing. That is right for Bybit and wrong for dYdX, whose chain runs a funding tick every hour. Worse, the model needs a funding-rate feed loaded, and with none it charges nothing and says nothing.
  • OctoBot charges funding, which puts it ahead of most of the category, at a single constant rate for the entire run — default 0.00005 per interval, always positive. Real funding changes sign and spikes; its data collector only downloads candles, so there is no historical series to charge against.
  • Jesse returns zero for the funding rate unless the bot is running live, so a perpetuals backtest contains no funding in either direction.
  • VectorBT, its commercial edition, backtesting.py, Lumibot, Hummingbot and hftbacktest have no funding model at all. In several of them the word does not appear in the package.
  • 3Commas does not mention funding anywhere in its backtesting documentation, which matters because two of its three paid plans sell futures bots.

Liquidation

The second missing cash flow is the one that ends the strategy rather than taxing it. Binance computes maintenance margin as position notional times a tiered maintenance margin rate, minus a tiered maintenance amount, and triggers on the mark price rather than the last trade. Both halves matter to a simulation: the rate is a step function of position size, so the same strategy at four times the size is liquidated at a different price, and a mark price is not in your candle file.

The engines split three ways. NautilusTrader models liquidation and ships it switched off — liquidation_enabled is false on a backtest venue until you set it — and margining reads whatever mark prices are in the cache, so a run with none marks to something else. Jesse checks for liquidations only in isolated margin mode while shipping cross as the default, in which the liquidation price is NaN and the check never fires. Freqtrade computes liquidation prices and keeps a buffer above them, but does not track liquidation fees at all — its documentation says plainly that this makes the profit and loss inaccurate for any run that actually gets liquidated, and warns that cross-margin's cross-position effects may not be fully simulated.

backtesting.py is the clearest of all, in a docstring: margin is the required margin ratio of a leveraged account, and "no difference is made between initial and maintenance margins". One number sets leverage and nothing checks it again.

What it costs

Put the defaults into arithmetic. The numbers below are ordinary published rates, not worst cases.

Fees. Hyperliquid's base tier, which is where an account with no volume history sits, is 0.045% taker and 0.015% maker on perpetuals. A strategy that enters and exits once a day, taking liquidity on both sides, pays 0.09% a day — about 33 percentage points of annual cost. Run that strategy in an engine defaulted to zero fees and you have added 33 points of annual return to it before the first line of your logic executes.

Funding. Hyperliquid's interest component alone is 11.6% a year paid to shorts, before any premium. A backtest of a carry-negative long that charges no funding is overstating the result by at least that, and by considerably more whenever the premium is positive. The tail is worse than the average: Hyperliquid caps funding at 4% per hour, and Binance switches to hourly settlement precisely when the rate is at its cap, which is exactly the regime in which a constant-rate model such as OctoBot's diverges furthest from the venue.

Liquidation. This one does not have an average. It is the difference between a drawdown and a zero, and an engine with the check switched off will happily carry a position through a price at which the venue would have closed it, then recover, then report the recovery as profit.

Slippage and queue position. No published rate exists, which is the point: it is a property of your size against the book at that instant. An engine with no slippage model is not estimating it at zero, it is not estimating it. For anything passive, that gap is the strategy.

What you can do about it

Read three files before the README. The default fee, the default slippage and the fill rule. In an open-source engine they are usually a settings module and one class, and in this catalogue's cards they are quoted with the file they came from. If the answer is 0.0, the first number you change is that one — your venue's real maker and taker rates, from its fee schedule, before you read any curve.

Decide whether you are backtesting spot or a perpetual, and say it out loud. If the instrument is a perpetual and the engine charges no funding, you have two honest options: switch to an engine that settles funding from real data, or restrict the conclusion to strategies whose holding period does not cross settlement boundaries. What is not honest is the third option, which is reading the curve anyway.

Bring the funding history yourself, because most engines will not. NautilusTrader settles it and fetches nothing; LEAN's model is silent when the feed is absent; OctoBot invents one constant. Funding rate history is a public endpoint on every venue named on this page, and it is a small download. The engines that do fetch it — Freqtrade in futures mode — are the exception and a good reason to pick one.

Check the venue's funding schedule against the engine's, by name. Eight-hourly is a default, not a rule: dYdX and Hyperliquid pay hourly, OKX documents one-, two- and four-hour intervals for some contracts, and Binance switches to hourly at the cap. An engine with one hard-coded schedule is right for some of your venues and wrong for the others, and it will not tell you which.

Turn the liquidation check on, and give it a mark price. It is off by default in NautilusTrader, inert under Jesse's default margin mode, and fee-free in Freqtrade. If your strategy uses leverage, a backtest that cannot be liquidated is measuring a different strategy.

Match the engine to the question, not to the feature list. If the result turns on whether a passive quote was filled, a candle engine cannot answer it at any setting and a queue-position simulator can, at the price of tick data and a much longer week. If the result turns on a signal held for days, tick simulation is expensive precision on the wrong axis, and the first thing to fix is the funding leg.

Then re-run the winner with pessimistic settings and see what survives. Real taker fees on both sides, a slippage model if the engine has one, funding from the venue's history, liquidation armed. A strategy whose edge disappears under its venue's published rates did not have an edge; it had a cost model. That run takes an afternoon, and it is the cheapest thing on this page.

The category page for backtesting and research libraries lists what each engine models, card by card, with the file the default came from.

Tools this bears on

Cards in the catalogue where what is above changes the decision.

  • NautilusTrader

    Event-driven Rust engine that settles perpetual funding at the venue boundary.

    FreeFree tierOpen source

  • Freqtrade

    Write the strategy in Python, backtest it, then run the same class live.

    FreeFree tierOpen source

  • LEAN

    The engine behind QuantConnect, Apache-2.0 and runnable on your own machine.

    FreeFree tierOpen source

  • Jesse

    Python backtesting and research framework whose live-trading half is a paid plugin.

    $899 onceFree tierOpen source

  • OctoBot

    GPL-3.0 Python bot with a web interface for grid, DCA, basket and TradingView strategies.

    FreeFree tierOpen source

  • HftBacktest

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

    FreeFree tierOpen source

  • Backtesting.py

    Two thousand lines, one OHLC series, and no idea what a funding payment is.

    FreeFree tierOpen source

  • VectorBT

    Vectorised Python backtesting that has never heard of funding or liquidation.

    FreeFree tier

FAQ

Why does my crypto backtest look so much better than live trading?

In roughly this order — fees, funding, slippage and the fill rule. Several engines in this catalogue default to zero fees and zero slippage and fill a limit order the instant price touches it, and most of them charge no funding at all on a perpetual. Those four defaults together can manufacture an edge in a strategy that has none.

Which backtesting engines actually model perpetual funding?

A minority. NautilusTrader settles funding, but only from funding data you supplied yourself. Freqtrade downloads mark and funding-rate candles automatically in futures mode. LEAN models one eight-hour schedule and applies it to every crypto venue it supports. OctoBot charges a single constant rate for the whole run. The rest of the category charges nothing.

Is a backtest with no funding model useless for perpetuals?

Not useless, but it is answering a different question. Without funding you are measuring the price leg of the trade only, which is fine for a strategy that holds for minutes and misleading for one that holds through settlement boundaries. The longer the average hold, the more of the result is the leg you did not simulate.

Can any backtest tell me whether my limit order would have been filled?

Only one kind, and it needs tick data. An engine that fills when the candle's high touches your price is asserting something the candle cannot know. A queue-position simulator replays every book update and fills you when the size ahead of you is gone — which is a different answer, and usually a worse one.

Sources

  1. Perpetual funding fee mechanism OKX,
  2. Introduction to Binance Futures Funding Rates Binance,
  3. How to Calculate Liquidation Price of USDⓈ-M Futures Contracts Binance,
  4. Funding Hyperliquid, read
  5. Fees Hyperliquid, read
  6. Funding dYdX, read

The catalogue next door

This page is background, not a listing. The products it bears on are in Backtesting & Research Libraries, each filled in against the same schema, with the fields to narrow it yourself.

Last updated . Corrected in place: this is a reference page, not a dated post.