Skip to content
Friday, 31 July 2026 · LondonENع
Rayan Azhari.Sustainability · Energy · Carbon · Built EnvironmentOccasional detours into philosophy, religion or programming, wherever curiosity leads
Production Quant Trading

Buy the Engine, Build the Edge: NautilusTrader, backtrader, or Roll Your Own

Choosing a quant trading framework is not a feature comparison between NautilusTrader, backtrader and a DIY loop. It comes down to one property, and a rubric you can apply to any engine: does your backtest run the same code as live?

Rayan AzhariChartered Environmentalist, MISEP · 9 min read
Title card for the essay Buy the Engine, Build the Edge, contrasting a vectorised backtest (the price you wished for) with an event-driven engine (the price you would get), linked by the same strategy class.

Every quant developer hits the same fork early: which backtesting framework do I build on? NautilusTrader? backtrader? vectorbt? Or is it simpler to just write my own event loop? The internet answers with feature tables, stars on GitHub, and benchmark speeds, which is exactly the wrong frame. The choice is not really about features. It is about which failures you are willing to own, and there is one property that decides more than all the features combined.

The two halves of a stack have opposite economics

A trading system splits cleanly into two kinds of code, and they should be sourced differently.

The edge, your signals, your sizing, your risk policy, is where your differentiation lives. Nobody else can write it, and a subtle bug there quietly corrupts your P&L. You must own every line.

The plumbing, order routing, fill simulation, position and cash accounting, reconnection, event scheduling, is undifferentiated and brutally hard to get right. There is no alpha in it. It is pure downside: invisible when correct, catastrophic when wrong, and the bugs surface in live trading where they cost real money. This is precisely the code you should not write.

So the heuristic is: build what encodes your edge; buy (or adopt) what merely has to be correct, especially anything that talks to the broker or accounts for cash and positions. A mature engine has already absorbed thousands of edge-case bug reports you would otherwise discover one margin call at a time. That is the sense in which you "buy the engine and build the edge."

The one property that decides it

Here is the question that outranks every feature: does your backtest run the same code as your live system? If it does not, you are validating one program and deploying a different one, and the gap between them is exactly where look-ahead, fill-model fantasy and timezone bugs live. This is what actually separates the framework families.

Figure

Two backtests, two different programs

The choice is not which is faster. It is whether the program you validate is the program you deploy.

Vectorised

for finding candidates

  • Whole-series array maths: years of data in seconds
  • Idealised fills: you get the price you saw
  • No order lifecycle; a position is just a number
  • Live is a full rewrite, so backtest-to-live parity is zero
Event-driven

for trusting them

  • One event at a time, exactly as in live
  • Models rejection, partial fills and latency
  • Full order state machine and cash accounting
  • The same strategy class drives backtest and live

Buy the plumbing that merely has to be correct; build only the edge.

Source: Building a Production Quant Trading System (Titan)

A vectorised backtest computes the whole signal and P&L series at once with array operations, returns = asset_returns * position.shift(1). It is gloriously fast, years of data in seconds, which is what you want when sweeping parameters. But it models a frictionless world: no order can be rejected, no fill is partial, no bracket leg is refused, and it assumes you transact at the price you saw. Its live counterpart is a rewrite, so the thing you tested and the thing you ship are two different programs.

An event-driven engine processes one event at a time, a bar, a tick, an order-accepted, a fill, a position-changed, exactly as they arrive in live trading. It is slower, but it models the things that actually break: an order sits in a submitted state until the venue accepts it, a fill arrives late, a reconnect finds a position you did not know you held. Crucially, a mature event engine lets the same strategy class drive both a historical backtest and a live session. You swap the data and execution clients, not the logic. "We tested this" and "we deployed this" finally describe the same program.

The war story: the edge that lived in the fills

Chart

The edge that lived in the fills

A candidate that looked excellent in a vectorised backtest lost most of its return once an event engine modelled real order handling, because a vectorised 'fill' is an assumption, not an execution. Illustrative and sanitised.

Vectorised backtest2.1 SharpeEvent-driven engine0.4 Sharpe
Source: Building a Production Quant Trading System (Titan), sanitised, illustrative

A candidate strategy looked excellent in its vectorised backtest: a clean equity curve, a Sharpe well past every gate. When it moved onto an event-driven engine with realistic order handling, much of the return simply evaporated. The vectorised version had been transacting at prices the strategy could never actually have obtained: it sized off a bar's close and implicitly filled at that same close, with no spread, no slippage, no rejected leg.

The shape of the bug is generic and worth naming: a vectorised backtest's "fill" is an assumption, not an execution. The fix was not a parameter change. It was treating the vectorised result as a triage signal only, never a deployment verdict. Anything headed for real capital has to clear an engine that models how orders actually behave.

"An event loop is just a for-loop over bars"

This is the sentence that launches a thousand DIY frameworks, and it is wrong. The easy part is looping over bars. The hard part is everything the loop has to do correctly between bars: the order-state machine, the cash and position ledgers that survive partial fills, reconnection that re-adopts positions opened before a restart, idempotent handling of duplicate broker events. Get that accounting subtly wrong and your backtest still looks perfect while your live book quietly drifts away from what you think you hold. That drift is the most expensive kind of bug, because nothing crashes to tell you.

Rolling your own execution engine means signing up to maintain all of it, forever, for zero alpha. Occasionally that is the right call, and it is worth being honest about when: a single-instrument, low-frequency strategy where the engine's learning curve dwarfs its benefit; an exotic asset class no framework models; or a shop that already runs battle-tested execution infrastructure. Outside those cases, adopting the machinery beats maintaining it.

Because the lesson is the property and not the brand, do not choose NautilusTrader or backtrader or anything else off a popularity contest. Score candidates against a rubric, and the name becomes incidental:

  1. Parity. Does the same strategy class run in both backtest and live, with only the data and execution clients swapped? This is the one that matters most.
  2. Realism. Does it model order rejection, partial fills and latency, or does it hand you the price you wished for?
  3. Auditable accounting. Can you inspect and trust its cash and position ledger through partial fills, reconnects and duplicate events?
  4. Maintenance. Is it actively maintained, so you are adopting a living project and not inheriting an abandoned one?

Those four properties are what "buy the engine" actually buys. And weigh the real cost of buying honestly too: not just "a framework to learn" and "slower", but the abstraction tax of fighting the engine's data and clock model when your need does not fit its grain, upgrade churn on a fast-moving dependency, debugging through someone else's event loop, and lock-in if the project stalls. Price those before you adopt.

Keep the vectorised layer, but fence it

None of this retires your fast, frictionless backtester. It just gives it one job. Vectorised tools (pandas, numpy, vectorbt) are where ideas are born and where most of them should die: test thousands of parameter combinations in minutes, and throw out the ninety-odd per cent that fail even in a frictionless world. Fill realism does not matter yet, because a strategy that fails without costs will only fail harder with them.

The discipline is the handoff. Vectorised maths is for finding candidates; the event engine is for trusting them. Never let a fast, flattering, frictionless number stand in for a deployment decision. Spend cheap vector-seconds finding the few candidates worth the expensive engine-hours, and let only the survivors of that second pass anywhere near capital.

The takeaway

If you remember one thing, make it the rubric, not the brand. The framework question that the forums frame as "NautilusTrader vs backtrader vs roll your own" is really: which of these runs the same strategy code in backtest and live, models real order handling, keeps an auditable ledger, and is still maintained? Answer that and the choice makes itself. Build the edge, which is yours alone; buy the plumbing, which merely has to be correct.

This is one of five stack decisions in the foundations of the system these essays come from. The full chapter, Stack choices and why, is free to read and walks the engine choice alongside the research layer, the lockfile, the broker realities and the container boundary. And if you want to see a real event-driven strategy class, the sanitised companion framework on GitHub is Apache-2.0: the lifecycle hooks, the sizing call, and the risk check are all there to read. And that chapter is part of Building a Production Quant Trading System, a living digital copy on Leanpub and a print paperback on Amazon if you want the whole build in one place.

This is an engineering essay, not investment advice, and it contains no tradable strategy. All figures are illustrative and sanitised.

Figure

Two backtests, two different programs

The choice is not which is faster. It is whether the program you validate is the program you deploy.

Vectorised

for finding candidates

  • Whole-series array maths: years of data in seconds
  • Idealised fills: you get the price you saw
  • No order lifecycle; a position is just a number
  • Live is a full rewrite, so backtest-to-live parity is zero
Event-driven

for trusting them

  • One event at a time, exactly as in live
  • Models rejection, partial fills and latency
  • Full order state machine and cash accounting
  • The same strategy class drives backtest and live

Buy the plumbing that merely has to be correct; build only the edge.

Source: Building a Production Quant Trading System (Titan)

Chart

The edge that lived in the fills

A candidate that looked excellent in a vectorised backtest lost most of its return once an event engine modelled real order handling, because a vectorised 'fill' is an assumption, not an execution. Illustrative and sanitised.

Vectorised backtest2.1 SharpeEvent-driven engine0.4 Sharpe
Source: Building a Production Quant Trading System (Titan), sanitised, illustrative

Further reading

Office energy, part 3 of 25

Related posts

Migrating from WordPress to Next.js: A Field Guide

A practical, end-to-end guide to moving a content site from WordPress to Next.js without losing your search rankings: the URL-preservation rule that governs everything, a content pipeline that survives the move, bilingual and RTL handling, the SEO and security work, and a cutover you can roll back.

· 20 min

I Stopped Paying for WordPress Hosting

Managed WordPress hosting is a tax you pay in money, in performance, and in security anxiety. Here is how I kept the WordPress editor I love, ran it locally for free, and served the public site as static HTML on the edge: a zero-dependency Node crawler I have open-sourced.

· 7 min

Essays in your inbox

New writing on Syria, sustainability and finance, a few times a month.

Unsubscribe anytime. Read by 4,200+ professionals.