So you can tell before you write whether there is a job here. If we aren't the right people for it, we would rather you knew now.
Three things. If one is missing, say so on the call: it changes what is worth doing, and sometimes it means there is no job, which we will tell you.
This part is for whoever writes your code. None of it is a rule: it is what makes an audit quick and cheap rather than slow and expensive, and anything in the middle column simply means we say so in the quote.
| Ideal | Workable | Ask us first | |
|---|---|---|---|
| File format | Parquet | CSV, a database export | data we have to keep downloading live; a platform you can't export from |
| Data size | Up to ~1 GB | 1–4 GB, adds a day or two or is requoted; 4–25 GB, requoted on a larger machine | larger than ~25 GB |
| How detailed | Daily or hourly prices | minute bars; tick data or L2 order-book snapshots up to ~25 GB, requoted | tick or L2 data over ~25 GB |
| Code | Python, with its history (e.g. Git) | R, MATLAB, C++ | a program we can't open; a signal you can't show us |
| Run time | Under 2 hours a run | 2–6 hours, adds days | longer than that |
Run time matters more than people expect. FC-1 alone means running your test at least twice, and every version we try in FC-6 is another run. A six-hour backtest is not a six-hour job.
On confidentiality: tell us if any part of the strategy can't be shown to us — we can usually work around a part we can't open, and it is far better known before we start than halfway through. Your code and data are deleted when we deliver, unless you ask us to keep them.
The eight checks work in any market. Knowing how a particular market works is a different thing, so here is where our knowledge stops.
Outside crypto we hold no market data of our own: you send yours or we buy it in, and we tell you which, and what it costs, before any work starts.
Every audit asks the same eight questions, numbered FC-1 to FC-8 so each finding in your report points at one of them. Open any check for an everyday example.
Think of a recipe. You bake a cake and it comes out perfect. You give the recipe to a friend, they follow it exactly, and something different comes out. Then the recipe was never the whole story — something in your kitchen was doing work nobody wrote down.
The strategy is the recipe. The number is the cake. So we take the recipe to a different kitchen and bake it. More often than anyone expects, a different cake comes out — and then the number was never really about the strategy. It was about that one computer, on that one day.
And if nobody else can get your number, every change you make afterwards — bigger size, new settings — is being judged against something that wobbles.
You go fishing with a net. The holes are big, so the little fish swim straight through. You pull it up, look in, and every fish is a big one. You could easily decide that all fish are big. The net told you that, and the net was wrong.
A plan needs a list of things it was allowed to buy, and nearly everyone builds that list the same way: ask the computer, today, what is available — then run the test back across the last five years. That list is a net with big holes. Anything that died along the way slipped through and is not in it, so the test only ever buys things it already knows survived. Nothing warns you. An absence does not announce itself.
The ones that slip through the net are the ones that failed. So the one thing their test can never show you is what failure costs.
You take money out of the ATM and it charges you $2. Once? You would shrug. But this plan goes to the ATM five times a day, every day, for six years. Now the $2 is not a detail on the receipt. It is the single biggest thing in the plan, and it can turn a winner into a loser on its own.
So how does anyone know it is $2? Somebody said so. They looked at a different machine, a while ago, and wrote it down, and it has been copied from document to document ever since. Nobody has looked at a receipt. And it was never one number anyway — taking out $20 and taking out $2,000 are not charged the same way, and the figure people quote is nearly always the $20 one.
In our own work we found one counted about twenty times too big. Three strategies had already been switched off because of it.
A cost that was copied instead of measured goes wrong in both directions. It flatters plans that do not work, and it quietly kills plans that do — and the ones it kills do not complain. They just go quiet.
A full load of whites, and one red sock goes in with them. Everything comes out pink. And here is the real problem: the damage is not in the sock any more. It is spread thinly across everything, and there is nothing left to pull out and throw away. Every item is a bit pink, and a bit pink is easy to live with.
Market data arrives from outside, and now and then a row is broken. Not slightly. Ridiculously. One day’s figure came in thousands of times bigger than that market had ever moved in a day, went into the wash with thirty months of ordinary days, and came out as a result of nearly 200% a year.
Every safety check passed, because they all checked what came out of the machine rather than what went into it. By then there is no bright red sock, just a faintly rosy month — and a rosy month is not suspicious, it is what everybody was hoping for. Worse, checking two rows and checking every row look identical when you read the code. The only difference is how many it ran on.
Anything checked by sampling is unchecked. The row that breaks everything is the strange one, and the strange row is exactly what a sample is least likely to hold.
A strategy decides from a picture of the market — who wants to buy, who wants to sell. That picture is always slightly old. A second or two.
Most days that changes nothing. On the day everybody runs for the door, it is the whole story. The strategy sees a large crowd of buyers, which looks like safety — but that crowd left a few seconds ago. The signal was not broken. It was reading a photo of a room that was already empty. And that is not bad luck: it is what the strategy does every time the market moves faster than its picture updates.
Which means it will happen again — and your own backtest cannot tell you how often, because it is the thing that recorded those days as wins.
I hide a candy in one hand and you guess. Wrong. Guess again. Wrong. Keep going. On the ninth try you get it, and I write down: guessed right. I do not write down the eight misses. Read my notes and you are a mind reader.
Someone builds the plan, then tries it forty different ways and writes up the one that came out best. The other thirty-nine leave no trace. So the number you are shown is doing two jobs at once: it is the score, and it is also the reason this version is the one in front of you. Pick the highest of forty and of course it is high.
Trade count hides the same trick. Two thousand trades that all landed in the same six panic weeks are six answers, said loudly. So we set days aside before the searching starts and let the plan try them — once. Peek, adjust, peek again, and those days are part of the search too.
Trying lots of things is not cheating — everybody does it. That is exactly why the only real test is data set aside before the searching started, and why you only get one shot at it.
Imagine telling a playground: “I will swap my sandwich for anyone’s cookie.” Who says yes? The ones with the bad cookies. Whoever has the good cookie keeps it.
A strategy that waits for a good price has the same problem: the moments it gets that price are often the moments everyone else is leaving — which is why the price came to it. Add two more: the shelf said $10, but there was one left and you wanted fifty; and by the time you got to the register it was gone. A backtest usually writes down the shelf price, because on paper nothing runs out and nobody else is in the store.
And the days that made most of the money are usually the fastest, wildest ones — which are exactly the days when the price on paper is least likely to have been real.
Imagine someone takes 41 photos of themselves and shows you the best one. The photo is real. Nobody faked it. But it is not how they normally look — it is their luckiest try out of 41.
And a player who scores 8 shots out of 10 is not an 80% shooter. That was one afternoon. So when a result says 61.4%, the question is simply: if you ran it all again, what would you get? If the honest answer is anywhere between 45 and 75, that is not a number. It is a direction — which is still worth something, but it is a different thing to tell an investor.
A number with no wiggle room written next to it is a promise nobody has tested.
They come from mistakes we have actually hit in our own research — not from a textbook. All eight are published on our front page as case studies against ourselves, with the dated internal document each one came from.
The tools behind four of them (FC-1, FC-4, FC-6, FC-8) are public: fctools, free for anyone to use (MIT licence), with a checksum and a signature so your team can confirm the download is ours. Run them on your own work before you send it — anything they catch is cheaper found by you.
We run your code twice, on two different machines, and see whether the same number comes back. Nothing else means anything until this does.
What going wrong looks likeThe author cannot get their own headline number out again — far more common than anyone expects, and usually a hidden setting that changes how the program orders its work from run to run.Was the list of assets built as it looked back then, or from the ones still trading today? And if something was already patched to fix this, does the patch actually test the thing it claims to?
What going wrong looks likeThe names that disappeared are absent from the test — and the ones that disappear are usually the losers, so the record flatters itself.Where did the fee figure come from? Is it charged correctly — once for the whole portfolio, or once per position? Was it measured at the order sizes you would actually trade, or read off a screen?
What going wrong looks likeOne cost assumption appears in three documents with three different numbers, and nobody has measured which is right.Was every line of data checked, or just a sample? We run the checks that already exist across the whole set rather than the slice someone looked at.
What going wrong looks likeA single bad price carries thirty months of apparent return, and removing it removes the strategy.Do the filters meant to exclude stale data actually look at a date? We read what the code does, not what it is called.
What going wrong looks likeA filter named for keeping data recent that never reads a date. It counts lines and wears the name.How many versions were tried before this one, and was this one chosen before or after seeing how it performed? Is there any data it has genuinely never been run on?
What going wrong looks likeHundreds of versions tested, the best one reported, and nothing held back to check it against. The result is the search.Could every position in the result actually have been bought and sold, at the price used, in the size assumed — and using only what was knowable at that moment?
What going wrong looks likePositions opened and closed at prices that existed, in sizes that did not. Or open positions quietly dropped at the end instead of valued, which turns a loss into a profit.Is the headline a single number or a range? We work out its likely range and ask whether that range is wide enough to include zero.
What going wrong looks likeA figure quoted to two decimal places that is really a range — and a perfectly reasonable alternative way of running the same code lands somewhere else entirely.Start to finish, for a full audit. A Triage is steps 1 to 3 and stops there. Here is what the finished report looks like.
An audit that finds nothing wrong is a real and useful outcome: it is what you are buying the right to conclude. We would rather agree that now than argue about it later.
We don't silently absorb it, and we don't silently bill for it.
Both are usually true. Neither one settles the question.
Usually true. It is also not the reassurance it sounds like, because a charting website is not where the data lives. It is a window onto somebody else’s copy.
The history can change after you have looked at it. Exchanges cancel bad trades and repair them days later. Data providers fill in gaps after the fact. The chart you built the plan on is not always the chart you would draw today, and nothing tells you when a past price has changed.
The same coin does not have one price. One exchange’s price is not another’s. And a coin that gets removed simply stops appearing in the search box — which is FC-2, built into the interface.
A chart cannot tell you what trading costs. A chart shows each period's opening, highest, lowest and closing price, and how much traded. That is a record of what the market did, not of what you would have paid. So the fee in the test did not come from the exchange at all. It came from a document.
And fees were never one number. They are tiered by volume and account level, and they change across the years a test covers. A test that quietly assumes every order waited patiently for a good price, when the strategy would in fact have had to pay up to trade at once, is wrong in the flattering direction every single time. Perps also pay funding, a real recurring cost that is often left out altogether.
Official is not the same as checked, and a display is not the same as the record.
That is, every buy and sell order waiting at each price, often as L2 snapshots. It is better than most people arrive with, and it genuinely helps. With it, what a trade would have cost stops being a guess and becomes something you can measure.
Having it and using it are different things. The question is what the code did with it. Did it work through the waiting orders, one price at a time, until the whole order was filled? Or did it read the best price and assume the whole thing went through there? The second is the common case, and in the output the two look identical.
It shows what was on offer, not what you would have got. Your own order moves the price. Somebody else was reaching for the same thing. And the days that make most of the money are the fast ones, when there are fewest orders waiting and the picture you are reading is most out of date.
And it leaves the other seven checks exactly where they were. A perfect order book says nothing about which assets were on the list, how many versions were tried before this one, or whether a single broken figure is carrying the result.
Better data does not audit itself. It moves the place where the mistake can hide.
That is what the first call is for, and it is free. Tell us what the strategy is and what you have. If there isn't enough there to reproduce anything, we'll tell you on that call and turn the work down — before any money changes hands.
Backtest forensics · OsloPrices, and the ten findings we published against our own research, are on the front page.
Research due-diligence and code-audit services only. This is not investment advice, not a recommendation to buy or sell anything, and not an offer to manage money. Our findings say whether a stated result can be reproduced from the material supplied — they are not an opinion on how any strategy will perform in future. The case studies referred to describe defects found in our own internal research; no client work is described.