infrastructure · engineering
Why we build in-house
Most of our trading stack is software we wrote ourselves - and why Python and Rust sit on opposite sides of a deliberately narrow line.
By Tom
Most of our trading stack is software we wrote ourselves. That is deliberate, and it is not cheap, and anyone who sees the bill before they hear the reasoning tends to assume we have lost perspective somewhere. We have not.
We are a proprietary, systematic firm: the capital on the book is ours, raised from nobody, deployed across liquid markets worldwide, and we keep the headcount deliberately small. In a firm shaped like that, the infrastructure is not a cost centre bolted under the research to keep the lights on. It is the research.
I mean that literally. A backtest is a claim: run this rule over these years and this is what would have happened. The live system is the apparatus that turns the claim into a measurement, the instrument you point at the real market to see whether the claim survives contact with money. An instrument you cannot open is one you cannot calibrate. If we cannot see inside the apparatus, every number that comes out of it carries an asterisk we are never able to read, and we end up sizing real positions against results we only half understand.
Physicists live by this: no lab trusts a reading from a device it is forbidden to take apart, because a reading is only as good as your ability to explain how the device produced it. The P&L is our reading, the stack is the device, and we insist on being allowed to open it.
Walking a live-versus-backtest gap
Early on, the live result always disagrees with its backtest. Always. The first time you take a strategy into production the two numbers refuse to line up, and for a while the whole job is to explain the gap: give the discrepancy a name rather than a shrug.
That work is where most of the real understanding gets made. You start at the model output and walk outward toward the venue, one layer at a time, asking the same narrow question at each boundary: is the thing leaving this layer what the next layer thinks it received? Somewhere on that path the backtest and the live system parted company, and the parting has a specific, findable cause.
When you cannot inspect a layer directly, you bisect it. You capture the exact inputs the layer saw, replay them offline, and compare its intermediate state against what the live run produced, halving the suspect region each time the two agree until the fault is cornered in one place. It is the trick you use to find the dead bulb in an old string of Christmas lights: the whole string is dark, no single bulb looks guilty, so you test half, then half of that, until one bulb is left holding the blame. Building a pipeline that can actually be bisected like this is its own real craft, since you have to record the right things at the right boundaries long before you ever need them. That is a longer discussion for another day, so I will leave it there and come back to the fault.
When we finally corner one of these, the culprit is almost always mundane and almost always ours. Sometimes it is a fill assumption that was too kind, the backtest filling at a price no live order would have caught. Once it was timestamp skew, the production model seeing the market a few milliseconds later than the backtest had pretended, which on that horizon was enough to flip the decision. Another time a fee we had modelled as a flat rate turned out to be charged on a tier, so everything downstream of it was gently wrong. Nothing exotic in any of it. Each one moved real money, and none of them would have confessed if the layer hiding the fault had been somebody else’s black box.
The build-buy line
The rule is narrower than “build everything,” which is really just a way to go broke slowly rebuilding solved problems for the pleasure of owning them. It comes down to a test with one question: does this sit on the hot path or the risk path? If it does, we understand it completely or we replace it. If it does not, we are content to borrow.
Take the build side first. Market data we own end to end, because every downstream number inherits its assumptions: how a book is rebuilt from an incremental feed, what you do with a gap in the sequence numbers, which timestamp you treat as the truth. Buy that layer and you have bought someone else’s answers to questions you will need to interrogate at three in the morning. Execution we own for the same reason running the other way, because it is where our intent becomes an order on a venue, and the difference between the price we expected and the price we got is either explained or it is a leak we cannot locate. Accounting we own because it is the ledger everything reconciles against, and if the position of record is a mystery then so is the P&L. The risk checks that gate all of these we own most jealously of all, because a risk limit is a promise about the worst case, and we will not make that promise through code we are unable to read.
The buy side is just as principled. We run on an operating system we did not write, against a database we did not write, over compression and numerical libraries we did not write, and we sleep fine. A mature numerical library is decades of careful work by people who think about floating-point edge cases for a living; the interface is narrow, the behaviour is specified, and our version would be slower, buggier, and no more ours in any way that counts. A database is the same story, since durability and concurrency are huge problems a good engine has already solved at a scale we will never stress it to. Rewriting these would waste the firm’s scarcest hours reinventing a wheel that already rolls, and no amount of calling it diligence would make it so.
The line between build and buy comes down to this: own what you must be able to reason about, and borrow what you would only ever reason about badly.
Two languages, one boundary
Inside what we build, two languages do the work, on opposite sides of one deliberate line.
Python is where ideas live and mostly die. While we are still deciding whether an effect is real, what matters is how fast we can get from a hypothesis to killing it. Iteration speed beats runtime speed at that stage by a wide margin, and Python’s ecosystem for the job is unmatched: the loop from idea to falsification is short, and most ideas do not survive it. That is the point. The cheap place to learn that something does not work is a research notebook, not a live book with money on it.
Rust is where the survivors run. The moment a strategy earns a place on the hot path, every constraint inverts. Now we want determinism, latency we can predict rather than merely hope for, and memory safety without a garbage collector deciding to pause the world on its own schedule. That last one matters more than it sounds. A tail we cannot explain is a tail we cannot size against, and a runtime that stops to collect garbage at a moment of its own choosing manufactures exactly those tails, a stall landing in the wrong millisecond that turns a fill into a miss. Rust lets us write the market-data and execution path without inviting that whole category of surprise in.
The genuinely interesting engineering happens in the seam between the two languages, more than in either one on its own, and we treat that seam as a real interface rather than a convention. It is narrow, versioned, and typed strictly enough that a shape which changes in research cannot quietly reach production. We hold both sides to it with contract tests: the same recorded inputs run through the research code and the production code, and the outputs must agree to a tolerance fixed in advance, or the build fails and nothing ships.
The crossing is explicit. A research artefact is never reimplemented by hand in Rust, because a reimplementation is a fresh chance to introduce a difference nobody notices until it costs money. Instead the research process emits a configuration - the parameters, the exact feature definitions, the version of the logic that was tested - and the production system reads it directly. What we ran in research and what we run live are one description of the strategy, executed by two engines. Every gap between them is somewhere a result can quietly change meaning, so we leave as few as we can.
The unglamorous eighty per cent
This is not the part you would put in a brochure. The strategy is a sliver of the code. The rest, call it eighty per cent of the work, is the machinery that keeps the sliver honest and alive, and it is relentlessly unglamorous.
Telemetry is most of it. If you cannot see what the system did in enough detail to reconstruct why, you are back to owning a black box you happen to have written yourself, which is no improvement on buying one. So we spend real effort on instrumentation nobody outside the firm will ever admire: every decision logged with the state that produced it, every order tagged so it can be walked back to its cause. Deployment is another slice, because getting a change onto a live system without a heart-stopping moment is its own engineering problem, and doing it with positions open raises the stakes. The test harnesses have to replay history faithfully enough that a green suite actually means something. And someone has to carry the pager: if a venue starts misbehaving in the small hours, the fix does not get handed off to a follow-the-sun support rota sitting in a different time zone. It lands on whoever is on call, and stays there until it is resolved.
This is the honest reason building costs more than buying. The strategy is cheap. Everything that lets us trust it and keep it alive is most of the bill, and there is no version of doing this well where that part quietly gets cheaper.
Why owning it compounds
Here is what makes the bill worth paying: understanding compounds. Every layer we genuinely know makes the next bad night shorter, because the search for a fault starts from a map instead of from nothing. Whoever has already taken the market-data path apart once does not rediscover it under pressure; they go straight to the few places the problem could plausibly be. Knowledge accretes in a codebase you own, and it simply does not in one you rent.
The alternative is the thing we will not do. Trade against your own ignorance and you are not running a systematic strategy at all; you are running someone else’s, blind, and calling the parts you cannot see bad luck when they take your money. We would rather pay to understand. We will spend almost anything before we spend that.